aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJeff Mahoney <jeffm@suse.com>2016-06-08 00:36:38 -0400
committerDavid Sterba <dsterba@suse.com>2016-06-17 12:32:40 -0400
commit64c12921e11b3a0c10d088606e328c58e29274d8 (patch)
tree731fb378fc65fa1f3c767075979801ea07b71dd6 /fs
parentc871b0f2fd27e7f9097d507f47de5270f88003b9 (diff)
btrfs: account for non-CoW'd blocks in btrfs_abort_transaction
The test for !trans->blocks_used in btrfs_abort_transaction is insufficient to determine whether it's safe to drop the transaction handle on the floor. btrfs_cow_block, informed by should_cow_block, can return blocks that have already been CoW'd in the current transaction. trans->blocks_used is only incremented for new block allocations. If an operation overlaps the blocks in the current transaction entirely and must abort the transaction, we'll happily let it clean up the trans handle even though it may have modified the blocks and will commit an incomplete operation. In the long-term, I'd like to do closer tracking of when the fs is actually modified so we can still recover as gracefully as possible, but that approach will need some discussion. In the short term, since this is the only code using trans->blocks_used, let's just switch it to a bool indicating whether any blocks were used and set it when should_cow_block returns false. Cc: stable@vger.kernel.org # 3.4+ Signed-off-by: Jeff Mahoney <jeffm@suse.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c5
-rw-r--r--fs/btrfs/extent-tree.c2
-rw-r--r--fs/btrfs/super.c2
-rw-r--r--fs/btrfs/transaction.h2
4 files changed, 7 insertions, 4 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 827c949fa4bc..6276add8538a 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1554,6 +1554,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
1554 trans->transid, root->fs_info->generation); 1554 trans->transid, root->fs_info->generation);
1555 1555
1556 if (!should_cow_block(trans, root, buf)) { 1556 if (!should_cow_block(trans, root, buf)) {
1557 trans->dirty = true;
1557 *cow_ret = buf; 1558 *cow_ret = buf;
1558 return 0; 1559 return 0;
1559 } 1560 }
@@ -2777,8 +2778,10 @@ again:
2777 * then we don't want to set the path blocking, 2778 * then we don't want to set the path blocking,
2778 * so we test it here 2779 * so we test it here
2779 */ 2780 */
2780 if (!should_cow_block(trans, root, b)) 2781 if (!should_cow_block(trans, root, b)) {
2782 trans->dirty = true;
2781 goto cow_done; 2783 goto cow_done;
2784 }
2782 2785
2783 /* 2786 /*
2784 * must have write locks on this node and the 2787 * must have write locks on this node and the
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 5439e85c4813..29e5d000bbee 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -8045,7 +8045,7 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
8045 set_extent_dirty(&trans->transaction->dirty_pages, buf->start, 8045 set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
8046 buf->start + buf->len - 1, GFP_NOFS); 8046 buf->start + buf->len - 1, GFP_NOFS);
8047 } 8047 }
8048 trans->blocks_used++; 8048 trans->dirty = true;
8049 /* this returns a buffer locked for blocking */ 8049 /* this returns a buffer locked for blocking */
8050 return buf; 8050 return buf;
8051} 8051}
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4339b6613f19..bf70d33b5791 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -235,7 +235,7 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
235 trans->aborted = errno; 235 trans->aborted = errno;
236 /* Nothing used. The other threads that have joined this 236 /* Nothing used. The other threads that have joined this
237 * transaction may be able to continue. */ 237 * transaction may be able to continue. */
238 if (!trans->blocks_used && list_empty(&trans->new_bgs)) { 238 if (!trans->dirty && list_empty(&trans->new_bgs)) {
239 const char *errstr; 239 const char *errstr;
240 240
241 errstr = btrfs_decode_error(errno); 241 errstr = btrfs_decode_error(errno);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 9fe0ec2bf0fe..c5abee4f01ad 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -110,7 +110,6 @@ struct btrfs_trans_handle {
110 u64 chunk_bytes_reserved; 110 u64 chunk_bytes_reserved;
111 unsigned long use_count; 111 unsigned long use_count;
112 unsigned long blocks_reserved; 112 unsigned long blocks_reserved;
113 unsigned long blocks_used;
114 unsigned long delayed_ref_updates; 113 unsigned long delayed_ref_updates;
115 struct btrfs_transaction *transaction; 114 struct btrfs_transaction *transaction;
116 struct btrfs_block_rsv *block_rsv; 115 struct btrfs_block_rsv *block_rsv;
@@ -121,6 +120,7 @@ struct btrfs_trans_handle {
121 bool can_flush_pending_bgs; 120 bool can_flush_pending_bgs;
122 bool reloc_reserved; 121 bool reloc_reserved;
123 bool sync; 122 bool sync;
123 bool dirty;
124 unsigned int type; 124 unsigned int type;
125 /* 125 /*
126 * this root is only needed to validate that the root passed to 126 * this root is only needed to validate that the root passed to