nvmf: Add synchronization primitives for subsystems

This allows the user to pause a subsystem, make some
modifications, and then resume it.

Change-Id: Ia18371023d8fc66e1797fda293a01b68c0a61c96
Signed-off-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-on: https://review.gerrithub.io/392422
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Ben Walker 2017-12-19 16:39:04 -07:00 committed by Jim Harris
parent acbec142ae
commit 95ac75aabe
10 changed files with 570 additions and 270 deletions

View File

@ -160,6 +160,7 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
const char *sn;
size_t num_ns;
struct spdk_nvmf_ns_params ns_list[MAX_NAMESPACES] = {};
struct spdk_nvmf_subsystem *subsystem;
nqn = spdk_conf_section_get_val(sp, "NQN");
mode = spdk_conf_section_get_val(sp, "Mode");
@ -258,17 +259,17 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
num_ns++;
}
ret = spdk_nvmf_construct_subsystem(nqn,
num_listen_addrs, listen_addrs,
num_hosts, hosts, allow_any_host,
sn,
num_ns, ns_list);
subsystem = spdk_nvmf_construct_subsystem(nqn,
num_listen_addrs, listen_addrs,
num_hosts, hosts, allow_any_host,
sn,
num_ns, ns_list);
for (i = 0; i < MAX_LISTEN_ADDRESSES; i++) {
free(listen_addrs_str[i]);
}
return ret;
return (subsystem != NULL);
}
static int
@ -310,8 +311,8 @@ spdk_nvmf_parse_conf(void)
return 0;
}
int
spdk_nvmf_construct_subsystem(const char *name,
struct spdk_nvmf_subsystem *
spdk_nvmf_construct_subsystem(const char *name,
int num_listen_addresses, struct rpc_listen_address *addresses,
int num_hosts, char *hosts[], bool allow_any_host,
const char *sn, size_t num_ns, struct spdk_nvmf_ns_params *ns_list)
@ -323,23 +324,23 @@ spdk_nvmf_construct_subsystem(const char *name,
if (name == NULL) {
SPDK_ERRLOG("No NQN specified for subsystem\n");
return -1;
return NULL;
}
if (num_listen_addresses > MAX_LISTEN_ADDRESSES) {
SPDK_ERRLOG("invalid listen adresses number\n");
return -1;
return NULL;
}
if (num_hosts > MAX_HOSTS) {
SPDK_ERRLOG("invalid hosts number\n");
return -1;
return NULL;
}
subsystem = nvmf_tgt_create_subsystem(name, SPDK_NVMF_SUBTYPE_NVME, num_ns);
if (subsystem == NULL) {
SPDK_ERRLOG("Subsystem creation failed\n");
return -1;
return NULL;
}
/* Parse Listen sections */
@ -409,9 +410,9 @@ spdk_nvmf_construct_subsystem(const char *name,
}
return 0;
return subsystem;
error:
spdk_nvmf_delete_subsystem(subsystem);
return -1;
spdk_nvmf_subsystem_destroy(subsystem);
return NULL;
}

View File

@ -333,6 +333,22 @@ free_rpc_subsystem(struct rpc_subsystem *req)
free_rpc_hosts(&req->hosts);
}
static void
spdk_rpc_nvmf_subsystem_started(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
struct spdk_jsonrpc_request *request = cb_arg;
struct spdk_json_write_ctx *w;
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_json_write_bool(w, true);
spdk_jsonrpc_end_result(request, w);
}
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, true},
@ -349,8 +365,7 @@ spdk_rpc_construct_nvmf_subsystem(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_subsystem req = {};
struct spdk_json_write_ctx *w;
int ret;
struct spdk_nvmf_subsystem *subsystem;
req.core = -1; /* Explicitly set the core as the uninitialized value */
if (spdk_json_decode_object(params, rpc_subsystem_decoders,
@ -384,25 +399,23 @@ spdk_rpc_construct_nvmf_subsystem(struct spdk_jsonrpc_request *request,
SPDK_NOTICELOG("Ignoring it and continuing.\n");
}
ret = spdk_nvmf_construct_subsystem(req.nqn,
req.listen_addresses.num_listen_address,
req.listen_addresses.addresses,
req.hosts.num_hosts, req.hosts.hosts, req.allow_any_host,
req.serial_number,
req.namespaces.num_ns, req.namespaces.ns_params);
if (ret) {
subsystem = spdk_nvmf_construct_subsystem(req.nqn,
req.listen_addresses.num_listen_address,
req.listen_addresses.addresses,
req.hosts.num_hosts, req.hosts.hosts, req.allow_any_host,
req.serial_number,
req.namespaces.num_ns, req.namespaces.ns_params);
if (!subsystem) {
goto invalid;
}
free_rpc_subsystem(&req);
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_json_write_bool(w, true);
spdk_jsonrpc_end_result(request, w);
spdk_nvmf_subsystem_start(subsystem,
spdk_rpc_nvmf_subsystem_started,
request);
return;
invalid:
@ -421,6 +434,24 @@ free_rpc_delete_subsystem(struct rpc_delete_subsystem *r)
free(r->nqn);
}
static void
spdk_rpc_nvmf_subsystem_stopped(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
struct spdk_jsonrpc_request *request = cb_arg;
struct spdk_json_write_ctx *w;
spdk_nvmf_subsystem_destroy(subsystem);
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_json_write_bool(w, true);
spdk_jsonrpc_end_result(request, w);
}
static const struct spdk_json_object_decoder rpc_delete_subsystem_decoders[] = {
{"nqn", offsetof(struct rpc_delete_subsystem, nqn), spdk_json_decode_string},
};
@ -430,7 +461,7 @@ spdk_rpc_delete_nvmf_subsystem(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_delete_subsystem req = {};
struct spdk_json_write_ctx *w;
struct spdk_nvmf_subsystem *subsystem;
if (spdk_json_decode_object(params, rpc_delete_subsystem_decoders,
SPDK_COUNTOF(rpc_delete_subsystem_decoders),
@ -444,20 +475,17 @@ spdk_rpc_delete_nvmf_subsystem(struct spdk_jsonrpc_request *request,
goto invalid;
}
if (nvmf_tgt_shutdown_subsystem_by_nqn(req.nqn)) {
SPDK_ERRLOG("shutdown_subsystem failed\n");
subsystem = spdk_nvmf_tgt_find_subsystem(g_tgt.tgt, req.nqn);
if (!subsystem) {
goto invalid;
}
free_rpc_delete_subsystem(&req);
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_nvmf_subsystem_stop(subsystem,
spdk_rpc_nvmf_subsystem_stopped,
request);
spdk_json_write_bool(w, true);
spdk_jsonrpc_end_result(request, w);
return;
invalid:

View File

@ -99,7 +99,7 @@ nvmf_tgt_create_subsystem(const char *name, enum spdk_nvmf_subtype subtype, uint
return NULL;
}
subsystem = spdk_nvmf_create_subsystem(g_tgt.tgt, name, subtype, num_ns);
subsystem = spdk_nvmf_subsystem_create(g_tgt.tgt, name, subtype, num_ns);
if (subsystem == NULL) {
SPDK_ERRLOG("Subsystem creation failed\n");
return NULL;
@ -120,7 +120,7 @@ nvmf_tgt_shutdown_subsystem_by_nqn(const char *nqn)
return -EINVAL;
}
spdk_nvmf_delete_subsystem(subsystem);
spdk_nvmf_subsystem_destroy(subsystem);
return 0;
}
@ -187,7 +187,7 @@ nvmf_tgt_destroy_poll_group(void *ctx)
static void
nvmf_tgt_create_poll_group_done(void *ctx)
{
g_tgt.state = NVMF_TGT_INIT_START_ACCEPTOR;
g_tgt.state = NVMF_TGT_INIT_START_SUBSYSTEMS;
nvmf_tgt_advance_state(NULL, NULL);
}
@ -207,6 +207,36 @@ nvmf_tgt_create_poll_group(void *ctx)
g_active_poll_groups++;
}
static void
nvmf_tgt_subsystem_started(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
if (subsystem) {
spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL);
return;
}
g_tgt.state = NVMF_TGT_INIT_START_ACCEPTOR;
nvmf_tgt_advance_state(NULL, NULL);
}
static void
nvmf_tgt_subsystem_stopped(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status)
{
subsystem = spdk_nvmf_subsystem_get_next(subsystem);
if (subsystem) {
spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL);
return;
}
g_tgt.state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS;
nvmf_tgt_advance_state(NULL, NULL);
}
static void
nvmf_tgt_advance_state(void *arg1, void *arg2)
{
@ -254,6 +284,18 @@ nvmf_tgt_advance_state(void *arg1, void *arg2)
NULL,
nvmf_tgt_create_poll_group_done);
break;
case NVMF_TGT_INIT_START_SUBSYSTEMS: {
struct spdk_nvmf_subsystem *subsystem;
subsystem = spdk_nvmf_subsystem_get_first(g_tgt.tgt);
if (subsystem) {
spdk_nvmf_subsystem_start(subsystem, nvmf_tgt_subsystem_started, NULL);
} else {
g_tgt.state = NVMF_TGT_INIT_START_ACCEPTOR;
}
break;
}
case NVMF_TGT_INIT_START_ACCEPTOR:
g_acceptor_poller = spdk_poller_register(acceptor_poll, g_tgt.tgt,
g_spdk_nvmf_tgt_conf.acceptor_poll_rate);
@ -268,8 +310,20 @@ nvmf_tgt_advance_state(void *arg1, void *arg2)
break;
case NVMF_TGT_FINI_STOP_ACCEPTOR:
spdk_poller_unregister(&g_acceptor_poller);
g_tgt.state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS;
g_tgt.state = NVMF_TGT_FINI_STOP_SUBSYSTEMS;
break;
case NVMF_TGT_FINI_STOP_SUBSYSTEMS: {
struct spdk_nvmf_subsystem *subsystem;
subsystem = spdk_nvmf_subsystem_get_first(g_tgt.tgt);
if (subsystem) {
spdk_nvmf_subsystem_stop(subsystem, nvmf_tgt_subsystem_stopped, NULL);
} else {
g_tgt.state = NVMF_TGT_FINI_DESTROY_POLL_GROUPS;
}
break;
}
case NVMF_TGT_FINI_DESTROY_POLL_GROUPS:
/* Send a message to each thread and destroy the poll group */
spdk_for_each_thread(nvmf_tgt_destroy_poll_group,

View File

@ -55,10 +55,12 @@ enum nvmf_tgt_state {
NVMF_TGT_INIT_NONE = 0,
NVMF_TGT_INIT_PARSE_CONFIG,
NVMF_TGT_INIT_CREATE_POLL_GROUPS,
NVMF_TGT_INIT_START_SUBSYSTEMS,
NVMF_TGT_INIT_START_ACCEPTOR,
NVMF_TGT_RUNNING,
NVMF_TGT_FINI_STOP_ACCEPTOR,
NVMF_TGT_FINI_DESTROY_POLL_GROUPS,
NVMF_TGT_FINI_STOP_SUBSYSTEMS,
NVMF_TGT_FINI_FREE_RESOURCES,
NVMF_TGT_STOPPED,
NVMF_TGT_ERROR,
@ -86,11 +88,10 @@ struct spdk_nvmf_ns_params {
uint32_t nsid;
};
int
spdk_nvmf_construct_subsystem(const char *name,
int num_listen_addresses, struct rpc_listen_address *addresses,
int num_hosts, char *hosts[], bool allow_any_host,
const char *sn, size_t num_ns, struct spdk_nvmf_ns_params *ns_list);
struct spdk_nvmf_subsystem *spdk_nvmf_construct_subsystem(const char *name,
int num_listen_addresses, struct rpc_listen_address *addresses,
int num_hosts, char *hosts[], bool allow_any_host,
const char *sn, size_t num_ns, struct spdk_nvmf_ns_params *ns_list);
int
nvmf_tgt_shutdown_subsystem_by_nqn(const char *nqn);

View File

@ -132,16 +132,86 @@ int spdk_nvmf_poll_group_add(struct spdk_nvmf_poll_group *group,
int spdk_nvmf_poll_group_remove(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_qpair *qpair);
/*
* The NVMf subsystem, as indicated in the specification, is a collection
* of controllers. Any individual controller has
* access to all the NVMe device/namespaces maintained by the subsystem.
/**
* Create an NVMe-oF subsystem.
*
* Subsystems are in one of three states: Inactive, Active, Paused. This
* state affects which operations may be performed on the subsystem. Upon
* creation, the subsystem will be in the Inactive state and may be activated
* by calling spdk_nvmf_subsystem_start(). No I/O will be processed in the Inactive
* or Paused states, but changes to the state of the subsystem may be made.
*
* \param tgt The NVMe-oF target that will own this subsystem.
* \param nqn The NVMe qualified name of this subsystem.
* \param type Whether this subsystem is an I/O subsystem or a Discovery subsystem.
* \param num_ns The number of namespaces this subsystem contains.
*
* \return An spdk_nvmf_subsystem or NULL on error.
*/
struct spdk_nvmf_subsystem *spdk_nvmf_create_subsystem(struct spdk_nvmf_tgt *tgt,
struct spdk_nvmf_subsystem *spdk_nvmf_subsystem_create(struct spdk_nvmf_tgt *tgt,
const char *nqn,
enum spdk_nvmf_subtype type,
uint32_t num_ns);
/**
* Destroy an NVMe-oF subsystem. A subsystem may only be destroyed when in
* the Inactive state. See spdk_nvmf_subsystem_stop().
*
* \param subsystem The NVMe-oF subsystem to destroy.
*/
void spdk_nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem);
typedef void (*spdk_nvmf_subsystem_state_change_done)(struct spdk_nvmf_subsystem *subsystem,
void *cb_arg, int status);
/**
* Transition an NVMe-oF subsystem from Inactive to Active state.
*
* \param subsystem The NVMe-oF subsystem.
* \param cb_fn A function that will be called once the subsystem has changed state.
* \param cb_arg Argument passed to cb_fn.
*
*/
void spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg);
/**
* Transition an NVMe-oF subsystem from Active to Inactive state.
*
* \param subsystem The NVMe-oF subsystem.
* \param cb_fn A function that will be called once the subsystem has changed state.
* \param cb_arg Argument passed to cb_fn.
*
*/
void spdk_nvmf_subsystem_stop(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg);
/**
* Transition an NVMe-oF subsystem from Active to Paused state.
*
* \param subsystem The NVMe-oF subsystem.
* \param cb_fn A function that will be called once the subsystem has changed state.
* \param cb_arg Argument passed to cb_fn.
*
*/
void spdk_nvmf_subsystem_pause(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg);
/**
* Transition an NVMe-oF subsystem from Paused to Active state.
*
* \param subsystem The NVMe-oF subsystem.
* \param cb_fn A function that will be called once the subsystem has changed state.
* \param cb_arg Argument passed to cb_fn.
*
*/
void spdk_nvmf_subsystem_resume(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg);
/**
* Search the target for a subsystem with the given NQN
*/
@ -158,8 +228,6 @@ struct spdk_nvmf_subsystem *spdk_nvmf_subsystem_get_first(struct spdk_nvmf_tgt *
*/
struct spdk_nvmf_subsystem *spdk_nvmf_subsystem_get_next(struct spdk_nvmf_subsystem *subsystem);
void spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem);
/**
* Allow the given host NQN to connect to the given subsystem.
*

View File

@ -207,7 +207,7 @@ spdk_nvmf_tgt_destroy(struct spdk_nvmf_tgt *tgt)
if (tgt->subsystems) {
for (i = 0; i < tgt->max_sid; i++) {
if (tgt->subsystems[i]) {
spdk_nvmf_delete_subsystem(tgt->subsystems[i]);
spdk_nvmf_subsystem_destroy(tgt->subsystems[i]);
}
}
free(tgt->subsystems);
@ -416,18 +416,19 @@ spdk_nvmf_poll_group_add_transport(struct spdk_nvmf_poll_group *group,
return 0;
}
int
spdk_nvmf_poll_group_add_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
static int
poll_group_update_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_subsystem_poll_group *sgroup;
struct spdk_nvmf_ns *ns;
uint32_t new_num_channels, old_num_channels;
void *buf;
uint32_t i;
struct spdk_nvmf_ns *ns;
if (subsystem->id >= group->num_sgroups) {
void *buf;
buf = realloc(group->sgroups, (subsystem->id + 1) * sizeof(*sgroup));
if (!buf) {
return -ENOMEM;
@ -438,46 +439,92 @@ spdk_nvmf_poll_group_add_subsystem(struct spdk_nvmf_poll_group *group,
/* Zero out the newly allocated memory */
memset(&group->sgroups[group->num_sgroups],
0,
(subsystem->id + 1 - group->num_sgroups) * sizeof(struct spdk_nvmf_subsystem_poll_group));
(subsystem->id + 1 - group->num_sgroups) * sizeof(group->sgroups[0]));
group->num_sgroups = subsystem->id + 1;
}
sgroup = &group->sgroups[subsystem->id];
sgroup->num_channels = subsystem->max_nsid;
sgroup->channels = calloc(sgroup->num_channels, sizeof(struct spdk_io_channel *));
if (!sgroup->channels) {
return -1;
}
new_num_channels = subsystem->max_nsid;
old_num_channels = sgroup->num_channels;
for (i = 0; i < sgroup->num_channels; i++) {
ns = &subsystem->ns[i];
if (ns->allocated && sgroup->channels[i] == NULL) {
sgroup->channels[i] = spdk_bdev_get_io_channel(ns->desc);
if (sgroup->channels[i] == NULL) {
return -1;
if (new_num_channels == old_num_channels) {
/* Nothing to do */
} else if (old_num_channels == 0) {
/* First allocation */
sgroup->channels = calloc(new_num_channels, sizeof(sgroup->channels[0]));
if (!sgroup->channels) {
return -ENOMEM;
}
sgroup->num_channels = new_num_channels;
/* Initialize new channels */
for (i = old_num_channels; i < new_num_channels; i++) {
ns = &subsystem->ns[i];
if (ns->allocated) {
sgroup->channels[i] = spdk_bdev_get_io_channel(ns->desc);
} else {
sgroup->channels[i] = NULL;
}
}
} else if (new_num_channels < old_num_channels) {
/* Free the extra I/O channels */
for (i = new_num_channels; i < old_num_channels; i++) {
if (sgroup->channels[i]) {
spdk_put_io_channel(sgroup->channels[i]);
}
}
/* Shrink array */
buf = realloc(sgroup->channels, new_num_channels * sizeof(sgroup->channels[0]));
if (new_num_channels > 0 && !buf) {
return -ENOMEM;
}
sgroup->channels = buf;
sgroup->num_channels = new_num_channels;
} else {
/* Grow array */
buf = realloc(sgroup->channels, new_num_channels * sizeof(sgroup->channels[0]));
if (!buf) {
return -ENOMEM;
}
sgroup->channels = buf;
sgroup->num_channels = new_num_channels;
/* Initialize new channels */
for (i = old_num_channels; i < new_num_channels; i++) {
ns = &subsystem->ns[i];
if (ns->allocated) {
sgroup->channels[i] = spdk_bdev_get_io_channel(ns->desc);
} else {
sgroup->channels[i] = NULL;
}
}
}
/* TODO: Handle namespaces where the bdev was swapped out */
return 0;
}
int
spdk_nvmf_poll_group_remove_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_add_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_subsystem_poll_group *sgroup;
uint32_t nsid = ns->id - 1;
int rc;
sgroup = &group->sgroups[ns->subsystem->id];
if (sgroup->channels[nsid]) {
spdk_put_io_channel(sgroup->channels[nsid]);
sgroup->channels[nsid] = NULL;
rc = poll_group_update_subsystem(group, subsystem);
if (rc) {
return rc;
}
sgroup = &group->sgroups[subsystem->id];
sgroup->state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
return 0;
}
@ -505,46 +552,52 @@ spdk_nvmf_poll_group_remove_subsystem(struct spdk_nvmf_poll_group *group,
}
int
spdk_nvmf_poll_group_add_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_pause_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_subsystem_poll_group *sgroup;
uint32_t ns_idx;
sgroup = &group->sgroups[subsystem->id];
/* The index into the channels array is (nsid - 1) */
ns_idx = ns->id - 1;
if (ns_idx >= sgroup->num_channels) {
void *buf;
buf = realloc(sgroup->channels,
ns->id * sizeof(struct spdk_io_channel *));
if (!buf) {
return -ENOMEM;
}
/* Zero out the newly allocated memory */
memset(&sgroup->channels[sgroup->num_channels],
0,
(ns->id - sgroup->num_channels) * sizeof(struct spdk_io_channel *));
sgroup->num_channels = ns->id;
sgroup->channels = buf;
}
/* The channel could have been created in response to a subsystem creation
* event already propagating through the system */
if (sgroup->channels[ns_idx] == NULL) {
sgroup->channels[ns_idx] = spdk_bdev_get_io_channel(ns->desc);
}
if (sgroup->channels[ns_idx] == NULL) {
if (subsystem->id >= group->num_sgroups) {
return -1;
}
sgroup = &group->sgroups[subsystem->id];
if (sgroup == NULL) {
return -1;
}
assert(sgroup->state == SPDK_NVMF_SUBSYSTEM_ACTIVE);
/* TODO: This currently does not quiesce I/O */
sgroup->state = SPDK_NVMF_SUBSYSTEM_PAUSED;
return 0;
}
int
spdk_nvmf_poll_group_resume_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_subsystem_poll_group *sgroup;
int rc;
if (subsystem->id >= group->num_sgroups) {
return -1;
}
sgroup = &group->sgroups[subsystem->id];
if (sgroup == NULL) {
return -1;
}
assert(sgroup->state == SPDK_NVMF_SUBSYSTEM_PAUSED);
rc = poll_group_update_subsystem(group, subsystem);
if (rc) {
return rc;
}
sgroup->state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
return 0;
}

View File

@ -45,6 +45,16 @@
#define SPDK_NVMF_DEFAULT_NUM_CTRLRS_PER_LCORE 1
enum spdk_nvmf_subsystem_state {
SPDK_NVMF_SUBSYSTEM_INACTIVE = 0,
SPDK_NVMF_SUBSYSTEM_ACTIVATING,
SPDK_NVMF_SUBSYSTEM_ACTIVE,
SPDK_NVMF_SUBSYSTEM_PAUSING,
SPDK_NVMF_SUBSYSTEM_PAUSED,
SPDK_NVMF_SUBSYSTEM_RESUMING,
SPDK_NVMF_SUBSYSTEM_DEACTIVATING,
};
struct spdk_nvmf_tgt {
struct spdk_nvmf_tgt_opts opts;
@ -81,6 +91,8 @@ struct spdk_nvmf_subsystem_poll_group {
/* Array of channels for each namespace indexed by nsid - 1 */
struct spdk_io_channel **channels;
uint32_t num_channels;
enum spdk_nvmf_subsystem_state state;
};
struct spdk_nvmf_poll_group {
@ -91,7 +103,6 @@ struct spdk_nvmf_poll_group {
/* Array of poll groups indexed by subsystem id (sid) */
struct spdk_nvmf_subsystem_poll_group *sgroups;
uint32_t num_sgroups;
};
typedef enum _spdk_nvmf_request_exec_status {
@ -185,7 +196,9 @@ struct spdk_nvmf_ctrlr {
};
struct spdk_nvmf_subsystem {
uint32_t id;
uint32_t id;
enum spdk_nvmf_subsystem_state state;
char subnqn[SPDK_NVMF_NQN_MAX_LEN + 1];
enum spdk_nvmf_subtype subtype;
uint16_t next_cntlid;
@ -218,11 +231,10 @@ int spdk_nvmf_poll_group_add_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem);
int spdk_nvmf_poll_group_remove_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem);
int spdk_nvmf_poll_group_add_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem,
struct spdk_nvmf_ns *ns);
int spdk_nvmf_poll_group_remove_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_ns *ns);
int spdk_nvmf_poll_group_pause_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem);
int spdk_nvmf_poll_group_resume_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem);
void spdk_nvmf_request_exec(struct spdk_nvmf_request *req);
int spdk_nvmf_request_complete(struct spdk_nvmf_request *req);
int spdk_nvmf_request_abort(struct spdk_nvmf_request *req);

View File

@ -71,25 +71,8 @@ spdk_nvmf_valid_nqn(const char *nqn)
return true;
}
static void
spdk_nvmf_subsystem_create_done(struct spdk_io_channel_iter *i, int status)
{
}
static void
spdk_nvmf_subsystem_add_to_poll_group(struct spdk_io_channel_iter *i)
{
struct spdk_nvmf_subsystem *subsystem = spdk_io_channel_iter_get_ctx(i);
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
int rc;
rc = spdk_nvmf_poll_group_add_subsystem(group, subsystem);
spdk_for_each_channel_continue(i, rc);
}
struct spdk_nvmf_subsystem *
spdk_nvmf_create_subsystem(struct spdk_nvmf_tgt *tgt,
spdk_nvmf_subsystem_create(struct spdk_nvmf_tgt *tgt,
const char *nqn,
enum spdk_nvmf_subtype type,
uint32_t num_ns)
@ -129,6 +112,7 @@ spdk_nvmf_create_subsystem(struct spdk_nvmf_tgt *tgt,
return NULL;
}
subsystem->state = SPDK_NVMF_SUBSYSTEM_INACTIVE;
subsystem->tgt = tgt;
subsystem->id = sid;
subsystem->subtype = type;
@ -152,67 +136,23 @@ spdk_nvmf_create_subsystem(struct spdk_nvmf_tgt *tgt,
tgt->subsystems[sid] = subsystem;
tgt->discovery_genctr++;
/* Send a message to each poll group to notify it that a new subsystem
* is available.
* TODO: This call does not currently allow the user to wait for these
* messages to propagate. It also does not protect against two calls
* to this function overlapping
*/
spdk_for_each_channel(tgt,
spdk_nvmf_subsystem_add_to_poll_group,
subsystem,
spdk_nvmf_subsystem_create_done);
return subsystem;
}
static void
spdk_nvmf_subsystem_delete_done(struct spdk_io_channel_iter *i, int status)
{
struct spdk_nvmf_tgt *tgt = spdk_io_channel_iter_get_io_device(i);
struct spdk_nvmf_subsystem *subsystem = spdk_io_channel_iter_get_ctx(i);
struct spdk_nvmf_ns *ns;
for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem); ns != NULL;
ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) {
if (ns->bdev == NULL) {
continue;
}
spdk_bdev_close(ns->desc);
}
free(subsystem->ns);
tgt->subsystems[subsystem->id] = NULL;
tgt->discovery_genctr++;
free(subsystem);
}
static void
spdk_nvmf_subsystem_remove_from_poll_group(struct spdk_io_channel_iter *i)
{
struct spdk_nvmf_subsystem *subsystem = spdk_io_channel_iter_get_ctx(i);
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
int rc;
rc = spdk_nvmf_poll_group_remove_subsystem(group, subsystem);
spdk_for_each_channel_continue(i, rc);
}
void
spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
spdk_nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_listener *listener, *listener_tmp;
struct spdk_nvmf_host *host, *host_tmp;
struct spdk_nvmf_ctrlr *ctrlr, *ctrlr_tmp;
struct spdk_nvmf_ns *ns;
if (!subsystem) {
return;
}
assert(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE);
SPDK_DEBUGLOG(SPDK_LOG_NVMF, "subsystem is %p\n", subsystem);
TAILQ_FOREACH_SAFE(listener, &subsystem->listeners, link, listener_tmp) {
@ -230,16 +170,210 @@ spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
spdk_nvmf_ctrlr_destruct(ctrlr);
}
/* Send a message to each poll group to notify it that a subsystem
* is no longer available.
* TODO: This call does not currently allow the user to wait for these
* messages to propagate. It also does not protect against two calls
* to this function overlapping
*/
for (ns = spdk_nvmf_subsystem_get_first_ns(subsystem); ns != NULL;
ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns)) {
if (ns->bdev == NULL) {
continue;
}
spdk_bdev_close(ns->desc);
}
free(subsystem->ns);
subsystem->tgt->subsystems[subsystem->id] = NULL;
subsystem->tgt->discovery_genctr++;
free(subsystem);
}
static int
spdk_nvmf_subsystem_set_state(struct spdk_nvmf_subsystem *subsystem,
enum spdk_nvmf_subsystem_state state)
{
enum spdk_nvmf_subsystem_state actual_old_state, expected_old_state;
switch (state) {
case SPDK_NVMF_SUBSYSTEM_INACTIVE:
expected_old_state = SPDK_NVMF_SUBSYSTEM_DEACTIVATING;
break;
case SPDK_NVMF_SUBSYSTEM_ACTIVATING:
expected_old_state = SPDK_NVMF_SUBSYSTEM_INACTIVE;
break;
case SPDK_NVMF_SUBSYSTEM_ACTIVE:
expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING;
break;
case SPDK_NVMF_SUBSYSTEM_PAUSING:
expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
break;
case SPDK_NVMF_SUBSYSTEM_PAUSED:
expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSING;
break;
case SPDK_NVMF_SUBSYSTEM_RESUMING:
expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSED;
break;
case SPDK_NVMF_SUBSYSTEM_DEACTIVATING:
expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE;
break;
default:
assert(false);
return -1;
}
actual_old_state = __sync_val_compare_and_swap(&subsystem->state, expected_old_state, state);
if (actual_old_state != expected_old_state) {
if (actual_old_state == SPDK_NVMF_SUBSYSTEM_RESUMING &&
state == SPDK_NVMF_SUBSYSTEM_ACTIVE) {
expected_old_state = SPDK_NVMF_SUBSYSTEM_RESUMING;
}
actual_old_state = __sync_val_compare_and_swap(&subsystem->state, expected_old_state, state);
}
assert(actual_old_state == expected_old_state);
return actual_old_state - expected_old_state;
}
struct subsystem_state_change_ctx {
struct spdk_nvmf_subsystem *subsystem;
enum spdk_nvmf_subsystem_state requested_state;
spdk_nvmf_subsystem_state_change_done cb_fn;
void *cb_arg;
};
static void
subsystem_state_change_done(struct spdk_io_channel_iter *i, int status)
{
struct subsystem_state_change_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
if (status == 0) {
status = spdk_nvmf_subsystem_set_state(ctx->subsystem, ctx->requested_state);
if (status) {
status = -1;
}
}
ctx->cb_fn(ctx->subsystem, ctx->cb_arg, status);
free(ctx);
}
static void
subsystem_state_change_on_pg(struct spdk_io_channel_iter *i)
{
struct subsystem_state_change_ctx *ctx;
struct spdk_io_channel *ch;
struct spdk_nvmf_poll_group *group;
int rc = -1;
ctx = spdk_io_channel_iter_get_ctx(i);
ch = spdk_io_channel_iter_get_channel(i);
group = spdk_io_channel_get_ctx(ch);
switch (ctx->requested_state) {
case SPDK_NVMF_SUBSYSTEM_INACTIVE:
rc = spdk_nvmf_poll_group_remove_subsystem(group, ctx->subsystem);
break;
case SPDK_NVMF_SUBSYSTEM_ACTIVE:
if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_ACTIVATING) {
rc = spdk_nvmf_poll_group_add_subsystem(group, ctx->subsystem);
} else if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_RESUMING) {
rc = spdk_nvmf_poll_group_resume_subsystem(group, ctx->subsystem);
}
break;
case SPDK_NVMF_SUBSYSTEM_PAUSED:
rc = spdk_nvmf_poll_group_pause_subsystem(group, ctx->subsystem);
break;
default:
assert(false);
break;
}
spdk_for_each_channel_continue(i, rc);
}
static void
spdk_nvmf_subsystem_state_change(struct spdk_nvmf_subsystem *subsystem,
enum spdk_nvmf_subsystem_state requested_state,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg)
{
struct subsystem_state_change_ctx *ctx;
enum spdk_nvmf_subsystem_state intermediate_state;
int rc;
switch (requested_state) {
case SPDK_NVMF_SUBSYSTEM_INACTIVE:
intermediate_state = SPDK_NVMF_SUBSYSTEM_DEACTIVATING;
break;
case SPDK_NVMF_SUBSYSTEM_ACTIVE:
if (subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED) {
intermediate_state = SPDK_NVMF_SUBSYSTEM_RESUMING;
} else {
intermediate_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING;
}
break;
case SPDK_NVMF_SUBSYSTEM_PAUSED:
intermediate_state = SPDK_NVMF_SUBSYSTEM_PAUSING;
break;
default:
assert(false);
cb_fn(subsystem, cb_arg, -EINVAL);
return;
}
ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
cb_fn(subsystem, cb_arg, -ENOMEM);
return;
}
rc = spdk_nvmf_subsystem_set_state(subsystem, intermediate_state);
if (rc) {
free(ctx);
cb_fn(subsystem, cb_arg, -1);
return;
}
ctx->subsystem = subsystem;
ctx->requested_state = requested_state;
ctx->cb_fn = cb_fn;
ctx->cb_arg = cb_arg;
spdk_for_each_channel(subsystem->tgt,
spdk_nvmf_subsystem_remove_from_poll_group,
subsystem,
spdk_nvmf_subsystem_delete_done);
subsystem_state_change_on_pg,
ctx,
subsystem_state_change_done);
}
void
spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg)
{
spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg);
}
void
spdk_nvmf_subsystem_stop(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg)
{
spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_INACTIVE, cb_fn, cb_arg);
}
void
spdk_nvmf_subsystem_pause(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg)
{
spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_PAUSED, cb_fn, cb_arg);
}
void
spdk_nvmf_subsystem_resume(struct spdk_nvmf_subsystem *subsystem,
spdk_nvmf_subsystem_state_change_done cb_fn,
void *cb_arg)
{
spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg);
}
struct spdk_nvmf_subsystem *
@ -427,29 +561,6 @@ spdk_nvmf_listener_get_trid(struct spdk_nvmf_listener *listener)
return &listener->trid;
}
static void
spdk_nvmf_remove_ns_done(struct spdk_io_channel_iter *i, int status)
{
struct spdk_nvmf_ns *ns = spdk_io_channel_iter_get_ctx(i);
struct spdk_nvmf_subsystem *subsystem = ns->subsystem;
spdk_bdev_close(ns->desc);
ns->allocated = false;
subsystem->num_allocated_nsid--;
}
static void
spdk_nvmf_subsystem_remove_ns_from_poll_group(struct spdk_io_channel_iter *i)
{
struct spdk_nvmf_ns *ns = spdk_io_channel_iter_get_ctx(i);
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
int rc;
rc = spdk_nvmf_poll_group_remove_ns(group, ns);
spdk_for_each_channel_continue(i, rc);
}
int
spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid)
{
@ -464,31 +575,13 @@ spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t ns
return -1;
}
spdk_for_each_channel(ns->subsystem->tgt,
spdk_nvmf_subsystem_remove_ns_from_poll_group,
ns,
spdk_nvmf_remove_ns_done);
spdk_bdev_close(ns->desc);
ns->allocated = false;
subsystem->num_allocated_nsid--;
return 0;
}
static void
spdk_nvmf_subsystem_add_ns_done(struct spdk_io_channel_iter *i, int status)
{
return;
}
static void
spdk_nvmf_subsystem_ns_update_poll_group(struct spdk_io_channel_iter *i)
{
struct spdk_nvmf_ns *ns = spdk_io_channel_iter_get_ctx(i);
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
int rc;
rc = spdk_nvmf_poll_group_add_ns(group, ns->subsystem, ns);
spdk_for_each_channel_continue(i, rc);
}
static void
_spdk_nvmf_ns_hot_remove(void *ctx)
{
@ -516,6 +609,9 @@ spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bd
uint32_t i;
int rc;
assert(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE ||
subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED);
if (nsid == SPDK_NVME_GLOBAL_NS_TAG) {
SPDK_ERRLOG("Invalid NSID %" PRIu32 "\n", nsid);
return 0;
@ -590,17 +686,6 @@ spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bd
subsystem->max_nsid = spdk_max(subsystem->max_nsid, nsid);
subsystem->num_allocated_nsid++;
/* Send a message to each poll group to notify it that a new namespace
* is available.
* TODO: This call does not currently allow the user to wait for these
* messages to propagate. It also does not protect against two calls
* to this function overlapping
*/
spdk_for_each_channel(subsystem->tgt,
spdk_nvmf_subsystem_ns_update_poll_group,
ns,
spdk_nvmf_subsystem_add_ns_done);
return nsid;
}

View File

@ -160,16 +160,15 @@ spdk_nvmf_poll_group_remove_subsystem(struct spdk_nvmf_poll_group *group,
}
int
spdk_nvmf_poll_group_remove_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_pause_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
return 0;
}
int
spdk_nvmf_poll_group_add_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_resume_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
return 0;
}
@ -200,7 +199,7 @@ test_discovery_log(void)
struct spdk_nvme_transport_id trid = {};
/* Add one subsystem and verify that the discovery log contains it */
subsystem = spdk_nvmf_create_subsystem(&tgt, "nqn.2016-06.io.spdk:subsystem1",
subsystem = spdk_nvmf_subsystem_create(&tgt, "nqn.2016-06.io.spdk:subsystem1",
SPDK_NVMF_SUBTYPE_NVME, 0);
SPDK_CU_ASSERT_FATAL(subsystem != NULL);
@ -249,7 +248,7 @@ test_discovery_log(void)
offsetof(struct spdk_nvmf_discovery_log_page, entries[0]),
sizeof(*entry));
CU_ASSERT(entry->trtype == 42);
spdk_nvmf_delete_subsystem(subsystem);
spdk_nvmf_subsystem_destroy(subsystem);
free(tgt.subsystems);
free(tgt.discovery_log_page);
}

View File

@ -117,16 +117,15 @@ spdk_nvmf_poll_group_remove_subsystem(struct spdk_nvmf_poll_group *group,
}
int
spdk_nvmf_poll_group_remove_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_pause_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
return 0;
}
int
spdk_nvmf_poll_group_add_ns(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem,
struct spdk_nvmf_ns *ns)
spdk_nvmf_poll_group_resume_subsystem(struct spdk_nvmf_poll_group *group,
struct spdk_nvmf_subsystem *subsystem)
{
return 0;
}
@ -240,27 +239,27 @@ nvmf_test_create_subsystem(void)
struct spdk_nvmf_subsystem *subsystem;
strncpy(nqn, "nqn.2016-06.io.spdk:subsystem1", sizeof(nqn));
subsystem = spdk_nvmf_create_subsystem(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
subsystem = spdk_nvmf_subsystem_create(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
SPDK_CU_ASSERT_FATAL(subsystem != NULL);
CU_ASSERT_STRING_EQUAL(subsystem->subnqn, nqn);
spdk_nvmf_delete_subsystem(subsystem);
spdk_nvmf_subsystem_destroy(subsystem);
/* Longest valid name */
strncpy(nqn, "nqn.2016-06.io.spdk:", sizeof(nqn));
memset(nqn + strlen(nqn), 'a', 223 - strlen(nqn));
nqn[223] = '\0';
CU_ASSERT(strlen(nqn) == 223);
subsystem = spdk_nvmf_create_subsystem(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
subsystem = spdk_nvmf_subsystem_create(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
SPDK_CU_ASSERT_FATAL(subsystem != NULL);
CU_ASSERT_STRING_EQUAL(subsystem->subnqn, nqn);
spdk_nvmf_delete_subsystem(subsystem);
spdk_nvmf_subsystem_destroy(subsystem);
/* Name that is one byte longer than allowed */
strncpy(nqn, "nqn.2016-06.io.spdk:", sizeof(nqn));
memset(nqn + strlen(nqn), 'a', 224 - strlen(nqn));
nqn[224] = '\0';
CU_ASSERT(strlen(nqn) == 224);
subsystem = spdk_nvmf_create_subsystem(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
subsystem = spdk_nvmf_subsystem_create(&tgt, nqn, SPDK_NVMF_SUBTYPE_NVME, 0);
CU_ASSERT(subsystem == NULL);
free(tgt.subsystems);