aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Chiang <achiang@hp.com>2009-03-13 14:07:36 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-03-24 19:38:26 -0400
commit669420644c79c207f83fdf9105ae782867e2991f (patch)
tree668491b3700bcc65e45d5ff9471f6fde5d5743af
parentffa6a7054d172a2f57248dff2de600ca795c5656 (diff)
sysfs: only allow one scheduled removal callback per kobj
The only way for a sysfs attribute to remove itself (without deadlock) is to use the sysfs_schedule_callback() interface. Vegard Nossum discovered that a poorly written sysfs ->store callback can repeatedly schedule remove callbacks on the same device over and over, e.g. $ while true ; do echo 1 > /sys/devices/.../remove ; done If the 'remove' attribute uses the sysfs_schedule_callback API and also does not protect itself from concurrent accesses, its callback handler will be called multiple times, and will eventually attempt to perform operations on a freed kobject, leading to many problems. Instead of requiring all callers of sysfs_schedule_callback to implement their own synchronization, provide the protection in the infrastructure. Now, sysfs_schedule_callback will only allow one scheduled callback per kobject. On subsequent calls with the same kobject, return -EAGAIN. This is a short term fix. The long term fix is to allow sysfs attributes to remove themselves directly, without any of this callback hokey pokey. [cornelia.huck@de.ibm.com: s390 ccwgroup bits] Reported-by: vegard.nossum@gmail.com Signed-off-by: Alex Chiang <achiang@hp.com> Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/s390/cio/ccwgroup.c5
-rw-r--r--fs/sysfs/file.c26
2 files changed, 26 insertions, 5 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 918e6fce2573..b91c1719b075 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -104,8 +104,9 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
104 rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); 104 rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
105out: 105out:
106 if (rc) { 106 if (rc) {
107 /* Release onoff "lock" when ungrouping failed. */ 107 if (rc != -EAGAIN)
108 atomic_set(&gdev->onoff, 0); 108 /* Release onoff "lock" when ungrouping failed. */
109 atomic_set(&gdev->onoff, 0);
109 return rc; 110 return rc;
110 } 111 }
111 return count; 112 return count;
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 1f4a3f877262..289c43a47263 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -659,13 +659,16 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
659EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); 659EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
660 660
661struct sysfs_schedule_callback_struct { 661struct sysfs_schedule_callback_struct {
662 struct kobject *kobj; 662 struct list_head workq_list;
663 struct kobject *kobj;
663 void (*func)(void *); 664 void (*func)(void *);
664 void *data; 665 void *data;
665 struct module *owner; 666 struct module *owner;
666 struct work_struct work; 667 struct work_struct work;
667}; 668};
668 669
670static DEFINE_MUTEX(sysfs_workq_mutex);
671static LIST_HEAD(sysfs_workq);
669static void sysfs_schedule_callback_work(struct work_struct *work) 672static void sysfs_schedule_callback_work(struct work_struct *work)
670{ 673{
671 struct sysfs_schedule_callback_struct *ss = container_of(work, 674 struct sysfs_schedule_callback_struct *ss = container_of(work,
@@ -674,6 +677,9 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
674 (ss->func)(ss->data); 677 (ss->func)(ss->data);
675 kobject_put(ss->kobj); 678 kobject_put(ss->kobj);
676 module_put(ss->owner); 679 module_put(ss->owner);
680 mutex_lock(&sysfs_workq_mutex);
681 list_del(&ss->workq_list);
682 mutex_unlock(&sysfs_workq_mutex);
677 kfree(ss); 683 kfree(ss);
678} 684}
679 685
@@ -695,15 +701,25 @@ static void sysfs_schedule_callback_work(struct work_struct *work)
695 * until @func returns. 701 * until @func returns.
696 * 702 *
697 * Returns 0 if the request was submitted, -ENOMEM if storage could not 703 * Returns 0 if the request was submitted, -ENOMEM if storage could not
698 * be allocated, -ENODEV if a reference to @owner isn't available. 704 * be allocated, -ENODEV if a reference to @owner isn't available,
705 * -EAGAIN if a callback has already been scheduled for @kobj.
699 */ 706 */
700int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), 707int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
701 void *data, struct module *owner) 708 void *data, struct module *owner)
702{ 709{
703 struct sysfs_schedule_callback_struct *ss; 710 struct sysfs_schedule_callback_struct *ss, *tmp;
704 711
705 if (!try_module_get(owner)) 712 if (!try_module_get(owner))
706 return -ENODEV; 713 return -ENODEV;
714
715 mutex_lock(&sysfs_workq_mutex);
716 list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
717 if (ss->kobj == kobj) {
718 mutex_unlock(&sysfs_workq_mutex);
719 return -EAGAIN;
720 }
721 mutex_unlock(&sysfs_workq_mutex);
722
707 ss = kmalloc(sizeof(*ss), GFP_KERNEL); 723 ss = kmalloc(sizeof(*ss), GFP_KERNEL);
708 if (!ss) { 724 if (!ss) {
709 module_put(owner); 725 module_put(owner);
@@ -715,6 +731,10 @@ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
715 ss->data = data; 731 ss->data = data;
716 ss->owner = owner; 732 ss->owner = owner;
717 INIT_WORK(&ss->work, sysfs_schedule_callback_work); 733 INIT_WORK(&ss->work, sysfs_schedule_callback_work);
734 INIT_LIST_HEAD(&ss->workq_list);
735 mutex_lock(&sysfs_workq_mutex);
736 list_add_tail(&ss->workq_list, &sysfs_workq);
737 mutex_unlock(&sysfs_workq_mutex);
718 schedule_work(&ss->work); 738 schedule_work(&ss->work);
719 return 0; 739 return 0;
720} 740}