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