From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 22:36:03 +0000 (-0800) Subject: Revert "kernfs: remove KERNFS_REMOVED" X-Git-Url: http://pileus.org/git/?a=commitdiff_plain;ds=sidebyside;h=798c75a0d44cdbd6e3d82a6a676e6de38525b3bb;p=~andy%2Flinux 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 Signed-off-by: Greg Kroah-Hartman --- diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 7f8afc1d08f..1c9130a3304 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; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 231a171f48b..bdd38854ef6 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, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - 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) { kernfs_put(kn); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 57a93f4d645..c6ba5bc37a9 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -26,8 +26,7 @@ struct kernfs_iattrs { struct simple_xattrs xattrs; }; -/* +1 to avoid triggering overflow warning when negating it */ -#define KN_DEACTIVATED_BIAS (INT_MIN + 1) +#define KN_DEACTIVATED_BIAS INT_MIN /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index b2c106ca343..a03e26036ef 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, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - error = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - error = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } + kernfs_addrm_start(&acxt); + error = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!error) return kn; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 289d4f639ad..42ad32ff22f 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,6 +37,7 @@ enum kernfs_node_type { #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { + KERNFS_REMOVED = 0x0010, KERNFS_NS = 0x0020, KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080,