xref: /illumos-gate/usr/src/uts/common/io/comstar/port/fct/discovery.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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 2009 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 			fct_i_remote_port_t *t_irp =
1114 			    iport->iport_rp_slots[rp->rp_handle];
1115 			(void) snprintf(info, 160, "fct_register_remote_port: "
1116 			    "FCA returned a handle %d for portid %x "
1117 			    "which was already in use for a different "
1118 			    "portid (%x)", rp->rp_handle, rp->rp_id,
1119 			    t_irp->irp_rp->rp_id);
1120 			info[159] = 0;
1121 			goto hba_fatal_err;
1122 		}
1123 	} else {
1124 		/* Pick a handle for this port */
1125 		for (i = 0; i < port->port_max_logins; i++) {
1126 			if (iport->iport_rp_slots[i] == NULL) {
1127 				break;
1128 			}
1129 		}
1130 		if (i == port->port_max_logins) {
1131 			/* This is really pushing it. */
1132 			(void) snprintf(info, 160, "fct_register_remote_port "
1133 			    "Cannot register portid %x because all the "
1134 			    "handles are used up", rp->rp_id);
1135 			info[159] = 0;
1136 			goto hba_fatal_err;
1137 		}
1138 		rp->rp_handle = i;
1139 	}
1140 	/* By this time rport_handle is valid */
1141 	if ((irp->irp_flags & IRP_HANDLE_OPENED) == 0) {
1142 		iport->iport_rp_slots[rp->rp_handle] = irp;
1143 		atomic_or_32(&irp->irp_flags, IRP_HANDLE_OPENED);
1144 	}
1145 	(void) atomic_add_64_nv(&iport->iport_last_change, 1);
1146 	fct_log_remote_port_event(port, ESC_SUNFC_TARGET_ADD,
1147 	    rp->rp_pwwn, rp->rp_id);
1148 
1149 register_rp_done:;
1150 	rw_exit(&irp->irp_lock);
1151 	rw_exit(&iport->iport_lock);
1152 	return (FCT_SUCCESS);
1153 
1154 hba_fatal_err:;
1155 	rw_exit(&irp->irp_lock);
1156 	rw_exit(&iport->iport_lock);
1157 	/*
1158 	 * XXX Throw HBA fatal error event
1159 	 */
1160 	(void) fct_port_shutdown(iport->iport_port,
1161 	    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1162 	return (FCT_FAILURE);
1163 }
1164 
1165 fct_status_t
1166 fct_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp)
1167 {
1168 	fct_status_t		 ret   = FCT_SUCCESS;
1169 	fct_i_local_port_t	*iport = PORT_TO_IPORT(port);
1170 	fct_i_remote_port_t	*irp   = RP_TO_IRP(rp);
1171 
1172 	if (irp->irp_snn) {
1173 		kmem_free(irp->irp_snn, strlen(irp->irp_snn) + 1);
1174 		irp->irp_snn = NULL;
1175 	}
1176 	if (irp->irp_spn) {
1177 		kmem_free(irp->irp_spn, strlen(irp->irp_spn) + 1);
1178 		irp->irp_spn = NULL;
1179 	}
1180 
1181 	if ((ret = port->port_deregister_remote_port(port, rp)) !=
1182 	    FCT_SUCCESS) {
1183 		return (ret);
1184 	}
1185 
1186 	if (irp->irp_flags & IRP_HANDLE_OPENED) {
1187 		atomic_and_32(&irp->irp_flags, ~IRP_HANDLE_OPENED);
1188 		iport->iport_rp_slots[rp->rp_handle] = NULL;
1189 	}
1190 	(void) atomic_add_64_nv(&iport->iport_last_change, 1);
1191 	fct_log_remote_port_event(port, ESC_SUNFC_TARGET_REMOVE,
1192 	    rp->rp_pwwn, rp->rp_id);
1193 
1194 	return (FCT_SUCCESS);
1195 }
1196 
1197 fct_status_t
1198 fct_send_accrjt(fct_cmd_t *cmd, uint8_t accrjt, uint8_t reason, uint8_t expl)
1199 {
1200 	fct_local_port_t *port = (fct_local_port_t *)cmd->cmd_port;
1201 	fct_els_t *els = (fct_els_t *)cmd->cmd_specific;
1202 
1203 	els->els_resp_size = els->els_resp_alloc_size = 8;
1204 	els->els_resp_payload = (uint8_t *)kmem_zalloc(8, KM_SLEEP);
1205 	els->els_resp_payload[0] = accrjt;
1206 	if (accrjt == 1) {
1207 		els->els_resp_payload[5] = reason;
1208 		els->els_resp_payload[6] = expl;
1209 	} else {
1210 		els->els_resp_size = 4;
1211 	}
1212 
1213 	return (port->port_send_cmd_response(cmd, 0));
1214 }
1215 
1216 
1217 disc_action_t
1218 fct_walk_discovery_queue(fct_i_local_port_t *iport)
1219 {
1220 	char			info[80];
1221 	fct_i_remote_port_t	**pirp;
1222 	fct_i_remote_port_t	*prev_irp = NULL;
1223 	disc_action_t		suggested_action = DISC_ACTION_NO_WORK;
1224 	fct_i_remote_port_t	*irp_dereg_list = NULL;
1225 	fct_i_remote_port_t	*irp_cur_item = NULL;
1226 
1227 	for (pirp = &iport->iport_rpwe_head; *pirp != NULL; ) {
1228 		fct_i_remote_port_t *irp = *pirp;
1229 		disc_action_t ret = DISC_ACTION_NO_WORK;
1230 		int do_deregister = 0;
1231 		int irp_deregister_timer = 0;
1232 
1233 		if (irp->irp_els_list) {
1234 			ret |= fct_process_els(iport, irp);
1235 		}
1236 
1237 		irp_deregister_timer = irp->irp_deregister_timer;
1238 		if (irp_deregister_timer) {
1239 			if (ddi_get_lbolt() >= irp_deregister_timer) {
1240 				do_deregister = 1;
1241 			} else {
1242 				ret |= DISC_ACTION_DELAY_RESCAN;
1243 			}
1244 		}
1245 		suggested_action |= ret;
1246 
1247 		if (irp->irp_els_list == NULL) {
1248 			mutex_exit(&iport->iport_worker_lock);
1249 			rw_enter(&iport->iport_lock, RW_WRITER);
1250 			rw_enter(&irp->irp_lock, RW_WRITER);
1251 			mutex_enter(&iport->iport_worker_lock);
1252 			if (irp->irp_els_list == NULL) {
1253 				if (!irp_deregister_timer ||
1254 				    (do_deregister &&
1255 				    !irp->irp_sa_elses_count &&
1256 				    !irp->irp_nsa_elses_count &&
1257 				    !irp->irp_fcp_xchg_count &&
1258 				    !irp->irp_nonfcp_xchg_count)) {
1259 					/* dequeue irp from discovery queue */
1260 					atomic_and_32(&irp->irp_flags,
1261 					    ~IRP_IN_DISCOVERY_QUEUE);
1262 					*pirp = irp->irp_discovery_next;
1263 					if (iport->iport_rpwe_head == NULL)
1264 						iport->iport_rpwe_tail = NULL;
1265 					else if (irp == iport->iport_rpwe_tail)
1266 						iport->iport_rpwe_tail =
1267 						    prev_irp;
1268 
1269 					irp->irp_discovery_next = NULL;
1270 					if (do_deregister) {
1271 						fct_deque_rp(iport, irp);
1272 						rw_exit(&irp->irp_lock);
1273 						/* queue irp for deregister */
1274 						irp->irp_next = NULL;
1275 						if (!irp_dereg_list) {
1276 							irp_dereg_list =
1277 							    irp_cur_item = irp;
1278 						} else {
1279 							irp_cur_item->irp_next =
1280 							    irp;
1281 							irp_cur_item = irp;
1282 						}
1283 					} else {
1284 						rw_exit(&irp->irp_lock);
1285 					}
1286 					rw_exit(&iport->iport_lock);
1287 					if ((irp = *pirp) == NULL)
1288 						break;
1289 				} else {
1290 					/*
1291 					 * wait for another scan until
1292 					 * deregister timeout
1293 					 */
1294 					rw_exit(&irp->irp_lock);
1295 					rw_exit(&iport->iport_lock);
1296 				}
1297 			} else {
1298 				rw_exit(&irp->irp_lock);
1299 				rw_exit(&iport->iport_lock);
1300 				/*
1301 				 * When we dropped the lock,
1302 				 * something went in.
1303 				 */
1304 				suggested_action |= DISC_ACTION_RESCAN;
1305 			}
1306 		}
1307 		pirp = &(irp->irp_discovery_next);
1308 		prev_irp = irp;
1309 	}
1310 	/* do deregister */
1311 	if (irp_dereg_list) {
1312 		fct_i_remote_port_t *irp_next_item;
1313 		/* drop the lock */
1314 		mutex_exit(&iport->iport_worker_lock);
1315 
1316 		for (irp_cur_item = irp_dereg_list; irp_cur_item != NULL; ) {
1317 			irp_next_item = irp_cur_item->irp_next;
1318 			if (fct_deregister_remote_port(iport->iport_port,
1319 			    irp_cur_item->irp_rp) == FCT_SUCCESS) {
1320 				fct_free(irp_cur_item->irp_rp);
1321 			} else if (++irp_cur_item->irp_dereg_count >= 5) {
1322 				irp_cur_item->irp_deregister_timer = 0;
1323 				irp_cur_item->irp_dereg_count = 0;
1324 
1325 				/*
1326 				 * It looks like we can't deregister it in the
1327 				 * normal way, so we have to use extrem way
1328 				 */
1329 				(void) snprintf(info, 80,
1330 				    "fct_walk_discovery_queue: "
1331 				    "iport-%p, can't deregister irp-%p after "
1332 				    "trying 5 times", (void *)iport,
1333 				    (void *)irp_cur_item);
1334 				info[79] = 0;
1335 				(void) fct_port_shutdown(iport->iport_port,
1336 				    STMF_RFLAG_FATAL_ERROR |
1337 				    STMF_RFLAG_RESET, info);
1338 				suggested_action |= DISC_ACTION_RESCAN;
1339 				break;
1340 			} else {
1341 				/* grab the iport_lock */
1342 				rw_enter(&iport->iport_lock, RW_WRITER);
1343 				/* recover */
1344 				irp_cur_item->irp_deregister_timer =
1345 				    ddi_get_lbolt() +
1346 				    drv_usectohz(USEC_DEREG_RP_INTERVAL);
1347 				fct_post_to_discovery_queue(iport,
1348 				    irp_cur_item, NULL);
1349 				fct_queue_rp(iport, irp_cur_item);
1350 				rw_exit(&iport->iport_lock);
1351 				suggested_action |= DISC_ACTION_DELAY_RESCAN;
1352 			}
1353 			irp_cur_item = irp_next_item;
1354 		}
1355 		mutex_enter(&iport->iport_worker_lock);
1356 	}
1357 	return (suggested_action);
1358 }
1359 
1360 disc_action_t
1361 fct_process_plogi(fct_i_cmd_t *icmd)
1362 {
1363 	fct_cmd_t		*cmd = icmd->icmd_cmd;
1364 	fct_remote_port_t	*rp = cmd->cmd_rp;
1365 	fct_local_port_t	*port = cmd->cmd_port;
1366 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1367 	    port->port_fct_private;
1368 	fct_els_t		*els = (fct_els_t *)
1369 	    cmd->cmd_specific;
1370 	fct_i_remote_port_t	*irp = (fct_i_remote_port_t *)
1371 	    rp->rp_fct_private;
1372 	uint8_t			*p;
1373 	fct_status_t		 ret;
1374 	uint8_t			 cmd_type   = cmd->cmd_type;
1375 	uint32_t		 icmd_flags = icmd->icmd_flags;
1376 	clock_t			 end_time;
1377 	char			 info[160];
1378 
1379 	/* Drain I/Os */
1380 	if ((irp->irp_nonfcp_xchg_count + irp->irp_fcp_xchg_count) > 1) {
1381 		/* Trigger cleanup if necessary */
1382 		if ((irp->irp_flags & IRP_SESSION_CLEANUP) == 0) {
1383 			stmf_trace(iport->iport_alias, "handling PLOGI rp_id"
1384 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1385 			/* Cleanup everything except elses */
1386 			if (fct_trigger_rport_cleanup(irp, ~(cmd->cmd_type))) {
1387 				atomic_or_32(&irp->irp_flags,
1388 				    IRP_SESSION_CLEANUP);
1389 			} else {
1390 				/* XXX: handle this */
1391 				/* EMPTY */
1392 			}
1393 		}
1394 
1395 		end_time = icmd->icmd_start_time +
1396 		    drv_usectohz(USEC_ELS_TIMEOUT);
1397 		if (ddi_get_lbolt() > end_time) {
1398 			(void) snprintf(info, 160,
1399 			    "fct_process_plogi: unable to "
1400 			    "clean up I/O. iport-%p, icmd-%p", (void *)iport,
1401 			    (void *)icmd);
1402 			info[159] = 0;
1403 			(void) fct_port_shutdown(iport->iport_port,
1404 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1405 
1406 			return (DISC_ACTION_DELAY_RESCAN);
1407 		}
1408 
1409 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1410 			stmf_trace(iport->iport_alias, "handling"
1411 			    " PLOGI rp_id %x, waiting for cmds to"
1412 			    " drain", cmd->cmd_rportid);
1413 		}
1414 		return (DISC_ACTION_DELAY_RESCAN);
1415 	}
1416 	atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1417 
1418 	/* Session can only be terminated after all the I/Os have drained */
1419 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1420 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1421 		    irp->irp_session);
1422 		stmf_free(irp->irp_session);
1423 		irp->irp_session = NULL;
1424 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1425 	}
1426 
1427 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1428 		els->els_resp_size = els->els_req_size;
1429 		p = els->els_resp_payload = (uint8_t *)kmem_zalloc(
1430 		    els->els_resp_size, KM_SLEEP);
1431 		els->els_resp_alloc_size = els->els_resp_size;
1432 		bcopy(els->els_req_payload, p, els->els_resp_size);
1433 		p[0] = ELS_OP_ACC;
1434 		bcopy(p+20, rp->rp_pwwn, 8);
1435 		bcopy(p+28, rp->rp_nwwn, 8);
1436 		bcopy(port->port_pwwn, p+20, 8);
1437 		bcopy(port->port_nwwn, p+28, 8);
1438 		stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
1439 		    rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
1440 	}
1441 
1442 	ret = fct_register_remote_port(port, rp, cmd);
1443 	fct_dequeue_els(irp);
1444 	if ((ret == FCT_SUCCESS) && !(icmd->icmd_flags & ICMD_IMPLICIT)) {
1445 		if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1446 			ret = port->port_send_cmd_response(cmd, 0);
1447 			if ((ret == FCT_SUCCESS) && IPORT_IN_NS_TOPO(iport) &&
1448 			    !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
1449 				fct_cmd_t *ct_cmd = fct_create_solct(port,
1450 				    rp, NS_GSNN_NN, fct_gsnn_cb);
1451 				if (ct_cmd) {
1452 					fct_post_to_solcmd_queue(port, ct_cmd);
1453 				}
1454 				ct_cmd = fct_create_solct(port, rp,
1455 				    NS_GSPN_ID, fct_gspn_cb);
1456 				if (ct_cmd)
1457 					fct_post_to_solcmd_queue(port, ct_cmd);
1458 				ct_cmd = fct_create_solct(port, rp,
1459 				    NS_GCS_ID, fct_gcs_cb);
1460 				if (ct_cmd)
1461 					fct_post_to_solcmd_queue(port, ct_cmd);
1462 				ct_cmd = fct_create_solct(port, rp,
1463 				    NS_GFT_ID, fct_gft_cb);
1464 				if (ct_cmd)
1465 					fct_post_to_solcmd_queue(port, ct_cmd);
1466 			}
1467 		} else {
1468 			/*
1469 			 * The reason we set this flag is to prevent
1470 			 * killing a PRLI while we have not yet processed
1471 			 * a response to PLOGI. Because the initiator
1472 			 * will send a PRLI as soon as it responds to PLOGI.
1473 			 * Check fct_process_els() for more info.
1474 			 */
1475 			atomic_or_32(&irp->irp_flags,
1476 			    IRP_SOL_PLOGI_IN_PROGRESS);
1477 			atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1478 			ret = port->port_send_cmd(cmd);
1479 			if (ret != FCT_SUCCESS) {
1480 				atomic_and_32(&icmd->icmd_flags,
1481 				    ~ICMD_KNOWN_TO_FCA);
1482 				atomic_and_32(&irp->irp_flags,
1483 				    ~IRP_SOL_PLOGI_IN_PROGRESS);
1484 			}
1485 		}
1486 	}
1487 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1488 
1489 	if (ret == FCT_SUCCESS) {
1490 		if (cmd_type == FCT_CMD_RCVD_ELS) {
1491 			atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
1492 			atomic_add_32(&iport->iport_nrps_login, 1);
1493 			if (irp->irp_deregister_timer)
1494 				irp->irp_deregister_timer = 0;
1495 		}
1496 		if (icmd_flags & ICMD_IMPLICIT) {
1497 			p = els->els_resp_payload;
1498 			p[0] = ELS_OP_ACC;
1499 			cmd->cmd_comp_status = FCT_SUCCESS;
1500 			fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE);
1501 		}
1502 	} else {
1503 		fct_queue_cmd_for_termination(cmd, ret);
1504 	}
1505 
1506 	/* Do not touch cmd here as it may have been freed */
1507 
1508 	return (DISC_ACTION_RESCAN);
1509 }
1510 
1511 uint8_t fct_prli_temp[] = { 0x20, 0x10, 0, 0x14, 8, 0, 0x20, 0, 0, 0, 0, 0,
1512 				0, 0, 0, 0 };
1513 
1514 disc_action_t
1515 fct_process_prli(fct_i_cmd_t *icmd)
1516 {
1517 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1518 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1519 	fct_local_port_t	*port  = cmd->cmd_port;
1520 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1521 	    port->port_fct_private;
1522 	fct_els_t		*els   = (fct_els_t *)
1523 	    cmd->cmd_specific;
1524 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1525 	    rp->rp_fct_private;
1526 	stmf_scsi_session_t	*ses   = NULL;
1527 	fct_status_t		 ret;
1528 	clock_t			 end_time;
1529 	char			 info[160];
1530 
1531 	/* We dont support solicited PRLIs yet */
1532 	ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS);
1533 
1534 	if (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS) {
1535 		/*
1536 		 * Dont process the PRLI yet. Let the framework process the
1537 		 * PLOGI completion 1st. This should be very quick because
1538 		 * the reason we got the PRLI is because the initiator
1539 		 * has responded to PLOGI already.
1540 		 */
1541 		/* XXX: Probably need a timeout here */
1542 		return (DISC_ACTION_DELAY_RESCAN);
1543 	}
1544 	/* The caller has made sure that login is done */
1545 
1546 	/* Make sure the process is fcp in this case */
1547 	if ((els->els_req_size != 20) || (bcmp(els->els_req_payload,
1548 	    fct_prli_temp, 16))) {
1549 		if (els->els_req_payload[4] != 0x08)
1550 			stmf_trace(iport->iport_alias, "PRLI received from"
1551 			    " %x for unknown FC-4 type %x", cmd->cmd_rportid,
1552 			    els->els_req_payload[4]);
1553 		else
1554 			stmf_trace(iport->iport_alias, "Rejecting PRLI from %x "
1555 			    " pld sz %d, prli_flags %x", cmd->cmd_rportid,
1556 			    els->els_req_size, els->els_req_payload[6]);
1557 
1558 		fct_dequeue_els(irp);
1559 		atomic_add_16(&irp->irp_sa_elses_count, -1);
1560 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0x2c);
1561 		goto prli_end;
1562 	}
1563 
1564 	if (irp->irp_fcp_xchg_count) {
1565 		/* Trigger cleanup if necessary */
1566 		if ((irp->irp_flags & IRP_FCP_CLEANUP) == 0) {
1567 			stmf_trace(iport->iport_alias, "handling PRLI from"
1568 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1569 			if (fct_trigger_rport_cleanup(irp, FCT_CMD_FCP_XCHG)) {
1570 				atomic_or_32(&irp->irp_flags, IRP_FCP_CLEANUP);
1571 			} else {
1572 				/* XXX: handle this */
1573 				/* EMPTY */
1574 			}
1575 		}
1576 
1577 		end_time = icmd->icmd_start_time +
1578 		    drv_usectohz(USEC_ELS_TIMEOUT);
1579 		if (ddi_get_lbolt() > end_time) {
1580 			(void) snprintf(info, 160,
1581 			    "fct_process_prli: unable to clean "
1582 			    "up I/O. iport-%p, icmd-%p", (void *)iport,
1583 			    (void *)icmd);
1584 			info[159] = 0;
1585 			(void) fct_port_shutdown(iport->iport_port,
1586 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1587 
1588 			return (DISC_ACTION_DELAY_RESCAN);
1589 		}
1590 
1591 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1592 			stmf_trace(iport->iport_alias, "handling"
1593 			    " PRLI from %x, waiting for cmds to"
1594 			    " drain", cmd->cmd_rportid);
1595 		}
1596 		return (DISC_ACTION_DELAY_RESCAN);
1597 	}
1598 	atomic_and_32(&irp->irp_flags, ~IRP_FCP_CLEANUP);
1599 
1600 	/* Session can only be terminated after all the I/Os have drained */
1601 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1602 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1603 		    irp->irp_session);
1604 		stmf_free(irp->irp_session);
1605 		irp->irp_session = NULL;
1606 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1607 	}
1608 
1609 	/* All good, lets start a session */
1610 	ses = (stmf_scsi_session_t *)stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0, 0);
1611 	if (ses) {
1612 		ses->ss_port_private = irp;
1613 		ses->ss_rport_id = (scsi_devid_desc_t *)irp->irp_id;
1614 		ses->ss_lport = port->port_lport;
1615 		if (stmf_register_scsi_session(port->port_lport, ses) !=
1616 		    STMF_SUCCESS) {
1617 			stmf_free(ses);
1618 			ses = NULL;
1619 		} else {
1620 			irp->irp_session = ses;
1621 			irp->irp_session->ss_rport_alias = irp->irp_snn;
1622 
1623 			/*
1624 			 * The reason IRP_SCSI_SESSION_STARTED is different
1625 			 * from IRP_PRLI_DONE is that we clear IRP_PRLI_DONE
1626 			 * inside interrupt context. We dont want to deregister
1627 			 * the session from an interrupt.
1628 			 */
1629 			atomic_or_32(&irp->irp_flags, IRP_SCSI_SESSION_STARTED);
1630 		}
1631 	}
1632 
1633 	fct_dequeue_els(irp);
1634 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1635 	if (ses == NULL) {
1636 		/* fail PRLI */
1637 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0);
1638 	} else {
1639 		/* accept PRLI */
1640 		els->els_resp_payload = (uint8_t *)kmem_zalloc(20, KM_SLEEP);
1641 		bcopy(fct_prli_temp, els->els_resp_payload, 20);
1642 		els->els_resp_payload[0] = 2;
1643 		els->els_resp_payload[6] = 0x21;
1644 
1645 		/* XXX the two bytes below need to set as per capabilities */
1646 		els->els_resp_payload[18] = 0;
1647 		els->els_resp_payload[19] = 0x12;
1648 
1649 		els->els_resp_size = els->els_resp_alloc_size = 20;
1650 		if ((ret = port->port_send_cmd_response(cmd, 0)) !=
1651 		    FCT_SUCCESS) {
1652 			stmf_deregister_scsi_session(port->port_lport, ses);
1653 			stmf_free(irp->irp_session);
1654 			irp->irp_session = NULL;
1655 			atomic_and_32(&irp->irp_flags,
1656 			    ~IRP_SCSI_SESSION_STARTED);
1657 		} else {
1658 			/* Mark that PRLI is done */
1659 			atomic_or_32(&irp->irp_flags, IRP_PRLI_DONE);
1660 		}
1661 	}
1662 
1663 prli_end:;
1664 	if (ret != FCT_SUCCESS)
1665 		fct_queue_cmd_for_termination(cmd, ret);
1666 
1667 	return (DISC_ACTION_RESCAN);
1668 }
1669 
1670 disc_action_t
1671 fct_process_logo(fct_i_cmd_t *icmd)
1672 {
1673 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1674 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1675 	fct_local_port_t	*port  = cmd->cmd_port;
1676 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1677 	    port->port_fct_private;
1678 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1679 	    rp->rp_fct_private;
1680 	fct_status_t		 ret;
1681 	char			 info[160];
1682 	clock_t			 end_time;
1683 
1684 	/* Drain I/Os */
1685 	if ((irp->irp_nonfcp_xchg_count + irp->irp_fcp_xchg_count) > 1) {
1686 		/* Trigger cleanup if necessary */
1687 		if ((irp->irp_flags & IRP_SESSION_CLEANUP) == 0) {
1688 			stmf_trace(iport->iport_alias, "handling LOGO rp_id"
1689 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1690 			/* Cleanup everything except elses */
1691 			if (fct_trigger_rport_cleanup(irp, ~(cmd->cmd_type))) {
1692 				atomic_or_32(&irp->irp_flags,
1693 				    IRP_SESSION_CLEANUP);
1694 			} else {
1695 				/* XXX: need more handling */
1696 				return (DISC_ACTION_DELAY_RESCAN);
1697 			}
1698 		}
1699 
1700 		end_time = icmd->icmd_start_time +
1701 		    drv_usectohz(USEC_ELS_TIMEOUT);
1702 		if (ddi_get_lbolt() > end_time) {
1703 			(void) snprintf(info, 160,
1704 			    "fct_process_logo: unable to clean "
1705 			    "up I/O. iport-%p, icmd-%p", (void *)iport,
1706 			    (void *)icmd);
1707 			info[159] = 0;
1708 			(void) fct_port_shutdown(iport->iport_port,
1709 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1710 
1711 			return (DISC_ACTION_DELAY_RESCAN);
1712 		}
1713 
1714 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1715 			stmf_trace(iport->iport_alias, "handling"
1716 			    " LOGO rp_id %x, waiting for cmds to"
1717 			    " drain", cmd->cmd_rportid);
1718 		}
1719 		return (DISC_ACTION_DELAY_RESCAN);
1720 	}
1721 	atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1722 
1723 	/* Session can only be terminated after all the I/Os have drained */
1724 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1725 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1726 		    irp->irp_session);
1727 		stmf_free(irp->irp_session);
1728 		irp->irp_session = NULL;
1729 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1730 	}
1731 
1732 	fct_dequeue_els(irp);
1733 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1734 
1735 	/* don't send response if this is an implicit logout cmd */
1736 	if (!(icmd->icmd_flags & ICMD_IMPLICIT)) {
1737 		if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1738 			ret = fct_send_accrjt(cmd, ELS_OP_ACC, 0, 0);
1739 		} else {
1740 			atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
1741 			ret = port->port_send_cmd(cmd);
1742 			if (ret != FCT_SUCCESS) {
1743 				atomic_and_32(&icmd->icmd_flags,
1744 				    ~ICMD_KNOWN_TO_FCA);
1745 			}
1746 		}
1747 
1748 		if (ret != FCT_SUCCESS) {
1749 			fct_queue_cmd_for_termination(cmd, ret);
1750 		}
1751 	} else {
1752 		fct_cmd_free(cmd);
1753 	}
1754 
1755 	irp->irp_deregister_timer = ddi_get_lbolt() +
1756 	    drv_usectohz(USEC_DEREG_RP_TIMEOUT);
1757 	irp->irp_dereg_count = 0;
1758 
1759 	/* Do not touch cmd here as it may have been freed */
1760 
1761 	ASSERT(irp->irp_flags & IRP_IN_DISCOVERY_QUEUE);
1762 
1763 	return (DISC_ACTION_RESCAN);
1764 }
1765 
1766 disc_action_t
1767 fct_process_prlo(fct_i_cmd_t *icmd)
1768 {
1769 	fct_cmd_t		*cmd   = icmd->icmd_cmd;
1770 	fct_remote_port_t	*rp    = cmd->cmd_rp;
1771 	fct_local_port_t	*port  = cmd->cmd_port;
1772 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1773 	    port->port_fct_private;
1774 	fct_i_remote_port_t	*irp   = (fct_i_remote_port_t *)
1775 	    rp->rp_fct_private;
1776 	fct_status_t		 ret;
1777 	clock_t			 end_time;
1778 	char			 info[160];
1779 
1780 	/* We do not support solicited PRLOs yet */
1781 	ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS);
1782 
1783 	/* Drain I/Os */
1784 	if (irp->irp_fcp_xchg_count) {
1785 		/* Trigger cleanup if necessary */
1786 		if ((irp->irp_flags & IRP_FCP_CLEANUP) == 0) {
1787 			stmf_trace(iport->iport_alias, "handling LOGO from"
1788 			    " %x. Triggering cleanup", cmd->cmd_rportid);
1789 			/* Cleanup everything except elses */
1790 			if (fct_trigger_rport_cleanup(irp, FCT_CMD_FCP_XCHG)) {
1791 				atomic_or_32(&irp->irp_flags,
1792 				    IRP_FCP_CLEANUP);
1793 			} else {
1794 				/* XXX: need more handling */
1795 				return (DISC_ACTION_DELAY_RESCAN);
1796 			}
1797 		}
1798 
1799 		end_time = icmd->icmd_start_time +
1800 		    drv_usectohz(USEC_ELS_TIMEOUT);
1801 		if (ddi_get_lbolt() > end_time) {
1802 			(void) snprintf(info, 160,
1803 			    "fct_process_prlo: unable to "
1804 			    "clean up I/O. iport-%p, icmd-%p", (void *)iport,
1805 			    (void *)icmd);
1806 			info[159] = 0;
1807 			(void) fct_port_shutdown(iport->iport_port,
1808 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1809 
1810 			return (DISC_ACTION_DELAY_RESCAN);
1811 		}
1812 
1813 		if ((ddi_get_lbolt() & 0x7f) == 0) {
1814 			stmf_trace(iport->iport_alias, "handling"
1815 			    " PRLO from %x, waiting for cmds to"
1816 			    " drain", cmd->cmd_rportid);
1817 		}
1818 		return (DISC_ACTION_DELAY_RESCAN);
1819 	}
1820 	atomic_and_32(&irp->irp_flags, ~IRP_FCP_CLEANUP);
1821 
1822 	/* Session can only be terminated after all the I/Os have drained */
1823 	if (irp->irp_flags & IRP_SCSI_SESSION_STARTED) {
1824 		stmf_deregister_scsi_session(iport->iport_port->port_lport,
1825 		    irp->irp_session);
1826 		stmf_free(irp->irp_session);
1827 		irp->irp_session = NULL;
1828 		atomic_and_32(&irp->irp_flags, ~IRP_SCSI_SESSION_STARTED);
1829 	}
1830 
1831 	fct_dequeue_els(irp);
1832 	atomic_add_16(&irp->irp_sa_elses_count, -1);
1833 	ret = fct_send_accrjt(cmd, ELS_OP_ACC, 0, 0);
1834 	if (ret != FCT_SUCCESS)
1835 		fct_queue_cmd_for_termination(cmd, ret);
1836 
1837 	return (DISC_ACTION_RESCAN);
1838 }
1839 
1840 disc_action_t
1841 fct_process_rcvd_adisc(fct_i_cmd_t *icmd)
1842 {
1843 	fct_cmd_t		*cmd = icmd->icmd_cmd;
1844 	fct_remote_port_t	*rp = cmd->cmd_rp;
1845 	fct_local_port_t	*port = cmd->cmd_port;
1846 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1847 	    port->port_fct_private;
1848 	fct_els_t		*els = (fct_els_t *)
1849 	    cmd->cmd_specific;
1850 	fct_i_remote_port_t	*irp = (fct_i_remote_port_t *)
1851 	    rp->rp_fct_private;
1852 	uint8_t			*p;
1853 	uint32_t		*q;
1854 	fct_status_t		ret;
1855 
1856 	fct_dequeue_els(irp);
1857 	atomic_add_16(&irp->irp_nsa_elses_count, -1);
1858 
1859 	/* Validate the adisc request */
1860 	p = els->els_req_payload;
1861 	q = (uint32_t *)p;
1862 	if ((els->els_req_size != 28) || (bcmp(rp->rp_pwwn, p + 8, 8)) ||
1863 	    (bcmp(rp->rp_nwwn, p + 16, 8))) {
1864 		ret = fct_send_accrjt(cmd, ELS_OP_LSRJT, 3, 0);
1865 	} else {
1866 		rp->rp_hard_address = BE_32(q[1]);
1867 		els->els_resp_size = els->els_resp_alloc_size = 28;
1868 		els->els_resp_payload = (uint8_t *)kmem_zalloc(28, KM_SLEEP);
1869 		bcopy(p, els->els_resp_payload, 28);
1870 		p = els->els_resp_payload;
1871 		q = (uint32_t *)p;
1872 		p[0] = ELS_OP_ACC;
1873 		q[1] = BE_32(port->port_hard_address);
1874 		bcopy(port->port_pwwn, p + 8, 8);
1875 		bcopy(port->port_nwwn, p + 16, 8);
1876 		q[6] = BE_32(iport->iport_link_info.portid);
1877 		ret = port->port_send_cmd_response(cmd, 0);
1878 	}
1879 	if (ret != FCT_SUCCESS) {
1880 		fct_queue_cmd_for_termination(cmd, ret);
1881 	}
1882 
1883 	return (DISC_ACTION_RESCAN);
1884 }
1885 
1886 disc_action_t
1887 fct_process_unknown_els(fct_i_cmd_t *icmd)
1888 {
1889 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
1890 	fct_status_t		 ret   = FCT_FAILURE;
1891 	uint8_t			 op    = 0;
1892 
1893 	ASSERT(icmd->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS);
1894 	fct_dequeue_els(ICMD_TO_IRP(icmd));
1895 	atomic_add_16(&ICMD_TO_IRP(icmd)->irp_nsa_elses_count, -1);
1896 	op = ICMD_TO_ELS(icmd)->els_req_payload[0];
1897 	stmf_trace(iport->iport_alias, "Rejecting unknown unsol els %x (%s)",
1898 	    op, FCT_ELS_NAME(op));
1899 	ret = fct_send_accrjt(icmd->icmd_cmd, ELS_OP_LSRJT, 1, 0);
1900 	if (ret != FCT_SUCCESS) {
1901 		fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
1902 	}
1903 
1904 	return (DISC_ACTION_RESCAN);
1905 }
1906 
1907 disc_action_t
1908 fct_process_rscn(fct_i_cmd_t *icmd)
1909 {
1910 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
1911 	fct_status_t		 ret   = FCT_FAILURE;
1912 	uint8_t			 op    = 0;
1913 	uint8_t			*rscn_req_payload;
1914 	uint32_t		 rscn_req_size;
1915 
1916 	fct_dequeue_els(ICMD_TO_IRP(icmd));
1917 	atomic_add_16(&ICMD_TO_IRP(icmd)->irp_nsa_elses_count, -1);
1918 	if (icmd->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1919 		op = ICMD_TO_ELS(icmd)->els_req_payload[0];
1920 		stmf_trace(iport->iport_alias, "Accepting RSCN %x (%s)",
1921 		    op, FCT_ELS_NAME(op));
1922 		rscn_req_size = ICMD_TO_ELS(icmd)->els_req_size;
1923 		rscn_req_payload = kmem_alloc(rscn_req_size, KM_SLEEP);
1924 		bcopy(ICMD_TO_ELS(icmd)->els_req_payload, rscn_req_payload,
1925 		    rscn_req_size);
1926 		ret = fct_send_accrjt(icmd->icmd_cmd, ELS_OP_ACC, 1, 0);
1927 		if (ret != FCT_SUCCESS) {
1928 			fct_queue_cmd_for_termination(icmd->icmd_cmd, ret);
1929 		} else {
1930 			if (fct_rscn_options & RSCN_OPTION_VERIFY) {
1931 				fct_rscn_verify(iport, rscn_req_payload,
1932 				    rscn_req_size);
1933 			}
1934 		}
1935 
1936 		kmem_free(rscn_req_payload, rscn_req_size);
1937 	} else {
1938 		ASSERT(0);
1939 	}
1940 
1941 	return (DISC_ACTION_RESCAN);
1942 }
1943 
1944 disc_action_t
1945 fct_process_els(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1946 {
1947 	fct_i_cmd_t	*cmd_to_abort = NULL;
1948 	fct_i_cmd_t	**ppcmd, *icmd;
1949 	fct_cmd_t	*cmd;
1950 	fct_els_t	*els;
1951 	int		dq;
1952 	disc_action_t	ret = DISC_ACTION_NO_WORK;
1953 	uint8_t		op;
1954 
1955 	mutex_exit(&iport->iport_worker_lock);
1956 
1957 	/*
1958 	 * Do some cleanup based on the following.
1959 	 * - We can only have one session affecting els pending.
1960 	 * - If any session affecting els is pending no other els is allowed.
1961 	 * - If PLOGI is not done, nothing except PLOGI or LOGO is allowed.
1962 	 * NOTE: If port is down the cleanup is done outside of this
1963 	 *	function.
1964 	 * NOTE: There is a side effect, if a sa ELS (non PLOGI) is received
1965 	 * while a PLOGI is pending, it will kill itself and the PLOGI.
1966 	 * which is probably ok.
1967 	 */
1968 	rw_enter(&irp->irp_lock, RW_WRITER);
1969 	ppcmd = &irp->irp_els_list;
1970 	while ((*ppcmd) != NULL) {
1971 		int special_prli_cond = 0;
1972 		dq = 0;
1973 
1974 		els = (fct_els_t *)((*ppcmd)->icmd_cmd)->cmd_specific;
1975 
1976 		if (((*ppcmd)->icmd_cmd->cmd_type == FCT_CMD_RCVD_ELS) &&
1977 		    (els->els_req_payload[0] == ELS_OP_PRLI) &&
1978 		    (irp->irp_flags & IRP_SOL_PLOGI_IN_PROGRESS)) {
1979 			/*
1980 			 * The initiator sent a PRLI right after responding
1981 			 * to PLOGI and we have not yet finished processing
1982 			 * the PLOGI completion. We should not kill the PRLI
1983 			 * as the initiator may not retry it.
1984 			 */
1985 			special_prli_cond = 1;
1986 		}
1987 
1988 		if ((*ppcmd)->icmd_flags & ICMD_BEING_ABORTED) {
1989 			dq = 1;
1990 		} else if (irp->irp_sa_elses_count > 1) {
1991 			dq = 1;
1992 			/* This els might have set the CLEANUP flag */
1993 			atomic_and_32(&irp->irp_flags, ~IRP_SESSION_CLEANUP);
1994 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 1",
1995 			    els->els_req_payload[0]);
1996 		} else if (irp->irp_sa_elses_count &&
1997 		    (((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING) == 0)) {
1998 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 2",
1999 			    els->els_req_payload[0]);
2000 			dq = 1;
2001 		} else if (((irp->irp_flags & IRP_PLOGI_DONE) == 0) &&
2002 		    (els->els_req_payload[0] != ELS_OP_PLOGI) &&
2003 		    (els->els_req_payload[0] != ELS_OP_LOGO) &&
2004 		    (special_prli_cond == 0)) {
2005 			stmf_trace(iport->iport_alias, "Killing ELS %x cond 3",
2006 			    els->els_req_payload[0]);
2007 			dq = 1;
2008 		}
2009 
2010 		if (dq) {
2011 			fct_i_cmd_t *c = (*ppcmd)->icmd_next;
2012 
2013 			if ((*ppcmd)->icmd_flags & ICMD_SESSION_AFFECTING)
2014 				atomic_add_16(&irp->irp_sa_elses_count, -1);
2015 			else
2016 				atomic_add_16(&irp->irp_nsa_elses_count, -1);
2017 			(*ppcmd)->icmd_next = cmd_to_abort;
2018 			cmd_to_abort = *ppcmd;
2019 			*ppcmd = c;
2020 		} else {
2021 			ppcmd = &((*ppcmd)->icmd_next);
2022 		}
2023 	}
2024 	rw_exit(&irp->irp_lock);
2025 
2026 	while (cmd_to_abort) {
2027 		fct_i_cmd_t *c = cmd_to_abort->icmd_next;
2028 
2029 		atomic_and_32(&cmd_to_abort->icmd_flags, ~ICMD_IN_IRP_QUEUE);
2030 		fct_queue_cmd_for_termination(cmd_to_abort->icmd_cmd,
2031 		    FCT_ABORTED);
2032 		cmd_to_abort = c;
2033 	}
2034 
2035 	/*
2036 	 * pick from the top of the queue
2037 	 */
2038 	icmd = irp->irp_els_list;
2039 	if (icmd == NULL) {
2040 		/*
2041 		 * The cleanup took care of everything.
2042 		 */
2043 
2044 		mutex_enter(&iport->iport_worker_lock);
2045 		return (DISC_ACTION_RESCAN);
2046 	}
2047 
2048 	cmd = icmd->icmd_cmd;
2049 	els = ICMD_TO_ELS(icmd);
2050 	op = els->els_req_payload[0];
2051 	if ((icmd->icmd_flags & ICMD_ELS_PROCESSING_STARTED) == 0) {
2052 		stmf_trace(iport->iport_alias, "Processing %ssol ELS %x (%s) "
2053 		    "rp_id=%x", (cmd->cmd_type == FCT_CMD_RCVD_ELS) ? "un" : "",
2054 		    op, FCT_ELS_NAME(op), cmd->cmd_rportid);
2055 		atomic_or_32(&icmd->icmd_flags, ICMD_ELS_PROCESSING_STARTED);
2056 	}
2057 
2058 	if (op == ELS_OP_PLOGI) {
2059 		ret |= fct_process_plogi(icmd);
2060 	} else if (op == ELS_OP_PRLI) {
2061 		ret |= fct_process_prli(icmd);
2062 	} else if (op == ELS_OP_LOGO) {
2063 		ret |= fct_process_logo(icmd);
2064 	} else if ((op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
2065 		ret |= fct_process_prlo(icmd);
2066 	} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
2067 		fct_status_t s;
2068 		fct_local_port_t *port = iport->iport_port;
2069 
2070 		fct_dequeue_els(irp);
2071 		atomic_add_16(&irp->irp_nsa_elses_count, -1);
2072 		atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
2073 		if ((s = port->port_send_cmd(cmd)) != FCT_SUCCESS) {
2074 			atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2075 			fct_queue_cmd_for_termination(cmd, s);
2076 			stmf_trace(iport->iport_alias, "Solicited els "
2077 			    "transport failed, ret = %llx", s);
2078 		}
2079 	} else if (op == ELS_OP_ADISC) {
2080 		ret |= fct_process_rcvd_adisc(icmd);
2081 	} else if (op == ELS_OP_RSCN) {
2082 		(void) fct_process_rscn(icmd);
2083 	} else {
2084 		(void) fct_process_unknown_els(icmd);
2085 	}
2086 
2087 	/*
2088 	 * This if condition will be false if a sa ELS trigged a cleanup
2089 	 * and set the ret = DISC_ACTION_DELAY_RESCAN. In that case we should
2090 	 * keep it that way.
2091 	 */
2092 	if (ret == DISC_ACTION_NO_WORK) {
2093 		/*
2094 		 * Since we dropped the lock, we will force a rescan. The
2095 		 * only exception is if someone returned
2096 		 * DISC_ACTION_DELAY_RESCAN, in which case that should be the
2097 		 * return value.
2098 		 */
2099 		ret = DISC_ACTION_RESCAN;
2100 	}
2101 
2102 	mutex_enter(&iport->iport_worker_lock);
2103 	return (ret);
2104 }
2105 
2106 void
2107 fct_handle_sol_els_completion(fct_i_local_port_t *iport, fct_i_cmd_t *icmd)
2108 {
2109 	fct_i_remote_port_t	*irp = NULL;
2110 	fct_els_t		*els = ICMD_TO_ELS(icmd);
2111 	uint8_t			 op  = els->els_req_payload[0];
2112 
2113 	if (icmd->icmd_cmd->cmd_rp) {
2114 		irp = ICMD_TO_IRP(icmd);
2115 	}
2116 	if (icmd->icmd_cmd->cmd_rp &&
2117 	    (icmd->icmd_cmd->cmd_comp_status == FCT_SUCCESS) &&
2118 	    (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2119 		bcopy(els->els_resp_payload + 20, irp->irp_rp->rp_pwwn, 8);
2120 		bcopy(els->els_resp_payload + 28, irp->irp_rp->rp_nwwn, 8);
2121 
2122 		stmf_wwn_to_devid_desc((scsi_devid_desc_t *)irp->irp_id,
2123 		    irp->irp_rp->rp_pwwn, PROTOCOL_FIBRE_CHANNEL);
2124 		atomic_or_32(&irp->irp_flags, IRP_PLOGI_DONE);
2125 		atomic_add_32(&iport->iport_nrps_login, 1);
2126 		if (irp->irp_deregister_timer) {
2127 			irp->irp_deregister_timer = 0;
2128 			irp->irp_dereg_count = 0;
2129 		}
2130 	}
2131 
2132 	if (irp && (els->els_req_payload[0] == ELS_OP_PLOGI)) {
2133 		atomic_and_32(&irp->irp_flags, ~IRP_SOL_PLOGI_IN_PROGRESS);
2134 	}
2135 	atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2136 	stmf_trace(iport->iport_alias, "Sol ELS %x (%s) completed with "
2137 	    "status %llx, did/%x", op, FCT_ELS_NAME(op),
2138 	    icmd->icmd_cmd->cmd_comp_status, icmd->icmd_cmd->cmd_rportid);
2139 }
2140 
2141 static disc_action_t
2142 fct_check_cmdlist(fct_i_local_port_t *iport)
2143 {
2144 	int		num_to_release, ndx;
2145 	fct_i_cmd_t	*icmd;
2146 	uint32_t	total, max_active;
2147 
2148 	ASSERT(MUTEX_HELD(&iport->iport_worker_lock));
2149 
2150 	total = iport->iport_total_alloced_ncmds;
2151 	max_active = iport->iport_max_active_ncmds;
2152 
2153 	if (total <= max_active)
2154 		return (DISC_ACTION_NO_WORK);
2155 	/*
2156 	 * Everytime, we release half of the difference
2157 	 */
2158 	num_to_release = (total + 1 - max_active) / 2;
2159 
2160 	mutex_exit(&iport->iport_worker_lock);
2161 	for (ndx = 0; ndx < num_to_release; ndx++) {
2162 		mutex_enter(&iport->iport_cached_cmd_lock);
2163 		icmd = iport->iport_cached_cmdlist;
2164 		if (icmd == NULL) {
2165 			mutex_exit(&iport->iport_cached_cmd_lock);
2166 			break;
2167 		}
2168 		iport->iport_cached_cmdlist = icmd->icmd_next;
2169 		iport->iport_cached_ncmds--;
2170 		mutex_exit(&iport->iport_cached_cmd_lock);
2171 		atomic_add_32(&iport->iport_total_alloced_ncmds, -1);
2172 		fct_free(icmd->icmd_cmd);
2173 	}
2174 	mutex_enter(&iport->iport_worker_lock);
2175 	return (DISC_ACTION_RESCAN);
2176 }
2177 
2178 /*
2179  * The efficiency of handling solicited commands is very low here. But
2180  * fortunately, we seldom send solicited commands. So it will not hurt
2181  * the system performance much.
2182  */
2183 static disc_action_t
2184 fct_check_solcmd_queue(fct_i_local_port_t *iport)
2185 {
2186 	fct_i_cmd_t	*icmd	    = NULL;
2187 	fct_i_cmd_t	*prev_icmd  = NULL;
2188 	fct_i_cmd_t	*next_icmd  = NULL;
2189 
2190 	ASSERT(mutex_owned(&iport->iport_worker_lock));
2191 	for (icmd = iport->iport_solcmd_queue; icmd; icmd = next_icmd) {
2192 		ASSERT(icmd->icmd_flags | ICMD_IN_SOLCMD_QUEUE);
2193 		next_icmd = icmd->icmd_solcmd_next;
2194 		if (icmd->icmd_flags & ICMD_SOLCMD_NEW) {
2195 			/*
2196 			 * This solicited cmd is new.
2197 			 * Dispatch ELSes to discovery queue to make use of
2198 			 * existent framework.
2199 			 */
2200 			icmd->icmd_flags &= ~ICMD_SOLCMD_NEW;
2201 			mutex_exit(&iport->iport_worker_lock);
2202 
2203 			if (icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_ELS) {
2204 				fct_handle_els(icmd->icmd_cmd);
2205 			} else {
2206 				fct_handle_solct(icmd->icmd_cmd);
2207 			}
2208 
2209 			mutex_enter(&iport->iport_worker_lock);
2210 		} else if (icmd->icmd_flags & ICMD_CMD_COMPLETE) {
2211 			/*
2212 			 * To make fct_check_solcmd simple and flexible,
2213 			 * We need only call callback to finish post-handling.
2214 			 */
2215 			if (icmd->icmd_cb) {
2216 				/*
2217 				 * mutex ???
2218 				 */
2219 				icmd->icmd_cb(icmd);
2220 			}
2221 
2222 
2223 			/*
2224 			 * Release resources for this solicited cmd
2225 			 */
2226 			if (iport->iport_solcmd_queue == icmd) {
2227 				iport->iport_solcmd_queue = next_icmd;
2228 			} else {
2229 				prev_icmd = iport->iport_solcmd_queue;
2230 				while (prev_icmd->icmd_solcmd_next != icmd) {
2231 					prev_icmd = prev_icmd->icmd_solcmd_next;
2232 				}
2233 				prev_icmd->icmd_solcmd_next = next_icmd;
2234 			}
2235 
2236 			icmd->icmd_cb = NULL;
2237 			mutex_exit(&iport->iport_worker_lock);
2238 			fct_cmd_free(icmd->icmd_cmd);
2239 			mutex_enter(&iport->iport_worker_lock);
2240 		} else {
2241 			/*
2242 			 * This solicited cmd is still ongoing.
2243 			 * We need check if it's time to abort this cmd
2244 			 */
2245 			if (((icmd->icmd_start_time + drv_usectohz(
2246 			    USEC_SOL_TIMEOUT)) < ddi_get_lbolt()) &&
2247 			    !(icmd->icmd_flags & ICMD_BEING_ABORTED)) {
2248 				fct_q_for_termination_lock_held(iport,
2249 				    icmd, FCT_ABORTED);
2250 			}
2251 		}
2252 	}
2253 
2254 	return (DISC_ACTION_DELAY_RESCAN);
2255 }
2256 
2257 void
2258 fct_handle_solct(fct_cmd_t *cmd)
2259 {
2260 	fct_status_t		 ret	  = FCT_SUCCESS;
2261 	fct_i_cmd_t		*icmd	  = CMD_TO_ICMD(cmd);
2262 	fct_i_local_port_t	*iport	  = ICMD_TO_IPORT(icmd);
2263 	fct_i_remote_port_t	*irp	  = ICMD_TO_IRP(icmd);
2264 
2265 	ASSERT(cmd->cmd_type == FCT_CMD_SOL_CT);
2266 	rw_enter(&iport->iport_lock, RW_READER);
2267 	/*
2268 	 * Let's make sure local port is sane
2269 	 */
2270 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2271 		rw_exit(&iport->iport_lock);
2272 
2273 		stmf_trace(iport->iport_alias, "fct_transport_solct: "
2274 		    "solcmd-%p transport failed, becasue port state was %x",
2275 		    cmd, iport->iport_link_state);
2276 		fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2277 		return;
2278 	}
2279 
2280 	/*
2281 	 * Let's make sure we have plogi-ed to name server
2282 	 */
2283 	rw_enter(&irp->irp_lock, RW_READER);
2284 	if (!(irp->irp_flags & IRP_PLOGI_DONE)) {
2285 		rw_exit(&irp->irp_lock);
2286 		rw_exit(&iport->iport_lock);
2287 
2288 		stmf_trace(iport->iport_alias, "fct_transport_solct: "
2289 		    "Must login to name server first - cmd-%p", cmd);
2290 		fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
2291 		return;
2292 	}
2293 
2294 	/*
2295 	 * Let's get a slot for this solcmd
2296 	 */
2297 	if (fct_alloc_cmd_slot(iport, cmd) == FCT_SLOT_EOL) {
2298 		rw_exit(&irp->irp_lock);
2299 		rw_exit(&iport->iport_lock);
2300 
2301 		stmf_trace(iport->iport_alias, "fct_transport_solcmd: "
2302 		    "ran out of xchg resources - cmd-%p", cmd);
2303 		fct_queue_cmd_for_termination(cmd, FCT_NO_XCHG_RESOURCE);
2304 		return;
2305 	}
2306 
2307 	if (fct_netbuf_to_value(ICMD_TO_CT(icmd)->ct_req_payload + 8, 2) ==
2308 	    NS_GID_PN) {
2309 		fct_i_remote_port_t	*query_irp = NULL;
2310 
2311 		query_irp = fct_lookup_irp_by_portwwn(iport,
2312 		    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2313 		if (query_irp) {
2314 			atomic_and_32(&query_irp->irp_flags, ~IRP_RSCN_QUEUED);
2315 		}
2316 	}
2317 	rw_exit(&irp->irp_lock);
2318 	rw_exit(&iport->iport_lock);
2319 
2320 	atomic_add_16(&irp->irp_nonfcp_xchg_count, 1);
2321 	atomic_or_32(&icmd->icmd_flags, ICMD_KNOWN_TO_FCA);
2322 	icmd->icmd_start_time = ddi_get_lbolt();
2323 	ret = iport->iport_port->port_send_cmd(cmd);
2324 	if (ret != FCT_SUCCESS) {
2325 		atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2326 		fct_queue_cmd_for_termination(cmd, ret);
2327 	}
2328 }
2329 
2330 void
2331 fct_logo_cb(fct_i_cmd_t *icmd)
2332 {
2333 	ASSERT(!(icmd->icmd_flags & ICMD_IMPLICIT));
2334 	if (!FCT_IS_ELS_ACC(icmd)) {
2335 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_logo_cb: "
2336 		    "solicited LOGO is not accepted - icmd/%p", icmd);
2337 	}
2338 }
2339 
2340 void
2341 fct_gsnn_cb(fct_i_cmd_t *icmd)
2342 {
2343 	int			 snlen	   = 0;
2344 	char			*sn	   = NULL;
2345 	fct_i_remote_port_t	*query_irp = NULL;
2346 
2347 	if (!FCT_IS_CT_ACC(icmd)) {
2348 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2349 		    "GSNN is not accepted by NS - icmd/%p", icmd);
2350 		return;
2351 	}
2352 	mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2353 
2354 	rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2355 	mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2356 	query_irp = fct_lookup_irp_by_nodewwn(ICMD_TO_IPORT(icmd),
2357 	    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2358 
2359 	if (!query_irp) {
2360 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2361 		    "can't get rp icmd-%p", icmd);
2362 		goto exit_gsnn_cb;
2363 	} else {
2364 		snlen = ICMD_TO_CT(icmd)->ct_resp_payload[16];
2365 	}
2366 
2367 	if (query_irp && snlen) {
2368 		/*
2369 		 * Release previous resource, then allocate needed resource
2370 		 */
2371 		sn = query_irp->irp_snn;
2372 		if (sn) {
2373 			kmem_free(sn, strlen(sn) + 1);
2374 		}
2375 
2376 		query_irp->irp_snn = NULL;
2377 		sn = kmem_zalloc(snlen + 1, KM_SLEEP);
2378 		(void) strncpy(sn, (char *)
2379 		    ICMD_TO_CT(icmd)->ct_resp_payload + 17, snlen);
2380 		if (strlen(sn) != snlen) {
2381 			stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2382 			    "fct_gsnn_cb: %s, but len=%d", sn, snlen);
2383 			kmem_free(sn, snlen + 1);
2384 			sn = NULL;
2385 		}
2386 
2387 		/*
2388 		 * Update symbolic node name
2389 		 */
2390 		query_irp->irp_snn = sn;
2391 		if ((query_irp->irp_flags & IRP_SCSI_SESSION_STARTED) &&
2392 		    (query_irp->irp_session)) {
2393 			query_irp->irp_session->ss_rport_alias =
2394 			    query_irp->irp_snn;
2395 		}
2396 	} else {
2397 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gsnn_cb: "
2398 		    "irp/%p, snlen/%d", query_irp, snlen);
2399 	}
2400 
2401 exit_gsnn_cb:
2402 	rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2403 }
2404 
2405 void
2406 fct_link_init_cb(fct_i_cmd_t *icmd)
2407 {
2408 	fct_i_local_port_t	*iport = ICMD_TO_IPORT(icmd);
2409 
2410 	iport->iport_li_state &= ~LI_STATE_FLAG_CMD_WAITING;
2411 	if (icmd->icmd_cmd->cmd_comp_status != FCT_SUCCESS) {
2412 		stmf_trace(iport->iport_alias, "fct_link_init_cb: ELS-%x failed"
2413 		    "comp_status- %llx", ICMD_TO_ELS(icmd)->els_req_payload[0],
2414 		    icmd->icmd_cmd->cmd_comp_status);
2415 		iport->iport_li_comp_status = icmd->icmd_cmd->cmd_comp_status;
2416 	} else if (icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_ELS) {
2417 		if (!FCT_IS_ELS_ACC(icmd)) {
2418 			stmf_trace(iport->iport_alias,
2419 			    "fct_link_init_cb: ELS-%x is rejected",
2420 			    ICMD_TO_ELS(icmd)->els_req_payload[0]);
2421 			iport->iport_li_comp_status = FCT_REJECT_STATUS(
2422 			    ICMD_TO_ELS(icmd)->els_resp_payload[1],
2423 			    ICMD_TO_ELS(icmd)->els_resp_payload[2]);
2424 		} else {
2425 			iport->iport_li_comp_status = FCT_SUCCESS;
2426 		}
2427 	} else {
2428 		ASSERT(icmd->icmd_cmd->cmd_type == FCT_CMD_SOL_CT);
2429 		if (!FCT_IS_CT_ACC(icmd)) {
2430 			stmf_trace(iport->iport_alias,
2431 			    "fct_link_init_cb: CT-%02x%02x is rejected",
2432 			    ICMD_TO_CT(icmd)->ct_req_payload[8],
2433 			    ICMD_TO_CT(icmd)->ct_req_payload[9]);
2434 			iport->iport_li_comp_status = FCT_REJECT_STATUS(
2435 			    ICMD_TO_CT(icmd)->ct_resp_payload[8],
2436 			    ICMD_TO_CT(icmd)->ct_resp_payload[9]);
2437 		} else {
2438 			iport->iport_li_comp_status = FCT_SUCCESS;
2439 		}
2440 	}
2441 }
2442 
2443 void
2444 fct_gcs_cb(fct_i_cmd_t *icmd)
2445 {
2446 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2447 	fct_i_remote_port_t	*query_irp = NULL;
2448 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2449 	uint32_t		 query_portid;
2450 	uint8_t			*resp;
2451 	uint8_t			*req;
2452 
2453 	if (!FCT_IS_CT_ACC(icmd)) {
2454 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gcs_cb: "
2455 		    "GCS_ID is not accepted by NS - icmd/%p", icmd);
2456 		return;
2457 	}
2458 	mutex_exit(&iport->iport_worker_lock);
2459 
2460 	resp = ct->ct_resp_payload;
2461 	req = ct->ct_req_payload;
2462 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2463 
2464 	rw_enter(&iport->iport_lock, RW_READER);
2465 	mutex_enter(&iport->iport_worker_lock);
2466 	query_irp = fct_portid_to_portptr(iport, query_portid);
2467 
2468 	if (query_irp) {
2469 		query_irp->irp_cos = (resp[16] << 27) | (resp[17] << 18) |
2470 		    (resp[18] << 8) | resp[19];
2471 	}
2472 	rw_exit(&iport->iport_lock);
2473 }
2474 
2475 void
2476 fct_gft_cb(fct_i_cmd_t *icmd)
2477 {
2478 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2479 	fct_i_remote_port_t	*query_irp = NULL;
2480 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2481 	uint32_t		 query_portid;
2482 	uint8_t			*resp;
2483 	uint8_t			*req;
2484 
2485 	if (!FCT_IS_CT_ACC(icmd)) {
2486 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gft_cb: "
2487 		    "GFT_ID is not accepted by NS - icmd/%p", icmd);
2488 		return;
2489 	}
2490 	mutex_exit(&iport->iport_worker_lock);
2491 
2492 	resp = ct->ct_resp_payload;
2493 	req = ct->ct_req_payload;
2494 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2495 
2496 	rw_enter(&iport->iport_lock, RW_READER);
2497 	mutex_enter(&iport->iport_worker_lock);
2498 	query_irp = fct_portid_to_portptr(iport, query_portid);
2499 
2500 	if (query_irp) {
2501 		(void) memcpy(query_irp->irp_fc4types, resp + 16, 32);
2502 	}
2503 	rw_exit(&iport->iport_lock);
2504 }
2505 
2506 void
2507 fct_gid_cb(fct_i_cmd_t *icmd)
2508 {
2509 	fct_cmd_t		*cmd	   = NULL;
2510 	fct_i_remote_port_t	*query_irp = NULL;
2511 	uint32_t		 nsportid  = 0;
2512 	int			 do_logo   = 0;
2513 
2514 	mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2515 
2516 	rw_enter(&ICMD_TO_IPORT(icmd)->iport_lock, RW_READER);
2517 	mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2518 	query_irp = fct_lookup_irp_by_portwwn(ICMD_TO_IPORT(icmd),
2519 	    ICMD_TO_CT(icmd)->ct_req_payload + 16);
2520 
2521 	if (!query_irp || (query_irp &&
2522 	    (PTR2INT(icmd->icmd_cb_private, uint32_t) !=
2523 	    query_irp->irp_rscn_counter))) {
2524 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2525 		    "new RSCN arrived - query_irp/%p, private-%x", query_irp,
2526 		    PTR2INT(icmd->icmd_cb_private, uint32_t));
2527 		goto exit_gid_cb;
2528 	}
2529 
2530 	if ((query_irp->irp_flags & IRP_RSCN_QUEUED) ||
2531 	    (!(query_irp->irp_flags & IRP_PLOGI_DONE)))	{
2532 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2533 		    "not proper irp_flags - query_irp/%p", query_irp);
2534 		goto exit_gid_cb;
2535 	}
2536 
2537 	if (!FCT_IS_CT_ACC(icmd)) {
2538 		/*
2539 		 * Check if it has disappeared
2540 		 */
2541 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gid_cb: "
2542 		    "GPN_ID is not accepted by NS - icmd/%p", icmd);
2543 		do_logo = 1;
2544 	} else {
2545 		/*
2546 		 * Check if its portid has changed
2547 		 */
2548 		nsportid = fct_netbuf_to_value(
2549 		    ICMD_TO_CT(icmd)->ct_resp_payload + 17, 3);
2550 		if (nsportid != query_irp->irp_rp->rp_id) {
2551 			stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias,
2552 			    "portid has changed - query_irp/%p", query_irp);
2553 			do_logo = 1;
2554 		}
2555 	}
2556 
2557 	if (do_logo) {
2558 		cmd = fct_create_solels(ICMD_TO_PORT(icmd),
2559 		    query_irp->irp_rp, 1, ELS_OP_LOGO, 0, fct_logo_cb);
2560 		if (cmd) {
2561 			mutex_exit(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2562 			fct_post_implicit_logo(cmd);
2563 			mutex_enter(&ICMD_TO_IPORT(icmd)->iport_worker_lock);
2564 		}
2565 	}
2566 
2567 exit_gid_cb:
2568 	rw_exit(&ICMD_TO_IPORT(icmd)->iport_lock);
2569 }
2570 
2571 void
2572 fct_gspn_cb(fct_i_cmd_t *icmd)
2573 {
2574 	fct_sol_ct_t		*ct	   = ICMD_TO_CT(icmd);
2575 	fct_i_remote_port_t	*query_irp = NULL;
2576 	fct_i_local_port_t	*iport	   = ICMD_TO_IPORT(icmd);
2577 	uint32_t		 query_portid;
2578 	uint8_t			*resp;
2579 	uint8_t			*req;
2580 	uint8_t			 spnlen;
2581 
2582 	if (!FCT_IS_CT_ACC(icmd)) {
2583 		stmf_trace(ICMD_TO_IPORT(icmd)->iport_alias, "fct_gspn_cb: "
2584 		    "GSPN_ID is not accepted by NS - icmd/%p", icmd);
2585 		return;
2586 	}
2587 	mutex_exit(&iport->iport_worker_lock);
2588 
2589 	resp = ct->ct_resp_payload;
2590 	req = ct->ct_req_payload;
2591 	query_portid = (req[17] << 16) | (req[18] << 8) | req[19];
2592 
2593 	rw_enter(&iport->iport_lock, RW_READER);
2594 	mutex_enter(&iport->iport_worker_lock);
2595 	query_irp = fct_portid_to_portptr(iport, query_portid);
2596 	if (query_irp) {
2597 		spnlen = resp[16];
2598 		if (spnlen > 0) {
2599 			if (query_irp->irp_spn) {
2600 				kmem_free(query_irp->irp_spn,
2601 				    strlen(query_irp->irp_spn) + 1);
2602 			}
2603 			query_irp->irp_spn = kmem_zalloc(spnlen + 1, KM_SLEEP);
2604 			(void) strncpy(query_irp->irp_spn,
2605 			    (char *)resp + 17, spnlen);
2606 		}
2607 	}
2608 	rw_exit(&iport->iport_lock);
2609 }
2610 
2611 /*
2612  * For lookup functions, we move locking up one level
2613  */
2614 fct_i_remote_port_t *
2615 fct_lookup_irp_by_nodewwn(fct_i_local_port_t *iport, uint8_t *nodewwn)
2616 {
2617 	fct_i_remote_port_t	*irp = NULL;
2618 	int			 idx = 0;
2619 
2620 	for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2621 		for (irp = iport->iport_rp_tb[idx]; irp;
2622 		    irp = irp->irp_next) {
2623 			if (bcmp(irp->irp_rp->rp_nwwn, nodewwn, FC_WWN_LEN)) {
2624 				continue;
2625 			} else {
2626 				return (irp);
2627 			}
2628 		}
2629 	}
2630 
2631 	return (NULL);
2632 }
2633 
2634 fct_i_remote_port_t *
2635 fct_lookup_irp_by_portwwn(fct_i_local_port_t *iport, uint8_t *portwwn)
2636 {
2637 	fct_i_remote_port_t	*irp = NULL;
2638 	int			 idx = 0;
2639 
2640 	for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2641 		for (irp = iport->iport_rp_tb[idx]; irp;
2642 		    irp = irp->irp_next) {
2643 			if (bcmp(irp->irp_rp->rp_pwwn, portwwn, FC_WWN_LEN)) {
2644 				continue;
2645 			} else {
2646 				return (irp);
2647 			}
2648 		}
2649 	}
2650 
2651 	return (NULL);
2652 }
2653 
2654 #ifdef	lint
2655 #define	FCT_VERIFY_RSCN()	_NOTE(EMPTY)
2656 #else
2657 #define	FCT_VERIFY_RSCN()						\
2658 do {									\
2659 	ct_cmd = fct_create_solct(port, irp->irp_rp, NS_GID_PN,		\
2660 	    fct_gid_cb);						\
2661 	if (ct_cmd) {							\
2662 		uint32_t cnt;						\
2663 		cnt = atomic_add_32_nv(&irp->irp_rscn_counter, 1);	\
2664 		CMD_TO_ICMD(ct_cmd)->icmd_cb_private =			\
2665 		    INT2PTR(cnt, void *);				\
2666 		irp->irp_flags |= IRP_RSCN_QUEUED;			\
2667 		fct_post_to_solcmd_queue(port, ct_cmd);			\
2668 	}								\
2669 } while (0)
2670 #endif
2671 
2672 /* ARGSUSED */
2673 static void
2674 fct_rscn_verify(fct_i_local_port_t *iport, uint8_t *rscn_req_payload,
2675     uint32_t rscn_req_size)
2676 {
2677 	int			idx		= 0;
2678 	uint8_t			page_format	= 0;
2679 	uint32_t		page_portid	= 0;
2680 	uint8_t			*page_buf	= NULL;
2681 	uint8_t			*last_page_buf	= NULL;
2682 #ifndef	lint
2683 	fct_cmd_t		*ct_cmd		= NULL;
2684 	fct_local_port_t	*port		= NULL;
2685 #endif
2686 	fct_i_remote_port_t	*irp		= NULL;
2687 
2688 	page_buf = rscn_req_payload + 4;
2689 	last_page_buf = rscn_req_payload +
2690 	    fct_netbuf_to_value(rscn_req_payload + 2, 2) - 4;
2691 #ifndef	lint
2692 	port = iport->iport_port;
2693 #endif
2694 	for (; page_buf <= last_page_buf; page_buf += 4) {
2695 		page_format = 0x03 & page_buf[0];
2696 		page_portid = fct_netbuf_to_value(page_buf + 1, 3);
2697 
2698 		rw_enter(&iport->iport_lock, RW_READER);
2699 		if (!page_format) {
2700 			irp = fct_portid_to_portptr(iport, page_portid);
2701 			if (!(irp && !(irp->irp_flags & IRP_RSCN_QUEUED))) {
2702 				rw_exit(&iport->iport_lock);
2703 
2704 				continue; /* try next page */
2705 			}
2706 
2707 			if (FC_WELL_KNOWN_ADDR(irp->irp_portid) ||
2708 			    !(irp->irp_flags & IRP_PLOGI_DONE)) {
2709 				rw_exit(&iport->iport_lock);
2710 
2711 				continue; /* try next page */
2712 			}
2713 
2714 			FCT_VERIFY_RSCN();
2715 		} else {
2716 			for (idx = 0; idx < FCT_HASH_TABLE_SIZE; idx++) {
2717 				for (irp = iport->iport_rp_tb[idx];
2718 				    irp; irp = irp->irp_next) {
2719 					if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
2720 						continue; /* try next irp */
2721 
2722 					if (!(irp->irp_flags & IRP_PLOGI_DONE))
2723 						continue; /* try next irp */
2724 
2725 					if (irp->irp_flags & IRP_RSCN_QUEUED) {
2726 						continue; /* try next irp */
2727 					}
2728 #ifndef	lint
2729 					if (!((0xFFFFFF << (page_format * 8)) &
2730 					    (page_portid ^ irp->irp_portid))) {
2731 						FCT_VERIFY_RSCN();
2732 					}
2733 #endif
2734 				}
2735 			}
2736 		}
2737 		rw_exit(&iport->iport_lock);
2738 	}
2739 }
2740