From cc079a4d907eedd24c69ab2a88e9e0fcdef58a5d Mon Sep 17 00:00:00 2001 From: Andrew Price Date: Apr 15 2018 16:57:01 +0000 Subject: mkfs.gfs2: Scale down journal size for smaller devices Currently the default behaviour when the journal size is not specified is to use a default size of 128M, which means that mkfs.gfs2 can run out of space while writing to a small device. The hard default also means that some xfstests fail with gfs2 as they try to create small file systems. This patch addresses these problems by setting sensible default journal sizes depending on the size of the file system. Journal sizes specified by the user are limited to half of the fs. As the minimum journal size is 8MB that means we effectively get a hard minimum file system size of 16MB (per journal). Resolves: rhbz#1498068 Signed-off-by: Andrew Price --- diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h index ebf6bca..570c89b 100644 --- a/gfs2/libgfs2/libgfs2.h +++ b/gfs2/libgfs2/libgfs2.h @@ -319,6 +319,8 @@ struct metapath { #define GFS2_DEFAULT_BSIZE (4096) #define GFS2_DEFAULT_JSIZE (128) +#define GFS2_MAX_JSIZE (1024) +#define GFS2_MIN_JSIZE (8) #define GFS2_DEFAULT_RGSIZE (256) #define GFS2_DEFAULT_UTSIZE (1) #define GFS2_DEFAULT_QCSIZE (1) diff --git a/gfs2/man/mkfs.gfs2.8 b/gfs2/man/mkfs.gfs2.8 index 342a636..35e355a 100644 --- a/gfs2/man/mkfs.gfs2.8 +++ b/gfs2/man/mkfs.gfs2.8 @@ -32,8 +32,9 @@ Enable debugging output. Print out a help message describing the available options, then exit. .TP \fB-J\fP \fImegabytes\fR -The size of each journal. The default journal size is 128 megabytes and the -minimum size is 8 megabytes. +The size of each journal. The minimum size is 8 megabytes and the maximum is +1024. If this is not specified, a value based on a sensible proportion of the +file system will be chosen. .TP \fB-j\fP \fIjournals\fR The number of journals for mkfs.gfs2 to create. At least one journal is diff --git a/gfs2/mkfs/main_mkfs.c b/gfs2/mkfs/main_mkfs.c index 2e08bc6..24e5a85 100644 --- a/gfs2/mkfs/main_mkfs.c +++ b/gfs2/mkfs/main_mkfs.c @@ -552,7 +552,7 @@ static void opts_check(struct mkfs_opts *opts) if (!opts->journals) die( _("no journals specified\n")); - if (opts->jsize < 8 || opts->jsize > 1024) + if (opts->jsize < GFS2_MIN_JSIZE || opts->jsize > GFS2_MAX_JSIZE) die( _("bad journal size\n")); if (!opts->qcsize || opts->qcsize > 64) @@ -575,6 +575,7 @@ static void print_results(struct gfs2_sb *sb, struct mkfs_opts *opts, uint64_t r printf("%-27s%.2f %s (%"PRIu64" %s)\n", _("Filesystem size:"), (fssize / ((float)(1 << 30)) * sb->sb_bsize), _("GB"), fssize, _("blocks")); printf("%-27s%u\n", _("Journals:"), opts->journals); + printf("%-27s%uMB\n", _("Journal size:"), opts->jsize); printf("%-27s%"PRIu64"\n", _("Resource groups:"), rgrps); printf("%-27s\"%s\"\n", _("Locking protocol:"), opts->lockproto); printf("%-27s\"%s\"\n", _("Lock table:"), opts->locktable); @@ -823,6 +824,36 @@ static int place_rgrps(struct gfs2_sbd *sdp, lgfs2_rgrps_t rgs, struct mkfs_opts return 0; } +/* + * Find a reasonable journal file size (in blocks) given the number of blocks + * in the filesystem. For very small filesystems, it is not reasonable to + * have a journal that fills more than half of the filesystem. + * + * n.b. comments assume 4k blocks + * + * This was copied and adapted from e2fsprogs. + */ +static int default_journal_size(unsigned bsize, uint64_t num_blocks) +{ + int min_blocks = (GFS2_MIN_JSIZE << 20) / bsize; + + if (num_blocks < 2 * min_blocks) + return -1; + if (num_blocks < 131072) /* 512 MB */ + return min_blocks; /* 8 MB */ + if (num_blocks < 512*1024) /* 2 GB */ + return (4096); /* 16 MB */ + if (num_blocks < 2048*1024) /* 8 GB */ + return (8192); /* 32 MB */ + if (num_blocks < 4096*1024) /* 16 GB */ + return (16384); /* 64 MB */ + if (num_blocks < 262144*1024) /* 1 TB */ + return (32768); /* 128 MB */ + if (num_blocks < 2621440*1024) /* 10 TB */ + return (131072); /* 512 MB */ + return 262144; /* 1 GB */ +} + static void sbd_init(struct gfs2_sbd *sdp, struct mkfs_opts *opts, unsigned bsize) { memset(sdp, 0, sizeof(struct gfs2_sbd)); @@ -847,9 +878,28 @@ static void sbd_init(struct gfs2_sbd *sdp, struct mkfs_opts *opts, unsigned bsiz opts->dev.size / ((float)(1 << 30)), _("GB"), opts->dev.size / sdp->bsize, _("blocks")); } - /* TODO: Check if the fssize is too small, somehow */ sdp->device.length = opts->fssize; } + /* opts->jsize has already been max/min checked but we need to check it + makes sense for the device size, or set a sensible default, if one + will fit. For user-provided journal sizes, limit it to half of the fs. */ + if (!opts->got_jsize) { + int default_jsize = default_journal_size(sdp->bsize, sdp->device.length / opts->journals); + if (default_jsize < 0) { + fprintf(stderr, _("gfs2 will not fit on this device.\n")); + exit(1); + } + opts->jsize = (default_jsize * sdp->bsize) >> 20; + } else if ((((opts->jsize * opts->journals) << 20) / sdp->bsize) > (sdp->device.length / 2)) { + unsigned max_jsize = (sdp->device.length / 2 * sdp->bsize / opts->journals) >> 20; + + fprintf(stderr, _("gfs2 will not fit on this device.\n")); + if (max_jsize >= GFS2_MIN_JSIZE) + fprintf(stderr, _("Maximum size for %u journals on this device is %uMB.\n"), + opts->journals, max_jsize); + exit(1); + } + sdp->jsize = opts->jsize; } static int probe_contents(struct mkfs_dev *dev) diff --git a/tests/edit.at b/tests/edit.at index 3bd4163..e1a0fca 100644 --- a/tests/edit.at +++ b/tests/edit.at @@ -6,7 +6,7 @@ AT_KEYWORDS(gfs2_edit edit) GFS_TGT_REGEN AT_CHECK([$GFS_MKFS -p lock_nolock $GFS_TGT $(($(gfs_max_blocks 4096)/2))], 0, [ignore], [ignore]) AT_CHECK([gfs2_edit savemeta $GFS_TGT test.meta > savemeta.log], 0, [ignore], [ignore]) -AT_CHECK([head -2 savemeta.log], 0, [There are 1310718 blocks of 4096 bytes in the filesystem. +AT_CHECK([head -2 savemeta.log], 0, [There are 1310716 blocks of 4096 bytes in the filesystem. Filesystem size: 4.1023GB ], [ignore]) GFS_TGT_REGEN diff --git a/tests/mkfs.at b/tests/mkfs.at index 274a81d..68195e9 100644 --- a/tests/mkfs.at +++ b/tests/mkfs.at @@ -123,3 +123,13 @@ AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=0:512:65536:393216:512 $GFS_ # Check rgrp alignment to minimum_io_size: 65536 / 4096 == 16 AT_CHECK([gfs2_edit -p rindex $GFS_TGT | grep ri_addr | awk '{print $2, $2 % 16; if ($2 % 16 != 0) { exit 1 }}'], 0, [ignore], [ignore]) AT_CLEANUP + +AT_SETUP([Small filesystems]) +AT_KEYWORDS(mkfs.gfs2 mkfs) +GFS_TGT_SIZE(32M) +AT_CHECK([$GFS_MKFS -p lock_nolock $GFS_TGT], 0, [ignore], [ignore]) +AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore]) +GFS_TGT_SIZE(64M) +AT_CHECK([$GFS_MKFS -p lock_nolock -j2 $GFS_TGT], 0, [ignore], [ignore]) +AT_CHECK([fsck.gfs2 -n $GFS_TGT], 0, [ignore], [ignore]) +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index cc1bd54..522ac1c 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -4,6 +4,12 @@ m4_define([GFS_TGT_REGEN], [AT_CHECK([rm -f $GFS_TGT && truncate -s ${GFS_TGT_SZ}G ${GFS_TGT}], [ignore], [ignore], [ignore]) AT_SKIP_IF([test ! -f ${GFS_TGT}])]) +# Regenerate the sparse file used for testing, with a given size, and skip the test if it fails +# Usage: GFS_TGT_REGEN() +m4_define([GFS_TGT_SIZE], +[AT_CHECK([rm -f $GFS_TGT && truncate -s $1 ${GFS_TGT}], [ignore], [ignore], [ignore]) +AT_SKIP_IF([test ! -f ${GFS_TGT}])]) + # Regenerate, check, fsck is used a lot so combine it into one macro # Usage: GFS_FSCK_CHECK ([mkfs.gfs2 ... $GFS_TGT]) m4_define([GFS_FSCK_CHECK],