]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: validate rt groups vs reported hardware zones
authorChristoph Hellwig <hch@lst.de>
Mon, 14 Apr 2025 05:36:12 +0000 (07:36 +0200)
committerAndrey Albershteyn <aalbersh@kernel.org>
Tue, 29 Apr 2025 16:09:57 +0000 (18:09 +0200)
Run a report zones ioctl, and verify the rt group state vs the
reported hardware zone state.  Note that there is no way to actually
fix up any discrepancies here, as that would be rather scary without
having transactions.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
repair/Makefile
repair/phase5.c
repair/zoned.c [new file with mode: 0644]
repair/zoned.h [new file with mode: 0644]

index ff5b1f5abedac01f548985d2ba605a1b5c02b0d1..fb0b2f96cc91a9749c8bbdb74e1872e0d425f041 100644 (file)
@@ -81,6 +81,7 @@ CFILES = \
        strblobs.c \
        threads.c \
        versions.c \
+       zoned.c \
        xfs_repair.c
 
 LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBXCMD) $(LIBFROG) $(LIBUUID) $(LIBRT) \
index e350b411c2434b7425617b9aa38f3dc3be86a50a..e44c2688571746e547aa905ebbc73007720ebbc7 100644 (file)
@@ -21,6 +21,7 @@
 #include "rmap.h"
 #include "bulkload.h"
 #include "agbtree.h"
+#include "zoned.h"
 
 static uint64_t        *sb_icount_ag;          /* allocated inodes per ag */
 static uint64_t        *sb_ifree_ag;           /* free inodes per ag */
@@ -631,15 +632,7 @@ check_rtmetadata(
        struct xfs_mount        *mp)
 {
        if (xfs_has_zoned(mp)) {
-               /*
-                * Here we could/should verify the zone state a bit when we are
-                * on actual zoned devices:
-                *      - compare hw write pointer to last written
-                *      - compare zone state to last written
-                *
-                * Note much we can do when running in zoned mode on a
-                * conventional device.
-                */
+               check_zones(mp);
                return;
        }
 
diff --git a/repair/zoned.c b/repair/zoned.c
new file mode 100644 (file)
index 0000000..456076b
--- /dev/null
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Christoph Hellwig.
+ */
+#include <ctype.h>
+#include <linux/blkzoned.h>
+#include "libxfs_priv.h"
+#include "libxfs.h"
+#include "xfs_zones.h"
+#include "err_protos.h"
+#include "zoned.h"
+
+/* random size that allows efficient processing */
+#define ZONES_PER_IOCTL                        16384
+
+static void
+report_zones_cb(
+       struct xfs_mount        *mp,
+       struct blk_zone         *zone)
+{
+       xfs_rtblock_t           zsbno = xfs_daddr_to_rtb(mp, zone->start);
+       xfs_rgblock_t           write_pointer;
+       xfs_rgnumber_t          rgno;
+       struct xfs_rtgroup      *rtg;
+
+       if (xfs_rtb_to_rgbno(mp, zsbno) != 0) {
+               do_error(_("mismatched zone start 0x%llx."),
+                               (unsigned long long)zsbno);
+               return;
+       }
+
+       rgno = xfs_rtb_to_rgno(mp, zsbno);
+       rtg = xfs_rtgroup_grab(mp, rgno);
+       if (!rtg) {
+               do_error(_("realtime group not found for zone %u."), rgno);
+               return;
+       }
+
+       if (!rtg_rmap(rtg))
+               do_warn(_("no rmap inode for zone %u."), rgno);
+       else
+               xfs_zone_validate(zone, rtg, &write_pointer);
+       xfs_rtgroup_rele(rtg);
+}
+
+void
+check_zones(
+       struct xfs_mount        *mp)
+{
+       int                     fd = mp->m_rtdev_targp->bt_bdev_fd;
+       uint64_t                sector = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart);
+       unsigned int            zone_size, zone_capacity;
+       uint64_t                device_size;
+       size_t                  rep_size;
+       struct blk_zone_report  *rep;
+       unsigned int            i, n = 0;
+
+       if (ioctl(fd, BLKGETSIZE64, &device_size))
+               return; /* not a block device */
+       if (ioctl(fd, BLKGETZONESZ, &zone_size) || !zone_size)
+               return; /* not zoned */
+
+       /* BLKGETSIZE64 reports a byte value */
+       device_size = BTOBB(device_size);
+       if (device_size / zone_size < mp->m_sb.sb_rgcount) {
+               do_error(_("rt device too small\n"));
+               return;
+       }
+
+       rep_size = sizeof(struct blk_zone_report) +
+                  sizeof(struct blk_zone) * ZONES_PER_IOCTL;
+       rep = malloc(rep_size);
+       if (!rep) {
+               do_warn(_("malloc failed for zone report\n"));
+               return;
+       }
+
+       while (n < mp->m_sb.sb_rgcount) {
+               struct blk_zone *zones = (struct blk_zone *)(rep + 1);
+               int ret;
+
+               memset(rep, 0, rep_size);
+               rep->sector = sector;
+               rep->nr_zones = ZONES_PER_IOCTL;
+
+               ret = ioctl(fd, BLKREPORTZONE, rep);
+               if (ret) {
+                       do_error(_("ioctl(BLKREPORTZONE) failed: %d!\n"), ret);
+                       goto out_free;
+               }
+               if (!rep->nr_zones)
+                       break;
+
+               for (i = 0; i < rep->nr_zones; i++) {
+                       if (n >= mp->m_sb.sb_rgcount)
+                               break;
+
+                       if (zones[i].len != zone_size) {
+                               do_error(_("Inconsistent zone size!\n"));
+                               goto out_free;
+                       }
+
+                       switch (zones[i].type) {
+                       case BLK_ZONE_TYPE_CONVENTIONAL:
+                       case BLK_ZONE_TYPE_SEQWRITE_REQ:
+                               break;
+                       case BLK_ZONE_TYPE_SEQWRITE_PREF:
+                               do_error(
+_("Found sequential write preferred zone\n"));
+                               goto out_free;
+                       default:
+                               do_error(
+_("Found unknown zone type (0x%x)\n"), zones[i].type);
+                               goto out_free;
+                       }
+
+                       if (!n) {
+                               zone_capacity = zones[i].capacity;
+                               if (zone_capacity > zone_size) {
+                                       do_error(
+_("Zone capacity larger than zone size!\n"));
+                                       goto out_free;
+                               }
+                       } else if (zones[i].capacity != zone_capacity) {
+                               do_error(
+_("Inconsistent zone capacity!\n"));
+                               goto out_free;
+                       }
+
+                       report_zones_cb(mp, &zones[i]);
+                       n++;
+               }
+               sector = zones[rep->nr_zones - 1].start +
+                        zones[rep->nr_zones - 1].len;
+       }
+
+out_free:
+       free(rep);
+}
diff --git a/repair/zoned.h b/repair/zoned.h
new file mode 100644 (file)
index 0000000..ab76bf1
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Christoph Hellwig.
+ */
+#ifndef _XFS_REPAIR_ZONED_H_
+#define _XFS_REPAIR_ZONED_H_
+
+void check_zones(struct xfs_mount *mp);
+
+#endif /* _XFS_REPAIR_ZONED_H_ */