Commit 523ded71 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

device_schedule_callback() needs a module reference

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: default avatarAlan 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent fa1a8c23
...@@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) ...@@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
EXPORT_SYMBOL_GPL(device_remove_bin_file); 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. * @dev: device.
* @func: callback function to invoke later. * @func: callback function to invoke later.
* @owner: module owning the callback routine
* *
* Attribute methods must not unregister themselves or their parent device * Attribute methods must not unregister themselves or their parent device
* (which would amount to the same thing). Attempts to do so will deadlock, * (which would amount to the same thing). Attempts to do so will deadlock,
...@@ -493,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file); ...@@ -493,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file);
* argument in the workqueue's process context. @dev will be pinned until * argument in the workqueue's process context. @dev will be pinned until
* @func returns. * @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 * 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 * 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 * underlying sysfs routine (since it is intended for use by attribute
* methods), and if sysfs isn't available you'll get nothing but -ENOSYS. * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
*/ */
int device_schedule_callback(struct device *dev, int device_schedule_callback_owner(struct device *dev,
void (*func)(struct device *)) void (*func)(struct device *), struct module *owner)
{ {
return sysfs_schedule_callback(&dev->kobj, 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) static void klist_children_get(struct klist_node *n)
{ {
......
...@@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct { ...@@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct {
struct kobject *kobj; struct kobject *kobj;
void (*func)(void *); void (*func)(void *);
void *data; void *data;
struct module *owner;
struct work_struct work; struct work_struct work;
}; };
...@@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) ...@@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
(ss->func)(ss->data); (ss->func)(ss->data);
kobject_put(ss->kobj); kobject_put(ss->kobj);
module_put(ss->owner);
kfree(ss); kfree(ss);
} }
...@@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) ...@@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
* @kobj: object we're acting for. * @kobj: object we're acting for.
* @func: callback function to invoke later. * @func: callback function to invoke later.
* @data: argument to pass to @func. * @data: argument to pass to @func.
* @owner: module owning the callback code
* *
* sysfs attribute methods must not unregister themselves or their parent * sysfs attribute methods must not unregister themselves or their parent
* kobject (which would amount to the same thing). Attempts to do so will * 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) ...@@ -663,20 +666,25 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
* until @func returns. * until @func returns.
* *
* Returns 0 if the request was submitted, -ENOMEM if storage could not * 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 *), int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
void *data) void *data, struct module *owner)
{ {
struct sysfs_schedule_callback_struct *ss; struct sysfs_schedule_callback_struct *ss;
if (!try_module_get(owner))
return -ENODEV;
ss = kmalloc(sizeof(*ss), GFP_KERNEL); ss = kmalloc(sizeof(*ss), GFP_KERNEL);
if (!ss) if (!ss) {
module_put(owner);
return -ENOMEM; return -ENOMEM;
}
kobject_get(kobj); kobject_get(kobj);
ss->kobj = kobj; ss->kobj = kobj;
ss->func = func; ss->func = func;
ss->data = data; ss->data = data;
ss->owner = owner;
INIT_WORK(&ss->work, sysfs_schedule_callback_work); INIT_WORK(&ss->work, sysfs_schedule_callback_work);
schedule_work(&ss->work); schedule_work(&ss->work);
return 0; return 0;
......
...@@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev, ...@@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev,
struct bin_attribute *attr); struct bin_attribute *attr);
extern void device_remove_bin_file(struct device *dev, extern void device_remove_bin_file(struct device *dev,
struct bin_attribute *attr); struct bin_attribute *attr);
extern int device_schedule_callback(struct device *dev, extern int device_schedule_callback_owner(struct device *dev,
void (*func)(struct device *)); 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 */ /* device resource management */
typedef void (*dr_release_t)(struct device *dev, void *res); typedef void (*dr_release_t)(struct device *dev, void *res);
......
...@@ -80,7 +80,7 @@ struct sysfs_ops { ...@@ -80,7 +80,7 @@ struct sysfs_ops {
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
extern int sysfs_schedule_callback(struct kobject *kobj, 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 extern int __must_check
sysfs_create_dir(struct kobject *, struct dentry *); sysfs_create_dir(struct kobject *, struct dentry *);
...@@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void); ...@@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void);
#else /* CONFIG_SYSFS */ #else /* CONFIG_SYSFS */
static inline int sysfs_schedule_callback(struct kobject *kobj, static inline int sysfs_schedule_callback(struct kobject *kobj,
void (*func)(void *), void *data) void (*func)(void *), void *data, struct module *owner)
{ {
return -ENOSYS; return -ENOSYS;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment