nvme: Added support for TP-8009, Auto-discovery of Discovery controllers for NVME initiator using mDNS using Avahi
Approach: Avahi Daemon needs to be running to provide the mDNS server service. In the SPDK, Avahi-client library based client API is implemented. The client API will connect to the Avahi-daemon and receive events for new discovery and removal of an existing discovery entry. Following sets on new RPCs have been introduced. scripts/rpc.py bdev_nvme_start_mdns_discovery -b cdc_auto -s _nvme-disc._tcp User shall initiate an mDNS based discovery using this RPC. This will start a Avahi-client based poller looking for new discovery events from the Avahi server. On a new discovery of the discovery controller, the existing bdev_nvme_start_discovery API will be invoked with the trid of the discovery controller learnt. This will enable automatic connection of the initiator to the subsystems discovered from the discovery controller. Multiple mdns discovery instances can be run by specifying a unique bdev-prefix and a unique servicename to discover as parameters. scripts/rpc.py bdev_nvme_stop_mdns_discovery -b cdc_auto This will stop the Avahi poller that was started for the specified service.Internally bdev_nvme_stop_discovery API will be invoked for each of the discovery controllers learnt automatically by this instance of mdns discovery service. This will result in termination of connections to all the subsystems learnt by this mdns discovery instance. scripts/rpc.py bdev_nvme_get_mdns_discovery_info This RPC will display the list of mdns discovery instances running and the trid of the controllers discovered by these instances. Test Result: root@ubuntu-pm-18-226:~/param-spdk/spdk/build/bin# ./nvmf_tgt -i 1 -s 2048 -m 0xF root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_start_mdns_discovery -b cdc_auto -s _nvme-disc._tcp root@ubuntu-pm-18-226:~/param-spdk/spdk# root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_get_mdns_discovery_info [ { "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" } } ] } ] root@ubuntu-pm-18-226:~/param-spdk/spdk# root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_get_discovery_info [ { "name": "cdc_auto0", "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.2.21", "trsvcid": "8009", "subnqn": "nqn.2014-08.org.nvmexpress.discovery" }, "referrals": [] }, { "name": "cdc_auto1", "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.1.21", "trsvcid": "8009", "subnqn": "nqn.2014-08.org.nvmexpress.discovery" }, "referrals": [] } ] root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_get_bdevs [ { "name": "cdc_auto02n1", "aliases": [ "600110d6-1681-1681-0403-000045805c45" ], "product_name": "NVMe disk", "block_size": 512, "num_blocks": 32768, "uuid": "600110d6-1681-1681-0403-000045805c45", "assigned_rate_limits": { "rw_ios_per_sec": 0, "rw_mbytes_per_sec": 0, "r_mbytes_per_sec": 0, "w_mbytes_per_sec": 0 }, "claimed": false, "zoned": false, "supported_io_types": { "read": true, "write": true, "unmap": true, "write_zeroes": true, "flush": true, "reset": true, "compare": true, "compare_and_write": true, "abort": true, "nvme_admin": true, "nvme_io": true }, "driver_specific": { "nvme": [ { "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.1.40", "trsvcid": "4420", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.0" }, "ctrlr_data": { "cntlid": 3, "vendor_id": "0x0000", "model_number": "SANBlaze VLUN P3T0", "serial_number": "00-681681dc681681dc", "firmware_revision": "V10.5", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.0", "oacs": { "security": 0, "format": 1, "firmware": 0, "ns_manage": 1 }, "multi_ctrlr": true, "ana_reporting": true }, "vs": { "nvme_version": "2.0" }, "ns_data": { "id": 1, "ana_state": "optimized", "can_share": true } } ], "mp_policy": "active_passive" } }, { "name": "cdc_auto00n1", "aliases": [ "600110da-09a6-09a6-0302-00005eeb19b4" ], "product_name": "NVMe disk", "block_size": 512, "num_blocks": 2048, "uuid": "600110da-09a6-09a6-0302-00005eeb19b4", "assigned_rate_limits": { "rw_ios_per_sec": 0, "rw_mbytes_per_sec": 0, "r_mbytes_per_sec": 0, "w_mbytes_per_sec": 0 }, "claimed": false, "zoned": false, "supported_io_types": { "read": true, "write": true, "unmap": true, "write_zeroes": true, "flush": true, "reset": true, "compare": true, "compare_and_write": true, "abort": true, "nvme_admin": true, "nvme_io": true }, "driver_specific": { "nvme": [ { "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.2.40", "trsvcid": "4420", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.2.0" }, "ctrlr_data": { "cntlid": 1, "vendor_id": "0x0000", "model_number": "SANBlaze VLUN P2T0", "serial_number": "00-ab09a6f5ab09a6f5", "firmware_revision": "V10.5", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.2.0", "oacs": { "security": 0, "format": 1, "firmware": 0, "ns_manage": 1 }, "multi_ctrlr": true, "ana_reporting": true }, "vs": { "nvme_version": "2.0" }, "ns_data": { "id": 1, "ana_state": "optimized", "can_share": true } } ], "mp_policy": "active_passive" } }, { "name": "cdc_auto01n1", "aliases": [ "600110d6-dce8-dce8-0403-00010b2d3d8c" ], "product_name": "NVMe disk", "block_size": 512, "num_blocks": 32768, "uuid": "600110d6-dce8-dce8-0403-00010b2d3d8c", "assigned_rate_limits": { "rw_ios_per_sec": 0, "rw_mbytes_per_sec": 0, "r_mbytes_per_sec": 0, "w_mbytes_per_sec": 0 }, "claimed": false, "zoned": false, "supported_io_types": { "read": true, "write": true, "unmap": true, "write_zeroes": true, "flush": true, "reset": true, "compare": true, "compare_and_write": true, "abort": true, "nvme_admin": true, "nvme_io": true }, "driver_specific": { "nvme": [ { "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.1.40", "trsvcid": "4420", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.1" }, "ctrlr_data": { "cntlid": 3, "vendor_id": "0x0000", "model_number": "SANBlaze VLUN P3T1", "serial_number": "01-6ddce86d6ddce86d", "firmware_revision": "V10.5", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.1", "oacs": { "security": 0, "format": 1, "firmware": 0, "ns_manage": 1 }, "multi_ctrlr": true, "ana_reporting": true }, "vs": { "nvme_version": "2.0" }, "ns_data": { "id": 1, "ana_state": "optimized", "can_share": true } } ], "mp_policy": "active_passive" } }, { "name": "cdc_auto01n2", "aliases": [ "600110d6-dce8-dce8-0403-00010b2d3d8d" ], "product_name": "NVMe disk", "block_size": 512, "num_blocks": 32768, "uuid": "600110d6-dce8-dce8-0403-00010b2d3d8d", "assigned_rate_limits": { "rw_ios_per_sec": 0, "rw_mbytes_per_sec": 0, "r_mbytes_per_sec": 0, "w_mbytes_per_sec": 0 }, "claimed": false, "zoned": false, "supported_io_types": { "read": true, "write": true, "unmap": true, "write_zeroes": true, "flush": true, "reset": true, "compare": true, "compare_and_write": true, "abort": true, "nvme_admin": true, "nvme_io": true }, "driver_specific": { "nvme": [ { "trid": { "trtype": "TCP", "adrfam": "IPv4", "traddr": "66.1.1.40", "trsvcid": "4420", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.1" }, "ctrlr_data": { "cntlid": 3, "vendor_id": "0x0000", "model_number": "SANBlaze VLUN P3T1", "serial_number": "01-6ddce86d6ddce86d", "firmware_revision": "V10.5", "subnqn": "nqn.2014-08.com.sanblaze:virtualun.virtualun.3.1", "oacs": { "security": 0, "format": 1, "firmware": 0, "ns_manage": 1 }, "multi_ctrlr": true, "ana_reporting": true }, "vs": { "nvme_version": "2.0" }, "ns_data": { "id": 2, "ana_state": "optimized", "can_share": true } } ], "mp_policy": "active_passive" } } ] root@ubuntu-pm-18-226:~/param-spdk/spdk# root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_stop_mdns_discovery -b cdc_auto root@ubuntu-pm-18-226:~/param-spdk/spdk# root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_get_mdns_discovery_info [] root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_nvme_get_discovery_info [] root@ubuntu-pm-18-226:~/param-spdk/spdk# scripts/rpc.py bdev_get_bdevs [] root@ubuntu-pm-18-226:~/param-spdk/spdk# Signed-off-by: Parameswaran Krishnamurthy <parameswaran.krishna@dell.com> Change-Id: Ic2c2e614e2549a655c7f81ae844b80d8505a4f02 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15703 Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com> Reviewed-by: Shuhei Matsumoto <smatsumoto@nvidia.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Paul Luse <paul.e.luse@intel.com> Reviewed-by: Boris Glimcher <Boris.Glimcher@emc.com> Reviewed-by: <qun.wan@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
parent
b3785e87c5
commit
2796687d54
@ -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.
|
||||
|
4
CONFIG
4
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
|
||||
|
19
configure
vendored
19
configure
vendored
@ -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 <avahi-client/client.h>\n#include <avahi-common/malloc.h>\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)
|
||||
|
140
doc/jsonrpc.md
140
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
~~~
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
655
module/bdev/nvme/bdev_mdns_client.c
Normal file
655
module/bdev/nvme/bdev_mdns_client.c
Normal file
@ -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 <avahi-client/client.h>
|
||||
#include <avahi-client/lookup.h>
|
||||
#include <avahi-common/simple-watch.h>
|
||||
#include <avahi-common/malloc.h>
|
||||
#include <avahi-common/error.h>
|
||||
|
||||
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
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user