| From akpm@linux-foundation.org Thu Apr 26 00:12:58 2007 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Thu, 26 Apr 2007 00:12:04 -0700 |
| Subject: device_schedule_callback() needs a module reference |
| To: greg@kroah.com |
| Cc: akpm@linux-foundation.org, stern@rowland.harvard.edu, cornelia.huck@de.ibm.com, neilb@suse.de, satyam.sharma@gmail.com |
| Message-ID: <200704260712.l3Q7C4J0023437@shell0.pdx.osdl.net> |
| |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| 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 | 16 ++++++++++------ |
| fs/sysfs/file.c | 14 +++++++++++--- |
| include/linux/device.h | 8 ++++++-- |
| include/linux/sysfs.h | 4 ++-- |
| 4 files changed, 29 insertions(+), 13 deletions(-) |
| |
| --- a/drivers/base/core.c |
| +++ b/drivers/base/core.c |
| @@ -480,9 +480,10 @@ void device_remove_bin_file(struct devic |
| 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) |
| { |
| --- a/fs/sysfs/file.c |
| +++ b/fs/sysfs/file.c |
| @@ -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 |
| |
| (ss->func)(ss->data); |
| kobject_put(ss->kobj); |
| + module_put(ss->owner); |
| kfree(ss); |
| } |
| |
| @@ -651,6 +653,7 @@ static void sysfs_schedule_callback_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 |
| * 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; |
| --- a/include/linux/device.h |
| +++ b/include/linux/device.h |
| @@ -367,8 +367,12 @@ extern int __must_check device_create_bi |
| 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); |
| --- a/include/linux/sysfs.h |
| +++ b/include/linux/sysfs.h |
| @@ -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; |
| } |