From 2c0500e6036dc996ea9553c9d56b26f54d815e45 Mon Sep 17 00:00:00 2001
From: "Darrick J. Wong" <djwong@kernel.org>
Date: Fri, 29 Sep 2023 09:53:42 +0000
Subject: [PATCH 07/11] mkfs: enable the new force-align feature

Make it so that we can create filesystems with the forcealign feature
turned on.

jpg: enforce extsize must be a power-of-2 for forcealign, relocate
     is_power_of_2() to be accessible for mkfs

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: John Garry <john.g.garry@oracle.com>

---
 include/libxfs.h     |   6 +++
 libxfs/libxfs_priv.h |   6 ---
 man/man8/mkfs.xfs.8  |  15 ++++++
 mkfs/xfs_mkfs.c      | 124 ++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 143 insertions(+), 8 deletions(-)

diff --git a/include/libxfs.h b/include/libxfs.h
index 24424d0..cf4baff 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -43,6 +43,12 @@ struct iomap;
 #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
 #define unlikely(x) (x)
 
+static inline __attribute__((const))
+int is_power_of_2(unsigned long n)
+{
+	return (n != 0 && ((n & (n - 1)) == 0));
+}
+
 /*
  * This mirrors the kernel include for xfs_buf.h - it's implicitly included in
  * every files via a similar include in the kernel xfs_linux.h.
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 15bae1f..0dc6627 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -369,12 +369,6 @@ find_next_zero_bit(const unsigned long *addr, unsigned long size,
 }
 #define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0)
 
-static inline __attribute__((const))
-int is_power_of_2(unsigned long n)
-{
-	return (n != 0 && ((n & (n - 1)) == 0));
-}
-
 /*
  * xfs_iroundup: round up argument to next power of two
  */
diff --git a/man/man8/mkfs.xfs.8 b/man/man8/mkfs.xfs.8
index 5cd69fa..8f159a4 100644
--- a/man/man8/mkfs.xfs.8
+++ b/man/man8/mkfs.xfs.8
@@ -654,6 +654,21 @@ space over time such that no free extents are large enough to
 accommodate a chunk of 64 inodes. Without this feature enabled, inode
 allocations can fail with out of space errors under severe fragmented
 free space conditions.
+.TP
+.BI forcealign[= value]
+If
+.B value
+is 1, mark the root directory so that all file data extent allocations will be
+aligned to the extent size hint.
+These allocations will be mapped into the file range at offsets that are
+aligned to the extent size hint.
+The
+.B extszinherit
+option must be specified.
+The
+.B cowextsize
+option must not be specified.
+This feature is only available for filesystems formatted with -m crc=1.
 .RE
 .PP
 .PD 0
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index fb0c53f..1253ece 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -81,6 +81,7 @@ enum {
 	I_ATTR,
 	I_PROJID32BIT,
 	I_SPINODES,
+	I_FORCEALIGN,
 	I_MAX_OPTS,
 };
 
@@ -451,6 +452,7 @@ static struct opt_params iopts = {
 		[I_ATTR] = "attr",
 		[I_PROJID32BIT] = "projid32bit",
 		[I_SPINODES] = "sparse",
+		[I_FORCEALIGN] = "forcealign",
 		[I_MAX_OPTS] = NULL,
 	},
 	.subopt_params = {
@@ -500,6 +502,12 @@ static struct opt_params iopts = {
 		  .maxval = 1,
 		  .defaultval = 1,
 		},
+		{ .index = I_FORCEALIGN,
+		  .conflicts = { { NULL, LAST_CONFLICT } },
+		  .minval = 0,
+		  .maxval = 1,
+		  .defaultval = 1,
+		},
 	},
 };
 
@@ -829,6 +837,7 @@ struct sb_feat_args {
 	bool	bigtime;		/* XFS_SB_FEAT_INCOMPAT_BIGTIME */
 	bool	nodalign;
 	bool	nortalign;
+	bool	forcealign;		/* XFS_SB_FEAT_RO_COMPAT_FORCEALIGN */
 };
 
 struct cli_params {
@@ -961,6 +970,7 @@ usage( void )
 /* force overwrite */	[-f]\n\
 /* inode size */	[-i perblock=n|size=num,maxpct=n,attr=0|1|2,\n\
 			    projid32bit=0|1,sparse=0|1]\n\
+				forcealign=0|1\n\
 /* no discard */	[-K]\n\
 /* log subvol */	[-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
 			    sunit=value|su=num,sectsize=num,lazy-count=0|1]\n\
@@ -1601,6 +1611,8 @@ inode_opts_parser(
 	const char		*value,
 	struct cli_params	*cli)
 {
+	long long val;
+
 	switch (subopt) {
 	case I_ALIGN:
 		cli->sb_feat.inode_align = getnum(value, opts, subopt);
@@ -1623,6 +1635,17 @@ inode_opts_parser(
 	case I_SPINODES:
 		cli->sb_feat.spinodes = getnum(value, opts, subopt);
 		break;
+	case I_FORCEALIGN:
+		val = getnum(value, opts, subopt);
+
+		if (val == 1) {
+			cli->sb_feat.forcealign = true;
+			cli->fsx.fsx_xflags |= FS_XFLAG_FORCEALIGN;
+		} else {
+			cli->sb_feat.forcealign = false;
+			cli->fsx.fsx_xflags &= ~FS_XFLAG_FORCEALIGN;
+		}
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -2228,6 +2251,13 @@ _("timestamps later than 2038 not supported without CRC support\n"));
 			usage();
 		}
 		cli->sb_feat.bigtime = false;
+		
+		if (cli->sb_feat.forcealign) {
+			fprintf(stderr,
+_("forced file data alignment not supported without CRC support\n"));
+			usage();
+		}
+		cli->sb_feat.forcealign = false;
 	}
 
 	if (!cli->sb_feat.finobt) {
@@ -2262,6 +2292,13 @@ _("cowextsize not supported without reflink support\n"));
 		usage();
 	}
 
+	if ((cli->fsx.fsx_xflags & FS_XFLAG_FORCEALIGN) &&
+	    (cli->fsx.fsx_cowextsize > 0 || cli->fsx.fsx_extsize == 0 || !is_power_of_2(cli->fsx.fsx_extsize))) {
+		fprintf(stderr,
+_("forcealign requires a non-zero power-of-2 extent size hint and no cow extent size hint\n"));
+		usage();
+	}
+
 	/*
 	 * Copy features across to config structure now.
 	 */
@@ -2511,6 +2548,34 @@ _("illegal CoW extent size hint %lld, must be less than %u.\n"),
 	}
 }
 
+/* Validate the incoming forcealign flag. */
+static void
+validate_forcealign(
+	struct xfs_mount	*mp,
+	struct cli_params	*cli)
+{
+	if (!(cli->fsx.fsx_xflags & FS_XFLAG_FORCEALIGN))
+		return;
+
+	if (cli->fsx.fsx_cowextsize != 0) {
+		fprintf(stderr,
+_("cannot set CoW extent size hint when forcealign is set.\n"));
+		usage();
+	}
+
+	if (cli->fsx.fsx_extsize == 0) {
+		fprintf(stderr,
+_("cannot set forcealign without an extent size hint.\n"));
+		usage();
+	}
+
+	if (cli->fsx.fsx_xflags & (FS_XFLAG_REALTIME | FS_XFLAG_RTINHERIT)) {
+		fprintf(stderr,
+_("cannot set forcealign and realtime flags.\n"));
+		usage();
+	}
+}
+
 /*
  * Validate the configured stripe geometry, or is none is specified, pull
  * the configuration from the underlying device.
@@ -2978,11 +3043,63 @@ _("agsize (%s) not a multiple of fs blk size (%d)\n"),
  */
 static void
 align_ag_geometry(
-	struct mkfs_params	*cfg)
+	struct mkfs_params	*cfg,
+	struct cli_params	*cli)
 {
 	uint64_t	tmp_agsize;
 	int		dsunit = cfg->dsunit;
 
+	/*
+	 * If the sysadmin wants to force all file data space mappings to be
+	 * aligned to the extszinherit value, then we need the AGs to be
+	 * aligned to the same value.  Skip these checks if the extent size
+	 * hint is zero; the extszinherit validation will fail the format
+	 * later.
+	 */
+	if (cli->sb_feat.forcealign && cli->fsx.fsx_extsize != 0) {
+		/* Perfect alignment; we're done. */
+		if (cfg->agsize % cli->fsx.fsx_extsize == 0)
+			goto validate;
+
+		/*
+		 * Round up to file extent size boundary.  Make sure that
+		 * agsize is still larger than XFS_AG_MIN_BLOCKS(blocklog).
+		 */
+		tmp_agsize = ((cfg->agsize + cli->fsx.fsx_extsize - 1) /
+				cli->fsx.fsx_extsize) * cli->fsx.fsx_extsize;
+
+		/*
+		 * Round down to file extent size boundary if rounding up
+		 * created an AG size that is larger than the AG max.
+		 */
+		if (tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog))
+			tmp_agsize = (cfg->agsize / cli->fsx.fsx_extsize) *
+							cli->fsx.fsx_extsize;
+
+		if (tmp_agsize < XFS_AG_MIN_BLOCKS(cfg->blocklog) &&
+		    tmp_agsize > XFS_AG_MAX_BLOCKS(cfg->blocklog)) {
+			/*
+			 * Set the agsize to the invalid value so the following
+			 * validation of the ag will fail and print a nice error
+			 * and exit.
+			 */
+			cfg->agsize = tmp_agsize;
+			goto validate;
+		}
+
+		/* Update geometry to be file extent size aligned */
+		cfg->agsize = tmp_agsize;
+		if (!cli_opt_set(&dopts, D_AGCOUNT))
+			cfg->agcount = cfg->dblocks / cfg->agsize +
+					(cfg->dblocks % cfg->agsize != 0);
+
+		if (cli_opt_set(&dopts, D_AGSIZE))
+			fprintf(stderr,
+_("agsize rounded to %lld, extszhint = %d\n"),
+				(long long)cfg->agsize, cli->fsx.fsx_extsize);
+		goto validate;
+	}
+
 	if (!dsunit)
 		goto validate;
 
@@ -3202,6 +3319,8 @@ sb_set_features(
 		sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_REFLINK;
 	if (fp->inobtcnt)
 		sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT;
+	if (fp->forcealign)
+		sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_FORCEALIGN;
 	if (fp->bigtime)
 		sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_BIGTIME;
 
@@ -4164,7 +4283,7 @@ main(
 	 * aligns to device geometry correctly.
 	 */
 	calculate_initial_ag_geometry(&cfg, &cli);
-	align_ag_geometry(&cfg);
+	align_ag_geometry(&cfg, &cli);
 
 	calculate_imaxpct(&cfg, &cli);
 
@@ -4187,6 +4306,7 @@ main(
 	/* Validate the extent size hints now that @mp is fully set up. */
 	validate_extsize_hint(mp, &cli);
 	validate_cowextsize_hint(mp, &cli);
+	validate_forcealign(mp, &cli);
 
 	/* Print the intended geometry of the fs. */
 	if (!quiet || dry_run) {
-- 
2.33.0

