aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Mooney <[email protected]>2020-08-26 23:48:29 -0500
committerGitHub <[email protected]>2020-08-26 21:48:29 -0700
commit8d42c98d953f0a0b522950d712f22957e06bad48 (patch)
treeaf0d16031975438746c5e6835a3e07ef3559e79f
parent600def792e17ba4e31327fa792f0e03e3ab6a2a3 (diff)
dnode_sync is careless with range tree
Because dnode_sync_free_range() must drop dn_mtx during its processing, using it as a callback to range_tree_vacate() is not safe. No other operations (besides destroy) are allowed once range_tree_vacate() has begun, and dropping dn_mtx would leave a window open for another thread to observe that invalid (and unsafe) state via dnode_block_freed(). Reviewed-by: Matthew Ahrens <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Igor Kozhukhov <[email protected]> Signed-off-by: Patrick Mooney <[email protected]> Closes #10708 Closes #10823
-rw-r--r--module/zfs/dnode_sync.c14
1 files changed, 12 insertions, 2 deletions
diff --git a/module/zfs/dnode_sync.c b/module/zfs/dnode_sync.c
index eafea3403..ae44cb697 100644
--- a/module/zfs/dnode_sync.c
+++ b/module/zfs/dnode_sync.c
@@ -23,6 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
+ * Copyright 2020 Oxide Computer Company
*/
#include <sys/zfs_context.h>
@@ -762,13 +763,22 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dsfra.dsfra_dnode = dn;
dsfra.dsfra_tx = tx;
dsfra.dsfra_free_indirects = freeing_dnode;
+ mutex_enter(&dn->dn_mtx);
if (freeing_dnode) {
ASSERT(range_tree_contains(dn->dn_free_ranges[txgoff],
0, dn->dn_maxblkid + 1));
}
- mutex_enter(&dn->dn_mtx);
- range_tree_vacate(dn->dn_free_ranges[txgoff],
+ /*
+ * Because dnode_sync_free_range() must drop dn_mtx during its
+ * processing, using it as a callback to range_tree_vacate() is
+ * not safe. No other operations (besides destroy) are allowed
+ * once range_tree_vacate() has begun, and dropping dn_mtx
+ * would leave a window open for another thread to observe that
+ * invalid (and unsafe) state.
+ */
+ range_tree_walk(dn->dn_free_ranges[txgoff],
dnode_sync_free_range, &dsfra);
+ range_tree_vacate(dn->dn_free_ranges[txgoff], NULL, NULL);
range_tree_destroy(dn->dn_free_ranges[txgoff]);
dn->dn_free_ranges[txgoff] = NULL;
mutex_exit(&dn->dn_mtx);