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