From: Greg Kroah-Hartman Date: Mon, 11 Nov 2019 08:47:56 +0000 (+0100) Subject: 4.14-stable patches X-Git-Tag: v4.4.201~35 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b897d14c7e4470f8b29b7d1790afde4eedb7bbc6;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: configfs-fix-a-deadlock-in-configfs_symlink.patch configfs-fix-bool-initialization-comparison.patch configfs-new-object-reprsenting-tree-fragments.patch configfs-provide-exclusion-between-io-and-removals.patch configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch --- diff --git a/queue-4.14/configfs-fix-a-deadlock-in-configfs_symlink.patch b/queue-4.14/configfs-fix-a-deadlock-in-configfs_symlink.patch new file mode 100644 index 00000000000..b483f3dd827 --- /dev/null +++ b/queue-4.14/configfs-fix-a-deadlock-in-configfs_symlink.patch @@ -0,0 +1,81 @@ +From 351e5d869e5ac10cb40c78b5f2d7dfc816ad4587 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Sat, 3 Aug 2019 11:51:18 -0400 +Subject: configfs: fix a deadlock in configfs_symlink() + +From: Al Viro + +commit 351e5d869e5ac10cb40c78b5f2d7dfc816ad4587 upstream. + +Configfs abuses symlink(2). Unlike the normal filesystems, it +wants the target resolved at symlink(2) time, like link(2) would've +done. The problem is that ->symlink() is called with the parent +directory locked exclusive, so resolving the target inside the +->symlink() is easily deadlocked. + +Short of really ugly games in sys_symlink() itself, all we can +do is to unlock the parent before resolving the target and +relock it after. However, that invalidates the checks done +by the caller of ->symlink(), so we have to + * check that dentry is still where it used to be +(it couldn't have been moved, but it could've been unhashed) + * recheck that it's still negative (somebody else +might've successfully created a symlink with the same name +while we were looking the target up) + * recheck the permissions on the parent directory. + +Cc: stable@vger.kernel.org +Signed-off-by: Al Viro +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/symlink.c | 33 ++++++++++++++++++++++++++++++++- + 1 file changed, 32 insertions(+), 1 deletion(-) + +--- a/fs/configfs/symlink.c ++++ b/fs/configfs/symlink.c +@@ -157,11 +157,42 @@ int configfs_symlink(struct inode *dir, + !type->ct_item_ops->allow_link) + goto out_put; + ++ /* ++ * This is really sick. What they wanted was a hybrid of ++ * link(2) and symlink(2) - they wanted the target resolved ++ * at syscall time (as link(2) would've done), be a directory ++ * (which link(2) would've refused to do) *AND* be a deep ++ * fucking magic, making the target busy from rmdir POV. ++ * symlink(2) is nothing of that sort, and the locking it ++ * gets matches the normal symlink(2) semantics. Without ++ * attempts to resolve the target (which might very well ++ * not even exist yet) done prior to locking the parent ++ * directory. This perversion, OTOH, needs to resolve ++ * the target, which would lead to obvious deadlocks if ++ * attempted with any directories locked. ++ * ++ * Unfortunately, that garbage is userland ABI and we should've ++ * said "no" back in 2005. Too late now, so we get to ++ * play very ugly games with locking. ++ * ++ * Try *ANYTHING* of that sort in new code, and you will ++ * really regret it. Just ask yourself - what could a BOFH ++ * do to me and do I want to find it out first-hand? ++ * ++ * AV, a thoroughly annoyed bastard. ++ */ ++ inode_unlock(dir); + ret = get_target(symname, &path, &target_item, dentry->d_sb); ++ inode_lock(dir); + if (ret) + goto out_put; + +- ret = type->ct_item_ops->allow_link(parent_item, target_item); ++ if (dentry->d_inode || d_unhashed(dentry)) ++ ret = -EEXIST; ++ else ++ ret = inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ if (!ret) ++ ret = type->ct_item_ops->allow_link(parent_item, target_item); + if (!ret) { + mutex_lock(&configfs_symlink_mutex); + ret = create_link(parent_item, target_item, dentry); diff --git a/queue-4.14/configfs-fix-bool-initialization-comparison.patch b/queue-4.14/configfs-fix-bool-initialization-comparison.patch new file mode 100644 index 00000000000..665374e33fb --- /dev/null +++ b/queue-4.14/configfs-fix-bool-initialization-comparison.patch @@ -0,0 +1,64 @@ +From 3f6928c347707a65cee10a9f54b85ad5fb078b3f Mon Sep 17 00:00:00 2001 +From: Thomas Meyer +Date: Sat, 7 Oct 2017 16:02:21 +0200 +Subject: configfs: Fix bool initialization/comparison + +From: Thomas Meyer + +commit 3f6928c347707a65cee10a9f54b85ad5fb078b3f upstream. + +Bool initializations should use true and false. Bool tests don't need +comparisons. + +Signed-off-by: Thomas Meyer +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/file.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/fs/configfs/file.c ++++ b/fs/configfs/file.c +@@ -166,7 +166,7 @@ configfs_read_bin_file(struct file *file + retval = -ETXTBSY; + goto out; + } +- buffer->read_in_progress = 1; ++ buffer->read_in_progress = true; + + if (buffer->needs_read_fill) { + /* perform first read with buf == NULL to get extent */ +@@ -325,7 +325,7 @@ configfs_write_bin_file(struct file *fil + len = -ETXTBSY; + goto out; + } +- buffer->write_in_progress = 1; ++ buffer->write_in_progress = true; + + /* buffer grows? */ + if (*ppos + count > buffer->bin_buffer_size) { +@@ -429,8 +429,8 @@ static int check_perm(struct inode * ino + } + mutex_init(&buffer->mutex); + buffer->needs_read_fill = 1; +- buffer->read_in_progress = 0; +- buffer->write_in_progress = 0; ++ buffer->read_in_progress = false; ++ buffer->write_in_progress = false; + buffer->ops = ops; + file->private_data = buffer; + goto Done; +@@ -488,10 +488,10 @@ static int configfs_release_bin_file(str + ssize_t len = 0; + int ret; + +- buffer->read_in_progress = 0; ++ buffer->read_in_progress = false; + + if (buffer->write_in_progress) { +- buffer->write_in_progress = 0; ++ buffer->write_in_progress = false; + + len = bin_attr->write(item, buffer->bin_buffer, + buffer->bin_buffer_size); diff --git a/queue-4.14/configfs-new-object-reprsenting-tree-fragments.patch b/queue-4.14/configfs-new-object-reprsenting-tree-fragments.patch new file mode 100644 index 00000000000..066e51fe48e --- /dev/null +++ b/queue-4.14/configfs-new-object-reprsenting-tree-fragments.patch @@ -0,0 +1,396 @@ +From 47320fbe11a6059ae502c9c16b668022fdb4cf76 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Sun, 25 Aug 2019 19:56:13 -0400 +Subject: configfs: new object reprsenting tree fragments + +From: Al Viro + +commit 47320fbe11a6059ae502c9c16b668022fdb4cf76 upstream. + +Refcounted, hangs of configfs_dirent, created by operations that add +fragments to configfs tree (mkdir and configfs_register_{subsystem,group}). +Will be used in the next commit to provide exclusion between fragment +removal and ->show/->store calls. + +Signed-off-by: Al Viro +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/configfs_internal.h | 15 ++++- + fs/configfs/dir.c | 105 +++++++++++++++++++++++++++++++--------- + fs/configfs/file.c | 4 - + 3 files changed, 97 insertions(+), 27 deletions(-) + +--- a/fs/configfs/configfs_internal.h ++++ b/fs/configfs/configfs_internal.h +@@ -34,6 +34,15 @@ + #include + #include + ++struct configfs_fragment { ++ atomic_t frag_count; ++ struct rw_semaphore frag_sem; ++ bool frag_dead; ++}; ++ ++void put_fragment(struct configfs_fragment *); ++struct configfs_fragment *get_fragment(struct configfs_fragment *); ++ + struct configfs_dirent { + atomic_t s_count; + int s_dependent_count; +@@ -48,6 +57,7 @@ struct configfs_dirent { + #ifdef CONFIG_LOCKDEP + int s_depth; + #endif ++ struct configfs_fragment *s_frag; + }; + + #define CONFIGFS_ROOT 0x0001 +@@ -75,8 +85,8 @@ extern int configfs_create(struct dentry + extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); + extern int configfs_create_bin_file(struct config_item *, + const struct configfs_bin_attribute *); +-extern int configfs_make_dirent(struct configfs_dirent *, +- struct dentry *, void *, umode_t, int); ++extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *, ++ void *, umode_t, int, struct configfs_fragment *); + extern int configfs_dirent_is_ready(struct configfs_dirent *); + + extern void configfs_hash_and_remove(struct dentry * dir, const char * name); +@@ -151,6 +161,7 @@ static inline void release_configfs_dire + { + if (!(sd->s_type & CONFIGFS_ROOT)) { + kfree(sd->s_iattr); ++ put_fragment(sd->s_frag); + kmem_cache_free(configfs_dir_cachep, sd); + } + } +--- a/fs/configfs/dir.c ++++ b/fs/configfs/dir.c +@@ -164,11 +164,38 @@ configfs_adjust_dir_dirent_depth_after_p + + #endif /* CONFIG_LOCKDEP */ + ++static struct configfs_fragment *new_fragment(void) ++{ ++ struct configfs_fragment *p; ++ ++ p = kmalloc(sizeof(struct configfs_fragment), GFP_KERNEL); ++ if (p) { ++ atomic_set(&p->frag_count, 1); ++ init_rwsem(&p->frag_sem); ++ p->frag_dead = false; ++ } ++ return p; ++} ++ ++void put_fragment(struct configfs_fragment *frag) ++{ ++ if (frag && atomic_dec_and_test(&frag->frag_count)) ++ kfree(frag); ++} ++ ++struct configfs_fragment *get_fragment(struct configfs_fragment *frag) ++{ ++ if (likely(frag)) ++ atomic_inc(&frag->frag_count); ++ return frag; ++} ++ + /* + * Allocates a new configfs_dirent and links it to the parent configfs_dirent + */ + static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd, +- void *element, int type) ++ void *element, int type, ++ struct configfs_fragment *frag) + { + struct configfs_dirent * sd; + +@@ -188,6 +215,7 @@ static struct configfs_dirent *configfs_ + kmem_cache_free(configfs_dir_cachep, sd); + return ERR_PTR(-ENOENT); + } ++ sd->s_frag = get_fragment(frag); + list_add(&sd->s_sibling, &parent_sd->s_children); + spin_unlock(&configfs_dirent_lock); + +@@ -222,11 +250,11 @@ static int configfs_dirent_exists(struct + + int configfs_make_dirent(struct configfs_dirent * parent_sd, + struct dentry * dentry, void * element, +- umode_t mode, int type) ++ umode_t mode, int type, struct configfs_fragment *frag) + { + struct configfs_dirent * sd; + +- sd = configfs_new_dirent(parent_sd, element, type); ++ sd = configfs_new_dirent(parent_sd, element, type, frag); + if (IS_ERR(sd)) + return PTR_ERR(sd); + +@@ -273,7 +301,8 @@ static void init_symlink(struct inode * + * until it is validated by configfs_dir_set_ready() + */ + +-static int configfs_create_dir(struct config_item *item, struct dentry *dentry) ++static int configfs_create_dir(struct config_item *item, struct dentry *dentry, ++ struct configfs_fragment *frag) + { + int error; + umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; +@@ -286,7 +315,8 @@ static int configfs_create_dir(struct co + return error; + + error = configfs_make_dirent(p->d_fsdata, dentry, item, mode, +- CONFIGFS_DIR | CONFIGFS_USET_CREATING); ++ CONFIGFS_DIR | CONFIGFS_USET_CREATING, ++ frag); + if (unlikely(error)) + return error; + +@@ -351,9 +381,10 @@ int configfs_create_link(struct configfs + { + int err = 0; + umode_t mode = S_IFLNK | S_IRWXUGO; ++ struct configfs_dirent *p = parent->d_fsdata; + +- err = configfs_make_dirent(parent->d_fsdata, dentry, sl, mode, +- CONFIGFS_ITEM_LINK); ++ err = configfs_make_dirent(p, dentry, sl, mode, ++ CONFIGFS_ITEM_LINK, p->s_frag); + if (!err) { + err = configfs_create(dentry, mode, init_symlink); + if (err) { +@@ -612,7 +643,8 @@ static int populate_attrs(struct config_ + + static int configfs_attach_group(struct config_item *parent_item, + struct config_item *item, +- struct dentry *dentry); ++ struct dentry *dentry, ++ struct configfs_fragment *frag); + static void configfs_detach_group(struct config_item *item); + + static void detach_groups(struct config_group *group) +@@ -660,7 +692,8 @@ static void detach_groups(struct config_ + * try using vfs_mkdir. Just a thought. + */ + static int create_default_group(struct config_group *parent_group, +- struct config_group *group) ++ struct config_group *group, ++ struct configfs_fragment *frag) + { + int ret; + struct configfs_dirent *sd; +@@ -676,7 +709,7 @@ static int create_default_group(struct c + d_add(child, NULL); + + ret = configfs_attach_group(&parent_group->cg_item, +- &group->cg_item, child); ++ &group->cg_item, child, frag); + if (!ret) { + sd = child->d_fsdata; + sd->s_type |= CONFIGFS_USET_DEFAULT; +@@ -690,13 +723,14 @@ static int create_default_group(struct c + return ret; + } + +-static int populate_groups(struct config_group *group) ++static int populate_groups(struct config_group *group, ++ struct configfs_fragment *frag) + { + struct config_group *new_group; + int ret = 0; + + list_for_each_entry(new_group, &group->default_groups, group_entry) { +- ret = create_default_group(group, new_group); ++ ret = create_default_group(group, new_group, frag); + if (ret) { + detach_groups(group); + break; +@@ -810,11 +844,12 @@ static void link_group(struct config_gro + */ + static int configfs_attach_item(struct config_item *parent_item, + struct config_item *item, +- struct dentry *dentry) ++ struct dentry *dentry, ++ struct configfs_fragment *frag) + { + int ret; + +- ret = configfs_create_dir(item, dentry); ++ ret = configfs_create_dir(item, dentry, frag); + if (!ret) { + ret = populate_attrs(item); + if (ret) { +@@ -844,12 +879,13 @@ static void configfs_detach_item(struct + + static int configfs_attach_group(struct config_item *parent_item, + struct config_item *item, +- struct dentry *dentry) ++ struct dentry *dentry, ++ struct configfs_fragment *frag) + { + int ret; + struct configfs_dirent *sd; + +- ret = configfs_attach_item(parent_item, item, dentry); ++ ret = configfs_attach_item(parent_item, item, dentry, frag); + if (!ret) { + sd = dentry->d_fsdata; + sd->s_type |= CONFIGFS_USET_DIR; +@@ -865,7 +901,7 @@ static int configfs_attach_group(struct + */ + inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD); + configfs_adjust_dir_dirent_depth_before_populate(sd); +- ret = populate_groups(to_config_group(item)); ++ ret = populate_groups(to_config_group(item), frag); + if (ret) { + configfs_detach_item(item); + d_inode(dentry)->i_flags |= S_DEAD; +@@ -1260,6 +1296,7 @@ static int configfs_mkdir(struct inode * + struct configfs_dirent *sd; + struct config_item_type *type; + struct module *subsys_owner = NULL, *new_item_owner = NULL; ++ struct configfs_fragment *frag; + char *name; + + sd = dentry->d_parent->d_fsdata; +@@ -1278,6 +1315,12 @@ static int configfs_mkdir(struct inode * + goto out; + } + ++ frag = new_fragment(); ++ if (!frag) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ + /* Get a working ref for the duration of this function */ + parent_item = configfs_get_config_item(dentry->d_parent); + type = parent_item->ci_type; +@@ -1380,9 +1423,9 @@ static int configfs_mkdir(struct inode * + spin_unlock(&configfs_dirent_lock); + + if (group) +- ret = configfs_attach_group(parent_item, item, dentry); ++ ret = configfs_attach_group(parent_item, item, dentry, frag); + else +- ret = configfs_attach_item(parent_item, item, dentry); ++ ret = configfs_attach_item(parent_item, item, dentry, frag); + + spin_lock(&configfs_dirent_lock); + sd->s_type &= ~CONFIGFS_USET_IN_MKDIR; +@@ -1419,6 +1462,7 @@ out_put: + * reference. + */ + config_item_put(parent_item); ++ put_fragment(frag); + + out: + return ret; +@@ -1587,7 +1631,7 @@ static int configfs_dir_open(struct inod + */ + err = -ENOENT; + if (configfs_dirent_is_ready(parent_sd)) { +- file->private_data = configfs_new_dirent(parent_sd, NULL, 0); ++ file->private_data = configfs_new_dirent(parent_sd, NULL, 0, NULL); + if (IS_ERR(file->private_data)) + err = PTR_ERR(file->private_data); + else +@@ -1743,8 +1787,13 @@ int configfs_register_group(struct confi + { + struct configfs_subsystem *subsys = parent_group->cg_subsys; + struct dentry *parent; ++ struct configfs_fragment *frag; + int ret; + ++ frag = new_fragment(); ++ if (!frag) ++ return -ENOMEM; ++ + mutex_lock(&subsys->su_mutex); + link_group(parent_group, group); + mutex_unlock(&subsys->su_mutex); +@@ -1752,7 +1801,7 @@ int configfs_register_group(struct confi + parent = parent_group->cg_item.ci_dentry; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); +- ret = create_default_group(parent_group, group); ++ ret = create_default_group(parent_group, group, frag); + if (ret) + goto err_out; + +@@ -1760,12 +1809,14 @@ int configfs_register_group(struct confi + configfs_dir_set_ready(group->cg_item.ci_dentry->d_fsdata); + spin_unlock(&configfs_dirent_lock); + inode_unlock(d_inode(parent)); ++ put_fragment(frag); + return 0; + err_out: + inode_unlock(d_inode(parent)); + mutex_lock(&subsys->su_mutex); + unlink_group(group); + mutex_unlock(&subsys->su_mutex); ++ put_fragment(frag); + return ret; + } + EXPORT_SYMBOL(configfs_register_group); +@@ -1852,10 +1903,17 @@ int configfs_register_subsystem(struct c + struct dentry *dentry; + struct dentry *root; + struct configfs_dirent *sd; ++ struct configfs_fragment *frag; ++ ++ frag = new_fragment(); ++ if (!frag) ++ return -ENOMEM; + + root = configfs_pin_fs(); +- if (IS_ERR(root)) ++ if (IS_ERR(root)) { ++ put_fragment(frag); + return PTR_ERR(root); ++ } + + if (!group->cg_item.ci_name) + group->cg_item.ci_name = group->cg_item.ci_namebuf; +@@ -1871,7 +1929,7 @@ int configfs_register_subsystem(struct c + d_add(dentry, NULL); + + err = configfs_attach_group(sd->s_element, &group->cg_item, +- dentry); ++ dentry, frag); + if (err) { + BUG_ON(d_inode(dentry)); + d_drop(dentry); +@@ -1889,6 +1947,7 @@ int configfs_register_subsystem(struct c + unlink_group(group); + configfs_release_fs(); + } ++ put_fragment(frag); + + return err; + } +--- a/fs/configfs/file.c ++++ b/fs/configfs/file.c +@@ -502,7 +502,7 @@ int configfs_create_file(struct config_i + + inode_lock_nested(d_inode(dir), I_MUTEX_NORMAL); + error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, +- CONFIGFS_ITEM_ATTR); ++ CONFIGFS_ITEM_ATTR, parent_sd->s_frag); + inode_unlock(d_inode(dir)); + + return error; +@@ -524,7 +524,7 @@ int configfs_create_bin_file(struct conf + + inode_lock_nested(dir->d_inode, I_MUTEX_NORMAL); + error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, +- CONFIGFS_ITEM_BIN_ATTR); ++ CONFIGFS_ITEM_BIN_ATTR, parent_sd->s_frag); + inode_unlock(dir->d_inode); + + return error; diff --git a/queue-4.14/configfs-provide-exclusion-between-io-and-removals.patch b/queue-4.14/configfs-provide-exclusion-between-io-and-removals.patch new file mode 100644 index 00000000000..fbab044835a --- /dev/null +++ b/queue-4.14/configfs-provide-exclusion-between-io-and-removals.patch @@ -0,0 +1,277 @@ +From b0841eefd9693827afb9888235e26ddd098f9cef Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Sat, 31 Aug 2019 09:43:43 +0200 +Subject: configfs: provide exclusion between IO and removals + +From: Al Viro + +commit b0841eefd9693827afb9888235e26ddd098f9cef upstream. + +Make sure that attribute methods are not called after the item +has been removed from the tree. To do so, we + * at the point of no return in removals, grab ->frag_sem +exclusive and mark the fragment dead. + * call the methods of attributes with ->frag_sem taken +shared and only after having verified that the fragment is still +alive. + + The main benefit is for method instances - they are +guaranteed that the objects they are accessing *and* all ancestors +are still there. Another win is that we don't need to bother +with extra refcount on config_item when opening a file - +the item will be alive for as long as it stays in the tree, and +we won't touch it/attributes/any associated data after it's +been removed from the tree. + +Signed-off-by: Al Viro +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/dir.c | 23 ++++++++++++++++ + fs/configfs/file.c | 75 ++++++++++++++++++++++++++++++++++++++++------------- + 2 files changed, 80 insertions(+), 18 deletions(-) + +--- a/fs/configfs/dir.c ++++ b/fs/configfs/dir.c +@@ -1474,6 +1474,7 @@ static int configfs_rmdir(struct inode * + struct config_item *item; + struct configfs_subsystem *subsys; + struct configfs_dirent *sd; ++ struct configfs_fragment *frag; + struct module *subsys_owner = NULL, *dead_item_owner = NULL; + int ret; + +@@ -1531,6 +1532,16 @@ static int configfs_rmdir(struct inode * + } + } while (ret == -EAGAIN); + ++ frag = sd->s_frag; ++ if (down_write_killable(&frag->frag_sem)) { ++ spin_lock(&configfs_dirent_lock); ++ configfs_detach_rollback(dentry); ++ spin_unlock(&configfs_dirent_lock); ++ return -EINTR; ++ } ++ frag->frag_dead = true; ++ up_write(&frag->frag_sem); ++ + /* Get a working ref for the duration of this function */ + item = configfs_get_config_item(dentry); + +@@ -1832,6 +1843,12 @@ void configfs_unregister_group(struct co + struct configfs_subsystem *subsys = group->cg_subsys; + struct dentry *dentry = group->cg_item.ci_dentry; + struct dentry *parent = group->cg_item.ci_parent->ci_dentry; ++ struct configfs_dirent *sd = dentry->d_fsdata; ++ struct configfs_fragment *frag = sd->s_frag; ++ ++ down_write(&frag->frag_sem); ++ frag->frag_dead = true; ++ up_write(&frag->frag_sem); + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + spin_lock(&configfs_dirent_lock); +@@ -1957,12 +1974,18 @@ void configfs_unregister_subsystem(struc + struct config_group *group = &subsys->su_group; + struct dentry *dentry = group->cg_item.ci_dentry; + struct dentry *root = dentry->d_sb->s_root; ++ struct configfs_dirent *sd = dentry->d_fsdata; ++ struct configfs_fragment *frag = sd->s_frag; + + if (dentry->d_parent != root) { + pr_err("Tried to unregister non-subsystem!\n"); + return; + } + ++ down_write(&frag->frag_sem); ++ frag->frag_dead = true; ++ up_write(&frag->frag_sem); ++ + inode_lock_nested(d_inode(root), + I_MUTEX_PARENT); + inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD); +--- a/fs/configfs/file.c ++++ b/fs/configfs/file.c +@@ -62,22 +62,32 @@ struct configfs_buffer { + }; + }; + ++static inline struct configfs_fragment *to_frag(struct file *file) ++{ ++ struct configfs_dirent *sd = file->f_path.dentry->d_fsdata; ++ ++ return sd->s_frag; ++} + +-static int fill_read_buffer(struct configfs_buffer * buffer) ++static int fill_read_buffer(struct file *file, struct configfs_buffer *buffer) + { +- ssize_t count; ++ struct configfs_fragment *frag = to_frag(file); ++ ssize_t count = -ENOENT; + + if (!buffer->page) + buffer->page = (char *) get_zeroed_page(GFP_KERNEL); + if (!buffer->page) + return -ENOMEM; + +- count = buffer->attr->show(buffer->item, buffer->page); ++ down_read(&frag->frag_sem); ++ if (!frag->frag_dead) ++ count = buffer->attr->show(buffer->item, buffer->page); ++ up_read(&frag->frag_sem); ++ + if (count < 0) + return count; + if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE)) + return -EIO; +- + buffer->needs_read_fill = 0; + buffer->count = count; + return 0; +@@ -110,7 +120,7 @@ configfs_read_file(struct file *file, ch + + mutex_lock(&buffer->mutex); + if (buffer->needs_read_fill) { +- retval = fill_read_buffer(buffer); ++ retval = fill_read_buffer(file, buffer); + if (retval) + goto out; + } +@@ -147,6 +157,7 @@ static ssize_t + configfs_read_bin_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) + { ++ struct configfs_fragment *frag = to_frag(file); + struct configfs_buffer *buffer = file->private_data; + ssize_t retval = 0; + ssize_t len = min_t(size_t, count, PAGE_SIZE); +@@ -162,7 +173,12 @@ configfs_read_bin_file(struct file *file + + if (buffer->needs_read_fill) { + /* perform first read with buf == NULL to get extent */ +- len = buffer->bin_attr->read(buffer->item, NULL, 0); ++ down_read(&frag->frag_sem); ++ if (!frag->frag_dead) ++ len = buffer->bin_attr->read(buffer->item, NULL, 0); ++ else ++ len = -ENOENT; ++ up_read(&frag->frag_sem); + if (len <= 0) { + retval = len; + goto out; +@@ -182,8 +198,13 @@ configfs_read_bin_file(struct file *file + buffer->bin_buffer_size = len; + + /* perform second read to fill buffer */ +- len = buffer->bin_attr->read(buffer->item, +- buffer->bin_buffer, len); ++ down_read(&frag->frag_sem); ++ if (!frag->frag_dead) ++ len = buffer->bin_attr->read(buffer->item, ++ buffer->bin_buffer, len); ++ else ++ len = -ENOENT; ++ up_read(&frag->frag_sem); + if (len < 0) { + retval = len; + vfree(buffer->bin_buffer); +@@ -234,9 +255,16 @@ fill_write_buffer(struct configfs_buffer + } + + static int +-flush_write_buffer(struct configfs_buffer *buffer, size_t count) ++flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count) + { +- return buffer->attr->store(buffer->item, buffer->page, count); ++ struct configfs_fragment *frag = to_frag(file); ++ int res = -ENOENT; ++ ++ down_read(&frag->frag_sem); ++ if (!frag->frag_dead) ++ res = buffer->attr->store(buffer->item, buffer->page, count); ++ up_read(&frag->frag_sem); ++ return res; + } + + +@@ -266,7 +294,7 @@ configfs_write_file(struct file *file, c + mutex_lock(&buffer->mutex); + len = fill_write_buffer(buffer, buf, count); + if (len > 0) +- len = flush_write_buffer(buffer, len); ++ len = flush_write_buffer(file, buffer, len); + if (len > 0) + *ppos += len; + mutex_unlock(&buffer->mutex); +@@ -342,6 +370,7 @@ out: + static int __configfs_open_file(struct inode *inode, struct file *file, int type) + { + struct dentry *dentry = file->f_path.dentry; ++ struct configfs_fragment *frag = to_frag(file); + struct configfs_attribute *attr; + struct configfs_buffer *buffer; + int error; +@@ -351,8 +380,13 @@ static int __configfs_open_file(struct i + if (!buffer) + goto out; + ++ error = -ENOENT; ++ down_read(&frag->frag_sem); ++ if (unlikely(frag->frag_dead)) ++ goto out_free_buffer; ++ + error = -EINVAL; +- buffer->item = configfs_get_config_item(dentry->d_parent); ++ buffer->item = to_item(dentry->d_parent); + if (!buffer->item) + goto out_free_buffer; + +@@ -410,6 +444,7 @@ static int __configfs_open_file(struct i + buffer->read_in_progress = false; + buffer->write_in_progress = false; + file->private_data = buffer; ++ up_read(&frag->frag_sem); + return 0; + + out_put_module: +@@ -417,6 +452,7 @@ out_put_module: + out_put_item: + config_item_put(buffer->item); + out_free_buffer: ++ up_read(&frag->frag_sem); + kfree(buffer); + out: + return error; +@@ -426,8 +462,6 @@ static int configfs_release(struct inode + { + struct configfs_buffer *buffer = filp->private_data; + +- if (buffer->item) +- config_item_put(buffer->item); + module_put(buffer->owner); + if (buffer->page) + free_page((unsigned long)buffer->page); +@@ -453,12 +487,17 @@ static int configfs_release_bin_file(str + buffer->read_in_progress = false; + + if (buffer->write_in_progress) { ++ struct configfs_fragment *frag = to_frag(file); + buffer->write_in_progress = false; + +- /* result of ->release() is ignored */ +- buffer->bin_attr->write(buffer->item, buffer->bin_buffer, +- buffer->bin_buffer_size); +- ++ down_read(&frag->frag_sem); ++ if (!frag->frag_dead) { ++ /* result of ->release() is ignored */ ++ buffer->bin_attr->write(buffer->item, ++ buffer->bin_buffer, ++ buffer->bin_buffer_size); ++ } ++ up_read(&frag->frag_sem); + /* vfree on NULL is safe */ + vfree(buffer->bin_buffer); + buffer->bin_buffer = NULL; diff --git a/queue-4.14/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch b/queue-4.14/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch new file mode 100644 index 00000000000..47f1e9c7447 --- /dev/null +++ b/queue-4.14/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch @@ -0,0 +1,409 @@ +From ff4dd081977da56566a848f071aed8fa92d604a1 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Fri, 30 Aug 2019 11:30:03 -0400 +Subject: configfs: stash the data we need into configfs_buffer at open time + +From: Al Viro + +commit ff4dd081977da56566a848f071aed8fa92d604a1 upstream. + +simplifies the ->read()/->write()/->release() instances nicely + +Signed-off-by: Al Viro +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/file.c | 229 +++++++++++++++++++++-------------------------------- + 1 file changed, 95 insertions(+), 134 deletions(-) + +--- a/fs/configfs/file.c ++++ b/fs/configfs/file.c +@@ -53,24 +53,18 @@ struct configfs_buffer { + bool write_in_progress; + char *bin_buffer; + int bin_buffer_size; ++ int cb_max_size; ++ struct config_item *item; ++ struct module *owner; ++ union { ++ struct configfs_attribute *attr; ++ struct configfs_bin_attribute *bin_attr; ++ }; + }; + + +-/** +- * fill_read_buffer - allocate and fill buffer from item. +- * @dentry: dentry pointer. +- * @buffer: data buffer for file. +- * +- * Allocate @buffer->page, if it hasn't been already, then call the +- * config_item's show() method to fill the buffer with this attribute's +- * data. +- * This is called only once, on the file's first read. +- */ +-static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buffer) ++static int fill_read_buffer(struct configfs_buffer * buffer) + { +- struct configfs_attribute * attr = to_attr(dentry); +- struct config_item * item = to_item(dentry->d_parent); +- int ret = 0; + ssize_t count; + + if (!buffer->page) +@@ -78,15 +72,15 @@ static int fill_read_buffer(struct dentr + if (!buffer->page) + return -ENOMEM; + +- count = attr->show(item, buffer->page); ++ count = buffer->attr->show(buffer->item, buffer->page); ++ if (count < 0) ++ return count; ++ if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE)) ++ return -EIO; + +- BUG_ON(count > (ssize_t)SIMPLE_ATTR_SIZE); +- if (count >= 0) { +- buffer->needs_read_fill = 0; +- buffer->count = count; +- } else +- ret = count; +- return ret; ++ buffer->needs_read_fill = 0; ++ buffer->count = count; ++ return 0; + } + + /** +@@ -111,12 +105,13 @@ static int fill_read_buffer(struct dentr + static ssize_t + configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) + { +- struct configfs_buffer * buffer = file->private_data; ++ struct configfs_buffer *buffer = file->private_data; + ssize_t retval = 0; + + mutex_lock(&buffer->mutex); + if (buffer->needs_read_fill) { +- if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) ++ retval = fill_read_buffer(buffer); ++ if (retval) + goto out; + } + pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", +@@ -153,9 +148,6 @@ configfs_read_bin_file(struct file *file + size_t count, loff_t *ppos) + { + struct configfs_buffer *buffer = file->private_data; +- struct dentry *dentry = file->f_path.dentry; +- struct config_item *item = to_item(dentry->d_parent); +- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); + ssize_t retval = 0; + ssize_t len = min_t(size_t, count, PAGE_SIZE); + +@@ -170,14 +162,14 @@ configfs_read_bin_file(struct file *file + + if (buffer->needs_read_fill) { + /* perform first read with buf == NULL to get extent */ +- len = bin_attr->read(item, NULL, 0); ++ len = buffer->bin_attr->read(buffer->item, NULL, 0); + if (len <= 0) { + retval = len; + goto out; + } + + /* do not exceed the maximum value */ +- if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { ++ if (buffer->cb_max_size && len > buffer->cb_max_size) { + retval = -EFBIG; + goto out; + } +@@ -190,7 +182,8 @@ configfs_read_bin_file(struct file *file + buffer->bin_buffer_size = len; + + /* perform second read to fill buffer */ +- len = bin_attr->read(item, buffer->bin_buffer, len); ++ len = buffer->bin_attr->read(buffer->item, ++ buffer->bin_buffer, len); + if (len < 0) { + retval = len; + vfree(buffer->bin_buffer); +@@ -240,25 +233,10 @@ fill_write_buffer(struct configfs_buffer + return error ? -EFAULT : count; + } + +- +-/** +- * flush_write_buffer - push buffer to config_item. +- * @dentry: dentry to the attribute +- * @buffer: data buffer for file. +- * @count: number of bytes +- * +- * Get the correct pointers for the config_item and the attribute we're +- * dealing with, then call the store() method for the attribute, +- * passing the buffer that we acquired in fill_write_buffer(). +- */ +- + static int +-flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size_t count) ++flush_write_buffer(struct configfs_buffer *buffer, size_t count) + { +- struct configfs_attribute * attr = to_attr(dentry); +- struct config_item * item = to_item(dentry->d_parent); +- +- return attr->store(item, buffer->page, count); ++ return buffer->attr->store(buffer->item, buffer->page, count); + } + + +@@ -282,13 +260,13 @@ flush_write_buffer(struct dentry * dentr + static ssize_t + configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) + { +- struct configfs_buffer * buffer = file->private_data; ++ struct configfs_buffer *buffer = file->private_data; + ssize_t len; + + mutex_lock(&buffer->mutex); + len = fill_write_buffer(buffer, buf, count); + if (len > 0) +- len = flush_write_buffer(file->f_path.dentry, buffer, len); ++ len = flush_write_buffer(buffer, len); + if (len > 0) + *ppos += len; + mutex_unlock(&buffer->mutex); +@@ -313,8 +291,6 @@ configfs_write_bin_file(struct file *fil + size_t count, loff_t *ppos) + { + struct configfs_buffer *buffer = file->private_data; +- struct dentry *dentry = file->f_path.dentry; +- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); + void *tbuf = NULL; + ssize_t len; + +@@ -330,8 +306,8 @@ configfs_write_bin_file(struct file *fil + /* buffer grows? */ + if (*ppos + count > buffer->bin_buffer_size) { + +- if (bin_attr->cb_max_size && +- *ppos + count > bin_attr->cb_max_size) { ++ if (buffer->cb_max_size && ++ *ppos + count > buffer->cb_max_size) { + len = -EFBIG; + goto out; + } +@@ -363,31 +339,45 @@ out: + return len; + } + +-static int check_perm(struct inode * inode, struct file * file, int type) ++static int __configfs_open_file(struct inode *inode, struct file *file, int type) + { +- struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); +- struct configfs_attribute * attr = to_attr(file->f_path.dentry); +- struct configfs_bin_attribute *bin_attr = NULL; +- struct configfs_buffer * buffer; +- struct configfs_item_operations * ops = NULL; +- int error = 0; ++ struct dentry *dentry = file->f_path.dentry; ++ struct configfs_attribute *attr; ++ struct configfs_buffer *buffer; ++ int error; + +- if (!item || !attr) +- goto Einval; ++ error = -ENOMEM; ++ buffer = kzalloc(sizeof(struct configfs_buffer), GFP_KERNEL); ++ if (!buffer) ++ goto out; + +- if (type & CONFIGFS_ITEM_BIN_ATTR) +- bin_attr = to_bin_attr(file->f_path.dentry); ++ error = -EINVAL; ++ buffer->item = configfs_get_config_item(dentry->d_parent); ++ if (!buffer->item) ++ goto out_free_buffer; ++ ++ attr = to_attr(dentry); ++ if (!attr) ++ goto out_put_item; ++ ++ if (type & CONFIGFS_ITEM_BIN_ATTR) { ++ buffer->bin_attr = to_bin_attr(dentry); ++ buffer->cb_max_size = buffer->bin_attr->cb_max_size; ++ } else { ++ buffer->attr = attr; ++ } + ++ buffer->owner = attr->ca_owner; + /* Grab the module reference for this attribute if we have one */ +- if (!try_module_get(attr->ca_owner)) { +- error = -ENODEV; +- goto Done; +- } ++ error = -ENODEV; ++ if (!try_module_get(buffer->owner)) ++ goto out_put_item; + +- if (item->ci_type) +- ops = item->ci_type->ct_item_ops; +- else +- goto Eaccess; ++ error = -EACCES; ++ if (!buffer->item->ci_type) ++ goto out_put_module; ++ ++ buffer->ops = buffer->item->ci_type->ct_item_ops; + + /* File needs write support. + * The inode's perms must say it's ok, +@@ -395,13 +385,11 @@ static int check_perm(struct inode * ino + */ + if (file->f_mode & FMODE_WRITE) { + if (!(inode->i_mode & S_IWUGO)) +- goto Eaccess; +- ++ goto out_put_module; + if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) +- goto Eaccess; +- +- if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) +- goto Eaccess; ++ goto out_put_module; ++ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->write) ++ goto out_put_module; + } + + /* File needs read support. +@@ -410,90 +398,65 @@ static int check_perm(struct inode * ino + */ + if (file->f_mode & FMODE_READ) { + if (!(inode->i_mode & S_IRUGO)) +- goto Eaccess; +- ++ goto out_put_module; + if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) +- goto Eaccess; +- +- if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) +- goto Eaccess; ++ goto out_put_module; ++ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->read) ++ goto out_put_module; + } + +- /* No error? Great, allocate a buffer for the file, and store it +- * it in file->private_data for easy access. +- */ +- buffer = kzalloc(sizeof(struct configfs_buffer),GFP_KERNEL); +- if (!buffer) { +- error = -ENOMEM; +- goto Enomem; +- } + mutex_init(&buffer->mutex); + buffer->needs_read_fill = 1; + buffer->read_in_progress = false; + buffer->write_in_progress = false; +- buffer->ops = ops; + file->private_data = buffer; +- goto Done; ++ return 0; + +- Einval: +- error = -EINVAL; +- goto Done; +- Eaccess: +- error = -EACCES; +- Enomem: +- module_put(attr->ca_owner); +- Done: +- if (error && item) +- config_item_put(item); ++out_put_module: ++ module_put(buffer->owner); ++out_put_item: ++ config_item_put(buffer->item); ++out_free_buffer: ++ kfree(buffer); ++out: + return error; + } + + static int configfs_release(struct inode *inode, struct file *filp) + { +- struct config_item * item = to_item(filp->f_path.dentry->d_parent); +- struct configfs_attribute * attr = to_attr(filp->f_path.dentry); +- struct module * owner = attr->ca_owner; +- struct configfs_buffer * buffer = filp->private_data; +- +- if (item) +- config_item_put(item); +- /* After this point, attr should not be accessed. */ +- module_put(owner); +- +- if (buffer) { +- if (buffer->page) +- free_page((unsigned long)buffer->page); +- mutex_destroy(&buffer->mutex); +- kfree(buffer); +- } ++ struct configfs_buffer *buffer = filp->private_data; ++ ++ if (buffer->item) ++ config_item_put(buffer->item); ++ module_put(buffer->owner); ++ if (buffer->page) ++ free_page((unsigned long)buffer->page); ++ mutex_destroy(&buffer->mutex); ++ kfree(buffer); + return 0; + } + + static int configfs_open_file(struct inode *inode, struct file *filp) + { +- return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); ++ return __configfs_open_file(inode, filp, CONFIGFS_ITEM_ATTR); + } + + static int configfs_open_bin_file(struct inode *inode, struct file *filp) + { +- return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); ++ return __configfs_open_file(inode, filp, CONFIGFS_ITEM_BIN_ATTR); + } + +-static int configfs_release_bin_file(struct inode *inode, struct file *filp) ++static int configfs_release_bin_file(struct inode *inode, struct file *file) + { +- struct configfs_buffer *buffer = filp->private_data; +- struct dentry *dentry = filp->f_path.dentry; +- struct config_item *item = to_item(dentry->d_parent); +- struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); +- ssize_t len = 0; +- int ret; ++ struct configfs_buffer *buffer = file->private_data; + + buffer->read_in_progress = false; + + if (buffer->write_in_progress) { + buffer->write_in_progress = false; + +- len = bin_attr->write(item, buffer->bin_buffer, ++ /* result of ->release() is ignored */ ++ buffer->bin_attr->write(buffer->item, buffer->bin_buffer, + buffer->bin_buffer_size); + + /* vfree on NULL is safe */ +@@ -503,10 +466,8 @@ static int configfs_release_bin_file(str + buffer->needs_read_fill = 1; + } + +- ret = configfs_release(inode, filp); +- if (len < 0) +- return len; +- return ret; ++ configfs_release(inode, file); ++ return 0; + } + + diff --git a/queue-4.14/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch b/queue-4.14/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch new file mode 100644 index 00000000000..e8eb3a923e7 --- /dev/null +++ b/queue-4.14/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch @@ -0,0 +1,51 @@ +From f19e4ed1e1edbfa3c9ccb9fed17759b7d6db24c6 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Thu, 29 Aug 2019 23:13:30 -0400 +Subject: configfs_register_group() shouldn't be (and isn't) called in rmdirable parts + +From: Al Viro + +commit f19e4ed1e1edbfa3c9ccb9fed17759b7d6db24c6 upstream. + +revert cc57c07343bd "configfs: fix registered group removal" +It was an attempt to handle something that fundamentally doesn't +work - configfs_register_group() should never be done in a part +of tree that can be rmdir'ed. And in mainline it never had been, +so let's not borrow trouble; the fix was racy anyway, it would take +a lot more to make that work and desired semantics is not clear. + +Signed-off-by: Al Viro +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/configfs/dir.c | 11 ----------- + 1 file changed, 11 deletions(-) + +--- a/fs/configfs/dir.c ++++ b/fs/configfs/dir.c +@@ -1782,16 +1782,6 @@ void configfs_unregister_group(struct co + struct dentry *dentry = group->cg_item.ci_dentry; + struct dentry *parent = group->cg_item.ci_parent->ci_dentry; + +- mutex_lock(&subsys->su_mutex); +- if (!group->cg_item.ci_parent->ci_group) { +- /* +- * The parent has already been unlinked and detached +- * due to a rmdir. +- */ +- goto unlink_group; +- } +- mutex_unlock(&subsys->su_mutex); +- + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + spin_lock(&configfs_dirent_lock); + configfs_detach_prep(dentry, NULL); +@@ -1806,7 +1796,6 @@ void configfs_unregister_group(struct co + dput(dentry); + + mutex_lock(&subsys->su_mutex); +-unlink_group: + unlink_group(group); + mutex_unlock(&subsys->su_mutex); + } diff --git a/queue-4.14/series b/queue-4.14/series index 60fb7259f63..1a0895dd55d 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -37,3 +37,9 @@ can-rx-offload-can_rx_offload_queue_sorted-fix-error-handling-avoid-skb-mem-leak can-gs_usb-gs_can_open-prevent-memory-leak.patch can-mcba_usb-fix-use-after-free-on-disconnect.patch can-peak_usb-fix-slab-info-leak.patch +configfs-fix-bool-initialization-comparison.patch +configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch +configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch +configfs-new-object-reprsenting-tree-fragments.patch +configfs-provide-exclusion-between-io-and-removals.patch +configfs-fix-a-deadlock-in-configfs_symlink.patch