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