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