aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-03-15 15:50:34 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-03-15 18:29:26 -0400
commitd9a9cdfb078d755e648d53ec25b7370f84ee5729 (patch)
tree308380483fd6241b1d0ef5916b9329c1c5df00f6
parent6ab27c6bf38d5ff71dafeca77b79e7c284804b75 (diff)
[PATCH] sysfs and driver core: add callback helper, used by SCSI and S390
This patch (as868) adds a helper routine for device drivers that need to set up a callback to perform some action in a different process's context. This is intended for use by attribute methods that want to unregister themselves or their parent device. Attribute method calls are mutually exclusive with unregistration, so such actions cannot be taken directly. Two attribute methods are converted to use the new helper routine: one for SCSI device deletion and one for System/390 ccwgroup devices. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: Hugh Dickins <hugh@veritas.com> Cc: Cornelia Huck <cornelia.huck@de.ibm.com> Cc: Oliver Neukum <oneukum@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/base/core.c29
-rw-r--r--drivers/s390/cio/ccwgroup.c18
-rw-r--r--drivers/scsi/scsi_sysfs.c14
-rw-r--r--fs/sysfs/file.c54
-rw-r--r--include/linux/device.h2
-rw-r--r--include/linux/sysfs.h9
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}
408EXPORT_SYMBOL_GPL(device_remove_bin_file); 408EXPORT_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 */
431int 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}
437EXPORT_SYMBOL_GPL(device_schedule_callback);
438
410static void klist_children_get(struct klist_node *n) 439static 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 */
74static 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
74static ssize_t 82static ssize_t
75ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 83ccwgroup_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}
453static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); 453static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
454 454
455static void sdev_store_delete_callback(struct device *dev)
456{
457 scsi_remove_device(to_scsi_device(dev));
458}
459
455static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, 460static 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};
461static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); 473static 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}
630EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); 630EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
631 631
632struct sysfs_schedule_callback_struct {
633 struct kobject *kobj;
634 void (*func)(void *);
635 void *data;
636 struct work_struct work;
637};
638
639static 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 */
668int 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}
684EXPORT_SYMBOL_GPL(sysfs_schedule_callback);
685
632 686
633EXPORT_SYMBOL_GPL(sysfs_create_file); 687EXPORT_SYMBOL_GPL(sysfs_create_file);
634EXPORT_SYMBOL_GPL(sysfs_remove_file); 688EXPORT_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);
354extern void device_remove_bin_file(struct device *dev, 354extern void device_remove_bin_file(struct device *dev,
355 struct bin_attribute *attr); 355 struct bin_attribute *attr);
356extern int device_schedule_callback(struct device *dev,
357 void (*func)(struct device *));
356 358
357/* device resource management */ 359/* device resource management */
358typedef void (*dr_release_t)(struct device *dev, void *res); 360typedef 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
81extern int sysfs_schedule_callback(struct kobject *kobj,
82 void (*func)(void *), void *data);
83
81extern int __must_check 84extern int __must_check
82sysfs_create_dir(struct kobject *, struct dentry *); 85sysfs_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
138static inline int sysfs_schedule_callback(struct kobject *kobj,
139 void (*func)(void *), void *data)
140{
141 return -ENOSYS;
142}
143
135static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) 144static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow)
136{ 145{
137 return 0; 146 return 0;