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:
Parameswaran Krishnamurthy 2022-11-30 20:11:23 +00:00 committed by Jim Harris
parent b3785e87c5
commit 2796687d54
13 changed files with 1057 additions and 6 deletions

View File

@ -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
View File

@ -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
View File

@ -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)

View File

@ -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"
}
}
]
}
]
~~~

View File

@ -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

View File

@ -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)

View 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

View File

@ -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.
*/

View File

@ -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);
/**

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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)