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