Spdk/examples/nvmf/nvmf/nvmf.c
Jim Harris 488570ebd4 Replace most BSD 3-clause license text with SPDX identifier.
Many open source projects have moved to using SPDX identifiers
to specify license information, reducing the amount of
boilerplate code in every source file.  This patch replaces
the bulk of SPDK .c, .cpp and Makefiles with the BSD-3-Clause
identifier.

Almost all of these files share the exact same license text,
and this patch only modifies the files that contain the
most common license text.  There can be slight variations
because the third clause contains company names - most say
"Intel Corporation", but there are instances for Nvidia,
Samsung, Eideticom and even "the copyright holder".

Used a bash script to automate replacement of the license text
with SPDX identifier which is checked into scripts/spdx.sh.

Signed-off-by: Jim Harris <james.r.harris@intel.com>
Change-Id: Iaa88ab5e92ea471691dc298cfe41ebfb5d169780
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12904
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Dong Yi <dongx.yi@intel.com>
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: <qun.wan@intel.com>
2022-06-09 07:35:12 +00:00

881 lines
22 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "spdk/stdinc.h"
#include "spdk/env.h"
#include "spdk/event.h"
#include "spdk/init.h"
#include "spdk/string.h"
#include "spdk/thread.h"
#include "spdk/bdev.h"
#include "spdk/rpc.h"
#include "spdk/nvmf.h"
#include "spdk/likely.h"
#include "spdk_internal/event.h"
#define NVMF_DEFAULT_SUBSYSTEMS 32
static const char *g_rpc_addr = SPDK_DEFAULT_RPC_ADDR;
enum nvmf_target_state {
NVMF_INIT_SUBSYSTEM = 0,
NVMF_INIT_TARGET,
NVMF_INIT_POLL_GROUPS,
NVMF_INIT_START_SUBSYSTEMS,
NVMF_RUNNING,
NVMF_FINI_STOP_SUBSYSTEMS,
NVMF_FINI_POLL_GROUPS,
NVMF_FINI_TARGET,
NVMF_FINI_SUBSYSTEM,
};
struct nvmf_lw_thread {
TAILQ_ENTRY(nvmf_lw_thread) link;
bool resched;
};
struct nvmf_reactor {
uint32_t core;
struct spdk_ring *threads;
TAILQ_ENTRY(nvmf_reactor) link;
};
struct nvmf_target_poll_group {
struct spdk_nvmf_poll_group *group;
struct spdk_thread *thread;
TAILQ_ENTRY(nvmf_target_poll_group) link;
};
struct nvmf_target {
struct spdk_nvmf_tgt *tgt;
int max_subsystems;
};
TAILQ_HEAD(, nvmf_reactor) g_reactors = TAILQ_HEAD_INITIALIZER(g_reactors);
TAILQ_HEAD(, nvmf_target_poll_group) g_poll_groups = TAILQ_HEAD_INITIALIZER(g_poll_groups);
static uint32_t g_num_poll_groups = 0;
static struct nvmf_reactor *g_main_reactor = NULL;
static struct nvmf_reactor *g_next_reactor = NULL;
static struct spdk_thread *g_init_thread = NULL;
static struct spdk_thread *g_fini_thread = NULL;
static struct nvmf_target g_nvmf_tgt = {
.max_subsystems = NVMF_DEFAULT_SUBSYSTEMS,
};
static struct nvmf_target_poll_group *g_next_pg = NULL;
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool g_reactors_exit = false;
static enum nvmf_target_state g_target_state;
static bool g_intr_received = false;
static uint32_t g_migrate_pg_period_us = 0;
static struct spdk_poller *g_migrate_pg_poller = NULL;
static void nvmf_target_advance_state(void);
static int nvmf_schedule_spdk_thread(struct spdk_thread *thread);
static void
usage(char *program_name)
{
printf("%s options", program_name);
printf("\n");
printf("\t[-g period of round robin poll group migration (us) (default: 0 (disabled))]\n");
printf("\t[-h show this usage]\n");
printf("\t[-i shared memory ID (optional)]\n");
printf("\t[-m core mask for DPDK]\n");
printf("\t[-n max subsystems for target(default: 32)]\n");
printf("\t[-r RPC listen address (default /var/tmp/spdk.sock)]\n");
printf("\t[-s memory size in MB for DPDK (default: 0MB)]\n");
printf("\t[-u disable PCI access]\n");
}
static int
parse_args(int argc, char **argv, struct spdk_env_opts *opts)
{
int op;
long int value;
while ((op = getopt(argc, argv, "g:i:m:n:p:r:s:u:h")) != -1) {
switch (op) {
case 'g':
value = spdk_strtol(optarg, 10);
if (value < 0) {
fprintf(stderr, "converting a string to integer failed\n");
return -EINVAL;
}
g_migrate_pg_period_us = value;
break;
case 'i':
value = spdk_strtol(optarg, 10);
if (value < 0) {
fprintf(stderr, "converting a string to integer failed\n");
return -EINVAL;
}
opts->shm_id = value;
break;
case 'm':
opts->core_mask = optarg;
break;
case 'n':
g_nvmf_tgt.max_subsystems = spdk_strtol(optarg, 10);
if (g_nvmf_tgt.max_subsystems < 0) {
fprintf(stderr, "converting a string to integer failed\n");
return -EINVAL;
}
break;
case 'r':
g_rpc_addr = optarg;
break;
case 's':
value = spdk_strtol(optarg, 10);
if (value < 0) {
fprintf(stderr, "converting a string to integer failed\n");
return -EINVAL;
}
opts->mem_size = value;
break;
case 'u':
opts->no_pci = true;
break;
case 'h':
usage(argv[0]);
exit(EXIT_SUCCESS);
default:
usage(argv[0]);
return 1;
}
}
return 0;
}
static int
nvmf_reactor_run(void *arg)
{
struct nvmf_reactor *nvmf_reactor = arg;
struct nvmf_lw_thread *lw_thread;
struct spdk_thread *thread;
/* run all the lightweight threads in this nvmf_reactor by FIFO. */
do {
if (spdk_ring_dequeue(nvmf_reactor->threads, (void **)&lw_thread, 1)) {
thread = spdk_thread_get_from_ctx(lw_thread);
spdk_thread_poll(thread, 0, 0);
if (spdk_unlikely(spdk_thread_is_exited(thread) &&
spdk_thread_is_idle(thread))) {
spdk_thread_destroy(thread);
} else if (spdk_unlikely(lw_thread->resched)) {
lw_thread->resched = false;
nvmf_schedule_spdk_thread(thread);
} else {
spdk_ring_enqueue(nvmf_reactor->threads, (void **)&lw_thread, 1, NULL);
}
}
} while (!g_reactors_exit);
/* free all the lightweight threads */
while (spdk_ring_dequeue(nvmf_reactor->threads, (void **)&lw_thread, 1)) {
thread = spdk_thread_get_from_ctx(lw_thread);
spdk_set_thread(thread);
if (spdk_thread_is_exited(thread)) {
spdk_thread_destroy(thread);
} else {
/* This thread is not exited yet, and may need to communicate with other threads
* to be exited. So mark it as exiting, and check again after traversing other threads.
*/
spdk_thread_exit(thread);
spdk_thread_poll(thread, 0, 0);
spdk_ring_enqueue(nvmf_reactor->threads, (void **)&lw_thread, 1, NULL);
}
}
return 0;
}
static int
nvmf_schedule_spdk_thread(struct spdk_thread *thread)
{
struct nvmf_reactor *nvmf_reactor;
struct nvmf_lw_thread *lw_thread;
struct spdk_cpuset *cpumask;
uint32_t i;
/* Lightweight threads may have a requested cpumask.
* This is a request only - the scheduler does not have to honor it.
* For this scheduler implementation, each reactor is pinned to
* a particular core so honoring the request is reasonably easy.
*/
cpumask = spdk_thread_get_cpumask(thread);
lw_thread = spdk_thread_get_ctx(thread);
assert(lw_thread != NULL);
memset(lw_thread, 0, sizeof(*lw_thread));
/* assign lightweight threads to nvmf reactor(core)
* Here we use the mutex.The way the actual SPDK event framework
* solves this is by using internal rings for messages between reactors
*/
pthread_mutex_lock(&g_mutex);
for (i = 0; i < spdk_env_get_core_count(); i++) {
if (g_next_reactor == NULL) {
g_next_reactor = TAILQ_FIRST(&g_reactors);
}
nvmf_reactor = g_next_reactor;
g_next_reactor = TAILQ_NEXT(g_next_reactor, link);
/* each spdk_thread has the core affinity */
if (spdk_cpuset_get_cpu(cpumask, nvmf_reactor->core)) {
spdk_ring_enqueue(nvmf_reactor->threads, (void **)&lw_thread, 1, NULL);
break;
}
}
pthread_mutex_unlock(&g_mutex);
if (i == spdk_env_get_core_count()) {
fprintf(stderr, "failed to schedule spdk thread\n");
return -1;
}
return 0;
}
static void
nvmf_request_spdk_thread_reschedule(struct spdk_thread *thread)
{
struct nvmf_lw_thread *lw_thread;
assert(thread == spdk_get_thread());
lw_thread = spdk_thread_get_ctx(thread);
assert(lw_thread != NULL);
lw_thread->resched = true;
}
static int
nvmf_reactor_thread_op(struct spdk_thread *thread, enum spdk_thread_op op)
{
switch (op) {
case SPDK_THREAD_OP_NEW:
return nvmf_schedule_spdk_thread(thread);
case SPDK_THREAD_OP_RESCHED:
nvmf_request_spdk_thread_reschedule(thread);
return 0;
default:
return -ENOTSUP;
}
}
static bool
nvmf_reactor_thread_op_supported(enum spdk_thread_op op)
{
switch (op) {
case SPDK_THREAD_OP_NEW:
case SPDK_THREAD_OP_RESCHED:
return true;
default:
return false;
}
}
static int
nvmf_init_threads(void)
{
int rc;
uint32_t i;
char thread_name[32];
struct nvmf_reactor *nvmf_reactor;
struct spdk_cpuset cpumask;
uint32_t main_core = spdk_env_get_current_core();
/* Whenever SPDK creates a new lightweight thread it will call
* nvmf_schedule_spdk_thread asking for the application to begin
* polling it via spdk_thread_poll(). Each lightweight thread in
* SPDK optionally allocates extra memory to be used by the application
* framework. The size of the extra memory allocated is the second parameter.
*/
spdk_thread_lib_init_ext(nvmf_reactor_thread_op, nvmf_reactor_thread_op_supported,
sizeof(struct nvmf_lw_thread), SPDK_DEFAULT_MSG_MEMPOOL_SIZE);
/* Spawn one system thread per CPU core. The system thread is called a reactor.
* SPDK will spawn lightweight threads that must be mapped to reactors in
* nvmf_schedule_spdk_thread. Using a single system thread per CPU core is a
* choice unique to this application. SPDK itself does not require this specific
* threading model. For example, another viable threading model would be
* dynamically scheduling the lightweight threads onto a thread pool using a
* work queue.
*/
SPDK_ENV_FOREACH_CORE(i) {
nvmf_reactor = calloc(1, sizeof(struct nvmf_reactor));
if (!nvmf_reactor) {
fprintf(stderr, "failed to alloc nvmf reactor\n");
rc = -ENOMEM;
goto err_exit;
}
nvmf_reactor->core = i;
nvmf_reactor->threads = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 1024, SPDK_ENV_SOCKET_ID_ANY);
if (!nvmf_reactor->threads) {
fprintf(stderr, "failed to alloc ring\n");
free(nvmf_reactor);
rc = -ENOMEM;
goto err_exit;
}
TAILQ_INSERT_TAIL(&g_reactors, nvmf_reactor, link);
if (i == main_core) {
g_main_reactor = nvmf_reactor;
g_next_reactor = g_main_reactor;
} else {
rc = spdk_env_thread_launch_pinned(i,
nvmf_reactor_run,
nvmf_reactor);
if (rc) {
fprintf(stderr, "failed to pin reactor launch\n");
goto err_exit;
}
}
}
/* Spawn a lightweight thread only on the current core to manage this application. */
spdk_cpuset_zero(&cpumask);
spdk_cpuset_set_cpu(&cpumask, main_core, true);
snprintf(thread_name, sizeof(thread_name), "nvmf_main_thread");
g_init_thread = spdk_thread_create(thread_name, &cpumask);
if (!g_init_thread) {
fprintf(stderr, "failed to create spdk thread\n");
return -1;
}
fprintf(stdout, "nvmf threads initialize successfully\n");
return 0;
err_exit:
return rc;
}
static void
nvmf_destroy_threads(void)
{
struct nvmf_reactor *nvmf_reactor, *tmp;
TAILQ_FOREACH_SAFE(nvmf_reactor, &g_reactors, link, tmp) {
spdk_ring_free(nvmf_reactor->threads);
free(nvmf_reactor);
}
pthread_mutex_destroy(&g_mutex);
spdk_thread_lib_fini();
fprintf(stdout, "nvmf threads destroy successfully\n");
}
static void
nvmf_tgt_destroy_done(void *ctx, int status)
{
fprintf(stdout, "destroyed the nvmf target service\n");
g_target_state = NVMF_FINI_SUBSYSTEM;
nvmf_target_advance_state();
}
static void
nvmf_destroy_nvmf_tgt(void)
{
if (g_nvmf_tgt.tgt) {
spdk_nvmf_tgt_destroy(g_nvmf_tgt.tgt, nvmf_tgt_destroy_done, NULL);
} else {
g_target_state = NVMF_FINI_SUBSYSTEM;
}
}
static void
nvmf_create_nvmf_tgt(void)
{
struct spdk_nvmf_subsystem *subsystem;
struct spdk_nvmf_target_opts tgt_opts;
tgt_opts.max_subsystems = g_nvmf_tgt.max_subsystems;
snprintf(tgt_opts.name, sizeof(tgt_opts.name), "%s", "nvmf_example");
/* Construct the default NVMe-oF target
* An NVMe-oF target is a collection of subsystems, namespace, and poll
* groups, and defines the scope of the NVMe-oF discovery service.
*/
g_nvmf_tgt.tgt = spdk_nvmf_tgt_create(&tgt_opts);
if (g_nvmf_tgt.tgt == NULL) {
fprintf(stderr, "spdk_nvmf_tgt_create() failed\n");
goto error;
}
/* Create and add discovery subsystem to the NVMe-oF target.
* NVMe-oF defines a discovery mechanism that a host uses to determine
* the NVM subsystems that expose namespaces that the host may access.
* It provides a host with following capabilities:
* 1,The ability to discover a list of NVM subsystems with namespaces
* that are accessible to the host.
* 2,The ability to discover multiple paths to an NVM subsystem.
* 3,The ability to discover controllers that are statically configured.
*/
subsystem = spdk_nvmf_subsystem_create(g_nvmf_tgt.tgt, SPDK_NVMF_DISCOVERY_NQN,
SPDK_NVMF_SUBTYPE_DISCOVERY, 0);
if (subsystem == NULL) {
fprintf(stderr, "failed to create discovery nvmf library subsystem\n");
goto error;
}
/* Allow any host to access the discovery subsystem */
spdk_nvmf_subsystem_set_allow_any_host(subsystem, true);
fprintf(stdout, "created a nvmf target service\n");
g_target_state = NVMF_INIT_POLL_GROUPS;
return;
error:
g_target_state = NVMF_FINI_TARGET;
}
static void
nvmf_tgt_subsystem_stop_next(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
int rc;
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
if (subsystem) {
rc = spdk_nvmf_subsystem_stop(subsystem,
nvmf_tgt_subsystem_stop_next,
cb_arg);
if (rc) {
nvmf_tgt_subsystem_stop_next(subsystem, cb_arg, 0);
fprintf(stderr, "Unable to stop NVMe-oF subsystem. Trying others.\n");
}
return;
}
fprintf(stdout, "all subsystems of target stopped\n");
g_target_state = NVMF_FINI_POLL_GROUPS;
nvmf_target_advance_state();
}
static void
nvmf_tgt_stop_subsystems(struct nvmf_target *nvmf_tgt)
{
struct spdk_nvmf_subsystem *subsystem;
int rc;
subsystem = spdk_nvmf_subsystem_get_first(nvmf_tgt->tgt);
if (spdk_likely(subsystem)) {
rc = spdk_nvmf_subsystem_stop(subsystem,
nvmf_tgt_subsystem_stop_next,
NULL);
if (rc) {
nvmf_tgt_subsystem_stop_next(subsystem, NULL, 0);
fprintf(stderr, "Unable to stop NVMe-oF subsystem. Trying others.\n");
}
} else {
g_target_state = NVMF_FINI_POLL_GROUPS;
}
}
static void
nvmf_tgt_subsystem_start_next(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
int rc;
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
if (subsystem) {
rc = spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_start_next,
cb_arg);
if (rc) {
g_target_state = NVMF_FINI_STOP_SUBSYSTEMS;
fprintf(stderr, "Unable to start NVMe-oF subsystem. shutting down app.\n");
nvmf_target_advance_state();
}
return;
}
fprintf(stdout, "all subsystems of target started\n");
g_target_state = NVMF_RUNNING;
nvmf_target_advance_state();
}
static void
nvmf_tgt_start_subsystems(struct nvmf_target *nvmf_tgt)
{
struct spdk_nvmf_subsystem *subsystem;
int rc;
/* Subsystem is the NVM subsystem which is a combine of namespaces
* except the discovery subsystem which is used for discovery service.
* It also controls the hosts that means the subsystem determines whether
* the host can access this subsystem.
*/
subsystem = spdk_nvmf_subsystem_get_first(nvmf_tgt->tgt);
if (spdk_likely(subsystem)) {
/* In SPDK there are three states in subsystem: Inactive, Active, Paused.
* Start subsystem means make it from inactive to active that means
* subsystem start to work or it can be accessed.
*/
rc = spdk_nvmf_subsystem_start(subsystem,
nvmf_tgt_subsystem_start_next,
NULL);
if (rc) {
fprintf(stderr, "Unable to start NVMe-oF subsystem. shutting down app.\n");
g_target_state = NVMF_FINI_STOP_SUBSYSTEMS;
}
} else {
g_target_state = NVMF_RUNNING;
}
}
static void
nvmf_tgt_create_poll_groups_done(void *ctx)
{
struct nvmf_target_poll_group *pg = ctx;
if (!g_next_pg) {
g_next_pg = pg;
}
TAILQ_INSERT_TAIL(&g_poll_groups, pg, link);
assert(g_num_poll_groups < spdk_env_get_core_count());
if (++g_num_poll_groups == spdk_env_get_core_count()) {
fprintf(stdout, "create targets's poll groups done\n");
g_target_state = NVMF_INIT_START_SUBSYSTEMS;
nvmf_target_advance_state();
}
}
static void
nvmf_tgt_create_poll_group(void *ctx)
{
struct nvmf_target_poll_group *pg;
pg = calloc(1, sizeof(struct nvmf_target_poll_group));
if (!pg) {
fprintf(stderr, "failed to allocate poll group\n");
assert(false);
return;
}
pg->thread = spdk_get_thread();
pg->group = spdk_nvmf_poll_group_create(g_nvmf_tgt.tgt);
if (!pg->group) {
fprintf(stderr, "failed to create poll group of the target\n");
free(pg);
assert(false);
return;
}
spdk_thread_send_msg(g_init_thread, nvmf_tgt_create_poll_groups_done, pg);
}
/* Create a lightweight thread per poll group instead of assuming a pool of lightweight
* threads already exist at start up time. A poll group is a collection of unrelated NVMe-oF
* connections. Each poll group is only accessed from the associated lightweight thread.
*/
static void
nvmf_poll_groups_create(void)
{
struct spdk_cpuset tmp_cpumask = {};
uint32_t i;
char thread_name[32];
struct spdk_thread *thread;
assert(g_init_thread != NULL);
SPDK_ENV_FOREACH_CORE(i) {
spdk_cpuset_zero(&tmp_cpumask);
spdk_cpuset_set_cpu(&tmp_cpumask, i, true);
snprintf(thread_name, sizeof(thread_name), "nvmf_tgt_poll_group_%u", i);
thread = spdk_thread_create(thread_name, &tmp_cpumask);
assert(thread != NULL);
spdk_thread_send_msg(thread, nvmf_tgt_create_poll_group, NULL);
}
}
static void
_nvmf_tgt_destroy_poll_groups_done(void *ctx)
{
assert(g_num_poll_groups > 0);
if (--g_num_poll_groups == 0) {
fprintf(stdout, "destroy targets's poll groups done\n");
g_target_state = NVMF_FINI_TARGET;
nvmf_target_advance_state();
}
}
static void
nvmf_tgt_destroy_poll_groups_done(void *cb_arg, int status)
{
struct nvmf_target_poll_group *pg = cb_arg;
free(pg);
spdk_thread_send_msg(g_fini_thread, _nvmf_tgt_destroy_poll_groups_done, NULL);
spdk_thread_exit(spdk_get_thread());
}
static void
nvmf_tgt_destroy_poll_group(void *ctx)
{
struct nvmf_target_poll_group *pg = ctx;
spdk_nvmf_poll_group_destroy(pg->group, nvmf_tgt_destroy_poll_groups_done, pg);
}
static void
nvmf_poll_groups_destroy(void)
{
struct nvmf_target_poll_group *pg, *tmp;
g_fini_thread = spdk_get_thread();
assert(g_fini_thread != NULL);
TAILQ_FOREACH_SAFE(pg, &g_poll_groups, link, tmp) {
TAILQ_REMOVE(&g_poll_groups, pg, link);
spdk_thread_send_msg(pg->thread, nvmf_tgt_destroy_poll_group, pg);
}
}
static void
nvmf_subsystem_fini_done(void *cb_arg)
{
fprintf(stdout, "bdev subsystem finish successfully\n");
spdk_rpc_finish();
g_reactors_exit = true;
}
static void
nvmf_subsystem_init_done(int rc, void *cb_arg)
{
fprintf(stdout, "bdev subsystem init successfully\n");
rc = spdk_rpc_initialize(g_rpc_addr);
if (rc) {
spdk_app_stop(rc);
return;
}
spdk_rpc_set_state(SPDK_RPC_RUNTIME);
g_target_state = NVMF_INIT_TARGET;
nvmf_target_advance_state();
}
static void
migrate_poll_group_by_rr(void *ctx)
{
uint32_t current_core, next_core;
struct spdk_cpuset cpumask = {};
current_core = spdk_env_get_current_core();
next_core = spdk_env_get_next_core(current_core);
if (next_core == UINT32_MAX) {
next_core = spdk_env_get_first_core();
}
spdk_cpuset_set_cpu(&cpumask, next_core, true);
spdk_thread_set_cpumask(&cpumask);
}
static int
migrate_poll_groups_by_rr(void *ctx)
{
struct nvmf_target_poll_group *pg;
TAILQ_FOREACH(pg, &g_poll_groups, link) {
spdk_thread_send_msg(pg->thread, migrate_poll_group_by_rr, NULL);
}
return SPDK_POLLER_BUSY;
}
static void
nvmf_target_advance_state(void)
{
enum nvmf_target_state prev_state;
do {
prev_state = g_target_state;
switch (g_target_state) {
case NVMF_INIT_SUBSYSTEM:
/* initialize the bdev layer */
spdk_subsystem_init(nvmf_subsystem_init_done, NULL);
return;
case NVMF_INIT_TARGET:
nvmf_create_nvmf_tgt();
break;
case NVMF_INIT_POLL_GROUPS:
nvmf_poll_groups_create();
break;
case NVMF_INIT_START_SUBSYSTEMS:
nvmf_tgt_start_subsystems(&g_nvmf_tgt);
break;
case NVMF_RUNNING:
fprintf(stdout, "nvmf target is running\n");
if (g_migrate_pg_period_us != 0) {
g_migrate_pg_poller = SPDK_POLLER_REGISTER(migrate_poll_groups_by_rr, NULL,
g_migrate_pg_period_us);
}
break;
case NVMF_FINI_STOP_SUBSYSTEMS:
spdk_poller_unregister(&g_migrate_pg_poller);
nvmf_tgt_stop_subsystems(&g_nvmf_tgt);
break;
case NVMF_FINI_POLL_GROUPS:
nvmf_poll_groups_destroy();
break;
case NVMF_FINI_TARGET:
nvmf_destroy_nvmf_tgt();
break;
case NVMF_FINI_SUBSYSTEM:
spdk_subsystem_fini(nvmf_subsystem_fini_done, NULL);
break;
}
} while (g_target_state != prev_state);
}
static void
nvmf_target_app_start(void *arg)
{
g_target_state = NVMF_INIT_SUBSYSTEM;
nvmf_target_advance_state();
}
static void
_nvmf_shutdown_cb(void *ctx)
{
/* Still in initialization state, defer shutdown operation */
if (g_target_state < NVMF_RUNNING) {
spdk_thread_send_msg(spdk_get_thread(), _nvmf_shutdown_cb, NULL);
return;
} else if (g_target_state > NVMF_RUNNING) {
/* Already in Shutdown status, ignore the signal */
return;
}
g_target_state = NVMF_FINI_STOP_SUBSYSTEMS;
nvmf_target_advance_state();
}
static void
nvmf_shutdown_cb(int signo)
{
if (!g_intr_received) {
g_intr_received = true;
spdk_thread_send_msg(g_init_thread, _nvmf_shutdown_cb, NULL);
}
}
static int
nvmf_setup_signal_handlers(void)
{
struct sigaction sigact;
sigset_t sigmask;
int signals[] = {SIGINT, SIGTERM};
int num_signals = sizeof(signals) / sizeof(int);
int rc, i;
rc = sigemptyset(&sigmask);
if (rc) {
fprintf(stderr, "errno:%d--failed to empty signal set\n", errno);
return rc;
}
memset(&sigact, 0, sizeof(sigact));
rc = sigemptyset(&sigact.sa_mask);
if (rc) {
fprintf(stderr, "errno:%d--failed to empty signal set\n", errno);
return rc;
}
/* Install the same handler for SIGINT and SIGTERM */
sigact.sa_handler = nvmf_shutdown_cb;
for (i = 0; i < num_signals; i++) {
rc = sigaction(signals[i], &sigact, NULL);
if (rc < 0) {
fprintf(stderr, "errno:%d--sigaction() failed\n", errno);
return rc;
}
rc = sigaddset(&sigmask, signals[i]);
if (rc) {
fprintf(stderr, "errno:%d--failed to add set\n", errno);
return rc;
}
}
pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL);
return 0;
}
int main(int argc, char **argv)
{
int rc;
struct spdk_env_opts opts;
spdk_env_opts_init(&opts);
opts.name = "nvmf-example";
rc = parse_args(argc, argv, &opts);
if (rc != 0) {
return rc;
}
if (spdk_env_init(&opts) < 0) {
fprintf(stderr, "unable to initialize SPDK env\n");
return -EINVAL;
}
/* Initialize the threads */
rc = nvmf_init_threads();
assert(rc == 0);
/* Send a message to the thread assigned to the main reactor
* that continues initialization. This is how we bootstrap the
* program so that all code from here on is running on an SPDK thread.
*/
assert(g_init_thread != NULL);
rc = nvmf_setup_signal_handlers();
assert(rc == 0);
spdk_thread_send_msg(g_init_thread, nvmf_target_app_start, NULL);
nvmf_reactor_run(g_main_reactor);
spdk_env_thread_wait_all();
nvmf_destroy_threads();
spdk_env_fini();
return rc;
}