]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.20-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Jan 2019 15:47:18 +0000 (16:47 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Jan 2019 15:47:18 +0000 (16:47 +0100)
added patches:
btrfs-fix-access-to-available-allocation-bits-when-starting-balance.patch
btrfs-fix-deadlock-when-enabling-quotas-due-to-concurrent-snapshot-creation.patch
btrfs-use-nofs-context-when-initializing-security-xattrs-to-avoid-deadlock.patch

queue-4.20/btrfs-fix-access-to-available-allocation-bits-when-starting-balance.patch [new file with mode: 0644]
queue-4.20/btrfs-fix-deadlock-when-enabling-quotas-due-to-concurrent-snapshot-creation.patch [new file with mode: 0644]
queue-4.20/btrfs-use-nofs-context-when-initializing-security-xattrs-to-avoid-deadlock.patch [new file with mode: 0644]
queue-4.20/series

diff --git a/queue-4.20/btrfs-fix-access-to-available-allocation-bits-when-starting-balance.patch b/queue-4.20/btrfs-fix-access-to-available-allocation-bits-when-starting-balance.patch
new file mode 100644 (file)
index 0000000..a7bd54b
--- /dev/null
@@ -0,0 +1,100 @@
+From 5a8067c0d17feb7579db0476191417b441a8996e Mon Sep 17 00:00:00 2001
+From: Filipe Manana <fdmanana@suse.com>
+Date: Mon, 19 Nov 2018 09:48:12 +0000
+Subject: Btrfs: fix access to available allocation bits when starting balance
+
+From: Filipe Manana <fdmanana@suse.com>
+
+commit 5a8067c0d17feb7579db0476191417b441a8996e upstream.
+
+The available allocation bits members from struct btrfs_fs_info are
+protected by a sequence lock, and when starting balance we access them
+incorrectly in two different ways:
+
+1) In the read sequence lock loop at btrfs_balance() we use the values we
+   read from fs_info->avail_*_alloc_bits and we can immediately do actions
+   that have side effects and can not be undone (printing a message and
+   jumping to a label). This is wrong because a retry might be needed, so
+   our actions must not have side effects and must be repeatable as long
+   as read_seqretry() returns a non-zero value. In other words, we were
+   essentially ignoring the sequence lock;
+
+2) Right below the read sequence lock loop, we were reading the values
+   from avail_metadata_alloc_bits and avail_data_alloc_bits without any
+   protection from concurrent writers, that is, reading them outside of
+   the read sequence lock critical section.
+
+So fix this by making sure we only read the available allocation bits
+while in a read sequence lock critical section and that what we do in the
+critical section is repeatable (has nothing that can not be undone) so
+that any eventual retry that is needed is handled properly.
+
+Fixes: de98ced9e743 ("Btrfs: use seqlock to protect fs_info->avail_{data, metadata, system}_alloc_bits")
+Fixes: 14506127979a ("btrfs: fix a bogus warning when converting only data or metadata")
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/volumes.c |   39 +++++++++++++++++++++++----------------
+ 1 file changed, 23 insertions(+), 16 deletions(-)
+
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -3724,6 +3724,7 @@ int btrfs_balance(struct btrfs_fs_info *
+       int ret;
+       u64 num_devices;
+       unsigned seq;
++      bool reducing_integrity;
+       if (btrfs_fs_closing(fs_info) ||
+           atomic_read(&fs_info->balance_pause_req) ||
+@@ -3803,24 +3804,30 @@ int btrfs_balance(struct btrfs_fs_info *
+                    !(bctl->sys.target & allowed)) ||
+                   ((bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) &&
+                    (fs_info->avail_metadata_alloc_bits & allowed) &&
+-                   !(bctl->meta.target & allowed))) {
+-                      if (bctl->flags & BTRFS_BALANCE_FORCE) {
+-                              btrfs_info(fs_info,
+-                              "balance: force reducing metadata integrity");
+-                      } else {
+-                              btrfs_err(fs_info,
+-      "balance: reduces metadata integrity, use --force if you want this");
+-                              ret = -EINVAL;
+-                              goto out;
+-                      }
+-              }
++                   !(bctl->meta.target & allowed)))
++                      reducing_integrity = true;
++              else
++                      reducing_integrity = false;
++
++              /* if we're not converting, the target field is uninitialized */
++              meta_target = (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
++                      bctl->meta.target : fs_info->avail_metadata_alloc_bits;
++              data_target = (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
++                      bctl->data.target : fs_info->avail_data_alloc_bits;
+       } while (read_seqretry(&fs_info->profiles_lock, seq));
+-      /* if we're not converting, the target field is uninitialized */
+-      meta_target = (bctl->meta.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+-              bctl->meta.target : fs_info->avail_metadata_alloc_bits;
+-      data_target = (bctl->data.flags & BTRFS_BALANCE_ARGS_CONVERT) ?
+-              bctl->data.target : fs_info->avail_data_alloc_bits;
++      if (reducing_integrity) {
++              if (bctl->flags & BTRFS_BALANCE_FORCE) {
++                      btrfs_info(fs_info,
++                                 "balance: force reducing metadata integrity");
++              } else {
++                      btrfs_err(fs_info,
++        "balance: reduces metadata integrity, use --force if you want this");
++                      ret = -EINVAL;
++                      goto out;
++              }
++      }
++
+       if (btrfs_get_num_tolerated_disk_barrier_failures(meta_target) <
+               btrfs_get_num_tolerated_disk_barrier_failures(data_target)) {
+               int meta_index = btrfs_bg_flags_to_raid_index(meta_target);
diff --git a/queue-4.20/btrfs-fix-deadlock-when-enabling-quotas-due-to-concurrent-snapshot-creation.patch b/queue-4.20/btrfs-fix-deadlock-when-enabling-quotas-due-to-concurrent-snapshot-creation.patch
new file mode 100644 (file)
index 0000000..f2e3ed1
--- /dev/null
@@ -0,0 +1,132 @@
+From 9a6f209e36500efac51528132a3e3083586eda5f Mon Sep 17 00:00:00 2001
+From: Filipe Manana <fdmanana@suse.com>
+Date: Mon, 19 Nov 2018 14:15:36 +0000
+Subject: Btrfs: fix deadlock when enabling quotas due to concurrent snapshot creation
+
+From: Filipe Manana <fdmanana@suse.com>
+
+commit 9a6f209e36500efac51528132a3e3083586eda5f upstream.
+
+If the quota enable and snapshot creation ioctls are called concurrently
+we can get into a deadlock where the task enabling quotas will deadlock
+on the fs_info->qgroup_ioctl_lock mutex because it attempts to lock it
+twice, or the task creating a snapshot tries to commit the transaction
+while the task enabling quota waits for the former task to commit the
+transaction while holding the mutex. The following time diagrams show how
+both cases happen.
+
+First scenario:
+
+           CPU 0                                    CPU 1
+
+ btrfs_ioctl()
+  btrfs_ioctl_quota_ctl()
+   btrfs_quota_enable()
+    mutex_lock(fs_info->qgroup_ioctl_lock)
+    btrfs_start_transaction()
+
+                                             btrfs_ioctl()
+                                              btrfs_ioctl_snap_create_v2
+                                               create_snapshot()
+                                                --> adds snapshot to the
+                                                    list pending_snapshots
+                                                    of the current
+                                                    transaction
+
+    btrfs_commit_transaction()
+     create_pending_snapshots()
+       create_pending_snapshot()
+        qgroup_account_snapshot()
+         btrfs_qgroup_inherit()
+          mutex_lock(fs_info->qgroup_ioctl_lock)
+           --> deadlock, mutex already locked
+               by this task at
+               btrfs_quota_enable()
+
+Second scenario:
+
+           CPU 0                                    CPU 1
+
+ btrfs_ioctl()
+  btrfs_ioctl_quota_ctl()
+   btrfs_quota_enable()
+    mutex_lock(fs_info->qgroup_ioctl_lock)
+    btrfs_start_transaction()
+
+                                             btrfs_ioctl()
+                                              btrfs_ioctl_snap_create_v2
+                                               create_snapshot()
+                                                --> adds snapshot to the
+                                                    list pending_snapshots
+                                                    of the current
+                                                    transaction
+
+                                                btrfs_commit_transaction()
+                                                 --> waits for task at
+                                                     CPU 0 to release
+                                                     its transaction
+                                                     handle
+
+    btrfs_commit_transaction()
+     --> sees another task started
+         the transaction commit first
+     --> releases its transaction
+         handle
+     --> waits for the transaction
+         commit to be completed by
+         the task at CPU 1
+
+                                                 create_pending_snapshot()
+                                                  qgroup_account_snapshot()
+                                                   btrfs_qgroup_inherit()
+                                                    mutex_lock(fs_info->qgroup_ioctl_lock)
+                                                     --> deadlock, task at CPU 0
+                                                         has the mutex locked but
+                                                         it is waiting for us to
+                                                         finish the transaction
+                                                         commit
+
+So fix this by setting the quota enabled flag in fs_info after committing
+the transaction at btrfs_quota_enable(). This ends up serializing quota
+enable and snapshot creation as if the snapshot creation happened just
+before the quota enable request. The quota rescan task, scheduled after
+committing the transaction in btrfs_quote_enable(), will do the accounting.
+
+Fixes: 6426c7ad697d ("btrfs: qgroup: Fix qgroup accounting when creating snapshot")
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/qgroup.c |   14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/fs/btrfs/qgroup.c
++++ b/fs/btrfs/qgroup.c
+@@ -1013,16 +1013,22 @@ out_add_root:
+               btrfs_abort_transaction(trans, ret);
+               goto out_free_path;
+       }
+-      spin_lock(&fs_info->qgroup_lock);
+-      fs_info->quota_root = quota_root;
+-      set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
+-      spin_unlock(&fs_info->qgroup_lock);
+       ret = btrfs_commit_transaction(trans);
+       trans = NULL;
+       if (ret)
+               goto out_free_path;
++      /*
++       * Set quota enabled flag after committing the transaction, to avoid
++       * deadlocks on fs_info->qgroup_ioctl_lock with concurrent snapshot
++       * creation.
++       */
++      spin_lock(&fs_info->qgroup_lock);
++      fs_info->quota_root = quota_root;
++      set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
++      spin_unlock(&fs_info->qgroup_lock);
++
+       ret = qgroup_rescan_init(fs_info, 0, 1);
+       if (!ret) {
+               qgroup_rescan_zero_tracking(fs_info);
diff --git a/queue-4.20/btrfs-use-nofs-context-when-initializing-security-xattrs-to-avoid-deadlock.patch b/queue-4.20/btrfs-use-nofs-context-when-initializing-security-xattrs-to-avoid-deadlock.patch
new file mode 100644 (file)
index 0000000..f993d68
--- /dev/null
@@ -0,0 +1,58 @@
+From 827aa18e7b903c5ff3b3cd8fec328a99b1dbd411 Mon Sep 17 00:00:00 2001
+From: Filipe Manana <fdmanana@suse.com>
+Date: Mon, 10 Dec 2018 17:53:35 +0000
+Subject: Btrfs: use nofs context when initializing security xattrs to avoid deadlock
+
+From: Filipe Manana <fdmanana@suse.com>
+
+commit 827aa18e7b903c5ff3b3cd8fec328a99b1dbd411 upstream.
+
+When initializing the security xattrs, we are holding a transaction handle
+therefore we need to use a GFP_NOFS context in order to avoid a deadlock
+with reclaim in case it's triggered.
+
+Fixes: 39a27ec1004e8 ("btrfs: use GFP_KERNEL for xattr and acl allocations")
+Reviewed-by: Nikolay Borisov <nborisov@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/btrfs/xattr.c |    8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/fs/btrfs/xattr.c
++++ b/fs/btrfs/xattr.c
+@@ -11,6 +11,7 @@
+ #include <linux/security.h>
+ #include <linux/posix_acl_xattr.h>
+ #include <linux/iversion.h>
++#include <linux/sched/mm.h>
+ #include "ctree.h"
+ #include "btrfs_inode.h"
+ #include "transaction.h"
+@@ -422,9 +423,15 @@ static int btrfs_initxattrs(struct inode
+ {
+       const struct xattr *xattr;
+       struct btrfs_trans_handle *trans = fs_info;
++      unsigned int nofs_flag;
+       char *name;
+       int err = 0;
++      /*
++       * We're holding a transaction handle, so use a NOFS memory allocation
++       * context to avoid deadlock if reclaim happens.
++       */
++      nofs_flag = memalloc_nofs_save();
+       for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+               name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+                              strlen(xattr->name) + 1, GFP_KERNEL);
+@@ -440,6 +447,7 @@ static int btrfs_initxattrs(struct inode
+               if (err < 0)
+                       break;
+       }
++      memalloc_nofs_restore(nofs_flag);
+       return err;
+ }
index 0ab279ba0b8e638a574af4cd2870b5228ef9651b..b586f5793146b9c4542b98c5b73fd6dcd41a506a 100644 (file)
@@ -52,3 +52,6 @@ ext4-track-writeback-errors-using-the-generic-tracking-infrastructure.patch
 ext4-fix-special-inode-number-checks-in-__ext4_iget.patch
 mm-page_mapped-don-t-assume-compound-page-is-huge-or-thp.patch
 sunrpc-use-after-free-in-svc_process_common.patch
+btrfs-fix-access-to-available-allocation-bits-when-starting-balance.patch
+btrfs-fix-deadlock-when-enabling-quotas-due-to-concurrent-snapshot-creation.patch
+btrfs-use-nofs-context-when-initializing-security-xattrs-to-avoid-deadlock.patch