From b14b3e39858a3024e0ea38a296ffe9755258e8eb Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Fri, 4 Apr 2025 11:10:44 -0700 Subject: [PATCH] Fix FDT rollback to not overwrite unnecessary fields (#17205) When a dedup write fails, we try to roll the DDT entry back to a known good state. However, this also rolls the refcounts and the last-update time back to the state they were at when we started this write. This doesn't appear to be able to cause any refcount leaks (after the fix in 17123). This PR prevents that from happening by only rolling back the parts of the DDT entry that have been updated by the write so far. Sponsored-by: iXsystems, Inc. Sponsored-by: Klara, Inc. Signed-off-by: Paul Dagnelie Co-authored-by: Paul Dagnelie Reviewed-by: Alexander Motin Reviewed-by: Tony Hutter --- include/sys/ddt.h | 2 ++ module/zfs/ddt.c | 21 +++++++++++++++++++++ module/zfs/zio.c | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/sys/ddt.h b/include/sys/ddt.h index 8f2cc9c5a..8bdd7ca3a 100644 --- a/include/sys/ddt.h +++ b/include/sys/ddt.h @@ -339,6 +339,8 @@ extern void ddt_bp_create(enum zio_checksum checksum, const ddt_key_t *ddk, extern void ddt_phys_extend(ddt_univ_phys_t *ddp, ddt_phys_variant_t v, const blkptr_t *bp); +extern void ddt_phys_unextend(ddt_univ_phys_t *cur, ddt_univ_phys_t *orig, + ddt_phys_variant_t v); extern void ddt_phys_copy(ddt_univ_phys_t *dst, const ddt_univ_phys_t *src, ddt_phys_variant_t v); extern void ddt_phys_clear(ddt_univ_phys_t *ddp, ddt_phys_variant_t v); diff --git a/module/zfs/ddt.c b/module/zfs/ddt.c index ec577fff6..b0cd7f089 100644 --- a/module/zfs/ddt.c +++ b/module/zfs/ddt.c @@ -731,6 +731,27 @@ ddt_phys_extend(ddt_univ_phys_t *ddp, ddt_phys_variant_t v, const blkptr_t *bp) } } +void +ddt_phys_unextend(ddt_univ_phys_t *cur, ddt_univ_phys_t *orig, + ddt_phys_variant_t v) +{ + ASSERT3U(v, <, DDT_PHYS_NONE); + dva_t *cur_dvas = (v == DDT_PHYS_FLAT) ? + cur->ddp_flat.ddp_dva : cur->ddp_trad[v].ddp_dva; + dva_t *orig_dvas = (v == DDT_PHYS_FLAT) ? + orig->ddp_flat.ddp_dva : orig->ddp_trad[v].ddp_dva; + + for (int d = 0; d < SPA_DVAS_PER_BP; d++) + cur_dvas[d] = orig_dvas[d]; + + if (ddt_phys_birth(orig, v) == 0) { + if (v == DDT_PHYS_FLAT) + cur->ddp_flat.ddp_phys_birth = 0; + else + cur->ddp_trad[v].ddp_phys_birth = 0; + } +} + void ddt_phys_copy(ddt_univ_phys_t *dst, const ddt_univ_phys_t *src, ddt_phys_variant_t v) diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 5d39d28a7..47f229dcb 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -3607,7 +3607,7 @@ zio_ddt_child_write_done(zio_t *zio) * chain. We need to revert the entry back to what it was at * the last time it was successfully extended. */ - ddt_phys_copy(ddp, orig, v); + ddt_phys_unextend(ddp, orig, v); ddt_phys_clear(orig, v); ddt_exit(ddt);