Commit 96c3f433 authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

Btrfs: flush delayed inodes if we're short on space

Those crazy gentoo guys have been complaining about ENOSPC errors on their
portage volumes.  This is because doing things like untar tends to create
lots of new files which will soak up all the reservation space in the
delayed inodes.  Usually this gets papered over by the fact that we will try
and commit the transaction, however if this happens in the wrong spot or we
choose not to commit the transaction you will be screwed.  So add the
ability to expclitly flush delayed inodes to free up space.  Please test
this out guys to make sure it works since as usual I cannot reproduce.
Thanks,
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent b27f7c0c
...@@ -1113,8 +1113,8 @@ static int btrfs_update_delayed_inode(struct btrfs_trans_handle *trans, ...@@ -1113,8 +1113,8 @@ static int btrfs_update_delayed_inode(struct btrfs_trans_handle *trans,
* Returns < 0 on error and returns with an aborted transaction with any * Returns < 0 on error and returns with an aborted transaction with any
* outstanding delayed items cleaned up. * outstanding delayed items cleaned up.
*/ */
int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root) struct btrfs_root *root, int nr)
{ {
struct btrfs_root *curr_root = root; struct btrfs_root *curr_root = root;
struct btrfs_delayed_root *delayed_root; struct btrfs_delayed_root *delayed_root;
...@@ -1122,6 +1122,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, ...@@ -1122,6 +1122,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_block_rsv *block_rsv; struct btrfs_block_rsv *block_rsv;
int ret = 0; int ret = 0;
bool count = (nr > 0);
if (trans->aborted) if (trans->aborted)
return -EIO; return -EIO;
...@@ -1137,7 +1138,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, ...@@ -1137,7 +1138,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
delayed_root = btrfs_get_delayed_root(root); delayed_root = btrfs_get_delayed_root(root);
curr_node = btrfs_first_delayed_node(delayed_root); curr_node = btrfs_first_delayed_node(delayed_root);
while (curr_node) { while (curr_node && (!count || (count && nr--))) {
curr_root = curr_node->root; curr_root = curr_node->root;
ret = btrfs_insert_delayed_items(trans, path, curr_root, ret = btrfs_insert_delayed_items(trans, path, curr_root,
curr_node); curr_node);
...@@ -1149,6 +1150,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, ...@@ -1149,6 +1150,7 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
path, curr_node); path, curr_node);
if (ret) { if (ret) {
btrfs_release_delayed_node(curr_node); btrfs_release_delayed_node(curr_node);
curr_node = NULL;
btrfs_abort_transaction(trans, root, ret); btrfs_abort_transaction(trans, root, ret);
break; break;
} }
...@@ -1158,12 +1160,26 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, ...@@ -1158,12 +1160,26 @@ int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
btrfs_release_delayed_node(prev_node); btrfs_release_delayed_node(prev_node);
} }
if (curr_node)
btrfs_release_delayed_node(curr_node);
btrfs_free_path(path); btrfs_free_path(path);
trans->block_rsv = block_rsv; trans->block_rsv = block_rsv;
return ret; return ret;
} }
int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
return __btrfs_run_delayed_items(trans, root, -1);
}
int btrfs_run_delayed_items_nr(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int nr)
{
return __btrfs_run_delayed_items(trans, root, nr);
}
static int __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans, static int __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
struct btrfs_delayed_node *node) struct btrfs_delayed_node *node)
{ {
......
...@@ -107,6 +107,8 @@ int btrfs_inode_delayed_dir_index_count(struct inode *inode); ...@@ -107,6 +107,8 @@ int btrfs_inode_delayed_dir_index_count(struct inode *inode);
int btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int btrfs_run_delayed_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_run_delayed_items_nr(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int nr);
void btrfs_balance_delayed_items(struct btrfs_root *root); void btrfs_balance_delayed_items(struct btrfs_root *root);
......
...@@ -3728,6 +3728,60 @@ static int may_commit_transaction(struct btrfs_root *root, ...@@ -3728,6 +3728,60 @@ static int may_commit_transaction(struct btrfs_root *root,
return btrfs_commit_transaction(trans, root); return btrfs_commit_transaction(trans, root);
} }
enum flush_state {
FLUSH_DELALLOC = 1,
FLUSH_DELALLOC_WAIT = 2,
FLUSH_DELAYED_ITEMS_NR = 3,
FLUSH_DELAYED_ITEMS = 4,
COMMIT_TRANS = 5,
};
static int flush_space(struct btrfs_root *root,
struct btrfs_space_info *space_info, u64 num_bytes,
u64 orig_bytes, int state)
{
struct btrfs_trans_handle *trans;
int nr;
int ret;
switch (state) {
case FLUSH_DELALLOC:
case FLUSH_DELALLOC_WAIT:
ret = shrink_delalloc(root, num_bytes,
state == FLUSH_DELALLOC_WAIT);
if (ret > 0)
ret = 0;
break;
case FLUSH_DELAYED_ITEMS_NR:
case FLUSH_DELAYED_ITEMS:
if (state == FLUSH_DELAYED_ITEMS_NR) {
u64 bytes = btrfs_calc_trans_metadata_size(root, 1);
nr = (int)div64_u64(num_bytes, bytes);
if (!nr)
nr = 1;
nr *= 2;
} else {
nr = -1;
}
trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
break;
}
ret = btrfs_run_delayed_items_nr(trans, root, nr);
btrfs_end_transaction(trans, root);
break;
case COMMIT_TRANS:
ret = may_commit_transaction(root, space_info, orig_bytes, 0);
break;
default:
ret = -ENOSPC;
break;
}
return ret;
}
/** /**
* reserve_metadata_bytes - try to reserve bytes from the block_rsv's space * reserve_metadata_bytes - try to reserve bytes from the block_rsv's space
* @root - the root we're allocating for * @root - the root we're allocating for
...@@ -3749,11 +3803,10 @@ static int reserve_metadata_bytes(struct btrfs_root *root, ...@@ -3749,11 +3803,10 @@ static int reserve_metadata_bytes(struct btrfs_root *root,
struct btrfs_space_info *space_info = block_rsv->space_info; struct btrfs_space_info *space_info = block_rsv->space_info;
u64 used; u64 used;
u64 num_bytes = orig_bytes; u64 num_bytes = orig_bytes;
int retries = 0; int flush_state = FLUSH_DELALLOC;
int ret = 0; int ret = 0;
bool committed = false;
bool flushing = false; bool flushing = false;
bool wait_ordered = false; bool committed = false;
again: again:
ret = 0; ret = 0;
...@@ -3812,9 +3865,8 @@ static int reserve_metadata_bytes(struct btrfs_root *root, ...@@ -3812,9 +3865,8 @@ static int reserve_metadata_bytes(struct btrfs_root *root,
* amount plus the amount of bytes that we need for this * amount plus the amount of bytes that we need for this
* reservation. * reservation.
*/ */
wait_ordered = true;
num_bytes = used - space_info->total_bytes + num_bytes = used - space_info->total_bytes +
(orig_bytes * (retries + 1)); (orig_bytes * 2);
} }
if (ret) { if (ret) {
...@@ -3867,8 +3919,6 @@ static int reserve_metadata_bytes(struct btrfs_root *root, ...@@ -3867,8 +3919,6 @@ static int reserve_metadata_bytes(struct btrfs_root *root,
trace_btrfs_space_reservation(root->fs_info, trace_btrfs_space_reservation(root->fs_info,
"space_info", space_info->flags, orig_bytes, 1); "space_info", space_info->flags, orig_bytes, 1);
ret = 0; ret = 0;
} else {
wait_ordered = true;
} }
} }
...@@ -3887,36 +3937,13 @@ static int reserve_metadata_bytes(struct btrfs_root *root, ...@@ -3887,36 +3937,13 @@ static int reserve_metadata_bytes(struct btrfs_root *root,
if (!ret || !flush) if (!ret || !flush)
goto out; goto out;
/* ret = flush_space(root, space_info, num_bytes, orig_bytes,
* We do synchronous shrinking since we don't actually unreserve flush_state);
* metadata until after the IO is completed. flush_state++;
*/ if (!ret)
ret = shrink_delalloc(root, num_bytes, wait_ordered);
if (ret < 0)
goto out;
ret = 0;
/*
* So if we were overcommitted it's possible that somebody else flushed
* out enough space and we simply didn't have enough space to reclaim,
* so go back around and try again.
*/
if (retries < 2) {
wait_ordered = true;
retries++;
goto again; goto again;
} else if (flush_state <= COMMIT_TRANS)
ret = -ENOSPC;
if (committed)
goto out;
ret = may_commit_transaction(root, space_info, orig_bytes, 0);
if (!ret) {
committed = true;
goto again; goto again;
}
out: out:
if (flushing) { if (flushing) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment