Btrfs: use the global block reserve if we cannot reserve space
We call use_block_rsv right before we make an allocation in order to make sure we have enough space. Now normally people have called btrfs_start_transaction() with the appropriate amount of space that we need, so we just use some of that pre-reserved space and move along happily. The problem is where people use btrfs_join_transaction(), which doesn't actually reserve any space. So we try and reserve space here, but we cannot flush delalloc, so this forces us to return -ENOSPC when in reality we have plenty of space. The most common symptom is seeing a bunch of "couldn't dirty inode" messages in syslog. With xfstests 224 we end up falling back to start_transaction and then doing all the flush delalloc stuff which causes to hang for a very long time. So instead steal from the global reserve, which is what this is meant for anyway. With this patch and the other 2 I have sent xfstests 224 now passes successfully. Thanks, Signed-off-by: Josef Bacik <josef@redhat.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
e9e22899de
commit
68a82277b8
1 changed files with 27 additions and 1 deletions
|
@ -5646,6 +5646,7 @@ use_block_rsv(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root, u32 blocksize)
|
struct btrfs_root *root, u32 blocksize)
|
||||||
{
|
{
|
||||||
struct btrfs_block_rsv *block_rsv;
|
struct btrfs_block_rsv *block_rsv;
|
||||||
|
struct btrfs_block_rsv *global_rsv = &root->fs_info->global_block_rsv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
block_rsv = get_block_rsv(trans, root);
|
block_rsv = get_block_rsv(trans, root);
|
||||||
|
@ -5653,14 +5654,39 @@ use_block_rsv(struct btrfs_trans_handle *trans,
|
||||||
if (block_rsv->size == 0) {
|
if (block_rsv->size == 0) {
|
||||||
ret = reserve_metadata_bytes(trans, root, block_rsv,
|
ret = reserve_metadata_bytes(trans, root, block_rsv,
|
||||||
blocksize, 0);
|
blocksize, 0);
|
||||||
if (ret)
|
/*
|
||||||
|
* If we couldn't reserve metadata bytes try and use some from
|
||||||
|
* the global reserve.
|
||||||
|
*/
|
||||||
|
if (ret && block_rsv != global_rsv) {
|
||||||
|
ret = block_rsv_use_bytes(global_rsv, blocksize);
|
||||||
|
if (!ret)
|
||||||
|
return global_rsv;
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
} else if (ret) {
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
return block_rsv;
|
return block_rsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = block_rsv_use_bytes(block_rsv, blocksize);
|
ret = block_rsv_use_bytes(block_rsv, blocksize);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return block_rsv;
|
return block_rsv;
|
||||||
|
if (ret) {
|
||||||
|
WARN_ON(1);
|
||||||
|
ret = reserve_metadata_bytes(trans, root, block_rsv, blocksize,
|
||||||
|
0);
|
||||||
|
if (!ret) {
|
||||||
|
spin_lock(&block_rsv->lock);
|
||||||
|
block_rsv->size += blocksize;
|
||||||
|
spin_unlock(&block_rsv->lock);
|
||||||
|
return block_rsv;
|
||||||
|
} else if (ret && block_rsv != global_rsv) {
|
||||||
|
ret = block_rsv_use_bytes(global_rsv, blocksize);
|
||||||
|
if (!ret)
|
||||||
|
return global_rsv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ERR_PTR(-ENOSPC);
|
return ERR_PTR(-ENOSPC);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue