1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  24  */
  25 
  26 #include <sys/conf.h>
  27 #include <sys/file.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/modctl.h>
  31 #include <sys/scsi/scsi.h>
  32 #include <sys/scsi/impl/scsi_reset_notify.h>
  33 #include <sys/disp.h>
  34 #include <sys/byteorder.h>
  35 #include <sys/varargs.h>
  36 #include <sys/atomic.h>
  37 #include <sys/sdt.h>
  38 
  39 #include <sys/stmf.h>
  40 #include <sys/stmf_ioctl.h>
  41 #include <sys/portif.h>
  42 #include <sys/fct.h>
  43 #include <sys/fctio.h>
  44 
  45 #include "fct_impl.h"
  46 #include "discovery.h"
  47 
  48 static int fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
  49 static int fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
  50 static int fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
  51     void **result);
  52 static int fct_open(dev_t *devp, int flag, int otype, cred_t *credp);
  53 static int fct_close(dev_t dev, int flag, int otype, cred_t *credp);
  54 static int fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
  55     cred_t *credp, int *rval);
  56 static int fct_fctiocmd(intptr_t data, int mode);
  57 void fct_init_kstats(fct_i_local_port_t *iport);
  58 
  59 static dev_info_t *fct_dip;
  60 static struct cb_ops fct_cb_ops = {
  61         fct_open,                       /* open */
  62         fct_close,                      /* close */
  63         nodev,                          /* strategy */
  64         nodev,                          /* print */
  65         nodev,                          /* dump */
  66         nodev,                          /* read */
  67         nodev,                          /* write */
  68         fct_ioctl,                      /* ioctl */
  69         nodev,                          /* devmap */
  70         nodev,                          /* mmap */
  71         nodev,                          /* segmap */
  72         nochpoll,                       /* chpoll */
  73         ddi_prop_op,                    /* cb_prop_op */
  74         0,                              /* streamtab */
  75         D_NEW | D_MP,                   /* cb_flag */
  76         CB_REV,                         /* rev */
  77         nodev,                          /* aread */
  78         nodev                           /* awrite */
  79 };
  80 
  81 static struct dev_ops fct_ops = {
  82         DEVO_REV,
  83         0,
  84         fct_getinfo,
  85         nulldev,                /* identify */
  86         nulldev,                /* probe */
  87         fct_attach,
  88         fct_detach,
  89         nodev,                  /* reset */
  90         &fct_cb_ops,
  91         NULL,                   /* bus_ops */
  92         NULL                    /* power */
  93 };
  94 
  95 #define FCT_NAME        "COMSTAR FCT"
  96 #define FCT_MODULE_NAME "fct"
  97 
  98 extern struct mod_ops mod_driverops;
  99 static struct modldrv modldrv = {
 100         &mod_driverops,
 101         FCT_NAME,
 102         &fct_ops
 103 };
 104 
 105 static struct modlinkage modlinkage = {
 106         MODREV_1,
 107         &modldrv,
 108         NULL
 109 };
 110 
 111 static uint32_t rportid_table_size = FCT_HASH_TABLE_SIZE;
 112 static int max_cached_ncmds = FCT_MAX_CACHED_CMDS;
 113 static fct_i_local_port_t *fct_iport_list = NULL;
 114 static kmutex_t fct_global_mutex;
 115 uint32_t fct_rscn_options = RSCN_OPTION_VERIFY;
 116 
 117 int
 118 _init(void)
 119 {
 120         int ret;
 121 
 122         ret = mod_install(&modlinkage);
 123         if (ret)
 124                 return (ret);
 125         /* XXX */
 126         mutex_init(&fct_global_mutex, NULL, MUTEX_DRIVER, NULL);
 127         return (ret);
 128 }
 129 
 130 int
 131 _fini(void)
 132 {
 133         int ret;
 134 
 135         ret = mod_remove(&modlinkage);
 136         if (ret)
 137                 return (ret);
 138         /* XXX */
 139         mutex_destroy(&fct_global_mutex);
 140         return (ret);
 141 }
 142 
 143 int
 144 _info(struct modinfo *modinfop)
 145 {
 146         return (mod_info(&modlinkage, modinfop));
 147 }
 148 
 149 /* ARGSUSED */
 150 static int
 151 fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 152 {
 153         switch (cmd) {
 154         case DDI_INFO_DEVT2DEVINFO:
 155                 *result = fct_dip;
 156                 break;
 157         case DDI_INFO_DEVT2INSTANCE:
 158                 *result = (void *)(uintptr_t)ddi_get_instance(fct_dip);
 159                 break;
 160         default:
 161                 return (DDI_FAILURE);
 162         }
 163 
 164         return (DDI_SUCCESS);
 165 }
 166 
 167 static int
 168 fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 169 {
 170         switch (cmd) {
 171         case DDI_ATTACH:
 172                 fct_dip = dip;
 173 
 174                 if (ddi_create_minor_node(dip, "admin", S_IFCHR, 0,
 175                     DDI_NT_STMF_PP, 0) != DDI_SUCCESS) {
 176                         break;
 177                 }
 178                 ddi_report_dev(dip);
 179                 return (DDI_SUCCESS);
 180         }
 181 
 182         return (DDI_FAILURE);
 183 }
 184 
 185 static int
 186 fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 187 {
 188         switch (cmd) {
 189         case DDI_DETACH:
 190                 ddi_remove_minor_node(dip, 0);
 191                 return (DDI_SUCCESS);
 192         }
 193 
 194         return (DDI_FAILURE);
 195 }
 196 
 197 /* ARGSUSED */
 198 static int
 199 fct_open(dev_t *devp, int flag, int otype, cred_t *credp)
 200 {
 201         if (otype != OTYP_CHR)
 202                 return (EINVAL);
 203         return (0);
 204 }
 205 
 206 /* ARGSUSED */
 207 static int
 208 fct_close(dev_t dev, int flag, int otype, cred_t *credp)
 209 {
 210         return (0);
 211 }
 212 
 213 /* ARGSUSED */
 214 static int
 215 fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
 216     cred_t *credp, int *rval)
 217 {
 218         int             ret = 0;
 219 
 220         if ((cmd & 0xff000000) != FCT_IOCTL) {
 221                 return (ENOTTY);
 222         }
 223 
 224         if (drv_priv(credp) != 0) {
 225                 return (EPERM);
 226         }
 227 
 228         switch (cmd) {
 229         case FCTIO_CMD:
 230                 ret = fct_fctiocmd(data, mode);
 231                 break;
 232         default:
 233                 ret = ENOTTY;
 234                 break;
 235         }
 236 
 237         return (ret);
 238 }
 239 
 240 int
 241 fct_copyin_iocdata(intptr_t data, int mode, fctio_t **fctio,
 242     void **ibuf, void **abuf, void **obuf)
 243 {
 244         int ret = 0;
 245 
 246         *ibuf = NULL;
 247         *abuf = NULL;
 248         *obuf = NULL;
 249         *fctio = kmem_zalloc(sizeof (fctio_t), KM_SLEEP);
 250         if (ddi_copyin((void *)data, *fctio, sizeof (fctio_t), mode)) {
 251                 ret = EFAULT;
 252                 goto copyin_iocdata_done;
 253         }
 254 
 255         if ((*fctio)->fctio_ilen) {
 256                 *ibuf = kmem_zalloc((*fctio)->fctio_ilen, KM_SLEEP);
 257                 if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_ibuf,
 258                     *ibuf, (*fctio)->fctio_ilen, mode)) {
 259                         ret = EFAULT;
 260                         goto copyin_iocdata_done;
 261                 }
 262         }
 263         if ((*fctio)->fctio_alen) {
 264                 *abuf = kmem_zalloc((*fctio)->fctio_alen, KM_SLEEP);
 265                 if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_abuf,
 266                     *abuf, (*fctio)->fctio_alen, mode)) {
 267                         ret = EFAULT;
 268                         goto copyin_iocdata_done;
 269                 }
 270         }
 271         if ((*fctio)->fctio_olen)
 272                 *obuf = kmem_zalloc((*fctio)->fctio_olen, KM_SLEEP);
 273         if (ret == 0)
 274                 return (0);
 275         ret = EFAULT;
 276 copyin_iocdata_done:
 277         if (*obuf) {
 278                 kmem_free(*obuf, (*fctio)->fctio_olen);
 279                 *obuf = NULL;
 280         }
 281         if (*abuf) {
 282                 kmem_free(*abuf, (*fctio)->fctio_alen);
 283                 *abuf = NULL;
 284         }
 285         if (*ibuf) {
 286                 kmem_free(*ibuf, (*fctio)->fctio_ilen);
 287                 *ibuf = NULL;
 288         }
 289         kmem_free(*fctio, sizeof (fctio_t));
 290         return (ret);
 291 }
 292 
 293 int
 294 fct_copyout_iocdata(intptr_t data, int mode, fctio_t *fctio, void *obuf)
 295 {
 296         int ret = 0;
 297 
 298         if (fctio->fctio_olen) {
 299                 ret = ddi_copyout(obuf,
 300                     (void *)(unsigned long)fctio->fctio_obuf, fctio->fctio_olen,
 301                     mode);
 302                 if (ret) {
 303                         return (EFAULT);
 304                 }
 305         }
 306         ret = ddi_copyout(fctio, (void *)data, sizeof (fctio_t), mode);
 307         if (ret) {
 308                 return (EFAULT);
 309         }
 310         return (0);
 311 }
 312 
 313 int
 314 fct_get_port_list(char *pathList, int count)
 315 {
 316         fct_i_local_port_t *iport;
 317         int     i = 0, maxPorts = 0;
 318 
 319         ASSERT(pathList != NULL);
 320 
 321         mutex_enter(&fct_global_mutex);
 322         for (iport = fct_iport_list; iport; iport = iport->iport_next) {
 323                 if (i < count)
 324                         bcopy(iport->iport_port->port_pwwn,
 325                             pathList + 8 * i, 8);
 326                 maxPorts ++;
 327                 i++;
 328         }
 329         mutex_exit(&fct_global_mutex);
 330         return (maxPorts);
 331 }
 332 
 333 /* invoked with fct_global_mutex locked */
 334 fct_i_local_port_t *
 335 fct_get_iport_per_wwn(uint8_t *pwwn)
 336 {
 337         fct_i_local_port_t *iport;
 338 
 339         ASSERT(mutex_owned(&fct_global_mutex));
 340         for (iport = fct_iport_list; iport; iport = iport->iport_next) {
 341                 if (bcmp(iport->iport_port->port_pwwn, pwwn, 8) == 0)
 342                         return (iport);
 343         }
 344         return (NULL);
 345 }
 346 
 347 int
 348 fct_get_adapter_attr(uint8_t *pwwn, fc_tgt_hba_adapter_attributes_t *hba_attr,
 349     uint32_t *err_detail)
 350 {
 351         fct_i_local_port_t *iport;
 352         fct_port_attrs_t *attr;
 353 
 354         hba_attr->version = FCT_HBA_ADAPTER_ATTRIBUTES_VERSION;
 355         iport = fct_get_iport_per_wwn(pwwn);
 356         if (!iport) {
 357                 *err_detail = FCTIO_BADWWN;
 358                 return (ENXIO);
 359         }
 360 
 361         attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
 362             KM_SLEEP);
 363         mutex_exit(&fct_global_mutex);
 364         iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
 365         mutex_enter(&fct_global_mutex);
 366 
 367         bcopy(attr->manufacturer, hba_attr->Manufacturer,
 368             sizeof (hba_attr->Manufacturer));
 369         bcopy(attr->serial_number, hba_attr->SerialNumber,
 370             sizeof (hba_attr->SerialNumber));
 371         bcopy(attr->model, hba_attr->Model, sizeof (hba_attr->Model));
 372         bcopy(attr->model_description, hba_attr->ModelDescription,
 373             sizeof (hba_attr->ModelDescription));
 374         if (iport->iport_port->port_sym_node_name)
 375                 bcopy(iport->iport_port->port_sym_node_name,
 376                     hba_attr->NodeSymbolicName,
 377                     strlen(iport->iport_port->port_sym_node_name));
 378         else
 379                 bcopy(utsname.nodename, hba_attr->NodeSymbolicName,
 380                     strlen(utsname.nodename));
 381         bcopy(attr->hardware_version, hba_attr->HardwareVersion,
 382             sizeof (hba_attr->HardwareVersion));
 383         bcopy(attr->option_rom_version, hba_attr->OptionROMVersion,
 384             sizeof (hba_attr->OptionROMVersion));
 385         bcopy(attr->firmware_version, hba_attr->FirmwareVersion,
 386             sizeof (hba_attr->FirmwareVersion));
 387         hba_attr->VendorSpecificID = attr->vendor_specific_id;
 388         bcopy(iport->iport_port->port_nwwn, hba_attr->NodeWWN,
 389             sizeof (hba_attr->NodeWWN));
 390 
 391         bcopy(attr->driver_name, hba_attr->DriverName,
 392             sizeof (hba_attr->DriverName));
 393         bcopy(attr->driver_version, hba_attr->DriverVersion,
 394             sizeof (hba_attr->DriverVersion));
 395 
 396 
 397         /* hba_attr->NumberOfPorts = fct_count_fru_ports(iport); */
 398         hba_attr->NumberOfPorts = 1;
 399 
 400         kmem_free(attr, sizeof (fct_port_attrs_t));
 401         return (0);
 402 }
 403 
 404 int
 405 fct_get_adapter_port_attr(fct_i_local_port_t *ilport, uint8_t *pwwn,
 406     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *err_detail)
 407 {
 408         fct_i_local_port_t *iport = ilport;
 409         fct_i_remote_port_t *irp = NULL;
 410         fct_port_attrs_t *attr;
 411         int i = 0;
 412 
 413         port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
 414 
 415         if (!ilport) {
 416                 iport = fct_get_iport_per_wwn(pwwn);
 417                 if (!iport) {
 418                         *err_detail = FCTIO_BADWWN;
 419                         return (ENXIO);
 420                 }
 421         }
 422 
 423         attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
 424             KM_SLEEP);
 425         mutex_exit(&fct_global_mutex);
 426         iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
 427         mutex_enter(&fct_global_mutex);
 428 
 429         port_attr->lastChange = iport->iport_last_change;
 430         bcopy(iport->iport_port->port_nwwn, port_attr->NodeWWN,
 431             sizeof (port_attr->NodeWWN));
 432         bcopy(iport->iport_port->port_pwwn, port_attr->PortWWN,
 433             sizeof (port_attr->PortWWN));
 434         bzero(port_attr->FabricName, sizeof (port_attr->FabricName));
 435         port_attr->PortFcId = iport->iport_link_info.portid;
 436         if ((iport->iport_link_state & S_LINK_ONLINE) ||
 437             (iport->iport_link_state & S_RCVD_LINK_UP)) {
 438                 port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
 439         } else {
 440                 port_attr->PortState = FC_HBA_PORTSTATE_OFFLINE;
 441         }
 442         switch (iport->iport_link_info.port_topology) {
 443                 case PORT_TOPOLOGY_PT_TO_PT:
 444                         port_attr->PortType = FC_HBA_PORTTYPE_PTP;
 445                         break;
 446                 case PORT_TOPOLOGY_PRIVATE_LOOP:
 447                         port_attr->PortType = FC_HBA_PORTTYPE_LPORT;
 448                         break;
 449                 case PORT_TOPOLOGY_PUBLIC_LOOP:
 450                         port_attr->PortType = FC_HBA_PORTTYPE_NLPORT;
 451                         break;
 452                 case PORT_TOPOLOGY_FABRIC_PT_TO_PT:
 453                         port_attr->PortType = FC_HBA_PORTTYPE_FPORT;
 454                         break;
 455                 default:
 456                         port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
 457                         break;
 458         }
 459         port_attr->PortSupportedClassofService = attr->supported_cos;
 460         port_attr->PortSupportedFc4Types[0] = 0;
 461         port_attr->PortActiveFc4Types[2] = 1;
 462         if (iport->iport_port->port_sym_port_name)
 463                 bcopy(iport->iport_port->port_sym_port_name,
 464                     port_attr->PortSymbolicName,
 465                     strlen(iport->iport_port->port_sym_port_name));
 466         else if (iport->iport_port->port_default_alias)
 467                 bcopy(iport->iport_port->port_default_alias,
 468                     port_attr->PortSymbolicName,
 469                     strlen(iport->iport_port->port_default_alias));
 470         else
 471                 port_attr->PortSymbolicName[0] = 0;
 472         /* the definition is different so need to translate */
 473         if (attr->supported_speed & PORT_SPEED_1G)
 474                 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_1GBIT;
 475         if (attr->supported_speed & PORT_SPEED_2G)
 476                 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_2GBIT;
 477         if (attr->supported_speed & PORT_SPEED_4G)
 478                 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_4GBIT;
 479         if (attr->supported_speed & PORT_SPEED_8G)
 480                 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_8GBIT;
 481         if (attr->supported_speed & PORT_SPEED_10G)
 482                 port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_10GBIT;
 483         switch (iport->iport_link_info.port_speed) {
 484                 case PORT_SPEED_1G:
 485                         port_attr->PortSpeed = FC_HBA_PORTSPEED_1GBIT;
 486                         break;
 487                 case PORT_SPEED_2G:
 488                         port_attr->PortSpeed = FC_HBA_PORTSPEED_2GBIT;
 489                         break;
 490                 case PORT_SPEED_4G:
 491                         port_attr->PortSpeed = FC_HBA_PORTSPEED_4GBIT;
 492                         break;
 493                 case PORT_SPEED_8G:
 494                         port_attr->PortSpeed = FC_HBA_PORTSPEED_8GBIT;
 495                         break;
 496                 case PORT_SPEED_10G:
 497                         port_attr->PortSpeed = FC_HBA_PORTSPEED_10GBIT;
 498                         break;
 499                 default:
 500                         port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
 501                         break;
 502         }
 503         port_attr->PortMaxFrameSize = attr->max_frame_size;
 504         rw_enter(&iport->iport_lock, RW_READER);
 505         port_attr->NumberofDiscoveredPorts = iport->iport_nrps_login;
 506         for (; i < iport->iport_port->port_max_logins; i++) {
 507                 irp = iport->iport_rp_slots[i];
 508                 if (irp && irp->irp_flags & IRP_PLOGI_DONE) {
 509                         if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
 510                                 port_attr->NumberofDiscoveredPorts --;
 511                 }
 512         }
 513         rw_exit(&iport->iport_lock);
 514 
 515         kmem_free(attr, sizeof (fct_port_attrs_t));
 516 
 517         return (0);
 518 }
 519 
 520 int
 521 fct_get_discovered_port_attr(fct_i_remote_port_t *remote_port,
 522     uint8_t *port_wwn, uint32_t index, fc_tgt_hba_port_attributes_t *port_attr,
 523     uint32_t *error_detail)
 524 {
 525         fct_i_local_port_t *iport;
 526         fct_i_remote_port_t *irp = remote_port;
 527         int     count = 0, i = 0;
 528 
 529         port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
 530         if (!remote_port) {
 531                 iport = fct_get_iport_per_wwn(port_wwn);
 532                 if (!iport) {
 533                         *error_detail = FCTIO_BADWWN;
 534                         return (ENXIO);
 535                 }
 536 
 537                 rw_enter(&iport->iport_lock, RW_READER);
 538 
 539                 if (index >= iport->iport_nrps_login) {
 540                         rw_exit(&iport->iport_lock);
 541                         *error_detail = FCTIO_OUTOFBOUNDS;
 542                         return (EINVAL);
 543                 }
 544                 for (; i < iport->iport_port->port_max_logins; i++) {
 545                         irp = iport->iport_rp_slots[i];
 546                         if (irp && irp->irp_flags & IRP_PLOGI_DONE &&
 547                             !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
 548                                 count ++;
 549                                 if ((index + 1) <= count)
 550                                         break;
 551                         }
 552                 }
 553                 if (i >= iport->iport_port->port_max_logins) {
 554                         rw_exit(&iport->iport_lock);
 555                         *error_detail = FCTIO_OUTOFBOUNDS;
 556                         return (EINVAL);
 557                 }
 558                 ASSERT(irp);
 559         } else {
 560                 iport = (fct_i_local_port_t *)
 561                     irp->irp_rp->rp_port->port_fct_private;
 562         }
 563         port_attr->lastChange = iport->iport_last_change;
 564         rw_enter(&irp->irp_lock, RW_READER);
 565         bcopy(irp->irp_rp->rp_pwwn, port_attr->PortWWN,
 566             sizeof (port_attr->PortWWN));
 567         bcopy(irp->irp_rp->rp_nwwn, port_attr->NodeWWN,
 568             sizeof (port_attr->NodeWWN));
 569         port_attr->PortFcId = irp->irp_portid;
 570         if (irp->irp_spn)
 571                 (void) strncpy(port_attr->PortSymbolicName, irp->irp_spn,
 572                     strlen(irp->irp_spn));
 573         else
 574                 port_attr->PortSymbolicName[0] = '\0';
 575         port_attr->PortSupportedClassofService = irp->irp_cos;
 576         bcopy((caddr_t)irp->irp_fc4types, port_attr->PortActiveFc4Types,
 577             sizeof (irp->irp_fc4types));
 578         bcopy((caddr_t)irp->irp_fc4types, port_attr->PortSupportedFc4Types,
 579             sizeof (irp->irp_fc4types));
 580         if (irp->irp_flags & IRP_PLOGI_DONE)
 581                 port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
 582         else
 583                 port_attr->PortState = FC_HBA_PORTSTATE_UNKNOWN;
 584 
 585         port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
 586         port_attr->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
 587         port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
 588         port_attr->PortMaxFrameSize = 0;
 589         port_attr->NumberofDiscoveredPorts = 0;
 590         rw_exit(&irp->irp_lock);
 591         if (!remote_port) {
 592                 rw_exit(&iport->iport_lock);
 593         }
 594         return (0);
 595 }
 596 
 597 int
 598 fct_get_port_attr(uint8_t *port_wwn,
 599     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *error_detail)
 600 {
 601         fct_i_local_port_t *iport;
 602         fct_i_remote_port_t *irp;
 603         int i, ret;
 604 
 605         iport = fct_get_iport_per_wwn(port_wwn);
 606         if (iport) {
 607                 return (fct_get_adapter_port_attr(iport, port_wwn,
 608                     port_attr, error_detail));
 609         }
 610         /* else */
 611         for (iport = fct_iport_list; iport; iport = iport->iport_next) {
 612                 rw_enter(&iport->iport_lock, RW_READER);
 613                 for (i = 0; i < rportid_table_size; i++) {
 614                         irp = iport->iport_rp_tb[i];
 615                         while (irp) {
 616                                 if (bcmp(irp->irp_rp->rp_pwwn,
 617                                     port_wwn, 8) == 0 &&
 618                                     irp->irp_flags & IRP_PLOGI_DONE) {
 619                                         ret = fct_get_discovered_port_attr(
 620                                             irp, NULL, 0, port_attr,
 621                                             error_detail);
 622                                         rw_exit(&iport->iport_lock);
 623                                         return (ret);
 624                                 }
 625                                 irp = irp->irp_next;
 626                         }
 627                 }
 628                 rw_exit(&iport->iport_lock);
 629         }
 630         *error_detail = FCTIO_BADWWN;
 631         return (ENXIO);
 632 }
 633 
 634 /* ARGSUSED */
 635 int
 636 fct_get_port_stats(uint8_t *port_wwn,
 637     fc_tgt_hba_adapter_port_stats_t *port_stats, uint32_t *error_detail)
 638 {
 639         int ret;
 640         fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
 641         fct_port_link_status_t  stat;
 642         uint32_t buf_size = sizeof (fc_tgt_hba_adapter_port_stats_t);
 643 
 644         if (!iport)
 645                 return (ENXIO);
 646         port_stats->version = FCT_HBA_ADAPTER_PORT_STATS_VERSION;
 647 
 648         if (iport->iport_port->port_info == NULL) {
 649                 *error_detail = FCTIO_FAILURE;
 650                 return (EIO);
 651         }
 652         ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
 653             iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
 654         if (ret != STMF_SUCCESS) {
 655                 *error_detail = FCTIO_FAILURE;
 656                 return (EIO);
 657         }
 658 
 659         port_stats->SecondsSinceLastReset = 0;
 660         port_stats->TxFrames = 0;
 661         port_stats->TxWords = 0;
 662         port_stats->RxFrames = 0;
 663         port_stats->RxWords = 0;
 664         port_stats->LIPCount = 0;
 665         port_stats->NOSCount = 0;
 666         port_stats->ErrorFrames = 0;
 667         port_stats->DumpedFrames = 0;
 668         port_stats->LinkFailureCount = stat.LinkFailureCount;
 669         port_stats->LossOfSyncCount = stat.LossOfSyncCount;
 670         port_stats->LossOfSignalCount = stat.LossOfSignalsCount;
 671         port_stats->PrimitiveSeqProtocolErrCount =
 672             stat.PrimitiveSeqProtocolErrorCount;
 673         port_stats->InvalidTxWordCount =
 674             stat.InvalidTransmissionWordCount;
 675         port_stats->InvalidCRCCount = stat.InvalidCRCCount;
 676 
 677         return (ret);
 678 }
 679 
 680 int
 681 fct_get_link_status(uint8_t *port_wwn, uint64_t *dest_id,
 682     fct_port_link_status_t *link_status, uint32_t *error_detail)
 683 {
 684         fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
 685         fct_i_remote_port_t *irp = NULL;
 686         uint32_t buf_size = sizeof (fct_port_link_status_t);
 687         stmf_status_t ret = 0;
 688         int i;
 689         fct_cmd_t *cmd = NULL;
 690 
 691         if (!iport) {
 692                 *error_detail = FCTIO_BADWWN;
 693                 return (ENXIO);
 694         }
 695 
 696         /*
 697          * If what we are requesting is zero or same as local port,
 698          * then we use port_info()
 699          */
 700         if (dest_id == NULL || *dest_id == iport->iport_link_info.portid) {
 701                 if (iport->iport_port->port_info == NULL) {
 702                         *error_detail = FCTIO_FAILURE;
 703                         return (EIO);
 704                 }
 705                 ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
 706                     iport->iport_port, NULL,
 707                     (uint8_t *)link_status, &buf_size);
 708                 if (ret == STMF_SUCCESS) {
 709                         return (0);
 710                 } else {
 711                         *error_detail = FCTIO_FAILURE;
 712                         return (EIO);
 713                 }
 714         }
 715 
 716         /*
 717          * For remote port, we will send RLS
 718          */
 719         for (i = 0; i < rportid_table_size; i++) {
 720                 irp = iport->iport_rp_tb[i];
 721                 while (irp) {
 722                         if (irp->irp_rp->rp_id == *dest_id &&
 723                             irp->irp_flags & IRP_PLOGI_DONE) {
 724                                 goto SEND_RLS_ELS;
 725                         }
 726                         irp = irp->irp_next;
 727                 }
 728         }
 729         return (ENXIO);
 730 
 731 SEND_RLS_ELS:
 732         cmd = fct_create_solels(iport->iport_port,
 733             irp->irp_rp, 0, ELS_OP_RLS,
 734             0, fct_rls_cb);
 735         if (!cmd)
 736                 return (ENOMEM);
 737         iport->iport_rls_cb_data.fct_link_status = link_status;
 738         CMD_TO_ICMD(cmd)->icmd_cb_private = &iport->iport_rls_cb_data;
 739         fct_post_to_solcmd_queue(iport->iport_port, cmd);
 740         sema_p(&iport->iport_rls_sema);
 741         if (iport->iport_rls_cb_data.fct_els_res != FCT_SUCCESS)
 742                 ret = EIO;
 743         return (ret);
 744 }
 745 
 746 static int
 747 fct_forcelip(uint8_t *port_wwn, uint32_t *fctio_errno)
 748 {
 749         fct_status_t             rval;
 750         fct_i_local_port_t      *iport;
 751 
 752         mutex_enter(&fct_global_mutex);
 753         iport = fct_get_iport_per_wwn(port_wwn);
 754         mutex_exit(&fct_global_mutex);
 755         if (iport == NULL) {
 756                 return (-1);
 757         }
 758 
 759         iport->iport_port->port_ctl(iport->iport_port,
 760             FCT_CMD_FORCE_LIP, &rval);
 761         if (rval != FCT_SUCCESS) {
 762                 *fctio_errno = FCTIO_FAILURE;
 763         } else {
 764                 *fctio_errno = 0;
 765         }
 766 
 767         return (0);
 768 }
 769 
 770 static int
 771 fct_fctiocmd(intptr_t data, int mode)
 772 {
 773         int ret  = 0;
 774         void            *ibuf = NULL;
 775         void            *obuf = NULL;
 776         void            *abuf = NULL;
 777         fctio_t         *fctio;
 778         uint32_t        attr_length;
 779 
 780         ret = fct_copyin_iocdata(data, mode, &fctio, &ibuf, &abuf, &obuf);
 781         if (ret) {
 782                 return (ret);
 783         }
 784 
 785         switch (fctio->fctio_cmd) {
 786         case FCTIO_ADAPTER_LIST: {
 787                 fc_tgt_hba_list_t *list = (fc_tgt_hba_list_t *)obuf;
 788                 int             count;
 789 
 790                 if (fctio->fctio_olen < sizeof (fc_tgt_hba_list_t)) {
 791                         ret = EINVAL;
 792                         break;
 793                 }
 794                 list->numPorts = (fctio->fctio_olen -
 795                     sizeof (fc_tgt_hba_list_t))/8 + 1;
 796 
 797                 list->version = FCT_HBA_LIST_VERSION;
 798                 count = fct_get_port_list((char *)list->port_wwn,
 799                     list->numPorts);
 800                 if (count < 0) {
 801                         ret = ENXIO;
 802                         break;
 803                 }
 804                 if (count > list->numPorts) {
 805                         fctio->fctio_errno = FCTIO_MOREDATA;
 806                         ret = ENOSPC;
 807                 }
 808                 list->numPorts = count;
 809                 break;
 810                 }
 811         case FCTIO_GET_ADAPTER_ATTRIBUTES: {
 812                 fc_tgt_hba_adapter_attributes_t *hba_attr;
 813                 uint8_t *port_wwn = (uint8_t *)ibuf;
 814 
 815                 attr_length = sizeof (fc_tgt_hba_adapter_attributes_t);
 816                 if (fctio->fctio_olen < attr_length ||
 817                     fctio->fctio_xfer != FCTIO_XFER_READ) {
 818                         ret = EINVAL;
 819                         break;
 820                 }
 821                 hba_attr = (fc_tgt_hba_adapter_attributes_t *)obuf;
 822 
 823                 mutex_enter(&fct_global_mutex);
 824                 ret = fct_get_adapter_attr(port_wwn, hba_attr,
 825                     &fctio->fctio_errno);
 826                 mutex_exit(&fct_global_mutex);
 827 
 828                 break;
 829                 }
 830         case FCTIO_GET_ADAPTER_PORT_ATTRIBUTES: {
 831                 fc_tgt_hba_port_attributes_t *port_attr;
 832 
 833                 uint8_t *port_wwn = (uint8_t *)ibuf;
 834 
 835                 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
 836                 if (fctio->fctio_olen < attr_length ||
 837                     fctio->fctio_xfer != FCTIO_XFER_READ) {
 838                         ret = EINVAL;
 839                         break;
 840                 }
 841                 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
 842 
 843                 mutex_enter(&fct_global_mutex);
 844                 ret = fct_get_adapter_port_attr(NULL, port_wwn, port_attr,
 845                     &fctio->fctio_errno);
 846                 mutex_exit(&fct_global_mutex);
 847 
 848                 break;
 849                 }
 850         case FCTIO_GET_DISCOVERED_PORT_ATTRIBUTES: {
 851                 uint8_t *port_wwn = (uint8_t *)ibuf;
 852                 uint32_t *port_index = (uint32_t *)abuf;
 853                 fc_tgt_hba_port_attributes_t *port_attr;
 854 
 855                 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
 856                 if (fctio->fctio_olen < attr_length ||
 857                     fctio->fctio_xfer != FCTIO_XFER_READ) {
 858                         ret = EINVAL;
 859                         break;
 860                 }
 861                 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
 862 
 863                 mutex_enter(&fct_global_mutex);
 864                 ret = fct_get_discovered_port_attr(NULL, port_wwn,
 865                     *port_index, port_attr, &fctio->fctio_errno);
 866                 mutex_exit(&fct_global_mutex);
 867 
 868                 break;
 869                 }
 870         case FCTIO_GET_PORT_ATTRIBUTES: {
 871                 uint8_t *port_wwn = (uint8_t *)ibuf;
 872                 fc_tgt_hba_port_attributes_t *port_attr;
 873 
 874                 attr_length = sizeof (fc_tgt_hba_port_attributes_t);
 875                 if (fctio->fctio_olen < attr_length ||
 876                     fctio->fctio_xfer != FCTIO_XFER_READ) {
 877                         ret = EINVAL;
 878                         break;
 879                 }
 880 
 881                 port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
 882 
 883                 mutex_enter(&fct_global_mutex);
 884                 ret = fct_get_port_attr(port_wwn, port_attr,
 885                     &fctio->fctio_errno);
 886                 mutex_exit(&fct_global_mutex);
 887 
 888                 break;
 889                 }
 890         case FCTIO_GET_ADAPTER_PORT_STATS: {
 891                 uint8_t *port_wwn = (uint8_t *)ibuf;
 892                 fc_tgt_hba_adapter_port_stats_t *port_stats =
 893                     (fc_tgt_hba_adapter_port_stats_t *)obuf;
 894                 mutex_enter(&fct_global_mutex);
 895                 ret = fct_get_port_stats(port_wwn, port_stats,
 896                     &fctio->fctio_errno);
 897                 mutex_exit(&fct_global_mutex);
 898                 break;
 899                 }
 900         case FCTIO_GET_LINK_STATUS: {
 901                 uint8_t *port_wwn = (uint8_t *)ibuf;
 902                 fct_port_link_status_t *link_status =
 903                     (fct_port_link_status_t *)obuf;
 904                 uint64_t *dest_id = abuf;
 905 
 906                 mutex_enter(&fct_global_mutex);
 907                 ret = fct_get_link_status(port_wwn, dest_id, link_status,
 908                     &fctio->fctio_errno);
 909                 mutex_exit(&fct_global_mutex);
 910                 break;
 911                 }
 912 
 913         case FCTIO_FORCE_LIP:
 914                 ret = fct_forcelip((uint8_t *)ibuf, &fctio->fctio_errno);
 915                 break;
 916 
 917         default:
 918                 break;
 919         }
 920         if (ret == 0) {
 921                 ret = fct_copyout_iocdata(data, mode, fctio, obuf);
 922         } else if (fctio->fctio_errno) {
 923                 (void) fct_copyout_iocdata(data, mode, fctio, obuf);
 924         }
 925 
 926         if (obuf) {
 927                 kmem_free(obuf, fctio->fctio_olen);
 928                 obuf = NULL;
 929         }
 930         if (abuf) {
 931                 kmem_free(abuf, fctio->fctio_alen);
 932                 abuf = NULL;
 933         }
 934 
 935         if (ibuf) {
 936                 kmem_free(ibuf, fctio->fctio_ilen);
 937                 ibuf = NULL;
 938         }
 939         kmem_free(fctio, sizeof (fctio_t));
 940         return (ret);
 941 }
 942 
 943 typedef struct {
 944         void    *bp;    /* back pointer from internal struct to main struct */
 945         int     alloc_size;
 946         fct_struct_id_t struct_id;
 947 } __ifct_t;
 948 
 949 typedef struct {
 950         __ifct_t        *fp;    /* Framework private */
 951         void            *cp;    /* Caller private */
 952         void            *ss;    /* struct specific */
 953 } __fct_t;
 954 
 955 static struct {
 956         int shared;
 957         int fw_private;
 958         int struct_specific;
 959 } fct_sizes[] = { { 0, 0, 0 },
 960         { GET_STRUCT_SIZE(fct_local_port_t),
 961                 GET_STRUCT_SIZE(fct_i_local_port_t), 0 },
 962         { GET_STRUCT_SIZE(fct_remote_port_t),
 963                 GET_STRUCT_SIZE(fct_i_remote_port_t), 0 },
 964         { GET_STRUCT_SIZE(fct_cmd_t),
 965                 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
 966         { GET_STRUCT_SIZE(fct_cmd_t),
 967                 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
 968         { GET_STRUCT_SIZE(fct_cmd_t),
 969                 GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_sol_ct_t) },
 970         { GET_STRUCT_SIZE(fct_cmd_t), GET_STRUCT_SIZE(fct_i_cmd_t),
 971                 GET_STRUCT_SIZE(fct_rcvd_abts_t) },
 972         { GET_STRUCT_SIZE(fct_cmd_t),   /* FCT_STRUCT_CMD_FCP_XCHG */
 973                 GET_STRUCT_SIZE(fct_i_cmd_t), 0 },
 974         { GET_STRUCT_SIZE(fct_dbuf_store_t),
 975                 GET_STRUCT_SIZE(__ifct_t), 0 }
 976 };
 977 
 978 void *
 979 fct_alloc(fct_struct_id_t struct_id, int additional_size, int flags)
 980 {
 981         int fct_size;
 982         int kmem_flag;
 983         __fct_t *sh;
 984 
 985         if ((struct_id == 0) || (struct_id >= FCT_MAX_STRUCT_IDS))
 986                 return (NULL);
 987 
 988         if ((curthread->t_flag & T_INTR_THREAD) || (flags & AF_FORCE_NOSLEEP)) {
 989                 kmem_flag = KM_NOSLEEP;
 990         } else {
 991                 kmem_flag = KM_SLEEP;
 992         }
 993 
 994         additional_size = (additional_size + 7) & (~7);
 995         fct_size = fct_sizes[struct_id].shared +
 996             fct_sizes[struct_id].fw_private +
 997             fct_sizes[struct_id].struct_specific + additional_size;
 998 
 999         if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1000                 stmf_local_port_t *lport;
1001 
1002                 lport = (stmf_local_port_t *)stmf_alloc(
1003                     STMF_STRUCT_STMF_LOCAL_PORT, fct_size, flags);
1004                 if (lport) {
1005                         sh = (__fct_t *)lport->lport_port_private;
1006                         sh->ss = lport;
1007                 } else {
1008                         return (NULL);
1009                 }
1010         } else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1011                 stmf_dbuf_store_t *ds;
1012 
1013                 ds = (stmf_dbuf_store_t *)stmf_alloc(STMF_STRUCT_DBUF_STORE,
1014                     fct_size, flags);
1015                 if (ds) {
1016                         sh = (__fct_t *)ds->ds_port_private;
1017                         sh->ss = ds;
1018                 } else {
1019                         return (NULL);
1020                 }
1021         } else {
1022                 sh = (__fct_t *)kmem_zalloc(fct_size, kmem_flag);
1023         }
1024 
1025         if (sh == NULL)
1026                 return (NULL);
1027 
1028         sh->fp = (__ifct_t *)GET_BYTE_OFFSET(sh, fct_sizes[struct_id].shared);
1029         sh->cp = GET_BYTE_OFFSET(sh->fp, fct_sizes[struct_id].fw_private);
1030         if (fct_sizes[struct_id].struct_specific)
1031                 sh->ss = GET_BYTE_OFFSET(sh->cp, additional_size);
1032 
1033         sh->fp->bp = sh;
1034         sh->fp->alloc_size = fct_size;
1035         sh->fp->struct_id = struct_id;
1036 
1037         if (struct_id == FCT_STRUCT_CMD_FCP_XCHG) {
1038                 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_FCP_XCHG;
1039         } else if (struct_id == FCT_STRUCT_CMD_RCVD_ELS) {
1040                 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ELS;
1041         } else if (struct_id == FCT_STRUCT_CMD_SOL_ELS) {
1042                 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_ELS;
1043         } else if (struct_id == FCT_STRUCT_CMD_RCVD_ABTS) {
1044                 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ABTS;
1045         } else if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1046                 ((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_CT;
1047         }
1048 
1049         return (sh);
1050 }
1051 
1052 void
1053 fct_free(void *ptr)
1054 {
1055         __fct_t *sh = (__fct_t *)ptr;
1056         fct_struct_id_t struct_id = sh->fp->struct_id;
1057 
1058         if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1059                 fct_sol_ct_t *ct = (fct_sol_ct_t *)
1060                     ((fct_cmd_t *)ptr)->cmd_specific;
1061 
1062                 if (ct->ct_req_alloc_size) {
1063                         kmem_free(ct->ct_req_payload, ct->ct_req_alloc_size);
1064                 }
1065                 if (ct->ct_resp_alloc_size) {
1066                         kmem_free(ct->ct_resp_payload, ct->ct_resp_alloc_size);
1067                 }
1068         } else if ((struct_id == FCT_STRUCT_CMD_RCVD_ELS) ||
1069             (struct_id == FCT_STRUCT_CMD_SOL_ELS)) {
1070                 fct_els_t *els = (fct_els_t *)
1071                         ((fct_cmd_t *)ptr)->cmd_specific;
1072                 if (els->els_req_alloc_size)
1073                         kmem_free(els->els_req_payload,
1074                                 els->els_req_alloc_size);
1075                 if (els->els_resp_alloc_size)
1076                         kmem_free(els->els_resp_payload,
1077                                 els->els_resp_alloc_size);
1078         }
1079 
1080         if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1081                 stmf_free(((fct_local_port_t *)ptr)->port_lport);
1082         } else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1083                 stmf_free(((fct_dbuf_store_t *)ptr)->fds_ds);
1084         } else {
1085                 kmem_free(ptr, sh->fp->alloc_size);
1086         }
1087 }
1088 
1089 stmf_data_buf_t *
1090 fct_alloc_dbuf(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
1091     uint32_t flags)
1092 {
1093         fct_local_port_t *port = (fct_local_port_t *)
1094             task->task_lport->lport_port_private;
1095 
1096         return (port->port_fds->fds_alloc_data_buf(port, size,
1097             pminsize, flags));
1098 }
1099 
1100 stmf_status_t
1101 fct_setup_dbuf(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t flags)
1102 {
1103         fct_local_port_t *port = (fct_local_port_t *)
1104             task->task_lport->lport_port_private;
1105 
1106         ASSERT(port->port_fds->fds_setup_dbuf != NULL);
1107         if (port->port_fds->fds_setup_dbuf == NULL)
1108                 return (STMF_FAILURE);
1109 
1110         return (port->port_fds->fds_setup_dbuf(port, dbuf, flags));
1111 }
1112 
1113 void
1114 fct_teardown_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1115 {
1116         fct_dbuf_store_t *fds = ds->ds_port_private;
1117 
1118         fds->fds_teardown_dbuf(fds, dbuf);
1119 }
1120 
1121 void
1122 fct_free_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1123 {
1124         fct_dbuf_store_t *fds;
1125 
1126         fds = (fct_dbuf_store_t *)ds->ds_port_private;
1127 
1128         fds->fds_free_data_buf(fds, dbuf);
1129 }
1130 
1131 static uint32_t taskq_cntr = 0;
1132 
1133 fct_status_t
1134 fct_register_local_port(fct_local_port_t *port)
1135 {
1136         fct_i_local_port_t      *iport;
1137         stmf_local_port_t       *lport;
1138         fct_cmd_slot_t          *slot;
1139         int                     i;
1140         char                    taskq_name[FCT_TASKQ_NAME_LEN];
1141 
1142         iport = (fct_i_local_port_t *)port->port_fct_private;
1143         if (port->port_fca_version != FCT_FCA_MODREV_1) {
1144                 cmn_err(CE_WARN,
1145                     "fct: %s driver version mismatch",
1146                     port->port_default_alias);
1147                 return (FCT_FAILURE);
1148         }
1149         if (port->port_default_alias) {
1150                 int l = strlen(port->port_default_alias);
1151 
1152                 if (l < 16) {
1153                         iport->iport_alias = iport->iport_alias_mem;
1154                 } else {
1155                         iport->iport_alias =
1156                             (char *)kmem_zalloc(l+1, KM_SLEEP);
1157                 }
1158                 (void) strcpy(iport->iport_alias, port->port_default_alias);
1159         } else {
1160                 iport->iport_alias = NULL;
1161         }
1162         stmf_wwn_to_devid_desc((scsi_devid_desc_t *)iport->iport_id,
1163             port->port_pwwn, PROTOCOL_FIBRE_CHANNEL);
1164         (void) snprintf(taskq_name, sizeof (taskq_name), "stmf_fct_taskq_%d",
1165             atomic_inc_32_nv(&taskq_cntr));
1166         if ((iport->iport_worker_taskq = ddi_taskq_create(NULL,
1167             taskq_name, 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
1168                 return (FCT_FAILURE);
1169         }
1170         mutex_init(&iport->iport_worker_lock, NULL, MUTEX_DRIVER, NULL);
1171         cv_init(&iport->iport_worker_cv, NULL, CV_DRIVER, NULL);
1172         rw_init(&iport->iport_lock, NULL, RW_DRIVER, NULL);
1173         sema_init(&iport->iport_rls_sema, 0, NULL, SEMA_DRIVER, NULL);
1174 
1175         /* Remote port mgmt */
1176         iport->iport_rp_slots = (fct_i_remote_port_t **)kmem_zalloc(
1177             port->port_max_logins * sizeof (fct_i_remote_port_t *), KM_SLEEP);
1178         iport->iport_rp_tb = kmem_zalloc(rportid_table_size *
1179             sizeof (fct_i_remote_port_t *), KM_SLEEP);
1180 
1181         /* fct_cmds for SCSI traffic */
1182         iport->iport_total_alloced_ncmds = 0;
1183         iport->iport_cached_ncmds = 0;
1184         port->port_fca_fcp_cmd_size =
1185             (port->port_fca_fcp_cmd_size + 7) & ~7;
1186         iport->iport_cached_cmdlist = NULL;
1187         mutex_init(&iport->iport_cached_cmd_lock, NULL, MUTEX_DRIVER, NULL);
1188 
1189         /* Initialize cmd slots */
1190         iport->iport_cmd_slots = (fct_cmd_slot_t *)kmem_zalloc(
1191             port->port_max_xchges * sizeof (fct_cmd_slot_t), KM_SLEEP);
1192         iport->iport_next_free_slot = 0;
1193         for (i = 0; i < port->port_max_xchges; ) {
1194                 slot = &iport->iport_cmd_slots[i];
1195                 slot->slot_no = (uint16_t)i;
1196                 slot->slot_next = (uint16_t)(++i);
1197         }
1198         slot->slot_next = FCT_SLOT_EOL;
1199         iport->iport_nslots_free = port->port_max_xchges;
1200 
1201         iport->iport_task_green_limit =
1202             (port->port_max_xchges * FCT_TASK_GREEN_LIMIT) / 100;
1203         iport->iport_task_yellow_limit =
1204             (port->port_max_xchges * FCT_TASK_YELLOW_LIMIT) / 100;
1205         iport->iport_task_red_limit =
1206             (port->port_max_xchges * FCT_TASK_RED_LIMIT) / 100;
1207 
1208         /* Start worker thread */
1209         atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1210         (void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1211             fct_port_worker, port, DDI_SLEEP);
1212         /* Wait for taskq to start */
1213         while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1214                 delay(1);
1215         }
1216 
1217         lport = port->port_lport;
1218         lport->lport_id = (scsi_devid_desc_t *)iport->iport_id;
1219         lport->lport_alias = iport->iport_alias;
1220         lport->lport_pp = port->port_pp;
1221         port->port_fds->fds_ds->ds_alloc_data_buf = fct_alloc_dbuf;
1222         port->port_fds->fds_ds->ds_free_data_buf = fct_free_dbuf;
1223         port->port_fds->fds_ds->ds_setup_dbuf = fct_setup_dbuf;
1224         port->port_fds->fds_ds->ds_teardown_dbuf = fct_teardown_dbuf;
1225         lport->lport_ds = port->port_fds->fds_ds;
1226         lport->lport_xfer_data = fct_xfer_scsi_data;
1227         lport->lport_send_status = fct_send_scsi_status;
1228         lport->lport_task_free = fct_scsi_task_free;
1229         lport->lport_abort = fct_scsi_abort;
1230         lport->lport_ctl = fct_ctl;
1231         lport->lport_info = fct_info;
1232         lport->lport_event_handler = fct_event_handler;
1233         /* set up as alua participating port */
1234         stmf_set_port_alua(lport);
1235         if (stmf_register_local_port(port->port_lport) != FCT_SUCCESS) {
1236                 goto fct_regport_fail1;
1237         }
1238         (void) stmf_lport_add_event(lport, LPORT_EVENT_INITIAL_LUN_MAPPED);
1239 
1240         mutex_enter(&fct_global_mutex);
1241         iport->iport_next = fct_iport_list;
1242         iport->iport_prev = NULL;
1243         if (iport->iport_next)
1244                 iport->iport_next->iport_prev = iport;
1245         fct_iport_list = iport;
1246         mutex_exit(&fct_global_mutex);
1247 
1248         fct_init_kstats(iport);
1249 
1250         fct_log_local_port_event(port, ESC_SUNFC_PORT_ATTACH);
1251 
1252         return (FCT_SUCCESS);
1253 
1254 fct_regport_fail1:;
1255         /* Stop the taskq 1st */
1256         if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1257                 atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1258                 cv_broadcast(&iport->iport_worker_cv);
1259                 while (iport->iport_flags & IPORT_WORKER_RUNNING) {
1260                         delay(1);
1261                 }
1262         }
1263         ddi_taskq_destroy(iport->iport_worker_taskq);
1264         if (iport->iport_rp_tb) {
1265                 kmem_free(iport->iport_rp_tb, rportid_table_size *
1266                     sizeof (fct_i_remote_port_t *));
1267         }
1268         return (FCT_FAILURE);
1269 }
1270 
1271 fct_status_t
1272 fct_deregister_local_port(fct_local_port_t *port)
1273 {
1274         fct_i_local_port_t      *iport;
1275         fct_i_cmd_t             *icmd, *next_icmd;
1276         int                     ndx;
1277 
1278         iport = (fct_i_local_port_t *)port->port_fct_private;
1279 
1280         if ((iport->iport_state != FCT_STATE_OFFLINE) ||
1281             iport->iport_state_not_acked) {
1282                 return (FCT_FAILURE);
1283         }
1284 
1285         /* Stop the taskq 1st */
1286         if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1287                 atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1288                 cv_broadcast(&iport->iport_worker_cv);
1289                 for (ndx = 0; ndx < 100; ndx++) {
1290                         if ((iport->iport_flags & IPORT_WORKER_RUNNING)
1291                             == 0) {
1292                                 break;
1293                         }
1294                         delay(drv_usectohz(10000));
1295                 }
1296                 if (ndx == 100) {
1297                         atomic_and_32(&iport->iport_flags,
1298                             ~IPORT_TERMINATE_WORKER);
1299                         return (FCT_WORKER_STUCK);
1300                 }
1301         }
1302 
1303         if (stmf_deregister_local_port(port->port_lport) != FCT_SUCCESS) {
1304                 goto fct_deregport_fail1;
1305         }
1306 
1307         mutex_enter(&fct_global_mutex);
1308         if (iport->iport_next)
1309                 iport->iport_next->iport_prev = iport->iport_prev;
1310         if (iport->iport_prev)
1311                 iport->iport_prev->iport_next = iport->iport_next;
1312         else
1313                 fct_iport_list = iport->iport_next;
1314         mutex_exit(&fct_global_mutex);
1315         /*
1316          * At this time, there should be no outstanding and pending
1317          * I/Os, so we can just release resources.
1318          */
1319         ASSERT(iport->iport_total_alloced_ncmds == iport->iport_cached_ncmds);
1320         for (icmd = iport->iport_cached_cmdlist; icmd; icmd = next_icmd) {
1321                 next_icmd = icmd->icmd_next;
1322                 fct_free(icmd->icmd_cmd);
1323         }
1324         mutex_destroy(&iport->iport_cached_cmd_lock);
1325         kmem_free(iport->iport_cmd_slots, port->port_max_xchges *
1326             sizeof (fct_cmd_slot_t));
1327         kmem_free(iport->iport_rp_slots, port->port_max_logins *
1328             sizeof (fct_i_remote_port_t *));
1329         rw_destroy(&iport->iport_lock);
1330         cv_destroy(&iport->iport_worker_cv);
1331         sema_destroy(&iport->iport_rls_sema);
1332         mutex_destroy(&iport->iport_worker_lock);
1333         ddi_taskq_destroy(iport->iport_worker_taskq);
1334         if (iport->iport_rp_tb) {
1335                 kmem_free(iport->iport_rp_tb, rportid_table_size *
1336                     sizeof (fct_i_remote_port_t *));
1337         }
1338 
1339         if (iport->iport_kstat_portstat) {
1340                 kstat_delete(iport->iport_kstat_portstat);
1341         }
1342 
1343         fct_log_local_port_event(port, ESC_SUNFC_PORT_DETACH);
1344         return (FCT_SUCCESS);
1345 
1346 fct_deregport_fail1:;
1347         /* Restart the worker */
1348         atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1349         (void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1350             fct_port_worker, port, DDI_SLEEP);
1351         /* Wait for taskq to start */
1352         while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1353                 delay(1);
1354         }
1355         return (FCT_FAILURE);
1356 }
1357 
1358 /* ARGSUSED */
1359 void
1360 fct_handle_event(fct_local_port_t *port, int event_id, uint32_t event_flags,
1361                 caddr_t arg)
1362 {
1363         char                    info[FCT_INFO_LEN];
1364         fct_i_event_t           *e;
1365         fct_i_local_port_t      *iport = (fct_i_local_port_t *)
1366             port->port_fct_private;
1367 
1368         e = kmem_zalloc(sizeof (fct_i_event_t), KM_NOSLEEP);
1369 
1370         if (e == NULL) {
1371                 /*
1372                  * XXX Throw HBA fatal error event
1373                  */
1374                 (void) snprintf(info, sizeof (info),
1375                     "fct_handle_event: iport-%p, allocation "
1376                     "of fct_i_event failed", (void *)iport);
1377                 (void) fct_port_shutdown(iport->iport_port,
1378                     STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1379                 return;
1380         }
1381         /* Just queue the event */
1382         e->event_type = event_id;
1383         mutex_enter(&iport->iport_worker_lock);
1384         if (iport->iport_event_head == NULL) {
1385                 iport->iport_event_head = iport->iport_event_tail = e;
1386         } else {
1387                 iport->iport_event_tail->event_next = e;
1388                 iport->iport_event_tail = e;
1389         }
1390         if (IS_WORKER_SLEEPING(iport))
1391                 cv_signal(&iport->iport_worker_cv);
1392         mutex_exit(&iport->iport_worker_lock);
1393 }
1394 
1395 /*
1396  * Called with iport_lock held as reader.
1397  */
1398 fct_i_remote_port_t *
1399 fct_portid_to_portptr(fct_i_local_port_t *iport, uint32_t portid)
1400 {
1401         fct_i_remote_port_t     *irp;
1402 
1403         irp = iport->iport_rp_tb[FCT_PORTID_HASH_FUNC(portid)];
1404         for (; irp != NULL; irp = irp->irp_next) {
1405                 if (irp->irp_portid == portid)
1406                         return (irp);
1407         }
1408 
1409         return (NULL);
1410 
1411 }
1412 
1413 /*
1414  * Called with irp_lock held as writer.
1415  */
1416 void
1417 fct_queue_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1418 {
1419         int hash_key =
1420             FCT_PORTID_HASH_FUNC(irp->irp_portid);
1421 
1422         irp->irp_next = iport->iport_rp_tb[hash_key];
1423         iport->iport_rp_tb[hash_key] = irp;
1424         iport->iport_nrps++;
1425 }
1426 
1427 /*
1428  * Called with irp_lock and iport_lock held as writer.
1429  */
1430 void
1431 fct_deque_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1432 {
1433         fct_i_remote_port_t     *irp_next = NULL;
1434         fct_i_remote_port_t     *irp_last = NULL;
1435         int hash_key                      =
1436             FCT_PORTID_HASH_FUNC(irp->irp_portid);
1437 
1438         irp_next = iport->iport_rp_tb[hash_key];
1439         irp_last = NULL;
1440         while (irp_next != NULL) {
1441                 if (irp == irp_next) {
1442                         if (irp->irp_flags & IRP_PLOGI_DONE) {
1443                                 atomic_dec_32(&iport->iport_nrps_login);
1444                         }
1445                         atomic_and_32(&irp->irp_flags,
1446                             ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1447                         break;
1448                 }
1449                 irp_last = irp_next;
1450                 irp_next = irp_next->irp_next;
1451         }
1452 
1453         if (irp_next) {
1454                 if (irp_last == NULL) {
1455                         iport->iport_rp_tb[hash_key] =
1456                             irp->irp_next;
1457                 } else {
1458                         irp_last->irp_next = irp->irp_next;
1459                 }
1460                 irp->irp_next = NULL;
1461                 iport->iport_nrps--;
1462         }
1463 }
1464 
1465 int
1466 fct_is_irp_logging_out(fct_i_remote_port_t *irp, int force_implicit)
1467 {
1468         int logging_out = 0;
1469 
1470         rw_enter(&irp->irp_lock, RW_WRITER);
1471         if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1472                 logging_out = 0;
1473                 goto ilo_done;
1474         }
1475         if ((irp->irp_els_list == NULL) && (irp->irp_deregister_timer)) {
1476                 if (force_implicit && irp->irp_nonfcp_xchg_count) {
1477                         logging_out = 0;
1478                 } else {
1479                         logging_out = 1;
1480                 }
1481                 goto ilo_done;
1482         }
1483         if (irp->irp_els_list) {
1484                 fct_i_cmd_t *icmd;
1485                 /* Last session affecting ELS should be a LOGO */
1486                 for (icmd = irp->irp_els_list; icmd; icmd = icmd->icmd_next) {
1487                         uint8_t op = (ICMD_TO_ELS(icmd))->els_req_payload[0];
1488                         if (op == ELS_OP_LOGO) {
1489                                 if (force_implicit) {
1490                                         if (icmd->icmd_flags & ICMD_IMPLICIT)
1491                                                 logging_out = 1;
1492                                         else
1493                                                 logging_out = 0;
1494                                 } else {
1495                                         logging_out = 1;
1496                                 }
1497                         } else if ((op == ELS_OP_PLOGI) ||
1498                             (op == ELS_OP_PRLI) ||
1499                             (op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
1500                                 logging_out = 0;
1501                         }
1502                 }
1503         }
1504 ilo_done:;
1505         rw_exit(&irp->irp_lock);
1506 
1507         return (logging_out);
1508 }
1509 
1510 /*
1511  * The force_implicit flag enforces the implicit semantics which may be
1512  * needed if a received logout got stuck e.g. a response to a received
1513  * LOGO never came back from the FCA.
1514  */
1515 int
1516 fct_implicitly_logo_all(fct_i_local_port_t *iport, int force_implicit)
1517 {
1518         fct_i_remote_port_t     *irp = NULL;
1519         fct_cmd_t               *cmd = NULL;
1520         int                      i   = 0;
1521         int                     nports = 0;
1522 
1523         if (!iport->iport_nrps) {
1524                 return (nports);
1525         }
1526 
1527         rw_enter(&iport->iport_lock, RW_WRITER);
1528         for (i = 0; i < rportid_table_size; i++) {
1529                 irp = iport->iport_rp_tb[i];
1530                 while (irp) {
1531                         if ((!(irp->irp_flags & IRP_PLOGI_DONE)) &&
1532                             (fct_is_irp_logging_out(irp, force_implicit))) {
1533                                 irp = irp->irp_next;
1534                                 continue;
1535                         }
1536 
1537                         cmd = fct_create_solels(iport->iport_port, irp->irp_rp,
1538                             1, ELS_OP_LOGO, 0, fct_logo_cb);
1539                         if (cmd == NULL) {
1540                                 stmf_trace(iport->iport_alias,
1541                                     "fct_implictly_logo_all: cmd null");
1542                                 rw_exit(&iport->iport_lock);
1543 
1544                                 return (nports);
1545                         }
1546 
1547                         fct_post_implicit_logo(cmd);
1548                         nports++;
1549                         irp = irp->irp_next;
1550                 }
1551         }
1552         rw_exit(&iport->iport_lock);
1553 
1554         return (nports);
1555 }
1556 
1557 void
1558 fct_rehash(fct_i_local_port_t *iport)
1559 {
1560         fct_i_remote_port_t **iport_rp_tb_tmp;
1561         fct_i_remote_port_t **iport_rp_tb_new;
1562         fct_i_remote_port_t *irp;
1563         fct_i_remote_port_t *irp_next;
1564         int i;
1565 
1566         iport_rp_tb_new = kmem_zalloc(rportid_table_size *
1567             sizeof (fct_i_remote_port_t *), KM_SLEEP);
1568         rw_enter(&iport->iport_lock, RW_WRITER);
1569         /* reconstruct the hash table */
1570         iport_rp_tb_tmp = iport->iport_rp_tb;
1571         iport->iport_rp_tb = iport_rp_tb_new;
1572         iport->iport_nrps = 0;
1573         for (i = 0; i < rportid_table_size; i++) {
1574                 irp = iport_rp_tb_tmp[i];
1575                 while (irp) {
1576                         irp_next = irp->irp_next;
1577                         fct_queue_rp(iport, irp);
1578                         irp = irp_next;
1579                 }
1580         }
1581         rw_exit(&iport->iport_lock);
1582         kmem_free(iport_rp_tb_tmp, rportid_table_size *
1583             sizeof (fct_i_remote_port_t *));
1584 
1585 }
1586 
1587 uint8_t
1588 fct_local_port_cleanup_done(fct_i_local_port_t *iport)
1589 {
1590         fct_i_remote_port_t *irp;
1591         int i;
1592 
1593         if (iport->iport_nrps_login)
1594                 return (0);
1595         /* loop all rps to check if the cmd have already been drained */
1596         for (i = 0; i < rportid_table_size; i++) {
1597                 irp = iport->iport_rp_tb[i];
1598                 while (irp) {
1599                         if (irp->irp_fcp_xchg_count ||
1600                             irp->irp_nonfcp_xchg_count)
1601                                 return (0);
1602                         irp = irp->irp_next;
1603                 }
1604         }
1605         return (1);
1606 }
1607 
1608 fct_cmd_t *
1609 fct_scsi_task_alloc(fct_local_port_t *port, uint16_t rp_handle,
1610                 uint32_t rportid, uint8_t *lun, uint16_t cdb_length,
1611                 uint16_t task_ext)
1612 {
1613         fct_cmd_t *cmd;
1614         fct_i_cmd_t *icmd;
1615         fct_i_local_port_t *iport =
1616             (fct_i_local_port_t *)port->port_fct_private;
1617         fct_i_remote_port_t *irp;
1618         scsi_task_t *task;
1619         fct_remote_port_t *rp;
1620         uint16_t cmd_slot;
1621 
1622         rw_enter(&iport->iport_lock, RW_READER);
1623         if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
1624                 rw_exit(&iport->iport_lock);
1625                 stmf_trace(iport->iport_alias, "cmd alloc called while the port"
1626                     " was offline");
1627                 return (NULL);
1628         }
1629 
1630         if (rp_handle == FCT_HANDLE_NONE) {
1631                 irp = fct_portid_to_portptr(iport, rportid);
1632                 if (irp == NULL) {
1633                         rw_exit(&iport->iport_lock);
1634                         stmf_trace(iport->iport_alias, "cmd received from "
1635                             "non existent port %x", rportid);
1636                         return (NULL);
1637                 }
1638         } else {
1639                 if ((rp_handle >= port->port_max_logins) ||
1640                     ((irp = iport->iport_rp_slots[rp_handle]) == NULL)) {
1641                         rw_exit(&iport->iport_lock);
1642                         stmf_trace(iport->iport_alias, "cmd received from "
1643                             "invalid port handle %x", rp_handle);
1644                         return (NULL);
1645                 }
1646         }
1647         rp = irp->irp_rp;
1648 
1649         rw_enter(&irp->irp_lock, RW_READER);
1650         if ((irp->irp_flags & IRP_PRLI_DONE) == 0) {
1651                 rw_exit(&irp->irp_lock);
1652                 rw_exit(&iport->iport_lock);
1653                 stmf_trace(iport->iport_alias, "cmd alloc called while fcp "
1654                     "login was not done. portid=%x, rp=%p", rp->rp_id, rp);
1655                 return (NULL);
1656         }
1657 
1658         mutex_enter(&iport->iport_cached_cmd_lock);
1659         if ((icmd = iport->iport_cached_cmdlist) != NULL) {
1660                 iport->iport_cached_cmdlist = icmd->icmd_next;
1661                 iport->iport_cached_ncmds--;
1662                 cmd = icmd->icmd_cmd;
1663         } else {
1664                 icmd = NULL;
1665         }
1666         mutex_exit(&iport->iport_cached_cmd_lock);
1667         if (icmd == NULL) {
1668                 cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_FCP_XCHG,
1669                     port->port_fca_fcp_cmd_size, 0);
1670                 if (cmd == NULL) {
1671                         rw_exit(&irp->irp_lock);
1672                         rw_exit(&iport->iport_lock);
1673                         stmf_trace(iport->iport_alias, "Ran out of "
1674                             "memory, port=%p", port);
1675                         return (NULL);
1676                 }
1677 
1678                 icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1679                 icmd->icmd_next = NULL;
1680                 cmd->cmd_port = port;
1681                 atomic_inc_32(&iport->iport_total_alloced_ncmds);
1682         }
1683 
1684         /*
1685          * The accuracy of iport_max_active_ncmds is not important
1686          */
1687         if ((iport->iport_total_alloced_ncmds - iport->iport_cached_ncmds) >
1688             iport->iport_max_active_ncmds) {
1689                 iport->iport_max_active_ncmds =
1690                     iport->iport_total_alloced_ncmds -
1691                     iport->iport_cached_ncmds;
1692         }
1693 
1694         /* Lets get a slot */
1695         cmd_slot = fct_alloc_cmd_slot(iport, cmd);
1696         if (cmd_slot == FCT_SLOT_EOL) {
1697                 rw_exit(&irp->irp_lock);
1698                 rw_exit(&iport->iport_lock);
1699                 stmf_trace(iport->iport_alias, "Ran out of xchg resources");
1700                 cmd->cmd_handle = 0;
1701                 fct_cmd_free(cmd);
1702                 return (NULL);
1703         }
1704         atomic_inc_16(&irp->irp_fcp_xchg_count);
1705         cmd->cmd_rp = rp;
1706         icmd->icmd_flags |= ICMD_IN_TRANSITION | ICMD_KNOWN_TO_FCA;
1707         rw_exit(&irp->irp_lock);
1708         rw_exit(&iport->iport_lock);
1709 
1710         icmd->icmd_start_time = ddi_get_lbolt();
1711 
1712         cmd->cmd_specific = stmf_task_alloc(port->port_lport, irp->irp_session,
1713             lun, cdb_length, task_ext);
1714         if ((task = (scsi_task_t *)cmd->cmd_specific) != NULL) {
1715                 task->task_port_private = cmd;
1716                 return (cmd);
1717         }
1718 
1719         fct_cmd_free(cmd);
1720 
1721         return (NULL);
1722 }
1723 
1724 void
1725 fct_scsi_task_free(scsi_task_t *task)
1726 {
1727         fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1728 
1729         cmd->cmd_comp_status = task->task_completion_status;
1730         fct_cmd_free(cmd);
1731 }
1732 
1733 void
1734 fct_post_rcvd_cmd(fct_cmd_t *cmd, stmf_data_buf_t *dbuf)
1735 {
1736         fct_dbuf_store_t *fds;
1737 
1738         if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1739                 fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1740                 fct_i_local_port_t *iport =
1741                     (fct_i_local_port_t *)cmd->cmd_port->port_fct_private;
1742                 fct_i_remote_port_t *irp =
1743                     (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private;
1744                 scsi_task_t *task = (scsi_task_t *)cmd->cmd_specific;
1745 
1746                 uint16_t irp_task = irp->irp_fcp_xchg_count;
1747                 uint32_t load = iport->iport_total_alloced_ncmds -
1748                     iport->iport_cached_ncmds;
1749 
1750                 DTRACE_FC_4(scsi__command,
1751                     fct_cmd_t, cmd,
1752                     fct_i_local_port_t, iport,
1753                     scsi_task_t, task,
1754                     fct_i_remote_port_t, irp);
1755 
1756                 if (load >= iport->iport_task_green_limit) {
1757                         if ((load < iport->iport_task_yellow_limit &&
1758                             irp_task >= 4) ||
1759                             (load >= iport->iport_task_yellow_limit &&
1760                             load < iport->iport_task_red_limit &&
1761                             irp_task >= 1) ||
1762                             (load >= iport->iport_task_red_limit))
1763                                 task->task_additional_flags |=
1764                                     TASK_AF_PORT_LOAD_HIGH;
1765                 }
1766                 /*
1767                  * If the target driver accepts sglists, fill in task fields.
1768                  */
1769                 fds = cmd->cmd_port->port_fds;
1770                 if (fds->fds_setup_dbuf != NULL) {
1771                         task->task_additional_flags |= TASK_AF_ACCEPT_LU_DBUF;
1772                         task->task_copy_threshold = fds->fds_copy_threshold;
1773                         task->task_max_xfer_len = fds->fds_max_sgl_xfer_len;
1774                         /*
1775                          * A single stream load encounters a little extra
1776                          * latency if large xfers are done in 1 chunk.
1777                          * Give a hint to the LU that starting the xfer
1778                          * with a smaller chunk would be better in this case.
1779                          * For any other load, use maximum chunk size.
1780                          */
1781                         if (load == 1) {
1782                                 /* estimate */
1783                                 task->task_1st_xfer_len = 128*1024;
1784                         } else {
1785                                 /* zero means no hint */
1786                                 task->task_1st_xfer_len = 0;
1787                         }
1788                 }
1789 
1790                 stmf_post_task((scsi_task_t *)cmd->cmd_specific, dbuf);
1791                 atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_TRANSITION);
1792                 return;
1793         }
1794         /* We dont need dbuf for other cmds */
1795         if (dbuf) {
1796                 cmd->cmd_port->port_fds->fds_free_data_buf(
1797                     cmd->cmd_port->port_fds, dbuf);
1798                 dbuf = NULL;
1799         }
1800         if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1801                 fct_handle_els(cmd);
1802                 return;
1803         }
1804         if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) {
1805                 fct_handle_rcvd_abts(cmd);
1806                 return;
1807         }
1808 
1809         ASSERT(0);
1810 }
1811 
1812 /*
1813  * This function bypasses fct_handle_els()
1814  */
1815 void
1816 fct_post_implicit_logo(fct_cmd_t *cmd)
1817 {
1818         fct_local_port_t *port = cmd->cmd_port;
1819         fct_i_local_port_t *iport =
1820             (fct_i_local_port_t *)port->port_fct_private;
1821         fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1822         fct_remote_port_t *rp = cmd->cmd_rp;
1823         fct_i_remote_port_t *irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1824 
1825         icmd->icmd_start_time = ddi_get_lbolt();
1826 
1827         rw_enter(&irp->irp_lock, RW_WRITER);
1828         atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
1829         atomic_inc_16(&irp->irp_nonfcp_xchg_count);
1830         atomic_inc_16(&irp->irp_sa_elses_count);
1831         /*
1832          * An implicit LOGO can also be posted to a irp where a PLOGI might
1833          * be in process. That PLOGI will reset this flag and decrement the
1834          * iport_nrps_login counter.
1835          */
1836         if (irp->irp_flags & IRP_PLOGI_DONE) {
1837                 atomic_dec_32(&iport->iport_nrps_login);
1838         }
1839         atomic_and_32(&irp->irp_flags, ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1840         atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1841         fct_post_to_discovery_queue(iport, irp, icmd);
1842         rw_exit(&irp->irp_lock);
1843 }
1844 
1845 /*
1846  * called with iport_lock held, return the slot number
1847  */
1848 uint16_t
1849 fct_alloc_cmd_slot(fct_i_local_port_t *iport, fct_cmd_t *cmd)
1850 {
1851         uint16_t cmd_slot;
1852         uint32_t old, new;
1853         fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1854 
1855         do {
1856                 old = iport->iport_next_free_slot;
1857                 cmd_slot = old & 0xFFFF;
1858                 if (cmd_slot == FCT_SLOT_EOL)
1859                         return (cmd_slot);
1860                 /*
1861                  * We use high order 16 bits as a counter which keeps on
1862                  * incrementing to avoid ABA issues with atomic lists.
1863                  */
1864                 new = ((old + (0x10000)) & 0xFFFF0000);
1865                 new |= iport->iport_cmd_slots[cmd_slot].slot_next;
1866         } while (atomic_cas_32(&iport->iport_next_free_slot, old, new) != old);
1867 
1868         atomic_dec_16(&iport->iport_nslots_free);
1869         iport->iport_cmd_slots[cmd_slot].slot_cmd = icmd;
1870         cmd->cmd_handle = (uint32_t)cmd_slot | 0x80000000 |
1871             (((uint32_t)(iport->iport_cmd_slots[cmd_slot].slot_uniq_cntr))
1872             << 24);
1873         return (cmd_slot);
1874 }
1875 
1876 /*
1877  * If icmd is not NULL, irp_lock must be held
1878  */
1879 void
1880 fct_post_to_discovery_queue(fct_i_local_port_t *iport,
1881     fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
1882 {
1883         fct_i_cmd_t     **p;
1884 
1885         ASSERT(!MUTEX_HELD(&iport->iport_worker_lock));
1886         if (icmd) {
1887                 icmd->icmd_next = NULL;
1888                 for (p = &irp->irp_els_list; *p != NULL;
1889                     p = &((*p)->icmd_next))
1890                         ;
1891 
1892                 *p = icmd;
1893                 atomic_or_32(&icmd->icmd_flags, ICMD_IN_IRP_QUEUE);
1894         }
1895 
1896         mutex_enter(&iport->iport_worker_lock);
1897         if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1898 
1899                 /*
1900                  * CAUTION: do not grab local_port/remote_port locks after
1901                  * grabbing the worker lock.
1902                  */
1903                 irp->irp_discovery_next = NULL;
1904                 if (iport->iport_rpwe_tail) {
1905                         iport->iport_rpwe_tail->irp_discovery_next = irp;
1906                         iport->iport_rpwe_tail = irp;
1907                 } else {
1908                         iport->iport_rpwe_head = iport->iport_rpwe_tail = irp;
1909                 }
1910 
1911                 atomic_or_32(&irp->irp_flags, IRP_IN_DISCOVERY_QUEUE);
1912         }
1913 
1914         /*
1915          * We need always signal the port worker irrespective of the fact that
1916          * irp is already in discovery queue or not.
1917          */
1918         if (IS_WORKER_SLEEPING(iport)) {
1919                 cv_signal(&iport->iport_worker_cv);
1920         }
1921         mutex_exit(&iport->iport_worker_lock);
1922 }
1923 
1924 stmf_status_t
1925 fct_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t ioflags)
1926 {
1927         fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1928 
1929         DTRACE_FC_5(xfer__start,
1930             fct_cmd_t, cmd,
1931             fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1932             scsi_task_t, task,
1933             fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1934             stmf_data_buf_t, dbuf);
1935 
1936         return (cmd->cmd_port->port_xfer_scsi_data(cmd, dbuf, ioflags));
1937 }
1938 
1939 void
1940 fct_scsi_data_xfer_done(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags)
1941 {
1942         fct_i_cmd_t     *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1943         uint32_t        old, new;
1944         uint32_t        iof = 0;
1945 
1946         DTRACE_FC_5(xfer__done,
1947             fct_cmd_t, cmd,
1948             fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1949             scsi_task_t, ((scsi_task_t *)cmd->cmd_specific),
1950             fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1951             stmf_data_buf_t, dbuf);
1952 
1953         if (ioflags & FCT_IOF_FCA_DONE) {
1954                 do {
1955                         old = new = icmd->icmd_flags;
1956                         if (old & ICMD_BEING_ABORTED) {
1957                                 return;
1958                         }
1959                         new &= ~ICMD_KNOWN_TO_FCA;
1960                 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1961                 iof = STMF_IOF_LPORT_DONE;
1962                 cmd->cmd_comp_status = dbuf->db_xfer_status;
1963         }
1964 
1965         if (icmd->icmd_flags & ICMD_BEING_ABORTED)
1966                 return;
1967         stmf_data_xfer_done((scsi_task_t *)cmd->cmd_specific, dbuf, iof);
1968 }
1969 
1970 stmf_status_t
1971 fct_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
1972 {
1973         fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1974 
1975         DTRACE_FC_4(scsi__response,
1976             fct_cmd_t, cmd,
1977             fct_i_local_port_t,
1978             (fct_i_local_port_t *)cmd->cmd_port->port_fct_private,
1979             scsi_task_t, task,
1980             fct_i_remote_port_t,
1981             (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private);
1982 
1983         return (cmd->cmd_port->port_send_cmd_response(cmd, ioflags));
1984 }
1985 
1986 void
1987 fct_send_response_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
1988 {
1989         fct_i_cmd_t     *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1990         fct_local_port_t *port = cmd->cmd_port;
1991         fct_i_local_port_t *iport = (fct_i_local_port_t *)
1992             port->port_fct_private;
1993         uint32_t old, new;
1994 
1995         if ((ioflags & FCT_IOF_FCA_DONE) == 0) {
1996                 /* Until we support confirmed completions, this is an error */
1997                 fct_queue_cmd_for_termination(cmd, s);
1998                 return;
1999         }
2000         do {
2001                 old = new = icmd->icmd_flags;
2002                 if (old & ICMD_BEING_ABORTED) {
2003                         return;
2004                 }
2005                 new &= ~ICMD_KNOWN_TO_FCA;
2006         } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2007 
2008         cmd->cmd_comp_status = s;
2009         if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2010                 stmf_send_status_done((scsi_task_t *)cmd->cmd_specific, s,
2011                     STMF_IOF_LPORT_DONE);
2012                 return;
2013         }
2014 
2015         if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
2016                 fct_cmd_free(cmd);
2017                 return;
2018         } else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
2019                 fct_handle_sol_els_completion(iport, icmd);
2020         } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
2021                 /* Tell the caller that we are done */
2022                 atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2023         } else {
2024                 ASSERT(0);
2025         }
2026 }
2027 
2028 void
2029 fct_cmd_free(fct_cmd_t *cmd)
2030 {
2031         char                    info[FCT_INFO_LEN];
2032         fct_i_cmd_t             *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2033         fct_local_port_t        *port = cmd->cmd_port;
2034         fct_i_local_port_t      *iport = (fct_i_local_port_t *)
2035             port->port_fct_private;
2036         fct_i_remote_port_t     *irp = NULL;
2037         int                     do_abts_acc = 0;
2038         uint32_t                old, new;
2039 
2040         ASSERT(!mutex_owned(&iport->iport_worker_lock));
2041         /* Give the slot back */
2042         if (CMD_HANDLE_VALID(cmd->cmd_handle)) {
2043                 uint16_t n = CMD_HANDLE_SLOT_INDEX(cmd->cmd_handle);
2044                 fct_cmd_slot_t *slot;
2045 
2046                 /*
2047                  * If anything went wrong, grab the lock as writer. This is
2048                  * probably unnecessary.
2049                  */
2050                 if ((cmd->cmd_comp_status != FCT_SUCCESS) ||
2051                     (icmd->icmd_flags & ICMD_ABTS_RECEIVED)) {
2052                         rw_enter(&iport->iport_lock, RW_WRITER);
2053                 } else {
2054                         rw_enter(&iport->iport_lock, RW_READER);
2055                 }
2056 
2057                 if ((icmd->icmd_flags & ICMD_ABTS_RECEIVED) &&
2058                     (cmd->cmd_link != NULL)) {
2059                         do_abts_acc = 1;
2060                 }
2061 
2062                 /* XXX Validate slot before freeing */
2063 
2064                 slot = &iport->iport_cmd_slots[n];
2065                 slot->slot_uniq_cntr++;
2066                 slot->slot_cmd = NULL;
2067                 do {
2068                         old = iport->iport_next_free_slot;
2069                         slot->slot_next = old & 0xFFFF;
2070                         new = (old + 0x10000) & 0xFFFF0000;
2071                         new |= slot->slot_no;
2072                 } while (atomic_cas_32(&iport->iport_next_free_slot,
2073                     old, new) != old);
2074                 cmd->cmd_handle = 0;
2075                 atomic_inc_16(&iport->iport_nslots_free);
2076                 if (cmd->cmd_rp) {
2077                         irp = (fct_i_remote_port_t *)
2078                             cmd->cmd_rp->rp_fct_private;
2079                         if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2080                                 atomic_dec_16(&irp->irp_fcp_xchg_count);
2081                         else
2082                                 atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2083                 }
2084                 rw_exit(&iport->iport_lock);
2085         } else if ((icmd->icmd_flags & ICMD_IMPLICIT) &&
2086             (icmd->icmd_flags & ICMD_IMPLICIT_CMD_HAS_RESOURCE)) {
2087                 /* for implicit cmd, no cmd slot is used */
2088                 if (cmd->cmd_rp) {
2089                         irp = (fct_i_remote_port_t *)
2090                             cmd->cmd_rp->rp_fct_private;
2091                         if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2092                                 atomic_dec_16(&irp->irp_fcp_xchg_count);
2093                         else
2094                                 atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2095                 }
2096         }
2097 
2098         if (do_abts_acc) {
2099                 fct_cmd_t *lcmd = cmd->cmd_link;
2100                 fct_fill_abts_acc(lcmd);
2101                 if (port->port_send_cmd_response(lcmd,
2102                     FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2103                         /*
2104                          * XXX Throw HBA fatal error event
2105                          * Later shutdown svc will terminate the ABTS in the end
2106                          */
2107                         (void) snprintf(info, sizeof (info),
2108                             "fct_cmd_free: iport-%p, ABTS_ACC"
2109                             " port_send_cmd_response failed", (void *)iport);
2110                         (void) fct_port_shutdown(iport->iport_port,
2111                             STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2112                         return;
2113                 } else {
2114                         fct_cmd_free(lcmd);
2115                         cmd->cmd_link = NULL;
2116                 }
2117         }
2118 
2119         /* Free the cmd */
2120         if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2121                 if (iport->iport_cached_ncmds < max_cached_ncmds) {
2122                         icmd->icmd_flags = 0;
2123                         mutex_enter(&iport->iport_cached_cmd_lock);
2124                         icmd->icmd_next = iport->iport_cached_cmdlist;
2125                         iport->iport_cached_cmdlist = icmd;
2126                         iport->iport_cached_ncmds++;
2127                         mutex_exit(&iport->iport_cached_cmd_lock);
2128                 } else {
2129                         atomic_dec_32(&iport->iport_total_alloced_ncmds);
2130                         fct_free(cmd);
2131                 }
2132         } else {
2133                 fct_free(cmd);
2134         }
2135 }
2136 
2137 /* ARGSUSED */
2138 stmf_status_t
2139 fct_scsi_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
2140                                                         uint32_t flags)
2141 {
2142         stmf_status_t ret = STMF_SUCCESS;
2143         scsi_task_t *task;
2144         fct_cmd_t *cmd;
2145         fct_i_cmd_t *icmd;
2146         fct_local_port_t *port;
2147         uint32_t old, new;
2148 
2149         ASSERT(abort_cmd == STMF_LPORT_ABORT_TASK);
2150 
2151         task = (scsi_task_t *)arg;
2152         cmd = (fct_cmd_t *)task->task_port_private;
2153         icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2154         port = (fct_local_port_t *)lport->lport_port_private;
2155 
2156         do {
2157                 old = new = icmd->icmd_flags;
2158                 if ((old & ICMD_KNOWN_TO_FCA) == 0)
2159                         return (STMF_NOT_FOUND);
2160                 ASSERT((old & ICMD_FCA_ABORT_CALLED) == 0);
2161                 new |= ICMD_BEING_ABORTED | ICMD_FCA_ABORT_CALLED;
2162         } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2163         ret = port->port_abort_cmd(port, cmd, 0);
2164         if ((ret == FCT_NOT_FOUND) || (ret == FCT_ABORT_SUCCESS)) {
2165                 atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2166         } else if (ret == FCT_BUSY) {
2167                 atomic_and_32(&icmd->icmd_flags, ~ICMD_FCA_ABORT_CALLED);
2168         }
2169 
2170         return (ret);
2171 }
2172 
2173 void
2174 fct_ctl(struct stmf_local_port *lport, int cmd, void *arg)
2175 {
2176         fct_local_port_t *port;
2177         fct_i_local_port_t *iport;
2178         stmf_change_status_t st;
2179         stmf_change_status_t *pst;
2180 
2181         ASSERT((cmd == STMF_CMD_LPORT_ONLINE) ||
2182             (cmd == STMF_ACK_LPORT_ONLINE_COMPLETE) ||
2183             (cmd == STMF_CMD_LPORT_OFFLINE) ||
2184             (cmd == STMF_ACK_LPORT_OFFLINE_COMPLETE) ||
2185             (cmd == FCT_CMD_PORT_ONLINE_COMPLETE) ||
2186             (cmd == FCT_CMD_PORT_OFFLINE_COMPLETE));
2187 
2188         port = (fct_local_port_t *)lport->lport_port_private;
2189         pst = (stmf_change_status_t *)arg;
2190         st.st_completion_status = STMF_SUCCESS;
2191         st.st_additional_info = NULL;
2192 
2193         iport = (fct_i_local_port_t *)port->port_fct_private;
2194         /*
2195          * We are mostly a passthrough, except during offline.
2196          */
2197         switch (cmd) {
2198         case STMF_CMD_LPORT_ONLINE:
2199                 if (iport->iport_state == FCT_STATE_ONLINE)
2200                         st.st_completion_status = STMF_ALREADY;
2201                 else if (iport->iport_state != FCT_STATE_OFFLINE)
2202                         st.st_completion_status = STMF_INVALID_ARG;
2203                 if (st.st_completion_status != STMF_SUCCESS) {
2204                         (void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport,
2205                             &st);
2206                         break;
2207                 }
2208                 iport->iport_state_not_acked = 1;
2209                 iport->iport_state = FCT_STATE_ONLINING;
2210                 port->port_ctl(port, FCT_CMD_PORT_ONLINE, arg);
2211                 break;
2212         case FCT_CMD_PORT_ONLINE_COMPLETE:
2213                 ASSERT(iport->iport_state == FCT_STATE_ONLINING);
2214                 if (pst->st_completion_status != FCT_SUCCESS) {
2215                         iport->iport_state = FCT_STATE_OFFLINE;
2216                         iport->iport_state_not_acked = 0;
2217                 } else {
2218                         iport->iport_state = FCT_STATE_ONLINE;
2219                 }
2220                 (void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, arg);
2221                 break;
2222         case STMF_ACK_LPORT_ONLINE_COMPLETE:
2223                 ASSERT(iport->iport_state == FCT_STATE_ONLINE);
2224                 iport->iport_state_not_acked = 0;
2225                 port->port_ctl(port, FCT_ACK_PORT_ONLINE_COMPLETE, arg);
2226                 break;
2227 
2228         case STMF_CMD_LPORT_OFFLINE:
2229                 if (iport->iport_state == FCT_STATE_OFFLINE)
2230                         st.st_completion_status = STMF_ALREADY;
2231                 else if (iport->iport_state != FCT_STATE_ONLINE)
2232                         st.st_completion_status = STMF_INVALID_ARG;
2233                 if (st.st_completion_status != STMF_SUCCESS) {
2234                         (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2235                             &st);
2236                         break;
2237                 }
2238                 iport->iport_state_not_acked = 1;
2239                 iport->iport_state = FCT_STATE_OFFLINING;
2240                 port->port_ctl(port, FCT_CMD_PORT_OFFLINE, arg);
2241                 break;
2242         case FCT_CMD_PORT_OFFLINE_COMPLETE:
2243                 ASSERT(iport->iport_state == FCT_STATE_OFFLINING);
2244                 if (pst->st_completion_status != FCT_SUCCESS) {
2245                         iport->iport_state = FCT_STATE_ONLINE;
2246                         iport->iport_state_not_acked = 0;
2247                         (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2248                             pst);
2249                         break;
2250                 }
2251 
2252                 /*
2253                  * If FCA's offline was successful, we dont tell stmf yet.
2254                  * Becasue now we have to do the cleanup before we go upto
2255                  * stmf. That cleanup is done by the worker thread.
2256                  */
2257 
2258                 /* FCA is offline, post a link down, its harmless anyway */
2259                 fct_handle_event(port, FCT_EVENT_LINK_DOWN, 0, 0);
2260 
2261                 /* Trigger port offline processing by the worker */
2262                 iport->iport_offline_prstate = FCT_OPR_START;
2263                 break;
2264         case STMF_ACK_LPORT_OFFLINE_COMPLETE:
2265                 ASSERT(iport->iport_state == FCT_STATE_OFFLINE);
2266                 iport->iport_state_not_acked = 0;
2267                 port->port_ctl(port, FCT_ACK_PORT_OFFLINE_COMPLETE, arg);
2268                 break;
2269         }
2270 }
2271 
2272 /* ARGSUSED */
2273 stmf_status_t
2274 fct_info(uint32_t cmd, stmf_local_port_t *lport, void *arg, uint8_t *buf,
2275                                                 uint32_t *bufsizep)
2276 {
2277         return (STMF_NOT_SUPPORTED);
2278 }
2279 
2280 /*
2281  * implicit: if it's true, it means it will only be used in fct module, or else
2282  * it will be sent to the link.
2283  */
2284 fct_cmd_t *
2285 fct_create_solels(fct_local_port_t *port, fct_remote_port_t *rp, int implicit,
2286     uchar_t elsop, uint32_t wkdid, fct_icmd_cb_t icmdcb)
2287 {
2288         fct_cmd_t               *cmd    = NULL;
2289         fct_i_cmd_t             *icmd   = NULL;
2290         fct_els_t               *els    = NULL;
2291         fct_i_remote_port_t     *irp    = NULL;
2292         uint8_t                 *p      = NULL;
2293         uint32_t                 ptid   = 0;
2294 
2295         cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_SOL_ELS,
2296             port->port_fca_sol_els_private_size, 0);
2297         if (!cmd) {
2298                 return (NULL);
2299         }
2300 
2301         if (rp) {
2302                 irp = RP_TO_IRP(rp);
2303         } else if (((irp = fct_portid_to_portptr(PORT_TO_IPORT(port),
2304             wkdid)) == NULL) && (elsop != ELS_OP_PLOGI)) {
2305                 stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2306                     "fct_create_solels: Must PLOGI to %x first", wkdid);
2307                 fct_free(cmd);
2308                 return (NULL);
2309         }
2310 
2311         cmd->cmd_port        = port;
2312         cmd->cmd_oxid        = PTR2INT(cmd, uint16_t);
2313         cmd->cmd_rxid        = 0xFFFF;
2314         cmd->cmd_handle = 0;
2315         icmd            = CMD_TO_ICMD(cmd);
2316         els             = ICMD_TO_ELS(icmd);
2317         icmd->icmd_cb        = icmdcb;
2318         if (irp) {
2319                 cmd->cmd_rp     = irp->irp_rp;
2320                 cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2321                 cmd->cmd_rportid   = irp->irp_rp->rp_id;
2322         } else {
2323                 cmd->cmd_rp_handle = FCT_HANDLE_NONE;
2324                 cmd->cmd_rportid   = wkdid;
2325         }
2326         cmd->cmd_lportid = (PORT_TO_IPORT(port))->iport_link_info.portid;
2327 
2328         if (implicit) {
2329                 /*
2330                  * Since we will not send it to FCA, so we only allocate space
2331                  */
2332                 ASSERT(elsop & (ELS_OP_LOGO | ELS_OP_PLOGI));
2333                 icmd->icmd_flags |= ICMD_IMPLICIT;
2334                 if (elsop == ELS_OP_LOGO) {
2335                         /*
2336                          * Handling implicit LOGO should dependent on as less
2337                          * as resources. So a trick here.
2338                          */
2339                         els->els_req_size = 1;
2340                         els->els_req_payload = cmd->cmd_fca_private;
2341                 } else {
2342                         els->els_req_alloc_size = els->els_req_size = 116;
2343                         els->els_resp_alloc_size = els->els_resp_size = 116;
2344                         els->els_req_payload = (uint8_t *)
2345                             kmem_zalloc(els->els_req_size, KM_SLEEP);
2346                         els->els_resp_payload = (uint8_t *)
2347                             kmem_zalloc(els->els_resp_size, KM_SLEEP);
2348                 }
2349         } else {
2350                 /*
2351                  * Allocate space for its request and response
2352                  * Fill the request payload according to spec.
2353                  */
2354                 switch (elsop) {
2355                 case ELS_OP_LOGO:
2356                         els->els_resp_alloc_size = els->els_resp_size = 4;
2357                         els->els_resp_payload = (uint8_t *)kmem_zalloc(
2358                             els->els_resp_size, KM_SLEEP);
2359                         els->els_req_alloc_size = els->els_req_size = 16;
2360                         els->els_req_payload = (uint8_t *)kmem_zalloc(
2361                             els->els_req_size, KM_SLEEP);
2362                         ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2363                         fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2364                         bcopy(port->port_pwwn, els->els_req_payload + 8, 8);
2365                         break;
2366 
2367                 case ELS_OP_RSCN:
2368                         els->els_resp_alloc_size = els->els_resp_size = 4;
2369                         els->els_resp_payload = (uint8_t *)kmem_zalloc(
2370                             els->els_resp_size, KM_SLEEP);
2371                         els->els_req_size = els->els_req_alloc_size = 8;
2372                         els->els_req_payload = (uint8_t *)kmem_zalloc(
2373                             els->els_req_size, KM_SLEEP);
2374                         els->els_req_payload[1] = 0x04;
2375                         els->els_req_payload[3] = 0x08;
2376                         els->els_req_payload[4] |= 0x80;
2377                         ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2378                         fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2379                         break;
2380 
2381                 case ELS_OP_PLOGI:
2382                         els->els_resp_alloc_size = els->els_resp_size = 116;
2383                         els->els_resp_payload = (uint8_t *)
2384                             kmem_zalloc(els->els_resp_size, KM_SLEEP);
2385                         els->els_req_alloc_size = els->els_req_size = 116;
2386                         p = els->els_req_payload = (uint8_t *)
2387                             kmem_zalloc(els->els_req_size, KM_SLEEP);
2388                         bcopy(port->port_pwwn, p + 20, 8);
2389                         bcopy(port->port_nwwn, p + 28, 8);
2390 
2391                         /*
2392                          * Common service parameters
2393                          */
2394                         p[0x04] = 0x09;         /* high version */
2395                         p[0x05] = 0x08;         /* low version */
2396                         p[0x06] = 0x00;         /* BB credit: 0x0065 */
2397                         p[0x07] = 0x65;
2398 
2399                         /* CI0: Continuously Increasing Offset - 1 */
2400                         /* RRO: Randomly Relative Offset - 0 */
2401                         /* VVV: Vendor Version Level - 0 */
2402                         /* N-F: N or F Port Payload Sender - 0 (N) */
2403                         /* BBM: BB Credit Management - 0 (Normal) */
2404                         p[0x08] = 0x80;
2405                         p[0x09] = 0x00;
2406 
2407                         /* Max RX size */
2408                         p[0x0A] = 0x08;
2409                         p[0x0B] = 0x00;
2410 
2411                         /* NPTCS: N Port Total Concurrent Sequences - 0x0000 */
2412                         p[0x0C] = 0x00;
2413                         p[0x0D] = 0x00;
2414 
2415                         /* ROIC: Relative Offset By Info - 0xFFFF */
2416                         p[0x0E] = 0xFF;
2417                         p[0x0F] = 0xFF;
2418 
2419                         /* EDTOV: Error Detect Timeout - 0x000007D0 */
2420                         p[0x10] = 0x00;
2421                         p[0x11] = 0x00;
2422                         p[0x12] = 0x07;
2423                         p[0x13] = 0xD0;
2424 
2425                         /*
2426                          * Class-3 Parameters
2427                          */
2428                         /* C3-VAL: Class 3 Value - 1 */
2429                         /* C3-XID: X_ID Reassignment - 0 */
2430                         /* C3-IPA: Initial Process Assignment */
2431                         /* C3-AI-DCC: Data compression capable */
2432                         /* C3-AI-DC-HB: Data compression history buffer size */
2433                         /* C3-AI-DCE: Data encrytion capable */
2434                         /* C3-AI-CSC: Clock synchronization capable */
2435                         /* C3-ErrPol: Error pliciy */
2436                         /* C3-CatSeq: Information Cat. Per Sequence */
2437                         /* C3-AR-DCC: */
2438                         /* C3-AR-DC-HB: */
2439                         /* C3-AR-DCE: */
2440                         /* C3-AR-CSC */
2441                         p[0x44] = 0x80;
2442                         p[0x45] = 0x00;
2443                         p[0x46] = 0x00;
2444                         p[0x47] = 0x00;
2445                         p[0x48] = 0x00;
2446                         p[0x49] = 0x00;
2447 
2448                         /* C3-RxSize: Class 3 receive data size */
2449                         p[0x4A] = 0x08;
2450                         p[0x4B] = 0x00;
2451 
2452                         /* C3-ConSeq: Class 3 Concourrent sequences */
2453                         p[0x4C] = 0x00;
2454                         p[0x4D] = 0xFF;
2455 
2456                         /* C3-OSPE: Class 3 open sequence per exchange */
2457                         p[0x50] = 0x00;
2458                         p[0x51] = 0x01;
2459 
2460                         break;
2461 
2462                 case ELS_OP_SCR:
2463                         els->els_resp_alloc_size = els->els_resp_size = 4;
2464                         els->els_resp_payload = (uint8_t *)
2465                             kmem_zalloc(els->els_resp_size, KM_SLEEP);
2466                         els->els_req_alloc_size = els->els_req_size = 8;
2467                         p = els->els_req_payload = (uint8_t *)
2468                             kmem_zalloc(els->els_req_size, KM_SLEEP);
2469                         p[7] = FC_SCR_FULL_REGISTRATION;
2470                         break;
2471                 case ELS_OP_RLS:
2472                         els->els_resp_alloc_size = els->els_resp_size = 28;
2473                         els->els_resp_payload = (uint8_t *)
2474                             kmem_zalloc(els->els_resp_size, KM_SLEEP);
2475                         els->els_req_alloc_size = els->els_req_size = 8;
2476                         p = els->els_req_payload = (uint8_t *)
2477                             kmem_zalloc(els->els_req_size, KM_SLEEP);
2478                         ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2479                         fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2480                         break;
2481 
2482                 default:
2483                         ASSERT(0);
2484                 }
2485         }
2486 
2487         els->els_req_payload[0] = elsop;
2488         return (cmd);
2489 }
2490 
2491 fct_cmd_t *
2492 fct_create_solct(fct_local_port_t *port, fct_remote_port_t *query_rp,
2493     uint16_t ctop, fct_icmd_cb_t icmdcb)
2494 {
2495         fct_cmd_t               *cmd     = NULL;
2496         fct_i_cmd_t             *icmd    = NULL;
2497         fct_sol_ct_t            *ct      = NULL;
2498         uint8_t                 *p       = NULL;
2499         fct_i_remote_port_t     *irp     = NULL;
2500         fct_i_local_port_t      *iport   = NULL;
2501         char                    *nname   = NULL;
2502         int                      namelen = 0;
2503 
2504         /*
2505          * Allocate space
2506          */
2507         cmd = fct_alloc(FCT_STRUCT_CMD_SOL_CT,
2508             port->port_fca_sol_ct_private_size, 0);
2509         if (!cmd) {
2510                 return (NULL);
2511         }
2512 
2513         /*
2514          * We should have PLOGIed to the name server (0xFFFFFC)
2515          * Caution: this irp is not query_rp->rp_fct_private.
2516          */
2517         irp = fct_portid_to_portptr((fct_i_local_port_t *)
2518             port->port_fct_private, FS_NAME_SERVER);
2519         if (irp == NULL) {
2520                 stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2521                     "fct_create_solct: Must PLOGI name server first");
2522                 fct_free(cmd);
2523                 return (NULL);
2524         }
2525 
2526         cmd->cmd_port           = port;
2527         cmd->cmd_rp     = irp->irp_rp;
2528         cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2529         cmd->cmd_rportid   = irp->irp_rp->rp_id;
2530         cmd->cmd_lportid   = (PORT_TO_IPORT(port))->iport_link_info.portid;
2531         cmd->cmd_oxid           = PTR2INT(cmd, uint16_t);
2532         cmd->cmd_rxid           = 0xFFFF;
2533         cmd->cmd_handle         = 0;
2534         icmd               = CMD_TO_ICMD(cmd);
2535         ct                 = ICMD_TO_CT(icmd);
2536         icmd->icmd_cb           = icmdcb;
2537         iport              = ICMD_TO_IPORT(icmd);
2538 
2539         switch (ctop) {
2540         case NS_GSNN_NN:
2541                 /*
2542                  * Allocate max space for its sybolic name
2543                  */
2544                 ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2545                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2546                     KM_SLEEP);
2547 
2548                 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2549                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2550                     KM_SLEEP);
2551 
2552                 bcopy(query_rp->rp_nwwn, p + 16, 8);
2553                 break;
2554 
2555         case NS_RNN_ID:
2556                 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2557                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2558                     KM_SLEEP);
2559                 ct->ct_req_size = ct->ct_req_alloc_size = 28;
2560                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2561                     KM_SLEEP);
2562 
2563                 /*
2564                  * Port Identifier
2565                  */
2566                 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2567                 p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2568                 p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2569 
2570                 /*
2571                  * Node Name
2572                  */
2573                 bcopy(port->port_nwwn, p + 20, 8);
2574                 break;
2575 
2576         case NS_RCS_ID:
2577                 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2578                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2579                     KM_SLEEP);
2580                 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2581                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2582                     KM_SLEEP);
2583 
2584                 /*
2585                  * Port Identifier
2586                  */
2587                 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2588                 p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2589                 p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2590 
2591                 /*
2592                  * Class of Service
2593                  */
2594                 *(p + 23) = FC_NS_CLASS3;
2595                 break;
2596 
2597         case NS_RFT_ID:
2598                 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2599                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2600                     KM_SLEEP);
2601                 ct->ct_req_size = ct->ct_req_alloc_size = 52;
2602                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2603                     KM_SLEEP);
2604 
2605                 /*
2606                  * Port Identifier
2607                  */
2608                 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2609                 p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2610                 p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2611 
2612                 /*
2613                  * FC-4 Protocol Types
2614                  */
2615                 *(p + 22) = 0x1;        /* 0x100 */
2616                 break;
2617 
2618         case NS_RSPN_ID:
2619                 /*
2620                  * If we get here, port->port_sym_port_name is always not NULL.
2621                  */
2622                 ASSERT(port->port_sym_port_name);
2623                 namelen = strlen(port->port_sym_port_name);
2624                 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2625                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2626                     KM_SLEEP);
2627                 ct->ct_req_size = ct->ct_req_alloc_size =
2628                     (21 + namelen + 3) & ~3;
2629                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2630                     KM_SLEEP);
2631 
2632                 /*
2633                  * Port Identifier
2634                  */
2635                 p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2636                 p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2637                 p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2638 
2639                 /*
2640                  * String length
2641                  */
2642                 p[20] = namelen;
2643 
2644                 /*
2645                  * Symbolic port name
2646                  */
2647                 bcopy(port->port_sym_port_name, p + 21, ct->ct_req_size - 21);
2648                 break;
2649 
2650         case NS_RSNN_NN:
2651                 namelen = port->port_sym_node_name == NULL ?
2652                     strlen(utsname.nodename) :
2653                     strlen(port->port_sym_node_name);
2654                 nname = port->port_sym_node_name == NULL ?
2655                     utsname.nodename : port->port_sym_node_name;
2656 
2657                 ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2658                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2659                     KM_SLEEP);
2660                 ct->ct_req_size = ct->ct_req_alloc_size =
2661                     (25 + namelen + 3) & ~3;
2662                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2663                     KM_SLEEP);
2664 
2665                 /*
2666                  * Node name
2667                  */
2668                 bcopy(port->port_nwwn, p + 16, 8);
2669 
2670                 /*
2671                  * String length
2672                  */
2673                 p[24] = namelen;
2674 
2675                 /*
2676                  * Symbolic node name
2677                  */
2678                 bcopy(nname, p + 25, ct->ct_req_size - 25);
2679                 break;
2680 
2681         case NS_GSPN_ID:
2682                 ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2683                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2684                     KM_SLEEP);
2685                 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2686                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2687                     KM_SLEEP);
2688                 /*
2689                  * Port Identifier
2690                  */
2691                 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2692                 p[18] = (query_rp->rp_id >>  8) & 0xFF;
2693                 p[19] = (query_rp->rp_id >>  0) & 0xFF;
2694                 break;
2695 
2696         case NS_GCS_ID:
2697                 ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2698                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2699                     KM_SLEEP);
2700                 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2701                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2702                     KM_SLEEP);
2703                 /*
2704                  * Port Identifier
2705                  */
2706                 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2707                 p[18] = (query_rp->rp_id >>  8) & 0xFF;
2708                 p[19] = (query_rp->rp_id >>  0) & 0xFF;
2709                 break;
2710 
2711         case NS_GFT_ID:
2712                 ct->ct_resp_alloc_size = ct->ct_resp_size = 48;
2713                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2714                     KM_SLEEP);
2715                 ct->ct_req_size = ct->ct_req_alloc_size = 20;
2716                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2717                     KM_SLEEP);
2718                 /*
2719                  * Port Identifier
2720                  */
2721                 p[17] = (query_rp->rp_id >> 16) & 0xFF;
2722                 p[18] = (query_rp->rp_id >>  8) & 0xFF;
2723                 p[19] = (query_rp->rp_id >>  0) & 0xFF;
2724                 break;
2725 
2726         case NS_GID_PN:
2727                 ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2728                 ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2729                     KM_SLEEP);
2730 
2731                 ct->ct_req_size = ct->ct_req_alloc_size = 24;
2732                 p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2733                     KM_SLEEP);
2734 
2735                 bcopy(query_rp->rp_pwwn, p + 16, 8);
2736                 break;
2737 
2738         default:
2739                 /* CONSTCOND */
2740                 ASSERT(0);
2741         }
2742 
2743         FCT_FILL_CTIU_PREAMBLE(p, ctop);
2744         return (cmd);
2745 }
2746 
2747 /*
2748  * Cmd can only be solicited CT/ELS. They will be dispatched to the discovery
2749  * queue eventually too.
2750  * We queue solicited cmds here to track solicited cmds and to take full use
2751  * of single thread mechanism.
2752  * But in current implmentation, we don't use  this mechanism on SOL_CT, PLOGI.
2753  * To avoid to interrupt current flow, ICMD_IN_SOLCMD_QUEUE is used here.
2754  */
2755 void
2756 fct_post_to_solcmd_queue(fct_local_port_t *port, fct_cmd_t *cmd)
2757 {
2758         fct_i_local_port_t      *iport  = (fct_i_local_port_t *)
2759             port->port_fct_private;
2760         fct_i_cmd_t *icmd               = (fct_i_cmd_t *)cmd->cmd_fct_private;
2761 
2762         mutex_enter(&iport->iport_worker_lock);
2763         icmd->icmd_solcmd_next = iport->iport_solcmd_queue;
2764         iport->iport_solcmd_queue = icmd;
2765         atomic_or_32(&icmd->icmd_flags, ICMD_IN_SOLCMD_QUEUE | ICMD_SOLCMD_NEW);
2766         if (IS_WORKER_SLEEPING(iport)) {
2767                 cv_signal(&iport->iport_worker_cv);
2768         }
2769         mutex_exit(&iport->iport_worker_lock);
2770 }
2771 
2772 /* ARGSUSED */
2773 void
2774 fct_event_handler(stmf_local_port_t *lport, int eventid, void *arg,
2775     uint32_t flags)
2776 {
2777         fct_local_port_t        *port  = (fct_local_port_t *)
2778             lport->lport_port_private;
2779         fct_i_local_port_t      *iport = (fct_i_local_port_t *)
2780             port->port_fct_private;
2781         stmf_scsi_session_t     *ss;
2782         fct_i_remote_port_t     *irp;
2783 
2784         switch (eventid) {
2785         case LPORT_EVENT_INITIAL_LUN_MAPPED:
2786                 ss = (stmf_scsi_session_t *)arg;
2787                 irp = (fct_i_remote_port_t *)ss->ss_port_private;
2788                 stmf_trace(iport->iport_alias,
2789                     "Initial LUN mapped to session ss-%p, irp-%p", ss, irp);
2790                 break;
2791 
2792         default:
2793                 stmf_trace(iport->iport_alias,
2794                     "Unknown event received, %d", eventid);
2795         }
2796 }
2797 
2798 void
2799 fct_send_cmd_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2800 {
2801         /* XXX For now just call send_resp_done() */
2802         fct_send_response_done(cmd, s, ioflags);
2803 }
2804 
2805 void
2806 fct_cmd_fca_aborted(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2807 {
2808         fct_i_cmd_t             *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2809         char                    info[FCT_INFO_LEN];
2810         unsigned long long      st;
2811 
2812         st = s; /* To make gcc happy */
2813         ASSERT(icmd->icmd_flags & ICMD_BEING_ABORTED);
2814         if ((((s != FCT_ABORT_SUCCESS) && (s != FCT_NOT_FOUND))) ||
2815             ((ioflags & FCT_IOF_FCA_DONE) == 0)) {
2816                 (void) snprintf(info, sizeof (info),
2817                     "fct_cmd_fca_aborted: cmd-%p, "
2818                     "s-%llx, iofalgs-%x", (void *)cmd, st, ioflags);
2819                 (void) fct_port_shutdown(cmd->cmd_port,
2820                     STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2821                 return;
2822         }
2823 
2824         atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2825         /* For non FCP Rest of the work is done by the terminator */
2826         /* For FCP stuff just call stmf */
2827         if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2828                 stmf_task_lport_aborted((scsi_task_t *)cmd->cmd_specific,
2829                     s, STMF_IOF_LPORT_DONE);
2830         }
2831 }
2832 
2833 /*
2834  * FCA drivers will use it, when they want to abort some FC transactions
2835  * due to lack of resource.
2836  */
2837 uint16_t
2838 fct_get_rp_handle(fct_local_port_t *port, uint32_t rportid)
2839 {
2840         fct_i_remote_port_t     *irp;
2841 
2842         irp = fct_portid_to_portptr(
2843             (fct_i_local_port_t *)(port->port_fct_private), rportid);
2844         if (irp == NULL) {
2845                 return (0xFFFF);
2846         } else {
2847                 return (irp->irp_rp->rp_handle);
2848         }
2849 }
2850 
2851 fct_cmd_t *
2852 fct_handle_to_cmd(fct_local_port_t *port, uint32_t fct_handle)
2853 {
2854         fct_cmd_slot_t *slot;
2855         uint16_t ndx;
2856 
2857         if (!CMD_HANDLE_VALID(fct_handle))
2858                 return (NULL);
2859         if ((ndx = CMD_HANDLE_SLOT_INDEX(fct_handle)) >= port->port_max_xchges)
2860                 return (NULL);
2861 
2862         slot = &((fct_i_local_port_t *)port->port_fct_private)->iport_cmd_slots[
2863             ndx];
2864 
2865         if ((slot->slot_uniq_cntr | 0x80) != (fct_handle >> 24))
2866                 return (NULL);
2867         return (slot->slot_cmd->icmd_cmd);
2868 }
2869 
2870 void
2871 fct_queue_scsi_task_for_termination(fct_cmd_t *cmd, fct_status_t s)
2872 {
2873         fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2874 
2875         uint32_t old, new;
2876 
2877         do {
2878                 old = icmd->icmd_flags;
2879                 if ((old & (ICMD_BEING_ABORTED | ICMD_KNOWN_TO_FCA)) !=
2880                     ICMD_KNOWN_TO_FCA)
2881                         return;
2882                 new = old | ICMD_BEING_ABORTED;
2883         } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2884         stmf_abort(STMF_QUEUE_TASK_ABORT, (scsi_task_t *)cmd->cmd_specific,
2885             s, NULL);
2886 }
2887 
2888 void
2889 fct_fill_abts_acc(fct_cmd_t *cmd)
2890 {
2891         fct_rcvd_abts_t *abts = (fct_rcvd_abts_t *)cmd->cmd_specific;
2892         uint8_t *p;
2893 
2894         abts->abts_resp_rctl = BLS_OP_BA_ACC;
2895         p = abts->abts_resp_payload;
2896         bzero(p, 12);
2897         *((uint16_t *)(p+4)) = BE_16(cmd->cmd_oxid);
2898         *((uint16_t *)(p+6)) = BE_16(cmd->cmd_rxid);
2899         p[10] = p[11] = 0xff;
2900 }
2901 
2902 void
2903 fct_handle_rcvd_abts(fct_cmd_t *cmd)
2904 {
2905         char                    info[FCT_INFO_LEN];
2906         fct_local_port_t        *port = cmd->cmd_port;
2907         fct_i_local_port_t      *iport =
2908             (fct_i_local_port_t *)port->port_fct_private;
2909         fct_i_cmd_t             *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2910         fct_i_remote_port_t     *irp;
2911         fct_cmd_t               *c = NULL;
2912         fct_i_cmd_t             *ic = NULL;
2913         int                     found = 0;
2914         int                     i;
2915 
2916         icmd->icmd_start_time = ddi_get_lbolt();
2917         icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
2918 
2919         rw_enter(&iport->iport_lock, RW_WRITER);
2920         /* Make sure local port is sane */
2921         if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2922                 rw_exit(&iport->iport_lock);
2923                 stmf_trace(iport->iport_alias, "ABTS not posted becasue"
2924                     "port state was %x", iport->iport_link_state);
2925                 fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2926                 return;
2927         }
2928 
2929         if (cmd->cmd_rp_handle == FCT_HANDLE_NONE)
2930                 irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
2931         else if (cmd->cmd_rp_handle < port->port_max_logins)
2932                 irp = iport->iport_rp_slots[cmd->cmd_rp_handle];
2933         else
2934                 irp = NULL;
2935         if (irp == NULL) {
2936                 /* XXX Throw a logout to the initiator */
2937                 rw_exit(&iport->iport_lock);
2938                 stmf_trace(iport->iport_alias, "ABTS received from"
2939                     " %x without a session", cmd->cmd_rportid);
2940                 fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
2941                 return;
2942         }
2943 
2944         DTRACE_FC_3(abts__receive,
2945             fct_cmd_t, cmd,
2946             fct_local_port_t, port,
2947             fct_i_remote_port_t, irp);
2948 
2949         cmd->cmd_rp = irp->irp_rp;
2950 
2951         /*
2952          * No need to allocate an xchg resource. ABTSes use the same
2953          * xchg resource as the cmd they are aborting.
2954          */
2955         rw_enter(&irp->irp_lock, RW_WRITER);
2956         mutex_enter(&iport->iport_worker_lock);
2957         /* Lets find the command first */
2958         for (i = 0; i < port->port_max_xchges; i++) {
2959                 if ((ic = iport->iport_cmd_slots[i].slot_cmd) == NULL)
2960                         continue;
2961                 if ((ic->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
2962                         continue;
2963                 c = ic->icmd_cmd;
2964                 if (!CMD_HANDLE_VALID(c->cmd_handle))
2965                         continue;
2966                 if ((c->cmd_rportid != cmd->cmd_rportid) ||
2967                     (c->cmd_oxid != cmd->cmd_oxid))
2968                         continue;
2969                 /* Found the command */
2970                 found = 1;
2971                 break;
2972         }
2973         if (!found) {
2974                 mutex_exit(&iport->iport_worker_lock);
2975                 rw_exit(&irp->irp_lock);
2976                 rw_exit(&iport->iport_lock);
2977                 /* Dont even bother queueing it. Just respond */
2978                 fct_fill_abts_acc(cmd);
2979                 if (port->port_send_cmd_response(cmd,
2980                     FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2981                         /*
2982                          * XXX Throw HBA fatal error event
2983                          * Later shutdown svc will terminate the ABTS in the end
2984                          */
2985                         (void) snprintf(info, sizeof (info),
2986                             "fct_handle_rcvd_abts: iport-%p, "
2987                             "ABTS_ACC port_send_cmd_response failed",
2988                             (void *)iport);
2989                         (void) fct_port_shutdown(iport->iport_port,
2990                             STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2991                 } else {
2992                         fct_cmd_free(cmd);
2993                 }
2994                 return;
2995         }
2996 
2997         /* Check if this an abts retry */
2998         if (c->cmd_link && (ic->icmd_flags & ICMD_ABTS_RECEIVED)) {
2999                 /* Kill this abts. */
3000                 fct_q_for_termination_lock_held(iport, icmd, FCT_ABORTED);
3001                 if (IS_WORKER_SLEEPING(iport))
3002                         cv_signal(&iport->iport_worker_cv);
3003                 mutex_exit(&iport->iport_worker_lock);
3004                 rw_exit(&irp->irp_lock);
3005                 rw_exit(&iport->iport_lock);
3006                 return;
3007         }
3008         c->cmd_link = cmd;
3009         atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
3010         cmd->cmd_link = c;
3011         mutex_exit(&iport->iport_worker_lock);
3012         rw_exit(&irp->irp_lock);
3013         fct_queue_cmd_for_termination(c, FCT_ABTS_RECEIVED);
3014         rw_exit(&iport->iport_lock);
3015 }
3016 
3017 void
3018 fct_queue_cmd_for_termination(fct_cmd_t *cmd, fct_status_t s)
3019 {
3020         fct_local_port_t *port = cmd->cmd_port;
3021         fct_i_local_port_t *iport = (fct_i_local_port_t *)
3022             port->port_fct_private;
3023         fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
3024 
3025         if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
3026                 fct_queue_scsi_task_for_termination(cmd, s);
3027                 return;
3028         }
3029         mutex_enter(&iport->iport_worker_lock);
3030         fct_q_for_termination_lock_held(iport, icmd, s);
3031         if (IS_WORKER_SLEEPING(iport))
3032                 cv_signal(&iport->iport_worker_cv);
3033         mutex_exit(&iport->iport_worker_lock);
3034 }
3035 
3036 /*
3037  * This function will not be called for SCSI CMDS
3038  */
3039 void
3040 fct_q_for_termination_lock_held(fct_i_local_port_t *iport, fct_i_cmd_t *icmd,
3041                 fct_status_t s)
3042 {
3043         uint32_t old, new;
3044         fct_i_cmd_t **ppicmd;
3045 
3046         do {
3047                 old = icmd->icmd_flags;
3048                 if (old & ICMD_BEING_ABORTED)
3049                         return;
3050                 new = old | ICMD_BEING_ABORTED;
3051         } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3052 
3053         icmd->icmd_start_time = ddi_get_lbolt();
3054         icmd->icmd_cmd->cmd_comp_status = s;
3055 
3056         icmd->icmd_next = NULL;
3057         for (ppicmd = &(iport->iport_abort_queue); *ppicmd != NULL;
3058             ppicmd = &((*ppicmd)->icmd_next))
3059                 ;
3060 
3061         *ppicmd = icmd;
3062 }
3063 
3064 /*
3065  * For those cmds, for which we called fca_abort but it has not yet completed,
3066  * reset the FCA_ABORT_CALLED flag, so that abort can be called again.
3067  * This is done after a FCA offline. The reason is that after offline, the
3068  * firmware is not running so abort will never complete. But if we call it
3069  * again, the FCA will detect that it is not offline and it will
3070  * not call the firmware at all. Most likely it will abort in a synchronous
3071  * manner i.e. return FCT_ABORT_SUCCESS or FCT_NOT_FOUND.
3072  */
3073 void
3074 fct_reset_flag_abort_called(fct_i_local_port_t *iport)
3075 {
3076         fct_i_cmd_t *icmd;
3077         uint32_t old, new;
3078         int i, do_clear;
3079 
3080         ASSERT(mutex_owned(&iport->iport_worker_lock));
3081         mutex_exit(&iport->iport_worker_lock);
3082         rw_enter(&iport->iport_lock, RW_WRITER);
3083         mutex_enter(&iport->iport_worker_lock);
3084 
3085         for (i = 0; i < iport->iport_port->port_max_xchges; i++) {
3086                 if (iport->iport_cmd_slots[i].slot_cmd == NULL)
3087                         continue;
3088 
3089                 icmd = iport->iport_cmd_slots[i].slot_cmd;
3090 
3091                 do {
3092                         old = new = icmd->icmd_flags;
3093                         if ((old & (ICMD_KNOWN_TO_FCA |
3094                             ICMD_FCA_ABORT_CALLED)) == (ICMD_KNOWN_TO_FCA |
3095                             ICMD_FCA_ABORT_CALLED)) {
3096                                 new &= ~ICMD_FCA_ABORT_CALLED;
3097                                 do_clear = 1;
3098                         } else {
3099                                 do_clear = 0;
3100                                 break;
3101                         }
3102                 } while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3103                 if (do_clear &&
3104                     (icmd->icmd_cmd->cmd_type == FCT_CMD_FCP_XCHG)) {
3105                         stmf_abort(STMF_REQUEUE_TASK_ABORT_LPORT,
3106                             icmd->icmd_cmd->cmd_specific, 0, NULL);
3107                 }
3108         }
3109 
3110         rw_exit(&iport->iport_lock);
3111 }
3112 
3113 /*
3114  * Modify the irp_deregister_timer such that the ports start deregistering
3115  * quickly.
3116  */
3117 void
3118 fct_irp_deregister_speedup(fct_i_local_port_t *iport)
3119 {
3120         fct_i_remote_port_t *irp;
3121         int i;
3122 
3123         if (!iport->iport_nrps)
3124                 return;
3125 
3126         for (i = 0; i < rportid_table_size; i++) {
3127                 irp = iport->iport_rp_tb[i];
3128                 while (irp) {
3129                         irp->irp_deregister_timer = ddi_get_lbolt() - 1;
3130                         irp = irp->irp_next;
3131                 }
3132         }
3133 }
3134 
3135 disc_action_t
3136 fct_handle_port_offline(fct_i_local_port_t *iport)
3137 {
3138         if (iport->iport_offline_prstate == FCT_OPR_START) {
3139                 fct_reset_flag_abort_called(iport);
3140                 iport->iport_offline_prstate = FCT_OPR_CMD_CLEANUP_WAIT;
3141                 /* fct_ctl has already submitted a link offline event */
3142                 return (DISC_ACTION_DELAY_RESCAN);
3143         }
3144         if (iport->iport_offline_prstate == FCT_OPR_CMD_CLEANUP_WAIT) {
3145                 if (iport->iport_link_state != PORT_STATE_LINK_DOWN)
3146                         return (DISC_ACTION_DELAY_RESCAN);
3147                 /*
3148                  * All I/Os have been killed at this time. Lets speedup
3149                  * the port deregister process.
3150                  */
3151                 mutex_exit(&iport->iport_worker_lock);
3152                 rw_enter(&iport->iport_lock, RW_WRITER);
3153                 fct_irp_deregister_speedup(iport);
3154                 rw_exit(&iport->iport_lock);
3155                 mutex_enter(&iport->iport_worker_lock);
3156                 iport->iport_offline_prstate = FCT_OPR_INT_CLEANUP_WAIT;
3157                 return (DISC_ACTION_RESCAN);
3158         }
3159         if (iport->iport_offline_prstate == FCT_OPR_INT_CLEANUP_WAIT) {
3160                 stmf_change_status_t st;
3161 
3162                 if (iport->iport_solcmd_queue) {
3163                         return (DISC_ACTION_DELAY_RESCAN);
3164                 }
3165 
3166                 if (iport->iport_nrps) {
3167                         /*
3168                          * A port logout may have gone when implicit logo all
3169                          * was retried. So do the port speedup again here.
3170                          */
3171                         mutex_exit(&iport->iport_worker_lock);
3172                         rw_enter(&iport->iport_lock, RW_WRITER);
3173                         fct_irp_deregister_speedup(iport);
3174                         rw_exit(&iport->iport_lock);
3175                         mutex_enter(&iport->iport_worker_lock);
3176                         return (DISC_ACTION_DELAY_RESCAN);
3177                 }
3178 
3179                 if (iport->iport_event_head != NULL) {
3180                         return (DISC_ACTION_DELAY_RESCAN);
3181                 }
3182 
3183                 st.st_completion_status = STMF_SUCCESS;
3184                 st.st_additional_info = NULL;
3185                 iport->iport_offline_prstate = FCT_OPR_DONE;
3186                 iport->iport_state = FCT_STATE_OFFLINE;
3187                 mutex_exit(&iport->iport_worker_lock);
3188                 (void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
3189                     iport->iport_port->port_lport, &st);
3190                 mutex_enter(&iport->iport_worker_lock);
3191                 return (DISC_ACTION_DELAY_RESCAN);
3192         }
3193 
3194         /* NOTREACHED */
3195         return (0);
3196 }
3197 
3198 /*
3199  * See stmf.h for information on rflags. Additional info is just a text
3200  * description of the reason for this call. Additional_info can be NULL.
3201  * Also the caller can declare additional info on the stack. stmf_ctl
3202  * makes a copy of it before returning.
3203  */
3204 fct_status_t
3205 fct_port_initialize(fct_local_port_t *port, uint32_t rflags,
3206                                 char *additional_info)
3207 {
3208         stmf_state_change_info_t st;
3209 
3210         st.st_rflags = rflags;
3211         st.st_additional_info = additional_info;
3212         stmf_trace(NULL, "fct_port_initialize: port-%p, %s", port,
3213             additional_info? additional_info : "no more information");
3214         return (stmf_ctl(STMF_CMD_LPORT_ONLINE, port->port_lport, &st));
3215 }
3216 
3217 fct_status_t
3218 fct_port_shutdown(fct_local_port_t *port, uint32_t rflags,
3219                                 char *additional_info)
3220 {
3221         stmf_state_change_info_t st;
3222 
3223         st.st_rflags = rflags;
3224         st.st_additional_info = additional_info;
3225         stmf_trace(NULL, "fct_port_shutdown: port-%p, %s", port,
3226             additional_info? additional_info : "no more information");
3227         return (stmf_ctl(STMF_CMD_LPORT_OFFLINE, port->port_lport, &st));
3228 }
3229 
3230 /*
3231  * Called by worker thread. The aim is to terminate the command
3232  * using whatever means it takes.
3233  * Called with worker lock held.
3234  */
3235 disc_action_t
3236 fct_cmd_terminator(fct_i_local_port_t *iport)
3237 {
3238         char                    info[FCT_INFO_LEN];
3239         clock_t                 endtime;
3240         fct_i_cmd_t             **ppicmd;
3241         fct_i_cmd_t             *icmd;
3242         fct_cmd_t               *cmd;
3243         fct_local_port_t        *port = iport->iport_port;
3244         disc_action_t           ret = DISC_ACTION_NO_WORK;
3245         fct_status_t            abort_ret;
3246         int                     fca_done, fct_done, cmd_implicit = 0;
3247         int                     flags;
3248         unsigned long long      st;
3249 
3250         /* Lets Limit each run to 20ms max. */
3251         endtime = ddi_get_lbolt() + drv_usectohz(20000);
3252 
3253         /* Start from where we left off last time */
3254         if (iport->iport_ppicmd_term) {
3255                 ppicmd = iport->iport_ppicmd_term;
3256                 iport->iport_ppicmd_term = NULL;
3257         } else {
3258                 ppicmd = &iport->iport_abort_queue;
3259         }
3260 
3261         /*
3262          * Once a command gets on discovery queue, this is the only thread
3263          * which can access it. So no need for the lock here.
3264          */
3265         mutex_exit(&iport->iport_worker_lock);
3266 
3267         while ((icmd = *ppicmd) != NULL) {
3268                 cmd = icmd->icmd_cmd;
3269 
3270                 /* Always remember that cmd->cmd_rp can be NULL */
3271                 if ((icmd->icmd_flags & (ICMD_KNOWN_TO_FCA |
3272                     ICMD_FCA_ABORT_CALLED)) == ICMD_KNOWN_TO_FCA) {
3273                         atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3274                         if (CMD_HANDLE_VALID(cmd->cmd_handle))
3275                                 flags = 0;
3276                         else
3277                                 flags = FCT_IOF_FORCE_FCA_DONE;
3278                         abort_ret = port->port_abort_cmd(port, cmd, flags);
3279                         if ((abort_ret != FCT_SUCCESS) &&
3280                             (abort_ret != FCT_ABORT_SUCCESS) &&
3281                             (abort_ret != FCT_NOT_FOUND)) {
3282                                 if (flags & FCT_IOF_FORCE_FCA_DONE) {
3283                                         /*
3284                                          * XXX trigger port fatal,
3285                                          * Abort the termination, and shutdown
3286                                          * svc will trigger fct_cmd_termination
3287                                          * again.
3288                                          */
3289                                         (void) snprintf(info, sizeof (info),
3290                                             "fct_cmd_terminator:"
3291                                             " iport-%p, port_abort_cmd with "
3292                                             "FORCE_FCA_DONE failed",
3293                                             (void *)iport);
3294                                         (void) fct_port_shutdown(
3295                                             iport->iport_port,
3296                                             STMF_RFLAG_FATAL_ERROR |
3297                                             STMF_RFLAG_RESET, info);
3298 
3299                                         mutex_enter(&iport->iport_worker_lock);
3300                                         iport->iport_ppicmd_term = ppicmd;
3301                                         return (DISC_ACTION_DELAY_RESCAN);
3302                                 }
3303                                 atomic_and_32(&icmd->icmd_flags,
3304                                     ~ICMD_FCA_ABORT_CALLED);
3305                         } else if ((flags & FCT_IOF_FORCE_FCA_DONE) ||
3306                             (abort_ret == FCT_ABORT_SUCCESS) ||
3307                             (abort_ret == FCT_NOT_FOUND)) {
3308                                 atomic_and_32(&icmd->icmd_flags,
3309                                     ~ICMD_KNOWN_TO_FCA);
3310                         }
3311                         ret |= DISC_ACTION_DELAY_RESCAN;
3312                 } else if (icmd->icmd_flags & ICMD_IMPLICIT) {
3313                         if (cmd->cmd_type == FCT_CMD_SOL_ELS)
3314                                 cmd->cmd_comp_status = FCT_ABORTED;
3315                         atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3316                         cmd_implicit = 1;
3317                 }
3318                 if ((icmd->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
3319                         fca_done = 1;
3320                 else
3321                         fca_done = 0;
3322                 if ((icmd->icmd_flags & ICMD_IN_IRP_QUEUE) == 0)
3323                         fct_done = 1;
3324                 else
3325                         fct_done = 0;
3326                 if ((fca_done || cmd_implicit) && fct_done) {
3327                         mutex_enter(&iport->iport_worker_lock);
3328                         ASSERT(*ppicmd == icmd);
3329                         *ppicmd = (*ppicmd)->icmd_next;
3330                         mutex_exit(&iport->iport_worker_lock);
3331                         if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) ||
3332                             (cmd->cmd_type == FCT_CMD_RCVD_ABTS)) {
3333                                 /* Free the cmd */
3334                                 fct_cmd_free(cmd);
3335                         } else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
3336                                 fct_handle_sol_els_completion(iport, icmd);
3337                                 if (icmd->icmd_flags & ICMD_IMPLICIT) {
3338                                         if (IS_LOGO_ELS(icmd)) {
3339                                                 /* IMPLICIT LOGO is special */
3340                                                 fct_cmd_free(cmd);
3341                                         }
3342                                 }
3343                         } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3344                                 fct_sol_ct_t *ct = ICMD_TO_CT(icmd);
3345 
3346                                 /* Tell the caller that we are done */
3347                                 atomic_or_32(&icmd->icmd_flags,
3348                                     ICMD_CMD_COMPLETE);
3349                                 if (fct_netbuf_to_value(
3350                                     ct->ct_req_payload + 8, 2) == NS_GID_PN) {
3351                                         fct_i_remote_port_t *irp;
3352 
3353                                         rw_enter(&iport->iport_lock, RW_READER);
3354                                         irp = fct_lookup_irp_by_portwwn(iport,
3355                                             ct->ct_req_payload + 16);
3356 
3357                                         if (irp) {
3358                                                 atomic_and_32(&irp->irp_flags,
3359                                                     ~IRP_RSCN_QUEUED);
3360                                         }
3361                                         rw_exit(&iport->iport_lock);
3362                                 }
3363                         } else {
3364                                 ASSERT(0);
3365                         }
3366                 } else {
3367                         clock_t timeout_ticks;
3368                         if (port->port_fca_abort_timeout)
3369                                 timeout_ticks = drv_usectohz(
3370                                     port->port_fca_abort_timeout*1000);
3371                         else
3372                                 /* 10 seconds by default */
3373                                 timeout_ticks = drv_usectohz(10 * 1000000);
3374                         if ((ddi_get_lbolt() >
3375                             (icmd->icmd_start_time+timeout_ticks)) &&
3376                             iport->iport_state == FCT_STATE_ONLINE) {
3377                                 /* timeout, reset the port */
3378                                 char cmd_type[10];
3379                                 if (cmd->cmd_type == FCT_CMD_RCVD_ELS ||
3380                                     cmd->cmd_type == FCT_CMD_SOL_ELS) {
3381                                         fct_els_t *els = cmd->cmd_specific;
3382                                         (void) snprintf(cmd_type,
3383                                             sizeof (cmd_type), "%x.%x",
3384                                             cmd->cmd_type,
3385                                             els->els_req_payload[0]);
3386                                 } else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3387                                         fct_sol_ct_t *ct = cmd->cmd_specific;
3388                                         (void) snprintf(cmd_type,
3389                                             sizeof (cmd_type), "%x.%02x%02x",
3390                                             cmd->cmd_type,
3391                                             ct->ct_req_payload[8],
3392                                             ct->ct_req_payload[9]);
3393                                 } else {
3394                                         cmd_type[0] = 0;
3395                                 }
3396                                 st = cmd->cmd_comp_status;   /* gcc fix */
3397                                 (void) snprintf(info, sizeof (info),
3398                                     "fct_cmd_terminator:"
3399                                     " iport-%p, cmd_type(0x%s),"
3400                                     " reason(%llx)", (void *)iport, cmd_type,
3401                                     st);
3402                                 (void) fct_port_shutdown(port,
3403                                     STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET,
3404                                     info);
3405                         }
3406                         ppicmd = &((*ppicmd)->icmd_next);
3407                 }
3408 
3409                 if (ddi_get_lbolt() > endtime) {
3410                         mutex_enter(&iport->iport_worker_lock);
3411                         iport->iport_ppicmd_term = ppicmd;
3412                         return (DISC_ACTION_DELAY_RESCAN);
3413                 }
3414         }
3415         mutex_enter(&iport->iport_worker_lock);
3416         if (iport->iport_abort_queue)
3417                 return (DISC_ACTION_DELAY_RESCAN);
3418         if (ret == DISC_ACTION_NO_WORK)
3419                 return (DISC_ACTION_RESCAN);
3420         return (ret);
3421 }
3422 
3423 /*
3424  * Send a syslog event for adapter port level events.
3425  */
3426 void
3427 fct_log_local_port_event(fct_local_port_t *port, char *subclass)
3428 {
3429         nvlist_t *attr_list;
3430         int port_instance;
3431 
3432         if (!fct_dip)
3433                 return;
3434         port_instance = ddi_get_instance(fct_dip);
3435 
3436         if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3437             KM_SLEEP) != DDI_SUCCESS) {
3438                 goto alloc_failed;
3439         }
3440 
3441         if (nvlist_add_uint32(attr_list, "instance", port_instance)
3442             != DDI_SUCCESS) {
3443                 goto error;
3444         }
3445 
3446         if (nvlist_add_byte_array(attr_list, "port-wwn",
3447             port->port_pwwn, 8) != DDI_SUCCESS) {
3448                 goto error;
3449         }
3450 
3451         (void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3452             subclass, attr_list, NULL, DDI_SLEEP);
3453 
3454         nvlist_free(attr_list);
3455         return;
3456 
3457 error:
3458         nvlist_free(attr_list);
3459 alloc_failed:
3460         stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3461             "Unable to send %s event", subclass);
3462 }
3463 
3464 void
3465 fct_log_remote_port_event(fct_local_port_t *port, char *subclass,
3466     uint8_t *rp_pwwn, uint32_t rp_id)
3467 {
3468         nvlist_t *attr_list;
3469         int port_instance;
3470 
3471         if (!fct_dip)
3472                 return;
3473         port_instance = ddi_get_instance(fct_dip);
3474 
3475         if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3476             KM_SLEEP) != DDI_SUCCESS) {
3477                 goto alloc_failed;
3478         }
3479 
3480         if (nvlist_add_uint32(attr_list, "instance", port_instance)
3481             != DDI_SUCCESS) {
3482                 goto error;
3483         }
3484 
3485         if (nvlist_add_byte_array(attr_list, "port-wwn",
3486             port->port_pwwn, 8) != DDI_SUCCESS) {
3487                 goto error;
3488         }
3489 
3490         if (nvlist_add_byte_array(attr_list, "target-port-wwn",
3491             rp_pwwn, 8) != DDI_SUCCESS) {
3492                 goto error;
3493         }
3494 
3495         if (nvlist_add_uint32(attr_list, "target-port-id",
3496             rp_id) != DDI_SUCCESS) {
3497                 goto error;
3498         }
3499 
3500         (void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3501             subclass, attr_list, NULL, DDI_SLEEP);
3502 
3503         nvlist_free(attr_list);
3504         return;
3505 
3506 error:
3507         nvlist_free(attr_list);
3508 alloc_failed:
3509         stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3510             "Unable to send %s event", subclass);
3511 }
3512 
3513 uint64_t
3514 fct_netbuf_to_value(uint8_t *buf, uint8_t nbytes)
3515 {
3516         uint64_t        ret = 0;
3517         uint8_t         idx = 0;
3518 
3519         do {
3520                 ret |= (buf[idx] << (8 * (nbytes -idx - 1)));
3521         } while (++idx < nbytes);
3522 
3523         return (ret);
3524 }
3525 
3526 void
3527 fct_value_to_netbuf(uint64_t value, uint8_t *buf, uint8_t nbytes)
3528 {
3529         uint8_t         idx = 0;
3530 
3531         for (idx = 0; idx < nbytes; idx++) {
3532                 buf[idx] = 0xFF & (value >> (8 * (nbytes - idx - 1)));
3533         }
3534 }
3535 
3536 /*
3537  * from_ptr: ptr to uchar_t array of size WWN_SIZE
3538  * to_ptr: char ptr to string of size WWN_SIZE*2+1
3539  */
3540 void
3541 fct_wwn_to_str(char *to_ptr, const uint8_t *from_ptr)
3542 {
3543         ASSERT(to_ptr != NULL && from_ptr != NULL);
3544 
3545         (void) sprintf(to_ptr, "%02x%02x%02x%02x%02x%02x%02x%02x",
3546             from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
3547             from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
3548 }
3549 
3550 static int
3551 fct_update_stats(kstat_t *ks, int rw)
3552 {
3553         fct_i_local_port_t *iport;
3554         fct_port_stat_t *port_kstat;
3555         fct_port_link_status_t stat;
3556         uint32_t        buf_size = sizeof (stat);
3557         int             ret;
3558 
3559         if (rw == KSTAT_WRITE)
3560                 return (EACCES);
3561 
3562         iport = (fct_i_local_port_t *)ks->ks_private;
3563         port_kstat = (fct_port_stat_t *)ks->ks_data;
3564 
3565         if (iport->iport_port->port_info == NULL) {
3566                 return (EIO);
3567         }
3568         ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
3569             iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
3570         if (ret != STMF_SUCCESS) {
3571                 return (EIO);
3572         }
3573 
3574         port_kstat->link_failure_cnt.value.ui32 =
3575             stat.LinkFailureCount;
3576         port_kstat->loss_of_sync_cnt.value.ui32 =
3577             stat.LossOfSyncCount;
3578         port_kstat->loss_of_signals_cnt.value.ui32 =
3579             stat.LossOfSignalsCount;
3580         port_kstat->prim_seq_protocol_err_cnt.value.ui32 =
3581             stat.PrimitiveSeqProtocolErrorCount;
3582         port_kstat->invalid_tx_word_cnt.value.ui32 =
3583             stat.InvalidTransmissionWordCount;
3584         port_kstat->invalid_crc_cnt.value.ui32 =
3585             stat.InvalidCRCCount;
3586 
3587         return (0);
3588 }
3589 
3590 void
3591 fct_init_kstats(fct_i_local_port_t *iport)
3592 {
3593         kstat_t *ks;
3594         fct_port_stat_t *port_kstat;
3595         char    name[256];
3596 
3597         if (iport->iport_alias)
3598                 (void) sprintf(name, "iport_%s", iport->iport_alias);
3599         else
3600                 (void) sprintf(name, "iport_%"PRIxPTR"", (uintptr_t)iport);
3601         ks = kstat_create(FCT_MODULE_NAME, 0, name, "rawdata",
3602             KSTAT_TYPE_NAMED, sizeof (fct_port_stat_t) / sizeof (kstat_named_t),
3603             0);
3604 
3605         if (ks == NULL) {
3606                 return;
3607         }
3608         port_kstat = (fct_port_stat_t *)ks->ks_data;
3609 
3610         iport->iport_kstat_portstat = ks;
3611         kstat_named_init(&port_kstat->link_failure_cnt,
3612             "Link_failure_cnt", KSTAT_DATA_UINT32);
3613         kstat_named_init(&port_kstat->loss_of_sync_cnt,
3614             "Loss_of_sync_cnt", KSTAT_DATA_UINT32);
3615         kstat_named_init(&port_kstat->loss_of_signals_cnt,
3616             "Loss_of_signals_cnt", KSTAT_DATA_UINT32);
3617         kstat_named_init(&port_kstat->prim_seq_protocol_err_cnt,
3618             "Prim_seq_protocol_err_cnt", KSTAT_DATA_UINT32);
3619         kstat_named_init(&port_kstat->invalid_tx_word_cnt,
3620             "Invalid_tx_word_cnt", KSTAT_DATA_UINT32);
3621         kstat_named_init(&port_kstat->invalid_crc_cnt,
3622             "Invalid_crc_cnt", KSTAT_DATA_UINT32);
3623         ks->ks_update = fct_update_stats;
3624         ks->ks_private = (void *)iport;
3625         kstat_install(ks);
3626 
3627 }