diff options
Diffstat (limited to 'fs/kernfs')
-rw-r--r-- | fs/kernfs/dir.c | 79 | ||||
-rw-r--r-- | fs/kernfs/file.c | 10 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 3 | ||||
-rw-r--r-- | fs/kernfs/symlink.c | 10 |
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 | */ |
186 | static void kernfs_drain(struct kernfs_node *kn) | 185 | static 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; |
950 | out_unlock: | 944 | out: |
951 | mutex_unlock(&kernfs_mutex); | 945 | mutex_unlock(&kernfs_mutex); |
952 | kernfs_put_active(kn); | ||
953 | out_put_new_parent: | ||
954 | kernfs_put_active(new_parent); | ||
955 | out: | ||
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; |