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