summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--module/zfs/dmu_tx.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/module/zfs/dmu_tx.c b/module/zfs/dmu_tx.c
index cbadcc86f..7d65e842f 100644
--- a/module/zfs/dmu_tx.c
+++ b/module/zfs/dmu_tx.c
@@ -925,6 +925,25 @@ dmu_tx_try_assign(dmu_tx_t *tx, uint64_t txg_how)
txh = list_next(&tx->tx_holds, txh)) {
dnode_t *dn = txh->txh_dnode;
if (dn != NULL) {
+ /*
+ * This thread can't hold the dn_struct_rwlock
+ * while assigning the tx, because this can lead to
+ * deadlock. Specifically, if this dnode is already
+ * assigned to an earlier txg, this thread may need
+ * to wait for that txg to sync (the ERESTART case
+ * below). The other thread that has assigned this
+ * dnode to an earlier txg prevents this txg from
+ * syncing until its tx can complete (calling
+ * dmu_tx_commit()), but it may need to acquire the
+ * dn_struct_rwlock to do so (e.g. via
+ * dmu_buf_hold*()).
+ *
+ * Note that this thread can't hold the lock for
+ * read either, but the rwlock doesn't record
+ * enough information to make that assertion.
+ */
+ ASSERT(!RW_WRITE_HELD(&dn->dn_struct_rwlock));
+
mutex_enter(&dn->dn_mtx);
if (dn->dn_assigned_txg == tx->tx_txg - 1) {
mutex_exit(&dn->dn_mtx);