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