Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
17static bool
18xfs_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
35static bool
36xfs_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
66static bool
67xfs_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
84static bool
85xfs_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
114static bool
115xfs_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
132bool
133xfs_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}