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 "spdk/stdinc.h" 35 36 #include "nvmf_internal.h" 37 #include "transport.h" 38 39 #include "spdk/event.h" 40 #include "spdk/likely.h" 41 #include "spdk/string.h" 42 #include "spdk/trace.h" 43 #include "spdk/nvmf_spec.h" 44 #include "spdk/uuid.h" 45 46 #include "spdk/bdev_module.h" 47 #include "spdk_internal/log.h" 48 #include "spdk_internal/utf.h" 49 50 /* 51 * States for parsing valid domains in NQNs according to RFC 1034 52 */ 53 enum spdk_nvmf_nqn_domain_states { 54 /* First character of a domain must be a letter */ 55 SPDK_NVMF_DOMAIN_ACCEPT_LETTER = 0, 56 57 /* Subsequent characters can be any of letter, digit, or hyphen */ 58 SPDK_NVMF_DOMAIN_ACCEPT_LDH = 1, 59 60 /* A domain label must end with either a letter or digit */ 61 SPDK_NVMF_DOMAIN_ACCEPT_ANY = 2 62 }; 63 64 /* Returns true if is a valid ASCII string as defined by the NVMe spec */ 65 static bool 66 spdk_nvmf_valid_ascii_string(const void *buf, size_t size) 67 { 68 const uint8_t *str = buf; 69 size_t i; 70 71 for (i = 0; i < size; i++) { 72 if (str[i] < 0x20 || str[i] > 0x7E) { 73 return false; 74 } 75 } 76 77 return true; 78 } 79 80 static bool 81 spdk_nvmf_valid_nqn(const char *nqn) 82 { 83 size_t len; 84 struct spdk_uuid uuid_value; 85 uint32_t i; 86 int bytes_consumed; 87 uint32_t domain_label_length; 88 char *reverse_domain_end; 89 uint32_t reverse_domain_end_index; 90 enum spdk_nvmf_nqn_domain_states domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LETTER; 91 92 /* Check for length requirements */ 93 len = strlen(nqn); 94 if (len > SPDK_NVMF_NQN_MAX_LEN) { 95 SPDK_ERRLOG("Invalid NQN \"%s\": length %zu > max %d\n", nqn, len, SPDK_NVMF_NQN_MAX_LEN); 96 return false; 97 } 98 99 /* The nqn must be at least as long as SPDK_NVMF_NQN_MIN_LEN to contain the necessary prefix. */ 100 if (len < SPDK_NVMF_NQN_MIN_LEN) { 101 SPDK_ERRLOG("Invalid NQN \"%s\": length %zu < min %d\n", nqn, len, SPDK_NVMF_NQN_MIN_LEN); 102 return false; 103 } 104 105 /* Check for discovery controller nqn */ 106 if (!strcmp(nqn, SPDK_NVMF_DISCOVERY_NQN)) { 107 return true; 108 } 109 110 /* Check for equality with the generic nqn structure of the form "nqn.2014-08.org.nvmexpress:uuid:11111111-2222-3333-4444-555555555555" */ 111 if (!strncmp(nqn, SPDK_NVMF_NQN_UUID_PRE, SPDK_NVMF_NQN_UUID_PRE_LEN)) { 112 if (len != SPDK_NVMF_NQN_UUID_PRE_LEN + SPDK_NVMF_UUID_STRING_LEN) { 113 SPDK_ERRLOG("Invalid NQN \"%s\": uuid is not the correct length\n", nqn); 114 return false; 115 } 116 117 if (spdk_uuid_parse(&uuid_value, &nqn[SPDK_NVMF_NQN_UUID_PRE_LEN])) { 118 SPDK_ERRLOG("Invalid NQN \"%s\": uuid is not formatted correctly\n", nqn); 119 return false; 120 } 121 return true; 122 } 123 124 /* If the nqn does not match the uuid structure, the next several checks validate the form "nqn.yyyy-mm.reverse.domain:user-string" */ 125 126 if (strncmp(nqn, "nqn.", 4) != 0) { 127 SPDK_ERRLOG("Invalid NQN \"%s\": NQN must begin with \"nqn.\".\n", nqn); 128 return false; 129 } 130 131 /* Check for yyyy-mm. */ 132 if (!(isdigit(nqn[4]) && isdigit(nqn[5]) && isdigit(nqn[6]) && isdigit(nqn[7]) && 133 nqn[8] == '-' && isdigit(nqn[9]) && isdigit(nqn[10]) && nqn[11] == '.')) { 134 SPDK_ERRLOG("Invalid date code in NQN \"%s\"\n", nqn); 135 return false; 136 } 137 138 reverse_domain_end = strchr(nqn, ':'); 139 if (reverse_domain_end != NULL && (reverse_domain_end_index = reverse_domain_end - nqn) < len - 1) { 140 } else { 141 SPDK_ERRLOG("Invalid NQN \"%s\". NQN must contain user specified name with a ':' as a prefix.\n", 142 nqn); 143 return false; 144 } 145 146 /* Check for valid reverse domain */ 147 domain_label_length = 0; 148 for (i = 12; i < reverse_domain_end_index; i++) { 149 if (domain_label_length > SPDK_DOMAIN_LABEL_MAX_LEN) { 150 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". At least one Label is too long.\n", nqn); 151 return false; 152 } 153 154 switch (domain_state) { 155 156 case SPDK_NVMF_DOMAIN_ACCEPT_LETTER: { 157 if (isalpha(nqn[i])) { 158 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY; 159 domain_label_length++; 160 break; 161 } else { 162 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must start with a letter.\n", nqn); 163 return false; 164 } 165 } 166 167 case SPDK_NVMF_DOMAIN_ACCEPT_LDH: { 168 if (isalpha(nqn[i]) || isdigit(nqn[i])) { 169 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY; 170 domain_label_length++; 171 break; 172 } else if (nqn[i] == '-') { 173 if (i == reverse_domain_end_index - 1) { 174 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n", 175 nqn); 176 return false; 177 } 178 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LDH; 179 domain_label_length++; 180 break; 181 } else if (nqn[i] == '.') { 182 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n", 183 nqn); 184 return false; 185 } else { 186 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only [a-z,A-Z,0-9,'-','.'].\n", 187 nqn); 188 return false; 189 } 190 } 191 192 case SPDK_NVMF_DOMAIN_ACCEPT_ANY: { 193 if (isalpha(nqn[i]) || isdigit(nqn[i])) { 194 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_ANY; 195 domain_label_length++; 196 break; 197 } else if (nqn[i] == '-') { 198 if (i == reverse_domain_end_index - 1) { 199 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must end with an alphanumeric symbol.\n", 200 nqn); 201 return false; 202 } 203 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LDH; 204 domain_label_length++; 205 break; 206 } else if (nqn[i] == '.') { 207 domain_state = SPDK_NVMF_DOMAIN_ACCEPT_LETTER; 208 domain_label_length = 0; 209 break; 210 } else { 211 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only [a-z,A-Z,0-9,'-','.'].\n", 212 nqn); 213 return false; 214 } 215 } 216 } 217 } 218 219 i = reverse_domain_end_index + 1; 220 while (i < len) { 221 bytes_consumed = utf8_valid(&nqn[i], &nqn[len]); 222 if (bytes_consumed <= 0) { 223 SPDK_ERRLOG("Invalid domain name in NQN \"%s\". Label names must contain only valid utf-8.\n", nqn); 224 return false; 225 } 226 227 i += bytes_consumed; 228 } 229 return true; 230 } 231 232 struct spdk_nvmf_subsystem * 233 spdk_nvmf_subsystem_create(struct spdk_nvmf_tgt *tgt, 234 const char *nqn, 235 enum spdk_nvmf_subtype type, 236 uint32_t num_ns) 237 { 238 struct spdk_nvmf_subsystem *subsystem; 239 uint32_t sid; 240 241 if (spdk_nvmf_tgt_find_subsystem(tgt, nqn)) { 242 SPDK_ERRLOG("Subsystem NQN '%s' already exists\n", nqn); 243 return NULL; 244 } 245 246 if (!spdk_nvmf_valid_nqn(nqn)) { 247 return NULL; 248 } 249 250 if (type == SPDK_NVMF_SUBTYPE_DISCOVERY && num_ns != 0) { 251 SPDK_ERRLOG("Discovery subsystem cannot have namespaces.\n"); 252 return NULL; 253 } 254 255 /* Find a free subsystem id (sid) */ 256 for (sid = 0; sid < tgt->opts.max_subsystems; sid++) { 257 if (tgt->subsystems[sid] == NULL) { 258 break; 259 } 260 } 261 if (sid >= tgt->opts.max_subsystems) { 262 return NULL; 263 } 264 265 subsystem = calloc(1, sizeof(struct spdk_nvmf_subsystem)); 266 if (subsystem == NULL) { 267 return NULL; 268 } 269 270 subsystem->thread = spdk_get_thread(); 271 subsystem->state = SPDK_NVMF_SUBSYSTEM_INACTIVE; 272 subsystem->tgt = tgt; 273 subsystem->id = sid; 274 subsystem->subtype = type; 275 subsystem->max_nsid = num_ns; 276 subsystem->max_allowed_nsid = num_ns; 277 subsystem->next_cntlid = 0; 278 snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", nqn); 279 TAILQ_INIT(&subsystem->listeners); 280 TAILQ_INIT(&subsystem->hosts); 281 TAILQ_INIT(&subsystem->ctrlrs); 282 283 if (num_ns != 0) { 284 subsystem->ns = calloc(num_ns, sizeof(struct spdk_nvmf_ns *)); 285 if (subsystem->ns == NULL) { 286 SPDK_ERRLOG("Namespace memory allocation failed\n"); 287 free(subsystem); 288 return NULL; 289 } 290 } 291 292 tgt->subsystems[sid] = subsystem; 293 tgt->discovery_genctr++; 294 295 return subsystem; 296 } 297 298 static void 299 _spdk_nvmf_subsystem_remove_host(struct spdk_nvmf_subsystem *subsystem, struct spdk_nvmf_host *host) 300 { 301 TAILQ_REMOVE(&subsystem->hosts, host, link); 302 free(host->nqn); 303 free(host); 304 } 305 306 static int _spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid); 307 308 void 309 spdk_nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem) 310 { 311 struct spdk_nvmf_listener *listener, *listener_tmp; 312 struct spdk_nvmf_host *host, *host_tmp; 313 struct spdk_nvmf_ctrlr *ctrlr, *ctrlr_tmp; 314 struct spdk_nvmf_ns *ns; 315 316 if (!subsystem) { 317 return; 318 } 319 320 assert(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE); 321 322 SPDK_DEBUGLOG(SPDK_LOG_NVMF, "subsystem is %p\n", subsystem); 323 324 TAILQ_FOREACH_SAFE(listener, &subsystem->listeners, link, listener_tmp) { 325 TAILQ_REMOVE(&subsystem->listeners, listener, link); 326 free(listener); 327 } 328 329 TAILQ_FOREACH_SAFE(host, &subsystem->hosts, link, host_tmp) { 330 _spdk_nvmf_subsystem_remove_host(subsystem, host); 331 } 332 333 TAILQ_FOREACH_SAFE(ctrlr, &subsystem->ctrlrs, link, ctrlr_tmp) { 334 spdk_nvmf_ctrlr_destruct(ctrlr); 335 } 336 337 ns = spdk_nvmf_subsystem_get_first_ns(subsystem); 338 while (ns != NULL) { 339 struct spdk_nvmf_ns *next_ns = spdk_nvmf_subsystem_get_next_ns(subsystem, ns); 340 341 _spdk_nvmf_subsystem_remove_ns(subsystem, ns->opts.nsid); 342 ns = next_ns; 343 } 344 345 free(subsystem->ns); 346 347 subsystem->tgt->subsystems[subsystem->id] = NULL; 348 subsystem->tgt->discovery_genctr++; 349 350 free(subsystem); 351 } 352 353 static int 354 spdk_nvmf_subsystem_set_state(struct spdk_nvmf_subsystem *subsystem, 355 enum spdk_nvmf_subsystem_state state) 356 { 357 enum spdk_nvmf_subsystem_state actual_old_state, expected_old_state; 358 359 switch (state) { 360 case SPDK_NVMF_SUBSYSTEM_INACTIVE: 361 expected_old_state = SPDK_NVMF_SUBSYSTEM_DEACTIVATING; 362 break; 363 case SPDK_NVMF_SUBSYSTEM_ACTIVATING: 364 expected_old_state = SPDK_NVMF_SUBSYSTEM_INACTIVE; 365 break; 366 case SPDK_NVMF_SUBSYSTEM_ACTIVE: 367 expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING; 368 break; 369 case SPDK_NVMF_SUBSYSTEM_PAUSING: 370 expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE; 371 break; 372 case SPDK_NVMF_SUBSYSTEM_PAUSED: 373 expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSING; 374 break; 375 case SPDK_NVMF_SUBSYSTEM_RESUMING: 376 expected_old_state = SPDK_NVMF_SUBSYSTEM_PAUSED; 377 break; 378 case SPDK_NVMF_SUBSYSTEM_DEACTIVATING: 379 expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVE; 380 break; 381 default: 382 assert(false); 383 return -1; 384 } 385 386 actual_old_state = __sync_val_compare_and_swap(&subsystem->state, expected_old_state, state); 387 if (actual_old_state != expected_old_state) { 388 if (actual_old_state == SPDK_NVMF_SUBSYSTEM_RESUMING && 389 state == SPDK_NVMF_SUBSYSTEM_ACTIVE) { 390 expected_old_state = SPDK_NVMF_SUBSYSTEM_RESUMING; 391 } 392 /* This is for the case when activating the subsystem fails. */ 393 if (actual_old_state == SPDK_NVMF_SUBSYSTEM_ACTIVATING && 394 state == SPDK_NVMF_SUBSYSTEM_DEACTIVATING) { 395 expected_old_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING; 396 } 397 actual_old_state = __sync_val_compare_and_swap(&subsystem->state, expected_old_state, state); 398 } 399 assert(actual_old_state == expected_old_state); 400 return actual_old_state - expected_old_state; 401 } 402 403 struct subsystem_state_change_ctx { 404 struct spdk_nvmf_subsystem *subsystem; 405 406 enum spdk_nvmf_subsystem_state requested_state; 407 408 spdk_nvmf_subsystem_state_change_done cb_fn; 409 void *cb_arg; 410 }; 411 412 static void 413 subsystem_state_change_done(struct spdk_io_channel_iter *i, int status) 414 { 415 struct subsystem_state_change_ctx *ctx = spdk_io_channel_iter_get_ctx(i); 416 417 if (status == 0) { 418 status = spdk_nvmf_subsystem_set_state(ctx->subsystem, ctx->requested_state); 419 if (status) { 420 status = -1; 421 } 422 } 423 424 if (ctx->cb_fn) { 425 ctx->cb_fn(ctx->subsystem, ctx->cb_arg, status); 426 } 427 free(ctx); 428 } 429 430 static void 431 subsystem_state_change_continue(void *ctx, int status) 432 { 433 struct spdk_io_channel_iter *i = ctx; 434 spdk_for_each_channel_continue(i, status); 435 } 436 437 static void 438 subsystem_state_change_on_pg(struct spdk_io_channel_iter *i) 439 { 440 struct subsystem_state_change_ctx *ctx; 441 struct spdk_io_channel *ch; 442 struct spdk_nvmf_poll_group *group; 443 444 ctx = spdk_io_channel_iter_get_ctx(i); 445 ch = spdk_io_channel_iter_get_channel(i); 446 group = spdk_io_channel_get_ctx(ch); 447 448 switch (ctx->requested_state) { 449 case SPDK_NVMF_SUBSYSTEM_INACTIVE: 450 spdk_nvmf_poll_group_remove_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i); 451 break; 452 case SPDK_NVMF_SUBSYSTEM_ACTIVE: 453 if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_ACTIVATING) { 454 spdk_nvmf_poll_group_add_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i); 455 } else if (ctx->subsystem->state == SPDK_NVMF_SUBSYSTEM_RESUMING) { 456 spdk_nvmf_poll_group_resume_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i); 457 } 458 break; 459 case SPDK_NVMF_SUBSYSTEM_PAUSED: 460 spdk_nvmf_poll_group_pause_subsystem(group, ctx->subsystem, subsystem_state_change_continue, i); 461 break; 462 default: 463 assert(false); 464 break; 465 } 466 } 467 468 static int 469 spdk_nvmf_subsystem_state_change(struct spdk_nvmf_subsystem *subsystem, 470 enum spdk_nvmf_subsystem_state requested_state, 471 spdk_nvmf_subsystem_state_change_done cb_fn, 472 void *cb_arg) 473 { 474 struct subsystem_state_change_ctx *ctx; 475 enum spdk_nvmf_subsystem_state intermediate_state; 476 int rc; 477 478 switch (requested_state) { 479 case SPDK_NVMF_SUBSYSTEM_INACTIVE: 480 intermediate_state = SPDK_NVMF_SUBSYSTEM_DEACTIVATING; 481 break; 482 case SPDK_NVMF_SUBSYSTEM_ACTIVE: 483 if (subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED) { 484 intermediate_state = SPDK_NVMF_SUBSYSTEM_RESUMING; 485 } else { 486 intermediate_state = SPDK_NVMF_SUBSYSTEM_ACTIVATING; 487 } 488 break; 489 case SPDK_NVMF_SUBSYSTEM_PAUSED: 490 intermediate_state = SPDK_NVMF_SUBSYSTEM_PAUSING; 491 break; 492 default: 493 assert(false); 494 return -EINVAL; 495 } 496 497 ctx = calloc(1, sizeof(*ctx)); 498 if (!ctx) { 499 return -ENOMEM; 500 } 501 502 rc = spdk_nvmf_subsystem_set_state(subsystem, intermediate_state); 503 if (rc) { 504 free(ctx); 505 return rc; 506 } 507 508 ctx->subsystem = subsystem; 509 ctx->requested_state = requested_state; 510 ctx->cb_fn = cb_fn; 511 ctx->cb_arg = cb_arg; 512 513 spdk_for_each_channel(subsystem->tgt, 514 subsystem_state_change_on_pg, 515 ctx, 516 subsystem_state_change_done); 517 518 return 0; 519 } 520 521 int 522 spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem, 523 spdk_nvmf_subsystem_state_change_done cb_fn, 524 void *cb_arg) 525 { 526 return spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg); 527 } 528 529 int 530 spdk_nvmf_subsystem_stop(struct spdk_nvmf_subsystem *subsystem, 531 spdk_nvmf_subsystem_state_change_done cb_fn, 532 void *cb_arg) 533 { 534 return spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_INACTIVE, cb_fn, cb_arg); 535 } 536 537 int 538 spdk_nvmf_subsystem_pause(struct spdk_nvmf_subsystem *subsystem, 539 spdk_nvmf_subsystem_state_change_done cb_fn, 540 void *cb_arg) 541 { 542 return spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_PAUSED, cb_fn, cb_arg); 543 } 544 545 int 546 spdk_nvmf_subsystem_resume(struct spdk_nvmf_subsystem *subsystem, 547 spdk_nvmf_subsystem_state_change_done cb_fn, 548 void *cb_arg) 549 { 550 return spdk_nvmf_subsystem_state_change(subsystem, SPDK_NVMF_SUBSYSTEM_ACTIVE, cb_fn, cb_arg); 551 } 552 553 struct spdk_nvmf_subsystem * 554 spdk_nvmf_subsystem_get_first(struct spdk_nvmf_tgt *tgt) 555 { 556 struct spdk_nvmf_subsystem *subsystem; 557 uint32_t sid; 558 559 for (sid = 0; sid < tgt->opts.max_subsystems; sid++) { 560 subsystem = tgt->subsystems[sid]; 561 if (subsystem) { 562 return subsystem; 563 } 564 } 565 566 return NULL; 567 } 568 569 struct spdk_nvmf_subsystem * 570 spdk_nvmf_subsystem_get_next(struct spdk_nvmf_subsystem *subsystem) 571 { 572 uint32_t sid; 573 struct spdk_nvmf_tgt *tgt; 574 575 if (!subsystem) { 576 return NULL; 577 } 578 579 tgt = subsystem->tgt; 580 581 for (sid = subsystem->id + 1; sid < tgt->opts.max_subsystems; sid++) { 582 subsystem = tgt->subsystems[sid]; 583 if (subsystem) { 584 return subsystem; 585 } 586 } 587 588 return NULL; 589 } 590 591 static struct spdk_nvmf_host * 592 _spdk_nvmf_subsystem_find_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) 593 { 594 struct spdk_nvmf_host *host = NULL; 595 596 TAILQ_FOREACH(host, &subsystem->hosts, link) { 597 if (strcmp(hostnqn, host->nqn) == 0) { 598 return host; 599 } 600 } 601 602 return NULL; 603 } 604 605 int 606 spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) 607 { 608 struct spdk_nvmf_host *host; 609 610 if (!spdk_nvmf_valid_nqn(hostnqn)) { 611 return -EINVAL; 612 } 613 614 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 615 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 616 return -EAGAIN; 617 } 618 619 if (_spdk_nvmf_subsystem_find_host(subsystem, hostnqn)) { 620 /* This subsystem already allows the specified host. */ 621 return 0; 622 } 623 624 host = calloc(1, sizeof(*host)); 625 if (!host) { 626 return -ENOMEM; 627 } 628 host->nqn = strdup(hostnqn); 629 if (!host->nqn) { 630 free(host); 631 return -ENOMEM; 632 } 633 634 TAILQ_INSERT_HEAD(&subsystem->hosts, host, link); 635 subsystem->tgt->discovery_genctr++; 636 637 return 0; 638 } 639 640 int 641 spdk_nvmf_subsystem_remove_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) 642 { 643 struct spdk_nvmf_host *host; 644 645 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 646 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 647 return -EAGAIN; 648 } 649 650 host = _spdk_nvmf_subsystem_find_host(subsystem, hostnqn); 651 if (host == NULL) { 652 return -ENOENT; 653 } 654 655 _spdk_nvmf_subsystem_remove_host(subsystem, host); 656 return 0; 657 } 658 659 int 660 spdk_nvmf_subsystem_set_allow_any_host(struct spdk_nvmf_subsystem *subsystem, bool allow_any_host) 661 { 662 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 663 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 664 return -EAGAIN; 665 } 666 667 subsystem->allow_any_host = allow_any_host; 668 669 return 0; 670 } 671 672 bool 673 spdk_nvmf_subsystem_get_allow_any_host(const struct spdk_nvmf_subsystem *subsystem) 674 { 675 return subsystem->allow_any_host; 676 } 677 678 bool 679 spdk_nvmf_subsystem_host_allowed(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) 680 { 681 if (!hostnqn) { 682 return false; 683 } 684 685 if (subsystem->allow_any_host) { 686 return true; 687 } 688 689 return _spdk_nvmf_subsystem_find_host(subsystem, hostnqn) != NULL; 690 } 691 692 struct spdk_nvmf_host * 693 spdk_nvmf_subsystem_get_first_host(struct spdk_nvmf_subsystem *subsystem) 694 { 695 return TAILQ_FIRST(&subsystem->hosts); 696 } 697 698 699 struct spdk_nvmf_host * 700 spdk_nvmf_subsystem_get_next_host(struct spdk_nvmf_subsystem *subsystem, 701 struct spdk_nvmf_host *prev_host) 702 { 703 return TAILQ_NEXT(prev_host, link); 704 } 705 706 const char * 707 spdk_nvmf_host_get_nqn(struct spdk_nvmf_host *host) 708 { 709 return host->nqn; 710 } 711 712 static struct spdk_nvmf_listener * 713 _spdk_nvmf_subsystem_find_listener(struct spdk_nvmf_subsystem *subsystem, 714 const struct spdk_nvme_transport_id *trid) 715 { 716 struct spdk_nvmf_listener *listener; 717 718 TAILQ_FOREACH(listener, &subsystem->listeners, link) { 719 if (spdk_nvme_transport_id_compare(&listener->trid, trid) == 0) { 720 return listener; 721 } 722 } 723 724 return NULL; 725 } 726 727 int 728 spdk_nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem, 729 struct spdk_nvme_transport_id *trid) 730 { 731 struct spdk_nvmf_transport *transport; 732 struct spdk_nvmf_listener *listener; 733 734 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 735 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 736 return -EAGAIN; 737 } 738 739 if (_spdk_nvmf_subsystem_find_listener(subsystem, trid)) { 740 /* Listener already exists in this subsystem */ 741 return 0; 742 } 743 744 transport = spdk_nvmf_tgt_get_transport(subsystem->tgt, trid->trtype); 745 if (transport == NULL) { 746 SPDK_ERRLOG("Unknown transport type %d\n", trid->trtype); 747 return -EINVAL; 748 } 749 750 listener = calloc(1, sizeof(*listener)); 751 if (!listener) { 752 return -ENOMEM; 753 } 754 755 listener->trid = *trid; 756 listener->transport = transport; 757 758 TAILQ_INSERT_HEAD(&subsystem->listeners, listener, link); 759 760 return 0; 761 } 762 763 int 764 spdk_nvmf_subsystem_remove_listener(struct spdk_nvmf_subsystem *subsystem, 765 const struct spdk_nvme_transport_id *trid) 766 { 767 struct spdk_nvmf_listener *listener; 768 769 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 770 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 771 return -EAGAIN; 772 } 773 774 listener = _spdk_nvmf_subsystem_find_listener(subsystem, trid); 775 if (listener == NULL) { 776 return -ENOENT; 777 } 778 779 TAILQ_REMOVE(&subsystem->listeners, listener, link); 780 free(listener); 781 782 return 0; 783 } 784 785 /* 786 * TODO: this is the whitelist and will be called during connection setup 787 */ 788 bool 789 spdk_nvmf_subsystem_listener_allowed(struct spdk_nvmf_subsystem *subsystem, 790 struct spdk_nvme_transport_id *trid) 791 { 792 struct spdk_nvmf_listener *listener; 793 794 if (TAILQ_EMPTY(&subsystem->listeners)) { 795 return true; 796 } 797 798 TAILQ_FOREACH(listener, &subsystem->listeners, link) { 799 if (spdk_nvme_transport_id_compare(&listener->trid, trid) == 0) { 800 return true; 801 } 802 } 803 804 return false; 805 } 806 807 struct spdk_nvmf_listener * 808 spdk_nvmf_subsystem_get_first_listener(struct spdk_nvmf_subsystem *subsystem) 809 { 810 return TAILQ_FIRST(&subsystem->listeners); 811 } 812 813 struct spdk_nvmf_listener * 814 spdk_nvmf_subsystem_get_next_listener(struct spdk_nvmf_subsystem *subsystem, 815 struct spdk_nvmf_listener *prev_listener) 816 { 817 return TAILQ_NEXT(prev_listener, link); 818 } 819 820 const struct spdk_nvme_transport_id * 821 spdk_nvmf_listener_get_trid(struct spdk_nvmf_listener *listener) 822 { 823 return &listener->trid; 824 } 825 826 struct subsystem_update_ns_ctx { 827 struct spdk_nvmf_subsystem *subsystem; 828 829 spdk_nvmf_subsystem_state_change_done cb_fn; 830 void *cb_arg; 831 }; 832 833 static void 834 subsystem_update_ns_done(struct spdk_io_channel_iter *i, int status) 835 { 836 struct subsystem_update_ns_ctx *ctx = spdk_io_channel_iter_get_ctx(i); 837 838 if (ctx->cb_fn) { 839 ctx->cb_fn(ctx->subsystem, ctx->cb_arg, status); 840 } 841 free(ctx); 842 } 843 844 static void 845 subsystem_update_ns_on_pg(struct spdk_io_channel_iter *i) 846 { 847 int rc; 848 struct subsystem_update_ns_ctx *ctx; 849 struct spdk_nvmf_poll_group *group; 850 struct spdk_nvmf_subsystem *subsystem; 851 852 ctx = spdk_io_channel_iter_get_ctx(i); 853 group = spdk_io_channel_get_ctx(spdk_io_channel_iter_get_channel(i)); 854 subsystem = ctx->subsystem; 855 856 rc = spdk_nvmf_poll_group_update_subsystem(group, subsystem); 857 spdk_for_each_channel_continue(i, rc); 858 } 859 860 static int 861 spdk_nvmf_subsystem_update_ns(struct spdk_nvmf_subsystem *subsystem, spdk_channel_for_each_cpl cpl, 862 void *ctx) 863 { 864 spdk_for_each_channel(subsystem->tgt, 865 subsystem_update_ns_on_pg, 866 ctx, 867 cpl); 868 869 return 0; 870 } 871 872 static void 873 spdk_nvmf_subsystem_ns_changed(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid) 874 { 875 struct spdk_nvmf_ctrlr *ctrlr; 876 877 TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) { 878 spdk_nvmf_ctrlr_ns_changed(ctrlr, nsid); 879 } 880 } 881 882 static int 883 _spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid) 884 { 885 struct spdk_nvmf_ns *ns; 886 887 assert(subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED || 888 subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE); 889 890 if (nsid == 0 || nsid > subsystem->max_nsid) { 891 return -1; 892 } 893 894 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 895 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 896 return -1; 897 } 898 899 ns = subsystem->ns[nsid - 1]; 900 if (!ns) { 901 return -1; 902 } 903 904 subsystem->ns[nsid - 1] = NULL; 905 906 spdk_bdev_module_release_bdev(ns->bdev); 907 spdk_bdev_close(ns->desc); 908 free(ns); 909 910 spdk_nvmf_subsystem_ns_changed(subsystem, nsid); 911 912 return 0; 913 } 914 915 int 916 spdk_nvmf_subsystem_remove_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid, 917 spdk_nvmf_subsystem_state_change_done cb_fn, void *cb_arg) 918 { 919 int rc; 920 struct subsystem_update_ns_ctx *ctx; 921 922 rc = _spdk_nvmf_subsystem_remove_ns(subsystem, nsid); 923 if (rc < 0) { 924 return rc; 925 } 926 927 ctx = calloc(1, sizeof(*ctx)); 928 929 if (ctx == NULL) { 930 return -ENOMEM; 931 } 932 933 ctx->subsystem = subsystem; 934 ctx->cb_fn = cb_fn; 935 ctx->cb_arg = cb_arg; 936 937 spdk_nvmf_subsystem_update_ns(subsystem, subsystem_update_ns_done, ctx); 938 939 return 0; 940 } 941 942 static void 943 _spdk_nvmf_ns_hot_remove_done(struct spdk_nvmf_subsystem *subsystem, void *cb_arg, int status) 944 { 945 if (status != 0) { 946 SPDK_ERRLOG("Failed to make changes to NVMe-oF subsystem with id %u\n", subsystem->id); 947 } 948 spdk_nvmf_subsystem_resume(subsystem, NULL, NULL); 949 } 950 951 static void 952 _spdk_nvmf_ns_hot_remove(struct spdk_nvmf_subsystem *subsystem, 953 void *cb_arg, int status) 954 { 955 struct spdk_nvmf_ns *ns = cb_arg; 956 957 spdk_nvmf_subsystem_remove_ns(subsystem, ns->opts.nsid, _spdk_nvmf_ns_hot_remove_done, 958 subsystem); 959 } 960 961 static void 962 spdk_nvmf_ns_hot_remove(void *remove_ctx) 963 { 964 struct spdk_nvmf_ns *ns = remove_ctx; 965 int rc; 966 967 rc = spdk_nvmf_subsystem_pause(ns->subsystem, _spdk_nvmf_ns_hot_remove, ns); 968 if (rc) { 969 SPDK_ERRLOG("Unable to pause subsystem to process namespace removal!\n"); 970 } 971 } 972 973 void 974 spdk_nvmf_ns_opts_get_defaults(struct spdk_nvmf_ns_opts *opts, size_t opts_size) 975 { 976 /* All current fields are set to 0 by default. */ 977 memset(opts, 0, opts_size); 978 } 979 980 /* Dummy bdev module used to to claim bdevs. */ 981 static struct spdk_bdev_module ns_bdev_module = { 982 .name = "NVMe-oF Target", 983 }; 984 985 uint32_t 986 spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bdev *bdev, 987 const struct spdk_nvmf_ns_opts *user_opts, size_t opts_size) 988 { 989 struct spdk_nvmf_ns_opts opts; 990 struct spdk_nvmf_ns *ns; 991 int rc; 992 993 if (!(subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE || 994 subsystem->state == SPDK_NVMF_SUBSYSTEM_PAUSED)) { 995 return 0; 996 } 997 998 spdk_nvmf_ns_opts_get_defaults(&opts, sizeof(opts)); 999 if (user_opts) { 1000 memcpy(&opts, user_opts, spdk_min(sizeof(opts), opts_size)); 1001 } 1002 1003 if (spdk_mem_all_zero(&opts.uuid, sizeof(opts.uuid))) { 1004 opts.uuid = *spdk_bdev_get_uuid(bdev); 1005 } 1006 1007 if (opts.nsid == SPDK_NVME_GLOBAL_NS_TAG) { 1008 SPDK_ERRLOG("Invalid NSID %" PRIu32 "\n", opts.nsid); 1009 return 0; 1010 } 1011 1012 if (opts.nsid == 0) { 1013 /* 1014 * NSID not specified - find a free index. 1015 * 1016 * If no free slots are found, opts.nsid will be subsystem->max_nsid + 1, which will 1017 * expand max_nsid if possible. 1018 */ 1019 for (opts.nsid = 1; opts.nsid <= subsystem->max_nsid; opts.nsid++) { 1020 if (_spdk_nvmf_subsystem_get_ns(subsystem, opts.nsid) == NULL) { 1021 break; 1022 } 1023 } 1024 } 1025 1026 if (_spdk_nvmf_subsystem_get_ns(subsystem, opts.nsid)) { 1027 SPDK_ERRLOG("Requested NSID %" PRIu32 " already in use\n", opts.nsid); 1028 return 0; 1029 } 1030 1031 if (opts.nsid > subsystem->max_nsid) { 1032 struct spdk_nvmf_ns **new_ns_array; 1033 1034 /* If MaxNamespaces was specified, we can't extend max_nsid beyond it. */ 1035 if (subsystem->max_allowed_nsid > 0 && opts.nsid > subsystem->max_allowed_nsid) { 1036 SPDK_ERRLOG("Can't extend NSID range above MaxNamespaces\n"); 1037 return 0; 1038 } 1039 1040 /* If a controller is connected, we can't change NN. */ 1041 if (!TAILQ_EMPTY(&subsystem->ctrlrs)) { 1042 SPDK_ERRLOG("Can't extend NSID range while controllers are connected\n"); 1043 return 0; 1044 } 1045 1046 new_ns_array = realloc(subsystem->ns, sizeof(struct spdk_nvmf_ns *) * opts.nsid); 1047 if (new_ns_array == NULL) { 1048 SPDK_ERRLOG("Memory allocation error while resizing namespace array.\n"); 1049 return 0; 1050 } 1051 1052 memset(new_ns_array + subsystem->max_nsid, 0, 1053 sizeof(struct spdk_nvmf_ns *) * (opts.nsid - subsystem->max_nsid)); 1054 subsystem->ns = new_ns_array; 1055 subsystem->max_nsid = opts.nsid; 1056 } 1057 1058 ns = calloc(1, sizeof(*ns)); 1059 if (ns == NULL) { 1060 SPDK_ERRLOG("Namespace allocation failed\n"); 1061 return 0; 1062 } 1063 1064 ns->bdev = bdev; 1065 ns->opts = opts; 1066 ns->subsystem = subsystem; 1067 rc = spdk_bdev_open(bdev, true, spdk_nvmf_ns_hot_remove, ns, &ns->desc); 1068 if (rc != 0) { 1069 SPDK_ERRLOG("Subsystem %s: bdev %s cannot be opened, error=%d\n", 1070 subsystem->subnqn, spdk_bdev_get_name(bdev), rc); 1071 free(ns); 1072 return 0; 1073 } 1074 rc = spdk_bdev_module_claim_bdev(bdev, ns->desc, &ns_bdev_module); 1075 if (rc != 0) { 1076 spdk_bdev_close(ns->desc); 1077 free(ns); 1078 return 0; 1079 } 1080 subsystem->ns[opts.nsid - 1] = ns; 1081 1082 SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Subsystem %s: bdev %s assigned nsid %" PRIu32 "\n", 1083 spdk_nvmf_subsystem_get_nqn(subsystem), 1084 spdk_bdev_get_name(bdev), 1085 opts.nsid); 1086 1087 spdk_nvmf_subsystem_ns_changed(subsystem, opts.nsid); 1088 1089 return opts.nsid; 1090 } 1091 1092 static uint32_t 1093 spdk_nvmf_subsystem_get_next_allocated_nsid(struct spdk_nvmf_subsystem *subsystem, 1094 uint32_t prev_nsid) 1095 { 1096 uint32_t nsid; 1097 1098 if (prev_nsid >= subsystem->max_nsid) { 1099 return 0; 1100 } 1101 1102 for (nsid = prev_nsid + 1; nsid <= subsystem->max_nsid; nsid++) { 1103 if (subsystem->ns[nsid - 1]) { 1104 return nsid; 1105 } 1106 } 1107 1108 return 0; 1109 } 1110 1111 struct spdk_nvmf_ns * 1112 spdk_nvmf_subsystem_get_first_ns(struct spdk_nvmf_subsystem *subsystem) 1113 { 1114 uint32_t first_nsid; 1115 1116 first_nsid = spdk_nvmf_subsystem_get_next_allocated_nsid(subsystem, 0); 1117 return _spdk_nvmf_subsystem_get_ns(subsystem, first_nsid); 1118 } 1119 1120 struct spdk_nvmf_ns * 1121 spdk_nvmf_subsystem_get_next_ns(struct spdk_nvmf_subsystem *subsystem, 1122 struct spdk_nvmf_ns *prev_ns) 1123 { 1124 uint32_t next_nsid; 1125 1126 next_nsid = spdk_nvmf_subsystem_get_next_allocated_nsid(subsystem, prev_ns->opts.nsid); 1127 return _spdk_nvmf_subsystem_get_ns(subsystem, next_nsid); 1128 } 1129 1130 struct spdk_nvmf_ns * 1131 spdk_nvmf_subsystem_get_ns(struct spdk_nvmf_subsystem *subsystem, uint32_t nsid) 1132 { 1133 return _spdk_nvmf_subsystem_get_ns(subsystem, nsid); 1134 } 1135 1136 uint32_t 1137 spdk_nvmf_ns_get_id(const struct spdk_nvmf_ns *ns) 1138 { 1139 return ns->opts.nsid; 1140 } 1141 1142 struct spdk_bdev * 1143 spdk_nvmf_ns_get_bdev(struct spdk_nvmf_ns *ns) 1144 { 1145 return ns->bdev; 1146 } 1147 1148 void 1149 spdk_nvmf_ns_get_opts(const struct spdk_nvmf_ns *ns, struct spdk_nvmf_ns_opts *opts, 1150 size_t opts_size) 1151 { 1152 memset(opts, 0, opts_size); 1153 memcpy(opts, &ns->opts, spdk_min(sizeof(ns->opts), opts_size)); 1154 } 1155 1156 const char * 1157 spdk_nvmf_subsystem_get_sn(const struct spdk_nvmf_subsystem *subsystem) 1158 { 1159 return subsystem->sn; 1160 } 1161 1162 int 1163 spdk_nvmf_subsystem_set_sn(struct spdk_nvmf_subsystem *subsystem, const char *sn) 1164 { 1165 size_t len, max_len; 1166 1167 max_len = sizeof(subsystem->sn) - 1; 1168 len = strlen(sn); 1169 if (len > max_len) { 1170 SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Invalid sn \"%s\": length %zu > max %zu\n", 1171 sn, len, max_len); 1172 return -1; 1173 } 1174 1175 if (!spdk_nvmf_valid_ascii_string(sn, len)) { 1176 SPDK_DEBUGLOG(SPDK_LOG_NVMF, "Non-ASCII sn\n"); 1177 SPDK_TRACEDUMP(SPDK_LOG_NVMF, "sn", sn, len); 1178 return -1; 1179 } 1180 1181 snprintf(subsystem->sn, sizeof(subsystem->sn), "%s", sn); 1182 1183 return 0; 1184 } 1185 1186 const char * 1187 spdk_nvmf_subsystem_get_nqn(struct spdk_nvmf_subsystem *subsystem) 1188 { 1189 return subsystem->subnqn; 1190 } 1191 1192 /* Workaround for astyle formatting bug */ 1193 typedef enum spdk_nvmf_subtype nvmf_subtype_t; 1194 1195 nvmf_subtype_t 1196 spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem) 1197 { 1198 return subsystem->subtype; 1199 } 1200 1201 static uint16_t 1202 spdk_nvmf_subsystem_gen_cntlid(struct spdk_nvmf_subsystem *subsystem) 1203 { 1204 int count; 1205 1206 /* 1207 * In the worst case, we might have to try all CNTLID values between 1 and 0xFFF0 - 1 1208 * before we find one that is unused (or find that all values are in use). 1209 */ 1210 for (count = 0; count < 0xFFF0 - 1; count++) { 1211 subsystem->next_cntlid++; 1212 if (subsystem->next_cntlid >= 0xFFF0) { 1213 /* The spec reserves cntlid values in the range FFF0h to FFFFh. */ 1214 subsystem->next_cntlid = 1; 1215 } 1216 1217 /* Check if a controller with this cntlid currently exists. */ 1218 if (spdk_nvmf_subsystem_get_ctrlr(subsystem, subsystem->next_cntlid) == NULL) { 1219 /* Found unused cntlid */ 1220 return subsystem->next_cntlid; 1221 } 1222 } 1223 1224 /* All valid cntlid values are in use. */ 1225 return 0xFFFF; 1226 } 1227 1228 int 1229 spdk_nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, struct spdk_nvmf_ctrlr *ctrlr) 1230 { 1231 ctrlr->cntlid = spdk_nvmf_subsystem_gen_cntlid(subsystem); 1232 if (ctrlr->cntlid == 0xFFFF) { 1233 /* Unable to get a cntlid */ 1234 SPDK_ERRLOG("Reached max simultaneous ctrlrs\n"); 1235 return -EBUSY; 1236 } 1237 1238 TAILQ_INSERT_TAIL(&subsystem->ctrlrs, ctrlr, link); 1239 1240 return 0; 1241 } 1242 1243 void 1244 spdk_nvmf_subsystem_remove_ctrlr(struct spdk_nvmf_subsystem *subsystem, 1245 struct spdk_nvmf_ctrlr *ctrlr) 1246 { 1247 assert(subsystem == ctrlr->subsys); 1248 TAILQ_REMOVE(&subsystem->ctrlrs, ctrlr, link); 1249 } 1250 1251 struct spdk_nvmf_ctrlr * 1252 spdk_nvmf_subsystem_get_ctrlr(struct spdk_nvmf_subsystem *subsystem, uint16_t cntlid) 1253 { 1254 struct spdk_nvmf_ctrlr *ctrlr; 1255 1256 TAILQ_FOREACH(ctrlr, &subsystem->ctrlrs, link) { 1257 if (ctrlr->cntlid == cntlid) { 1258 return ctrlr; 1259 } 1260 } 1261 1262 return NULL; 1263 } 1264 1265 uint32_t 1266 spdk_nvmf_subsystem_get_max_namespaces(const struct spdk_nvmf_subsystem *subsystem) 1267 { 1268 return subsystem->max_allowed_nsid; 1269 } 1270