1 /*- 2 * Copyright (c) 2017 Broadcom. All rights reserved. 3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 /** 35 * @file 36 * Details SLI port (sport) functions. 37 */ 38 39 #include "ocs.h" 40 #include "ocs_fabric.h" 41 #include "ocs_els.h" 42 #include "ocs_device.h" 43 44 static void ocs_vport_update_spec(ocs_sport_t *sport); 45 static void ocs_vport_link_down(ocs_sport_t *sport); 46 47 void ocs_mgmt_sport_list(ocs_textbuf_t *textbuf, void *sport); 48 void ocs_mgmt_sport_get_all(ocs_textbuf_t *textbuf, void *sport); 49 int ocs_mgmt_sport_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *sport); 50 int ocs_mgmt_sport_set(char *parent, char *name, char *value, void *sport); 51 int ocs_mgmt_sport_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length, 52 void *arg_out, uint32_t arg_out_length, void *sport); 53 static ocs_mgmt_functions_t sport_mgmt_functions = { 54 .get_list_handler = ocs_mgmt_sport_list, 55 .get_handler = ocs_mgmt_sport_get, 56 .get_all_handler = ocs_mgmt_sport_get_all, 57 .set_handler = ocs_mgmt_sport_set, 58 .exec_handler = ocs_mgmt_sport_exec, 59 }; 60 61 /*! 62 @defgroup sport_sm SLI Port (sport) State Machine: States 63 */ 64 65 /** 66 * @ingroup sport_sm 67 * @brief SLI port HW callback. 68 * 69 * @par Description 70 * This function is called in response to a HW sport event. This code resolves 71 * the reference to the sport object, and posts the corresponding event. 72 * 73 * @param arg Pointer to the OCS context. 74 * @param event HW sport event. 75 * @param data Application-specific event (pointer to the sport). 76 * 77 * @return Returns 0 on success, or a negative error value on failure. 78 */ 79 80 int32_t 81 ocs_port_cb(void *arg, ocs_hw_port_event_e event, void *data) 82 { 83 ocs_t *ocs = arg; 84 ocs_sli_port_t *sport = data; 85 86 switch (event) { 87 case OCS_HW_PORT_ALLOC_OK: 88 ocs_log_debug(ocs, "OCS_HW_PORT_ALLOC_OK\n"); 89 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_ALLOC_OK, NULL); 90 break; 91 case OCS_HW_PORT_ALLOC_FAIL: 92 ocs_log_debug(ocs, "OCS_HW_PORT_ALLOC_FAIL\n"); 93 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_ALLOC_FAIL, NULL); 94 break; 95 case OCS_HW_PORT_ATTACH_OK: 96 ocs_log_debug(ocs, "OCS_HW_PORT_ATTACH_OK\n"); 97 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_ATTACH_OK, NULL); 98 break; 99 case OCS_HW_PORT_ATTACH_FAIL: 100 ocs_log_debug(ocs, "OCS_HW_PORT_ATTACH_FAIL\n"); 101 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_ATTACH_FAIL, NULL); 102 break; 103 case OCS_HW_PORT_FREE_OK: 104 ocs_log_debug(ocs, "OCS_HW_PORT_FREE_OK\n"); 105 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_FREE_OK, NULL); 106 break; 107 case OCS_HW_PORT_FREE_FAIL: 108 ocs_log_debug(ocs, "OCS_HW_PORT_FREE_FAIL\n"); 109 ocs_sm_post_event(&sport->sm, OCS_EVT_SPORT_FREE_FAIL, NULL); 110 break; 111 default: 112 ocs_log_test(ocs, "unknown event %#x\n", event); 113 } 114 115 return 0; 116 } 117 118 /** 119 * @ingroup sport_sm 120 * @brief Allocate a SLI port object. 121 * 122 * @par Description 123 * A sport object is allocated and associated with the domain. Various 124 * structure members are initialized. 125 * 126 * @param domain Pointer to the domain structure. 127 * @param wwpn World wide port name in host endian. 128 * @param wwnn World wide node name in host endian. 129 * @param fc_id Port ID of sport may be specified, use UINT32_MAX to fabric choose 130 * @param enable_ini Enables initiator capability on this port using a non-zero value. 131 * @param enable_tgt Enables target capability on this port using a non-zero value. 132 * 133 * @return Pointer to an ocs_sport_t object; or NULL. 134 */ 135 136 ocs_sport_t * 137 ocs_sport_alloc(ocs_domain_t *domain, uint64_t wwpn, uint64_t wwnn, uint32_t fc_id, uint8_t enable_ini, uint8_t enable_tgt) 138 { 139 ocs_sport_t *sport; 140 141 if (domain->ocs->ctrlmask & OCS_CTRLMASK_INHIBIT_INITIATOR) { 142 enable_ini = 0; 143 } 144 145 /* Return a failure if this sport has already been allocated */ 146 if (wwpn != 0) { 147 sport = ocs_sport_find_wwn(domain, wwnn, wwpn); 148 if (sport != NULL) { 149 ocs_log_test(domain->ocs, "Failed: SPORT %016llx %016llx already allocated\n", 150 (unsigned long long)wwnn, (unsigned long long)wwpn); 151 return NULL; 152 } 153 } 154 155 sport = ocs_malloc(domain->ocs, sizeof(*sport), OCS_M_NOWAIT | OCS_M_ZERO); 156 if (sport) { 157 sport->ocs = domain->ocs; 158 ocs_snprintf(sport->display_name, sizeof(sport->display_name), "------"); 159 sport->domain = domain; 160 sport->lookup = spv_new(domain->ocs); 161 sport->instance_index = domain->sport_instance_count++; 162 ocs_sport_lock_init(sport); 163 ocs_list_init(&sport->node_list, ocs_node_t, link); 164 sport->sm.app = sport; 165 sport->enable_ini = enable_ini; 166 sport->enable_tgt = enable_tgt; 167 sport->enable_rscn = (sport->enable_ini || (sport->enable_tgt && enable_target_rscn(sport->ocs))); 168 169 /* Copy service parameters from domain */ 170 ocs_memcpy(sport->service_params, domain->service_params, sizeof(fc_plogi_payload_t)); 171 172 /* Update requested fc_id */ 173 sport->fc_id = fc_id; 174 175 /* Update the sport's service parameters for the new wwn's */ 176 sport->wwpn = wwpn; 177 sport->wwnn = wwnn; 178 ocs_snprintf(sport->wwnn_str, sizeof(sport->wwnn_str), "%016llx" , (unsigned long long)wwnn); 179 180 /* Initialize node group list */ 181 ocs_lock_init(sport->ocs, &sport->node_group_lock, "node_group_lock[%d]", sport->instance_index); 182 ocs_list_init(&sport->node_group_dir_list, ocs_node_group_dir_t, link); 183 184 /* if this is the "first" sport of the domain, then make it the "phys" sport */ 185 ocs_domain_lock(domain); 186 if (ocs_list_empty(&domain->sport_list)) { 187 domain->sport = sport; 188 } 189 190 ocs_list_add_tail(&domain->sport_list, sport); 191 ocs_domain_unlock(domain); 192 193 sport->mgmt_functions = &sport_mgmt_functions; 194 195 ocs_log_debug(domain->ocs, "[%s] allocate sport\n", sport->display_name); 196 } 197 return sport; 198 } 199 200 /** 201 * @ingroup sport_sm 202 * @brief Free a SLI port object. 203 * 204 * @par Description 205 * The sport object is freed. 206 * 207 * @param sport Pointer to the SLI port object. 208 * 209 * @return None. 210 */ 211 212 void 213 ocs_sport_free(ocs_sport_t *sport) 214 { 215 ocs_domain_t *domain; 216 ocs_node_group_dir_t *node_group_dir; 217 ocs_node_group_dir_t *node_group_dir_next; 218 int post_all_free = FALSE; 219 220 if (sport) { 221 domain = sport->domain; 222 ocs_log_debug(domain->ocs, "[%s] free sport\n", sport->display_name); 223 ocs_domain_lock(domain); 224 ocs_list_remove(&domain->sport_list, sport); 225 ocs_sport_lock(sport); 226 spv_del(sport->lookup); 227 sport->lookup = NULL; 228 229 ocs_lock(&domain->lookup_lock); 230 /* Remove the sport from the domain's sparse vector lookup table */ 231 spv_set(domain->lookup, sport->fc_id, NULL); 232 ocs_unlock(&domain->lookup_lock); 233 234 /* if this is the physical sport, then clear it out of the domain */ 235 if (sport == domain->sport) { 236 domain->sport = NULL; 237 } 238 239 /* 240 * If the domain's sport_list is empty, then post the ALL_NODES_FREE event to the domain, 241 * after the lock is released. The domain may be free'd as a result of the event. 242 */ 243 if (ocs_list_empty(&domain->sport_list)) { 244 post_all_free = TRUE; 245 } 246 247 /* Free any node group directories */ 248 ocs_lock(&sport->node_group_lock); 249 ocs_list_foreach_safe(&sport->node_group_dir_list, node_group_dir, node_group_dir_next) { 250 ocs_unlock(&sport->node_group_lock); 251 ocs_node_group_dir_free(node_group_dir); 252 ocs_lock(&sport->node_group_lock); 253 } 254 ocs_unlock(&sport->node_group_lock); 255 ocs_sport_unlock(sport); 256 ocs_domain_unlock(domain); 257 258 if (post_all_free) { 259 ocs_domain_post_event(domain, OCS_EVT_ALL_CHILD_NODES_FREE, NULL); 260 } 261 262 ocs_sport_lock_free(sport); 263 ocs_lock_free(&sport->node_group_lock); 264 ocs_scsi_sport_deleted(sport); 265 266 ocs_free(sport->ocs, sport, sizeof(*sport)); 267 268 } 269 } 270 271 /** 272 * @ingroup sport_sm 273 * @brief Free memory resources of a SLI port object. 274 * 275 * @par Description 276 * After the sport object is freed, its child objects are freed. 277 * 278 * @param sport Pointer to the SLI port object. 279 * 280 * @return None. 281 */ 282 283 void ocs_sport_force_free(ocs_sport_t *sport) 284 { 285 ocs_node_t *node; 286 ocs_node_t *next; 287 288 /* shutdown sm processing */ 289 ocs_sm_disable(&sport->sm); 290 291 ocs_scsi_notify_sport_force_free(sport); 292 293 ocs_sport_lock(sport); 294 ocs_list_foreach_safe(&sport->node_list, node, next) { 295 ocs_node_force_free(node); 296 } 297 ocs_sport_unlock(sport); 298 ocs_sport_free(sport); 299 } 300 301 /** 302 * @ingroup sport_sm 303 * @brief Return a SLI port object, given an instance index. 304 * 305 * @par Description 306 * A pointer to a sport object is returned, given its instance @c index. 307 * 308 * @param domain Pointer to the domain. 309 * @param index Instance index value to find. 310 * 311 * @return Returns a pointer to the ocs_sport_t object; or NULL. 312 */ 313 314 ocs_sport_t * 315 ocs_sport_get_instance(ocs_domain_t *domain, uint32_t index) 316 { 317 ocs_sport_t *sport; 318 319 ocs_domain_lock(domain); 320 ocs_list_foreach(&domain->sport_list, sport) { 321 if (sport->instance_index == index) { 322 ocs_domain_unlock(domain); 323 return sport; 324 } 325 } 326 ocs_domain_unlock(domain); 327 return NULL; 328 } 329 330 /** 331 * @ingroup sport_sm 332 * @brief Find a SLI port object, given an FC_ID. 333 * 334 * @par Description 335 * Returns a pointer to the sport object, given an FC_ID. 336 * 337 * @param domain Pointer to the domain. 338 * @param d_id FC_ID to find. 339 * 340 * @return Returns a pointer to the ocs_sport_t; or NULL. 341 */ 342 343 ocs_sport_t * 344 ocs_sport_find(ocs_domain_t *domain, uint32_t d_id) 345 { 346 ocs_sport_t *sport; 347 348 ocs_assert(domain, NULL); 349 ocs_lock(&domain->lookup_lock); 350 if (domain->lookup == NULL) { 351 ocs_log_test(domain->ocs, "assertion failed: domain->lookup is not valid\n"); 352 ocs_unlock(&domain->lookup_lock); 353 return NULL; 354 } 355 356 sport = spv_get(domain->lookup, d_id); 357 ocs_unlock(&domain->lookup_lock); 358 return sport; 359 } 360 361 /** 362 * @ingroup sport_sm 363 * @brief Find a SLI port, given the WWNN and WWPN. 364 * 365 * @par Description 366 * Return a pointer to a sport, given the WWNN and WWPN. 367 * 368 * @param domain Pointer to the domain. 369 * @param wwnn World wide node name. 370 * @param wwpn World wide port name. 371 * 372 * @return Returns a pointer to a SLI port, if found; or NULL. 373 */ 374 375 ocs_sport_t * 376 ocs_sport_find_wwn(ocs_domain_t *domain, uint64_t wwnn, uint64_t wwpn) 377 { 378 ocs_sport_t *sport = NULL; 379 380 ocs_domain_lock(domain); 381 ocs_list_foreach(&domain->sport_list, sport) { 382 if ((sport->wwnn == wwnn) && (sport->wwpn == wwpn)) { 383 ocs_domain_unlock(domain); 384 return sport; 385 } 386 } 387 ocs_domain_unlock(domain); 388 return NULL; 389 } 390 391 /** 392 * @ingroup sport_sm 393 * @brief Request a SLI port attach. 394 * 395 * @par Description 396 * External call to request an attach for a sport, given an FC_ID. 397 * 398 * @param sport Pointer to the sport context. 399 * @param fc_id FC_ID of which to attach. 400 * 401 * @return Returns 0 on success, or a negative error value on failure. 402 */ 403 404 int32_t 405 ocs_sport_attach(ocs_sport_t *sport, uint32_t fc_id) 406 { 407 ocs_hw_rtn_e rc; 408 ocs_node_t *node; 409 410 /* Set our lookup */ 411 ocs_lock(&sport->domain->lookup_lock); 412 spv_set(sport->domain->lookup, fc_id, sport); 413 ocs_unlock(&sport->domain->lookup_lock); 414 415 /* Update our display_name */ 416 ocs_node_fcid_display(fc_id, sport->display_name, sizeof(sport->display_name)); 417 ocs_sport_lock(sport); 418 ocs_list_foreach(&sport->node_list, node) { 419 ocs_node_update_display_name(node); 420 } 421 ocs_sport_unlock(sport); 422 ocs_log_debug(sport->ocs, "[%s] attach sport: fc_id x%06x\n", sport->display_name, fc_id); 423 424 rc = ocs_hw_port_attach(&sport->ocs->hw, sport, fc_id); 425 if (rc != OCS_HW_RTN_SUCCESS) { 426 ocs_log_err(sport->ocs, "ocs_hw_port_attach failed: %d\n", rc); 427 return -1; 428 } 429 return 0; 430 } 431 432 /** 433 * @brief Common SLI port state machine declarations and initialization. 434 */ 435 #define std_sport_state_decl() \ 436 ocs_sport_t *sport = NULL; \ 437 ocs_domain_t *domain = NULL; \ 438 ocs_t *ocs = NULL; \ 439 \ 440 ocs_assert(ctx, NULL); \ 441 sport = ctx->app; \ 442 ocs_assert(sport, NULL); \ 443 \ 444 domain = sport->domain; \ 445 ocs_assert(domain, NULL); \ 446 ocs = sport->ocs; \ 447 ocs_assert(ocs, NULL); 448 449 /** 450 * @brief Common SLI port state machine trace logging. 451 */ 452 #define sport_sm_trace(sport) \ 453 do { \ 454 if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(ocs)) \ 455 ocs_log_debug(ocs, "[%s] %-20s\n", sport->display_name, ocs_sm_event_name(evt)); \ 456 } while (0) 457 458 /** 459 * @brief SLI port state machine: Common event handler. 460 * 461 * @par Description 462 * Handle common sport events. 463 * 464 * @param funcname Function name to display. 465 * @param ctx Sport state machine context. 466 * @param evt Event to process. 467 * @param arg Per event optional argument. 468 * 469 * @return Returns NULL. 470 */ 471 472 static void * 473 __ocs_sport_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 474 { 475 std_sport_state_decl(); 476 477 switch(evt) { 478 case OCS_EVT_ENTER: 479 case OCS_EVT_REENTER: 480 case OCS_EVT_EXIT: 481 case OCS_EVT_ALL_CHILD_NODES_FREE: 482 break; 483 case OCS_EVT_SPORT_ATTACH_OK: 484 ocs_sm_transition(ctx, __ocs_sport_attached, NULL); 485 break; 486 case OCS_EVT_SHUTDOWN: { 487 ocs_node_t *node; 488 ocs_node_t *node_next; 489 int node_list_empty; 490 491 /* Flag this sport as shutting down */ 492 sport->shutting_down = 1; 493 494 if (sport->is_vport) { 495 ocs_vport_link_down(sport); 496 } 497 498 ocs_sport_lock(sport); 499 node_list_empty = ocs_list_empty(&sport->node_list); 500 ocs_sport_unlock(sport); 501 502 if (node_list_empty) { 503 /* sm: node list is empty / ocs_hw_port_free 504 * Remove the sport from the domain's sparse vector lookup table */ 505 ocs_lock(&domain->lookup_lock); 506 spv_set(domain->lookup, sport->fc_id, NULL); 507 ocs_unlock(&domain->lookup_lock); 508 ocs_sm_transition(ctx, __ocs_sport_wait_port_free, NULL); 509 if (ocs_hw_port_free(&ocs->hw, sport)) { 510 ocs_log_test(sport->ocs, "ocs_hw_port_free failed\n"); 511 /* Not much we can do, free the sport anyways */ 512 ocs_sport_free(sport); 513 } 514 } else { 515 /* sm: node list is not empty / shutdown nodes */ 516 ocs_sm_transition(ctx, __ocs_sport_wait_shutdown, NULL); 517 ocs_sport_lock(sport); 518 ocs_list_foreach_safe(&sport->node_list, node, node_next) { 519 /* 520 * If this is a vport, logout of the fabric controller so that it 521 * deletes the vport on the switch. 522 */ 523 if((node->rnode.fc_id == FC_ADDR_FABRIC) && (sport->is_vport)) { 524 /* if link is down, don't send logo */ 525 if (sport->ocs->hw.link.status == SLI_LINK_STATUS_DOWN) { 526 ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); 527 } else { 528 ocs_log_debug(ocs,"[%s] sport shutdown vport,sending logo to node\n", 529 node->display_name); 530 531 if (ocs_send_logo(node, OCS_FC_ELS_SEND_DEFAULT_TIMEOUT, 532 0, NULL, NULL) == NULL) { 533 /* failed to send LOGO, go ahead and cleanup node anyways */ 534 node_printf(node, "Failed to send LOGO\n"); 535 ocs_node_post_event(node, OCS_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); 536 } else { 537 /* sent LOGO, wait for response */ 538 ocs_node_transition(node, __ocs_d_wait_logo_rsp, NULL); 539 } 540 } 541 } else { 542 ocs_node_post_event(node, OCS_EVT_SHUTDOWN, NULL); 543 } 544 } 545 ocs_sport_unlock(sport); 546 } 547 break; 548 } 549 default: 550 ocs_log_test(sport->ocs, "[%s] %-20s %-20s not handled\n", sport->display_name, funcname, ocs_sm_event_name(evt)); 551 break; 552 } 553 554 return NULL; 555 } 556 557 /** 558 * @ingroup sport_sm 559 * @brief SLI port state machine: Physical sport allocated. 560 * 561 * @par Description 562 * This is the initial state for sport objects. 563 * 564 * @param ctx Remote node state machine context. 565 * @param evt Event to process. 566 * @param arg Per event optional argument. 567 * 568 * @return Returns NULL. 569 */ 570 571 void * 572 __ocs_sport_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 573 { 574 std_sport_state_decl(); 575 576 sport_sm_trace(sport); 577 578 switch(evt) { 579 /* the physical sport is attached */ 580 case OCS_EVT_SPORT_ATTACH_OK: 581 ocs_assert(sport == domain->sport, NULL); 582 ocs_sm_transition(ctx, __ocs_sport_attached, NULL); 583 break; 584 585 case OCS_EVT_SPORT_ALLOC_OK: 586 /* ignore */ 587 break; 588 default: 589 __ocs_sport_common(__func__, ctx, evt, arg); 590 return NULL; 591 } 592 return NULL; 593 } 594 595 /** 596 * @ingroup sport_sm 597 * @brief SLI port state machine: Handle initial virtual port events. 598 * 599 * @par Description 600 * This state is entered when a virtual port is instantiated, 601 * 602 * @param ctx Remote node state machine context. 603 * @param evt Event to process. 604 * @param arg Per event optional argument. 605 * 606 * @return Returns NULL. 607 */ 608 609 void * 610 __ocs_sport_vport_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 611 { 612 std_sport_state_decl(); 613 614 sport_sm_trace(sport); 615 616 switch(evt) { 617 case OCS_EVT_ENTER: { 618 uint64_t be_wwpn = ocs_htobe64(sport->wwpn); 619 620 if (sport->wwpn == 0) { 621 ocs_log_debug(ocs, "vport: letting f/w select WWN\n"); 622 } 623 624 if (sport->fc_id != UINT32_MAX) { 625 ocs_log_debug(ocs, "vport: hard coding port id: %x\n", sport->fc_id); 626 } 627 628 ocs_sm_transition(ctx, __ocs_sport_vport_wait_alloc, NULL); 629 /* If wwpn is zero, then we'll let the f/w */ 630 if (ocs_hw_port_alloc(&ocs->hw, sport, sport->domain, 631 (sport->wwpn == 0) ? NULL : (uint8_t *)&be_wwpn)) { 632 ocs_log_err(ocs, "Can't allocate port\n"); 633 break; 634 } 635 636 break; 637 } 638 default: 639 __ocs_sport_common(__func__, ctx, evt, arg); 640 return NULL; 641 } 642 return NULL; 643 } 644 645 /** 646 * @ingroup sport_sm 647 * @brief SLI port state machine: Wait for the HW SLI port allocation to complete. 648 * 649 * @par Description 650 * Waits for the HW sport allocation request to complete. 651 * 652 * @param ctx Remote node state machine context. 653 * @param evt Event to process. 654 * @param arg Per event optional argument. 655 * 656 * @return Returns NULL. 657 */ 658 659 void * 660 __ocs_sport_vport_wait_alloc(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 661 { 662 std_sport_state_decl(); 663 664 sport_sm_trace(sport); 665 666 switch(evt) { 667 case OCS_EVT_SPORT_ALLOC_OK: { 668 fc_plogi_payload_t *sp = (fc_plogi_payload_t*) sport->service_params; 669 ocs_node_t *fabric; 670 671 /* If we let f/w assign wwn's, then sport wwn's with those returned by hw */ 672 if (sport->wwnn == 0) { 673 sport->wwnn = ocs_be64toh(sport->sli_wwnn); 674 sport->wwpn = ocs_be64toh(sport->sli_wwpn); 675 ocs_snprintf(sport->wwnn_str, sizeof(sport->wwnn_str), "%016llx", (unsigned long long) sport->wwpn); 676 } 677 678 /* Update the sport's service parameters */ 679 sp->port_name_hi = ocs_htobe32((uint32_t) (sport->wwpn >> 32ll)); 680 sp->port_name_lo = ocs_htobe32((uint32_t) sport->wwpn); 681 sp->node_name_hi = ocs_htobe32((uint32_t) (sport->wwnn >> 32ll)); 682 sp->node_name_lo = ocs_htobe32((uint32_t) sport->wwnn); 683 684 /* if sport->fc_id is uninitialized, then request that the fabric node use FDISC 685 * to find an fc_id. Otherwise we're restoring vports, or we're in 686 * fabric emulation mode, so attach the fc_id 687 */ 688 if (sport->fc_id == UINT32_MAX) { 689 fabric = ocs_node_alloc(sport, FC_ADDR_FABRIC, FALSE, FALSE); 690 if (fabric == NULL) { 691 ocs_log_err(ocs, "ocs_node_alloc() failed\n"); 692 return NULL; 693 } 694 ocs_node_transition(fabric, __ocs_vport_fabric_init, NULL); 695 } else { 696 ocs_snprintf(sport->wwnn_str, sizeof(sport->wwnn_str), "%016llx", (unsigned long long)sport->wwpn); 697 ocs_sport_attach(sport, sport->fc_id); 698 } 699 ocs_sm_transition(ctx, __ocs_sport_vport_allocated, NULL); 700 break; 701 } 702 default: 703 __ocs_sport_common(__func__, ctx, evt, arg); 704 return NULL; 705 } 706 return NULL; 707 } 708 709 /** 710 * @ingroup sport_sm 711 * @brief SLI port state machine: virtual sport allocated. 712 * 713 * @par Description 714 * This state is entered after the sport is allocated; it then waits for a fabric node 715 * FDISC to complete, which requests a sport attach. 716 * The sport attach complete is handled in this state. 717 * 718 * @param ctx Remote node state machine context. 719 * @param evt Event to process. 720 * @param arg Per event optional argument. 721 * 722 * @return Returns NULL. 723 */ 724 725 void * 726 __ocs_sport_vport_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 727 { 728 std_sport_state_decl(); 729 730 sport_sm_trace(sport); 731 732 switch(evt) { 733 case OCS_EVT_SPORT_ATTACH_OK: { 734 ocs_node_t *node; 735 736 if (!(domain->femul_enable)) { 737 /* Find our fabric node, and forward this event */ 738 node = ocs_node_find(sport, FC_ADDR_FABRIC); 739 if (node == NULL) { 740 ocs_log_test(ocs, "can't find node %06x\n", FC_ADDR_FABRIC); 741 break; 742 } 743 /* sm: / forward sport attach to fabric node */ 744 ocs_node_post_event(node, evt, NULL); 745 } 746 ocs_sm_transition(ctx, __ocs_sport_attached, NULL); 747 break; 748 } 749 default: 750 __ocs_sport_common(__func__, ctx, evt, arg); 751 return NULL; 752 } 753 return NULL; 754 } 755 756 /** 757 * @ingroup sport_sm 758 * @brief SLI port state machine: Attached. 759 * 760 * @par Description 761 * State entered after the sport attach has completed. 762 * 763 * @param ctx Remote node state machine context. 764 * @param evt Event to process. 765 * @param arg Per event optional argument. 766 * 767 * @return Returns NULL. 768 */ 769 770 void * 771 __ocs_sport_attached(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 772 { 773 std_sport_state_decl(); 774 775 sport_sm_trace(sport); 776 777 switch(evt) { 778 case OCS_EVT_ENTER: { 779 ocs_node_t *node; 780 781 ocs_log_debug(ocs, "[%s] SPORT attached WWPN %016llx WWNN %016llx \n", sport->display_name, 782 sport->wwpn, sport->wwnn); 783 ocs_sport_lock(sport); 784 ocs_list_foreach(&sport->node_list, node) { 785 ocs_node_update_display_name(node); 786 } 787 ocs_sport_unlock(sport); 788 sport->tgt_id = sport->fc_id; 789 if (sport->enable_ini) { 790 ocs_scsi_ini_new_sport(sport); 791 } 792 if (sport->enable_tgt) { 793 ocs_scsi_tgt_new_sport(sport); 794 } 795 796 /* Update the vport (if its not the physical sport) parameters */ 797 if (sport->is_vport) { 798 ocs_vport_update_spec(sport); 799 } 800 801 break; 802 } 803 804 case OCS_EVT_EXIT: 805 ocs_log_debug(ocs, "[%s] SPORT deattached WWPN %016llx WWNN %016llx \n", sport->display_name, 806 sport->wwpn, sport->wwnn); 807 if (sport->enable_ini) { 808 ocs_scsi_ini_del_sport(sport); 809 } 810 if (sport->enable_tgt) { 811 ocs_scsi_tgt_del_sport(sport); 812 } 813 break; 814 default: 815 __ocs_sport_common(__func__, ctx, evt, arg); 816 return NULL; 817 } 818 return NULL; 819 } 820 821 /** 822 * @ingroup sport_sm 823 * @brief SLI port state machine: Wait for the node shutdowns to complete. 824 * 825 * @par Description 826 * Waits for the ALL_CHILD_NODES_FREE event to be posted from the node 827 * shutdown process. 828 * 829 * @param ctx Remote node state machine context. 830 * @param evt Event to process. 831 * @param arg Per event optional argument. 832 * 833 * @return Returns NULL. 834 */ 835 836 void * 837 __ocs_sport_wait_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 838 { 839 std_sport_state_decl(); 840 841 sport_sm_trace(sport); 842 843 switch(evt) { 844 case OCS_EVT_SPORT_ALLOC_OK: 845 case OCS_EVT_SPORT_ALLOC_FAIL: 846 case OCS_EVT_SPORT_ATTACH_OK: 847 case OCS_EVT_SPORT_ATTACH_FAIL: 848 /* ignore these events - just wait for the all free event */ 849 break; 850 851 case OCS_EVT_ALL_CHILD_NODES_FREE: { 852 /* Remove the sport from the domain's sparse vector lookup table */ 853 ocs_lock(&domain->lookup_lock); 854 spv_set(domain->lookup, sport->fc_id, NULL); 855 ocs_unlock(&domain->lookup_lock); 856 ocs_sm_transition(ctx, __ocs_sport_wait_port_free, NULL); 857 if (ocs_hw_port_free(&ocs->hw, sport)) { 858 ocs_log_err(sport->ocs, "ocs_hw_port_free failed\n"); 859 /* Not much we can do, free the sport anyways */ 860 ocs_sport_free(sport); 861 } 862 break; 863 } 864 default: 865 __ocs_sport_common(__func__, ctx, evt, arg); 866 return NULL; 867 } 868 return NULL; 869 } 870 871 /** 872 * @ingroup sport_sm 873 * @brief SLI port state machine: Wait for the HW's port free to complete. 874 * 875 * @par Description 876 * Waits for the HW's port free to complete. 877 * 878 * @param ctx Remote node state machine context. 879 * @param evt Event to process. 880 * @param arg Per event optional argument. 881 * 882 * @return Returns NULL. 883 */ 884 885 void * 886 __ocs_sport_wait_port_free(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg) 887 { 888 std_sport_state_decl(); 889 890 sport_sm_trace(sport); 891 892 switch(evt) { 893 case OCS_EVT_SPORT_ATTACH_OK: 894 /* Ignore as we are waiting for the free CB */ 895 break; 896 case OCS_EVT_SPORT_FREE_OK: { 897 /* All done, free myself */ 898 ocs_sport_free(sport); 899 break; 900 } 901 default: 902 __ocs_sport_common(__func__, ctx, evt, arg); 903 return NULL; 904 } 905 return NULL; 906 } 907 908 /** 909 * @ingroup sport_sm 910 * @brief Start the vports on a domain 911 * 912 * @par Description 913 * Use the vport specification to find the associated vports and start them. 914 * 915 * @param domain Pointer to the domain context. 916 * 917 * @return Returns 0 on success, or a negative error value on failure. 918 */ 919 int32_t 920 ocs_vport_start(ocs_domain_t *domain) 921 { 922 ocs_t *ocs = domain->ocs; 923 ocs_xport_t *xport = ocs->xport; 924 ocs_vport_spec_t *vport; 925 ocs_vport_spec_t *next; 926 ocs_sport_t *sport; 927 int32_t rc = 0; 928 929 ocs_device_lock(ocs); 930 ocs_list_foreach_safe(&xport->vport_list, vport, next) { 931 if (vport->domain_instance == domain->instance_index && 932 vport->sport == NULL) { 933 /* If role not set, skip this vport */ 934 if (!(vport->enable_ini || vport->enable_tgt)) { 935 continue; 936 } 937 938 /* Allocate a sport */ 939 vport->sport = sport = ocs_sport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id, 940 vport->enable_ini, vport->enable_tgt); 941 if (sport == NULL) { 942 rc = -1; 943 } else { 944 sport->is_vport = 1; 945 sport->tgt_data = vport->tgt_data; 946 sport->ini_data = vport->ini_data; 947 948 /* Transition to vport_init */ 949 ocs_sm_transition(&sport->sm, __ocs_sport_vport_init, NULL); 950 } 951 } 952 } 953 ocs_device_unlock(ocs); 954 return rc; 955 } 956 957 /** 958 * @ingroup sport_sm 959 * @brief Clear the sport reference in the vport specification. 960 * 961 * @par Description 962 * Clear the sport pointer on the vport specification when the vport is torn down. This allows it to be 963 * re-created when the link is re-established. 964 * 965 * @param sport Pointer to the sport context. 966 */ 967 static void 968 ocs_vport_link_down(ocs_sport_t *sport) 969 { 970 ocs_t *ocs = sport->ocs; 971 ocs_xport_t *xport = ocs->xport; 972 ocs_vport_spec_t *vport; 973 974 ocs_device_lock(ocs); 975 ocs_list_foreach(&xport->vport_list, vport) { 976 if (vport->sport == sport) { 977 vport->sport = NULL; 978 break; 979 } 980 } 981 ocs_device_unlock(ocs); 982 } 983 984 /** 985 * @ingroup sport_sm 986 * @brief Allocate a new virtual SLI port. 987 * 988 * @par Description 989 * A new sport is created, in response to an external management request. 990 * 991 * @n @b Note: If the WWPN is zero, the firmware will assign the WWNs. 992 * 993 * @param domain Pointer to the domain context. 994 * @param wwpn World wide port name. 995 * @param wwnn World wide node name 996 * @param fc_id Requested port ID (used in fabric emulation mode). 997 * @param ini TRUE, if port is created as an initiator node. 998 * @param tgt TRUE, if port is created as a target node. 999 * @param tgt_data Pointer to target specific data 1000 * @param ini_data Pointer to initiator specific data 1001 * @param restore_vport If TRUE, then the vport will be re-created automatically 1002 * on link disruption. 1003 * 1004 * @return Returns 0 on success; or a negative error value on failure. 1005 */ 1006 1007 int32_t 1008 ocs_sport_vport_new(ocs_domain_t *domain, uint64_t wwpn, uint64_t wwnn, 1009 uint32_t fc_id, uint8_t ini, uint8_t tgt, void *tgt_data, 1010 void *ini_data, uint8_t restore_vport) 1011 { 1012 ocs_sport_t *sport; 1013 1014 if (ini && (domain->ocs->enable_ini == 0)) { 1015 ocs_log_test(domain->ocs, "driver initiator functionality not enabled\n"); 1016 return -1; 1017 } 1018 1019 if (tgt && (domain->ocs->enable_tgt == 0)) { 1020 ocs_log_test(domain->ocs, "driver target functionality not enabled\n"); 1021 return -1; 1022 } 1023 1024 /* Create a vport spec if we need to recreate this vport after a link up event */ 1025 if (restore_vport) { 1026 if (ocs_vport_create_spec(domain->ocs, wwnn, wwpn, fc_id, ini, tgt, tgt_data, ini_data)) { 1027 ocs_log_test(domain->ocs, "failed to create vport object entry\n"); 1028 return -1; 1029 } 1030 return ocs_vport_start(domain); 1031 } 1032 1033 /* Allocate a sport */ 1034 sport = ocs_sport_alloc(domain, wwpn, wwnn, fc_id, ini, tgt); 1035 1036 if (sport == NULL) { 1037 return -1; 1038 } 1039 1040 sport->is_vport = 1; 1041 sport->tgt_data = tgt_data; 1042 sport->ini_data = ini_data; 1043 1044 /* Transition to vport_init */ 1045 ocs_sm_transition(&sport->sm, __ocs_sport_vport_init, NULL); 1046 1047 return 0; 1048 } 1049 1050 int32_t 1051 ocs_sport_vport_alloc(ocs_domain_t *domain, ocs_vport_spec_t *vport) 1052 { 1053 ocs_sport_t *sport = NULL; 1054 1055 if (domain == NULL) { 1056 return (0); 1057 } 1058 1059 ocs_assert((vport->sport == NULL), -1); 1060 1061 /* Allocate a sport */ 1062 vport->sport = sport = ocs_sport_alloc(domain, vport->wwpn, vport->wwnn, UINT32_MAX, vport->enable_ini, vport->enable_tgt); 1063 1064 if (sport == NULL) { 1065 return -1; 1066 } 1067 1068 sport->is_vport = 1; 1069 sport->tgt_data = vport->tgt_data; 1070 sport->ini_data = vport->tgt_data; 1071 1072 /* Transition to vport_init */ 1073 ocs_sm_transition(&sport->sm, __ocs_sport_vport_init, NULL); 1074 1075 return (0); 1076 } 1077 1078 /** 1079 * @ingroup sport_sm 1080 * @brief Remove a previously-allocated virtual port. 1081 * 1082 * @par Description 1083 * A previously-allocated virtual port is removed by posting the shutdown event to the 1084 * sport with a matching WWN. 1085 * 1086 * @param ocs Pointer to the device object. 1087 * @param domain Pointer to the domain structure (may be NULL). 1088 * @param wwpn World wide port name of the port to delete (host endian). 1089 * @param wwnn World wide node name of the port to delete (host endian). 1090 * 1091 * @return Returns 0 on success, or a negative error value on failure. 1092 */ 1093 1094 int32_t ocs_sport_vport_del(ocs_t *ocs, ocs_domain_t *domain, uint64_t wwpn, uint64_t wwnn) 1095 { 1096 ocs_xport_t *xport = ocs->xport; 1097 ocs_sport_t *sport; 1098 int found = 0; 1099 ocs_vport_spec_t *vport; 1100 ocs_vport_spec_t *next; 1101 uint32_t instance; 1102 1103 /* If no domain is given, use instance 0, otherwise use domain instance */ 1104 if (domain == NULL) { 1105 instance = 0; 1106 } else { 1107 instance = domain->instance_index; 1108 } 1109 1110 /* walk the ocs_vport_list and remove from there */ 1111 1112 ocs_device_lock(ocs); 1113 ocs_list_foreach_safe(&xport->vport_list, vport, next) { 1114 if ((vport->domain_instance == instance) && 1115 (vport->wwpn == wwpn) && (vport->wwnn == wwnn)) { 1116 vport->sport = NULL; 1117 break; 1118 } 1119 } 1120 ocs_device_unlock(ocs); 1121 1122 if (domain == NULL) { 1123 /* No domain means no sport to look for */ 1124 return 0; 1125 } 1126 1127 ocs_domain_lock(domain); 1128 ocs_list_foreach(&domain->sport_list, sport) { 1129 if ((sport->wwpn == wwpn) && (sport->wwnn == wwnn)) { 1130 found = 1; 1131 break; 1132 } 1133 } 1134 if (found) { 1135 /* Shutdown this SPORT */ 1136 ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL); 1137 } 1138 ocs_domain_unlock(domain); 1139 return 0; 1140 } 1141 1142 /** 1143 * @brief Force free all saved vports. 1144 * 1145 * @par Description 1146 * Delete all device vports. 1147 * 1148 * @param ocs Pointer to the device object. 1149 * 1150 * @return None. 1151 */ 1152 1153 void 1154 ocs_vport_del_all(ocs_t *ocs) 1155 { 1156 ocs_xport_t *xport = ocs->xport; 1157 ocs_vport_spec_t *vport; 1158 ocs_vport_spec_t *next; 1159 1160 ocs_device_lock(ocs); 1161 ocs_list_foreach_safe(&xport->vport_list, vport, next) { 1162 ocs_list_remove(&xport->vport_list, vport); 1163 ocs_free(ocs, vport, sizeof(*vport)); 1164 } 1165 ocs_device_unlock(ocs); 1166 } 1167 1168 /** 1169 * @ingroup sport_sm 1170 * @brief Generate a SLI port ddump. 1171 * 1172 * @par Description 1173 * Generates the SLI port ddump data. 1174 * 1175 * @param textbuf Pointer to the text buffer. 1176 * @param sport Pointer to the SLI-4 port. 1177 * 1178 * @return Returns 0 on success, or a negative value on failure. 1179 */ 1180 1181 int 1182 ocs_ddump_sport(ocs_textbuf_t *textbuf, ocs_sli_port_t *sport) 1183 { 1184 ocs_node_t *node; 1185 ocs_node_group_dir_t *node_group_dir; 1186 int retval = 0; 1187 1188 ocs_ddump_section(textbuf, "sport", sport->instance_index); 1189 ocs_ddump_value(textbuf, "display_name", "%s", sport->display_name); 1190 1191 ocs_ddump_value(textbuf, "is_vport", "%d", sport->is_vport); 1192 ocs_ddump_value(textbuf, "enable_ini", "%d", sport->enable_ini); 1193 ocs_ddump_value(textbuf, "enable_tgt", "%d", sport->enable_tgt); 1194 ocs_ddump_value(textbuf, "shutting_down", "%d", sport->shutting_down); 1195 ocs_ddump_value(textbuf, "topology", "%d", sport->topology); 1196 ocs_ddump_value(textbuf, "p2p_winner", "%d", sport->p2p_winner); 1197 ocs_ddump_value(textbuf, "p2p_port_id", "%06x", sport->p2p_port_id); 1198 ocs_ddump_value(textbuf, "p2p_remote_port_id", "%06x", sport->p2p_remote_port_id); 1199 ocs_ddump_value(textbuf, "wwpn", "%016llx", (unsigned long long)sport->wwpn); 1200 ocs_ddump_value(textbuf, "wwnn", "%016llx", (unsigned long long)sport->wwnn); 1201 /*TODO: service_params */ 1202 1203 ocs_ddump_value(textbuf, "indicator", "x%x", sport->indicator); 1204 ocs_ddump_value(textbuf, "fc_id", "x%06x", sport->fc_id); 1205 ocs_ddump_value(textbuf, "index", "%d", sport->index); 1206 1207 ocs_display_sparams(NULL, "sport_sparams", 1, textbuf, sport->service_params+4); 1208 1209 /* HLM dump */ 1210 ocs_ddump_section(textbuf, "hlm", sport->instance_index); 1211 ocs_lock(&sport->node_group_lock); 1212 ocs_list_foreach(&sport->node_group_dir_list, node_group_dir) { 1213 ocs_remote_node_group_t *remote_node_group; 1214 1215 ocs_ddump_section(textbuf, "node_group_dir", node_group_dir->instance_index); 1216 1217 ocs_ddump_value(textbuf, "node_group_list_count", "%d", node_group_dir->node_group_list_count); 1218 ocs_ddump_value(textbuf, "next_idx", "%d", node_group_dir->next_idx); 1219 ocs_list_foreach(&node_group_dir->node_group_list, remote_node_group) { 1220 ocs_ddump_section(textbuf, "node_group", remote_node_group->instance_index); 1221 ocs_ddump_value(textbuf, "indicator", "x%x", remote_node_group->indicator); 1222 ocs_ddump_value(textbuf, "index", "x%x", remote_node_group->index); 1223 ocs_ddump_value(textbuf, "instance_index", "x%x", remote_node_group->instance_index); 1224 ocs_ddump_endsection(textbuf, "node_group", 0); 1225 } 1226 ocs_ddump_endsection(textbuf, "node_group_dir", 0); 1227 } 1228 ocs_unlock(&sport->node_group_lock); 1229 ocs_ddump_endsection(textbuf, "hlm", sport->instance_index); 1230 1231 ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_SPORT, sport); 1232 ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_SPORT, sport); 1233 1234 /* Dump all the nodes */ 1235 if (ocs_sport_lock_try(sport) != TRUE) { 1236 /* Didn't get lock */ 1237 return -1; 1238 } 1239 /* Here the sport lock is held */ 1240 ocs_list_foreach(&sport->node_list, node) { 1241 retval = ocs_ddump_node(textbuf, node); 1242 if (retval != 0) { 1243 break; 1244 } 1245 } 1246 ocs_sport_unlock(sport); 1247 1248 ocs_ddump_endsection(textbuf, "sport", sport->index); 1249 1250 return retval; 1251 } 1252 1253 void 1254 ocs_mgmt_sport_list(ocs_textbuf_t *textbuf, void *object) 1255 { 1256 ocs_node_t *node; 1257 ocs_sport_t *sport = (ocs_sport_t *)object; 1258 1259 ocs_mgmt_start_section(textbuf, "sport", sport->instance_index); 1260 1261 /* Add my status values to textbuf */ 1262 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "indicator"); 1263 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fc_id"); 1264 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "index"); 1265 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name"); 1266 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "is_vport"); 1267 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "enable_ini"); 1268 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "enable_tgt"); 1269 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "p2p"); 1270 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "p2p_winner"); 1271 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "p2p_port_id"); 1272 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "p2p_remote_port_id"); 1273 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "wwpn"); 1274 ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "wwnn"); 1275 1276 if (ocs_sport_lock_try(sport) == TRUE) { 1277 /* If we get here, then we are holding the sport lock */ 1278 ocs_list_foreach(&sport->node_list, node) { 1279 if ((node->mgmt_functions) && (node->mgmt_functions->get_list_handler)) { 1280 node->mgmt_functions->get_list_handler(textbuf, node); 1281 } 1282 } 1283 ocs_sport_unlock(sport); 1284 } 1285 1286 ocs_mgmt_end_section(textbuf, "sport", sport->instance_index); 1287 } 1288 1289 int 1290 ocs_mgmt_sport_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object) 1291 { 1292 ocs_node_t *node; 1293 ocs_sport_t *sport = (ocs_sport_t *)object; 1294 char qualifier[80]; 1295 int retval = -1; 1296 1297 ocs_mgmt_start_section(textbuf, "sport", sport->instance_index); 1298 1299 snprintf(qualifier, sizeof(qualifier), "%s/sport[%d]", parent, sport->instance_index); 1300 1301 /* If it doesn't start with my qualifier I don't know what to do with it */ 1302 if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) { 1303 char *unqualified_name = name + strlen(qualifier) +1; 1304 1305 /* See if it's a value I can supply */ 1306 if (ocs_strcmp(unqualified_name, "indicator") == 0) { 1307 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "0x%x", sport->indicator); 1308 retval = 0; 1309 } else if (ocs_strcmp(unqualified_name, "fc_id") == 0) { 1310 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "0x%06x", sport->fc_id); 1311 retval = 0; 1312 } else if (ocs_strcmp(unqualified_name, "index") == 0) { 1313 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "index", "%d", sport->index); 1314 retval = 0; 1315 } else if (ocs_strcmp(unqualified_name, "display_name") == 0) { 1316 ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", sport->display_name); 1317 retval = 0; 1318 } else if (ocs_strcmp(unqualified_name, "is_vport") == 0) { 1319 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_vport", sport->is_vport); 1320 retval = 0; 1321 } else if (ocs_strcmp(unqualified_name, "enable_ini") == 0) { 1322 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "enable_ini", sport->enable_ini); 1323 retval = 0; 1324 } else if (ocs_strcmp(unqualified_name, "enable_tgt") == 0) { 1325 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "enable_tgt", sport->enable_tgt); 1326 retval = 0; 1327 } else if (ocs_strcmp(unqualified_name, "p2p_winner") == 0) { 1328 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "p2p_winner", sport->p2p_winner); 1329 retval = 0; 1330 } else if (ocs_strcmp(unqualified_name, "p2p_port_id") == 0) { 1331 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "p2p_port_id", "0x%06x", sport->p2p_port_id); 1332 retval = 0; 1333 } else if (ocs_strcmp(unqualified_name, "p2p_remote_port_id") == 0) { 1334 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "p2p_remote_port_id", "0x%06x", sport->p2p_remote_port_id); 1335 retval = 0; 1336 } else if (ocs_strcmp(unqualified_name, "wwpn") == 0) { 1337 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwpn", "0x%016llx", (unsigned long long)sport->wwpn); 1338 retval = 0; 1339 } else if (ocs_strcmp(unqualified_name, "wwnn") == 0) { 1340 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwnn", "0x%016llx", (unsigned long long)sport->wwnn); 1341 retval = 0; 1342 } else { 1343 /* If I didn't know the value of this status pass the request to each of my children */ 1344 ocs_sport_lock(sport); 1345 ocs_list_foreach(&sport->node_list, node) { 1346 if ((node->mgmt_functions) && (node->mgmt_functions->get_handler)) { 1347 retval = node->mgmt_functions->get_handler(textbuf, qualifier, name, node); 1348 } 1349 1350 if (retval == 0) { 1351 break; 1352 } 1353 } 1354 ocs_sport_unlock(sport); 1355 } 1356 } 1357 1358 ocs_mgmt_end_section(textbuf, "sport", sport->instance_index); 1359 1360 return retval; 1361 } 1362 1363 void 1364 ocs_mgmt_sport_get_all(ocs_textbuf_t *textbuf, void *object) 1365 { 1366 ocs_node_t *node; 1367 ocs_sport_t *sport = (ocs_sport_t *)object; 1368 1369 ocs_mgmt_start_section(textbuf, "sport", sport->instance_index); 1370 1371 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "0x%x", sport->indicator); 1372 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "0x%06x", sport->fc_id); 1373 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "index", "%d", sport->index); 1374 ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", sport->display_name); 1375 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_vport", sport->is_vport); 1376 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "enable_ini", sport->enable_ini); 1377 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "enable_tgt", sport->enable_tgt); 1378 ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "p2p_winner", sport->p2p_winner); 1379 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "p2p_port_id", "0x%06x", sport->p2p_port_id); 1380 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "p2p_remote_port_id", "0x%06x", sport->p2p_remote_port_id); 1381 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwpn", "0x%016llx" , (unsigned long long)sport->wwpn); 1382 ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwnn", "0x%016llx", (unsigned long long)sport->wwnn); 1383 1384 ocs_sport_lock(sport); 1385 ocs_list_foreach(&sport->node_list, node) { 1386 if ((node->mgmt_functions) && (node->mgmt_functions->get_all_handler)) { 1387 node->mgmt_functions->get_all_handler(textbuf, node); 1388 } 1389 } 1390 ocs_sport_unlock(sport); 1391 1392 ocs_mgmt_end_section(textbuf, "sport", sport->instance_index); 1393 } 1394 1395 int 1396 ocs_mgmt_sport_set(char *parent, char *name, char *value, void *object) 1397 { 1398 ocs_node_t *node; 1399 ocs_sport_t *sport = (ocs_sport_t *)object; 1400 char qualifier[80]; 1401 int retval = -1; 1402 1403 snprintf(qualifier, sizeof(qualifier), "%s/sport[%d]", parent, sport->instance_index); 1404 1405 /* If it doesn't start with my qualifier I don't know what to do with it */ 1406 if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) { 1407 /* The sport has no settable values. Pass the request to each node. */ 1408 1409 ocs_sport_lock(sport); 1410 ocs_list_foreach(&sport->node_list, node) { 1411 if ((node->mgmt_functions) && (node->mgmt_functions->set_handler)) { 1412 retval = node->mgmt_functions->set_handler(qualifier, name, value, node); 1413 } 1414 if (retval == 0) { 1415 break; 1416 } 1417 } 1418 ocs_sport_unlock(sport); 1419 } 1420 1421 return retval; 1422 } 1423 1424 int 1425 ocs_mgmt_sport_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length, 1426 void *arg_out, uint32_t arg_out_length, void *object) 1427 { 1428 ocs_node_t *node; 1429 ocs_sport_t *sport = (ocs_sport_t *)object; 1430 char qualifier[80]; 1431 int retval = -1; 1432 1433 snprintf(qualifier, sizeof(qualifier), "%s.sport%d", parent, sport->instance_index); 1434 1435 /* If it doesn't start with my qualifier I don't know what to do with it */ 1436 if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) { 1437 /* See if it's an action I can perform */ 1438 1439 /* if (ocs_strcmp .... 1440 * { 1441 * } else 1442 */ 1443 1444 { 1445 /* If I didn't know how to do this action pass the request to each of my children */ 1446 ocs_sport_lock(sport); 1447 ocs_list_foreach(&sport->node_list, node) { 1448 if ((node->mgmt_functions) && (node->mgmt_functions->exec_handler)) { 1449 retval = node->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length, 1450 arg_out, arg_out_length, node); 1451 } 1452 1453 if (retval == 0) { 1454 break; 1455 } 1456 } 1457 ocs_sport_unlock(sport); 1458 } 1459 } 1460 1461 return retval; 1462 } 1463 1464 /** 1465 * @brief Save the virtual port's parameters. 1466 * 1467 * @par Description 1468 * The information required to restore a virtual port is saved. 1469 * 1470 * @param sport Pointer to the sport context. 1471 * 1472 * @return None. 1473 */ 1474 1475 static void 1476 ocs_vport_update_spec(ocs_sport_t *sport) 1477 { 1478 ocs_t *ocs = sport->ocs; 1479 ocs_xport_t *xport = ocs->xport; 1480 ocs_vport_spec_t *vport; 1481 1482 ocs_device_lock(ocs); 1483 ocs_list_foreach(&xport->vport_list, vport) { 1484 if (vport->sport == sport) { 1485 vport->wwnn = sport->wwnn; 1486 vport->wwpn = sport->wwpn; 1487 vport->tgt_data = sport->tgt_data; 1488 vport->ini_data = sport->ini_data; 1489 break; 1490 } 1491 } 1492 ocs_device_unlock(ocs); 1493 } 1494 1495 /** 1496 * @brief Create a saved vport entry. 1497 * 1498 * A saved vport entry is added to the vport list, which is restored following 1499 * a link up. This function is used to allow vports to be created the first time 1500 * the link comes up without having to go through the ioctl() API. 1501 * 1502 * @param ocs Pointer to device context. 1503 * @param wwnn World wide node name (may be zero for auto-select). 1504 * @param wwpn World wide port name (may be zero for auto-select). 1505 * @param fc_id Requested port ID (used in fabric emulation mode). 1506 * @param enable_ini TRUE if vport is to be an initiator port. 1507 * @param enable_tgt TRUE if vport is to be a target port. 1508 * @param tgt_data Pointer to target specific data. 1509 * @param ini_data Pointer to initiator specific data. 1510 * 1511 * @return None. 1512 */ 1513 1514 int8_t 1515 ocs_vport_create_spec(ocs_t *ocs, uint64_t wwnn, uint64_t wwpn, uint32_t fc_id, uint32_t enable_ini, uint32_t enable_tgt, void *tgt_data, void *ini_data) 1516 { 1517 ocs_xport_t *xport = ocs->xport; 1518 ocs_vport_spec_t *vport; 1519 1520 /* walk the ocs_vport_list and return failure if a valid(vport with non zero WWPN and WWNN) vport entry 1521 is already created */ 1522 ocs_list_foreach(&xport->vport_list, vport) { 1523 if ((wwpn && (vport->wwpn == wwpn)) && (wwnn && (vport->wwnn == wwnn))) { 1524 ocs_log_test(ocs, "Failed: VPORT %016llx %016llx already allocated\n", 1525 (unsigned long long)wwnn, (unsigned long long)wwpn); 1526 return -1; 1527 } 1528 } 1529 1530 vport = ocs_malloc(ocs, sizeof(*vport), OCS_M_ZERO | OCS_M_NOWAIT); 1531 if (vport == NULL) { 1532 ocs_log_err(ocs, "ocs_malloc failed\n"); 1533 return -1; 1534 } 1535 1536 vport->wwnn = wwnn; 1537 vport->wwpn = wwpn; 1538 vport->fc_id = fc_id; 1539 vport->domain_instance = 0; /*TODO: may need to change this */ 1540 vport->enable_tgt = enable_tgt; 1541 vport->enable_ini = enable_ini; 1542 vport->tgt_data = tgt_data; 1543 vport->ini_data = ini_data; 1544 1545 ocs_device_lock(ocs); 1546 ocs_list_add_tail(&xport->vport_list, vport); 1547 ocs_device_unlock(ocs); 1548 return 0; 1549 } 1550 1551 /* node group api */ 1552 1553 /** 1554 * @brief Perform the AND operation on source vectors. 1555 * 1556 * @par Description 1557 * Performs an AND operation on the 8-bit values in source vectors @c b and @c c. 1558 * The resulting value is stored in @c a. 1559 * 1560 * @param a Destination-byte vector. 1561 * @param b Source-byte vector. 1562 * @param c Source-byte vector. 1563 * @param n Byte count. 1564 * 1565 * @return None. 1566 */ 1567 1568 static void 1569 and8(uint8_t *a, uint8_t *b, uint8_t *c, uint32_t n) 1570 { 1571 uint32_t i; 1572 1573 for (i = 0; i < n; i ++) { 1574 *a = *b & *c; 1575 a++; 1576 b++; 1577 c++; 1578 } 1579 } 1580 1581 /** 1582 * @brief Service parameters mask data. 1583 */ 1584 static fc_sparms_t sparms_cmp_mask = { 1585 0, /*uint32_t command_code: 8, */ 1586 0, /* resv1: 24; */ 1587 {~0, ~0, ~0, ~0}, /* uint32_t common_service_parameters[4]; */ 1588 0, /* uint32_t port_name_hi; */ 1589 0, /* uint32_t port_name_lo; */ 1590 0, /* uint32_t node_name_hi; */ 1591 0, /* uint32_t node_name_lo; */ 1592 {~0, ~0, ~0, ~0}, /* uint32_t class1_service_parameters[4]; */ 1593 {~0, ~0, ~0, ~0}, /* uint32_t class2_service_parameters[4]; */ 1594 {~0, ~0, ~0, ~0}, /* uint32_t class3_service_parameters[4]; */ 1595 {~0, ~0, ~0, ~0}, /* uint32_t class4_service_parameters[4]; */ 1596 {~0, ~0, ~0, ~0}}; /* uint32_t vendor_version_level[4]; */ 1597 1598 /** 1599 * @brief Compare service parameters. 1600 * 1601 * @par Description 1602 * Returns 0 if the two service parameters are the same, excluding the port/node name 1603 * elements. 1604 * 1605 * @param sp1 Pointer to service parameters 1. 1606 * @param sp2 Pointer to service parameters 2. 1607 * 1608 * @return Returns 0 if parameters match; otherwise, returns a positive or negative value, 1609 * depending on the arithmetic magnitude of the first mismatching byte. 1610 */ 1611 1612 int 1613 ocs_sparm_cmp(uint8_t *sp1, uint8_t *sp2) 1614 { 1615 int i; 1616 int v; 1617 uint8_t *sp3 = (uint8_t*) &sparms_cmp_mask; 1618 1619 for (i = 0; i < OCS_SERVICE_PARMS_LENGTH; i ++) { 1620 v = ((int)(sp1[i] & sp3[i])) - ((int)(sp2[i] & sp3[i])); 1621 if (v) { 1622 break; 1623 } 1624 } 1625 return v; 1626 } 1627 1628 /** 1629 * @brief Allocate a node group directory entry. 1630 * 1631 * @par Description 1632 * A node group directory entry is allocated, initialized, and added to the sport's 1633 * node group directory list. 1634 * 1635 * @param sport Pointer to the sport object. 1636 * @param sparms Pointer to the service parameters. 1637 * 1638 * @return Returns a pointer to the allocated ocs_node_group_dir_t; or NULL. 1639 */ 1640 1641 ocs_node_group_dir_t * 1642 ocs_node_group_dir_alloc(ocs_sport_t *sport, uint8_t *sparms) 1643 { 1644 ocs_node_group_dir_t *node_group_dir; 1645 1646 node_group_dir = ocs_malloc(sport->ocs, sizeof(*node_group_dir), OCS_M_ZERO | OCS_M_NOWAIT); 1647 if (node_group_dir != NULL) { 1648 node_group_dir->sport = sport; 1649 1650 ocs_lock(&sport->node_group_lock); 1651 node_group_dir->instance_index = sport->node_group_dir_next_instance++; 1652 and8(node_group_dir->service_params, sparms, (uint8_t*)&sparms_cmp_mask, OCS_SERVICE_PARMS_LENGTH); 1653 ocs_list_init(&node_group_dir->node_group_list, ocs_remote_node_group_t, link); 1654 1655 node_group_dir->node_group_list_count = 0; 1656 node_group_dir->next_idx = 0; 1657 ocs_list_add_tail(&sport->node_group_dir_list, node_group_dir); 1658 ocs_unlock(&sport->node_group_lock); 1659 1660 ocs_log_debug(sport->ocs, "[%s] [%d] allocating node group directory\n", sport->display_name, 1661 node_group_dir->instance_index); 1662 } 1663 return node_group_dir; 1664 } 1665 1666 /** 1667 * @brief Free a node group directory entry. 1668 * 1669 * @par Description 1670 * The node group directory entry @c node_group_dir is removed 1671 * from the sport's node group directory list and freed. 1672 * 1673 * @param node_group_dir Pointer to the node group directory entry. 1674 * 1675 * @return None. 1676 */ 1677 1678 void 1679 ocs_node_group_dir_free(ocs_node_group_dir_t *node_group_dir) 1680 { 1681 ocs_sport_t *sport; 1682 if (node_group_dir != NULL) { 1683 sport = node_group_dir->sport; 1684 ocs_log_debug(sport->ocs, "[%s] [%d] freeing node group directory\n", sport->display_name, 1685 node_group_dir->instance_index); 1686 ocs_lock(&sport->node_group_lock); 1687 if (!ocs_list_empty(&node_group_dir->node_group_list)) { 1688 ocs_log_test(sport->ocs, "[%s] WARNING: node group list not empty\n", sport->display_name); 1689 } 1690 ocs_list_remove(&sport->node_group_dir_list, node_group_dir); 1691 ocs_unlock(&sport->node_group_lock); 1692 ocs_free(sport->ocs, node_group_dir, sizeof(*node_group_dir)); 1693 } 1694 } 1695 1696 /** 1697 * @brief Find a matching node group directory entry. 1698 * 1699 * @par Description 1700 * The sport's node group directory list is searched for a matching set of 1701 * service parameters. The first matching entry is returned; otherwise 1702 * NULL is returned. 1703 * 1704 * @param sport Pointer to the sport object. 1705 * @param sparms Pointer to the sparams to match. 1706 * 1707 * @return Returns a pointer to the first matching entry found; or NULL. 1708 */ 1709 1710 ocs_node_group_dir_t * 1711 ocs_node_group_dir_find(ocs_sport_t *sport, uint8_t *sparms) 1712 { 1713 ocs_node_group_dir_t *node_dir = NULL; 1714 1715 ocs_lock(&sport->node_group_lock); 1716 ocs_list_foreach(&sport->node_group_dir_list, node_dir) { 1717 if (ocs_sparm_cmp(sparms, node_dir->service_params) == 0) { 1718 ocs_unlock(&sport->node_group_lock); 1719 return node_dir; 1720 } 1721 } 1722 ocs_unlock(&sport->node_group_lock); 1723 return NULL; 1724 } 1725 1726 /** 1727 * @brief Allocate a remote node group object. 1728 * 1729 * @par Description 1730 * A remote node group object is allocated, initialized, and placed on the node group 1731 * list of @c node_group_dir. The HW remote node group @b alloc function is called. 1732 * 1733 * @param node_group_dir Pointer to the node group directory. 1734 * 1735 * @return Returns a pointer to the allocated remote node group object; or NULL. 1736 */ 1737 1738 ocs_remote_node_group_t * 1739 ocs_remote_node_group_alloc(ocs_node_group_dir_t *node_group_dir) 1740 { 1741 ocs_t *ocs; 1742 ocs_sport_t *sport; 1743 ocs_remote_node_group_t *node_group; 1744 ocs_hw_rtn_e hrc; 1745 1746 ocs_assert(node_group_dir, NULL); 1747 ocs_assert(node_group_dir->sport, NULL); 1748 ocs_assert(node_group_dir->sport->ocs, NULL); 1749 1750 sport = node_group_dir->sport; 1751 ocs = sport->ocs; 1752 1753 node_group = ocs_malloc(ocs, sizeof(*node_group), OCS_M_ZERO | OCS_M_NOWAIT); 1754 if (node_group != NULL) { 1755 /* set pointer to node group directory */ 1756 node_group->node_group_dir = node_group_dir; 1757 1758 ocs_lock(&node_group_dir->sport->node_group_lock); 1759 node_group->instance_index = sport->node_group_next_instance++; 1760 ocs_unlock(&node_group_dir->sport->node_group_lock); 1761 1762 /* invoke HW node group inialization */ 1763 hrc = ocs_hw_node_group_alloc(&ocs->hw, node_group); 1764 if (hrc != OCS_HW_RTN_SUCCESS) { 1765 ocs_log_err(ocs, "ocs_hw_node_group_alloc() failed: %d\n", hrc); 1766 ocs_free(ocs, node_group, sizeof(*node_group)); 1767 return NULL; 1768 } 1769 1770 ocs_log_debug(ocs, "[%s] [%d] indicator x%03x allocating node group\n", sport->display_name, 1771 node_group->indicator, node_group->instance_index); 1772 1773 /* add to the node group directory entry node group list */ 1774 ocs_lock(&node_group_dir->sport->node_group_lock); 1775 ocs_list_add_tail(&node_group_dir->node_group_list, node_group); 1776 node_group_dir->node_group_list_count ++; 1777 ocs_unlock(&node_group_dir->sport->node_group_lock); 1778 } 1779 return node_group; 1780 } 1781 1782 /** 1783 * @brief Free a remote node group object. 1784 * 1785 * @par Description 1786 * The remote node group object @c node_group is removed from its 1787 * node group directory entry and freed. 1788 * 1789 * @param node_group Pointer to the remote node group object. 1790 * 1791 * @return None. 1792 */ 1793 1794 void 1795 ocs_remote_node_group_free(ocs_remote_node_group_t *node_group) 1796 { 1797 ocs_sport_t *sport; 1798 ocs_node_group_dir_t *node_group_dir; 1799 1800 if (node_group != NULL) { 1801 ocs_assert(node_group->node_group_dir); 1802 ocs_assert(node_group->node_group_dir->sport); 1803 ocs_assert(node_group->node_group_dir->sport->ocs); 1804 1805 node_group_dir = node_group->node_group_dir; 1806 sport = node_group_dir->sport; 1807 1808 ocs_log_debug(sport->ocs, "[%s] [%d] freeing node group\n", sport->display_name, node_group->instance_index); 1809 1810 /* Remove from node group directory node group list */ 1811 ocs_lock(&sport->node_group_lock); 1812 ocs_list_remove(&node_group_dir->node_group_list, node_group); 1813 node_group_dir->node_group_list_count --; 1814 /* TODO: note that we're going to have the node_group_dir entry persist forever ... we could delete it if 1815 * the group_list_count goes to zero (or the linked list is empty */ 1816 ocs_unlock(&sport->node_group_lock); 1817 ocs_free(sport->ocs, node_group, sizeof(*node_group)); 1818 } 1819 } 1820 1821 /** 1822 * @brief Initialize a node for high login mode. 1823 * 1824 * @par Description 1825 * The @c node is initialized for high login mode. The following steps are performed: 1826 * 1. The sports node group directory is searched for a matching set of service parameters. 1827 * 2. If a matching set is not found, a node group directory entry is allocated. 1828 * 3. If less than the @c hlm_group_size number of remote node group objects is present in the 1829 * node group directory, a new remote node group object is allocated and added to the list. 1830 * 4. A remote node group object is selected, and the node is attached to the node group. 1831 * 1832 * @param node Pointer to the node. 1833 * 1834 * @return Returns 0 on success, or a negative error value on failure. 1835 */ 1836 1837 int 1838 ocs_node_group_init(ocs_node_t *node) 1839 { 1840 ocs_t *ocs; 1841 ocs_sport_t *sport; 1842 ocs_node_group_dir_t *node_group_dir; 1843 ocs_remote_node_group_t *node_group; 1844 ocs_hw_rtn_e hrc; 1845 1846 ocs_assert(node, -1); 1847 ocs_assert(node->sport, -1); 1848 ocs_assert(node->ocs, -1); 1849 1850 ocs = node->ocs; 1851 sport = node->sport; 1852 1853 ocs_assert(ocs->enable_hlm, -1); 1854 1855 /* see if there's a node group directory allocated for this service parameter set */ 1856 node_group_dir = ocs_node_group_dir_find(sport, node->service_params); 1857 if (node_group_dir == NULL) { 1858 /* not found, so allocate one */ 1859 node_group_dir = ocs_node_group_dir_alloc(sport, node->service_params); 1860 if (node_group_dir == NULL) { 1861 /* node group directory allocation failed ... can't continue, however, 1862 * the node will be allocated with a normal (not shared) RPI 1863 */ 1864 ocs_log_err(ocs, "ocs_node_group_dir_alloc() failed\n"); 1865 return -1; 1866 } 1867 } 1868 1869 /* check to see if we've allocated hlm_group_size's worth of node group structures for this 1870 * directory entry, if not, then allocate and use a new one, otherwise pick the next one. 1871 */ 1872 ocs_lock(&node->sport->node_group_lock); 1873 if (node_group_dir->node_group_list_count < ocs->hlm_group_size) { 1874 ocs_unlock(&node->sport->node_group_lock); 1875 node_group = ocs_remote_node_group_alloc(node_group_dir); 1876 if (node_group == NULL) { 1877 ocs_log_err(ocs, "ocs_remote_node_group_alloc() failed\n"); 1878 return -1; 1879 } 1880 ocs_lock(&node->sport->node_group_lock); 1881 } else { 1882 uint32_t idx = 0; 1883 1884 ocs_list_foreach(&node_group_dir->node_group_list, node_group) { 1885 if (idx >= ocs->hlm_group_size) { 1886 ocs_log_err(node->ocs, "assertion failed: idx >= ocs->hlm_group_size\n"); 1887 ocs_unlock(&node->sport->node_group_lock); 1888 return -1; 1889 } 1890 1891 if (idx == node_group_dir->next_idx) { 1892 break; 1893 } 1894 idx ++; 1895 } 1896 if (idx == ocs->hlm_group_size) { 1897 node_group = ocs_list_get_head(&node_group_dir->node_group_list); 1898 } 1899 if (++node_group_dir->next_idx >= node_group_dir->node_group_list_count) { 1900 node_group_dir->next_idx = 0; 1901 } 1902 } 1903 ocs_unlock(&node->sport->node_group_lock); 1904 1905 /* Initialize a pointer in the node back to the node group */ 1906 node->node_group = node_group; 1907 1908 /* Join this node into the group */ 1909 hrc = ocs_hw_node_group_attach(&ocs->hw, node_group, &node->rnode); 1910 1911 return (hrc == OCS_HW_RTN_SUCCESS) ? 0 : -1; 1912 } 1913