]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Nov 2019 08:48:12 +0000 (09:48 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Nov 2019 08:48:12 +0000 (09:48 +0100)
added patches:
configfs-fix-a-deadlock-in-configfs_symlink.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

queue-4.19/configfs-fix-a-deadlock-in-configfs_symlink.patch [new file with mode: 0644]
queue-4.19/configfs-new-object-reprsenting-tree-fragments.patch [new file with mode: 0644]
queue-4.19/configfs-provide-exclusion-between-io-and-removals.patch [new file with mode: 0644]
queue-4.19/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch [new file with mode: 0644]
queue-4.19/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch [new file with mode: 0644]
queue-4.19/series

diff --git a/queue-4.19/configfs-fix-a-deadlock-in-configfs_symlink.patch b/queue-4.19/configfs-fix-a-deadlock-in-configfs_symlink.patch
new file mode 100644 (file)
index 0000000..b483f3d
--- /dev/null
@@ -0,0 +1,81 @@
+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);
diff --git a/queue-4.19/configfs-new-object-reprsenting-tree-fragments.patch b/queue-4.19/configfs-new-object-reprsenting-tree-fragments.patch
new file mode 100644 (file)
index 0000000..d8cb77a
--- /dev/null
@@ -0,0 +1,396 @@
+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;
+       const 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.19/configfs-provide-exclusion-between-io-and-removals.patch b/queue-4.19/configfs-provide-exclusion-between-io-and-removals.patch
new file mode 100644 (file)
index 0000000..fbab044
--- /dev/null
@@ -0,0 +1,277 @@
+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;
diff --git a/queue-4.19/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch b/queue-4.19/configfs-stash-the-data-we-need-into-configfs_buffer-at-open-time.patch
new file mode 100644 (file)
index 0000000..47f1e9c
--- /dev/null
@@ -0,0 +1,409 @@
+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;
+ }
diff --git a/queue-4.19/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch b/queue-4.19/configfs_register_group-shouldn-t-be-and-isn-t-called-in-rmdirable-parts.patch
new file mode 100644 (file)
index 0000000..e8eb3a9
--- /dev/null
@@ -0,0 +1,51 @@
+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);
+ }
index 6671cc2dd2f79f189659d4cf361566f223c3a55f..7cb91da85e8922f20cb8b3b7988cc7c2e713ec61 100644 (file)
@@ -49,3 +49,8 @@ can-gs_usb-gs_can_open-prevent-memory-leak.patch
 can-dev-add-missing-of_node_put-after-calling-of_get_child_by_name.patch
 can-mcba_usb-fix-use-after-free-on-disconnect.patch
 can-peak_usb-fix-slab-info-leak.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