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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * fcsm - ULP Module for Fibre Channel SAN Management
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/file.h>
  32 #include <sys/kmem.h>
  33 #include <sys/scsi/scsi.h>
  34 #include <sys/var.h>
  35 #include <sys/byteorder.h>
  36 #include <sys/fibre-channel/fc.h>
  37 #include <sys/fibre-channel/impl/fc_ulpif.h>
  38 #include <sys/fibre-channel/ulp/fcsm.h>
  39 
  40 /* Definitions */
  41 #define FCSM_VERSION            "20090729-1.28"
  42 #define FCSM_NAME_VERSION       "SunFC FCSM v" FCSM_VERSION
  43 
  44 /* Global Variables */
  45 static char             fcsm_name[] = "FCSM";
  46 static void             *fcsm_state = NULL;
  47 static kmutex_t         fcsm_global_mutex;
  48 static uint32_t         fcsm_flag = FCSM_IDLE;
  49 static dev_info_t       *fcsm_dip = NULL;
  50 static fcsm_t           *fcsm_port_head = NULL;
  51 static kmem_cache_t     *fcsm_job_cache = NULL;
  52 static int              fcsm_num_attaching = 0;
  53 static int              fcsm_num_detaching = 0;
  54 static int              fcsm_detached = 0;
  55 
  56 static int              fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
  57 static int              fcsm_retry_interval = FCSM_RETRY_INTERVAL;
  58 static int              fcsm_retry_ticker = FCSM_RETRY_TICKER;
  59 static int              fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
  60 static int              fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
  61 static clock_t          fcsm_retry_ticks;
  62 static clock_t          fcsm_offline_ticks;
  63 
  64 
  65 
  66 #ifdef DEBUG
  67 uint32_t                fcsm_debug = 0;
  68 #endif
  69 
  70 
  71 /* Character/Block entry points */
  72 struct cb_ops   fcsm_cb_ops = {
  73         fcsm_open,      /* open */
  74         fcsm_close,     /* close */
  75         nodev,          /* strategy */
  76         nodev,          /* print */
  77         nodev,          /* dump */
  78         nodev,          /* read */
  79         nodev,          /* write */
  80         fcsm_ioctl,     /* ioctl */
  81         nodev,          /* devmap */
  82         nodev,          /* mmap */
  83         nodev,          /* segmap */
  84         nochpoll,       /* poll */
  85         ddi_prop_op,
  86         NULL,           /* streams info */
  87         D_NEW | D_MP,
  88         CB_REV,
  89         nodev,          /* aread */
  90         nodev           /* awrite */
  91 };
  92 
  93 struct dev_ops fcsm_ops = {
  94         DEVO_REV,
  95         0,              /* refcnt */
  96         fcsm_getinfo,   /* get info */
  97         nulldev,        /* identify (obsolete) */
  98         nulldev,        /* probe (not required for self-identifying devices) */
  99         fcsm_attach,    /* attach */
 100         fcsm_detach,    /* detach */
 101         nodev,          /* reset */
 102         &fcsm_cb_ops,       /* char/block entry points structure for leaf drivers */
 103         NULL,           /* bus operations for nexus driver */
 104         NULL            /* power management */
 105 };
 106 
 107 
 108 struct modldrv modldrv = {
 109         &mod_driverops,
 110         FCSM_NAME_VERSION,
 111         &fcsm_ops
 112 };
 113 
 114 struct modlinkage modlinkage = {
 115         MODREV_1,
 116         &modldrv,
 117         NULL
 118 };
 119 
 120 static fc_ulp_modinfo_t fcsm_modinfo = {
 121         &fcsm_modinfo,              /* ulp_handle */
 122         FCTL_ULP_MODREV_4,      /* ulp_rev */
 123         FC_TYPE_FC_SERVICES,    /* ulp_type */
 124         fcsm_name,              /* ulp_name */
 125         0,                      /* ulp_statec_mask: get all statec callbacks */
 126         fcsm_port_attach,       /* ulp_port_attach */
 127         fcsm_port_detach,       /* ulp_port_detach */
 128         fcsm_port_ioctl,        /* ulp_port_ioctl */
 129         fcsm_els_cb,            /* ulp_els_callback */
 130         fcsm_data_cb,           /* ulp_data_callback */
 131         fcsm_statec_cb          /* ulp_statec_callback */
 132 };
 133 
 134 struct fcsm_xlat_pkt_state {
 135         uchar_t xlat_state;
 136         int     xlat_rval;
 137 } fcsm_xlat_pkt_state [] = {
 138         { FC_PKT_SUCCESS,               FC_SUCCESS },
 139         { FC_PKT_REMOTE_STOP,           FC_FAILURE },
 140         { FC_PKT_LOCAL_RJT,             FC_TRANSPORT_ERROR },
 141         { FC_PKT_NPORT_RJT,             FC_PREJECT },
 142         { FC_PKT_FABRIC_RJT,            FC_FREJECT },
 143         { FC_PKT_LOCAL_BSY,             FC_TRAN_BUSY },
 144         { FC_PKT_TRAN_BSY,              FC_TRAN_BUSY },
 145         { FC_PKT_NPORT_BSY,             FC_PBUSY },
 146         { FC_PKT_FABRIC_BSY,            FC_FBUSY },
 147         { FC_PKT_LS_RJT,                FC_PREJECT },
 148         { FC_PKT_BA_RJT,                FC_PREJECT },
 149         { FC_PKT_TIMEOUT,               FC_FAILURE },
 150         { FC_PKT_FS_RJT,                FC_FAILURE },
 151         { FC_PKT_TRAN_ERROR,            FC_TRANSPORT_ERROR },
 152         { FC_PKT_FAILURE,               FC_FAILURE },
 153         { FC_PKT_PORT_OFFLINE,          FC_OFFLINE },
 154         { FC_PKT_ELS_IN_PROGRESS,       FC_FAILURE }
 155 };
 156 
 157 struct fcsm_xlat_port_state {
 158         uint32_t        xlat_pstate;
 159         caddr_t         xlat_state_str;
 160 } fcsm_xlat_port_state [] = {
 161         { FC_STATE_OFFLINE,             "OFFLINE" },
 162         { FC_STATE_ONLINE,              "ONLINE" },
 163         { FC_STATE_LOOP,                "LOOP" },
 164         { FC_STATE_NAMESERVICE,         "NAMESERVICE" },
 165         { FC_STATE_RESET,               "RESET" },
 166         { FC_STATE_RESET_REQUESTED,     "RESET_REQUESTED" },
 167         { FC_STATE_LIP,                 "LIP" },
 168         { FC_STATE_LIP_LBIT_SET,        "LIP_LBIT_SET" },
 169         { FC_STATE_DEVICE_CHANGE,       "DEVICE_CHANGE" },
 170         { FC_STATE_TARGET_PORT_RESET,   "TARGET_PORT_RESET" }
 171 };
 172 
 173 struct fcsm_xlat_topology {
 174         uint32_t        xlat_top;
 175         caddr_t         xlat_top_str;
 176 } fcsm_xlat_topology [] = {
 177         { FC_TOP_UNKNOWN,       "UNKNOWN" },
 178         { FC_TOP_PRIVATE_LOOP,  "Private Loop" },
 179         { FC_TOP_PUBLIC_LOOP,   "Public Loop" },
 180         { FC_TOP_FABRIC,        "Fabric" },
 181         { FC_TOP_PT_PT,         "Point-to-Point" },
 182         { FC_TOP_NO_NS,         "NO_NS" }
 183 };
 184 
 185 struct fcsm_xlat_dev_type {
 186         uint32_t        xlat_type;
 187         caddr_t         xlat_str;
 188 } fcsm_xlat_dev_type [] = {
 189         { PORT_DEVICE_NOCHANGE,         "No Change" },
 190         { PORT_DEVICE_NEW,              "New" },
 191         { PORT_DEVICE_OLD,              "Old" },
 192         { PORT_DEVICE_CHANGED,          "Changed" },
 193         { PORT_DEVICE_DELETE,           "Delete" },
 194         { PORT_DEVICE_USER_LOGIN,       "User Login" },
 195         { PORT_DEVICE_USER_LOGOUT,      "User Logout" },
 196         { PORT_DEVICE_USER_CREATE,      "User Create" },
 197         { PORT_DEVICE_USER_DELETE,      "User Delete" }
 198 };
 199 
 200 int
 201 _init(void)
 202 {
 203         int             rval;
 204 
 205         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
 206 
 207         fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
 208         fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
 209 
 210         if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
 211             FCSM_INIT_INSTANCES)) {
 212                 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 213                     "_init: ddi_soft_state_init failed");
 214                 return (ENOMEM);
 215         }
 216 
 217         mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
 218 
 219         fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
 220             sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
 221             fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
 222 
 223         if (fcsm_job_cache == NULL) {
 224                 mutex_destroy(&fcsm_global_mutex);
 225                 ddi_soft_state_fini(&fcsm_state);
 226                 return (ENOMEM);
 227         }
 228 
 229         /*
 230          * Now call fc_ulp_add to add this ULP in the transport layer
 231          * database. This will cause 'ulp_port_attach' callback function
 232          * to be called.
 233          */
 234         rval = fc_ulp_add(&fcsm_modinfo);
 235         if (rval != 0) {
 236                 switch (rval) {
 237                 case FC_ULP_SAMEMODULE:
 238                         fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 239                             "_init: FC SAN Management module is already "
 240                             "registered with transport layer");
 241                         rval = EEXIST;
 242                         break;
 243 
 244                 case FC_ULP_SAMETYPE:
 245                         fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 246                             "_init: Another module with same type 0x%x is "
 247                             "already registered with transport layer",
 248                             fcsm_modinfo.ulp_type);
 249                         rval = EEXIST;
 250                         break;
 251 
 252                 case FC_BADULP:
 253                         fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 254                             "_init: Please upgrade this module. Current "
 255                             "version 0x%x is not the most recent version",
 256                             fcsm_modinfo.ulp_rev);
 257                         rval = EIO;
 258                         break;
 259                 default:
 260                         fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 261                             "_init: fc_ulp_add failed with status 0x%x", rval);
 262                         rval = EIO;
 263                         break;
 264                 }
 265                 kmem_cache_destroy(fcsm_job_cache);
 266                 mutex_destroy(&fcsm_global_mutex);
 267                 ddi_soft_state_fini(&fcsm_state);
 268                 return (rval);
 269         }
 270 
 271         if ((rval = mod_install(&modlinkage)) != 0) {
 272                 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
 273                     "_init: mod_install failed with status 0x%x", rval));
 274                 (void) fc_ulp_remove(&fcsm_modinfo);
 275                 kmem_cache_destroy(fcsm_job_cache);
 276                 mutex_destroy(&fcsm_global_mutex);
 277                 ddi_soft_state_fini(&fcsm_state);
 278                 return (rval);
 279         }
 280 
 281         return (rval);
 282 }
 283 
 284 int
 285 _fini(void)
 286 {
 287         int     rval;
 288 #ifdef  DEBUG
 289         int     status;
 290 #endif /* DEBUG */
 291 
 292         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
 293 
 294         /*
 295          * don't start cleaning up until we know that the module remove
 296          * has worked  -- if this works, then we know that each instance
 297          * has successfully been DDI_DETACHed
 298          */
 299         if ((rval = mod_remove(&modlinkage)) != 0) {
 300                 return (rval);
 301         }
 302 
 303 #ifdef DEBUG
 304         status = fc_ulp_remove(&fcsm_modinfo);
 305         if (status != 0) {
 306                 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
 307                     "_fini: fc_ulp_remove failed with status 0x%x", status));
 308         }
 309 #else
 310         (void) fc_ulp_remove(&fcsm_modinfo);
 311 #endif /* DEBUG */
 312 
 313         fcsm_detached = 0;
 314 
 315         /*
 316          * It is possible to modunload fcsm manually, which will cause
 317          * a bypass of all the port_detach functionality.  We may need
 318          * to force that code path to be executed to properly clean up
 319          * in that case.
 320          */
 321         fcsm_force_port_detach_all();
 322 
 323         kmem_cache_destroy(fcsm_job_cache);
 324         mutex_destroy(&fcsm_global_mutex);
 325         ddi_soft_state_fini(&fcsm_state);
 326 
 327         return (rval);
 328 }
 329 
 330 
 331 int
 332 _info(struct modinfo *modinfop)
 333 {
 334         return (mod_info(&modlinkage, modinfop));
 335 }
 336 
 337 /* ARGSUSED */
 338 static int
 339 fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 340 {
 341         int rval = DDI_FAILURE;
 342 
 343         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 344             "attach: cmd 0x%x", cmd));
 345 
 346         switch (cmd) {
 347         case DDI_ATTACH:
 348                 mutex_enter(&fcsm_global_mutex);
 349                 if (fcsm_dip != NULL) {
 350                         mutex_exit(&fcsm_global_mutex);
 351                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 352                             "attach: duplicate attach of fcsm!!"));
 353                         break;
 354                 }
 355 
 356                 fcsm_dip = dip;
 357 
 358                 /*
 359                  * The detach routine cleans up all the port instances
 360                  * i.e. it detaches all ports.
 361                  * If _fini never got called after detach, then
 362                  * perform an fc_ulp_remove() followed by fc_ulp_add()
 363                  * to ensure that port_attach callbacks are called
 364                  * again.
 365                  */
 366                 if (fcsm_detached) {
 367                         int status;
 368 
 369                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 370                             "attach: rebinding to transport driver"));
 371 
 372                         mutex_exit(&fcsm_global_mutex);
 373 
 374                         (void) fc_ulp_remove(&fcsm_modinfo);
 375 
 376                         /*
 377                          * Reset the detached flag, so that ports can attach
 378                          */
 379                         mutex_enter(&fcsm_global_mutex);
 380                         fcsm_detached = 0;
 381                         mutex_exit(&fcsm_global_mutex);
 382 
 383                         status = fc_ulp_add(&fcsm_modinfo);
 384 
 385                         if (status != 0) {
 386                                 /*
 387                                  * ULP add failed. So set the
 388                                  * detached flag again
 389                                  */
 390                                 mutex_enter(&fcsm_global_mutex);
 391                                 fcsm_detached = 1;
 392                                 mutex_exit(&fcsm_global_mutex);
 393 
 394                                 switch (status) {
 395                                 case FC_ULP_SAMEMODULE:
 396                                         fcsm_display(CE_WARN, SM_LOG, NULL,
 397                                             NULL, "attach: FC SAN Management "
 398                                             "module is already "
 399                                             "registered with transport layer");
 400                                         break;
 401 
 402                                 case FC_ULP_SAMETYPE:
 403                                         fcsm_display(CE_WARN, SM_LOG, NULL,
 404                                             NULL, "attach: Another module with "
 405                                             "same type 0x%x is already "
 406                                             "registered with transport layer",
 407                                             fcsm_modinfo.ulp_type);
 408                                         break;
 409 
 410                                 case FC_BADULP:
 411                                         fcsm_display(CE_WARN, SM_LOG, NULL,
 412                                             NULL, "attach: Please upgrade this "
 413                                             "module. Current version 0x%x is "
 414                                             "not the most recent version",
 415                                             fcsm_modinfo.ulp_rev);
 416                                         break;
 417                                 default:
 418                                         fcsm_display(CE_WARN, SM_LOG, NULL,
 419                                             NULL, "attach: fc_ulp_add failed "
 420                                             "with status 0x%x", status);
 421                                         break;
 422                                 }
 423 
 424                                 /* Return failure */
 425                                 break;
 426                         }
 427 
 428                         mutex_enter(&fcsm_global_mutex);
 429                 }
 430 
 431                 /* Create a minor node */
 432                 if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
 433                     NULL, DDI_PSEUDO, 0) == DDI_SUCCESS) {
 434                         /* Announce presence of the device */
 435                         mutex_exit(&fcsm_global_mutex);
 436                         ddi_report_dev(dip);
 437                         rval = DDI_SUCCESS;
 438                 } else {
 439                         fcsm_dip = NULL;
 440                         mutex_exit(&fcsm_global_mutex);
 441                         fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
 442                             NULL, NULL, "attach: create minor node failed");
 443                 }
 444                 break;
 445 
 446         case DDI_RESUME:
 447                 rval = DDI_SUCCESS;
 448                 break;
 449 
 450         default:
 451                 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
 452                     "attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
 453                 break;
 454         }
 455 
 456         return (rval);
 457 }
 458 
 459 /* ARGSUSED */
 460 static int
 461 fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 462 {
 463         int     instance;
 464         int     rval = DDI_SUCCESS;
 465 
 466         instance = getminor((dev_t)arg);
 467 
 468         switch (cmd) {
 469         case DDI_INFO_DEVT2INSTANCE:
 470                 *result = (void *)(long)instance; /* minor number is instance */
 471                 break;
 472 
 473         case DDI_INFO_DEVT2DEVINFO:
 474                 mutex_enter(&fcsm_global_mutex);
 475                 *result = (void *)fcsm_dip;
 476                 mutex_exit(&fcsm_global_mutex);
 477                 break;
 478 
 479         default:
 480                 rval = DDI_FAILURE;
 481                 break;
 482         }
 483 
 484         return (rval);
 485 }
 486 
 487 
 488 /* ARGSUSED */
 489 static int
 490 fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
 491     fc_attach_cmd_t cmd, uint32_t s_id)
 492 {
 493         int     instance;
 494         int     rval = FC_FAILURE;
 495 
 496         instance = ddi_get_instance(pinfo->port_dip);
 497 
 498         /*
 499          * Set the attaching flag, so that fcsm_detach will fail, if
 500          * port attach is in progress.
 501          */
 502         mutex_enter(&fcsm_global_mutex);
 503         if (fcsm_detached) {
 504                 mutex_exit(&fcsm_global_mutex);
 505 
 506                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 507                     "port_attach: end. detach in progress. failing attach "
 508                     "instance 0x%x", instance));
 509                 return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
 510                     FC_FAILURE_SILENT : FC_FAILURE);
 511         }
 512 
 513         fcsm_num_attaching++;
 514         mutex_exit(&fcsm_global_mutex);
 515 
 516         switch (cmd) {
 517         case FC_CMD_ATTACH:
 518                 if (fcsm_handle_port_attach(pinfo, s_id, instance)
 519                     != DDI_SUCCESS) {
 520                         ASSERT(ddi_get_soft_state(fcsm_state,
 521                             instance) == NULL);
 522                         break;
 523                 }
 524                 rval = FC_SUCCESS;
 525                 break;
 526 
 527         case FC_CMD_RESUME:
 528         case FC_CMD_POWER_UP: {
 529                 fcsm_t  *fcsm;
 530                 char fcsm_pathname[MAXPATHLEN];
 531 
 532                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 533                     "port_attach: cmd 0x%x instance 0x%x", cmd, instance));
 534 
 535                 /* Get the soft state structure */
 536                 if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
 537                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
 538                             "port_attach: instance 0x%x, cmd 0x%x "
 539                             "get softstate failed", instance, cmd));
 540                         break;
 541                 }
 542 
 543                 ASSERT(fcsm->sm_instance == instance);
 544 
 545                 /* If this instance is not attached, then return failure */
 546                 mutex_enter(&fcsm->sm_mutex);
 547                 if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
 548                         mutex_exit(&fcsm->sm_mutex);
 549                         fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
 550                             "port_detach: port is not attached");
 551                         break;
 552                 }
 553                 mutex_exit(&fcsm->sm_mutex);
 554 
 555                 if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
 556                     DDI_SUCCESS) {
 557                         break;
 558                 }
 559 
 560                 (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
 561                 fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
 562                     "attached to path %s", fcsm_pathname);
 563                 rval = FC_SUCCESS;
 564                 break;
 565         }
 566 
 567         default:
 568                 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
 569                     "port_attach: unknown cmd 0x%x for port 0x%x",
 570                     cmd, instance));
 571                 break;
 572         }
 573 
 574         mutex_enter(&fcsm_global_mutex);
 575         fcsm_num_attaching--;
 576         mutex_exit(&fcsm_global_mutex);
 577         return (rval);
 578 }
 579 
 580 
 581 static int
 582 fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
 583 {
 584         fcsm_t          *fcsm;
 585         kthread_t       *thread;
 586         char            name[32];
 587         char fcsm_pathname[MAXPATHLEN];
 588 
 589         /* Allocate a soft state structure for the port */
 590         if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
 591                 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 592                     "port_attach: instance 0x%x, soft state alloc failed",
 593                     instance);
 594                 return (DDI_FAILURE);
 595         }
 596 
 597         if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
 598                 fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
 599                     "port_attach: instance 0x%x, get soft state failed",
 600                     instance);
 601                 ddi_soft_state_free(fcsm_state, instance);
 602                 return (DDI_FAILURE);
 603         }
 604 
 605 
 606         /* Initialize the mutex */
 607         mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
 608         cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
 609 
 610         mutex_enter(&fcsm->sm_mutex);
 611         fcsm->sm_flags               |= FCSM_ATTACHING;
 612         fcsm->sm_sid         = s_id;
 613         fcsm->sm_instance    = instance;
 614         fcsm->sm_port_state  = pinfo->port_state;
 615 
 616         /*
 617          * Make a copy of the port_information structure, since fctl
 618          * uses a temporary structure.
 619          */
 620         fcsm->sm_port_info   = *pinfo;       /* Structure copy !!! */
 621         mutex_exit(&fcsm->sm_mutex);
 622 
 623 
 624         (void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
 625         fcsm->sm_cmd_cache = kmem_cache_create(name,
 626             sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
 627             fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
 628             NULL, (void *)fcsm, NULL, 0);
 629         if (fcsm->sm_cmd_cache == NULL) {
 630                 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
 631                     "port_attach: pkt cache create failed");
 632                 cv_destroy(&fcsm->sm_job_cv);
 633                 mutex_destroy(&fcsm->sm_mutex);
 634                 ddi_soft_state_free(fcsm_state, instance);
 635                 return (DDI_FAILURE);
 636         }
 637 
 638         thread = thread_create((caddr_t)NULL, 0, fcsm_job_thread,
 639             (caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
 640         if (thread == NULL) {
 641                 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
 642                     "port_attach: job thread create failed");
 643                 kmem_cache_destroy(fcsm->sm_cmd_cache);
 644                 cv_destroy(&fcsm->sm_job_cv);
 645                 mutex_destroy(&fcsm->sm_mutex);
 646                 ddi_soft_state_free(fcsm_state, instance);
 647                 return (DDI_FAILURE);
 648         }
 649 
 650         fcsm->sm_thread = thread;
 651 
 652         /* Add this structure to fcsm global linked list */
 653         mutex_enter(&fcsm_global_mutex);
 654         if (fcsm_port_head == NULL) {
 655                 fcsm_port_head = fcsm;
 656         } else {
 657                 fcsm->sm_next = fcsm_port_head;
 658                 fcsm_port_head = fcsm;
 659         }
 660         mutex_exit(&fcsm_global_mutex);
 661 
 662         mutex_enter(&fcsm->sm_mutex);
 663         fcsm->sm_flags &= ~FCSM_ATTACHING;
 664         fcsm->sm_flags |= FCSM_ATTACHED;
 665         fcsm->sm_port_top = pinfo->port_flags;
 666         fcsm->sm_port_state = pinfo->port_state;
 667         if (pinfo->port_acc_attr == NULL) {
 668                 /*
 669                  * The corresponding FCA doesn't support DMA at all
 670                  */
 671                 fcsm->sm_flags |= FCSM_USING_NODMA_FCA;
 672         }
 673         mutex_exit(&fcsm->sm_mutex);
 674 
 675         (void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
 676         fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
 677             "attached to path %s", fcsm_pathname);
 678 
 679         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
 680             "port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
 681             fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
 682             pinfo->port_state,
 683             fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
 684 
 685         return (DDI_SUCCESS);
 686 }
 687 
 688 static int
 689 fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
 690     fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
 691 {
 692         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
 693             "port_resume: cmd 0x%x", cmd));
 694 
 695         mutex_enter(&fcsm->sm_mutex);
 696 
 697         switch (cmd) {
 698         case FC_CMD_RESUME:
 699                 ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
 700                 fcsm->sm_flags &= ~FCSM_SUSPENDED;
 701                 break;
 702 
 703         case FC_CMD_POWER_UP:
 704                 /* If port is suspended, then no need to resume */
 705                 fcsm->sm_flags &= ~FCSM_POWER_DOWN;
 706                 if (fcsm->sm_flags & FCSM_SUSPENDED) {
 707                         mutex_exit(&fcsm->sm_mutex);
 708                         return (DDI_SUCCESS);
 709                 }
 710                 break;
 711         default:
 712                 mutex_exit(&fcsm->sm_mutex);
 713                 return (DDI_FAILURE);
 714         }
 715 
 716         fcsm->sm_sid = s_id;
 717 
 718         /*
 719          * Make a copy of the new port_information structure
 720          */
 721         fcsm->sm_port_info   = *pinfo;       /* Structure copy !!! */
 722         mutex_exit(&fcsm->sm_mutex);
 723 
 724         fcsm_resume_port(fcsm);
 725 
 726         /*
 727          * Invoke state change processing.
 728          * This will ensure that
 729          *    - offline timer is started if new port state changed to offline.
 730          *    - MGMT_SERVER_LOGIN flag is reset.
 731          *    - Port topology is updated.
 732          */
 733         fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
 734             pinfo->port_flags, NULL, 0, s_id);
 735 
 736         return (DDI_SUCCESS);
 737 }
 738 
 739 
 740 /* ARGSUSED */
 741 static int
 742 fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 743 {
 744         int     rval = DDI_SUCCESS;
 745 
 746         switch (cmd) {
 747         case DDI_DETACH: {
 748                 fcsm_t  *fcsm;
 749 
 750                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 751                     "detach: start. cmd <DETACH>", cmd));
 752 
 753                 mutex_enter(&fcsm_global_mutex);
 754 
 755                 /*
 756                  * If port attach/detach in progress, then wait for 5 seconds
 757                  * for them to complete.
 758                  */
 759                 if (fcsm_num_attaching || fcsm_num_detaching) {
 760                         int count;
 761 
 762                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
 763                             "detach: wait for port attach/detach to complete"));
 764 
 765                         count = 0;
 766                         while ((count++ <= 30) &&
 767                             (fcsm_num_attaching || fcsm_num_detaching)) {
 768                                 mutex_exit(&fcsm_global_mutex);
 769                                 delay(drv_usectohz(1000000));
 770                                 mutex_enter(&fcsm_global_mutex);
 771                         }
 772 
 773                         /* Port attach/detach still in prog, so fail detach */
 774                         if (fcsm_num_attaching || fcsm_num_detaching) {
 775                                 mutex_exit(&fcsm_global_mutex);
 776                                 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
 777                                     NULL, "detach: Failing detach. port "
 778                                     "attach/detach in progress"));
 779                                 rval = DDI_FAILURE;
 780                                 break;
 781                         }
 782                 }
 783 
 784                 if (fcsm_port_head == NULL) {
 785                         /* Not much do, Succeed to detach. */
 786                         ddi_remove_minor_node(fcsm_dip, NULL);
 787                         fcsm_dip = NULL;
 788                         fcsm_detached = 0;
 789                         mutex_exit(&fcsm_global_mutex);
 790                         break;
 791                 }
 792 
 793                 /*
 794                  * Check to see, if any ports are active.
 795                  * If not, then set the DETACHING flag to indicate
 796                  * that they are being detached.
 797                  */
 798                 fcsm = fcsm_port_head;
 799                 while (fcsm != NULL) {
 800 
 801                         mutex_enter(&fcsm->sm_mutex);
 802                         if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
 803                             fcsm->sm_ncmds || fcsm->sm_cb_count) {
 804                                 /* port is busy. We can't detach */
 805                                 mutex_exit(&fcsm->sm_mutex);
 806                                 break;
 807                         }
 808 
 809                         fcsm->sm_flags |= FCSM_DETACHING;
 810                         mutex_exit(&fcsm->sm_mutex);
 811 
 812                         fcsm = fcsm->sm_next;
 813                 }
 814 
 815                 /*
 816                  * If all ports could not be marked for detaching,
 817                  * then clear the flags and fail the detach.
 818                  * Also if a port attach is currently in progress
 819                  * then fail the detach.
 820                  */
 821                 if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
 822                         /*
 823                          * Some ports were busy, so can't detach.
 824                          * Clear the DETACHING flag and return failure
 825                          */
 826                         fcsm = fcsm_port_head;
 827                         while (fcsm != NULL) {
 828                                 mutex_enter(&fcsm->sm_mutex);
 829                                 if (fcsm->sm_flags & FCSM_DETACHING) {
 830                                         fcsm->sm_flags &= ~FCSM_DETACHING;
 831                                 }
 832                                 mutex_exit(&fcsm->sm_mutex);
 833 
 834                                 fcsm = fcsm->sm_next;
 835                         }
 836                         mutex_exit(&fcsm_global_mutex);
 837                         return (DDI_FAILURE);
 838                 } else {
 839                         fcsm_detached = 1;
 840                         /*
 841                          * Mark all the detaching ports as detached, as we
 842                          * will be detaching them
 843                          */
 844                         fcsm = fcsm_port_head;
 845                         while (fcsm != NULL) {
 846                                 mutex_enter(&fcsm->sm_mutex);
 847                                 fcsm->sm_flags &= ~FCSM_DETACHING;
 848                                 fcsm->sm_flags |= FCSM_DETACHED;
 849                                 mutex_exit(&fcsm->sm_mutex);
 850 
 851                                 fcsm = fcsm->sm_next;
 852                         }
 853                 }
 854                 mutex_exit(&fcsm_global_mutex);
 855 
 856 
 857                 /*
 858                  * Go ahead and detach the ports
 859                  */
 860                 mutex_enter(&fcsm_global_mutex);
 861                 while (fcsm_port_head != NULL) {
 862                         fcsm = fcsm_port_head;
 863                         mutex_exit(&fcsm_global_mutex);
 864 
 865                         /*
 866                          * Call fcsm_cleanup_port(). This cleansup and
 867                          * removes the fcsm structure from global linked list
 868                          */
 869                         fcsm_cleanup_port(fcsm);
 870 
 871                         /*
 872                          * Soft state cleanup done.
 873                          * Remember that fcsm struct doesn't exist anymore.
 874                          */
 875 
 876                         mutex_enter(&fcsm_global_mutex);
 877                 }
 878 
 879                 ddi_remove_minor_node(fcsm_dip, NULL);
 880                 fcsm_dip = NULL;
 881                 mutex_exit(&fcsm_global_mutex);
 882                 break;
 883         }
 884 
 885         case DDI_SUSPEND:
 886                 rval = DDI_SUCCESS;
 887                 break;
 888 
 889         default:
 890                 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
 891                     "detach: unknown cmd 0x%x", cmd));
 892                 rval = DDI_FAILURE;
 893                 break;
 894         }
 895 
 896         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
 897             "detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
 898 
 899         return (rval);
 900 }
 901 
 902 
 903 /* ARGSUSED */
 904 static void
 905 fcsm_force_port_detach_all(void)
 906 {
 907         fcsm_t  *fcsm;
 908 
 909         fcsm = fcsm_port_head;
 910 
 911         while (fcsm) {
 912                 fcsm_cleanup_port(fcsm);
 913                 /*
 914                  * fcsm_cleanup_port will remove the current fcsm structure
 915                  * from the list, which will cause fcsm_port_head to point
 916                  * to what would have been the next structure on the list.
 917                  */
 918                 fcsm = fcsm_port_head;
 919         }
 920 }
 921 
 922 
 923 /* ARGSUSED */
 924 static int
 925 fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
 926 {
 927         int     instance;
 928         int     rval = FC_FAILURE;
 929         fcsm_t  *fcsm;
 930 
 931         instance = ddi_get_instance(pinfo->port_dip);
 932 
 933         mutex_enter(&fcsm_global_mutex);
 934         if (fcsm_detached) {
 935                 mutex_exit(&fcsm_global_mutex);
 936 
 937                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
 938                     "port_detach: end. instance 0x%x, fcsm is detached",
 939                     instance));
 940                 return (FC_SUCCESS);
 941         }
 942         fcsm_num_detaching++;   /* Set the flag */
 943         mutex_exit(&fcsm_global_mutex);
 944 
 945         /* Get the soft state structure */
 946         if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
 947                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
 948                     "port_detach: instance 0x%x, cmd 0x%x get softstate failed",
 949                     instance, cmd));
 950                 mutex_enter(&fcsm_global_mutex);
 951                 fcsm_num_detaching--;
 952                 mutex_exit(&fcsm_global_mutex);
 953                 return (rval);
 954         }
 955 
 956         ASSERT(fcsm->sm_instance == instance);
 957 
 958         /* If this instance is not attached, then fail the detach */
 959         mutex_enter(&fcsm->sm_mutex);
 960         if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
 961                 mutex_exit(&fcsm->sm_mutex);
 962                 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
 963                     "port_detach: port is not attached");
 964                 mutex_enter(&fcsm_global_mutex);
 965                 fcsm_num_detaching--;
 966                 mutex_exit(&fcsm_global_mutex);
 967                 return (rval);
 968         }
 969         mutex_exit(&fcsm->sm_mutex);
 970 
 971         /*
 972          * If fcsm has been detached, then all instance has already been
 973          * detached or are being detached. So succeed this detach.
 974          */
 975 
 976         switch (cmd) {
 977         case FC_CMD_DETACH:
 978         case FC_CMD_SUSPEND:
 979         case FC_CMD_POWER_DOWN:
 980                 break;
 981 
 982         default:
 983                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
 984                     "port_detach: port unknown cmd 0x%x", cmd));
 985                 mutex_enter(&fcsm_global_mutex);
 986                 fcsm_num_detaching--;
 987                 mutex_exit(&fcsm_global_mutex);
 988                 return (rval);
 989         };
 990 
 991         if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
 992                 rval = FC_SUCCESS;
 993         }
 994 
 995         mutex_enter(&fcsm_global_mutex);
 996         fcsm_num_detaching--;
 997         mutex_exit(&fcsm_global_mutex);
 998 
 999         /* If it was a detach, then fcsm state structure no longer exists */
1000         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1001             "port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
1002         return (rval);
1003 }
1004 
1005 
1006 static int
1007 fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
1008     fc_detach_cmd_t cmd)
1009 {
1010         uint32_t        flag;
1011         int             count;
1012 #ifdef DEBUG
1013         char            pathname[MAXPATHLEN];
1014 #endif /* DEBUG */
1015 
1016         /*
1017          * If port is already powered down OR suspended and there is nothing
1018          * else to do then just return.
1019          * Otherwise, set the flag, so that no more new activity will be
1020          * initiated on this port.
1021          */
1022         mutex_enter(&fcsm->sm_mutex);
1023 
1024         switch (cmd) {
1025         case FC_CMD_DETACH:
1026                 flag = FCSM_DETACHING;
1027                 break;
1028 
1029         case FC_CMD_SUSPEND:
1030         case FC_CMD_POWER_DOWN:
1031                 ((cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) :
1032                     (flag = FCSM_POWER_DOWN));
1033                 if (fcsm->sm_flags &
1034                     (FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
1035                         fcsm->sm_flags |= flag;
1036                         mutex_exit(&fcsm->sm_mutex);
1037                         return (DDI_SUCCESS);
1038                 }
1039                 break;
1040 
1041         default:
1042                 mutex_exit(&fcsm->sm_mutex);
1043                 return (DDI_FAILURE);
1044         };
1045 
1046         fcsm->sm_flags |= flag;
1047 
1048         /*
1049          * If some commands are pending OR callback in progress, then
1050          * wait for some finite amount of time for their completion.
1051          * TODO: add more checks here to check for cmd timeout, offline
1052          * timeout and other (??) threads.
1053          */
1054         count = 0;
1055         while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
1056                 mutex_exit(&fcsm->sm_mutex);
1057                 delay(drv_usectohz(1000000));
1058                 mutex_enter(&fcsm->sm_mutex);
1059         }
1060         if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
1061                 fcsm->sm_flags &= ~flag;
1062                 mutex_exit(&fcsm->sm_mutex);
1063                 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
1064                     "port_detach: Failing suspend, port is busy");
1065                 return (DDI_FAILURE);
1066         }
1067         if (flag == FCSM_DETACHING) {
1068                 fcsm->sm_flags &= ~FCSM_DETACHING;
1069                 fcsm->sm_flags |= FCSM_DETACHED;
1070         }
1071 
1072         mutex_exit(&fcsm->sm_mutex);
1073 
1074         FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
1075             "port_detach: cmd 0x%x pathname <%s>",
1076             cmd, ddi_pathname(pinfo->port_dip, pathname)));
1077 
1078         if (cmd == FC_CMD_DETACH) {
1079                 fcsm_cleanup_port(fcsm);
1080                 /*
1081                  * Soft state cleanup done.
1082                  * Always remember that fcsm struct doesn't exist anymore.
1083                  */
1084         } else {
1085                 fcsm_suspend_port(fcsm);
1086         }
1087 
1088         return (DDI_SUCCESS);
1089 }
1090 
1091 static void
1092 fcsm_suspend_port(fcsm_t *fcsm)
1093 {
1094         mutex_enter(&fcsm->sm_mutex);
1095 
1096         if (fcsm->sm_offline_tid != NULL) {
1097                 timeout_id_t    tid;
1098 
1099                 tid = fcsm->sm_offline_tid;
1100                 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1101                 mutex_exit(&fcsm->sm_mutex);
1102                 (void) untimeout(tid);
1103                 mutex_enter(&fcsm->sm_mutex);
1104                 fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
1105         }
1106 
1107         if (fcsm->sm_retry_tid != NULL) {
1108                 timeout_id_t    tid;
1109 
1110                 tid = fcsm->sm_retry_tid;
1111                 fcsm->sm_retry_tid = (timeout_id_t)NULL;
1112                 mutex_exit(&fcsm->sm_mutex);
1113                 (void) untimeout(tid);
1114                 mutex_enter(&fcsm->sm_mutex);
1115                 fcsm->sm_flags |= FCSM_RESTORE_RETRY_TIMEOUT;
1116         }
1117 
1118         mutex_exit(&fcsm->sm_mutex);
1119 }
1120 
1121 static void
1122 fcsm_resume_port(fcsm_t *fcsm)
1123 {
1124         mutex_enter(&fcsm->sm_mutex);
1125 
1126         if (fcsm->sm_flags & FCSM_RESTORE_OFFLINE_TIMEOUT) {
1127                 fcsm->sm_flags &= ~FCSM_RESTORE_OFFLINE_TIMEOUT;
1128 
1129                 /*
1130                  * If port if offline, link is not marked down and offline
1131                  * timer is not already running, then restart offline timer.
1132                  */
1133                 if (!(fcsm->sm_flags & FCSM_LINK_DOWN) &&
1134                     fcsm->sm_offline_tid == NULL &&
1135                     (fcsm->sm_flags & FCSM_PORT_OFFLINE)) {
1136                         fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1137                             (caddr_t)fcsm, fcsm_offline_ticks);
1138                 }
1139         }
1140 
1141         if (fcsm->sm_flags & FCSM_RESTORE_RETRY_TIMEOUT) {
1142                 fcsm->sm_flags &= ~FCSM_RESTORE_RETRY_TIMEOUT;
1143 
1144                 /*
1145                  * If retry queue is not suspended and some cmds are waiting
1146                  * to be retried, then restart the retry timer
1147                  */
1148                 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1149                         fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1150                             (caddr_t)fcsm, fcsm_retry_ticks);
1151                 }
1152         }
1153         mutex_exit(&fcsm->sm_mutex);
1154 }
1155 
1156 static void
1157 fcsm_cleanup_port(fcsm_t *fcsm)
1158 {
1159         fcsm_t          *curr, *prev;
1160         int             status;
1161         fcsm_job_t      *job;
1162 
1163         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1164             "fcsm_cleanup_port: entered"));
1165 
1166         /*
1167          * Kill the job thread
1168          */
1169         job = fcsm_alloc_job(KM_SLEEP);
1170         ASSERT(job != NULL);
1171         fcsm_init_job(job, fcsm->sm_instance, FCSM_JOB_THREAD_SHUTDOWN,
1172             FCSM_JOBFLAG_SYNC, NULL, NULL, NULL, NULL);
1173 
1174         status = fcsm_process_job(job, 0);
1175         ASSERT(status == FC_SUCCESS);
1176 
1177         ASSERT(job->job_result == FC_SUCCESS);
1178         fcsm_dealloc_job(job);
1179 
1180         /*
1181          * We got here after ensuring the no commands are pending or active.
1182          * Therefore retry timeout thread should NOT be running.
1183          * Kill the offline timeout thread if currently running.
1184          */
1185         mutex_enter(&fcsm->sm_mutex);
1186 
1187         ASSERT(fcsm->sm_retry_tid == NULL);
1188 
1189         if (fcsm->sm_offline_tid != NULL) {
1190                 timeout_id_t    tid;
1191 
1192                 tid = fcsm->sm_offline_tid;
1193                 fcsm->sm_offline_tid = (timeout_id_t)NULL;
1194                 mutex_exit(&fcsm->sm_mutex);
1195                 (void) untimeout(tid);
1196         } else {
1197                 mutex_exit(&fcsm->sm_mutex);
1198         }
1199 
1200         /* Remove from the fcsm state structure from global linked list */
1201         mutex_enter(&fcsm_global_mutex);
1202         curr = fcsm_port_head;
1203         prev = NULL;
1204         while (curr != fcsm && curr != NULL) {
1205                 prev = curr;
1206                 curr = curr->sm_next;
1207         }
1208         ASSERT(curr != NULL);
1209 
1210         if (prev == NULL) {
1211                 fcsm_port_head = curr->sm_next;
1212         } else {
1213                 prev->sm_next = curr->sm_next;
1214         }
1215         mutex_exit(&fcsm_global_mutex);
1216 
1217         if (fcsm->sm_cmd_cache != NULL) {
1218                 kmem_cache_destroy(fcsm->sm_cmd_cache);
1219         }
1220         cv_destroy(&fcsm->sm_job_cv);
1221         mutex_destroy(&fcsm->sm_mutex);
1222 
1223         /* Free the fcsm state structure */
1224         ddi_soft_state_free(fcsm_state, fcsm->sm_instance);
1225 }
1226 
1227 
1228 /* ARGSUSED */
1229 static void
1230 fcsm_statec_cb(opaque_t ulph, opaque_t port_handle, uint32_t port_state,
1231     uint32_t port_top, fc_portmap_t *devlist, uint32_t dev_cnt,
1232     uint32_t port_sid)
1233 {
1234         fcsm_t          *fcsm;
1235         timeout_id_t    offline_tid, retry_tid;
1236 
1237         mutex_enter(&fcsm_global_mutex);
1238         if (fcsm_detached) {
1239                 mutex_exit(&fcsm_global_mutex);
1240                 return;
1241         }
1242 
1243         fcsm = ddi_get_soft_state(fcsm_state,
1244             fc_ulp_get_port_instance(port_handle));
1245         if (fcsm == NULL) {
1246                 mutex_exit(&fcsm_global_mutex);
1247                 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1248                     "statec_cb: instance 0x%x not found",
1249                     fc_ulp_get_port_instance(port_handle)));
1250                 return;
1251         }
1252         mutex_enter(&fcsm->sm_mutex);
1253         ASSERT(fcsm->sm_instance == fc_ulp_get_port_instance(port_handle));
1254         if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
1255                 mutex_exit(&fcsm->sm_mutex);
1256                 mutex_exit(&fcsm_global_mutex);
1257                 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, fcsm, NULL,
1258                     "statec_cb: port not attached"));
1259                 return;
1260         }
1261 
1262         ASSERT(fcsm->sm_cb_count >= 0);
1263 
1264         fcsm->sm_cb_count++;
1265         mutex_exit(&fcsm->sm_mutex);
1266         mutex_exit(&fcsm_global_mutex);
1267 
1268         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1269             "statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
1270             fcsm_port_state_to_str(FC_PORT_STATE_MASK(port_state)), port_state,
1271             fcsm_topology_to_str(port_top), port_top, dev_cnt));
1272 
1273         fcsm_disp_devlist(fcsm, devlist, dev_cnt);
1274 
1275         mutex_enter(&fcsm->sm_mutex);
1276 
1277         /*
1278          * Reset the Mgmt server Login flag, so that login is performed again.
1279          */
1280         fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
1281 
1282         fcsm->sm_sid = port_sid;
1283         fcsm->sm_port_top = port_top;
1284         fcsm->sm_port_state = port_state;
1285 
1286         switch (port_state) {
1287         case FC_STATE_OFFLINE:
1288         case FC_STATE_RESET:
1289         case FC_STATE_RESET_REQUESTED:
1290                 fcsm->sm_flags |= FCSM_PORT_OFFLINE;
1291                 break;
1292 
1293         case FC_STATE_ONLINE:
1294         case FC_STATE_LOOP:
1295         case FC_STATE_LIP:
1296         case FC_STATE_LIP_LBIT_SET:
1297                 fcsm->sm_flags &= ~FCSM_PORT_OFFLINE;
1298                 fcsm->sm_flags &= ~FCSM_LINK_DOWN;
1299                 break;
1300 
1301         case FC_STATE_NAMESERVICE:
1302         case FC_STATE_DEVICE_CHANGE:
1303         case FC_STATE_TARGET_PORT_RESET:
1304         default:
1305                 /* Do nothing */
1306                 break;
1307         }
1308 
1309         offline_tid = retry_tid = NULL;
1310         if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1311                 /*
1312                  * Port is offline.
1313                  * Suspend cmd processing and start offline timeout thread.
1314                  */
1315                 if (fcsm->sm_offline_tid == NULL) {
1316                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1317                             "statec_cb: schedule offline timeout thread"));
1318                         fcsm->sm_flags |= FCSM_CMD_RETRY_Q_SUSPENDED;
1319                         /* Stop the cmd retry thread */
1320                         retry_tid = fcsm->sm_retry_tid;
1321                         fcsm->sm_retry_tid = (timeout_id_t)NULL;
1322 
1323                         fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
1324                             (caddr_t)fcsm, fcsm_offline_ticks);
1325                 }
1326 
1327         } else {
1328                 /*
1329                  * Port is online.
1330                  * Cancel offline timeout thread and resume command processing.
1331                  */
1332                 if (fcsm->sm_offline_tid) {
1333                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1334                             "statec_cb: cancel offline timeout thread"));
1335                         offline_tid = fcsm->sm_offline_tid;
1336                         fcsm->sm_offline_tid = (timeout_id_t)NULL;
1337                 }
1338 
1339                 fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1340                 /* Start retry thread if needed */
1341                 if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1342                         fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1343                             (caddr_t)fcsm, fcsm_retry_ticks);
1344                 }
1345         }
1346 
1347         mutex_exit(&fcsm->sm_mutex);
1348 
1349         if (offline_tid != NULL) {
1350                 (void) untimeout(offline_tid);
1351         }
1352 
1353         if (retry_tid != NULL) {
1354                 (void) untimeout(retry_tid);
1355         }
1356 
1357         mutex_enter(&fcsm->sm_mutex);
1358         fcsm->sm_cb_count--;
1359         ASSERT(fcsm->sm_cb_count >= 0);
1360         mutex_exit(&fcsm->sm_mutex);
1361 }
1362 
1363 
1364 static void
1365 fcsm_offline_timeout(void *handle)
1366 {
1367         fcsm_t  *fcsm = (fcsm_t *)handle;
1368 
1369         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1370             "offline_timeout"));
1371 
1372         mutex_enter(&fcsm->sm_mutex);
1373         if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
1374                 fcsm->sm_flags |= FCSM_LINK_DOWN;
1375         }
1376         fcsm->sm_offline_tid = (timeout_id_t)NULL;
1377         fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
1378 
1379         /* Start the retry thread if needed */
1380         if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
1381                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1382                     "offline_timeout: reschedule cmd retry thread"));
1383                 ASSERT(fcsm->sm_retry_tid == NULL);
1384                 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
1385                     (caddr_t)fcsm, fcsm_retry_ticks);
1386         }
1387         mutex_exit(&fcsm->sm_mutex);
1388 }
1389 
1390 /* ARGSUSED */
1391 static int
1392 fcsm_els_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1393     uint32_t claimed)
1394 {
1395         return (FC_UNCLAIMED);
1396 }
1397 
1398 
1399 /* ARGSUSED */
1400 static int
1401 fcsm_data_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
1402     uint32_t claimed)
1403 {
1404         return (FC_UNCLAIMED);
1405 }
1406 
1407 
1408 /* ARGSUSED */
1409 static int
1410 fcsm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1411     int *rval_p)
1412 {
1413         int retval = 0;
1414 
1415         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: start"));
1416 
1417         mutex_enter(&fcsm_global_mutex);
1418         if (!(fcsm_flag & FCSM_OPEN)) {
1419                 mutex_exit(&fcsm_global_mutex);
1420                 return (ENXIO);
1421         }
1422         mutex_exit(&fcsm_global_mutex);
1423 
1424         /* Allow only root to talk */
1425         if (drv_priv(credp)) {
1426                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1427                     "ioctl: end (disallowing underprivileged user)"));
1428                 return (EPERM);
1429         }
1430 
1431         switch (cmd) {
1432 
1433         case FCSMIO_CMD: {
1434                 fcio_t  fcio;
1435                 int     status;
1436 #ifdef  _MULTI_DATAMODEL
1437                 switch (ddi_model_convert_from(mode & FMODELS)) {
1438                 case DDI_MODEL_ILP32: {
1439                         struct fcio32 fcio32;
1440 
1441                         if (status = ddi_copyin((void *)arg, (void *)&fcio32,
1442                             sizeof (struct fcio32), mode)) {
1443                                 retval = EFAULT;
1444                                 break;
1445                         }
1446                         fcio.fcio_xfer = fcio32.fcio_xfer;
1447                         fcio.fcio_cmd = fcio32.fcio_cmd;
1448                         fcio.fcio_flags = fcio32.fcio_flags;
1449                         fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
1450                         fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
1451                         fcio.fcio_ibuf = (caddr_t)(long)fcio32.fcio_ibuf;
1452                         fcio.fcio_olen = (size_t)fcio32.fcio_olen;
1453                         fcio.fcio_obuf = (caddr_t)(long)fcio32.fcio_obuf;
1454                         fcio.fcio_alen = (size_t)fcio32.fcio_alen;
1455                         fcio.fcio_abuf = (caddr_t)(long)fcio32.fcio_abuf;
1456                         fcio.fcio_errno = fcio32.fcio_errno;
1457                         break;
1458                 }
1459 
1460                 case DDI_MODEL_NONE:
1461                         if (status = ddi_copyin((void *)arg, (void *)&fcio,
1462                             sizeof (fcio_t), mode)) {
1463                                 retval = EFAULT;
1464                         }
1465                         break;
1466                 }
1467 #else   /* _MULTI_DATAMODEL */
1468                 if (status = ddi_copyin((void *)arg, (void *)&fcio,
1469                     sizeof (fcio_t), mode)) {
1470                         retval = EFAULT;
1471                         break;
1472                 }
1473 #endif  /* _MULTI_DATAMODEL */
1474                 if (!status) {
1475                         retval = fcsm_fciocmd(arg, mode, credp, &fcio);
1476                 }
1477                 break;
1478         }
1479 
1480         default:
1481                 retval = ENOTTY;
1482                 break;
1483         }
1484 
1485         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: end"));
1486         return (retval);
1487 }
1488 
1489 /* ARGSUSED */
1490 static int
1491 fcsm_port_ioctl(opaque_t ulph, opaque_t port_handle, dev_t dev, int cmd,
1492     intptr_t arg, int mode, cred_t *credp, int *rval, uint32_t claimed)
1493 {
1494         return (FC_UNCLAIMED);
1495 }
1496 
1497 
1498 /* ARGSUSED */
1499 static int
1500 fcsm_fciocmd(intptr_t arg, int mode, cred_t *credp, fcio_t *fcio)
1501 {
1502         int  retval = 0;
1503 
1504         switch (fcio->fcio_cmd) {
1505         case  FCSMIO_CT_CMD: {
1506                 fcsm_t          *fcsm;
1507                 caddr_t         user_ibuf, user_obuf;
1508                 caddr_t         req_iu, rsp_iu, abuf;
1509                 int             status, instance, count;
1510 
1511                 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1512                     (fcio->fcio_ilen == 0) || (fcio->fcio_ibuf == 0) ||
1513                     (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0) ||
1514                     (fcio->fcio_alen == 0) || (fcio->fcio_abuf == 0) ||
1515                     (fcio->fcio_flags != 0) || (fcio->fcio_cmd_flags != 0) ||
1516                     (fcio->fcio_ilen > FCSM_MAX_CT_SIZE) ||
1517                     (fcio->fcio_olen > FCSM_MAX_CT_SIZE) ||
1518                     (fcio->fcio_alen > MAXPATHLEN)) {
1519                         retval = EINVAL;
1520                         break;
1521                 }
1522 
1523                 /*
1524                  * Get the destination port for which this ioctl
1525                  * is targeted. The abuf will have the fp_minor
1526                  * number.
1527                  */
1528                 abuf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
1529                 ASSERT(abuf != NULL);
1530                 if (ddi_copyin(fcio->fcio_abuf, abuf, fcio->fcio_alen, mode)) {
1531                         retval = EFAULT;
1532                         kmem_free(abuf, fcio->fcio_alen);
1533                         break;
1534                 }
1535 
1536                 instance = *((int *)abuf);
1537                 kmem_free(abuf, fcio->fcio_alen);
1538 
1539                 if (instance < 0) {
1540                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1541                             "fciocmd: instance 0x%x, invalid instance",
1542                             instance));
1543                         retval = ENXIO;
1544                         break;
1545                 }
1546 
1547                 /*
1548                  * We confirmed that path corresponds to our port driver
1549                  * and a valid instance.
1550                  * If this port instance is not yet attached, then wait
1551                  * for a finite time for attach to complete
1552                  */
1553                 fcsm = ddi_get_soft_state(fcsm_state, instance);
1554                 count = 0;
1555                 while (count++ <= 30) {
1556                         if (fcsm != NULL) {
1557                                 mutex_enter(&fcsm->sm_mutex);
1558                                 if (fcsm->sm_flags & FCSM_ATTACHED) {
1559                                         mutex_exit(&fcsm->sm_mutex);
1560                                         break;
1561                                 }
1562                                 mutex_exit(&fcsm->sm_mutex);
1563                         }
1564                         if (count == 1) {
1565                                 FCSM_DEBUG(SMDL_TRACE,
1566                                     (CE_WARN, SM_LOG, NULL, NULL,
1567                                     "fciocmd: instance 0x%x, "
1568                                     "wait for port attach", instance));
1569                         }
1570                         delay(drv_usectohz(1000000));
1571                         fcsm = ddi_get_soft_state(fcsm_state, instance);
1572                 }
1573                 if (count > 30) {
1574                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
1575                             "fciocmd: instance 0x%x, port not attached",
1576                             instance));
1577                         retval = ENXIO;
1578                         break;
1579                 }
1580 
1581                 req_iu = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
1582                 rsp_iu = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1583                 ASSERT((req_iu != NULL) && (rsp_iu != NULL));
1584 
1585                 if (ddi_copyin(fcio->fcio_ibuf, req_iu,
1586                     fcio->fcio_ilen, mode)) {
1587                         retval = EFAULT;
1588                         kmem_free(req_iu, fcio->fcio_ilen);
1589                         kmem_free(rsp_iu, fcio->fcio_olen);
1590                         break;
1591                 }
1592 
1593                 user_ibuf = fcio->fcio_ibuf;
1594                 user_obuf = fcio->fcio_obuf;
1595                 fcio->fcio_ibuf = req_iu;
1596                 fcio->fcio_obuf = rsp_iu;
1597 
1598                 status = fcsm_ct_passthru(fcsm->sm_instance, fcio, KM_SLEEP,
1599                     FCSM_JOBFLAG_SYNC, NULL);
1600                 if (status != FC_SUCCESS) {
1601                         retval = EIO;
1602                 }
1603 
1604                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1605                     "fciocmd: cmd 0x%x completion status 0x%x",
1606                     fcio->fcio_cmd, status));
1607                 fcio->fcio_errno = status;
1608                 fcio->fcio_ibuf = user_ibuf;
1609                 fcio->fcio_obuf = user_obuf;
1610 
1611                 if (ddi_copyout(rsp_iu, fcio->fcio_obuf,
1612                     fcio->fcio_olen, mode)) {
1613                         retval = EFAULT;
1614                         kmem_free(req_iu, fcio->fcio_ilen);
1615                         kmem_free(rsp_iu, fcio->fcio_olen);
1616                         break;
1617                 }
1618 
1619                 kmem_free(req_iu, fcio->fcio_ilen);
1620                 kmem_free(rsp_iu, fcio->fcio_olen);
1621 
1622                 if (fcsm_fcio_copyout(fcio, arg, mode)) {
1623                         retval = EFAULT;
1624                 }
1625                 break;
1626         }
1627 
1628         case  FCSMIO_ADAPTER_LIST: {
1629                 fc_hba_list_t   *list;
1630                 int                     count;
1631 
1632                 if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
1633                     (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
1634                         retval = EINVAL;
1635                         break;
1636                 }
1637 
1638                 list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
1639 
1640                 if (ddi_copyin(fcio->fcio_obuf, list, fcio->fcio_olen, mode)) {
1641                         retval = EFAULT;
1642                         break;
1643                 }
1644                 list->version = FC_HBA_LIST_VERSION;
1645 
1646                 if (fcio->fcio_olen < MAXPATHLEN * list->numAdapters) {
1647                         retval = EFAULT;
1648                         break;
1649                 }
1650 
1651                 count = fc_ulp_get_adapter_paths((char *)list->hbaPaths,
1652                     list->numAdapters);
1653                 if (count < 0) {
1654                         /* Did something go wrong? */
1655                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1656                             "Error fetching adapter list."));
1657                         retval = ENXIO;
1658                         kmem_free(list, fcio->fcio_olen);
1659                         break;
1660                 }
1661                 /* Sucess (or short buffer) */
1662                 list->numAdapters = count;
1663                 if (ddi_copyout(list, fcio->fcio_obuf,
1664                     fcio->fcio_olen, mode)) {
1665                         retval = EFAULT;
1666                 }
1667                 kmem_free(list, fcio->fcio_olen);
1668                 break;
1669         }
1670 
1671         default:
1672                 FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
1673                     "fciocmd: unknown cmd <0x%x>", fcio->fcio_cmd));
1674                 retval = ENOTTY;
1675                 break;
1676         }
1677 
1678         return (retval);
1679 }
1680 
1681 static int
1682 fcsm_fcio_copyout(fcio_t *fcio, intptr_t arg, int mode)
1683 {
1684         int status;
1685 
1686 #ifdef  _MULTI_DATAMODEL
1687         switch (ddi_model_convert_from(mode & FMODELS)) {
1688         case DDI_MODEL_ILP32: {
1689                 struct fcio32 fcio32;
1690 
1691                 fcio32.fcio_xfer = fcio->fcio_xfer;
1692                 fcio32.fcio_cmd = fcio->fcio_cmd;
1693                 fcio32.fcio_flags = fcio->fcio_flags;
1694                 fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
1695                 fcio32.fcio_ilen = fcio->fcio_ilen;
1696                 fcio32.fcio_ibuf = (caddr32_t)(long)fcio->fcio_ibuf;
1697                 fcio32.fcio_olen = fcio->fcio_olen;
1698                 fcio32.fcio_obuf = (caddr32_t)(long)fcio->fcio_obuf;
1699                 fcio32.fcio_alen = fcio->fcio_alen;
1700                 fcio32.fcio_abuf = (caddr32_t)(long)fcio->fcio_abuf;
1701                 fcio32.fcio_errno = fcio->fcio_errno;
1702 
1703                 status = ddi_copyout((void *)&fcio32, (void *)arg,
1704                     sizeof (struct fcio32), mode);
1705                 break;
1706         }
1707         case DDI_MODEL_NONE:
1708                 status = ddi_copyout((void *)fcio, (void *)arg,
1709                     sizeof (fcio_t), mode);
1710                 break;
1711         }
1712 #else   /* _MULTI_DATAMODEL */
1713         status = ddi_copyout((void *)fcio, (void *)arg, sizeof (fcio_t), mode);
1714 #endif  /* _MULTI_DATAMODEL */
1715 
1716         return (status);
1717 }
1718 
1719 
1720 /* ARGSUSED */
1721 static int
1722 fcsm_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1723 {
1724         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "open"));
1725 
1726         if (otyp != OTYP_CHR) {
1727                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1728                     "fcsm_open: failed. open type 0x%x for minor 0x%x is not "
1729                     "OTYP_CHR", otyp, getminor(*devp)));
1730                 return (EINVAL);
1731         }
1732 
1733         /*
1734          * Allow anybody to open (both root and non-root users).
1735          * Previlege level checks are made on the per ioctl basis.
1736          */
1737         mutex_enter(&fcsm_global_mutex);
1738         if (flags & FEXCL) {
1739                 if (fcsm_flag & FCSM_OPEN) {
1740                         mutex_exit(&fcsm_global_mutex);
1741                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1742                             "fcsm_open: exclusive open of 0x%x failed",
1743                             getminor(*devp)));
1744                         return (EBUSY);
1745                 } else {
1746                         ASSERT(fcsm_flag == FCSM_IDLE);
1747                         fcsm_flag |= FCSM_EXCL;
1748                 }
1749         } else {
1750                 if (fcsm_flag & FCSM_EXCL) {
1751                         mutex_exit(&fcsm_global_mutex);
1752                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1753                             "fcsm_open: failed. Device minor 0x%x is in "
1754                             "exclusive open mode", getminor(*devp)));
1755                         return (EBUSY);
1756                 }
1757 
1758         }
1759         fcsm_flag |= FCSM_OPEN;
1760         mutex_exit(&fcsm_global_mutex);
1761         return (0);
1762 }
1763 
1764 
1765 /* ARGSUSED */
1766 static int
1767 fcsm_close(dev_t dev, int flag, int otyp, cred_t *credp)
1768 {
1769         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "close"));
1770 
1771         if (otyp != OTYP_CHR) {
1772                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1773                     "fcsm_close: failed. close type 0x%x for minor 0x%x is not "
1774                     "OTYP_CHR", otyp, getminor(dev)));
1775                 return (EINVAL);
1776         }
1777 
1778         mutex_enter(&fcsm_global_mutex);
1779         if ((fcsm_flag & FCSM_OPEN) == 0) {
1780                 mutex_exit(&fcsm_global_mutex);
1781                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
1782                     "fcsm_close: failed. minor 0x%x is already closed",
1783                     getminor(dev)));
1784                 return (ENODEV);
1785         }
1786         fcsm_flag = FCSM_IDLE;
1787         mutex_exit(&fcsm_global_mutex);
1788         return (0);
1789 }
1790 
1791 
1792 /* ARGSUSED */
1793 static void
1794 fcsm_disp_devlist(fcsm_t *fcsm, fc_portmap_t *devlist, uint32_t dev_cnt)
1795 {
1796         fc_portmap_t    *map;
1797         uint32_t        i;
1798 
1799         if (dev_cnt == 0) {
1800                 return;
1801         }
1802 
1803         ASSERT(devlist != NULL);
1804         for (i = 0; i < dev_cnt; i++) {
1805                 map = &devlist[i];
1806                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
1807                     "list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
1808                     "state (0x%x) "
1809                     "type <%s>(0x%x) "
1810                     "flags (0x%x)",
1811                     i, map->map_did.port_id,
1812                     map->map_pwwn.raw_wwn[0], map->map_pwwn.raw_wwn[1],
1813                     map->map_pwwn.raw_wwn[2], map->map_pwwn.raw_wwn[3],
1814                     map->map_pwwn.raw_wwn[4], map->map_pwwn.raw_wwn[5],
1815                     map->map_pwwn.raw_wwn[6], map->map_pwwn.raw_wwn[7],
1816                     map->map_state,
1817                     fcsm_dev_type_to_str(map->map_type), map->map_type,
1818                     map->map_flags));
1819         }
1820 }
1821 
1822 /* ARGSUSED */
1823 static void
1824 fcsm_display(int level, int flags, fcsm_t *fcsm, fc_packet_t *pkt,
1825     const char *fmt, ...)
1826 {
1827         caddr_t buf;
1828         va_list ap;
1829 
1830         buf = kmem_zalloc(256, KM_NOSLEEP);
1831         if (buf == NULL) {
1832                 return;
1833         }
1834 
1835         if (fcsm) {
1836                 (void) sprintf(buf + strlen(buf), "fcsm(%d): ",
1837                     ddi_get_instance(fcsm->sm_port_info.port_dip));
1838         } else {
1839                 (void) sprintf(buf, "fcsm: ");
1840         }
1841 
1842         va_start(ap, fmt);
1843         (void) vsprintf(buf + strlen(buf), fmt, ap);
1844         va_end(ap);
1845 
1846         if (pkt) {
1847                 caddr_t state, reason, action, expln;
1848 
1849                 (void) fc_ulp_pkt_error(pkt, &state, &reason, &action, &expln);
1850 
1851                 (void) sprintf(buf + strlen(buf),
1852                     " state: %s(0x%x); reason: %s(0x%x)",
1853                     state, pkt->pkt_state, reason, pkt->pkt_reason);
1854         }
1855 
1856         switch (flags) {
1857         case SM_LOG:
1858                 cmn_err(level, "!%s", buf);
1859                 break;
1860 
1861         case SM_CONSOLE:
1862                 cmn_err(level, "^%s", buf);
1863                 break;
1864 
1865         default:
1866                 cmn_err(level, "%s", buf);
1867                 break;
1868         }
1869 
1870         kmem_free(buf, 256);
1871 }
1872 
1873 
1874 /*
1875  * Convert FC packet state to FC errno
1876  */
1877 int
1878 fcsm_pkt_state_to_rval(uchar_t state, uint32_t reason)
1879 {
1880         int count;
1881 
1882         if (state == FC_PKT_LOCAL_RJT && (reason == FC_REASON_NO_CONNECTION ||
1883             reason == FC_REASON_LOGIN_REQUIRED)) {
1884                 return (FC_LOGINREQ);
1885         } else if (state == FC_PKT_PORT_OFFLINE &&
1886             reason == FC_REASON_LOGIN_REQUIRED) {
1887                 return (FC_LOGINREQ);
1888         }
1889 
1890         for (count = 0; count < sizeof (fcsm_xlat_pkt_state) /
1891             sizeof (fcsm_xlat_pkt_state[0]); count++) {
1892                 if (fcsm_xlat_pkt_state[count].xlat_state == state) {
1893                         return (fcsm_xlat_pkt_state[count].xlat_rval);
1894                 }
1895         }
1896 
1897         return (FC_FAILURE);
1898 }
1899 
1900 
1901 /*
1902  * Convert port state state to descriptive string
1903  */
1904 caddr_t
1905 fcsm_port_state_to_str(uint32_t port_state)
1906 {
1907         int count;
1908 
1909         for (count = 0; count < sizeof (fcsm_xlat_port_state) /
1910             sizeof (fcsm_xlat_port_state[0]); count++) {
1911                 if (fcsm_xlat_port_state[count].xlat_pstate == port_state) {
1912                         return (fcsm_xlat_port_state[count].xlat_state_str);
1913                 }
1914         }
1915 
1916         return (NULL);
1917 }
1918 
1919 
1920 /*
1921  * Convert port topology state to descriptive string
1922  */
1923 caddr_t
1924 fcsm_topology_to_str(uint32_t topology)
1925 {
1926         int count;
1927 
1928         for (count = 0; count < sizeof (fcsm_xlat_topology) /
1929             sizeof (fcsm_xlat_topology[0]); count++) {
1930                 if (fcsm_xlat_topology[count].xlat_top == topology) {
1931                         return (fcsm_xlat_topology[count].xlat_top_str);
1932                 }
1933         }
1934 
1935         return (NULL);
1936 }
1937 
1938 
1939 /*
1940  * Convert port topology state to descriptive string
1941  */
1942 static caddr_t
1943 fcsm_dev_type_to_str(uint32_t type)
1944 {
1945         int count;
1946 
1947         for (count = 0; count < sizeof (fcsm_xlat_dev_type) /
1948             sizeof (fcsm_xlat_dev_type[0]); count++) {
1949                 if (fcsm_xlat_dev_type[count].xlat_type == type) {
1950                         return (fcsm_xlat_dev_type[count].xlat_str);
1951                 }
1952         }
1953 
1954         return (NULL);
1955 }
1956 
1957 static int
1958 fcsm_cmd_cache_constructor(void *buf, void *cdarg, int kmflags)
1959 {
1960         fcsm_cmd_t              *cmd = (fcsm_cmd_t *)buf;
1961         fcsm_t                  *fcsm = (fcsm_t *)cdarg;
1962         int                     (*callback)(caddr_t);
1963         fc_packet_t             *pkt;
1964         fc_ulp_port_info_t      *pinfo;
1965 
1966         ASSERT(fcsm != NULL && buf != NULL);
1967         callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
1968 
1969         cmd->cmd_fp_pkt              = &cmd->cmd_fc_packet;
1970         cmd->cmd_job         = NULL;
1971         cmd->cmd_fcsm                = fcsm;
1972         cmd->cmd_dma_flags   = 0;
1973 
1974         pkt = &cmd->cmd_fc_packet;
1975 
1976         pkt->pkt_ulp_rscn_infop = NULL;
1977         pkt->pkt_fca_private = (opaque_t)((caddr_t)cmd + sizeof (fcsm_cmd_t));
1978         pkt->pkt_ulp_private = (opaque_t)cmd;
1979 
1980         if (!(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
1981                 pinfo = &fcsm->sm_port_info;
1982                 if (ddi_dma_alloc_handle(pinfo->port_dip,
1983                     pinfo->port_cmd_dma_attr,
1984                     callback, NULL, &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
1985                         return (1);
1986                 }
1987 
1988                 if (ddi_dma_alloc_handle(pinfo->port_dip,
1989                     pinfo->port_resp_dma_attr,
1990                     callback, NULL, &pkt->pkt_resp_dma) != DDI_SUCCESS) {
1991                         ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1992                         return (1);
1993                 }
1994         } else {
1995                 pkt->pkt_cmd_dma  = NULL;
1996                 pkt->pkt_cmd   = NULL;
1997                 pkt->pkt_resp_dma = NULL;
1998                 pkt->pkt_resp          = NULL;
1999         }
2000 
2001         pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
2002         pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
2003             pkt->pkt_data_cookie_cnt = 0;
2004         pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
2005             pkt->pkt_data_cookie = NULL;
2006 
2007         return (0);
2008 }
2009 
2010 
2011 /* ARGSUSED */
2012 static void
2013 fcsm_cmd_cache_destructor(void *buf, void *cdarg)
2014 {
2015         fcsm_cmd_t      *cmd = (fcsm_cmd_t *)buf;
2016         fcsm_t          *fcsm = (fcsm_t *)cdarg;
2017         fc_packet_t     *pkt;
2018 
2019         ASSERT(fcsm == cmd->cmd_fcsm);
2020 
2021         pkt = cmd->cmd_fp_pkt;
2022 
2023         if (pkt->pkt_cmd_dma != NULL) {
2024                 ddi_dma_free_handle(&pkt->pkt_cmd_dma);
2025         }
2026 
2027         if (pkt->pkt_resp_dma != NULL) {
2028                 ddi_dma_free_handle(&pkt->pkt_resp_dma);
2029         }
2030 }
2031 
2032 
2033 static fcsm_cmd_t *
2034 fcsm_alloc_cmd(fcsm_t *fcsm, uint32_t cmd_len, uint32_t resp_len, int sleep)
2035 {
2036         fcsm_cmd_t      *cmd;
2037         fc_packet_t     *pkt;
2038         int             rval;
2039         ulong_t         real_len;
2040         int             (*callback)(caddr_t);
2041         ddi_dma_cookie_t        pkt_cookie;
2042         ddi_dma_cookie_t        *cp;
2043         uint32_t                cnt;
2044         fc_ulp_port_info_t      *pinfo;
2045 
2046         ASSERT(fcsm != NULL);
2047         pinfo = &fcsm->sm_port_info;
2048 
2049         callback = (sleep == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
2050 
2051         cmd = (fcsm_cmd_t *)kmem_cache_alloc(fcsm->sm_cmd_cache, sleep);
2052         if (cmd == NULL) {
2053                 FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, fcsm, NULL,
2054                     "alloc_cmd: kmem_cache_alloc failed"));
2055                 return (NULL);
2056         }
2057 
2058         cmd->cmd_retry_count = 0;
2059         cmd->cmd_max_retries = 0;
2060         cmd->cmd_retry_interval      = 0;
2061         cmd->cmd_transport   = NULL;
2062 
2063         ASSERT(cmd->cmd_dma_flags == 0);
2064         ASSERT(cmd->cmd_fp_pkt == &cmd->cmd_fc_packet);
2065         pkt = cmd->cmd_fp_pkt;
2066 
2067         /* Zero out the important fc_packet fields */
2068         pkt->pkt_pd          = NULL;
2069         pkt->pkt_datalen     = 0;
2070         pkt->pkt_data                = NULL;
2071         pkt->pkt_state               = 0;
2072         pkt->pkt_action              = 0;
2073         pkt->pkt_reason              = 0;
2074         pkt->pkt_expln               = 0;
2075 
2076         /*
2077          * Now that pkt_pd is initialized, we can call fc_ulp_init_packet
2078          */
2079 
2080         if (fc_ulp_init_packet((opaque_t)pinfo->port_handle, pkt, sleep)
2081             != FC_SUCCESS) {
2082                 kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2083                 return (NULL);
2084         }
2085 
2086         if ((cmd_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2087                 ASSERT(pkt->pkt_cmd_dma != NULL);
2088 
2089                 rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
2090                     fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2091                     callback, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
2092                     &pkt->pkt_cmd_acc);
2093 
2094                 if (rval != DDI_SUCCESS) {
2095                         (void) fc_ulp_uninit_packet(
2096                             (opaque_t)pinfo->port_handle, pkt);
2097                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2098                         fcsm_free_cmd_dma(cmd);
2099                         return (NULL);
2100                 }
2101 
2102                 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_MEM;
2103 
2104                 if (real_len < cmd_len) {
2105                         (void) fc_ulp_uninit_packet(
2106                             (opaque_t)pinfo->port_handle, pkt);
2107                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2108                         fcsm_free_cmd_dma(cmd);
2109                         return (NULL);
2110                 }
2111 
2112                 rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
2113                     pkt->pkt_cmd, real_len, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
2114                     callback, NULL, &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
2115 
2116                 if (rval != DDI_DMA_MAPPED) {
2117                         (void) fc_ulp_uninit_packet(
2118                             (opaque_t)pinfo->port_handle, pkt);
2119                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2120                         fcsm_free_cmd_dma(cmd);
2121                         return (NULL);
2122                 }
2123 
2124                 cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_BIND;
2125 
2126                 if (pkt->pkt_cmd_cookie_cnt >
2127                     pinfo->port_cmd_dma_attr->dma_attr_sgllen) {
2128                         (void) fc_ulp_uninit_packet(
2129                             (opaque_t)pinfo->port_handle, pkt);
2130                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2131                         fcsm_free_cmd_dma(cmd);
2132                         return (NULL);
2133                 }
2134 
2135                 ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2136 
2137                 cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2138                     pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
2139                     KM_NOSLEEP);
2140 
2141                 if (cp == NULL) {
2142                         (void) fc_ulp_uninit_packet(
2143                             (opaque_t)pinfo->port_handle, pkt);
2144                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2145                         fcsm_free_cmd_dma(cmd);
2146                         return (NULL);
2147                 }
2148 
2149                 *cp = pkt_cookie;
2150                 cp++;
2151                 for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
2152                         ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
2153                         *cp = pkt_cookie;
2154                 }
2155         } else if (cmd_len != 0) {
2156                 pkt->pkt_cmd = kmem_zalloc(cmd_len, KM_SLEEP);
2157         }
2158 
2159         if ((resp_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
2160                 ASSERT(pkt->pkt_resp_dma != NULL);
2161 
2162                 rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
2163                     fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
2164                     callback, NULL, (caddr_t *)&pkt->pkt_resp, &real_len,
2165                     &pkt->pkt_resp_acc);
2166 
2167                 if (rval != DDI_SUCCESS) {
2168                         (void) fc_ulp_uninit_packet(
2169                             (opaque_t)pinfo->port_handle, pkt);
2170                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2171                         fcsm_free_cmd_dma(cmd);
2172                         return (NULL);
2173                 }
2174 
2175                 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_MEM;
2176 
2177                 if (real_len < resp_len) {
2178                         (void) fc_ulp_uninit_packet(
2179                             (opaque_t)pinfo->port_handle, pkt);
2180                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2181                         fcsm_free_cmd_dma(cmd);
2182                         return (NULL);
2183                 }
2184 
2185                 rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
2186                     pkt->pkt_resp, real_len, DDI_DMA_READ | DDI_DMA_CONSISTENT,
2187                     callback, NULL, &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
2188 
2189                 if (rval != DDI_DMA_MAPPED) {
2190                         (void) fc_ulp_uninit_packet(
2191                             (opaque_t)pinfo->port_handle, pkt);
2192                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2193                         fcsm_free_cmd_dma(cmd);
2194                         return (NULL);
2195                 }
2196 
2197                 cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_BIND;
2198 
2199                 if (pkt->pkt_resp_cookie_cnt >
2200                     pinfo->port_resp_dma_attr->dma_attr_sgllen) {
2201                         (void) fc_ulp_uninit_packet(
2202                             (opaque_t)pinfo->port_handle, pkt);
2203                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2204                         fcsm_free_cmd_dma(cmd);
2205                         return (NULL);
2206                 }
2207 
2208                 ASSERT(pkt->pkt_resp_cookie_cnt != 0);
2209 
2210                 cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2211                     pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
2212                     KM_NOSLEEP);
2213 
2214                 if (cp == NULL) {
2215                         (void) fc_ulp_uninit_packet(
2216                             (opaque_t)pinfo->port_handle, pkt);
2217                         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2218                         fcsm_free_cmd_dma(cmd);
2219                         return (NULL);
2220                 }
2221 
2222                 *cp = pkt_cookie;
2223                 cp++;
2224                 for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
2225                         ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
2226                         *cp = pkt_cookie;
2227                 }
2228         } else if (resp_len != 0) {
2229                 pkt->pkt_resp = kmem_zalloc(resp_len, KM_SLEEP);
2230         }
2231 
2232         pkt->pkt_cmdlen = cmd_len;
2233         pkt->pkt_rsplen = resp_len;
2234 
2235         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2236             "alloc_cmd: cmd 0x%p", (void *)cmd));
2237         return (cmd);
2238 }
2239 
2240 static void
2241 fcsm_free_cmd(fcsm_cmd_t *cmd)
2242 {
2243         fcsm_t          *fcsm;
2244 
2245         fcsm = cmd->cmd_fcsm;
2246         ASSERT(fcsm != NULL);
2247 
2248         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2249             "free_cmd: cmd 0x%p", (void *)cmd));
2250 
2251         fcsm_free_cmd_dma(cmd);
2252 
2253         (void) fc_ulp_uninit_packet((opaque_t)fcsm->sm_port_info.port_handle,
2254             cmd->cmd_fp_pkt);
2255         kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
2256 }
2257 
2258 static void
2259 fcsm_free_cmd_dma(fcsm_cmd_t *cmd)
2260 {
2261         fc_packet_t     *pkt;
2262 
2263         pkt = cmd->cmd_fp_pkt;
2264         ASSERT(pkt != NULL);
2265 
2266         if (cmd->cmd_fcsm->sm_flags & FCSM_USING_NODMA_FCA) {
2267                 if (pkt->pkt_cmd) {
2268                         kmem_free(pkt->pkt_cmd, pkt->pkt_cmdlen);
2269                         pkt->pkt_cmd = NULL;
2270                 }
2271 
2272                 if (pkt->pkt_resp) {
2273                         kmem_free(pkt->pkt_resp, pkt->pkt_rsplen);
2274                         pkt->pkt_resp = NULL;
2275                 }
2276         }
2277 
2278         pkt->pkt_cmdlen = 0;
2279         pkt->pkt_rsplen = 0;
2280         pkt->pkt_tran_type = 0;
2281         pkt->pkt_tran_flags = 0;
2282 
2283         if (pkt->pkt_cmd_cookie != NULL) {
2284                 kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
2285                     sizeof (ddi_dma_cookie_t));
2286                 pkt->pkt_cmd_cookie = NULL;
2287         }
2288 
2289         if (pkt->pkt_resp_cookie != NULL) {
2290                 kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
2291                     sizeof (ddi_dma_cookie_t));
2292                 pkt->pkt_resp_cookie = NULL;
2293         }
2294 
2295         if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_BIND) {
2296                 (void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
2297         }
2298 
2299         if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_MEM) {
2300                 if (pkt->pkt_cmd_acc) {
2301                         ddi_dma_mem_free(&pkt->pkt_cmd_acc);
2302                 }
2303         }
2304 
2305         if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_BIND) {
2306                 (void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
2307         }
2308 
2309         if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_MEM) {
2310                 if (pkt->pkt_resp_acc) {
2311                         ddi_dma_mem_free(&pkt->pkt_resp_acc);
2312                 }
2313         }
2314 
2315         cmd->cmd_dma_flags = 0;
2316 }
2317 
2318 /* ARGSUSED */
2319 static int
2320 fcsm_job_cache_constructor(void *buf, void *cdarg, int kmflag)
2321 {
2322         fcsm_job_t *job = (fcsm_job_t *)buf;
2323 
2324         mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
2325         sema_init(&job->job_sema, 0, NULL, SEMA_DEFAULT, NULL);
2326 
2327         return (0);
2328 }
2329 
2330 /* ARGSUSED */
2331 static void
2332 fcsm_job_cache_destructor(void *buf, void *cdarg)
2333 {
2334         fcsm_job_t *job = (fcsm_job_t *)buf;
2335 
2336         sema_destroy(&job->job_sema);
2337         mutex_destroy(&job->job_mutex);
2338 }
2339 
2340 
2341 static fcsm_job_t *
2342 fcsm_alloc_job(int sleep)
2343 {
2344         fcsm_job_t      *job;
2345 
2346         job = (fcsm_job_t *)kmem_cache_alloc(fcsm_job_cache, sleep);
2347         if (job != NULL) {
2348                 job->job_code                = FCSM_JOB_NONE;
2349                 job->job_flags               = 0;
2350                 job->job_port_instance       = -1;
2351                 job->job_result              = -1;
2352                 job->job_arg         = (opaque_t)0;
2353                 job->job_caller_priv = (opaque_t)0;
2354                 job->job_comp                = NULL;
2355                 job->job_comp_arg    = (opaque_t)0;
2356                 job->job_priv                = (void *)0;
2357                 job->job_priv_flags  = 0;
2358                 job->job_next                = 0;
2359         }
2360 
2361         return (job);
2362 }
2363 
2364 static void
2365 fcsm_dealloc_job(fcsm_job_t *job)
2366 {
2367         kmem_cache_free(fcsm_job_cache, (void *)job);
2368 }
2369 
2370 
2371 static void
2372 fcsm_init_job(fcsm_job_t *job, int instance, uint32_t command, uint32_t flags,
2373     opaque_t arg, opaque_t caller_priv,
2374     void (*comp)(opaque_t, fcsm_job_t *, int), opaque_t comp_arg)
2375 {
2376         ASSERT(job != NULL);
2377         job->job_port_instance       = instance;
2378         job->job_code                = command;
2379         job->job_flags               = flags;
2380         job->job_arg         = arg;
2381         job->job_caller_priv = caller_priv;
2382         job->job_comp                = comp;
2383         job->job_comp_arg    = comp_arg;
2384         job->job_retry_count = 0;
2385 }
2386 
2387 static int
2388 fcsm_process_job(fcsm_job_t *job, int priority_flag)
2389 {
2390         fcsm_t  *fcsm;
2391         int     sync;
2392 
2393         ASSERT(job != NULL);
2394         ASSERT(!MUTEX_HELD(&job->job_mutex));
2395 
2396         fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2397 
2398         if (fcsm == NULL) {
2399                 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
2400                     "process_job: port instance 0x%x not found",
2401                     job->job_port_instance));
2402                 return (FC_BADDEV);
2403         }
2404 
2405         mutex_enter(&job->job_mutex);
2406         /* Both SYNC and ASYNC flags should not be set */
2407         ASSERT(((job->job_flags & (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) ==
2408             FCSM_JOBFLAG_SYNC) || ((job->job_flags &
2409             (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) == FCSM_JOBFLAG_ASYNC));
2410         /*
2411          * Check if job is a synchronous job. We might not be able to
2412          * check it reliably after enque_job(), if job is an ASYNC job.
2413          */
2414         sync = job->job_flags & FCSM_JOBFLAG_SYNC;
2415         mutex_exit(&job->job_mutex);
2416 
2417         /* Queue the job for processing by job thread */
2418         fcsm_enque_job(fcsm, job, priority_flag);
2419 
2420         /* Wait for job completion, if it is a synchronous job */
2421         if (sync) {
2422                 /*
2423                  * This is a Synchronous Job. So job structure is available.
2424                  * Caller is responsible for freeing it.
2425                  */
2426                 FCSM_DEBUG(SMDL_ERR, (CE_CONT, SM_LOG, fcsm, NULL,
2427                     "process_job: Waiting for sync job <%p> completion",
2428                     (void *)job));
2429                 sema_p(&job->job_sema);
2430         }
2431 
2432         return (FC_SUCCESS);
2433 }
2434 
2435 static void
2436 fcsm_enque_job(fcsm_t *fcsm, fcsm_job_t *job, int priority_flag)
2437 {
2438         ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
2439 
2440         mutex_enter(&fcsm->sm_mutex);
2441         /* Queue the job at the head or tail depending on the job priority */
2442         if (priority_flag) {
2443                 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2444                     "enque_job: job 0x%p is high priority", job));
2445                 /* Queue at the head */
2446                 if (fcsm->sm_job_tail == NULL) {
2447                         ASSERT(fcsm->sm_job_head == NULL);
2448                         fcsm->sm_job_head = fcsm->sm_job_tail = job;
2449                 } else {
2450                         ASSERT(fcsm->sm_job_head != NULL);
2451                         job->job_next = fcsm->sm_job_head;
2452                         fcsm->sm_job_head = job;
2453                 }
2454         } else {
2455                 FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
2456                     "enque_job: job 0x%p is normal", job));
2457                 /* Queue at the tail */
2458                 if (fcsm->sm_job_tail == NULL) {
2459                         ASSERT(fcsm->sm_job_head == NULL);
2460                         fcsm->sm_job_head = fcsm->sm_job_tail = job;
2461                 } else {
2462                         ASSERT(fcsm->sm_job_head != NULL);
2463                         fcsm->sm_job_tail->job_next = job;
2464                         fcsm->sm_job_tail = job;
2465                 }
2466                 job->job_next = NULL;
2467         }
2468 
2469         /* Signal the job thread to process the job */
2470         cv_signal(&fcsm->sm_job_cv);
2471         mutex_exit(&fcsm->sm_mutex);
2472 }
2473 
2474 static int
2475 fcsm_retry_job(fcsm_t *fcsm, fcsm_job_t *job)
2476 {
2477         /*
2478          * If it is a CT passthru job and status is login required, then
2479          * retry the job so that login can be performed again.
2480          * Ensure that this retry is performed a finite number of times,
2481          * so that a faulty fabric does not cause us to retry forever.
2482          */
2483 
2484         switch (job->job_code) {
2485         case FCSM_JOB_CT_PASSTHRU: {
2486                 uint32_t        jobflag;
2487                 fc_ct_header_t  *ct_header;
2488 
2489                 if (job->job_result != FC_LOGINREQ) {
2490                         break;
2491                 }
2492 
2493                 /*
2494                  * If it is a management server command
2495                  * then Reset the Management server login flag, so that login
2496                  * gets re-established.
2497                  * If it is a Name server command,
2498                  * then it is 'fp' responsibility to perform the login.
2499                  */
2500                 ASSERT(job->job_arg != NULL);
2501                 ct_header =
2502                     (fc_ct_header_t *)((fcio_t *)job->job_arg)->fcio_ibuf;
2503                 if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2504                         mutex_enter(&fcsm->sm_mutex);
2505                         fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
2506                         mutex_exit(&fcsm->sm_mutex);
2507                 }
2508 
2509                 if (job->job_retry_count >= fcsm_max_job_retries) {
2510                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2511                             "retry_job: job 0x%p max retries (%d) reached",
2512                             (void *)job, job->job_retry_count));
2513                         break;
2514                 }
2515 
2516                 /*
2517                  * Login is required again. Retry the command, so that
2518                  * login will get performed again.
2519                  */
2520                 mutex_enter(&job->job_mutex);
2521                 job->job_retry_count++;
2522                 jobflag = job->job_flags;
2523                 mutex_exit(&job->job_mutex);
2524 
2525                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2526                     "retry_job: retry(%d) job 0x%p",
2527                     job->job_retry_count, (void *)job));
2528                 /*
2529                  * This job should get picked up before the
2530                  * other jobs sitting in the queue.
2531                  * Requeue the command at the head and then
2532                  * reset the SERIALIZE flag.
2533                  */
2534                 fcsm_enque_job(fcsm, job, 1);
2535                 if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
2536                         mutex_enter(&fcsm->sm_mutex);
2537                         ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2538                         fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2539 
2540                         /* Signal the job thread to process the job */
2541                         cv_signal(&fcsm->sm_job_cv);
2542                         mutex_exit(&fcsm->sm_mutex);
2543                 }
2544 
2545                 /* Command is queued for retrying */
2546                 return (0);
2547         }
2548 
2549         default:
2550                 break;
2551         }
2552         return (1);
2553 }
2554 
2555 static void
2556 fcsm_jobdone(fcsm_job_t *job)
2557 {
2558         fcsm_t  *fcsm;
2559 
2560         fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2561         ASSERT(fcsm != NULL);
2562 
2563         if (job->job_result != FC_SUCCESS) {
2564                 if (fcsm_retry_job(fcsm, job) == 0) {
2565                         /* Job retried. so just return from here */
2566                         return;
2567                 }
2568         }
2569 
2570         if (job->job_comp) {
2571                 job->job_comp(job->job_comp_arg, job, job->job_result);
2572         }
2573 
2574         mutex_enter(&job->job_mutex);
2575         if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2576                 mutex_exit(&job->job_mutex);
2577                 mutex_enter(&fcsm->sm_mutex);
2578                 ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
2579                 fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
2580 
2581                 /* Signal the job thread to process the job */
2582                 cv_signal(&fcsm->sm_job_cv);
2583                 mutex_exit(&fcsm->sm_mutex);
2584                 mutex_enter(&job->job_mutex);
2585         }
2586 
2587         if (job->job_flags & FCSM_JOBFLAG_SYNC) {
2588                 mutex_exit(&job->job_mutex);
2589                 sema_v(&job->job_sema);
2590         } else {
2591                 mutex_exit(&job->job_mutex);
2592                 /* Async job, free the job structure */
2593                 fcsm_dealloc_job(job);
2594         }
2595 }
2596 
2597 fcsm_job_t *
2598 fcsm_deque_job(fcsm_t *fcsm)
2599 {
2600         fcsm_job_t      *job;
2601 
2602         ASSERT(MUTEX_HELD(&fcsm->sm_mutex));
2603 
2604         if (fcsm->sm_job_head == NULL) {
2605                 ASSERT(fcsm->sm_job_tail == NULL);
2606                 job = NULL;
2607         } else {
2608                 ASSERT(fcsm->sm_job_tail != NULL);
2609                 job = fcsm->sm_job_head;
2610                 if (job->job_next == NULL) {
2611                         ASSERT(fcsm->sm_job_tail == job);
2612                         fcsm->sm_job_tail = NULL;
2613                 }
2614                 fcsm->sm_job_head = job->job_next;
2615                 job->job_next = NULL;
2616         }
2617 
2618         return (job);
2619 }
2620 
2621 
2622 /* Dedicated per port thread to process various commands */
2623 static void
2624 fcsm_job_thread(fcsm_t *fcsm)
2625 {
2626         fcsm_job_t      *job;
2627 
2628         ASSERT(fcsm != NULL);
2629         CALLB_CPR_INIT(&fcsm->sm_cpr_info, &fcsm->sm_mutex,
2630             callb_generic_cpr, "fcsm_job_thread");
2631 
2632         for (;;) {
2633                 mutex_enter(&fcsm->sm_mutex);
2634 
2635                 while (fcsm->sm_job_head == NULL ||
2636                     fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD) {
2637                         CALLB_CPR_SAFE_BEGIN(&fcsm->sm_cpr_info);
2638                         cv_wait(&fcsm->sm_job_cv, &fcsm->sm_mutex);
2639                         CALLB_CPR_SAFE_END(&fcsm->sm_cpr_info, &fcsm->sm_mutex);
2640                 }
2641 
2642                 job = fcsm_deque_job(fcsm);
2643 
2644                 mutex_exit(&fcsm->sm_mutex);
2645 
2646                 mutex_enter(&job->job_mutex);
2647                 if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
2648                         mutex_exit(&job->job_mutex);
2649 
2650                         mutex_enter(&fcsm->sm_mutex);
2651                         ASSERT(!(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD));
2652                         fcsm->sm_flags |= FCSM_SERIALIZE_JOBTHREAD;
2653                         mutex_exit(&fcsm->sm_mutex);
2654                 } else {
2655                         mutex_exit(&job->job_mutex);
2656                 }
2657 
2658                 ASSERT(fcsm->sm_instance == job->job_port_instance);
2659 
2660                 switch (job->job_code) {
2661                 case FCSM_JOB_NONE:
2662                         fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
2663                             "job_thread: uninitialized job code");
2664                         job->job_result = FC_FAILURE;
2665                         fcsm_jobdone(job);
2666                         break;
2667 
2668                 case FCSM_JOB_THREAD_SHUTDOWN:
2669                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2670                             "job_thread: job code <JOB PORT SHUTDOWN>"));
2671 
2672                         /*
2673                          * There should not be any pending jobs, when this
2674                          * is being called.
2675                          */
2676                         mutex_enter(&fcsm->sm_mutex);
2677                         ASSERT(fcsm->sm_job_head == NULL);
2678                         ASSERT(fcsm->sm_job_tail == NULL);
2679                         ASSERT(fcsm->sm_retry_head == NULL);
2680                         ASSERT(fcsm->sm_retry_tail == NULL);
2681                         job->job_result = FC_SUCCESS;
2682                         CALLB_CPR_EXIT(&fcsm->sm_cpr_info);
2683                         /* CPR_EXIT has also dropped the fcsm->sm_mutex */
2684 
2685                         fcsm_jobdone(job);
2686                         thread_exit();
2687                         /* NOTREACHED */
2688                         break;
2689 
2690                 case FCSM_JOB_LOGIN_NAME_SERVER:
2691                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2692                             "job_thread: job code <LOGIN_NAME_SERVER>"));
2693                         job->job_result = FC_SUCCESS;
2694                         fcsm_jobdone(job);
2695                         break;
2696 
2697                 case FCSM_JOB_LOGIN_MGMT_SERVER:
2698                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2699                             "job_thread: job code <LOGIN_MGMT_SERVER>"));
2700                         fcsm_job_login_mgmt_server(job);
2701                         break;
2702 
2703                 case FCSM_JOB_CT_PASSTHRU:
2704                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2705                             "job_thread: job code <CT_PASSTHRU>"));
2706                         fcsm_job_ct_passthru(job);
2707                         break;
2708 
2709                 default:
2710                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2711                             "job_thread: job code <UNKNOWN>"));
2712                         job->job_result = FC_FAILURE;
2713                         fcsm_jobdone(job);
2714                         break;
2715                 }
2716         }
2717 
2718         /* NOTREACHED */
2719 }
2720 
2721 
2722 static void
2723 fcsm_ct_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, fc_ct_aiu_t *req_iu, size_t req_len,
2724     void (*comp_func)())
2725 {
2726         fc_packet_t     *pkt;
2727 
2728         pkt = cmd->cmd_fp_pkt;
2729         ASSERT(pkt != NULL);
2730 
2731         ASSERT(req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE ||
2732             (req_iu->aiu_header.ct_fcstype == FCSTYPE_DIRECTORY &&
2733             req_iu->aiu_header.ct_fcssubtype == FCSSUB_DS_NAME_SERVER));
2734 
2735 
2736         /* Set the pkt d_id properly */
2737         if (req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE) {
2738                 pkt->pkt_cmd_fhdr.d_id       = FS_MANAGEMENT_SERVER;
2739         } else {
2740                 pkt->pkt_cmd_fhdr.d_id       = FS_NAME_SERVER;
2741         }
2742 
2743         pkt->pkt_cmd_fhdr.r_ctl      = R_CTL_UNSOL_CONTROL;
2744         pkt->pkt_cmd_fhdr.rsvd       = 0;
2745         pkt->pkt_cmd_fhdr.s_id       = fcsm->sm_sid;
2746         pkt->pkt_cmd_fhdr.type       = FC_TYPE_FC_SERVICES;
2747         pkt->pkt_cmd_fhdr.f_ctl      = F_CTL_SEQ_INITIATIVE |
2748             F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
2749         pkt->pkt_cmd_fhdr.seq_id = 0;
2750         pkt->pkt_cmd_fhdr.df_ctl = 0;
2751         pkt->pkt_cmd_fhdr.seq_cnt = 0;
2752         pkt->pkt_cmd_fhdr.ox_id = 0xffff;
2753         pkt->pkt_cmd_fhdr.rx_id = 0xffff;
2754         pkt->pkt_cmd_fhdr.ro = 0;
2755 
2756         pkt->pkt_timeout     = FCSM_MS_TIMEOUT;
2757         pkt->pkt_comp                = comp_func;
2758 
2759         FCSM_REP_WR(pkt->pkt_cmd_acc, req_iu, pkt->pkt_cmd, req_len);
2760 
2761         cmd->cmd_transport = fc_ulp_transport;
2762 }
2763 
2764 static void
2765 fcsm_ct_intr(fcsm_cmd_t *cmd)
2766 {
2767         fc_packet_t     *pkt;
2768         fcsm_job_t      *job;
2769         fcio_t          *fcio;
2770         fcsm_t          *fcsm;
2771 
2772         pkt = cmd->cmd_fp_pkt;
2773         job = cmd->cmd_job;
2774         ASSERT(job != NULL);
2775 
2776         fcio = job->job_arg;
2777         ASSERT(fcio != NULL);
2778 
2779         if (pkt->pkt_state != FC_PKT_SUCCESS) {
2780                 FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
2781                     "ct_intr: CT command <0x%x> to did 0x%x failed",
2782                     ((fc_ct_aiu_t *)fcio->fcio_ibuf)->aiu_header.ct_cmdrsp,
2783                     pkt->pkt_cmd_fhdr.d_id));
2784         } else {
2785                 /* Get the CT response payload */
2786                 fcsm = cmd->cmd_fcsm;
2787                 FCSM_REP_RD(pkt->pkt_resp_acc, fcio->fcio_obuf,
2788                     pkt->pkt_resp, fcio->fcio_olen);
2789         }
2790 
2791         job->job_result =
2792             fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
2793 
2794         fcsm_free_cmd(cmd);
2795 
2796         fcsm_jobdone(job);
2797 }
2798 
2799 
2800 static void
2801 fcsm_job_ct_passthru(fcsm_job_t *job)
2802 {
2803         fcsm_t          *fcsm;
2804         fcio_t          *fcio;
2805         fcsm_cmd_t      *cmd;
2806         int             status;
2807         fc_ct_header_t  *ct_header;
2808 
2809         ASSERT(job != NULL);
2810         ASSERT(job->job_port_instance != -1);
2811 
2812         job->job_result = FC_FAILURE;
2813         fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
2814         if (fcsm == NULL) {
2815                 fcsm_jobdone(job);
2816                 return;
2817         }
2818 
2819         /*
2820          * Process the CT Passthru job only if port is attached
2821          * to a FABRIC.
2822          */
2823         if (!FC_TOP_EXTERNAL(fcsm->sm_port_top)) {
2824                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2825                     "job_ct_passthru: end (non-fabric port)"));
2826                 job->job_result = FC_BADDEV;
2827                 fcsm_jobdone(job);
2828                 return;
2829         }
2830 
2831         fcio = job->job_arg;
2832         ASSERT(fcio != NULL);
2833 
2834         /*
2835          * If it is NOT a Management Seriver (MS) or Name Server (NS) command
2836          * then complete the command with failure.
2837          */
2838         ct_header = (fc_ct_header_t *)fcio->fcio_ibuf;
2839 
2840         /*
2841          * According to libHBAAPI spec, CT header from libHBAAPI would always
2842          * be big endian, so we must swap CT header before continue in little
2843          * endian platforms.
2844          */
2845         mutex_enter(&job->job_mutex);
2846         if (!(job->job_flags & FCSM_JOBFLAG_CTHEADER_BE)) {
2847                 job->job_flags |= FCSM_JOBFLAG_CTHEADER_BE;
2848                 *((uint32_t *)((uint32_t *)ct_header + 0)) =
2849                     BE_32(*((uint32_t *)((uint32_t *)ct_header + 0)));
2850                 *((uint32_t *)((uint32_t *)ct_header + 1)) =
2851                     BE_32(*((uint32_t *)((uint32_t *)ct_header + 1)));
2852                 *((uint32_t *)((uint32_t *)ct_header + 2)) =
2853                     BE_32(*((uint32_t *)((uint32_t *)ct_header + 2)));
2854                 *((uint32_t *)((uint32_t *)ct_header + 3)) =
2855                     BE_32(*((uint32_t *)((uint32_t *)ct_header + 3)));
2856         }
2857         mutex_exit(&job->job_mutex);
2858 
2859         if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
2860                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2861                     "job_ct_passthru: Management Server Cmd"));
2862         } else if (ct_header->ct_fcstype == FCSTYPE_DIRECTORY) {
2863                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2864                     "job_ct_passthru: Name Server Cmd"));
2865         } else {
2866                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2867                     "job_ct_passthru: Unsupported Destination "
2868                     "gs_type <0x%x> gs_subtype <0x%x>",
2869                     ct_header->ct_fcstype, ct_header->ct_fcssubtype));
2870         }
2871 
2872         if (ct_header->ct_fcstype != FCSTYPE_MGMTSERVICE &&
2873             (ct_header->ct_fcstype != FCSTYPE_DIRECTORY ||
2874             ct_header->ct_fcssubtype != FCSSUB_DS_NAME_SERVER)) {
2875                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2876                     "job_ct_passthru: end (Not a Name Server OR "
2877                     "Mgmt Server Cmd)"));
2878                 job->job_result = FC_BADCMD;
2879                 fcsm_jobdone(job);
2880                 return;
2881         }
2882 
2883         /*
2884          * If it is an MS command and we are not logged in to the management
2885          * server, then start the login and requeue the command.
2886          * If login to management server is in progress, then reque the
2887          * command to wait for login to complete.
2888          */
2889         mutex_enter(&fcsm->sm_mutex);
2890         if ((ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) &&
2891             !(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN)) {
2892                 mutex_exit(&fcsm->sm_mutex);
2893                 if (fcsm_login_and_process_job(fcsm, job) != FC_SUCCESS) {
2894                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2895                             "job_ct_passthru: perform login failed"));
2896                         job->job_result = FC_FAILURE;
2897                         fcsm_jobdone(job);
2898                 }
2899                 return;
2900         }
2901         mutex_exit(&fcsm->sm_mutex);
2902 
2903         /*
2904          * We are already logged in to the management server.
2905          * Issue the CT Passthru command
2906          */
2907         cmd = fcsm_alloc_cmd(fcsm, fcio->fcio_ilen, fcio->fcio_olen, KM_SLEEP);
2908         if (cmd == NULL) {
2909                 job->job_result = FC_NOMEM;
2910                 fcsm_jobdone(job);
2911                 return;
2912         }
2913 
2914         FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
2915             fcsm_max_cmd_retries, fcsm_ct_intr);
2916 
2917         fcsm_ct_init(fcsm, cmd, (fc_ct_aiu_t *)fcio->fcio_ibuf, fcio->fcio_ilen,
2918             fcsm_pkt_common_intr);
2919 
2920         if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
2921                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
2922                     "job_ct_passthru: issue CT Passthru failed, status 0x%x",
2923                     status));
2924                 job->job_result = status;
2925                 fcsm_free_cmd(cmd);
2926                 fcsm_jobdone(job);
2927                 return;
2928         }
2929 }
2930 
2931 static int
2932 fcsm_login_and_process_job(fcsm_t *fcsm, fcsm_job_t *orig_job)
2933 {
2934         fcsm_job_t      *login_job;
2935 #ifdef DEBUG
2936         int             status;
2937 #endif /* DEBUG */
2938 
2939         if (orig_job->job_code != FCSM_JOB_CT_PASSTHRU) {
2940                 return (FC_FAILURE);
2941         }
2942 
2943         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
2944             "login_and_process_job: start login."));
2945 
2946         mutex_enter(&fcsm->sm_mutex);
2947         if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) {
2948                 /*
2949                  * Directory server login completed just now, while the
2950                  * mutex was dropped. Just queue the command again for
2951                  * processing.
2952                  */
2953                 mutex_exit(&fcsm->sm_mutex);
2954                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2955                     "login_and_process_job: got job 0x%p. login just "
2956                     "completed", (void *)orig_job));
2957                 fcsm_enque_job(fcsm, orig_job, 0);
2958                 return (FC_SUCCESS);
2959         }
2960 
2961         if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG) {
2962                 /*
2963                  * Ideally we shouldn't have come here, since login
2964                  * job has the serialize flag set.
2965                  * Anyway, put the command back on the queue.
2966                  */
2967                 mutex_exit(&fcsm->sm_mutex);
2968                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
2969                     "login_and_process_job: got job 0x%p while login to "
2970                     "management server in progress", (void *)orig_job));
2971                 fcsm_enque_job(fcsm, orig_job, 0);
2972                 return (FC_SUCCESS);
2973         }
2974 
2975         fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGIN_IN_PROG;
2976         mutex_exit(&fcsm->sm_mutex);
2977 
2978         login_job = fcsm_alloc_job(KM_SLEEP);
2979         ASSERT(login_job != NULL);
2980 
2981         /*
2982          * Mark the login job as SERIALIZE, so that all other jobs will
2983          * be processed after completing the login.
2984          * Save the original job (CT Passthru job) in the caller private
2985          * field in the job structure, so that CT command can be issued
2986          * after login has completed.
2987          */
2988         fcsm_init_job(login_job, fcsm->sm_instance, FCSM_JOB_LOGIN_MGMT_SERVER,
2989             FCSM_JOBFLAG_ASYNC | FCSM_JOBFLAG_SERIALIZE,
2990             (opaque_t)NULL, (opaque_t)orig_job, fcsm_login_ms_comp, NULL);
2991         orig_job->job_priv = (void *)login_job;
2992 
2993 #ifdef DEBUG
2994         status = fcsm_process_job(login_job, 1);
2995         ASSERT(status == FC_SUCCESS);
2996 #else /* DEBUG */
2997         (void) fcsm_process_job(login_job, 1);
2998 #endif /* DEBUG */
2999         return (FC_SUCCESS);
3000 }
3001 
3002 
3003 /* ARGSUSED */
3004 static void
3005 fcsm_login_ms_comp(opaque_t comp_arg, fcsm_job_t *login_job, int result)
3006 {
3007         fcsm_t          *fcsm;
3008         fcsm_job_t      *orig_job;
3009 
3010         ASSERT(login_job != NULL);
3011 
3012         orig_job = (fcsm_job_t *)login_job->job_caller_priv;
3013 
3014         ASSERT(orig_job != NULL);
3015         ASSERT(orig_job->job_priv == (void *)login_job);
3016         orig_job->job_priv = NULL;
3017 
3018         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3019             "login_ms_comp: result 0x%x", login_job->job_result));
3020 
3021         /* Set the login flag in the per port fcsm structure */
3022         ASSERT(login_job->job_port_instance == orig_job->job_port_instance);
3023         fcsm = ddi_get_soft_state(fcsm_state, login_job->job_port_instance);
3024         ASSERT(fcsm != NULL);
3025 
3026         mutex_enter(&fcsm->sm_mutex);
3027         ASSERT((fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) == 0);
3028         ASSERT(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG);
3029         fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGIN_IN_PROG;
3030         if (login_job->job_result != FC_SUCCESS) {
3031                 caddr_t msg;
3032 
3033                 /*
3034                  * Login failed. Complete the original job with FC_LOGINREQ
3035                  * status. Retry of that job will cause login to be
3036                  * retried.
3037                  */
3038                 mutex_exit(&fcsm->sm_mutex);
3039                 orig_job->job_result = FC_LOGINREQ;
3040                 fcsm_jobdone(orig_job);
3041 
3042                 (void) fc_ulp_error(login_job->job_result, &msg);
3043                 fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
3044                     "login_ms_comp: Management server login failed: <%s>", msg);
3045                 return;
3046         }
3047         fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGGED_IN;
3048         mutex_exit(&fcsm->sm_mutex);
3049 
3050         /*
3051          * Queue the original job at the head of the queue for processing.
3052          */
3053         fcsm_enque_job(fcsm, orig_job, 1);
3054 }
3055 
3056 
3057 static void
3058 fcsm_els_init(fcsm_cmd_t *cmd, uint32_t d_id)
3059 {
3060         fc_packet_t     *pkt;
3061         fcsm_t          *fcsm;
3062 
3063         fcsm = cmd->cmd_fcsm;
3064         pkt = cmd->cmd_fp_pkt;
3065         ASSERT(fcsm != NULL && pkt != NULL);
3066 
3067         pkt->pkt_cmd_fhdr.r_ctl      = R_CTL_ELS_REQ;
3068         pkt->pkt_cmd_fhdr.d_id       = d_id;
3069         pkt->pkt_cmd_fhdr.rsvd       = 0;
3070         pkt->pkt_cmd_fhdr.s_id       = fcsm->sm_sid;
3071         pkt->pkt_cmd_fhdr.type       = FC_TYPE_EXTENDED_LS;
3072         pkt->pkt_cmd_fhdr.f_ctl      = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
3073         pkt->pkt_cmd_fhdr.seq_id = 0;
3074         pkt->pkt_cmd_fhdr.df_ctl = 0;
3075         pkt->pkt_cmd_fhdr.seq_cnt = 0;
3076         pkt->pkt_cmd_fhdr.ox_id = 0xffff;
3077         pkt->pkt_cmd_fhdr.rx_id = 0xffff;
3078         pkt->pkt_cmd_fhdr.ro = 0;
3079 
3080         pkt->pkt_timeout     = FCSM_ELS_TIMEOUT;
3081 }
3082 
3083 
3084 static int
3085 fcsm_xlogi_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, uint32_t d_id,
3086     void (*comp_func)(), uchar_t ls_code)
3087 {
3088         ls_code_t       payload;
3089         fc_packet_t     *pkt;
3090         la_els_logi_t   *login_params;
3091         int             status;
3092 
3093         login_params = (la_els_logi_t *)
3094             kmem_zalloc(sizeof (la_els_logi_t), KM_SLEEP);
3095         if (login_params == NULL) {
3096                 return (FC_NOMEM);
3097         }
3098 
3099         status = fc_ulp_get_port_login_params(fcsm->sm_port_info.port_handle,
3100             login_params);
3101         if (status != FC_SUCCESS) {
3102                 kmem_free(login_params, sizeof (la_els_logi_t));
3103                 return (status);
3104         }
3105 
3106         pkt = cmd->cmd_fp_pkt;
3107 
3108         fcsm_els_init(cmd, d_id);
3109         pkt->pkt_comp = comp_func;
3110 
3111         payload.ls_code = ls_code;
3112         payload.mbz = 0;
3113 
3114         FCSM_REP_WR(pkt->pkt_cmd_acc, login_params,
3115             pkt->pkt_cmd, sizeof (la_els_logi_t));
3116         FCSM_REP_WR(pkt->pkt_cmd_acc, &payload,
3117             pkt->pkt_cmd, sizeof (payload));
3118 
3119         cmd->cmd_transport = fc_ulp_issue_els;
3120 
3121         kmem_free(login_params, sizeof (la_els_logi_t));
3122 
3123         return (FC_SUCCESS);
3124 }
3125 
3126 static void
3127 fcsm_xlogi_intr(fcsm_cmd_t *cmd)
3128 {
3129         fc_packet_t     *pkt;
3130         fcsm_job_t      *job;
3131         fcsm_t          *fcsm;
3132 
3133         pkt = cmd->cmd_fp_pkt;
3134         job = cmd->cmd_job;
3135         ASSERT(job != NULL);
3136 
3137         fcsm = cmd->cmd_fcsm;
3138         ASSERT(fcsm != NULL);
3139 
3140         if (pkt->pkt_state != FC_PKT_SUCCESS) {
3141                 fcsm_display(CE_WARN, SM_LOG, fcsm, pkt,
3142                     "xlogi_intr: login to DID 0x%x failed",
3143                     pkt->pkt_cmd_fhdr.d_id);
3144         } else {
3145                 /* Get the Login parameters of the Management Server */
3146                 FCSM_REP_RD(pkt->pkt_resp_acc, &fcsm->sm_ms_service_params,
3147                     pkt->pkt_resp, sizeof (la_els_logi_t));
3148         }
3149 
3150         job->job_result =
3151             fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3152 
3153         fcsm_free_cmd(cmd);
3154 
3155         fcsm_jobdone(job);
3156 }
3157 
3158 static void
3159 fcsm_job_login_mgmt_server(fcsm_job_t *job)
3160 {
3161         fcsm_t          *fcsm;
3162         fcsm_cmd_t      *cmd;
3163         int             status;
3164 
3165         ASSERT(job != NULL);
3166         ASSERT(job->job_port_instance != -1);
3167 
3168         fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
3169         if (fcsm == NULL) {
3170                 job->job_result = FC_NOMEM;
3171                 fcsm_jobdone(job);
3172                 return;
3173         }
3174 
3175         /*
3176          * Issue the  Login command to the management server.
3177          */
3178         cmd = fcsm_alloc_cmd(fcsm, sizeof (la_els_logi_t),
3179             sizeof (la_els_logi_t), KM_SLEEP);
3180         if (cmd == NULL) {
3181                 job->job_result = FC_NOMEM;
3182                 fcsm_jobdone(job);
3183                 return;
3184         }
3185 
3186         FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
3187             fcsm_max_cmd_retries, fcsm_xlogi_intr);
3188 
3189         status = fcsm_xlogi_init(fcsm, cmd, FS_MANAGEMENT_SERVER,
3190             fcsm_pkt_common_intr, LA_ELS_PLOGI);
3191 
3192         if (status != FC_SUCCESS) {
3193                 FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3194                     "job_login_mgmt_server: plogi init failed. status 0x%x",
3195                     status));
3196                 job->job_result = status;
3197                 fcsm_free_cmd(cmd);
3198                 fcsm_jobdone(job);
3199                 return;
3200         }
3201 
3202         if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
3203                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3204                     "job_ct_passthru: issue login cmd failed, status 0x%x",
3205                     status));
3206                 job->job_result = status;
3207                 fcsm_free_cmd(cmd);
3208                 fcsm_jobdone(job);
3209                 return;
3210         }
3211 }
3212 
3213 
3214 int
3215 fcsm_ct_passthru(int instance, fcio_t *fcio, int sleep, int job_flags,
3216     void (*func)(fcio_t *))
3217 {
3218         fcsm_job_t      *job;
3219         int             status;
3220 
3221         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3222             "ct_passthru: instance 0x%x fcio 0x%p", instance, fcio));
3223         job = fcsm_alloc_job(sleep);
3224         ASSERT(sleep == KM_NOSLEEP || job != NULL);
3225 
3226         fcsm_init_job(job, instance, FCSM_JOB_CT_PASSTHRU, job_flags,
3227             (opaque_t)fcio, (opaque_t)func, fcsm_ct_passthru_comp, NULL);
3228         status = fcsm_process_job(job, 0);
3229         if (status != FC_SUCCESS) {
3230                 /* Job could not be issued. So free the job and return */
3231                 fcsm_dealloc_job(job);
3232                 return (status);
3233         }
3234 
3235         if (job_flags & FCSM_JOBFLAG_SYNC) {
3236                 status = job->job_result;
3237                 fcsm_dealloc_job(job);
3238         }
3239 
3240         return (status);
3241 }
3242 
3243 
3244 /* ARGSUSED */
3245 static void
3246 fcsm_ct_passthru_comp(opaque_t comp_arg, fcsm_job_t *job, int result)
3247 {
3248         ASSERT(job != NULL);
3249         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3250             "ct_passthru_comp: result 0x%x port 0x%x",
3251             job->job_result, job->job_port_instance));
3252 }
3253 
3254 
3255 static void
3256 fcsm_pkt_common_intr(fc_packet_t *pkt)
3257 {
3258         fcsm_cmd_t      *cmd;
3259         int             jobstatus;
3260         fcsm_t          *fcsm;
3261 
3262         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
3263             "pkt_common_intr"));
3264 
3265         cmd = (fcsm_cmd_t *)pkt->pkt_ulp_private;
3266         ASSERT(cmd != NULL);
3267 
3268         if (pkt->pkt_state == FC_PKT_SUCCESS) {
3269                 /* Command completed successfully. Just complete the command */
3270                 cmd->cmd_comp(cmd);
3271                 return;
3272         }
3273 
3274         fcsm = cmd->cmd_fcsm;
3275         ASSERT(fcsm != NULL);
3276 
3277         FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
3278             "fc packet to DID 0x%x failed for pkt 0x%p",
3279             pkt->pkt_cmd_fhdr.d_id, pkt));
3280 
3281         mutex_enter(&fcsm->sm_mutex);
3282         if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3283                 /*
3284                  * No need to retry the command. The link previously
3285                  * suffered an offline  timeout.
3286                  */
3287                 mutex_exit(&fcsm->sm_mutex);
3288                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3289                     "pkt_common_intr: end. Link is down"));
3290                 cmd->cmd_comp(cmd);
3291                 return;
3292         }
3293         mutex_exit(&fcsm->sm_mutex);
3294 
3295         jobstatus = fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
3296         if (jobstatus == FC_LOGINREQ) {
3297                 /*
3298                  * Login to the destination is required. No need to
3299                  * retry this cmd again.
3300                  */
3301                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
3302                     "pkt_common_intr: end. LOGIN required"));
3303                 cmd->cmd_comp(cmd);
3304                 return;
3305         }
3306 
3307         switch (pkt->pkt_state) {
3308         case FC_PKT_PORT_OFFLINE:
3309         case FC_PKT_LOCAL_RJT:
3310         case FC_PKT_TIMEOUT: {
3311                 uchar_t         pkt_state;
3312 
3313                 pkt_state = pkt->pkt_state;
3314                 cmd->cmd_retry_interval = fcsm_retry_interval;
3315                 if (fcsm_retry_cmd(cmd) != 0) {
3316                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3317                             cmd->cmd_fcsm, NULL,
3318                             "common_intr: max retries(%d) reached, status 0x%x",
3319                             cmd->cmd_retry_count));
3320 
3321                         /*
3322                          * Restore the pkt_state to the actual failure status
3323                          * received at the time of pkt completion.
3324                          */
3325                         pkt->pkt_state = pkt_state;
3326                         pkt->pkt_reason = 0;
3327                         cmd->cmd_comp(cmd);
3328                 } else {
3329                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
3330                             cmd->cmd_fcsm, NULL,
3331                             "pkt_common_intr: retry(%d) on pkt state (0x%x)",
3332                             cmd->cmd_retry_count, pkt_state));
3333                 }
3334                 break;
3335         }
3336         default:
3337                 cmd->cmd_comp(cmd);
3338                 break;
3339         }
3340 }
3341 
3342 static int
3343 fcsm_issue_cmd(fcsm_cmd_t *cmd)
3344 {
3345         fc_packet_t     *pkt;
3346         fcsm_t          *fcsm;
3347         int             status;
3348 
3349         pkt = cmd->cmd_fp_pkt;
3350         fcsm = cmd->cmd_fcsm;
3351 
3352         /* Explicitly invalidate this field till fcsm decides to use it */
3353         pkt->pkt_ulp_rscn_infop = NULL;
3354 
3355         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3356             "issue_cmd: entry"));
3357 
3358         ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3359         mutex_enter(&fcsm->sm_mutex);
3360         if (fcsm->sm_flags & FCSM_LINK_DOWN) {
3361                 /*
3362                  * Update the pkt_state/pkt_reason appropriately.
3363                  * Caller of this function can decide whether to call
3364                  * 'pkt->pkt_comp' or use the 'status' returned by this func.
3365                  */
3366                 mutex_exit(&fcsm->sm_mutex);
3367                 pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3368                 pkt->pkt_reason = FC_REASON_OFFLINE;
3369                 return (FC_OFFLINE);
3370         }
3371         mutex_exit(&fcsm->sm_mutex);
3372 
3373         ASSERT(cmd->cmd_transport != NULL);
3374         status = cmd->cmd_transport(fcsm->sm_port_info.port_handle, pkt);
3375         if (status != FC_SUCCESS) {
3376                 switch (status) {
3377                 case FC_LOGINREQ:
3378                         /*
3379                          * No need to retry. Return the cause of failure.
3380                          * Also update the pkt_state/pkt_reason. Caller of
3381                          * this function can decide, whether to call
3382                          * 'pkt->pkt_comp' or use the 'status' code returned
3383                          * by this function.
3384                          */
3385                         pkt->pkt_state = FC_PKT_LOCAL_RJT;
3386                         pkt->pkt_reason = FC_REASON_LOGIN_REQUIRED;
3387                         break;
3388 
3389                 case FC_DEVICE_BUSY_NEW_RSCN:
3390                         /*
3391                          * There was a newer RSCN than what fcsm knows about.
3392                          * So, just retry again
3393                          */
3394                         cmd->cmd_retry_count = 0;
3395                         /*FALLTHROUGH*/
3396                 case FC_OFFLINE:
3397                 case FC_STATEC_BUSY:
3398                         /*
3399                          * TODO: set flag, so that command is retried after
3400                          * port is back online.
3401                          * FALL Through for now.
3402                          */
3403 
3404                 case FC_TRAN_BUSY:
3405                 case FC_NOMEM:
3406                 case FC_DEVICE_BUSY:
3407                         cmd->cmd_retry_interval = fcsm_retry_interval;
3408                         if (fcsm_retry_cmd(cmd) != 0) {
3409                                 FCSM_DEBUG(SMDL_TRACE,
3410                                     (CE_WARN, SM_LOG, fcsm, NULL,
3411                                     "issue_cmd: max retries (%d) reached",
3412                                     cmd->cmd_retry_count));
3413 
3414                                 /*
3415                                  * status variable is not changed here.
3416                                  * Return the cause of the original
3417                                  * cmd_transport failure.
3418                                  * Update the pkt_state/pkt_reason. Caller
3419                                  * of this function can decide whether to
3420                                  * call 'pkt->pkt_comp' or use the 'status'
3421                                  * code returned by this function.
3422                                  */
3423                                 pkt->pkt_state = FC_PKT_TRAN_BSY;
3424                                 pkt->pkt_reason = 0;
3425                         } else {
3426                                 FCSM_DEBUG(SMDL_TRACE,
3427                                     (CE_WARN, SM_LOG, fcsm, NULL,
3428                                     "issue_cmd: retry (%d) on fc status (0x%x)",
3429                                     cmd->cmd_retry_count, status));
3430 
3431                                 status = FC_SUCCESS;
3432                         }
3433                         break;
3434 
3435                 default:
3436                         FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
3437                             "issue_cmd: failure status 0x%x", status));
3438 
3439                         pkt->pkt_state = FC_PKT_TRAN_ERROR;
3440                         pkt->pkt_reason = 0;
3441                         break;
3442 
3443 
3444                 }
3445         }
3446 
3447         return (status);
3448 }
3449 
3450 
3451 static int
3452 fcsm_retry_cmd(fcsm_cmd_t *cmd)
3453 {
3454         if (cmd->cmd_retry_count < cmd->cmd_max_retries) {
3455                 cmd->cmd_retry_count++;
3456                 fcsm_enque_cmd(cmd->cmd_fcsm, cmd);
3457                 return (0);
3458         }
3459 
3460         return (1);
3461 }
3462 
3463 static void
3464 fcsm_enque_cmd(fcsm_t *fcsm, fcsm_cmd_t *cmd)
3465 {
3466         ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3467 
3468         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "enque_cmd"));
3469 
3470         cmd->cmd_next = NULL;
3471         mutex_enter(&fcsm->sm_mutex);
3472         if (fcsm->sm_retry_tail) {
3473                 ASSERT(fcsm->sm_retry_head != NULL);
3474                 fcsm->sm_retry_tail->cmd_next = cmd;
3475                 fcsm->sm_retry_tail = cmd;
3476         } else {
3477                 ASSERT(fcsm->sm_retry_tail == NULL);
3478                 fcsm->sm_retry_head = fcsm->sm_retry_tail = cmd;
3479 
3480                 /* Schedule retry thread, if not already running */
3481                 if (fcsm->sm_retry_tid == NULL) {
3482                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3483                             "enque_cmd: schedule retry thread"));
3484                         fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3485                             (caddr_t)fcsm, fcsm_retry_ticks);
3486                 }
3487         }
3488         mutex_exit(&fcsm->sm_mutex);
3489 }
3490 
3491 
3492 static fcsm_cmd_t *
3493 fcsm_deque_cmd(fcsm_t *fcsm)
3494 {
3495         fcsm_cmd_t      *cmd;
3496 
3497         ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
3498 
3499         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "deque_cmd"));
3500 
3501         mutex_enter(&fcsm->sm_mutex);
3502         if (fcsm->sm_retry_head == NULL) {
3503                 ASSERT(fcsm->sm_retry_tail == NULL);
3504                 cmd = NULL;
3505         } else {
3506                 cmd = fcsm->sm_retry_head;
3507                 fcsm->sm_retry_head = cmd->cmd_next;
3508                 if (fcsm->sm_retry_head == NULL) {
3509                         fcsm->sm_retry_tail = NULL;
3510                 }
3511                 cmd->cmd_next = NULL;
3512         }
3513         mutex_exit(&fcsm->sm_mutex);
3514 
3515         return (cmd);
3516 }
3517 
3518 static void
3519 fcsm_retry_timeout(void *handle)
3520 {
3521         fcsm_t          *fcsm;
3522         fcsm_cmd_t      *curr_tail;
3523         fcsm_cmd_t      *cmd;
3524         int             done = 0;
3525         int             linkdown;
3526 
3527         fcsm = (fcsm_t *)handle;
3528 
3529         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "retry_timeout"));
3530 
3531         /*
3532          * If retry cmd queue is suspended, then go away.
3533          * This retry thread will be restarted, when cmd queue resumes.
3534          */
3535         mutex_enter(&fcsm->sm_mutex);
3536         if (fcsm->sm_flags & FCSM_CMD_RETRY_Q_SUSPENDED) {
3537                 /*
3538                  * Clear the retry_tid, to indicate that this routine is not
3539                  * currently being rescheduled.
3540                  */
3541                 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3542                 mutex_exit(&fcsm->sm_mutex);
3543                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3544                     "retry_timeout: end. No processing. "
3545                     "Queue is currently suspended for this instance"));
3546                 return;
3547         }
3548 
3549         linkdown = (fcsm->sm_flags & FCSM_LINK_DOWN) ? 1 : 0;
3550 
3551         /*
3552          * Save the curr_tail, so that we only process the commands
3553          * which are in the queue at this time.
3554          */
3555         curr_tail = fcsm->sm_retry_tail;
3556         mutex_exit(&fcsm->sm_mutex);
3557 
3558         /*
3559          * Check for done flag before dequeing the command.
3560          * Dequeing before checking the done flag will cause a command
3561          * to be lost.
3562          */
3563         while ((!done) && ((cmd = fcsm_deque_cmd(fcsm)) != NULL)) {
3564 
3565                 if (cmd == curr_tail) {
3566                         done = 1;
3567                 }
3568 
3569                 cmd->cmd_retry_interval -= fcsm_retry_ticker;
3570 
3571                 if (linkdown) {
3572                         fc_packet_t *pkt;
3573 
3574                         /*
3575                          * No need to retry the command. The link has
3576                          * suffered an offline  timeout.
3577                          */
3578                         pkt = cmd->cmd_fp_pkt;
3579                         pkt->pkt_state = FC_PKT_PORT_OFFLINE;
3580                         pkt->pkt_reason = FC_REASON_OFFLINE;
3581                         pkt->pkt_comp(pkt);
3582                         continue;
3583                 }
3584 
3585                 if (cmd->cmd_retry_interval <= 0) {
3586                         /* Retry the command */
3587                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3588                             "retry_timeout: issue cmd 0x%p", (void *)cmd));
3589                         if (fcsm_issue_cmd(cmd) != FC_SUCCESS) {
3590                                 cmd->cmd_fp_pkt->pkt_comp(cmd->cmd_fp_pkt);
3591                         }
3592                 } else {
3593                         /*
3594                          * Put the command back on the queue. Retry time
3595                          * has not yet reached.
3596                          */
3597                         FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3598                             "retry_timeout: queue cmd 0x%p", (void *)cmd));
3599                         fcsm_enque_cmd(fcsm, cmd);
3600                 }
3601         }
3602 
3603         mutex_enter(&fcsm->sm_mutex);
3604         if (fcsm->sm_retry_head) {
3605                 /* Activate timer */
3606                 fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
3607                     (caddr_t)fcsm, fcsm_retry_ticks);
3608                 FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
3609                     "retry_timeout: retry thread rescheduled"));
3610         } else {
3611                 /*
3612                  * Reset the tid variable. The first thread which queues the
3613                  * command, will restart the timer.
3614                  */
3615                 fcsm->sm_retry_tid = (timeout_id_t)NULL;
3616         }
3617         mutex_exit(&fcsm->sm_mutex);
3618 }