Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+10 −6
Original line number Original line Diff line number Diff line
@@ -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);
 * 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)
{
{
+11 −3
Original line number Original line Diff line number Diff line
@@ -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)


	(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)
 * @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)
 * 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;
+6 −2
Original line number Original line Diff line number Diff line
@@ -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);
+2 −2
Original line number Original line Diff line number Diff line
@@ -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);
#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;
}
}