From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> Reviewed-by: Christoph Hellwig <hch@xxxxxx> Reviewed-by: Bill O'Donnell <bodonnel@xxxxxxxxxx> --- mkfs/xfs_mkfs.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c index fcbf5413269a..b8e2c0da60c4 100644 --- a/mkfs/xfs_mkfs.c +++ b/mkfs/xfs_mkfs.c @@ -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);