1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. All rights reserved. 5 * Copyright (c) 2020 Mellanox Technologies LTD. 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 /* 35 * NVMe over Fabrics transport-independent functions 36 */ 37 38 #include "nvme_internal.h" 39 40 #include "spdk/endian.h" 41 #include "spdk/string.h" 42 43 static int 44 nvme_fabric_prop_set_cmd(struct spdk_nvme_ctrlr *ctrlr, 45 uint32_t offset, uint8_t size, uint64_t value) 46 { 47 struct spdk_nvmf_fabric_prop_set_cmd cmd = {}; 48 struct nvme_completion_poll_status *status; 49 int rc; 50 51 assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8); 52 53 status = calloc(1, sizeof(*status)); 54 if (!status) { 55 SPDK_ERRLOG("Failed to allocate status tracker\n"); 56 return -ENOMEM; 57 } 58 59 cmd.opcode = SPDK_NVME_OPC_FABRIC; 60 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET; 61 cmd.ofst = offset; 62 cmd.attrib.size = size; 63 cmd.value.u64 = value; 64 65 rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd, 66 NULL, 0, 67 nvme_completion_poll_cb, status); 68 if (rc < 0) { 69 free(status); 70 return rc; 71 } 72 73 if (nvme_wait_for_completion(ctrlr->adminq, status)) { 74 if (!status->timed_out) { 75 free(status); 76 } 77 SPDK_ERRLOG("Property Set failed\n"); 78 return -1; 79 } 80 free(status); 81 82 return 0; 83 } 84 85 static int 86 nvme_fabric_prop_get_cmd(struct spdk_nvme_ctrlr *ctrlr, 87 uint32_t offset, uint8_t size, uint64_t *value) 88 { 89 struct spdk_nvmf_fabric_prop_set_cmd cmd = {}; 90 struct nvme_completion_poll_status *status; 91 struct spdk_nvmf_fabric_prop_get_rsp *response; 92 int rc; 93 94 assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8); 95 96 status = calloc(1, sizeof(*status)); 97 if (!status) { 98 SPDK_ERRLOG("Failed to allocate status tracker\n"); 99 return -ENOMEM; 100 } 101 102 cmd.opcode = SPDK_NVME_OPC_FABRIC; 103 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET; 104 cmd.ofst = offset; 105 cmd.attrib.size = size; 106 107 rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd, 108 NULL, 0, nvme_completion_poll_cb, 109 status); 110 if (rc < 0) { 111 free(status); 112 return rc; 113 } 114 115 if (nvme_wait_for_completion(ctrlr->adminq, status)) { 116 if (!status->timed_out) { 117 free(status); 118 } 119 SPDK_ERRLOG("Property Get failed\n"); 120 return -1; 121 } 122 123 response = (struct spdk_nvmf_fabric_prop_get_rsp *)&status->cpl; 124 125 if (size == SPDK_NVMF_PROP_SIZE_4) { 126 *value = response->value.u32.low; 127 } else { 128 *value = response->value.u64; 129 } 130 131 free(status); 132 133 return 0; 134 } 135 136 int 137 nvme_fabric_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value) 138 { 139 return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value); 140 } 141 142 int 143 nvme_fabric_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value) 144 { 145 return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value); 146 } 147 148 int 149 nvme_fabric_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value) 150 { 151 uint64_t tmp_value; 152 int rc; 153 rc = nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, &tmp_value); 154 155 if (!rc) { 156 *value = (uint32_t)tmp_value; 157 } 158 return rc; 159 } 160 161 int 162 nvme_fabric_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value) 163 { 164 return nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value); 165 } 166 167 static void 168 nvme_fabric_discover_probe(struct spdk_nvmf_discovery_log_page_entry *entry, 169 struct spdk_nvme_probe_ctx *probe_ctx, 170 int discover_priority) 171 { 172 struct spdk_nvme_transport_id trid; 173 uint8_t *end; 174 size_t len; 175 176 memset(&trid, 0, sizeof(trid)); 177 178 if (entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) { 179 SPDK_WARNLOG("Skipping unsupported discovery service referral\n"); 180 return; 181 } else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) { 182 SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype); 183 return; 184 } 185 186 trid.trtype = entry->trtype; 187 spdk_nvme_transport_id_populate_trstring(&trid, spdk_nvme_transport_id_trtype_str(entry->trtype)); 188 if (!spdk_nvme_transport_available_by_name(trid.trstring)) { 189 SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n", 190 trid.trtype); 191 return; 192 } 193 194 snprintf(trid.trstring, sizeof(trid.trstring), "%s", probe_ctx->trid.trstring); 195 trid.adrfam = entry->adrfam; 196 197 /* Ensure that subnqn is null terminated. */ 198 end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1); 199 if (!end) { 200 SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n"); 201 return; 202 } 203 len = end - entry->subnqn; 204 memcpy(trid.subnqn, entry->subnqn, len); 205 trid.subnqn[len] = '\0'; 206 207 /* Convert traddr to a null terminated string. */ 208 len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' '); 209 memcpy(trid.traddr, entry->traddr, len); 210 if (spdk_str_chomp(trid.traddr) != 0) { 211 SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRADDR\n"); 212 } 213 214 /* Convert trsvcid to a null terminated string. */ 215 len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' '); 216 memcpy(trid.trsvcid, entry->trsvcid, len); 217 if (spdk_str_chomp(trid.trsvcid) != 0) { 218 SPDK_DEBUGLOG(nvme, "Trailing newlines removed from discovery TRSVCID\n"); 219 } 220 221 SPDK_DEBUGLOG(nvme, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n", 222 trid.subnqn, trid.trtype, 223 trid.traddr, trid.trsvcid); 224 225 /* Copy the priority from the discovery ctrlr */ 226 trid.priority = discover_priority; 227 228 nvme_ctrlr_probe(&trid, probe_ctx, NULL); 229 } 230 231 static int 232 nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr, 233 void *log_page, uint32_t size, uint64_t offset) 234 { 235 struct nvme_completion_poll_status *status; 236 int rc; 237 238 status = calloc(1, sizeof(*status)); 239 if (!status) { 240 SPDK_ERRLOG("Failed to allocate status tracker\n"); 241 return -ENOMEM; 242 } 243 244 rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset, 245 nvme_completion_poll_cb, status); 246 if (rc < 0) { 247 free(status); 248 return -1; 249 } 250 251 if (nvme_wait_for_completion(ctrlr->adminq, status)) { 252 if (!status->timed_out) { 253 free(status); 254 } 255 return -1; 256 } 257 free(status); 258 259 return 0; 260 } 261 262 int 263 nvme_fabric_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx, 264 bool direct_connect) 265 { 266 struct spdk_nvme_ctrlr_opts discovery_opts; 267 struct spdk_nvme_ctrlr *discovery_ctrlr; 268 union spdk_nvme_cc_register cc; 269 int rc; 270 struct nvme_completion_poll_status *status; 271 272 if (strcmp(probe_ctx->trid.subnqn, SPDK_NVMF_DISCOVERY_NQN) != 0) { 273 /* It is not a discovery_ctrlr info and try to directly connect it */ 274 rc = nvme_ctrlr_probe(&probe_ctx->trid, probe_ctx, NULL); 275 return rc; 276 } 277 278 spdk_nvme_ctrlr_get_default_ctrlr_opts(&discovery_opts, sizeof(discovery_opts)); 279 /* For discovery_ctrlr set the timeout to 0 */ 280 discovery_opts.keep_alive_timeout_ms = 0; 281 282 discovery_ctrlr = nvme_transport_ctrlr_construct(&probe_ctx->trid, &discovery_opts, NULL); 283 if (discovery_ctrlr == NULL) { 284 return -1; 285 } 286 nvme_qpair_set_state(discovery_ctrlr->adminq, NVME_QPAIR_ENABLED); 287 288 /* TODO: this should be using the normal NVMe controller initialization process +1 */ 289 cc.raw = 0; 290 cc.bits.en = 1; 291 cc.bits.iosqes = 6; /* SQ entry size == 64 == 2^6 */ 292 cc.bits.iocqes = 4; /* CQ entry size == 16 == 2^4 */ 293 rc = nvme_transport_ctrlr_set_reg_4(discovery_ctrlr, offsetof(struct spdk_nvme_registers, cc.raw), 294 cc.raw); 295 if (rc < 0) { 296 SPDK_ERRLOG("Failed to set cc\n"); 297 nvme_ctrlr_destruct(discovery_ctrlr); 298 return -1; 299 } 300 301 status = calloc(1, sizeof(*status)); 302 if (!status) { 303 SPDK_ERRLOG("Failed to allocate status tracker\n"); 304 nvme_ctrlr_destruct(discovery_ctrlr); 305 return -ENOMEM; 306 } 307 308 /* get the cdata info */ 309 rc = nvme_ctrlr_cmd_identify(discovery_ctrlr, SPDK_NVME_IDENTIFY_CTRLR, 0, 0, 0, 310 &discovery_ctrlr->cdata, sizeof(discovery_ctrlr->cdata), 311 nvme_completion_poll_cb, status); 312 if (rc != 0) { 313 SPDK_ERRLOG("Failed to identify cdata\n"); 314 nvme_ctrlr_destruct(discovery_ctrlr); 315 free(status); 316 return rc; 317 } 318 319 if (nvme_wait_for_completion(discovery_ctrlr->adminq, status)) { 320 SPDK_ERRLOG("nvme_identify_controller failed!\n"); 321 nvme_ctrlr_destruct(discovery_ctrlr); 322 if (!status->timed_out) { 323 free(status); 324 } 325 return -ENXIO; 326 } 327 328 free(status); 329 330 /* Direct attach through spdk_nvme_connect() API */ 331 if (direct_connect == true) { 332 /* Set the ready state to skip the normal init process */ 333 discovery_ctrlr->state = NVME_CTRLR_STATE_READY; 334 nvme_ctrlr_connected(probe_ctx, discovery_ctrlr); 335 nvme_ctrlr_add_process(discovery_ctrlr, 0); 336 return 0; 337 } 338 339 rc = nvme_fabric_ctrlr_discover(discovery_ctrlr, probe_ctx); 340 nvme_ctrlr_destruct(discovery_ctrlr); 341 return rc; 342 } 343 344 int 345 nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr, 346 struct spdk_nvme_probe_ctx *probe_ctx) 347 { 348 struct spdk_nvmf_discovery_log_page *log_page; 349 struct spdk_nvmf_discovery_log_page_entry *log_page_entry; 350 char buffer[4096]; 351 int rc; 352 uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0; 353 uint64_t remaining_num_rec = 0; 354 uint16_t recfmt; 355 356 memset(buffer, 0x0, 4096); 357 buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page, 358 entries[0])) / 359 sizeof(struct spdk_nvmf_discovery_log_page_entry); 360 buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry); 361 do { 362 rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset); 363 if (rc < 0) { 364 SPDK_DEBUGLOG(nvme, "Get Log Page - Discovery error\n"); 365 return rc; 366 } 367 368 if (!remaining_num_rec) { 369 log_page = (struct spdk_nvmf_discovery_log_page *)buffer; 370 recfmt = from_le16(&log_page->recfmt); 371 if (recfmt != 0) { 372 SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt); 373 return -EPROTO; 374 } 375 remaining_num_rec = log_page->numrec; 376 log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]); 377 log_page_entry = &log_page->entries[0]; 378 numrec = spdk_min(remaining_num_rec, buffer_max_entries_first); 379 } else { 380 numrec = spdk_min(remaining_num_rec, buffer_max_entries); 381 log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer; 382 } 383 384 for (i = 0; i < numrec; i++) { 385 nvme_fabric_discover_probe(log_page_entry++, probe_ctx, ctrlr->trid.priority); 386 } 387 remaining_num_rec -= numrec; 388 log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry); 389 } while (remaining_num_rec != 0); 390 391 return 0; 392 } 393 394 int 395 nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries) 396 { 397 struct nvme_completion_poll_status *status; 398 struct spdk_nvmf_fabric_connect_rsp *rsp; 399 struct spdk_nvmf_fabric_connect_cmd cmd; 400 struct spdk_nvmf_fabric_connect_data *nvmf_data; 401 struct spdk_nvme_ctrlr *ctrlr; 402 int rc; 403 404 if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) { 405 return -EINVAL; 406 } 407 408 ctrlr = qpair->ctrlr; 409 if (!ctrlr) { 410 return -EINVAL; 411 } 412 413 nvmf_data = spdk_zmalloc(sizeof(*nvmf_data), 0, NULL, 414 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); 415 if (!nvmf_data) { 416 SPDK_ERRLOG("nvmf_data allocation error\n"); 417 return -ENOMEM; 418 } 419 420 status = calloc(1, sizeof(*status)); 421 if (!status) { 422 SPDK_ERRLOG("Failed to allocate status tracker\n"); 423 spdk_free(nvmf_data); 424 return -ENOMEM; 425 } 426 427 memset(&cmd, 0, sizeof(cmd)); 428 cmd.opcode = SPDK_NVME_OPC_FABRIC; 429 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT; 430 cmd.qid = qpair->id; 431 cmd.sqsize = num_entries - 1; 432 cmd.kato = ctrlr->opts.keep_alive_timeout_ms; 433 434 if (nvme_qpair_is_admin_queue(qpair)) { 435 nvmf_data->cntlid = 0xFFFF; 436 } else { 437 nvmf_data->cntlid = ctrlr->cntlid; 438 } 439 440 SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id), 441 "host ID size mismatch"); 442 memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid)); 443 snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn); 444 snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn); 445 446 rc = spdk_nvme_ctrlr_cmd_io_raw(ctrlr, qpair, 447 (struct spdk_nvme_cmd *)&cmd, 448 nvmf_data, sizeof(*nvmf_data), 449 nvme_completion_poll_cb, status); 450 if (rc < 0) { 451 SPDK_ERRLOG("Connect command failed\n"); 452 spdk_free(nvmf_data); 453 free(status); 454 return rc; 455 } 456 457 /* If we time out, the qpair will abort the request upon destruction. */ 458 if (nvme_wait_for_completion_timeout(qpair, status, ctrlr->opts.fabrics_connect_timeout_us)) { 459 SPDK_ERRLOG("Connect command failed\n"); 460 spdk_free(nvmf_data); 461 if (!status->timed_out) { 462 free(status); 463 } 464 return -EIO; 465 } 466 467 if (nvme_qpair_is_admin_queue(qpair)) { 468 rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status->cpl; 469 ctrlr->cntlid = rsp->status_code_specific.success.cntlid; 470 SPDK_DEBUGLOG(nvme, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid); 471 } 472 473 spdk_free(nvmf_data); 474 free(status); 475 return 0; 476 } 477