device_schedule_callback() needs a module reference
authorAlan Stern <stern@rowland.harvard.edu>
Thu, 26 Apr 2007 07:12:04 +0000 (00:12 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 27 Apr 2007 17:57:32 +0000 (10:57 -0700)
This patch (as896b) fixes an oversight in the design of
device_schedule_callback().  It is necessary to acquire a reference to the
module owning the callback routine, to prevent the module from being
unloaded before the callback can run.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: Satyam Sharma <satyam.sharma@gmail.com>
Cc: Neil Brown <neilb@suse.de>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/base/core.c
fs/sysfs/file.c
include/linux/device.h
include/linux/sysfs.h

index f69305c..8aa090d 100644 (file)
@@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
 EXPORT_SYMBOL_GPL(device_remove_bin_file);
 
 /**
- * device_schedule_callback - helper to schedule a callback for a device
+ * device_schedule_callback_owner - helper to schedule a callback for a device
  * @dev: device.
  * @func: callback function to invoke later.
+ * @owner: module owning the callback routine
  *
  * Attribute methods must not unregister themselves or their parent device
  * (which would amount to the same thing).  Attempts to do so will deadlock,
@@ -493,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file);
  * argument in the workqueue's process context.  @dev will be pinned until
  * @func returns.
  *
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
  * Returns 0 if the request was submitted, -ENOMEM if storage could not
- * be allocated.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
  *
  * NOTE: This routine won't work if CONFIG_SYSFS isn't set!  It uses an
  * underlying sysfs routine (since it is intended for use by attribute
  * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
  */
-int device_schedule_callback(struct device *dev,
-               void (*func)(struct device *))
+int device_schedule_callback_owner(struct device *dev,
+               void (*func)(struct device *), struct module *owner)
 {
        return sysfs_schedule_callback(&dev->kobj,
-                       (void (*)(void *)) func, dev);
+                       (void (*)(void *)) func, dev, owner);
 }
-EXPORT_SYMBOL_GPL(device_schedule_callback);
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
 
 static void klist_children_get(struct klist_node *n)
 {
index fc46333..db0413a 100644 (file)
@@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct {
        struct kobject          *kobj;
        void                    (*func)(void *);
        void                    *data;
+       struct module           *owner;
        struct work_struct      work;
 };
 
@@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
 
        (ss->func)(ss->data);
        kobject_put(ss->kobj);
+       module_put(ss->owner);
        kfree(ss);
 }
 
@@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
  * @kobj: object we're acting for.
  * @func: callback function to invoke later.
  * @data: argument to pass to @func.
+ * @owner: module owning the callback code
  *
  * sysfs attribute methods must not unregister themselves or their parent
  * kobject (which would amount to the same thing).  Attempts to do so will
@@ -663,20 +666,25 @@ 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.
+ * be allocated, -ENODEV if a reference to @owner isn't available.
  */
 int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
-               void *data)
+               void *data, struct module *owner)
 {
        struct sysfs_schedule_callback_struct *ss;
 
+       if (!try_module_get(owner))
+               return -ENODEV;
        ss = kmalloc(sizeof(*ss), GFP_KERNEL);
-       if (!ss)
+       if (!ss) {
+               module_put(owner);
                return -ENOMEM;
+       }
        kobject_get(kobj);
        ss->kobj = kobj;
        ss->func = func;
        ss->data = data;
+       ss->owner = owner;
        INIT_WORK(&ss->work, sysfs_schedule_callback_work);
        schedule_work(&ss->work);
        return 0;
index af603a1..8511d14 100644 (file)
@@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev,
                                               struct bin_attribute *attr);
 extern void device_remove_bin_file(struct device *dev,
                                   struct bin_attribute *attr);
-extern int device_schedule_callback(struct device *dev,
-               void (*func)(struct device *));
+extern int device_schedule_callback_owner(struct device *dev,
+               void (*func)(struct device *), struct module *owner);
+
+/* This is a macro to avoid include problems with THIS_MODULE */
+#define device_schedule_callback(dev, func)                    \
+       device_schedule_callback_owner(dev, func, THIS_MODULE)
 
 /* device resource management */
 typedef void (*dr_release_t)(struct device *dev, void *res);
index fea9a6b..7d5d1ec 100644 (file)
@@ -80,7 +80,7 @@ struct sysfs_ops {
 #ifdef CONFIG_SYSFS
 
 extern int sysfs_schedule_callback(struct kobject *kobj,
-               void (*func)(void *), void *data);
+               void (*func)(void *), void *data, struct module *owner);
 
 extern int __must_check
 sysfs_create_dir(struct kobject *, struct dentry *);
@@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void);
 #else /* CONFIG_SYSFS */
 
 static inline int sysfs_schedule_callback(struct kobject *kobj,
-               void (*func)(void *), void *data)
+               void (*func)(void *), void *data, struct module *owner)
 {
        return -ENOSYS;
 }