diff options
| -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, 1 insertions, 144 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 0dd65281cc65..20da3ad1696b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
| @@ -614,39 +614,6 @@ 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 | |||
| 650 | static void klist_children_get(struct klist_node *n) | 617 | static void klist_children_get(struct klist_node *n) |
| 651 | { | 618 | { |
| 652 | struct device_private *p = to_device_private_parent(n); | 619 | struct device_private *p = to_device_private_parent(n); |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1b8b91b67fdb..28cc1acd5439 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
| @@ -453,95 +453,3 @@ 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 233bbbeb768d..d1d1c055b48e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
| @@ -566,12 +566,6 @@ 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) | ||
| 575 | 569 | ||
| 576 | /* device resource management */ | 570 | /* device resource management */ |
| 577 | typedef void (*dr_release_t)(struct device *dev, void *res); | 571 | typedef void (*dr_release_t)(struct device *dev, void *res); |
| @@ -932,10 +926,7 @@ extern int device_online(struct device *dev); | |||
| 932 | extern struct device *__root_device_register(const char *name, | 926 | extern struct device *__root_device_register(const char *name, |
| 933 | struct module *owner); | 927 | struct module *owner); |
| 934 | 928 | ||
| 935 | /* | 929 | /* This is a macro to avoid include problems with THIS_MODULE */ |
| 936 | * This is a macro to avoid include problems with THIS_MODULE, | ||
| 937 | * just as per what is done for device_schedule_callback() above. | ||
| 938 | */ | ||
| 939 | #define root_device_register(name) \ | 930 | #define root_device_register(name) \ |
| 940 | __root_device_register(name, THIS_MODULE) | 931 | __root_device_register(name, THIS_MODULE) |
| 941 | 932 | ||
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 084354b0e814..5ffaa3443712 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
| @@ -179,9 +179,6 @@ struct sysfs_ops { | |||
| 179 | 179 | ||
| 180 | #ifdef CONFIG_SYSFS | 180 | #ifdef CONFIG_SYSFS |
| 181 | 181 | ||
| 182 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | ||
| 183 | void *data, struct module *owner); | ||
| 184 | |||
| 185 | int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); | 182 | int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); |
| 186 | void sysfs_remove_dir(struct kobject *kobj); | 183 | void sysfs_remove_dir(struct kobject *kobj); |
| 187 | int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, | 184 | int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, |
| @@ -255,12 +252,6 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) | |||
| 255 | 252 | ||
| 256 | #else /* CONFIG_SYSFS */ | 253 | #else /* CONFIG_SYSFS */ |
| 257 | 254 | ||
| 258 | static inline int sysfs_schedule_callback(struct kobject *kobj, | ||
| 259 | void (*func)(void *), void *data, struct module *owner) | ||
| 260 | { | ||
| 261 | return -ENOSYS; | ||
| 262 | } | ||
| 263 | |||
| 264 | static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) | 255 | static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) |
| 265 | { | 256 | { |
| 266 | return 0; | 257 | return 0; |
