aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-01-10 08:57:19 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-10 16:44:25 -0500
commitea1c472dfeada211a0100daa7976e8e8e779b858 (patch)
treeba19d47bf769857075edccba0f6006004ec6bb91 /fs/kernfs
parentd92d2e6bd72b653f9811e0c9c46307c743b3fc58 (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. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/dir.c27
1 files changed, 11 insertions, 16 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 510b5062ef30..ed62de6cdf8f 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,26 +175,22 @@ 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 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 185 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
191 /* atomic_add_return() is a mb(), put_active() will always see
192 * the updated kn->u.completion.
193 */
194 v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active);
195 186
196 if (v != KN_DEACTIVATED_BIAS) { 187 atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
188
189 if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS)
197 lock_contended(&kn->dep_map, _RET_IP_); 190 lock_contended(&kn->dep_map, _RET_IP_);
198 wait_for_completion(&wait); 191
199 } 192 wait_event(root->deactivate_waitq,
193 atomic_read(&kn->active) == KN_DEACTIVATED_BIAS);
200 194
201 lock_acquired(&kn->dep_map, _RET_IP_); 195 lock_acquired(&kn->dep_map, _RET_IP_);
202 rwsem_release(&kn->dep_map, 1, _RET_IP_); 196 rwsem_release(&kn->dep_map, 1, _RET_IP_);
@@ -613,6 +607,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
613 607
614 root->dir_ops = kdops; 608 root->dir_ops = kdops;
615 root->kn = kn; 609 root->kn = kn;
610 init_waitqueue_head(&root->deactivate_waitq);
616 611
617 return root; 612 return root;
618} 613}