Skip to content

Commit ae34372

Browse files
htejungregkh
authored andcommitted
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>
1 parent a69d001 commit ae34372

File tree

5 files changed

+60
-43
lines changed

5 files changed

+60
-43
lines changed

fs/kernfs/dir.c

+44-35
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ static void kernfs_unlink_sibling(struct kernfs_node *kn)
127127
kn->parent->dir.subdirs--;
128128

129129
rb_erase(&kn->rb, &kn->parent->dir.children);
130+
RB_CLEAR_NODE(&kn->rb);
130131
}
131132

132133
/**
@@ -177,18 +178,16 @@ void kernfs_put_active(struct kernfs_node *kn)
177178
}
178179

179180
/**
180-
* kernfs_deactivate - deactivate kernfs_node
181-
* @kn: kernfs_node to deactivate
181+
* kernfs_drain - drain kernfs_node
182+
* @kn: kernfs_node to drain
182183
*
183-
* Deny new active references and drain existing ones.
184+
* Drain existing usages.
184185
*/
185-
static void kernfs_deactivate(struct kernfs_node *kn)
186+
static void kernfs_drain(struct kernfs_node *kn)
186187
{
187188
struct kernfs_root *root = kernfs_root(kn);
188189

189-
BUG_ON(!(kn->flags & KERNFS_REMOVED));
190-
191-
atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
190+
WARN_ON_ONCE(atomic_read(&kn->active) >= 0);
192191

193192
if (kernfs_lockdep(kn)) {
194193
rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_);
@@ -233,13 +232,15 @@ void kernfs_put(struct kernfs_node *kn)
233232
return;
234233
root = kernfs_root(kn);
235234
repeat:
236-
/* Moving/renaming is always done while holding reference.
235+
/*
236+
* Moving/renaming is always done while holding reference.
237237
* kn->parent won't change beneath us.
238238
*/
239239
parent = kn->parent;
240240

241-
WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n",
242-
parent ? parent->name : "", kn->name);
241+
WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS,
242+
"kernfs_put: %s/%s: released with incorrect active_ref %d\n",
243+
parent ? parent->name : "", kn->name, atomic_read(&kn->active));
243244

244245
if (kernfs_type(kn) == KERNFS_LINK)
245246
kernfs_put(kn->symlink.target_kn);
@@ -281,8 +282,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
281282
kn = dentry->d_fsdata;
282283
mutex_lock(&kernfs_mutex);
283284

284-
/* The kernfs node has been deleted */
285-
if (kn->flags & KERNFS_REMOVED)
285+
/* Force fresh lookup if removed */
286+
if (kn->parent && RB_EMPTY_NODE(&kn->rb))
286287
goto out_bad;
287288

288289
/* The kernfs node has been moved? */
@@ -350,11 +351,12 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name,
350351
kn->ino = ret;
351352

352353
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);
354356

355357
kn->name = name;
356358
kn->mode = mode;
357-
kn->flags = flags | KERNFS_REMOVED;
359+
kn->flags = flags;
358360

359361
return kn;
360362

@@ -413,6 +415,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
413415
struct kernfs_iattrs *ps_iattr;
414416
int ret;
415417

418+
WARN_ON_ONCE(atomic_read(&parent->active) < 0);
419+
416420
if (has_ns != (bool)kn->ns) {
417421
WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n",
418422
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,
422426
if (kernfs_type(parent) != KERNFS_DIR)
423427
return -EINVAL;
424428

425-
if (parent->flags & KERNFS_REMOVED)
426-
return -ENOENT;
427-
428429
kn->hash = kernfs_name_hash(kn->name, kn->ns);
429430
kn->parent = parent;
430431
kernfs_get(parent);
@@ -441,8 +442,7 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn,
441442
}
442443

443444
/* Mark the entry added into directory tree */
444-
kn->flags &= ~KERNFS_REMOVED;
445-
445+
atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
446446
return 0;
447447
}
448448

@@ -470,7 +470,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt,
470470
* Removal can be called multiple times on the same node. Only the
471471
* first invocation is effective and puts the base ref.
472472
*/
473-
if (kn->flags & KERNFS_REMOVED)
473+
if (atomic_read(&kn->active) < 0)
474474
return;
475475

476476
if (kn->parent) {
@@ -484,7 +484,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt,
484484
}
485485
}
486486

487-
kn->flags |= KERNFS_REMOVED;
487+
atomic_add(KN_DEACTIVATED_BIAS, &kn->active);
488488
kn->u.removed_list = acxt->removed;
489489
acxt->removed = kn;
490490
}
@@ -512,7 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt)
512512

513513
acxt->removed = kn->u.removed_list;
514514

515-
kernfs_deactivate(kn);
515+
kernfs_drain(kn);
516516
kernfs_unmap_bin_file(kn);
517517
kernfs_put(kn);
518518
}
@@ -610,7 +610,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
610610
return ERR_PTR(-ENOMEM);
611611
}
612612

613-
kn->flags &= ~KERNFS_REMOVED;
613+
atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
614614
kn->priv = priv;
615615
kn->dir.root = root;
616616

@@ -662,9 +662,13 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
662662
kn->priv = priv;
663663

664664
/* link in */
665-
kernfs_addrm_start(&acxt);
666-
rc = kernfs_add_one(&acxt, kn, parent);
667-
kernfs_addrm_finish(&acxt);
665+
rc = -ENOENT;
666+
if (kernfs_get_active(parent)) {
667+
kernfs_addrm_start(&acxt);
668+
rc = kernfs_add_one(&acxt, kn, parent);
669+
kernfs_addrm_finish(&acxt);
670+
kernfs_put_active(parent);
671+
}
668672

669673
if (!rc)
670674
return kn;
@@ -899,27 +903,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
899903
{
900904
int error;
901905

902-
mutex_lock(&kernfs_mutex);
903-
904906
error = -ENOENT;
905-
if ((kn->flags | new_parent->flags) & KERNFS_REMOVED)
907+
if (!kernfs_get_active(new_parent))
906908
goto out;
909+
if (!kernfs_get_active(kn))
910+
goto out_put_new_parent;
911+
912+
mutex_lock(&kernfs_mutex);
907913

908914
error = 0;
909915
if ((kn->parent == new_parent) && (kn->ns == new_ns) &&
910916
(strcmp(kn->name, new_name) == 0))
911-
goto out; /* nothing to rename */
917+
goto out_unlock; /* nothing to rename */
912918

913919
error = -EEXIST;
914920
if (kernfs_find_ns(new_parent, new_name, new_ns))
915-
goto out;
921+
goto out_unlock;
916922

917923
/* rename kernfs_node */
918924
if (strcmp(kn->name, new_name) != 0) {
919925
error = -ENOMEM;
920926
new_name = kstrdup(new_name, GFP_KERNEL);
921927
if (!new_name)
922-
goto out;
928+
goto out_unlock;
923929

924930
if (kn->flags & KERNFS_STATIC_NAME)
925931
kn->flags &= ~KERNFS_STATIC_NAME;
@@ -941,8 +947,12 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
941947
kernfs_link_sibling(kn);
942948

943949
error = 0;
944-
out:
950+
out_unlock:
945951
mutex_unlock(&kernfs_mutex);
952+
kernfs_put_active(kn);
953+
out_put_new_parent:
954+
kernfs_put_active(new_parent);
955+
out:
946956
return error;
947957
}
948958

@@ -962,8 +972,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,
962972
struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos)
963973
{
964974
if (pos) {
965-
int valid = !(pos->flags & KERNFS_REMOVED) &&
966-
pos->parent == parent && hash == pos->hash;
975+
int valid = pos->parent == parent && hash == pos->hash;
967976
kernfs_put(pos);
968977
if (!valid)
969978
pos = NULL;

fs/kernfs/file.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -856,9 +856,13 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
856856
if (ops->mmap)
857857
kn->flags |= KERNFS_HAS_MMAP;
858858

859-
kernfs_addrm_start(&acxt);
860-
rc = kernfs_add_one(&acxt, kn, parent);
861-
kernfs_addrm_finish(&acxt);
859+
rc = -ENOENT;
860+
if (kernfs_get_active(parent)) {
861+
kernfs_addrm_start(&acxt);
862+
rc = kernfs_add_one(&acxt, kn, parent);
863+
kernfs_addrm_finish(&acxt);
864+
kernfs_put_active(parent);
865+
}
862866

863867
if (rc) {
864868
kernfs_put(kn);

fs/kernfs/kernfs-internal.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ struct kernfs_iattrs {
2626
struct simple_xattrs xattrs;
2727
};
2828

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)
3031

3132
/* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */
3233

fs/kernfs/symlink.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
4040
kn->symlink.target_kn = target;
4141
kernfs_get(target); /* ref owned by symlink */
4242

43-
kernfs_addrm_start(&acxt);
44-
error = kernfs_add_one(&acxt, kn, parent);
45-
kernfs_addrm_finish(&acxt);
43+
error = -ENOENT;
44+
if (kernfs_get_active(parent)) {
45+
kernfs_addrm_start(&acxt);
46+
error = kernfs_add_one(&acxt, kn, parent);
47+
kernfs_addrm_finish(&acxt);
48+
kernfs_put_active(parent);
49+
}
4650

4751
if (!error)
4852
return kn;

include/linux/kernfs.h

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ enum kernfs_node_type {
3737
#define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK
3838

3939
enum kernfs_node_flag {
40-
KERNFS_REMOVED = 0x0010,
4140
KERNFS_NS = 0x0020,
4241
KERNFS_HAS_SEQ_SHOW = 0x0040,
4342
KERNFS_HAS_MMAP = 0x0080,

0 commit comments

Comments
 (0)