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