diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-03-25 23:54:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-03-25 23:54:57 -0400 |
commit | 72099304eeb316c4b00df3ae83efe4375729bd78 (patch) | |
tree | 3d4a7dbe61ad9bbaeb767b1fb43e190901bcb70b | |
parent | b7ce40cff0b9f6597f8318fd761accd92727f61f (diff) |
Revert "sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner()"
This reverts commit d1ba277e79889085a2faec3b68b91ce89c63f888.
As reported by Stephen, this patch breaks linux-next as a ppc patch
suddenly (after 2 years) started using this old api call. So revert it
for now, it will go away in 3.15-rc2 when we can change the PPC call to
the new api.
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Tejun Heo <tj@kernel.org>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/base/core.c | 33 | ||||
-rw-r--r-- | fs/sysfs/file.c | 92 | ||||
-rw-r--r-- | include/linux/device.h | 11 | ||||
-rw-r--r-- | include/linux/sysfs.h | 9 |
4 files changed, 144 insertions, 1 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 20da3ad1696b..0dd65281cc65 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -614,6 +614,39 @@ void device_remove_bin_file(struct device *dev, | |||
614 | } | 614 | } |
615 | EXPORT_SYMBOL_GPL(device_remove_bin_file); | 615 | EXPORT_SYMBOL_GPL(device_remove_bin_file); |
616 | 616 | ||
617 | /** | ||
618 | * device_schedule_callback_owner - helper to schedule a callback for a device | ||
619 | * @dev: device. | ||
620 | * @func: callback function to invoke later. | ||
621 | * @owner: module owning the callback routine | ||
622 | * | ||
623 | * Attribute methods must not unregister themselves or their parent device | ||
624 | * (which would amount to the same thing). Attempts to do so will deadlock, | ||
625 | * since unregistration is mutually exclusive with driver callbacks. | ||
626 | * | ||
627 | * Instead methods can call this routine, which will attempt to allocate | ||
628 | * and schedule a workqueue request to call back @func with @dev as its | ||
629 | * argument in the workqueue's process context. @dev will be pinned until | ||
630 | * @func returns. | ||
631 | * | ||
632 | * This routine is usually called via the inline device_schedule_callback(), | ||
633 | * which automatically sets @owner to THIS_MODULE. | ||
634 | * | ||
635 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
636 | * be allocated, -ENODEV if a reference to @owner isn't available. | ||
637 | * | ||
638 | * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an | ||
639 | * underlying sysfs routine (since it is intended for use by attribute | ||
640 | * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. | ||
641 | */ | ||
642 | int device_schedule_callback_owner(struct device *dev, | ||
643 | void (*func)(struct device *), struct module *owner) | ||
644 | { | ||
645 | return sysfs_schedule_callback(&dev->kobj, | ||
646 | (void (*)(void *)) func, dev, owner); | ||
647 | } | ||
648 | EXPORT_SYMBOL_GPL(device_schedule_callback_owner); | ||
649 | |||
617 | static void klist_children_get(struct klist_node *n) | 650 | static void klist_children_get(struct klist_node *n) |
618 | { | 651 | { |
619 | struct device_private *p = to_device_private_parent(n); | 652 | struct device_private *p = to_device_private_parent(n); |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 28cc1acd5439..1b8b91b67fdb 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -453,3 +453,95 @@ void sysfs_remove_bin_file(struct kobject *kobj, | |||
453 | kernfs_remove_by_name(kobj->sd, attr->attr.name); | 453 | kernfs_remove_by_name(kobj->sd, attr->attr.name); |
454 | } | 454 | } |
455 | EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); | 455 | EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |
456 | |||
457 | struct sysfs_schedule_callback_struct { | ||
458 | struct list_head workq_list; | ||
459 | struct kobject *kobj; | ||
460 | void (*func)(void *); | ||
461 | void *data; | ||
462 | struct module *owner; | ||
463 | struct work_struct work; | ||
464 | }; | ||
465 | |||
466 | static struct workqueue_struct *sysfs_workqueue; | ||
467 | static DEFINE_MUTEX(sysfs_workq_mutex); | ||
468 | static LIST_HEAD(sysfs_workq); | ||
469 | static void sysfs_schedule_callback_work(struct work_struct *work) | ||
470 | { | ||
471 | struct sysfs_schedule_callback_struct *ss = container_of(work, | ||
472 | struct sysfs_schedule_callback_struct, work); | ||
473 | |||
474 | (ss->func)(ss->data); | ||
475 | kobject_put(ss->kobj); | ||
476 | module_put(ss->owner); | ||
477 | mutex_lock(&sysfs_workq_mutex); | ||
478 | list_del(&ss->workq_list); | ||
479 | mutex_unlock(&sysfs_workq_mutex); | ||
480 | kfree(ss); | ||
481 | } | ||
482 | |||
483 | /** | ||
484 | * sysfs_schedule_callback - helper to schedule a callback for a kobject | ||
485 | * @kobj: object we're acting for. | ||
486 | * @func: callback function to invoke later. | ||
487 | * @data: argument to pass to @func. | ||
488 | * @owner: module owning the callback code | ||
489 | * | ||
490 | * sysfs attribute methods must not unregister themselves or their parent | ||
491 | * kobject (which would amount to the same thing). Attempts to do so will | ||
492 | * deadlock, since unregistration is mutually exclusive with driver | ||
493 | * callbacks. | ||
494 | * | ||
495 | * Instead methods can call this routine, which will attempt to allocate | ||
496 | * and schedule a workqueue request to call back @func with @data as its | ||
497 | * argument in the workqueue's process context. @kobj will be pinned | ||
498 | * until @func returns. | ||
499 | * | ||
500 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
501 | * be allocated, -ENODEV if a reference to @owner isn't available, | ||
502 | * -EAGAIN if a callback has already been scheduled for @kobj. | ||
503 | */ | ||
504 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | ||
505 | void *data, struct module *owner) | ||
506 | { | ||
507 | struct sysfs_schedule_callback_struct *ss, *tmp; | ||
508 | |||
509 | if (!try_module_get(owner)) | ||
510 | return -ENODEV; | ||
511 | |||
512 | mutex_lock(&sysfs_workq_mutex); | ||
513 | list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) | ||
514 | if (ss->kobj == kobj) { | ||
515 | module_put(owner); | ||
516 | mutex_unlock(&sysfs_workq_mutex); | ||
517 | return -EAGAIN; | ||
518 | } | ||
519 | mutex_unlock(&sysfs_workq_mutex); | ||
520 | |||
521 | if (sysfs_workqueue == NULL) { | ||
522 | sysfs_workqueue = create_singlethread_workqueue("sysfsd"); | ||
523 | if (sysfs_workqueue == NULL) { | ||
524 | module_put(owner); | ||
525 | return -ENOMEM; | ||
526 | } | ||
527 | } | ||
528 | |||
529 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); | ||
530 | if (!ss) { | ||
531 | module_put(owner); | ||
532 | return -ENOMEM; | ||
533 | } | ||
534 | kobject_get(kobj); | ||
535 | ss->kobj = kobj; | ||
536 | ss->func = func; | ||
537 | ss->data = data; | ||
538 | ss->owner = owner; | ||
539 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); | ||
540 | INIT_LIST_HEAD(&ss->workq_list); | ||
541 | mutex_lock(&sysfs_workq_mutex); | ||
542 | list_add_tail(&ss->workq_list, &sysfs_workq); | ||
543 | mutex_unlock(&sysfs_workq_mutex); | ||
544 | queue_work(sysfs_workqueue, &ss->work); | ||
545 | return 0; | ||
546 | } | ||
547 | EXPORT_SYMBOL_GPL(sysfs_schedule_callback); | ||
diff --git a/include/linux/device.h b/include/linux/device.h index fb1ba13f7665..1ff3f1697513 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -566,6 +566,12 @@ extern int __must_check device_create_bin_file(struct device *dev, | |||
566 | const struct bin_attribute *attr); | 566 | const struct bin_attribute *attr); |
567 | extern void device_remove_bin_file(struct device *dev, | 567 | extern void device_remove_bin_file(struct device *dev, |
568 | const struct bin_attribute *attr); | 568 | const struct bin_attribute *attr); |
569 | extern int device_schedule_callback_owner(struct device *dev, | ||
570 | void (*func)(struct device *dev), struct module *owner); | ||
571 | |||
572 | /* This is a macro to avoid include problems with THIS_MODULE */ | ||
573 | #define device_schedule_callback(dev, func) \ | ||
574 | device_schedule_callback_owner(dev, func, THIS_MODULE) | ||
569 | 575 | ||
570 | /* device resource management */ | 576 | /* device resource management */ |
571 | typedef void (*dr_release_t)(struct device *dev, void *res); | 577 | typedef void (*dr_release_t)(struct device *dev, void *res); |
@@ -925,7 +931,10 @@ extern int device_online(struct device *dev); | |||
925 | extern struct device *__root_device_register(const char *name, | 931 | extern struct device *__root_device_register(const char *name, |
926 | struct module *owner); | 932 | struct module *owner); |
927 | 933 | ||
928 | /* This is a macro to avoid include problems with THIS_MODULE */ | 934 | /* |
935 | * This is a macro to avoid include problems with THIS_MODULE, | ||
936 | * just as per what is done for device_schedule_callback() above. | ||
937 | */ | ||
929 | #define root_device_register(name) \ | 938 | #define root_device_register(name) \ |
930 | __root_device_register(name, THIS_MODULE) | 939 | __root_device_register(name, THIS_MODULE) |
931 | 940 | ||
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fdaa0c6fc7a2..e0bf210ddffd 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
@@ -178,6 +178,9 @@ struct sysfs_ops { | |||
178 | 178 | ||
179 | #ifdef CONFIG_SYSFS | 179 | #ifdef CONFIG_SYSFS |
180 | 180 | ||
181 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | ||
182 | void *data, struct module *owner); | ||
183 | |||
181 | int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); | 184 | int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); |
182 | void sysfs_remove_dir(struct kobject *kobj); | 185 | void sysfs_remove_dir(struct kobject *kobj); |
183 | int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, | 186 | int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, |
@@ -251,6 +254,12 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) | |||
251 | 254 | ||
252 | #else /* CONFIG_SYSFS */ | 255 | #else /* CONFIG_SYSFS */ |
253 | 256 | ||
257 | static inline int sysfs_schedule_callback(struct kobject *kobj, | ||
258 | void (*func)(void *), void *data, struct module *owner) | ||
259 | { | ||
260 | return -ENOSYS; | ||
261 | } | ||
262 | |||
254 | static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) | 263 | static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) |
255 | { | 264 | { |
256 | return 0; | 265 | return 0; |