--- /dev/null
+From 351e5d869e5ac10cb40c78b5f2d7dfc816ad4587 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sat, 3 Aug 2019 11:51:18 -0400
+Subject: configfs: fix a deadlock in configfs_symlink()
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+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 <viro@zeniv.linux.org.uk>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);
--- /dev/null
+From 3f6928c347707a65cee10a9f54b85ad5fb078b3f Mon Sep 17 00:00:00 2001
+From: Thomas Meyer <thomas@m3y3r.de>
+Date: Sat, 7 Oct 2017 16:02:21 +0200
+Subject: configfs: Fix bool initialization/comparison
+
+From: Thomas Meyer <thomas@m3y3r.de>
+
+commit 3f6928c347707a65cee10a9f54b85ad5fb078b3f upstream.
+
+Bool initializations should use true and false. Bool tests don't need
+comparisons.
+
+Signed-off-by: Thomas Meyer <thomas@m3y3r.de>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);
--- /dev/null
+From 47320fbe11a6059ae502c9c16b668022fdb4cf76 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sun, 25 Aug 2019 19:56:13 -0400
+Subject: configfs: new object reprsenting tree fragments
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+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 <viro@zeniv.linux.org.uk>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 <linux/list.h>
+ #include <linux/spinlock.h>
+
++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;
--- /dev/null
+From b0841eefd9693827afb9888235e26ddd098f9cef Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sat, 31 Aug 2019 09:43:43 +0200
+Subject: configfs: provide exclusion between IO and removals
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+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 <viro@zeniv.linux.org.uk>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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;
--- /dev/null
+From ff4dd081977da56566a848f071aed8fa92d604a1 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+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 <viro@zeniv.linux.org.uk>
+
+commit ff4dd081977da56566a848f071aed8fa92d604a1 upstream.
+
+simplifies the ->read()/->write()/->release() instances nicely
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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;
+ }
+
+
--- /dev/null
+From f19e4ed1e1edbfa3c9ccb9fed17759b7d6db24c6 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+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 <viro@zeniv.linux.org.uk>
+
+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 <viro@zeniv.linux.org.uk>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);
+ }
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