From c85d383b0bb1d86c676ccf817dd22ad43b589b61 Mon Sep 17 00:00:00 2001
From: John Garry <john.g.garry@oracle.com>
Date: Fri, 1 Mar 2024 17:40:12 +0000
Subject: [PATCH 08/11] mkfs: Add atomic writes suppport

Use a command like the following to enable:
/mkfs.xfs -f -i forcealign=1 -d extsize=4096  -d atomic-writes=1  /dev/sda

Forcealign enablement is required, and with that a specific extent size
needs to be set. And extent size of 4096B (for 4K FS block size) is
acceptable.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 include/linux.h     |  4 ++++
 libxfs/xfs_format.h |  8 ++++++--
 mkfs/xfs_mkfs.c     | 49 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/include/linux.h b/include/linux.h
index d95365b..7f295f0 100644
--- a/include/linux.h
+++ b/include/linux.h
@@ -251,6 +251,10 @@ struct fsxattr {
 #define FS_XFLAG_FORCEALIGN	0x00020000
 #endif
 
+#ifndef FS_XFLAG_ATOMICWRITES
+#define FS_XFLAG_ATOMICWRITES	0x00040000
+#endif
+
 #ifdef HAVE_GETFSMAP
 # include <linux/fsmap.h>
 #else
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index c2db380..1dfb11c 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -451,12 +451,14 @@ xfs_sb_has_compat_feature(
 #define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)		/* reflinked files */
 #define XFS_SB_FEAT_RO_COMPAT_INOBTCNT (1 << 3)		/* inobt block counts */
 #define XFS_SB_FEAT_RO_COMPAT_FORCEALIGN (1 << 30)	/* aligned file data extents */
+#define XFS_SB_FEAT_RO_COMPAT_ATOMICWRITES (1 << 31)	/* aligned file data extents */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
 		(XFS_SB_FEAT_RO_COMPAT_FINOBT | \
 		 XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
 		 XFS_SB_FEAT_RO_COMPAT_REFLINK| \
 		 XFS_SB_FEAT_RO_COMPAT_FORCEALIGN| \
-		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT)
+		 XFS_SB_FEAT_RO_COMPAT_INOBTCNT| \
+		 XFS_SB_FEAT_RO_COMPAT_ATOMICWRITES)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN	~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
@@ -1180,16 +1182,18 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
 #define XFS_DIFLAG2_BIGTIME_BIT	3	/* big timestamps */
 /* data extent mappings for regular files must be aligned to extent size hint */
 #define XFS_DIFLAG2_FORCEALIGN_BIT 5
+#define XFS_DIFLAG2_ATOMICWRITES_BIT 6
 
 #define XFS_DIFLAG2_DAX		(1 << XFS_DIFLAG2_DAX_BIT)
 #define XFS_DIFLAG2_REFLINK     (1 << XFS_DIFLAG2_REFLINK_BIT)
 #define XFS_DIFLAG2_COWEXTSIZE  (1 << XFS_DIFLAG2_COWEXTSIZE_BIT)
 #define XFS_DIFLAG2_BIGTIME	(1 << XFS_DIFLAG2_BIGTIME_BIT)
 #define XFS_DIFLAG2_FORCEALIGN	(1 << XFS_DIFLAG2_FORCEALIGN_BIT)
+#define XFS_DIFLAG2_ATOMICWRITES	(1 << XFS_DIFLAG2_ATOMICWRITES_BIT)
 
 #define XFS_DIFLAG2_ANY \
 	(XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \
-	 XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_FORCEALIGN)
+	 XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_FORCEALIGN | XFS_DIFLAG2_ATOMICWRITES)
 
 static inline bool xfs_dinode_has_bigtime(const struct xfs_dinode *dip)
 {
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 1253ece..5169255 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -70,6 +70,7 @@ enum {
 	D_EXTSZINHERIT,
 	D_COWEXTSIZE,
 	D_DAXINHERIT,
+	D_ATOMICWRITES,
 	D_MAX_OPTS,
 };
 
@@ -306,6 +307,7 @@ static struct opt_params dopts = {
 		[D_EXTSZINHERIT] = "extszinherit",
 		[D_COWEXTSIZE] = "cowextsize",
 		[D_DAXINHERIT] = "daxinherit",
+		[D_ATOMICWRITES] = "atomic-writes",
 		[D_MAX_OPTS] = NULL,
 	},
 	.subopt_params = {
@@ -437,6 +439,12 @@ static struct opt_params dopts = {
 		  .maxval = 1,
 		  .defaultval = 1,
 		},
+		{ .index = D_ATOMICWRITES,
+		  .conflicts = { { NULL, LAST_CONFLICT } },
+		  .minval = 0,
+		  .maxval = 1,
+		  .defaultval = 1,
+		},
 	},
 };
 
@@ -838,6 +846,7 @@ struct sb_feat_args {
 	bool	nodalign;
 	bool	nortalign;
 	bool	forcealign;		/* XFS_SB_FEAT_RO_COMPAT_FORCEALIGN */
+	bool	atomicwrites;		/* XFS_SB_FEAT_RO_COMPAT_ATOMICWRITES */
 };
 
 struct cli_params {
@@ -1598,6 +1607,13 @@ data_opts_parser(
 		else
 			cli->fsx.fsx_xflags &= ~FS_XFLAG_DAX;
 		break;
+	case D_ATOMICWRITES:
+		if (getnum(value, opts, subopt) == 1) {
+			cli->sb_feat.atomicwrites = true;
+		} else {
+			cli->sb_feat.atomicwrites = false;
+		}
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -2557,6 +2573,12 @@ validate_forcealign(
 	if (!(cli->fsx.fsx_xflags & FS_XFLAG_FORCEALIGN))
 		return;
 
+	if (cli->fsx.fsx_xflags & (FS_XFLAG_REALTIME | FS_XFLAG_RTINHERIT)) {
+		fprintf(stderr,
+_("cannot set forcealign and realtime flags.\n"));
+		usage();
+	}
+
 	if (cli->fsx.fsx_cowextsize != 0) {
 		fprintf(stderr,
 _("cannot set CoW extent size hint when forcealign is set.\n"));
@@ -2576,6 +2598,30 @@ _("cannot set forcealign and realtime flags.\n"));
 	}
 }
 
+/* Validate the incoming forcealign flag. */
+static void
+validate_atomicwrites(
+	struct mkfs_params	*cfg,
+	struct xfs_mount	*mp,
+	struct cli_params	*cli,
+	char			*dfile
+	)
+{
+	if (!cli->sb_feat.atomicwrites)
+		return;
+
+	if (!(cli->fsx.fsx_xflags & FS_XFLAG_FORCEALIGN)) {
+		fprintf(stderr,
+_("cannot set atomicwrites without forcealign.\n"));
+		usage();
+	}
+
+	/*
+	 * TODO: Add a check to see if the dfile can support atomic writes of
+	 * extsize.
+	 */
+}
+
 /*
  * Validate the configured stripe geometry, or is none is specified, pull
  * the configuration from the underlying device.
@@ -3321,6 +3367,8 @@ sb_set_features(
 		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->atomicwrites)
+		sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_ATOMICWRITES;
 	if (fp->bigtime)
 		sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_BIGTIME;
 
@@ -4307,6 +4355,7 @@ main(
 	validate_extsize_hint(mp, &cli);
 	validate_cowextsize_hint(mp, &cli);
 	validate_forcealign(mp, &cli);
+	validate_atomicwrites(&cfg, mp, &cli, dfile);
 
 	/* Print the intended geometry of the fs. */
 	if (!quiet || dry_run) {
-- 
2.33.0

