aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-01-10 08:57:21 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-10 16:44:25 -0500
commitae34372eb8408b3d07e870f1939f99007a730d28 (patch)
tree5c291bca19df41c3085f8fbf96047f81d9ab545b /fs/kernfs
parenta69d001cfc712b96ec9d7ba44d6285702a38dabf (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 those of deactivation and removal from rbtree. 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. KERNFS_REMOVED is also used to decide whether a node is still visible to vfs layer, which is rather redundant as equivalent determination can be made by testing whether the node is on its parent's children rbtree or not. 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.. * KERNFS_REMOVED tests in add and rename paths are replaced with kernfs_get/put_active() of the target nodes. Due to the way the add path is structured now, active ref handling is done in the callers of kernfs_add_one(). This will be consolidated up later. * kernfs_remove_one() is updated to deactivate instead of setting KERNFS_REMOVED. This removes deactivation from kernfs_deactivate(), which is now renamed to kernfs_drain(). * kernfs_dop_revalidate() now tests RB_EMPTY_NODE(&kn->rb) instead of KERNFS_REMOVED and KERNFS_REMOVED test in kernfs_dir_pos() is dropped. A node which is removed from the children rbtree is not included in the iteration in the first place. This means that a node may be visible through vfs a bit longer - it's now also visible after deactivation until the actual removal. This slightly enlarged window difference doesn't make any difference to the userland. * 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. 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.c79
-rw-r--r--fs/kernfs/file.c10
-rw-r--r--fs/kernfs/kernfs-internal.h3
-rw-r--r--fs/kernfs/symlink.c10
4 files changed, 60 insertions, 42 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 1c9130a33048..7f8afc1d08f1 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -127,6 +127,7 @@ static void kernfs_unlink_sibling(struct kernfs_node *kn)
127 kn->parent->dir.subdirs--; 127 kn->parent->dir.subdirs--;
128 128
129 rb_erase(&kn->rb, &kn->parent->dir.children); 129 rb_erase(&kn->rb, &kn->parent->dir.children);
130 RB_CLEAR_NODE(&kn->rb);
130} 131}
131 132
132/** 133/**
@@ -177,18 +178,16 @@ void kernfs_put_active(struct kernfs_node *kn)
177} 178}
178 179
179/** 180/**
180 * kernfs_deactivate - deactivate kernfs_node 181 * kernfs_drain - drain kernfs_node
181 * @kn: kernfs_node to deactivate 182 * @kn: kernfs_node to drain
182 * 183 *
183 * Deny new active references and drain existing ones. 184 * Drain existing usages.
184 */ 185 */
185static void kernfs_deactivate(struct kernfs_node *kn) 186static void kernfs_drain(struct kernfs_node *kn)
186{ 187{
187 struct kernfs_root *root = kernfs_root(kn); 188 struct kernfs_root *root = kernfs_root(kn);
188 189
189 BUG_ON(!(kn->flags & KERNFS_REMOVED)); 190 WARN_ON_ONCE(atomic_read(&kn->active) >= 0);
190
191 atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
192 191
193 if (kernfs_lockdep(kn)) { 192 if (kernfs_lockdep(kn)) {
194 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 193 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@@ -233,13 +232,15 @@ void kernfs_put(struct kernfs_node *kn)
233 return; 232 return;
234 root = kernfs_root(kn); 233 root = kernfs_root(kn);
235 repeat: 234 repeat:
236 /* Moving/renaming is always done while holding reference. 235 /*
236 * Moving/renaming is always done while holding reference.
237 * kn->parent won't change beneath us. 237 * kn->parent won't change beneath us.
238 */ 238 */
239 parent = kn->parent; 239 parent = kn->parent;
240 240
241 WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", 241 WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
242 parent ? parent->name : "", kn->name); 242 "kernfs_put: %s/%s: released with incorrect active_ref %d\n",
243 parent ? parent->name : "", kn->name, atomic_read(&kn->active));
243 244
244 if (kernfs_type(kn) == KERNFS_LINK) 245 if (kernfs_type(kn) == KERNFS_LINK)
245 kernfs_put(kn->symlink.target_kn); 246 kernfs_put(kn->symlink.target_kn);
@@ -281,8 +282,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
281 kn = dentry->d_fsdata; 282 kn = dentry->d_fsdata;
282 mutex_lock(&kernfs_mutex); 283 mutex_lock(&kernfs_mutex);
283 284
284 /* The kernfs node has been deleted */ 285 /* Force fresh lookup if removed */
285 if (kn->flags & KERNFS_REMOVED) 286 if (kn->parent && RB_EMPTY_NODE(&kn->rb))
286 goto out_bad; 287 goto out_bad;
287 288
288 /* The kernfs node has been moved? */ 289 /* The kernfs node has been moved? */
@@ -350,11 +351,12 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name,
350 kn->ino = ret; 351 kn->ino = ret;
351 352
352 atomic_set(&kn->count, 1); 353 atomic_set(&kn->count, 1);
353 atomic_set(&kn->active, 0); 354 atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
355 RB_CLEAR_NODE(&kn->rb);
354 356
355 kn->name = name; 357 kn->name = name;
356 kn->mode = mode; 358 kn->mode = mode;
357 kn->flags = flags | KERNFS_REMOVED; 359 kn->flags = flags;
358 360
359 return kn; 361 return kn;
360 362
@@ -413,6 +415,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
413 struct kernfs_iattrs *ps_iattr; 415 struct kernfs_iattrs *ps_iattr;
414 int ret; 416 int ret;
415 417
418 WARN_ON_ONCE(atomic_read(&parent->active) < 0);
419
416 if (has_ns != (bool)kn->ns) { 420 if (has_ns != (bool)kn->ns) {
417 WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", 421 WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
418 has_ns ? "required" : "invalid", parent->name, kn->name); 422 has_ns ? "required" : "invalid", parent->name, kn->name);
@@ -422,9 +426,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
422 if (kernfs_type(parent) != KERNFS_DIR) 426 if (kernfs_type(parent) != KERNFS_DIR)
423 return -EINVAL; 427 return -EINVAL;
424 428
425 if (parent->flags & KERNFS_REMOVED)
426 return -ENOENT;
427
428 kn->hash = kernfs_name_hash(kn->name, kn->ns); 429 kn->hash = kernfs_name_hash(kn->name, kn->ns);
429 kn->parent = parent; 430 kn->parent = parent;
430 kernfs_get(parent); 431 kernfs_get(parent);
@@ -441,8 +442,7 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
441 } 442 }
442 443
443 /* Mark the entry added into directory tree */ 444 /* Mark the entry added into directory tree */
444 kn->flags &= ~KERNFS_REMOVED; 445 atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
445
446 return 0; 446 return 0;
447} 447}
448 448
@@ -470,7 +470,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt,
470 * Removal can be called multiple times on the same node. Only the 470 * Removal can be called multiple times on the same node. Only the
471 * first invocation is effective and puts the base ref. 471 * first invocation is effective and puts the base ref.
472 */ 472 */
473 if (kn->flags & KERNFS_REMOVED) 473 if (atomic_read(&kn->active) < 0)
474 return; 474 return;
475 475
476 if (kn->parent) { 476 if (kn->parent) {
@@ -484,7 +484,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt,
484 } 484 }
485 } 485 }
486 486
487 kn->flags |= KERNFS_REMOVED; 487 atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
488 kn->u.removed_list = acxt->removed; 488 kn->u.removed_list = acxt->removed;
489 acxt->removed = kn; 489 acxt->removed = kn;
490} 490}
@@ -512,7 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt)
512 512
513 acxt->removed = kn->u.removed_list; 513 acxt->removed = kn->u.removed_list;
514 514
515 kernfs_deactivate(kn); 515 kernfs_drain(kn);
516 kernfs_unmap_bin_file(kn); 516 kernfs_unmap_bin_file(kn);
517 kernfs_put(kn); 517 kernfs_put(kn);
518 } 518 }
@@ -610,7 +610,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
610 return ERR_PTR(-ENOMEM); 610 return ERR_PTR(-ENOMEM);
611 } 611 }
612 612
613 kn->flags &= ~KERNFS_REMOVED; 613 atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
614 kn->priv = priv; 614 kn->priv = priv;
615 kn->dir.root = root; 615 kn->dir.root = root;
616 616
@@ -662,9 +662,13 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
662 kn->priv = priv; 662 kn->priv = priv;
663 663
664 /* link in */ 664 /* link in */
665 kernfs_addrm_start(&acxt); 665 rc = -ENOENT;
666 rc = kernfs_add_one(&acxt, kn, parent); 666 if (kernfs_get_active(parent)) {
667 kernfs_addrm_finish(&acxt); 667 kernfs_addrm_start(&acxt);
668 rc = kernfs_add_one(&acxt, kn, parent);
669 kernfs_addrm_finish(&acxt);
670 kernfs_put_active(parent);
671 }
668 672
669 if (!rc) 673 if (!rc)
670 return kn; 674 return kn;
@@ -899,27 +903,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
899{ 903{
900 int error; 904 int error;
901 905
902 mutex_lock(&kernfs_mutex);
903
904 error = -ENOENT; 906 error = -ENOENT;
905 if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) 907 if (!kernfs_get_active(new_parent))
906 goto out; 908 goto out;
909 if (!kernfs_get_active(kn))
910 goto out_put_new_parent;
911
912 mutex_lock(&kernfs_mutex);
907 913
908 error = 0; 914 error = 0;
909 if ((kn->parent == new_parent) && (kn->ns == new_ns) && 915 if ((kn->parent == new_parent) && (kn->ns == new_ns) &&
910 (strcmp(kn->name, new_name) == 0)) 916 (strcmp(kn->name, new_name) == 0))
911 goto out; /* nothing to rename */ 917 goto out_unlock; /* nothing to rename */
912 918
913 error = -EEXIST; 919 error = -EEXIST;
914 if (kernfs_find_ns(new_parent, new_name, new_ns)) 920 if (kernfs_find_ns(new_parent, new_name, new_ns))
915 goto out; 921 goto out_unlock;
916 922
917 /* rename kernfs_node */ 923 /* rename kernfs_node */
918 if (strcmp(kn->name, new_name) != 0) { 924 if (strcmp(kn->name, new_name) != 0) {
919 error = -ENOMEM; 925 error = -ENOMEM;
920 new_name = kstrdup(new_name, GFP_KERNEL); 926 new_name = kstrdup(new_name, GFP_KERNEL);
921 if (!new_name) 927 if (!new_name)
922 goto out; 928 goto out_unlock;
923 929
924 if (kn->flags & KERNFS_STATIC_NAME) 930 if (kn->flags & KERNFS_STATIC_NAME)
925 kn->flags &= ~KERNFS_STATIC_NAME; 931 kn->flags &= ~KERNFS_STATIC_NAME;
@@ -941,8 +947,12 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
941 kernfs_link_sibling(kn); 947 kernfs_link_sibling(kn);
942 948
943 error = 0; 949 error = 0;
944 out: 950out_unlock:
945 mutex_unlock(&kernfs_mutex); 951 mutex_unlock(&kernfs_mutex);
952 kernfs_put_active(kn);
953out_put_new_parent:
954 kernfs_put_active(new_parent);
955out:
946 return error; 956 return error;
947} 957}
948 958
@@ -962,8 +972,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
962 struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) 972 struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
963{ 973{
964 if (pos) { 974 if (pos) {
965 int valid = !(pos->flags & KERNFS_REMOVED) && 975 int valid = pos->parent == parent && hash == pos->hash;
966 pos->parent == parent && hash == pos->hash;
967 kernfs_put(pos); 976 kernfs_put(pos);
968 if (!valid) 977 if (!valid)
969 pos = NULL; 978 pos = NULL;
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index bdd38854ef65..231a171f48b6 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -856,9 +856,13 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
856 if (ops->mmap) 856 if (ops->mmap)
857 kn->flags |= KERNFS_HAS_MMAP; 857 kn->flags |= KERNFS_HAS_MMAP;
858 858
859 kernfs_addrm_start(&acxt); 859 rc = -ENOENT;
860 rc = kernfs_add_one(&acxt, kn, parent); 860 if (kernfs_get_active(parent)) {
861 kernfs_addrm_finish(&acxt); 861 kernfs_addrm_start(&acxt);
862 rc = kernfs_add_one(&acxt, kn, parent);
863 kernfs_addrm_finish(&acxt);
864 kernfs_put_active(parent);
865 }
862 866
863 if (rc) { 867 if (rc) {
864 kernfs_put(kn); 868 kernfs_put(kn);
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index c6ba5bc37a98..57a93f4d645c 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
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index a03e26036ef9..b2c106ca3434 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -40,9 +40,13 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
40 kn->symlink.target_kn = target; 40 kn->symlink.target_kn = target;
41 kernfs_get(target); /* ref owned by symlink */ 41 kernfs_get(target); /* ref owned by symlink */
42 42
43 kernfs_addrm_start(&acxt); 43 error = -ENOENT;
44 error = kernfs_add_one(&acxt, kn, parent); 44 if (kernfs_get_active(parent)) {
45 kernfs_addrm_finish(&acxt); 45 kernfs_addrm_start(&acxt);
46 error = kernfs_add_one(&acxt, kn, parent);
47 kernfs_addrm_finish(&acxt);
48 kernfs_put_active(parent);
49 }
46 50
47 if (!error) 51 if (!error)
48 return kn; 52 return kn;