Blame SOURCES/bz1498068-mkfs_gfs2_Scale_down_journal_size_for_smaller_devices.patch

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