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>
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;
}
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;
}
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;
* 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);