]> www.infradead.org Git - users/hch/misc.git/commitdiff
xfs: define the format of rt groups
authorDarrick J. Wong <djwong@kernel.org>
Wed, 29 May 2024 04:11:12 +0000 (21:11 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 24 Jul 2024 05:33:35 +0000 (22:33 -0700)
Define the ondisk format of realtime group metadata, and a superblock
for realtime volumes.  rt supers are protected by a separate rocompat
bit so that we can leave them off if the rt device is zoned.

Add a xfs_sb_version_hasrtgroups so that xfs_repair knows how to zero
the tail of superblocks.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/libxfs/xfs_format.h
fs/xfs/libxfs/xfs_ondisk.h
fs/xfs/libxfs/xfs_rtgroup.c
fs/xfs/libxfs/xfs_rtgroup.h
fs/xfs/libxfs/xfs_sb.c
fs/xfs/libxfs/xfs_shared.h
fs/xfs/xfs_mount.h

index 424c2fc03851bed0ad25bd165acf45bbd2cef4f7..88255cf27bfe58e3c838d6836928a7ce0ac511e4 100644 (file)
@@ -265,8 +265,15 @@ struct xfs_dsb {
        uuid_t          sb_meta_uuid;   /* metadata file system unique id */
 
        __be64          sb_metadirino;  /* metadata directory tree root */
+       __be32          sb_rgcount;     /* # of realtime groups */
+       __be32          sb_rgextents;   /* size of rtgroup in rtx */
 
-       /* must be padded to 64 bit alignment */
+       /*
+        * The size of this structure must be padded to 64 bit alignment.
+        *
+        * NOTE: Don't forget to update secondary_sb_whack in xfs_repair when
+        * adding new fields here.
+        */
 };
 
 #define XFS_SB_CRC_OFF         offsetof(struct xfs_dsb, sb_crc)
@@ -355,10 +362,11 @@ xfs_sb_has_compat_feature(
        return (sbp->sb_features_compat & feature) != 0;
 }
 
-#define XFS_SB_FEAT_RO_COMPAT_FINOBT   (1 << 0)                /* free inode btree */
-#define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)                /* reverse map btree */
-#define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)                /* reflinked files */
-#define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)                /* inobt block counts */
+#define XFS_SB_FEAT_RO_COMPAT_FINOBT   (1 << 0)        /* free inode btree */
+#define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)        /* reverse map btree */
+#define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)        /* reflinked files */
+#define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)        /* inobt block counts */
+#define XFS_SB_FEAT_RO_COMPAT_RTSB     (1U << 30)      /* realtime superblock */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
                (XFS_SB_FEAT_RO_COMPAT_FINOBT | \
                 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
@@ -381,6 +389,7 @@ xfs_sb_has_ro_compat_feature(
 #define XFS_SB_FEAT_INCOMPAT_NREXT64   (1 << 5)  /* large extent counters */
 #define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6)  /* exchangerange supported */
 #define XFS_SB_FEAT_INCOMPAT_PARENT    (1 << 7)  /* parent pointers */
+#define XFS_SB_FEAT_INCOMPAT_RTGROUPS  (1U << 30)      /* realtime groups */
 #define XFS_SB_FEAT_INCOMPAT_METADIR   (1U << 31)      /* metadata dir tree */
 #define XFS_SB_FEAT_INCOMPAT_ALL \
                (XFS_SB_FEAT_INCOMPAT_FTYPE | \
@@ -441,6 +450,12 @@ static inline bool xfs_sb_version_hasmetadir(const struct xfs_sb *sbp)
                (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR);
 }
 
+static inline bool xfs_sb_version_hasrtgroups(const struct xfs_sb *sbp)
+{
+       return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
+               (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS);
+}
+
 static inline bool
 xfs_is_quota_inode(struct xfs_sb *sbp, xfs_ino_t ino)
 {
@@ -723,6 +738,39 @@ union xfs_suminfo_raw {
        __u32           old;
 };
 
+/*
+ * Realtime allocation groups break the rt section into multiple pieces that
+ * could be locked independently.  Realtime block group numbers are 32-bit
+ * quantities.  Block numbers within a group are also 32-bit quantities, but
+ * the upper bit must never be set.  rtgroup 0 might have a superblock in it,
+ * so the minimum size of an rtgroup is 2 rtx.
+ */
+#define XFS_MAX_RGBLOCKS       ((xfs_rgblock_t)(1U << 31) - 1)
+#define XFS_MIN_RGEXTENTS      ((xfs_rtxlen_t)2)
+#define XFS_MAX_RGNUMBER       ((xfs_rgnumber_t)(-1U))
+
+#define XFS_RTSB_MAGIC 0x46726F67      /* 'Frog' */
+
+/*
+ * Realtime superblock - on disk version.  Must be padded to 64 bit alignment.
+ * The first block of the realtime volume contains this superblock.
+ */
+struct xfs_rtsb {
+       __be32          rsb_magicnum;   /* magic number == XFS_RTSB_MAGIC */
+       __le32          rsb_crc;        /* superblock crc */
+
+       __be32          rsb_pad;        /* zero */
+       unsigned char   rsb_fname[XFSLABEL_MAX]; /* file system name */
+
+       uuid_t          rsb_uuid;       /* user-visible file system unique id */
+       uuid_t          rsb_meta_uuid;  /* metadata file system unique id */
+
+       /* must be padded to 64 bit alignment */
+};
+
+#define XFS_RTSB_CRC_OFF       offsetof(struct xfs_rtsb, rsb_crc)
+#define XFS_RTSB_DADDR         ((xfs_daddr_t)0) /* daddr in rt section */
+
 /*
  * XFS Timestamps
  * ==============
index 8bca86e350fdc11980a00cf52a492fc9b7504496..38b314113d8f247b68643f999fac8ab891adf94a 100644 (file)
@@ -37,7 +37,7 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(struct xfs_dinode,                176);
        XFS_CHECK_STRUCT_SIZE(struct xfs_disk_dquot,            104);
        XFS_CHECK_STRUCT_SIZE(struct xfs_dqblk,                 136);
-       XFS_CHECK_STRUCT_SIZE(struct xfs_dsb,                   272);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_dsb,                   280);
        XFS_CHECK_STRUCT_SIZE(struct xfs_dsymlink_hdr,          56);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_key,             4);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_rec,             16);
@@ -53,6 +53,7 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(xfs_inobt_ptr_t,                  4);
        XFS_CHECK_STRUCT_SIZE(xfs_refcount_ptr_t,               4);
        XFS_CHECK_STRUCT_SIZE(xfs_rmap_ptr_t,                   4);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_rtsb,                  56);
 
        /* dir/attr trees */
        XFS_CHECK_STRUCT_SIZE(struct xfs_attr3_leaf_hdr,        80);
index 38188e522ce9d8add7b46185d1eb3a6394195c43..519d2829d86b775b2b8dbddbd0bb49722a6a8935 100644 (file)
@@ -28,6 +28,7 @@
 #include "xfs_trace.h"
 #include "xfs_inode.h"
 #include "xfs_icache.h"
+#include "xfs_buf_item.h"
 #include "xfs_rtgroup.h"
 #include "xfs_rtbitmap.h"
 
@@ -268,3 +269,84 @@ xfs_rtgroup_block_count(
        return __xfs_rtgroup_block_count(mp, rgno, mp->m_sb.sb_rgcount,
                        mp->m_sb.sb_rblocks);
 }
+
+/* Check superblock fields for a read or a write. */
+static xfs_failaddr_t
+xfs_rtsb_verify_common(
+       struct xfs_buf          *bp)
+{
+       struct xfs_rtsb         *rsb = bp->b_addr;
+
+       if (!xfs_verify_magic(bp, rsb->rsb_magicnum))
+               return __this_address;
+       if (rsb->rsb_pad)
+               return __this_address;
+
+       /* Everything to the end of the fs block must be zero */
+       if (memchr_inv(rsb + 1, 0, BBTOB(bp->b_length) - sizeof(*rsb)))
+               return __this_address;
+
+       return NULL;
+}
+
+/* Check superblock fields for a read or revalidation. */
+static inline xfs_failaddr_t
+xfs_rtsb_verify_all(
+       struct xfs_buf          *bp)
+{
+       struct xfs_rtsb         *rsb = bp->b_addr;
+       struct xfs_mount        *mp = bp->b_mount;
+       xfs_failaddr_t          fa;
+
+       fa = xfs_rtsb_verify_common(bp);
+       if (fa)
+               return fa;
+
+       if (memcmp(&rsb->rsb_fname, &mp->m_sb.sb_fname, XFSLABEL_MAX))
+               return __this_address;
+       if (!uuid_equal(&rsb->rsb_uuid, &mp->m_sb.sb_uuid))
+               return __this_address;
+       if (!uuid_equal(&rsb->rsb_meta_uuid, &mp->m_sb.sb_meta_uuid))
+               return  __this_address;
+
+       return NULL;
+}
+
+static void
+xfs_rtsb_read_verify(
+       struct xfs_buf          *bp)
+{
+       xfs_failaddr_t          fa;
+
+       if (!xfs_buf_verify_cksum(bp, XFS_RTSB_CRC_OFF)) {
+               xfs_verifier_error(bp, -EFSBADCRC, __this_address);
+               return;
+       }
+
+       fa = xfs_rtsb_verify_all(bp);
+       if (fa)
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+}
+
+static void
+xfs_rtsb_write_verify(
+       struct xfs_buf          *bp)
+{
+       xfs_failaddr_t          fa;
+
+       fa = xfs_rtsb_verify_common(bp);
+       if (fa) {
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+               return;
+       }
+
+       xfs_buf_update_cksum(bp, XFS_RTSB_CRC_OFF);
+}
+
+const struct xfs_buf_ops xfs_rtsb_buf_ops = {
+       .name           = "xfs_rtsb",
+       .magic          = { 0, cpu_to_be32(XFS_RTSB_MAGIC) },
+       .verify_read    = xfs_rtsb_read_verify,
+       .verify_write   = xfs_rtsb_write_verify,
+       .verify_struct  = xfs_rtsb_verify_all,
+};
index 809add4719f3b3b8b490fc7bf526000227a35dde..8443fd154796823288686fab5214012233da12e5 100644 (file)
@@ -127,6 +127,89 @@ xfs_verify_rgbext(
        return xfs_verify_rgbno(rtg, rgbno + len - 1);
 }
 
+static inline xfs_rtblock_t
+xfs_rgbno_to_rtb(
+       struct xfs_mount        *mp,
+       xfs_rgnumber_t          rgno,
+       xfs_rgblock_t           rgbno)
+{
+       ASSERT(xfs_has_rtgroups(mp));
+
+       if (mp->m_rgblklog >= 0)
+               return ((xfs_rtblock_t)rgno << mp->m_rgblklog) | rgbno;
+
+       return ((xfs_rtblock_t)rgno * mp->m_rgblocks) + rgbno;
+}
+
+static inline xfs_rgnumber_t
+xfs_rtb_to_rgno(
+       struct xfs_mount        *mp,
+       xfs_rtblock_t           rtbno)
+{
+       ASSERT(xfs_has_rtgroups(mp));
+
+       if (mp->m_rgblklog >= 0)
+               return rtbno >> mp->m_rgblklog;
+
+       return div_u64(rtbno, mp->m_rgblocks);
+}
+
+static inline xfs_rgblock_t
+xfs_rtb_to_rgbno(
+       struct xfs_mount        *mp,
+       xfs_rtblock_t           rtbno,
+       xfs_rgnumber_t          *rgno)
+{
+       uint32_t                rem;
+
+       ASSERT(xfs_has_rtgroups(mp));
+
+       if (mp->m_rgblklog >= 0) {
+               *rgno = rtbno >> mp->m_rgblklog;
+               return rtbno & mp->m_rgblkmask;
+       }
+
+       *rgno = div_u64_rem(rtbno, mp->m_rgblocks, &rem);
+       return rem;
+}
+
+static inline xfs_daddr_t
+xfs_rtb_to_daddr(
+       struct xfs_mount        *mp,
+       xfs_rtblock_t           rtbno)
+{
+       return rtbno << mp->m_blkbb_log;
+}
+
+static inline xfs_rtblock_t
+xfs_daddr_to_rtb(
+       struct xfs_mount        *mp,
+       xfs_daddr_t             daddr)
+{
+       return daddr >> mp->m_blkbb_log;
+}
+
+static inline xfs_rgnumber_t
+xfs_daddr_to_rgno(
+       struct xfs_mount        *mp,
+       xfs_daddr_t             daddr)
+{
+       xfs_rtblock_t           rtb = daddr >> mp->m_blkbb_log;
+
+       return xfs_rtb_to_rgno(mp, rtb);
+}
+
+static inline xfs_rgblock_t
+xfs_daddr_to_rgbno(
+       struct xfs_mount        *mp,
+       xfs_daddr_t             daddr)
+{
+       xfs_rtblock_t           rtb = daddr >> mp->m_blkbb_log;
+       xfs_rgnumber_t          rgno;
+
+       return xfs_rtb_to_rgbno(mp, rtb, &rgno);
+}
+
 #ifdef CONFIG_XFS_RT
 xfs_rgblock_t xfs_rtgroup_block_count(struct xfs_mount *mp,
                xfs_rgnumber_t rgno);
index 9aeaf3c89de441d179a61cf27f48d66a9995de61..93e56e934da737d49937a678d0d3b078c6226412 100644 (file)
@@ -182,6 +182,10 @@ xfs_sb_version_to_features(
                features |= XFS_FEAT_PARENT;
        if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)
                features |= XFS_FEAT_METADIR;
+       if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS)
+               features |= XFS_FEAT_RTGROUPS;
+       if (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_RTSB)
+               features |= XFS_FEAT_RTSB;
 
        return features;
 }
@@ -309,6 +313,65 @@ xfs_validate_sb_write(
        return 0;
 }
 
+static int
+xfs_validate_sb_rtgroups(
+       struct xfs_mount        *mp,
+       struct xfs_sb           *sbp)
+{
+       uint64_t                groups;
+
+       if (!(sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)) {
+               xfs_warn(mp,
+"Realtime groups require metadata directory tree.");
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rextsize == 0) {
+               xfs_warn(mp,
+"Realtime extent size must not be zero.");
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rgextents > XFS_MAX_RGBLOCKS / sbp->sb_rextsize) {
+               xfs_warn(mp,
+"Realtime group size (%u) must be less than %u rt extents.",
+                               sbp->sb_rgextents,
+                               XFS_MAX_RGBLOCKS / sbp->sb_rextsize);
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rgextents < XFS_MIN_RGEXTENTS) {
+               xfs_warn(mp,
+"Realtime group size (%u) must be at least %u rt extents.",
+                               sbp->sb_rgextents, XFS_MIN_RGEXTENTS);
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rgcount > XFS_MAX_RGNUMBER) {
+               xfs_warn(mp,
+"Realtime groups (%u) must be less than %u.",
+                               sbp->sb_rgcount, XFS_MAX_RGNUMBER);
+               return -EINVAL;
+       }
+
+       groups = howmany_64(sbp->sb_rextents, sbp->sb_rgextents);
+       if (groups != sbp->sb_rgcount) {
+               xfs_warn(mp,
+"Realtime groups (%u) do not cover the entire rt section; need (%llu) groups.",
+                               sbp->sb_rgcount, groups);
+               return -EINVAL;
+       }
+
+       /* Exchange-range is required for fsr to work on realtime files */
+       if (!(sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_EXCHRANGE)) {
+               xfs_warn(mp,
+"Realtime groups feature requires exchange-range support.");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /* Check the validity of the SB. */
 STATIC int
 xfs_validate_sb_common(
@@ -320,6 +383,7 @@ xfs_validate_sb_common(
        uint32_t                agcount = 0;
        uint32_t                rem;
        bool                    has_dalign;
+       int                     error;
 
        if (!xfs_verify_magic(bp, dsb->sb_magicnum)) {
                xfs_warn(mp,
@@ -369,6 +433,22 @@ xfs_validate_sb_common(
                                return -EINVAL;
                        }
                }
+
+               if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS) {
+                       error = xfs_validate_sb_rtgroups(mp, sbp);
+                       if (error)
+                               return error;
+               }
+
+               if (sbp->sb_features_ro_compat &
+                                       XFS_SB_FEAT_RO_COMPAT_RTSB) {
+                       if (!(sbp->sb_features_incompat &
+                                       XFS_SB_FEAT_INCOMPAT_RTGROUPS)) {
+                               xfs_warn(mp,
+"Realtime superblock feature requires realtime groups feature.");
+                               return -EINVAL;
+                       }
+               }
        } else if (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD |
                                XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD)) {
                        xfs_notice(mp,
@@ -691,8 +771,13 @@ __xfs_sb_from_disk(
        else
                to->sb_metadirino = NULLFSINO;
 
-       to->sb_rgcount = 0;
-       to->sb_rgextents = 0;
+       if (to->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS) {
+               to->sb_rgcount = be32_to_cpu(from->sb_rgcount);
+               to->sb_rgextents = be32_to_cpu(from->sb_rgextents);
+       } else {
+               to->sb_rgcount = 0;
+               to->sb_rgextents = 0;
+       }
 }
 
 void
@@ -843,6 +928,11 @@ xfs_sb_to_disk(
 
        if (from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)
                to->sb_metadirino = cpu_to_be64(from->sb_metadirino);
+
+       if (from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS) {
+               to->sb_rgcount = cpu_to_be32(from->sb_rgcount);
+               to->sb_rgextents = cpu_to_be32(from->sb_rgextents);
+       }
 }
 
 /*
@@ -998,9 +1088,9 @@ xfs_sb_mount_common(
        mp->m_blockwmask = mp->m_blockwsize - 1;
        mp->m_rtxblklog = log2_if_power2(sbp->sb_rextsize);
        mp->m_rtxblkmask = mask64_if_power2(sbp->sb_rextsize);
-       mp->m_rgblocks = 0;
-       mp->m_rgblklog = 0;
-       mp->m_rgblkmask = 0;
+       mp->m_rgblocks = sbp->sb_rgextents * sbp->sb_rextsize;
+       mp->m_rgblklog = log2_if_power2(mp->m_rgblocks);
+       mp->m_rgblkmask = mask64_if_power2(mp->m_rgblocks);
 
        mp->m_alloc_mxr[0] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 1);
        mp->m_alloc_mxr[1] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 0);
index 2f7413afbf46cdfafb93ea15daa7d3b9013a5275..0343926d2a6b4f9210b39e799cc25ddb55051be5 100644 (file)
@@ -39,6 +39,7 @@ extern const struct xfs_buf_ops xfs_inode_buf_ra_ops;
 extern const struct xfs_buf_ops xfs_refcountbt_buf_ops;
 extern const struct xfs_buf_ops xfs_rmapbt_buf_ops;
 extern const struct xfs_buf_ops xfs_rtbuf_ops;
+extern const struct xfs_buf_ops xfs_rtsb_buf_ops;
 extern const struct xfs_buf_ops xfs_sb_buf_ops;
 extern const struct xfs_buf_ops xfs_sb_quiet_buf_ops;
 extern const struct xfs_buf_ops xfs_symlink_buf_ops;
index 7c6638843e4bd87c63789656547aa796e94cd733..c1219bb7b5ebfa71ec576c2721447fc80ca1b66c 100644 (file)
@@ -150,7 +150,7 @@ typedef struct xfs_mount {
        int                     m_logbsize;     /* size of each log buffer */
        uint                    m_rsumlevels;   /* rt summary levels */
        uint                    m_rsumsize;     /* size of rt summary, bytes */
-       unsigned int            m_rgblocks;     /* size of rtgroup in rtblocks */
+       uint32_t                m_rgblocks;     /* size of rtgroup in rtblocks */
        int                     m_fixedfsid[2]; /* unchanged for life of FS */
        uint                    m_qflags;       /* quota status flags */
        uint64_t                m_features;     /* active filesystem features */