diff options
-rw-r--r-- | drivers/base/core.c | 29 | ||||
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 18 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 14 | ||||
-rw-r--r-- | fs/sysfs/file.c | 54 | ||||
-rw-r--r-- | include/linux/device.h | 2 | ||||
-rw-r--r-- | include/linux/sysfs.h | 9 |
6 files changed, 122 insertions, 4 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index f191afe62b4d..ad0f4a2f25c4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -407,6 +407,35 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) | |||
407 | } | 407 | } |
408 | EXPORT_SYMBOL_GPL(device_remove_bin_file); | 408 | EXPORT_SYMBOL_GPL(device_remove_bin_file); |
409 | 409 | ||
410 | /** | ||
411 | * device_schedule_callback - helper to schedule a callback for a device | ||
412 | * @dev: device. | ||
413 | * @func: callback function to invoke later. | ||
414 | * | ||
415 | * Attribute methods must not unregister themselves or their parent device | ||
416 | * (which would amount to the same thing). Attempts to do so will deadlock, | ||
417 | * since unregistration is mutually exclusive with driver callbacks. | ||
418 | * | ||
419 | * Instead methods can call this routine, which will attempt to allocate | ||
420 | * and schedule a workqueue request to call back @func with @dev as its | ||
421 | * argument in the workqueue's process context. @dev will be pinned until | ||
422 | * @func returns. | ||
423 | * | ||
424 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
425 | * be allocated. | ||
426 | * | ||
427 | * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an | ||
428 | * underlying sysfs routine (since it is intended for use by attribute | ||
429 | * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. | ||
430 | */ | ||
431 | int device_schedule_callback(struct device *dev, | ||
432 | void (*func)(struct device *)) | ||
433 | { | ||
434 | return sysfs_schedule_callback(&dev->kobj, | ||
435 | (void (*)(void *)) func, dev); | ||
436 | } | ||
437 | EXPORT_SYMBOL_GPL(device_schedule_callback); | ||
438 | |||
410 | static void klist_children_get(struct klist_node *n) | 439 | static void klist_children_get(struct klist_node *n) |
411 | { | 440 | { |
412 | struct device *dev = container_of(n, struct device, knode_parent); | 441 | struct device *dev = container_of(n, struct device, knode_parent); |
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index d48e3ca4752c..5aeb68e732b0 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c | |||
@@ -71,19 +71,31 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) | |||
71 | * Provide an 'ungroup' attribute so the user can remove group devices no | 71 | * Provide an 'ungroup' attribute so the user can remove group devices no |
72 | * longer needed or accidentially created. Saves memory :) | 72 | * longer needed or accidentially created. Saves memory :) |
73 | */ | 73 | */ |
74 | static void ccwgroup_ungroup_callback(struct device *dev) | ||
75 | { | ||
76 | struct ccwgroup_device *gdev = to_ccwgroupdev(dev); | ||
77 | |||
78 | __ccwgroup_remove_symlinks(gdev); | ||
79 | device_unregister(dev); | ||
80 | } | ||
81 | |||
74 | static ssize_t | 82 | static ssize_t |
75 | ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 83 | ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
76 | { | 84 | { |
77 | struct ccwgroup_device *gdev; | 85 | struct ccwgroup_device *gdev; |
86 | int rc; | ||
78 | 87 | ||
79 | gdev = to_ccwgroupdev(dev); | 88 | gdev = to_ccwgroupdev(dev); |
80 | 89 | ||
81 | if (gdev->state != CCWGROUP_OFFLINE) | 90 | if (gdev->state != CCWGROUP_OFFLINE) |
82 | return -EINVAL; | 91 | return -EINVAL; |
83 | 92 | ||
84 | __ccwgroup_remove_symlinks(gdev); | 93 | /* Note that we cannot unregister the device from one of its |
85 | device_unregister(dev); | 94 | * attribute methods, so we have to use this roundabout approach. |
86 | 95 | */ | |
96 | rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); | ||
97 | if (rc) | ||
98 | count = rc; | ||
87 | return count; | 99 | return count; |
88 | } | 100 | } |
89 | 101 | ||
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c275dcac3f18..939de0de18bc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -452,10 +452,22 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha | |||
452 | } | 452 | } |
453 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); | 453 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); |
454 | 454 | ||
455 | static void sdev_store_delete_callback(struct device *dev) | ||
456 | { | ||
457 | scsi_remove_device(to_scsi_device(dev)); | ||
458 | } | ||
459 | |||
455 | static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, | 460 | static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, |
456 | size_t count) | 461 | size_t count) |
457 | { | 462 | { |
458 | scsi_remove_device(to_scsi_device(dev)); | 463 | int rc; |
464 | |||
465 | /* An attribute cannot be unregistered by one of its own methods, | ||
466 | * so we have to use this roundabout approach. | ||
467 | */ | ||
468 | rc = device_schedule_callback(dev, sdev_store_delete_callback); | ||
469 | if (rc) | ||
470 | count = rc; | ||
459 | return count; | 471 | return count; |
460 | }; | 472 | }; |
461 | static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); | 473 | static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 8d4d839a9d88..1bafdf6e171c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -629,6 +629,60 @@ void sysfs_remove_file_from_group(struct kobject *kobj, | |||
629 | } | 629 | } |
630 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); | 630 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); |
631 | 631 | ||
632 | struct sysfs_schedule_callback_struct { | ||
633 | struct kobject *kobj; | ||
634 | void (*func)(void *); | ||
635 | void *data; | ||
636 | struct work_struct work; | ||
637 | }; | ||
638 | |||
639 | static void sysfs_schedule_callback_work(struct work_struct *work) | ||
640 | { | ||
641 | struct sysfs_schedule_callback_struct *ss = container_of(work, | ||
642 | struct sysfs_schedule_callback_struct, work); | ||
643 | |||
644 | (ss->func)(ss->data); | ||
645 | kobject_put(ss->kobj); | ||
646 | kfree(ss); | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * sysfs_schedule_callback - helper to schedule a callback for a kobject | ||
651 | * @kobj: object we're acting for. | ||
652 | * @func: callback function to invoke later. | ||
653 | * @data: argument to pass to @func. | ||
654 | * | ||
655 | * sysfs attribute methods must not unregister themselves or their parent | ||
656 | * kobject (which would amount to the same thing). Attempts to do so will | ||
657 | * deadlock, since unregistration is mutually exclusive with driver | ||
658 | * callbacks. | ||
659 | * | ||
660 | * Instead methods can call this routine, which will attempt to allocate | ||
661 | * and schedule a workqueue request to call back @func with @data as its | ||
662 | * argument in the workqueue's process context. @kobj will be pinned | ||
663 | * until @func returns. | ||
664 | * | ||
665 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
666 | * be allocated. | ||
667 | */ | ||
668 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | ||
669 | void *data) | ||
670 | { | ||
671 | struct sysfs_schedule_callback_struct *ss; | ||
672 | |||
673 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); | ||
674 | if (!ss) | ||
675 | return -ENOMEM; | ||
676 | kobject_get(kobj); | ||
677 | ss->kobj = kobj; | ||
678 | ss->func = func; | ||
679 | ss->data = data; | ||
680 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); | ||
681 | schedule_work(&ss->work); | ||
682 | return 0; | ||
683 | } | ||
684 | EXPORT_SYMBOL_GPL(sysfs_schedule_callback); | ||
685 | |||
632 | 686 | ||
633 | EXPORT_SYMBOL_GPL(sysfs_create_file); | 687 | EXPORT_SYMBOL_GPL(sysfs_create_file); |
634 | EXPORT_SYMBOL_GPL(sysfs_remove_file); | 688 | EXPORT_SYMBOL_GPL(sysfs_remove_file); |
diff --git a/include/linux/device.h b/include/linux/device.h index 39a3199a826d..caad9bba9652 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -353,6 +353,8 @@ extern int __must_check device_create_bin_file(struct device *dev, | |||
353 | struct bin_attribute *attr); | 353 | struct bin_attribute *attr); |
354 | extern void device_remove_bin_file(struct device *dev, | 354 | extern void device_remove_bin_file(struct device *dev, |
355 | struct bin_attribute *attr); | 355 | struct bin_attribute *attr); |
356 | extern int device_schedule_callback(struct device *dev, | ||
357 | void (*func)(struct device *)); | ||
356 | 358 | ||
357 | /* device resource management */ | 359 | /* device resource management */ |
358 | typedef void (*dr_release_t)(struct device *dev, void *res); | 360 | typedef void (*dr_release_t)(struct device *dev, void *res); |
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 523405e1e1f6..0544edda7168 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
@@ -78,6 +78,9 @@ struct sysfs_ops { | |||
78 | 78 | ||
79 | #ifdef CONFIG_SYSFS | 79 | #ifdef CONFIG_SYSFS |
80 | 80 | ||
81 | extern int sysfs_schedule_callback(struct kobject *kobj, | ||
82 | void (*func)(void *), void *data); | ||
83 | |||
81 | extern int __must_check | 84 | extern int __must_check |
82 | sysfs_create_dir(struct kobject *, struct dentry *); | 85 | sysfs_create_dir(struct kobject *, struct dentry *); |
83 | 86 | ||
@@ -132,6 +135,12 @@ extern int __must_check sysfs_init(void); | |||
132 | 135 | ||
133 | #else /* CONFIG_SYSFS */ | 136 | #else /* CONFIG_SYSFS */ |
134 | 137 | ||
138 | static inline int sysfs_schedule_callback(struct kobject *kobj, | ||
139 | void (*func)(void *), void *data) | ||
140 | { | ||
141 | return -ENOSYS; | ||
142 | } | ||
143 | |||
135 | static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) | 144 | static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) |
136 | { | 145 | { |
137 | return 0; | 146 | return 0; |