1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Emulex. All rights reserved. 24 * Use is subject to License terms. 25 */ 26 27 28 #include "emlxs.h" 29 30 31 /* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */ 32 EMLXS_MSG_DEF(EMLXS_NODE_C); 33 34 35 extern void 36 emlxs_node_close(emlxs_port_t *port, NODELIST *ndlp, uint32_t ringno, 37 uint32_t tics) 38 { 39 emlxs_hba_t *hba = HBA; 40 RING *rp; 41 NODELIST *prev; 42 43 /* If node is on a ring service queue, then remove it */ 44 mutex_enter(&EMLXS_RINGTX_LOCK); 45 46 /* Return if node destroyed */ 47 if (!ndlp || !ndlp->nlp_active) { 48 mutex_exit(&EMLXS_RINGTX_LOCK); 49 50 return; 51 } 52 if (ringno == FC_IP_RING) { 53 /* Clear IP XRI */ 54 ndlp->nlp_Xri = 0; 55 } 56 /* Check if node is already closed */ 57 if (ndlp->nlp_flag[ringno] & NLP_CLOSED) { 58 /* If so, check to see if the timer needs to be updated */ 59 if (tics) { 60 if ((ndlp->nlp_tics[ringno] && 61 (ndlp->nlp_tics[ringno] < 62 (tics + hba->timer_tics))) || 63 !(ndlp->nlp_flag[ringno] & NLP_TIMER)) { 64 EMLXS_MSGF(EMLXS_CONTEXT, 65 &emlxs_node_closed_msg, 66 "node=%p did=%06x %s. timeout=%d updated.", 67 ndlp, ndlp->nlp_DID, 68 emlxs_ring_xlate(ringno), tics); 69 70 ndlp->nlp_tics[ringno] = hba->timer_tics + tics; 71 ndlp->nlp_flag[ringno] |= NLP_TIMER; 72 } 73 } 74 mutex_exit(&EMLXS_RINGTX_LOCK); 75 76 return; 77 } 78 /* Set the node closed */ 79 ndlp->nlp_flag[ringno] |= NLP_CLOSED; 80 81 if (tics) { 82 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg, 83 "node=%p did=%06x %s. timeout=%d set.", 84 ndlp, ndlp->nlp_DID, emlxs_ring_xlate(ringno), tics); 85 86 ndlp->nlp_tics[ringno] = hba->timer_tics + tics; 87 ndlp->nlp_flag[ringno] |= NLP_TIMER; 88 } else { 89 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg, 90 "node=%p did=%06x %s.", ndlp, ndlp->nlp_DID, 91 emlxs_ring_xlate(ringno)); 92 } 93 94 if (ndlp->nlp_next[ringno]) { 95 /* Remove node from ring queue */ 96 rp = &hba->ring[ringno]; 97 98 /* If this is the only node on list */ 99 if (rp->nodeq.q_first == (void *) ndlp && rp->nodeq.q_last == 100 (void *) ndlp) { 101 rp->nodeq.q_last = NULL; 102 rp->nodeq.q_first = NULL; 103 rp->nodeq.q_cnt = 0; 104 } else if (rp->nodeq.q_first == (void *) ndlp) { 105 rp->nodeq.q_first = ndlp->nlp_next[ringno]; 106 ((NODELIST *) rp->nodeq.q_last)->nlp_next[ringno] = 107 rp->nodeq.q_first; 108 rp->nodeq.q_cnt--; 109 } else { /* This is a little more difficult */ 110 /* Find the previous node in the circular ring queue */ 111 prev = ndlp; 112 while (prev->nlp_next[ringno] != ndlp) { 113 prev = prev->nlp_next[ringno]; 114 } 115 116 prev->nlp_next[ringno] = ndlp->nlp_next[ringno]; 117 118 if (rp->nodeq.q_last == (void *) ndlp) { 119 rp->nodeq.q_last = (void *) prev; 120 } 121 rp->nodeq.q_cnt--; 122 123 } 124 125 /* Clear node */ 126 ndlp->nlp_next[ringno] = NULL; 127 } 128 mutex_exit(&EMLXS_RINGTX_LOCK); 129 130 return; 131 132 } /* emlxs_node_close() */ 133 134 135 extern void 136 emlxs_node_open(emlxs_port_t *port, NODELIST * ndlp, uint32_t ringno) 137 { 138 emlxs_hba_t *hba = HBA; 139 RING *rp; 140 uint32_t found; 141 NODELIST *nlp; 142 MAILBOXQ *mbox; 143 uint32_t i; 144 145 /* If node needs servicing, then add it to the ring queues */ 146 mutex_enter(&EMLXS_RINGTX_LOCK); 147 148 /* Return if node destroyed */ 149 if (!ndlp || !ndlp->nlp_active) { 150 mutex_exit(&EMLXS_RINGTX_LOCK); 151 152 return; 153 } 154 /* Return if node already open */ 155 if (!(ndlp->nlp_flag[ringno] & NLP_CLOSED)) { 156 mutex_exit(&EMLXS_RINGTX_LOCK); 157 158 return; 159 } 160 /* Set the node open (not closed) */ 161 ndlp->nlp_flag[ringno] &= ~NLP_CLOSED; 162 163 if ((ndlp->nlp_flag[ringno] & NLP_TIMER) && ndlp->nlp_tics[ringno] && 164 (ndlp->nlp_tics[ringno] <= hba->timer_tics)) { 165 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_opened_msg, 166 "node=%p did=%06x %s. Timeout.", ndlp, ndlp->nlp_DID, 167 emlxs_ring_xlate(ringno)); 168 } else { 169 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_opened_msg, 170 "node=%p did=%06x %s.", ndlp, ndlp->nlp_DID, 171 emlxs_ring_xlate(ringno)); 172 } 173 174 /* Clear the timer */ 175 ndlp->nlp_flag[ringno] &= ~NLP_TIMER; 176 ndlp->nlp_tics[ringno] = 0; 177 178 /* 179 * If the ptx or the tx queue needs servicing and the node is not 180 * already on the ring queue 181 */ 182 if ((ndlp->nlp_ptx[ringno].q_first || ndlp->nlp_tx[ringno].q_first) && 183 !ndlp->nlp_next[ringno]) { 184 rp = &hba->ring[ringno]; 185 186 /* If so, then add it to the ring queue */ 187 if (rp->nodeq.q_first) { 188 ((NODELIST *)rp->nodeq.q_last)->nlp_next[ringno] = 189 (uint8_t *)ndlp; 190 ndlp->nlp_next[ringno] = rp->nodeq.q_first; 191 192 /* 193 * If this is not the base node then add it to the 194 * tail 195 */ 196 if (!ndlp->nlp_base) { 197 rp->nodeq.q_last = (uint8_t *)ndlp; 198 } else { /* Otherwise, add it to the head */ 199 /* The command node always gets priority */ 200 rp->nodeq.q_first = (uint8_t *)ndlp; 201 } 202 203 rp->nodeq.q_cnt++; 204 } else { 205 rp->nodeq.q_first = (uint8_t *)ndlp; 206 rp->nodeq.q_last = (uint8_t *)ndlp; 207 ndlp->nlp_next[ringno] = ndlp; 208 rp->nodeq.q_cnt = 1; 209 } 210 } 211 mutex_exit(&EMLXS_RINGTX_LOCK); 212 213 /* If link attention needs to be cleared */ 214 if ((hba->state == FC_LINK_UP) && 215 (ringno == FC_FCP_RING)) { 216 217 /* Scan to see if any FCP2 devices are still closed */ 218 found = 0; 219 rw_enter(&port->node_rwlock, RW_READER); 220 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 221 nlp = port->node_table[i]; 222 while (nlp != NULL) { 223 if ((nlp->nlp_fcp_info & NLP_FCP_2_DEVICE) && 224 (nlp->nlp_flag[FC_FCP_RING] & NLP_CLOSED)) { 225 found = 1; 226 break; 227 } 228 nlp = nlp->nlp_list_next; 229 } 230 231 if (found) { 232 break; 233 } 234 } 235 236 rw_exit(&port->node_rwlock); 237 238 if (!found) { 239 /* Clear link attention */ 240 if ((mbox = (MAILBOXQ *) 241 emlxs_mem_get(hba, MEM_MBOX | MEM_PRI))) { 242 mutex_enter(&EMLXS_PORT_LOCK); 243 244 /* 245 * If state is not FC_LINK_UP, then either 246 * the link has gone down or a FC_CLEAR_LA 247 * has already been issued 248 */ 249 if (hba->state != FC_LINK_UP) { 250 mutex_exit(&EMLXS_PORT_LOCK); 251 (void) emlxs_mem_put(hba, MEM_MBOX, 252 (uint8_t *)mbox); 253 goto done; 254 } 255 emlxs_ffstate_change_locked(hba, FC_CLEAR_LA); 256 hba->discovery_timer = 0; 257 mutex_exit(&EMLXS_PORT_LOCK); 258 259 emlxs_mb_clear_la(hba, (MAILBOX *) mbox); 260 261 if (emlxs_mb_issue_cmd(hba, (MAILBOX *) mbox, 262 MBX_NOWAIT, 0) != MBX_BUSY) { 263 (void) emlxs_mem_put(hba, MEM_MBOX, 264 (uint8_t *)mbox); 265 } 266 } else { 267 /* 268 * Close the node and try again in a few 269 * seconds 270 */ 271 emlxs_node_close(port, ndlp, ringno, 5); 272 return; 273 } 274 } 275 } 276 done: 277 278 /* Wake any sleeping threads */ 279 mutex_enter(&EMLXS_PKT_LOCK); 280 cv_broadcast(&EMLXS_PKT_CV); 281 mutex_exit(&EMLXS_PKT_LOCK); 282 283 return; 284 285 } /* emlxs_node_open() */ 286 287 288 static int 289 emlxs_node_match_did(emlxs_port_t *port, NODELIST *ndlp, uint32_t did) 290 { 291 D_ID mydid; 292 D_ID odid; 293 D_ID ndid; 294 295 if (ndlp->nlp_DID == did) { 296 return (1); 297 } 298 299 /* 300 * Next check for area/domain == 0 match 301 */ 302 mydid.un.word = port->did; 303 if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) { 304 goto out; 305 } 306 ndid.un.word = did; 307 odid.un.word = ndlp->nlp_DID; 308 if (ndid.un.b.id == odid.un.b.id) { 309 if ((mydid.un.b.domain == ndid.un.b.domain) && 310 (mydid.un.b.area == ndid.un.b.area)) { 311 ndid.un.word = ndlp->nlp_DID; 312 odid.un.word = did; 313 if ((ndid.un.b.domain == 0) && 314 (ndid.un.b.area == 0)) { 315 return (1); 316 } 317 goto out; 318 } 319 ndid.un.word = ndlp->nlp_DID; 320 if ((mydid.un.b.domain == ndid.un.b.domain) && 321 (mydid.un.b.area == ndid.un.b.area)) { 322 odid.un.word = ndlp->nlp_DID; 323 ndid.un.word = did; 324 if ((ndid.un.b.domain == 0) && 325 (ndid.un.b.area == 0)) { 326 return (1); 327 } 328 } 329 } 330 out: 331 332 return (0); 333 334 } /* End emlxs_node_match_did */ 335 336 337 338 extern NODELIST * 339 emlxs_node_find_mac(emlxs_port_t *port, uint8_t *mac) 340 { 341 NODELIST *nlp; 342 uint32_t i; 343 344 rw_enter(&port->node_rwlock, RW_READER); 345 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 346 nlp = port->node_table[i]; 347 while (nlp != NULL) { 348 /* 349 * If portname matches mac address, return NODELIST 350 * entry 351 */ 352 if ((nlp->nlp_portname.IEEE[0] == mac[0])) { 353 if ((nlp->nlp_DID != Bcast_DID) && 354 ((nlp->nlp_DID & Fabric_DID_MASK) == 355 Fabric_DID_MASK)) { 356 nlp = (NODELIST *) nlp->nlp_list_next; 357 continue; 358 } 359 if ((nlp->nlp_portname.IEEE[1] == mac[1]) && 360 (nlp->nlp_portname.IEEE[2] == mac[2]) && 361 (nlp->nlp_portname.IEEE[3] == mac[3]) && 362 (nlp->nlp_portname.IEEE[4] == mac[4]) && 363 (nlp->nlp_portname.IEEE[5] == mac[5])) { 364 rw_exit(&port->node_rwlock); 365 return (nlp); 366 } 367 } 368 nlp = (NODELIST *) nlp->nlp_list_next; 369 } 370 } 371 rw_exit(&port->node_rwlock); 372 373 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, 374 "find: MAC=%02x%02x%02x%02x%02x%02x", 375 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 376 377 return (NULL); 378 379 } /* emlxs_node_find_mac() */ 380 381 382 extern NODELIST * 383 emlxs_node_find_did(emlxs_port_t *port, uint32_t did) 384 { 385 emlxs_hba_t *hba = HBA; 386 NODELIST *nlp; 387 uint32_t hash; 388 389 /* Check for invalid node ids */ 390 if (did == 0 || (did & 0xff000000)) { 391 return ((NODELIST *) 0); 392 } 393 /* Check for bcast node */ 394 if (did == Bcast_DID) { 395 /* Use the base node here */ 396 return (&port->node_base); 397 } 398 #ifdef MENLO_SUPPORT 399 /* Check for menlo node */ 400 if (did == EMLXS_MENLO_DID) { 401 /* Use the base node here */ 402 return (&port->node_base); 403 } 404 #endif /* MENLO_SUPPORT */ 405 406 /* Check for host node */ 407 if (did == port->did && !(hba->flag & FC_LOOPBACK_MODE)) { 408 /* Use the base node here */ 409 return (&port->node_base); 410 } 411 /* 412 * Convert well known fabric addresses to the Fabric_DID, since we 413 * don't login to some of them 414 */ 415 if ((did == SCR_DID)) { 416 did = Fabric_DID; 417 } 418 rw_enter(&port->node_rwlock, RW_READER); 419 hash = EMLXS_DID_HASH(did); 420 nlp = port->node_table[hash]; 421 while (nlp != NULL) { 422 /* Check for obvious match */ 423 if (nlp->nlp_DID == did) { 424 rw_exit(&port->node_rwlock); 425 return (nlp); 426 } 427 /* Check for detailed match */ 428 else if (emlxs_node_match_did(port, nlp, did)) { 429 rw_exit(&port->node_rwlock); 430 return (nlp); 431 } 432 nlp = (NODELIST *) nlp->nlp_list_next; 433 } 434 rw_exit(&port->node_rwlock); 435 436 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, 437 "find: did=%x", did); 438 439 /* no match found */ 440 return ((NODELIST *) 0); 441 442 } /* emlxs_node_find_did() */ 443 444 445 extern NODELIST * 446 emlxs_node_find_rpi(emlxs_port_t *port, uint32_t rpi) 447 { 448 NODELIST *nlp; 449 uint32_t i; 450 451 rw_enter(&port->node_rwlock, RW_READER); 452 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 453 nlp = port->node_table[i]; 454 while (nlp != NULL) { 455 if (nlp->nlp_Rpi == rpi) { 456 rw_exit(&port->node_rwlock); 457 return (nlp); 458 } 459 nlp = (NODELIST *) nlp->nlp_list_next; 460 } 461 } 462 rw_exit(&port->node_rwlock); 463 464 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, 465 "find: rpi=%x", rpi); 466 467 /* no match found */ 468 return ((NODELIST *) 0); 469 470 } /* emlxs_node_find_rpi() */ 471 472 473 extern NODELIST * 474 emlxs_node_find_wwpn(emlxs_port_t *port, uint8_t *wwpn) 475 { 476 NODELIST *nlp; 477 uint32_t i; 478 uint32_t j; 479 uint8_t *bptr1; 480 uint8_t *bptr2; 481 482 rw_enter(&port->node_rwlock, RW_READER); 483 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 484 nlp = port->node_table[i]; 485 while (nlp != NULL) { 486 bptr1 = (uint8_t *)&nlp->nlp_portname; 487 bptr1 += 7; 488 bptr2 = (uint8_t *)wwpn; 489 bptr2 += 7; 490 491 for (j = 0; j < 8; j++) { 492 if (*bptr1-- != *bptr2--) { 493 break; 494 } 495 } 496 497 if (j == 8) { 498 rw_exit(&port->node_rwlock); 499 return (nlp); 500 } 501 nlp = (NODELIST *) nlp->nlp_list_next; 502 } 503 } 504 rw_exit(&port->node_rwlock); 505 506 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, 507 "find: wwpn=%02x%02x%02x%02x%02x%02x%02x%02x", 508 wwpn[0], wwpn[1], wwpn[2], wwpn[3], 509 wwpn[4], wwpn[5], wwpn[6], wwpn[7]); 510 511 /* no match found */ 512 return ((NODELIST *) 0); 513 514 } /* emlxs_node_find_wwpn() */ 515 516 517 extern NODELIST * 518 emlxs_node_find_index(emlxs_port_t *port, uint32_t index, uint32_t nports_only) 519 { 520 NODELIST *nlp; 521 uint32_t i; 522 uint32_t count; 523 524 rw_enter(&port->node_rwlock, RW_READER); 525 526 if (index > port->node_count - 1) { 527 rw_exit(&port->node_rwlock); 528 return (NULL); 529 } 530 count = 0; 531 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 532 nlp = port->node_table[i]; 533 while (nlp != NULL) { 534 /* Skip fabric ports if requested */ 535 if (nports_only && (nlp->nlp_DID & 0xFFF000) == 536 0xFFF000) { 537 nlp = (NODELIST *) nlp->nlp_list_next; 538 continue; 539 } 540 if (count == index) { 541 rw_exit(&port->node_rwlock); 542 return (nlp); 543 } 544 nlp = (NODELIST *) nlp->nlp_list_next; 545 count++; 546 } 547 } 548 rw_exit(&port->node_rwlock); 549 550 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, 551 "find: index=%d", index); 552 553 /* no match found */ 554 return ((NODELIST *) 0); 555 556 } /* emlxs_node_find_wwpn() */ 557 558 559 extern uint32_t 560 emlxs_nport_count(emlxs_port_t *port) 561 { 562 NODELIST *nlp; 563 uint32_t i; 564 uint32_t nport_count = 0; 565 566 rw_enter(&port->node_rwlock, RW_READER); 567 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 568 nlp = port->node_table[i]; 569 while (nlp != NULL) { 570 if ((nlp->nlp_DID & 0xFFF000) != 0xFFF000) { 571 nport_count++; 572 } 573 nlp = (NODELIST *) nlp->nlp_list_next; 574 } 575 } 576 rw_exit(&port->node_rwlock); 577 578 return (nport_count); 579 580 } /* emlxs_nport_count() */ 581 582 583 584 extern void 585 emlxs_node_destroy_all(emlxs_port_t *port) 586 { 587 emlxs_hba_t *hba = HBA; 588 NODELIST *next; 589 NODELIST *ndlp; 590 uint8_t *wwn; 591 uint32_t i; 592 593 /* Flush and free the nodes */ 594 rw_enter(&port->node_rwlock, RW_WRITER); 595 for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) { 596 ndlp = port->node_table[i]; 597 port->node_table[i] = 0; 598 while (ndlp != NULL) { 599 next = ndlp->nlp_list_next; 600 ndlp->nlp_list_next = NULL; 601 ndlp->nlp_list_prev = NULL; 602 ndlp->nlp_active = 0; 603 604 if (port->node_count) { 605 port->node_count--; 606 } 607 wwn = (uint8_t *)&ndlp->nlp_portname; 608 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_destroy_msg, 609 "did=%06x rpi=%x " 610 "wwpn=%02x%02x%02x%02x%02x%02x%02x%02x count=%d", 611 ndlp->nlp_DID, ndlp->nlp_Rpi, 612 wwn[0], wwn[1], wwn[2], wwn[3], 613 wwn[4], wwn[5], wwn[6], wwn[7], port->node_count); 614 615 (void) emlxs_tx_node_flush(port, ndlp, 0, 0, 0); 616 617 (void) emlxs_mem_put(hba, MEM_NLP, (uint8_t *)ndlp); 618 619 ndlp = next; 620 } 621 } 622 port->node_count = 0; 623 rw_exit(&port->node_rwlock); 624 625 /* Clean the base node */ 626 mutex_enter(&EMLXS_PORT_LOCK); 627 port->node_base.nlp_list_next = NULL; 628 port->node_base.nlp_list_prev = NULL; 629 port->node_base.nlp_active = 1; 630 mutex_exit(&EMLXS_PORT_LOCK); 631 632 /* Flush the base node */ 633 (void) emlxs_tx_node_flush(port, &port->node_base, 0, 1, 0); 634 (void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0); 635 636 return; 637 638 } /* emlxs_node_destroy_all() */ 639 640 641 extern void 642 emlxs_node_add(emlxs_port_t *port, NODELIST *ndlp) 643 { 644 NODELIST *np; 645 uint8_t *wwn; 646 uint32_t hash; 647 648 rw_enter(&port->node_rwlock, RW_WRITER); 649 hash = EMLXS_DID_HASH(ndlp->nlp_DID); 650 np = port->node_table[hash]; 651 652 /* 653 * Insert node pointer to the head 654 */ 655 port->node_table[hash] = ndlp; 656 if (!np) { 657 ndlp->nlp_list_next = NULL; 658 } else { 659 ndlp->nlp_list_next = np; 660 } 661 port->node_count++; 662 663 wwn = (uint8_t *)&ndlp->nlp_portname; 664 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_msg, 665 "node=%p did=%06x rpi=%x " 666 "wwpn=%02x%02x%02x%02x%02x%02x%02x%02x count=%d", 667 ndlp, ndlp->nlp_DID, ndlp->nlp_Rpi, 668 wwn[0], wwn[1], wwn[2], wwn[3], 669 wwn[4], wwn[5], wwn[6], wwn[7], port->node_count); 670 671 rw_exit(&port->node_rwlock); 672 673 return; 674 675 } /* emlxs_node_add() */ 676 677 678 extern void 679 emlxs_node_rm(emlxs_port_t *port, NODELIST *ndlp) 680 { 681 emlxs_hba_t *hba = HBA; 682 NODELIST *np; 683 NODELIST *prevp; 684 uint8_t *wwn; 685 uint32_t hash; 686 687 rw_enter(&port->node_rwlock, RW_WRITER); 688 hash = EMLXS_DID_HASH(ndlp->nlp_DID); 689 np = port->node_table[hash]; 690 prevp = NULL; 691 while (np != NULL) { 692 if (np->nlp_DID == ndlp->nlp_DID) { 693 if (prevp == NULL) { 694 port->node_table[hash] = np->nlp_list_next; 695 } else { 696 prevp->nlp_list_next = np->nlp_list_next; 697 } 698 699 if (port->node_count) { 700 port->node_count--; 701 } 702 wwn = (uint8_t *)&ndlp->nlp_portname; 703 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_destroy_msg, 704 "did=%06x rpi=%x " 705 "wwpn=%02x%02x%02x%02x%02x%02x%02x%02x count=%d", 706 ndlp->nlp_DID, ndlp->nlp_Rpi, 707 wwn[0], wwn[1], wwn[2], wwn[3], 708 wwn[4], wwn[5], wwn[6], wwn[7], port->node_count); 709 710 (void) emlxs_tx_node_flush(port, ndlp, 0, 1, 0); 711 712 ndlp->nlp_active = 0; 713 (void) emlxs_mem_put(hba, MEM_NLP, (uint8_t *)ndlp); 714 715 break; 716 } 717 prevp = np; 718 np = np->nlp_list_next; 719 } 720 rw_exit(&port->node_rwlock); 721 722 return; 723 724 } /* emlxs_node_rm() */ 725