Skip to content

Commit

Permalink
btrfs: account for non-CoW'd blocks in btrfs_abort_transaction
Browse files Browse the repository at this point in the history
[ Upstream commit 64c1292 ]

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>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
  • Loading branch information
jeffmahoney authored and sashalevin committed Jul 12, 2016
1 parent bc4b57b commit eefeb9a
Show file tree
Hide file tree
Showing 5 changed files with 7 additions and 5 deletions.
5 changes: 4 additions & 1 deletion fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
trans->transid, root->fs_info->generation);

if (!should_cow_block(trans, root, buf)) {
trans->dirty = true;
*cow_ret = buf;
return 0;
}
Expand Down Expand Up @@ -2762,8 +2763,10 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
* then we don't want to set the path blocking,
* so we test it here
*/
if (!should_cow_block(trans, root, b))
if (!should_cow_block(trans, root, b)) {
trans->dirty = true;
goto cow_done;
}

/*
* must have write locks on this node and the
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -7237,7 +7237,7 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
buf->start + buf->len - 1, GFP_NOFS);
}
trans->blocks_used++;
trans->dirty = true;
/* this returns a buffer locked for blocking */
return buf;
}
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans,
trans->aborted = errno;
/* Nothing used. The other threads that have joined this
* transaction may be able to continue. */
if (!trans->blocks_used && list_empty(&trans->new_bgs)) {
if (!trans->dirty && list_empty(&trans->new_bgs)) {
const char *errstr;

errstr = btrfs_decode_error(errno);
Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,6 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type,

h->transid = cur_trans->transid;
h->transaction = cur_trans;
h->blocks_used = 0;
h->bytes_reserved = 0;
h->root = root;
h->delayed_ref_updates = 0;
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ struct btrfs_trans_handle {
u64 qgroup_reserved;
unsigned long use_count;
unsigned long blocks_reserved;
unsigned long blocks_used;
unsigned long delayed_ref_updates;
struct btrfs_transaction *transaction;
struct btrfs_block_rsv *block_rsv;
Expand All @@ -98,6 +97,7 @@ struct btrfs_trans_handle {
bool allocating_chunk;
bool reloc_reserved;
bool sync;
bool dirty;
unsigned int type;
/*
* this root is only needed to validate that the root passed to
Expand Down

0 comments on commit eefeb9a

Please sign in to comment.