xref: /linux/fs/xfs/libxfs/xfs_zones.c (revision c148bc7535650fbfa95a1f571b9ffa2ab478ea33)
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
xfs_zone_validate_empty(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_wp(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_full(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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
xfs_zone_validate_seq(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)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 		return xfs_zone_validate_wp(zone, rtg, write_pointer);
99 	case BLK_ZONE_COND_FULL:
100 		return xfs_zone_validate_full(zone, rtg, write_pointer);
101 	case BLK_ZONE_COND_NOT_WP:
102 	case BLK_ZONE_COND_OFFLINE:
103 	case BLK_ZONE_COND_READONLY:
104 		xfs_warn(mp, "zone %u has unsupported zone condition 0x%x.",
105 			rtg_rgno(rtg), zone->cond);
106 		return false;
107 	default:
108 		xfs_warn(mp, "zone %u has unknown zone condition 0x%x.",
109 			rtg_rgno(rtg), zone->cond);
110 		return false;
111 	}
112 }
113 
114 static bool
xfs_zone_validate_conv(struct blk_zone * zone,struct xfs_rtgroup * rtg)115 xfs_zone_validate_conv(
116 	struct blk_zone		*zone,
117 	struct xfs_rtgroup	*rtg)
118 {
119 	struct xfs_mount	*mp = rtg_mount(rtg);
120 
121 	switch (zone->cond) {
122 	case BLK_ZONE_COND_NOT_WP:
123 		return true;
124 	default:
125 		xfs_warn(mp,
126 "conventional zone %u has unsupported zone condition 0x%x.",
127 			 rtg_rgno(rtg), zone->cond);
128 		return false;
129 	}
130 }
131 
132 bool
xfs_zone_validate(struct blk_zone * zone,struct xfs_rtgroup * rtg,xfs_rgblock_t * write_pointer)133 xfs_zone_validate(
134 	struct blk_zone		*zone,
135 	struct xfs_rtgroup	*rtg,
136 	xfs_rgblock_t		*write_pointer)
137 {
138 	struct xfs_mount	*mp = rtg_mount(rtg);
139 	struct xfs_groups	*g = &mp->m_groups[XG_TYPE_RTG];
140 	uint32_t		expected_size;
141 
142 	/*
143 	 * Check that the zone capacity matches the rtgroup size stored in the
144 	 * superblock.  Note that all zones including the last one must have a
145 	 * uniform capacity.
146 	 */
147 	if (XFS_BB_TO_FSB(mp, zone->capacity) != g->blocks) {
148 		xfs_warn(mp,
149 "zone %u capacity (0x%llx) does not match RT group size (0x%x).",
150 			rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->capacity),
151 			g->blocks);
152 		return false;
153 	}
154 
155 	if (g->has_daddr_gaps) {
156 		expected_size = 1 << g->blklog;
157 	} else {
158 		if (zone->len != zone->capacity) {
159 			xfs_warn(mp,
160 "zone %u has capacity != size ((0x%llx vs 0x%llx)",
161 				rtg_rgno(rtg),
162 				XFS_BB_TO_FSB(mp, zone->len),
163 				XFS_BB_TO_FSB(mp, zone->capacity));
164 			return false;
165 		}
166 		expected_size = g->blocks;
167 	}
168 
169 	if (XFS_BB_TO_FSB(mp, zone->len) != expected_size) {
170 		xfs_warn(mp,
171 "zone %u length (0x%llx) does match geometry (0x%x).",
172 			rtg_rgno(rtg), XFS_BB_TO_FSB(mp, zone->len),
173 			expected_size);
174 	}
175 
176 	switch (zone->type) {
177 	case BLK_ZONE_TYPE_CONVENTIONAL:
178 		return xfs_zone_validate_conv(zone, rtg);
179 	case BLK_ZONE_TYPE_SEQWRITE_REQ:
180 		return xfs_zone_validate_seq(zone, rtg, write_pointer);
181 	default:
182 		xfs_warn(mp, "zoned %u has unsupported type 0x%x.",
183 			rtg_rgno(rtg), zone->type);
184 		return false;
185 	}
186 }
187