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