Blame SOURCES/bz1498068-mkfs_gfs2_Scale_down_journal_size_for_smaller_devices.patch

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