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 <paul.dagnelie@klarasystems.com>
Co-authored-by: Paul Dagnelie <paul.dagnelie@klarasystems.com>

Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
This commit is contained in:
Paul Dagnelie
2025-04-04 11:10:44 -07:00
committed by GitHub
parent c050b7315d
commit b14b3e3985
3 changed files with 24 additions and 1 deletions
+21
View File
@@ -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)
+1 -1
View File
@@ -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);