Home Home > GIT Browse > openSUSE-15.0
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2015-08-19 15:12:33 +0100
committerFilipe Manana <fdmanana@suse.com>2015-08-19 15:12:37 +0100
commit265cc322582945f873ce5957a38fa65c5df912b9 (patch)
treec50c96fdc927bb938f9b781f8998afee24be6178
parent94ad94841b74b77b33096335e70c322499c81012 (diff)
Btrfs: check if previous transaction aborted to avoid fsrpm-3.0.101-0.47.67
corruption (bnc#942350).
-rw-r--r--patches.suse/btrfs-8343-check-if-previous-transaction-aborted-to-avoid.patch114
-rw-r--r--series.conf1
2 files changed, 115 insertions, 0 deletions
diff --git a/patches.suse/btrfs-8343-check-if-previous-transaction-aborted-to-avoid.patch b/patches.suse/btrfs-8343-check-if-previous-transaction-aborted-to-avoid.patch
new file mode 100644
index 0000000000..b975a1a468
--- /dev/null
+++ b/patches.suse/btrfs-8343-check-if-previous-transaction-aborted-to-avoid.patch
@@ -0,0 +1,114 @@
+From: Filipe Manana <fdmanana@suse.com>
+Date: Wed, 12 Aug 2015 11:54:35 +0100
+Patch-mainline: submitted upstream for 4.3
+References: bnc#942350
+Subject: [PATCH] Btrfs: check if previous transaction aborted to avoid fs
+ corruption
+
+While we are committing a transaction, it's possible the previous one is
+still finishing its commit and therefore we wait for it to finish first.
+However we were not checking if that previous transaction ended up getting
+aborted after we waited for it to commit, so we ended up committing the
+current transaction which can lead to fs corruption because the new
+superblock can point to trees that have had one or more nodes/leafs that
+were never durably persisted.
+The following sequence diagram exemplifies how this is possible:
+
+ CPU 0 CPU 1
+
+ transaction N starts
+
+ (...)
+
+ btrfs_commit_transaction(N)
+
+ cur_trans->state = TRANS_STATE_COMMIT_START;
+ (...)
+ cur_trans->state = TRANS_STATE_COMMIT_DOING;
+ (...)
+
+ cur_trans->state = TRANS_STATE_UNBLOCKED;
+ root->fs_info->running_transaction = NULL;
+
+ btrfs_start_transaction()
+ --> starts transaction N + 1
+
+ btrfs_write_and_wait_transaction(trans, root);
+ --> starts writing all new or COWed ebs created
+ at transaction N
+
+ creates some new ebs, COWs some
+ existing ebs but doesn't COW or
+ deletes eb X
+
+ btrfs_commit_transaction(N + 1)
+ (...)
+ cur_trans->state = TRANS_STATE_COMMIT_START;
+ (...)
+ wait_for_commit(root, prev_trans);
+ --> prev_trans == transaction N
+
+ btrfs_write_and_wait_transaction() continues
+ writing ebs
+ --> fails writing eb X, we abort transaction N
+ and set bit BTRFS_FS_STATE_ERROR on
+ fs_info->fs_state, so no new transactions
+ can start after setting that bit
+
+ cleanup_transaction()
+ btrfs_cleanup_one_transaction()
+ wakes up task at CPU 1
+
+ continues, doesn't abort because
+ cur_trans->aborted (transaction N + 1)
+ is zero, and no checks for bit
+ BTRFS_FS_STATE_ERROR in fs_info->fs_state
+ are made
+
+ btrfs_write_and_wait_transaction(trans, root);
+ --> succeeds, no errors during writeback
+
+ write_ctree_super(trans, root, 0);
+ --> succeeds
+ --> we have now a superblock that points us
+ to some root that uses eb X, which was
+ never written to disk
+
+In this scenario future attempts to read eb X from disk results in an
+error message like "parent transid verify failed on X wanted Y found Z".
+
+So fix this by aborting the current transaction if after waiting for the
+previous transaction we verify that it was aborted.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Reviewed-by: Josef Bacik <jbacik@fb.com>
+Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
+
+Conflicts:
+ fs/btrfs/transaction.c
+
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+---
+ fs/btrfs/transaction.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
+index 17c0742..560ddde 100644
+--- a/fs/btrfs/transaction.c
++++ b/fs/btrfs/transaction.c
+@@ -1716,8 +1716,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
+ spin_unlock(&root->fs_info->trans_lock);
+
+ wait_for_commit(root, prev_trans);
++ ret = prev_trans->aborted;
+
+ put_transaction(prev_trans);
++ if (ret)
++ goto cleanup_transaction;
+ } else {
+ spin_unlock(&root->fs_info->trans_lock);
+ }
+--
+1.8.4.5
+
diff --git a/series.conf b/series.conf
index 52148c378d..33a6d739aa 100644
--- a/series.conf
+++ b/series.conf
@@ -3824,6 +3824,7 @@
# bnc#942350, fs corruption fixes
patches.suse/btrfs-8341-be-aware-of-btree-inode-write-errors-to-avoid-.patch
patches.suse/btrfs-8342-deal-with-convert_extent_bit-errors-to-avoid-f.patch
+ patches.suse/btrfs-8343-check-if-previous-transaction-aborted-to-avoid.patch
# Non-upstreamed btrfs patches
patches.suse/btrfs-0006-fix-error-check-of-btrfs_lookup_dentry.patch