[PATCH] btrfs: fix throttle_on_drops deadlock with user transactions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxx>
---
 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@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux