Add btrfs_end_transaction_throttle to force writers to wait for pending commits
The existing throttle mechanism was often not sufficient to prevent new writers from coming in and making a given transaction run forever. This adds an explicit wait at the end of most operations so they will allow the current transaction to close. There is no wait inside file_write, inode updates, or cow filling, all which have different deadlock possibilities. This is a temporary measure until better asynchronous commit support is added. This code leads to stalls as it waits for data=ordered writeback, and it really needs to be fixed. Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
Родитель
333db94cdd
Коммит
89ce8a63d0
|
@ -1672,24 +1672,6 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
|
||||||
set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, buf);
|
set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void btrfs_throttle(struct btrfs_root *root)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
struct backing_dev_info *bdi;
|
|
||||||
|
|
||||||
bdi = &root->fs_info->bdi;
|
|
||||||
if (atomic_read(&root->fs_info->throttles) &&
|
|
||||||
bdi_write_congested(bdi)) {
|
|
||||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
|
|
||||||
congestion_wait(WRITE, HZ/20);
|
|
||||||
#else
|
|
||||||
blk_congestion_wait(WRITE, HZ/20);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
|
void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -70,7 +70,6 @@ int btrfs_clear_buffer_defrag_done(struct extent_buffer *buf);
|
||||||
int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid);
|
int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid);
|
||||||
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
|
u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
|
||||||
void btrfs_csum_final(u32 crc, char *result);
|
void btrfs_csum_final(u32 crc, char *result);
|
||||||
void btrfs_throttle(struct btrfs_root *root);
|
|
||||||
int btrfs_open_device(struct btrfs_device *dev);
|
int btrfs_open_device(struct btrfs_device *dev);
|
||||||
int btrfs_verify_block_csum(struct btrfs_root *root,
|
int btrfs_verify_block_csum(struct btrfs_root *root,
|
||||||
struct extent_buffer *buf);
|
struct extent_buffer *buf);
|
||||||
|
|
|
@ -934,7 +934,6 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
|
||||||
balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
|
balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
|
||||||
if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
|
if (num_pages < (root->leafsize >> PAGE_CACHE_SHIFT) + 1)
|
||||||
btrfs_btree_balance_dirty(root, 1);
|
btrfs_btree_balance_dirty(root, 1);
|
||||||
btrfs_throttle(root);
|
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -855,10 +855,9 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
btrfs_del_ordered_inode(inode, 1);
|
btrfs_del_ordered_inode(inode, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
fail:
|
fail:
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -889,10 +888,9 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
ret = btrfs_end_transaction(trans, root);
|
ret = btrfs_end_transaction_throttle(trans, root);
|
||||||
fail:
|
fail:
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
|
|
||||||
if (ret && !err)
|
if (ret && !err)
|
||||||
err = ret;
|
err = ret;
|
||||||
|
@ -1871,14 +1869,13 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
|
||||||
btrfs_update_inode_block_group(trans, dir);
|
btrfs_update_inode_block_group(trans, dir);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
fail:
|
fail:
|
||||||
if (drop_inode) {
|
if (drop_inode) {
|
||||||
inode_dec_link_count(inode);
|
inode_dec_link_count(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1936,14 +1933,13 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
btrfs_update_inode_block_group(trans, dir);
|
btrfs_update_inode_block_group(trans, dir);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
fail:
|
fail:
|
||||||
if (drop_inode) {
|
if (drop_inode) {
|
||||||
inode_dec_link_count(inode);
|
inode_dec_link_count(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1985,14 +1981,13 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||||
drop_inode = 1;
|
drop_inode = 1;
|
||||||
|
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
fail:
|
fail:
|
||||||
if (drop_inode) {
|
if (drop_inode) {
|
||||||
inode_dec_link_count(inode);
|
inode_dec_link_count(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2055,13 +2050,12 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||||
|
|
||||||
out_fail:
|
out_fail:
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
if (drop_on_err)
|
if (drop_on_err)
|
||||||
iput(inode);
|
iput(inode);
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2587,10 +2581,9 @@ static void btrfs_truncate(struct inode *inode)
|
||||||
btrfs_update_inode(trans, root, inode);
|
btrfs_update_inode(trans, root, inode);
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
|
|
||||||
ret = btrfs_end_transaction(trans, root);
|
ret = btrfs_end_transaction_throttle(trans, root);
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2912,14 +2905,13 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
nr = trans->blocks_used;
|
nr = trans->blocks_used;
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction_throttle(trans, root);
|
||||||
out_fail:
|
out_fail:
|
||||||
if (drop_inode) {
|
if (drop_inode) {
|
||||||
inode_dec_link_count(inode);
|
inode_dec_link_count(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,6 @@ fail:
|
||||||
ret = err;
|
ret = err;
|
||||||
fail_commit:
|
fail_commit:
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +205,6 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
|
||||||
|
|
||||||
fail_unlock:
|
fail_unlock:
|
||||||
btrfs_btree_balance_dirty(root, nr);
|
btrfs_btree_balance_dirty(root, nr);
|
||||||
btrfs_throttle(root);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,8 +130,27 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
static noinline int wait_for_commit(struct btrfs_root *root,
|
||||||
struct btrfs_root *root)
|
struct btrfs_transaction *commit)
|
||||||
|
{
|
||||||
|
DEFINE_WAIT(wait);
|
||||||
|
mutex_lock(&root->fs_info->trans_mutex);
|
||||||
|
while(!commit->commit_done) {
|
||||||
|
prepare_to_wait(&commit->commit_wait, &wait,
|
||||||
|
TASK_UNINTERRUPTIBLE);
|
||||||
|
if (commit->commit_done)
|
||||||
|
break;
|
||||||
|
mutex_unlock(&root->fs_info->trans_mutex);
|
||||||
|
schedule();
|
||||||
|
mutex_lock(&root->fs_info->trans_mutex);
|
||||||
|
}
|
||||||
|
mutex_unlock(&root->fs_info->trans_mutex);
|
||||||
|
finish_wait(&commit->commit_wait, &wait);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root, int throttle)
|
||||||
{
|
{
|
||||||
struct btrfs_transaction *cur_trans;
|
struct btrfs_transaction *cur_trans;
|
||||||
|
|
||||||
|
@ -140,8 +159,18 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||||
WARN_ON(cur_trans != trans->transaction);
|
WARN_ON(cur_trans != trans->transaction);
|
||||||
WARN_ON(cur_trans->num_writers < 1);
|
WARN_ON(cur_trans->num_writers < 1);
|
||||||
cur_trans->num_writers--;
|
cur_trans->num_writers--;
|
||||||
|
|
||||||
if (waitqueue_active(&cur_trans->writer_wait))
|
if (waitqueue_active(&cur_trans->writer_wait))
|
||||||
wake_up(&cur_trans->writer_wait);
|
wake_up(&cur_trans->writer_wait);
|
||||||
|
|
||||||
|
if (cur_trans->in_commit && throttle) {
|
||||||
|
int ret;
|
||||||
|
mutex_unlock(&root->fs_info->trans_mutex);
|
||||||
|
ret = wait_for_commit(root, cur_trans);
|
||||||
|
BUG_ON(ret);
|
||||||
|
mutex_lock(&root->fs_info->trans_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
put_transaction(cur_trans);
|
put_transaction(cur_trans);
|
||||||
mutex_unlock(&root->fs_info->trans_mutex);
|
mutex_unlock(&root->fs_info->trans_mutex);
|
||||||
memset(trans, 0, sizeof(*trans));
|
memset(trans, 0, sizeof(*trans));
|
||||||
|
@ -149,6 +178,18 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
return __btrfs_end_transaction(trans, root, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root)
|
||||||
|
{
|
||||||
|
return __btrfs_end_transaction(trans, root, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
|
int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root)
|
struct btrfs_root *root)
|
||||||
|
@ -240,25 +281,6 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline int wait_for_commit(struct btrfs_root *root,
|
|
||||||
struct btrfs_transaction *commit)
|
|
||||||
{
|
|
||||||
DEFINE_WAIT(wait);
|
|
||||||
mutex_lock(&root->fs_info->trans_mutex);
|
|
||||||
while(!commit->commit_done) {
|
|
||||||
prepare_to_wait(&commit->commit_wait, &wait,
|
|
||||||
TASK_UNINTERRUPTIBLE);
|
|
||||||
if (commit->commit_done)
|
|
||||||
break;
|
|
||||||
mutex_unlock(&root->fs_info->trans_mutex);
|
|
||||||
schedule();
|
|
||||||
mutex_lock(&root->fs_info->trans_mutex);
|
|
||||||
}
|
|
||||||
mutex_unlock(&root->fs_info->trans_mutex);
|
|
||||||
finish_wait(&commit->commit_wait, &wait);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dirty_root {
|
struct dirty_root {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct btrfs_root *root;
|
struct btrfs_root *root;
|
||||||
|
@ -680,6 +702,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||||
root->fs_info->btree_inode->i_mapping, GFP_NOFS);
|
root->fs_info->btree_inode->i_mapping, GFP_NOFS);
|
||||||
|
|
||||||
trans->transaction->in_commit = 1;
|
trans->transaction->in_commit = 1;
|
||||||
|
printk("trans %Lu in commit\n", trans->transid);
|
||||||
cur_trans = trans->transaction;
|
cur_trans = trans->transaction;
|
||||||
if (cur_trans->list.prev != &root->fs_info->trans_list) {
|
if (cur_trans->list.prev != &root->fs_info->trans_list) {
|
||||||
prev_trans = list_entry(cur_trans->list.prev,
|
prev_trans = list_entry(cur_trans->list.prev,
|
||||||
|
@ -760,6 +783,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||||
kfree(pinned_copy);
|
kfree(pinned_copy);
|
||||||
|
|
||||||
cur_trans->commit_done = 1;
|
cur_trans->commit_done = 1;
|
||||||
|
printk("trans %Lu done in commit\n", cur_trans->transid);
|
||||||
root->fs_info->last_trans_committed = cur_trans->transid;
|
root->fs_info->last_trans_committed = cur_trans->transid;
|
||||||
wake_up(&cur_trans->commit_wait);
|
wake_up(&cur_trans->commit_wait);
|
||||||
put_transaction(cur_trans);
|
put_transaction(cur_trans);
|
||||||
|
|
|
@ -101,4 +101,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root);
|
struct btrfs_root *root);
|
||||||
int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
|
int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root);
|
struct btrfs_root *root);
|
||||||
|
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root);
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче