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