Btrfs: fix joining the same transaction handler more than 2 times

If we flush inodes with pending delalloc in a transaction, we may join
the same transaction handler more than 2 times.

The reason is:
  Task						use_count of trans handle
  commit_transaction				1
    |-> btrfs_start_delalloc_inodes		1
	  |-> run_delalloc_nocow		1
		|-> join_transaction		2
		|-> cow_file_range		2
			|-> join_transaction	3

In fact, cow_file_range needn't join the transaction again because the caller
have joined the transaction, so we fix this problem by this way.

Reported-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
Miao Xie 2012-11-01 07:32:18 +00:00 committed by Josef Bacik
parent 4fde183d8c
commit b7d5b0a819
2 changed files with 48 additions and 30 deletions

View File

@ -804,14 +804,14 @@ static u64 get_extent_allocation_hint(struct inode *inode, u64 start,
* required to start IO on it. It may be clean and already done with * required to start IO on it. It may be clean and already done with
* IO when we return. * IO when we return.
*/ */
static noinline int cow_file_range(struct inode *inode, static noinline int __cow_file_range(struct btrfs_trans_handle *trans,
struct page *locked_page, struct inode *inode,
u64 start, u64 end, int *page_started, struct btrfs_root *root,
unsigned long *nr_written, struct page *locked_page,
int unlock) u64 start, u64 end, int *page_started,
unsigned long *nr_written,
int unlock)
{ {
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans;
u64 alloc_hint = 0; u64 alloc_hint = 0;
u64 num_bytes; u64 num_bytes;
unsigned long ram_size; unsigned long ram_size;
@ -824,25 +824,10 @@ static noinline int cow_file_range(struct inode *inode,
int ret = 0; int ret = 0;
BUG_ON(btrfs_is_free_space_inode(inode)); BUG_ON(btrfs_is_free_space_inode(inode));
trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) {
extent_clear_unlock_delalloc(inode,
&BTRFS_I(inode)->io_tree,
start, end, locked_page,
EXTENT_CLEAR_UNLOCK_PAGE |
EXTENT_CLEAR_UNLOCK |
EXTENT_CLEAR_DELALLOC |
EXTENT_CLEAR_DIRTY |
EXTENT_SET_WRITEBACK |
EXTENT_END_WRITEBACK);
return PTR_ERR(trans);
}
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
num_bytes = (end - start + blocksize) & ~(blocksize - 1); num_bytes = (end - start + blocksize) & ~(blocksize - 1);
num_bytes = max(blocksize, num_bytes); num_bytes = max(blocksize, num_bytes);
disk_num_bytes = num_bytes; disk_num_bytes = num_bytes;
ret = 0;
/* if this is a small write inside eof, kick off defrag */ /* if this is a small write inside eof, kick off defrag */
if (num_bytes < 64 * 1024 && if (num_bytes < 64 * 1024 &&
@ -953,11 +938,9 @@ static noinline int cow_file_range(struct inode *inode,
alloc_hint = ins.objectid + ins.offset; alloc_hint = ins.objectid + ins.offset;
start += cur_alloc_size; start += cur_alloc_size;
} }
ret = 0;
out: out:
btrfs_end_transaction(trans, root);
return ret; return ret;
out_unlock: out_unlock:
extent_clear_unlock_delalloc(inode, extent_clear_unlock_delalloc(inode,
&BTRFS_I(inode)->io_tree, &BTRFS_I(inode)->io_tree,
@ -972,6 +955,39 @@ static noinline int cow_file_range(struct inode *inode,
goto out; goto out;
} }
static noinline int cow_file_range(struct inode *inode,
struct page *locked_page,
u64 start, u64 end, int *page_started,
unsigned long *nr_written,
int unlock)
{
struct btrfs_trans_handle *trans;
struct btrfs_root *root = BTRFS_I(inode)->root;
int ret;
trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) {
extent_clear_unlock_delalloc(inode,
&BTRFS_I(inode)->io_tree,
start, end, locked_page,
EXTENT_CLEAR_UNLOCK_PAGE |
EXTENT_CLEAR_UNLOCK |
EXTENT_CLEAR_DELALLOC |
EXTENT_CLEAR_DIRTY |
EXTENT_SET_WRITEBACK |
EXTENT_END_WRITEBACK);
return PTR_ERR(trans);
}
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
ret = __cow_file_range(trans, inode, root, locked_page, start, end,
page_started, nr_written, unlock);
btrfs_end_transaction(trans, root);
return ret;
}
/* /*
* work queue call back to started compression on a file and pages * work queue call back to started compression on a file and pages
*/ */
@ -1282,9 +1298,9 @@ static noinline int run_delalloc_nocow(struct inode *inode,
btrfs_release_path(path); btrfs_release_path(path);
if (cow_start != (u64)-1) { if (cow_start != (u64)-1) {
ret = cow_file_range(inode, locked_page, cow_start, ret = __cow_file_range(trans, inode, root, locked_page,
found_key.offset - 1, page_started, cow_start, found_key.offset - 1,
nr_written, 1); page_started, nr_written, 1);
if (ret) { if (ret) {
btrfs_abort_transaction(trans, root, ret); btrfs_abort_transaction(trans, root, ret);
goto error; goto error;
@ -1353,8 +1369,9 @@ static noinline int run_delalloc_nocow(struct inode *inode,
} }
if (cow_start != (u64)-1) { if (cow_start != (u64)-1) {
ret = cow_file_range(inode, locked_page, cow_start, end, ret = __cow_file_range(trans, inode, root, locked_page,
page_started, nr_written, 1); cow_start, end,
page_started, nr_written, 1);
if (ret) { if (ret) {
btrfs_abort_transaction(trans, root, ret); btrfs_abort_transaction(trans, root, ret);
goto error; goto error;

View File

@ -312,6 +312,7 @@ start_transaction(struct btrfs_root *root, u64 num_items, int type,
WARN_ON(type != TRANS_JOIN && type != TRANS_JOIN_NOLOCK); WARN_ON(type != TRANS_JOIN && type != TRANS_JOIN_NOLOCK);
h = current->journal_info; h = current->journal_info;
h->use_count++; h->use_count++;
WARN_ON(h->use_count > 2);
h->orig_rsv = h->block_rsv; h->orig_rsv = h->block_rsv;
h->block_rsv = NULL; h->block_rsv = NULL;
goto got_it; goto got_it;