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: 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>
This commit is contained in:
parent
fa1a8c23eb
commit
523ded71de
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user