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