From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> --- man/man8/mkfs.xfs.8.in | 19 +++++++++ mkfs/xfs_mkfs.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/man/man8/mkfs.xfs.8.in b/man/man8/mkfs.xfs.8.in index b18daa23395..8060d342c2a 100644 --- a/man/man8/mkfs.xfs.8.in +++ b/man/man8/mkfs.xfs.8.in @@ -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 diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c index fbe40b7edf1..cb09c6466a6 100644 --- a/mkfs/xfs_mkfs.c +++ b/mkfs/xfs_mkfs.c @@ -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; } @@ -3607,15 +3650,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_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, struct cli_params *cli, + struct libxfs_init *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; struct libxfs_init dummy_init = { }; @@ -3624,6 +3711,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); @@ -3681,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)); @@ -4202,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 = {}; @@ -4403,7 +4501,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);