From bbc780f8bab52fef1784151d3c4982cb1143edd2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 21 Nov 2013 20:15:48 +0100 Subject: driver core: fix device_create() error path We call put_device() in the error path, which is fine for dev==NULL. However, in case kobject_set_name_vargs() fails, we have dev!=NULL but device_initialized() wasn't called, yet. Fix this by splitting device_register() into explicit calls to device_add() and an early call to device_initialize(). Signed-off-by: David Herrmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 67b180d855b..aab43fbb833 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1603,6 +1603,7 @@ device_create_groups_vargs(struct class *class, struct device *parent, goto error; } + device_initialize(dev); dev->devt = devt; dev->class = class; dev->parent = parent; @@ -1614,7 +1615,7 @@ device_create_groups_vargs(struct class *class, struct device *parent, if (retval) goto error; - retval = device_register(dev); + retval = device_add(dev); if (retval) goto error; -- cgit v1.2.3-70-g09d2 From ecfbf6fd9c03be7dfe3eafc3846641b9d463607b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Dec 2013 06:11:02 +0100 Subject: Driver core: Fix device_add_attrs() error code path If the addition of dev_attr_online fails, device_add_attrs() should remove device attribute groups as well as type and class attribute groups before returning an error code. Make that happen. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index aab43fbb833..2b567177ef7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -491,11 +491,13 @@ static int device_add_attrs(struct device *dev) if (device_supports_offline(dev) && !dev->offline_disabled) { error = device_create_file(dev, &dev_attr_online); if (error) - goto err_remove_type_groups; + goto err_remove_dev_groups; } return 0; + err_remove_dev_groups: + device_remove_groups(dev, dev->groups); err_remove_type_groups: if (type) device_remove_groups(dev, type->groups); -- cgit v1.2.3-70-g09d2 From 1ae06819c77cff1ea2833c94f8c093fe8a5c79db Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:27 -0500 Subject: kernfs, sysfs, driver-core: implement kernfs_remove_self() and its wrappers Sometimes it's necessary to implement a node which wants to delete nodes including itself. This isn't straightforward because of kernfs active reference. While a file operation is in progress, an active reference is held and kernfs_remove() waits for all such references to drain before completing. For a self-deleting node, this is a deadlock as kernfs_remove() ends up waiting for an active reference that itself is sitting on top of. This currently is worked around in the sysfs layer using sysfs_schedule_callback() which makes such removals asynchronous. While it works, it's rather cumbersome and inherently breaks synchronicity of the operation - the file operation which triggered the operation may complete before the removal is finished (or even started) and the removal may fail asynchronously. If a removal operation is immmediately followed by another operation which expects the specific name to be available (e.g. removal followed by rename onto the same name), there's no way to make the latter operation reliable. The thing is there's no inherent reason for this to be asynchrnous. All that's necessary to do this synchronous is a dedicated operation which drops its own active ref and deactivates self. This patch implements kernfs_remove_self() and its wrappers in sysfs and driver core. kernfs_remove_self() is to be called from one of the file operations, drops the active ref and deactivates using __kernfs_deactivate_self(), removes the self node, and restores active ref to the dead node using __kernfs_reactivate_self() so that the ref is balanced afterwards. __kernfs_remove() is updated so that it takes an early exit if the target node is already fully removed so that the active ref restored by kernfs_remove_self() after removal doesn't confuse the deactivation path. This makes implementing self-deleting nodes very easy. The normal removal path doesn't even need to be changed to use kernfs_remove_self() for the self-deleting node. The method can invoke kernfs_remove_self() on itself before proceeding the normal removal path. kernfs_remove() invoked on the node by the normal deletion path will simply be ignored. This will replace sysfs_schedule_callback(). A subtle feature of sysfs_schedule_callback() is that it collapses multiple invocations - even if multiple removals are triggered, the removal callback is run only once. An equivalent effect can be achieved by testing the return value of kernfs_remove_self() - only the one which gets %true return value should proceed with actual deletion. All other instances of kernfs_remove_self() will wait till the enclosing kernfs operation which invoked the winning instance of kernfs_remove_self() finishes and then return %false. This trivially makes all users of kernfs_remove_self() automatically show correct synchronous behavior even when there are multiple concurrent operations - all "echo 1 > delete" instances will finish only after the whole operation is completed by one of the instances. v2: For !CONFIG_SYSFS, dummy version kernfs_remove_self() was missing and sysfs_remove_file_self() had incorrect return type. Fix it. Reported by kbuild test bot. v3: Updated to use __kernfs_{de|re}activate_self(). Signed-off-by: Tejun Heo Cc: Alan Stern Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ++++++++++++ fs/kernfs/dir.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/file.c | 23 ++++++++++++++++ include/linux/device.h | 2 ++ include/linux/kernfs.h | 6 +++++ include/linux/sysfs.h | 7 +++++ 6 files changed, 127 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 2b567177ef7..9db57afcf81 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -570,6 +570,23 @@ void device_remove_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_file); +/** + * device_remove_file_self - remove sysfs attribute file from its own method. + * @dev: device. + * @attr: device attribute descriptor. + * + * See kernfs_remove_self() for details. + */ +bool device_remove_file_self(struct device *dev, + const struct device_attribute *attr) +{ + if (dev) + return sysfs_remove_file_self(&dev->kobj, &attr->attr); + else + return false; +} +EXPORT_SYMBOL_GPL(device_remove_file_self); + /** * device_create_bin_file - create sysfs binary attribute file for device. * @dev: device. diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1aeb57969bf..a8028be6cdb 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -985,6 +985,78 @@ void kernfs_remove(struct kernfs_node *kn) mutex_unlock(&kernfs_mutex); } +/** + * kernfs_remove_self - remove a kernfs_node from its own method + * @kn: the self kernfs_node to remove + * + * The caller must be running off of a kernfs operation which is invoked + * with an active reference - e.g. one of kernfs_ops. This can be used to + * implement a file operation which deletes itself. + * + * For example, the "delete" file for a sysfs device directory can be + * implemented by invoking kernfs_remove_self() on the "delete" file + * itself. This function breaks the circular dependency of trying to + * deactivate self while holding an active ref itself. It isn't necessary + * to modify the usual removal path to use kernfs_remove_self(). The + * "delete" implementation can simply invoke kernfs_remove_self() on self + * before proceeding with the usual removal path. kernfs will ignore later + * kernfs_remove() on self. + * + * kernfs_remove_self() can be called multiple times concurrently on the + * same kernfs_node. Only the first one actually performs removal and + * returns %true. All others will wait until the kernfs operation which + * won self-removal finishes and return %false. Note that the losers wait + * for the completion of not only the winning kernfs_remove_self() but also + * the whole kernfs_ops which won the arbitration. This can be used to + * guarantee, for example, all concurrent writes to a "delete" file to + * finish only after the whole operation is complete. + */ +bool kernfs_remove_self(struct kernfs_node *kn) +{ + bool ret; + + mutex_lock(&kernfs_mutex); + __kernfs_deactivate_self(kn); + + /* + * SUICIDAL is used to arbitrate among competing invocations. Only + * the first one will actually perform removal. When the removal + * is complete, SUICIDED is set and the active ref is restored + * while holding kernfs_mutex. The ones which lost arbitration + * waits for SUICDED && drained which can happen only after the + * enclosing kernfs operation which executed the winning instance + * of kernfs_remove_self() finished. + */ + if (!(kn->flags & KERNFS_SUICIDAL)) { + kn->flags |= KERNFS_SUICIDAL; + __kernfs_remove(kn); + kn->flags |= KERNFS_SUICIDED; + ret = true; + } else { + wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq; + DEFINE_WAIT(wait); + + while (true) { + prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE); + + if ((kn->flags & KERNFS_SUICIDED) && + atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) + break; + + mutex_unlock(&kernfs_mutex); + schedule(); + mutex_lock(&kernfs_mutex); + } + finish_wait(waitq, &wait); + WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); + ret = false; + } + + __kernfs_reactivate_self(kn); + mutex_unlock(&kernfs_mutex); + return ret; +} + /** * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it * @parent: parent of the target diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 810cf6e613e..1b8b91b67fd 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); +/** + * sysfs_remove_file_self - remove an object attribute from its own method + * @kobj: object we're acting for + * @attr: attribute descriptor + * + * See kernfs_remove_self() for details. + */ +bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) +{ + struct kernfs_node *parent = kobj->sd; + struct kernfs_node *kn; + bool ret; + + kn = kernfs_find_and_get(parent, attr->name); + if (WARN_ON_ONCE(!kn)) + return false; + + ret = kernfs_remove_self(kn); + + kernfs_put(kn); + return ret; +} + void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 952b01033c3..1ff3f169751 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -560,6 +560,8 @@ extern int device_create_file(struct device *device, const struct device_attribute *entry); extern void device_remove_file(struct device *dev, const struct device_attribute *attr); +extern bool device_remove_file_self(struct device *dev, + const struct device_attribute *attr); extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index ac869302705..0b7b7cc352e 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -43,6 +43,8 @@ enum kernfs_node_flag { KERNFS_HAS_MMAP = 0x0080, KERNFS_LOCKDEP = 0x0100, KERNFS_STATIC_NAME = 0x0200, + KERNFS_SUICIDAL = 0x0400, + KERNFS_SUICIDED = 0x0800, }; /* type-specific structures for kernfs_node union members */ @@ -239,6 +241,7 @@ void kernfs_reactivate(struct kernfs_node *kn); void kernfs_deactivate_self(struct kernfs_node *kn); void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); +bool kernfs_remove_self(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, @@ -296,6 +299,9 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, static inline void kernfs_remove(struct kernfs_node *kn) { } +static inline bool kernfs_remove_self(struct kernfs_node *kn) +{ return false; } + static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, const char *name, const void *ns) { return -ENOSYS; } diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 30b2ebee643..bd96c603ab6 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns); +bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); int __must_check sysfs_create_bin_file(struct kobject *kobj, @@ -301,6 +302,12 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj, { } +static inline bool sysfs_remove_file_self(struct kobject *kobj, + const struct attribute *attr) +{ + return false; +} + static inline void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr) { -- cgit v1.2.3-70-g09d2 From d1ba277e79889085a2faec3b68b91ce89c63f888 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:31 -0500 Subject: sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner() All device_schedule_callback_owner() users are converted to use device_remove_file_self(). Remove now unused {sysfs|device}_schedule_callback_owner(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 33 ------------------ fs/sysfs/file.c | 92 -------------------------------------------------- include/linux/device.h | 11 +----- include/linux/sysfs.h | 9 ----- 4 files changed, 1 insertion(+), 144 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 9db57afcf81..4195364f9fd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -615,39 +615,6 @@ void device_remove_bin_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_bin_file); -/** - * device_schedule_callback_owner - helper to schedule a callback for a device - * @dev: device. - * @func: callback function to invoke later. - * @owner: module owning the callback routine - * - * Attribute methods must not unregister themselves or their parent device - * (which would amount to the same thing). Attempts to do so will deadlock, - * since unregistration is mutually exclusive with driver callbacks. - * - * Instead methods can call this routine, which will attempt to allocate - * and schedule a workqueue request to call back @func with @dev as its - * argument in the workqueue's process context. @dev will be pinned until - * @func returns. - * - * This routine is usually called via the inline device_schedule_callback(), - * which automatically sets @owner to THIS_MODULE. - * - * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated, -ENODEV if a reference to @owner isn't available. - * - * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an - * underlying sysfs routine (since it is intended for use by attribute - * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. - */ -int device_schedule_callback_owner(struct device *dev, - void (*func)(struct device *), struct module *owner) -{ - return sysfs_schedule_callback(&dev->kobj, - (void (*)(void *)) func, dev, owner); -} -EXPORT_SYMBOL_GPL(device_schedule_callback_owner); - static void klist_children_get(struct klist_node *n) { struct device_private *p = to_device_private_parent(n); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1b8b91b67fd..28cc1acd543 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -453,95 +453,3 @@ void sysfs_remove_bin_file(struct kobject *kobj, kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); - -struct sysfs_schedule_callback_struct { - struct list_head workq_list; - struct kobject *kobj; - void (*func)(void *); - void *data; - struct module *owner; - struct work_struct work; -}; - -static struct workqueue_struct *sysfs_workqueue; -static DEFINE_MUTEX(sysfs_workq_mutex); -static LIST_HEAD(sysfs_workq); -static void sysfs_schedule_callback_work(struct work_struct *work) -{ - struct sysfs_schedule_callback_struct *ss = container_of(work, - struct sysfs_schedule_callback_struct, work); - - (ss->func)(ss->data); - kobject_put(ss->kobj); - module_put(ss->owner); - mutex_lock(&sysfs_workq_mutex); - list_del(&ss->workq_list); - mutex_unlock(&sysfs_workq_mutex); - kfree(ss); -} - -/** - * sysfs_schedule_callback - helper to schedule a callback for a kobject - * @kobj: object we're acting for. - * @func: callback function to invoke later. - * @data: argument to pass to @func. - * @owner: module owning the callback code - * - * sysfs attribute methods must not unregister themselves or their parent - * kobject (which would amount to the same thing). Attempts to do so will - * deadlock, since unregistration is mutually exclusive with driver - * callbacks. - * - * Instead methods can call this routine, which will attempt to allocate - * and schedule a workqueue request to call back @func with @data as its - * argument in the workqueue's process context. @kobj will be pinned - * until @func returns. - * - * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated, -ENODEV if a reference to @owner isn't available, - * -EAGAIN if a callback has already been scheduled for @kobj. - */ -int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data, struct module *owner) -{ - struct sysfs_schedule_callback_struct *ss, *tmp; - - if (!try_module_get(owner)) - return -ENODEV; - - mutex_lock(&sysfs_workq_mutex); - list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) - if (ss->kobj == kobj) { - module_put(owner); - mutex_unlock(&sysfs_workq_mutex); - return -EAGAIN; - } - mutex_unlock(&sysfs_workq_mutex); - - if (sysfs_workqueue == NULL) { - sysfs_workqueue = create_singlethread_workqueue("sysfsd"); - if (sysfs_workqueue == NULL) { - module_put(owner); - return -ENOMEM; - } - } - - ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (!ss) { - module_put(owner); - return -ENOMEM; - } - kobject_get(kobj); - ss->kobj = kobj; - ss->func = func; - ss->data = data; - ss->owner = owner; - INIT_WORK(&ss->work, sysfs_schedule_callback_work); - INIT_LIST_HEAD(&ss->workq_list); - mutex_lock(&sysfs_workq_mutex); - list_add_tail(&ss->workq_list, &sysfs_workq); - mutex_unlock(&sysfs_workq_mutex); - queue_work(sysfs_workqueue, &ss->work); - return 0; -} -EXPORT_SYMBOL_GPL(sysfs_schedule_callback); diff --git a/include/linux/device.h b/include/linux/device.h index 1ff3f169751..fb1ba13f766 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, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, const struct bin_attribute *attr); -extern int device_schedule_callback_owner(struct device *dev, - void (*func)(struct device *dev), struct module *owner); - -/* This is a macro to avoid include problems with THIS_MODULE */ -#define device_schedule_callback(dev, func) \ - device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); @@ -931,10 +925,7 @@ extern int device_online(struct device *dev); extern struct device *__root_device_register(const char *name, struct module *owner); -/* - * This is a macro to avoid include problems with THIS_MODULE, - * just as per what is done for device_schedule_callback() above. - */ +/* This is a macro to avoid include problems with THIS_MODULE */ #define root_device_register(name) \ __root_device_register(name, THIS_MODULE) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index bd96c603ab6..14df05415af 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -178,9 +178,6 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS -int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data, struct module *owner); - int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, @@ -249,12 +246,6 @@ int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ -static inline int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data, struct module *owner) -{ - return -ENOSYS; -} - static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { return 0; -- cgit v1.2.3-70-g09d2 From a30f82b7ebc87cdec3ef48303278f02970086118 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 13:51:36 -0800 Subject: Revert "sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner()" This reverts commit d1ba277e79889085a2faec3b68b91ce89c63f888. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 33 ++++++++++++++++++ fs/sysfs/file.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 11 +++++- include/linux/sysfs.h | 9 +++++ 4 files changed, 144 insertions(+), 1 deletion(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4195364f9fd..9db57afcf81 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -615,6 +615,39 @@ void device_remove_bin_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_bin_file); +/** + * device_schedule_callback_owner - helper to schedule a callback for a device + * @dev: device. + * @func: callback function to invoke later. + * @owner: module owning the callback routine + * + * Attribute methods must not unregister themselves or their parent device + * (which would amount to the same thing). Attempts to do so will deadlock, + * since unregistration is mutually exclusive with driver callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @dev as its + * argument in the workqueue's process context. @dev will be pinned until + * @func returns. + * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated, -ENODEV if a reference to @owner isn't available. + * + * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an + * underlying sysfs routine (since it is intended for use by attribute + * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. + */ +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) +{ + return sysfs_schedule_callback(&dev->kobj, + (void (*)(void *)) func, dev, owner); +} +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); + static void klist_children_get(struct klist_node *n) { struct device_private *p = to_device_private_parent(n); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 28cc1acd543..1b8b91b67fd 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -453,3 +453,95 @@ void sysfs_remove_bin_file(struct kobject *kobj, kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); + +struct sysfs_schedule_callback_struct { + struct list_head workq_list; + struct kobject *kobj; + void (*func)(void *); + void *data; + struct module *owner; + struct work_struct work; +}; + +static struct workqueue_struct *sysfs_workqueue; +static DEFINE_MUTEX(sysfs_workq_mutex); +static LIST_HEAD(sysfs_workq); +static void sysfs_schedule_callback_work(struct work_struct *work) +{ + struct sysfs_schedule_callback_struct *ss = container_of(work, + struct sysfs_schedule_callback_struct, work); + + (ss->func)(ss->data); + kobject_put(ss->kobj); + module_put(ss->owner); + mutex_lock(&sysfs_workq_mutex); + list_del(&ss->workq_list); + mutex_unlock(&sysfs_workq_mutex); + kfree(ss); +} + +/** + * sysfs_schedule_callback - helper to schedule a callback for a kobject + * @kobj: object we're acting for. + * @func: callback function to invoke later. + * @data: argument to pass to @func. + * @owner: module owning the callback code + * + * sysfs attribute methods must not unregister themselves or their parent + * kobject (which would amount to the same thing). Attempts to do so will + * deadlock, since unregistration is mutually exclusive with driver + * callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @data as its + * argument in the workqueue's process context. @kobj will be pinned + * until @func returns. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated, -ENODEV if a reference to @owner isn't available, + * -EAGAIN if a callback has already been scheduled for @kobj. + */ +int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), + void *data, struct module *owner) +{ + struct sysfs_schedule_callback_struct *ss, *tmp; + + if (!try_module_get(owner)) + return -ENODEV; + + mutex_lock(&sysfs_workq_mutex); + list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) + if (ss->kobj == kobj) { + module_put(owner); + mutex_unlock(&sysfs_workq_mutex); + return -EAGAIN; + } + mutex_unlock(&sysfs_workq_mutex); + + if (sysfs_workqueue == NULL) { + sysfs_workqueue = create_singlethread_workqueue("sysfsd"); + if (sysfs_workqueue == NULL) { + module_put(owner); + return -ENOMEM; + } + } + + ss = kmalloc(sizeof(*ss), GFP_KERNEL); + if (!ss) { + module_put(owner); + return -ENOMEM; + } + kobject_get(kobj); + ss->kobj = kobj; + ss->func = func; + ss->data = data; + ss->owner = owner; + INIT_WORK(&ss->work, sysfs_schedule_callback_work); + INIT_LIST_HEAD(&ss->workq_list); + mutex_lock(&sysfs_workq_mutex); + list_add_tail(&ss->workq_list, &sysfs_workq); + mutex_unlock(&sysfs_workq_mutex); + queue_work(sysfs_workqueue, &ss->work); + return 0; +} +EXPORT_SYMBOL_GPL(sysfs_schedule_callback); diff --git a/include/linux/device.h b/include/linux/device.h index fb1ba13f766..1ff3f169751 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, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, const struct bin_attribute *attr); +extern int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *dev), struct module *owner); + +/* This is a macro to avoid include problems with THIS_MODULE */ +#define device_schedule_callback(dev, func) \ + device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); @@ -925,7 +931,10 @@ extern int device_online(struct device *dev); extern struct device *__root_device_register(const char *name, struct module *owner); -/* This is a macro to avoid include problems with THIS_MODULE */ +/* + * This is a macro to avoid include problems with THIS_MODULE, + * just as per what is done for device_schedule_callback() above. + */ #define root_device_register(name) \ __root_device_register(name, THIS_MODULE) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 14df05415af..bd96c603ab6 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -178,6 +178,9 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS +int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), + void *data, struct module *owner); + int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, @@ -246,6 +249,12 @@ int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ +static inline int sysfs_schedule_callback(struct kobject *kobj, + void (*func)(void *), void *data, struct module *owner) +{ + return -ENOSYS; +} + static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { return 0; -- cgit v1.2.3-70-g09d2 From a9f138b0e537de55933335d580ebd38c2bc53c47 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:05:13 -0800 Subject: Revert "kernfs, sysfs, driver-core: implement kernfs_remove_self() and its wrappers" This reverts commit 1ae06819c77cff1ea2833c94f8c093fe8a5c79db. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Cc: Alan Stern Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ------------ fs/kernfs/dir.c | 72 -------------------------------------------------- fs/sysfs/file.c | 23 ---------------- include/linux/device.h | 2 -- include/linux/kernfs.h | 6 ----- include/linux/sysfs.h | 7 ----- 6 files changed, 127 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 9db57afcf81..2b567177ef7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -570,23 +570,6 @@ void device_remove_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_file); -/** - * device_remove_file_self - remove sysfs attribute file from its own method. - * @dev: device. - * @attr: device attribute descriptor. - * - * See kernfs_remove_self() for details. - */ -bool device_remove_file_self(struct device *dev, - const struct device_attribute *attr) -{ - if (dev) - return sysfs_remove_file_self(&dev->kobj, &attr->attr); - else - return false; -} -EXPORT_SYMBOL_GPL(device_remove_file_self); - /** * device_create_bin_file - create sysfs binary attribute file for device. * @dev: device. diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a8028be6cdb..1aeb57969bf 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -985,78 +985,6 @@ void kernfs_remove(struct kernfs_node *kn) mutex_unlock(&kernfs_mutex); } -/** - * kernfs_remove_self - remove a kernfs_node from its own method - * @kn: the self kernfs_node to remove - * - * The caller must be running off of a kernfs operation which is invoked - * with an active reference - e.g. one of kernfs_ops. This can be used to - * implement a file operation which deletes itself. - * - * For example, the "delete" file for a sysfs device directory can be - * implemented by invoking kernfs_remove_self() on the "delete" file - * itself. This function breaks the circular dependency of trying to - * deactivate self while holding an active ref itself. It isn't necessary - * to modify the usual removal path to use kernfs_remove_self(). The - * "delete" implementation can simply invoke kernfs_remove_self() on self - * before proceeding with the usual removal path. kernfs will ignore later - * kernfs_remove() on self. - * - * kernfs_remove_self() can be called multiple times concurrently on the - * same kernfs_node. Only the first one actually performs removal and - * returns %true. All others will wait until the kernfs operation which - * won self-removal finishes and return %false. Note that the losers wait - * for the completion of not only the winning kernfs_remove_self() but also - * the whole kernfs_ops which won the arbitration. This can be used to - * guarantee, for example, all concurrent writes to a "delete" file to - * finish only after the whole operation is complete. - */ -bool kernfs_remove_self(struct kernfs_node *kn) -{ - bool ret; - - mutex_lock(&kernfs_mutex); - __kernfs_deactivate_self(kn); - - /* - * SUICIDAL is used to arbitrate among competing invocations. Only - * the first one will actually perform removal. When the removal - * is complete, SUICIDED is set and the active ref is restored - * while holding kernfs_mutex. The ones which lost arbitration - * waits for SUICDED && drained which can happen only after the - * enclosing kernfs operation which executed the winning instance - * of kernfs_remove_self() finished. - */ - if (!(kn->flags & KERNFS_SUICIDAL)) { - kn->flags |= KERNFS_SUICIDAL; - __kernfs_remove(kn); - kn->flags |= KERNFS_SUICIDED; - ret = true; - } else { - wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq; - DEFINE_WAIT(wait); - - while (true) { - prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE); - - if ((kn->flags & KERNFS_SUICIDED) && - atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) - break; - - mutex_unlock(&kernfs_mutex); - schedule(); - mutex_lock(&kernfs_mutex); - } - finish_wait(waitq, &wait); - WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); - ret = false; - } - - __kernfs_reactivate_self(kn); - mutex_unlock(&kernfs_mutex); - return ret; -} - /** * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it * @parent: parent of the target diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1b8b91b67fd..810cf6e613e 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -372,29 +372,6 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); -/** - * sysfs_remove_file_self - remove an object attribute from its own method - * @kobj: object we're acting for - * @attr: attribute descriptor - * - * See kernfs_remove_self() for details. - */ -bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) -{ - struct kernfs_node *parent = kobj->sd; - struct kernfs_node *kn; - bool ret; - - kn = kernfs_find_and_get(parent, attr->name); - if (WARN_ON_ONCE(!kn)) - return false; - - ret = kernfs_remove_self(kn); - - kernfs_put(kn); - return ret; -} - void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 1ff3f169751..952b01033c3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -560,8 +560,6 @@ extern int device_create_file(struct device *device, const struct device_attribute *entry); extern void device_remove_file(struct device *dev, const struct device_attribute *attr); -extern bool device_remove_file_self(struct device *dev, - const struct device_attribute *attr); extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 0b7b7cc352e..ac869302705 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -43,8 +43,6 @@ enum kernfs_node_flag { KERNFS_HAS_MMAP = 0x0080, KERNFS_LOCKDEP = 0x0100, KERNFS_STATIC_NAME = 0x0200, - KERNFS_SUICIDAL = 0x0400, - KERNFS_SUICIDED = 0x0800, }; /* type-specific structures for kernfs_node union members */ @@ -241,7 +239,6 @@ void kernfs_reactivate(struct kernfs_node *kn); void kernfs_deactivate_self(struct kernfs_node *kn); void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); -bool kernfs_remove_self(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, @@ -299,9 +296,6 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, static inline void kernfs_remove(struct kernfs_node *kn) { } -static inline bool kernfs_remove_self(struct kernfs_node *kn) -{ return false; } - static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, const char *name, const void *ns) { return -ENOSYS; } diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index bd96c603ab6..30b2ebee643 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -198,7 +198,6 @@ int __must_check sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns); -bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); int __must_check sysfs_create_bin_file(struct kobject *kobj, @@ -302,12 +301,6 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj, { } -static inline bool sysfs_remove_file_self(struct kobject *kobj, - const struct attribute *attr) -{ - return false; -} - static inline void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr) { -- cgit v1.2.3-70-g09d2