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
xfs_zone_validate_empty(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_wp(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_full(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_seq(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_conv(struct blk_zone * zone,struct xfs_rtgroup * rtg)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
xfs_zone_validate(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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