xref: /linux/fs/xfs/libxfs/xfs_zones.c (revision cc25df3e2e22a956d3a0d427369367b4a901d203)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2023-2025 Christoph Hellwig.
4  * Copyright (c) 2024-2025, Western Digital Corporation or its affiliates.
5  */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_log_format.h"
11 #include "xfs_trans_resv.h"
12 #include "xfs_mount.h"
13 #include "xfs_inode.h"
14 #include "xfs_rtgroup.h"
15 #include "xfs_zones.h"
16 
17 static bool
18 xfs_zone_validate_empty(
19 	struct blk_zone		*zone,
20 	struct xfs_rtgroup	*rtg,
21 	xfs_rgblock_t		*write_pointer)
22 {
23 	struct xfs_mount	*mp = rtg_mount(rtg);
24 
25 	if (rtg_rmap(rtg)->i_used_blocks > 0) {
26 		xfs_warn(mp, "empty zone %u has non-zero used counter (0x%x).",
27 			 rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
28 		return false;
29 	}
30 
31 	*write_pointer = 0;
32 	return true;
33 }
34 
35 static bool
36 xfs_zone_validate_wp(
37 	struct blk_zone		*zone,
38 	struct xfs_rtgroup	*rtg,
39 	xfs_rgblock_t		*write_pointer)
40 {
41 	struct xfs_mount	*mp = rtg_mount(rtg);
42 	xfs_rtblock_t		wp_fsb = xfs_daddr_to_rtb(mp, zone->wp);
43 
44 	if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) {
45 		xfs_warn(mp, "zone %u has too large used counter (0x%x).",
46 			 rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
47 		return false;
48 	}
49 
50 	if (xfs_rtb_to_rgno(mp, wp_fsb) != rtg_rgno(rtg)) {
51 		xfs_warn(mp, "zone %u write pointer (0x%llx) outside of zone.",
52 			 rtg_rgno(rtg), wp_fsb);
53 		return false;
54 	}
55 
56 	*write_pointer = xfs_rtb_to_rgbno(mp, wp_fsb);
57 	if (*write_pointer >= rtg->rtg_extents) {
58 		xfs_warn(mp, "zone %u has invalid write pointer (0x%x).",
59 			 rtg_rgno(rtg), *write_pointer);
60 		return false;
61 	}
62 
63 	return true;
64 }
65 
66 static bool
67 xfs_zone_validate_full(
68 	struct blk_zone		*zone,
69 	struct xfs_rtgroup	*rtg,
70 	xfs_rgblock_t		*write_pointer)
71 {
72 	struct xfs_mount	*mp = rtg_mount(rtg);
73 
74 	if (rtg_rmap(rtg)->i_used_blocks > rtg->rtg_extents) {
75 		xfs_warn(mp, "zone %u has too large used counter (0x%x).",
76 			 rtg_rgno(rtg), rtg_rmap(rtg)->i_used_blocks);
77 		return false;
78 	}
79 
80 	*write_pointer = rtg->rtg_extents;
81 	return true;
82 }
83 
84 static bool
85 xfs_zone_validate_seq(
86 	struct blk_zone		*zone,
87 	struct xfs_rtgroup	*rtg,
88 	xfs_rgblock_t		*write_pointer)
89 {
90 	struct xfs_mount	*mp = rtg_mount(rtg);
91 
92 	switch (zone->cond) {
93 	case BLK_ZONE_COND_EMPTY:
94 		return xfs_zone_validate_empty(zone, rtg, write_pointer);
95 	case BLK_ZONE_COND_IMP_OPEN:
96 	case BLK_ZONE_COND_EXP_OPEN:
97 	case BLK_ZONE_COND_CLOSED:
98 	case BLK_ZONE_COND_ACTIVE:
99 		return xfs_zone_validate_wp(zone, rtg, write_pointer);
100 	case BLK_ZONE_COND_FULL:
101 		return xfs_zone_validate_full(zone, rtg, write_pointer);
102 	case BLK_ZONE_COND_NOT_WP:
103 	case BLK_ZONE_COND_OFFLINE:
104 	case BLK_ZONE_COND_READONLY:
105 		xfs_warn(mp, "zone %u has unsupported zone condition 0x%x.",
106 			rtg_rgno(rtg), zone->cond);
107 		return false;
108 	default:
109 		xfs_warn(mp, "zone %u has unknown zone condition 0x%x.",
110 			rtg_rgno(rtg), zone->cond);
111 		return false;
112 	}
113 }
114 
115 static bool
116 xfs_zone_validate_conv(
117 	struct blk_zone		*zone,
118 	struct xfs_rtgroup	*rtg)
119 {
120 	struct xfs_mount	*mp = rtg_mount(rtg);
121 
122 	switch (zone->cond) {
123 	case BLK_ZONE_COND_NOT_WP:
124 		return true;
125 	default:
126 		xfs_warn(mp,
127 "conventional zone %u has unsupported zone condition 0x%x.",
128 			 rtg_rgno(rtg), zone->cond);
129 		return false;
130 	}
131 }
132 
133 bool
134 xfs_zone_validate(
135 	struct blk_zone		*zone,
136 	struct xfs_rtgroup	*rtg,
137 	xfs_rgblock_t		*write_pointer)
138 {
139 	struct xfs_mount	*mp = rtg_mount(rtg);
140 	struct xfs_groups	*g = &mp->m_groups[XG_TYPE_RTG];
141 	uint32_t		expected_size;
142 
143 	/*
144 	 * Check that the zone capacity matches the rtgroup size stored in the
145 	 * superblock.  Note that all zones including the last one must have a
146 	 * uniform capacity.
147 	 */
148 	if (XFS_BB_TO_FSB(mp, zone->capacity) != g->blocks) {
149 		xfs_warn(mp,
150 "zone %u capacity (0x%llx) does not match RT group size (0x%x).",
151 			rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->capacity),
152 			g->blocks);
153 		return false;
154 	}
155 
156 	if (g->has_daddr_gaps) {
157 		expected_size = 1 << g->blklog;
158 	} else {
159 		if (zone->len != zone->capacity) {
160 			xfs_warn(mp,
161 "zone %u has capacity != size ((0x%llx vs 0x%llx)",
162 				rtg_rgno(rtg),
163 				XFS_BB_TO_FSB(mp, zone->len),
164 				XFS_BB_TO_FSB(mp, zone->capacity));
165 			return false;
166 		}
167 		expected_size = g->blocks;
168 	}
169 
170 	if (XFS_BB_TO_FSB(mp, zone->len) != expected_size) {
171 		xfs_warn(mp,
172 "zone %u length (0x%llx) does match geometry (0x%x).",
173 			rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->len),
174 			expected_size);
175 	}
176 
177 	switch (zone->type) {
178 	case BLK_ZONE_TYPE_CONVENTIONAL:
179 		return xfs_zone_validate_conv(zone, rtg);
180 	case BLK_ZONE_TYPE_SEQWRITE_REQ:
181 		return xfs_zone_validate_seq(zone, rtg, write_pointer);
182 	default:
183 		xfs_warn(mp, "zoned %u has unsupported type 0x%x.",
184 			rtg_rgno(rtg), zone->type);
185 		return false;
186 	}
187 }
188