From 31c4fa93bb161e0e6afe6bb3d46f8fdcf43f5c84 Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Fri, 1 Aug 2025 09:58:53 -0700 Subject: [PATCH] Fix dynamic gang block headers on raidz and mirror devices Reviewed-by: Brian Behlendorf Reviewed-by: Alexander Motin Signed-off-by: Paul Dagnelie Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Closes #17587 --- include/sys/vdev.h | 14 ++++++++++++++ include/sys/vdev_impl.h | 1 - module/zfs/zio.c | 4 ++-- module/zfs/zio_checksum.c | 6 +++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/sys/vdev.h b/include/sys/vdev.h index 7f5a9aaef..510474d6c 100644 --- a/include/sys/vdev.h +++ b/include/sys/vdev.h @@ -139,6 +139,7 @@ extern uint64_t vdev_asize_to_psize_txg(vdev_t *vd, uint64_t asize, extern uint64_t vdev_psize_to_asize_txg(vdev_t *vd, uint64_t psize, uint64_t txg); extern uint64_t vdev_psize_to_asize(vdev_t *vd, uint64_t psize); +extern uint64_t vdev_get_min_alloc(vdev_t *vd); /* * Return the amount of space allocated for a gang block header. Note that @@ -151,6 +152,19 @@ vdev_gang_header_asize(vdev_t *vd) return (vdev_psize_to_asize_txg(vd, SPA_OLD_GANGBLOCKSIZE, 0)); } +/* + * Return the amount of data that can be stored in a gang header. Because we + * need to ensure gang headers can always be allocated (as long as there is + * space available), this is the minimum allocatable size on the vdev. Note that + * since the physical birth txg is not provided, this must be constant for + * a given vdev. (e.g. raidz expansion can't change this) + */ +static inline uint64_t +vdev_gang_header_psize(vdev_t *vd) +{ + return (vdev_get_min_alloc(vd)); +} + extern int vdev_fault(spa_t *spa, uint64_t guid, vdev_aux_t aux); extern int vdev_degrade(spa_t *spa, uint64_t guid, vdev_aux_t aux); extern int vdev_online(spa_t *spa, uint64_t guid, uint64_t flags, diff --git a/include/sys/vdev_impl.h b/include/sys/vdev_impl.h index fa22fa2ba..4ab472bd6 100644 --- a/include/sys/vdev_impl.h +++ b/include/sys/vdev_impl.h @@ -621,7 +621,6 @@ extern uint64_t vdev_default_asize(vdev_t *vd, uint64_t psize, uint64_t txg); extern uint64_t vdev_default_min_asize(vdev_t *vd); extern uint64_t vdev_get_min_asize(vdev_t *vd); extern void vdev_set_min_asize(vdev_t *vd); -extern uint64_t vdev_get_min_alloc(vdev_t *vd); extern uint64_t vdev_get_nparity(vdev_t *vd); extern uint64_t vdev_get_ndisks(vdev_t *vd); diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 7e4caaa83..0fde2d6f7 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -2956,8 +2956,8 @@ zio_gang_tree_assemble(zio_t *gio, blkptr_t *bp, zio_gang_node_t **gnpp) for (int dva = 0; dva < BP_GET_NDVAS(bp); dva++) { vdev_t *vd = vdev_lookup_top(gio->io_spa, DVA_GET_VDEV(&bp->blk_dva[dva])); - uint64_t asize = vdev_gang_header_asize(vd); - gangblocksize = MIN(gangblocksize, asize); + uint64_t psize = vdev_gang_header_psize(vd); + gangblocksize = MIN(gangblocksize, psize); } spa_config_exit(gio->io_spa, SCL_VDEV, FTAG); } else { diff --git a/module/zfs/zio_checksum.c b/module/zfs/zio_checksum.c index 8cec3a6f5..4cb9da0db 100644 --- a/module/zfs/zio_checksum.c +++ b/module/zfs/zio_checksum.c @@ -569,7 +569,11 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) SPA_OLD_GANGBLOCKSIZE, offset, info); if (error == 0) { ASSERT3U(zio->io_child_type, ==, ZIO_CHILD_VDEV); - zio_t *pio = zio_unique_parent(zio); + zio_t *pio; + for (pio = zio_unique_parent(zio); + pio->io_child_type != ZIO_CHILD_GANG; + pio = zio_unique_parent(pio)) + ; zio_gang_node_t *gn = pio->io_private; gn->gn_gangblocksize = SPA_OLD_GANGBLOCKSIZE; }