1720c2d58SChristoph Hellwig // SPDX-License-Identifier: GPL-2.0 2720c2d58SChristoph Hellwig /* 3720c2d58SChristoph Hellwig * Copyright (c) 2023-2025 Christoph Hellwig. 4720c2d58SChristoph Hellwig * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates. 5720c2d58SChristoph Hellwig */ 6720c2d58SChristoph Hellwig #include "xfs.h" 7720c2d58SChristoph Hellwig #include "xfs_fs.h" 8720c2d58SChristoph Hellwig #include "xfs_shared.h" 9720c2d58SChristoph Hellwig #include "xfs_format.h" 10720c2d58SChristoph Hellwig #include "xfs_log_format.h" 11720c2d58SChristoph Hellwig #include "xfs_trans_resv.h" 12720c2d58SChristoph Hellwig #include "xfs_mount.h" 13720c2d58SChristoph Hellwig #include "xfs_inode.h" 14720c2d58SChristoph Hellwig #include "xfs_rtgroup.h" 15720c2d58SChristoph Hellwig #include "xfs_zones.h" 16720c2d58SChristoph Hellwig 17720c2d58SChristoph Hellwig static bool 18720c2d58SChristoph Hellwig xfs_zone_validate_empty( 19720c2d58SChristoph Hellwig struct blk_zone *zone, 20720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg, 21720c2d58SChristoph Hellwig xfs_rgblock_t *write_pointer) 22720c2d58SChristoph Hellwig { 23720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 24720c2d58SChristoph Hellwig 25720c2d58SChristoph Hellwig if (rtg_rmap(rtg)->i_used_blocks > 0) { 26720c2d58SChristoph Hellwig xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).", 27720c2d58SChristoph Hellwig rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); 28720c2d58SChristoph Hellwig return false; 29720c2d58SChristoph Hellwig } 30720c2d58SChristoph Hellwig 31720c2d58SChristoph Hellwig *write_pointer = 0; 32720c2d58SChristoph Hellwig return true; 33720c2d58SChristoph Hellwig } 34720c2d58SChristoph Hellwig 35720c2d58SChristoph Hellwig static bool 36720c2d58SChristoph Hellwig xfs_zone_validate_wp( 37720c2d58SChristoph Hellwig struct blk_zone *zone, 38720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg, 39720c2d58SChristoph Hellwig xfs_rgblock_t *write_pointer) 40720c2d58SChristoph Hellwig { 41720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 42720c2d58SChristoph Hellwig xfs_rtblock_t wp_fsb = xfs_daddr_to_rtb(mp, zone->wp); 43720c2d58SChristoph Hellwig 44720c2d58SChristoph Hellwig if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) { 45720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u has too large used counter (0x%x).", 46720c2d58SChristoph Hellwig rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); 47720c2d58SChristoph Hellwig return false; 48720c2d58SChristoph Hellwig } 49720c2d58SChristoph Hellwig 50720c2d58SChristoph Hellwig if (xfs_rtb_to_rgno(mp, wp_fsb) != rtg_rgno(rtg)) { 51720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u write pointer (0x%llx) outside of zone.", 52720c2d58SChristoph Hellwig rtg_rgno(rtg), wp_fsb); 53720c2d58SChristoph Hellwig return false; 54720c2d58SChristoph Hellwig } 55720c2d58SChristoph Hellwig 56720c2d58SChristoph Hellwig *write_pointer = xfs_rtb_to_rgbno(mp, wp_fsb); 57720c2d58SChristoph Hellwig if (*write_pointer >= rtg->rtg_extents) { 58720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u has invalid write pointer (0x%x).", 59720c2d58SChristoph Hellwig rtg_rgno(rtg), *write_pointer); 60720c2d58SChristoph Hellwig return false; 61720c2d58SChristoph Hellwig } 62720c2d58SChristoph Hellwig 63720c2d58SChristoph Hellwig return true; 64720c2d58SChristoph Hellwig } 65720c2d58SChristoph Hellwig 66720c2d58SChristoph Hellwig static bool 67720c2d58SChristoph Hellwig xfs_zone_validate_full( 68720c2d58SChristoph Hellwig struct blk_zone *zone, 69720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg, 70720c2d58SChristoph Hellwig xfs_rgblock_t *write_pointer) 71720c2d58SChristoph Hellwig { 72720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 73720c2d58SChristoph Hellwig 74720c2d58SChristoph Hellwig if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) { 75720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u has too large used counter (0x%x).", 76720c2d58SChristoph Hellwig rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks); 77720c2d58SChristoph Hellwig return false; 78720c2d58SChristoph Hellwig } 79720c2d58SChristoph Hellwig 80720c2d58SChristoph Hellwig *write_pointer = rtg->rtg_extents; 81720c2d58SChristoph Hellwig return true; 82720c2d58SChristoph Hellwig } 83720c2d58SChristoph Hellwig 84720c2d58SChristoph Hellwig static bool 85720c2d58SChristoph Hellwig xfs_zone_validate_seq( 86720c2d58SChristoph Hellwig struct blk_zone *zone, 87720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg, 88720c2d58SChristoph Hellwig xfs_rgblock_t *write_pointer) 89720c2d58SChristoph Hellwig { 90720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 91720c2d58SChristoph Hellwig 92720c2d58SChristoph Hellwig switch (zone->cond) { 93720c2d58SChristoph Hellwig case BLK_ZONE_COND_EMPTY: 94720c2d58SChristoph Hellwig return xfs_zone_validate_empty(zone, rtg, write_pointer); 95720c2d58SChristoph Hellwig case BLK_ZONE_COND_IMP_OPEN: 96720c2d58SChristoph Hellwig case BLK_ZONE_COND_EXP_OPEN: 97720c2d58SChristoph Hellwig case BLK_ZONE_COND_CLOSED: 98720c2d58SChristoph Hellwig return xfs_zone_validate_wp(zone, rtg, write_pointer); 99720c2d58SChristoph Hellwig case BLK_ZONE_COND_FULL: 100720c2d58SChristoph Hellwig return xfs_zone_validate_full(zone, rtg, write_pointer); 101720c2d58SChristoph Hellwig case BLK_ZONE_COND_NOT_WP: 102720c2d58SChristoph Hellwig case BLK_ZONE_COND_OFFLINE: 103720c2d58SChristoph Hellwig case BLK_ZONE_COND_READONLY: 104720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u has unsupported zone condition 0x%x.", 105720c2d58SChristoph Hellwig rtg_rgno(rtg), zone->cond); 106720c2d58SChristoph Hellwig return false; 107720c2d58SChristoph Hellwig default: 108720c2d58SChristoph Hellwig xfs_warn(mp, "zone %u has unknown zone condition 0x%x.", 109720c2d58SChristoph Hellwig rtg_rgno(rtg), zone->cond); 110720c2d58SChristoph Hellwig return false; 111720c2d58SChristoph Hellwig } 112720c2d58SChristoph Hellwig } 113720c2d58SChristoph Hellwig 114720c2d58SChristoph Hellwig static bool 115720c2d58SChristoph Hellwig xfs_zone_validate_conv( 116720c2d58SChristoph Hellwig struct blk_zone *zone, 117720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg) 118720c2d58SChristoph Hellwig { 119720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 120720c2d58SChristoph Hellwig 121720c2d58SChristoph Hellwig switch (zone->cond) { 122720c2d58SChristoph Hellwig case BLK_ZONE_COND_NOT_WP: 123720c2d58SChristoph Hellwig return true; 124720c2d58SChristoph Hellwig default: 125720c2d58SChristoph Hellwig xfs_warn(mp, 126720c2d58SChristoph Hellwig "conventional zone %u has unsupported zone condition 0x%x.", 127720c2d58SChristoph Hellwig rtg_rgno(rtg), zone->cond); 128720c2d58SChristoph Hellwig return false; 129720c2d58SChristoph Hellwig } 130720c2d58SChristoph Hellwig } 131720c2d58SChristoph Hellwig 132720c2d58SChristoph Hellwig bool 133720c2d58SChristoph Hellwig xfs_zone_validate( 134720c2d58SChristoph Hellwig struct blk_zone *zone, 135720c2d58SChristoph Hellwig struct xfs_rtgroup *rtg, 136720c2d58SChristoph Hellwig xfs_rgblock_t *write_pointer) 137720c2d58SChristoph Hellwig { 138720c2d58SChristoph Hellwig struct xfs_mount *mp = rtg_mount(rtg); 139720c2d58SChristoph Hellwig struct xfs_groups *g = &mp->m_groups[XG_TYPE_RTG]; 140*97c69ba1SChristoph Hellwig uint32_t expected_size; 141720c2d58SChristoph Hellwig 142720c2d58SChristoph Hellwig /* 143720c2d58SChristoph Hellwig * Check that the zone capacity matches the rtgroup size stored in the 144720c2d58SChristoph Hellwig * superblock. Note that all zones including the last one must have a 145720c2d58SChristoph Hellwig * uniform capacity. 146720c2d58SChristoph Hellwig */ 147720c2d58SChristoph Hellwig if (XFS_BB_TO_FSB(mp, zone->capacity) != g->blocks) { 148720c2d58SChristoph Hellwig xfs_warn(mp, 149720c2d58SChristoph Hellwig "zone %u capacity (0x%llx) does not match RT group size (0x%x).", 150720c2d58SChristoph Hellwig rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->capacity), 151720c2d58SChristoph Hellwig g->blocks); 152720c2d58SChristoph Hellwig return false; 153720c2d58SChristoph Hellwig } 154720c2d58SChristoph Hellwig 155*97c69ba1SChristoph Hellwig if (g->has_daddr_gaps) { 156*97c69ba1SChristoph Hellwig expected_size = 1 << g->blklog; 157*97c69ba1SChristoph Hellwig } else { 158*97c69ba1SChristoph Hellwig if (zone->len != zone->capacity) { 159*97c69ba1SChristoph Hellwig xfs_warn(mp, 160*97c69ba1SChristoph Hellwig "zone %u has capacity != size ((0x%llx vs 0x%llx)", 161*97c69ba1SChristoph Hellwig rtg_rgno(rtg), 162*97c69ba1SChristoph Hellwig XFS_BB_TO_FSB(mp, zone->len), 163*97c69ba1SChristoph Hellwig XFS_BB_TO_FSB(mp, zone->capacity)); 164*97c69ba1SChristoph Hellwig return false; 165*97c69ba1SChristoph Hellwig } 166*97c69ba1SChristoph Hellwig expected_size = g->blocks; 167*97c69ba1SChristoph Hellwig } 168*97c69ba1SChristoph Hellwig 169*97c69ba1SChristoph Hellwig if (XFS_BB_TO_FSB(mp, zone->len) != expected_size) { 170720c2d58SChristoph Hellwig xfs_warn(mp, 171720c2d58SChristoph Hellwig "zone %u length (0x%llx) does match geometry (0x%x).", 172720c2d58SChristoph Hellwig rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->len), 173*97c69ba1SChristoph Hellwig expected_size); 174720c2d58SChristoph Hellwig } 175720c2d58SChristoph Hellwig 176720c2d58SChristoph Hellwig switch (zone->type) { 177720c2d58SChristoph Hellwig case BLK_ZONE_TYPE_CONVENTIONAL: 178720c2d58SChristoph Hellwig return xfs_zone_validate_conv(zone, rtg); 179720c2d58SChristoph Hellwig case BLK_ZONE_TYPE_SEQWRITE_REQ: 180720c2d58SChristoph Hellwig return xfs_zone_validate_seq(zone, rtg, write_pointer); 181720c2d58SChristoph Hellwig default: 182720c2d58SChristoph Hellwig xfs_warn(mp, "zoned %u has unsupported type 0x%x.", 183720c2d58SChristoph Hellwig rtg_rgno(rtg), zone->type); 184720c2d58SChristoph Hellwig return false; 185720c2d58SChristoph Hellwig } 186720c2d58SChristoph Hellwig } 187