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