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/acceptor.h" 48 49 #define PORTNUMSTRLEN 32 50 51 static struct spdk_iscsi_portal * 52 spdk_iscsi_portal_find_by_addr(const char *host, const char *port) 53 { 54 struct spdk_iscsi_portal *p; 55 56 TAILQ_FOREACH(p, &g_spdk_iscsi.portal_head, g_tailq) { 57 if (!strcmp(p->host, host) && !strcmp(p->port, port)) { 58 return p; 59 } 60 } 61 62 return NULL; 63 } 64 65 /* Assumes caller allocated host and port strings on the heap */ 66 struct spdk_iscsi_portal * 67 spdk_iscsi_portal_create(const char *host, const char *port, const char *cpumask) 68 { 69 struct spdk_iscsi_portal *p = NULL, *tmp; 70 struct spdk_cpuset *core_mask = NULL; 71 int rc; 72 73 assert(host != NULL); 74 assert(port != NULL); 75 76 77 p = calloc(1, sizeof(*p)); 78 if (!p) { 79 SPDK_ERRLOG("calloc() failed for portal\n"); 80 return NULL; 81 } 82 83 /* check and overwrite abbreviation of wildcard */ 84 if (strcasecmp(host, "[*]") == 0) { 85 SPDK_WARNLOG("Please use \"[::]\" as IPv6 wildcard\n"); 86 SPDK_WARNLOG("Convert \"[*]\" to \"[::]\" automatically\n"); 87 SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)"); 88 p->host = strdup("[::]"); 89 } else if (strcasecmp(host, "*") == 0) { 90 SPDK_WARNLOG("Please use \"0.0.0.0\" as IPv4 wildcard\n"); 91 SPDK_WARNLOG("Convert \"*\" to \"0.0.0.0\" automatically\n"); 92 SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)"); 93 p->host = strdup("0.0.0.0"); 94 } else { 95 p->host = strdup(host); 96 } 97 if (!p->host) { 98 SPDK_ERRLOG("strdup() failed for host\n"); 99 goto error_out; 100 } 101 102 p->port = strdup(port); 103 if (!p->port) { 104 SPDK_ERRLOG("strdup() failed for host\n"); 105 goto error_out; 106 } 107 108 core_mask = spdk_cpuset_alloc(); 109 if (!core_mask) { 110 SPDK_ERRLOG("spdk_cpuset_alloc() failed for host\n"); 111 goto error_out; 112 } 113 114 if (cpumask != NULL) { 115 rc = spdk_app_parse_core_mask(cpumask, core_mask); 116 if (rc < 0) { 117 SPDK_ERRLOG("cpumask (%s) is invalid\n", cpumask); 118 goto error_out; 119 } 120 if (spdk_cpuset_count(core_mask) == 0) { 121 SPDK_ERRLOG("cpumask (%s) does not contain core mask (0x%s)\n", 122 cpumask, spdk_cpuset_fmt(spdk_app_get_core_mask())); 123 goto error_out; 124 } 125 } else { 126 spdk_cpuset_copy(core_mask, spdk_app_get_core_mask()); 127 } 128 129 p->cpumask = core_mask; 130 131 p->sock = NULL; 132 p->group = NULL; /* set at a later time by caller */ 133 p->acceptor_poller = NULL; 134 135 pthread_mutex_lock(&g_spdk_iscsi.mutex); 136 tmp = spdk_iscsi_portal_find_by_addr(host, port); 137 if (tmp != NULL) { 138 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 139 SPDK_ERRLOG("portal (%s, %s) already exists\n", host, port); 140 goto error_out; 141 } 142 143 TAILQ_INSERT_TAIL(&g_spdk_iscsi.portal_head, p, g_tailq); 144 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 145 146 return p; 147 148 error_out: 149 spdk_cpuset_free(core_mask); 150 free(p->port); 151 free(p->host); 152 free(p); 153 154 return NULL; 155 } 156 157 void 158 spdk_iscsi_portal_destroy(struct spdk_iscsi_portal *p) 159 { 160 assert(p != NULL); 161 162 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_destroy\n"); 163 164 pthread_mutex_lock(&g_spdk_iscsi.mutex); 165 TAILQ_REMOVE(&g_spdk_iscsi.portal_head, p, g_tailq); 166 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 167 168 free(p->host); 169 free(p->port); 170 spdk_cpuset_free(p->cpumask); 171 free(p); 172 173 } 174 175 static int 176 spdk_iscsi_portal_open(struct spdk_iscsi_portal *p) 177 { 178 struct spdk_sock *sock; 179 int port; 180 181 if (p->sock != NULL) { 182 SPDK_ERRLOG("portal (%s, %s) is already opened\n", 183 p->host, p->port); 184 return -1; 185 } 186 187 port = (int)strtol(p->port, NULL, 0); 188 sock = spdk_sock_listen(p->host, port); 189 if (sock == NULL) { 190 SPDK_ERRLOG("listen error %.64s.%d\n", p->host, port); 191 return -1; 192 } 193 194 p->sock = sock; 195 196 /* 197 * When the portal is created by config file, incoming connection 198 * requests for the socket are pended to accept until reactors start. 199 * However the gap between listen() and accept() will be slight and 200 * the requests will be queued by the nonzero backlog of the socket 201 * or resend by TCP. 202 */ 203 spdk_iscsi_acceptor_start(p); 204 205 return 0; 206 } 207 208 static void 209 spdk_iscsi_portal_close(struct spdk_iscsi_portal *p) 210 { 211 if (p->sock) { 212 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "close portal (%s, %s)\n", 213 p->host, p->port); 214 spdk_iscsi_acceptor_stop(p); 215 spdk_sock_close(&p->sock); 216 } 217 } 218 219 static int 220 spdk_iscsi_parse_portal(const char *portalstring, struct spdk_iscsi_portal **ip, 221 int dry_run) 222 { 223 char *host = NULL, *port = NULL, *cpumask = NULL; 224 int len, rc = -1; 225 const char *p, *q; 226 227 if (portalstring == NULL) { 228 SPDK_ERRLOG("portal error\n"); 229 goto error_out; 230 } 231 232 /* IP address */ 233 if (portalstring[0] == '[') { 234 /* IPv6 */ 235 p = strchr(portalstring + 1, ']'); 236 if (p == NULL) { 237 SPDK_ERRLOG("portal error\n"); 238 goto error_out; 239 } 240 p++; 241 } else { 242 /* IPv4 */ 243 p = strchr(portalstring, ':'); 244 if (p == NULL) { 245 p = portalstring + strlen(portalstring); 246 } 247 } 248 249 if (!dry_run) { 250 len = p - portalstring; 251 host = malloc(len + 1); 252 if (host == NULL) { 253 SPDK_ERRLOG("malloc() failed for host\n"); 254 goto error_out; 255 } 256 memcpy(host, portalstring, len); 257 host[len] = '\0'; 258 } 259 260 /* Port number (IPv4 and IPv6 are the same) */ 261 if (p[0] == '\0') { 262 if (!dry_run) { 263 port = malloc(PORTNUMSTRLEN); 264 if (!port) { 265 SPDK_ERRLOG("malloc() failed for port\n"); 266 goto error_out; 267 } 268 snprintf(port, PORTNUMSTRLEN, "%d", DEFAULT_PORT); 269 } 270 } else { 271 if (p[0] != ':') { 272 SPDK_ERRLOG("portal error\n"); 273 goto error_out; 274 } 275 q = strchr(portalstring, '@'); 276 if (q == NULL) { 277 q = portalstring + strlen(portalstring); 278 } 279 if (q == p) { 280 SPDK_ERRLOG("no port specified\n"); 281 goto error_out; 282 } 283 284 if (!dry_run) { 285 len = q - p - 1; 286 port = malloc(len + 1); 287 if (port == NULL) { 288 SPDK_ERRLOG("malloc() failed for port\n"); 289 goto error_out; 290 } 291 memcpy(port, p + 1, len); 292 port[len] = '\0'; 293 } 294 } 295 296 /* Cpumask (IPv4 and IPv6 are the same) */ 297 p = strchr(portalstring, '@'); 298 if (p != NULL) { 299 q = portalstring + strlen(portalstring); 300 if (q == p) { 301 SPDK_ERRLOG("no cpumask specified\n"); 302 goto error_out; 303 } 304 if (!dry_run) { 305 len = q - p - 1; 306 cpumask = malloc(len + 1); 307 if (cpumask == NULL) { 308 SPDK_ERRLOG("malloc() failed for cpumask\n"); 309 goto error_out; 310 } 311 memcpy(cpumask, p + 1, len); 312 cpumask[len] = '\0'; 313 } 314 } 315 316 if (!dry_run) { 317 *ip = spdk_iscsi_portal_create(host, port, cpumask); 318 if (!*ip) { 319 goto error_out; 320 } 321 } 322 323 rc = 0; 324 error_out: 325 free(host); 326 free(port); 327 free(cpumask); 328 329 return rc; 330 } 331 332 struct spdk_iscsi_portal_grp * 333 spdk_iscsi_portal_grp_create(int tag) 334 { 335 struct spdk_iscsi_portal_grp *pg = malloc(sizeof(*pg)); 336 337 if (!pg) { 338 SPDK_ERRLOG("malloc() failed for portal group\n"); 339 return NULL; 340 } 341 342 pg->ref = 0; 343 pg->tag = tag; 344 345 TAILQ_INIT(&pg->head); 346 347 return pg; 348 } 349 350 void 351 spdk_iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg) 352 { 353 struct spdk_iscsi_portal *p; 354 355 assert(pg != NULL); 356 357 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grp_destroy\n"); 358 while (!TAILQ_EMPTY(&pg->head)) { 359 p = TAILQ_FIRST(&pg->head); 360 TAILQ_REMOVE(&pg->head, p, per_pg_tailq); 361 spdk_iscsi_portal_destroy(p); 362 } 363 free(pg); 364 } 365 366 int 367 spdk_iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg) 368 { 369 int rc = -1; 370 struct spdk_iscsi_portal_grp *tmp; 371 372 assert(pg != NULL); 373 374 pthread_mutex_lock(&g_spdk_iscsi.mutex); 375 tmp = spdk_iscsi_portal_grp_find_by_tag(pg->tag); 376 if (tmp == NULL) { 377 TAILQ_INSERT_TAIL(&g_spdk_iscsi.pg_head, pg, tailq); 378 rc = 0; 379 } 380 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 381 return rc; 382 } 383 384 void 385 spdk_iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg, 386 struct spdk_iscsi_portal *p) 387 { 388 assert(pg != NULL); 389 assert(p != NULL); 390 391 p->group = pg; 392 TAILQ_INSERT_TAIL(&pg->head, p, per_pg_tailq); 393 } 394 395 static int 396 spdk_iscsi_parse_portal_grp(struct spdk_conf_section *sp) 397 { 398 struct spdk_iscsi_portal_grp *pg; 399 struct spdk_iscsi_portal *p; 400 const char *val; 401 char *label, *portal; 402 int portals = 0, i = 0, rc = 0; 403 404 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add portal group (from config file) %d\n", 405 spdk_conf_section_get_num(sp)); 406 407 val = spdk_conf_section_get_val(sp, "Comment"); 408 if (val != NULL) { 409 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val); 410 } 411 412 /* counts number of definitions */ 413 for (i = 0; ; i++) { 414 /* 415 * label is no longer used, but we keep it in the config 416 * file definition so that we do not break existing config 417 * files. 418 */ 419 label = spdk_conf_section_get_nmval(sp, "Portal", i, 0); 420 portal = spdk_conf_section_get_nmval(sp, "Portal", i, 1); 421 if (label == NULL || portal == NULL) { 422 break; 423 } 424 rc = spdk_iscsi_parse_portal(portal, &p, 1); 425 if (rc < 0) { 426 SPDK_ERRLOG("parse portal error (%s)\n", portal); 427 return -1; 428 } 429 } 430 431 portals = i; 432 if (portals > MAX_PORTAL) { 433 SPDK_ERRLOG("%d > MAX_PORTAL\n", portals); 434 return -1; 435 } 436 437 pg = spdk_iscsi_portal_grp_create(spdk_conf_section_get_num(sp)); 438 if (!pg) { 439 SPDK_ERRLOG("portal group malloc error (%s)\n", spdk_conf_section_get_name(sp)); 440 return -1; 441 } 442 443 for (i = 0; i < portals; i++) { 444 label = spdk_conf_section_get_nmval(sp, "Portal", i, 0); 445 portal = spdk_conf_section_get_nmval(sp, "Portal", i, 1); 446 if (label == NULL || portal == NULL) { 447 SPDK_ERRLOG("portal error\n"); 448 goto error; 449 } 450 451 rc = spdk_iscsi_parse_portal(portal, &p, 0); 452 if (rc < 0) { 453 SPDK_ERRLOG("parse portal error (%s)\n", portal); 454 goto error; 455 } 456 457 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, 458 "RIndex=%d, Host=%s, Port=%s, Tag=%d\n", 459 i, p->host, p->port, spdk_conf_section_get_num(sp)); 460 461 spdk_iscsi_portal_grp_add_portal(pg, p); 462 } 463 464 rc = spdk_iscsi_portal_grp_open(pg); 465 if (rc != 0) { 466 SPDK_ERRLOG("portal_grp_open failed\n"); 467 goto error; 468 } 469 470 /* Add portal group to the end of the pg list */ 471 rc = spdk_iscsi_portal_grp_register(pg); 472 if (rc != 0) { 473 SPDK_ERRLOG("register portal failed\n"); 474 goto error; 475 } 476 477 return 0; 478 479 error: 480 spdk_iscsi_portal_grp_release(pg); 481 return -1; 482 } 483 484 struct spdk_iscsi_portal_grp * 485 spdk_iscsi_portal_grp_find_by_tag(int tag) 486 { 487 struct spdk_iscsi_portal_grp *pg; 488 489 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 490 if (pg->tag == tag) { 491 return pg; 492 } 493 } 494 495 return NULL; 496 } 497 498 int 499 spdk_iscsi_parse_portal_grps(void) 500 { 501 int rc = 0; 502 struct spdk_conf_section *sp; 503 504 sp = spdk_conf_first_section(NULL); 505 while (sp != NULL) { 506 if (spdk_conf_section_match_prefix(sp, "PortalGroup")) { 507 if (spdk_conf_section_get_num(sp) == 0) { 508 SPDK_ERRLOG("Group 0 is invalid\n"); 509 return -1; 510 } 511 512 /* Build portal group from cfg section PortalGroup */ 513 rc = spdk_iscsi_parse_portal_grp(sp); 514 if (rc < 0) { 515 SPDK_ERRLOG("parse_portal_group() failed\n"); 516 return -1; 517 } 518 } 519 sp = spdk_conf_next_section(sp); 520 } 521 return 0; 522 } 523 524 void 525 spdk_iscsi_portal_grps_destroy(void) 526 { 527 struct spdk_iscsi_portal_grp *pg; 528 529 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grps_destroy\n"); 530 pthread_mutex_lock(&g_spdk_iscsi.mutex); 531 while (!TAILQ_EMPTY(&g_spdk_iscsi.pg_head)) { 532 pg = TAILQ_FIRST(&g_spdk_iscsi.pg_head); 533 TAILQ_REMOVE(&g_spdk_iscsi.pg_head, pg, tailq); 534 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 535 spdk_iscsi_portal_grp_destroy(pg); 536 pthread_mutex_lock(&g_spdk_iscsi.mutex); 537 } 538 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 539 } 540 541 int 542 spdk_iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg) 543 { 544 struct spdk_iscsi_portal *p; 545 int rc; 546 547 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 548 rc = spdk_iscsi_portal_open(p); 549 if (rc < 0) { 550 return rc; 551 } 552 } 553 return 0; 554 } 555 556 static void 557 spdk_iscsi_portal_grp_close(struct spdk_iscsi_portal_grp *pg) 558 { 559 struct spdk_iscsi_portal *p; 560 561 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 562 spdk_iscsi_portal_close(p); 563 } 564 } 565 566 void 567 spdk_iscsi_portal_grp_close_all(void) 568 { 569 struct spdk_iscsi_portal_grp *pg; 570 571 SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_portal_grp_close_all\n"); 572 pthread_mutex_lock(&g_spdk_iscsi.mutex); 573 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 574 spdk_iscsi_portal_grp_close(pg); 575 } 576 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 577 } 578 579 struct spdk_iscsi_portal_grp * 580 spdk_iscsi_portal_grp_unregister(int tag) 581 { 582 struct spdk_iscsi_portal_grp *pg; 583 584 pthread_mutex_lock(&g_spdk_iscsi.mutex); 585 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 586 if (pg->tag == tag) { 587 TAILQ_REMOVE(&g_spdk_iscsi.pg_head, pg, tailq); 588 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 589 return pg; 590 } 591 } 592 pthread_mutex_unlock(&g_spdk_iscsi.mutex); 593 return NULL; 594 } 595 596 void 597 spdk_iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg) 598 { 599 spdk_iscsi_portal_grp_close(pg); 600 spdk_iscsi_portal_grp_destroy(pg); 601 } 602 603 static const char *portal_group_section = \ 604 "\n" 605 "# Users must change the PortalGroup section(s) to match the IP addresses\n" 606 "# for their environment.\n" 607 "# PortalGroup sections define which network portals the iSCSI target\n" 608 "# will use to listen for incoming connections. These are also used to\n" 609 "# determine which targets are accessible over each portal group.\n" 610 "# Up to 1024 Portal directives are allowed. These define the network\n" 611 "# portals of the portal group. The user must specify a IP address\n" 612 "# for each network portal, and may optionally specify a port and\n" 613 "# a cpumask. If the port is omitted, 3260 will be used. Cpumask will\n" 614 "# be used to set the processor affinity of the iSCSI connection\n" 615 "# through the portal. If the cpumask is omitted, cpumask will be\n" 616 "# set to all available processors.\n" 617 "# Syntax:\n" 618 "# Portal <Name> <IP address>[:<port>[@<cpumask>]]\n"; 619 620 #define PORTAL_GROUP_TMPL \ 621 "[PortalGroup%d]\n" \ 622 " Comment \"Portal%d\"\n" 623 624 #define PORTAL_TMPL \ 625 " Portal DA1 %s:%s@0x%s\n" 626 627 void 628 spdk_iscsi_portal_grps_config_text(FILE *fp) 629 { 630 struct spdk_iscsi_portal *p = NULL; 631 struct spdk_iscsi_portal_grp *pg = NULL; 632 633 /* Create portal group section */ 634 fprintf(fp, "%s", portal_group_section); 635 636 /* Dump portal groups */ 637 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 638 if (NULL == pg) { continue; } 639 fprintf(fp, PORTAL_GROUP_TMPL, pg->tag, pg->tag); 640 /* Dump portals */ 641 TAILQ_FOREACH(p, &pg->head, per_pg_tailq) { 642 if (NULL == p) { continue; } 643 fprintf(fp, PORTAL_TMPL, p->host, p->port, 644 spdk_cpuset_fmt(p->cpumask)); 645 } 646 } 647 } 648 649 static void 650 spdk_iscsi_portal_grp_info_json(struct spdk_iscsi_portal_grp *pg, 651 struct spdk_json_write_ctx *w) 652 { 653 struct spdk_iscsi_portal *portal; 654 655 spdk_json_write_object_begin(w); 656 657 spdk_json_write_named_int32(w, "tag", pg->tag); 658 659 spdk_json_write_named_array_begin(w, "portals"); 660 TAILQ_FOREACH(portal, &pg->head, per_pg_tailq) { 661 spdk_json_write_object_begin(w); 662 663 spdk_json_write_named_string(w, "host", portal->host); 664 spdk_json_write_named_string(w, "port", portal->port); 665 spdk_json_write_named_string_fmt(w, "cpumask", "0x%s", 666 spdk_cpuset_fmt(portal->cpumask)); 667 668 spdk_json_write_object_end(w); 669 } 670 spdk_json_write_array_end(w); 671 672 spdk_json_write_object_end(w); 673 } 674 675 static void 676 spdk_iscsi_portal_grp_config_json(struct spdk_iscsi_portal_grp *pg, 677 struct spdk_json_write_ctx *w) 678 { 679 spdk_json_write_object_begin(w); 680 681 spdk_json_write_named_string(w, "method", "add_portal_group"); 682 683 spdk_json_write_name(w, "params"); 684 spdk_iscsi_portal_grp_info_json(pg, w); 685 686 spdk_json_write_object_end(w); 687 } 688 689 void 690 spdk_iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w) 691 { 692 struct spdk_iscsi_portal_grp *pg; 693 694 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 695 spdk_iscsi_portal_grp_info_json(pg, w); 696 } 697 } 698 699 void 700 spdk_iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w) 701 { 702 struct spdk_iscsi_portal_grp *pg; 703 704 TAILQ_FOREACH(pg, &g_spdk_iscsi.pg_head, tailq) { 705 spdk_iscsi_portal_grp_config_json(pg, w); 706 } 707 } 708