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).

Signed-off-by: Andrew Price <anpr...@redhat.com>
---

v3: Use 128M journals up to 1TB filesystems (assuming 4K blocks) and rework the
    journal size ranges to be more sensible, based on Bob's feedback.

 gfs2/libgfs2/libgfs2.h |  2 ++
 gfs2/man/mkfs.gfs2.8   |  5 +++--
 gfs2/mkfs/main_mkfs.c  | 54 ++++++++++++++++++++++++++++++++++++++++++++++++--
 tests/edit.at          |  2 +-
 tests/mkfs.at          | 10 ++++++++++
 tests/testsuite.at     |  6 ++++++
 6 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 85ac74cb..15d2a9d1 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 342a636d..35e355a5 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 54ff2db6..05e97498 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);
@@ -814,6 +815,36 @@ static int place_rgrps(struct gfs2_sbd *sdp, lgfs2_rgrps_t 
rgs, uint64_t *rgaddr
        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));
@@ -838,9 +869,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 3bd41636..e1a0fca7 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 be888171..7ae6fcc2 100644
--- a/tests/mkfs.at
+++ b/tests/mkfs.at
@@ -155,3 +155,13 @@ AT_CHECK([rgrifieldscheck.sh $GFS_TGT], 0, [ignore], 
[ignore])
 AT_CHECK([$GFS_MKFS -p lock_nolock -o test_topology=0:512:65536:393216:512 
$GFS_TGT], 0, [ignore], [ignore])
 AT_CHECK([rgrifieldscheck.sh $GFS_TGT], 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 cc1bd54d..522ac1c2 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(<size>)
+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],
-- 
2.14.3

Reply via email to