]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/lib/nvmf/session.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / spdk / lib / nvmf / session.c
1 /*-
2 * BSD LICENSE
3 *
4 * Copyright (c) Intel Corporation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
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
16 * distribution.
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.
20 *
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.
32 */
33
34 #include <arpa/inet.h>
35 #include <stdint.h>
36 #include <string.h>
37
38 #include "session.h"
39 #include "nvmf_internal.h"
40 #include "request.h"
41 #include "subsystem.h"
42 #include "transport.h"
43
44 #include "spdk/trace.h"
45 #include "spdk/nvme_spec.h"
46 #include "spdk/util.h"
47
48 #include "spdk_internal/log.h"
49
50 #define MIN_KEEP_ALIVE_TIMEOUT 10000
51
52 static void
53 nvmf_init_discovery_session_properties(struct spdk_nvmf_session *session)
54 {
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;
66
67 strncpy((char *)session->vcdata.subnqn, SPDK_NVMF_DISCOVERY_NQN, sizeof(session->vcdata.subnqn));
68
69 /* Properties */
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 */
78
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;
84
85 session->vcprop.cc.raw = 0;
86
87 session->vcprop.csts.raw = 0;
88 session->vcprop.csts.bits.rdy = 0; /* Init controller as not ready */
89 }
90
91 static void
92 nvmf_init_nvme_session_properties(struct spdk_nvmf_session *session)
93 {
94 assert((g_nvmf_tgt.max_io_size % 4096) == 0);
95
96 /* Init the controller details */
97 session->subsys->ops->ctrlr_get_data(session);
98
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;
106
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 */
112
113 /* TODO: this should be set by the transport */
114 session->vcdata.nvmf_specific.ioccsz += g_nvmf_tgt.in_capsule_data_size / 16;
115
116 strncpy((char *)session->vcdata.subnqn, session->subsys->subnqn, sizeof(session->vcdata.subnqn));
117
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);
132
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 */
142
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;
149 }
150
151 session->vcprop.cc.raw = 0;
152 session->vcprop.cc.bits.en = 0; /* Init controller disabled */
153
154 session->vcprop.csts.raw = 0;
155 session->vcprop.csts.bits.rdy = 0; /* Init controller as not ready */
156
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);
163 }
164
165 static void session_destruct(struct spdk_nvmf_session *session)
166 {
167 TAILQ_REMOVE(&session->subsys->sessions, session, link);
168 session->transport->session_fini(session);
169 }
170
171 void
172 spdk_nvmf_session_destruct(struct spdk_nvmf_session *session)
173 {
174 while (!TAILQ_EMPTY(&session->connections)) {
175 struct spdk_nvmf_conn *conn = TAILQ_FIRST(&session->connections);
176
177 TAILQ_REMOVE(&session->connections, conn, link);
178 session->num_connections--;
179 conn->transport->conn_fini(conn);
180 }
181
182 session_destruct(session);
183 }
184
185 static void
186 invalid_connect_response(struct spdk_nvmf_fabric_connect_rsp *rsp, uint8_t iattr, uint16_t ipo)
187 {
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;
192 }
193
194 static uint16_t
195 spdk_nvmf_session_gen_cntlid(void)
196 {
197 static uint16_t cntlid = 0; /* cntlid is static, so its value is preserved */
198 struct spdk_nvmf_subsystem *subsystem;
199 uint16_t count;
200
201 count = UINT16_MAX - 1;
202 do {
203 /* cntlid is an unsigned 16-bit integer, so let it overflow
204 * back to 0 if necessary.
205 */
206 cntlid++;
207 if (cntlid == 0) {
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
210 * initiators.
211 */
212 cntlid++;
213 }
214
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.
218 */
219 subsystem = spdk_nvmf_find_subsystem_with_cntlid(cntlid);
220
221 count--;
222
223 } while (subsystem != NULL && count > 0);
224
225 if (count == 0) {
226 return 0;
227 }
228
229 return cntlid;
230 }
231
232 void
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)
237 {
238 struct spdk_nvmf_session *session;
239 struct spdk_nvmf_subsystem *subsystem;
240
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))
243
244 SPDK_TRACELOG(SPDK_TRACE_NVMF, "recfmt 0x%x qid %u sqsize %u\n",
245 cmd->recfmt, cmd->qid, cmd->sqsize);
246
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]),
253 data->hostid[8],
254 data->hostid[9],
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);
259
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);
264 return;
265 }
266
267 /*
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.
270 */
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);
275 return;
276 }
277 conn->sq_head_max = cmd->sqsize;
278
279 if (cmd->qid == 0) {
280 conn->type = CONN_TYPE_AQ;
281
282 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Connect Admin Queue for controller ID 0x%x\n", data->cntlid);
283
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);
288 return;
289 }
290
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;
296 return;
297 }
298
299 TAILQ_INIT(&session->connections);
300
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;
306 return;
307 }
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;
313
314 memcpy(session->hostid, data->hostid, sizeof(session->hostid));
315
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);
319 free(session);
320 return;
321 }
322
323 if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) {
324 nvmf_init_nvme_session_properties(session);
325 } else {
326 nvmf_init_discovery_session_properties(session);
327 }
328
329 TAILQ_INSERT_TAIL(&subsystem->sessions, session, link);
330 } else {
331 struct spdk_nvmf_session *tmp;
332
333 conn->type = CONN_TYPE_IOQ;
334 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Connect I/O Queue for controller id 0x%x\n", data->cntlid);
335
336 session = NULL;
337 TAILQ_FOREACH(tmp, &subsystem->sessions, link) {
338 if (tmp->cntlid == data->cntlid) {
339 session = tmp;
340 break;
341 }
342 }
343 if (session == NULL) {
344 SPDK_ERRLOG("Unknown controller ID 0x%x\n", data->cntlid);
345 INVALID_CONNECT_DATA(cntlid);
346 return;
347 }
348
349 if (!session->vcprop.cc.bits.en) {
350 SPDK_ERRLOG("Got I/O connect before ctrlr was enabled\n");
351 INVALID_CONNECT_CMD(qid);
352 return;
353 }
354
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);
359 return;
360 }
361
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);
366 return;
367 }
368
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;
374 return;
375 }
376
377 if (conn->transport->session_add_conn(session, conn)) {
378 INVALID_CONNECT_CMD(qid);
379 return;
380 }
381 }
382
383 session->num_connections++;
384 TAILQ_INSERT_HEAD(&session->connections, conn, link);
385 conn->sess = session;
386
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);
391 }
392
393 void
394 spdk_nvmf_session_disconnect(struct spdk_nvmf_conn *conn)
395 {
396 struct spdk_nvmf_session *session = conn->sess;
397
398 assert(session != NULL);
399 session->num_connections--;
400 TAILQ_REMOVE(&session->connections, conn, link);
401
402 conn->transport->session_remove_conn(session, conn);
403 conn->transport->conn_fini(conn);
404
405 if (session->num_connections == 0) {
406 session_destruct(session);
407 }
408 }
409
410 static uint64_t
411 nvmf_prop_get_cap(struct spdk_nvmf_session *session)
412 {
413 return session->vcprop.cap.raw;
414 }
415
416 static uint64_t
417 nvmf_prop_get_vs(struct spdk_nvmf_session *session)
418 {
419 return session->vcprop.vs.raw;
420 }
421
422 static uint64_t
423 nvmf_prop_get_cc(struct spdk_nvmf_session *session)
424 {
425 return session->vcprop.cc.raw;
426 }
427
428 static bool
429 nvmf_prop_set_cc(struct spdk_nvmf_session *session, uint64_t value)
430 {
431 union spdk_nvme_cc_register cc, diff;
432
433 cc.raw = (uint32_t)value;
434
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);
437
438 /*
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.
441 */
442 diff.raw = cc.raw ^ session->vcprop.cc.raw;
443
444 if (diff.bits.en) {
445 if (cc.bits.en) {
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;
449 } else {
450 SPDK_ERRLOG("CC.EN transition from 1 to 0 (reset) not implemented!\n");
451
452 }
453 diff.bits.en = 0;
454 }
455
456 if (diff.bits.shn) {
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;
467 } else {
468 SPDK_ERRLOG("Prop Set CC: Invalid SHN value %u%ub\n",
469 cc.bits.shn >> 1, cc.bits.shn & 1);
470 return false;
471 }
472 diff.bits.shn = 0;
473 }
474
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;
480 }
481
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;
487 }
488
489 if (diff.raw != 0) {
490 SPDK_ERRLOG("Prop Set CC toggled reserved bits 0x%x!\n", diff.raw);
491 return false;
492 }
493
494 return true;
495 }
496
497 static uint64_t
498 nvmf_prop_get_csts(struct spdk_nvmf_session *session)
499 {
500 return session->vcprop.csts.raw;
501 }
502
503 struct nvmf_prop {
504 uint32_t ofst;
505 uint8_t size;
506 char name[11];
507 uint64_t (*get_cb)(struct spdk_nvmf_session *session);
508 bool (*set_cb)(struct spdk_nvmf_session *session, uint64_t value);
509 };
510
511 #define PROP(field, size, get_cb, set_cb) \
512 { \
513 offsetof(struct spdk_nvme_registers, field), \
514 SPDK_NVMF_PROP_SIZE_##size, \
515 #field, \
516 get_cb, set_cb \
517 }
518
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),
524 };
525
526 static const struct nvmf_prop *
527 find_prop(uint32_t ofst)
528 {
529 size_t i;
530
531 for (i = 0; i < SPDK_COUNTOF(nvmf_props); i++) {
532 const struct nvmf_prop *prop = &nvmf_props[i];
533
534 if (prop->ofst == ofst) {
535 return prop;
536 }
537 }
538
539 return NULL;
540 }
541
542 void
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)
546 {
547 const struct nvmf_prop *prop;
548
549 response->status.sc = 0;
550 response->value.u64 = 0;
551
552 SPDK_TRACELOG(SPDK_TRACE_NVMF, "size %d, offset 0x%x\n",
553 cmd->attrib.size, cmd->ofst);
554
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;
559 return;
560 }
561
562 prop = find_prop(cmd->ofst);
563 if (prop == NULL || prop->get_cb == NULL) {
564 /* Reserved properties return 0 when read */
565 return;
566 }
567
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;
573 return;
574 }
575
576 response->value.u64 = prop->get_cb(session);
577 SPDK_TRACELOG(SPDK_TRACE_NVMF, "response value: 0x%" PRIx64 "\n", response->value.u64);
578 }
579
580 void
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)
584 {
585 const struct nvmf_prop *prop;
586 uint64_t value;
587
588 SPDK_TRACELOG(SPDK_TRACE_NVMF, "size %d, offset 0x%x, value 0x%" PRIx64 "\n",
589 cmd->attrib.size, cmd->ofst, cmd->value.u64);
590
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;
595 return;
596 }
597
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;
603 return;
604 }
605
606 value = cmd->value.u64;
607 if (prop->size == SPDK_NVMF_PROP_SIZE_4) {
608 value = (uint32_t)value;
609 }
610
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;
614 return;
615 }
616 }
617
618 int
619 spdk_nvmf_session_poll(struct spdk_nvmf_session *session)
620 {
621 struct spdk_nvmf_conn *conn, *tmp;
622 struct spdk_nvmf_subsystem *subsys = session->subsys;
623
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;
627
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;
633 }
634 }
635
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);
640 }
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);
645 }
646 }
647 }
648 }
649
650 return 0;
651 }
652
653 int
654 spdk_nvmf_session_set_features_host_identifier(struct spdk_nvmf_request *req)
655 {
656 struct spdk_nvme_cpl *response = &req->rsp->nvme_cpl;
657
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;
661 }
662
663 int
664 spdk_nvmf_session_get_features_host_identifier(struct spdk_nvmf_request *req)
665 {
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;
669
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;
676 }
677
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;
682 }
683
684 memcpy(req->data, session->hostid, sizeof(session->hostid));
685 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
686 }
687
688 int
689 spdk_nvmf_session_set_features_keep_alive_timer(struct spdk_nvmf_request *req)
690 {
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;
694
695 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Set Features - Keep Alive Timer (%u ms)\n", cmd->cdw11);
696
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;
701 } else {
702 session->kato = cmd->cdw11;
703 }
704
705 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Set Features - Keep Alive Timer set to %u ms\n", session->kato);
706
707 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
708 }
709
710 int
711 spdk_nvmf_session_get_features_keep_alive_timer(struct spdk_nvmf_request *req)
712 {
713 struct spdk_nvmf_session *session = req->conn->sess;
714 struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
715
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;
719 }
720
721 int
722 spdk_nvmf_session_set_features_number_of_queues(struct spdk_nvmf_request *req)
723 {
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;
727
728 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Set Features - Number of Queues, cdw11 0x%x\n",
729 req->cmd->nvme_cmd.cdw11);
730
731 /* Extra 1 connection for Admin queue */
732 nr_io_queues = session->max_connections_allowed - 1;
733
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;
738 } else {
739 /* Number of IO queues has a zero based value */
740 rsp->cdw0 = ((nr_io_queues - 1) << 16) |
741 (nr_io_queues - 1);
742 }
743
744 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
745 }
746
747 int
748 spdk_nvmf_session_get_features_number_of_queues(struct spdk_nvmf_request *req)
749 {
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;
753
754 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Get Features - Number of Queues\n");
755
756 nr_io_queues = session->max_connections_allowed - 1;
757
758 /* Number of IO queues has a zero based value */
759 rsp->cdw0 = ((nr_io_queues - 1) << 16) |
760 (nr_io_queues - 1);
761
762 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
763 }
764
765 int
766 spdk_nvmf_session_set_features_async_event_configuration(struct spdk_nvmf_request *req)
767 {
768 struct spdk_nvmf_session *session = req->conn->sess;
769 struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
770
771 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Set Features - Async Event Configuration, cdw11 0x%08x\n",
772 cmd->cdw11);
773 session->async_event_config.raw = cmd->cdw11;
774 return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
775 }
776
777 int
778 spdk_nvmf_session_get_features_async_event_configuration(struct spdk_nvmf_request *req)
779 {
780 struct spdk_nvmf_session *session = req->conn->sess;
781 struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
782
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;
786 }
787
788 int
789 spdk_nvmf_session_async_event_request(struct spdk_nvmf_request *req)
790 {
791 struct spdk_nvmf_session *session = req->conn->sess;
792 struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
793
794 SPDK_TRACELOG(SPDK_TRACE_NVMF, "Async Event Request\n");
795
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;
802 }
803
804 session->aer_req = req;
805 return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
806 }