Spdk/module/event/subsystems/nvmf/nvmf_tgt.c
paul luse a6dbe3721e update Intel copyright notices
per Intel policy to include file commit date using git cmd
below.  The policy does not apply to non-Intel (C) notices.

git log --follow -C90% --format=%ad --date default <file> | tail -1

and then pull just the 4 digit year from the result.

Intel copyrights were not added to files where Intel either had
no contribution ot the contribution lacked substance (ie license
header updates, formatting changes, etc).  Contribution date used
"--follow -C95%" to get the most accurate date.

Note that several files in this patch didn't end the license/(c)
block with a blank comment line so these were added as the vast
majority of files do have this last blank line.  Simply there for
consistency.

Signed-off-by: paul luse <paul.e.luse@intel.com>
Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Community-CI: Mellanox Build Bot
2022-11-10 08:28:53 +00:00

553 lines
14 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
* All rights reserved.
* Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include "event_nvmf.h"
#include "spdk/bdev.h"
#include "spdk/thread.h"
#include "spdk/log.h"
#include "spdk/nvme.h"
#include "spdk/nvmf_cmd.h"
#include "spdk_internal/usdt.h"
enum nvmf_tgt_state {
NVMF_TGT_INIT_NONE = 0,
NVMF_TGT_INIT_CREATE_TARGET,
NVMF_TGT_INIT_CREATE_POLL_GROUPS,
NVMF_TGT_INIT_START_SUBSYSTEMS,
NVMF_TGT_RUNNING,
NVMF_TGT_FINI_STOP_SUBSYSTEMS,
NVMF_TGT_FINI_DESTROY_SUBSYSTEMS,
NVMF_TGT_FINI_DESTROY_POLL_GROUPS,
NVMF_TGT_FINI_DESTROY_TARGET,
NVMF_TGT_STOPPED,
NVMF_TGT_ERROR,
};
struct nvmf_tgt_poll_group {
struct spdk_nvmf_poll_group *group;
struct spdk_thread *thread;
TAILQ_ENTRY(nvmf_tgt_poll_group) link;
};
struct spdk_nvmf_tgt_conf g_spdk_nvmf_tgt_conf = {
.admin_passthru.identify_ctrlr = false
};
struct spdk_cpuset *g_poll_groups_mask = NULL;
struct spdk_nvmf_tgt *g_spdk_nvmf_tgt = NULL;
uint32_t g_spdk_nvmf_tgt_max_subsystems = 0;
uint16_t g_spdk_nvmf_tgt_crdt[3] = {0, 0, 0};
static enum nvmf_tgt_state g_tgt_state;
static struct spdk_thread *g_tgt_init_thread = NULL;
static struct spdk_thread *g_tgt_fini_thread = NULL;
static TAILQ_HEAD(, nvmf_tgt_poll_group) g_poll_groups = TAILQ_HEAD_INITIALIZER(g_poll_groups);
static size_t g_num_poll_groups = 0;
static void nvmf_tgt_advance_state(void);
static void
nvmf_shutdown_cb(void *arg1)
{
/* Still in initialization state, defer shutdown operation */
if (g_tgt_state < NVMF_TGT_RUNNING) {
spdk_thread_send_msg(spdk_get_thread(), nvmf_shutdown_cb, NULL);
return;
} else if (g_tgt_state != NVMF_TGT_RUNNING && g_tgt_state != NVMF_TGT_ERROR) {
/* Already in Shutdown status, ignore the signal */
return;
}
if (g_tgt_state == NVMF_TGT_ERROR) {
/* Parse configuration error */
g_tgt_state = NVMF_TGT_FINI_DESTROY_TARGET;
} else {
g_tgt_state = NVMF_TGT_FINI_STOP_SUBSYSTEMS;
}
nvmf_tgt_advance_state();
}
static void
nvmf_subsystem_fini(void)
{
nvmf_shutdown_cb(NULL);
}
static void
_nvmf_tgt_destroy_poll_group_done(void *ctx)
{
assert(g_num_poll_groups > 0);
if (--g_num_poll_groups == 0) {
g_tgt_state = NVMF_TGT_FINI_DESTROY_TARGET;
nvmf_tgt_advance_state();
}
}
static void
nvmf_tgt_destroy_poll_group_done(void *cb_arg, int status)
{
struct nvmf_tgt_poll_group *pg = cb_arg;
free(pg);
spdk_thread_send_msg(g_tgt_fini_thread, _nvmf_tgt_destroy_poll_group_done, NULL);
spdk_thread_exit(spdk_get_thread());
}
static void
nvmf_tgt_destroy_poll_group(void *ctx)
{
struct nvmf_tgt_poll_group *pg = ctx;
spdk_nvmf_poll_group_destroy(pg->group, nvmf_tgt_destroy_poll_group_done, pg);
}
static void
nvmf_tgt_destroy_poll_groups(void)
{
struct nvmf_tgt_poll_group *pg, *tpg;
g_tgt_fini_thread = spdk_get_thread();
assert(g_tgt_fini_thread != NULL);
TAILQ_FOREACH_SAFE(pg, &g_poll_groups, link, tpg) {
TAILQ_REMOVE(&g_poll_groups, pg, link);
spdk_thread_send_msg(pg->thread, nvmf_tgt_destroy_poll_group, pg);
}
}
static uint32_t
nvmf_get_cpuset_count(void)
{
if (g_poll_groups_mask) {
return spdk_cpuset_count(g_poll_groups_mask);
} else {
return spdk_env_get_core_count();
}
}
static void
nvmf_tgt_create_poll_group_done(void *ctx)
{
struct nvmf_tgt_poll_group *pg = ctx;
assert(pg);
if (!pg->group) {
SPDK_ERRLOG("Failed to create nvmf poll group\n");
/* Change the state to error but wait for completions from all other threads */
g_tgt_state = NVMF_TGT_ERROR;
}
TAILQ_INSERT_TAIL(&g_poll_groups, pg, link);
assert(g_num_poll_groups < nvmf_get_cpuset_count());
if (++g_num_poll_groups == nvmf_get_cpuset_count()) {
if (g_tgt_state != NVMF_TGT_ERROR) {
g_tgt_state = NVMF_TGT_INIT_START_SUBSYSTEMS;
}
nvmf_tgt_advance_state();
}
}
static void
nvmf_tgt_create_poll_group(void *ctx)
{
struct nvmf_tgt_poll_group *pg;
pg = calloc(1, sizeof(*pg));
if (!pg) {
SPDK_ERRLOG("Not enough memory to allocate poll groups\n");
g_tgt_state = NVMF_TGT_ERROR;
nvmf_tgt_advance_state();
return;
}
pg->thread = spdk_get_thread();
pg->group = spdk_nvmf_poll_group_create(g_spdk_nvmf_tgt);
spdk_thread_send_msg(g_tgt_init_thread, nvmf_tgt_create_poll_group_done, pg);
}
static void
nvmf_tgt_create_poll_groups(void)
{
uint32_t cpu, count = 0;
char thread_name[32];
struct spdk_thread *thread;
g_tgt_init_thread = spdk_get_thread();
assert(g_tgt_init_thread != NULL);
SPDK_ENV_FOREACH_CORE(cpu) {
if (g_poll_groups_mask && !spdk_cpuset_get_cpu(g_poll_groups_mask, cpu)) {
continue;
}
snprintf(thread_name, sizeof(thread_name), "nvmf_tgt_poll_group_%u", count++);
thread = spdk_thread_create(thread_name, g_poll_groups_mask);
assert(thread != NULL);
spdk_thread_send_msg(thread, nvmf_tgt_create_poll_group, NULL);
}
}
static void
nvmf_tgt_subsystem_started(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
int rc;
if (subsystem) {
rc = spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL);
if (rc) {
g_tgt_state = NVMF_TGT_FINI_STOP_SUBSYSTEMS;
SPDK_ERRLOG("Unable to start NVMe-oF subsystem. Stopping app.\n");
nvmf_tgt_advance_state();
}
return;
}
g_tgt_state = NVMF_TGT_RUNNING;
nvmf_tgt_advance_state();
}
static void
nvmf_tgt_subsystem_stopped(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
int rc;
if (subsystem) {
rc = spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL);
if (rc) {
SPDK_ERRLOG("Unable to stop NVMe-oF subsystem %s with rc %d, Trying others.\n",
spdk_nvmf_subsystem_get_nqn(subsystem), rc);
nvmf_tgt_subsystem_stopped(subsystem, NULL, 0);
}
return;
}
g_tgt_state = NVMF_TGT_FINI_DESTROY_SUBSYSTEMS;
nvmf_tgt_advance_state();
}
static void
_nvmf_tgt_subsystem_destroy(void *cb_arg)
{
struct spdk_nvmf_subsystem *subsystem, *next_subsystem;
int rc;
subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt);
while (subsystem != NULL) {
next_subsystem = spdk_nvmf_subsystem_get_next(subsystem);
rc = spdk_nvmf_subsystem_destroy(subsystem, _nvmf_tgt_subsystem_destroy, NULL);
if (rc) {
if (rc == -EINPROGRESS) {
/* If ret is -EINPROGRESS, nvmf_tgt_subsystem_destroyed will be called when subsystem
* is destroyed, _nvmf_tgt_subsystem_destroy will continue to destroy other subsystems if any */
return;
} else {
SPDK_ERRLOG("Unable to destroy subsystem %s, rc %d. Trying others.\n",
spdk_nvmf_subsystem_get_nqn(subsystem), rc);
}
}
subsystem = next_subsystem;
}
g_tgt_state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS;
nvmf_tgt_advance_state();
}
static void
nvmf_tgt_destroy_done(void *ctx, int status)
{
g_tgt_state = NVMF_TGT_STOPPED;
nvmf_tgt_advance_state();
}
static int
nvmf_add_discovery_subsystem(void)
{
struct spdk_nvmf_subsystem *subsystem;
subsystem = spdk_nvmf_subsystem_create(g_spdk_nvmf_tgt, SPDK_NVMF_DISCOVERY_NQN,
SPDK_NVMF_SUBTYPE_DISCOVERY, 0);
if (subsystem == NULL) {
SPDK_ERRLOG("Failed creating discovery nvmf library subsystem\n");
return -1;
}
spdk_nvmf_subsystem_set_allow_any_host(subsystem, true);
return 0;
}
static int
nvmf_tgt_create_target(void)
{
struct spdk_nvmf_target_opts opts = {
.name = "nvmf_tgt"
};
opts.max_subsystems = g_spdk_nvmf_tgt_max_subsystems;
opts.crdt[0] = g_spdk_nvmf_tgt_crdt[0];
opts.crdt[1] = g_spdk_nvmf_tgt_crdt[1];
opts.crdt[2] = g_spdk_nvmf_tgt_crdt[2];
opts.discovery_filter = g_spdk_nvmf_tgt_conf.discovery_filter;
g_spdk_nvmf_tgt = spdk_nvmf_tgt_create(&opts);
if (!g_spdk_nvmf_tgt) {
SPDK_ERRLOG("spdk_nvmf_tgt_create() failed\n");
return -1;
}
if (nvmf_add_discovery_subsystem() != 0) {
SPDK_ERRLOG("nvmf_add_discovery_subsystem failed\n");
return -1;
}
return 0;
}
static void
fixup_identify_ctrlr(struct spdk_nvmf_request *req)
{
uint32_t length;
int rc;
struct spdk_nvme_ctrlr_data *nvme_cdata;
struct spdk_nvme_ctrlr_data nvmf_cdata = {};
struct spdk_nvmf_ctrlr *ctrlr = spdk_nvmf_request_get_ctrlr(req);
struct spdk_nvme_cpl *rsp = spdk_nvmf_request_get_response(req);
/* This is the identify data from the NVMe drive */
spdk_nvmf_request_get_data(req, (void **)&nvme_cdata, &length);
/* Get the NVMF identify data */
rc = spdk_nvmf_ctrlr_identify_ctrlr(ctrlr, &nvmf_cdata);
if (rc != SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE) {
rsp->status.sct = SPDK_NVME_SCT_GENERIC;
rsp->status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
return;
}
/* Fixup NVMF identify data with NVMe identify data */
/* Serial Number (SN) */
memcpy(&nvmf_cdata.sn[0], &nvme_cdata->sn[0], sizeof(nvmf_cdata.sn));
/* Model Number (MN) */
memcpy(&nvmf_cdata.mn[0], &nvme_cdata->mn[0], sizeof(nvmf_cdata.mn));
/* Firmware Revision (FR) */
memcpy(&nvmf_cdata.fr[0], &nvme_cdata->fr[0], sizeof(nvmf_cdata.fr));
/* IEEE OUI Identifier (IEEE) */
memcpy(&nvmf_cdata.ieee[0], &nvme_cdata->ieee[0], sizeof(nvmf_cdata.ieee));
/* FRU Globally Unique Identifier (FGUID) */
/* Copy the fixed up data back to the response */
memcpy(nvme_cdata, &nvmf_cdata, length);
}
static int
nvmf_custom_identify_hdlr(struct spdk_nvmf_request *req)
{
struct spdk_nvme_cmd *cmd = spdk_nvmf_request_get_cmd(req);
struct spdk_bdev *bdev;
struct spdk_bdev_desc *desc;
struct spdk_io_channel *ch;
struct spdk_nvmf_subsystem *subsys;
int rc;
if (cmd->cdw10_bits.identify.cns != SPDK_NVME_IDENTIFY_CTRLR) {
return -1; /* continue */
}
subsys = spdk_nvmf_request_get_subsystem(req);
if (subsys == NULL) {
return -1;
}
/* Only procss this request if it has exactly one namespace */
if (spdk_nvmf_subsystem_get_max_nsid(subsys) != 1) {
return -1;
}
/* Forward to first namespace if it supports NVME admin commands */
rc = spdk_nvmf_request_get_bdev(1, req, &bdev, &desc, &ch);
if (rc) {
/* No bdev found for this namespace. Continue. */
return -1;
}
if (!spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_ADMIN)) {
return -1;
}
return spdk_nvmf_bdev_ctrlr_nvme_passthru_admin(bdev, desc, ch, req, fixup_identify_ctrlr);
}
static void
nvmf_tgt_advance_state(void)
{
enum nvmf_tgt_state prev_state;
int rc = -1;
int ret;
do {
SPDK_DTRACE_PROBE1(nvmf_tgt_state, g_tgt_state);
prev_state = g_tgt_state;
switch (g_tgt_state) {
case NVMF_TGT_INIT_NONE: {
g_tgt_state = NVMF_TGT_INIT_CREATE_TARGET;
break;
}
case NVMF_TGT_INIT_CREATE_TARGET:
ret = nvmf_tgt_create_target();
g_tgt_state = (ret == 0) ? NVMF_TGT_INIT_CREATE_POLL_GROUPS : NVMF_TGT_ERROR;
break;
case NVMF_TGT_INIT_CREATE_POLL_GROUPS:
if (g_spdk_nvmf_tgt_conf.admin_passthru.identify_ctrlr) {
SPDK_NOTICELOG("Custom identify ctrlr handler enabled\n");
spdk_nvmf_set_custom_admin_cmd_hdlr(SPDK_NVME_OPC_IDENTIFY, nvmf_custom_identify_hdlr);
}
/* Create poll group threads, and send a message to each thread
* and create a poll group.
*/
nvmf_tgt_create_poll_groups();
break;
case NVMF_TGT_INIT_START_SUBSYSTEMS: {
struct spdk_nvmf_subsystem *subsystem;
subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt);
if (subsystem) {
ret = spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL);
if (ret) {
SPDK_ERRLOG("Unable to start NVMe-oF subsystem. Stopping app.\n");
g_tgt_state = NVMF_TGT_FINI_STOP_SUBSYSTEMS;
}
} else {
g_tgt_state = NVMF_TGT_RUNNING;
}
break;
}
case NVMF_TGT_RUNNING:
spdk_subsystem_init_next(0);
break;
case NVMF_TGT_FINI_STOP_SUBSYSTEMS: {
struct spdk_nvmf_subsystem *subsystem;
subsystem = spdk_nvmf_subsystem_get_first(g_spdk_nvmf_tgt);
if (subsystem) {
ret = spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL);
if (ret) {
nvmf_tgt_subsystem_stopped(subsystem, NULL, 0);
}
} else {
g_tgt_state = NVMF_TGT_FINI_DESTROY_SUBSYSTEMS;
}
break;
}
case NVMF_TGT_FINI_DESTROY_SUBSYSTEMS:
_nvmf_tgt_subsystem_destroy(NULL);
/* Function above can be asynchronous, it will call nvmf_tgt_advance_state() once done.
* So just return here */
return;
case NVMF_TGT_FINI_DESTROY_POLL_GROUPS:
/* Send a message to each poll group thread, and terminate the thread */
nvmf_tgt_destroy_poll_groups();
break;
case NVMF_TGT_FINI_DESTROY_TARGET:
spdk_nvmf_tgt_destroy(g_spdk_nvmf_tgt, nvmf_tgt_destroy_done, NULL);
break;
case NVMF_TGT_STOPPED:
spdk_subsystem_fini_next();
return;
case NVMF_TGT_ERROR:
spdk_subsystem_init_next(rc);
return;
}
} while (g_tgt_state != prev_state);
}
static void
nvmf_subsystem_init(void)
{
g_tgt_state = NVMF_TGT_INIT_NONE;
nvmf_tgt_advance_state();
}
static void
nvmf_subsystem_dump_discover_filter(struct spdk_json_write_ctx *w)
{
static char const *const answers[] = {
"match_any",
"transport",
"address",
"transport,address",
"svcid",
"transport,svcid",
"address,svcid",
"transport,address,svcid"
};
if ((g_spdk_nvmf_tgt_conf.discovery_filter & ~(SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_TYPE |
SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_ADDRESS |
SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_SVCID)) != 0) {
SPDK_ERRLOG("Incorrect discovery filter %d\n", g_spdk_nvmf_tgt_conf.discovery_filter);
assert(0);
return;
}
spdk_json_write_named_string(w, "discovery_filter", answers[g_spdk_nvmf_tgt_conf.discovery_filter]);
}
static void
nvmf_subsystem_write_config_json(struct spdk_json_write_ctx *w)
{
spdk_json_write_array_begin(w);
spdk_json_write_object_begin(w);
spdk_json_write_named_string(w, "method", "nvmf_set_config");
spdk_json_write_named_object_begin(w, "params");
nvmf_subsystem_dump_discover_filter(w);
spdk_json_write_named_object_begin(w, "admin_cmd_passthru");
spdk_json_write_named_bool(w, "identify_ctrlr",
g_spdk_nvmf_tgt_conf.admin_passthru.identify_ctrlr);
spdk_json_write_object_end(w);
if (g_poll_groups_mask) {
spdk_json_write_named_string(w, "poll_groups_mask", spdk_cpuset_fmt(g_poll_groups_mask));
}
spdk_json_write_object_end(w);
spdk_json_write_object_end(w);
spdk_nvmf_tgt_write_config_json(w, g_spdk_nvmf_tgt);
spdk_json_write_array_end(w);
}
static struct spdk_subsystem g_spdk_subsystem_nvmf = {
.name = "nvmf",
.init = nvmf_subsystem_init,
.fini = nvmf_subsystem_fini,
.write_config_json = nvmf_subsystem_write_config_json,
};
SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_nvmf)
SPDK_SUBSYSTEM_DEPEND(nvmf, bdev)
SPDK_SUBSYSTEM_DEPEND(nvmf, sock)