diff --git a/CHANGELOG.md b/CHANGELOG.md index 581ac0379..4928597cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,10 @@ is associated with a fabrics controller. Added `min_cntlid` and `max_cntlid` to `nvmf_create_subsystem` to limit the controller ID range. Added `spdk_nvmf_subsystem_get_min_cntlid` and `spdk_nvmf_subsystem_get_max_cntlid` to request those values. +A new parameter, `poll_groups_mask` was added to the nvmf_set_config RPC that allows specifying +a subset of cores for the nvmf poll groups. This helps to avoid imbalances when some cores are +busy with periodic timer tasks that run very frequently. + `spdk_nvmf_request_get_buffers_multi` API is removed. Added the `nvmf_set_crdt` RPC for setting command retry delay times. diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 86c8a6369..4839b5753 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -6783,6 +6783,7 @@ Name | Optional | Type | Description ----------------------- | -------- | ----------- | ----------- acceptor_poll_rate | Optional | number | Polling interval of the acceptor for incoming connections (microseconds) admin_cmd_passthru | Optional | object | Admin command passthru configuration +poll_groups_mask | Optional | string | Set cpumask for NVMf poll groups ### admin_cmd_passthru {#spdk_nvmf_admin_passthru_conf} diff --git a/module/event/subsystems/nvmf/nvmf_rpc.c b/module/event/subsystems/nvmf/nvmf_rpc.c index 84705a63c..c18efeb0f 100644 --- a/module/event/subsystems/nvmf/nvmf_rpc.c +++ b/module/event/subsystems/nvmf/nvmf_rpc.c @@ -35,6 +35,7 @@ #include "spdk/rpc.h" #include "spdk/util.h" +#include "spdk/cpuset.h" static const struct spdk_json_object_decoder nvmf_rpc_subsystem_tgt_opts_decoder[] = { {"max_subsystems", 0, spdk_json_decode_uint32, true} @@ -98,10 +99,57 @@ static int decode_admin_passthru(const struct spdk_json_val *val, void *out) return 0; } +static int +nvmf_is_subset_of_env_core_mask(const struct spdk_cpuset *set) +{ + uint32_t i, tmp_counter = 0; + + SPDK_ENV_FOREACH_CORE(i) { + if (spdk_cpuset_get_cpu(set, i)) { + ++tmp_counter; + } + } + return spdk_cpuset_count(set) - tmp_counter; +} + +static int +nvmf_decode_poll_groups_mask(const struct spdk_json_val *val, void *out) +{ + char *mask = spdk_json_strdup(val); + int ret = -1; + + if (mask == NULL) { + return -1; + } + + if (!(g_poll_groups_mask = spdk_cpuset_alloc())) { + SPDK_ERRLOG("Unable to allocate a poll groups mask object in nvmf_decode_poll_groups_mask.\n"); + free(mask); + return -1; + } + + ret = spdk_cpuset_parse(g_poll_groups_mask, mask); + free(mask); + if (ret == 0) { + if (nvmf_is_subset_of_env_core_mask(g_poll_groups_mask) == 0) { + return 0; + } else { + SPDK_ERRLOG("Poll groups cpumask 0x%s is out of range\n", spdk_cpuset_fmt(g_poll_groups_mask)); + } + } else { + SPDK_ERRLOG("Invalid cpumask\n"); + } + + spdk_cpuset_free(g_poll_groups_mask); + g_poll_groups_mask = NULL; + return -1; +} + static const struct spdk_json_object_decoder nvmf_rpc_subsystem_tgt_conf_decoder[] = { {"acceptor_poll_rate", offsetof(struct spdk_nvmf_tgt_conf, acceptor_poll_rate), spdk_json_decode_uint32, true}, {"conn_sched", offsetof(struct spdk_nvmf_tgt_conf, conn_sched), decode_conn_sched, true}, - {"admin_cmd_passthru", offsetof(struct spdk_nvmf_tgt_conf, admin_passthru), decode_admin_passthru, true} + {"admin_cmd_passthru", offsetof(struct spdk_nvmf_tgt_conf, admin_passthru), decode_admin_passthru, true}, + {"poll_groups_mask", 0, nvmf_decode_poll_groups_mask, true} }; static void diff --git a/module/event/subsystems/nvmf/nvmf_tgt.c b/module/event/subsystems/nvmf/nvmf_tgt.c index 65b40b8d6..3478a564f 100644 --- a/module/event/subsystems/nvmf/nvmf_tgt.c +++ b/module/event/subsystems/nvmf/nvmf_tgt.c @@ -488,6 +488,9 @@ nvmf_subsystem_write_config_json(struct spdk_json_write_ctx *w) spdk_json_write_named_bool(w, "identify_ctrlr", g_spdk_nvmf_tgt_conf.admin_passthru.identify_ctrlr); spdk_json_write_object_end(w); + if (g_poll_groups_mask) { + spdk_json_write_named_string(w, "poll_groups_mask", spdk_cpuset_fmt(g_poll_groups_mask)); + } spdk_json_write_object_end(w); spdk_json_write_object_end(w); diff --git a/scripts/rpc.py b/scripts/rpc.py index bec5c13e3..b940c37e7 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -1895,7 +1895,8 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse rpc.nvmf.nvmf_set_config(args.client, acceptor_poll_rate=args.acceptor_poll_rate, conn_sched=args.conn_sched, - passthru_identify_ctrlr=args.passthru_identify_ctrlr) + passthru_identify_ctrlr=args.passthru_identify_ctrlr, + poll_groups_mask=args.poll_groups_mask) p = subparsers.add_parser('nvmf_set_config', aliases=['set_nvmf_target_config'], help='Set NVMf target config') @@ -1903,6 +1904,7 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('-s', '--conn-sched', help='(Deprecated). Ignored.') p.add_argument('-i', '--passthru-identify-ctrlr', help="""Passthrough fields like serial number and model number when the controller has a single namespace that is an NVMe bdev""", action='store_true') + p.add_argument('-m', '--poll-groups-mask', help='Set cpumask for NVMf poll groups (optional)', type=str) p.set_defaults(func=nvmf_set_config) def nvmf_create_transport(args): diff --git a/scripts/rpc/nvmf.py b/scripts/rpc/nvmf.py index 96164c0f2..65a610105 100644 --- a/scripts/rpc/nvmf.py +++ b/scripts/rpc/nvmf.py @@ -23,7 +23,8 @@ def nvmf_set_max_subsystems(client, def nvmf_set_config(client, acceptor_poll_rate=None, conn_sched=None, - passthru_identify_ctrlr=None): + passthru_identify_ctrlr=None, + poll_groups_mask=None): """Set NVMe-oF target subsystem configuration. Args: @@ -43,6 +44,8 @@ def nvmf_set_config(client, admin_cmd_passthru = {} admin_cmd_passthru['identify_ctrlr'] = passthru_identify_ctrlr params['admin_cmd_passthru'] = admin_cmd_passthru + if poll_groups_mask: + params['poll_groups_mask'] = poll_groups_mask return client.call('nvmf_set_config', params)