1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>. 5 * Copyright (c) Intel Corporation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * * Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * * Neither the name of Intel Corporation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include "spdk/stdinc.h" 36 37 #include "spdk/conf.h" 38 #include "spdk/sock.h" 39 #include "spdk/event.h" 40 #include "spdk/string.h" 41 42 #include "spdk_internal/log.h" 43 44 #include "iscsi/iscsi.h" 45 #include "iscsi/conn.h" 46 #include "iscsi/portal_grp.h" 47 #include "iscsi/tgt_node.h" 48 49 #define PORTNUMSTRLEN 32 50 #define ACCEPT_TIMEOUT_US 1000 /* 1ms */ 51 52 static int 53 iscsi_portal_accept(void *arg) 54 { 55 struct spdk_iscsi_portal *portal = arg; 56 struct spdk_sock *sock; 57 int rc; 58 int count = 0; 59 60 if (portal->sock == NULL) { 61 return -1; 62 } 63 64 while (1) { 65 sock = spdk_sock_accept(portal->sock); 66 if (sock != NULL) { 67 rc = spdk_iscsi_conn_construct(portal, sock); 68 if (rc < 0) { 69 spdk_sock_close(&sock); 70 SPDK_ERRLOG("spdk_iscsi_connection_construct() failed\n"); 71 break; 72 } 73 count++; 74 } else { 75 if (errno != EAGAIN && errno != EWOULDBLOCK) { 76 SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno)); 77 } 78 break; 79 } 80 } 81 82 return count; 83 } 84 85 static void 86 iscsi_acceptor_start(struct spdk_iscsi_portal *p) 87 { 88 struct spdk_io_channel *ch; 89 90 p->acceptor_poller = spdk_poller_register(iscsi_portal_accept, p, ACCEPT_TIMEOUT_US); 91 92 ch = spdk_get_io_channel(&g_spdk_iscsi); 93 assert(ch != NULL); 94 p->acceptor_pg = spdk_io_channel_get_ctx(ch); 95 } 96 97 static void 98 iscsi_acceptor_stop(struct spdk_iscsi_portal *p) 99 { 100 struct spdk_io_channel *ch; 101 102 spdk_poller_unregister(&p->acceptor_poller); 103 104 assert(p->acceptor_pg != NULL); 105 ch = spdk_io_channel_from_ctx(p->acceptor_pg); 106 spdk_put_io_channel(ch); 107 } 108 109 static struct spdk_iscsi_portal * 110 iscsi_portal_find_by_addr(const char *host, const char *port) 111 { 112 struct spdk_iscsi_portal *p; 113 114 TAILQ_FOREACH(p, &g_spdk_iscsi.portal_head, g_tailq) { 115 if (!strcmp(p->host, host) && !strcmp(p->port, port)) { 116 return p; 117 } 118 } 119 120 return NULL; 121 } 122 123 /* Assumes caller allocated host and port strings on the heap */ 124 struct spdk_iscsi_portal * 125 spdk_iscsi_portal_create(const char *host, const char *port) 126 { 127 struct spdk_iscsi_portal *p = NULL, *tmp; 128 129 assert(host != NULL); 130 assert(port != NULL); 131 132 if (strlen(host) > MAX_PORTAL_ADDR || strlen(port) > MAX_PORTAL_PORT) { 133 return NULL; 134 } 135 136 p = calloc(1, sizeof(*p)); 137 if (!p) { 138 SPDK_ERRLOG("calloc() failed for portal\n"); 139 return NULL; 140 } 141 142 /* check and overwrite abbreviation of wildcard */ 143 if (strcasecmp(host, "[*]") == 0) { 144 SPDK_WARNLOG("Please use \"[::]\" as IPv6 wildcard\n"); 145 SPDK_WARNLOG("Convert \"[*]\" to \"[::]\" automatically\n"); 146 SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)"); 147 snprintf(p->host, sizeof(p->host), "[::]"); 148 } else if (strcasecmp(host, "*") == 0) { 149 SPDK_WARNLOG("Please use \"0.0.0.0\" as IPv4 wildcard\n"); 150 SPDK_WARNLOG("Convert \"*\" to \"0.0.0.0\" automatically\n"); 151 SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)"); 152 snprintf(p->host, sizeof(p->host), "0.0.0.0"); 153 } else { 154 memcpy(p->host, host, strlen(host)); 155 } 156 157 memcpy(p->port, port, strlen(port)); 158 159 p->sock = NULL; 160 p->group = NULL; /* set at a later time by caller */ 161 p->acceptor_poller = NULL; 162 163 pthread_mutex_lock(&g_spdk_iscsi.mutex); 164 tmp = iscsi_portal_find_by_addr(host, port); 165 if (tmp != NULL) { 166 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 167 SPDK_ERRLOG("portal (%s, %s) already exists\n", host, port); 168 goto error_out; 169 } 170 171 TAILQ_INSERT_TAIL(&g_spdk_iscsi.portal_head, p, g_tailq); 172 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 173 174 return p; 175 176 error_out: 177 free(p); 178 179 return NULL; 180 } 181 182 void 183 spdk_iscsi_portal_destroy(struct spdk_iscsi_portal *p) 184 { 185 assert(p != NULL); 186 187 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_destroy\n"); 188 189 pthread_mutex_lock(&g_spdk_iscsi.mutex); 190 TAILQ_REMOVE(&g_spdk_iscsi.portal_head, p, g_tailq); 191 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 192 193 free(p); 194 195 } 196 197 static int 198 iscsi_portal_open(struct spdk_iscsi_portal *p) 199 { 200 struct spdk_sock *sock; 201 int port; 202 203 if (p->sock != NULL) { 204 SPDK_ERRLOG("portal (%s, %s) is already opened\n", 205 p->host, p->port); 206 return -1; 207 } 208 209 port = (int)strtol(p->port, NULL, 0); 210 sock = spdk_sock_listen(p->host, port); 211 if (sock == NULL) { 212 SPDK_ERRLOG("listen error %.64s.%d\n", p->host, port); 213 return -1; 214 } 215 216 p->sock = sock; 217 218 /* 219 * When the portal is created by config file, incoming connection 220 * requests for the socket are pended to accept until reactors start. 221 * However the gap between listen() and accept() will be slight and 222 * the requests will be queued by the nonzero backlog of the socket 223 * or resend by TCP. 224 */ 225 iscsi_acceptor_start(p); 226 227 return 0; 228 } 229 230 static void 231 iscsi_portal_close(struct spdk_iscsi_portal *p) 232 { 233 if (p->sock) { 234 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "close portal (%s, %s)\n", 235 p->host, p->port); 236 iscsi_acceptor_stop(p); 237 spdk_sock_close(&p->sock); 238 } 239 } 240 241 static int 242 iscsi_parse_portal(const char *portalstring, struct spdk_iscsi_portal **ip, 243 int dry_run) 244 { 245 char *host = NULL, *port = NULL; 246 int len, rc = -1; 247 const char *p; 248 249 if (portalstring == NULL) { 250 SPDK_ERRLOG("portal error\n"); 251 goto error_out; 252 } 253 254 /* IP address */ 255 if (portalstring[0] == '[') { 256 /* IPv6 */ 257 p = strchr(portalstring + 1, ']'); 258 if (p == NULL) { 259 SPDK_ERRLOG("portal error\n"); 260 goto error_out; 261 } 262 p++; 263 } else { 264 /* IPv4 */ 265 p = strchr(portalstring, ':'); 266 if (p == NULL) { 267 p = portalstring + strlen(portalstring); 268 } 269 } 270 271 if (!dry_run) { 272 len = p - portalstring; 273 host = malloc(len + 1); 274 if (host == NULL) { 275 SPDK_ERRLOG("malloc() failed for host\n"); 276 goto error_out; 277 } 278 memcpy(host, portalstring, len); 279 host[len] = '\0'; 280 } 281 282 /* Port number (IPv4 and IPv6 are the same) */ 283 if (p[0] == '\0') { 284 if (!dry_run) { 285 port = malloc(PORTNUMSTRLEN); 286 if (!port) { 287 SPDK_ERRLOG("malloc() failed for port\n"); 288 goto error_out; 289 } 290 snprintf(port, PORTNUMSTRLEN, "%d", DEFAULT_PORT); 291 } 292 } else { 293 if (!dry_run) { 294 p++; 295 len = strlen(p); 296 port = malloc(len + 1); 297 if (port == NULL) { 298 SPDK_ERRLOG("malloc() failed for port\n"); 299 goto error_out; 300 } 301 memcpy(port, p, len); 302 port[len] = '\0'; 303 } 304 } 305 306 if (!dry_run) { 307 *ip = spdk_iscsi_portal_create(host, port); 308 if (!*ip) { 309 goto error_out; 310 } 311 } 312 313 rc = 0; 314 error_out: 315 free(host); 316 free(port); 317 318 return rc; 319 } 320 321 struct spdk_iscsi_portal_grp * 322 spdk_iscsi_portal_grp_create(int tag) 323 { 324 struct spdk_iscsi_portal_grp *pg = malloc(sizeof(*pg)); 325 326 if (!pg) { 327 SPDK_ERRLOG("malloc() failed for portal group\n"); 328 return NULL; 329 } 330 331 pg->ref = 0; 332 pg->tag = tag; 333 334 pthread_mutex_lock(&g_spdk_iscsi.mutex); 335 pg->disable_chap = g_spdk_iscsi.disable_chap; 336 pg->require_chap = g_spdk_iscsi.require_chap; 337 pg->mutual_chap = g_spdk_iscsi.mutual_chap; 338 pg->chap_group = g_spdk_iscsi.chap_group; 339 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 340 341 TAILQ_INIT(&pg->head); 342 343 return pg; 344 } 345 346 void 347 spdk_iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg) 348 { 349 struct spdk_iscsi_portal *p; 350 351 assert(pg != NULL); 352 353 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grp_destroy\n"); 354 while (!TAILQ_EMPTY(&pg->head)) { 355 p = TAILQ_FIRST(&pg->head); 356 TAILQ_REMOVE(&pg->head, p, per_pg_tailq); 357 spdk_iscsi_portal_destroy(p); 358 } 359 free(pg); 360 } 361 362 int 363 spdk_iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg) 364 { 365 int rc = -1; 366 struct spdk_iscsi_portal_grp *tmp; 367 368 assert(pg != NULL); 369 370 pthread_mutex_lock(&g_spdk_iscsi.mutex); 371 tmp = spdk_iscsi_portal_grp_find_by_tag(pg->tag); 372 if (tmp == NULL) { 373 TAILQ_INSERT_TAIL(&g_spdk_iscsi.pg_head, pg, tailq); 374 rc = 0; 375 } 376 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 377 return rc; 378 } 379 380 void 381 spdk_iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg, 382 struct spdk_iscsi_portal *p) 383 { 384 assert(pg != NULL); 385 assert(p != NULL); 386 387 p->group = pg; 388 TAILQ_INSERT_TAIL(&pg->head, p, per_pg_tailq); 389 } 390 391 int 392 spdk_iscsi_portal_grp_set_chap_params(struct spdk_iscsi_portal_grp *pg, 393 bool disable_chap, bool require_chap, 394 bool mutual_chap, int32_t chap_group) 395 { 396 if (!spdk_iscsi_check_chap_params(disable_chap, require_chap, 397 mutual_chap, chap_group)) { 398 return -EINVAL; 399 } 400 401 pg->disable_chap = disable_chap; 402 pg->require_chap = require_chap; 403 pg->mutual_chap = mutual_chap; 404 pg->chap_group = chap_group; 405 406 return 0; 407 } 408 409 static int 410 iscsi_parse_portal_grp(struct spdk_conf_section *sp) 411 { 412 struct spdk_iscsi_portal_grp *pg; 413 struct spdk_iscsi_portal *p; 414 const char *val; 415 char *label, *portal; 416 int portals = 0, i = 0, rc = 0; 417 418 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add portal group (from config file) %d\n", 419 spdk_conf_section_get_num(sp)); 420 421 val = spdk_conf_section_get_val(sp, "Comment"); 422 if (val != NULL) { 423 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val); 424 } 425 426 /* counts number of definitions */ 427 for (i = 0; ; i++) { 428 /* 429 * label is no longer used, but we keep it in the config 430 * file definition so that we do not break existing config 431 * files. 432 */ 433 label = spdk_conf_section_get_nmval(sp, "Portal", i, 0); 434 portal = spdk_conf_section_get_nmval(sp, "Portal", i, 1); 435 if (label == NULL || portal == NULL) { 436 break; 437 } 438 rc = iscsi_parse_portal(portal, &p, 1); 439 if (rc < 0) { 440 SPDK_ERRLOG("parse portal error (%s)\n", portal); 441 return -1; 442 } 443 } 444 445 portals = i; 446 if (portals > MAX_PORTAL) { 447 SPDK_ERRLOG("%d > MAX_PORTAL\n", portals); 448 return -1; 449 } 450 451 pg = spdk_iscsi_portal_grp_create(spdk_conf_section_get_num(sp)); 452 if (!pg) { 453 SPDK_ERRLOG("portal group malloc error (%s)\n", spdk_conf_section_get_name(sp)); 454 return -1; 455 } 456 457 for (i = 0; i < portals; i++) { 458 label = spdk_conf_section_get_nmval(sp, "Portal", i, 0); 459 portal = spdk_conf_section_get_nmval(sp, "Portal", i, 1); 460 if (label == NULL || portal == NULL) { 461 SPDK_ERRLOG("portal error\n"); 462 goto error; 463 } 464 465 rc = iscsi_parse_portal(portal, &p, 0); 466 if (rc < 0) { 467 SPDK_ERRLOG("parse portal error (%s)\n", portal); 468 goto error; 469 } 470 471 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, 472 "RIndex=%d, Host=%s, Port=%s, Tag=%d\n", 473 i, p->host, p->port, spdk_conf_section_get_num(sp)); 474 475 spdk_iscsi_portal_grp_add_portal(pg, p); 476 } 477 478 rc = spdk_iscsi_portal_grp_open(pg); 479 if (rc != 0) { 480 SPDK_ERRLOG("portal_grp_open failed\n"); 481 goto error; 482 } 483 484 /* Add portal group to the end of the pg list */ 485 rc = spdk_iscsi_portal_grp_register(pg); 486 if (rc != 0) { 487 SPDK_ERRLOG("register portal failed\n"); 488 goto error; 489 } 490 491 return 0; 492 493 error: 494 spdk_iscsi_portal_grp_release(pg); 495 return -1; 496 } 497 498 struct spdk_iscsi_portal_grp * 499 spdk_iscsi_portal_grp_find_by_tag(int tag) 500 { 501 struct spdk_iscsi_portal_grp *pg; 502 503 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 504 if (pg->tag == tag) { 505 return pg; 506 } 507 } 508 509 return NULL; 510 } 511 512 int 513 spdk_iscsi_parse_portal_grps(void) 514 { 515 int rc = 0; 516 struct spdk_conf_section *sp; 517 518 sp = spdk_conf_first_section(NULL); 519 while (sp != NULL) { 520 if (spdk_conf_section_match_prefix(sp, "PortalGroup")) { 521 if (spdk_conf_section_get_num(sp) == 0) { 522 SPDK_ERRLOG("Group 0 is invalid\n"); 523 return -1; 524 } 525 526 /* Build portal group from cfg section PortalGroup */ 527 rc = iscsi_parse_portal_grp(sp); 528 if (rc < 0) { 529 SPDK_ERRLOG("parse_portal_group() failed\n"); 530 return -1; 531 } 532 } 533 sp = spdk_conf_next_section(sp); 534 } 535 return 0; 536 } 537 538 void 539 spdk_iscsi_portal_grps_destroy(void) 540 { 541 struct spdk_iscsi_portal_grp *pg; 542 543 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grps_destroy\n"); 544 pthread_mutex_lock(&g_spdk_iscsi.mutex); 545 while (!TAILQ_EMPTY(&g_spdk_iscsi.pg_head)) { 546 pg = TAILQ_FIRST(&g_spdk_iscsi.pg_head); 547 TAILQ_REMOVE(&g_spdk_iscsi.pg_head, pg, tailq); 548 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 549 spdk_iscsi_portal_grp_destroy(pg); 550 pthread_mutex_lock(&g_spdk_iscsi.mutex); 551 } 552 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 553 } 554 555 int 556 spdk_iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg) 557 { 558 struct spdk_iscsi_portal *p; 559 int rc; 560 561 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 562 rc = iscsi_portal_open(p); 563 if (rc < 0) { 564 return rc; 565 } 566 } 567 return 0; 568 } 569 570 static void 571 iscsi_portal_grp_close(struct spdk_iscsi_portal_grp *pg) 572 { 573 struct spdk_iscsi_portal *p; 574 575 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 576 iscsi_portal_close(p); 577 } 578 } 579 580 void 581 spdk_iscsi_portal_grp_close_all(void) 582 { 583 struct spdk_iscsi_portal_grp *pg; 584 585 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grp_close_all\n"); 586 pthread_mutex_lock(&g_spdk_iscsi.mutex); 587 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 588 iscsi_portal_grp_close(pg); 589 } 590 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 591 } 592 593 struct spdk_iscsi_portal_grp * 594 spdk_iscsi_portal_grp_unregister(int tag) 595 { 596 struct spdk_iscsi_portal_grp *pg; 597 598 pthread_mutex_lock(&g_spdk_iscsi.mutex); 599 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 600 if (pg->tag == tag) { 601 TAILQ_REMOVE(&g_spdk_iscsi.pg_head, pg, tailq); 602 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 603 return pg; 604 } 605 } 606 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 607 return NULL; 608 } 609 610 void 611 spdk_iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg) 612 { 613 iscsi_portal_grp_close(pg); 614 spdk_iscsi_portal_grp_destroy(pg); 615 } 616 617 static const char *portal_group_section = \ 618 "\n" 619 "# Users must change the PortalGroup section(s) to match the IP addresses\n" 620 "# for their environment.\n" 621 "# PortalGroup sections define which network portals the iSCSI target\n" 622 "# will use to listen for incoming connections. These are also used to\n" 623 "# determine which targets are accessible over each portal group.\n" 624 "# Up to 1024 Portal directives are allowed. These define the network\n" 625 "# portals of the portal group. The user must specify a IP address\n" 626 "# for each network portal, and may optionally specify a port.\n" 627 "# If the port is omitted, 3260 will be used\n" 628 "# Syntax:\n" 629 "# Portal <Name> <IP address>[:<port>]\n"; 630 631 #define PORTAL_GROUP_TMPL \ 632 "[PortalGroup%d]\n" \ 633 " Comment \"Portal%d\"\n" 634 635 #define PORTAL_TMPL \ 636 " Portal DA1 %s:%s\n" 637 638 void 639 spdk_iscsi_portal_grps_config_text(FILE *fp) 640 { 641 struct spdk_iscsi_portal *p = NULL; 642 struct spdk_iscsi_portal_grp *pg = NULL; 643 644 /* Create portal group section */ 645 fprintf(fp, "%s", portal_group_section); 646 647 /* Dump portal groups */ 648 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 649 if (NULL == pg) { continue; } 650 fprintf(fp, PORTAL_GROUP_TMPL, pg->tag, pg->tag); 651 /* Dump portals */ 652 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 653 if (NULL == p) { continue; } 654 fprintf(fp, PORTAL_TMPL, p->host, p->port); 655 } 656 } 657 } 658 659 static void 660 iscsi_portal_grp_info_json(struct spdk_iscsi_portal_grp *pg, 661 struct spdk_json_write_ctx *w) 662 { 663 struct spdk_iscsi_portal *portal; 664 665 spdk_json_write_object_begin(w); 666 667 spdk_json_write_named_int32(w, "tag", pg->tag); 668 669 spdk_json_write_named_array_begin(w, "portals"); 670 TAILQ_FOREACH(portal, &pg->head, per_pg_tailq) { 671 spdk_json_write_object_begin(w); 672 673 spdk_json_write_named_string(w, "host", portal->host); 674 spdk_json_write_named_string(w, "port", portal->port); 675 676 spdk_json_write_object_end(w); 677 } 678 spdk_json_write_array_end(w); 679 680 spdk_json_write_object_end(w); 681 } 682 683 static void 684 iscsi_portal_grp_config_json(struct spdk_iscsi_portal_grp *pg, 685 struct spdk_json_write_ctx *w) 686 { 687 spdk_json_write_object_begin(w); 688 689 spdk_json_write_named_string(w, "method", "iscsi_create_portal_group"); 690 691 spdk_json_write_name(w, "params"); 692 iscsi_portal_grp_info_json(pg, w); 693 694 spdk_json_write_object_end(w); 695 } 696 697 void 698 spdk_iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w) 699 { 700 struct spdk_iscsi_portal_grp *pg; 701 702 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 703 iscsi_portal_grp_info_json(pg, w); 704 } 705 } 706 707 void 708 spdk_iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w) 709 { 710 struct spdk_iscsi_portal_grp *pg; 711 712 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 713 iscsi_portal_grp_config_json(pg, w); 714 } 715 } 716