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 "session.h" 38 #include "subsystem.h" 39 #include "transport.h" 40 41 #include "spdk/string.h" 42 #include "spdk/trace.h" 43 #include "spdk/nvmf_spec.h" 44 45 #include "spdk_internal/bdev.h" 46 #include "spdk_internal/log.h" 47 48 bool 49 spdk_nvmf_subsystem_exists(const char *subnqn) 50 { 51 struct spdk_nvmf_subsystem *subsystem; 52 53 if (!subnqn) { 54 return false; 55 } 56 57 TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) { 58 if (strcmp(subnqn, subsystem->subnqn) == 0) { 59 return true; 60 } 61 } 62 63 return false; 64 } 65 66 struct spdk_nvmf_subsystem * 67 nvmf_find_subsystem(const char *subnqn) 68 { 69 struct spdk_nvmf_subsystem *subsystem; 70 71 if (!subnqn) { 72 return NULL; 73 } 74 75 TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) { 76 if (strcmp(subnqn, subsystem->subnqn) == 0) { 77 return subsystem; 78 } 79 } 80 81 return NULL; 82 } 83 84 struct spdk_nvmf_subsystem * 85 spdk_nvmf_find_subsystem_with_cntlid(uint16_t cntlid) 86 { 87 struct spdk_nvmf_subsystem *subsystem; 88 struct spdk_nvmf_session *session; 89 90 TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) { 91 TAILQ_FOREACH(session, &subsystem->sessions, link) { 92 if (session->cntlid == cntlid) { 93 return subsystem; 94 } 95 } 96 } 97 98 return NULL; 99 } 100 101 bool 102 spdk_nvmf_subsystem_host_allowed(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) 103 { 104 struct spdk_nvmf_host *host; 105 106 if (!hostnqn) { 107 return false; 108 } 109 110 if (subsystem->num_hosts == 0) { 111 /* No hosts means any host can connect */ 112 return true; 113 } 114 115 TAILQ_FOREACH(host, &subsystem->hosts, link) { 116 if (strcmp(hostnqn, host->nqn) == 0) { 117 return true; 118 } 119 } 120 121 return false; 122 } 123 124 int 125 spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem) 126 { 127 return subsystem->ops->attach(subsystem); 128 } 129 130 static bool 131 nvmf_subsystem_removable(struct spdk_nvmf_subsystem *subsystem) 132 { 133 struct spdk_nvmf_session *session; 134 struct spdk_nvmf_conn *conn; 135 136 if (subsystem->is_removed) { 137 TAILQ_FOREACH(session, &subsystem->sessions, link) { 138 TAILQ_FOREACH(conn, &session->connections, link) { 139 if (!conn->transport->conn_is_idle(conn)) { 140 return false; 141 } 142 } 143 } 144 return true; 145 } 146 return false; 147 } 148 149 void 150 spdk_nvmf_subsystem_poll(struct spdk_nvmf_subsystem *subsystem) 151 { 152 struct spdk_nvmf_session *session; 153 154 /* Check the backing physical device for completions. */ 155 if (subsystem->ops->poll_for_completions) { 156 subsystem->ops->poll_for_completions(subsystem); 157 } 158 159 TAILQ_FOREACH(session, &subsystem->sessions, link) { 160 /* For each connection in the session, check for completions */ 161 spdk_nvmf_session_poll(session); 162 } 163 164 if (nvmf_subsystem_removable(subsystem)) { 165 if (subsystem->ops->detach) { 166 subsystem->ops->detach(subsystem); 167 } 168 } 169 } 170 171 static bool 172 spdk_nvmf_valid_nqn(const char *nqn) 173 { 174 size_t len; 175 176 len = strlen(nqn); 177 if (len >= SPDK_NVMF_NQN_MAX_LEN) { 178 SPDK_ERRLOG("Invalid NQN \"%s\": length %zu > max %d\n", nqn, len, SPDK_NVMF_NQN_MAX_LEN - 1); 179 return false; 180 } 181 182 if (strncmp(nqn, "nqn.", 4) != 0) { 183 SPDK_ERRLOG("Invalid NQN \"%s\": NQN must begin with \"nqn.\".\n", nqn); 184 return false; 185 } 186 187 /* yyyy-mm. */ 188 if (!(isdigit(nqn[4]) && isdigit(nqn[5]) && isdigit(nqn[6]) && isdigit(nqn[7]) && 189 nqn[8] == '-' && isdigit(nqn[9]) && isdigit(nqn[10]) && nqn[11] == '.')) { 190 SPDK_ERRLOG("Invalid date code in NQN \"%s\"\n", nqn); 191 return false; 192 } 193 194 return true; 195 } 196 197 struct spdk_nvmf_subsystem * 198 spdk_nvmf_create_subsystem(const char *nqn, 199 enum spdk_nvmf_subtype type, 200 enum spdk_nvmf_subsystem_mode mode, 201 void *cb_ctx, 202 spdk_nvmf_subsystem_connect_fn connect_cb, 203 spdk_nvmf_subsystem_disconnect_fn disconnect_cb) 204 { 205 struct spdk_nvmf_subsystem *subsystem; 206 207 if (!spdk_nvmf_valid_nqn(nqn)) { 208 return NULL; 209 } 210 211 subsystem = calloc(1, sizeof(struct spdk_nvmf_subsystem)); 212 if (subsystem == NULL) { 213 return NULL; 214 } 215 216 g_nvmf_tgt.current_subsystem_id++; 217 218 subsystem->id = g_nvmf_tgt.current_subsystem_id; 219 subsystem->subtype = type; 220 subsystem->mode = mode; 221 subsystem->cb_ctx = cb_ctx; 222 subsystem->connect_cb = connect_cb; 223 subsystem->disconnect_cb = disconnect_cb; 224 snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", nqn); 225 TAILQ_INIT(&subsystem->allowed_listeners); 226 TAILQ_INIT(&subsystem->hosts); 227 TAILQ_INIT(&subsystem->sessions); 228 229 if (type == SPDK_NVMF_SUBTYPE_DISCOVERY) { 230 subsystem->ops = &spdk_nvmf_discovery_ctrlr_ops; 231 } else if (mode == NVMF_SUBSYSTEM_MODE_DIRECT) { 232 subsystem->ops = &spdk_nvmf_direct_ctrlr_ops; 233 subsystem->dev.direct.outstanding_admin_cmd_count = 0; 234 } else { 235 subsystem->ops = &spdk_nvmf_virtual_ctrlr_ops; 236 } 237 238 TAILQ_INSERT_TAIL(&g_nvmf_tgt.subsystems, subsystem, entries); 239 g_nvmf_tgt.discovery_genctr++; 240 241 return subsystem; 242 } 243 244 void 245 spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem) 246 { 247 struct spdk_nvmf_subsystem_allowed_listener *allowed_listener, *allowed_listener_tmp; 248 struct spdk_nvmf_host *host, *host_tmp; 249 struct spdk_nvmf_session *session, *session_tmp; 250 251 if (!subsystem) { 252 return; 253 } 254 255 SPDK_TRACELOG(SPDK_TRACE_NVMF, "subsystem is %p\n", subsystem); 256 257 TAILQ_FOREACH_SAFE(allowed_listener, 258 &subsystem->allowed_listeners, link, allowed_listener_tmp) { 259 TAILQ_REMOVE(&subsystem->allowed_listeners, allowed_listener, link); 260 261 free(allowed_listener); 262 } 263 264 TAILQ_FOREACH_SAFE(host, &subsystem->hosts, link, host_tmp) { 265 TAILQ_REMOVE(&subsystem->hosts, host, link); 266 free(host->nqn); 267 free(host); 268 subsystem->num_hosts--; 269 } 270 271 TAILQ_FOREACH_SAFE(session, &subsystem->sessions, link, session_tmp) { 272 spdk_nvmf_session_destruct(session); 273 } 274 275 if (subsystem->ops->detach) { 276 subsystem->ops->detach(subsystem); 277 } 278 279 TAILQ_REMOVE(&g_nvmf_tgt.subsystems, subsystem, entries); 280 g_nvmf_tgt.discovery_genctr++; 281 282 free(subsystem); 283 } 284 285 struct spdk_nvmf_listen_addr * 286 spdk_nvmf_tgt_listen(const char *trname, const char *traddr, const char *trsvcid) 287 { 288 struct spdk_nvmf_listen_addr *listen_addr; 289 const struct spdk_nvmf_transport *transport; 290 int rc; 291 292 TAILQ_FOREACH(listen_addr, &g_nvmf_tgt.listen_addrs, link) { 293 if ((strcmp(listen_addr->trname, trname) == 0) && 294 (strcmp(listen_addr->traddr, traddr) == 0) && 295 (strcmp(listen_addr->trsvcid, trsvcid) == 0)) { 296 return listen_addr; 297 } 298 } 299 300 transport = spdk_nvmf_transport_get(trname); 301 if (!transport) { 302 SPDK_ERRLOG("Unknown transport '%s'\n", trname); 303 return NULL; 304 } 305 306 listen_addr = spdk_nvmf_listen_addr_create(trname, traddr, trsvcid); 307 if (!listen_addr) { 308 return NULL; 309 } 310 311 rc = transport->listen_addr_add(listen_addr); 312 if (rc < 0) { 313 spdk_nvmf_listen_addr_cleanup(listen_addr); 314 SPDK_ERRLOG("Unable to listen on address '%s'\n", traddr); 315 return NULL; 316 } 317 318 TAILQ_INSERT_HEAD(&g_nvmf_tgt.listen_addrs, listen_addr, link); 319 g_nvmf_tgt.discovery_genctr++; 320 321 return listen_addr; 322 } 323 324 int 325 spdk_nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem, 326 struct spdk_nvmf_listen_addr *listen_addr) 327 { 328 struct spdk_nvmf_subsystem_allowed_listener *allowed_listener; 329 330 allowed_listener = calloc(1, sizeof(*allowed_listener)); 331 if (!allowed_listener) { 332 return -1; 333 } 334 335 allowed_listener->listen_addr = listen_addr; 336 337 TAILQ_INSERT_HEAD(&subsystem->allowed_listeners, allowed_listener, link); 338 339 return 0; 340 } 341 342 /* 343 * TODO: this is the whitelist and will be called during connection setup 344 */ 345 bool 346 spdk_nvmf_subsystem_listener_allowed(struct spdk_nvmf_subsystem *subsystem, 347 struct spdk_nvmf_listen_addr *listen_addr) 348 { 349 struct spdk_nvmf_subsystem_allowed_listener *allowed_listener; 350 351 if (TAILQ_EMPTY(&subsystem->allowed_listeners)) { 352 return true; 353 } 354 355 TAILQ_FOREACH(allowed_listener, &subsystem->allowed_listeners, link) { 356 if (allowed_listener->listen_addr == listen_addr) { 357 return true; 358 } 359 } 360 361 return false; 362 } 363 364 int 365 spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, const char *host_nqn) 366 { 367 struct spdk_nvmf_host *host; 368 369 host = calloc(1, sizeof(*host)); 370 if (!host) { 371 return -1; 372 } 373 host->nqn = strdup(host_nqn); 374 if (!host->nqn) { 375 free(host); 376 return -1; 377 } 378 379 TAILQ_INSERT_HEAD(&subsystem->hosts, host, link); 380 subsystem->num_hosts++; 381 g_nvmf_tgt.discovery_genctr++; 382 383 return 0; 384 } 385 386 int 387 nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem, 388 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_pci_addr *pci_addr) 389 { 390 subsystem->dev.direct.ctrlr = ctrlr; 391 subsystem->dev.direct.pci_addr = *pci_addr; 392 393 return 0; 394 } 395 396 static void spdk_nvmf_ctrlr_hot_remove(void *remove_ctx) 397 { 398 struct spdk_nvmf_subsystem *subsystem = (struct spdk_nvmf_subsystem *)remove_ctx; 399 400 subsystem->is_removed = true; 401 } 402 403 int 404 spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bdev *bdev) 405 { 406 int i = 0; 407 408 assert(subsystem->mode == NVMF_SUBSYSTEM_MODE_VIRTUAL); 409 while (i < MAX_VIRTUAL_NAMESPACE && subsystem->dev.virt.ns_list[i]) { 410 i++; 411 } 412 if (i == MAX_VIRTUAL_NAMESPACE) { 413 SPDK_ERRLOG("spdk_nvmf_subsystem_add_ns() failed\n"); 414 return -1; 415 } 416 417 if (!spdk_bdev_claim(bdev, spdk_nvmf_ctrlr_hot_remove, subsystem)) { 418 SPDK_ERRLOG("Subsystem %s: bdev %s is already claimed\n", 419 subsystem->subnqn, bdev->name); 420 return -1; 421 } 422 423 subsystem->dev.virt.ns_list[i] = bdev; 424 subsystem->dev.virt.ns_count++; 425 return 0; 426 } 427 428 int 429 spdk_nvmf_subsystem_set_sn(struct spdk_nvmf_subsystem *subsystem, const char *sn) 430 { 431 if (subsystem->mode != NVMF_SUBSYSTEM_MODE_VIRTUAL) { 432 return -1; 433 } 434 435 snprintf(subsystem->dev.virt.sn, sizeof(subsystem->dev.virt.sn), "%s", sn); 436 437 return 0; 438 } 439 440 const char * 441 spdk_nvmf_subsystem_get_nqn(struct spdk_nvmf_subsystem *subsystem) 442 { 443 return subsystem->subnqn; 444 } 445 446 /* Workaround for astyle formatting bug */ 447 typedef enum spdk_nvmf_subtype nvmf_subtype_t; 448 449 nvmf_subtype_t 450 spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem) 451 { 452 return subsystem->subtype; 453 } 454 455 /* Workaround for astyle formatting bug */ 456 typedef enum spdk_nvmf_subsystem_mode nvmf_mode_t; 457 458 nvmf_mode_t 459 spdk_nvmf_subsystem_get_mode(struct spdk_nvmf_subsystem *subsystem) 460 { 461 return subsystem->mode; 462 } 463