forked from mirrors/linux
		
	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
					
				
					 4 changed files with 29 additions and 13 deletions
				
			
		|  | @ -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) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
| #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; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Alan Stern
						Alan Stern