]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
mkfs: allow sizing internal logs for concurrency
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 23:07:47 +0000 (16:07 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 17 Apr 2024 21:06:27 +0000 (14:06 -0700)
Add a -l option to mkfs so that sysadmins can configure the filesystem
so that the log can handle a certain number of transactions (front and
backend) without any threads contending for log grant space.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bill O'Donnell <bodonnel@redhat.com>
man/man8/mkfs.xfs.8.in
mkfs/xfs_mkfs.c

index b18daa233959dfa290ce4a4d4a4b8809567f8478..8060d342c2a447ab1e9c1900c5b88f4e11d97243 100644 (file)
@@ -795,6 +795,25 @@ if you want to disable this feature for older kernels which don't support
 it.
 .IP
 This option is only tunable on the deprecated V4 format.
+.TP
+.BI concurrency= value
+Allocate a log that is estimated to be large enough to handle the desired level
+of concurrency without userspace program threads contending for log space.
+This scheme will neither create a log smaller than the minimum required,
+nor create a log larger than the maximum possible.
+This option is only valid for internal logs and is not compatible with the
+size option.
+This option is not compatible with the
+.B logdev
+or
+.B size
+options.
+The magic value
+.I nr_cpus
+or
+.I 1
+or no value at all will set this parameter to the number of active processors
+in the system.
 .RE
 .PP
 .PD 0
index f69a4a1dac9bf32718595ef96a94d693c9b0118d..f4a9bf20f391ff0883abdaf61d72227379ef5574 100644 (file)
@@ -105,6 +105,7 @@ enum {
        L_FILE,
        L_NAME,
        L_LAZYSBCNTR,
+       L_CONCURRENCY,
        L_MAX_OPTS,
 };
 
@@ -541,6 +542,7 @@ static struct opt_params lopts = {
                [L_FILE] = "file",
                [L_NAME] = "name",
                [L_LAZYSBCNTR] = "lazy-count",
+               [L_CONCURRENCY] = "concurrency",
                [L_MAX_OPTS] = NULL,
        },
        .subopt_params = {
@@ -561,7 +563,8 @@ static struct opt_params lopts = {
                  .defaultval = 1,
                },
                { .index = L_SIZE,
-                 .conflicts = { { NULL, LAST_CONFLICT } },
+                 .conflicts = { { &lopts, L_CONCURRENCY },
+                                { NULL, LAST_CONFLICT } },
                  .convert = true,
                  .minval = 2 * 1024 * 1024LL,  /* XXX: XFS_MIN_LOG_BYTES */
                  .maxval = XFS_MAX_LOG_BYTES,
@@ -592,6 +595,7 @@ static struct opt_params lopts = {
                  .conflicts = { { &lopts, L_AGNUM },
                                 { &lopts, L_NAME },
                                 { &lopts, L_INTERNAL },
+                                { &lopts, L_CONCURRENCY },
                                 { NULL, LAST_CONFLICT } },
                  .defaultval = SUBOPT_NEEDS_VAL,
                },
@@ -606,6 +610,7 @@ static struct opt_params lopts = {
                },
                { .index = L_FILE,
                  .conflicts = { { &lopts, L_INTERNAL },
+                                { &lopts, L_CONCURRENCY },
                                 { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
@@ -624,6 +629,15 @@ static struct opt_params lopts = {
                  .maxval = 1,
                  .defaultval = 1,
                },
+               { .index = L_CONCURRENCY,
+                 .conflicts = { { &lopts, L_SIZE },
+                                { &lopts, L_FILE },
+                                { &lopts, L_DEV },
+                                { NULL, LAST_CONFLICT } },
+                 .minval = 0,
+                 .maxval = INT_MAX,
+                 .defaultval = 1,
+               },
        },
 };
 
@@ -904,6 +918,7 @@ struct cli_params {
        int     is_supported;
        int     proto_slashes_are_spaces;
        int     data_concurrency;
+       int     log_concurrency;
 
        /* parameters where 0 is not a valid value */
        int64_t agcount;
@@ -1012,7 +1027,8 @@ usage( void )
                            projid32bit=0|1,sparse=0|1,nrext64=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\
+                           sunit=value|su=num,sectsize=num,lazy-count=0|1,\n\
+                           concurrency=num]\n\
 /* label */            [-L label (maximum 12 characters)]\n\
 /* naming */           [-n size=num,version=2|ci,ftype=0|1]\n\
 /* no-op info only */  [-N]\n\
@@ -1712,6 +1728,30 @@ inode_opts_parser(
        return 0;
 }
 
+static void
+set_log_concurrency(
+       struct opt_params       *opts,
+       int                     subopt,
+       const char              *value,
+       struct cli_params       *cli)
+{
+       long long               optnum;
+
+       /*
+        * "nr_cpus" or 1 means set the concurrency level to the CPU count.  If
+        * this cannot be determined, fall back to the default computation.
+        */
+       if (!strcmp(value, "nr_cpus"))
+               optnum = 1;
+       else
+               optnum = getnum(value, opts, subopt);
+
+       if (optnum == 1)
+               cli->log_concurrency = nr_cpus();
+       else
+               cli->log_concurrency = optnum;
+}
+
 static int
 log_opts_parser(
        struct opt_params       *opts,
@@ -1752,6 +1792,9 @@ log_opts_parser(
        case L_LAZYSBCNTR:
                cli->sb_feat.lazy_sb_counters = getnum(value, opts, subopt);
                break;
+       case L_CONCURRENCY:
+               set_log_concurrency(opts, subopt, value, cli);
+               break;
        default:
                return -EINVAL;
        }
@@ -3602,6 +3645,48 @@ _("internal log size %lld too large, must be less than %d\n"),
        cfg->logblocks = min(cfg->logblocks, *max_logblocks);
 }
 
+static uint64_t
+calc_concurrency_logblocks(
+       struct mkfs_params      *cfg,
+       struct cli_params       *cli,
+       struct libxfs_init      *xi,
+       unsigned int            max_tx_bytes)
+{
+       uint64_t                log_bytes;
+       uint64_t                logblocks = cfg->logblocks;
+       unsigned int            new_logblocks;
+
+       if (cli->log_concurrency < 0) {
+               if (!ddev_is_solidstate(xi))
+                       goto out;
+
+               cli->log_concurrency = nr_cpus();
+       }
+       if (cli->log_concurrency == 0)
+               goto out;
+
+       /*
+        * If this filesystem is smaller than a gigabyte, there's little to be
+        * gained from making the log larger.
+        */
+       if (cfg->dblocks < GIGABYTES(1, cfg->blocklog))
+               goto out;
+
+       /*
+        * Create a log that is large enough to handle simultaneous maximally
+        * sized transactions at the concurrency level specified by the user
+        * without blocking for space.  Increase the figure by 50% so that
+        * background threads can also run.
+        */
+       log_bytes = max_tx_bytes * 3 * cli->log_concurrency / 2;
+       new_logblocks = min(XFS_MAX_LOG_BYTES >> cfg->blocklog,
+                               log_bytes >> cfg->blocklog);
+
+       logblocks = max(logblocks, new_logblocks);
+out:
+       return logblocks;
+}
+
 static void
 calculate_log_size(
        struct mkfs_params      *cfg,
@@ -3612,6 +3697,7 @@ calculate_log_size(
        struct xfs_sb           *sbp = &mp->m_sb;
        int                     min_logblocks;  /* absolute minimum */
        int                     max_logblocks;  /* absolute max for this AG */
+       unsigned int            max_tx_bytes = 0;
        struct xfs_mount        mount;
        struct libxfs_init      dummy_init = { };
 
@@ -3620,6 +3706,12 @@ calculate_log_size(
        mount.m_sb = *sbp;
        libxfs_mount(&mount, &mp->m_sb, &dummy_init, 0);
        min_logblocks = libxfs_log_calc_minimum_size(&mount);
+       if (cli->log_concurrency != 0) {
+               struct xfs_trans_res    res;
+
+               libxfs_log_get_max_trans_res(&mount, &res);
+               max_tx_bytes = res.tr_logres * res.tr_logcount;
+       }
        libxfs_umount(&mount);
 
        ASSERT(min_logblocks);
@@ -3682,6 +3774,10 @@ _("max log size %d smaller than min log size %d, filesystem is too small\n"),
                cfg->logblocks = (cfg->dblocks << cfg->blocklog) / 2048;
                cfg->logblocks = cfg->logblocks >> cfg->blocklog;
 
+               if (cli->log_concurrency != 0)
+                       cfg->logblocks = calc_concurrency_logblocks(cfg, cli,
+                                                       xi, max_tx_bytes);
+
                /* But don't go below a reasonable size */
                cfg->logblocks = max(cfg->logblocks,
                                XFS_MIN_REALISTIC_LOG_BLOCKS(cfg->blocklog));
@@ -4203,6 +4299,7 @@ main(
                .loginternal = 1,
                .is_supported   = 1,
                .data_concurrency = -1, /* auto detect non-mechanical storage */
+               .log_concurrency = -1, /* auto detect non-mechanical ddev */
        };
        struct mkfs_params      cfg = {};