]> granicus.if.org Git - zfs/commitdiff
OpenZFS 7086 - ztest attempts dva_get_dsize_sync on an embedded blockpointer
authorMatthew Ahrens <mahrens@delphix.com>
Mon, 29 Aug 2016 18:40:16 +0000 (11:40 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 30 Aug 2016 21:25:50 +0000 (14:25 -0700)
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 <prakash.surya@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7086
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/98fa317
Closes #5039

module/zfs/dbuf.c

index d297506e97ffa94a8b3094f9b46e1f6b416c101e..e452fd23a0cdff067a89ea522b76abf66292801e 100644 (file)
@@ -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);