aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Ahrens <[email protected]>2016-08-29 11:40:16 -0700
committerBrian Behlendorf <[email protected]>2016-08-30 14:25:50 -0700
commit98ace739bd89b541af30d9d627ee42622fbbd861 (patch)
tree61f79d07871452ab018ff73208311387ee0ffd5d
parentc40db193a5e503fffacf6d96a7dd48a0f1b36601 (diff)
OpenZFS 7086 - ztest attempts dva_get_dsize_sync on an embedded blockpointer
In dbuf_dirty(), we need to grab the dn_struct_rwlock before looking at the db_blkptr, to prevent it from being changed by syncing context. Reviewed by: Prakash Surya <[email protected]> Reviewed by: George Wilson <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> OpenZFS-issue: https://www.illumos.org/issues/7086 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/98fa317 Closes #5039
-rw-r--r--module/zfs/dbuf.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c
index d297506e9..e452fd23a 100644
--- a/module/zfs/dbuf.c
+++ b/module/zfs/dbuf.c
@@ -1468,7 +1468,20 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
dnode_setdirty(dn, tx);
DB_DNODE_EXIT(db);
return (dr);
- } else if (do_free_accounting) {
+ }
+
+ /*
+ * The dn_struct_rwlock prevents db_blkptr from changing
+ * due to a write from syncing context completing
+ * while we are running, so we want to acquire it before
+ * looking at db_blkptr.
+ */
+ if (!RW_WRITE_HELD(&dn->dn_struct_rwlock)) {
+ rw_enter(&dn->dn_struct_rwlock, RW_READER);
+ drop_struct_lock = TRUE;
+ }
+
+ if (do_free_accounting) {
blkptr_t *bp = db->db_blkptr;
int64_t willfree = (bp && !BP_IS_HOLE(bp)) ?
bp_get_dsize(os->os_spa, bp) : db->db.db_size;
@@ -1484,11 +1497,6 @@ dbuf_dirty(dmu_buf_impl_t *db, dmu_tx_t *tx)
dnode_willuse_space(dn, -willfree, tx);
}
- if (!RW_WRITE_HELD(&dn->dn_struct_rwlock)) {
- rw_enter(&dn->dn_struct_rwlock, RW_READER);
- drop_struct_lock = TRUE;
- }
-
if (db->db_level == 0) {
dnode_new_blkid(dn, db->db_blkid, tx, drop_struct_lock);
ASSERT(dn->dn_maxblkid >= db->db_blkid);