diff --git a/CHANGELOG.md b/CHANGELOG.md index 8029678c9..a3593a671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ decrypt operations. New RPC `dpdk_cryptodev_scan_accel_module` has been added to ### bdev +Added RPCs bdev_nvme_start_mdns_discovery, bdev_nvme_get_mdns_discovery_info and +bdev_nvme_stop_mdns_discovery to perform Avahi based mDNS discovery service, +as per NVMe TP 8009 - Automated Discovery of Ethernet Discovery Controllers. + Both of interleaved and separated metadata are now supported by the malloc bdev module. Protection information is now supported by the malloc bdev module. diff --git a/CONFIG b/CONFIG index 5c261507b..54517ce56 100644 --- a/CONFIG +++ b/CONFIG @@ -2,6 +2,7 @@ # Copyright (C) 2015 Intel Corporation. # All rights reserved. # Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. # # configure options: __CONFIGURE_OPTIONS__ @@ -208,3 +209,6 @@ CONFIG_IPSEC_MB_DIR= # Generate Storage Management Agent's protobuf interface CONFIG_SMA=n + +# Build with Avahi support +CONFIG_AVAHI=n diff --git a/configure b/configure index b3cc419ee..f093aeb72 100755 --- a/configure +++ b/configure @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (C) 2017 Intel Corporation # All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. # set -e @@ -120,6 +121,8 @@ function usage() { echo " (Typically /usr/lib/llvm-VER/lib/clang/VER/lib/linux/libclang_rt.fuzzer_no_main-ARCH.a)" echo " --with-sma Generate Storage Management Agent's protobuf interface" echo " --without-sma No path required." + echo " --with-avahi Build with Avahi mDNS discovery client service enabled in bdev-nvme module." + echo " --without-avahi No path required." echo "" echo "Environment variables:" echo "" @@ -641,6 +644,12 @@ for i in "$@"; do --without-sma) CONFIG[SMA]=n ;; + --with-avahi) + CONFIG[AVAHI]=y + ;; + --without-avahi) + CONFIG[AVAHI]=n + ;; --) break ;; @@ -1217,6 +1226,16 @@ if [[ "${CONFIG[SMA]}" = "y" ]]; then fi fi +if [[ "${CONFIG[AVAHI]}" = "y" ]]; then + if ! echo -e '#include \n#include \n' \ + 'int main(void) { return 0; }\n' \ + | "${BUILD_CMD[@]}" -lavahi-client -lavahi-common - 2> /dev/null; then + echo "--with-avahi requires libavahi-client and libavahi-common." + echo "Please install then re-run this script." + exit 1 + fi +fi + # For ARM Neoverse-N1 platform, debug build needs gcc version newer than 8.4 if [[ "${CONFIG[DEBUG]}" = "y" && $arch = aarch64* && "$CC_TYPE" = "gcc" ]]; then GCC_VERSION=$($CC -dumpfullversion) diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 52e8096a5..376ba2ca3 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -11379,3 +11379,143 @@ Example response: "result": true } ~~~ + +### bdev_nvme_start_mdns_discovery {#rpc_bdev_nvme_start_mdns_discovery} + +Starts an mDNS based discovery service for the specified service type for the +auto-discovery of discovery controllers (NVMe TP-8009). + +The discovery service will listen for the mDNS discovery events from the +Avahi-daemon and will connect to the newly learnt discovery controllers. +Then the discovery service will automatically attach to any subsystem found in the +discovery log page from the discovery controllers learnt using mDNS. +When determining a controller name to use when attaching, it will use +the 'name' parameter as a prefix, followed by a unique integer assigned for each of the +discovery controllers learnt through mDNS, followed by a unique integer for that discovery +service. If the discovery service identifies a subsystem that has been previously +attached but is listed with a different path, it will use the same controller name +as the previous entry, and connect as a multipath. + +When the discovery service sees that a subsystem entry has been removed +from the log page, it will automatically detach from that controller as well. + +The 'name' is also used to later stop the discovery service. + +#### Parameters + +Name | Optional | Type | Description +-------------------------- | -------- | ----------- | ----------- +name | Required | string | Prefix for NVMe discovery services found +svcname | Required | string | Service to discover: e.g. _nvme-disc._tcp +hostnqn | Optional | string | NVMe-oF hostnqn + +#### Example + +Example request: + +~~~json +{ + "jsonrpc": "2.0", + "method": "bdev_nvme_start_mdns_discovery", + "id": 1, + "params": { + "name": "cdc_auto", + "svcname": "_nvme-disc._tcp", + "hostnqn": "nqn.2021-12.io.spdk:host1", + } +} +~~~ + +Example response: + +~~~json +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + +### bdev_nvme_stop_mdns_discovery {#rpc_bdev_nvme_stop_mdns_discovery} + +Stops a mDNS discovery service. This includes detaching any controllers that were +discovered via the service that is being stopped. + +#### Parameters + +Name | Optional | Type | Description +-------------------------- | -------- | ----------- | ----------- +name | Required | string | Name of mDNS discovery instance to stop + +#### Example + +Example request: + +~~~json +{ + "jsonrpc": "2.0", + "method": "bdev_nvme_stop_mdns_discovery", + "id": 1, + "params": { + "name": "cdc_auto" + } +} +~~~ + +Example response: + +~~~json +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + +### bdev_nvme_get_mdns_discovery_info {#rpc_bdev_nvme_get_mdns_discovery_info} + +Get the information about the mDNS discovery service instances. + +#### Example + +Example request: +~~~json +{ + "jsonrpc": "2.0", + "method": "bdev_nvme_get_mdns_discovery_info", + "id": 1 +} +~~~ + +Example response: + +~~~json +[ + { + "name": "cdc_auto", + "svcname": "_nvme-disc._tcp", + "referrals": [ + { + "name": "cdc_auto0", + "trid": { + "trtype": "TCP", + "adrfam": "IPv4", + "traddr": "66.1.2.21", + "trsvcid": "8009", + "subnqn": "nqn.2014-08.org.nvmexpress.discovery" + } + }, + { + "name": "cdc_auto1", + "trid": { + "trtype": "TCP", + "adrfam": "IPv4", + "traddr": "66.1.1.21", + "trsvcid": "8009", + "subnqn": "nqn.2014-08.org.nvmexpress.discovery" + } + } + ] + } +] +~~~ diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk index 8bce5e56b..652b0a1cb 100644 --- a/mk/spdk.common.mk +++ b/mk/spdk.common.mk @@ -4,6 +4,7 @@ # Copyright (c) 2019, 2021 Mellanox Corporation. # Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES # All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. # ifeq ($(wildcard $(SPDK_ROOT_DIR)/mk/config.mk),) @@ -175,6 +176,10 @@ LDFLAGS += -L$(CONFIG_URING_PATH) endif endif +ifeq ($(CONFIG_AVAHI),y) +SYS_LIBS += -lavahi-common -lavahi-client +endif + IPSEC_MB_DIR=$(CONFIG_IPSEC_MB_DIR) ISAL_DIR=$(SPDK_ROOT_DIR)/isa-l diff --git a/module/bdev/nvme/Makefile b/module/bdev/nvme/Makefile index 6ec1180e7..8e888a667 100644 --- a/module/bdev/nvme/Makefile +++ b/module/bdev/nvme/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (C) 2015 Intel Corporation. # All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. # SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) @@ -9,7 +10,7 @@ include $(SPDK_ROOT_DIR)/mk/spdk.common.mk SO_VER := 5 SO_MINOR := 0 -C_SRCS = bdev_nvme.c bdev_nvme_rpc.c nvme_rpc.c +C_SRCS = bdev_nvme.c bdev_nvme_rpc.c nvme_rpc.c bdev_mdns_client.c C_SRCS-$(CONFIG_NVME_CUSE) += bdev_nvme_cuse_rpc.c ifeq ($(OS),Linux) diff --git a/module/bdev/nvme/bdev_mdns_client.c b/module/bdev/nvme/bdev_mdns_client.c new file mode 100644 index 000000000..2616f62af --- /dev/null +++ b/module/bdev/nvme/bdev_mdns_client.c @@ -0,0 +1,655 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2022 Dell Inc, or its subsidiaries. + * All rights reserved. + */ + +#include "spdk/stdinc.h" +#include "spdk/version.h" + +#include "spdk_internal/event.h" + +#include "spdk/assert.h" +#include "spdk/config.h" +#include "spdk/env.h" +#include "spdk/init.h" +#include "spdk/log.h" +#include "spdk/thread.h" +#include "spdk/trace.h" +#include "spdk/string.h" +#include "spdk/scheduler.h" +#include "spdk/rpc.h" +#include "spdk/util.h" +#include "spdk/nvme.h" +#include "bdev_nvme.h" + +#ifdef SPDK_CONFIG_AVAHI +#include +#include +#include +#include +#include + +static AvahiSimplePoll *g_avahi_simple_poll = NULL; +static AvahiClient *g_avahi_client = NULL; + +struct mdns_discovery_entry_ctx { + char name[256]; + struct spdk_nvme_transport_id trid; + struct spdk_nvme_ctrlr_opts drv_opts; + TAILQ_ENTRY(mdns_discovery_entry_ctx) tailq; + struct mdns_discovery_ctx *ctx; +}; + +struct mdns_discovery_ctx { + char *name; + char *svcname; + char *hostnqn; + AvahiServiceBrowser *sb; + struct spdk_poller *poller; + struct spdk_nvme_ctrlr_opts drv_opts; + struct nvme_ctrlr_opts bdev_opts; + uint32_t seqno; + bool stop; + struct spdk_thread *calling_thread; + TAILQ_ENTRY(mdns_discovery_ctx) tailq; + TAILQ_HEAD(, mdns_discovery_entry_ctx) mdns_discovery_entry_ctxs; +}; + +TAILQ_HEAD(mdns_discovery_ctxs, mdns_discovery_ctx); +static struct mdns_discovery_ctxs g_mdns_discovery_ctxs = TAILQ_HEAD_INITIALIZER( + g_mdns_discovery_ctxs); + +static struct mdns_discovery_entry_ctx * +create_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx, struct spdk_nvme_transport_id *trid) +{ + struct mdns_discovery_entry_ctx *new_ctx; + + assert(ctx); + assert(trid); + new_ctx = calloc(1, sizeof(*new_ctx)); + if (new_ctx == NULL) { + SPDK_ERRLOG("could not allocate new mdns_entry_ctx\n"); + return NULL; + } + + new_ctx->ctx = ctx; + memcpy(&new_ctx->trid, trid, sizeof(struct spdk_nvme_transport_id)); + snprintf(new_ctx->name, sizeof(new_ctx->name), "%s%u_nvme", ctx->name, ctx->seqno); + memcpy(&new_ctx->drv_opts, &ctx->drv_opts, sizeof(ctx->drv_opts)); + snprintf(new_ctx->drv_opts.hostnqn, sizeof(ctx->drv_opts.hostnqn), "%s", ctx->hostnqn); + ctx->seqno = ctx->seqno + 1; + return new_ctx; +} + +static void +mdns_bdev_nvme_start_discovery(void *_entry_ctx) +{ + int status; + struct mdns_discovery_entry_ctx *entry_ctx = _entry_ctx; + + assert(_entry_ctx); + status = bdev_nvme_start_discovery(&entry_ctx->trid, entry_ctx->name, + &entry_ctx->ctx->drv_opts, + &entry_ctx->ctx->bdev_opts, + 0, true, NULL, NULL); + if (status) { + SPDK_ERRLOG("Error starting discovery for name %s addr %s port %s subnqn %s &trid %p\n", + entry_ctx->ctx->name, entry_ctx->trid.traddr, entry_ctx->trid.trsvcid, + entry_ctx->trid.subnqn, &entry_ctx->trid); + } +} + +static void +free_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx) +{ + struct mdns_discovery_entry_ctx *entry_ctx = NULL; + + if (!ctx) { + return; + } + + TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { + free(entry_ctx); + } +} + +static void +free_mdns_discovery_ctx(struct mdns_discovery_ctx *ctx) +{ + if (!ctx) { + return; + } + + free(ctx->name); + free(ctx->svcname); + free(ctx->hostnqn); + avahi_service_browser_free(ctx->sb); + free_mdns_discovery_entry_ctx(ctx); + free(ctx); +} + +/* get_key_val_avahi_resolve_txt - Search for the key string in the TXT received + * from Avavi daemon and return its value. + * input + * txt: TXT returned by Ahavi daemon will be of format + * "NQN=nqn.1988-11.com.dell:SFSS:1:20221122170722e8" "p=tcp foo" and the + * AvahiStringList txt is a linked list with each node holding a + * key-value pair like key:p value:tcp + * + * key: Key string to search in the txt list + * output + * Returns the value for the key or NULL if key is not present + * Returned string needs to be freed with avahi_free() + */ +static char * +get_key_val_avahi_resolve_txt(AvahiStringList *txt, const char *key) +{ + char *k = NULL, *v = NULL; + AvahiStringList *p = NULL; + int r; + + if (!txt || !key) { + return NULL; + } + + p = avahi_string_list_find(txt, key); + if (!p) { + return NULL; + } + + r = avahi_string_list_get_pair(p, &k, &v, NULL); + if (r < 0) { + return NULL; + } + + avahi_free(k); + return v; +} + +static int +get_spdk_nvme_transport_from_proto_str(char *protocol, enum spdk_nvme_transport_type *trtype) +{ + int status = -1; + + if (!protocol || !trtype) { + return status; + } + + if (strcmp("tcp", protocol) == 0) { + *trtype = SPDK_NVME_TRANSPORT_TCP; + return 0; + } + + return status; +} + +static enum spdk_nvmf_adrfam +get_spdk_nvme_adrfam_from_avahi_addr(const AvahiAddress *address) { + + if (!address) + { + /* Return ipv4 by default */ + return SPDK_NVMF_ADRFAM_IPV4; + } + + switch (address->proto) + { + case AVAHI_PROTO_INET: + return SPDK_NVMF_ADRFAM_IPV4; + case AVAHI_PROTO_INET6: + return SPDK_NVMF_ADRFAM_IPV6; + default: + return SPDK_NVMF_ADRFAM_IPV4; + } +} + +static struct mdns_discovery_ctx * +get_mdns_discovery_ctx_by_svcname(const char *svcname) +{ + struct mdns_discovery_ctx *ctx = NULL, *tmp_ctx = NULL; + + if (!svcname) { + return NULL; + } + + TAILQ_FOREACH_SAFE(ctx, &g_mdns_discovery_ctxs, tailq, tmp_ctx) { + if (strcmp(ctx->svcname, svcname) == 0) { + return ctx; + } + } + return NULL; +} + +static void +mdns_resolve_callback( + AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void *userdata) +{ + assert(r); + /* Called whenever a service has been resolved successfully or timed out */ + switch (event) { + case AVAHI_RESOLVER_FAILURE: + SPDK_ERRLOG("(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", + name, type, domain, + avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + case AVAHI_RESOLVER_FOUND: { + char ipaddr[SPDK_NVMF_TRADDR_MAX_LEN + 1], port_str[SPDK_NVMF_TRSVCID_MAX_LEN + 1], *t; + struct spdk_nvme_transport_id *trid = NULL; + char *subnqn = NULL, *proto = NULL; + struct mdns_discovery_ctx *ctx = NULL; + struct mdns_discovery_entry_ctx *entry_ctx = NULL; + int status = -1; + + memset(ipaddr, 0, sizeof(ipaddr)); + memset(port_str, 0, sizeof(port_str)); + SPDK_INFOLOG(bdev_nvme, "Service '%s' of type '%s' in domain '%s'\n", name, type, domain); + avahi_address_snprint(ipaddr, sizeof(ipaddr), address); + snprintf(port_str, sizeof(port_str), "%d", port); + t = avahi_string_list_to_string(txt); + SPDK_INFOLOG(bdev_nvme, + "\t%s:%u (%s)\n" + "\tTXT=%s\n" + "\tcookie is %u\n" + "\tis_local: %i\n" + "\tour_own: %i\n" + "\twide_area: %i\n" + "\tmulticast: %i\n" + "\tcached: %i\n", + host_name, port, ipaddr, + t, + avahi_string_list_get_service_cookie(txt), + !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), + !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), + !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), + !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), + !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); + + ctx = get_mdns_discovery_ctx_by_svcname(type); + if (!ctx) { + SPDK_ERRLOG("Unknown Service '%s'\n", type); + break; + } + + trid = (struct spdk_nvme_transport_id *) calloc(1, sizeof(struct spdk_nvme_transport_id)); + if (!trid) { + SPDK_ERRLOG(" Error allocating memory for trid\n"); + break; + } + trid->adrfam = get_spdk_nvme_adrfam_from_avahi_addr(address); + if (trid->adrfam != SPDK_NVMF_ADRFAM_IPV4) { + /* TODO: For now process only ipv4 addresses */ + SPDK_INFOLOG(bdev_nvme, "trid family is not IPV4 %d\n", trid->adrfam); + free(trid); + break; + } + subnqn = get_key_val_avahi_resolve_txt(txt, "NQN"); + if (!subnqn) { + free(trid); + SPDK_ERRLOG("subnqn received is empty for service %s\n", ctx->svcname); + break; + } + proto = get_key_val_avahi_resolve_txt(txt, "p"); + if (!proto) { + free(trid); + avahi_free(subnqn); + SPDK_ERRLOG("Protocol not received for service %s\n", ctx->svcname); + break; + } + status = get_spdk_nvme_transport_from_proto_str(proto, &trid->trtype); + if (status) { + free(trid); + avahi_free(subnqn); + avahi_free(proto); + SPDK_ERRLOG("Unable to derive nvme transport type for service %s\n", ctx->svcname); + break; + } + trid->adrfam = get_spdk_nvme_adrfam_from_avahi_addr(address); + snprintf(trid->traddr, sizeof(trid->traddr), "%s", ipaddr); + snprintf(trid->trsvcid, sizeof(trid->trsvcid), "%s", port_str); + snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", subnqn); + TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { + if (!spdk_nvme_transport_id_compare(trid, &entry_ctx->trid)) { + SPDK_ERRLOG("mDNS discovery entry exists already. trid->traddr: %s trid->trsvcid: %s\n", + trid->traddr, trid->trsvcid); + free(trid); + avahi_free(subnqn); + avahi_free(proto); + break; + } + } + entry_ctx = create_mdns_discovery_entry_ctx(ctx, trid); + TAILQ_INSERT_TAIL(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); + spdk_thread_send_msg(ctx->calling_thread, mdns_bdev_nvme_start_discovery, entry_ctx); + free(trid); + avahi_free(subnqn); + avahi_free(proto); + break; + } + default: + SPDK_ERRLOG("Unknown Avahi resolver event: %d", event); + } + avahi_service_resolver_free(r); +} + +static void +mdns_browse_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void *userdata) +{ + AvahiClient *c = userdata; + + assert(b); + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ + switch (event) { + case AVAHI_BROWSER_FAILURE: + SPDK_ERRLOG("(Browser) Failure: %s\n", + avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + return; + case AVAHI_BROWSER_NEW: + SPDK_DEBUGLOG(bdev_nvme, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, + domain); + /* We ignore the returned resolver object. In the callback + function we free it. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, + mdns_resolve_callback, c))) { + SPDK_ERRLOG("Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + } + break; + case AVAHI_BROWSER_REMOVE: + SPDK_ERRLOG("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + /* On remove, we are not doing the automatic cleanup of connections + * to the targets that were learnt from the CDC, for which remove event has + * been received. If required, user can clear the connections manually by + * invoking bdev_nvme_stop_discovery. We can implement the automatic cleanup + * later, if there is a requirement in the future. + */ + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + SPDK_INFOLOG(bdev_nvme, "(Browser) %s\n", + event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + default: + SPDK_ERRLOG("Unknown Avahi browser event: %d", event); + } +} + +static void +client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) +{ + assert(c); + /* Called whenever the client or server state changes */ + if (state == AVAHI_CLIENT_FAILURE) { + SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); + } +} + +static int +bdev_nvme_avahi_iterate(void *arg) +{ + struct mdns_discovery_ctx *ctx = arg; + int rc; + + if (ctx->stop) { + SPDK_INFOLOG(bdev_nvme, "Stopping avahi poller for service %s\n", ctx->svcname); + spdk_poller_unregister(&ctx->poller); + TAILQ_REMOVE(&g_mdns_discovery_ctxs, ctx, tailq); + free_mdns_discovery_ctx(ctx); + return SPDK_POLLER_IDLE; + } + + if (g_avahi_simple_poll == NULL) { + spdk_poller_unregister(&ctx->poller); + return SPDK_POLLER_IDLE; + } + + rc = avahi_simple_poll_iterate(g_avahi_simple_poll, 0); + if (rc && rc != -EAGAIN) { + SPDK_ERRLOG("avahi poll returned error for service: %s/n", ctx->svcname); + return SPDK_POLLER_IDLE; + } + + return SPDK_POLLER_BUSY; +} + +static void +start_mdns_discovery_poller(void *arg) +{ + struct mdns_discovery_ctx *ctx = arg; + + assert(arg); + TAILQ_INSERT_TAIL(&g_mdns_discovery_ctxs, ctx, tailq); + ctx->poller = SPDK_POLLER_REGISTER(bdev_nvme_avahi_iterate, ctx, 1000 * 1000); +} + +int +bdev_nvme_start_mdns_discovery(const char *base_name, + const char *svcname, + struct spdk_nvme_ctrlr_opts *drv_opts, + struct nvme_ctrlr_opts *bdev_opts) +{ + AvahiServiceBrowser *sb = NULL; + int error; + struct mdns_discovery_ctx *ctx; + + assert(base_name); + assert(svcname); + + TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { + if (strcmp(ctx->name, base_name) == 0) { + SPDK_ERRLOG("mDNS discovery already running with name %s\n", base_name); + return -EEXIST; + } + + if (strcmp(ctx->svcname, svcname) == 0) { + SPDK_ERRLOG("mDNS discovery already running for service %s\n", svcname); + return -EEXIST; + } + } + + if (g_avahi_simple_poll == NULL) { + + /* Allocate main loop object */ + if (!(g_avahi_simple_poll = avahi_simple_poll_new())) { + SPDK_ERRLOG("Failed to create poll object for mDNS discovery for service: %s.\n", svcname); + return -ENOMEM; + } + } + + if (g_avahi_client == NULL) { + + /* Allocate a new client */ + g_avahi_client = avahi_client_new(avahi_simple_poll_get(g_avahi_simple_poll), 0, client_callback, + NULL, &error); + /* Check whether creating the client object succeeded */ + if (!g_avahi_client) { + SPDK_ERRLOG("Failed to create mDNS client for service:%s Error: %s\n", svcname, + avahi_strerror(error)); + return -ENOMEM; + } + } + + /* Create the service browser */ + if (!(sb = avahi_service_browser_new(g_avahi_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, svcname, + NULL, 0, mdns_browse_callback, g_avahi_client))) { + SPDK_ERRLOG("Failed to create service browser for service: %s Error: %s\n", svcname, + avahi_strerror(avahi_client_errno(g_avahi_client))); + return -ENOMEM; + } + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + SPDK_ERRLOG("Error creating mDNS discovery ctx for service: %s\n", svcname); + avahi_service_browser_free(sb); + return -ENOMEM; + } + + ctx->svcname = strdup(svcname); + if (ctx->svcname == NULL) { + SPDK_ERRLOG("Error creating mDNS discovery ctx svcname for service: %s\n", svcname); + free_mdns_discovery_ctx(ctx); + avahi_service_browser_free(sb); + return -ENOMEM; + } + ctx->name = strdup(base_name); + if (ctx->name == NULL) { + SPDK_ERRLOG("Error creating mDNS discovery ctx name for service: %s\n", svcname); + free_mdns_discovery_ctx(ctx); + avahi_service_browser_free(sb); + return -ENOMEM; + } + memcpy(&ctx->drv_opts, drv_opts, sizeof(*drv_opts)); + memcpy(&ctx->bdev_opts, bdev_opts, sizeof(*bdev_opts)); + ctx->sb = sb; + ctx->calling_thread = spdk_get_thread(); + TAILQ_INIT(&ctx->mdns_discovery_entry_ctxs); + /* Even if user did not specify hostnqn, we can still strdup("\0"); */ + ctx->hostnqn = strdup(ctx->drv_opts.hostnqn); + if (ctx->hostnqn == NULL) { + SPDK_ERRLOG("Error creating mDNS discovery ctx hostnqn for service: %s\n", svcname); + free_mdns_discovery_ctx(ctx); + return -ENOMEM; + } + /* Start the poller for the Avahi client browser in g_bdev_nvme_init_thread */ + spdk_thread_send_msg(g_bdev_nvme_init_thread, start_mdns_discovery_poller, ctx); + return 0; +} + +static void +mdns_stop_discovery_entry(struct mdns_discovery_ctx *ctx) +{ + struct mdns_discovery_entry_ctx *entry_ctx = NULL; + + assert(ctx); + + TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { + bdev_nvme_stop_discovery(entry_ctx->name, NULL, NULL); + } +} + +int +bdev_nvme_stop_mdns_discovery(const char *name) +{ + struct mdns_discovery_ctx *ctx; + + assert(name); + TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { + if (strcmp(name, ctx->name) == 0) { + if (ctx->stop) { + return -EALREADY; + } + /* set stop to true to stop the mdns poller instance */ + ctx->stop = true; + mdns_stop_discovery_entry(ctx); + return 0; + } + } + + return -ENOENT; +} + +void +bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) +{ + struct mdns_discovery_ctx *ctx; + struct mdns_discovery_entry_ctx *entry_ctx; + struct spdk_json_write_ctx *w; + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_array_begin(w); + TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { + spdk_json_write_object_begin(w); + spdk_json_write_named_string(w, "name", ctx->name); + spdk_json_write_named_string(w, "svcname", ctx->svcname); + + spdk_json_write_named_array_begin(w, "referrals"); + TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { + spdk_json_write_object_begin(w); + spdk_json_write_named_string(w, "name", entry_ctx->name); + spdk_json_write_named_object_begin(w, "trid"); + nvme_bdev_dump_trid_json(&entry_ctx->trid, w); + spdk_json_write_object_end(w); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + spdk_jsonrpc_end_result(request, w); +} + +void +bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) +{ + struct mdns_discovery_ctx *ctx; + + TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { + spdk_json_write_object_begin(w); + + spdk_json_write_named_string(w, "method", "bdev_nvme_start_mdns_discovery"); + + spdk_json_write_named_object_begin(w, "params"); + spdk_json_write_named_string(w, "name", ctx->name); + spdk_json_write_named_string(w, "svcname", ctx->svcname); + spdk_json_write_named_string(w, "hostnqn", ctx->hostnqn); + spdk_json_write_object_end(w); + + spdk_json_write_object_end(w); + } +} + +#else /* SPDK_CONFIG_AVAHI */ + +int +bdev_nvme_start_mdns_discovery(const char *base_name, + const char *svcname, + struct spdk_nvme_ctrlr_opts *drv_opts, + struct nvme_ctrlr_opts *bdev_opts) +{ + SPDK_ERRLOG("spdk not built with --with-avahi option\n"); + return -ENOTSUP; +} + +int +bdev_nvme_stop_mdns_discovery(const char *name) +{ + SPDK_ERRLOG("spdk not built with --with-avahi option\n"); + return -ENOTSUP; +} + +void +bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) +{ + SPDK_ERRLOG("spdk not built with --with-avahi option\n"); + spdk_jsonrpc_send_error_response(request, -ENOTSUP, spdk_strerror(ENOTSUP)); +} + +void +bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) +{ + /* Empty function to be invoked, when SPDK is built without --with-avahi */ +} + +#endif diff --git a/module/bdev/nvme/bdev_nvme.c b/module/bdev/nvme/bdev_nvme.c index 64d7f8ee8..26a727ceb 100644 --- a/module/bdev/nvme/bdev_nvme.c +++ b/module/bdev/nvme/bdev_nvme.c @@ -2,6 +2,7 @@ * Copyright (C) 2016 Intel Corporation. All rights reserved. * Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved. * Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022 Dell Inc, or its subsidiaries. All rights reserved. */ #include "spdk/stdinc.h" @@ -133,7 +134,7 @@ static struct spdk_bdev_nvme_opts g_opts = { static int g_hot_insert_nvme_controller_index = 0; static uint64_t g_nvme_hotplug_poll_period_us = NVME_HOTPLUG_POLL_PERIOD_DEFAULT; static bool g_nvme_hotplug_enabled = false; -static struct spdk_thread *g_bdev_nvme_init_thread; +struct spdk_thread *g_bdev_nvme_init_thread; static struct spdk_poller *g_hotplug_poller; static struct spdk_poller *g_hotplug_probe_poller; static struct spdk_nvme_probe_ctx *g_hotplug_probe_ctx; @@ -5188,6 +5189,10 @@ struct discovery_ctx { uint32_t index; uint32_t attach_in_progress; char *hostnqn; + + /* Denotes if the discovery service was started by the mdns discovery. + */ + bool from_mdns_discovery_service; }; TAILQ_HEAD(discovery_ctxs, discovery_ctx); @@ -5641,6 +5646,7 @@ bdev_nvme_start_discovery(struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr_opts *drv_opts, struct nvme_ctrlr_opts *bdev_opts, uint64_t attach_timeout, + bool from_mdns, spdk_bdev_nvme_start_discovery_fn cb_fn, void *cb_ctx) { struct discovery_ctx *ctx; @@ -5677,6 +5683,7 @@ bdev_nvme_start_discovery(struct spdk_nvme_transport_id *trid, } memcpy(&ctx->drv_opts, drv_opts, sizeof(*drv_opts)); memcpy(&ctx->bdev_opts, bdev_opts, sizeof(*bdev_opts)); + ctx->from_mdns_discovery_service = from_mdns; ctx->bdev_opts.from_discovery_service = true; ctx->calling_thread = spdk_get_thread(); ctx->start_cb_fn = cb_fn; @@ -6864,7 +6871,8 @@ nvme_ctrlr_config_json(struct spdk_json_write_ctx *w, if (nvme_ctrlr->opts.from_discovery_service) { /* Do not emit an RPC for this - it will be implicitly - * covered by a separate bdev_nvme_start_discovery RPC. + * covered by a separate bdev_nvme_start_discovery or + * bdev_nvme_start_mdns_discovery RPC. */ return; } @@ -6924,9 +6932,13 @@ bdev_nvme_config_json(struct spdk_json_write_ctx *w) } TAILQ_FOREACH(ctx, &g_discovery_ctxs, tailq) { - bdev_nvme_discovery_config_json(w, ctx); + if (!ctx->from_mdns_discovery_service) { + bdev_nvme_discovery_config_json(w, ctx); + } } + bdev_nvme_mdns_discovery_config_json(w); + /* Dump as last parameter to give all NVMe bdevs chance to be constructed * before enabling hotplug poller. */ diff --git a/module/bdev/nvme/bdev_nvme.h b/module/bdev/nvme/bdev_nvme.h index 568d60995..81b877e9b 100644 --- a/module/bdev/nvme/bdev_nvme.h +++ b/module/bdev/nvme/bdev_nvme.h @@ -2,6 +2,7 @@ * Copyright (C) 2016 Intel Corporation. All rights reserved. * Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved. * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022 Dell Inc, or its subsidiaries. All rights reserved. */ #ifndef SPDK_BDEV_NVME_H @@ -12,11 +13,13 @@ #include "spdk/queue.h" #include "spdk/nvme.h" #include "spdk/bdev_module.h" +#include "spdk/jsonrpc.h" TAILQ_HEAD(nvme_bdev_ctrlrs, nvme_bdev_ctrlr); extern struct nvme_bdev_ctrlrs g_nvme_bdev_ctrlrs; extern pthread_mutex_t g_bdev_nvme_mutex; extern bool g_bdev_nvme_module_finish; +extern struct spdk_thread *g_bdev_nvme_init_thread; #define NVME_MAX_CONTROLLERS 1024 @@ -281,11 +284,20 @@ int bdev_nvme_create(struct spdk_nvme_transport_id *trid, int bdev_nvme_start_discovery(struct spdk_nvme_transport_id *trid, const char *base_name, struct spdk_nvme_ctrlr_opts *drv_opts, struct nvme_ctrlr_opts *bdev_opts, - uint64_t timeout, spdk_bdev_nvme_start_discovery_fn cb_fn, void *cb_ctx); + uint64_t timeout, bool from_mdns, + spdk_bdev_nvme_start_discovery_fn cb_fn, void *cb_ctx); int bdev_nvme_stop_discovery(const char *name, spdk_bdev_nvme_stop_discovery_fn cb_fn, void *cb_ctx); void bdev_nvme_get_discovery_info(struct spdk_json_write_ctx *w); +int bdev_nvme_start_mdns_discovery(const char *base_name, + const char *svcname, + struct spdk_nvme_ctrlr_opts *drv_opts, + struct nvme_ctrlr_opts *bdev_opts); +int bdev_nvme_stop_mdns_discovery(const char *name); +void bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request); +void bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w); + struct spdk_nvme_ctrlr *bdev_nvme_get_ctrlr(struct spdk_bdev *bdev); /** diff --git a/module/bdev/nvme/bdev_nvme_rpc.c b/module/bdev/nvme/bdev_nvme_rpc.c index e36702956..04463a4ec 100644 --- a/module/bdev/nvme/bdev_nvme_rpc.c +++ b/module/bdev/nvme/bdev_nvme_rpc.c @@ -2,6 +2,7 @@ * Copyright (C) 2016 Intel Corporation. All rights reserved. * Copyright (c) 2019-2021 Mellanox Technologies LTD. All rights reserved. * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2022 Dell Inc, or its subsidiaries. All rights reserved. */ #include "spdk/stdinc.h" @@ -1685,7 +1686,7 @@ rpc_bdev_nvme_start_discovery(struct spdk_jsonrpc_request *request, cb_fn = ctx->req.wait_for_attach ? rpc_bdev_nvme_start_discovery_done : NULL; cb_ctx = ctx->req.wait_for_attach ? request : NULL; rc = bdev_nvme_start_discovery(&trid, ctx->req.name, &ctx->req.opts, &ctx->req.bdev_opts, - ctx->req.attach_timeout_ms, cb_fn, cb_ctx); + ctx->req.attach_timeout_ms, false, cb_fn, cb_ctx); if (rc) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); } else if (!ctx->req.wait_for_attach) { @@ -2290,3 +2291,135 @@ cleanup: } SPDK_RPC_REGISTER("bdev_nvme_set_multipath_policy", rpc_bdev_nvme_set_multipath_policy, SPDK_RPC_RUNTIME) + +struct rpc_bdev_nvme_start_mdns_discovery { + char *name; + char *svcname; + char *hostnqn; + struct spdk_nvme_ctrlr_opts opts; + struct nvme_ctrlr_opts bdev_opts; +}; + +static void +free_rpc_bdev_nvme_start_mdns_discovery(struct rpc_bdev_nvme_start_mdns_discovery *req) +{ + free(req->name); + free(req->svcname); + free(req->hostnqn); +} + +static const struct spdk_json_object_decoder rpc_bdev_nvme_start_mdns_discovery_decoders[] = { + {"name", offsetof(struct rpc_bdev_nvme_start_mdns_discovery, name), spdk_json_decode_string}, + {"svcname", offsetof(struct rpc_bdev_nvme_start_mdns_discovery, svcname), spdk_json_decode_string}, + {"hostnqn", offsetof(struct rpc_bdev_nvme_start_mdns_discovery, hostnqn), spdk_json_decode_string, true}, +}; + +struct rpc_bdev_nvme_start_mdns_discovery_ctx { + struct rpc_bdev_nvme_start_mdns_discovery req; + struct spdk_jsonrpc_request *request; +}; + +static void +rpc_bdev_nvme_start_mdns_discovery(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_nvme_start_mdns_discovery_ctx *ctx; + int rc; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM)); + return; + } + + spdk_nvme_ctrlr_get_default_ctrlr_opts(&ctx->req.opts, sizeof(ctx->req.opts)); + + if (spdk_json_decode_object(params, rpc_bdev_nvme_start_mdns_discovery_decoders, + SPDK_COUNTOF(rpc_bdev_nvme_start_mdns_discovery_decoders), + &ctx->req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "spdk_json_decode_object failed"); + goto cleanup; + } + + if (ctx->req.hostnqn) { + snprintf(ctx->req.opts.hostnqn, sizeof(ctx->req.opts.hostnqn), "%s", + ctx->req.hostnqn); + } + ctx->request = request; + rc = bdev_nvme_start_mdns_discovery(ctx->req.name, ctx->req.svcname, &ctx->req.opts, + &ctx->req.bdev_opts); + if (rc) { + spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); + } else { + spdk_jsonrpc_send_bool_response(request, true); + } + +cleanup: + free_rpc_bdev_nvme_start_mdns_discovery(&ctx->req); + free(ctx); +} +SPDK_RPC_REGISTER("bdev_nvme_start_mdns_discovery", rpc_bdev_nvme_start_mdns_discovery, + SPDK_RPC_RUNTIME) + +struct rpc_bdev_nvme_stop_mdns_discovery { + char *name; +}; + +static const struct spdk_json_object_decoder rpc_bdev_nvme_stop_mdns_discovery_decoders[] = { + {"name", offsetof(struct rpc_bdev_nvme_stop_mdns_discovery, name), spdk_json_decode_string}, +}; + +struct rpc_bdev_nvme_stop_mdns_discovery_ctx { + struct rpc_bdev_nvme_stop_mdns_discovery req; + struct spdk_jsonrpc_request *request; +}; + +static void +rpc_bdev_nvme_stop_mdns_discovery(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_nvme_stop_mdns_discovery_ctx *ctx; + int rc; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM)); + return; + } + + if (spdk_json_decode_object(params, rpc_bdev_nvme_stop_mdns_discovery_decoders, + SPDK_COUNTOF(rpc_bdev_nvme_stop_mdns_discovery_decoders), + &ctx->req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "spdk_json_decode_object failed"); + goto cleanup; + } + + ctx->request = request; + rc = bdev_nvme_stop_mdns_discovery(ctx->req.name); + + if (rc) { + spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); + goto cleanup; + } + spdk_jsonrpc_send_bool_response(ctx->request, true); + +cleanup: + free(ctx->req.name); + free(ctx); +} +SPDK_RPC_REGISTER("bdev_nvme_stop_mdns_discovery", rpc_bdev_nvme_stop_mdns_discovery, + SPDK_RPC_RUNTIME) + +static void +rpc_bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + bdev_nvme_get_mdns_discovery_info(request); +} + +SPDK_RPC_REGISTER("bdev_nvme_get_mdns_discovery_info", rpc_bdev_nvme_get_mdns_discovery_info, + SPDK_RPC_RUNTIME) diff --git a/python/spdk/rpc/bdev.py b/python/spdk/rpc/bdev.py index eb4453eb4..e754a1465 100644 --- a/python/spdk/rpc/bdev.py +++ b/python/spdk/rpc/bdev.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (C) 2017 Intel Corporation. # All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. def bdev_set_options(client, bdev_io_pool_size=None, bdev_io_cache_size=None, bdev_auto_examine=None, @@ -1724,3 +1725,36 @@ def bdev_daos_resize(client, name, new_size): 'new_size': new_size, } return client.call('bdev_daos_resize', params) + + +def bdev_nvme_start_mdns_discovery(client, name, svcname, hostnqn=None): + """Start discovery with mDNS + + Args: + name: bdev name prefix; "n" + unique seqno + namespace ID will be appended to create unique names + svcname: service to discover ("_nvme-disc._tcp") + hostnqn: NQN to connect from (optional) + """ + params = {'name': name, + 'svcname': svcname} + + if hostnqn: + params['hostnqn'] = hostnqn + return client.call('bdev_nvme_start_mdns_discovery', params) + + +def bdev_nvme_stop_mdns_discovery(client, name): + """Stop a previously started mdns discovery service + + Args: + name: name of the discovery service to stop + """ + params = {'name': name} + + return client.call('bdev_nvme_stop_mdns_discovery', params) + + +def bdev_nvme_get_mdns_discovery_info(client): + """Get information about the automatic mdns discovery + """ + return client.call('bdev_nvme_get_mdns_discovery_info') diff --git a/scripts/rpc.py b/scripts/rpc.py index 6ec8c7283..83780031c 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (C) 2016 Intel Corporation # All rights reserved. +# Copyright (c) 2022 Dell Inc, or its subsidiaries. # import logging @@ -3256,6 +3257,31 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('--large-bufsize', help='size of a large buffer', type=int) p.set_defaults(func=iobuf_set_options) + def bdev_nvme_start_mdns_discovery(args): + rpc.bdev.bdev_nvme_start_mdns_discovery(args.client, + name=args.name, + svcname=args.svcname, + hostnqn=args.hostnqn) + + p = subparsers.add_parser('bdev_nvme_start_mdns_discovery', help='Start mdns based automatic discovery') + p.add_argument('-b', '--name', help="Name of the NVMe controller prefix for each bdev name", required=True) + p.add_argument('-s', '--svcname', help='Service type to discover: e.g., _nvme-disc._tcp', required=True) + p.add_argument('-q', '--hostnqn', help='NVMe-oF host subnqn') + p.set_defaults(func=bdev_nvme_start_mdns_discovery) + + def bdev_nvme_stop_mdns_discovery(args): + rpc.bdev.bdev_nvme_stop_mdns_discovery(args.client, name=args.name) + + p = subparsers.add_parser('bdev_nvme_stop_mdns_discovery', help='Stop automatic mdns discovery') + p.add_argument('-b', '--name', help="Name of the service to stop", required=True) + p.set_defaults(func=bdev_nvme_stop_mdns_discovery) + + def bdev_nvme_get_mdns_discovery_info(args): + print_dict(rpc.bdev.bdev_nvme_get_mdns_discovery_info(args.client)) + + p = subparsers.add_parser('bdev_nvme_get_mdns_discovery_info', help='Get information about the automatic mdns discovery') + p.set_defaults(func=bdev_nvme_get_mdns_discovery_info) + def check_called_name(name): if name in deprecated_aliases: print("{} is deprecated, use {} instead.".format(name, deprecated_aliases[name]), file=sys.stderr) diff --git a/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c b/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c index 45c6353f1..20b8aac18 100644 --- a/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c +++ b/test/unit/lib/bdev/nvme/bdev_nvme.c/bdev_nvme_ut.c @@ -16,6 +16,8 @@ #include "unit/lib/json_mock.c" +#include "bdev/nvme/bdev_mdns_client.c" + static void *g_accel_p = (void *)0xdeadbeaf; DEFINE_STUB(spdk_nvme_probe_async, struct spdk_nvme_probe_ctx *, @@ -46,6 +48,10 @@ DEFINE_STUB(spdk_nvme_ctrlr_get_discovery_log_page, int, (struct spdk_nvme_ctrlr *ctrlr, spdk_nvme_discovery_cb cb_fn, void *cb_arg), 0); DEFINE_RETURN_MOCK(spdk_nvme_ctrlr_get_memory_domains, int); + +DEFINE_STUB_V(spdk_jsonrpc_send_error_response, (struct spdk_jsonrpc_request *request, + int error_code, const char *msg)); + int spdk_nvme_ctrlr_get_memory_domains(const struct spdk_nvme_ctrlr *ctrlr, struct spdk_memory_domain **domains, int array_size)