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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/scsi/scsi.h>
32 #include <sys/scsi/impl/scsi_reset_notify.h>
33 #include <sys/disp.h>
34 #include <sys/byteorder.h>
35 #include <sys/varargs.h>
36 #include <sys/atomic.h>
37 
38 #include <stmf.h>
39 #include <stmf_ioctl.h>
40 #include <portif.h>
41 #include <fct.h>
42 #include <fct_impl.h>
43 #include <discovery.h>
44 #include <fctio.h>
45 
46 disc_action_t fct_handle_local_port_event(fct_i_local_port_t *iport);
47 disc_action_t fct_walk_discovery_queue(fct_i_local_port_t *iport);
48 disc_action_t fct_process_els(fct_i_local_port_t *iport,
49     fct_i_remote_port_t *irp);
50 fct_status_t fct_send_accrjt(fct_cmd_t *cmd, uint8_t accrjt,
51     uint8_t reason, uint8_t expl);
52 disc_action_t fct_link_init_complete(fct_i_local_port_t *iport);
53 fct_status_t fct_complete_previous_li_cmd(fct_i_local_port_t *iport);
54 fct_status_t fct_sol_plogi(fct_i_local_port_t *iport, uint32_t id,
55     fct_cmd_t **ret_ppcmd, int implicit);
56 fct_status_t fct_sol_ct(fct_i_local_port_t *iport, uint32_t id,
57     fct_cmd_t **ret_ppcmd, uint16_t opcode);
58 fct_status_t fct_ns_scr(fct_i_local_port_t *iport, uint32_t id,
59     fct_cmd_t **ret_ppcmd);
60 static disc_action_t fct_check_cmdlist(fct_i_local_port_t *iport);
61 static disc_action_t fct_check_solcmd_queue(fct_i_local_port_t *iport);
62 static void fct_rscn_verify(fct_i_local_port_t *iport,
63     uint8_t *rscn_req_payload, uint32_t rscn_req_size);
64 void fct_gid_cb(fct_i_cmd_t *icmd);
65 
66 char *fct_els_names[] = { 0, "LS_RJT", "ACC", "PLOGI", "FLOGI", "LOGO",
67 				"ABTX", "RCS", "RES", "RSS", "RSI", "ESTS",
68 				"ESTC", "ADVC", "RTV", "RLS",
69 	/* 0x10 */		"ECHO", "TEST", "RRQ", "REC", "SRR", 0, 0,
70 				0, 0, 0, 0, 0, 0, 0, 0, 0,
71 	/* 0x20 */		"PRLI", "PRLO", "SCN", "TPLS",
72 				"TPRLO", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 	/* 0x30 */		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 	/* 0x40 */		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 	/* 0x50 */		"PDISC", "FDISC", "ADISC", "RNC", "FARP",
76 				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 	/* 0x60 */		"FAN", "RSCN", "SCR", 0, 0, 0, 0, 0, 0, 0, 0,
78 				0, 0, 0, 0, 0,
79 	/* 0x70 */		"LINIT", "LPC", "LSTS", 0, 0, 0, 0, 0,
80 				"RNID", "RLIR", "LIRR", 0, 0, 0, 0, 0
81 		};
82 
83 extern uint32_t fct_rscn_options;
84 
85 /*
86  * NOTE: if anybody drops the iport_worker_lock then they should not return
87  * DISC_ACTION_NO_WORK. Which also means, dont drop the lock if you have
88  * nothing to do. Or else return DISC_ACTION_RESCAN or DISC_ACTION_DELAY_RESCAN.
89  * But you cannot be infinitly returning those so have some logic to
90  * determine that there is nothing to do without dropping the lock.
91  */
92 void
93 fct_port_worker(void *arg)
94 {
95 	fct_local_port_t	*port = (fct_local_port_t *)arg;
96 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
97 	    port->port_fct_private;
98 	disc_action_t		suggested_action;
99 	clock_t			dl, short_delay, long_delay;
100 	int64_t			tmp_delay;
101 
102 	iport->iport_cmdcheck_clock = ddi_get_lbolt() +
103 	    drv_usectohz(FCT_CMDLIST_CHECK_SECONDS * 1000000);
104 	short_delay = drv_usectohz(10000);
105 	long_delay = drv_usectohz(1000000);
106 
107 	stmf_trace(iport->iport_alias, "iport is %p", iport);
108 	/* Discovery loop */
109 	mutex_enter(&iport->iport_worker_lock);
110 	atomic_or_32(&iport->iport_flags, IPORT_WORKER_RUNNING);
111 	while ((iport->iport_flags & IPORT_TERMINATE_WORKER) == 0) {
112 		suggested_action = DISC_ACTION_NO_WORK;
113 		/*
114 		 * Local port events are of the highest prioriy
115 		 */
116 		if (iport->iport_event_head) {
117 			suggested_action |= fct_handle_local_port_event(iport);
118 		}
119 
120 		/*
121 		 * We could post solicited ELSes to discovery queue.
122 		 * solicited CT will be processed inside fct_check_solcmd_queue
123 		 */
124 		if (iport->iport_solcmd_queue) {
125 			suggested_action |= fct_check_solcmd_queue(iport);
126 		}
127 
128 		/*
129 		 * All solicited and unsolicited ELS will be handled here
130 		 */
131 		if (iport->iport_rpwe_head) {
132 			suggested_action |= fct_walk_discovery_queue(iport);
133 		}
134 
135 		/*
136 		 * We only process it when there's no outstanding link init CMD
137 		 */
138 		if ((iport->iport_link_state ==	PORT_STATE_LINK_INIT_START) &&
139 		    !(iport->iport_li_state & (LI_STATE_FLAG_CMD_WAITING |
140 		    LI_STATE_FLAG_NO_LI_YET))) {
141 			suggested_action |= fct_process_link_init(iport);
142 		}
143 
144 		/*
145 		 * We process cmd aborting in the end
146 		 */
147 		if (iport->iport_abort_queue) {
148 			suggested_action |= fct_cmd_terminator(iport);
149 		}
150 
151 		/*
152 		 * Check cmd max/free
153 		 */
154 		if (iport->iport_cmdcheck_clock <= ddi_get_lbolt()) {
155 			suggested_action |= fct_check_cmdlist(iport);
156 			iport->iport_cmdcheck_clock = ddi_get_lbolt() +
157 			    drv_usectohz(FCT_CMDLIST_CHECK_SECONDS * 1000000);
158 			iport->iport_max_active_ncmds = 0;
159 		}
160 
161 		if (iport->iport_offline_prstate != FCT_OPR_DONE) {
162 			suggested_action |= fct_handle_port_offline(iport);
163 		}
164 
165 		if (suggested_action & DISC_ACTION_RESCAN) {
166 			continue;
167 		} else if (suggested_action & DISC_ACTION_DELAY_RESCAN) {
168 			/*
169 			 * This is not very optimum as whoever returned
170 			 * DISC_ACTION_DELAY_RESCAN must have dropped the lock
171 			 * and more things might have queued up. But since
172 			 * we are only doing small delays, it only delays
173 			 * things by a few ms, which is okey.
174 			 */
175 			if (suggested_action & DISC_ACTION_USE_SHORT_DELAY) {
176 				dl = short_delay;
177 			} else {
178 				dl = long_delay;
179 			}
180 			atomic_or_32(&iport->iport_flags,
181 			    IPORT_WORKER_DOING_TIMEDWAIT);
182 			(void) cv_timedwait(&iport->iport_worker_cv,
183 			    &iport->iport_worker_lock, ddi_get_lbolt() + dl);
184 			atomic_and_32(&iport->iport_flags,
185 			    ~IPORT_WORKER_DOING_TIMEDWAIT);
186 		} else {
187 			atomic_or_32(&iport->iport_flags,
188 			    IPORT_WORKER_DOING_WAIT);
189 			tmp_delay = (int64_t)(iport->iport_cmdcheck_clock -
190 			    ddi_get_lbolt());
191 			if (tmp_delay < 0) {
192 				tmp_delay = (int64_t)short_delay;
193 			}
194 			(void) cv_timedwait(&iport->iport_worker_cv,
195 			    &iport->iport_worker_lock, ddi_get_lbolt() +
196 			    (clock_t)tmp_delay);
197 			atomic_and_32(&iport->iport_flags,
198 			    ~IPORT_WORKER_DOING_WAIT);
199 		}
200 	}
201 
202 	atomic_and_32(&iport->iport_flags, ~IPORT_WORKER_RUNNING);
203 	mutex_exit(&iport->iport_worker_lock);
204 }
205 
206 static char *topologies[] = { "Unknown", "Direct Pt-to-Pt", "Private Loop",
207 				"Unknown", "Unknown", "Fabric Pt-to-Pt",
208 				"Public Loop" };
209 
210 void
211 fct_li_to_txt(fct_link_info_t *li, char *topology, char *speed)
212 {
213 	uint8_t s = li->port_speed;
214 
215 	if (li->port_topology > PORT_TOPOLOGY_PUBLIC_LOOP) {
216 		(void) sprintf(topology, "Invalid %02x", li->port_topology);
217 	} else {
218 		(void) strcpy(topology, topologies[li->port_topology]);
219 	}
220 
221 	if ((s == 0) || ((s & 0xf0) != 0) || ((s & (s - 1)) != 0)) {
222 		speed[0] = '?';
223 	} else {
224 		speed[0] = '0' + li->port_speed;
225 	}
226 
227 	speed[1] = 'G';
228 	speed[2] = 0;
229 }
230 
231 /*
232  * discovery lock held.
233  * XXX: Implement command cleanup upon Link down.
234  * XXX: Implement a clean start and FC-GS registrations upon Link up.
235  *
236  * ================ Local Port State Machine ============
237  * <hba fatal>		 <Link up>---|
238  *   |				     v
239  *   |	      <Start>--->[LINK_DOWN]--->[LINK_INIT_START]--->[LINK_INIT_DONE]
240  *   |			  ^    ^		  ^    |		   |
241  *   |		      |---|    |  |--<Link down>  |-|  |---><Link Reset><--|
242  *   |		      |	       |  v		    |	       v
243  *   |->[FATAL_CLEANING]  [LINK_DOWN_CLEANING]--->[LINK_UP_CLEANING]
244  *					       ^
245  *					       |--<Link up>
246  * =======================================================
247  * An explicit port_online() is only allowed in LINK_DOWN state.
248  * An explicit port_offline() is only allowed in LINKDOWN and
249  * LINK_INIT_DONE state.
250  */
251 disc_action_t
252 fct_handle_local_port_event(fct_i_local_port_t *iport)
253 {
254 	disc_action_t	ret = DISC_ACTION_RESCAN;
255 	fct_i_event_t	*in;
256 	uint16_t	old_state, new_state, new_bits;
257 	int		dqueue_and_free = 1;
258 	int		retry_implicit_logo = 0;
259 
260 	if (iport->iport_event_head == NULL)
261 		return (DISC_ACTION_NO_WORK);
262 	in = iport->iport_event_head;
263 	mutex_exit(&iport->iport_worker_lock);
264 
265 	rw_enter(&iport->iport_lock, RW_WRITER);
266 	/* Calculate new state */
267 	new_state = iport->iport_link_state;
268 	if (in->event_type == FCT_EVENT_LINK_DOWN) {
269 		new_state = PORT_STATE_LINK_DOWN_CLEANING;
270 	} else if (in->event_type == FCT_EVENT_LINK_UP) {
271 		if (iport->iport_link_state == PORT_STATE_LINK_DOWN_CLEANING)
272 			new_state = PORT_STATE_LINK_UP_CLEANING;
273 		else if (iport->iport_link_state == PORT_STATE_LINK_DOWN)
274 			new_state = PORT_STATE_LINK_INIT_START;
275 		else { /* This should not happen */
276 			stmf_trace(iport->iport_alias,
277 			    "Link up received when link state was"
278 			    "%x, Ignoring...", iport->iport_link_state);
279 		}
280 	} else if (in->event_type == FCT_I_EVENT_CLEANUP_POLL) {
281 		if (!fct_local_port_cleanup_done(iport)) {
282 			if (iport->iport_link_cleanup_retry >= 3) {
283 				iport->iport_link_cleanup_retry = 0;
284 				retry_implicit_logo = 1;
285 			} else {
286 				iport->iport_link_cleanup_retry++;
287 			}
288 			dqueue_and_free = 0;
289 			ret = DISC_ACTION_DELAY_RESCAN;
290 		} else {
291 			if (iport->iport_link_state ==
292 			    PORT_STATE_LINK_DOWN_CLEANING) {
293 				new_state = PORT_STATE_LINK_DOWN;
294 			} else if (iport->iport_link_state ==
295 			    PORT_STATE_LINK_UP_CLEANING) {
296 				new_state = PORT_STATE_LINK_INIT_START;
297 			} else { /* This should not have happened */
298 				cmn_err(CE_WARN, "port state changed to %x "
299 				    "during cleanup", iport->iport_link_state);
300 				new_state = PORT_STATE_LINK_DOWN;
301 			}
302 		}
303 	} else if (in->event_type == FCT_EVENT_LINK_RESET) {
304 		/* Link reset is only allowed when we are Online */
305 		if (iport->iport_link_state & S_LINK_ONLINE) {
306 			new_state = PORT_STATE_LINK_UP_CLEANING;
307 		}
308 	} else if (in->event_type == FCT_I_EVENT_LINK_INIT_DONE) {
309 		if (iport->iport_link_state == PORT_STATE_LINK_INIT_START) {
310 			new_state = PORT_STATE_LINK_INIT_DONE;
311 			iport->iport_li_state = LI_STATE_START;
312 		}
313 	} else {
314 		ASSERT(0);
315 	}
316 	new_bits = iport->iport_link_state ^
317 					(iport->iport_link_state | new_state);
318 	old_state = iport->iport_link_state;
319 	iport->iport_link_state = new_state;
320 	rw_exit(&iport->iport_lock);
321 
322 	stmf_trace(iport->iport_alias, "port state change from %x to %x",
323 			old_state, new_state);
324 
325 	if (new_bits & S_PORT_CLEANUP) {
326 		(void) fct_implicitly_logo_all(iport, 0);
327 		fct_handle_event(iport->iport_port,
328 			FCT_I_EVENT_CLEANUP_POLL, 0, 0);
329 	}
330 	if (retry_implicit_logo) {
331 		(void) fct_implicitly_logo_all(iport, 1);
332 	}
333 	if (new_bits & S_INIT_LINK) {
334 		fct_link_info_t *li = &iport->iport_link_info;
335 		fct_status_t li_ret;
336 		iport->iport_li_state |= LI_STATE_FLAG_NO_LI_YET;
337 		bzero(li, sizeof (*li));
338 		if ((li_ret = iport->iport_port->port_get_link_info(
339 		    iport->iport_port, li)) != FCT_SUCCESS) {
340 			stmf_trace(iport->iport_alias, "iport-%p: "
341 			    "port_get_link_info failed, ret %llx, forcing "
342 			    "link down.", iport, li_ret);
343 			fct_handle_event(iport->iport_port,
344 				FCT_EVENT_LINK_DOWN, 0, 0);
345 		} else {
346 			iport->iport_login_retry = 0;
347 			/* This will reset LI_STATE_FLAG_NO_LI_YET */
348 			iport->iport_li_state = LI_STATE_START;
349 			atomic_or_32(&iport->iport_flags,
350 					IPORT_ALLOW_UNSOL_FLOGI);
351 		}
352 		fct_log_local_port_event(iport->iport_port,
353 		    ESC_SUNFC_PORT_ONLINE);
354 	} else if (new_bits & S_RCVD_LINK_DOWN) {
355 		fct_log_local_port_event(iport->iport_port,
356 		    ESC_SUNFC_PORT_OFFLINE);
357 	}
358 
359 	mutex_enter(&iport->iport_worker_lock);
360 	if (in && dqueue_and_free) {
361 		iport->iport_event_head = in->event_next;
362 		if (iport->iport_event_head == NULL)
363 			iport->iport_event_tail = NULL;
364 		kmem_free(in, sizeof (*in));
365 	}
366 	return (ret);
367 }
368 
369 int
370 fct_lport_has_bigger_wwn(fct_i_local_port_t *iport)
371 {
372 	uint8_t *l, *r;
373 	int i;
374 	uint64_t wl, wr;
375 
376 	l = iport->iport_port->port_pwwn;
377 	r = iport->iport_link_info.port_rpwwn;
378 
379 	for (i = 0, wl = 0; i < 8; i++) {
380 		wl <<= 8;
381 		wl |= l[i];
382 	}
383 	for (i = 0, wr = 0; i < 8; i++) {
384 		wr <<= 8;
385 		wr |= r[i];
386 	}
387 
388 	if (wl > wr) {
389 		return (1);
390 	}
391 
392 	return (0);
393 }
394 
395 void
396 fct_do_flogi(fct_i_local_port_t *iport)
397 {
398 	fct_flogi_xchg_t fx;
399 	fct_status_t ret;
400 	int force_link_down = 0;
401 	int do_retry = 0;
402 
403 	bzero(&fx, sizeof (fx));
404 	fx.fx_op = ELS_OP_FLOGI;
405 	if (iport->iport_login_retry == 0) {
406 		fx.fx_sec_timeout = 2;
407 	} else {
408 		fx.fx_sec_timeout = 5;
409 	}
410 	if (iport->iport_link_info.port_topology & PORT_TOPOLOGY_PRIVATE_LOOP) {
411 		fx.fx_sid = iport->iport_link_info.portid & 0xFF;
412 	}
413 	fx.fx_did = 0xFFFFFE;
414 	bcopy(iport->iport_port->port_nwwn, fx.fx_nwwn, 8);
415 	bcopy(iport->iport_port->port_pwwn, fx.fx_pwwn, 8);
416 	mutex_exit(&iport->iport_worker_lock);
417 	ret = iport->iport_port->port_flogi_xchg(iport->iport_port, &fx);
418 	mutex_enter(&iport->iport_worker_lock);
419 	if (IPORT_FLOGI_DONE(iport)) {
420 		/* The unsolicited path finished it. */
421 		return;
422 	}
423 	if (ret == FCT_NOT_FOUND) {
424 		if (iport->iport_link_info.port_topology &
425 				PORT_TOPOLOGY_PRIVATE_LOOP) {
426 			/* This is a private loop. There is no switch. */
427 			iport->iport_link_info.port_no_fct_flogi = 1;
428 			return;
429 		}
430 		/*
431 		 * This is really an error. This means we cannot init the
432 		 * link. Lets force the link to go down.
433 		 */
434 		force_link_down = 1;
435 	} else if ((ret == FCT_SUCCESS) && (fx.fx_op == ELS_OP_LSRJT)) {
436 		if ((fx.fx_rjt_reason == 5) || (fx.fx_rjt_reason == 0xe) ||
437 		    ((fx.fx_rjt_reason == 9) && (fx.fx_rjt_expl == 0x29))) {
438 			do_retry = 1;
439 		} else {
440 			force_link_down = 1;
441 		}
442 	} else if (ret == STMF_TIMEOUT) {
443 		do_retry = 1;
444 	} else if (ret != FCT_SUCCESS) {
445 		force_link_down = 1;
446 	}
447 
448 	if (do_retry) {
449 		iport->iport_login_retry++;
450 		if (iport->iport_login_retry >= 5)
451 			force_link_down = 1;
452 		return;
453 	}
454 
455 	if (force_link_down) {
456 		stmf_trace(iport->iport_alias, "iport-%p: flogi xchg failed. "
457 		    "Forcing link down, ret=%llx login_retry=%d ret_op=%d "
458 		    "reason=%d expl=%d", iport, ret, iport->iport_login_retry,
459 		    fx.fx_op, fx.fx_rjt_reason, fx.fx_rjt_expl);
460 		mutex_exit(&iport->iport_worker_lock);
461 		fct_handle_event(iport->iport_port, FCT_EVENT_LINK_DOWN, 0, 0);
462 		mutex_enter(&iport->iport_worker_lock);
463 		return;
464 	}
465 
466 	/* FLOGI succeeded. Update local port state */
467 	ASSERT(fx.fx_op == ELS_OP_ACC);
468 	bcopy(fx.fx_nwwn, iport->iport_link_info.port_rnwwn, 8);
469 	bcopy(fx.fx_pwwn, iport->iport_link_info.port_rpwwn, 8);
470 	if (fx.fx_fport) {
471 		iport->iport_link_info.port_topology |=
472 			    PORT_TOPOLOGY_FABRIC_BIT;
473 		iport->iport_link_info.portid = fx.fx_did;
474 	}
475 	iport->iport_link_info.port_fct_flogi_done = 1;
476 }
477 
478 /*
479  * Called by FCAs to handle unsolicited FLOGIs.
480  */
481 fct_status_t
482 fct_handle_rcvd_flogi(fct_local_port_t *port, fct_flogi_xchg_t *fx)
483 {
484 	fct_i_local_port_t *iport;
485 	uint32_t t;
486 
487 	iport = (fct_i_local_port_t *)port->port_fct_private;
488 	if ((iport->iport_flags & IPORT_ALLOW_UNSOL_FLOGI) == 0) {
489 		return (FCT_FAILURE);
490 	}
491 
492 	mutex_enter(&iport->iport_worker_lock);
493 	if (((iport->iport_flags & IPORT_ALLOW_UNSOL_FLOGI) == 0) ||
494 	    (iport->iport_link_state !=	PORT_STATE_LINK_INIT_START) ||
495 	    ((iport->iport_li_state & LI_STATE_MASK) > LI_STATE_N2N_PLOGI)) {
496 		mutex_exit(&iport->iport_worker_lock);
497 		return (FCT_FAILURE);
498 	}
499 
500 	if (iport->iport_link_info.port_fct_flogi_done == 0) {
501 		iport->iport_link_info.port_fct_flogi_done = 1;
502 		bcopy(fx->fx_pwwn, iport->iport_link_info.port_rpwwn, 8);
503 		bcopy(fx->fx_nwwn, iport->iport_link_info.port_rnwwn, 8);
504 	}
505 
506 	fx->fx_op = ELS_OP_ACC;
507 	t = fx->fx_sid;
508 	fx->fx_sid = fx->fx_did;
509 	fx->fx_did = t;
510 	bcopy(iport->iport_port->port_pwwn, fx->fx_pwwn, 8);
511 	bcopy(iport->iport_port->port_nwwn, fx->fx_nwwn, 8);
512 	mutex_exit(&iport->iport_worker_lock);
513 
514 	return (FCT_SUCCESS);
515 }
516 
517 /*
518  * iport_li_state can only be changed here and local_event
519  */
520 disc_action_t
521 fct_process_link_init(fct_i_local_port_t *iport)
522 {
523 	fct_cmd_t	*cmd	  = NULL;
524 	char		*pname	  = NULL;
525 	uint8_t		 elsop	  = 0;
526 	uint16_t	 ctop	  = 0;
527 	uint32_t	 wkdid	  = 0;
528 	int		 implicit = 0;
529 	int		force_login = 0;
530 	disc_action_t	 ret	  = DISC_ACTION_RESCAN;
531 	fct_link_info_t *li = &iport->iport_link_info;
532 	char		topo[24], speed[4];
533 
534 	ASSERT(MUTEX_HELD(&iport->iport_worker_lock));
535 
536 check_state_again:
537 	switch (iport->iport_li_state & LI_STATE_MASK) {
538 	case LI_STATE_DO_FLOGI:
539 		/* Is FLOGI even needed or already done ? */
540 		if ((iport->iport_link_info.port_no_fct_flogi) ||
541 		    (IPORT_FLOGI_DONE(iport))) {
542 			iport->iport_li_state++;
543 			goto check_state_again;
544 		}
545 		fct_do_flogi(iport);
546 		break;
547 
548 	case LI_STATE_FINI_TOPOLOGY:
549 		fct_li_to_txt(li, topo, speed);
550 		cmn_err(CE_NOTE, "%s LINK UP, portid %x, topology %s,"
551 		    "speed %s", iport->iport_alias, li->portid,
552 		    topo, speed);
553 		if (li->port_topology !=
554 		    iport->iport_link_old_topology) {
555 			if (iport->iport_nrps) {
556 				/*
557 				 * rehash it if change from fabric to
558 				 * none fabric, vice versa
559 				 */
560 				if ((li->port_topology ^
561 				    iport->iport_link_old_topology) &
562 				    PORT_TOPOLOGY_FABRIC_BIT) {
563 					mutex_exit(&iport->iport_worker_lock);
564 					fct_rehash(iport);
565 					mutex_enter(&iport->iport_worker_lock);
566 				}
567 			}
568 			iport->iport_link_old_topology = li->port_topology;
569 		}
570 		/* Skip next level if topo is not N2N */
571 		if (li->port_topology != PORT_TOPOLOGY_PT_TO_PT) {
572 			iport->iport_li_state += 2;
573 			atomic_and_32(&iport->iport_flags,
574 			    ~IPORT_ALLOW_UNSOL_FLOGI);
575 		} else {
576 			iport->iport_li_state++;
577 			iport->iport_login_retry = 0;
578 			iport->iport_li_cmd_timeout = ddi_get_lbolt() +
579 				drv_usectohz(25 * 1000000);
580 		}
581 		goto check_state_again;
582 
583 	case LI_STATE_N2N_PLOGI:
584 		ASSERT(IPORT_FLOGI_DONE(iport));
585 		ASSERT(iport->iport_link_info.port_topology ==
586 				PORT_TOPOLOGY_PT_TO_PT);
587 		if (iport->iport_li_state & LI_STATE_FLAG_CMD_RETCHECK) {
588 			iport->iport_li_state &= ~LI_STATE_FLAG_CMD_RETCHECK;
589 			if (iport->iport_li_comp_status != FCT_SUCCESS) {
590 				iport->iport_login_retry++;
591 				if (iport->iport_login_retry >= 3) {
592 					stmf_trace(iport->iport_alias, "Failing"
593 					    " to PLOGI to remote port in N2N "
594 					    " ret=%llx, forcing link down",
595 					    iport->iport_li_comp_status);
596 					mutex_exit(&iport->iport_worker_lock);
597 					fct_handle_event(iport->iport_port,
598 						FCT_EVENT_LINK_DOWN, 0, 0);
599 					mutex_enter(&iport->iport_worker_lock);
600 				}
601 			}
602 		}
603 		/* Find out if we need to do PLOGI at all */
604 		if (iport->iport_nrps_login) {
605 			iport->iport_li_state++;
606 			atomic_and_32(&iport->iport_flags,
607 			    ~IPORT_ALLOW_UNSOL_FLOGI);
608 			goto check_state_again;
609 		}
610 		if ((ddi_get_lbolt() >= iport->iport_li_cmd_timeout) &&
611 		    (!fct_lport_has_bigger_wwn(iport))) {
612 			/* Cant wait forever */
613 			stmf_trace(iport->iport_alias, "N2N: Remote port is "
614 			    "not logging in, forcing from our side");
615 			force_login = 1;
616 		} else {
617 			force_login = 0;
618 		}
619 		if (force_login || fct_lport_has_bigger_wwn(iport)) {
620 			elsop	 = ELS_OP_PLOGI;
621 			wkdid	 = 1;
622 			iport->iport_link_info.portid = 0xEF;
623 			implicit = 0;
624 			iport->iport_li_state |= LI_STATE_FLAG_CMD_RETCHECK;
625 		} else {
626 			ret = DISC_ACTION_DELAY_RESCAN;
627 		}
628 		break;
629 
630 	case LI_STATE_DO_FCLOGIN:
631 		if (iport->iport_li_state & LI_STATE_FLAG_CMD_RETCHECK) {
632 			iport->iport_li_state &= ~LI_STATE_FLAG_CMD_RETCHECK;
633 			if (iport->iport_li_comp_status != FCT_SUCCESS) {
634 				/*
635 				 * Fabric controller login failed. Just skip all
636 				 * the fabric controller related cmds.
637 				 */
638 				iport->iport_li_state = LI_STATE_DO_SCR + 1;
639 			} else {
640 				/*
641 				 * Good. Now lets go to next state
642 				 */
643 				iport->iport_li_state++;
644 			}
645 			goto check_state_again;
646 		}
647 		if (!IPORT_IN_NS_TOPO(iport)) {
648 			iport->iport_li_state = LI_STATE_DO_SCR + 1;
649 			goto check_state_again;
650 		}
651 
652 		elsop	 = ELS_OP_PLOGI;
653 		wkdid	 = FS_FABRIC_CONTROLLER;
654 		implicit = 1;
655 
656 		/*
657 		 * We want to come back in the same state and check its ret
658 		 * We can't modify the state here
659 		 */
660 		iport->iport_li_state |= LI_STATE_FLAG_CMD_RETCHECK;
661 		break;
662 
663 	case LI_STATE_DO_SCR:
664 		elsop = ELS_OP_SCR;
665 		wkdid = FS_FABRIC_CONTROLLER;
666 
667 		/*
668 		 * We dont care about success of this state. Just go to
669 		 * next state upon completion.
670 		 */
671 		iport->iport_li_state++;
672 		break;
673 
674 	case LI_STATE_DO_NSLOGIN:
675 		if (iport->iport_li_state & LI_STATE_FLAG_CMD_RETCHECK) {
676 			iport->iport_li_state &= ~LI_STATE_FLAG_CMD_RETCHECK;
677 			if (iport->iport_li_comp_status != FCT_SUCCESS) {
678 				iport->iport_li_state = LI_STATE_DO_RSNN + 1;
679 			} else {
680 				iport->iport_li_state++;
681 			}
682 			goto check_state_again;
683 		}
684 
685 		if (!IPORT_IN_NS_TOPO(iport)) {
686 			iport->iport_li_state = LI_STATE_DO_RSNN + 1;
687 			goto check_state_again;
688 		}
689 
690 		elsop			= ELS_OP_PLOGI;
691 		wkdid			= FS_NAME_SERVER;
692 		iport->iport_li_state	|= LI_STATE_FLAG_CMD_RETCHECK;
693 		break;
694 
695 		/*
696 		 * CT state
697 		 */
698 	case LI_STATE_DO_RNN:
699 		ctop = NS_RNN_ID;
700 		iport->iport_li_state++;
701 		break;
702 
703 	case LI_STATE_DO_RCS:
704 		ctop = NS_RCS_ID;
705 		iport->iport_li_state++;
706 		break;
707 
708 	case LI_STATE_DO_RFT:
709 		ctop = NS_RFT_ID;
710 		iport->iport_li_state++;
711 		break;
712 
713 	case LI_STATE_DO_RSPN:
714 		/*
715 		 * Check if we need skip the state
716 		 */
717 		pname = iport->iport_port->port_sym_port_name !=
718 		    NULL ? iport->iport_port->port_sym_port_name : NULL;
719 		if (pname == NULL) {
720 			pname = iport->iport_port->port_default_alias !=
721 			    NULL ? iport->iport_port->port_default_alias : NULL;
722 			iport->iport_port->port_sym_port_name = pname;
723 		}
724 
725 		if (pname == NULL) {
726 			iport->iport_li_state++;
727 			goto check_state_again;
728 		}
729 
730 		ctop = NS_RSPN_ID;
731 		iport->iport_li_state++;
732 		break;
733 
734 	case LI_STATE_DO_RSNN:
735 		ctop = NS_RSNN_NN;
736 		iport->iport_li_state++;
737 		break;
738 
739 	case LI_STATE_MAX:
740 		mutex_exit(&iport->iport_worker_lock);
741 
742 		fct_handle_event(iport->iport_port,
743 		    FCT_I_EVENT_LINK_INIT_DONE, 0, 0);
744 
745 		mutex_enter(&iport->iport_worker_lock);
746 		break;
747 
748 	default:
749 		ASSERT(0);
750 	}
751 
752 	if (elsop != 0) {
753 		cmd = fct_create_solels(iport->iport_port, NULL, implicit,
754 		    elsop, wkdid, fct_link_init_cb);
755 	} else if (ctop != 0) {
756 		cmd = fct_create_solct(iport->iport_port, NULL, ctop,
757 		    fct_link_init_cb);
758 	}
759 
760 	if (cmd) {
761 		iport->iport_li_state |= LI_STATE_FLAG_CMD_WAITING;
762 		mutex_exit(&iport->iport_worker_lock);
763 
764 		fct_post_to_solcmd_queue(iport->iport_port, cmd);
765 
766 		mutex_enter(&iport->iport_worker_lock);
767 	}
768 
769 	return (ret);
770 }
771 
772 /*
773  * Handles both solicited and unsolicited elses. Can be called inside
774  * interrupt context.
775  */
776 void
777 fct_handle_els(fct_cmd_t *cmd)
778 {
779 	fct_local_port_t	*port = cmd->cmd_port;
780 	fct_i_local_port_t *iport =
781 			(fct_i_local_port_t *)port->port_fct_private;
782 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
783 	fct_els_t		*els  = (fct_els_t *)cmd->cmd_specific;
784 	fct_remote_port_t	*rp;
785 	fct_i_remote_port_t	*irp;
786 	uint16_t		 cmd_slot;
787 	uint8_t			 op;
788 
789 	op = els->els_req_payload[0];
790 	icmd->icmd_start_time = ddi_get_lbolt();
791 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
792 		icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
793 	}
794 	stmf_trace(iport->iport_alias, "Posting %ssol ELS %x (%s) rp_id=%x"
795 	    " lp_id=%x", (cmd->cmd_type == FCT_CMD_RCVD_ELS) ? "un" : "",
796 			op, FCT_ELS_NAME(op), cmd->cmd_rportid,
797 			cmd->cmd_lportid);
798 
799 	rw_enter(&iport->iport_lock, RW_READER);
800 start_els_posting:;
801 	/* Make sure local port is sane */
802 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
803 		rw_exit(&iport->iport_lock);
804 		stmf_trace(iport->iport_alias, "ELS %x not posted becasue"
805 		    "port state was %x", els->els_req_payload[0],
806 		    iport->iport_link_state);
807 		fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
808 		return;
809 	}
810 
811 	/* Weed out any bad initiators in case of N2N topology */
812 	if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
813 	    (els->els_req_payload[0] == ELS_OP_PLOGI) &&
814 	    (iport->iport_link_state == PORT_STATE_LINK_INIT_START) &&
815 	    (iport->iport_link_info.port_topology == PORT_TOPOLOGY_PT_TO_PT)) {
816 		int state;
817 		int killit = 0;
818 
819 		mutex_enter(&iport->iport_worker_lock);
820 		state = iport->iport_li_state & LI_STATE_MASK;
821 		/*
822 		 * We dont allow remote port to plogi in N2N if we have not yet
823 		 * resolved the topology.
824 		 */
825 		if (state <= LI_STATE_FINI_TOPOLOGY) {
826 			killit = 1;
827 			stmf_trace(iport->iport_alias, "port %x is trying to "
828 			    "PLOGI in N2N topology, While we have not resolved"
829 			    " the topology. Dropping...", cmd->cmd_rportid);
830 		} else if (state <= LI_STATE_N2N_PLOGI) {
831 			if (fct_lport_has_bigger_wwn(iport)) {
832 				killit = 1;
833 				stmf_trace(iport->iport_alias, "port %x is "
834 				    "trying to PLOGI in N2N topology, even "
835 				    "though it has smaller PWWN",
836 				    cmd->cmd_rportid);
837 			} else {
838 				/*
839 				 * Remote port is assigning us a PORTID as
840 				 * a part of PLOGI.
841 				 */
842 				iport->iport_link_info.portid =
843 							cmd->cmd_lportid;
844 			}
845 		}
846 		mutex_exit(&iport->iport_worker_lock);
847 		if (killit) {
848 			rw_exit(&iport->iport_lock);
849 			fct_queue_cmd_for_termination(cmd,
850 						FCT_LOCAL_PORT_OFFLINE);
851 			return;
852 		}
853 	}
854 
855 	/*
856 	 * For all unsolicited ELSes that are not FLOGIs, our portid
857 	 * has been established by now. Sometimes port IDs change due to
858 	 * link resets but remote ports may still send ELSes using the
859 	 * old IDs. Kill those right here.
860 	 */
861 	if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
862 	    (els->els_req_payload[0] != ELS_OP_FLOGI)) {
863 		if (cmd->cmd_lportid != iport->iport_link_info.portid) {
864 			rw_exit(&iport->iport_lock);
865 			stmf_trace(iport->iport_alias, "Rcvd %s with "
866 			    "wrong lportid %x, expecting %x. Killing ELS.",
867 			    FCT_ELS_NAME(op), cmd->cmd_lportid,
868 			    iport->iport_link_info.portid);
869 			fct_queue_cmd_for_termination(cmd,
870 					FCT_NOT_FOUND);
871 			return;
872 		}
873 	}
874 
875 	/*
876 	 * We always lookup by portid. port handles are too
877 	 * unreliable at this stage.
878 	 */
879 	irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
880 	if (els->els_req_payload[0] == ELS_OP_PLOGI) {
881 		if (irp == NULL) {
882 			/* drop the lock while we do allocations */
883 			rw_exit(&iport->iport_lock);
884 			rp = fct_alloc(FCT_STRUCT_REMOTE_PORT,
885 				port->port_fca_rp_private_size, 0);
886 			if (rp == NULL) {
887 				fct_queue_cmd_for_termination(cmd,
888 					FCT_ALLOC_FAILURE);
889 				return;
890 			}
891 			irp = (fct_i_remote_port_t *)rp->rp_fct_private;
892 			rw_init(&irp->irp_lock, 0, RW_DRIVER, 0);
893 			irp->irp_rp = rp;
894 			irp->irp_portid = cmd->cmd_rportid;
895 			rp->rp_port = port;
896 			rp->rp_id = cmd->cmd_rportid;
897 			rp->rp_handle = FCT_HANDLE_NONE;
898 			/*
899 			 * Grab port lock as writer since we are going
900 			 * to modify the local port struct.
901 			 */
902 			rw_enter(&iport->iport_lock, RW_WRITER);
903 			/* Make sure nobody created the struct except us */
904 			if (fct_portid_to_portptr(iport, cmd->cmd_rportid)) {
905 				/* Oh well, free it */
906 				fct_free(rp);
907 			} else {
908 				fct_queue_rp(iport, irp);
909 			}
910 			rw_downgrade(&iport->iport_lock);
911 			/* Start over becasue we dropped the lock */
912 			goto start_els_posting;
913 		}
914 
915 		/* A PLOGI is by default a logout of previous session */
916 		irp->irp_deregister_timer = ddi_get_lbolt() +
917 			drv_usectohz(USEC_DEREG_RP_TIMEOUT);
918 		irp->irp_dereg_count = 0;
919 		fct_post_to_discovery_queue(iport, irp, NULL);
920 
921 		/* A PLOGI also invalidates any RSCNs related to this rp */
922 		atomic_add_32(&irp->irp_rscn_counter, 1);
923 	} else {
924 		/*
925 		 * For everything else, we have (or be able to lookup) a
926 		 * valid port pointer.
927 		 */
928 		if (irp == NULL) {
929 			rw_exit(&iport->iport_lock);
930 			if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
931 				/* XXX Throw a logout to the initiator */
932 				stmf_trace(iport->iport_alias, "ELS %x "
933 				    "received from %x without a session",
934 				    els->els_req_payload[0], cmd->cmd_rportid);
935 			} else {
936 				stmf_trace(iport->iport_alias, "Sending ELS %x "
937 				    "to %x without a session",
938 				    els->els_req_payload[0], cmd->cmd_rportid);
939 			}
940 			fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
941 			return;
942 		}
943 	}
944 	cmd->cmd_rp = rp = irp->irp_rp;
945 
946 	/*
947 	 * Lets get a slot for this els
948 	 */
949 	if (!(icmd->icmd_flags & ICMD_IMPLICIT)) {
950 		cmd_slot = fct_alloc_cmd_slot(iport, cmd);
951 		if (cmd_slot == FCT_SLOT_EOL) {
952 			/* This should not have happened */
953 			rw_exit(&iport->iport_lock);
954 			stmf_trace(iport->iport_alias,
955 					"ran out of xchg resources");
956 			fct_queue_cmd_for_termination(cmd,
957 					FCT_NO_XCHG_RESOURCE);
958 			return;
959 		}
960 	} else {
961 		/*
962 		 * Tell the framework that fct_cmd_free() can decrement the
963 		 * irp_nonfcp_xchg_count variable.
964 		 */
965 		atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
966 	}
967 	atomic_add_16(&irp->irp_nonfcp_xchg_count, 1);
968 
969 	/*
970 	 * Grab the remote port lock while we modify the port state.
971 	 * we should not drop the fca port lock (as a reader) until we
972 	 * modify the remote port state.
973 	 */
974 	rw_enter(&irp->irp_lock, RW_WRITER);
975 	if ((op == ELS_OP_PLOGI) || (op == ELS_OP_PRLI) ||
976 	    (op == ELS_OP_LOGO) || (op == ELS_OP_PRLO) ||
977 	    (op == ELS_OP_TPRLO)) {
978 		uint32_t rf = IRP_PRLI_DONE;
979 		if ((op == ELS_OP_PLOGI) || (op == ELS_OP_LOGO)) {
980 			rf |= IRP_PLOGI_DONE;
981 			if (irp->irp_flags & IRP_PLOGI_DONE)
982 				atomic_add_32(&iport->iport_nrps_login, -1);
983 		}
984 		atomic_add_16(&irp->irp_sa_elses_count, 1);
985 		atomic_and_32(&irp->irp_flags, ~rf);
986 		atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
987 	} else {
988 		atomic_add_16(&irp->irp_nsa_elses_count, 1);
989 	}
990 
991 	fct_post_to_discovery_queue(iport, irp, icmd);
992 
993 	rw_exit(&irp->irp_lock);
994 	rw_exit(&iport->iport_lock);
995 }
996 
997 /*
998  * Cleanup I/Os for a rport. ttc is a bit Mask of cmd types to clean.
999  * No locks held.
1000  */
1001 int
1002 fct_trigger_rport_cleanup(fct_i_remote_port_t *irp, int ttc)
1003 {
1004 	fct_remote_port_t	*rp = irp->irp_rp;
1005 	fct_local_port_t	*port = rp->rp_port;
1006 	fct_i_local_port_t	*iport =
1007 				(fct_i_local_port_t *)port->port_fct_private;
1008 	fct_cmd_t		*cmd;
1009 	fct_i_cmd_t		*icmd;
1010 	int			i;
1011 	int			ret;
1012 	uint16_t		total, cleaned, skipped, unhandled;
1013 
1014 	rw_enter(&iport->iport_lock, RW_WRITER);
1015 	rw_enter(&irp->irp_lock, RW_WRITER);
1016 	mutex_enter(&iport->iport_worker_lock);
1017 	total = port->port_max_xchges - iport->iport_nslots_free;
1018 	cleaned = skipped = unhandled = 0;
1019 
1020 	for (i = 0; i < port->port_max_xchges; i++) {
1021 		if (iport->iport_cmd_slots[i].slot_cmd == NULL)
1022 			continue;
1023 		icmd = iport->iport_cmd_slots[i].slot_cmd;
1024 		if (icmd->icmd_flags & ICMD_IN_TRANSITION) {
1025 			unhandled++;
1026 			continue;
1027 		}
1028 
1029 		if (icmd->icmd_flags & ICMD_CMD_COMPLETE) {
1030 			unhandled++;
1031 			continue;
1032 		}
1033 
1034 		cmd = icmd->icmd_cmd;
1035 		if (cmd->cmd_rp != rp) {
1036 			skipped++;
1037 			continue;
1038 		}
1039 		if (cmd->cmd_type & ttc) {
1040 			if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
1041 				fct_queue_scsi_task_for_termination(cmd,
1042 						FCT_ABORTED);
1043 			else
1044 				fct_q_for_termination_lock_held(iport, icmd,
1045 						FCT_ABORTED);
1046 			cleaned++;
1047 		} else {
1048 			skipped++;
1049 		}
1050 	}
1051 	if (((cleaned + skipped) == total) && (unhandled == 0)) {
1052 		ret = 1;
1053 	} else {
1054 		/*
1055 		 * XXX: handle this situation.
1056 		 */
1057 		stmf_trace(iport->iport_alias, "Clean up trouble for irp"
1058 		    " %p, c/s/u/t = %d/%d/%d/%d", irp, cleaned, skipped,
1059 			unhandled, total);
1060 		ret = 0;
1061 	}
1062 	if ((cleaned) && IS_WORKER_SLEEPING(iport))
1063 		cv_signal(&iport->iport_worker_cv);
1064 	mutex_exit(&iport->iport_worker_lock);
1065 	rw_exit(&irp->irp_lock);
1066 	rw_exit(&iport->iport_lock);
1067 	return (ret);
1068 }
1069 
1070 void
1071 fct_dequeue_els(fct_i_remote_port_t *irp)
1072 {
1073 	fct_i_cmd_t *icmd;
1074 
1075 	rw_enter(&irp->irp_lock, RW_WRITER);
1076 	icmd = irp->irp_els_list;
1077 	irp->irp_els_list = icmd->icmd_next;
1078 	atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_IRP_QUEUE);
1079 	rw_exit(&irp->irp_lock);
1080 }
1081 
1082 fct_status_t
1083 fct_register_remote_port(fct_local_port_t *port, fct_remote_port_t *rp,
1084 				fct_cmd_t *cmd)
1085 {
1086 	fct_status_t ret;
1087 	fct_i_local_port_t	*iport;
1088 	fct_i_remote_port_t	*irp;
1089 	int			i;
1090 	char			info[160];
1091 
1092 	iport = (fct_i_local_port_t *)port->port_fct_private;
1093 	irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1094 
1095 	if ((ret = port->port_register_remote_port(port, rp, cmd)) !=
1096 			FCT_SUCCESS)
1097 		return (ret);
1098 
1099 	rw_enter(&iport->iport_lock, RW_WRITER);
1100 	rw_enter(&irp->irp_lock, RW_WRITER);
1101 	if (rp->rp_handle != FCT_HANDLE_NONE) {
1102 		if (rp->rp_handle >= port->port_max_logins) {
1103 			(void) snprintf(info, 160,
1104 			    "fct_register_remote_port: FCA "
1105 			    "returned a	handle (%d) for portid %x which is "
1106 			    "out of range (max logins = %d)", rp->rp_handle,
1107 			    rp->rp_id, port->port_max_logins);
1108 			info[159] = 0;
1109 			goto hba_fatal_err;
1110 		}
1111 		if ((iport->iport_rp_slots[rp->rp_handle] != NULL) &&
1112 		    (iport->iport_rp_slots[rp->rp_handle] != irp)) {
1113 			(void) snprintf(info, 160, "fct_register_remote_port: "
1114 			    "FCA returned a handle %d for portid %x "
1115 			    "which was already in use for a different "
1116 			    "portid (%x)", rp->rp_handle, rp->rp_id,
1117 		    (iport->iport_rp_slots[rp->rp_handle])->irp_rp->rp_id);
1118 			info[159] = 0;
1119 			goto hba_fatal_err;
1120 		}
1121 	} else {
1122 		/* Pick a handle for this port */
1123 		for (i = 0; i < port->port_max_logins; i++) {
1124 			if (iport->iport_rp_slots[i] == NULL) {
1125 				break;
1126 			}
1127 		}
1128 		if (i == port->port_max_logins) {
1129 			/* This is really pushing it. */
1130 			(void) snprintf(info, 160, "fct_register_remote_port "
1131 			    "Cannot register portid %x because all the "
1132 			    "handles are used up", rp->rp_id);
1133 			info[159] = 0;
1134 			goto hba_fatal_err;
1135 		}
1136 		rp->rp_handle = i;
1137 	}
1138 	/* By this time rport_handle is valid */
1139 	if ((irp->irp_flags & IRP_HANDLE_OPENED) == 0) {
1140 		iport->iport_rp_slots[rp->rp_handle] = irp;
1141 		atomic_or_32(&irp->irp_flags, IRP_HANDLE_OPENED);
1142 	}
1143 	(void) atomic_add_64_nv(&iport->iport_last_change, 1);
1144 	fct_log_remote_port_event(port, ESC_SUNFC_TARGET_ADD,
1145 	    rp->rp_pwwn, rp->rp_id);
1146 
1147 register_rp_done:;
1148 	rw_exit(&irp->irp_lock);
1149 	rw_exit(&iport->iport_lock);
1150 	return (FCT_SUCCESS);
1151 
1152 hba_fatal_err:;
1153 	rw_exit(&irp->irp_lock);
1154 	rw_exit(&iport->iport_lock);
1155 	/*
1156 	 * XXX Throw HBA fatal error event
1157 	 */
1158 	(void) fct_port_shutdown(iport->iport_port,
1159 	    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1160 	return (FCT_FAILURE);
1161 }
1162 
1163 fct_status_t
1164 fct_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp)
1165 {
1166 	fct_status_t		 ret   = FCT_SUCCESS;
1167 	fct_i_local_port_t	*iport = PORT_TO_IPORT(port);
1168 	fct_i_remote_port_t	*irp   = RP_TO_IRP(rp);
1169 
1170 	if (irp->irp_snn) {
1171 		kmem_free(irp->irp_snn, strlen(irp->irp_snn) + 1);
1172 		irp->irp_snn = NULL;
1173 	}
1174 	if (irp->irp_spn) {
1175 		kmem_free(irp->irp_spn, strlen(irp->irp_spn) + 1);
1176 		irp->irp_spn = NULL;
1177 	}
1178 
1179 	if ((ret = port->port_deregister_remote_port(port, rp)) !=
1180 	    FCT_SUCCESS) {
1181 		return (ret);
1182 	}
1183 
1184 	if (irp->irp_flags & IRP_HANDLE_OPENED) {
1185 		atomic_and_32(&irp->irp_flags, ~IRP_HANDLE_OPENED);
1186 		iport->iport_rp_slots[rp->rp_handle] = NULL;
1187 	}
1188 	(void) atomic_add_64_nv(&iport->iport_last_change, 1);
1189 	fct_log_remote_port_event(port, ESC_SUNFC_TARGET_REMOVE,
1190 	    rp->rp_pwwn, rp->rp_id);
1191 
1192 	return (FCT_SUCCESS);
1193 }
1194 
1195 fct_status_t
1196 fct_send_accrjt(fct_cmd_t *cmd, uint8_t accrjt, uint8_t reason, uint8_t expl)
1197 {
1198 	fct_local_port_t *port = (fct_local_port_t *)cmd->cmd_port;
1199 	fct_els_t *els = (fct_els_t *)cmd->cmd_specific;
1200 
1201 	els->els_resp_size = els->els_resp_alloc_size = 8;
1202 	els->els_resp_payload = (uint8_t *)kmem_zalloc(8, KM_SLEEP);
1203 	els->els_resp_payload[0] = accrjt;
1204 	if (accrjt == 1) {
1205 		els->els_resp_payload[5] = reason;
1206 		els->els_resp_payload[6] = expl;
1207 	} else {
1208 		els->els_resp_size = 4;
1209 	}
1210 
1211 	return (port->port_send_cmd_response(cmd, 0));
1212 }
1213 
1214 
1215 disc_action_t
1216 fct_walk_discovery_queue(fct_i_local_port_t *iport)
1217 {
1218 	char			info[80];
1219 	fct_i_remote_port_t	**pirp;
1220 	fct_i_remote_port_t	*prev_irp = NULL;
1221 	disc_action_t		suggested_action = DISC_ACTION_NO_WORK;
1222 	fct_i_remote_port_t	*irp_dereg_list = NULL;
1223 	fct_i_remote_port_t	*irp_cur_item = NULL;
1224 
1225 	for (pirp = &iport->iport_rpwe_head; *pirp != NULL; ) {
1226 		fct_i_remote_port_t *irp = *pirp;
1227 		disc_action_t ret = DISC_ACTION_NO_WORK;
1228 		int do_deregister = 0;
1229 
1230 		if (irp->irp_els_list) {
1231 			ret |= fct_process_els(iport, irp);
1232 		}
1233 		if (irp->irp_deregister_timer) {
1234 			if (ddi_get_lbolt() >= irp->irp_deregister_timer) {
1235 				do_deregister = 1;
1236 			} else {
1237 				ret |= DISC_ACTION_DELAY_RESCAN;
1238 			}
1239 		}
1240 		suggested_action |= ret;
1241 
1242 		if (irp->irp_els_list == NULL) {
1243 			mutex_exit(&iport->iport_worker_lock);
1244 			rw_enter(&iport->iport_lock, RW_WRITER);
1245 			rw_enter(&irp->irp_lock, RW_WRITER);
1246 			mutex_enter(&iport->iport_worker_lock);
1247 			if (irp->irp_els_list == NULL) {
1248 				if (!irp->irp_deregister_timer ||
1249 				    (do_deregister &&
1250 				    !irp->irp_sa_elses_count &&
1251 				    !irp->irp_nsa_elses_count &&
1252 				    !irp->irp_fcp_xchg_count &&
1253 				    !irp->irp_nonfcp_xchg_count)) {
1254 					/* dequeue irp from discovery queue */
1255 					atomic_and_32(&irp->irp_flags,
1256 						~IRP_IN_DISCOVERY_QUEUE);
1257 					*pirp = irp->irp_discovery_next;
1258 					if (iport->iport_rpwe_head == NULL)
1259 						iport->iport_rpwe_tail = NULL;
1260 					else if (irp == iport->iport_rpwe_tail)
1261 						iport->iport_rpwe_tail =
1262 							prev_irp;
1263 
1264 					irp->irp_discovery_next = NULL;
1265 					if (do_deregister) {
1266 						fct_deque_rp(iport, irp);
1267 						rw_exit(&irp->irp_lock);
1268 						/* queue irp for deregister */
1269 						irp->irp_next = NULL;
1270 						if (!irp_dereg_list) {
1271 							irp_dereg_list =
1272 							    irp_cur_item = irp;
1273 						} else {
1274 							irp_cur_item->irp_next =
1275 								irp;
1276 							irp_cur_item = irp;
1277 						}
1278 					} else {
1279 						rw_exit(&irp->irp_lock);
1280 					}
1281 					rw_exit(&iport->iport_lock);
1282 					if ((irp = *pirp) == NULL)
1283 						break;
1284 				} else {
1285 					/*
1286 					 * wait for another scan until
1287 					 * deregister timeout
1288 					 */
1289 					rw_exit(&irp->irp_lock);
1290 					rw_exit(&iport->iport_lock);
1291 				}
1292 			} else {
1293 				rw_exit(&irp->irp_lock);
1294 				rw_exit(&iport->iport_lock);
1295 				/*
1296 				 * When we dropped the lock,
1297 				 * something went in.
1298 				 */
1299 				suggested_action |= DISC_ACTION_RESCAN;
1300 			}
1301 		}
1302 		pirp = &(irp->irp_discovery_next);
1303 		prev_irp = irp;
1304 	}
1305 	/* do deregister */
1306 	if (irp_dereg_list) {
1307 		fct_i_remote_port_t *irp_next_item;
1308 		/* drop the lock */
1309 		mutex_exit(&iport->iport_worker_lock);
1310 
1311 		for (irp_cur_item = irp_dereg_list; irp_cur_item != NULL; ) {
1312 			irp_next_item = irp_cur_item->irp_next;
1313 			if (fct_deregister_remote_port(iport->iport_port,
1314 			    irp_cur_item->irp_rp) == FCT_SUCCESS) {
1315 				fct_free(irp_cur_item->irp_rp);
1316 			} else if (++irp_cur_item->irp_dereg_count >= 5) {
1317 				irp_cur_item->irp_deregister_timer = 0;
1318 				irp_cur_item->irp_dereg_count = 0;
1319 
1320 				/*
1321 				 * It looks like we can't deregister it in the
1322 				 * normal way, so we have to use extrem way
1323 				 */
1324 				(void) snprintf(info, 80,
1325 				    "fct_walk_discovery_queue: "
1326 				    "iport-%p, can't deregister irp-%p after "
1327 				    "trying 5 times", (void *)iport,
1328 				    (void *)irp_cur_item);
1329 				info[79] = 0;
1330 				(void) fct_port_shutdown(iport->iport_port,
1331 				    STMF_RFLAG_FATAL_ERROR |
1332 				    STMF_RFLAG_RESET, info);
1333 				suggested_action |= DISC_ACTION_RESCAN;
1334 				break;
1335 			} else {
1336 				/* grab the iport_lock */
1337 				rw_enter(&iport->iport_lock, RW_WRITER);
1338 				/* recover */
1339 				irp_cur_item->irp_deregister_timer =
1340 				    ddi_get_lbolt() +
1341 				    drv_usectohz(USEC_DEREG_RP_INTERVAL);
1342 				fct_post_to_discovery_queue(iport,
1343 				    irp_cur_item, NULL);
1344 				fct_queue_rp(iport, irp_cur_item);
1345 				rw_exit(&iport->iport_lock);
1346 				suggested_action |= DISC_ACTION_DELAY_RESCAN;
1347 			}
1348 			irp_cur_item = irp_next_item;
1349 		}
1350 		mutex_enter(&iport->iport_worker_lock);
1351 	}
1352 	return (suggested_action);
1353 }
1354 
1355 disc_action_t
1356 fct_process_plogi(fct_i_cmd_t *icmd)
1357 {
1358 	fct_cmd_t		*cmd = icmd->icmd_cmd;
1359 	fct_remote_port_t	*rp = cmd->cmd_rp;
1360 	fct_local_port_t	*port = cmd->cmd_port;
1361 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1362 					port->port_fct_private;
1363 	fct_els_t		*els = (fct_els_t *)
1364 					cmd->cmd_specific;
1365 	fct_i_remote_port_t	*irp = (fct_i_remote_port_t *)
1366 					rp->rp_fct_private;
1367 	uint8_t			*p;
1368 	fct_status_t		 ret;
1369 	uint8_t			 cmd_type   = cmd->cmd_type;
1370 	uint32_t		 icmd_flags = icmd->icmd_flags;
1371 	clock_t			 end_time;
1372 	char			 info[160];
1373 
1374 	/* Drain I/Os */
1375 	if ((irp->irp_nonfcp_xchg_count + irp->irp_fcp_xchg_count) > 1) {
1376 		/* Trigger cleanup if necessary */
1377 		if ((irp->irp_flags & IRP_SESSION_CLEANUP) == 0) {
1378 			stmf_trace(iport->iport_alias, "handling PLOGI rp_id"
1379 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1380 			/* Cleanup everything except elses */
1381 			if (fct_trigger_rport_cleanup(irp, ~(cmd->cmd_type))) {
1382 				atomic_or_32(&irp->irp_flags,
1383 							IRP_SESSION_CLEANUP);
1384 			} else {
1385 				/* XXX: handle this */
1386 				/* EMPTY */
1387 			}
1388 		}
1389 
1390 		end_time = icmd->icmd_start_time +
1391 		    drv_usectohz(USEC_ELS_TIMEOUT);
1392 		if (ddi_get_lbolt() > end_time) {
1393 			(void) snprintf(info, 160,
1394 			    "fct_process_plogi: unable to "
1395 			    "clean up I/O. iport-%p, icmd-%p", (void *)iport,
1396 			    (void *)icmd);
1397 			info[159] = 0;
1398 			(void) fct_port_shutdown(iport->iport_port,
1399 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1400 
1401 			return (DISC_ACTION_DELAY_RESCAN);
1402 		}
1403 
1404 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1405 			stmf_trace(iport->iport_alias, "handling"
1406 				" PLOGI rp_id %x, waiting for cmds to"
1407 				" drain", cmd->cmd_rportid);
1408 		}
1409 		return (DISC_ACTION_DELAY_RESCAN);
1410 	}
1411 	atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1412 
1413 	/* Session can only be terminated after all the I/Os have drained */
1414 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1415 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1416 		    irp->irp_session);
1417 		stmf_free(irp->irp_session);
1418 		irp->irp_session = NULL;
1419 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1420 	}
1421 
1422 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1423 		els->els_resp_size = els->els_req_size;
1424 		p = els->els_resp_payload = (uint8_t *)kmem_zalloc(
1425 					els->els_resp_size, KM_SLEEP);
1426 		els->els_resp_alloc_size = els->els_resp_size;
1427 		bcopy(els->els_req_payload, p, els->els_resp_size);
1428 		p[0] = ELS_OP_ACC;
1429 		bcopy(p+20, rp->rp_pwwn, 8);
1430 		bcopy(p+28, rp->rp_nwwn, 8);
1431 		bcopy(port->port_pwwn, p+20, 8);
1432 		bcopy(port->port_nwwn, p+28, 8);
1433 		stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
1434 		    rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
1435 	}
1436 
1437 	ret = fct_register_remote_port(port, rp, cmd);
1438 	fct_dequeue_els(irp);
1439 	if ((ret == FCT_SUCCESS) && !(icmd->icmd_flags & ICMD_IMPLICIT)) {
1440 		if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1441 			ret = port->port_send_cmd_response(cmd, 0);
1442 			if ((ret == FCT_SUCCESS) && IPORT_IN_NS_TOPO(iport) &&
1443 			    !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
1444 				fct_cmd_t *ct_cmd = fct_create_solct(port,
1445 				    rp, NS_GSNN_NN, fct_gsnn_cb);
1446 				if (ct_cmd) {
1447 					fct_post_to_solcmd_queue(port, ct_cmd);
1448 				}
1449 				ct_cmd = fct_create_solct(port, rp,
1450 				    NS_GSPN_ID, fct_gspn_cb);
1451 				if (ct_cmd)
1452 					fct_post_to_solcmd_queue(port, ct_cmd);
1453 				ct_cmd = fct_create_solct(port, rp,
1454 				    NS_GCS_ID, fct_gcs_cb);
1455 				if (ct_cmd)
1456 					fct_post_to_solcmd_queue(port, ct_cmd);
1457 				ct_cmd = fct_create_solct(port, rp,
1458 				    NS_GFT_ID, fct_gft_cb);
1459 				if (ct_cmd)
1460 					fct_post_to_solcmd_queue(port, ct_cmd);
1461 			}
1462 		} else {
1463 			/*
1464 			 * The reason we set this flag is to prevent
1465 			 * killing a PRLI while we have not yet processed
1466 			 * a response to PLOGI. Because the initiator
1467 			 * will send a PRLI as soon as it responds to PLOGI.
1468 			 * Check fct_process_els() for more info.
1469 			 */
1470 			atomic_or_32(&irp->irp_flags,
1471 				IRP_SOL_PLOGI_IN_PROGRESS);
1472 			atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1473 			ret = port->port_send_cmd(cmd);
1474 			if (ret != FCT_SUCCESS) {
1475 				atomic_and_32(&icmd->icmd_flags,
1476 				    ~ICMD_KNOWN_TO_FCA);
1477 				atomic_and_32(&irp->irp_flags,
1478 					~IRP_SOL_PLOGI_IN_PROGRESS);
1479 			}
1480 		}
1481 	}
1482 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1483 
1484 	if (ret == FCT_SUCCESS) {
1485 		if (cmd_type == FCT_CMD_RCVD_ELS) {
1486 			atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
1487 			atomic_add_32(&iport->iport_nrps_login, 1);
1488 			if (irp->irp_deregister_timer)
1489 				irp->irp_deregister_timer = 0;
1490 		}
1491 		if (icmd_flags & ICMD_IMPLICIT) {
1492 			p = els->els_resp_payload;
1493 			p[0] = ELS_OP_ACC;
1494 			cmd->cmd_comp_status = FCT_SUCCESS;
1495 			fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE);
1496 		}
1497 	} else {
1498 		fct_queue_cmd_for_termination(cmd, ret);
1499 	}
1500 
1501 	/* Do not touch cmd here as it may have been freed */
1502 
1503 	return (DISC_ACTION_RESCAN);
1504 }
1505 
1506 uint8_t fct_prli_temp[] = { 0x20, 0x10, 0, 0x14, 8, 0, 0x20, 0, 0, 0, 0, 0,
1507 				0, 0, 0, 0 };
1508 
1509 disc_action_t
1510 fct_process_prli(fct_i_cmd_t *icmd)
1511 {
1512 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1513 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1514 	fct_local_port_t	*port  = cmd->cmd_port;
1515 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1516 					port->port_fct_private;
1517 	fct_els_t		*els   = (fct_els_t *)
1518 					cmd->cmd_specific;
1519 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1520 					rp->rp_fct_private;
1521 	stmf_scsi_session_t	*ses   = NULL;
1522 	fct_status_t		 ret;
1523 	clock_t			 end_time;
1524 	char			 info[160];
1525 
1526 	/* We dont support solicited PRLIs yet */
1527 	ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS);
1528 
1529 	if (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS) {
1530 		/*
1531 		 * Dont process the PRLI yet. Let the framework process the
1532 		 * PLOGI completion 1st. This should be very quick because
1533 		 * the reason we got the PRLI is because the initiator
1534 		 * has responded to PLOGI already.
1535 		 */
1536 		/* XXX: Probably need a timeout here */
1537 		return (DISC_ACTION_DELAY_RESCAN);
1538 	}
1539 	/* The caller has made sure that login is done */
1540 
1541 	/* Make sure the process is fcp in this case */
1542 	if ((els->els_req_size != 20) || (bcmp(els->els_req_payload,
1543 						fct_prli_temp, 16))) {
1544 		if (els->els_req_payload[4] != 0x08)
1545 			stmf_trace(iport->iport_alias, "PRLI received from"
1546 			    " %x for unknown FC-4 type %x", cmd->cmd_rportid,
1547 				els->els_req_payload[4]);
1548 		else
1549 			stmf_trace(iport->iport_alias, "Rejecting PRLI from %x "
1550 			    " pld sz %d, prli_flags %x", cmd->cmd_rportid,
1551 			    els->els_req_size, els->els_req_payload[6]);
1552 
1553 		fct_dequeue_els(irp);
1554 		atomic_add_16(&irp->irp_sa_elses_count, -1);
1555 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0x2c);
1556 		goto prli_end;
1557 	}
1558 
1559 	if (irp->irp_fcp_xchg_count) {
1560 		/* Trigger cleanup if necessary */
1561 		if ((irp->irp_flags & IRP_FCP_CLEANUP) == 0) {
1562 			stmf_trace(iport->iport_alias, "handling PRLI from"
1563 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1564 			if (fct_trigger_rport_cleanup(irp, FCT_CMD_FCP_XCHG)) {
1565 				atomic_or_32(&irp->irp_flags, IRP_FCP_CLEANUP);
1566 			} else {
1567 				/* XXX: handle this */
1568 				/* EMPTY */
1569 			}
1570 		}
1571 
1572 		end_time = icmd->icmd_start_time +
1573 		    drv_usectohz(USEC_ELS_TIMEOUT);
1574 		if (ddi_get_lbolt() > end_time) {
1575 			(void) snprintf(info, 160,
1576 			    "fct_process_prli: unable to clean "
1577 			    "up I/O. iport-%p, icmd-%p", (void *)iport,
1578 			    (void *)icmd);
1579 			info[159] = 0;
1580 			(void) fct_port_shutdown(iport->iport_port,
1581 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1582 
1583 			return (DISC_ACTION_DELAY_RESCAN);
1584 		}
1585 
1586 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1587 			stmf_trace(iport->iport_alias, "handling"
1588 				" PRLI from %x, waiting for cmds to"
1589 				" drain", cmd->cmd_rportid);
1590 		}
1591 		return (DISC_ACTION_DELAY_RESCAN);
1592 	}
1593 	atomic_and_32(&irp->irp_flags, ~IRP_FCP_CLEANUP);
1594 
1595 	/* Session can only be terminated after all the I/Os have drained */
1596 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1597 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1598 		    irp->irp_session);
1599 		stmf_free(irp->irp_session);
1600 		irp->irp_session = NULL;
1601 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1602 	}
1603 
1604 	/* All good, lets start a session */
1605 	ses = (stmf_scsi_session_t *)stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0, 0);
1606 	if (ses) {
1607 		ses->ss_port_private = irp;
1608 		ses->ss_rport_id = (scsi_devid_desc_t *)irp->irp_id;
1609 		ses->ss_lport = port->port_lport;
1610 		if (stmf_register_scsi_session(port->port_lport, ses) !=
1611 							STMF_SUCCESS) {
1612 			stmf_free(ses);
1613 			ses = NULL;
1614 		} else {
1615 			irp->irp_session = ses;
1616 			irp->irp_session->ss_rport_alias = irp->irp_snn;
1617 
1618 			/*
1619 			 * The reason IRP_SCSI_SESSION_STARTED is different
1620 			 * from IRP_PRLI_DONE is that we clear IRP_PRLI_DONE
1621 			 * inside interrupt context. We dont want to deregister
1622 			 * the session from an interrupt.
1623 			 */
1624 			atomic_or_32(&irp->irp_flags, IRP_SCSI_SESSION_STARTED);
1625 		}
1626 	}
1627 
1628 	fct_dequeue_els(irp);
1629 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1630 	if (ses == NULL) {
1631 		/* fail PRLI */
1632 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0);
1633 	} else {
1634 		/* accept PRLI */
1635 		els->els_resp_payload = (uint8_t *)kmem_zalloc(20, KM_SLEEP);
1636 		bcopy(fct_prli_temp, els->els_resp_payload, 20);
1637 		els->els_resp_payload[0] = 2;
1638 		els->els_resp_payload[6] = 0x21;
1639 
1640 		/* XXX the two bytes below need to set as per capabilities */
1641 		els->els_resp_payload[18] = 0;
1642 		els->els_resp_payload[19] = 0x12;
1643 
1644 		els->els_resp_size = els->els_resp_alloc_size = 20;
1645 		if ((ret = port->port_send_cmd_response(cmd, 0)) !=
1646 		    FCT_SUCCESS) {
1647 			stmf_deregister_scsi_session(port->port_lport, ses);
1648 			stmf_free(irp->irp_session);
1649 			irp->irp_session = NULL;
1650 			atomic_and_32(&irp->irp_flags,
1651 			    ~IRP_SCSI_SESSION_STARTED);
1652 		} else {
1653 			/* Mark that PRLI is done */
1654 			atomic_or_32(&irp->irp_flags, IRP_PRLI_DONE);
1655 		}
1656 	}
1657 
1658 prli_end:;
1659 	if (ret != FCT_SUCCESS)
1660 		fct_queue_cmd_for_termination(cmd, ret);
1661 
1662 	return (DISC_ACTION_RESCAN);
1663 }
1664 
1665 disc_action_t
1666 fct_process_logo(fct_i_cmd_t *icmd)
1667 {
1668 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1669 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1670 	fct_local_port_t	*port  = cmd->cmd_port;
1671 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1672 					port->port_fct_private;
1673 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1674 					rp->rp_fct_private;
1675 	fct_status_t		 ret;
1676 	char			 info[160];
1677 	clock_t			 end_time;
1678 
1679 	/* Drain I/Os */
1680 	if ((irp->irp_nonfcp_xchg_count + irp->irp_fcp_xchg_count) > 1) {
1681 		/* Trigger cleanup if necessary */
1682 		if ((irp->irp_flags & IRP_SESSION_CLEANUP) == 0) {
1683 			stmf_trace(iport->iport_alias, "handling LOGO rp_id"
1684 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1685 			/* Cleanup everything except elses */
1686 			if (fct_trigger_rport_cleanup(irp, ~(cmd->cmd_type))) {
1687 				atomic_or_32(&irp->irp_flags,
1688 							IRP_SESSION_CLEANUP);
1689 			} else {
1690 				/* XXX: need more handling */
1691 				return (DISC_ACTION_DELAY_RESCAN);
1692 			}
1693 		}
1694 
1695 		end_time = icmd->icmd_start_time +
1696 		    drv_usectohz(USEC_ELS_TIMEOUT);
1697 		if (ddi_get_lbolt() > end_time) {
1698 			(void) snprintf(info, 160,
1699 			    "fct_process_logo: unable to clean "
1700 			    "up I/O. iport-%p, icmd-%p", (void *)iport,
1701 			    (void *)icmd);
1702 			info[159] = 0;
1703 			(void) fct_port_shutdown(iport->iport_port,
1704 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1705 
1706 			return (DISC_ACTION_DELAY_RESCAN);
1707 		}
1708 
1709 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1710 			stmf_trace(iport->iport_alias, "handling"
1711 				" LOGO rp_id %x, waiting for cmds to"
1712 				" drain", cmd->cmd_rportid);
1713 		}
1714 		return (DISC_ACTION_DELAY_RESCAN);
1715 	}
1716 	atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1717 
1718 	/* Session can only be terminated after all the I/Os have drained */
1719 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1720 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1721 		    irp->irp_session);
1722 		stmf_free(irp->irp_session);
1723 		irp->irp_session = NULL;
1724 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1725 	}
1726 
1727 	fct_dequeue_els(irp);
1728 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1729 
1730 	/* don't send response if this is an implicit logout cmd */
1731 	if (!(icmd->icmd_flags & ICMD_IMPLICIT)) {
1732 		if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1733 			ret = fct_send_accrjt(cmd, ELS_OP_ACC, 0, 0);
1734 		} else {
1735 			atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1736 			ret = port->port_send_cmd(cmd);
1737 			if (ret != FCT_SUCCESS) {
1738 				atomic_and_32(&icmd->icmd_flags,
1739 				    ~ICMD_KNOWN_TO_FCA);
1740 			}
1741 		}
1742 
1743 		if (ret != FCT_SUCCESS) {
1744 			fct_queue_cmd_for_termination(cmd, ret);
1745 		}
1746 	} else {
1747 		fct_cmd_free(cmd);
1748 	}
1749 
1750 	irp->irp_deregister_timer = ddi_get_lbolt() +
1751 				drv_usectohz(USEC_DEREG_RP_TIMEOUT);
1752 	irp->irp_dereg_count = 0;
1753 
1754 	/* Do not touch cmd here as it may have been freed */
1755 
1756 	ASSERT(irp->irp_flags & IRP_IN_DISCOVERY_QUEUE);
1757 
1758 	return (DISC_ACTION_RESCAN);
1759 }
1760 
1761 disc_action_t
1762 fct_process_prlo(fct_i_cmd_t *icmd)
1763 {
1764 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1765 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1766 	fct_local_port_t	*port  = cmd->cmd_port;
1767 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1768 					port->port_fct_private;
1769 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1770 					rp->rp_fct_private;
1771 	fct_status_t		 ret;
1772 	clock_t			 end_time;
1773 	char			 info[160];
1774 
1775 	/* We do not support solicited PRLOs yet */
1776 	ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS);
1777 
1778 	/* Drain I/Os */
1779 	if (irp->irp_fcp_xchg_count) {
1780 		/* Trigger cleanup if necessary */
1781 		if ((irp->irp_flags & IRP_FCP_CLEANUP) == 0) {
1782 			stmf_trace(iport->iport_alias, "handling LOGO from"
1783 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1784 			/* Cleanup everything except elses */
1785 			if (fct_trigger_rport_cleanup(irp, FCT_CMD_FCP_XCHG)) {
1786 				atomic_or_32(&irp->irp_flags,
1787 							IRP_FCP_CLEANUP);
1788 			} else {
1789 				/* XXX: need more handling */
1790 				return (DISC_ACTION_DELAY_RESCAN);
1791 			}
1792 		}
1793 
1794 		end_time = icmd->icmd_start_time +
1795 		    drv_usectohz(USEC_ELS_TIMEOUT);
1796 		if (ddi_get_lbolt() > end_time) {
1797 			(void) snprintf(info, 160,
1798 			    "fct_process_prlo: unable to "
1799 			    "clean up I/O. iport-%p, icmd-%p", (void *)iport,
1800 			    (void *)icmd);
1801 			info[159] = 0;
1802 			(void) fct_port_shutdown(iport->iport_port,
1803 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1804 
1805 			return (DISC_ACTION_DELAY_RESCAN);
1806 		}
1807 
1808 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1809 			stmf_trace(iport->iport_alias, "handling"
1810 				" PRLO from %x, waiting for cmds to"
1811 				" drain", cmd->cmd_rportid);
1812 		}
1813 		return (DISC_ACTION_DELAY_RESCAN);
1814 	}
1815 	atomic_and_32(&irp->irp_flags, ~IRP_FCP_CLEANUP);
1816 
1817 	/* Session can only be terminated after all the I/Os have drained */
1818 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1819 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1820 		    irp->irp_session);
1821 		stmf_free(irp->irp_session);
1822 		irp->irp_session = NULL;
1823 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1824 	}
1825 
1826 	fct_dequeue_els(irp);
1827 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1828 	ret = fct_send_accrjt(cmd, ELS_OP_ACC, 0, 0);
1829 	if (ret != FCT_SUCCESS)
1830 		fct_queue_cmd_for_termination(cmd, ret);
1831 
1832 	return (DISC_ACTION_RESCAN);
1833 }
1834 
1835 disc_action_t
1836 fct_process_rcvd_adisc(fct_i_cmd_t *icmd)
1837 {
1838 	fct_cmd_t		*cmd = icmd->icmd_cmd;
1839 	fct_remote_port_t	*rp = cmd->cmd_rp;
1840 	fct_local_port_t	*port = cmd->cmd_port;
1841 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1842 					port->port_fct_private;
1843 	fct_els_t		*els = (fct_els_t *)
1844 					cmd->cmd_specific;
1845 	fct_i_remote_port_t	*irp = (fct_i_remote_port_t *)
1846 					rp->rp_fct_private;
1847 	uint8_t			*p;
1848 	uint32_t		*q;
1849 	fct_status_t		ret;
1850 
1851 	fct_dequeue_els(irp);
1852 	atomic_add_16(&irp->irp_nsa_elses_count, -1);
1853 
1854 	/* Validate the adisc request */
1855 	p = els->els_req_payload;
1856 	q = (uint32_t *)p;
1857 	if ((els->els_req_size != 28) || (bcmp(rp->rp_pwwn, p + 8, 8)) ||
1858 	    (bcmp(rp->rp_nwwn, p + 16, 8))) {
1859 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0);
1860 	} else {
1861 		rp->rp_hard_address = BE_32(q[1]);
1862 		els->els_resp_size = els->els_resp_alloc_size = 28;
1863 		els->els_resp_payload = (uint8_t *)kmem_zalloc(28, KM_SLEEP);
1864 		bcopy(p, els->els_resp_payload, 28);
1865 		p = els->els_resp_payload;
1866 		q = (uint32_t *)p;
1867 		p[0] = ELS_OP_ACC;
1868 		q[1] = BE_32(port->port_hard_address);
1869 		bcopy(port->port_pwwn, p + 8, 8);
1870 		bcopy(port->port_nwwn, p + 16, 8);
1871 		q[6] = BE_32(iport->iport_link_info.portid);
1872 		ret = port->port_send_cmd_response(cmd, 0);
1873 	}
1874 	if (ret != FCT_SUCCESS) {
1875 		fct_queue_cmd_for_termination(cmd, ret);
1876 	}
1877 
1878 	return (DISC_ACTION_RESCAN);
1879 }
1880 
1881 disc_action_t
1882 fct_process_unknown_els(fct_i_cmd_t *icmd)
1883 {
1884 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
1885 	fct_status_t		 ret   = FCT_FAILURE;
1886 	uint8_t			 op    = 0;
1887 
1888 	ASSERT(icmd->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS);
1889 	fct_dequeue_els(ICMD_TO_IRP(icmd));
1890 	atomic_add_16(&ICMD_TO_IRP(icmd)->irp_nsa_elses_count, -1);
1891 	op = ICMD_TO_ELS(icmd)->els_req_payload[0];
1892 	stmf_trace(iport->iport_alias, "Rejecting unknown unsol els %x (%s)",
1893 	    op, FCT_ELS_NAME(op));
1894 	ret = fct_send_accrjt(icmd->icmd_cmd, ELS_OP_LSRJT, 1, 0);
1895 	if (ret != FCT_SUCCESS) {
1896 		fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
1897 	}
1898 
1899 	return (DISC_ACTION_RESCAN);
1900 }
1901 
1902 disc_action_t
1903 fct_process_rscn(fct_i_cmd_t *icmd)
1904 {
1905 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
1906 	fct_status_t		 ret   = FCT_FAILURE;
1907 	uint8_t			 op    = 0;
1908 	uint8_t			*rscn_req_payload;
1909 	uint32_t		 rscn_req_size;
1910 
1911 	fct_dequeue_els(ICMD_TO_IRP(icmd));
1912 	atomic_add_16(&ICMD_TO_IRP(icmd)->irp_nsa_elses_count, -1);
1913 	if (icmd->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1914 		op = ICMD_TO_ELS(icmd)->els_req_payload[0];
1915 		stmf_trace(iport->iport_alias, "Accepting RSCN %x (%s)",
1916 		    op, FCT_ELS_NAME(op));
1917 		rscn_req_size = ICMD_TO_ELS(icmd)->els_req_size;
1918 		rscn_req_payload = kmem_alloc(rscn_req_size, KM_SLEEP);
1919 		bcopy(ICMD_TO_ELS(icmd)->els_req_payload, rscn_req_payload,
1920 		    rscn_req_size);
1921 		ret = fct_send_accrjt(icmd->icmd_cmd, ELS_OP_ACC, 1, 0);
1922 		if (ret != FCT_SUCCESS) {
1923 			fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
1924 		} else {
1925 			if (fct_rscn_options & RSCN_OPTION_VERIFY) {
1926 				fct_rscn_verify(iport, rscn_req_payload,
1927 				    rscn_req_size);
1928 			}
1929 		}
1930 
1931 		kmem_free(rscn_req_payload, rscn_req_size);
1932 	} else {
1933 		ASSERT(0);
1934 	}
1935 
1936 	return (DISC_ACTION_RESCAN);
1937 }
1938 
1939 disc_action_t
1940 fct_process_els(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1941 {
1942 	fct_i_cmd_t	*cmd_to_abort = NULL;
1943 	fct_i_cmd_t	**ppcmd, *icmd;
1944 	fct_cmd_t	*cmd;
1945 	fct_els_t	*els;
1946 	int		dq;
1947 	disc_action_t	ret = DISC_ACTION_NO_WORK;
1948 	uint8_t		op;
1949 
1950 	mutex_exit(&iport->iport_worker_lock);
1951 
1952 	/*
1953 	 * Do some cleanup based on the following.
1954 	 * - We can only have one session affecting els pending.
1955 	 * - If any session affecting els is pending no other els is allowed.
1956 	 * - If PLOGI is not done, nothing except PLOGI or LOGO is allowed.
1957 	 * NOTE: If port is down the cleanup is done outside of this
1958 	 *	function.
1959 	 * NOTE: There is a side effect, if a sa ELS (non PLOGI) is received
1960 	 * while a PLOGI is pending, it will kill itself and the PLOGI.
1961 	 * which is probably ok.
1962 	 */
1963 	rw_enter(&irp->irp_lock, RW_WRITER);
1964 	ppcmd = &irp->irp_els_list;
1965 	while ((*ppcmd) != NULL) {
1966 		int special_prli_cond = 0;
1967 		dq = 0;
1968 
1969 		els = (fct_els_t *)((*ppcmd)->icmd_cmd)->cmd_specific;
1970 
1971 		if (((*ppcmd)->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
1972 		    (els->els_req_payload[0] == ELS_OP_PRLI) &&
1973 		    (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS)) {
1974 			/*
1975 			 * The initiator sent a PRLI right after responding
1976 			 * to PLOGI and we have not yet finished processing
1977 			 * the PLOGI completion. We should not kill the PRLI
1978 			 * as the initiator may not retry it.
1979 			 */
1980 			special_prli_cond = 1;
1981 		}
1982 
1983 		if ((*ppcmd)->icmd_flags & ICMD_BEING_ABORTED) {
1984 			dq = 1;
1985 		} if (irp->irp_sa_elses_count > 1) {
1986 			dq = 1;
1987 			/* This els might have set the CLEANUP flag */
1988 			atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1989 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 1",
1990 				els->els_req_payload[0]);
1991 		} else if (irp->irp_sa_elses_count &&
1992 		    (((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING) == 0)) {
1993 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 2",
1994 				els->els_req_payload[0]);
1995 			dq = 1;
1996 		} else if (((irp->irp_flags & IRP_PLOGI_DONE) == 0) &&
1997 		    (els->els_req_payload[0] != ELS_OP_PLOGI) &&
1998 		    (els->els_req_payload[0] != ELS_OP_LOGO) &&
1999 		    (special_prli_cond == 0)) {
2000 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 3",
2001 				els->els_req_payload[0]);
2002 			dq = 1;
2003 		}
2004 
2005 		if (dq) {
2006 			fct_i_cmd_t *c = (*ppcmd)->icmd_next;
2007 
2008 			if ((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING)
2009 				atomic_add_16(&irp->irp_sa_elses_count, -1);
2010 			else
2011 				atomic_add_16(&irp->irp_nsa_elses_count, -1);
2012 			(*ppcmd)->icmd_next = cmd_to_abort;
2013 			cmd_to_abort = *ppcmd;
2014 			*ppcmd = c;
2015 		} else {
2016 			ppcmd = &((*ppcmd)->icmd_next);
2017 		}
2018 	}
2019 	rw_exit(&irp->irp_lock);
2020 
2021 	while (cmd_to_abort) {
2022 		fct_i_cmd_t *c = cmd_to_abort->icmd_next;
2023 
2024 		atomic_and_32(&cmd_to_abort->icmd_flags, ~ICMD_IN_IRP_QUEUE);
2025 		fct_queue_cmd_for_termination(cmd_to_abort->icmd_cmd,
2026 				FCT_ABORTED);
2027 		cmd_to_abort = c;
2028 	}
2029 
2030 	/*
2031 	 * pick from the top of the queue
2032 	 */
2033 	icmd = irp->irp_els_list;
2034 	if (icmd == NULL) {
2035 		/*
2036 		 * The cleanup took care of everything.
2037 		 */
2038 
2039 		mutex_enter(&iport->iport_worker_lock);
2040 		return (DISC_ACTION_RESCAN);
2041 	}
2042 
2043 	cmd = icmd->icmd_cmd;
2044 	els = ICMD_TO_ELS(icmd);
2045 	op = els->els_req_payload[0];
2046 	if ((icmd->icmd_flags & ICMD_ELS_PROCESSING_STARTED) == 0) {
2047 		stmf_trace(iport->iport_alias, "Processing %ssol ELS %x (%s) "
2048 		    "rp_id=%x", (cmd->cmd_type == FCT_CMD_RCVD_ELS) ? "un" : "",
2049 		    op, FCT_ELS_NAME(op), cmd->cmd_rportid);
2050 		atomic_or_32(&icmd->icmd_flags, ICMD_ELS_PROCESSING_STARTED);
2051 	}
2052 
2053 	if (op == ELS_OP_PLOGI) {
2054 		ret |= fct_process_plogi(icmd);
2055 	} else if (op == ELS_OP_PRLI) {
2056 		ret |= fct_process_prli(icmd);
2057 	} else if (op == ELS_OP_LOGO) {
2058 		ret |= fct_process_logo(icmd);
2059 	} else if ((op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
2060 		ret |= fct_process_prlo(icmd);
2061 	} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
2062 		fct_status_t s;
2063 		fct_local_port_t *port = iport->iport_port;
2064 
2065 		fct_dequeue_els(irp);
2066 		atomic_add_16(&irp->irp_nsa_elses_count, -1);
2067 		atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
2068 		if ((s = port->port_send_cmd(cmd)) != FCT_SUCCESS) {
2069 			atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2070 			fct_queue_cmd_for_termination(cmd, s);
2071 			stmf_trace(iport->iport_alias, "Solicited els "
2072 			    "transport failed, ret = %llx", s);
2073 		}
2074 	} else if (op == ELS_OP_ADISC) {
2075 		ret |= fct_process_rcvd_adisc(icmd);
2076 	} else if (op == ELS_OP_RSCN) {
2077 		(void) fct_process_rscn(icmd);
2078 	} else {
2079 		(void) fct_process_unknown_els(icmd);
2080 	}
2081 
2082 	/*
2083 	 * This if condition will be false if a sa ELS trigged a cleanup
2084 	 * and set the ret = DISC_ACTION_DELAY_RESCAN. In that case we should
2085 	 * keep it that way.
2086 	 */
2087 	if (ret == DISC_ACTION_NO_WORK) {
2088 		/*
2089 		 * Since we dropped the lock, we will force a rescan. The
2090 		 * only exception is if someone returned
2091 		 * DISC_ACTION_DELAY_RESCAN, in which case that should be the
2092 		 * return value.
2093 		 */
2094 		ret = DISC_ACTION_RESCAN;
2095 	}
2096 
2097 	mutex_enter(&iport->iport_worker_lock);
2098 	return (ret);
2099 }
2100 
2101 void
2102 fct_handle_sol_els_completion(fct_i_local_port_t *iport, fct_i_cmd_t *icmd)
2103 {
2104 	fct_i_remote_port_t	*irp = NULL;
2105 	fct_els_t		*els = ICMD_TO_ELS(icmd);
2106 	uint8_t			 op  = els->els_req_payload[0];
2107 
2108 	if (icmd->icmd_cmd->cmd_rp) {
2109 		irp = ICMD_TO_IRP(icmd);
2110 	}
2111 	if (icmd->icmd_cmd->cmd_rp &&
2112 	    (icmd->icmd_cmd->cmd_comp_status == FCT_SUCCESS) &&
2113 	    (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2114 		bcopy(els->els_resp_payload + 20, irp->irp_rp->rp_pwwn, 8);
2115 		bcopy(els->els_resp_payload + 28, irp->irp_rp->rp_nwwn, 8);
2116 
2117 		stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
2118 		    irp->irp_rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
2119 		atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
2120 		atomic_add_32(&iport->iport_nrps_login, 1);
2121 		if (irp->irp_deregister_timer) {
2122 			irp->irp_deregister_timer = 0;
2123 			irp->irp_dereg_count = 0;
2124 		}
2125 	}
2126 
2127 	if (irp && (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2128 		atomic_and_32(&irp->irp_flags, ~IRP_SOL_PLOGI_IN_PROGRESS);
2129 	}
2130 	atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2131 	stmf_trace(iport->iport_alias, "Sol ELS %x (%s) completed with "
2132 	    "status %llx, did/%x", op, FCT_ELS_NAME(op),
2133 	    icmd->icmd_cmd->cmd_comp_status, icmd->icmd_cmd->cmd_rportid);
2134 }
2135 
2136 static disc_action_t
2137 fct_check_cmdlist(fct_i_local_port_t *iport)
2138 {
2139 	int		num_to_release, ndx;
2140 	fct_i_cmd_t	*icmd;
2141 	uint32_t	total, max_active;
2142 
2143 	ASSERT(MUTEX_HELD(&iport->iport_worker_lock));
2144 
2145 	total = iport->iport_total_alloced_ncmds;
2146 	max_active = iport->iport_max_active_ncmds;
2147 
2148 	if (total <= max_active)
2149 		return (DISC_ACTION_NO_WORK);
2150 	/*
2151 	 * Everytime, we release half of the difference
2152 	 */
2153 	num_to_release = (total + 1 - max_active) / 2;
2154 
2155 	mutex_exit(&iport->iport_worker_lock);
2156 	for (ndx = 0; ndx < num_to_release; ndx++) {
2157 		mutex_enter(&iport->iport_cached_cmd_lock);
2158 		icmd = iport->iport_cached_cmdlist;
2159 		if (icmd == NULL) {
2160 			mutex_exit(&iport->iport_cached_cmd_lock);
2161 			break;
2162 		}
2163 		iport->iport_cached_cmdlist = icmd->icmd_next;
2164 		iport->iport_cached_ncmds--;
2165 		mutex_exit(&iport->iport_cached_cmd_lock);
2166 		atomic_add_32(&iport->iport_total_alloced_ncmds, -1);
2167 		fct_free(icmd->icmd_cmd);
2168 	}
2169 	mutex_enter(&iport->iport_worker_lock);
2170 	return (DISC_ACTION_RESCAN);
2171 }
2172 
2173 /*
2174  * The efficiency of handling solicited commands is very low here. But
2175  * fortunately, we seldom send solicited commands. So it will not hurt
2176  * the system performance much.
2177  */
2178 static disc_action_t
2179 fct_check_solcmd_queue(fct_i_local_port_t *iport)
2180 {
2181 	fct_i_cmd_t	*icmd	    = NULL;
2182 	fct_i_cmd_t	*prev_icmd  = NULL;
2183 	fct_i_cmd_t	*next_icmd  = NULL;
2184 
2185 	ASSERT(mutex_owned(&iport->iport_worker_lock));
2186 	for (icmd = iport->iport_solcmd_queue; icmd; icmd = next_icmd) {
2187 		ASSERT(icmd->icmd_flags | ICMD_IN_SOLCMD_QUEUE);
2188 		next_icmd = icmd->icmd_solcmd_next;
2189 		if (icmd->icmd_flags & ICMD_SOLCMD_NEW) {
2190 			/*
2191 			 * This solicited cmd is new.
2192 			 * Dispatch ELSes to discovery queue to make use of
2193 			 * existent framework.
2194 			 */
2195 			icmd->icmd_flags &= ~ICMD_SOLCMD_NEW;
2196 			mutex_exit(&iport->iport_worker_lock);
2197 
2198 			if (icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_ELS) {
2199 				fct_handle_els(icmd->icmd_cmd);
2200 			} else {
2201 				fct_handle_solct(icmd->icmd_cmd);
2202 			}
2203 
2204 			mutex_enter(&iport->iport_worker_lock);
2205 		} else if (icmd->icmd_flags & ICMD_CMD_COMPLETE) {
2206 			/*
2207 			 * To make fct_check_solcmd simple and flexible,
2208 			 * We need only call callback to finish post-handling.
2209 			 */
2210 			if (icmd->icmd_cb) {
2211 				/*
2212 				 * mutex ???
2213 				 */
2214 				icmd->icmd_cb(icmd);
2215 			}
2216 
2217 
2218 			/*
2219 			 * Release resources for this solicited cmd
2220 			 */
2221 			if (iport->iport_solcmd_queue == icmd) {
2222 				iport->iport_solcmd_queue = next_icmd;
2223 			} else {
2224 				for (prev_icmd = iport->iport_solcmd_queue;
2225 				    prev_icmd->icmd_solcmd_next != icmd;
2226 				    prev_icmd = prev_icmd->icmd_solcmd_next);
2227 				prev_icmd->icmd_solcmd_next = next_icmd;
2228 			}
2229 
2230 			icmd->icmd_cb = NULL;
2231 			mutex_exit(&iport->iport_worker_lock);
2232 			fct_cmd_free(icmd->icmd_cmd);
2233 			mutex_enter(&iport->iport_worker_lock);
2234 		} else {
2235 			/*
2236 			 * This solicited cmd is still ongoing.
2237 			 * We need check if it's time to abort this cmd
2238 			 */
2239 			if (((icmd->icmd_start_time + drv_usectohz(
2240 			    USEC_SOL_TIMEOUT)) < ddi_get_lbolt()) &&
2241 			    !(icmd->icmd_flags & ICMD_BEING_ABORTED)) {
2242 				fct_q_for_termination_lock_held(iport,
2243 				    icmd, FCT_ABORTED);
2244 			}
2245 		}
2246 	}
2247 
2248 	return (DISC_ACTION_DELAY_RESCAN);
2249 }
2250 
2251 void
2252 fct_handle_solct(fct_cmd_t *cmd)
2253 {
2254 	fct_status_t		 ret	  = FCT_SUCCESS;
2255 	fct_i_cmd_t		*icmd	  = CMD_TO_ICMD(cmd);
2256 	fct_i_local_port_t	*iport	  = ICMD_TO_IPORT(icmd);
2257 	fct_i_remote_port_t	*irp	  = ICMD_TO_IRP(icmd);
2258 
2259 	ASSERT(cmd->cmd_type == FCT_CMD_SOL_CT);
2260 	rw_enter(&iport->iport_lock, RW_READER);
2261 	/*
2262 	 * Let's make sure local port is sane
2263 	 */
2264 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2265 		rw_exit(&iport->iport_lock);
2266 
2267 		stmf_trace(iport->iport_alias, "fct_transport_solct: "
2268 		    "solcmd-%p transport failed, becasue port state was %x",
2269 		    cmd, iport->iport_link_state);
2270 		fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2271 		return;
2272 	}
2273 
2274 	/*
2275 	 * Let's make sure we have plogi-ed to name server
2276 	 */
2277 	rw_enter(&irp->irp_lock, RW_READER);
2278 	if (!(irp->irp_flags & IRP_PLOGI_DONE)) {
2279 		rw_exit(&irp->irp_lock);
2280 		rw_exit(&iport->iport_lock);
2281 
2282 		stmf_trace(iport->iport_alias, "fct_transport_solct: "
2283 		    "Must login to name server first - cmd-%p", cmd);
2284 		fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
2285 		return;
2286 	}
2287 
2288 	/*
2289 	 * Let's get a slot for this solcmd
2290 	 */
2291 	if (fct_alloc_cmd_slot(iport, cmd) == FCT_SLOT_EOL) {
2292 		rw_exit(&irp->irp_lock);
2293 		rw_exit(&iport->iport_lock);
2294 
2295 		stmf_trace(iport->iport_alias, "fct_transport_solcmd: "
2296 		    "ran out of xchg resources - cmd-%p", cmd);
2297 		fct_queue_cmd_for_termination(cmd, FCT_NO_XCHG_RESOURCE);
2298 		return;
2299 	}
2300 
2301 	if (fct_netbuf_to_value(ICMD_TO_CT(icmd)->ct_req_payload + 8, 2) ==
2302 	    NS_GID_PN) {
2303 		fct_i_remote_port_t	*query_irp = NULL;
2304 
2305 		query_irp = fct_lookup_irp_by_portwwn(iport,
2306 		    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2307 		if (query_irp) {
2308 			atomic_and_32(&query_irp->irp_flags, ~IRP_RSCN_QUEUED);
2309 		}
2310 	}
2311 	rw_exit(&irp->irp_lock);
2312 	rw_exit(&iport->iport_lock);
2313 
2314 	atomic_add_16(&irp->irp_nonfcp_xchg_count, 1);
2315 	atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
2316 	icmd->icmd_start_time = ddi_get_lbolt();
2317 	ret = iport->iport_port->port_send_cmd(cmd);
2318 	if (ret != FCT_SUCCESS) {
2319 		atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2320 		fct_queue_cmd_for_termination(cmd, ret);
2321 	}
2322 }
2323 
2324 void
2325 fct_logo_cb(fct_i_cmd_t *icmd)
2326 {
2327 	ASSERT(!(icmd->icmd_flags & ICMD_IMPLICIT));
2328 	if (!FCT_IS_ELS_ACC(icmd)) {
2329 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_logo_cb: "
2330 		    "solicited LOGO is not accepted - icmd/%p", icmd);
2331 	}
2332 }
2333 
2334 void
2335 fct_gsnn_cb(fct_i_cmd_t *icmd)
2336 {
2337 	int			 snlen	   = 0;
2338 	char			*sn	   = NULL;
2339 	fct_i_remote_port_t	*query_irp = NULL;
2340 
2341 	if (!FCT_IS_CT_ACC(icmd)) {
2342 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2343 		    "GSNN is not accepted by NS - icmd/%p", icmd);
2344 		return;
2345 	}
2346 	mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2347 
2348 	rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2349 	mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2350 	query_irp = fct_lookup_irp_by_nodewwn(ICMD_TO_IPORT(icmd),
2351 	    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2352 
2353 	if (!query_irp) {
2354 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2355 		    "can't get rp icmd-%p", icmd);
2356 		goto exit_gsnn_cb;
2357 	} else {
2358 		snlen = ICMD_TO_CT(icmd)->ct_resp_payload[16];
2359 	}
2360 
2361 	if (query_irp && snlen) {
2362 		/*
2363 		 * Release previous resource, then allocate needed resource
2364 		 */
2365 		sn = query_irp->irp_snn;
2366 		if (sn) {
2367 			kmem_free(sn, strlen(sn) + 1);
2368 		}
2369 
2370 		query_irp->irp_snn = NULL;
2371 		sn = kmem_zalloc(snlen + 1, KM_SLEEP);
2372 		(void) strncpy(sn, (char *)
2373 		    ICMD_TO_CT(icmd)->ct_resp_payload + 17, snlen);
2374 		if (strlen(sn) != snlen) {
2375 			stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2376 			    "fct_gsnn_cb: %s, but len=%d", sn, snlen);
2377 			kmem_free(sn, snlen + 1);
2378 			sn = NULL;
2379 		}
2380 
2381 		/*
2382 		 * Update symbolic node name
2383 		 */
2384 		query_irp->irp_snn = sn;
2385 		if ((query_irp->irp_flags & IRP_SCSI_SESSION_STARTED) &&
2386 		    (query_irp->irp_session)) {
2387 			query_irp->irp_session->ss_rport_alias =
2388 			    query_irp->irp_snn;
2389 		}
2390 	} else {
2391 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2392 		    "irp/%p, snlen/%d", query_irp, snlen);
2393 	}
2394 
2395 exit_gsnn_cb:
2396 	rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2397 }
2398 
2399 void
2400 fct_link_init_cb(fct_i_cmd_t *icmd)
2401 {
2402 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
2403 
2404 	iport->iport_li_state &= ~LI_STATE_FLAG_CMD_WAITING;
2405 	if (icmd->icmd_cmd->cmd_comp_status != FCT_SUCCESS) {
2406 		stmf_trace(iport->iport_alias, "fct_link_init_cb: ELS-%x failed"
2407 		    "comp_status- %llx", ICMD_TO_ELS(icmd)->els_req_payload[0],
2408 		    icmd->icmd_cmd->cmd_comp_status);
2409 		iport->iport_li_comp_status = icmd->icmd_cmd->cmd_comp_status;
2410 	} else if (icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_ELS) {
2411 		if (!FCT_IS_ELS_ACC(icmd)) {
2412 			stmf_trace(iport->iport_alias,
2413 			    "fct_link_init_cb: ELS-%x is rejected",
2414 			    ICMD_TO_ELS(icmd)->els_req_payload[0]);
2415 			iport->iport_li_comp_status = FCT_REJECT_STATUS(
2416 			    ICMD_TO_ELS(icmd)->els_resp_payload[1],
2417 			    ICMD_TO_ELS(icmd)->els_resp_payload[2]);
2418 		} else {
2419 			iport->iport_li_comp_status = FCT_SUCCESS;
2420 		}
2421 	} else {
2422 		ASSERT(icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_CT);
2423 		if (!FCT_IS_CT_ACC(icmd)) {
2424 			stmf_trace(iport->iport_alias,
2425 			    "fct_link_init_cb: CT-%02x%02x is rejected",
2426 			    ICMD_TO_CT(icmd)->ct_req_payload[8],
2427 			    ICMD_TO_CT(icmd)->ct_req_payload[9]);
2428 			iport->iport_li_comp_status = FCT_REJECT_STATUS(
2429 			    ICMD_TO_CT(icmd)->ct_resp_payload[8],
2430 			    ICMD_TO_CT(icmd)->ct_resp_payload[9]);
2431 		} else {
2432 			iport->iport_li_comp_status = FCT_SUCCESS;
2433 		}
2434 	}
2435 }
2436 
2437 void
2438 fct_gcs_cb(fct_i_cmd_t *icmd)
2439 {
2440 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2441 	fct_i_remote_port_t	*query_irp = NULL;
2442 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2443 	uint32_t		 query_portid;
2444 	uint8_t			*resp;
2445 	uint8_t			*req;
2446 
2447 	if (!FCT_IS_CT_ACC(icmd)) {
2448 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gcs_cb: "
2449 		    "GCS_ID is not accepted by NS - icmd/%p", icmd);
2450 		return;
2451 	}
2452 	mutex_exit(&iport->iport_worker_lock);
2453 
2454 	resp = ct->ct_resp_payload;
2455 	req = ct->ct_req_payload;
2456 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2457 
2458 	rw_enter(&iport->iport_lock, RW_READER);
2459 	mutex_enter(&iport->iport_worker_lock);
2460 	query_irp = fct_portid_to_portptr(iport, query_portid);
2461 
2462 	if (query_irp) {
2463 		query_irp->irp_cos = (resp[16] << 27) | (resp[17] << 18) |
2464 		    (resp[18] << 8) | resp[19];
2465 	}
2466 	rw_exit(&iport->iport_lock);
2467 }
2468 
2469 void
2470 fct_gft_cb(fct_i_cmd_t *icmd)
2471 {
2472 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2473 	fct_i_remote_port_t	*query_irp = NULL;
2474 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2475 	uint32_t		 query_portid;
2476 	uint8_t			*resp;
2477 	uint8_t			*req;
2478 
2479 	if (!FCT_IS_CT_ACC(icmd)) {
2480 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gft_cb: "
2481 		    "GFT_ID is not accepted by NS - icmd/%p", icmd);
2482 		return;
2483 	}
2484 	mutex_exit(&iport->iport_worker_lock);
2485 
2486 	resp = ct->ct_resp_payload;
2487 	req = ct->ct_req_payload;
2488 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2489 
2490 	rw_enter(&iport->iport_lock, RW_READER);
2491 	mutex_enter(&iport->iport_worker_lock);
2492 	query_irp = fct_portid_to_portptr(iport, query_portid);
2493 
2494 	if (query_irp) {
2495 		(void) memcpy(query_irp->irp_fc4types, resp + 16, 32);
2496 	}
2497 	rw_exit(&iport->iport_lock);
2498 }
2499 
2500 void
2501 fct_gid_cb(fct_i_cmd_t *icmd)
2502 {
2503 	fct_cmd_t		*cmd	   = NULL;
2504 	fct_i_remote_port_t	*query_irp = NULL;
2505 	uint32_t		 nsportid  = 0;
2506 	int			 do_logo   = 0;
2507 
2508 	mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2509 
2510 	rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2511 	mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2512 	query_irp = fct_lookup_irp_by_portwwn(ICMD_TO_IPORT(icmd),
2513 	    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2514 
2515 	if (!query_irp || (query_irp &&
2516 	    (PTR2INT(icmd->icmd_cb_private, uint32_t) !=
2517 	    query_irp->irp_rscn_counter))) {
2518 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2519 		    "new RSCN arrived - query_irp/%p, private-%x", query_irp,
2520 		    PTR2INT(icmd->icmd_cb_private, uint32_t));
2521 		goto exit_gid_cb;
2522 	}
2523 
2524 	if ((query_irp->irp_flags & IRP_RSCN_QUEUED) ||
2525 	    (!(query_irp->irp_flags & IRP_PLOGI_DONE)))	{
2526 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2527 		    "not proper irp_flags - query_irp/%p", query_irp);
2528 		goto exit_gid_cb;
2529 	}
2530 
2531 	if (!FCT_IS_CT_ACC(icmd)) {
2532 		/*
2533 		 * Check if it has disappeared
2534 		 */
2535 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2536 		    "GPN_ID is not accepted by NS - icmd/%p", icmd);
2537 		do_logo = 1;
2538 	} else {
2539 		/*
2540 		 * Check if its portid has changed
2541 		 */
2542 		nsportid = fct_netbuf_to_value(
2543 		    ICMD_TO_CT(icmd)->ct_resp_payload + 17, 3);
2544 		if (nsportid != query_irp->irp_rp->rp_id) {
2545 			stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2546 			    "portid has changed - query_irp/%p", query_irp);
2547 			do_logo = 1;
2548 		}
2549 	}
2550 
2551 	if (do_logo) {
2552 		cmd = fct_create_solels(ICMD_TO_PORT(icmd),
2553 		    query_irp->irp_rp, 1, ELS_OP_LOGO, 0, fct_logo_cb);
2554 		if (cmd) {
2555 			mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2556 			fct_post_implicit_logo(cmd);
2557 			mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2558 		}
2559 	}
2560 
2561 exit_gid_cb:
2562 	rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2563 }
2564 
2565 void
2566 fct_gspn_cb(fct_i_cmd_t *icmd)
2567 {
2568 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2569 	fct_i_remote_port_t	*query_irp = NULL;
2570 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2571 	uint32_t		 query_portid;
2572 	uint8_t			*resp;
2573 	uint8_t			*req;
2574 	uint8_t			 spnlen;
2575 
2576 	if (!FCT_IS_CT_ACC(icmd)) {
2577 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gspn_cb: "
2578 		    "GSPN_ID is not accepted by NS - icmd/%p", icmd);
2579 		return;
2580 	}
2581 	mutex_exit(&iport->iport_worker_lock);
2582 
2583 	resp = ct->ct_resp_payload;
2584 	req = ct->ct_req_payload;
2585 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2586 
2587 	rw_enter(&iport->iport_lock, RW_READER);
2588 	mutex_enter(&iport->iport_worker_lock);
2589 	query_irp = fct_portid_to_portptr(iport, query_portid);
2590 	if (query_irp) {
2591 		spnlen = resp[16];
2592 		if (spnlen > 0) {
2593 			if (query_irp->irp_spn) {
2594 				kmem_free(query_irp->irp_spn,
2595 				    strlen(query_irp->irp_spn) + 1);
2596 			}
2597 			query_irp->irp_spn = kmem_zalloc(spnlen + 1, KM_SLEEP);
2598 			(void) strncpy(query_irp->irp_spn,
2599 			    (char *)resp + 17, spnlen);
2600 		}
2601 	}
2602 	rw_exit(&iport->iport_lock);
2603 }
2604 
2605 /*
2606  * For lookup functions, we move locking up one level
2607  */
2608 fct_i_remote_port_t *
2609 fct_lookup_irp_by_nodewwn(fct_i_local_port_t *iport, uint8_t *nodewwn)
2610 {
2611 	fct_i_remote_port_t	*irp = NULL;
2612 	int			 idx = 0;
2613 
2614 	for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2615 		for (irp = iport->iport_rp_tb[idx]; irp;
2616 		    irp = irp->irp_next) {
2617 			if (bcmp(irp->irp_rp->rp_nwwn, nodewwn, FC_WWN_LEN)) {
2618 				continue;
2619 			} else {
2620 				return (irp);
2621 			}
2622 		}
2623 	}
2624 
2625 	return (NULL);
2626 }
2627 
2628 fct_i_remote_port_t *
2629 fct_lookup_irp_by_portwwn(fct_i_local_port_t *iport, uint8_t *portwwn)
2630 {
2631 	fct_i_remote_port_t	*irp = NULL;
2632 	int			 idx = 0;
2633 
2634 	for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2635 		for (irp = iport->iport_rp_tb[idx]; irp;
2636 		    irp = irp->irp_next) {
2637 			if (bcmp(irp->irp_rp->rp_pwwn, portwwn, FC_WWN_LEN)) {
2638 				continue;
2639 			} else {
2640 				return (irp);
2641 			}
2642 		}
2643 	}
2644 
2645 	return (NULL);
2646 }
2647 
2648 #ifdef	lint
2649 #define	FCT_VERIFY_RSCN()	_NOTE(EMPTY)
2650 #else
2651 #define	FCT_VERIFY_RSCN()						\
2652 do {									\
2653 	ct_cmd = fct_create_solct(port, irp->irp_rp, NS_GID_PN,		\
2654 	    fct_gid_cb);						\
2655 	if (ct_cmd) {							\
2656 		uint32_t cnt;						\
2657 		cnt = atomic_add_32_nv(&irp->irp_rscn_counter, 1);	\
2658 		CMD_TO_ICMD(ct_cmd)->icmd_cb_private =			\
2659 		    INT2PTR(cnt, void *);				\
2660 		irp->irp_flags |= IRP_RSCN_QUEUED;			\
2661 		fct_post_to_solcmd_queue(port, ct_cmd);			\
2662 	}								\
2663 } while (0)
2664 #endif
2665 
2666 /* ARGSUSED */
2667 static void
2668 fct_rscn_verify(fct_i_local_port_t *iport, uint8_t *rscn_req_payload,
2669     uint32_t rscn_req_size)
2670 {
2671 	int			idx		= 0;
2672 	uint8_t			page_format	= 0;
2673 	uint32_t		page_portid	= 0;
2674 	uint8_t			*page_buf	= NULL;
2675 	uint8_t			*last_page_buf	= NULL;
2676 #ifndef	lint
2677 	fct_cmd_t		*ct_cmd		= NULL;
2678 	fct_local_port_t	*port		= NULL;
2679 #endif
2680 	fct_i_remote_port_t	*irp		= NULL;
2681 
2682 	page_buf = rscn_req_payload + 4;
2683 	last_page_buf = rscn_req_payload +
2684 	    fct_netbuf_to_value(rscn_req_payload + 2, 2) - 4;
2685 #ifndef	lint
2686 	port = iport->iport_port;
2687 #endif
2688 	for (; page_buf <= last_page_buf; page_buf += 4) {
2689 		page_format = 0x03 & page_buf[0];
2690 		page_portid = fct_netbuf_to_value(page_buf + 1, 3);
2691 
2692 		rw_enter(&iport->iport_lock, RW_READER);
2693 		if (!page_format) {
2694 			irp = fct_portid_to_portptr(iport, page_portid);
2695 			if (!(irp && !(irp->irp_flags & IRP_RSCN_QUEUED))) {
2696 				rw_exit(&iport->iport_lock);
2697 
2698 				continue; /* try next page */
2699 			}
2700 
2701 			if (FC_WELL_KNOWN_ADDR(irp->irp_portid) ||
2702 			    !(irp->irp_flags & IRP_PLOGI_DONE)) {
2703 				rw_exit(&iport->iport_lock);
2704 
2705 				continue; /* try next page */
2706 			}
2707 
2708 			FCT_VERIFY_RSCN();
2709 		} else {
2710 			for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2711 				for (irp = iport->iport_rp_tb[idx];
2712 				    irp; irp = irp->irp_next) {
2713 					if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
2714 						continue; /* try next irp */
2715 
2716 					if (!(irp->irp_flags & IRP_PLOGI_DONE))
2717 						continue; /* try next irp */
2718 
2719 					if (irp->irp_flags & IRP_RSCN_QUEUED) {
2720 						continue; /* try next irp */
2721 					}
2722 #ifndef	lint
2723 					if (!((0xFFFFFF << (page_format * 8)) &
2724 					    (page_portid ^ irp->irp_portid))) {
2725 						FCT_VERIFY_RSCN();
2726 					}
2727 #endif
2728 				}
2729 			}
2730 		}
2731 		rw_exit(&iport->iport_lock);
2732 	}
2733 }
2734