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