Skip to content

Commit d9a9cdf

Browse files
AlanSternLinus Torvalds
authored andcommitted
[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>
1 parent 6ab27c6 commit d9a9cdf

File tree

6 files changed

+122
-4
lines changed

6 files changed

+122
-4
lines changed

drivers/base/core.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,35 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
407407
}
408408
EXPORT_SYMBOL_GPL(device_remove_bin_file);
409409

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+
410439
static void klist_children_get(struct klist_node *n)
411440
{
412441
struct device *dev = container_of(n, struct device, knode_parent);

drivers/s390/cio/ccwgroup.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,31 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
7171
* Provide an 'ungroup' attribute so the user can remove group devices no
7272
* longer needed or accidentially created. Saves memory :)
7373
*/
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+
7482
static ssize_t
7583
ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
7684
{
7785
struct ccwgroup_device *gdev;
86+
int rc;
7887

7988
gdev = to_ccwgroupdev(dev);
8089

8190
if (gdev->state != CCWGROUP_OFFLINE)
8291
return -EINVAL;
8392

84-
__ccwgroup_remove_symlinks(gdev);
85-
device_unregister(dev);
86-
93+
/* Note that we cannot unregister the device from one of its
94+
* attribute methods, so we have to use this roundabout approach.
95+
*/
96+
rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
97+
if (rc)
98+
count = rc;
8799
return count;
88100
}
89101

drivers/scsi/scsi_sysfs.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,22 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha
452452
}
453453
static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
454454

455+
static void sdev_store_delete_callback(struct device *dev)
456+
{
457+
scsi_remove_device(to_scsi_device(dev));
458+
}
459+
455460
static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf,
456461
size_t count)
457462
{
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;
459471
return count;
460472
};
461473
static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);

fs/sysfs/file.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,60 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
629629
}
630630
EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
631631

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+
632686

633687
EXPORT_SYMBOL_GPL(sysfs_create_file);
634688
EXPORT_SYMBOL_GPL(sysfs_remove_file);

include/linux/device.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ extern int __must_check device_create_bin_file(struct device *dev,
353353
struct bin_attribute *attr);
354354
extern void device_remove_bin_file(struct device *dev,
355355
struct bin_attribute *attr);
356+
extern int device_schedule_callback(struct device *dev,
357+
void (*func)(struct device *));
356358

357359
/* device resource management */
358360
typedef void (*dr_release_t)(struct device *dev, void *res);

include/linux/sysfs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ struct sysfs_ops {
7878

7979
#ifdef CONFIG_SYSFS
8080

81+
extern int sysfs_schedule_callback(struct kobject *kobj,
82+
void (*func)(void *), void *data);
83+
8184
extern int __must_check
8285
sysfs_create_dir(struct kobject *, struct dentry *);
8386

@@ -132,6 +135,12 @@ extern int __must_check sysfs_init(void);
132135

133136
#else /* CONFIG_SYSFS */
134137

138+
static inline int sysfs_schedule_callback(struct kobject *kobj,
139+
void (*func)(void *), void *data)
140+
{
141+
return -ENOSYS;
142+
}
143+
135144
static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow)
136145
{
137146
return 0;

0 commit comments

Comments
 (0)