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