diff options
Diffstat (limited to 'drivers')
-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 |
3 files changed, 57 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); |