]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
mkfs: allow sizing internal logs for concurrency
authorDarrick J. Wong <djwong@kernel.org>
Fri, 3 Jun 2022 21:28:19 +0000 (14:28 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 26 Jul 2022 19:54:55 +0000 (12:54 -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>
man/man8/mkfs.xfs.8.in
mkfs/xfs_mkfs.c

index 0a238ed580e916217b57bd213515d6970ccac1f3..1c90b5b55951833cef5998ca24a1040e7ac7f63b 100644 (file)
@@ -801,6 +801,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 90f5cc3591edc9a8db11118099195fd57a0fed1a..2e47bb4d2af5883270f0d95864b606aca921d829 100644 (file)
@@ -104,6 +104,7 @@ enum {
        L_FILE,
        L_NAME,
        L_LAZYSBCNTR,
+       L_CONCURRENCY,
        L_MAX_OPTS,
 };
 
@@ -534,6 +535,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 = {
@@ -554,7 +556,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,
@@ -585,6 +588,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,
                },
@@ -599,6 +603,7 @@ static struct opt_params lopts = {
                },
                { .index = L_FILE,
                  .conflicts = { { &lopts, L_INTERNAL },
+                                { &lopts, L_CONCURRENCY },
                                 { NULL, LAST_CONFLICT } },
                  .minval = 0,
                  .maxval = 1,
@@ -617,6 +622,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,
+               },
        },
 };
 
@@ -873,6 +887,7 @@ struct cli_params {
        int     lsunit;
        int     is_supported;
        int     data_concurrency;
+       int     log_concurrency;
 
        /* parameters where 0 is not a valid value */
        int64_t agcount;
@@ -981,7 +996,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\
@@ -1687,6 +1703,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,
@@ -1727,6 +1767,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;
        }
@@ -3577,15 +3620,59 @@ _("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_xinit     *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,
        struct cli_params       *cli,
+       struct libxfs_xinit     *xi,
        struct xfs_mount        *mp)
 {
        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;
 
        /* we need a temporary mount to calculate the minimum log size. */
@@ -3593,6 +3680,12 @@ calculate_log_size(
        mount.m_sb = *sbp;
        libxfs_mount(&mount, &mp->m_sb, 0, 0, 0, 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);
@@ -3650,6 +3743,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));
@@ -4177,6 +4274,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 = {};
 
@@ -4384,7 +4482,7 @@ main(
         * With the mount set up, we can finally calculate the log size
         * constraints and do default size calculations and final validation
         */
-       calculate_log_size(&cfg, &cli, mp);
+       calculate_log_size(&cfg, &cli, &xi, mp);
 
        finish_superblock_setup(&cfg, mp, sbp);