diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-04-26 03:12:04 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 13:57:32 -0400 |
commit | 523ded71de0c5e66973335bf99a80edfda9f401b (patch) | |
tree | 20f47e8ed91018977d9fb7f4169a6ef8db407b82 | |
parent | fa1a8c23eb7d3ded8a3c6d0e653339a2bc7fca9e (diff) |
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>
-rw-r--r-- | drivers/base/core.c | 16 | ||||
-rw-r--r-- | fs/sysfs/file.c | 14 | ||||
-rw-r--r-- | include/linux/device.h | 8 | ||||
-rw-r--r-- | include/linux/sysfs.h | 4 |
4 files changed, 29 insertions, 13 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index f69305c7269d..8aa090da1cd7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) | |||
480 | EXPORT_SYMBOL_GPL(device_remove_bin_file); | 480 | EXPORT_SYMBOL_GPL(device_remove_bin_file); |
481 | 481 | ||
482 | /** | 482 | /** |
483 | * device_schedule_callback - helper to schedule a callback for a device | 483 | * device_schedule_callback_owner - helper to schedule a callback for a device |
484 | * @dev: device. | 484 | * @dev: device. |
485 | * @func: callback function to invoke later. | 485 | * @func: callback function to invoke later. |
486 | * @owner: module owning the callback routine | ||
486 | * | 487 | * |
487 | * Attribute methods must not unregister themselves or their parent device | 488 | * Attribute methods must not unregister themselves or their parent device |
488 | * (which would amount to the same thing). Attempts to do so will deadlock, | 489 | * (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 | * argument in the workqueue's process context. @dev will be pinned until | 494 | * argument in the workqueue's process context. @dev will be pinned until |
494 | * @func returns. | 495 | * @func returns. |
495 | * | 496 | * |
497 | * This routine is usually called via the inline device_schedule_callback(), | ||
498 | * which automatically sets @owner to THIS_MODULE. | ||
499 | * | ||
496 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | 500 | * Returns 0 if the request was submitted, -ENOMEM if storage could not |
497 | * be allocated. | 501 | * be allocated, -ENODEV if a reference to @owner isn't available. |
498 | * | 502 | * |
499 | * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an | 503 | * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an |
500 | * underlying sysfs routine (since it is intended for use by attribute | 504 | * underlying sysfs routine (since it is intended for use by attribute |
501 | * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. | 505 | * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. |
502 | */ | 506 | */ |
503 | int device_schedule_callback(struct device *dev, | 507 | int device_schedule_callback_owner(struct device *dev, |
504 | void (*func)(struct device *)) | 508 | void (*func)(struct device *), struct module *owner) |
505 | { | 509 | { |
506 | return sysfs_schedule_callback(&dev->kobj, | 510 | return sysfs_schedule_callback(&dev->kobj, |
507 | (void (*)(void *)) func, dev); | 511 | (void (*)(void *)) func, dev, owner); |
508 | } | 512 | } |
509 | EXPORT_SYMBOL_GPL(device_schedule_callback); | 513 | EXPORT_SYMBOL_GPL(device_schedule_callback_owner); |
510 | 514 | ||
511 | static void klist_children_get(struct klist_node *n) | 515 | static void klist_children_get(struct klist_node *n) |
512 | { | 516 | { |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fc4633378dc0..db0413a411d6 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct { | |||
633 | struct kobject *kobj; | 633 | struct kobject *kobj; |
634 | void (*func)(void *); | 634 | void (*func)(void *); |
635 | void *data; | 635 | void *data; |
636 | struct module *owner; | ||
636 | struct work_struct work; | 637 | struct work_struct work; |
637 | }; | 638 | }; |
638 | 639 | ||
@@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) | |||
643 | 644 | ||
644 | (ss->func)(ss->data); | 645 | (ss->func)(ss->data); |
645 | kobject_put(ss->kobj); | 646 | kobject_put(ss->kobj); |
647 | module_put(ss->owner); | ||
646 | kfree(ss); | 648 | kfree(ss); |
647 | } | 649 | } |
648 | 650 | ||
@@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) | |||
651 | * @kobj: object we're acting for. | 653 | * @kobj: object we're acting for. |
652 | * @func: callback function to invoke later. | 654 | * @func: callback function to invoke later. |
653 | * @data: argument to pass to @func. | 655 | * @data: argument to pass to @func. |
656 | * @owner: module owning the callback code | ||
654 | * | 657 | * |
655 | * sysfs attribute methods must not unregister themselves or their parent | 658 | * sysfs attribute methods must not unregister themselves or their parent |
656 | * kobject (which would amount to the same thing). Attempts to do so will | 659 | * 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 | * until @func returns. | 666 | * until @func returns. |
664 | * | 667 | * |
665 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | 668 | * Returns 0 if the request was submitted, -ENOMEM if storage could not |
666 | * be allocated. | 669 | * be allocated, -ENODEV if a reference to @owner isn't available. |
667 | */ | 670 | */ |
668 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | 671 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), |
669 | void *data) | 672 | void *data, struct module *owner) |
670 | { | 673 | { |
671 | struct sysfs_schedule_callback_struct *ss; | 674 | struct sysfs_schedule_callback_struct *ss; |
672 | 675 | ||
676 | if (!try_module_get(owner)) | ||
677 | return -ENODEV; | ||
673 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); | 678 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); |
674 | if (!ss) | 679 | if (!ss) { |
680 | module_put(owner); | ||
675 | return -ENOMEM; | 681 | return -ENOMEM; |
682 | } | ||
676 | kobject_get(kobj); | 683 | kobject_get(kobj); |
677 | ss->kobj = kobj; | 684 | ss->kobj = kobj; |
678 | ss->func = func; | 685 | ss->func = func; |
679 | ss->data = data; | 686 | ss->data = data; |
687 | ss->owner = owner; | ||
680 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); | 688 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); |
681 | schedule_work(&ss->work); | 689 | schedule_work(&ss->work); |
682 | return 0; | 690 | return 0; |
diff --git a/include/linux/device.h b/include/linux/device.h index af603a137690..8511d14071b3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev, | |||
367 | struct bin_attribute *attr); | 367 | struct bin_attribute *attr); |
368 | extern void device_remove_bin_file(struct device *dev, | 368 | extern void device_remove_bin_file(struct device *dev, |
369 | struct bin_attribute *attr); | 369 | struct bin_attribute *attr); |
370 | extern int device_schedule_callback(struct device *dev, | 370 | extern int device_schedule_callback_owner(struct device *dev, |
371 | void (*func)(struct device *)); | 371 | void (*func)(struct device *), struct module *owner); |
372 | |||
373 | /* This is a macro to avoid include problems with THIS_MODULE */ | ||
374 | #define device_schedule_callback(dev, func) \ | ||
375 | device_schedule_callback_owner(dev, func, THIS_MODULE) | ||
372 | 376 | ||
373 | /* device resource management */ | 377 | /* device resource management */ |
374 | typedef void (*dr_release_t)(struct device *dev, void *res); | 378 | typedef void (*dr_release_t)(struct device *dev, void *res); |
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fea9a6b3fb7b..7d5d1ec95c2e 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
@@ -80,7 +80,7 @@ struct sysfs_ops { | |||
80 | #ifdef CONFIG_SYSFS | 80 | #ifdef CONFIG_SYSFS |
81 | 81 | ||
82 | extern int sysfs_schedule_callback(struct kobject *kobj, | 82 | extern int sysfs_schedule_callback(struct kobject *kobj, |
83 | void (*func)(void *), void *data); | 83 | void (*func)(void *), void *data, struct module *owner); |
84 | 84 | ||
85 | extern int __must_check | 85 | extern int __must_check |
86 | sysfs_create_dir(struct kobject *, struct dentry *); | 86 | sysfs_create_dir(struct kobject *, struct dentry *); |
@@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void); | |||
137 | #else /* CONFIG_SYSFS */ | 137 | #else /* CONFIG_SYSFS */ |
138 | 138 | ||
139 | static inline int sysfs_schedule_callback(struct kobject *kobj, | 139 | static inline int sysfs_schedule_callback(struct kobject *kobj, |
140 | void (*func)(void *), void *data) | 140 | void (*func)(void *), void *data, struct module *owner) |
141 | { | 141 | { |
142 | return -ENOSYS; | 142 | return -ENOSYS; |
143 | } | 143 | } |