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