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