diff --git a/include/spdk/nvmf.h b/include/spdk/nvmf.h index 86ca574f6..91ae484b0 100644 --- a/include/spdk/nvmf.h +++ b/include/spdk/nvmf.h @@ -117,7 +117,9 @@ struct spdk_nvmf_transport_poll_group_stat { }; /** - * Function to be called once the listener is associated with a subsystem. + * Function to be called once asynchronous listen add and remove + * operations are completed. See spdk_nvmf_subsystem_add_listener() + * and spdk_nvmf_transport_stop_listen_async(). * * \param ctx Context argument passed to this function. * \param status 0 if it completed successfully, or negative errno if it failed. @@ -994,6 +996,26 @@ int spdk_nvmf_transport_stop_listen(struct spdk_nvmf_transport *transport, const struct spdk_nvme_transport_id *trid); +/** + * Stop accepting new connections at the provided address. + * + * This is a counterpart to spdk_nvmf_tgt_listen(). It differs + * from spdk_nvmf_transport_stop_listen() in that it also destroys all + * qpairs that are connected to the specified listener. Because + * this function disconnects the qpairs, it has to be asynchronous. + * + * \param transport The transport associated with the listen address. + * \param trid The address to stop listening at. + * \param cb_fn The function to call on completion. + * \param cb_arg The argument to pass to the cb_fn. + * + * \return int. 0 when the asynchronous process starts successfully or a negated errno on failure. + */ +int spdk_nvmf_transport_stop_listen_async(struct spdk_nvmf_transport *transport, + const struct spdk_nvme_transport_id *trid, + spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn, + void *cb_arg); + /** * \brief Get current transport poll group statistics. * diff --git a/lib/nvmf/Makefile b/lib/nvmf/Makefile index b4556564a..410fab753 100644 --- a/lib/nvmf/Makefile +++ b/lib/nvmf/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk SO_VER := 5 -SO_MINOR := 0 +SO_MINOR := 1 C_SRCS = ctrlr.c ctrlr_discovery.c ctrlr_bdev.c \ subsystem.c nvmf.c nvmf_rpc.c transport.c tcp.c diff --git a/lib/nvmf/spdk_nvmf.map b/lib/nvmf/spdk_nvmf.map index 994e7437b..23af415cf 100644 --- a/lib/nvmf/spdk_nvmf.map +++ b/lib/nvmf/spdk_nvmf.map @@ -75,6 +75,7 @@ spdk_nvmf_tgt_add_transport; spdk_nvmf_transport_listen; spdk_nvmf_transport_stop_listen; + spdk_nvmf_transport_stop_listen_async; spdk_nvmf_transport_poll_group_get_stat; spdk_nvmf_transport_poll_group_free_stat; spdk_nvmf_rdma_init_hooks; diff --git a/lib/nvmf/transport.c b/lib/nvmf/transport.c index 11bb152df..323af72ff 100644 --- a/lib/nvmf/transport.c +++ b/lib/nvmf/transport.c @@ -254,6 +254,85 @@ spdk_nvmf_transport_stop_listen(struct spdk_nvmf_transport *transport, return 0; } +struct nvmf_stop_listen_ctx { + struct spdk_nvmf_transport *transport; + struct spdk_nvme_transport_id trid; + spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn; + void *cb_arg; +}; + +static void +nvmf_stop_listen_fini(struct spdk_io_channel_iter *i, int status) +{ + struct nvmf_stop_listen_ctx *ctx; + struct spdk_nvmf_transport *transport; + int rc = status; + + ctx = spdk_io_channel_iter_get_ctx(i); + transport = ctx->transport; + assert(transport != NULL); + + rc = spdk_nvmf_transport_stop_listen(transport, &ctx->trid); + if (rc) { + SPDK_ERRLOG("Failed to stop listening on address '%s'\n", ctx->trid.traddr); + } + + if (ctx->cb_fn) { + ctx->cb_fn(ctx->cb_arg, rc); + } + free(ctx); +} + +static void +nvmf_stop_listen_disconnect_qpairs(struct spdk_io_channel_iter *i) +{ + struct nvmf_stop_listen_ctx *ctx; + struct spdk_nvmf_poll_group *group; + struct spdk_io_channel *ch; + struct spdk_nvmf_qpair *qpair, *tmp_qpair; + struct spdk_nvme_transport_id tmp_trid; + + ctx = spdk_io_channel_iter_get_ctx(i); + ch = spdk_io_channel_iter_get_channel(i); + group = spdk_io_channel_get_ctx(ch); + + TAILQ_FOREACH_SAFE(qpair, &group->qpairs, link, tmp_qpair) { + /* skip qpairs that don't match the TRID. */ + if (spdk_nvmf_qpair_get_listen_trid(qpair, &tmp_trid)) { + continue; + } + + if (!spdk_nvme_transport_id_compare(&ctx->trid, &tmp_trid)) { + spdk_nvmf_qpair_disconnect(qpair, NULL, NULL); + } + } + spdk_for_each_channel_continue(i, 0); +} + +int +spdk_nvmf_transport_stop_listen_async(struct spdk_nvmf_transport *transport, + const struct spdk_nvme_transport_id *trid, + spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn, + void *cb_arg) +{ + struct nvmf_stop_listen_ctx *ctx; + + ctx = calloc(1, sizeof(struct nvmf_stop_listen_ctx)); + if (ctx == NULL) { + return -ENOMEM; + } + + ctx->trid = *trid; + ctx->transport = transport; + ctx->cb_fn = cb_fn; + ctx->cb_arg = cb_arg; + + spdk_for_each_channel(transport->tgt, nvmf_stop_listen_disconnect_qpairs, ctx, + nvmf_stop_listen_fini); + + return 0; +} + uint32_t nvmf_transport_accept(struct spdk_nvmf_transport *transport) {