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 2009 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 /* Timeout == -1 will enable the offline timer */
35 /* Timeout not -1 will apply the timeout */
36 extern void
37 emlxs_node_close(emlxs_port_t *port, NODELIST *ndlp, uint32_t channelno,
38     int32_t timeout)
39 {
40 	emlxs_hba_t *hba = HBA;
41 	emlxs_config_t *cfg = &CFG;
42 	CHANNEL *cp;
43 	NODELIST *prev;
44 	uint32_t offline = 0;
45 
46 
47 	/* If node is on a channel service queue, then remove it */
48 	mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
49 
50 	/* Return if node destroyed */
51 	if (!ndlp || !ndlp->nlp_active) {
52 		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
53 
54 		return;
55 	}
56 
57 	/* Check offline support */
58 	if (timeout == -1) {
59 		if (cfg[CFG_OFFLINE_TIMEOUT].current) {
60 			timeout = cfg[CFG_OFFLINE_TIMEOUT].current;
61 			offline = 1;
62 		} else {
63 			timeout = 0;
64 		}
65 	}
66 
67 	if (channelno == hba->channel_ip) {
68 		/* Clear IP XRI */
69 		ndlp->nlp_Xri = 0;
70 	}
71 
72 	/* Check if node is already closed */
73 	if (ndlp->nlp_flag[channelno] & NLP_CLOSED) {
74 		if (ndlp->nlp_flag[channelno] & NLP_OFFLINE) {
75 			mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
76 			return;
77 		}
78 
79 		if (offline) {
80 			ndlp->nlp_tics[channelno] = hba->timer_tics + timeout;
81 			ndlp->nlp_flag[channelno] |= NLP_OFFLINE;
82 			mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
83 
84 			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg,
85 			    "node=%p did=%06x channel=%d. offline=%d update.",
86 			    ndlp, ndlp->nlp_DID, channelno, timeout);
87 
88 		} else if (timeout) {
89 			ndlp->nlp_tics[channelno] = hba->timer_tics + timeout;
90 			mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
91 
92 			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg,
93 			    "node=%p did=%06x channel=%d. timeout=%d update.",
94 			    ndlp, ndlp->nlp_DID, channelno, timeout);
95 		} else {
96 			mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
97 		}
98 
99 		return;
100 	}
101 
102 	/* Set the node closed */
103 	ndlp->nlp_flag[channelno] |= NLP_CLOSED;
104 
105 	if (offline) {
106 		ndlp->nlp_tics[channelno] = hba->timer_tics + timeout;
107 		ndlp->nlp_flag[channelno] |= NLP_OFFLINE;
108 
109 		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg,
110 		    "node=%p did=%06x channel=%d. offline=%d set.",
111 		    ndlp, ndlp->nlp_DID, channelno, timeout);
112 
113 	} else if (timeout) {
114 		ndlp->nlp_tics[channelno] = hba->timer_tics + timeout;
115 
116 		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg,
117 		    "node=%p did=%06x channel=%d. timeout=%d set.",
118 		    ndlp, ndlp->nlp_DID, channelno, timeout);
119 	} else {
120 		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_closed_msg,
121 		    "node=%p did=%06x channel=%d.",
122 		    ndlp, ndlp->nlp_DID, channelno);
123 	}
124 
125 
126 	/*
127 	 * ndlp->nlp_next[] and cp->nodeq list have to be updated
128 	 * simulaneously
129 	 */
130 	if (ndlp->nlp_next[channelno]) {
131 		/* Remove node from channel queue */
132 		cp = &hba->chan[channelno];
133 
134 		/* If this is the only node on list */
135 		if (cp->nodeq.q_first == (void *)ndlp &&
136 		    cp->nodeq.q_last == (void *)ndlp) {
137 			cp->nodeq.q_last = NULL;
138 			cp->nodeq.q_first = NULL;
139 			cp->nodeq.q_cnt = 0;
140 		} else if (cp->nodeq.q_first == (void *)ndlp) {
141 			cp->nodeq.q_first = ndlp->nlp_next[channelno];
142 			((NODELIST *)cp->nodeq.q_last)->nlp_next[channelno] =
143 			    cp->nodeq.q_first;
144 			cp->nodeq.q_cnt--;
145 		} else {	/* This is a little more difficult */
146 
147 			/* Find the previous node in circular channel queue */
148 			prev = ndlp;
149 			while (prev->nlp_next[channelno] != ndlp) {
150 				prev = prev->nlp_next[channelno];
151 			}
152 
153 			prev->nlp_next[channelno] = ndlp->nlp_next[channelno];
154 
155 			if (cp->nodeq.q_last == (void *)ndlp) {
156 				cp->nodeq.q_last = (void *)prev;
157 			}
158 			cp->nodeq.q_cnt--;
159 
160 		}
161 
162 		/* Clear node */
163 		ndlp->nlp_next[channelno] = NULL;
164 	}
165 
166 	mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
167 
168 	return;
169 
170 } /* emlxs_node_close() */
171 
172 
173 /* Called by emlxs_timer_check_nodes() */
174 extern void
175 emlxs_node_timeout(emlxs_port_t *port, NODELIST *ndlp, uint32_t channelno)
176 {
177 	emlxs_hba_t *hba = HBA;
178 
179 	/* If node needs servicing, then add it to the channel queues */
180 	mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
181 
182 	/* Return if node destroyed */
183 	if (!ndlp || !ndlp->nlp_active) {
184 		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
185 		return;
186 	}
187 
188 	/* Open the node if not offline */
189 	if (!(ndlp->nlp_flag[channelno] & NLP_OFFLINE)) {
190 		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
191 
192 		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_timeout_msg,
193 		    "node=%p did=%06x channel=%d Opening.", ndlp, ndlp->nlp_DID,
194 		    channelno);
195 
196 		emlxs_node_open(port, ndlp, channelno);
197 		return;
198 	}
199 
200 	/* OFFLINE TIMEOUT OCCURRED! */
201 
202 	/* Clear the timer */
203 	ndlp->nlp_tics[channelno] = 0;
204 
205 	mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
206 
207 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_timeout_msg,
208 	    "node=%p did=%06x %s. Flushing.", ndlp, ndlp->nlp_DID,
209 	    channelno);
210 
211 	/* Flush tx queue for this channel */
212 	(void) emlxs_tx_node_flush(port, ndlp, &hba->chan[channelno], 0, 0);
213 
214 	/* Flush chip queue for this channel */
215 	(void) emlxs_chipq_node_flush(port, &hba->chan[channelno], ndlp, 0);
216 
217 	return;
218 
219 } /* emlxs_node_timeout() */
220 
221 
222 extern void
223 emlxs_node_open(emlxs_port_t *port, NODELIST *ndlp, uint32_t channelno)
224 {
225 	emlxs_hba_t *hba = HBA;
226 	CHANNEL *cp;
227 	uint32_t found;
228 	NODELIST *nlp;
229 	MAILBOXQ *mbox;
230 	uint32_t i;
231 	int rc;
232 
233 	/* If node needs servicing, then add it to the channel queues */
234 	mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
235 
236 	/* Return if node destroyed */
237 	if (!ndlp || !ndlp->nlp_active) {
238 		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
239 
240 		return;
241 	}
242 
243 	/* Return if node already open */
244 	if (!(ndlp->nlp_flag[channelno] & NLP_CLOSED)) {
245 		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
246 
247 		return;
248 	}
249 
250 	/* Set the node open (not closed) */
251 	ndlp->nlp_flag[channelno] &= ~(NLP_CLOSED|NLP_OFFLINE);
252 
253 	/* Clear the timer */
254 	ndlp->nlp_tics[channelno] = 0;
255 
256 	/*
257 	 * If the ptx or the tx queue needs servicing and
258 	 * the node is not already on the channel queue
259 	 */
260 	if ((ndlp->nlp_ptx[channelno].q_first ||
261 	    ndlp->nlp_tx[channelno].q_first) && !ndlp->nlp_next[channelno]) {
262 		cp = &hba->chan[channelno];
263 
264 		/* If so, then add it to the channel queue */
265 		if (cp->nodeq.q_first) {
266 			((NODELIST *)cp->nodeq.q_last)->nlp_next[channelno] =
267 			    (uint8_t *)ndlp;
268 			ndlp->nlp_next[channelno] = cp->nodeq.q_first;
269 
270 			/* If this is not the base node then */
271 			/* add it to the tail */
272 			if (!ndlp->nlp_base) {
273 				cp->nodeq.q_last = (uint8_t *)ndlp;
274 			} else {	/* Otherwise, add it to the head */
275 
276 				/* The command node always gets priority */
277 				cp->nodeq.q_first = (uint8_t *)ndlp;
278 			}
279 
280 			cp->nodeq.q_cnt++;
281 		} else {
282 			cp->nodeq.q_first = (uint8_t *)ndlp;
283 			cp->nodeq.q_last = (uint8_t *)ndlp;
284 			ndlp->nlp_next[channelno] = ndlp;
285 			cp->nodeq.q_cnt = 1;
286 		}
287 	}
288 
289 	mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
290 
291 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_opened_msg,
292 	    "node=%p did=%06x channel=%d", ndlp, ndlp->nlp_DID, channelno);
293 
294 	/* If link attention needs to be cleared */
295 	if ((hba->state == FC_LINK_UP) && (channelno == hba->channel_fcp)) {
296 		if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
297 		/* re Think this code path. For SLI4 channel fcp == els */
298 			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
299 			    "ADD CODE to RESUME RPIs node=%p did=%06x chan=%d",
300 			    ndlp, ndlp->nlp_DID, channelno);
301 
302 			goto done;
303 		}
304 
305 		/* Scan to see if any FCP2 devices are still closed */
306 		found = 0;
307 		rw_enter(&port->node_rwlock, RW_READER);
308 		for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
309 			nlp = port->node_table[i];
310 			while (nlp != NULL) {
311 				if ((nlp->nlp_fcp_info & NLP_FCP_2_DEVICE) &&
312 				    (nlp->nlp_flag[hba->channel_fcp] &
313 				    NLP_CLOSED)) {
314 					found = 1;
315 					break;
316 
317 				}
318 				nlp = nlp->nlp_list_next;
319 			}
320 
321 			if (found) {
322 				break;
323 			}
324 		}
325 
326 		rw_exit(&port->node_rwlock);
327 
328 		if (!found) {
329 			/* Clear link attention */
330 			if ((mbox = (MAILBOXQ *)emlxs_mem_get(hba,
331 			    MEM_MBOX, 1))) {
332 				mutex_enter(&EMLXS_PORT_LOCK);
333 
334 				/*
335 				 * If state is not FC_LINK_UP, then either the
336 				 * link has gone down or a FC_CLEAR_LA has
337 				 * already been issued
338 				 */
339 				if (hba->state != FC_LINK_UP) {
340 					mutex_exit(&EMLXS_PORT_LOCK);
341 					(void) emlxs_mem_put(hba, MEM_MBOX,
342 					    (uint8_t *)mbox);
343 					goto done;
344 				}
345 
346 				EMLXS_STATE_CHANGE_LOCKED(hba, FC_CLEAR_LA);
347 				hba->discovery_timer = 0;
348 				mutex_exit(&EMLXS_PORT_LOCK);
349 
350 				emlxs_mb_clear_la(hba, mbox);
351 
352 				rc =  EMLXS_SLI_ISSUE_MBOX_CMD(hba,
353 				    mbox, MBX_NOWAIT, 0);
354 				if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
355 					(void) emlxs_mem_put(hba, MEM_MBOX,
356 					    (uint8_t *)mbox);
357 				}
358 			} else {
359 				/* Close the node and try again */
360 				/* in a few seconds */
361 				emlxs_node_close(port, ndlp, channelno, 5);
362 				return;
363 			}
364 		}
365 	}
366 
367 done:
368 
369 	/* Wake any sleeping threads */
370 	mutex_enter(&EMLXS_PKT_LOCK);
371 	cv_broadcast(&EMLXS_PKT_CV);
372 	mutex_exit(&EMLXS_PKT_LOCK);
373 
374 	return;
375 
376 } /* emlxs_node_open() */
377 
378 
379 static int
380 emlxs_node_match_did(emlxs_port_t *port, NODELIST *ndlp, uint32_t did)
381 {
382 	D_ID mydid;
383 	D_ID odid;
384 	D_ID ndid;
385 
386 	if (ndlp->nlp_DID == did)
387 		return (1);
388 
389 	/*
390 	 * Next check for area/domain == 0 match
391 	 */
392 	mydid.un.word = port->did;
393 	if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) {
394 		goto out;
395 	}
396 
397 	ndid.un.word = did;
398 	odid.un.word = ndlp->nlp_DID;
399 	if (ndid.un.b.id == odid.un.b.id) {
400 		if ((mydid.un.b.domain == ndid.un.b.domain) &&
401 		    (mydid.un.b.area == ndid.un.b.area)) {
402 			ndid.un.word = ndlp->nlp_DID;
403 			odid.un.word = did;
404 			if ((ndid.un.b.domain == 0) && (ndid.un.b.area == 0)) {
405 				return (1);
406 			}
407 			goto out;
408 		}
409 
410 		ndid.un.word = ndlp->nlp_DID;
411 		if ((mydid.un.b.domain == ndid.un.b.domain) &&
412 		    (mydid.un.b.area == ndid.un.b.area)) {
413 			odid.un.word = ndlp->nlp_DID;
414 			ndid.un.word = did;
415 			if ((ndid.un.b.domain == 0) && (ndid.un.b.area == 0)) {
416 				return (1);
417 			}
418 		}
419 	}
420 
421 out:
422 
423 	return (0);
424 
425 } /* End emlxs_node_match_did */
426 
427 
428 
429 extern NODELIST *
430 emlxs_node_find_mac(emlxs_port_t *port, uint8_t *mac)
431 {
432 	NODELIST *nlp;
433 	uint32_t i;
434 
435 	rw_enter(&port->node_rwlock, RW_READER);
436 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
437 		nlp = port->node_table[i];
438 		while (nlp != NULL) {
439 			/*
440 			 * If portname matches mac address,
441 			 * return NODELIST entry
442 			 */
443 			if ((nlp->nlp_portname.IEEE[0] == mac[0])) {
444 				if ((nlp->nlp_DID != BCAST_DID) &&
445 				    ((nlp->nlp_DID & FABRIC_DID_MASK) ==
446 				    FABRIC_DID_MASK)) {
447 					nlp = (NODELIST *)nlp->nlp_list_next;
448 					continue;
449 				}
450 
451 				if ((nlp->nlp_portname.IEEE[1] == mac[1]) &&
452 				    (nlp->nlp_portname.IEEE[2] == mac[2]) &&
453 				    (nlp->nlp_portname.IEEE[3] == mac[3]) &&
454 				    (nlp->nlp_portname.IEEE[4] == mac[4]) &&
455 				    (nlp->nlp_portname.IEEE[5] == mac[5])) {
456 					rw_exit(&port->node_rwlock);
457 					return (nlp);
458 				}
459 
460 			}
461 
462 			nlp = (NODELIST *)nlp->nlp_list_next;
463 		}
464 	}
465 	rw_exit(&port->node_rwlock);
466 
467 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg,
468 	    "find: MAC=%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2],
469 	    mac[3], mac[4], mac[5]);
470 
471 	return (NULL);
472 
473 } /* emlxs_node_find_mac() */
474 
475 
476 extern NODELIST *
477 emlxs_node_find_did(emlxs_port_t *port, uint32_t did)
478 {
479 	emlxs_hba_t *hba = HBA;
480 	NODELIST *nlp;
481 	uint32_t hash;
482 
483 	/* Check for invalid node ids  */
484 	if ((did == 0) && (!(hba->flag & FC_LOOPBACK_MODE))) {
485 		return ((NODELIST *)0);
486 	}
487 
488 	if (did & 0xff000000) {
489 		return ((NODELIST *)0);
490 	}
491 
492 	/* Check for bcast node */
493 	if (did == BCAST_DID) {
494 		/* Use the base node here */
495 		return (&port->node_base);
496 	}
497 #ifdef MENLO_SUPPORT
498 	/* Check for menlo node */
499 	if (did == EMLXS_MENLO_DID) {
500 		/* Use the base node here */
501 		return (&port->node_base);
502 	}
503 #endif /* MENLO_SUPPORT */
504 
505 	/* Check for host node */
506 	if (did == port->did && !(hba->flag & FC_LOOPBACK_MODE)) {
507 		/* Use the base node here */
508 		return (&port->node_base);
509 	}
510 
511 	/*
512 	 * Convert well known fabric addresses to the FABRIC_DID,
513 	 * since we don't login to some of them
514 	 */
515 	if ((did == SCR_DID)) {
516 		did = FABRIC_DID;
517 	}
518 
519 	rw_enter(&port->node_rwlock, RW_READER);
520 	hash = EMLXS_DID_HASH(did);
521 	nlp = port->node_table[hash];
522 	while (nlp != NULL) {
523 		/* Check for obvious match */
524 		if (nlp->nlp_DID == did) {
525 			rw_exit(&port->node_rwlock);
526 			return (nlp);
527 		}
528 
529 		/* Check for detailed match */
530 		else if (emlxs_node_match_did(port, nlp, did)) {
531 			rw_exit(&port->node_rwlock);
532 			return (nlp);
533 		}
534 
535 		nlp = (NODELIST *)nlp->nlp_list_next;
536 	}
537 	rw_exit(&port->node_rwlock);
538 
539 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, "find: did=%x",
540 	    did);
541 
542 	/* no match found */
543 	return ((NODELIST *)0);
544 
545 } /* emlxs_node_find_did() */
546 
547 
548 extern NODELIST *
549 emlxs_node_find_rpi(emlxs_port_t *port, uint32_t rpi)
550 {
551 	NODELIST *nlp;
552 	uint32_t i;
553 
554 	rw_enter(&port->node_rwlock, RW_READER);
555 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
556 		nlp = port->node_table[i];
557 		while (nlp != NULL) {
558 			if (nlp->nlp_Rpi == rpi) {
559 				rw_exit(&port->node_rwlock);
560 				return (nlp);
561 			}
562 
563 			nlp = (NODELIST *)nlp->nlp_list_next;
564 		}
565 	}
566 	rw_exit(&port->node_rwlock);
567 
568 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, "find: rpi=%x",
569 	    rpi);
570 
571 	/* no match found */
572 	return ((NODELIST *)0);
573 
574 } /* emlxs_node_find_rpi() */
575 
576 
577 extern NODELIST *
578 emlxs_node_find_wwpn(emlxs_port_t *port, uint8_t *wwpn)
579 {
580 	NODELIST *nlp;
581 	uint32_t i;
582 	uint32_t j;
583 	uint8_t *bptr1;
584 	uint8_t *bptr2;
585 
586 	rw_enter(&port->node_rwlock, RW_READER);
587 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
588 		nlp = port->node_table[i];
589 		while (nlp != NULL) {
590 			bptr1 = (uint8_t *)&nlp->nlp_portname;
591 			bptr1 += 7;
592 			bptr2 = (uint8_t *)wwpn;
593 			bptr2 += 7;
594 
595 			for (j = 0; j < 8; j++) {
596 				if (*bptr1-- != *bptr2--) {
597 					break;
598 				}
599 			}
600 
601 			if (j == 8) {
602 				rw_exit(&port->node_rwlock);
603 				return (nlp);
604 			}
605 
606 			nlp = (NODELIST *)nlp->nlp_list_next;
607 		}
608 	}
609 	rw_exit(&port->node_rwlock);
610 
611 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg,
612 	    "find: wwpn=%02x%02x%02x%02x%02x%02x%02x%02x", wwpn[0], wwpn[1],
613 	    wwpn[2], wwpn[3], wwpn[4], wwpn[5], wwpn[6], wwpn[7]);
614 
615 	/* no match found */
616 	return ((NODELIST *)0);
617 
618 } /* emlxs_node_find_wwpn() */
619 
620 
621 extern NODELIST *
622 emlxs_node_find_index(emlxs_port_t *port, uint32_t index,
623     uint32_t nports_only)
624 {
625 	NODELIST *nlp;
626 	uint32_t i;
627 	uint32_t count;
628 
629 	rw_enter(&port->node_rwlock, RW_READER);
630 
631 	if (index > port->node_count - 1) {
632 		rw_exit(&port->node_rwlock);
633 		return (NULL);
634 	}
635 
636 	count = 0;
637 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
638 		nlp = port->node_table[i];
639 		while (nlp != NULL) {
640 			/* Skip fabric ports if requested */
641 			if (nports_only &&
642 			    (nlp->nlp_DID & 0xFFF000) == 0xFFF000) {
643 				nlp = (NODELIST *)nlp->nlp_list_next;
644 				continue;
645 			}
646 
647 			if (count == index) {
648 				rw_exit(&port->node_rwlock);
649 				return (nlp);
650 			}
651 
652 			nlp = (NODELIST *)nlp->nlp_list_next;
653 			count++;
654 		}
655 	}
656 	rw_exit(&port->node_rwlock);
657 
658 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_not_found_msg, "find: index=%d",
659 	    index);
660 
661 	/* no match found */
662 	return ((NODELIST *)0);
663 
664 } /* emlxs_node_find_wwpn() */
665 
666 
667 extern uint32_t
668 emlxs_nport_count(emlxs_port_t *port)
669 {
670 	NODELIST *nlp;
671 	uint32_t i;
672 	uint32_t nport_count = 0;
673 
674 	rw_enter(&port->node_rwlock, RW_READER);
675 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
676 		nlp = port->node_table[i];
677 		while (nlp != NULL) {
678 			if ((nlp->nlp_DID & 0xFFF000) != 0xFFF000) {
679 				nport_count++;
680 			}
681 
682 			nlp = (NODELIST *)nlp->nlp_list_next;
683 		}
684 	}
685 	rw_exit(&port->node_rwlock);
686 
687 	return (nport_count);
688 
689 } /* emlxs_nport_count() */
690 
691 
692 
693 extern void
694 emlxs_node_destroy_all(emlxs_port_t *port)
695 {
696 	emlxs_hba_t *hba = HBA;
697 	NODELIST *next;
698 	NODELIST *ndlp;
699 	RPIobj_t *rp;
700 	uint8_t *wwn;
701 	uint32_t i;
702 
703 	/* Flush and free the nodes */
704 	rw_enter(&port->node_rwlock, RW_WRITER);
705 	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
706 		ndlp = port->node_table[i];
707 		port->node_table[i] = 0;
708 		while (ndlp != NULL) {
709 			next = ndlp->nlp_list_next;
710 			ndlp->nlp_list_next = NULL;
711 			ndlp->nlp_list_prev = NULL;
712 			ndlp->nlp_active = 0;
713 
714 			if (port->node_count) {
715 				port->node_count--;
716 			}
717 
718 			wwn = (uint8_t *)&ndlp->nlp_portname;
719 			EMLXS_MSGF(EMLXS_CONTEXT,
720 			    &emlxs_node_destroy_msg, "did=%06x "
721 			    "rpi=%x wwpn=%02x%02x%02x%02x%02x%02x%02x%02x "
722 			    "count=%d", ndlp->nlp_DID, ndlp->nlp_Rpi, wwn[0],
723 			    wwn[1], wwn[2], wwn[3], wwn[4], wwn[5], wwn[6],
724 			    wwn[7], port->node_count);
725 
726 			(void) emlxs_tx_node_flush(port, ndlp, 0, 0, 0);
727 
728 			/* Break Node/RPI binding */
729 			if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
730 				rp = EMLXS_NODE_TO_RPI(hba, ndlp);
731 				ndlp->RPIp = NULL;
732 
733 				if (rp) {
734 					rp->node = NULL;
735 					(void) emlxs_sli4_free_rpi(hba, rp);
736 				}
737 			}
738 
739 			(void) emlxs_mem_put(hba, MEM_NLP, (uint8_t *)ndlp);
740 
741 			ndlp = next;
742 		}
743 	}
744 	port->node_count = 0;
745 	rw_exit(&port->node_rwlock);
746 
747 	/* Clean the base node */
748 	mutex_enter(&EMLXS_PORT_LOCK);
749 	port->node_base.nlp_list_next = NULL;
750 	port->node_base.nlp_list_prev = NULL;
751 	port->node_base.nlp_active = 1;
752 	mutex_exit(&EMLXS_PORT_LOCK);
753 
754 	/* Flush the base node */
755 	(void) emlxs_tx_node_flush(port, &port->node_base, 0, 1, 0);
756 	(void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0);
757 
758 	return;
759 
760 } /* emlxs_node_destroy_all() */
761 
762 
763 extern void
764 emlxs_node_add(emlxs_port_t *port, NODELIST *ndlp)
765 {
766 	emlxs_hba_t *hba = HBA;
767 	NODELIST *np;
768 	uint8_t *wwn;
769 	uint32_t hash;
770 	RPIobj_t *rp;
771 
772 	rw_enter(&port->node_rwlock, RW_WRITER);
773 	hash = EMLXS_DID_HASH(ndlp->nlp_DID);
774 	np = port->node_table[hash];
775 
776 	/*
777 	 * Insert node pointer to the head
778 	 */
779 	port->node_table[hash] = ndlp;
780 	if (!np) {
781 		ndlp->nlp_list_next = NULL;
782 	} else {
783 		ndlp->nlp_list_next = np;
784 	}
785 	port->node_count++;
786 
787 	wwn = (uint8_t *)&ndlp->nlp_portname;
788 	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_msg,
789 	    "node=%p did=%06x rpi=%x wwpn=%02x%02x%02x%02x%02x%02x%02x%02x "
790 	    "count=%d", ndlp, ndlp->nlp_DID, ndlp->nlp_Rpi, wwn[0], wwn[1],
791 	    wwn[2], wwn[3], wwn[4], wwn[5], wwn[6], wwn[7], port->node_count);
792 
793 	/* Add Node/RPI binding */
794 	if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
795 		rp = emlxs_sli4_find_rpi(hba, ndlp->nlp_Rpi);
796 
797 		if (rp) {
798 			rp->node = ndlp;
799 			ndlp->RPIp = rp;
800 		} else {
801 			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_create_msg,
802 			    "Unable to find RPI! did=%x rpi=%x",
803 			    ndlp->nlp_DID, ndlp->nlp_Rpi);
804 		}
805 	}
806 
807 	rw_exit(&port->node_rwlock);
808 
809 	return;
810 
811 } /* emlxs_node_add() */
812 
813 
814 extern void
815 emlxs_node_rm(emlxs_port_t *port, NODELIST *ndlp)
816 {
817 	emlxs_hba_t *hba = HBA;
818 	NODELIST *np;
819 	NODELIST *prevp;
820 	RPIobj_t *rp;
821 	uint8_t *wwn;
822 	uint32_t hash;
823 
824 	rw_enter(&port->node_rwlock, RW_WRITER);
825 	hash = EMLXS_DID_HASH(ndlp->nlp_DID);
826 	np = port->node_table[hash];
827 	prevp = NULL;
828 	while (np != NULL) {
829 		if (np->nlp_DID == ndlp->nlp_DID) {
830 			if (prevp == NULL) {
831 				port->node_table[hash] = np->nlp_list_next;
832 			} else {
833 				prevp->nlp_list_next = np->nlp_list_next;
834 			}
835 
836 			if (port->node_count) {
837 				port->node_count--;
838 			}
839 
840 			wwn = (uint8_t *)&ndlp->nlp_portname;
841 			EMLXS_MSGF(EMLXS_CONTEXT,
842 			    &emlxs_node_destroy_msg, "did=%06x "
843 			    "rpi=%x wwpn=%02x%02x%02x%02x%02x%02x%02x%02x "
844 			    "count=%d", ndlp->nlp_DID, ndlp->nlp_Rpi, wwn[0],
845 			    wwn[1], wwn[2], wwn[3], wwn[4], wwn[5], wwn[6],
846 			    wwn[7], port->node_count);
847 
848 			(void) emlxs_tx_node_flush(port, ndlp, 0, 1, 0);
849 
850 			ndlp->nlp_active = 0;
851 
852 			/* Break Node/RPI binding */
853 			if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
854 				rp = EMLXS_NODE_TO_RPI(hba, ndlp);
855 				ndlp->RPIp = NULL;
856 
857 				if (rp) {
858 					rp->node = NULL;
859 					(void) emlxs_sli4_free_rpi(hba, rp);
860 				}
861 			}
862 
863 			(void) emlxs_mem_put(hba, MEM_NLP, (uint8_t *)ndlp);
864 
865 			break;
866 		}
867 		prevp = np;
868 		np = np->nlp_list_next;
869 	}
870 	rw_exit(&port->node_rwlock);
871 
872 	return;
873 
874 } /* emlxs_node_rm() */
875