From d50f5b6d0b9e80d9974eddb1510bd0518509419d Mon Sep 17 00:00:00 2001 From: Gality <68463495+Gality369@users.noreply.github.com> Date: Tue, 12 May 2026 04:13:28 +0800 Subject: [PATCH] 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 Reviewed-by: Brian Behlendorf Signed-off-by: ZhengYuan Huang Co-authored-by: gality369 Closes #18472 --- module/zfs/dsl_dir.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index 335b11dc2ff..e88de3dbdfd 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -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