Commit 55470149 authored by Daniel Verkamp's avatar Daniel Verkamp
Browse files

nvmf: simplify property handling



Create a list of valid properties with get and set callbacks (set is
optional to allow read-only fields).

Remove handling for fields declared as "reserved" in the NVMe over
Fabrics 1.0 specification.

Also simplify the vcprop structure to only contain the required fields.

Change-Id: I14d3ddfd008c62b75fce8e64d193c87fb6f7b5ad
Signed-off-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
parent e2256173
Loading
Loading
Loading
Loading
+10 −133
Original line number Diff line number Diff line
@@ -253,137 +253,8 @@ struct spdk_nvmf_fabric_connect_rsp {
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_fabric_connect_rsp) == 16, "Incorrect size");

#define SPDK_NVMF_PROP_CAP_OFST		0x0
#define SPDK_NVMF_PROP_VS_OFST		0x8
#define SPDK_NVMF_PROP_INTMS_OFST	0xC
#define SPDK_NVMF_PROP_INTMC_OFST	0x10
#define SPDK_NVMF_PROP_CC_OFST		0x14
#define SPDK_NVMF_PROP_CSTS_OFST	0x1C
#define SPDK_NVMF_PROP_NSSR_OFST	0x20
#define SPDK_NVMF_PROP_AQA_OFST		0x24
#define SPDK_NVMF_PROP_ASQ_OFST		0x28
#define SPDK_NVMF_PROP_ACQ_OFST		0x30
#define SPDK_NVMF_PROP_CMBLOC_OFST	0x38
#define SPDK_NVMF_PROP_CMBSZ_OFST	0x3C

#define SPDK_NVMF_PROP_CAP_LEN		0x8
#define SPDK_NVMF_PROP_VS_LEN		0x4
#define SPDK_NVMF_PROP_INTMS_LEN	0x4
#define SPDK_NVMF_PROP_INTMC_LEN	0x4
#define SPDK_NVMF_PROP_CC_LEN		0x4
#define SPDK_NVMF_PROP_CSTS_LEN		0x4
#define SPDK_NVMF_PROP_NSSR_LEN		0x4
#define SPDK_NVMF_PROP_AQA_LEN		0x4
#define SPDK_NVMF_PROP_ASQ_LEN		0x8
#define SPDK_NVMF_PROP_ACQ_LEN		0x8
#define SPDK_NVMF_PROP_CMBLOC_LEN	0x4
#define SPDK_NVMF_PROP_CMBSZ_LEN	0x4

union spdk_nvmf_property_size {
	uint32_t	raw;
	struct {
		uint32_t reserved	: 16;

		/** property address space size */
		uint32_t size		: 16;
	} bits;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvmf_property_size) == 4, "Incorrect size");

union spdk_nvmf_capsule_attr_lo {
	uint32_t	raw;
	struct {
		/** maximum response capsule size */
		uint32_t rspsz		: 16;

		/** maximum command capsule size */
		uint32_t cmdsz		: 16;
	} bits;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvmf_capsule_attr_lo) == 4, "Incorrect size");

union spdk_nvmf_capsule_attr_hi {
	uint32_t	raw;
	struct {
		/** support capsule alignment in response capsules */
		uint32_t reserved	: 26;

		/** support capsule alignment in response capsules */
		uint32_t cairsp		: 1;

		/** support capsule alignment in command capsules */
		uint32_t caicmd		: 1;

		/** support capsule metadata in response capsules */
		uint32_t cmirsp		: 1;

		/** support capsule metadata in command capsules */
		uint32_t cmicmd		: 1;

		/** support capsule data in response capsules */
		uint32_t cdirsp		: 1;

		/** support capsule data in command capsules */
		uint32_t cdicmd		: 1;
	} bits;
};
SPDK_STATIC_ASSERT(sizeof(union spdk_nvmf_capsule_attr_hi) == 4, "Incorrect size");

struct spdk_nvmf_ctrlr_properties {
	union spdk_nvme_cap_register		cap;

	uint32_t				vs;
	uint32_t				intms;
	uint32_t				intmc;

	union spdk_nvme_cc_register		cc;

	uint32_t				reserved1;
	union spdk_nvme_csts_register		csts;
	uint32_t				nssr;

	union spdk_nvme_aqa_register		aqa;

	uint64_t				asq;
	uint64_t				acq;

	uint32_t				cmbloc;
	uint32_t				cmbsz;

	uint8_t					reserved2[0xEC0];
	uint8_t					reserved3[0x100];
	union spdk_nvmf_property_size		propsz;
	uint32_t				reserved4;
	union spdk_nvmf_capsule_attr_lo		capattr_lo;
	union spdk_nvmf_capsule_attr_hi		capattr_hi;
	uint8_t					reserved5[0x2F0];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_ctrlr_properties) == 4864, "Incorrect size");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_CAP_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, cap),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_VS_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, vs),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_INTMS_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, intms),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_INTMC_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, intmc),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_CC_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, cc),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_CSTS_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, csts),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_NSSR_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, nssr),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_AQA_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, aqa),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_ASQ_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, asq),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_ACQ_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, acq),
		   "Incorrect register offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_CMBLOC_OFST == offsetof(struct spdk_nvmf_ctrlr_properties,
		   cmbloc),
		   "Incorrect property offset");
SPDK_STATIC_ASSERT(SPDK_NVMF_PROP_CMBSZ_OFST == offsetof(struct spdk_nvmf_ctrlr_properties, cmbsz),
		   "Incorrect property offset");
#define SPDK_NVMF_PROP_SIZE_4	0
#define SPDK_NVMF_PROP_SIZE_8	1

struct spdk_nvmf_fabric_prop_get_cmd {
	uint8_t		opcode;
@@ -391,7 +262,10 @@ struct spdk_nvmf_fabric_prop_get_cmd {
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved2[35];
	uint8_t		attrib;
	struct {
		uint8_t size		: 2;
		uint8_t reserved	: 6;
	} attrib;
	uint8_t		reserved3[3];
	uint32_t	ofst;
	uint8_t		reserved4[16];
@@ -420,7 +294,10 @@ struct spdk_nvmf_fabric_prop_set_cmd {
	uint16_t	cid;
	uint8_t		fctype;
	uint8_t		reserved1[35];
	uint8_t		attrib;
	struct {
		uint8_t size		: 2;
		uint8_t reserved	: 6;
	} attrib;
	uint8_t		reserved2[3];
	uint32_t	ofst;

+135 −148
Original line number Diff line number Diff line
@@ -111,7 +111,10 @@ nvmf_init_discovery_session_properties(struct nvmf_session *session)
	session->vcprop.cap.bits.mpsmin = 0; /* 2 ^ 12 + mpsmin == 4k */
	session->vcprop.cap.bits.mpsmax = 0; /* 2 ^ 12 + mpsmax == 4k */

	session->vcprop.vs = 0x10000;	/* Version Supported: Major 1, Minor 0 */
	/* Version Supported: 1.0 */
	session->vcprop.vs.bits.mjr = 1;
	session->vcprop.vs.bits.mnr = 0;
	session->vcprop.vs.bits.ter = 0;

	session->vcprop.cc.raw = 0;

@@ -181,7 +184,10 @@ nvmf_init_nvme_session_properties(struct nvmf_session *session, int aq_depth)
	session->vcprop.cap.bits.mpsmin = 0; /* 2 ^ 12 + mpsmin == 4k */
	session->vcprop.cap.bits.mpsmax = 0; /* 2 ^ 12 + mpsmax == 4k */

	session->vcprop.vs = 0x10000;	/* Version Supported: Major 1, Minor 0 */
	/* Version Supported: 1.0 */
	session->vcprop.vs.bits.mjr = 1;
	session->vcprop.vs.bits.mnr = 0;
	session->vcprop.vs.bits.ter = 0;

	session->vcprop.cc.raw = 0;
	session->vcprop.cc.bits.en = 0; /* Init controller disabled */
@@ -189,33 +195,14 @@ nvmf_init_nvme_session_properties(struct nvmf_session *session, int aq_depth)
	session->vcprop.csts.raw = 0;
	session->vcprop.csts.bits.rdy = 0; /* Init controller as not ready */

	/* nssr not defined for v1.0 */

	/* Set AQA details to reflect the virtual connection SQ/CQ depth */
	session->vcprop.aqa.bits.asqs = (aq_depth & 0xFFF);
	session->vcprop.aqa.bits.acqs = (aq_depth & 0xFFF);

	session->vcprop.propsz.bits.size = sizeof(struct spdk_nvmf_ctrlr_properties) / 64;
	session->vcprop.capattr_hi.raw = 0;
	session->vcprop.capattr_lo.bits.rspsz = sizeof(union nvmf_c2h_msg) / 16;
	session->vcprop.capattr_lo.bits.cmdsz = sizeof(union nvmf_h2c_msg) / 16;

	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: max io queues %x\n",
		      session->max_io_queues);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: cap %" PRIx64 "\n",
		      session->vcprop.cap.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: vs %x\n", session->vcprop.vs);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: vs %x\n", session->vcprop.vs.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: cc %x\n", session->vcprop.cc.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: csts %x\n",
		      session->vcprop.csts.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: nssr %x\n", session->vcprop.nssr);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: aqa %x\n", session->vcprop.aqa.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: propsz %x\n",
		      session->vcprop.propsz.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: capattr_lo %x\n",
		      session->vcprop.capattr_lo.raw);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "	nvmf_init_session_properties: capattr_hi %x\n",
		      session->vcprop.capattr_hi.raw);
}

void
@@ -376,92 +363,126 @@ nvmf_complete_cmd(void *ctx, const struct spdk_nvme_cpl *cmp)
	spdk_nvmf_request_complete(req);
}

static uint64_t
nvmf_prop_get_cap(struct nvmf_session *session)
{
	return session->vcprop.cap.raw;
}

static uint64_t
nvmf_prop_get_vs(struct nvmf_session *session)
{
	return session->vcprop.vs.raw;
}

static uint64_t
nvmf_prop_get_cc(struct nvmf_session *session)
{
	return session->vcprop.cc.raw;
}

static bool
nvmf_prop_set_cc(struct nvmf_session *session, uint64_t value)
{
	union spdk_nvme_cc_register cc;

	cc.raw = (uint32_t)value;

	if (cc.bits.en && !session->vcprop.cc.bits.en) {
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CC Enable!\n");
		session->vcprop.csts.bits.rdy = 1;
	}

	if (cc.bits.shn && !session->vcprop.cc.bits.shn) {
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CC Shutdown!\n");
		session->vcprop.cc.bits.en = 0;
	}

	session->vcprop.cc.raw = cc.raw;
	return true;
}

static uint64_t
nvmf_prop_get_csts(struct nvmf_session *session)
{
	return session->vcprop.csts.raw;
}

struct nvmf_prop {
	uint32_t ofst;
	uint8_t size;
	char name[11];
	uint64_t (*get_cb)(struct nvmf_session *session);
	bool (*set_cb)(struct nvmf_session *session, uint64_t value);
};

#define PROP(field, size, get_cb, set_cb) \
	{ \
		offsetof(struct spdk_nvme_registers, field), \
		SPDK_NVMF_PROP_SIZE_##size, \
		#field, \
		get_cb, set_cb \
	}

static const struct nvmf_prop nvmf_props[] = {
	PROP(cap,  8, nvmf_prop_get_cap,  NULL),
	PROP(vs,   4, nvmf_prop_get_vs,   NULL),
	PROP(cc,   4, nvmf_prop_get_cc,   nvmf_prop_set_cc),
	PROP(csts, 4, nvmf_prop_get_csts, NULL),
};

static const struct nvmf_prop *
find_prop(uint32_t ofst)
{
	size_t i;

	for (i = 0; i < sizeof(nvmf_props) / sizeof(*nvmf_props); i++) {
		const struct nvmf_prop *prop = &nvmf_props[i];

		if (prop->ofst == ofst) {
			return prop;
		}
	}

	return NULL;
}

void
nvmf_property_get(struct nvmf_session *session,
		  struct spdk_nvmf_fabric_prop_get_cmd *cmd,
		  struct spdk_nvmf_fabric_prop_get_rsp *response)
{
	const struct nvmf_prop *prop;

	response->status.sc = 0;
	response->value.u64 = 0;

	SPDK_TRACELOG(SPDK_TRACE_NVMF, "nvmf_property_get: attrib %d, offset %x\n",
		      cmd->attrib, cmd->ofst);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "size %d, offset 0x%x\n",
		      cmd->attrib.size, cmd->ofst);

	if (cmd->ofst > offsetof(struct spdk_nvmf_ctrlr_properties, capattr_hi)) {
	if (cmd->attrib.size != SPDK_NVMF_PROP_SIZE_4 &&
	    cmd->attrib.size != SPDK_NVMF_PROP_SIZE_8) {
		SPDK_ERRLOG("Invalid size value %d\n", cmd->attrib.size);
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		return;
	}

	switch (cmd->ofst) {
	case (offsetof(struct spdk_nvmf_ctrlr_properties, cap)):
		response->value.u32.low = session->vcprop.cap.raw;
		if (cmd->attrib == 1)
			response->value.u64 = session->vcprop.cap.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, cap) + 4):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.cap.raw >> 32;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, vs)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.vs;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, intms)):
	case (offsetof(struct spdk_nvmf_ctrlr_properties, intmc)):
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, cc)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.cc.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, csts)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.csts.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, nssr)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.nssr;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, aqa)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.aqa.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, asq)):
	case (offsetof(struct spdk_nvmf_ctrlr_properties, acq)):
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, propsz)):
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.propsz.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, capattr_lo)):
		response->value.u32.low = session->vcprop.capattr_lo.raw;
		if (cmd->attrib == 1)
			response->value.u32.high = session->vcprop.capattr_hi.raw;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, capattr_hi)):
		if (cmd->attrib == 1)
	prop = find_prop(cmd->ofst);
	if (prop == NULL || prop->get_cb == NULL) {
		/* Reserved properties return 0 when read */
		return;
	}

	SPDK_TRACELOG(SPDK_TRACE_NVMF, "name: %s\n", prop->name);
	if (cmd->attrib.size != prop->size) {
		SPDK_ERRLOG("offset 0x%x size mismatch: cmd %u, prop %u\n",
			    cmd->ofst, cmd->attrib.size, prop->size);
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			response->value.u32.low = session->vcprop.capattr_hi.raw;
		break;
	default:
		break;
		return;
	}

	response->value.u64 = prop->get_cb(session);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "response value: 0x%" PRIx64 "\n", response->value.u64);
}

void
@@ -470,70 +491,36 @@ nvmf_property_set(struct nvmf_session *session,
		  struct spdk_nvmf_fabric_prop_set_rsp *response,
		  bool *shutdown)
{
	response->status.sc = 0;
	const struct nvmf_prop *prop;
	uint64_t value;

	SPDK_TRACELOG(SPDK_TRACE_NVMF,
		      "nvmf_property_set: attrib %d, offset %x, value %lx, value low %x, value high %x\n",
		      cmd->attrib, cmd->ofst, cmd->value.u64, cmd->value.u32.low, cmd->value.u32.high);
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "size %d, offset 0x%x, value 0x%" PRIx64 "\n",
		      cmd->attrib.size, cmd->ofst, cmd->value.u64);

	if (cmd->ofst > offsetof(struct spdk_nvmf_ctrlr_properties, capattr_hi)) {
	prop = find_prop(cmd->ofst);
	if (prop == NULL || prop->set_cb == NULL) {
		SPDK_ERRLOG("Invalid offset 0x%x\n", cmd->ofst);
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		return;
	}

	/* TBD: determine which values we allow to be changed, deal with spec version
		difference.  Fields within 32bit value, ex. for reset in csts */

	switch (cmd->ofst) {
	case (offsetof(struct spdk_nvmf_ctrlr_properties, cc)): {
		union spdk_nvme_cc_register cc;

		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CC\n");
		if (cmd->attrib == 1)
	SPDK_TRACELOG(SPDK_TRACE_NVMF, "name: %s\n", prop->name);
	if (cmd->attrib.size != prop->size) {
		SPDK_ERRLOG("offset 0x%x size mismatch: cmd %u, prop %u\n",
			    cmd->ofst, cmd->attrib.size, prop->size);
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else {
			cc.raw = cmd->value.u32.low;

			if (cc.bits.en == 1 && session->vcprop.cc.bits.en == 0) {
				SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CC Enable!\n");
				session->vcprop.csts.bits.rdy = 1;
		return;
	}

			if (cc.bits.shn && session->vcprop.cc.bits.shn == 0) {
				SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CC Shutdown!\n");
				session->vcprop.cc.bits.en = 0;
				*shutdown = true;
	value = cmd->value.u64;
	if (prop->size == SPDK_NVMF_PROP_SIZE_4) {
		value = (uint32_t)value;
	}

			session->vcprop.cc.raw = cc.raw;
		}
	}
	break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, csts)):
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set CSTS\n");
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			session->vcprop.csts.raw = cmd->value.u32.low;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, nssr)):
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set NSSR\n");
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			session->vcprop.nssr = cmd->value.u32.low;
		break;
	case (offsetof(struct spdk_nvmf_ctrlr_properties, aqa)):
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set AQA\n");
		if (cmd->attrib == 1)
			response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		else
			session->vcprop.aqa.raw = cmd->value.u32.low;
		break;
	default:
		SPDK_TRACELOG(SPDK_TRACE_NVMF, "Property Set Invalid Offset %x\n", cmd->ofst);
	if (!prop->set_cb(session, value)) {
		SPDK_ERRLOG("prop set_cb failed\n");
		response->status.sc = SPDK_NVMF_FABRIC_SC_INVALID_PARAM;
		break;
		return;
	}
}

+6 −1
Original line number Diff line number Diff line
@@ -68,7 +68,12 @@ struct nvmf_session {
	uint32_t	max_io_queues; /* maximum supported by backend NVMe library */
	int		active_queues;
	int		is_valid;
	struct spdk_nvmf_ctrlr_properties	vcprop;	/* virtual controller properties */
	struct {
		union spdk_nvme_cap_register	cap;
		union spdk_nvme_vs_register	vs;
		union spdk_nvme_cc_register	cc;
		union spdk_nvme_csts_register	csts;
	} vcprop; /* virtual controller properties */
	struct spdk_nvme_ctrlr_data	vcdata; /* virtual controller data */

	TAILQ_HEAD(connection_q, nvmf_connection_entry) connections;