]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xfs: make rextslog computation consistent with mkfs
authorDarrick J. Wong <djwong@kernel.org>
Wed, 27 Mar 2024 00:12:17 +0000 (17:12 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Apr 2024 13:28:47 +0000 (15:28 +0200)
commit a6a38f309afc4a7ede01242b603f36c433997780 upstream.

[backport: resolve merge conflicts due to refactoring rtbitmap/summary
macros and accessors]

There's a weird discrepancy in xfsprogs dating back to the creation of
the Linux port -- if there are zero rt extents, mkfs will set
sb_rextents and sb_rextslog both to zero:

sbp->sb_rextslog =
(uint8_t)(rtextents ?
libxfs_highbit32((unsigned int)rtextents) : 0);

However, that's not the check that xfs_repair uses for nonzero rtblocks:

if (sb->sb_rextslog !=
libxfs_highbit32((unsigned int)sb->sb_rextents))

The difference here is that xfs_highbit32 returns -1 if its argument is
zero.  Unfortunately, this means that in the weird corner case of a
realtime volume shorter than 1 rt extent, xfs_repair will immediately
flag a freshly formatted filesystem as corrupt.  Because mkfs has been
writing ondisk artifacts like this for decades, we have to accept that
as "correct".  TBH, zero rextslog for zero rtextents makes more sense to
me anyway.

Regrettably, the superblock verifier checks created in commit copied
xfs_repair even though mkfs has been writing out such filesystems for
ages.  Fix the superblock verifier to accept what mkfs spits out; the
userspace version of this patch will have to fix xfs_repair as well.

Note that the new helper leaves the zeroday bug where the upper 32 bits
of sb_rextents is ripped off and fed to highbit32.  This leads to a
seriously undersized rt summary file, which immediately breaks mkfs:

$ hugedisk.sh foo /dev/sdc $(( 0x100000080 * 4096))B
$ /sbin/mkfs.xfs -f /dev/sda -m rmapbt=0,reflink=0 -r rtdev=/dev/mapper/foo
meta-data=/dev/sda               isize=512    agcount=4, agsize=1298176 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=0
         =                       reflink=0    bigtime=1 inobtcount=1 nrext64=1
data     =                       bsize=4096   blocks=5192704, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =/dev/mapper/foo        extsz=4096   blocks=4294967424, rtextents=4294967424
Discarding blocks...Done.
mkfs.xfs: Error initializing the realtime space [117 - Structure needs cleaning]

The next patch will drop support for rt volumes with fewer than 1 or
more than 2^32-1 rt extents, since they've clearly been broken forever.

Fixes: f8e566c0f5e1f ("xfs: validate the realtime geometry in xfs_validate_sb_common")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
Acked-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/xfs/libxfs/xfs_rtbitmap.c
fs/xfs/libxfs/xfs_rtbitmap.h
fs/xfs/libxfs/xfs_sb.c
fs/xfs/xfs_rtalloc.c

index 9eb1b5aa7e35a62273fd55f3ea8470849ef4a51a..37b425ea3fedd09efe84b9a04872c08a002e9272 100644 (file)
@@ -1130,3 +1130,16 @@ xfs_rtalloc_extent_is_free(
        *is_free = matches;
        return 0;
 }
+
+/*
+ * Compute the maximum level number of the realtime summary file, as defined by
+ * mkfs.  The use of highbit32 on a 64-bit quantity is a historic artifact that
+ * prohibits correct use of rt volumes with more than 2^32 extents.
+ */
+uint8_t
+xfs_compute_rextslog(
+       xfs_rtbxlen_t           rtextents)
+{
+       return rtextents ? xfs_highbit32(rtextents) : 0;
+}
+
index c3ef22e67aa38ab87771356adba28a7a91f41a15..6becdc7a48ede41d568232bf3e71c288cc1794a9 100644 (file)
@@ -70,6 +70,9 @@ xfs_rtfree_extent(
 /* Same as above, but in units of rt blocks. */
 int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
                xfs_filblks_t rtlen);
+
+uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
+
 #else /* CONFIG_XFS_RT */
 # define xfs_rtfree_extent(t,b,l)                      (-ENOSYS)
 # define xfs_rtfree_blocks(t,rb,rl)                    (-ENOSYS)
@@ -77,6 +80,7 @@ int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
 # define xfs_rtalloc_query_all(m,t,f,p)                        (-ENOSYS)
 # define xfs_rtbuf_get(m,t,b,i,p)                      (-ENOSYS)
 # define xfs_rtalloc_extent_is_free(m,t,s,l,i)         (-ENOSYS)
+# define xfs_compute_rextslog(rtx)                     (0)
 #endif /* CONFIG_XFS_RT */
 
 #endif /* __XFS_RTBITMAP_H__ */
index 6264daaab37b06151bd12abc2ef9f41f61fcc675..25eec54f9bb2b5c9b86d0e6a7e9fe750b0ebb8db 100644 (file)
@@ -25,6 +25,7 @@
 #include "xfs_da_format.h"
 #include "xfs_health.h"
 #include "xfs_ag.h"
+#include "xfs_rtbitmap.h"
 
 /*
  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
@@ -509,7 +510,7 @@ xfs_validate_sb_common(
                                       NBBY * sbp->sb_blocksize);
 
                if (sbp->sb_rextents != rexts ||
-                   sbp->sb_rextslog != xfs_highbit32(sbp->sb_rextents) ||
+                   sbp->sb_rextslog != xfs_compute_rextslog(rexts) ||
                    sbp->sb_rbmblocks != rbmblocks) {
                        xfs_notice(mp,
                                "realtime geometry sanity check failed");
index 5a439d90e51cc6caec7fef194a4ce964100e4cbd..5fbe5e33c4253feea4fb9120ca5eac689d3f0ec1 100644 (file)
@@ -999,7 +999,7 @@ xfs_growfs_rt(
        nrextents = nrblocks;
        do_div(nrextents, in->extsize);
        nrbmblocks = howmany_64(nrextents, NBBY * sbp->sb_blocksize);
-       nrextslog = xfs_highbit32(nrextents);
+       nrextslog = xfs_compute_rextslog(nrextents);
        nrsumlevels = nrextslog + 1;
        nrsumsize = (uint)sizeof(xfs_suminfo_t) * nrsumlevels * nrbmblocks;
        nrsumblocks = XFS_B_TO_FSB(mp, nrsumsize);
@@ -1061,7 +1061,7 @@ xfs_growfs_rt(
                nsbp->sb_rextents = nsbp->sb_rblocks;
                do_div(nsbp->sb_rextents, nsbp->sb_rextsize);
                ASSERT(nsbp->sb_rextents != 0);
-               nsbp->sb_rextslog = xfs_highbit32(nsbp->sb_rextents);
+               nsbp->sb_rextslog = xfs_compute_rextslog(nsbp->sb_rextents);
                nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog + 1;
                nrsumsize =
                        (uint)sizeof(xfs_suminfo_t) * nrsumlevels *