xfs: split and refactor zone validation

Currently xfs_zone_validate mixes validating the software zone state in
the XFS realtime group with validating the hardware state reported in
struct blk_zone and deriving the write pointer from that.

Move all code that works on the realtime group to xfs_init_zone, and only
keep the hardware state validation in xfs_zone_validate.  This makes the
code more clear, and allows for better reuse in userspace.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Christoph Hellwig 2026-01-14 07:53:27 +01:00 committed by Carlos Maiolino
parent 776b76f754
commit 19c5b6051e
3 changed files with 68 additions and 114 deletions

View File

@ -15,173 +15,102 @@
#include "xfs_zones.h"
static bool
xfs_zone_validate_empty(
xfs_validate_blk_zone_seq(
struct xfs_mount *mp,
struct blk_zone *zone,
struct xfs_rtgroup *rtg,
unsigned int zone_no,
xfs_rgblock_t *write_pointer)
{
struct xfs_mount *mp = rtg_mount(rtg);
if (rtg_rmap(rtg)->i_used_blocks > 0) {
xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).",
rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
return false;
}
*write_pointer = 0;
return true;
}
static bool
xfs_zone_validate_wp(
struct blk_zone *zone,
struct xfs_rtgroup *rtg,
xfs_rgblock_t *write_pointer)
{
struct xfs_mount *mp = rtg_mount(rtg);
xfs_rtblock_t wp_fsb = xfs_daddr_to_rtb(mp, zone->wp);
if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) {
xfs_warn(mp, "zone %u has too large used counter (0x%x).",
rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
return false;
}
if (xfs_rtb_to_rgno(mp, wp_fsb) != rtg_rgno(rtg)) {
xfs_warn(mp, "zone %u write pointer (0x%llx) outside of zone.",
rtg_rgno(rtg), wp_fsb);
return false;
}
*write_pointer = xfs_rtb_to_rgbno(mp, wp_fsb);
if (*write_pointer >= rtg->rtg_extents) {
xfs_warn(mp, "zone %u has invalid write pointer (0x%x).",
rtg_rgno(rtg), *write_pointer);
return false;
}
return true;
}
static bool
xfs_zone_validate_full(
struct blk_zone *zone,
struct xfs_rtgroup *rtg,
xfs_rgblock_t *write_pointer)
{
struct xfs_mount *mp = rtg_mount(rtg);
if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) {
xfs_warn(mp, "zone %u has too large used counter (0x%x).",
rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
return false;
}
*write_pointer = rtg->rtg_extents;
return true;
}
static bool
xfs_zone_validate_seq(
struct blk_zone *zone,
struct xfs_rtgroup *rtg,
xfs_rgblock_t *write_pointer)
{
struct xfs_mount *mp = rtg_mount(rtg);
switch (zone->cond) {
case BLK_ZONE_COND_EMPTY:
return xfs_zone_validate_empty(zone, rtg, write_pointer);
*write_pointer = 0;
return true;
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
case BLK_ZONE_COND_CLOSED:
case BLK_ZONE_COND_ACTIVE:
return xfs_zone_validate_wp(zone, rtg, write_pointer);
if (zone->wp < zone->start ||
zone->wp >= zone->start + zone->capacity) {
xfs_warn(mp,
"zone %u write pointer (%llu) outside of zone.",
zone_no, zone->wp);
return false;
}
*write_pointer = XFS_BB_TO_FSB(mp, zone->wp - zone->start);
return true;
case BLK_ZONE_COND_FULL:
return xfs_zone_validate_full(zone, rtg, write_pointer);
*write_pointer = XFS_BB_TO_FSB(mp, zone->capacity);
return true;
case BLK_ZONE_COND_NOT_WP:
case BLK_ZONE_COND_OFFLINE:
case BLK_ZONE_COND_READONLY:
xfs_warn(mp, "zone %u has unsupported zone condition 0x%x.",
rtg_rgno(rtg), zone->cond);
zone_no, zone->cond);
return false;
default:
xfs_warn(mp, "zone %u has unknown zone condition 0x%x.",
rtg_rgno(rtg), zone->cond);
zone_no, zone->cond);
return false;
}
}
static bool
xfs_zone_validate_conv(
xfs_validate_blk_zone_conv(
struct xfs_mount *mp,
struct blk_zone *zone,
struct xfs_rtgroup *rtg)
unsigned int zone_no)
{
struct xfs_mount *mp = rtg_mount(rtg);
switch (zone->cond) {
case BLK_ZONE_COND_NOT_WP:
return true;
default:
xfs_warn(mp,
"conventional zone %u has unsupported zone condition 0x%x.",
rtg_rgno(rtg), zone->cond);
zone_no, zone->cond);
return false;
}
}
bool
xfs_zone_validate(
xfs_validate_blk_zone(
struct xfs_mount *mp,
struct blk_zone *zone,
struct xfs_rtgroup *rtg,
unsigned int zone_no,
uint32_t expected_size,
uint32_t expected_capacity,
xfs_rgblock_t *write_pointer)
{
struct xfs_mount *mp = rtg_mount(rtg);
struct xfs_groups *g = &mp->m_groups[XG_TYPE_RTG];
uint32_t expected_size;
/*
* Check that the zone capacity matches the rtgroup size stored in the
* superblock. Note that all zones including the last one must have a
* uniform capacity.
*/
if (XFS_BB_TO_FSB(mp, zone->capacity) != g->blocks) {
if (XFS_BB_TO_FSB(mp, zone->capacity) != expected_capacity) {
xfs_warn(mp,
"zone %u capacity (0x%llx) does not match RT group size (0x%x).",
rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->capacity),
g->blocks);
"zone %u capacity (%llu) does not match RT group size (%u).",
zone_no, XFS_BB_TO_FSB(mp, zone->capacity),
expected_capacity);
return false;
}
if (g->has_daddr_gaps) {
expected_size = 1 << g->blklog;
} else {
if (zone->len != zone->capacity) {
xfs_warn(mp,
"zone %u has capacity != size ((0x%llx vs 0x%llx)",
rtg_rgno(rtg),
XFS_BB_TO_FSB(mp, zone->len),
XFS_BB_TO_FSB(mp, zone->capacity));
return false;
}
expected_size = g->blocks;
}
if (XFS_BB_TO_FSB(mp, zone->len) != expected_size) {
xfs_warn(mp,
"zone %u length (0x%llx) does match geometry (0x%x).",
rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->len),
"zone %u length (%llu) does not match geometry (%u).",
zone_no, XFS_BB_TO_FSB(mp, zone->len),
expected_size);
return false;
}
switch (zone->type) {
case BLK_ZONE_TYPE_CONVENTIONAL:
return xfs_zone_validate_conv(zone, rtg);
return xfs_validate_blk_zone_conv(mp, zone, zone_no);
case BLK_ZONE_TYPE_SEQWRITE_REQ:
return xfs_zone_validate_seq(zone, rtg, write_pointer);
return xfs_validate_blk_zone_seq(mp, zone, zone_no,
write_pointer);
default:
xfs_warn(mp, "zoned %u has unsupported type 0x%x.",
rtg_rgno(rtg), zone->type);
zone_no, zone->type);
return false;
}
}

View File

@ -37,7 +37,8 @@ struct blk_zone;
*/
#define XFS_DEFAULT_MAX_OPEN_ZONES 128
bool xfs_zone_validate(struct blk_zone *zone, struct xfs_rtgroup *rtg,
xfs_rgblock_t *write_pointer);
bool xfs_validate_blk_zone(struct xfs_mount *mp, struct blk_zone *zone,
unsigned int zone_no, uint32_t expected_size,
uint32_t expected_capacity, xfs_rgblock_t *write_pointer);
#endif /* _LIBXFS_ZONES_H */

View File

@ -977,13 +977,15 @@ xfs_free_open_zones(
struct xfs_init_zones {
struct xfs_mount *mp;
uint32_t zone_size;
uint32_t zone_capacity;
uint64_t available;
uint64_t reclaimable;
};
/*
* For sequential write required zones, we restart writing at the hardware write
* pointer returned by xfs_zone_validate().
* pointer returned by xfs_validate_blk_zone().
*
* For conventional zones or conventional devices we have to query the rmap to
* find the highest recorded block and set the write pointer to the block after
@ -1018,6 +1020,25 @@ xfs_init_zone(
uint32_t used = rtg_rmap(rtg)->i_used_blocks;
int error;
if (write_pointer > rtg->rtg_extents) {
xfs_warn(mp, "zone %u has invalid write pointer (0x%x).",
rtg_rgno(rtg), write_pointer);
return -EFSCORRUPTED;
}
if (used > rtg->rtg_extents) {
xfs_warn(mp,
"zone %u has used counter (0x%x) larger than zone capacity (0x%llx).",
rtg_rgno(rtg), used, rtg->rtg_extents);
return -EFSCORRUPTED;
}
if (write_pointer == 0 && used != 0) {
xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).",
rtg_rgno(rtg), used);
return -EFSCORRUPTED;
}
/*
* If there are no used blocks, but the zone is not in empty state yet
* we lost power before the zoned reset. In that case finish the work
@ -1081,7 +1102,8 @@ xfs_get_zone_info_cb(
xfs_warn(mp, "realtime group not found for zone %u.", rgno);
return -EFSCORRUPTED;
}
if (!xfs_zone_validate(zone, rtg, &write_pointer)) {
if (!xfs_validate_blk_zone(mp, zone, idx, iz->zone_size,
iz->zone_capacity, &write_pointer)) {
xfs_rtgroup_rele(rtg);
return -EFSCORRUPTED;
}
@ -1227,6 +1249,8 @@ xfs_mount_zones(
{
struct xfs_init_zones iz = {
.mp = mp,
.zone_capacity = mp->m_groups[XG_TYPE_RTG].blocks,
.zone_size = xfs_rtgroup_raw_size(mp),
};
struct xfs_buftarg *bt = mp->m_rtdev_targp;
xfs_extlen_t zone_blocks = mp->m_groups[XG_TYPE_RTG].blocks;