summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-02-03 14:02:55 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-07 15:42:40 -0800
commitabd54f028ec30976d6e797e7474ec91d96186a0c (patch)
tree3fc9f5240ec0dfdcb210499878e08a18a678157f
parenta6607930b6cd829fc7f680b48a937d827e0bb931 (diff)
kernfs: replace kernfs_node->u.completion with kernfs_root->deactivate_waitq
kernfs_node->u.completion is used to notify deactivation completion from kernfs_put_active() to kernfs_deactivate(). We now allow multiple racing removals of the same node and the current removal scheme is no longer correct - kernfs_remove() invocation may return before the node is properly deactivated if it races against another removal. The removal path will be restructured to address the issue. To help such restructure which requires supporting multiple waiters, this patch replaces kernfs_node->u.completion with kernfs_root->deactivate_waitq. This makes deactivation event notifications share a per-root waitqueue_head; however, the wait path is quite cold and this will also allow shaving one pointer off kernfs_node. v2: Refreshed on top of ("kernfs: make kernfs_deactivate() honor KERNFS_LOCKDEP flag"). Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/kernfs/dir.c31
-rw-r--r--include/linux/kernfs.h4
2 files changed, 15 insertions, 20 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index bd6e18be6e1..2193d30156e 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -8,6 +8,7 @@
* This file is released under the GPLv2.
*/
+#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/idr.h>
@@ -151,6 +152,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
*/
void kernfs_put_active(struct kernfs_node *kn)
{
+ struct kernfs_root *root = kernfs_root(kn);
int v;
if (unlikely(!kn))
@@ -162,11 +164,7 @@ void kernfs_put_active(struct kernfs_node *kn)
if (likely(v != KN_DEACTIVATED_BIAS))
return;
- /*
- * atomic_dec_return() is a mb(), we'll always see the updated
- * kn->u.completion.
- */
- complete(kn->u.completion);
+ wake_up_all(&root->deactivate_waitq);
}
/**
@@ -177,28 +175,24 @@ void kernfs_put_active(struct kernfs_node *kn)
*/
static void kernfs_deactivate(struct kernfs_node *kn)
{
- DECLARE_COMPLETION_ONSTACK(wait);
- int v;
+ struct kernfs_root *root = kernfs_root(kn);
BUG_ON(!(kn->flags & KERNFS_REMOVED));
if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF))
return;
- kn->u.completion = (void *)&wait;
-
if (kn->flags & KERNFS_LOCKDEP)
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
- /* atomic_add_return() is a mb(), put_active() will always see
- * the updated kn->u.completion.
- */
- v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active);
- if (v != KN_DEACTIVATED_BIAS) {
- if (kn->flags & KERNFS_LOCKDEP)
- lock_contended(&kn->dep_map, _RET_IP_);
- wait_for_completion(&wait);
- }
+ atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
+
+ if ((kn->flags & KERNFS_LOCKDEP) &&
+ atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
+ lock_contended(&kn->dep_map, _RET_IP_);
+
+ wait_event(root->deactivate_waitq,
+ atomic_read(&kn->active) == KN_DEACTIVATED_BIAS);
if (kn->flags & KERNFS_LOCKDEP) {
lock_acquired(&kn->dep_map, _RET_IP_);
@@ -630,6 +624,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
root->dir_ops = kdops;
root->kn = kn;
+ init_waitqueue_head(&root->deactivate_waitq);
return root;
}
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 5be9f0228a3..295a3bf642b 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -15,7 +15,7 @@
#include <linux/lockdep.h>
#include <linux/rbtree.h>
#include <linux/atomic.h>
-#include <linux/completion.h>
+#include <linux/wait.h>
struct file;
struct dentry;
@@ -92,7 +92,6 @@ struct kernfs_node {
struct rb_node rb;
union {
- struct completion *completion;
struct kernfs_node *removed_list;
} u;
@@ -133,6 +132,7 @@ struct kernfs_root {
/* private fields, do not use outside kernfs proper */
struct ida ino_ida;
struct kernfs_dir_ops *dir_ops;
+ wait_queue_head_t deactivate_waitq;
};
struct kernfs_open_file {