diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-13 23:36:03 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-13 23:36:03 +0100 |
commit | 798c75a0d44cdbd6e3d82a6a676e6de38525b3bb (patch) | |
tree | 358f5deccf0f6b6afa833d0264f2e3aefcca02ce /fs/kernfs/dir.c | |
parent | Revert "kernfs: restructure removal path to fix possible premature return" (diff) | |
download | linux-798c75a0d44cdbd6e3d82a6a676e6de38525b3bb.tar.xz linux-798c75a0d44cdbd6e3d82a6a676e6de38525b3bb.zip |
Revert "kernfs: remove KERNFS_REMOVED"
This reverts commit ae34372eb8408b3d07e870f1939f99007a730d28.
Tejun writes:
I'm sorry but can you please revert the whole series?
get_active() waiting while a node is deactivated has potential
to lead to deadlock and that deactivate/reactivate interface is
something fundamentally flawed and that cgroup will have to work
with the remove_self() like everybody else. IOW, I think the
first posting was correct.
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs/dir.c')
-rw-r--r-- | fs/kernfs/dir.c | 79 |
1 files changed, 35 insertions, 44 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) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); - RB_CLEAR_NODE(&kn->rb); } /** @@ -178,16 +177,18 @@ void kernfs_put_active(struct kernfs_node *kn) } /** - * kernfs_drain - drain kernfs_node - * @kn: kernfs_node to drain + * kernfs_deactivate - deactivate kernfs_node + * @kn: kernfs_node to deactivate * - * Drain existing usages. + * Deny new active references and drain existing ones. */ -static void kernfs_drain(struct kernfs_node *kn) +static void kernfs_deactivate(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); - WARN_ON_ONCE(atomic_read(&kn->active) >= 0); + BUG_ON(!(kn->flags & KERNFS_REMOVED)); + + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); @@ -232,15 +233,13 @@ void kernfs_put(struct kernfs_node *kn) return; root = kernfs_root(kn); repeat: - /* - * Moving/renaming is always done while holding reference. + /* Moving/renaming is always done while holding reference. * kn->parent won't change beneath us. */ parent = kn->parent; - WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, - "kernfs_put: %s/%s: released with incorrect active_ref %d\n", - parent ? parent->name : "", kn->name, atomic_read(&kn->active)); + WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", + parent ? parent->name : "", kn->name); if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); @@ -282,8 +281,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) kn = dentry->d_fsdata; mutex_lock(&kernfs_mutex); - /* Force fresh lookup if removed */ - if (kn->parent && RB_EMPTY_NODE(&kn->rb)) + /* The kernfs node has been deleted */ + if (kn->flags & KERNFS_REMOVED) goto out_bad; /* The kernfs node has been moved? */ @@ -351,12 +350,11 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, kn->ino = ret; atomic_set(&kn->count, 1); - atomic_set(&kn->active, KN_DEACTIVATED_BIAS); - RB_CLEAR_NODE(&kn->rb); + atomic_set(&kn->active, 0); kn->name = name; kn->mode = mode; - kn->flags = flags; + kn->flags = flags | KERNFS_REMOVED; return kn; @@ -415,8 +413,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_iattrs *ps_iattr; int ret; - WARN_ON_ONCE(atomic_read(&parent->active) < 0); - if (has_ns != (bool)kn->ns) { WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", 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, if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; + if (parent->flags & KERNFS_REMOVED) + return -ENOENT; + kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; kernfs_get(parent); @@ -442,7 +441,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /* Mark the entry added into directory tree */ - atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags &= ~KERNFS_REMOVED; + return 0; } @@ -470,7 +470,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (atomic_read(&kn->active) < 0) + if (kn->flags & KERNFS_REMOVED) return; if (kn->parent) { @@ -484,7 +484,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, } } - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags |= KERNFS_REMOVED; kn->u.removed_list = acxt->removed; acxt->removed = kn; } @@ -512,7 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - kernfs_drain(kn); + kernfs_deactivate(kn); kernfs_unmap_bin_file(kn); kernfs_put(kn); } @@ -610,7 +610,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) return ERR_PTR(-ENOMEM); } - atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags &= ~KERNFS_REMOVED; kn->priv = priv; kn->dir.root = root; @@ -662,13 +662,9 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - rc = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!rc) return kn; @@ -903,29 +899,27 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, { int error; + mutex_lock(&kernfs_mutex); + error = -ENOENT; - if (!kernfs_get_active(new_parent)) + if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) goto out; - if (!kernfs_get_active(kn)) - goto out_put_new_parent; - - mutex_lock(&kernfs_mutex); error = 0; if ((kn->parent == new_parent) && (kn->ns == new_ns) && (strcmp(kn->name, new_name) == 0)) - goto out_unlock; /* nothing to rename */ + goto out; /* nothing to rename */ error = -EEXIST; if (kernfs_find_ns(new_parent, new_name, new_ns)) - goto out_unlock; + goto out; /* rename kernfs_node */ if (strcmp(kn->name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) - goto out_unlock; + goto out; if (kn->flags & KERNFS_STATIC_NAME) kn->flags &= ~KERNFS_STATIC_NAME; @@ -947,12 +941,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, kernfs_link_sibling(kn); error = 0; -out_unlock: + out: mutex_unlock(&kernfs_mutex); - kernfs_put_active(kn); -out_put_new_parent: - kernfs_put_active(new_parent); -out: return error; } @@ -972,7 +962,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { - int valid = pos->parent == parent && hash == pos->hash; + int valid = !(pos->flags & KERNFS_REMOVED) && + pos->parent == parent && hash == pos->hash; kernfs_put(pos); if (!valid) pos = NULL; |