]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: define the format of rt groups
authorDarrick J. Wong <djwong@kernel.org>
Tue, 7 Mar 2023 03:55:19 +0000 (19:55 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 22 Nov 2023 23:03:33 +0000 (15:03 -0800)
Define the ondisk format of realtime group metadata.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
12 files changed:
db/convert.c
include/libxfs.h
libfrog/util.c
libfrog/util.h
libxfs/libxfs_priv.h
libxfs/xfs_format.h
libxfs/xfs_rtgroup.c
libxfs/xfs_rtgroup.h
libxfs/xfs_sb.c
libxfs/xfs_shared.h
mkfs/xfs_mkfs.c
repair/sb.c

index 048c1766f8152b37a424d12a8ee5586c8610617b..7c0b1edc2358d1057dd827bcc55de2cc5bece1e8 100644 (file)
@@ -413,14 +413,6 @@ convert_f(int argc, char **argv)
        return 0;
 }
 
-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 uint64_t
 rt_daddr_to_rsumblock(
        struct xfs_mount        *mp,
index 9a765aaad3d5990934b5555f4a9a99ecef1de01a..f9f7e8d86c61745f5a9a18490cde02d63c795a35 100644 (file)
@@ -91,6 +91,8 @@ struct iomap;
 #include "xfs_parent.h"
 #include "xfs_imeta.h"
 #include "imeta_utils.h"
+#include "xfs_rtbitmap.h"
+#include "xfs_rtgroup.h"
 
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
index 8fb10cf82f5ca4fa0f382df3a84a4c4c06624bce..46047571a5531f16f8832c64c6734dbb5e9ef45c 100644 (file)
@@ -22,3 +22,17 @@ log2_roundup(unsigned int i)
        }
        return rval;
 }
+
+void *
+memchr_inv(const void *start, int c, size_t bytes)
+{
+       const unsigned char     *p = start;
+
+       while (bytes > 0) {
+               if (*p != (unsigned char)c)
+                       return (void *)p;
+               bytes--;
+       }
+
+       return NULL;
+}
index 1b97881bf1687c351aba142b3e9293825c5a752e..ac2f331c93ecd583bcc329f0c48d653bdff5224c 100644 (file)
@@ -8,4 +8,6 @@
 
 unsigned int   log2_roundup(unsigned int i);
 
+void *memchr_inv(const void *start, int c, size_t bytes);
+
 #endif /* __LIBFROG_UTIL_H__ */
index 57b8edc54a59e41567812ef4f56c84e12e9a52d7..4e4a51637e6bd52b19f0539a27126c01d7787958 100644 (file)
@@ -52,6 +52,7 @@
 #include "libfrog/radix-tree.h"
 #include "libfrog/bitmask.h"
 #include "libfrog/div64.h"
+#include "libfrog/util.h"
 #include "atomic.h"
 #include "spinlock.h"
 #include "linux-err.h"
@@ -391,6 +392,24 @@ static inline unsigned long long mask64_if_power2(unsigned long b)
        return is_power_of_2(b) ? b - 1 : 0;
 }
 
+/* If @b is a power of 2, return log2(b).  Else return zero. */
+static inline unsigned int log2_if_power(unsigned long b)
+{
+       unsigned long   mask = 1;
+       unsigned int    i;
+       unsigned int    ret = 1;
+
+       if (!is_power_of_2(b))
+              return 0;
+
+       for (i = 0; i < NBBY * sizeof(unsigned long); i++, mask <<= 1) {
+               if (b & mask)
+                       ret = i;
+       }
+
+       return ret;
+}
+
 /* buffer management */
 #define XBF_TRYLOCK                    0
 #define XBF_UNMAPPED                   0
index 32141f67bd73f90eb2be473bc76661b6af169f31..27ebd22556e05f0fae369bef3154a2f3fb7fbd09 100644 (file)
@@ -216,7 +216,17 @@ struct xfs_dsb {
         * pointers are no longer used.
         */
        __be64          sb_rbmino;
-       __be64          sb_rsumino;     /* summary inode for rt bitmap */
+       /*
+        * rtgroups requires metadir, so we reuse the rsumino space to hold
+        * the rg block count and shift values.
+        */
+       union {
+               __be64  sb_rsumino;     /* summary inode for rt bitmap */
+               struct {
+                       __be32  sb_rgcount;     /* # of realtime groups */
+                       __be32  sb_rgblocks;    /* rtblocks per group */
+               };
+       };
        __be32          sb_rextsize;    /* realtime extent size, blocks */
        __be32          sb_agblocks;    /* size of an allocation group */
        __be32          sb_agcount;     /* number of allocation groups */
@@ -398,6 +408,7 @@ xfs_sb_has_ro_compat_feature(
 #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4)      /* needs xfs_repair */
 #define XFS_SB_FEAT_INCOMPAT_NREXT64   (1 << 5)        /* large extent counters */
 #define XFS_SB_FEAT_INCOMPAT_PARENT    (1 << 6)        /* 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|    \
@@ -752,6 +763,55 @@ 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.
+ */
+#define XFS_MAX_RGBLOCKS       ((xfs_rgblock_t)(1U << 31) - 1)
+#define XFS_MAX_RGNUMBER       ((xfs_rgnumber_t)(-1U))
+
+#define XFS_RTSB_MAGIC 0x58524750      /* 'XRGP' */
+
+/*
+ * Realtime superblock - on disk version.  Must be padded to 64 bit alignment.
+ * The first block of each realtime group contains this superblock; this is
+ * how we avoid having file data extents cross a group boundary.
+ */
+struct xfs_rtsb {
+       __be32          rsb_magicnum;   /* magic number == XFS_RTSB_MAGIC */
+       __be32          rsb_blocksize;  /* logical block size, bytes */
+       __be64          rsb_rblocks;    /* number of realtime blocks */
+
+       __be64          rsb_rextents;   /* number of realtime extents */
+       __be64          rsb_lsn;        /* last write sequence */
+
+       __be32          rsb_rgcount;    /* # of realtime groups */
+       unsigned char   rsb_fname[XFSLABEL_MAX]; /* rt volume name */
+
+       uuid_t          rsb_uuid;       /* user-visible file system unique id */
+
+       __be32          rsb_rextsize;   /* realtime extent size, blocks */
+       __be32          rsb_rbmblocks;  /* number of rt bitmap blocks */
+
+       __be32          rsb_rgblocks;   /* rt blocks per group */
+       __u8            rsb_blocklog;   /* log2 of sb_blocksize */
+       __u8            rsb_sectlog;    /* log2 of sb_sectsize */
+       __u8            rsb_rextslog;   /* log2 of sb_rextents */
+       __u8            rsb_pad;
+
+       __le32          rsb_crc;        /* superblock crc */
+       __le32          rsb_pad2;
+
+       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 24591cd7d796f179a8b3c14330debb5b94de3e52..ed3ff1407f3e811a67cd17e6537d558b056000f8 100644 (file)
@@ -247,3 +247,98 @@ xfs_rtgroup_block_count(
        return __xfs_rtgroup_block_count(mp, rgno, mp->m_sb.sb_rgcount,
                        mp->m_sb.sb_rblocks);
 }
+
+static xfs_failaddr_t
+xfs_rtsb_verify(
+       struct xfs_buf          *bp)
+{
+       struct xfs_mount        *mp = bp->b_mount;
+       struct xfs_rtsb         *rsb = bp->b_addr;
+
+       if (!xfs_verify_magic(bp, rsb->rsb_magicnum))
+               return __this_address;
+       if (be32_to_cpu(rsb->rsb_blocksize) != mp->m_sb.sb_blocksize)
+               return __this_address;
+       if (be64_to_cpu(rsb->rsb_rblocks) != mp->m_sb.sb_rblocks)
+               return __this_address;
+
+       if (be64_to_cpu(rsb->rsb_rextents) != mp->m_sb.sb_rextents)
+               return __this_address;
+
+       if (!uuid_equal(&rsb->rsb_uuid, &mp->m_sb.sb_uuid))
+               return __this_address;
+
+       if (be32_to_cpu(rsb->rsb_rgcount) != mp->m_sb.sb_rgcount)
+               return __this_address;
+
+       if (be32_to_cpu(rsb->rsb_rextsize) != mp->m_sb.sb_rextsize)
+               return __this_address;
+       if (be32_to_cpu(rsb->rsb_rbmblocks) != mp->m_sb.sb_rbmblocks)
+               return __this_address;
+
+       if (be32_to_cpu(rsb->rsb_rgblocks) != mp->m_sb.sb_rgblocks)
+               return __this_address;
+       if (rsb->rsb_blocklog != mp->m_sb.sb_blocklog)
+               return __this_address;
+       if (rsb->rsb_sectlog != mp->m_sb.sb_sectlog)
+               return __this_address;
+       if (rsb->rsb_rextslog != mp->m_sb.sb_rextslog)
+               return __this_address;
+       if (rsb->rsb_pad)
+               return __this_address;
+
+       if (rsb->rsb_pad2)
+               return __this_address;
+
+       if (!uuid_equal(&rsb->rsb_meta_uuid, &mp->m_sb.sb_meta_uuid))
+               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;
+}
+
+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);
+       else {
+               fa = xfs_rtsb_verify(bp);
+               if (fa)
+                       xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+       }
+}
+
+static void
+xfs_rtsb_write_verify(
+       struct xfs_buf          *bp)
+{
+       struct xfs_rtsb         *rsb = bp->b_addr;
+       struct xfs_buf_log_item *bip = bp->b_log_item;
+       xfs_failaddr_t          fa;
+
+       fa = xfs_rtsb_verify(bp);
+       if (fa) {
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
+               return;
+       }
+
+       if (bip)
+               rsb->rsb_lsn = cpu_to_be64(bip->bli_item.li_lsn);
+
+       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,
+};
index 71f1a9bd821d2eacf21707b17753d553057377d4..2137d1b2a48cdd3c1b8551c88ed9675183dd0d74 100644 (file)
@@ -122,6 +122,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_sb.sb_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_sb.sb_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_sb.sb_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 153fc3a6294175091b87131fa73bbf613da9e769..c53ba70a8930b163b231e10a8e8458a945367fab 100644 (file)
@@ -177,6 +177,8 @@ 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;
 
        return features;
 }
@@ -304,6 +306,64 @@ 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_rgblocks > XFS_MAX_RGBLOCKS) {
+               xfs_warn(mp,
+"Realtime group size (%u) must be less than %u.",
+                        sbp->sb_rgblocks, XFS_MAX_RGBLOCKS);
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rextsize == 0) {
+               xfs_warn(mp,
+"Realtime extent size must not be zero.");
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rgblocks % sbp->sb_rextsize != 0) {
+               xfs_warn(mp,
+"Realtime group size (%u) must be an even multiple of extent size (%u).",
+                        sbp->sb_rgblocks, sbp->sb_rextsize);
+               return -EINVAL;
+       }
+
+       if (sbp->sb_rgblocks < (sbp->sb_rextsize << 1)) {
+               xfs_warn(mp,
+"Realtime group size (%u) must be greater than 1 rt extent.",
+                        sbp->sb_rgblocks);
+               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_rblocks, sbp->sb_rgblocks);
+       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;
+       }
+
+       return 0;
+}
+
 /* Check the validity of the SB. */
 STATIC int
 xfs_validate_sb_common(
@@ -315,6 +375,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,
@@ -364,6 +425,12 @@ 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;
+               }
        } else if (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD |
                                XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD)) {
                        xfs_notice(mp,
@@ -698,8 +765,13 @@ __xfs_sb_from_disk(
                to->sb_pquotino = NULLFSINO;
        }
 
-       to->sb_rgcount = 0;
-       to->sb_rgblocks = 0;
+       if (to->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS) {
+               to->sb_rgcount = be32_to_cpu(from->sb_rgcount);
+               to->sb_rgblocks = be32_to_cpu(from->sb_rgblocks);
+       } else {
+               to->sb_rgcount = 0;
+               to->sb_rgblocks = 0;
+       }
 }
 
 void
@@ -859,6 +931,12 @@ xfs_sb_to_disk(
                to->sb_gquotino = cpu_to_be64(NULLFSINO);
                to->sb_pquotino = cpu_to_be64(NULLFSINO);
        }
+
+       if (from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_RTGROUPS) {
+               /* must come after setting to_rsumino */
+               to->sb_rgcount = cpu_to_be32(from->sb_rgcount);
+               to->sb_rgblocks = cpu_to_be32(from->sb_rgblocks);
+       }
 }
 
 /*
@@ -1014,8 +1092,8 @@ 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_rgblklog = 0;
-       mp->m_rgblkmask = 0;
+       mp->m_rgblklog = log2_if_power2(sbp->sb_rgblocks);
+       mp->m_rgblkmask = mask64_if_power2(sbp->sb_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 2cecebe018814d3854d4567bae7026c5c6fe77b5..f76d2789e1c2d2dd92f879b4d0c34b1126baf0c9 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 00c747a6bdbdadb84e7146b76f906167f86cc5b3..d8618818282688a68bf27a121949425cd6a1ab24 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  * All Rights Reserved.
  */
+#include <stddef.h>
 #include "libfrog/util.h"
 #include "libxfs.h"
 #include <ctype.h>
index c5dbc6c2062f4dbd7c2e2da19be93be535ecb9f3..6e7f448596e2ae5203ff86ae7c112e3adaa7dd3c 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
  * All Rights Reserved.
  */
+#include <stddef.h>
 #include "libfrog/util.h"
 #include "libxfs.h"
 #include "libxcmd.h"