diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a3b35a0..943a06aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ needed; previously, the response was limited to 32 kilobytes. EXPERIMENTAL: Adds support for WDS and RDS capable CMBs in NVMe controllers. This support is experimental pending a functional allocator to free and reallocate CMB buffers. +### NVMe-oF Target + +Namespaces may now be assigned unique identifiers via new optional "eui64" and "nguid" parameters +to the `nvmf_subsystem_add_ns` RPC method. + ## v18.01: Blobstore Thin Provisioning ### Build System diff --git a/app/nvmf_tgt/nvmf_rpc.c b/app/nvmf_tgt/nvmf_rpc.c index 85343f042..e1d1e5a3a 100644 --- a/app/nvmf_tgt/nvmf_rpc.c +++ b/app/nvmf_tgt/nvmf_rpc.c @@ -43,6 +43,143 @@ #include "nvmf_tgt.h" +static bool +all_zero(const void *data, size_t size) +{ + const uint8_t *buf = data; + + while (size--) { + if (*buf++ != 0) { + return false; + } + } + return true; +} + +static int +json_write_hex_str(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + static const char hex_char[16] = "0123456789ABCDEF"; + const uint8_t *buf = data; + char *str, *out; + int rc; + + str = malloc(size * 2 + 1); + if (str == NULL) { + return -1; + } + + out = str; + while (size--) { + unsigned byte = *buf++; + + out[0] = hex_char[(byte >> 4) & 0xF]; + out[1] = hex_char[byte & 0xF]; + + out += 2; + } + *out = '\0'; + + rc = spdk_json_write_string(w, str); + free(str); + + return rc; +} + +static int +hex_nybble_to_num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xA; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xA; + } + + return -1; +} + +static int +hex_byte_to_num(const char *str) +{ + int hi, lo; + + hi = hex_nybble_to_num(str[0]); + if (hi < 0) { + return hi; + } + + lo = hex_nybble_to_num(str[1]); + if (lo < 0) { + return lo; + } + + return hi * 16 + lo; +} + +static int +decode_hex_string_be(const char *str, uint8_t *out, size_t size) +{ + size_t i; + + /* Decode a string in "ABCDEF012345" format to its binary representation */ + for (i = 0; i < size; i++) { + int num = hex_byte_to_num(str); + + if (num < 0) { + /* Invalid hex byte or end of string */ + return -1; + } + + out[i] = (uint8_t)num; + str += 2; + } + + if (i != size || *str != '\0') { + /* Length mismatch */ + return -1; + } + + return 0; +} + +static int +decode_ns_nguid(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 16-byte NGUID */ + rc = decode_hex_string_be(str, out, 16); + } + + free(str); + return rc; +} + +static int +decode_ns_eui64(const struct spdk_json_val *val, void *out) +{ + char *str = NULL; + int rc; + + rc = spdk_json_decode_string(val, &str); + if (rc == 0) { + /* 8-byte EUI-64 */ + rc = decode_hex_string_be(str, out, 8); + } + + free(str); + return rc; +} + static void dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *subsystem) { @@ -112,6 +249,7 @@ dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *s if (spdk_nvmf_subsystem_get_type(subsystem) == SPDK_NVMF_SUBTYPE_NVME) { struct spdk_nvmf_ns *ns; + struct spdk_nvmf_ns_opts ns_opts; spdk_json_write_name(w, "serial_number"); spdk_json_write_string(w, spdk_nvmf_subsystem_get_sn(subsystem)); @@ -119,6 +257,7 @@ dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *s spdk_json_write_array_begin(w); for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem); ns != NULL; ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) { + spdk_nvmf_ns_get_opts(ns, &ns_opts, sizeof(ns_opts)); spdk_json_write_object_begin(w); spdk_json_write_name(w, "nsid"); spdk_json_write_int32(w, spdk_nvmf_ns_get_id(ns)); @@ -127,6 +266,17 @@ dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *s /* NOTE: "name" is kept for compatibility only - new code should use bdev_name. */ spdk_json_write_name(w, "name"); spdk_json_write_string(w, spdk_bdev_get_name(spdk_nvmf_ns_get_bdev(ns))); + + if (!all_zero(ns_opts.nguid, sizeof(ns_opts.nguid))) { + spdk_json_write_name(w, "nguid"); + json_write_hex_str(w, ns_opts.nguid, sizeof(ns_opts.nguid)); + } + + if (!all_zero(ns_opts.eui64, sizeof(ns_opts.eui64))) { + spdk_json_write_name(w, "eui64"); + json_write_hex_str(w, ns_opts.eui64, sizeof(ns_opts.eui64)); + } + spdk_json_write_object_end(w); } spdk_json_write_array_end(w); @@ -271,6 +421,8 @@ decode_rpc_hosts(const struct spdk_json_val *val, void *out) struct spdk_nvmf_ns_params { char *bdev_name; uint32_t nsid; + char nguid[16]; + char eui64[8]; }; struct rpc_namespaces { @@ -282,6 +434,8 @@ struct rpc_namespaces { static const struct spdk_json_object_decoder rpc_ns_params_decoders[] = { {"nsid", offsetof(struct spdk_nvmf_ns_params, nsid), spdk_json_decode_uint32, true}, {"bdev_name", offsetof(struct spdk_nvmf_ns_params, bdev_name), spdk_json_decode_string}, + {"nguid", offsetof(struct spdk_nvmf_ns_params, nguid), decode_ns_nguid, true}, + {"eui64", offsetof(struct spdk_nvmf_ns_params, eui64), decode_ns_eui64, true}, }; static void @@ -482,6 +636,12 @@ spdk_rpc_construct_nvmf_subsystem(struct spdk_jsonrpc_request *request, spdk_nvmf_ns_opts_get_defaults(&ns_opts, sizeof(ns_opts)); ns_opts.nsid = ns_params->nsid; + SPDK_STATIC_ASSERT(sizeof(ns_opts.nguid) == sizeof(ns_params->nguid), "size mismatch"); + memcpy(ns_opts.nguid, ns_params->nguid, sizeof(ns_opts.nguid)); + + SPDK_STATIC_ASSERT(sizeof(ns_opts.eui64) == sizeof(ns_params->eui64), "size mismatch"); + memcpy(ns_opts.eui64, ns_params->eui64, sizeof(ns_opts.eui64)); + if (spdk_nvmf_subsystem_add_ns(subsystem, bdev, &ns_opts, sizeof(ns_opts)) == 0) { SPDK_ERRLOG("Unable to add namespace\n"); spdk_nvmf_subsystem_destroy(subsystem); @@ -769,6 +929,12 @@ nvmf_rpc_ns_paused(struct spdk_nvmf_subsystem *subsystem, spdk_nvmf_ns_opts_get_defaults(&ns_opts, sizeof(ns_opts)); ns_opts.nsid = ctx->ns_params.nsid; + SPDK_STATIC_ASSERT(sizeof(ns_opts.nguid) == sizeof(ctx->ns_params.nguid), "size mismatch"); + memcpy(ns_opts.nguid, ctx->ns_params.nguid, sizeof(ns_opts.nguid)); + + SPDK_STATIC_ASSERT(sizeof(ns_opts.eui64) == sizeof(ctx->ns_params.eui64), "size mismatch"); + memcpy(ns_opts.eui64, ctx->ns_params.eui64, sizeof(ns_opts.eui64)); + ctx->ns_params.nsid = spdk_nvmf_subsystem_add_ns(subsystem, bdev, &ns_opts, sizeof(ns_opts)); if (ctx->ns_params.nsid == 0) { SPDK_ERRLOG("Unable to add namespace\n"); diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 1150d9648..d052855a6 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -260,6 +260,8 @@ Name | Optional | Type | Description ----------------------- | -------- | ----------- | ----------- nsid | Optional | number | Namespace ID between 1 and 4294967294, inclusive. Default: Automatically assign NSID. bdev_name | Required | string | Name of bdev to expose as a namespace. +nguid | Optional | string | 16-byte namespace globally unique identifier in hexadecimal (e.g. "ABCDEF0123456789ABCDEF0123456789") +eui64 | Optional | string | 8-byte namespace EUI-64 in hexadecimal (e.g. "ABCDEF0123456789") ### Example diff --git a/include/spdk/nvmf.h b/include/spdk/nvmf.h index 48c042169..48321a1c3 100644 --- a/include/spdk/nvmf.h +++ b/include/spdk/nvmf.h @@ -376,6 +376,20 @@ struct spdk_nvmf_ns_opts { * Set to 0 to automatically assign a free NSID. */ uint32_t nsid; + + /** + * Namespace Globally Unique Identifier + * + * Fill with 0s if not specified. + */ + uint8_t nguid[16]; + + /** + * IEEE Extended Unique Identifier + * + * Fill with 0s if not specified. + */ + uint8_t eui64[8]; }; /** diff --git a/lib/nvmf/ctrlr.c b/lib/nvmf/ctrlr.c index 193f086ae..17cf48727 100644 --- a/lib/nvmf/ctrlr.c +++ b/lib/nvmf/ctrlr.c @@ -869,7 +869,7 @@ spdk_nvmf_ctrlr_identify_ns(struct spdk_nvmf_subsystem *subsystem, return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE; } - return spdk_nvmf_bdev_ctrlr_identify_ns(ns->bdev, nsdata); + return spdk_nvmf_bdev_ctrlr_identify_ns(ns, nsdata); } static int diff --git a/lib/nvmf/ctrlr_bdev.c b/lib/nvmf/ctrlr_bdev.c index acdaf6325..0b2727bac 100644 --- a/lib/nvmf/ctrlr_bdev.c +++ b/lib/nvmf/ctrlr_bdev.c @@ -103,8 +103,9 @@ nvmf_bdev_ctrlr_complete_cmd(struct spdk_bdev_io *bdev_io, bool success, } int -spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_bdev *bdev, struct spdk_nvme_ns_data *nsdata) +spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata) { + struct spdk_bdev *bdev = ns->bdev; uint64_t num_blocks; num_blocks = spdk_bdev_get_num_blocks(bdev); @@ -117,6 +118,12 @@ spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_bdev *bdev, struct spdk_nvme_ns_dat nsdata->lbaf[0].lbads = spdk_u32log2(spdk_bdev_get_block_size(bdev)); nsdata->noiob = spdk_bdev_get_optimal_io_boundary(bdev); + SPDK_STATIC_ASSERT(sizeof(nsdata->nguid) == sizeof(ns->opts.nguid), "size mismatch"); + memcpy(nsdata->nguid, ns->opts.nguid, sizeof(nsdata->nguid)); + + SPDK_STATIC_ASSERT(sizeof(nsdata->eui64) == sizeof(ns->opts.eui64), "size mismatch"); + memcpy(&nsdata->eui64, ns->opts.eui64, sizeof(nsdata->eui64)); + return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE; } diff --git a/lib/nvmf/nvmf_internal.h b/lib/nvmf/nvmf_internal.h index 4f5b92684..74b04dab9 100644 --- a/lib/nvmf/nvmf_internal.h +++ b/lib/nvmf/nvmf_internal.h @@ -236,7 +236,7 @@ int spdk_nvmf_ctrlr_process_io_cmd(struct spdk_nvmf_request *req); bool spdk_nvmf_ctrlr_dsm_supported(struct spdk_nvmf_ctrlr *ctrlr); bool spdk_nvmf_ctrlr_write_zeroes_supported(struct spdk_nvmf_ctrlr *ctrlr); -int spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_bdev *bdev, struct spdk_nvme_ns_data *nsdata); +int spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata); int spdk_nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, struct spdk_nvmf_ctrlr *ctrlr); diff --git a/scripts/rpc.py b/scripts/rpc.py index bd07c2cf5..a448b037a 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -374,6 +374,8 @@ if __name__ == "__main__": p.add_argument('nqn', help='NVMe-oF subsystem NQN') p.add_argument('bdev_name', help='The name of the bdev that will back this namespace') p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int) + p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)') + p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)') p.set_defaults(func=rpc.nvmf.nvmf_subsystem_add_ns) # pmem diff --git a/scripts/rpc/nvmf.py b/scripts/rpc/nvmf.py index 9bc6f72bb..b0f4e83a4 100755 --- a/scripts/rpc/nvmf.py +++ b/scripts/rpc/nvmf.py @@ -64,6 +64,12 @@ def nvmf_subsystem_add_ns(args): if args.nsid: ns['nsid'] = args.nsid + if args.nguid: + ns['nguid'] = args.nguid + + if args.eui64: + ns['eui64'] = args.eui64 + params = {'nqn': args.nqn, 'namespace': ns} diff --git a/test/nvmf/host/identify.sh b/test/nvmf/host/identify.sh index b90e709bf..79aa29edf 100755 --- a/test/nvmf/host/identify.sh +++ b/test/nvmf/host/identify.sh @@ -31,9 +31,19 @@ timing_exit start_nvmf_tgt bdevs="$bdevs $($rpc_py construct_malloc_bdev $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE)" -$rpc_py construct_nvmf_subsystem nqn.2016-06.io.spdk:cnode1 '' '' -a -s SPDK00000000000001 -n "$bdevs" +$rpc_py construct_nvmf_subsystem nqn.2016-06.io.spdk:cnode1 '' '' -a -s SPDK00000000000001 $rpc_py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t RDMA -a $NVMF_FIRST_TARGET_IP -s 4420 +for bdev in $bdevs; do + # NOTE: This will assign the same NGUID and EUI64 to all bdevs, + # but currently we only have one (see above), so this is OK. + $rpc_py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 "$bdev" \ + --nguid "ABCDEF0123456789ABCDEF0123456789" \ + --eui64 "ABCDEF0123456789" +done + +$rpc_py get_nvmf_subsystems + $rootdir/examples/nvme/identify/identify -r "\ trtype:RDMA \ adrfam:IPv4 \ diff --git a/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c b/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c index 3273bff2e..2c385293f 100644 --- a/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c +++ b/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c @@ -117,7 +117,7 @@ DEFINE_STUB(spdk_nvmf_ctrlr_write_zeroes_supported, DEFINE_STUB(spdk_nvmf_bdev_ctrlr_identify_ns, int, - (struct spdk_bdev *bdev, struct spdk_nvme_ns_data *nsdata), + (struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata), -1) DEFINE_STUB_V(spdk_nvmf_get_discovery_log_page,