dsl_dir: avoid dd_lock during snapshots_changed updates

Avoid holding dd_lock while updating the on-disk
snapshots_changed timestamp.

Both dsl_dir_zapify() and zap_update() may dirty buffers
and recurse into space accounting, which can take dd_lock.
Holding dd_lock across either operation can therefore
preserve the lock-order inversion reported by lockdep.

Only protect the in-memory dd_snap_cmtime update
with dd_lock. Perform the zapify and ZAP update without
dd_lock held, and retry the on-disk write if another updater
advanced dd_snap_cmtime while the write was in progress.

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: ZhengYuan Huang <[email protected]>
Co-authored-by: gality369 <[email protected]>
Closes #18472
This commit is contained in:
Gality authored and GitHub committed 2026-05-11 13:13:28 -07:00
1 parent 968f4db039
commit d50f5b6d0b
1 file changed
+18 -11
+18 -11
View File
@@ -2304,22 +2304,29 @@ dsl_dir_snap_cmtime_update(dsl_dir_t *dd, dmu_tx_t *tx)
{
dsl_pool_t *dp = dmu_tx_pool(tx);
inode_timespec_t t;
ASSERT(dsl_pool_sync_context(dp));
gethrestime(&t);
mutex_enter(&dd->dd_lock);
dd->dd_snap_cmtime = t;
if (spa_feature_is_enabled(dp->dp_spa,
SPA_FEATURE_EXTENSIBLE_DATASET)) {
objset_t *mos = dd->dd_pool->dp_meta_objset;
uint64_t ddobj = dd->dd_object;
dsl_dir_zapify(dd, tx);
VERIFY0(zap_update(mos, ddobj,
DD_FIELD_SNAPSHOTS_CHANGED,
sizeof (uint64_t),
sizeof (inode_timespec_t) / sizeof (uint64_t),
&t, tx));
}
mutex_exit(&dd->dd_lock);
if (!spa_feature_is_enabled(dp->dp_spa,
SPA_FEATURE_EXTENSIBLE_DATASET)) {
return;
}
objset_t *mos = dd->dd_pool->dp_meta_objset;
/*
* dsl_dir_zapify() and zap_update() may dirty buffers and recurse
* into space accounting, so do not call them with dd_lock held.
*/
dsl_dir_zapify(dd, tx);
VERIFY0(zap_update(mos, dd->dd_object, DD_FIELD_SNAPSHOTS_CHANGED,
sizeof (uint64_t),
sizeof (inode_timespec_t) / sizeof (uint64_t), &t, tx));
}
void