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