From be8a9d696608f92659cb2a9e73de903130e80a88 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Fri, 13 Jan 2017 14:36:26 -0700 Subject: [PATCH] nvme: add transport ID string parsing function Change-Id: I33c15c8a56c25667567b373d21a117cca1f756c7 Signed-off-by: Daniel Verkamp --- include/spdk/nvme.h | 23 ++++++ lib/nvme/nvme.c | 119 ++++++++++++++++++++++++++++ test/lib/nvme/unit/nvme_c/nvme_ut.c | 27 ++++++- 3 files changed, 168 insertions(+), 1 deletion(-) diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 843a50289..a0230cd8c 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -169,6 +169,29 @@ struct spdk_nvme_transport_id { char subnqn[SPDK_NVMF_NQN_MAX_LEN + 1]; }; +/** + * Parse the string representation of a transport ID. + * + * \param trid Output transport ID structure (must be allocated and initialized by caller). + * \param str Input string representation of a transport ID to parse. + * \return 0 if parsing was successful and trid is filled out, or negated errno values on failure. + * + * str must be a zero-terminated C string containing one or more key:value pairs separated by + * whitespace. + * + * Key | Value + * ------------ | ----- + * trtype | Transport type (e.g. PCIe, RDMA) + * adrfam | Address family (e.g. IPv4, IPv6) + * traddr | Transport address (e.g. 0000:04:00.0 for PCIe or 192.168.100.8 for RDMA) + * trsvcid | Transport service identifier (e.g. 4420) + * subnqn | Subsystem NQN + * + * Unspecified fields of trid are left unmodified, so the caller must initialize trid (for example, + * memset() to 0) before calling this function. + */ +int spdk_nvme_transport_id_parse(struct spdk_nvme_transport_id *trid, const char *str); + /** * Determine whether the NVMe library can handle a specific NVMe over Fabrics transport type. * diff --git a/lib/nvme/nvme.c b/lib/nvme/nvme.c index 3b0652ff3..168c829ae 100644 --- a/lib/nvme/nvme.c +++ b/lib/nvme/nvme.c @@ -443,4 +443,123 @@ spdk_nvme_probe(const struct spdk_nvme_transport_id *trid, void *cb_ctx, return rc; } +static int +parse_trtype(enum spdk_nvme_transport_type *trtype, const char *str) +{ + if (strcasecmp(str, "PCIe") == 0) { + *trtype = SPDK_NVME_TRANSPORT_PCIE; + } else if (strcasecmp(str, "RDMA") == 0) { + *trtype = SPDK_NVME_TRANSPORT_RDMA; + } else { + return -ENOENT; + } + return 0; +} + +static int +parse_adrfam(enum spdk_nvmf_adrfam *adrfam, const char *str) +{ + if (strcasecmp(str, "IPv4") == 0) { + *adrfam = SPDK_NVMF_ADRFAM_IPV4; + } else if (strcasecmp(str, "IPv6") == 0) { + *adrfam = SPDK_NVMF_ADRFAM_IPV6; + } else if (strcasecmp(str, "IB") == 0) { + *adrfam = SPDK_NVMF_ADRFAM_IB; + } else if (strcasecmp(str, "FC") == 0) { + *adrfam = SPDK_NVMF_ADRFAM_FC; + } else { + return -ENOENT; + } + return 0; +} + +int +spdk_nvme_transport_id_parse(struct spdk_nvme_transport_id *trid, const char *str) +{ + const char *sep; + const char *whitespace = " \t\n"; + size_t key_len, val_len; + char key[32]; + char val[1024]; + + if (trid == NULL || str == NULL) { + return -EINVAL; + } + + while (*str != '\0') { + str += strspn(str, whitespace); + + sep = strchr(str, ':'); + if (!sep) { + SPDK_ERRLOG("Key without : separator\n"); + return -EINVAL; + } + + key_len = sep - str; + if (key_len >= sizeof(key)) { + SPDK_ERRLOG("Transport key length %zu greater than maximum allowed %zu\n", + key_len, sizeof(key) - 1); + return -EINVAL; + } + + memcpy(key, str, key_len); + key[key_len] = '\0'; + + str += key_len + 1; /* Skip key: */ + val_len = strcspn(str, whitespace); + if (val_len == 0) { + SPDK_ERRLOG("Key without value\n"); + return -EINVAL; + } + + if (val_len >= sizeof(val)) { + SPDK_ERRLOG("Transport value length %zu greater than maximum allowed %zu\n", + val_len, sizeof(val) - 1); + return -EINVAL; + } + + memcpy(val, str, val_len); + val[val_len] = '\0'; + + str += val_len; + + if (strcasecmp(key, "trtype") == 0) { + if (parse_trtype(&trid->trtype, val) != 0) { + SPDK_ERRLOG("Unknown trtype '%s'\n", val); + return -EINVAL; + } + } else if (strcasecmp(key, "adrfam") == 0) { + if (parse_adrfam(&trid->adrfam, val) != 0) { + SPDK_ERRLOG("Unknown adrfam '%s'\n", val); + return -EINVAL; + } + } else if (strcasecmp(key, "traddr") == 0) { + if (val_len > SPDK_NVMF_TRADDR_MAX_LEN) { + SPDK_ERRLOG("traddr length %zu greater than maximum allowed %u\n", + val_len, SPDK_NVMF_TRADDR_MAX_LEN); + return -EINVAL; + } + memcpy(trid->traddr, val, val_len + 1); + } else if (strcasecmp(key, "trsvcid") == 0) { + if (val_len > SPDK_NVMF_TRSVCID_MAX_LEN) { + SPDK_ERRLOG("trsvcid length %zu greater than maximum allowed %u\n", + val_len, SPDK_NVMF_TRSVCID_MAX_LEN); + return -EINVAL; + } + memcpy(trid->trsvcid, val, val_len + 1); + } else if (strcasecmp(key, "subnqn") == 0) { + if (val_len > SPDK_NVMF_NQN_MAX_LEN) { + SPDK_ERRLOG("subnqn length %zu greater than maximum allowed %u\n", + val_len, SPDK_NVMF_NQN_MAX_LEN); + return -EINVAL; + } + memcpy(trid->subnqn, val, val_len + 1); + } else { + SPDK_ERRLOG("Unknown transport ID key '%s'\n", key); + } + } + + return 0; +} + SPDK_LOG_REGISTER_TRACE_FLAG("nvme", SPDK_TRACE_NVME) diff --git a/test/lib/nvme/unit/nvme_c/nvme_ut.c b/test/lib/nvme/unit/nvme_c/nvme_ut.c index 9cc44aa09..989753e26 100644 --- a/test/lib/nvme/unit/nvme_c/nvme_ut.c +++ b/test/lib/nvme/unit/nvme_c/nvme_ut.c @@ -162,6 +162,30 @@ test_opc_data_transfer(void) CU_ASSERT(xfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST); } +static void +test_trid_parse(void) +{ + struct spdk_nvme_transport_id trid; + + memset(&trid, 0, sizeof(trid)); + CU_ASSERT(spdk_nvme_transport_id_parse(&trid, + "trtype:rdma\n" + "adrfam:ipv4\n" + "traddr:192.168.100.8\n" + "trsvcid:4420\n" + "subnqn:nqn.2014-08.org.nvmexpress.discovery") == 0); + CU_ASSERT(trid.trtype == SPDK_NVME_TRANSPORT_RDMA); + CU_ASSERT(trid.adrfam == SPDK_NVMF_ADRFAM_IPV4); + CU_ASSERT(strcmp(trid.traddr, "192.168.100.8") == 0); + CU_ASSERT(strcmp(trid.trsvcid, "4420") == 0); + CU_ASSERT(strcmp(trid.subnqn, "nqn.2014-08.org.nvmexpress.discovery") == 0); + + memset(&trid, 0, sizeof(trid)); + CU_ASSERT(spdk_nvme_transport_id_parse(&trid, "trtype:PCIe traddr:0000:04:00.0") == 0); + CU_ASSERT(trid.trtype == SPDK_NVME_TRANSPORT_PCIE); + CU_ASSERT(strcmp(trid.traddr, "0000:04:00.0") == 0); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -178,7 +202,8 @@ int main(int argc, char **argv) } if ( - CU_add_test(suite, "test_opc_data_transfer", test_opc_data_transfer) == NULL + CU_add_test(suite, "test_opc_data_transfer", test_opc_data_transfer) == NULL || + CU_add_test(suite, "test_trid_parse", test_trid_parse) == NULL ) { CU_cleanup_registry(); return CU_get_error();