Skip to content

Commit

Permalink
dn_struct_rwlock can not be held in dmu_tx_try_assign()
Browse files Browse the repository at this point in the history
The thread calling dmu_tx_try_assign() 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*()).

This commit adds an assertion to dmu_tx_try_assign() to ensure that this
deadlock is not inadvertently introduced.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #8929
  • Loading branch information
ahrens authored and behlendorf committed Jun 22, 2019
1 parent ca4e5a7 commit cb9e5b7
Showing 1 changed file with 19 additions and 0 deletions.
19 changes: 19 additions & 0 deletions module/zfs/dmu_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit cb9e5b7

Please sign in to comment.