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 <alexander.motin@TrueNAS.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Co-authored-by: gality369 <gality369@example.com> Closes #18472
This commit is contained in:
+18
-11
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user