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