]> git.apps.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
mkfs: fix log sunit rounding when external logs are in use
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 23:07:28 +0000 (16:07 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 17 Apr 2024 21:06:22 +0000 (14:06 -0700)
Due to my heinous nature, I set up an external log device with 4k LBAs
using this command:

# losetup -b 4096 -o 4096 --sizelimit $(( (128 * 1048576) - 4096 )) -f /dev/sdb
# blockdev --getsize64 /dev/loop0
134213632

This creates a log device that is slightly smaller than 128MB in size.
Next I ran generic/054, which sets the log sunit to 256k and fails:

# mkfs.xfs -f /dev/sda -l logdev=/dev/loop0,su=256k,version=2 -s size=4096
meta-data=/dev/sda               isize=512    agcount=4, agsize=72448 blks
         =                       sectsz=4096  attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=1
         =                       metadir=0
data     =                       bsize=4096   blocks=289792, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1, parent=0
log      =/dev/loop0             bsize=4096   blocks=32768, version=2
         =                       sectsz=4096  sunit=64 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
         =                       rgcount=0    rgsize=0 blks
Discarding blocks...Done.
Discarding blocks...Done.
mkfs.xfs: libxfs_device_zero write failed: No space left on device

Notice that mkfs thinks it should format a 32768-fsblock external log,
but the log device itself is 32767 fsblocks.  Hence the write goes off
the end of the device and we get ENOSPC.

I tracked this behavior down to align_log_size in mkfs, which first
tries to round the log size up to a stripe boundary, then tries to round
it down.  Unfortunately, in the case of an external log we call the
function with XFS_MAX_LOG_BLOCKS without accounting for the possibility
that the log device might be smaller.

Correct the callsite and clean up the open-coded rounding.

Fixes: 8d1bff2be336 ("mkfs: reduce internal log size when log stripe units are in play")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bill O'Donnell <bodonnel@redhat.com>
mkfs/xfs_mkfs.c

index fcbf5413269ab7939c3720f677ed7f0a94c2edd3..b8e2c0da60c416e09af383a8c473143597c6464f 100644 (file)
@@ -3338,13 +3338,13 @@ _("log size %lld is not a multiple of the log stripe unit %d\n"),
                usage();
        }
 
-       tmp_logblocks = ((cfg->logblocks + (sunit - 1)) / sunit) * sunit;
+       tmp_logblocks = roundup_64(cfg->logblocks, sunit);
 
        /* If the log is too large, round down instead of round up */
        if ((tmp_logblocks > XFS_MAX_LOG_BLOCKS) ||
            ((tmp_logblocks << cfg->blocklog) > XFS_MAX_LOG_BYTES) ||
            tmp_logblocks > max_logblocks) {
-               tmp_logblocks = (cfg->logblocks / sunit) * sunit;
+               tmp_logblocks = rounddown_64(cfg->logblocks, sunit);
        }
        cfg->logblocks = tmp_logblocks;
 }
@@ -3465,6 +3465,7 @@ static void
 calculate_log_size(
        struct mkfs_params      *cfg,
        struct cli_params       *cli,
+       struct libxfs_init      *xi,
        struct xfs_mount        *mp)
 {
        struct xfs_sb           *sbp = &mp->m_sb;
@@ -3503,8 +3504,13 @@ _("external log device size %lld blocks too small, must be at least %lld blocks\
                }
                cfg->logstart = 0;
                cfg->logagno = 0;
-               if (cfg->lsunit)
-                       align_log_size(cfg, cfg->lsunit, XFS_MAX_LOG_BLOCKS);
+               if (cfg->lsunit) {
+                       uint64_t        max_logblocks;
+
+                       max_logblocks = min(DTOBT(xi->log.size, cfg->blocklog),
+                                           XFS_MAX_LOG_BLOCKS);
+                       align_log_size(cfg, cfg->lsunit, max_logblocks);
+               }
 
                validate_log_size(cfg->logblocks, cfg->blocklog, min_logblocks);
                return;
@@ -4257,7 +4263,7 @@ main(
         * With the mount set up, we can finally calculate the log size
         * constraints and do default size calculations and final validation
         */
-       calculate_log_size(&cfg, &cli, mp);
+       calculate_log_size(&cfg, &cli, &xi, mp);
 
        finish_superblock_setup(&cfg, mp, sbp);