nvmf: move subsytem poller to nvmf_tgt app

The application is now entirely responsible for scheduling subsystem
pollers and sending events between threads.

Change-Id: I88da1f53b5e8852c7c4acd6f0a7a1e2219fbed41
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Daniel Verkamp 2016-08-25 15:00:50 -07:00
parent 9f2fc78e3f
commit bc0867dcaf
12 changed files with 215 additions and 131 deletions

View File

@ -82,11 +82,11 @@ struct spdk_nvmf_tgt_conf g_spdk_nvmf_tgt_conf;
static int
spdk_add_nvmf_discovery_subsystem(void)
{
struct spdk_nvmf_subsystem *subsystem;
struct nvmf_tgt_subsystem *app_subsys;
subsystem = nvmf_create_subsystem(0, SPDK_NVMF_DISCOVERY_NQN, SPDK_NVMF_SUBTYPE_DISCOVERY,
rte_get_master_lcore());
if (subsystem == NULL) {
app_subsys = nvmf_tgt_create_subsystem(0, SPDK_NVMF_DISCOVERY_NQN, SPDK_NVMF_SUBTYPE_DISCOVERY,
rte_get_master_lcore());
if (app_subsys == NULL) {
SPDK_ERRLOG("Failed creating discovery nvmf library subsystem\n");
return -1;
}
@ -354,6 +354,7 @@ static int
spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
{
const char *nqn, *mode;
struct nvmf_tgt_subsystem *app_subsys;
struct spdk_nvmf_subsystem *subsystem;
int i, ret;
uint64_t mask;
@ -377,14 +378,16 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
}
lcore = spdk_nvmf_allocate_lcore(mask, lcore);
subsystem = nvmf_create_subsystem(sp->num, nqn, SPDK_NVMF_SUBTYPE_NVME, lcore);
if (subsystem == NULL) {
app_subsys = nvmf_tgt_create_subsystem(sp->num, nqn, SPDK_NVMF_SUBTYPE_NVME, lcore);
if (app_subsys == NULL) {
SPDK_ERRLOG("Subsystem createion failed\n");
return -1;
}
subsystem = app_subsys->subsystem;
mode = spdk_conf_section_get_val(sp, "Mode");
if (mode == NULL) {
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
SPDK_ERRLOG("No Mode specified for Subsystem %d\n", sp->num);
return -1;
}
@ -394,7 +397,7 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
} else if (strcasecmp(mode, "Virtual") == 0) {
subsystem->mode = NVMF_SUBSYSTEM_MODE_VIRTUAL;
} else {
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
SPDK_ERRLOG("Invalid Subsystem mode: %s\n", mode);
return -1;
}
@ -450,7 +453,7 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
bdf = spdk_conf_section_get_val(sp, "NVMe");
if (bdf == NULL) {
SPDK_ERRLOG("Subsystem %d: missing NVMe directive\n", sp->num);
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
@ -477,18 +480,18 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
sn = spdk_conf_section_get_val(sp, "SN");
if (sn == NULL) {
SPDK_ERRLOG("Subsystem %d: missing serial number\n", sp->num);
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
if (spdk_nvmf_validate_sn(sn) != 0) {
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
namespace = spdk_conf_section_get_val(sp, "Namespace");
if (namespace == NULL) {
SPDK_ERRLOG("Subsystem %d: missing Namespace directive\n", sp->num);
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
@ -504,19 +507,19 @@ spdk_nvmf_parse_subsystem(struct spdk_conf_section *sp)
namespace = spdk_conf_section_get_nmval(sp, "Namespace", i, 0);
if (!namespace) {
SPDK_ERRLOG("Namespace %d: missing block device\n", i);
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
bdev = spdk_bdev_get_by_name(namespace);
if (!bdev) {
SPDK_ERRLOG("bdev is NULL\n");
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}
if (spdk_nvmf_subsystem_add_ns(subsystem, bdev)) {
nvmf_delete_subsystem(subsystem);
nvmf_tgt_delete_subsystem(app_subsys);
return -1;
}

View File

@ -48,6 +48,8 @@
#include "nvmf/transport.h"
#include "nvmf/subsystem.h"
#include "nvmf/request.h"
#include "nvmf/session.h"
#include "spdk/log.h"
#include "spdk/nvme.h"
@ -61,12 +63,67 @@ struct rte_mempool *request_mempool;
static struct spdk_poller *g_acceptor_poller = NULL;
static TAILQ_HEAD(, nvmf_tgt_subsystem) g_subsystems = TAILQ_HEAD_INITIALIZER(g_subsystems);
static bool g_subsystems_shutdown;
static void
shutdown_complete(void)
{
int rc;
rc = spdk_nvmf_check_pools();
spdk_app_stop(rc);
}
static void
subsystem_delete_event(struct spdk_event *event)
{
struct nvmf_tgt_subsystem *app_subsys = spdk_event_get_arg1(event);
struct spdk_nvmf_subsystem *subsystem = app_subsys->subsystem;
TAILQ_REMOVE(&g_subsystems, app_subsys, tailq);
free(app_subsys);
spdk_nvmf_delete_subsystem(subsystem);
if (g_subsystems_shutdown && TAILQ_EMPTY(&g_subsystems)) {
/* Finished shutting down all subsystems - continue the shutdown process. */
shutdown_complete();
}
}
void
nvmf_tgt_delete_subsystem(struct nvmf_tgt_subsystem *app_subsys)
{
struct spdk_event *event;
/*
* Unregister the poller - this starts a chain of events that will eventually free
* the subsystem's memory.
*/
event = spdk_event_allocate(spdk_app_get_current_core(), subsystem_delete_event,
app_subsys, NULL, NULL);
spdk_poller_unregister(&app_subsys->poller, event);
}
static void
shutdown_subsystems(void)
{
struct nvmf_tgt_subsystem *app_subsys, *tmp;
g_subsystems_shutdown = true;
TAILQ_FOREACH_SAFE(app_subsys, &g_subsystems, tailq, tmp) {
nvmf_tgt_delete_subsystem(app_subsys);
}
}
static void
acceptor_poller_unregistered_event(struct spdk_event *event)
{
spdk_nvmf_acceptor_fini();
spdk_nvmf_transport_fini();
spdk_shutdown_nvmf_subsystems();
shutdown_subsystems();
}
static void
@ -83,6 +140,82 @@ spdk_nvmf_shutdown_cb(void)
spdk_poller_unregister(&g_acceptor_poller, event);
}
static void
subsystem_poll(void *arg)
{
struct nvmf_tgt_subsystem *app_subsys = arg;
spdk_nvmf_subsystem_poll(app_subsys->subsystem);
}
static void
connect_event(struct spdk_event *event)
{
struct spdk_nvmf_request *req = spdk_event_get_arg1(event);
spdk_nvmf_handle_connect(req);
}
static void
connect_cb(void *cb_ctx, struct spdk_nvmf_request *req)
{
struct nvmf_tgt_subsystem *app_subsys = cb_ctx;
struct spdk_event *event;
/* Pass an event to the lcore that owns this subsystem */
event = spdk_event_allocate(app_subsys->lcore, connect_event, req, NULL, NULL);
spdk_event_call(event);
}
static void
disconnect_event(struct spdk_event *event)
{
struct spdk_nvmf_conn *conn = spdk_event_get_arg1(event);
spdk_nvmf_handle_disconnect(conn);
}
static void
disconnect_cb(void *cb_ctx, struct spdk_nvmf_conn *conn)
{
struct nvmf_tgt_subsystem *app_subsys = cb_ctx;
struct spdk_event *event;
/* Pass an event to the core that owns this connection */
event = spdk_event_allocate(app_subsys->lcore, disconnect_event, conn, NULL, NULL);
spdk_event_call(event);
}
struct nvmf_tgt_subsystem *
nvmf_tgt_create_subsystem(int num, const char *name, enum spdk_nvmf_subtype subtype, uint32_t lcore)
{
struct spdk_nvmf_subsystem *subsystem;
struct nvmf_tgt_subsystem *app_subsys;
app_subsys = calloc(1, sizeof(*app_subsys));
if (app_subsys == NULL) {
SPDK_ERRLOG("Subsystem allocation failed\n");
return NULL;
}
subsystem = spdk_nvmf_create_subsystem(num, name, subtype, app_subsys, connect_cb, disconnect_cb);
if (subsystem == NULL) {
SPDK_ERRLOG("Subsystem creation failed\n");
free(app_subsys);
return NULL;
}
app_subsys->subsystem = subsystem;
app_subsys->lcore = lcore;
SPDK_TRACELOG(SPDK_TRACE_NVMF, "allocated subsystem %p on lcore %u\n", subsystem, lcore);
TAILQ_INSERT_TAIL(&g_subsystems, app_subsys, tailq);
spdk_poller_register(&app_subsys->poller, subsystem_poll, app_subsys, lcore, NULL, 0);
return app_subsys;
}
static void
usage(void)
{

View File

@ -34,12 +34,33 @@
#ifndef NVMF_TGT_H
#define NVMF_TGT_H
#include <stdint.h>
#include "spdk/nvmf_spec.h"
#include "spdk/queue.h"
struct spdk_nvmf_tgt_conf {
uint32_t acceptor_lcore;
};
struct nvmf_tgt_subsystem {
struct spdk_nvmf_subsystem *subsystem;
struct spdk_poller *poller;
TAILQ_ENTRY(nvmf_tgt_subsystem) tailq;
uint32_t lcore;
};
extern struct spdk_nvmf_tgt_conf g_spdk_nvmf_tgt_conf;
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);
void nvmf_tgt_delete_subsystem(struct nvmf_tgt_subsystem *app_subsys);
#endif

View File

@ -734,22 +734,13 @@ err0:
return -1;
}
static void
spdk_nvmf_handle_disconnect(spdk_event_t event)
{
struct nvmf_session *session = spdk_event_get_arg1(event);
struct spdk_nvmf_conn *conn = spdk_event_get_arg2(event);
nvmf_disconnect(session, conn);
}
static int
nvmf_rdma_disconnect(struct rdma_cm_event *evt)
{
struct spdk_nvmf_conn *conn;
struct nvmf_session *session;
struct spdk_nvmf_subsystem *subsystem;
struct spdk_nvmf_rdma_conn *rdma_conn;
spdk_event_t event;
if (evt->id == NULL) {
SPDK_ERRLOG("disconnect request: missing cm_id\n");
@ -775,11 +766,9 @@ nvmf_rdma_disconnect(struct rdma_cm_event *evt)
return 0;
}
/* Pass an event to the core that owns this connection */
event = spdk_event_allocate(session->subsys->lcore,
spdk_nvmf_handle_disconnect,
session, conn, NULL);
spdk_event_call(event);
subsystem = session->subsys;
subsystem->disconnect_cb(subsystem->cb_ctx, conn);
return 0;
}

View File

@ -148,10 +148,9 @@ nvmf_process_property_set(struct spdk_nvmf_request *req)
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
}
static void
nvmf_handle_connect(spdk_event_t event)
void
spdk_nvmf_handle_connect(struct spdk_nvmf_request *req)
{
struct spdk_nvmf_request *req = spdk_event_get_arg1(event);
struct spdk_nvmf_fabric_connect_cmd *connect = &req->cmd->connect_cmd;
struct spdk_nvmf_fabric_connect_data *connect_data = (struct spdk_nvmf_fabric_connect_data *)
req->data;
@ -180,7 +179,6 @@ static spdk_nvmf_request_exec_status
nvmf_process_connect(struct spdk_nvmf_request *req)
{
struct spdk_nvmf_subsystem *subsystem;
spdk_event_t event;
struct spdk_nvmf_fabric_connect_data *data = (struct spdk_nvmf_fabric_connect_data *)
req->data;
struct spdk_nvmf_fabric_connect_cmd *cmd = &req->cmd->connect_cmd;
@ -224,9 +222,7 @@ nvmf_process_connect(struct spdk_nvmf_request *req)
return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
}
/* Pass an event to the lcore that owns this subsystem */
event = spdk_event_allocate(subsystem->lcore, nvmf_handle_connect, req, NULL, NULL);
spdk_event_call(event);
subsystem->connect_cb(subsystem->cb_ctx, req);
return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
}

View File

@ -73,4 +73,6 @@ spdk_nvmf_request_exec(struct spdk_nvmf_request *req);
int spdk_nvmf_request_complete(struct spdk_nvmf_request *req);
void spdk_nvmf_handle_connect(struct spdk_nvmf_request *req);
#endif

View File

@ -324,9 +324,10 @@ spdk_nvmf_session_connect(struct spdk_nvmf_conn *conn,
}
void
nvmf_disconnect(struct nvmf_session *session,
struct spdk_nvmf_conn *conn)
spdk_nvmf_handle_disconnect(struct spdk_nvmf_conn *conn)
{
struct nvmf_session *session = conn->sess;
session->num_connections--;
TAILQ_REMOVE(&session->connections, conn, link);
conn->transport->conn_fini(conn);
@ -552,7 +553,7 @@ spdk_nvmf_session_poll(struct nvmf_session *session)
TAILQ_FOREACH_SAFE(conn, &session->connections, link, tmp) {
if (conn->transport->conn_poll(conn) < 0) {
SPDK_ERRLOG("Transport poll failed for conn %p; closing connection\n", conn);
nvmf_disconnect(session, conn);
spdk_nvmf_handle_disconnect(conn);
}
}

View File

@ -93,8 +93,7 @@ void spdk_nvmf_session_connect(struct spdk_nvmf_conn *conn,
struct spdk_nvmf_fabric_connect_data *data,
struct spdk_nvmf_fabric_connect_rsp *rsp);
void
nvmf_disconnect(struct nvmf_session *session, struct spdk_nvmf_conn *conn);
void spdk_nvmf_handle_disconnect(struct spdk_nvmf_conn *conn);
void
nvmf_property_get(struct nvmf_session *session,

View File

@ -44,7 +44,6 @@
#include "spdk/nvmf_spec.h"
static TAILQ_HEAD(, spdk_nvmf_subsystem) g_subsystems = TAILQ_HEAD_INITIALIZER(g_subsystems);
bool g_subsystems_shutdown;
struct spdk_nvmf_subsystem *
nvmf_find_subsystem(const char *subnqn, const char *hostnqn)
@ -93,14 +92,6 @@ spdk_nvmf_subsystem_poll(struct spdk_nvmf_subsystem *subsystem)
spdk_nvmf_session_poll(session);
}
static void
spdk_nvmf_subsystem_poller(void *arg)
{
struct spdk_nvmf_subsystem *subsystem = arg;
spdk_nvmf_subsystem_poll(subsystem);
}
static bool
spdk_nvmf_valid_nqn(const char *nqn)
{
@ -128,9 +119,10 @@ spdk_nvmf_valid_nqn(const char *nqn)
}
struct spdk_nvmf_subsystem *
nvmf_create_subsystem(int num, const char *name,
enum spdk_nvmf_subtype subtype,
uint32_t lcore)
spdk_nvmf_create_subsystem(int num, const char *name,
enum spdk_nvmf_subtype subtype, void *cb_ctx,
spdk_nvmf_subsystem_connect_fn connect_cb,
spdk_nvmf_subsystem_disconnect_fn disconnect_cb)
{
struct spdk_nvmf_subsystem *subsystem;
@ -143,27 +135,23 @@ nvmf_create_subsystem(int num, const char *name,
return NULL;
}
SPDK_TRACELOG(SPDK_TRACE_NVMF, "nvmf_create_subsystem: allocated subsystem %p on lcore 0x%x\n",
subsystem, lcore);
subsystem->num = num;
subsystem->subtype = subtype;
subsystem->cb_ctx = cb_ctx;
subsystem->connect_cb = connect_cb;
subsystem->disconnect_cb = disconnect_cb;
snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", name);
TAILQ_INIT(&subsystem->listen_addrs);
TAILQ_INIT(&subsystem->hosts);
subsystem->lcore = lcore;
spdk_poller_register(&subsystem->poller, spdk_nvmf_subsystem_poller, subsystem, lcore, NULL, 0);
TAILQ_INSERT_HEAD(&g_subsystems, subsystem, entries);
return subsystem;
}
static void
nvmf_delete_subsystem_poller_unreg(struct spdk_event *event)
void
spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_nvmf_subsystem *subsystem = spdk_event_get_arg1(event);
struct spdk_nvmf_listen_addr *listen_addr, *listen_addr_tmp;
struct spdk_nvmf_host *host, *host_tmp;
@ -196,32 +184,6 @@ nvmf_delete_subsystem_poller_unreg(struct spdk_event *event)
TAILQ_REMOVE(&g_subsystems, subsystem, entries);
free(subsystem);
if (g_subsystems_shutdown && TAILQ_EMPTY(&g_subsystems)) {
spdk_app_stop(spdk_nvmf_check_pools());
}
}
int
nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
{
struct spdk_event *event;
if (subsystem == NULL) {
SPDK_TRACELOG(SPDK_TRACE_NVMF,
"nvmf_delete_subsystem: there is no subsystem\n");
return 0;
}
/*
* Unregister the poller - this starts a chain of events that will eventually free
* the subsystem's memory.
*/
event = spdk_event_allocate(spdk_app_get_current_core(), nvmf_delete_subsystem_poller_unreg,
subsystem, NULL, NULL);
spdk_poller_unregister(&subsystem->poller, event);
return 0;
}
int
@ -336,19 +298,6 @@ spdk_format_discovery_log(struct spdk_nvmf_discovery_log_page *disc_log, uint32_
disc_log->numrec = numrec;
}
int
spdk_shutdown_nvmf_subsystems(void)
{
struct spdk_nvmf_subsystem *subsystem, *subsys_tmp;
g_subsystems_shutdown = true;
TAILQ_FOREACH_SAFE(subsystem, &g_subsystems, entries, subsys_tmp) {
nvmf_delete_subsystem(subsystem);
}
return 0;
}
int
spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bdev *bdev)
{

View File

@ -92,6 +92,9 @@ struct spdk_nvmf_ctrlr_ops {
void (*detach)(struct spdk_nvmf_subsystem *subsystem);
};
typedef void (*spdk_nvmf_subsystem_connect_fn)(void *cb_ctx, struct spdk_nvmf_request *req);
typedef void (*spdk_nvmf_subsystem_disconnect_fn)(void *cb_ctx, struct spdk_nvmf_conn *conn);
/*
* The NVMf subsystem, as indicated in the specification, is a collection
* of virtual controller sessions. Any individual controller session has
@ -120,7 +123,9 @@ struct spdk_nvmf_subsystem {
const struct spdk_nvmf_ctrlr_ops *ops;
struct spdk_poller *poller;
void *cb_ctx;
spdk_nvmf_subsystem_connect_fn connect_cb;
spdk_nvmf_subsystem_disconnect_fn disconnect_cb;
TAILQ_HEAD(, spdk_nvmf_listen_addr) listen_addrs;
uint32_t num_listen_addrs;
@ -131,13 +136,14 @@ struct spdk_nvmf_subsystem {
TAILQ_ENTRY(spdk_nvmf_subsystem) entries;
};
struct spdk_nvmf_subsystem *
nvmf_create_subsystem(int num, const char *name,
enum spdk_nvmf_subtype subtype,
uint32_t lcore);
struct spdk_nvmf_subsystem *spdk_nvmf_create_subsystem(int num,
const char *name,
enum spdk_nvmf_subtype subtype,
void *cb_ctx,
spdk_nvmf_subsystem_connect_fn connect_cb,
spdk_nvmf_subsystem_disconnect_fn disconnect_cb);
int
nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem);
void spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem);
struct spdk_nvmf_subsystem *
nvmf_find_subsystem(const char *subnqn, const char *hostnqn);
@ -155,9 +161,6 @@ int
nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem,
struct spdk_nvme_ctrlr *ctrlr);
int
spdk_shutdown_nvmf_subsystems(void);
void
spdk_format_discovery_log(struct spdk_nvmf_discovery_log_page *disc_log, uint32_t length);

View File

@ -111,7 +111,7 @@ struct spdk_nvme_ns *spdk_nvme_ctrlr_get_ns(struct spdk_nvme_ctrlr *ctrlr, uint3
}
void
nvmf_disconnect(struct nvmf_session *session, struct spdk_nvmf_conn *conn)
spdk_nvmf_handle_disconnect(struct spdk_nvmf_conn *conn)
{
}

View File

@ -47,17 +47,6 @@ SPDK_LOG_REGISTER_TRACE_FLAG("nvmf", SPDK_TRACE_NVMF)
struct spdk_nvmf_globals g_nvmf_tgt;
void
spdk_app_stop(int rc)
{
}
int
spdk_nvmf_check_pools(void)
{
return 0;
}
uint32_t
spdk_app_get_current_core(void)
{
@ -124,29 +113,28 @@ nvmf_test_create_subsystem(void)
struct spdk_nvmf_subsystem *subsystem;
strncpy(nqn, "nqn.2016-06.io.spdk:subsystem1", sizeof(nqn));
subsystem = nvmf_create_subsystem(1, nqn, SPDK_NVMF_SUBTYPE_NVME, 1);
subsystem = spdk_nvmf_create_subsystem(1, nqn, SPDK_NVMF_SUBTYPE_NVME, NULL, NULL, NULL);
SPDK_CU_ASSERT_FATAL(subsystem != NULL);
CU_ASSERT_EQUAL(subsystem->num, 1);
CU_ASSERT_EQUAL(subsystem->lcore, 1);
CU_ASSERT_STRING_EQUAL(subsystem->subnqn, nqn);
nvmf_delete_subsystem(subsystem);
spdk_nvmf_delete_subsystem(subsystem);
/* Longest valid name */
strncpy(nqn, "nqn.2016-06.io.spdk:", sizeof(nqn));
memset(nqn + strlen(nqn), 'a', 222 - strlen(nqn));
nqn[222] = '\0';
CU_ASSERT(strlen(nqn) == 222);
subsystem = nvmf_create_subsystem(2, nqn, SPDK_NVMF_SUBTYPE_NVME, 2);
subsystem = spdk_nvmf_create_subsystem(2, nqn, SPDK_NVMF_SUBTYPE_NVME, NULL, NULL, NULL);
SPDK_CU_ASSERT_FATAL(subsystem != NULL);
CU_ASSERT_STRING_EQUAL(subsystem->subnqn, nqn);
nvmf_delete_subsystem(subsystem);
spdk_nvmf_delete_subsystem(subsystem);
/* Name that is one byte longer than allowed */
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 = nvmf_create_subsystem(2, nqn, SPDK_NVMF_SUBTYPE_NVME, 2);
subsystem = spdk_nvmf_create_subsystem(2, nqn, SPDK_NVMF_SUBTYPE_NVME, NULL, NULL, NULL);
CU_ASSERT(subsystem == NULL);
}