diff options
author | Tejun Heo <tj@kernel.org> | 2014-02-03 14:03:00 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-07 18:42:41 -0500 |
commit | 81c173cb5e87fbb47ccd80630faefe39bbf68449 (patch) | |
tree | 7b5aaa2255ac62f2879e2431062f9b96a38e8574 /fs/kernfs | |
parent | 182fd64b66342219d6fcf2b84d337529d120d95c (diff) |
kernfs: remove KERNFS_REMOVED
KERNFS_REMOVED is used to mark half-initialized and dying nodes so
that they don't show up in lookups and deny adding new nodes under or
renaming it; however, its role overlaps that of deactivation.
It's necessary to deny addition of new children while removal is in
progress; however, this role considerably intersects with deactivation
- KERNFS_REMOVED prevents new children while deactivation prevents new
file operations. There's no reason to have them separate making
things more complex than necessary.
This patch removes KERNFS_REMOVED.
* Instead of KERNFS_REMOVED, each node now starts its life
deactivated. This means that we now use both atomic_add() and
atomic_sub() on KN_DEACTIVATED_BIAS, which is INT_MIN. The compiler
generates an overflow warnings when negating INT_MIN as the negation
can't be represented as a positive number. Nothing is actually
broken but let's bump BIAS by one to avoid the warnings for archs
which negates the subtrahend..
* A new helper kernfs_active() which tests whether kn->active >= 0 is
added for convenience and lockdep annotation. All KERNFS_REMOVED
tests are replaced with negated kernfs_active() tests.
* __kernfs_remove() is updated to deactivate, but not drain, all nodes
in the subtree instead of setting KERNFS_REMOVED. This removes
deactivation from kernfs_deactivate(), which is now renamed to
kernfs_drain().
* Sanity check on KERNFS_REMOVED in kernfs_put() is replaced with
checks on the active ref.
* Some comment style updates in the affected area.
v2: Reordered before removal path restructuring. kernfs_active()
dropped and kernfs_get/put_active() used instead. RB_EMPTY_NODE()
used in the lookup paths.
v3: Reverted most of v2 except for creating a new node with
KN_DEACTIVATED_BIAS.
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.c | 66 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 3 |
2 files changed, 37 insertions, 32 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 5cf137b63db9..d0fd739bf82d 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c | |||
@@ -22,6 +22,12 @@ DEFINE_MUTEX(kernfs_mutex); | |||
22 | 22 | ||
23 | #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) | 23 | #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) |
24 | 24 | ||
25 | static bool kernfs_active(struct kernfs_node *kn) | ||
26 | { | ||
27 | lockdep_assert_held(&kernfs_mutex); | ||
28 | return atomic_read(&kn->active) >= 0; | ||
29 | } | ||
30 | |||
25 | static bool kernfs_lockdep(struct kernfs_node *kn) | 31 | static bool kernfs_lockdep(struct kernfs_node *kn) |
26 | { | 32 | { |
27 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | 33 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
@@ -183,25 +189,20 @@ void kernfs_put_active(struct kernfs_node *kn) | |||
183 | } | 189 | } |
184 | 190 | ||
185 | /** | 191 | /** |
186 | * kernfs_deactivate - deactivate kernfs_node | 192 | * kernfs_drain - drain kernfs_node |
187 | * @kn: kernfs_node to deactivate | 193 | * @kn: kernfs_node to drain |
188 | * | 194 | * |
189 | * Deny new active references, drain existing ones and nuke all | 195 | * Drain existing usages and nuke all existing mmaps of @kn. Mutiple |
190 | * existing mmaps. Mutiple removers may invoke this function | 196 | * removers may invoke this function concurrently on @kn and all will |
191 | * concurrently on @kn and all will return after deactivation and | 197 | * return after draining is complete. |
192 | * draining are complete. | ||
193 | */ | 198 | */ |
194 | static void kernfs_deactivate(struct kernfs_node *kn) | 199 | static void kernfs_drain(struct kernfs_node *kn) |
195 | __releases(&kernfs_mutex) __acquires(&kernfs_mutex) | 200 | __releases(&kernfs_mutex) __acquires(&kernfs_mutex) |
196 | { | 201 | { |
197 | struct kernfs_root *root = kernfs_root(kn); | 202 | struct kernfs_root *root = kernfs_root(kn); |
198 | 203 | ||
199 | lockdep_assert_held(&kernfs_mutex); | 204 | lockdep_assert_held(&kernfs_mutex); |
200 | BUG_ON(!(kn->flags & KERNFS_REMOVED)); | 205 | WARN_ON_ONCE(kernfs_active(kn)); |
201 | |||
202 | /* only the first invocation on @kn should deactivate it */ | ||
203 | if (atomic_read(&kn->active) >= 0) | ||
204 | atomic_add(KN_DEACTIVATED_BIAS, &kn->active); | ||
205 | 206 | ||
206 | mutex_unlock(&kernfs_mutex); | 207 | mutex_unlock(&kernfs_mutex); |
207 | 208 | ||
@@ -253,13 +254,15 @@ void kernfs_put(struct kernfs_node *kn) | |||
253 | return; | 254 | return; |
254 | root = kernfs_root(kn); | 255 | root = kernfs_root(kn); |
255 | repeat: | 256 | repeat: |
256 | /* Moving/renaming is always done while holding reference. | 257 | /* |
258 | * Moving/renaming is always done while holding reference. | ||
257 | * kn->parent won't change beneath us. | 259 | * kn->parent won't change beneath us. |
258 | */ | 260 | */ |
259 | parent = kn->parent; | 261 | parent = kn->parent; |
260 | 262 | ||
261 | WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", | 263 | WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, |
262 | parent ? parent->name : "", kn->name); | 264 | "kernfs_put: %s/%s: released with incorrect active_ref %d\n", |
265 | parent ? parent->name : "", kn->name, atomic_read(&kn->active)); | ||
263 | 266 | ||
264 | if (kernfs_type(kn) == KERNFS_LINK) | 267 | if (kernfs_type(kn) == KERNFS_LINK) |
265 | kernfs_put(kn->symlink.target_kn); | 268 | kernfs_put(kn->symlink.target_kn); |
@@ -301,8 +304,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) | |||
301 | kn = dentry->d_fsdata; | 304 | kn = dentry->d_fsdata; |
302 | mutex_lock(&kernfs_mutex); | 305 | mutex_lock(&kernfs_mutex); |
303 | 306 | ||
304 | /* The kernfs node has been deleted */ | 307 | /* The kernfs node has been deactivated */ |
305 | if (kn->flags & KERNFS_REMOVED) | 308 | if (!kernfs_active(kn)) |
306 | goto out_bad; | 309 | goto out_bad; |
307 | 310 | ||
308 | /* The kernfs node has been moved? */ | 311 | /* The kernfs node has been moved? */ |
@@ -371,12 +374,12 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, | |||
371 | kn->ino = ret; | 374 | kn->ino = ret; |
372 | 375 | ||
373 | atomic_set(&kn->count, 1); | 376 | atomic_set(&kn->count, 1); |
374 | atomic_set(&kn->active, 0); | 377 | atomic_set(&kn->active, KN_DEACTIVATED_BIAS); |
375 | RB_CLEAR_NODE(&kn->rb); | 378 | RB_CLEAR_NODE(&kn->rb); |
376 | 379 | ||
377 | kn->name = name; | 380 | kn->name = name; |
378 | kn->mode = mode; | 381 | kn->mode = mode; |
379 | kn->flags = flags | KERNFS_REMOVED; | 382 | kn->flags = flags; |
380 | 383 | ||
381 | return kn; | 384 | return kn; |
382 | 385 | ||
@@ -432,7 +435,7 @@ int kernfs_add_one(struct kernfs_node *kn) | |||
432 | goto out_unlock; | 435 | goto out_unlock; |
433 | 436 | ||
434 | ret = -ENOENT; | 437 | ret = -ENOENT; |
435 | if (parent->flags & KERNFS_REMOVED) | 438 | if (!kernfs_active(parent)) |
436 | goto out_unlock; | 439 | goto out_unlock; |
437 | 440 | ||
438 | kn->hash = kernfs_name_hash(kn->name, kn->ns); | 441 | kn->hash = kernfs_name_hash(kn->name, kn->ns); |
@@ -449,7 +452,7 @@ int kernfs_add_one(struct kernfs_node *kn) | |||
449 | } | 452 | } |
450 | 453 | ||
451 | /* Mark the entry added into directory tree */ | 454 | /* Mark the entry added into directory tree */ |
452 | kn->flags &= ~KERNFS_REMOVED; | 455 | atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); |
453 | ret = 0; | 456 | ret = 0; |
454 | out_unlock: | 457 | out_unlock: |
455 | mutex_unlock(&kernfs_mutex); | 458 | mutex_unlock(&kernfs_mutex); |
@@ -549,7 +552,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) | |||
549 | return ERR_PTR(-ENOMEM); | 552 | return ERR_PTR(-ENOMEM); |
550 | } | 553 | } |
551 | 554 | ||
552 | kn->flags &= ~KERNFS_REMOVED; | 555 | atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); |
553 | kn->priv = priv; | 556 | kn->priv = priv; |
554 | kn->dir.root = root; | 557 | kn->dir.root = root; |
555 | 558 | ||
@@ -763,24 +766,25 @@ static void __kernfs_remove(struct kernfs_node *kn) | |||
763 | 766 | ||
764 | pr_debug("kernfs %s: removing\n", kn->name); | 767 | pr_debug("kernfs %s: removing\n", kn->name); |
765 | 768 | ||
766 | /* disable lookup and node creation under @kn */ | 769 | /* prevent any new usage under @kn by deactivating all nodes */ |
767 | pos = NULL; | 770 | pos = NULL; |
768 | while ((pos = kernfs_next_descendant_post(pos, kn))) | 771 | while ((pos = kernfs_next_descendant_post(pos, kn))) |
769 | pos->flags |= KERNFS_REMOVED; | 772 | if (kernfs_active(pos)) |
773 | atomic_add(KN_DEACTIVATED_BIAS, &pos->active); | ||
770 | 774 | ||
771 | /* deactivate and unlink the subtree node-by-node */ | 775 | /* deactivate and unlink the subtree node-by-node */ |
772 | do { | 776 | do { |
773 | pos = kernfs_leftmost_descendant(kn); | 777 | pos = kernfs_leftmost_descendant(kn); |
774 | 778 | ||
775 | /* | 779 | /* |
776 | * kernfs_deactivate() drops kernfs_mutex temporarily and | 780 | * kernfs_drain() drops kernfs_mutex temporarily and @pos's |
777 | * @pos's base ref could have been put by someone else by | 781 | * base ref could have been put by someone else by the time |
778 | * the time the function returns. Make sure it doesn't go | 782 | * the function returns. Make sure it doesn't go away |
779 | * away underneath us. | 783 | * underneath us. |
780 | */ | 784 | */ |
781 | kernfs_get(pos); | 785 | kernfs_get(pos); |
782 | 786 | ||
783 | kernfs_deactivate(pos); | 787 | kernfs_drain(pos); |
784 | 788 | ||
785 | /* | 789 | /* |
786 | * kernfs_unlink_sibling() succeeds once per node. Use it | 790 | * kernfs_unlink_sibling() succeeds once per node. Use it |
@@ -865,7 +869,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, | |||
865 | mutex_lock(&kernfs_mutex); | 869 | mutex_lock(&kernfs_mutex); |
866 | 870 | ||
867 | error = -ENOENT; | 871 | error = -ENOENT; |
868 | if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) | 872 | if (!kernfs_active(kn) || !kernfs_active(new_parent)) |
869 | goto out; | 873 | goto out; |
870 | 874 | ||
871 | error = 0; | 875 | error = 0; |
@@ -925,7 +929,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, | |||
925 | struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) | 929 | struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) |
926 | { | 930 | { |
927 | if (pos) { | 931 | if (pos) { |
928 | int valid = !(pos->flags & KERNFS_REMOVED) && | 932 | int valid = kernfs_active(pos) && |
929 | pos->parent == parent && hash == pos->hash; | 933 | pos->parent == parent && hash == pos->hash; |
930 | kernfs_put(pos); | 934 | kernfs_put(pos); |
931 | if (!valid) | 935 | if (!valid) |
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 46b58de794d6..a91d7a1113d9 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h | |||
@@ -26,7 +26,8 @@ struct kernfs_iattrs { | |||
26 | struct simple_xattrs xattrs; | 26 | struct simple_xattrs xattrs; |
27 | }; | 27 | }; |
28 | 28 | ||
29 | #define KN_DEACTIVATED_BIAS INT_MIN | 29 | /* +1 to avoid triggering overflow warning when negating it */ |
30 | #define KN_DEACTIVATED_BIAS (INT_MIN + 1) | ||
30 | 31 | ||
31 | /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ | 32 | /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ |
32 | 33 | ||