Btrfs: fix ioctl-initiated transactions vs wait_current_trans()

Commit 597:466b27332893 (btrfs_start_transaction: wait for commits in
progress) breaks the transaction start/stop ioctls by making
btrfs_start_transaction conditionally wait for the next transaction to
start.  If an application artificially is holding a transaction open,
things deadlock.

This workaround maintains a count of open ioctl-initiated transactions in
fs_info, and avoids wait_current_trans() if any are currently open (in
start_transaction() and btrfs_throttle()).  The start transaction ioctl
uses a new btrfs_start_ioctl_transaction() that _does_ call
wait_current_trans(), effectively pushing the join/wait decision to the
outer ioctl-initiated transaction.

This more or less neuters btrfs_throttle() when ioctl-initiated
transactions are in use, but that seems like a pretty fundamental
consequence of wrapping lots of write()'s in a transaction.  Btrfs has no
way to tell if the application considers a given operation as part of it's
transaction.

Obviously, if the transaction start/stop ioctls aren't being used, there
is no effect on current behavior.

Signed-off-by: Sage Weil <sage@newdream.net>
---
 ctree.h       |    1 +
 ioctl.c       |   12 +++++++++++-
 transaction.c |   18 +++++++++++++-----
 transaction.h |    2 ++
 4 files changed, 27 insertions(+), 6 deletions(-)

Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
Sage Weil 2008-08-04 10:41:27 -04:00 committed by Chris Mason
parent 3117a77370
commit 9ca9ee09c1
4 changed files with 27 additions and 6 deletions

View File

@ -518,6 +518,7 @@ struct btrfs_fs_info {
u64 generation; u64 generation;
u64 last_trans_committed; u64 last_trans_committed;
u64 open_ioctl_trans;
unsigned long mount_opt; unsigned long mount_opt;
u64 max_extent; u64 max_extent;
u64 max_inline; u64 max_inline;

View File

@ -715,7 +715,12 @@ long btrfs_ioctl_trans_start(struct file *file)
ret = -EINPROGRESS; ret = -EINPROGRESS;
goto out; goto out;
} }
trans = btrfs_start_transaction(root, 0);
mutex_lock(&root->fs_info->trans_mutex);
root->fs_info->open_ioctl_trans++;
mutex_unlock(&root->fs_info->trans_mutex);
trans = btrfs_start_ioctl_transaction(root, 0);
if (trans) if (trans)
file->private_data = trans; file->private_data = trans;
else else
@ -745,6 +750,11 @@ long btrfs_ioctl_trans_end(struct file *file)
} }
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
file->private_data = 0; file->private_data = 0;
mutex_lock(&root->fs_info->trans_mutex);
root->fs_info->open_ioctl_trans--;
mutex_unlock(&root->fs_info->trans_mutex);
out: out:
return ret; return ret;
} }

View File

@ -152,14 +152,14 @@ static void wait_current_trans(struct btrfs_root *root)
} }
struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
int num_blocks, int join) int num_blocks, int wait)
{ {
struct btrfs_trans_handle *h = struct btrfs_trans_handle *h =
kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS);
int ret; int ret;
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
if (!join) if ((wait == 1 && !root->fs_info->open_ioctl_trans) || wait == 2)
wait_current_trans(root); wait_current_trans(root);
ret = join_transaction(root); ret = join_transaction(root);
BUG_ON(ret); BUG_ON(ret);
@ -180,14 +180,21 @@ struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
int num_blocks) int num_blocks)
{ {
return start_transaction(root, num_blocks, 0); return start_transaction(root, num_blocks, 1);
} }
struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root, struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root,
int num_blocks) int num_blocks)
{ {
return start_transaction(root, num_blocks, 1); return start_transaction(root, num_blocks, 0);
} }
struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *r,
int num_blocks)
{
return start_transaction(r, num_blocks, 2);
}
static noinline int wait_for_commit(struct btrfs_root *root, static noinline int wait_for_commit(struct btrfs_root *root,
struct btrfs_transaction *commit) struct btrfs_transaction *commit)
{ {
@ -247,7 +254,8 @@ harder:
void btrfs_throttle(struct btrfs_root *root) void btrfs_throttle(struct btrfs_root *root)
{ {
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
wait_current_trans(root); if (!root->fs_info->open_ioctl_trans)
wait_current_trans(root);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
throttle_on_drops(root); throttle_on_drops(root);

View File

@ -83,6 +83,8 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
int num_blocks); int num_blocks);
struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root, struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root,
int num_blocks); int num_blocks);
struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *r,
int num_blocks);
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);
int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,