4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <arpa/inet.h>
39 #include "nvmf_internal.h"
41 #include "subsystem.h"
42 #include "transport.h"
44 #include "spdk/trace.h"
45 #include "spdk/nvme_spec.h"
46 #include "spdk/util.h"
48 #include "spdk_internal/log.h"
50 #define MIN_KEEP_ALIVE_TIMEOUT 10000
53 nvmf_init_discovery_session_properties(struct spdk_nvmf_session
*session
)
55 session
->vcdata
.maxcmd
= g_nvmf_tgt
.max_queue_depth
;
56 /* extended data for get log page supportted */
57 session
->vcdata
.lpa
.edlp
= 1;
58 session
->vcdata
.cntlid
= session
->cntlid
;
59 session
->vcdata
.nvmf_specific
.ioccsz
= sizeof(struct spdk_nvme_cmd
) / 16;
60 session
->vcdata
.nvmf_specific
.iorcsz
= sizeof(struct spdk_nvme_cpl
) / 16;
61 session
->vcdata
.nvmf_specific
.icdoff
= 0; /* offset starts directly after SQE */
62 session
->vcdata
.nvmf_specific
.ctrattr
.ctrlr_model
= SPDK_NVMF_CTRLR_MODEL_DYNAMIC
;
63 session
->vcdata
.nvmf_specific
.msdbd
= 1; /* target supports single SGL in capsule */
64 session
->vcdata
.sgls
.keyed_sgl
= 1;
65 session
->vcdata
.sgls
.sgl_offset
= 1;
67 strncpy((char *)session
->vcdata
.subnqn
, SPDK_NVMF_DISCOVERY_NQN
, sizeof(session
->vcdata
.subnqn
));
70 session
->vcprop
.cap
.raw
= 0;
71 session
->vcprop
.cap
.bits
.cqr
= 1; /* NVMF specification required */
72 session
->vcprop
.cap
.bits
.mqes
= (session
->vcdata
.maxcmd
- 1); /* max queue depth */
73 session
->vcprop
.cap
.bits
.ams
= 0; /* optional arb mechanisms */
74 session
->vcprop
.cap
.bits
.dstrd
= 0; /* fixed to 0 for NVMf */
75 session
->vcprop
.cap
.bits
.css_nvm
= 1; /* NVM command set */
76 session
->vcprop
.cap
.bits
.mpsmin
= 0; /* 2 ^ 12 + mpsmin == 4k */
77 session
->vcprop
.cap
.bits
.mpsmax
= 0; /* 2 ^ 12 + mpsmax == 4k */
79 /* Version Supported: 1.2.1 */
80 session
->vcprop
.vs
.bits
.mjr
= 1;
81 session
->vcprop
.vs
.bits
.mnr
= 2;
82 session
->vcprop
.vs
.bits
.ter
= 1;
83 session
->vcdata
.ver
= session
->vcprop
.vs
;
85 session
->vcprop
.cc
.raw
= 0;
87 session
->vcprop
.csts
.raw
= 0;
88 session
->vcprop
.csts
.bits
.rdy
= 0; /* Init controller as not ready */
92 nvmf_init_nvme_session_properties(struct spdk_nvmf_session
*session
)
94 assert((g_nvmf_tgt
.max_io_size
% 4096) == 0);
96 /* Init the controller details */
97 session
->subsys
->ops
->ctrlr_get_data(session
);
99 session
->vcdata
.aerl
= 0;
100 session
->vcdata
.cntlid
= session
->cntlid
;
101 session
->vcdata
.kas
= 10;
102 session
->vcdata
.maxcmd
= g_nvmf_tgt
.max_queue_depth
;
103 session
->vcdata
.mdts
= spdk_u32log2(g_nvmf_tgt
.max_io_size
/ 4096);
104 session
->vcdata
.sgls
.keyed_sgl
= 1;
105 session
->vcdata
.sgls
.sgl_offset
= 1;
107 session
->vcdata
.nvmf_specific
.ioccsz
= sizeof(struct spdk_nvme_cmd
) / 16;
108 session
->vcdata
.nvmf_specific
.iorcsz
= sizeof(struct spdk_nvme_cpl
) / 16;
109 session
->vcdata
.nvmf_specific
.icdoff
= 0; /* offset starts directly after SQE */
110 session
->vcdata
.nvmf_specific
.ctrattr
.ctrlr_model
= SPDK_NVMF_CTRLR_MODEL_DYNAMIC
;
111 session
->vcdata
.nvmf_specific
.msdbd
= 1; /* target supports single SGL in capsule */
113 /* TODO: this should be set by the transport */
114 session
->vcdata
.nvmf_specific
.ioccsz
+= g_nvmf_tgt
.in_capsule_data_size
/ 16;
116 strncpy((char *)session
->vcdata
.subnqn
, session
->subsys
->subnqn
, sizeof(session
->vcdata
.subnqn
));
118 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ctrlr data: maxcmd %x\n",
119 session
->vcdata
.maxcmd
);
120 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ext ctrlr data: ioccsz %x\n",
121 session
->vcdata
.nvmf_specific
.ioccsz
);
122 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ext ctrlr data: iorcsz %x\n",
123 session
->vcdata
.nvmf_specific
.iorcsz
);
124 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ext ctrlr data: icdoff %x\n",
125 session
->vcdata
.nvmf_specific
.icdoff
);
126 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ext ctrlr data: ctrattr %x\n",
127 *(uint8_t *)&session
->vcdata
.nvmf_specific
.ctrattr
);
128 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " ext ctrlr data: msdbd %x\n",
129 session
->vcdata
.nvmf_specific
.msdbd
);
130 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " sgls data: 0x%x\n",
131 *(uint32_t *)&session
->vcdata
.sgls
);
133 session
->vcprop
.cap
.raw
= 0;
134 session
->vcprop
.cap
.bits
.cqr
= 1;
135 session
->vcprop
.cap
.bits
.mqes
= (session
->vcdata
.maxcmd
- 1); /* max queue depth */
136 session
->vcprop
.cap
.bits
.ams
= 0; /* optional arb mechanisms */
137 session
->vcprop
.cap
.bits
.to
= 1; /* ready timeout - 500 msec units */
138 session
->vcprop
.cap
.bits
.dstrd
= 0; /* fixed to 0 for NVMf */
139 session
->vcprop
.cap
.bits
.css_nvm
= 1; /* NVM command set */
140 session
->vcprop
.cap
.bits
.mpsmin
= 0; /* 2 ^ 12 + mpsmin == 4k */
141 session
->vcprop
.cap
.bits
.mpsmax
= 0; /* 2 ^ 12 + mpsmax == 4k */
143 /* Report at least version 1.2.1 */
144 if (session
->vcprop
.vs
.raw
< SPDK_NVME_VERSION(1, 2, 1)) {
145 session
->vcprop
.vs
.bits
.mjr
= 1;
146 session
->vcprop
.vs
.bits
.mnr
= 2;
147 session
->vcprop
.vs
.bits
.ter
= 1;
148 session
->vcdata
.ver
= session
->vcprop
.vs
;
151 session
->vcprop
.cc
.raw
= 0;
152 session
->vcprop
.cc
.bits
.en
= 0; /* Init controller disabled */
154 session
->vcprop
.csts
.raw
= 0;
155 session
->vcprop
.csts
.bits
.rdy
= 0; /* Init controller as not ready */
157 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " cap %" PRIx64
"\n",
158 session
->vcprop
.cap
.raw
);
159 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " vs %x\n", session
->vcprop
.vs
.raw
);
160 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " cc %x\n", session
->vcprop
.cc
.raw
);
161 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " csts %x\n",
162 session
->vcprop
.csts
.raw
);
165 static void session_destruct(struct spdk_nvmf_session
*session
)
167 TAILQ_REMOVE(&session
->subsys
->sessions
, session
, link
);
168 session
->transport
->session_fini(session
);
172 spdk_nvmf_session_destruct(struct spdk_nvmf_session
*session
)
174 while (!TAILQ_EMPTY(&session
->connections
)) {
175 struct spdk_nvmf_conn
*conn
= TAILQ_FIRST(&session
->connections
);
177 TAILQ_REMOVE(&session
->connections
, conn
, link
);
178 session
->num_connections
--;
179 conn
->transport
->conn_fini(conn
);
182 session_destruct(session
);
186 invalid_connect_response(struct spdk_nvmf_fabric_connect_rsp
*rsp
, uint8_t iattr
, uint16_t ipo
)
188 rsp
->status
.sct
= SPDK_NVME_SCT_COMMAND_SPECIFIC
;
189 rsp
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
190 rsp
->status_code_specific
.invalid
.iattr
= iattr
;
191 rsp
->status_code_specific
.invalid
.ipo
= ipo
;
195 spdk_nvmf_session_gen_cntlid(void)
197 static uint16_t cntlid
= 0; /* cntlid is static, so its value is preserved */
198 struct spdk_nvmf_subsystem
*subsystem
;
201 count
= UINT16_MAX
- 1;
203 /* cntlid is an unsigned 16-bit integer, so let it overflow
204 * back to 0 if necessary.
208 /* 0 is not a valid cntlid because it is the reserved value in the RDMA
209 * private data for cntlid. This is the value sent by pre-NVMe-oF 1.1
215 /* Check if a subsystem with this cntlid currently exists. This could
216 * happen for a very long-lived session on a target with many short-lived
217 * sessions, where cntlid wraps around.
219 subsystem
= spdk_nvmf_find_subsystem_with_cntlid(cntlid
);
223 } while (subsystem
!= NULL
&& count
> 0);
233 spdk_nvmf_session_connect(struct spdk_nvmf_conn
*conn
,
234 struct spdk_nvmf_fabric_connect_cmd
*cmd
,
235 struct spdk_nvmf_fabric_connect_data
*data
,
236 struct spdk_nvmf_fabric_connect_rsp
*rsp
)
238 struct spdk_nvmf_session
*session
;
239 struct spdk_nvmf_subsystem
*subsystem
;
241 #define INVALID_CONNECT_CMD(field) invalid_connect_response(rsp, 0, offsetof(struct spdk_nvmf_fabric_connect_cmd, field))
242 #define INVALID_CONNECT_DATA(field) invalid_connect_response(rsp, 1, offsetof(struct spdk_nvmf_fabric_connect_data, field))
244 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "recfmt 0x%x qid %u sqsize %u\n",
245 cmd
->recfmt
, cmd
->qid
, cmd
->sqsize
);
247 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Connect data:\n");
248 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " cntlid: 0x%04x\n", data
->cntlid
);
249 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " hostid: %08x-%04x-%04x-%02x%02x-%04x%08x ***\n",
250 ntohl(*(uint32_t *)&data
->hostid
[0]),
251 ntohs(*(uint16_t *)&data
->hostid
[4]),
252 ntohs(*(uint16_t *)&data
->hostid
[6]),
255 ntohs(*(uint16_t *)&data
->hostid
[10]),
256 ntohl(*(uint32_t *)&data
->hostid
[12]));
257 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " subnqn: \"%s\"\n", data
->subnqn
);
258 SPDK_TRACELOG(SPDK_TRACE_NVMF
, " hostnqn: \"%s\"\n", data
->hostnqn
);
260 subsystem
= nvmf_find_subsystem(data
->subnqn
);
261 if (subsystem
== NULL
) {
262 SPDK_ERRLOG("Could not find subsystem '%s'\n", data
->subnqn
);
263 INVALID_CONNECT_DATA(subnqn
);
268 * SQSIZE is a 0-based value, so it must be at least 1 (minimum queue depth is 2) and
269 * strictly less than max_queue_depth.
271 if (cmd
->sqsize
== 0 || cmd
->sqsize
>= g_nvmf_tgt
.max_queue_depth
) {
272 SPDK_ERRLOG("Invalid SQSIZE %u (min 1, max %u)\n",
273 cmd
->sqsize
, g_nvmf_tgt
.max_queue_depth
- 1);
274 INVALID_CONNECT_CMD(sqsize
);
277 conn
->sq_head_max
= cmd
->sqsize
;
280 conn
->type
= CONN_TYPE_AQ
;
282 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Connect Admin Queue for controller ID 0x%x\n", data
->cntlid
);
284 if (data
->cntlid
!= 0xFFFF) {
285 /* This NVMf target only supports dynamic mode. */
286 SPDK_ERRLOG("The NVMf target only supports dynamic mode (CNTLID = 0x%x).\n", data
->cntlid
);
287 INVALID_CONNECT_DATA(cntlid
);
291 /* Establish a new session */
292 session
= conn
->transport
->session_init();
293 if (session
== NULL
) {
294 SPDK_ERRLOG("Memory allocation failure\n");
295 rsp
->status
.sc
= SPDK_NVME_SC_INTERNAL_DEVICE_ERROR
;
299 TAILQ_INIT(&session
->connections
);
301 session
->cntlid
= spdk_nvmf_session_gen_cntlid();
302 if (session
->cntlid
== 0) {
303 /* Unable to get a cntlid */
304 SPDK_ERRLOG("Reached max simultaneous sessions\n");
305 rsp
->status
.sc
= SPDK_NVME_SC_INTERNAL_DEVICE_ERROR
;
308 session
->kato
= cmd
->kato
;
309 session
->async_event_config
.raw
= 0;
310 session
->num_connections
= 0;
311 session
->subsys
= subsystem
;
312 session
->max_connections_allowed
= g_nvmf_tgt
.max_queues_per_session
;
314 memcpy(session
->hostid
, data
->hostid
, sizeof(session
->hostid
));
316 if (conn
->transport
->session_add_conn(session
, conn
)) {
317 rsp
->status
.sc
= SPDK_NVME_SC_INTERNAL_DEVICE_ERROR
;
318 conn
->transport
->session_fini(session
);
323 if (subsystem
->subtype
== SPDK_NVMF_SUBTYPE_NVME
) {
324 nvmf_init_nvme_session_properties(session
);
326 nvmf_init_discovery_session_properties(session
);
329 TAILQ_INSERT_TAIL(&subsystem
->sessions
, session
, link
);
331 struct spdk_nvmf_session
*tmp
;
333 conn
->type
= CONN_TYPE_IOQ
;
334 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Connect I/O Queue for controller id 0x%x\n", data
->cntlid
);
337 TAILQ_FOREACH(tmp
, &subsystem
->sessions
, link
) {
338 if (tmp
->cntlid
== data
->cntlid
) {
343 if (session
== NULL
) {
344 SPDK_ERRLOG("Unknown controller ID 0x%x\n", data
->cntlid
);
345 INVALID_CONNECT_DATA(cntlid
);
349 if (!session
->vcprop
.cc
.bits
.en
) {
350 SPDK_ERRLOG("Got I/O connect before ctrlr was enabled\n");
351 INVALID_CONNECT_CMD(qid
);
355 if (1u << session
->vcprop
.cc
.bits
.iosqes
!= sizeof(struct spdk_nvme_cmd
)) {
356 SPDK_ERRLOG("Got I/O connect with invalid IOSQES %u\n",
357 session
->vcprop
.cc
.bits
.iosqes
);
358 INVALID_CONNECT_CMD(qid
);
362 if (1u << session
->vcprop
.cc
.bits
.iocqes
!= sizeof(struct spdk_nvme_cpl
)) {
363 SPDK_ERRLOG("Got I/O connect with invalid IOCQES %u\n",
364 session
->vcprop
.cc
.bits
.iocqes
);
365 INVALID_CONNECT_CMD(qid
);
369 /* check if we would exceed session connection limit */
370 if (session
->num_connections
>= session
->max_connections_allowed
) {
371 SPDK_ERRLOG("connection limit %d\n", session
->num_connections
);
372 rsp
->status
.sct
= SPDK_NVME_SCT_COMMAND_SPECIFIC
;
373 rsp
->status
.sc
= SPDK_NVMF_FABRIC_SC_CONTROLLER_BUSY
;
377 if (conn
->transport
->session_add_conn(session
, conn
)) {
378 INVALID_CONNECT_CMD(qid
);
383 session
->num_connections
++;
384 TAILQ_INSERT_HEAD(&session
->connections
, conn
, link
);
385 conn
->sess
= session
;
387 rsp
->status
.sc
= SPDK_NVME_SC_SUCCESS
;
388 rsp
->status_code_specific
.success
.cntlid
= session
->vcdata
.cntlid
;
389 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "connect capsule response: cntlid = 0x%04x\n",
390 rsp
->status_code_specific
.success
.cntlid
);
394 spdk_nvmf_session_disconnect(struct spdk_nvmf_conn
*conn
)
396 struct spdk_nvmf_session
*session
= conn
->sess
;
398 assert(session
!= NULL
);
399 session
->num_connections
--;
400 TAILQ_REMOVE(&session
->connections
, conn
, link
);
402 conn
->transport
->session_remove_conn(session
, conn
);
403 conn
->transport
->conn_fini(conn
);
405 if (session
->num_connections
== 0) {
406 session_destruct(session
);
411 nvmf_prop_get_cap(struct spdk_nvmf_session
*session
)
413 return session
->vcprop
.cap
.raw
;
417 nvmf_prop_get_vs(struct spdk_nvmf_session
*session
)
419 return session
->vcprop
.vs
.raw
;
423 nvmf_prop_get_cc(struct spdk_nvmf_session
*session
)
425 return session
->vcprop
.cc
.raw
;
429 nvmf_prop_set_cc(struct spdk_nvmf_session
*session
, uint64_t value
)
431 union spdk_nvme_cc_register cc
, diff
;
433 cc
.raw
= (uint32_t)value
;
435 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "cur CC: 0x%08x\n", session
->vcprop
.cc
.raw
);
436 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "new CC: 0x%08x\n", cc
.raw
);
439 * Calculate which bits changed between the current and new CC.
440 * Mark each bit as 0 once it is handled to determine if any unhandled bits were changed.
442 diff
.raw
= cc
.raw
^ session
->vcprop
.cc
.raw
;
446 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Property Set CC Enable!\n");
447 session
->vcprop
.cc
.bits
.en
= 1;
448 session
->vcprop
.csts
.bits
.rdy
= 1;
450 SPDK_ERRLOG("CC.EN transition from 1 to 0 (reset) not implemented!\n");
457 if (cc
.bits
.shn
== SPDK_NVME_SHN_NORMAL
||
458 cc
.bits
.shn
== SPDK_NVME_SHN_ABRUPT
) {
459 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Property Set CC Shutdown %u%ub!\n",
460 cc
.bits
.shn
>> 1, cc
.bits
.shn
& 1);
461 session
->vcprop
.cc
.bits
.shn
= cc
.bits
.shn
;
462 session
->vcprop
.cc
.bits
.en
= 0;
463 session
->vcprop
.csts
.bits
.rdy
= 0;
464 session
->vcprop
.csts
.bits
.shst
= SPDK_NVME_SHST_COMPLETE
;
465 } else if (cc
.bits
.shn
== 0) {
466 session
->vcprop
.cc
.bits
.shn
= 0;
468 SPDK_ERRLOG("Prop Set CC: Invalid SHN value %u%ub\n",
469 cc
.bits
.shn
>> 1, cc
.bits
.shn
& 1);
475 if (diff
.bits
.iosqes
) {
476 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Prop Set IOSQES = %u (%u bytes)\n",
477 cc
.bits
.iosqes
, 1u << cc
.bits
.iosqes
);
478 session
->vcprop
.cc
.bits
.iosqes
= cc
.bits
.iosqes
;
479 diff
.bits
.iosqes
= 0;
482 if (diff
.bits
.iocqes
) {
483 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Prop Set IOCQES = %u (%u bytes)\n",
484 cc
.bits
.iocqes
, 1u << cc
.bits
.iocqes
);
485 session
->vcprop
.cc
.bits
.iocqes
= cc
.bits
.iocqes
;
486 diff
.bits
.iocqes
= 0;
490 SPDK_ERRLOG("Prop Set CC toggled reserved bits 0x%x!\n", diff
.raw
);
498 nvmf_prop_get_csts(struct spdk_nvmf_session
*session
)
500 return session
->vcprop
.csts
.raw
;
507 uint64_t (*get_cb
)(struct spdk_nvmf_session
*session
);
508 bool (*set_cb
)(struct spdk_nvmf_session
*session
, uint64_t value
);
511 #define PROP(field, size, get_cb, set_cb) \
513 offsetof(struct spdk_nvme_registers, field), \
514 SPDK_NVMF_PROP_SIZE_##size, \
519 static const struct nvmf_prop nvmf_props
[] = {
520 PROP(cap
, 8, nvmf_prop_get_cap
, NULL
),
521 PROP(vs
, 4, nvmf_prop_get_vs
, NULL
),
522 PROP(cc
, 4, nvmf_prop_get_cc
, nvmf_prop_set_cc
),
523 PROP(csts
, 4, nvmf_prop_get_csts
, NULL
),
526 static const struct nvmf_prop
*
527 find_prop(uint32_t ofst
)
531 for (i
= 0; i
< SPDK_COUNTOF(nvmf_props
); i
++) {
532 const struct nvmf_prop
*prop
= &nvmf_props
[i
];
534 if (prop
->ofst
== ofst
) {
543 spdk_nvmf_property_get(struct spdk_nvmf_session
*session
,
544 struct spdk_nvmf_fabric_prop_get_cmd
*cmd
,
545 struct spdk_nvmf_fabric_prop_get_rsp
*response
)
547 const struct nvmf_prop
*prop
;
549 response
->status
.sc
= 0;
550 response
->value
.u64
= 0;
552 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "size %d, offset 0x%x\n",
553 cmd
->attrib
.size
, cmd
->ofst
);
555 if (cmd
->attrib
.size
!= SPDK_NVMF_PROP_SIZE_4
&&
556 cmd
->attrib
.size
!= SPDK_NVMF_PROP_SIZE_8
) {
557 SPDK_ERRLOG("Invalid size value %d\n", cmd
->attrib
.size
);
558 response
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
562 prop
= find_prop(cmd
->ofst
);
563 if (prop
== NULL
|| prop
->get_cb
== NULL
) {
564 /* Reserved properties return 0 when read */
568 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "name: %s\n", prop
->name
);
569 if (cmd
->attrib
.size
!= prop
->size
) {
570 SPDK_ERRLOG("offset 0x%x size mismatch: cmd %u, prop %u\n",
571 cmd
->ofst
, cmd
->attrib
.size
, prop
->size
);
572 response
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
576 response
->value
.u64
= prop
->get_cb(session
);
577 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "response value: 0x%" PRIx64
"\n", response
->value
.u64
);
581 spdk_nvmf_property_set(struct spdk_nvmf_session
*session
,
582 struct spdk_nvmf_fabric_prop_set_cmd
*cmd
,
583 struct spdk_nvme_cpl
*response
)
585 const struct nvmf_prop
*prop
;
588 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "size %d, offset 0x%x, value 0x%" PRIx64
"\n",
589 cmd
->attrib
.size
, cmd
->ofst
, cmd
->value
.u64
);
591 prop
= find_prop(cmd
->ofst
);
592 if (prop
== NULL
|| prop
->set_cb
== NULL
) {
593 SPDK_ERRLOG("Invalid offset 0x%x\n", cmd
->ofst
);
594 response
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
598 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "name: %s\n", prop
->name
);
599 if (cmd
->attrib
.size
!= prop
->size
) {
600 SPDK_ERRLOG("offset 0x%x size mismatch: cmd %u, prop %u\n",
601 cmd
->ofst
, cmd
->attrib
.size
, prop
->size
);
602 response
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
606 value
= cmd
->value
.u64
;
607 if (prop
->size
== SPDK_NVMF_PROP_SIZE_4
) {
608 value
= (uint32_t)value
;
611 if (!prop
->set_cb(session
, value
)) {
612 SPDK_ERRLOG("prop set_cb failed\n");
613 response
->status
.sc
= SPDK_NVMF_FABRIC_SC_INVALID_PARAM
;
619 spdk_nvmf_session_poll(struct spdk_nvmf_session
*session
)
621 struct spdk_nvmf_conn
*conn
, *tmp
;
622 struct spdk_nvmf_subsystem
*subsys
= session
->subsys
;
624 if (subsys
->is_removed
&& subsys
->mode
== NVMF_SUBSYSTEM_MODE_VIRTUAL
) {
625 if (session
->aer_req
) {
626 struct spdk_nvmf_request
*aer
= session
->aer_req
;
628 aer
->rsp
->nvme_cpl
.status
.sct
= SPDK_NVME_SCT_GENERIC
;
629 aer
->rsp
->nvme_cpl
.status
.sc
= SPDK_NVME_SC_ABORTED_SQ_DELETION
;
630 aer
->rsp
->nvme_cpl
.status
.dnr
= 0;
631 spdk_nvmf_request_complete(aer
);
632 session
->aer_req
= NULL
;
636 TAILQ_FOREACH_SAFE(conn
, &session
->connections
, link
, tmp
) {
637 if (conn
->transport
->conn_poll(conn
) < 0) {
638 SPDK_ERRLOG("Transport poll failed for conn %p; closing connection\n", conn
);
639 spdk_nvmf_session_disconnect(conn
);
641 if (subsys
->subtype
== SPDK_NVMF_SUBTYPE_NVME
) {
642 if (subsys
->is_removed
&& conn
->transport
->conn_is_idle(conn
)) {
643 if (subsys
->ops
->detach
) {
644 subsys
->ops
->detach(subsys
);
654 spdk_nvmf_session_set_features_host_identifier(struct spdk_nvmf_request
*req
)
656 struct spdk_nvme_cpl
*response
= &req
->rsp
->nvme_cpl
;
658 SPDK_ERRLOG("Set Features - Host Identifier not allowed\n");
659 response
->status
.sc
= SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR
;
660 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
664 spdk_nvmf_session_get_features_host_identifier(struct spdk_nvmf_request
*req
)
666 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
667 struct spdk_nvme_cmd
*cmd
= &req
->cmd
->nvme_cmd
;
668 struct spdk_nvme_cpl
*response
= &req
->rsp
->nvme_cpl
;
670 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Get Features - Host Identifier\n");
671 if (!(cmd
->cdw11
& 1)) {
672 /* NVMe over Fabrics requires EXHID=1 (128-bit/16-byte host ID) */
673 SPDK_ERRLOG("Get Features - Host Identifier with EXHID=0 not allowed\n");
674 response
->status
.sc
= SPDK_NVME_SC_INVALID_FIELD
;
675 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
678 if (req
->data
== NULL
|| req
->length
< sizeof(session
->hostid
)) {
679 SPDK_ERRLOG("Invalid data buffer for Get Features - Host Identifier\n");
680 response
->status
.sc
= SPDK_NVME_SC_INVALID_FIELD
;
681 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
684 memcpy(req
->data
, session
->hostid
, sizeof(session
->hostid
));
685 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
689 spdk_nvmf_session_set_features_keep_alive_timer(struct spdk_nvmf_request
*req
)
691 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
692 struct spdk_nvme_cmd
*cmd
= &req
->cmd
->nvme_cmd
;
693 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
695 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Set Features - Keep Alive Timer (%u ms)\n", cmd
->cdw11
);
697 if (cmd
->cdw11
== 0) {
698 rsp
->status
.sc
= SPDK_NVME_SC_KEEP_ALIVE_INVALID
;
699 } else if (cmd
->cdw11
< MIN_KEEP_ALIVE_TIMEOUT
) {
700 session
->kato
= MIN_KEEP_ALIVE_TIMEOUT
;
702 session
->kato
= cmd
->cdw11
;
705 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Set Features - Keep Alive Timer set to %u ms\n", session
->kato
);
707 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
711 spdk_nvmf_session_get_features_keep_alive_timer(struct spdk_nvmf_request
*req
)
713 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
714 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
716 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Get Features - Keep Alive Timer\n");
717 rsp
->cdw0
= session
->kato
;
718 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
722 spdk_nvmf_session_set_features_number_of_queues(struct spdk_nvmf_request
*req
)
724 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
725 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
726 uint32_t nr_io_queues
;
728 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Set Features - Number of Queues, cdw11 0x%x\n",
729 req
->cmd
->nvme_cmd
.cdw11
);
731 /* Extra 1 connection for Admin queue */
732 nr_io_queues
= session
->max_connections_allowed
- 1;
734 /* verify that the contoller is ready to process commands */
735 if (session
->num_connections
> 1) {
736 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Queue pairs already active!\n");
737 rsp
->status
.sc
= SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR
;
739 /* Number of IO queues has a zero based value */
740 rsp
->cdw0
= ((nr_io_queues
- 1) << 16) |
744 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
748 spdk_nvmf_session_get_features_number_of_queues(struct spdk_nvmf_request
*req
)
750 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
751 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
752 uint32_t nr_io_queues
;
754 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Get Features - Number of Queues\n");
756 nr_io_queues
= session
->max_connections_allowed
- 1;
758 /* Number of IO queues has a zero based value */
759 rsp
->cdw0
= ((nr_io_queues
- 1) << 16) |
762 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
766 spdk_nvmf_session_set_features_async_event_configuration(struct spdk_nvmf_request
*req
)
768 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
769 struct spdk_nvme_cmd
*cmd
= &req
->cmd
->nvme_cmd
;
771 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Set Features - Async Event Configuration, cdw11 0x%08x\n",
773 session
->async_event_config
.raw
= cmd
->cdw11
;
774 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
778 spdk_nvmf_session_get_features_async_event_configuration(struct spdk_nvmf_request
*req
)
780 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
781 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
783 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Get Features - Async Event Configuration\n");
784 rsp
->cdw0
= session
->async_event_config
.raw
;
785 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
789 spdk_nvmf_session_async_event_request(struct spdk_nvmf_request
*req
)
791 struct spdk_nvmf_session
*session
= req
->conn
->sess
;
792 struct spdk_nvme_cpl
*rsp
= &req
->rsp
->nvme_cpl
;
794 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "Async Event Request\n");
796 assert(session
->vcdata
.aerl
+ 1 == 1);
797 if (session
->aer_req
!= NULL
) {
798 SPDK_TRACELOG(SPDK_TRACE_NVMF
, "AERL exceeded\n");
799 rsp
->status
.sct
= SPDK_NVME_SCT_COMMAND_SPECIFIC
;
800 rsp
->status
.sc
= SPDK_NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED
;
801 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE
;
804 session
->aer_req
= req
;
805 return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS
;