aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs/dir.c
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 18:42:40 -0500
commitabd54f028ec30976d6e797e7474ec91d96186a0c (patch)
tree3fc9f5240ec0dfdcb210499878e08a18a678157f /fs/kernfs/dir.c
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>
Diffstat (limited to 'fs/kernfs/dir.c')
-rw-r--r--fs/kernfs/dir.c31
1 files changed, 13 insertions, 18 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index bd6e18be6e1a..2193d30156ef 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -8,6 +8,7 @@
8 * This file is released under the GPLv2. 8 * This file is released under the GPLv2.
9 */ 9 */
10 10
11#include <linux/sched.h>
11#include <linux/fs.h> 12#include <linux/fs.h>
12#include <linux/namei.h> 13#include <linux/namei.h>
13#include <linux/idr.h> 14#include <linux/idr.h>
@@ -151,6 +152,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
151 */ 152 */
152void kernfs_put_active(struct kernfs_node *kn) 153void kernfs_put_active(struct kernfs_node *kn)
153{ 154{
155 struct kernfs_root *root = kernfs_root(kn);
154 int v; 156 int v;
155 157
156 if (unlikely(!kn)) 158 if (unlikely(!kn))
@@ -162,11 +164,7 @@ void kernfs_put_active(struct kernfs_node *kn)
162 if (likely(v != KN_DEACTIVATED_BIAS)) 164 if (likely(v != KN_DEACTIVATED_BIAS))
163 return; 165 return;
164 166
165 /* 167 wake_up_all(&root->deactivate_waitq);
166 * atomic_dec_return() is a mb(), we'll always see the updated
167 * kn->u.completion.
168 */
169 complete(kn->u.completion);
170} 168}
171 169
172/** 170/**
@@ -177,28 +175,24 @@ void kernfs_put_active(struct kernfs_node *kn)
177 */ 175 */
178static void kernfs_deactivate(struct kernfs_node *kn) 176static void kernfs_deactivate(struct kernfs_node *kn)
179{ 177{
180 DECLARE_COMPLETION_ONSTACK(wait); 178 struct kernfs_root *root = kernfs_root(kn);
181 int v;
182 179
183 BUG_ON(!(kn->flags & KERNFS_REMOVED)); 180 BUG_ON(!(kn->flags & KERNFS_REMOVED));
184 181
185 if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) 182 if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF))
186 return; 183 return;
187 184
188 kn->u.completion = (void *)&wait;
189
190 if (kn->flags & KERNFS_LOCKDEP) 185 if (kn->flags & KERNFS_LOCKDEP)
191 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 186 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
192 /* atomic_add_return() is a mb(), put_active() will always see
193 * the updated kn->u.completion.
194 */
195 v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active);
196 187
197 if (v != KN_DEACTIVATED_BIAS) { 188 atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
198 if (kn->flags & KERNFS_LOCKDEP) 189
199 lock_contended(&kn->dep_map, _RET_IP_); 190 if ((kn->flags & KERNFS_LOCKDEP) &&
200 wait_for_completion(&wait); 191 atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
201 } 192 lock_contended(&kn->dep_map, _RET_IP_);
193
194 wait_event(root->deactivate_waitq,
195 atomic_read(&kn->active) == KN_DEACTIVATED_BIAS);
202 196
203 if (kn->flags & KERNFS_LOCKDEP) { 197 if (kn->flags & KERNFS_LOCKDEP) {
204 lock_acquired(&kn->dep_map, _RET_IP_); 198 lock_acquired(&kn->dep_map, _RET_IP_);
@@ -630,6 +624,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
630 624
631 root->dir_ops = kdops; 625 root->dir_ops = kdops;
632 root->kn = kn; 626 root->kn = kn;
627 init_waitqueue_head(&root->deactivate_waitq);
633 628
634 return root; 629 return root;
635} 630}