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