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