Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / fs / sysfs / file.c
index ce8339c..561a9c0 100644 (file)
@@ -446,14 +446,30 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
        if (buffer->event != atomic_read(&od->event))
                goto trigger;
 
-       return 0;
+       return DEFAULT_POLLMASK;
 
  trigger:
        buffer->needs_read_fill = 1;
-       return POLLERR|POLLPRI;
+       return DEFAULT_POLLMASK|POLLERR|POLLPRI;
+}
+
+void sysfs_notify_dirent(struct sysfs_dirent *sd)
+{
+       struct sysfs_open_dirent *od;
+
+       spin_lock(&sysfs_open_dirent_lock);
+
+       od = sd->s_attr.open;
+       if (od) {
+               atomic_inc(&od->event);
+               wake_up_interruptible(&od->poll);
+       }
+
+       spin_unlock(&sysfs_open_dirent_lock);
 }
+EXPORT_SYMBOL_GPL(sysfs_notify_dirent);
 
-void sysfs_notify(struct kobject *k, char *dir, char *attr)
+void sysfs_notify(struct kobject *k, const char *dir, const char *attr)
 {
        struct sysfs_dirent *sd = k->sd;
 
@@ -463,19 +479,8 @@ void sysfs_notify(struct kobject *k, char *dir, char *attr)
                sd = sysfs_find_dirent(sd, dir);
        if (sd && attr)
                sd = sysfs_find_dirent(sd, attr);
-       if (sd) {
-               struct sysfs_open_dirent *od;
-
-               spin_lock(&sysfs_open_dirent_lock);
-
-               od = sd->s_attr.open;
-               if (od) {
-                       atomic_inc(&od->event);
-                       wake_up_interruptible(&od->poll);
-               }
-
-               spin_unlock(&sysfs_open_dirent_lock);
-       }
+       if (sd)
+               sysfs_notify_dirent(sd);
 
        mutex_unlock(&sysfs_mutex);
 }
@@ -654,13 +659,17 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
 EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
 
 struct sysfs_schedule_callback_struct {
-       struct kobject          *kobj;
+       struct list_head        workq_list;
+       struct kobject          *kobj;
        void                    (*func)(void *);
        void                    *data;
        struct module           *owner;
        struct work_struct      work;
 };
 
+static struct workqueue_struct *sysfs_workqueue;
+static DEFINE_MUTEX(sysfs_workq_mutex);
+static LIST_HEAD(sysfs_workq);
 static void sysfs_schedule_callback_work(struct work_struct *work)
 {
        struct sysfs_schedule_callback_struct *ss = container_of(work,
@@ -669,6 +678,9 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
        (ss->func)(ss->data);
        kobject_put(ss->kobj);
        module_put(ss->owner);
+       mutex_lock(&sysfs_workq_mutex);
+       list_del(&ss->workq_list);
+       mutex_unlock(&sysfs_workq_mutex);
        kfree(ss);
 }
 
@@ -690,15 +702,34 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
  * until @func returns.
  *
  * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated, -ENODEV if a reference to @owner isn't available.
+ * be allocated, -ENODEV if a reference to @owner isn't available,
+ * -EAGAIN if a callback has already been scheduled for @kobj.
  */
 int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
                void *data, struct module *owner)
 {
-       struct sysfs_schedule_callback_struct *ss;
+       struct sysfs_schedule_callback_struct *ss, *tmp;
 
        if (!try_module_get(owner))
                return -ENODEV;
+
+       mutex_lock(&sysfs_workq_mutex);
+       list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
+               if (ss->kobj == kobj) {
+                       module_put(owner);
+                       mutex_unlock(&sysfs_workq_mutex);
+                       return -EAGAIN;
+               }
+       mutex_unlock(&sysfs_workq_mutex);
+
+       if (sysfs_workqueue == NULL) {
+               sysfs_workqueue = create_singlethread_workqueue("sysfsd");
+               if (sysfs_workqueue == NULL) {
+                       module_put(owner);
+                       return -ENOMEM;
+               }
+       }
+
        ss = kmalloc(sizeof(*ss), GFP_KERNEL);
        if (!ss) {
                module_put(owner);
@@ -710,7 +741,11 @@ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
        ss->data = data;
        ss->owner = owner;
        INIT_WORK(&ss->work, sysfs_schedule_callback_work);
-       schedule_work(&ss->work);
+       INIT_LIST_HEAD(&ss->workq_list);
+       mutex_lock(&sysfs_workq_mutex);
+       list_add_tail(&ss->workq_list, &sysfs_workq);
+       mutex_unlock(&sysfs_workq_mutex);
+       queue_work(sysfs_workqueue, &ss->work);
        return 0;
 }
 EXPORT_SYMBOL_GPL(sysfs_schedule_callback);