kernfs: replace kernfs_node->u.completion with kernfs_root->deactivate_waitq
authorTejun Heo <tj@kernel.org>
Mon, 3 Feb 2014 19:02:55 +0000 (14:02 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Feb 2014 23:42:40 +0000 (15:42 -0800)
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>
fs/kernfs/dir.c
include/linux/kernfs.h

index bd6e18be6e1a231c10867bc8f4e292e49353d03f..2193d30156efeaf1bf07a772e9fee731f3d6f40d 100644 (file)
@@ -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;
 }
index 5be9f0228a3bbc8ddfad7a2fc245f6601cfc1f17..295a3bf642bab4aba6a82990312e9eb16b280805 100644 (file)
@@ -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 {