]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
mkfs: try to align AG size based on atomic write capabilities
authorDarrick J. Wong <djwong@kernel.org>
Tue, 1 Jul 2025 17:45:14 +0000 (10:45 -0700)
committerAndrey Albershteyn <aalbersh@kernel.org>
Fri, 18 Jul 2025 14:05:10 +0000 (16:05 +0200)
Try to align the AG size to the maximum hardware atomic write unit so
that we can give users maximum flexibility in choosing an RWF_ATOMIC
write size.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: John Garry <john.g.garry@oracle.com>
libxfs/topology.c
libxfs/topology.h
mkfs/xfs_mkfs.c

index 96ee74b61b30f5fc2ef4a9841cd338a1e95630d1..7764687beac000379d0183e075514584a3981f6c 100644 (file)
@@ -4,11 +4,18 @@
  * All Rights Reserved.
  */
 
+#ifdef OVERRIDE_SYSTEM_STATX
+#define statx sys_statx
+#endif
+#include <fcntl.h>
+#include <sys/stat.h>
+
 #include "libxfs_priv.h"
 #include "libxcmd.h"
 #include <blkid/blkid.h>
 #include "xfs_multidisk.h"
 #include "libfrog/platform.h"
+#include "libfrog/statx.h"
 
 #define TERABYTES(count, blog) ((uint64_t)(count) << (40 - (blog)))
 #define GIGABYTES(count, blog) ((uint64_t)(count) << (30 - (blog)))
@@ -278,6 +285,34 @@ out_free_probe:
                device);
 }
 
+static void
+get_hw_atomic_writes_topology(
+       struct libxfs_dev       *dev,
+       struct device_topology  *dt)
+{
+       struct statx            sx;
+       int                     fd;
+       int                     ret;
+
+       fd = open(dev->name, O_RDONLY);
+       if (fd < 0)
+               return;
+
+       ret = statx(fd, "", AT_EMPTY_PATH, STATX_WRITE_ATOMIC, &sx);
+       if (ret)
+               goto out_close;
+
+       if (!(sx.stx_mask & STATX_WRITE_ATOMIC))
+               goto out_close;
+
+       dt->awu_min = sx.stx_atomic_write_unit_min >> 9;
+       dt->awu_max = max(sx.stx_atomic_write_unit_max_opt,
+                         sx.stx_atomic_write_unit_max) >> 9;
+
+out_close:
+       close(fd);
+}
+
 static void
 get_device_topology(
        struct libxfs_dev       *dev,
@@ -316,6 +351,7 @@ get_device_topology(
                }
        } else {
                blkid_get_topology(dev->name, dt, force_overwrite);
+               get_hw_atomic_writes_topology(dev, dt);
        }
 
        ASSERT(dt->logical_sector_size);
index 207a8a7f150556174779c8d37aa2aec49a9e6ee0..f0ca65f3576e927f621ff3264c58ae376e19bdc7 100644 (file)
 struct device_topology {
        int     logical_sector_size;    /* logical sector size */
        int     physical_sector_size;   /* physical sector size */
-       int     sunit;          /* stripe unit */
-       int     swidth;         /* stripe width  */
+       int     sunit;                  /* stripe unit */
+       int     swidth;                 /* stripe width  */
+       int     awu_min;                /* min atomic write unit in bbcounts */
+       int     awu_max;                /* max atomic write unit in bbcounts */
 };
 
 struct fs_topology {
index b6de13cebc93ed90f85ee0d9f268d93332c8bef2..d2080804a21470548c601ad56040a615af2ed3b2 100644 (file)
@@ -3379,6 +3379,32 @@ _("illegal CoW extent size hint %lld, must be less than %u and a multiple of %u.
        }
 }
 
+static void
+validate_device_awu(
+       struct mkfs_params      *cfg,
+       struct device_topology  *dt)
+{
+       /* Ignore hw atomic write capability if it can't do even 1 fsblock */
+       if (BBTOB(dt->awu_min) > cfg->blocksize ||
+           BBTOB(dt->awu_max) < cfg->blocksize) {
+               dt->awu_min = 0;
+               dt->awu_max = 0;
+       }
+}
+
+static void
+validate_hw_atomic_writes(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct fs_topology      *ft)
+{
+       validate_device_awu(cfg, &ft->data);
+       if (cli->xi->log.name)
+               validate_device_awu(cfg, &ft->log);
+       if (cli->xi->rt.name)
+               validate_device_awu(cfg, &ft->rt);
+}
+
 /* Complain if this filesystem is not a supported configuration. */
 static void
 validate_supported(
@@ -4052,10 +4078,20 @@ _("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 fs_topology      *ft)
 {
-       uint64_t        tmp_agsize;
-       int             dsunit = cfg->dsunit;
+       uint64_t                tmp_agsize;
+       int                     dsunit = cfg->dsunit;
+
+       /*
+        * We've already validated (or discarded) the hardware atomic write
+        * geometry.  Try to align the agsize to the maximum atomic write unit
+        * to give users maximum flexibility in choosing atomic write sizes.
+        */
+       if (ft->data.awu_max > 0)
+               dsunit = max(DTOBT(ft->data.awu_max, cfg->blocklog),
+                               dsunit);
 
        if (!dsunit)
                goto validate;
@@ -4111,7 +4147,8 @@ _("agsize rounded to %lld, sunit = %d\n"),
                                (long long)cfg->agsize, dsunit);
        }
 
-       if ((cfg->agsize % cfg->dswidth) == 0 &&
+       if (cfg->dswidth > 0 &&
+           (cfg->agsize % cfg->dswidth) == 0 &&
            cfg->dswidth != cfg->dsunit &&
            cfg->agcount > 1) {
 
@@ -5875,6 +5912,7 @@ main(
        cfg.rtblocks = calc_dev_size(cli.rtsize, &cfg, &ropts, R_SIZE, "rt");
 
        validate_rtextsize(&cfg, &cli, &ft);
+       validate_hw_atomic_writes(&cfg, &cli, &ft);
 
        /*
         * Open and validate the device configurations
@@ -5893,7 +5931,7 @@ main(
         * aligns to device geometry correctly.
         */
        calculate_initial_ag_geometry(&cfg, &cli, &xi);
-       align_ag_geometry(&cfg);
+       align_ag_geometry(&cfg, &ft);
        if (cfg.sb_feat.zoned)
                calculate_zone_geometry(&cfg, &cli, &xi, &zt);
        else