]> git.proxmox.com Git - mirror_zfs.git/commitdiff
dbuf_sync_leaf: check DB_READ in state assertions
authorRob Norris <rob.norris@klarasystems.com>
Mon, 24 Jul 2023 08:02:21 +0000 (18:02 +1000)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 24 Jul 2023 23:36:17 +0000 (16:36 -0700)
Block cloning introduced a new state transition from DB_NOFILL to
DB_READ. This occurs when a block is cloned and then read on the
current txg.

In this case, the clone will move the dbuf to DB_NOFILL, and then the
read will be issued for the overidden block pointer. If that read is
still outstanding when it comes time to write, the dbuf will be in
DB_READ, which is not handled by the checks in dbuf_sync_leaf, thus
tripping the assertions.

This updates those checks to allow DB_READ as a valid state iff the
dirty record is for a BRT write and there is a override block pointer.
This is a safe situation because the block already exists, so there's
nothing that could change from underneath the read.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Kay Pedersen <mail@mkwg.de>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Original-patch-by: Kay Pedersen <mail@mkwg.de>
Sponsored-By: OpenDrives Inc.
Sponsored-By: Klara Inc.
Closes #15050

module/zfs/dbuf.c

index fbeac866ae91fc12922c637b9214e911cb2a437a..b7453578a76fef56b71c30003444884b8458080a 100644 (file)
@@ -4457,6 +4457,15 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
        } else if (db->db_state == DB_FILL) {
                /* This buffer was freed and is now being re-filled */
                ASSERT(db->db.db_data != dr->dt.dl.dr_data);
+       } else if (db->db_state == DB_READ) {
+               /*
+                * This buffer has a clone we need to write, and an in-flight
+                * read on the BP we're about to clone. Its safe to issue the
+                * write here because the read has already been issued and the
+                * contents won't change.
+                */
+               ASSERT(dr->dt.dl.dr_brtwrite &&
+                   dr->dt.dl.dr_override_state == DR_OVERRIDDEN);
        } else {
                ASSERT(db->db_state == DB_CACHED || db->db_state == DB_NOFILL);
        }