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