diff --git a/CHANGELOG.md b/CHANGELOG.md index a7f7d358a..13aa1d56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ Remove the probe_cb parameter in spdk_idxd_probe function. And remove the definition of spdk_idxd_probe_cb function pointer. It should be implemented in idxd_user.c. +### nvmf + +Added `min_cntlid` and `max_cntlid` to `nvmf_create_subsystem` to limit the controller ID range. + ## v21.04: ### accel diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 572802b86..370d0efe4 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -5911,6 +5911,8 @@ model_number | Optional | string | Model number of virtual contr max_namespaces | Optional | number | Maximum number of namespaces that can be attached to the subsystem. Default: 0 (Unlimited) allow_any_host | Optional | boolean | Allow any host (`true`) or enforce allowed host list (`false`). Default: `false`. ana_reporting | Optional | boolean | Enable ANA reporting feature (default: `false`). +min_cntlid | Optional | number | Minimum controller ID. Default: 1 +max_cntlid | Optional | number | Maximum controller ID. Default: 0xffef ### Example diff --git a/include/spdk/nvmf.h b/include/spdk/nvmf.h index 89435622b..cf8b52ad4 100644 --- a/include/spdk/nvmf.h +++ b/include/spdk/nvmf.h @@ -844,6 +844,24 @@ struct spdk_nvmf_ns *spdk_nvmf_subsystem_get_ns(struct spdk_nvmf_subsystem *subs */ uint32_t spdk_nvmf_subsystem_get_max_namespaces(const struct spdk_nvmf_subsystem *subsystem); +/** + * Get the minimum controller ID allowed in a subsystem. + * + * \param subsystem Subsystem to query. + * + * \return Minimum controller ID allowed in the subsystem. + */ +uint16_t spdk_nvmf_subsystem_get_min_cntlid(const struct spdk_nvmf_subsystem *subsystem); + +/** + * Get the maximum controller ID allowed in a subsystem. + * + * \param subsystem Subsystem to query. + * + * \return Maximum controller ID allowed in the subsystem. + */ +uint16_t spdk_nvmf_subsystem_get_max_cntlid(const struct spdk_nvmf_subsystem *subsystem); + /** * Get a namespace's NSID. * diff --git a/lib/nvmf/Makefile b/lib/nvmf/Makefile index e7eab45b9..3058f41f9 100644 --- a/lib/nvmf/Makefile +++ b/lib/nvmf/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk SO_VER := 8 -SO_MINOR := 0 +SO_MINOR := 1 C_SRCS = ctrlr.c ctrlr_discovery.c ctrlr_bdev.c \ subsystem.c nvmf.c nvmf_rpc.c transport.c tcp.c diff --git a/lib/nvmf/nvmf.c b/lib/nvmf/nvmf.c index 05aaff048..acaf9ef74 100644 --- a/lib/nvmf/nvmf.c +++ b/lib/nvmf/nvmf.c @@ -450,6 +450,9 @@ nvmf_write_subsystem_config_json(struct spdk_json_write_ctx *w, spdk_json_write_named_uint32(w, "max_namespaces", max_namespaces); } + spdk_json_write_named_uint32(w, "min_cntlid", spdk_nvmf_subsystem_get_min_cntlid(subsystem)); + spdk_json_write_named_uint32(w, "max_cntlid", spdk_nvmf_subsystem_get_max_cntlid(subsystem)); + /* } "params" */ spdk_json_write_object_end(w); diff --git a/lib/nvmf/nvmf_internal.h b/lib/nvmf/nvmf_internal.h index d76b742a3..9c6510305 100644 --- a/lib/nvmf/nvmf_internal.h +++ b/lib/nvmf/nvmf_internal.h @@ -49,6 +49,10 @@ #define NVMF_MAX_ASYNC_EVENTS (4) +/* The spec reserves cntlid values in the range FFF0h to FFFFh. */ +#define NVMF_MIN_CNTLID 1 +#define NVMF_MAX_CNTLID 0xFFEF + enum spdk_nvmf_subsystem_state { SPDK_NVMF_SUBSYSTEM_INACTIVE = 0, SPDK_NVMF_SUBSYSTEM_ACTIVATING, @@ -286,6 +290,9 @@ struct spdk_nvmf_subsystem { struct spdk_nvmf_ns **ns; uint32_t max_nsid; + uint16_t min_cntlid; + uint16_t max_cntlid; + TAILQ_HEAD(, spdk_nvmf_ctrlr) ctrlrs; /* A mutex used to protect the hosts list and allow_any_host flag. Unlike the namespace @@ -372,6 +379,21 @@ void nvmf_subsystem_set_ana_state(struct spdk_nvmf_subsystem *subsystem, enum spdk_nvme_ana_state ana_state, spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn, void *cb_arg); +/** + * Sets the controller ID range for a subsystem. + * Valid range is [1, 0xFFEF]. + * + * May only be performed on subsystems in the INACTIVE state. + * + * \param subsystem Subsystem to modify. + * \param min_cntlid Minimum controller ID. + * \param max_cntlid Maximum controller ID. + * + * \return 0 on success, or negated errno value on failure. + */ +int nvmf_subsystem_set_cntlid_range(struct spdk_nvmf_subsystem *subsystem, + uint16_t min_cntlid, uint16_t max_cntlid); + int nvmf_ctrlr_async_event_ns_notice(struct spdk_nvmf_ctrlr *ctrlr); int nvmf_ctrlr_async_event_ana_change_notice(struct spdk_nvmf_ctrlr *ctrlr); int nvmf_ctrlr_async_event_discovery_log_change_notice(struct spdk_nvmf_ctrlr *ctrlr); diff --git a/lib/nvmf/nvmf_rpc.c b/lib/nvmf/nvmf_rpc.c index 0998b0fa2..8ba9974f0 100644 --- a/lib/nvmf/nvmf_rpc.c +++ b/lib/nvmf/nvmf_rpc.c @@ -259,6 +259,9 @@ dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *s spdk_json_write_named_uint32(w, "max_namespaces", max_namespaces); } + spdk_json_write_named_uint32(w, "min_cntlid", spdk_nvmf_subsystem_get_min_cntlid(subsystem)); + spdk_json_write_named_uint32(w, "max_cntlid", spdk_nvmf_subsystem_get_max_cntlid(subsystem)); + spdk_json_write_named_array_begin(w, "namespaces"); for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem); ns != NULL; ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) { @@ -344,6 +347,8 @@ struct rpc_subsystem_create { uint32_t max_namespaces; bool allow_any_host; bool ana_reporting; + uint16_t min_cntlid; + uint16_t max_cntlid; }; static const struct spdk_json_object_decoder rpc_subsystem_create_decoders[] = { @@ -354,6 +359,8 @@ static const struct spdk_json_object_decoder rpc_subsystem_create_decoders[] = { {"max_namespaces", offsetof(struct rpc_subsystem_create, max_namespaces), spdk_json_decode_uint32, true}, {"allow_any_host", offsetof(struct rpc_subsystem_create, allow_any_host), spdk_json_decode_bool, true}, {"ana_reporting", offsetof(struct rpc_subsystem_create, ana_reporting), spdk_json_decode_bool, true}, + {"min_cntlid", offsetof(struct rpc_subsystem_create, min_cntlid), spdk_json_decode_uint16, true}, + {"max_cntlid", offsetof(struct rpc_subsystem_create, max_cntlid), spdk_json_decode_uint16, true}, }; static void @@ -388,6 +395,8 @@ rpc_nvmf_create_subsystem(struct spdk_jsonrpc_request *request, "Memory allocation failed"); return; } + req->min_cntlid = NVMF_MIN_CNTLID; + req->max_cntlid = NVMF_MAX_CNTLID; if (spdk_json_decode_object(params, rpc_subsystem_create_decoders, SPDK_COUNTOF(rpc_subsystem_create_decoders), @@ -436,6 +445,14 @@ rpc_nvmf_create_subsystem(struct spdk_jsonrpc_request *request, spdk_nvmf_subsystem_set_ana_reporting(subsystem, req->ana_reporting); + if (nvmf_subsystem_set_cntlid_range(subsystem, req->min_cntlid, req->max_cntlid)) { + SPDK_ERRLOG("Subsystem %s: invalid cntlid range [%u-%u]\n", req->nqn, req->min_cntlid, + req->max_cntlid); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid cntlid range [%u-%u]", req->min_cntlid, req->max_cntlid); + goto cleanup; + } + rc = spdk_nvmf_subsystem_start(subsystem, rpc_nvmf_subsystem_started, request); diff --git a/lib/nvmf/spdk_nvmf.map b/lib/nvmf/spdk_nvmf.map index 0247249bc..c43a96343 100644 --- a/lib/nvmf/spdk_nvmf.map +++ b/lib/nvmf/spdk_nvmf.map @@ -53,6 +53,8 @@ spdk_nvmf_subsystem_get_first_ns; spdk_nvmf_subsystem_get_next_ns; spdk_nvmf_subsystem_get_ns; + spdk_nvmf_subsystem_get_min_cntlid; + spdk_nvmf_subsystem_get_max_cntlid; spdk_nvmf_subsystem_get_max_namespaces; spdk_nvmf_ns_get_id; spdk_nvmf_ns_get_bdev; diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c index be9c12342..ee312719d 100644 --- a/lib/nvmf/subsystem.c +++ b/lib/nvmf/subsystem.c @@ -285,6 +285,8 @@ spdk_nvmf_subsystem_create(struct spdk_nvmf_tgt *tgt, subsystem->subtype = type; subsystem->max_nsid = num_ns; subsystem->next_cntlid = 0; + subsystem->min_cntlid = NVMF_MIN_CNTLID; + subsystem->max_cntlid = NVMF_MAX_CNTLID; snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", nqn); pthread_mutex_init(&subsystem->mutex, NULL); TAILQ_INIT(&subsystem->listeners); @@ -1632,20 +1634,44 @@ spdk_nvmf_subsystem_get_max_nsid(struct spdk_nvmf_subsystem *subsystem) return subsystem->max_nsid; } +int +nvmf_subsystem_set_cntlid_range(struct spdk_nvmf_subsystem *subsystem, + uint16_t min_cntlid, uint16_t max_cntlid) +{ + if (subsystem->state != SPDK_NVMF_SUBSYSTEM_INACTIVE) { + return -EAGAIN; + } + + if (min_cntlid > max_cntlid) { + return -EINVAL; + } + /* The spec reserves cntlid values in the range FFF0h to FFFFh. */ + if (min_cntlid < NVMF_MIN_CNTLID || min_cntlid > NVMF_MAX_CNTLID || + max_cntlid < NVMF_MIN_CNTLID || max_cntlid > NVMF_MAX_CNTLID) { + return -EINVAL; + } + subsystem->min_cntlid = min_cntlid; + subsystem->max_cntlid = max_cntlid; + if (subsystem->next_cntlid < min_cntlid || subsystem->next_cntlid > max_cntlid - 1) { + subsystem->next_cntlid = min_cntlid - 1; + } + + return 0; +} + static uint16_t nvmf_subsystem_gen_cntlid(struct spdk_nvmf_subsystem *subsystem) { int count; /* - * In the worst case, we might have to try all CNTLID values between 1 and 0xFFF0 - 1 + * In the worst case, we might have to try all CNTLID values between min_cntlid and max_cntlid * before we find one that is unused (or find that all values are in use). */ - for (count = 0; count < 0xFFF0 - 1; count++) { + for (count = 0; count < subsystem->max_cntlid - subsystem->min_cntlid + 1; count++) { subsystem->next_cntlid++; - if (subsystem->next_cntlid >= 0xFFF0) { - /* The spec reserves cntlid values in the range FFF0h to FFFFh. */ - subsystem->next_cntlid = 1; + if (subsystem->next_cntlid > subsystem->max_cntlid) { + subsystem->next_cntlid = subsystem->min_cntlid; } /* Check if a controller with this cntlid currently exists. */ @@ -1702,6 +1728,18 @@ spdk_nvmf_subsystem_get_max_namespaces(const struct spdk_nvmf_subsystem *subsyst return subsystem->max_nsid; } +uint16_t +spdk_nvmf_subsystem_get_min_cntlid(const struct spdk_nvmf_subsystem *subsystem) +{ + return subsystem->min_cntlid; +} + +uint16_t +spdk_nvmf_subsystem_get_max_cntlid(const struct spdk_nvmf_subsystem *subsystem) +{ + return subsystem->max_cntlid; +} + struct _nvmf_ns_registrant { uint64_t rkey; char *host_uuid; diff --git a/scripts/rpc.py b/scripts/rpc.py index 1e4f57bf3..ae6bcfafe 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -1917,7 +1917,9 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse model_number=args.model_number, allow_any_host=args.allow_any_host, max_namespaces=args.max_namespaces, - ana_reporting=args.ana_reporting) + ana_reporting=args.ana_reporting, + min_cntlid=args.min_cntlid, + max_cntlid=args.max_cntlid) p = subparsers.add_parser('nvmf_create_subsystem', aliases=['nvmf_subsystem_create'], help='Create an NVMe-oF subsystem') @@ -1933,6 +1935,8 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed", type=int, default=0) p.add_argument("-r", "--ana-reporting", action='store_true', help="Enable ANA reporting feature") + p.add_argument("-i", "--min_cntlid", help="Minimum controller ID", type=int, default=1) + p.add_argument("-I", "--max_cntlid", help="Maximum controller ID", type=int, default=0xffef) p.set_defaults(func=nvmf_create_subsystem) def nvmf_delete_subsystem(args): diff --git a/scripts/rpc/nvmf.py b/scripts/rpc/nvmf.py index 3070e49e0..8848b430b 100644 --- a/scripts/rpc/nvmf.py +++ b/scripts/rpc/nvmf.py @@ -177,7 +177,9 @@ def nvmf_create_subsystem(client, model_number='SPDK bdev Controller', allow_any_host=False, max_namespaces=0, - ana_reporting=False): + ana_reporting=False, + min_cntlid=1, + max_cntlid=0xffef): """Construct an NVMe over Fabrics target subsystem. Args: @@ -188,6 +190,8 @@ def nvmf_create_subsystem(client, allow_any_host: Allow any host (True) or enforce allowed host list (False). Default: False. max_namespaces: Maximum number of namespaces that can be attached to the subsystem (optional). Default: 0 (Unlimited). ana_reporting: Enable ANA reporting feature. Default: False. + min_cntlid: Minimum controller ID. Default: 1 + max_cntlid: Maximum controller ID. Default: 0xffef Returns: @@ -215,6 +219,12 @@ def nvmf_create_subsystem(client, if ana_reporting: params['ana_reporting'] = ana_reporting + if min_cntlid is not None: + params['min_cntlid'] = min_cntlid + + if max_cntlid is not None: + params['max_cntlid'] = max_cntlid + return client.call('nvmf_create_subsystem', params) diff --git a/test/nvmf/target/invalid.sh b/test/nvmf/target/invalid.sh index dbd8cdf19..a15115766 100755 --- a/test/nvmf/target/invalid.sh +++ b/test/nvmf/target/invalid.sh @@ -66,6 +66,20 @@ fi out=$("$rpc" nvmf_subsystem_remove_listener "$nqn" -t "$TEST_TRANSPORT" -a "$IP" -s 4421 2>&1) && false [[ $out != *"Unable to stop listener."* ]] +# Attempt to create subsystem with invalid controller ID range - outside [1, 0xffef] +out=$("$rpc" nvmf_create_subsystem "$nqn$RANDOM" -i 0 2>&1) && false +[[ $out == *"Invalid cntlid range"* ]] +out=$("$rpc" nvmf_create_subsystem "$nqn$RANDOM" -i 65520 2>&1) && false +[[ $out == *"Invalid cntlid range"* ]] +out=$("$rpc" nvmf_create_subsystem "$nqn$RANDOM" -I 0 2>&1) && false +[[ $out == *"Invalid cntlid range"* ]] +out=$("$rpc" nvmf_create_subsystem "$nqn$RANDOM" -I 65520 2>&1) && false +[[ $out == *"Invalid cntlid range"* ]] + +# Attempt to create subsystem with invalid controller ID range - [x, y] where x>y +out=$("$rpc" nvmf_create_subsystem "$nqn$RANDOM" -i 6 -I 5 2>&1) && false +[[ $out == *"Invalid cntlid range"* ]] + # Attempt to delete non-existing target out=$("$multi_target_rpc" nvmf_delete_target --name "$target" 2>&1) && false [[ $out == *"The specified target doesn't exist, cannot delete it."* ]] diff --git a/test/nvmf/target/nvme_cli.sh b/test/nvmf/target/nvme_cli.sh index 24ecd6853..ab0b13b08 100755 --- a/test/nvmf/target/nvme_cli.sh +++ b/test/nvmf/target/nvme_cli.sh @@ -23,7 +23,7 @@ $rpc_py nvmf_create_transport $NVMF_TRANSPORT_OPTS -u 8192 $rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE -b Malloc0 $rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE -b Malloc1 -$rpc_py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s $NVMF_SERIAL -d SPDK_Controller1 +$rpc_py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s $NVMF_SERIAL -d SPDK_Controller1 -i 291 $rpc_py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Malloc0 $rpc_py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Malloc1 $rpc_py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t $TEST_TRANSPORT -a $NVMF_FIRST_TARGET_IP -s $NVMF_PORT @@ -46,6 +46,11 @@ for ctrl in "${nvmes[@]}"; do echo "Wrong model number for controller" $nvme_model exit 1 fi + nvme_cntlid=$(nvme id-ctrl $ctrl | grep -w cntlid | sed 's/^.*: //' | sed 's/ *$//') + if [ "$nvme_cntlid" != "0x123" ]; then + echo "Wrong controller ID for controller" $nvme_model + exit 1 + fi done for ns in "${nvmes[@]}"; do