diff --git a/app/nvmf_tgt/Makefile b/app/nvmf_tgt/Makefile index 9cc122f91..675e33598 100644 --- a/app/nvmf_tgt/Makefile +++ b/app/nvmf_tgt/Makefile @@ -43,7 +43,7 @@ CFLAGS += $(DPDK_INC) # TODO: remove this once NVMf has a public API header CFLAGS += -I$(SPDK_ROOT_DIR)/lib -C_SRCS := conf.c nvmf_tgt.c +C_SRCS := conf.c nvmf_tgt.c nvmf_rpc.c SPDK_LIBS = \ $(SPDK_ROOT_DIR)/lib/nvmf/libspdk_nvmf.a \ diff --git a/app/nvmf_tgt/conf.c b/app/nvmf_tgt/conf.c index f71119112..8a4797d22 100644 --- a/app/nvmf_tgt/conf.c +++ b/app/nvmf_tgt/conf.c @@ -310,7 +310,7 @@ attach_cb(void *cb_ctx, struct spdk_pci_device *dev, struct spdk_nvme_ctrlr *ctr SPDK_NOTICELOG("Attaching NVMe device %x:%x:%x.%x to subsystem %s\n", found_domain, found_bus, found_dev, found_func, ctx->subsystem->subnqn); - rc = nvmf_subsystem_add_ctrlr(ctx->subsystem, ctrlr); + rc = nvmf_subsystem_add_ctrlr(ctx->subsystem, ctrlr, dev); if (rc < 0) { SPDK_ERRLOG("Failed to add controller to subsystem\n"); } @@ -566,3 +566,156 @@ spdk_nvmf_parse_conf(void) return 0; } + +int +spdk_nvmf_parse_subsystem_for_rpc(const char *name, + const char *mode, uint32_t lcore, + int num_listen_addresses, struct rpc_listen_address *addresses, + int num_hosts, char *hosts[], const char *bdf, + const char *sn, int num_devs, char *dev_list[]) +{ + struct spdk_nvmf_subsystem *subsystem; + struct nvmf_tgt_subsystem *app_subsys; + int i, ret; + uint64_t mask; + int num = 0; + + if (name == NULL) { + SPDK_ERRLOG("No NQN specified for Subsystem %d\n", num); + return -1; + } + + if (num_listen_addresses > MAX_LISTEN_ADDRESSES) { + SPDK_ERRLOG("invalid listen adresses number\n"); + return -1; + } + + if (num_hosts > MAX_HOSTS) { + SPDK_ERRLOG("invalid hosts number\n"); + return -1; + } + + app_subsys = nvmf_tgt_subsystem_first(); + while (app_subsys) { + if (num < app_subsys->subsystem->num) { + num = app_subsys->subsystem->num + 1; + } + app_subsys = nvmf_tgt_subsystem_next(app_subsys); + } + + /* Determine which core to assign to the subsystem */ + mask = spdk_app_get_core_mask(); + lcore = spdk_nvmf_allocate_lcore(mask, lcore); + + app_subsys = nvmf_tgt_create_subsystem(num, name, SPDK_NVMF_SUBTYPE_NVME, lcore); + if (app_subsys == NULL) { + SPDK_ERRLOG("Subsystem creation failed\n"); + return -1; + } + subsystem = app_subsys->subsystem; + + if (mode == NULL) { + SPDK_ERRLOG("No Mode specified for Subsystem %d\n", num); + return -1; + } + + if (strcasecmp(mode, "Direct") == 0) { + subsystem->mode = NVMF_SUBSYSTEM_MODE_DIRECT; + } else if (strcasecmp(mode, "Virtual") == 0) { + subsystem->mode = NVMF_SUBSYSTEM_MODE_VIRTUAL; + } else { + SPDK_ERRLOG("Invalid Subsystem mode: %s\n", mode); + return -1; + } + + /* Parse Listen sections */ + for (i = 0; i < num_listen_addresses; i++) { + const struct spdk_nvmf_transport *transport; + + transport = spdk_nvmf_transport_get(addresses[i].transport); + if (transport == NULL) { + SPDK_ERRLOG("Unknown transport type '%s'\n", addresses[i].transport); + return -1; + } + + spdk_nvmf_subsystem_add_listener(subsystem, transport, addresses[i].traddr, addresses[i].trsvcid); + } + + /* Parse Host sections */ + for (i = 0; i < num_hosts; i++) { + char *host_nqn; + + host_nqn = hosts[i]; + if (strcmp(host_nqn, "All") == 0) + break; + spdk_nvmf_subsystem_add_host(subsystem, host_nqn); + } + + if (subsystem->mode == NVMF_SUBSYSTEM_MODE_DIRECT) { + struct spdk_nvmf_probe_ctx ctx = { 0 }; + + if (bdf == NULL) { + SPDK_ERRLOG("Subsystem %d: missing NVMe directive\n", num); + return -1; + } + + ctx.subsystem = subsystem; + ctx.found = false; + if (strcmp(bdf, "*") == 0) { + ctx.any = true; + } else { + ret = sscanf(bdf, "%x:%x:%x.%x", &ctx.domain, &ctx.bus, &ctx.device, &ctx.function); + if (ret != 4) { + SPDK_ERRLOG("Invalid format for NVMe BDF: %s\n", bdf); + return -1; + } + ctx.any = false; + } + + if (spdk_nvme_probe(&ctx, probe_cb, attach_cb, NULL)) { + SPDK_ERRLOG("One or more controllers failed in spdk_nvme_probe()\n"); + } + } else { + struct spdk_bdev *bdev; + const char *namespace; + + if (sn == NULL) { + SPDK_ERRLOG("Subsystem %d: missing serial number\n", num); + return -1; + } + if (spdk_nvmf_validate_sn(sn) != 0) { + return -1; + } + + if (num_devs > MAX_VIRTUAL_NAMESPACE) { + return -1; + } + + subsystem->dev.virtual.ns_count = 0; + snprintf(subsystem->dev.virtual.sn, MAX_SN_LEN, "%s", sn); + subsystem->ops = &spdk_nvmf_virtual_ctrlr_ops; + + for (i = 0; i < num_devs; i++) { + namespace = dev_list[i]; + if (!namespace) { + SPDK_ERRLOG("Namespace %d: missing block device\n", i); + return -1; + } + bdev = spdk_bdev_get_by_name(namespace); + if (spdk_nvmf_subsystem_add_ns(subsystem, bdev)) { + return -1; + } + + SPDK_NOTICELOG("Attaching block device %s to subsystem %s\n", + bdev->name, subsystem->subnqn); + + } + } + + ret = spdk_nvmf_acceptor_init(); + if (ret < 0) { + SPDK_ERRLOG("spdk_nvmf_acceptor_start() failed\n"); + return -1; + } + return 0; +} diff --git a/app/nvmf_tgt/nvmf_rpc.c b/app/nvmf_tgt/nvmf_rpc.c new file mode 100644 index 000000000..f5ea2931d --- /dev/null +++ b/app/nvmf_tgt/nvmf_rpc.c @@ -0,0 +1,390 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "nvmf_tgt.h" +#include "nvmf/subsystem.h" +#include "nvmf/transport.h" +#include "spdk/log.h" +#include "spdk/rpc.h" +#include "spdk/pci.h" +#include "spdk/nvme.h" + +static void +dump_nvmf_subsystem(struct spdk_json_write_ctx *w, struct spdk_nvmf_subsystem *subsystem) +{ + struct spdk_nvmf_listen_addr *listen_addr; + struct spdk_nvmf_host *host; + + spdk_json_write_object_begin(w); + + spdk_json_write_name(w, "core"); + spdk_json_write_int32(w, subsystem->lcore); + + spdk_json_write_name(w, "nqn"); + spdk_json_write_string(w, subsystem->subnqn); + if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) { + spdk_json_write_name(w, "mode"); + if (subsystem->mode == NVMF_SUBSYSTEM_MODE_DIRECT) { + spdk_json_write_string(w, "direct"); + } else { + spdk_json_write_string(w, "virtual"); + } + } + spdk_json_write_name(w, "subtype"); + if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) { + spdk_json_write_string(w, "NVMe"); + } else { + spdk_json_write_string(w, "Discovery"); + } + + spdk_json_write_name(w, "listen_addrs"); + spdk_json_write_array_begin(w); + + TAILQ_FOREACH(listen_addr, &subsystem->listen_addrs, link) { + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "transport"); + spdk_json_write_string(w, listen_addr->transport->name); + spdk_json_write_name(w, "traddr"); + spdk_json_write_string(w, listen_addr->traddr); + spdk_json_write_name(w, "trsvcid"); + spdk_json_write_string(w, listen_addr->trsvcid); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + spdk_json_write_name(w, "hosts"); + spdk_json_write_array_begin(w); + + TAILQ_FOREACH(host, &subsystem->hosts, link) { + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "nqn"); + spdk_json_write_string(w, host->nqn); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) { + if (subsystem->mode == NVMF_SUBSYSTEM_MODE_DIRECT) { + struct spdk_pci_device *dev = subsystem->dev.direct.pci_dev; + uint16_t found_domain = spdk_pci_device_get_domain(dev); + uint8_t found_bus = spdk_pci_device_get_bus(dev); + uint8_t found_dev = spdk_pci_device_get_dev(dev); + uint8_t found_func = spdk_pci_device_get_func(dev); + spdk_json_write_name(w, "pci_address"); + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "domain"); + spdk_json_write_int32(w, found_domain); + spdk_json_write_name(w, "bus"); + spdk_json_write_int32(w, found_bus); + spdk_json_write_name(w, "device"); + spdk_json_write_int32(w, found_dev); + spdk_json_write_name(w, "function"); + spdk_json_write_int32(w, found_func); + spdk_json_write_object_end(w); + } else { + int i; + spdk_json_write_name(w, "serial_number"); + spdk_json_write_string(w, subsystem->dev.virtual.sn); + spdk_json_write_name(w, "namespaces"); + spdk_json_write_array_begin(w); + for (i = 0; i < subsystem->dev.virtual.ns_count; i++) { + spdk_json_write_object_begin(w); + spdk_json_write_name(w, "nsid"); + spdk_json_write_int32(w, i + 1); + spdk_json_write_name(w, "name"); + spdk_json_write_string(w, subsystem->dev.virtual.ns_list[i]->name); + spdk_json_write_object_end(w); + } + spdk_json_write_array_end(w); + + } + } + spdk_json_write_object_end(w); +} + +static void +spdk_rpc_get_nvmf_subsystems(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct spdk_json_write_ctx *w; + struct nvmf_tgt_subsystem *tgt_subsystem; + + if (params != NULL) { + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "get_nvmf_subsystems requires no parameters"); + return; + } + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_array_begin(w); + tgt_subsystem = nvmf_tgt_subsystem_first(); + while (tgt_subsystem) { + dump_nvmf_subsystem(w, tgt_subsystem->subsystem); + tgt_subsystem = nvmf_tgt_subsystem_next(tgt_subsystem); + } + spdk_json_write_array_end(w); + spdk_jsonrpc_end_result(conn, w); +} +SPDK_RPC_REGISTER("get_nvmf_subsystems", spdk_rpc_get_nvmf_subsystems) + +#define RPC_MAX_LISTEN_ADDRESSES 255 +#define RPC_MAX_HOSTS 255 + +struct rpc_listen_addresses { + size_t num_listen_address; + struct rpc_listen_address addresses[RPC_MAX_LISTEN_ADDRESSES]; +}; + +static const struct spdk_json_object_decoder rpc_listen_address_decoders[] = { + {"transport", offsetof(struct rpc_listen_address, transport), spdk_json_decode_string}, + {"traddr", offsetof(struct rpc_listen_address, traddr), spdk_json_decode_string}, + {"trsvcid", offsetof(struct rpc_listen_address, trsvcid), spdk_json_decode_string}, +}; + +static int +decode_rpc_listen_address(const struct spdk_json_val *val, void *out) +{ + struct rpc_listen_address *req = (struct rpc_listen_address *)out; + if (spdk_json_decode_object(val, rpc_listen_address_decoders, + sizeof(rpc_listen_address_decoders) / sizeof(*rpc_listen_address_decoders), + req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + return -1; + } + return 0; +} + +static int +decode_rpc_listen_addresses(const struct spdk_json_val *val, void *out) +{ + struct rpc_listen_addresses *listen_addresses = out; + return spdk_json_decode_array(val, decode_rpc_listen_address, &listen_addresses->addresses, + RPC_MAX_LISTEN_ADDRESSES, + &listen_addresses->num_listen_address, sizeof(struct rpc_listen_address)); +} + +struct rpc_hosts { + size_t num_hosts; + char *hosts[RPC_MAX_HOSTS]; +}; + +static int +decode_rpc_hosts(const struct spdk_json_val *val, void *out) +{ + struct rpc_hosts *rpc_hosts = out; + + return spdk_json_decode_array(val, spdk_json_decode_string, rpc_hosts->hosts, RPC_MAX_HOSTS, + &rpc_hosts->num_hosts, sizeof(char *)); +} + + + +struct rpc_dev_names { + size_t num_names; + char *names[MAX_VIRTUAL_NAMESPACE]; +}; + +static int +decode_rpc_dev_names(const struct spdk_json_val *val, void *out) +{ + struct rpc_dev_names *dev_names = out; + + return spdk_json_decode_array(val, spdk_json_decode_string, dev_names->names, + MAX_VIRTUAL_NAMESPACE, + &dev_names->num_names, sizeof(char *)); +} + +static void +free_rpc_dev_names(struct rpc_dev_names *r) +{ + size_t i; + + for (i = 0; i < r->num_names; i++) { + free(r->names[i]); + } +} + +static void +free_rpc_listen_addresses(struct rpc_listen_addresses *r) +{ + size_t i; + + for (i = 0; i < r->num_listen_address; i++) { + free(r->addresses[i].transport); + free(r->addresses[i].traddr); + free(r->addresses[i].trsvcid); + } +} + +static void +free_rpc_hosts(struct rpc_hosts *r) +{ + size_t i; + + for (i = 0; i < r->num_hosts; i++) { + free(r->hosts[i]); + } +} + +struct rpc_subsystem { + int32_t core; + char *mode; + char *nqn; + struct rpc_listen_addresses listen_addresses; + struct rpc_hosts hosts; + char *pci_address; + char *serial_number; + struct rpc_dev_names namespaces; +}; + +static void +free_rpc_subsystem(struct rpc_subsystem *req) +{ + free_rpc_dev_names(&req->namespaces); + free_rpc_listen_addresses(&req->listen_addresses); + free_rpc_hosts(&req->hosts); +} + +static const struct spdk_json_object_decoder rpc_subsystem_decoders[] = { + {"core", offsetof(struct rpc_subsystem, core), spdk_json_decode_int32, true}, + {"mode", offsetof(struct rpc_subsystem, mode), spdk_json_decode_string}, + {"nqn", offsetof(struct rpc_subsystem, nqn), spdk_json_decode_string}, + {"listen_addresses", offsetof(struct rpc_subsystem, listen_addresses), decode_rpc_listen_addresses}, + {"hosts", offsetof(struct rpc_subsystem, hosts), decode_rpc_hosts}, + {"pci_address", offsetof(struct rpc_subsystem, pci_address), spdk_json_decode_string, true}, + {"serial_number", offsetof(struct rpc_subsystem, serial_number), spdk_json_decode_string, true}, + {"namespaces", offsetof(struct rpc_subsystem, namespaces), decode_rpc_dev_names, true}, +}; + +static void +spdk_rpc_construct_nvmf_subsystem(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct rpc_subsystem req = {}; + struct spdk_json_write_ctx *w; + int ret; + + if (spdk_json_decode_object(params, rpc_subsystem_decoders, + sizeof(rpc_subsystem_decoders) / sizeof(*rpc_subsystem_decoders), + &req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + goto invalid; + } + + ret = spdk_nvmf_parse_subsystem_for_rpc(req.nqn, req.mode, req.core, + req.listen_addresses.num_listen_address, + req.listen_addresses.addresses, + req.hosts.num_hosts, req.hosts.hosts, req.pci_address, + req.serial_number, + req.namespaces.num_names, req.namespaces.names); + if (ret) { + goto invalid; + } + + free_rpc_subsystem(&req); + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(conn, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_subsystem(&req); +} +SPDK_RPC_REGISTER("construct_nvmf_subsystem", spdk_rpc_construct_nvmf_subsystem) + +struct rpc_delete_subsystem { + char *nqn; +}; + +static void +free_rpc_delete_subsystem(struct rpc_delete_subsystem *r) +{ + free(r->nqn); +} + +static const struct spdk_json_object_decoder rpc_delete_subsystem_decoders[] = { + {"nqn", offsetof(struct rpc_delete_subsystem, nqn), spdk_json_decode_string}, +}; + +static void +spdk_rpc_delete_nvmf_subsystem(struct spdk_jsonrpc_server_conn *conn, + const struct spdk_json_val *params, + const struct spdk_json_val *id) +{ + struct rpc_delete_subsystem req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_delete_subsystem_decoders, + sizeof(rpc_delete_subsystem_decoders) / sizeof(*rpc_delete_subsystem_decoders), + &req)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "spdk_json_decode_object failed\n"); + goto invalid; + } + + if (req.nqn == NULL) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "missing name param\n"); + goto invalid; + } + + if (nvmf_tgt_shutdown_subsystem_by_nqn(req.nqn)) { + SPDK_TRACELOG(SPDK_TRACE_DEBUG, "shutdown_subsystem failed\n"); + goto invalid; + } + + free_rpc_delete_subsystem(&req); + + if (id == NULL) { + return; + } + + w = spdk_jsonrpc_begin_result(conn, id); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(conn, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(conn, id, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_delete_subsystem(&req); +} +SPDK_RPC_REGISTER("delete_nvmf_subsystem", spdk_rpc_delete_nvmf_subsystem) diff --git a/app/nvmf_tgt/nvmf_tgt.c b/app/nvmf_tgt/nvmf_tgt.c index 0b7b7fabf..0ee94f6a9 100644 --- a/app/nvmf_tgt/nvmf_tgt.c +++ b/app/nvmf_tgt/nvmf_tgt.c @@ -266,6 +266,33 @@ nvmf_tgt_delete_subsystems(void) } } +struct nvmf_tgt_subsystem * +nvmf_tgt_subsystem_first(void) +{ + return TAILQ_FIRST(&g_subsystems); +} + +struct nvmf_tgt_subsystem * +nvmf_tgt_subsystem_next(struct nvmf_tgt_subsystem *subsystem) +{ + return TAILQ_NEXT(subsystem, tailq); +} + +int +nvmf_tgt_shutdown_subsystem_by_nqn(const char *nqn) +{ + struct nvmf_tgt_subsystem *tgt_subsystem, *subsys_tmp; + + TAILQ_FOREACH_SAFE(tgt_subsystem, &g_subsystems, tailq, subsys_tmp) { + if (strcmp(tgt_subsystem->subsystem->subnqn, nqn) == 0) { + TAILQ_REMOVE(&g_subsystems, tgt_subsystem, tailq); + nvmf_tgt_delete_subsystem(tgt_subsystem); + return 0; + } + } + return -1; +} + static void usage(void) { diff --git a/app/nvmf_tgt/nvmf_tgt.h b/app/nvmf_tgt/nvmf_tgt.h index d5ad86daa..ff53b0278 100644 --- a/app/nvmf_tgt/nvmf_tgt.h +++ b/app/nvmf_tgt/nvmf_tgt.h @@ -39,6 +39,12 @@ #include "spdk/nvmf_spec.h" #include "spdk/queue.h" +struct rpc_listen_address { + char *transport; + char *traddr; + char *trsvcid; +}; + struct spdk_nvmf_tgt_conf { uint32_t acceptor_lcore; }; @@ -54,10 +60,26 @@ struct nvmf_tgt_subsystem { extern struct spdk_nvmf_tgt_conf g_spdk_nvmf_tgt_conf; +struct nvmf_tgt_subsystem * +nvmf_tgt_subsystem_first(void); + +struct nvmf_tgt_subsystem * +nvmf_tgt_subsystem_next(struct nvmf_tgt_subsystem *subsystem); + int spdk_nvmf_parse_conf(void); struct nvmf_tgt_subsystem *nvmf_tgt_create_subsystem(int num, const char *name, enum spdk_nvmf_subtype subtype, uint32_t lcore); + +int +spdk_nvmf_parse_subsystem_for_rpc(const char *name, + const char *mode, uint32_t lcore, + int num_listen_addresses, struct rpc_listen_address *addresses, + int num_hosts, char *hosts[], const char *bdf, + const char *sn, int num_devs, char *dev_list[]); + +int +nvmf_tgt_shutdown_subsystem_by_nqn(const char *nqn); #endif diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c index 1b0299eff..419c82907 100644 --- a/lib/nvmf/subsystem.c +++ b/lib/nvmf/subsystem.c @@ -251,9 +251,10 @@ spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, char *host_n int nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, - struct spdk_nvme_ctrlr *ctrlr) + struct spdk_nvme_ctrlr *ctrlr, struct spdk_pci_device *dev) { subsystem->dev.direct.ctrlr = ctrlr; + subsystem->dev.direct.pci_dev = dev; /* Assume that all I/O will be handled on one thread for now */ subsystem->dev.direct.io_qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, 0); if (subsystem->dev.direct.io_qpair == NULL) { diff --git a/lib/nvmf/subsystem.h b/lib/nvmf/subsystem.h index d66e7e943..6dad52a0d 100644 --- a/lib/nvmf/subsystem.h +++ b/lib/nvmf/subsystem.h @@ -112,6 +112,7 @@ struct spdk_nvmf_subsystem { struct { struct spdk_nvme_ctrlr *ctrlr; struct spdk_nvme_qpair *io_qpair; + struct spdk_pci_device *pci_dev; } direct; struct { @@ -160,7 +161,7 @@ spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, int nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, - struct spdk_nvme_ctrlr *ctrlr); + struct spdk_nvme_ctrlr *ctrlr, struct spdk_pci_device *dev); void spdk_format_discovery_log(struct spdk_nvmf_discovery_log_page *disc_log, uint32_t length); diff --git a/scripts/rpc.py b/scripts/rpc.py index 5c63e3113..fe75700e1 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -302,6 +302,65 @@ def get_interfaces(args): p = subparsers.add_parser('get_interfaces', help='Display current interface list') p.set_defaults(func=get_interfaces) +def get_nvmf_subsystems(args): + print_dict(jsonrpc_call('get_nvmf_subsystems')) + +p = subparsers.add_parser('get_nvmf_subsystems', help='Display nvmf subsystems') +p.set_defaults(func=get_nvmf_subsystems) + +def construct_nvmf_subsystem(args): + namespaces = [] + hosts = [] + + listen_addresses = [dict(u.split(":") for u in a.split(" ")) for a in args.listen.split(",")] + + for u in args.hosts.split(" "): + hosts.append(u) + + for u in args.namespaces.split(" "): + namespaces.append(u) + + params = { + 'core': args.core, + 'mode': args.mode, + 'nqn': args.nqn, + 'listen_addresses': listen_addresses, + 'hosts': hosts, + 'pci_address': args.pci_address, + 'serial_number': args.serial_number, + 'namespaces': namespaces, + } + jsonrpc_call('construct_nvmf_subsystem', params) + +p = subparsers.add_parser('construct_nvmf_subsystem', help='Add a nvmf subsystem') +p.add_argument("-c", "--core", help='The core Nvmf target run on', type=int, default=0) +p.add_argument('mode', help='Target mode: Virtual or Direct') +p.add_argument('nqn', help='Target nqn(ASCII)') +p.add_argument('listen', help="""comma-separated list of Listen pairs enclosed +in quotes. Format: 'transport:transport0 traddr:traddr0 trsvcid:trsvcid0,transport:transport1 traddr:traddr1 trsvcid:trsvcid1' etc +Example: 'transport:RDMA traddr:192.168.100.8 trsvcid:4420,transport:RDMA traddr:192.168.100.9 trsvcid:4420'""") +p.add_argument('hosts', help="""Whitespace-separated list of host nqn list. +Format: 'nqn1 nqn2' etc +Example: 'nqn.2016-06.io.spdk:init nqn.2016-07.io.spdk:init'""") +p.add_argument("-p", "--pci_address", help="""Valid if mode == Direct. +Format: 'domain:device:function' etc +Example: '0000:00:01.0'""", default='0000:00:01.0') +p.add_argument("-s", "--serial_number", help="""Valid if mode == Virtual. +Format: 'sn' etc +Example: 'SPDK00000000000001'""", default='0000:00:01.0') +p.add_argument("-n", "--namespaces", help="""Whitespace-separated list of namespaces. +Format: 'dev1 dev2 dev3' etc +Example: 'Malloc0 Malloc1 Malloc2' +*** The devices must pre-exist ***""", default='Malloc0') +p.set_defaults(func=construct_nvmf_subsystem) + +def delete_nvmf_subsystem(args): + params = {'nqn': args.subsystem_nqn} + jsonrpc_call('delete_nvmf_subsystem', params) + +p = subparsers.add_parser('delete_nvmf_subsystem', help='Delete a nvmf subsystem') +p.add_argument('subsystem_nqn', help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.') +p.set_defaults(func=delete_nvmf_subsystem) def kill_instance(args): params = {'sig_name': args.sig_name}