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