]> git.proxmox.com Git - mirror_zfs.git/commitdiff
dbuf: Handle arcbuf assignment after block cloning
authorAlexander Motin <mav@FreeBSD.org>
Tue, 12 Dec 2023 20:53:59 +0000 (15:53 -0500)
committerGitHub <noreply@github.com>
Tue, 12 Dec 2023 20:53:59 +0000 (12:53 -0800)
In some cases dbuf_assign_arcbuf() may be called on a block that
was recently cloned.  If it happened in current TXG we must undo
the block cloning first, since the only one dirty record per TXG
can't and shouldn't mean both cloning and overwrite same time.

Reviewed-by: Kay Pedersen <mail@mkwg.de>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Alexander Motin <mav@FreeBSD.org>
Sponsored by: iXsystems, Inc.
Closes #15653

module/zfs/dbuf.c

index c5ccd4cd1e0c8da1d9ea5363e45a02771ad1c6e0..a07fe1733586ee9c1f12115761b9c71247f3d54c 100644 (file)
@@ -2945,7 +2945,8 @@ dbuf_assign_arcbuf(dmu_buf_impl_t *db, arc_buf_t *buf, dmu_tx_t *tx)
        while (db->db_state == DB_READ || db->db_state == DB_FILL)
                cv_wait(&db->db_changed, &db->db_mtx);
 
-       ASSERT(db->db_state == DB_CACHED || db->db_state == DB_UNCACHED);
+       ASSERT(db->db_state == DB_CACHED || db->db_state == DB_UNCACHED ||
+           db->db_state == DB_NOFILL);
 
        if (db->db_state == DB_CACHED &&
            zfs_refcount_count(&db->db_holds) - 1 > db->db_dirtycnt) {
@@ -2982,6 +2983,15 @@ dbuf_assign_arcbuf(dmu_buf_impl_t *db, arc_buf_t *buf, dmu_tx_t *tx)
                        arc_buf_destroy(db->db_buf, db);
                }
                db->db_buf = NULL;
+       } else if (db->db_state == DB_NOFILL) {
+               /*
+                * We will be completely replacing the cloned block.  In case
+                * it was cloned in this transaction group, let's undirty the
+                * pending clone and mark the block as uncached. This will be
+                * as if the clone was never done.
+                */
+               VERIFY(!dbuf_undirty(db, tx));
+               db->db_state = DB_UNCACHED;
        }
        ASSERT(db->db_buf == NULL);
        dbuf_set_data(db, buf);