aboutsummaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
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, 42 insertions, 60 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 7f8afc1d08f1..1c9130a33048 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -127,7 +127,6 @@ 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);
131} 130}
132 131
133/** 132/**
@@ -178,16 +177,18 @@ void kernfs_put_active(struct kernfs_node *kn)
178} 177}
179 178
180/** 179/**
181 * kernfs_drain - drain kernfs_node 180 * kernfs_deactivate - deactivate kernfs_node
182 * @kn: kernfs_node to drain 181 * @kn: kernfs_node to deactivate
183 * 182 *
184 * Drain existing usages. 183 * Deny new active references and drain existing ones.
185 */ 184 */
186static void kernfs_drain(struct kernfs_node *kn) 185static void kernfs_deactivate(struct kernfs_node *kn)
187{ 186{
188 struct kernfs_root *root = kernfs_root(kn); 187 struct kernfs_root *root = kernfs_root(kn);
189 188
190 WARN_ON_ONCE(atomic_read(&kn->active) >= 0); 189 BUG_ON(!(kn->flags & KERNFS_REMOVED));
190
191 atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
191 192
192 if (kernfs_lockdep(kn)) { 193 if (kernfs_lockdep(kn)) {
193 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); 194 rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@@ -232,15 +233,13 @@ void kernfs_put(struct kernfs_node *kn)
232 return; 233 return;
233 root = kernfs_root(kn); 234 root = kernfs_root(kn);
234 repeat: 235 repeat:
235 /* 236 /* Moving/renaming is always done while holding reference.
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_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, 241 WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n",
242 "kernfs_put: %s/%s: released with incorrect active_ref %d\n", 242 parent ? parent->name : "", kn->name);
243 parent ? parent->name : "", kn->name, atomic_read(&kn->active));
244 243
245 if (kernfs_type(kn) == KERNFS_LINK) 244 if (kernfs_type(kn) == KERNFS_LINK)
246 kernfs_put(kn->symlink.target_kn); 245 kernfs_put(kn->symlink.target_kn);
@@ -282,8 +281,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
282 kn = dentry->d_fsdata; 281 kn = dentry->d_fsdata;
283 mutex_lock(&kernfs_mutex); 282 mutex_lock(&kernfs_mutex);
284 283
285 /* Force fresh lookup if removed */ 284 /* The kernfs node has been deleted */
286 if (kn->parent && RB_EMPTY_NODE(&kn->rb)) 285 if (kn->flags & KERNFS_REMOVED)
287 goto out_bad; 286 goto out_bad;
288 287
289 /* The kernfs node has been moved? */ 288 /* The kernfs node has been moved? */
@@ -351,12 +350,11 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name,
351 kn->ino = ret; 350 kn->ino = ret;
352 351
353 atomic_set(&kn->count, 1); 352 atomic_set(&kn->count, 1);
354 atomic_set(&kn->active, KN_DEACTIVATED_BIAS); 353 atomic_set(&kn->active, 0);
355 RB_CLEAR_NODE(&kn->rb);
356 354
357 kn->name = name; 355 kn->name = name;
358 kn->mode = mode; 356 kn->mode = mode;
359 kn->flags = flags; 357 kn->flags = flags | KERNFS_REMOVED;
360 358
361 return kn; 359 return kn;
362 360
@@ -415,8 +413,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
415 struct kernfs_iattrs *ps_iattr; 413 struct kernfs_iattrs *ps_iattr;
416 int ret; 414 int ret;
417 415
418 WARN_ON_ONCE(atomic_read(&parent->active) < 0);
419
420 if (has_ns != (bool)kn->ns) { 416 if (has_ns != (bool)kn->ns) {
421 WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", 417 WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
422 has_ns ? "required" : "invalid", parent->name, kn->name); 418 has_ns ? "required" : "invalid", parent->name, kn->name);
@@ -426,6 +422,9 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
426 if (kernfs_type(parent) != KERNFS_DIR) 422 if (kernfs_type(parent) != KERNFS_DIR)
427 return -EINVAL; 423 return -EINVAL;
428 424
425 if (parent->flags & KERNFS_REMOVED)
426 return -ENOENT;
427
429 kn->hash = kernfs_name_hash(kn->name, kn->ns); 428 kn->hash = kernfs_name_hash(kn->name, kn->ns);
430 kn->parent = parent; 429 kn->parent = parent;
431 kernfs_get(parent); 430 kernfs_get(parent);
@@ -442,7 +441,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
442 } 441 }
443 442
444 /* Mark the entry added into directory tree */ 443 /* Mark the entry added into directory tree */
445 atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); 444 kn->flags &= ~KERNFS_REMOVED;
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 (atomic_read(&kn->active) < 0) 473 if (kn->flags & KERNFS_REMOVED)
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 atomic_add(KN_DEACTIVATED_BIAS, &kn->active); 487 kn->flags |= KERNFS_REMOVED;
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_drain(kn); 515 kernfs_deactivate(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 atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); 613 kn->flags &= ~KERNFS_REMOVED;
614 kn->priv = priv; 614 kn->priv = priv;
615 kn->dir.root = root; 615 kn->dir.root = root;
616 616
@@ -662,13 +662,9 @@ 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 rc = -ENOENT; 665 kernfs_addrm_start(&acxt);
666 if (kernfs_get_active(parent)) { 666 rc = kernfs_add_one(&acxt, kn, parent);
667 kernfs_addrm_start(&acxt); 667 kernfs_addrm_finish(&acxt);
668 rc = kernfs_add_one(&acxt, kn, parent);
669 kernfs_addrm_finish(&acxt);
670 kernfs_put_active(parent);
671 }
672 668
673 if (!rc) 669 if (!rc)
674 return kn; 670 return kn;
@@ -903,29 +899,27 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
903{ 899{
904 int error; 900 int error;
905 901
902 mutex_lock(&kernfs_mutex);
903
906 error = -ENOENT; 904 error = -ENOENT;
907 if (!kernfs_get_active(new_parent)) 905 if ((kn->flags | new_parent->flags) & KERNFS_REMOVED)
908 goto out; 906 goto out;
909 if (!kernfs_get_active(kn))
910 goto out_put_new_parent;
911
912 mutex_lock(&kernfs_mutex);
913 907
914 error = 0; 908 error = 0;
915 if ((kn->parent == new_parent) && (kn->ns == new_ns) && 909 if ((kn->parent == new_parent) && (kn->ns == new_ns) &&
916 (strcmp(kn->name, new_name) == 0)) 910 (strcmp(kn->name, new_name) == 0))
917 goto out_unlock; /* nothing to rename */ 911 goto out; /* nothing to rename */
918 912
919 error = -EEXIST; 913 error = -EEXIST;
920 if (kernfs_find_ns(new_parent, new_name, new_ns)) 914 if (kernfs_find_ns(new_parent, new_name, new_ns))
921 goto out_unlock; 915 goto out;
922 916
923 /* rename kernfs_node */ 917 /* rename kernfs_node */
924 if (strcmp(kn->name, new_name) != 0) { 918 if (strcmp(kn->name, new_name) != 0) {
925 error = -ENOMEM; 919 error = -ENOMEM;
926 new_name = kstrdup(new_name, GFP_KERNEL); 920 new_name = kstrdup(new_name, GFP_KERNEL);
927 if (!new_name) 921 if (!new_name)
928 goto out_unlock; 922 goto out;
929 923
930 if (kn->flags & KERNFS_STATIC_NAME) 924 if (kn->flags & KERNFS_STATIC_NAME)
931 kn->flags &= ~KERNFS_STATIC_NAME; 925 kn->flags &= ~KERNFS_STATIC_NAME;
@@ -947,12 +941,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
947 kernfs_link_sibling(kn); 941 kernfs_link_sibling(kn);
948 942
949 error = 0; 943 error = 0;
950out_unlock: 944 out:
951 mutex_unlock(&kernfs_mutex); 945 mutex_unlock(&kernfs_mutex);
952 kernfs_put_active(kn);
953out_put_new_parent:
954 kernfs_put_active(new_parent);
955out:
956 return error; 946 return error;
957} 947}
958 948
@@ -972,7 +962,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
972 struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) 962 struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
973{ 963{
974 if (pos) { 964 if (pos) {
975 int valid = pos->parent == parent && hash == pos->hash; 965 int valid = !(pos->flags & KERNFS_REMOVED) &&
966 pos->parent == parent && hash == pos->hash;
976 kernfs_put(pos); 967 kernfs_put(pos);
977 if (!valid) 968 if (!valid)
978 pos = NULL; 969 pos = NULL;
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 231a171f48b6..bdd38854ef65 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -856,13 +856,9 @@ 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 rc = -ENOENT; 859 kernfs_addrm_start(&acxt);
860 if (kernfs_get_active(parent)) { 860 rc = kernfs_add_one(&acxt, kn, parent);
861 kernfs_addrm_start(&acxt); 861 kernfs_addrm_finish(&acxt);
862 rc = kernfs_add_one(&acxt, kn, parent);
863 kernfs_addrm_finish(&acxt);
864 kernfs_put_active(parent);
865 }
866 862
867 if (rc) { 863 if (rc) {
868 kernfs_put(kn); 864 kernfs_put(kn);
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 57a93f4d645c..c6ba5bc37a98 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -26,8 +26,7 @@ struct kernfs_iattrs {
26 struct simple_xattrs xattrs; 26 struct simple_xattrs xattrs;
27}; 27};
28 28
29/* +1 to avoid triggering overflow warning when negating it */ 29#define KN_DEACTIVATED_BIAS INT_MIN
30#define KN_DEACTIVATED_BIAS (INT_MIN + 1)
31 30
32/* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ 31/* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */
33 32
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index b2c106ca3434..a03e26036ef9 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -40,13 +40,9 @@ 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 error = -ENOENT; 43 kernfs_addrm_start(&acxt);
44 if (kernfs_get_active(parent)) { 44 error = kernfs_add_one(&acxt, kn, parent);
45 kernfs_addrm_start(&acxt); 45 kernfs_addrm_finish(&acxt);
46 error = kernfs_add_one(&acxt, kn, parent);
47 kernfs_addrm_finish(&acxt);
48 kernfs_put_active(parent);
49 }
50 46
51 if (!error) 47 if (!error)
52 return kn; 48 return kn;