Sage Weil
2009-Apr-13 16:40 UTC
[PATCH] btrfs: fix throttle_on_drops deadlock with user transactions
Starting in b7ec40d7845bffca8bb3af2ea3f192d6257bbe21, drop_dirty_roots()
tries to avoid generating delayed refs in a transaction that is currently
closing (and trying to flush dirty refs out) by waiting for it to close.
However, if a transaction is held open by a user space TRANS_START ioctl,
that will deadlock against throttle_on_drops().
The underlying problem is that by the time we are ready to wait in
wait_transaction_pre_flush(), drop_dirty_roots() has already signaled its
intent to drop in fs_info->throttles, and the process calling
throttle_on_drops() will block. If the throttling process is part of a
user transaction, the transaction will never close and we deadlock.
Fix this by calling throttle_on_drops() only when we are very clearly
not part of a running user transaction (that is, when the open_ioctl_trans
counter is 0). Then do the throttle explicitly in
btrfs_start_ioctl_transaction().
Signed-off-by: Sage Weil <sage@newdream.net>
---
fs/btrfs/transaction.c | 40 ++++++++++++++++++++++++++++++++--------
1 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 2869b33..4a8d4f0 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -207,12 +207,6 @@ struct btrfs_trans_handle *btrfs_join_transaction(struct
btrfs_root *root,
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);
-}
-
/* wait for a transaction commit to be fully complete */
static noinline int wait_for_commit(struct btrfs_root *root,
struct btrfs_transaction *commit)
@@ -274,13 +268,35 @@ harder:
}
}
+/*
+ * if we are starting a user transaction, throttle_on_drops here (the
+ * usual paths won''t if the open_ioctl_trans count is non-zero). and
+ * tell start_transaction it is safe to wait_current_trans even if
+ * other user transactions are open.
+ */
+struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *r,
+ int num_blocks)
+{
+ throttle_on_drops(r);
+ return start_transaction(r, num_blocks, 2);
+}
+
+/*
+ * if open_ioctl_trans>0, we may be part of a user transaction and it
+ * is not safe to wait_current_trans or throttle_on_drops.
+ */
void btrfs_throttle(struct btrfs_root *root)
{
+ int throttle = 1;
+
mutex_lock(&root->fs_info->trans_mutex);
- if (!root->fs_info->open_ioctl_trans)
+ if (root->fs_info->open_ioctl_trans)
+ throttle = 0;
+ else
wait_current_trans(root);
mutex_unlock(&root->fs_info->trans_mutex);
- throttle_on_drops(root);
+ if (throttle)
+ throttle_on_drops(root);
}
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
@@ -319,6 +335,14 @@ static int __btrfs_end_transaction(struct
btrfs_trans_handle *trans,
if (waitqueue_active(&cur_trans->writer_wait))
wake_up(&cur_trans->writer_wait);
put_transaction(cur_trans);
+
+ /*
+ * if open_ioctl_trans>0, we may be part of a usertrans and it
+ * is not safe to throttle_on_drops.
+ */
+ if (root->fs_info->open_ioctl_trans)
+ throttle = 0;
+
mutex_unlock(&info->trans_mutex);
memset(trans, 0, sizeof(*trans));
kmem_cache_free(btrfs_trans_handle_cachep, trans);
--
1.5.6.5
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Chris Mason
2009-Apr-23 19:48 UTC
Re: [PATCH] btrfs: fix throttle_on_drops deadlock with user transactions
On Mon, 2009-04-13 at 09:40 -0700, Sage Weil wrote:> Starting in b7ec40d7845bffca8bb3af2ea3f192d6257bbe21, drop_dirty_roots() > tries to avoid generating delayed refs in a transaction that is currently > closing (and trying to flush dirty refs out) by waiting for it to close. > However, if a transaction is held open by a user space TRANS_START ioctl, > that will deadlock against throttle_on_drops(). > > The underlying problem is that by the time we are ready to wait in > wait_transaction_pre_flush(), drop_dirty_roots() has already signaled its > intent to drop in fs_info->throttles, and the process calling > throttle_on_drops() will block. If the throttling process is part of a > user transaction, the transaction will never close and we deadlock. >Thanks for this patch, I think that even if non-userland ioctl code doesn''t deadlock it can wait too long for throttle_on_drops in this case. Would something like the patch below also work?: diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 2869b33..01b1436 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -687,7 +687,13 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info) prepare_to_wait(&info->transaction_wait, &wait, TASK_UNINTERRUPTIBLE); mutex_unlock(&info->trans_mutex); + + atomic_dec(&info->throttles); + wake_up(&info->transaction_throttle); + schedule(); + + atomic_inc(&info->throttles); mutex_lock(&info->trans_mutex); finish_wait(&info->transaction_wait, &wait); } -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Sage Weil
2009-Apr-23 20:52 UTC
Re: [PATCH] btrfs: fix throttle_on_drops deadlock with user transactions
On Thu, 23 Apr 2009, Chris Mason wrote:> On Mon, 2009-04-13 at 09:40 -0700, Sage Weil wrote: > > Starting in b7ec40d7845bffca8bb3af2ea3f192d6257bbe21, drop_dirty_roots() > > tries to avoid generating delayed refs in a transaction that is currently > > closing (and trying to flush dirty refs out) by waiting for it to close. > > However, if a transaction is held open by a user space TRANS_START ioctl, > > that will deadlock against throttle_on_drops(). > > > > The underlying problem is that by the time we are ready to wait in > > wait_transaction_pre_flush(), drop_dirty_roots() has already signaled its > > intent to drop in fs_info->throttles, and the process calling > > throttle_on_drops() will block. If the throttling process is part of a > > user transaction, the transaction will never close and we deadlock. > > > > Thanks for this patch, > > I think that even if non-userland ioctl code doesn''t deadlock it can > wait too long for throttle_on_drops in this case. > > Would something like the patch below also work?:That does the trick, yep. Thanks! sage> > diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c > index 2869b33..01b1436 100644 > --- a/fs/btrfs/transaction.c > +++ b/fs/btrfs/transaction.c > @@ -687,7 +687,13 @@ static noinline int wait_transaction_pre_flush(struct btrfs_fs_info *info) > prepare_to_wait(&info->transaction_wait, &wait, > TASK_UNINTERRUPTIBLE); > mutex_unlock(&info->trans_mutex); > + > + atomic_dec(&info->throttles); > + wake_up(&info->transaction_throttle); > + > schedule(); > + > + atomic_inc(&info->throttles); > mutex_lock(&info->trans_mutex); > finish_wait(&info->transaction_wait, &wait); > } > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > >-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html