/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* fcsm - ULP Module for Fibre Channel SAN Management
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/scsi/scsi.h>
#include <sys/var.h>
#include <sys/byteorder.h>
#include <sys/fibre-channel/fc.h>
#include <sys/fibre-channel/impl/fc_ulpif.h>
#include <sys/fibre-channel/ulp/fcsm.h>
/* Definitions */
#define FCSM_VERSION "20090729-1.28"
#define FCSM_NAME_VERSION "SunFC FCSM v" FCSM_VERSION
/* Global Variables */
static char fcsm_name[] = "FCSM";
static void *fcsm_state = NULL;
static kmutex_t fcsm_global_mutex;
static uint32_t fcsm_flag = FCSM_IDLE;
static dev_info_t *fcsm_dip = NULL;
static fcsm_t *fcsm_port_head = NULL;
static kmem_cache_t *fcsm_job_cache = NULL;
static int fcsm_num_attaching = 0;
static int fcsm_num_detaching = 0;
static int fcsm_detached = 0;
static int fcsm_max_cmd_retries = FCSM_MAX_CMD_RETRIES;
static int fcsm_retry_interval = FCSM_RETRY_INTERVAL;
static int fcsm_retry_ticker = FCSM_RETRY_TICKER;
static int fcsm_offline_ticker = FCSM_OFFLINE_TICKER;
static int fcsm_max_job_retries = FCSM_MAX_JOB_RETRIES;
static clock_t fcsm_retry_ticks;
static clock_t fcsm_offline_ticks;
#ifdef DEBUG
uint32_t fcsm_debug = 0;
#endif
/* Character/Block entry points */
struct cb_ops fcsm_cb_ops = {
fcsm_open, /* open */
fcsm_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
fcsm_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op,
NULL, /* streams info */
D_NEW | D_MP,
CB_REV,
nodev, /* aread */
nodev /* awrite */
};
struct dev_ops fcsm_ops = {
DEVO_REV,
0, /* refcnt */
fcsm_getinfo, /* get info */
nulldev, /* identify (obsolete) */
nulldev, /* probe (not required for self-identifying devices) */
fcsm_attach, /* attach */
fcsm_detach, /* detach */
nodev, /* reset */
&fcsm_cb_ops, /* char/block entry points structure for leaf drivers */
NULL, /* bus operations for nexus driver */
NULL /* power management */
};
struct modldrv modldrv = {
&mod_driverops,
FCSM_NAME_VERSION,
&fcsm_ops
};
struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
static fc_ulp_modinfo_t fcsm_modinfo = {
&fcsm_modinfo, /* ulp_handle */
FCTL_ULP_MODREV_4, /* ulp_rev */
FC_TYPE_FC_SERVICES, /* ulp_type */
fcsm_name, /* ulp_name */
0, /* ulp_statec_mask: get all statec callbacks */
fcsm_port_attach, /* ulp_port_attach */
fcsm_port_detach, /* ulp_port_detach */
fcsm_port_ioctl, /* ulp_port_ioctl */
fcsm_els_cb, /* ulp_els_callback */
fcsm_data_cb, /* ulp_data_callback */
fcsm_statec_cb /* ulp_statec_callback */
};
struct fcsm_xlat_pkt_state {
uchar_t xlat_state;
int xlat_rval;
} fcsm_xlat_pkt_state [] = {
{ FC_PKT_SUCCESS, FC_SUCCESS },
{ FC_PKT_REMOTE_STOP, FC_FAILURE },
{ FC_PKT_LOCAL_RJT, FC_TRANSPORT_ERROR },
{ FC_PKT_NPORT_RJT, FC_PREJECT },
{ FC_PKT_FABRIC_RJT, FC_FREJECT },
{ FC_PKT_LOCAL_BSY, FC_TRAN_BUSY },
{ FC_PKT_TRAN_BSY, FC_TRAN_BUSY },
{ FC_PKT_NPORT_BSY, FC_PBUSY },
{ FC_PKT_FABRIC_BSY, FC_FBUSY },
{ FC_PKT_LS_RJT, FC_PREJECT },
{ FC_PKT_BA_RJT, FC_PREJECT },
{ FC_PKT_TIMEOUT, FC_FAILURE },
{ FC_PKT_FS_RJT, FC_FAILURE },
{ FC_PKT_TRAN_ERROR, FC_TRANSPORT_ERROR },
{ FC_PKT_FAILURE, FC_FAILURE },
{ FC_PKT_PORT_OFFLINE, FC_OFFLINE },
{ FC_PKT_ELS_IN_PROGRESS, FC_FAILURE }
};
struct fcsm_xlat_port_state {
uint32_t xlat_pstate;
caddr_t xlat_state_str;
} fcsm_xlat_port_state [] = {
{ FC_STATE_OFFLINE, "OFFLINE" },
{ FC_STATE_ONLINE, "ONLINE" },
{ FC_STATE_LOOP, "LOOP" },
{ FC_STATE_NAMESERVICE, "NAMESERVICE" },
{ FC_STATE_RESET, "RESET" },
{ FC_STATE_RESET_REQUESTED, "RESET_REQUESTED" },
{ FC_STATE_LIP, "LIP" },
{ FC_STATE_LIP_LBIT_SET, "LIP_LBIT_SET" },
{ FC_STATE_DEVICE_CHANGE, "DEVICE_CHANGE" },
{ FC_STATE_TARGET_PORT_RESET, "TARGET_PORT_RESET" }
};
struct fcsm_xlat_topology {
uint32_t xlat_top;
caddr_t xlat_top_str;
} fcsm_xlat_topology [] = {
{ FC_TOP_UNKNOWN, "UNKNOWN" },
{ FC_TOP_PRIVATE_LOOP, "Private Loop" },
{ FC_TOP_PUBLIC_LOOP, "Public Loop" },
{ FC_TOP_FABRIC, "Fabric" },
{ FC_TOP_PT_PT, "Point-to-Point" },
{ FC_TOP_NO_NS, "NO_NS" }
};
struct fcsm_xlat_dev_type {
uint32_t xlat_type;
caddr_t xlat_str;
} fcsm_xlat_dev_type [] = {
{ PORT_DEVICE_NOCHANGE, "No Change" },
{ PORT_DEVICE_NEW, "New" },
{ PORT_DEVICE_OLD, "Old" },
{ PORT_DEVICE_CHANGED, "Changed" },
{ PORT_DEVICE_DELETE, "Delete" },
{ PORT_DEVICE_USER_LOGIN, "User Login" },
{ PORT_DEVICE_USER_LOGOUT, "User Logout" },
{ PORT_DEVICE_USER_CREATE, "User Create" },
{ PORT_DEVICE_USER_DELETE, "User Delete" }
};
int
_init(void)
{
int rval;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_init"));
fcsm_retry_ticks = drv_usectohz(fcsm_retry_ticker * 1000 * 1000);
fcsm_offline_ticks = drv_usectohz(fcsm_offline_ticker * 1000 * 1000);
if (rval = ddi_soft_state_init(&fcsm_state, sizeof (fcsm_t),
FCSM_INIT_INSTANCES)) {
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"_init: ddi_soft_state_init failed");
return (ENOMEM);
}
mutex_init(&fcsm_global_mutex, NULL, MUTEX_DRIVER, NULL);
fcsm_job_cache = kmem_cache_create("fcsm_job_cache",
sizeof (fcsm_job_t), 8, fcsm_job_cache_constructor,
fcsm_job_cache_destructor, NULL, NULL, NULL, 0);
if (fcsm_job_cache == NULL) {
mutex_destroy(&fcsm_global_mutex);
ddi_soft_state_fini(&fcsm_state);
return (ENOMEM);
}
/*
* Now call fc_ulp_add to add this ULP in the transport layer
* database. This will cause 'ulp_port_attach' callback function
* to be called.
*/
rval = fc_ulp_add(&fcsm_modinfo);
if (rval != 0) {
switch (rval) {
case FC_ULP_SAMEMODULE:
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"_init: FC SAN Management module is already "
"registered with transport layer");
rval = EEXIST;
break;
case FC_ULP_SAMETYPE:
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"_init: Another module with same type 0x%x is "
"already registered with transport layer",
fcsm_modinfo.ulp_type);
rval = EEXIST;
break;
case FC_BADULP:
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"_init: Please upgrade this module. Current "
"version 0x%x is not the most recent version",
fcsm_modinfo.ulp_rev);
rval = EIO;
break;
default:
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"_init: fc_ulp_add failed with status 0x%x", rval);
rval = EIO;
break;
}
kmem_cache_destroy(fcsm_job_cache);
mutex_destroy(&fcsm_global_mutex);
ddi_soft_state_fini(&fcsm_state);
return (rval);
}
if ((rval = mod_install(&modlinkage)) != 0) {
FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
"_init: mod_install failed with status 0x%x", rval));
(void) fc_ulp_remove(&fcsm_modinfo);
kmem_cache_destroy(fcsm_job_cache);
mutex_destroy(&fcsm_global_mutex);
ddi_soft_state_fini(&fcsm_state);
return (rval);
}
return (rval);
}
int
_fini(void)
{
int rval;
#ifdef DEBUG
int status;
#endif /* DEBUG */
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "_fini"));
/*
* don't start cleaning up until we know that the module remove
* has worked -- if this works, then we know that each instance
* has successfully been DDI_DETACHed
*/
if ((rval = mod_remove(&modlinkage)) != 0) {
return (rval);
}
#ifdef DEBUG
status = fc_ulp_remove(&fcsm_modinfo);
if (status != 0) {
FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL, NULL,
"_fini: fc_ulp_remove failed with status 0x%x", status));
}
#else
(void) fc_ulp_remove(&fcsm_modinfo);
#endif /* DEBUG */
fcsm_detached = 0;
/*
* It is possible to modunload fcsm manually, which will cause
* a bypass of all the port_detach functionality. We may need
* to force that code path to be executed to properly clean up
* in that case.
*/
fcsm_force_port_detach_all();
kmem_cache_destroy(fcsm_job_cache);
mutex_destroy(&fcsm_global_mutex);
ddi_soft_state_fini(&fcsm_state);
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/* ARGSUSED */
static int
fcsm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rval = DDI_FAILURE;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"attach: cmd 0x%x", cmd));
switch (cmd) {
case DDI_ATTACH:
mutex_enter(&fcsm_global_mutex);
if (fcsm_dip != NULL) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"attach: duplicate attach of fcsm!!"));
break;
}
fcsm_dip = dip;
/*
* The detach routine cleans up all the port instances
* i.e. it detaches all ports.
* If _fini never got called after detach, then
* perform an fc_ulp_remove() followed by fc_ulp_add()
* to ensure that port_attach callbacks are called
* again.
*/
if (fcsm_detached) {
int status;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"attach: rebinding to transport driver"));
mutex_exit(&fcsm_global_mutex);
(void) fc_ulp_remove(&fcsm_modinfo);
/*
* Reset the detached flag, so that ports can attach
*/
mutex_enter(&fcsm_global_mutex);
fcsm_detached = 0;
mutex_exit(&fcsm_global_mutex);
status = fc_ulp_add(&fcsm_modinfo);
if (status != 0) {
/*
* ULP add failed. So set the
* detached flag again
*/
mutex_enter(&fcsm_global_mutex);
fcsm_detached = 1;
mutex_exit(&fcsm_global_mutex);
switch (status) {
case FC_ULP_SAMEMODULE:
fcsm_display(CE_WARN, SM_LOG, NULL,
NULL, "attach: FC SAN Management "
"module is already "
"registered with transport layer");
break;
case FC_ULP_SAMETYPE:
fcsm_display(CE_WARN, SM_LOG, NULL,
NULL, "attach: Another module with "
"same type 0x%x is already "
"registered with transport layer",
fcsm_modinfo.ulp_type);
break;
case FC_BADULP:
fcsm_display(CE_WARN, SM_LOG, NULL,
NULL, "attach: Please upgrade this "
"module. Current version 0x%x is "
"not the most recent version",
fcsm_modinfo.ulp_rev);
break;
default:
fcsm_display(CE_WARN, SM_LOG, NULL,
NULL, "attach: fc_ulp_add failed "
"with status 0x%x", status);
break;
}
/* Return failure */
break;
}
mutex_enter(&fcsm_global_mutex);
}
/* Create a minor node */
if (ddi_create_minor_node(fcsm_dip, "fcsm", S_IFCHR,
NULL, DDI_PSEUDO, 0) == DDI_SUCCESS) {
/* Announce presence of the device */
mutex_exit(&fcsm_global_mutex);
ddi_report_dev(dip);
rval = DDI_SUCCESS;
} else {
fcsm_dip = NULL;
mutex_exit(&fcsm_global_mutex);
fcsm_display(CE_WARN, SM_LOG_AND_CONSOLE,
NULL, NULL, "attach: create minor node failed");
}
break;
case DDI_RESUME:
rval = DDI_SUCCESS;
break;
default:
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
"attach: unknown cmd 0x%x dip 0x%p", cmd, dip));
break;
}
return (rval);
}
/* ARGSUSED */
static int
fcsm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
int instance;
int rval = DDI_SUCCESS;
instance = getminor((dev_t)arg);
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(long)instance; /* minor number is instance */
break;
case DDI_INFO_DEVT2DEVINFO:
mutex_enter(&fcsm_global_mutex);
*result = (void *)fcsm_dip;
mutex_exit(&fcsm_global_mutex);
break;
default:
rval = DDI_FAILURE;
break;
}
return (rval);
}
/* ARGSUSED */
static int
fcsm_port_attach(opaque_t ulph, fc_ulp_port_info_t *pinfo,
fc_attach_cmd_t cmd, uint32_t s_id)
{
int instance;
int rval = FC_FAILURE;
instance = ddi_get_instance(pinfo->port_dip);
/*
* Set the attaching flag, so that fcsm_detach will fail, if
* port attach is in progress.
*/
mutex_enter(&fcsm_global_mutex);
if (fcsm_detached) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"port_attach: end. detach in progress. failing attach "
"instance 0x%x", instance));
return (((cmd == FC_CMD_POWER_UP) || (cmd == FC_CMD_RESUME)) ?
FC_FAILURE_SILENT : FC_FAILURE);
}
fcsm_num_attaching++;
mutex_exit(&fcsm_global_mutex);
switch (cmd) {
case FC_CMD_ATTACH:
if (fcsm_handle_port_attach(pinfo, s_id, instance)
!= DDI_SUCCESS) {
ASSERT(ddi_get_soft_state(fcsm_state,
instance) == NULL);
break;
}
rval = FC_SUCCESS;
break;
case FC_CMD_RESUME:
case FC_CMD_POWER_UP: {
fcsm_t *fcsm;
char fcsm_pathname[MAXPATHLEN];
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"port_attach: cmd 0x%x instance 0x%x", cmd, instance));
/* Get the soft state structure */
if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"port_attach: instance 0x%x, cmd 0x%x "
"get softstate failed", instance, cmd));
break;
}
ASSERT(fcsm->sm_instance == instance);
/* If this instance is not attached, then return failure */
mutex_enter(&fcsm->sm_mutex);
if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
mutex_exit(&fcsm->sm_mutex);
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"port_detach: port is not attached");
break;
}
mutex_exit(&fcsm->sm_mutex);
if (fcsm_handle_port_resume(ulph, pinfo, cmd, s_id, fcsm) !=
DDI_SUCCESS) {
break;
}
(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
"attached to path %s", fcsm_pathname);
rval = FC_SUCCESS;
break;
}
default:
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
"port_attach: unknown cmd 0x%x for port 0x%x",
cmd, instance));
break;
}
mutex_enter(&fcsm_global_mutex);
fcsm_num_attaching--;
mutex_exit(&fcsm_global_mutex);
return (rval);
}
static int
fcsm_handle_port_attach(fc_ulp_port_info_t *pinfo, uint32_t s_id, int instance)
{
fcsm_t *fcsm;
kthread_t *thread;
char name[32];
char fcsm_pathname[MAXPATHLEN];
/* Allocate a soft state structure for the port */
if (ddi_soft_state_zalloc(fcsm_state, instance) != DDI_SUCCESS) {
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"port_attach: instance 0x%x, soft state alloc failed",
instance);
return (DDI_FAILURE);
}
if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
fcsm_display(CE_WARN, SM_LOG, NULL, NULL,
"port_attach: instance 0x%x, get soft state failed",
instance);
ddi_soft_state_free(fcsm_state, instance);
return (DDI_FAILURE);
}
/* Initialize the mutex */
mutex_init(&fcsm->sm_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&fcsm->sm_job_cv, NULL, CV_DRIVER, NULL);
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags |= FCSM_ATTACHING;
fcsm->sm_sid = s_id;
fcsm->sm_instance = instance;
fcsm->sm_port_state = pinfo->port_state;
/*
* Make a copy of the port_information structure, since fctl
* uses a temporary structure.
*/
fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
mutex_exit(&fcsm->sm_mutex);
(void) sprintf(name, "fcsm%d_cmd_cache", fcsm->sm_instance);
fcsm->sm_cmd_cache = kmem_cache_create(name,
sizeof (fcsm_cmd_t) + pinfo->port_fca_pkt_size, 8,
fcsm_cmd_cache_constructor, fcsm_cmd_cache_destructor,
NULL, (void *)fcsm, NULL, 0);
if (fcsm->sm_cmd_cache == NULL) {
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"port_attach: pkt cache create failed");
cv_destroy(&fcsm->sm_job_cv);
mutex_destroy(&fcsm->sm_mutex);
ddi_soft_state_free(fcsm_state, instance);
return (DDI_FAILURE);
}
thread = thread_create((caddr_t)NULL, 0, fcsm_job_thread,
(caddr_t)fcsm, 0, &p0, TS_RUN, v.v_maxsyspri-2);
if (thread == NULL) {
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"port_attach: job thread create failed");
kmem_cache_destroy(fcsm->sm_cmd_cache);
cv_destroy(&fcsm->sm_job_cv);
mutex_destroy(&fcsm->sm_mutex);
ddi_soft_state_free(fcsm_state, instance);
return (DDI_FAILURE);
}
fcsm->sm_thread = thread;
/* Add this structure to fcsm global linked list */
mutex_enter(&fcsm_global_mutex);
if (fcsm_port_head == NULL) {
fcsm_port_head = fcsm;
} else {
fcsm->sm_next = fcsm_port_head;
fcsm_port_head = fcsm;
}
mutex_exit(&fcsm_global_mutex);
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags &= ~FCSM_ATTACHING;
fcsm->sm_flags |= FCSM_ATTACHED;
fcsm->sm_port_top = pinfo->port_flags;
fcsm->sm_port_state = pinfo->port_state;
if (pinfo->port_acc_attr == NULL) {
/*
* The corresponding FCA doesn't support DMA at all
*/
fcsm->sm_flags |= FCSM_USING_NODMA_FCA;
}
mutex_exit(&fcsm->sm_mutex);
(void) ddi_pathname(fcsm->sm_port_info.port_dip, fcsm_pathname);
fcsm_display(CE_NOTE, SM_LOG, fcsm, NULL,
"attached to path %s", fcsm_pathname);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
fcsm_port_state_to_str(FC_PORT_STATE_MASK(pinfo->port_state)),
pinfo->port_state,
fcsm_topology_to_str(pinfo->port_flags), pinfo->port_flags));
return (DDI_SUCCESS);
}
static int
fcsm_handle_port_resume(opaque_t ulph, fc_ulp_port_info_t *pinfo,
fc_attach_cmd_t cmd, uint32_t s_id, fcsm_t *fcsm)
{
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"port_resume: cmd 0x%x", cmd));
mutex_enter(&fcsm->sm_mutex);
switch (cmd) {
case FC_CMD_RESUME:
ASSERT(!(fcsm->sm_flags & FCSM_POWER_DOWN));
fcsm->sm_flags &= ~FCSM_SUSPENDED;
break;
case FC_CMD_POWER_UP:
/* If port is suspended, then no need to resume */
fcsm->sm_flags &= ~FCSM_POWER_DOWN;
if (fcsm->sm_flags & FCSM_SUSPENDED) {
mutex_exit(&fcsm->sm_mutex);
return (DDI_SUCCESS);
}
break;
default:
mutex_exit(&fcsm->sm_mutex);
return (DDI_FAILURE);
}
fcsm->sm_sid = s_id;
/*
* Make a copy of the new port_information structure
*/
fcsm->sm_port_info = *pinfo; /* Structure copy !!! */
mutex_exit(&fcsm->sm_mutex);
fcsm_resume_port(fcsm);
/*
* Invoke state change processing.
* This will ensure that
* - offline timer is started if new port state changed to offline.
* - MGMT_SERVER_LOGIN flag is reset.
* - Port topology is updated.
*/
fcsm_statec_cb(ulph, (opaque_t)pinfo->port_handle, pinfo->port_state,
pinfo->port_flags, NULL, 0, s_id);
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
fcsm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int rval = DDI_SUCCESS;
switch (cmd) {
case DDI_DETACH: {
fcsm_t *fcsm;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"detach: start. cmd <DETACH>", cmd));
mutex_enter(&fcsm_global_mutex);
/*
* If port attach/detach in progress, then wait for 5 seconds
* for them to complete.
*/
if (fcsm_num_attaching || fcsm_num_detaching) {
int count;
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"detach: wait for port attach/detach to complete"));
count = 0;
while ((count++ <= 30) &&
(fcsm_num_attaching || fcsm_num_detaching)) {
mutex_exit(&fcsm_global_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&fcsm_global_mutex);
}
/* Port attach/detach still in prog, so fail detach */
if (fcsm_num_attaching || fcsm_num_detaching) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, NULL,
NULL, "detach: Failing detach. port "
"attach/detach in progress"));
rval = DDI_FAILURE;
break;
}
}
if (fcsm_port_head == NULL) {
/* Not much do, Succeed to detach. */
ddi_remove_minor_node(fcsm_dip, NULL);
fcsm_dip = NULL;
fcsm_detached = 0;
mutex_exit(&fcsm_global_mutex);
break;
}
/*
* Check to see, if any ports are active.
* If not, then set the DETACHING flag to indicate
* that they are being detached.
*/
fcsm = fcsm_port_head;
while (fcsm != NULL) {
mutex_enter(&fcsm->sm_mutex);
if (!(fcsm->sm_flags & FCSM_ATTACHED) ||
fcsm->sm_ncmds || fcsm->sm_cb_count) {
/* port is busy. We can't detach */
mutex_exit(&fcsm->sm_mutex);
break;
}
fcsm->sm_flags |= FCSM_DETACHING;
mutex_exit(&fcsm->sm_mutex);
fcsm = fcsm->sm_next;
}
/*
* If all ports could not be marked for detaching,
* then clear the flags and fail the detach.
* Also if a port attach is currently in progress
* then fail the detach.
*/
if (fcsm != NULL || fcsm_num_attaching || fcsm_num_detaching) {
/*
* Some ports were busy, so can't detach.
* Clear the DETACHING flag and return failure
*/
fcsm = fcsm_port_head;
while (fcsm != NULL) {
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_DETACHING) {
fcsm->sm_flags &= ~FCSM_DETACHING;
}
mutex_exit(&fcsm->sm_mutex);
fcsm = fcsm->sm_next;
}
mutex_exit(&fcsm_global_mutex);
return (DDI_FAILURE);
} else {
fcsm_detached = 1;
/*
* Mark all the detaching ports as detached, as we
* will be detaching them
*/
fcsm = fcsm_port_head;
while (fcsm != NULL) {
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags &= ~FCSM_DETACHING;
fcsm->sm_flags |= FCSM_DETACHED;
mutex_exit(&fcsm->sm_mutex);
fcsm = fcsm->sm_next;
}
}
mutex_exit(&fcsm_global_mutex);
/*
* Go ahead and detach the ports
*/
mutex_enter(&fcsm_global_mutex);
while (fcsm_port_head != NULL) {
fcsm = fcsm_port_head;
mutex_exit(&fcsm_global_mutex);
/*
* Call fcsm_cleanup_port(). This cleansup and
* removes the fcsm structure from global linked list
*/
fcsm_cleanup_port(fcsm);
/*
* Soft state cleanup done.
* Remember that fcsm struct doesn't exist anymore.
*/
mutex_enter(&fcsm_global_mutex);
}
ddi_remove_minor_node(fcsm_dip, NULL);
fcsm_dip = NULL;
mutex_exit(&fcsm_global_mutex);
break;
}
case DDI_SUSPEND:
rval = DDI_SUCCESS;
break;
default:
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
"detach: unknown cmd 0x%x", cmd));
rval = DDI_FAILURE;
break;
}
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"detach: end. cmd 0x%x, rval 0x%x", cmd, rval));
return (rval);
}
/* ARGSUSED */
static void
fcsm_force_port_detach_all(void)
{
fcsm_t *fcsm;
fcsm = fcsm_port_head;
while (fcsm) {
fcsm_cleanup_port(fcsm);
/*
* fcsm_cleanup_port will remove the current fcsm structure
* from the list, which will cause fcsm_port_head to point
* to what would have been the next structure on the list.
*/
fcsm = fcsm_port_head;
}
}
/* ARGSUSED */
static int
fcsm_port_detach(opaque_t ulph, fc_ulp_port_info_t *pinfo, fc_detach_cmd_t cmd)
{
int instance;
int rval = FC_FAILURE;
fcsm_t *fcsm;
instance = ddi_get_instance(pinfo->port_dip);
mutex_enter(&fcsm_global_mutex);
if (fcsm_detached) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"port_detach: end. instance 0x%x, fcsm is detached",
instance));
return (FC_SUCCESS);
}
fcsm_num_detaching++; /* Set the flag */
mutex_exit(&fcsm_global_mutex);
/* Get the soft state structure */
if ((fcsm = ddi_get_soft_state(fcsm_state, instance)) == NULL) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"port_detach: instance 0x%x, cmd 0x%x get softstate failed",
instance, cmd));
mutex_enter(&fcsm_global_mutex);
fcsm_num_detaching--;
mutex_exit(&fcsm_global_mutex);
return (rval);
}
ASSERT(fcsm->sm_instance == instance);
/* If this instance is not attached, then fail the detach */
mutex_enter(&fcsm->sm_mutex);
if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
mutex_exit(&fcsm->sm_mutex);
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"port_detach: port is not attached");
mutex_enter(&fcsm_global_mutex);
fcsm_num_detaching--;
mutex_exit(&fcsm_global_mutex);
return (rval);
}
mutex_exit(&fcsm->sm_mutex);
/*
* If fcsm has been detached, then all instance has already been
* detached or are being detached. So succeed this detach.
*/
switch (cmd) {
case FC_CMD_DETACH:
case FC_CMD_SUSPEND:
case FC_CMD_POWER_DOWN:
break;
default:
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"port_detach: port unknown cmd 0x%x", cmd));
mutex_enter(&fcsm_global_mutex);
fcsm_num_detaching--;
mutex_exit(&fcsm_global_mutex);
return (rval);
};
if (fcsm_handle_port_detach(pinfo, fcsm, cmd) == DDI_SUCCESS) {
rval = FC_SUCCESS;
}
mutex_enter(&fcsm_global_mutex);
fcsm_num_detaching--;
mutex_exit(&fcsm_global_mutex);
/* If it was a detach, then fcsm state structure no longer exists */
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"port_detach: end. cmd 0x%x rval 0x%x", cmd, rval));
return (rval);
}
static int
fcsm_handle_port_detach(fc_ulp_port_info_t *pinfo, fcsm_t *fcsm,
fc_detach_cmd_t cmd)
{
uint32_t flag;
int count;
#ifdef DEBUG
char pathname[MAXPATHLEN];
#endif /* DEBUG */
/*
* If port is already powered down OR suspended and there is nothing
* else to do then just return.
* Otherwise, set the flag, so that no more new activity will be
* initiated on this port.
*/
mutex_enter(&fcsm->sm_mutex);
switch (cmd) {
case FC_CMD_DETACH:
flag = FCSM_DETACHING;
break;
case FC_CMD_SUSPEND:
case FC_CMD_POWER_DOWN:
((cmd == FC_CMD_SUSPEND) ? (flag = FCSM_SUSPENDED) :
(flag = FCSM_POWER_DOWN));
if (fcsm->sm_flags &
(FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
fcsm->sm_flags |= flag;
mutex_exit(&fcsm->sm_mutex);
return (DDI_SUCCESS);
}
break;
default:
mutex_exit(&fcsm->sm_mutex);
return (DDI_FAILURE);
};
fcsm->sm_flags |= flag;
/*
* If some commands are pending OR callback in progress, then
* wait for some finite amount of time for their completion.
* TODO: add more checks here to check for cmd timeout, offline
* timeout and other (??) threads.
*/
count = 0;
while ((count++ <= 30) && (fcsm->sm_ncmds || fcsm->sm_cb_count)) {
mutex_exit(&fcsm->sm_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&fcsm->sm_mutex);
}
if (fcsm->sm_ncmds || fcsm->sm_cb_count) {
fcsm->sm_flags &= ~flag;
mutex_exit(&fcsm->sm_mutex);
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"port_detach: Failing suspend, port is busy");
return (DDI_FAILURE);
}
if (flag == FCSM_DETACHING) {
fcsm->sm_flags &= ~FCSM_DETACHING;
fcsm->sm_flags |= FCSM_DETACHED;
}
mutex_exit(&fcsm->sm_mutex);
FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
"port_detach: cmd 0x%x pathname <%s>",
cmd, ddi_pathname(pinfo->port_dip, pathname)));
if (cmd == FC_CMD_DETACH) {
fcsm_cleanup_port(fcsm);
/*
* Soft state cleanup done.
* Always remember that fcsm struct doesn't exist anymore.
*/
} else {
fcsm_suspend_port(fcsm);
}
return (DDI_SUCCESS);
}
static void
fcsm_suspend_port(fcsm_t *fcsm)
{
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_offline_tid != NULL) {
timeout_id_t tid;
tid = fcsm->sm_offline_tid;
fcsm->sm_offline_tid = (timeout_id_t)NULL;
mutex_exit(&fcsm->sm_mutex);
(void) untimeout(tid);
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags |= FCSM_RESTORE_OFFLINE_TIMEOUT;
}
if (fcsm->sm_retry_tid != NULL) {
timeout_id_t tid;
tid = fcsm->sm_retry_tid;
fcsm->sm_retry_tid = (timeout_id_t)NULL;
mutex_exit(&fcsm->sm_mutex);
(void) untimeout(tid);
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags |= FCSM_RESTORE_RETRY_TIMEOUT;
}
mutex_exit(&fcsm->sm_mutex);
}
static void
fcsm_resume_port(fcsm_t *fcsm)
{
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_RESTORE_OFFLINE_TIMEOUT) {
fcsm->sm_flags &= ~FCSM_RESTORE_OFFLINE_TIMEOUT;
/*
* If port if offline, link is not marked down and offline
* timer is not already running, then restart offline timer.
*/
if (!(fcsm->sm_flags & FCSM_LINK_DOWN) &&
fcsm->sm_offline_tid == NULL &&
(fcsm->sm_flags & FCSM_PORT_OFFLINE)) {
fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
(caddr_t)fcsm, fcsm_offline_ticks);
}
}
if (fcsm->sm_flags & FCSM_RESTORE_RETRY_TIMEOUT) {
fcsm->sm_flags &= ~FCSM_RESTORE_RETRY_TIMEOUT;
/*
* If retry queue is not suspended and some cmds are waiting
* to be retried, then restart the retry timer
*/
if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
(caddr_t)fcsm, fcsm_retry_ticks);
}
}
mutex_exit(&fcsm->sm_mutex);
}
static void
fcsm_cleanup_port(fcsm_t *fcsm)
{
fcsm_t *curr, *prev;
int status;
fcsm_job_t *job;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"fcsm_cleanup_port: entered"));
/*
* Kill the job thread
*/
job = fcsm_alloc_job(KM_SLEEP);
ASSERT(job != NULL);
fcsm_init_job(job, fcsm->sm_instance, FCSM_JOB_THREAD_SHUTDOWN,
FCSM_JOBFLAG_SYNC, NULL, NULL, NULL, NULL);
status = fcsm_process_job(job, 0);
ASSERT(status == FC_SUCCESS);
ASSERT(job->job_result == FC_SUCCESS);
fcsm_dealloc_job(job);
/*
* We got here after ensuring the no commands are pending or active.
* Therefore retry timeout thread should NOT be running.
* Kill the offline timeout thread if currently running.
*/
mutex_enter(&fcsm->sm_mutex);
ASSERT(fcsm->sm_retry_tid == NULL);
if (fcsm->sm_offline_tid != NULL) {
timeout_id_t tid;
tid = fcsm->sm_offline_tid;
fcsm->sm_offline_tid = (timeout_id_t)NULL;
mutex_exit(&fcsm->sm_mutex);
(void) untimeout(tid);
} else {
mutex_exit(&fcsm->sm_mutex);
}
/* Remove from the fcsm state structure from global linked list */
mutex_enter(&fcsm_global_mutex);
curr = fcsm_port_head;
prev = NULL;
while (curr != fcsm && curr != NULL) {
prev = curr;
curr = curr->sm_next;
}
ASSERT(curr != NULL);
if (prev == NULL) {
fcsm_port_head = curr->sm_next;
} else {
prev->sm_next = curr->sm_next;
}
mutex_exit(&fcsm_global_mutex);
if (fcsm->sm_cmd_cache != NULL) {
kmem_cache_destroy(fcsm->sm_cmd_cache);
}
cv_destroy(&fcsm->sm_job_cv);
mutex_destroy(&fcsm->sm_mutex);
/* Free the fcsm state structure */
ddi_soft_state_free(fcsm_state, fcsm->sm_instance);
}
/* ARGSUSED */
static void
fcsm_statec_cb(opaque_t ulph, opaque_t port_handle, uint32_t port_state,
uint32_t port_top, fc_portmap_t *devlist, uint32_t dev_cnt,
uint32_t port_sid)
{
fcsm_t *fcsm;
timeout_id_t offline_tid, retry_tid;
mutex_enter(&fcsm_global_mutex);
if (fcsm_detached) {
mutex_exit(&fcsm_global_mutex);
return;
}
fcsm = ddi_get_soft_state(fcsm_state,
fc_ulp_get_port_instance(port_handle));
if (fcsm == NULL) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
"statec_cb: instance 0x%x not found",
fc_ulp_get_port_instance(port_handle)));
return;
}
mutex_enter(&fcsm->sm_mutex);
ASSERT(fcsm->sm_instance == fc_ulp_get_port_instance(port_handle));
if ((fcsm->sm_flags & FCSM_ATTACHED) == 0) {
mutex_exit(&fcsm->sm_mutex);
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, fcsm, NULL,
"statec_cb: port not attached"));
return;
}
ASSERT(fcsm->sm_cb_count >= 0);
fcsm->sm_cb_count++;
mutex_exit(&fcsm->sm_mutex);
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
fcsm_port_state_to_str(FC_PORT_STATE_MASK(port_state)), port_state,
fcsm_topology_to_str(port_top), port_top, dev_cnt));
fcsm_disp_devlist(fcsm, devlist, dev_cnt);
mutex_enter(&fcsm->sm_mutex);
/*
* Reset the Mgmt server Login flag, so that login is performed again.
*/
fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
fcsm->sm_sid = port_sid;
fcsm->sm_port_top = port_top;
fcsm->sm_port_state = port_state;
switch (port_state) {
case FC_STATE_OFFLINE:
case FC_STATE_RESET:
case FC_STATE_RESET_REQUESTED:
fcsm->sm_flags |= FCSM_PORT_OFFLINE;
break;
case FC_STATE_ONLINE:
case FC_STATE_LOOP:
case FC_STATE_LIP:
case FC_STATE_LIP_LBIT_SET:
fcsm->sm_flags &= ~FCSM_PORT_OFFLINE;
fcsm->sm_flags &= ~FCSM_LINK_DOWN;
break;
case FC_STATE_NAMESERVICE:
case FC_STATE_DEVICE_CHANGE:
case FC_STATE_TARGET_PORT_RESET:
default:
/* Do nothing */
break;
}
offline_tid = retry_tid = NULL;
if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
/*
* Port is offline.
* Suspend cmd processing and start offline timeout thread.
*/
if (fcsm->sm_offline_tid == NULL) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"statec_cb: schedule offline timeout thread"));
fcsm->sm_flags |= FCSM_CMD_RETRY_Q_SUSPENDED;
/* Stop the cmd retry thread */
retry_tid = fcsm->sm_retry_tid;
fcsm->sm_retry_tid = (timeout_id_t)NULL;
fcsm->sm_offline_tid = timeout(fcsm_offline_timeout,
(caddr_t)fcsm, fcsm_offline_ticks);
}
} else {
/*
* Port is online.
* Cancel offline timeout thread and resume command processing.
*/
if (fcsm->sm_offline_tid) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"statec_cb: cancel offline timeout thread"));
offline_tid = fcsm->sm_offline_tid;
fcsm->sm_offline_tid = (timeout_id_t)NULL;
}
fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
/* Start retry thread if needed */
if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
(caddr_t)fcsm, fcsm_retry_ticks);
}
}
mutex_exit(&fcsm->sm_mutex);
if (offline_tid != NULL) {
(void) untimeout(offline_tid);
}
if (retry_tid != NULL) {
(void) untimeout(retry_tid);
}
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_cb_count--;
ASSERT(fcsm->sm_cb_count >= 0);
mutex_exit(&fcsm->sm_mutex);
}
static void
fcsm_offline_timeout(void *handle)
{
fcsm_t *fcsm = (fcsm_t *)handle;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"offline_timeout"));
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_PORT_OFFLINE) {
fcsm->sm_flags |= FCSM_LINK_DOWN;
}
fcsm->sm_offline_tid = (timeout_id_t)NULL;
fcsm->sm_flags &= ~FCSM_CMD_RETRY_Q_SUSPENDED;
/* Start the retry thread if needed */
if (fcsm->sm_retry_head && fcsm->sm_retry_tid == NULL) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"offline_timeout: reschedule cmd retry thread"));
ASSERT(fcsm->sm_retry_tid == NULL);
fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
(caddr_t)fcsm, fcsm_retry_ticks);
}
mutex_exit(&fcsm->sm_mutex);
}
/* ARGSUSED */
static int
fcsm_els_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
uint32_t claimed)
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
fcsm_data_cb(opaque_t ulph, opaque_t port_handle, fc_unsol_buf_t *buf,
uint32_t claimed)
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
fcsm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rval_p)
{
int retval = 0;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: start"));
mutex_enter(&fcsm_global_mutex);
if (!(fcsm_flag & FCSM_OPEN)) {
mutex_exit(&fcsm_global_mutex);
return (ENXIO);
}
mutex_exit(&fcsm_global_mutex);
/* Allow only root to talk */
if (drv_priv(credp)) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"ioctl: end (disallowing underprivileged user)"));
return (EPERM);
}
switch (cmd) {
case FCSMIO_CMD: {
fcio_t fcio;
int status;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32: {
struct fcio32 fcio32;
if (status = ddi_copyin((void *)arg, (void *)&fcio32,
sizeof (struct fcio32), mode)) {
retval = EFAULT;
break;
}
fcio.fcio_xfer = fcio32.fcio_xfer;
fcio.fcio_cmd = fcio32.fcio_cmd;
fcio.fcio_flags = fcio32.fcio_flags;
fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
fcio.fcio_ibuf = (caddr_t)(long)fcio32.fcio_ibuf;
fcio.fcio_olen = (size_t)fcio32.fcio_olen;
fcio.fcio_obuf = (caddr_t)(long)fcio32.fcio_obuf;
fcio.fcio_alen = (size_t)fcio32.fcio_alen;
fcio.fcio_abuf = (caddr_t)(long)fcio32.fcio_abuf;
fcio.fcio_errno = fcio32.fcio_errno;
break;
}
case DDI_MODEL_NONE:
if (status = ddi_copyin((void *)arg, (void *)&fcio,
sizeof (fcio_t), mode)) {
retval = EFAULT;
}
break;
}
#else /* _MULTI_DATAMODEL */
if (status = ddi_copyin((void *)arg, (void *)&fcio,
sizeof (fcio_t), mode)) {
retval = EFAULT;
break;
}
#endif /* _MULTI_DATAMODEL */
if (!status) {
retval = fcsm_fciocmd(arg, mode, credp, &fcio);
}
break;
}
default:
retval = ENOTTY;
break;
}
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "ioctl: end"));
return (retval);
}
/* ARGSUSED */
static int
fcsm_port_ioctl(opaque_t ulph, opaque_t port_handle, dev_t dev, int cmd,
intptr_t arg, int mode, cred_t *credp, int *rval, uint32_t claimed)
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
fcsm_fciocmd(intptr_t arg, int mode, cred_t *credp, fcio_t *fcio)
{
int retval = 0;
switch (fcio->fcio_cmd) {
case FCSMIO_CT_CMD: {
fcsm_t *fcsm;
caddr_t user_ibuf, user_obuf;
caddr_t req_iu, rsp_iu, abuf;
int status, instance, count;
if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
(fcio->fcio_ilen == 0) || (fcio->fcio_ibuf == 0) ||
(fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0) ||
(fcio->fcio_alen == 0) || (fcio->fcio_abuf == 0) ||
(fcio->fcio_flags != 0) || (fcio->fcio_cmd_flags != 0) ||
(fcio->fcio_ilen > FCSM_MAX_CT_SIZE) ||
(fcio->fcio_olen > FCSM_MAX_CT_SIZE) ||
(fcio->fcio_alen > MAXPATHLEN)) {
retval = EINVAL;
break;
}
/*
* Get the destination port for which this ioctl
* is targeted. The abuf will have the fp_minor
* number.
*/
abuf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
ASSERT(abuf != NULL);
if (ddi_copyin(fcio->fcio_abuf, abuf, fcio->fcio_alen, mode)) {
retval = EFAULT;
kmem_free(abuf, fcio->fcio_alen);
break;
}
instance = *((int *)abuf);
kmem_free(abuf, fcio->fcio_alen);
if (instance < 0) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"fciocmd: instance 0x%x, invalid instance",
instance));
retval = ENXIO;
break;
}
/*
* We confirmed that path corresponds to our port driver
* and a valid instance.
* If this port instance is not yet attached, then wait
* for a finite time for attach to complete
*/
fcsm = ddi_get_soft_state(fcsm_state, instance);
count = 0;
while (count++ <= 30) {
if (fcsm != NULL) {
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_ATTACHED) {
mutex_exit(&fcsm->sm_mutex);
break;
}
mutex_exit(&fcsm->sm_mutex);
}
if (count == 1) {
FCSM_DEBUG(SMDL_TRACE,
(CE_WARN, SM_LOG, NULL, NULL,
"fciocmd: instance 0x%x, "
"wait for port attach", instance));
}
delay(drv_usectohz(1000000));
fcsm = ddi_get_soft_state(fcsm_state, instance);
}
if (count > 30) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, NULL, NULL,
"fciocmd: instance 0x%x, port not attached",
instance));
retval = ENXIO;
break;
}
req_iu = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
rsp_iu = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
ASSERT((req_iu != NULL) && (rsp_iu != NULL));
if (ddi_copyin(fcio->fcio_ibuf, req_iu,
fcio->fcio_ilen, mode)) {
retval = EFAULT;
kmem_free(req_iu, fcio->fcio_ilen);
kmem_free(rsp_iu, fcio->fcio_olen);
break;
}
user_ibuf = fcio->fcio_ibuf;
user_obuf = fcio->fcio_obuf;
fcio->fcio_ibuf = req_iu;
fcio->fcio_obuf = rsp_iu;
status = fcsm_ct_passthru(fcsm->sm_instance, fcio, KM_SLEEP,
FCSM_JOBFLAG_SYNC, NULL);
if (status != FC_SUCCESS) {
retval = EIO;
}
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"fciocmd: cmd 0x%x completion status 0x%x",
fcio->fcio_cmd, status));
fcio->fcio_errno = status;
fcio->fcio_ibuf = user_ibuf;
fcio->fcio_obuf = user_obuf;
if (ddi_copyout(rsp_iu, fcio->fcio_obuf,
fcio->fcio_olen, mode)) {
retval = EFAULT;
kmem_free(req_iu, fcio->fcio_ilen);
kmem_free(rsp_iu, fcio->fcio_olen);
break;
}
kmem_free(req_iu, fcio->fcio_ilen);
kmem_free(rsp_iu, fcio->fcio_olen);
if (fcsm_fcio_copyout(fcio, arg, mode)) {
retval = EFAULT;
}
break;
}
case FCSMIO_ADAPTER_LIST: {
fc_hba_list_t *list;
int count;
if ((fcio->fcio_xfer != FCIO_XFER_RW) ||
(fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
retval = EINVAL;
break;
}
list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
if (ddi_copyin(fcio->fcio_obuf, list, fcio->fcio_olen, mode)) {
retval = EFAULT;
break;
}
list->version = FC_HBA_LIST_VERSION;
if (fcio->fcio_olen < MAXPATHLEN * list->numAdapters) {
retval = EFAULT;
break;
}
count = fc_ulp_get_adapter_paths((char *)list->hbaPaths,
list->numAdapters);
if (count < 0) {
/* Did something go wrong? */
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"Error fetching adapter list."));
retval = ENXIO;
kmem_free(list, fcio->fcio_olen);
break;
}
/* Sucess (or short buffer) */
list->numAdapters = count;
if (ddi_copyout(list, fcio->fcio_obuf,
fcio->fcio_olen, mode)) {
retval = EFAULT;
}
kmem_free(list, fcio->fcio_olen);
break;
}
default:
FCSM_DEBUG(SMDL_TRACE, (CE_NOTE, SM_LOG, NULL, NULL,
"fciocmd: unknown cmd <0x%x>", fcio->fcio_cmd));
retval = ENOTTY;
break;
}
return (retval);
}
static int
fcsm_fcio_copyout(fcio_t *fcio, intptr_t arg, int mode)
{
int status;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32: {
struct fcio32 fcio32;
fcio32.fcio_xfer = fcio->fcio_xfer;
fcio32.fcio_cmd = fcio->fcio_cmd;
fcio32.fcio_flags = fcio->fcio_flags;
fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
fcio32.fcio_ilen = fcio->fcio_ilen;
fcio32.fcio_ibuf = (caddr32_t)(long)fcio->fcio_ibuf;
fcio32.fcio_olen = fcio->fcio_olen;
fcio32.fcio_obuf = (caddr32_t)(long)fcio->fcio_obuf;
fcio32.fcio_alen = fcio->fcio_alen;
fcio32.fcio_abuf = (caddr32_t)(long)fcio->fcio_abuf;
fcio32.fcio_errno = fcio->fcio_errno;
status = ddi_copyout((void *)&fcio32, (void *)arg,
sizeof (struct fcio32), mode);
break;
}
case DDI_MODEL_NONE:
status = ddi_copyout((void *)fcio, (void *)arg,
sizeof (fcio_t), mode);
break;
}
#else /* _MULTI_DATAMODEL */
status = ddi_copyout((void *)fcio, (void *)arg, sizeof (fcio_t), mode);
#endif /* _MULTI_DATAMODEL */
return (status);
}
/* ARGSUSED */
static int
fcsm_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "open"));
if (otyp != OTYP_CHR) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"fcsm_open: failed. open type 0x%x for minor 0x%x is not "
"OTYP_CHR", otyp, getminor(*devp)));
return (EINVAL);
}
/*
* Allow anybody to open (both root and non-root users).
* Previlege level checks are made on the per ioctl basis.
*/
mutex_enter(&fcsm_global_mutex);
if (flags & FEXCL) {
if (fcsm_flag & FCSM_OPEN) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"fcsm_open: exclusive open of 0x%x failed",
getminor(*devp)));
return (EBUSY);
} else {
ASSERT(fcsm_flag == FCSM_IDLE);
fcsm_flag |= FCSM_EXCL;
}
} else {
if (fcsm_flag & FCSM_EXCL) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"fcsm_open: failed. Device minor 0x%x is in "
"exclusive open mode", getminor(*devp)));
return (EBUSY);
}
}
fcsm_flag |= FCSM_OPEN;
mutex_exit(&fcsm_global_mutex);
return (0);
}
/* ARGSUSED */
static int
fcsm_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL, "close"));
if (otyp != OTYP_CHR) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"fcsm_close: failed. close type 0x%x for minor 0x%x is not "
"OTYP_CHR", otyp, getminor(dev)));
return (EINVAL);
}
mutex_enter(&fcsm_global_mutex);
if ((fcsm_flag & FCSM_OPEN) == 0) {
mutex_exit(&fcsm_global_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"fcsm_close: failed. minor 0x%x is already closed",
getminor(dev)));
return (ENODEV);
}
fcsm_flag = FCSM_IDLE;
mutex_exit(&fcsm_global_mutex);
return (0);
}
/* ARGSUSED */
static void
fcsm_disp_devlist(fcsm_t *fcsm, fc_portmap_t *devlist, uint32_t dev_cnt)
{
fc_portmap_t *map;
uint32_t i;
if (dev_cnt == 0) {
return;
}
ASSERT(devlist != NULL);
for (i = 0; i < dev_cnt; i++) {
map = &devlist[i];
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
"state (0x%x) "
"type <%s>(0x%x) "
"flags (0x%x)",
i, map->map_did.port_id,
map->map_pwwn.raw_wwn[0], map->map_pwwn.raw_wwn[1],
map->map_pwwn.raw_wwn[2], map->map_pwwn.raw_wwn[3],
map->map_pwwn.raw_wwn[4], map->map_pwwn.raw_wwn[5],
map->map_pwwn.raw_wwn[6], map->map_pwwn.raw_wwn[7],
map->map_state,
fcsm_dev_type_to_str(map->map_type), map->map_type,
map->map_flags));
}
}
/* ARGSUSED */
static void
fcsm_display(int level, int flags, fcsm_t *fcsm, fc_packet_t *pkt,
const char *fmt, ...)
{
caddr_t buf;
va_list ap;
buf = kmem_zalloc(256, KM_NOSLEEP);
if (buf == NULL) {
return;
}
if (fcsm) {
(void) sprintf(buf + strlen(buf), "fcsm(%d): ",
ddi_get_instance(fcsm->sm_port_info.port_dip));
} else {
(void) sprintf(buf, "fcsm: ");
}
va_start(ap, fmt);
(void) vsprintf(buf + strlen(buf), fmt, ap);
va_end(ap);
if (pkt) {
caddr_t state, reason, action, expln;
(void) fc_ulp_pkt_error(pkt, &state, &reason, &action, &expln);
(void) sprintf(buf + strlen(buf),
" state: %s(0x%x); reason: %s(0x%x)",
state, pkt->pkt_state, reason, pkt->pkt_reason);
}
switch (flags) {
case SM_LOG:
cmn_err(level, "!%s", buf);
break;
case SM_CONSOLE:
cmn_err(level, "^%s", buf);
break;
default:
cmn_err(level, "%s", buf);
break;
}
kmem_free(buf, 256);
}
/*
* Convert FC packet state to FC errno
*/
int
fcsm_pkt_state_to_rval(uchar_t state, uint32_t reason)
{
int count;
if (state == FC_PKT_LOCAL_RJT && (reason == FC_REASON_NO_CONNECTION ||
reason == FC_REASON_LOGIN_REQUIRED)) {
return (FC_LOGINREQ);
} else if (state == FC_PKT_PORT_OFFLINE &&
reason == FC_REASON_LOGIN_REQUIRED) {
return (FC_LOGINREQ);
}
for (count = 0; count < sizeof (fcsm_xlat_pkt_state) /
sizeof (fcsm_xlat_pkt_state[0]); count++) {
if (fcsm_xlat_pkt_state[count].xlat_state == state) {
return (fcsm_xlat_pkt_state[count].xlat_rval);
}
}
return (FC_FAILURE);
}
/*
* Convert port state state to descriptive string
*/
caddr_t
fcsm_port_state_to_str(uint32_t port_state)
{
int count;
for (count = 0; count < sizeof (fcsm_xlat_port_state) /
sizeof (fcsm_xlat_port_state[0]); count++) {
if (fcsm_xlat_port_state[count].xlat_pstate == port_state) {
return (fcsm_xlat_port_state[count].xlat_state_str);
}
}
return (NULL);
}
/*
* Convert port topology state to descriptive string
*/
caddr_t
fcsm_topology_to_str(uint32_t topology)
{
int count;
for (count = 0; count < sizeof (fcsm_xlat_topology) /
sizeof (fcsm_xlat_topology[0]); count++) {
if (fcsm_xlat_topology[count].xlat_top == topology) {
return (fcsm_xlat_topology[count].xlat_top_str);
}
}
return (NULL);
}
/*
* Convert port topology state to descriptive string
*/
static caddr_t
fcsm_dev_type_to_str(uint32_t type)
{
int count;
for (count = 0; count < sizeof (fcsm_xlat_dev_type) /
sizeof (fcsm_xlat_dev_type[0]); count++) {
if (fcsm_xlat_dev_type[count].xlat_type == type) {
return (fcsm_xlat_dev_type[count].xlat_str);
}
}
return (NULL);
}
static int
fcsm_cmd_cache_constructor(void *buf, void *cdarg, int kmflags)
{
fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
fcsm_t *fcsm = (fcsm_t *)cdarg;
int (*callback)(caddr_t);
fc_packet_t *pkt;
fc_ulp_port_info_t *pinfo;
ASSERT(fcsm != NULL && buf != NULL);
callback = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
cmd->cmd_fp_pkt = &cmd->cmd_fc_packet;
cmd->cmd_job = NULL;
cmd->cmd_fcsm = fcsm;
cmd->cmd_dma_flags = 0;
pkt = &cmd->cmd_fc_packet;
pkt->pkt_ulp_rscn_infop = NULL;
pkt->pkt_fca_private = (opaque_t)((caddr_t)cmd + sizeof (fcsm_cmd_t));
pkt->pkt_ulp_private = (opaque_t)cmd;
if (!(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
pinfo = &fcsm->sm_port_info;
if (ddi_dma_alloc_handle(pinfo->port_dip,
pinfo->port_cmd_dma_attr,
callback, NULL, &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
return (1);
}
if (ddi_dma_alloc_handle(pinfo->port_dip,
pinfo->port_resp_dma_attr,
callback, NULL, &pkt->pkt_resp_dma) != DDI_SUCCESS) {
ddi_dma_free_handle(&pkt->pkt_cmd_dma);
return (1);
}
} else {
pkt->pkt_cmd_dma = NULL;
pkt->pkt_cmd = NULL;
pkt->pkt_resp_dma = NULL;
pkt->pkt_resp = NULL;
}
pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
pkt->pkt_data_cookie_cnt = 0;
pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
pkt->pkt_data_cookie = NULL;
return (0);
}
/* ARGSUSED */
static void
fcsm_cmd_cache_destructor(void *buf, void *cdarg)
{
fcsm_cmd_t *cmd = (fcsm_cmd_t *)buf;
fcsm_t *fcsm = (fcsm_t *)cdarg;
fc_packet_t *pkt;
ASSERT(fcsm == cmd->cmd_fcsm);
pkt = cmd->cmd_fp_pkt;
if (pkt->pkt_cmd_dma != NULL) {
ddi_dma_free_handle(&pkt->pkt_cmd_dma);
}
if (pkt->pkt_resp_dma != NULL) {
ddi_dma_free_handle(&pkt->pkt_resp_dma);
}
}
static fcsm_cmd_t *
fcsm_alloc_cmd(fcsm_t *fcsm, uint32_t cmd_len, uint32_t resp_len, int sleep)
{
fcsm_cmd_t *cmd;
fc_packet_t *pkt;
int rval;
ulong_t real_len;
int (*callback)(caddr_t);
ddi_dma_cookie_t pkt_cookie;
ddi_dma_cookie_t *cp;
uint32_t cnt;
fc_ulp_port_info_t *pinfo;
ASSERT(fcsm != NULL);
pinfo = &fcsm->sm_port_info;
callback = (sleep == KM_SLEEP) ? DDI_DMA_SLEEP: DDI_DMA_DONTWAIT;
cmd = (fcsm_cmd_t *)kmem_cache_alloc(fcsm->sm_cmd_cache, sleep);
if (cmd == NULL) {
FCSM_DEBUG(SMDL_ERR, (CE_WARN, SM_LOG, fcsm, NULL,
"alloc_cmd: kmem_cache_alloc failed"));
return (NULL);
}
cmd->cmd_retry_count = 0;
cmd->cmd_max_retries = 0;
cmd->cmd_retry_interval = 0;
cmd->cmd_transport = NULL;
ASSERT(cmd->cmd_dma_flags == 0);
ASSERT(cmd->cmd_fp_pkt == &cmd->cmd_fc_packet);
pkt = cmd->cmd_fp_pkt;
/* Zero out the important fc_packet fields */
pkt->pkt_pd = NULL;
pkt->pkt_datalen = 0;
pkt->pkt_data = NULL;
pkt->pkt_state = 0;
pkt->pkt_action = 0;
pkt->pkt_reason = 0;
pkt->pkt_expln = 0;
/*
* Now that pkt_pd is initialized, we can call fc_ulp_init_packet
*/
if (fc_ulp_init_packet((opaque_t)pinfo->port_handle, pkt, sleep)
!= FC_SUCCESS) {
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
return (NULL);
}
if ((cmd_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
ASSERT(pkt->pkt_cmd_dma != NULL);
rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
callback, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
&pkt->pkt_cmd_acc);
if (rval != DDI_SUCCESS) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_MEM;
if (real_len < cmd_len) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
pkt->pkt_cmd, real_len, DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
callback, NULL, &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
if (rval != DDI_DMA_MAPPED) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
cmd->cmd_dma_flags |= FCSM_CF_CMD_VALID_DMA_BIND;
if (pkt->pkt_cmd_cookie_cnt >
pinfo->port_cmd_dma_attr->dma_attr_sgllen) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
KM_NOSLEEP);
if (cp == NULL) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
*cp = pkt_cookie;
cp++;
for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
*cp = pkt_cookie;
}
} else if (cmd_len != 0) {
pkt->pkt_cmd = kmem_zalloc(cmd_len, KM_SLEEP);
}
if ((resp_len) && !(fcsm->sm_flags & FCSM_USING_NODMA_FCA)) {
ASSERT(pkt->pkt_resp_dma != NULL);
rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
fcsm->sm_port_info.port_acc_attr, DDI_DMA_CONSISTENT,
callback, NULL, (caddr_t *)&pkt->pkt_resp, &real_len,
&pkt->pkt_resp_acc);
if (rval != DDI_SUCCESS) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_MEM;
if (real_len < resp_len) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
pkt->pkt_resp, real_len, DDI_DMA_READ | DDI_DMA_CONSISTENT,
callback, NULL, &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
if (rval != DDI_DMA_MAPPED) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
cmd->cmd_dma_flags |= FCSM_CF_RESP_VALID_DMA_BIND;
if (pkt->pkt_resp_cookie_cnt >
pinfo->port_resp_dma_attr->dma_attr_sgllen) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
ASSERT(pkt->pkt_resp_cookie_cnt != 0);
cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
KM_NOSLEEP);
if (cp == NULL) {
(void) fc_ulp_uninit_packet(
(opaque_t)pinfo->port_handle, pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
fcsm_free_cmd_dma(cmd);
return (NULL);
}
*cp = pkt_cookie;
cp++;
for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
*cp = pkt_cookie;
}
} else if (resp_len != 0) {
pkt->pkt_resp = kmem_zalloc(resp_len, KM_SLEEP);
}
pkt->pkt_cmdlen = cmd_len;
pkt->pkt_rsplen = resp_len;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"alloc_cmd: cmd 0x%p", (void *)cmd));
return (cmd);
}
static void
fcsm_free_cmd(fcsm_cmd_t *cmd)
{
fcsm_t *fcsm;
fcsm = cmd->cmd_fcsm;
ASSERT(fcsm != NULL);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"free_cmd: cmd 0x%p", (void *)cmd));
fcsm_free_cmd_dma(cmd);
(void) fc_ulp_uninit_packet((opaque_t)fcsm->sm_port_info.port_handle,
cmd->cmd_fp_pkt);
kmem_cache_free(fcsm->sm_cmd_cache, (void *)cmd);
}
static void
fcsm_free_cmd_dma(fcsm_cmd_t *cmd)
{
fc_packet_t *pkt;
pkt = cmd->cmd_fp_pkt;
ASSERT(pkt != NULL);
if (cmd->cmd_fcsm->sm_flags & FCSM_USING_NODMA_FCA) {
if (pkt->pkt_cmd) {
kmem_free(pkt->pkt_cmd, pkt->pkt_cmdlen);
pkt->pkt_cmd = NULL;
}
if (pkt->pkt_resp) {
kmem_free(pkt->pkt_resp, pkt->pkt_rsplen);
pkt->pkt_resp = NULL;
}
}
pkt->pkt_cmdlen = 0;
pkt->pkt_rsplen = 0;
pkt->pkt_tran_type = 0;
pkt->pkt_tran_flags = 0;
if (pkt->pkt_cmd_cookie != NULL) {
kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
sizeof (ddi_dma_cookie_t));
pkt->pkt_cmd_cookie = NULL;
}
if (pkt->pkt_resp_cookie != NULL) {
kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
sizeof (ddi_dma_cookie_t));
pkt->pkt_resp_cookie = NULL;
}
if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_BIND) {
(void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
}
if (cmd->cmd_dma_flags & FCSM_CF_CMD_VALID_DMA_MEM) {
if (pkt->pkt_cmd_acc) {
ddi_dma_mem_free(&pkt->pkt_cmd_acc);
}
}
if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_BIND) {
(void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
}
if (cmd->cmd_dma_flags & FCSM_CF_RESP_VALID_DMA_MEM) {
if (pkt->pkt_resp_acc) {
ddi_dma_mem_free(&pkt->pkt_resp_acc);
}
}
cmd->cmd_dma_flags = 0;
}
/* ARGSUSED */
static int
fcsm_job_cache_constructor(void *buf, void *cdarg, int kmflag)
{
fcsm_job_t *job = (fcsm_job_t *)buf;
mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
sema_init(&job->job_sema, 0, NULL, SEMA_DEFAULT, NULL);
return (0);
}
/* ARGSUSED */
static void
fcsm_job_cache_destructor(void *buf, void *cdarg)
{
fcsm_job_t *job = (fcsm_job_t *)buf;
sema_destroy(&job->job_sema);
mutex_destroy(&job->job_mutex);
}
static fcsm_job_t *
fcsm_alloc_job(int sleep)
{
fcsm_job_t *job;
job = (fcsm_job_t *)kmem_cache_alloc(fcsm_job_cache, sleep);
if (job != NULL) {
job->job_code = FCSM_JOB_NONE;
job->job_flags = 0;
job->job_port_instance = -1;
job->job_result = -1;
job->job_arg = (opaque_t)0;
job->job_caller_priv = (opaque_t)0;
job->job_comp = NULL;
job->job_comp_arg = (opaque_t)0;
job->job_priv = (void *)0;
job->job_priv_flags = 0;
job->job_next = 0;
}
return (job);
}
static void
fcsm_dealloc_job(fcsm_job_t *job)
{
kmem_cache_free(fcsm_job_cache, (void *)job);
}
static void
fcsm_init_job(fcsm_job_t *job, int instance, uint32_t command, uint32_t flags,
opaque_t arg, opaque_t caller_priv,
void (*comp)(opaque_t, fcsm_job_t *, int), opaque_t comp_arg)
{
ASSERT(job != NULL);
job->job_port_instance = instance;
job->job_code = command;
job->job_flags = flags;
job->job_arg = arg;
job->job_caller_priv = caller_priv;
job->job_comp = comp;
job->job_comp_arg = comp_arg;
job->job_retry_count = 0;
}
static int
fcsm_process_job(fcsm_job_t *job, int priority_flag)
{
fcsm_t *fcsm;
int sync;
ASSERT(job != NULL);
ASSERT(!MUTEX_HELD(&job->job_mutex));
fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
if (fcsm == NULL) {
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, NULL, NULL,
"process_job: port instance 0x%x not found",
job->job_port_instance));
return (FC_BADDEV);
}
mutex_enter(&job->job_mutex);
/* Both SYNC and ASYNC flags should not be set */
ASSERT(((job->job_flags & (FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) ==
FCSM_JOBFLAG_SYNC) || ((job->job_flags &
(FCSM_JOBFLAG_SYNC | FCSM_JOBFLAG_ASYNC)) == FCSM_JOBFLAG_ASYNC));
/*
* Check if job is a synchronous job. We might not be able to
* check it reliably after enque_job(), if job is an ASYNC job.
*/
sync = job->job_flags & FCSM_JOBFLAG_SYNC;
mutex_exit(&job->job_mutex);
/* Queue the job for processing by job thread */
fcsm_enque_job(fcsm, job, priority_flag);
/* Wait for job completion, if it is a synchronous job */
if (sync) {
/*
* This is a Synchronous Job. So job structure is available.
* Caller is responsible for freeing it.
*/
FCSM_DEBUG(SMDL_ERR, (CE_CONT, SM_LOG, fcsm, NULL,
"process_job: Waiting for sync job <%p> completion",
(void *)job));
sema_p(&job->job_sema);
}
return (FC_SUCCESS);
}
static void
fcsm_enque_job(fcsm_t *fcsm, fcsm_job_t *job, int priority_flag)
{
ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
mutex_enter(&fcsm->sm_mutex);
/* Queue the job at the head or tail depending on the job priority */
if (priority_flag) {
FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
"enque_job: job 0x%p is high priority", job));
/* Queue at the head */
if (fcsm->sm_job_tail == NULL) {
ASSERT(fcsm->sm_job_head == NULL);
fcsm->sm_job_head = fcsm->sm_job_tail = job;
} else {
ASSERT(fcsm->sm_job_head != NULL);
job->job_next = fcsm->sm_job_head;
fcsm->sm_job_head = job;
}
} else {
FCSM_DEBUG(SMDL_INFO, (CE_CONT, SM_LOG, fcsm, NULL,
"enque_job: job 0x%p is normal", job));
/* Queue at the tail */
if (fcsm->sm_job_tail == NULL) {
ASSERT(fcsm->sm_job_head == NULL);
fcsm->sm_job_head = fcsm->sm_job_tail = job;
} else {
ASSERT(fcsm->sm_job_head != NULL);
fcsm->sm_job_tail->job_next = job;
fcsm->sm_job_tail = job;
}
job->job_next = NULL;
}
/* Signal the job thread to process the job */
cv_signal(&fcsm->sm_job_cv);
mutex_exit(&fcsm->sm_mutex);
}
static int
fcsm_retry_job(fcsm_t *fcsm, fcsm_job_t *job)
{
/*
* If it is a CT passthru job and status is login required, then
* retry the job so that login can be performed again.
* Ensure that this retry is performed a finite number of times,
* so that a faulty fabric does not cause us to retry forever.
*/
switch (job->job_code) {
case FCSM_JOB_CT_PASSTHRU: {
uint32_t jobflag;
fc_ct_header_t *ct_header;
if (job->job_result != FC_LOGINREQ) {
break;
}
/*
* If it is a management server command
* then Reset the Management server login flag, so that login
* gets re-established.
* If it is a Name server command,
* then it is 'fp' responsibility to perform the login.
*/
ASSERT(job->job_arg != NULL);
ct_header =
(fc_ct_header_t *)((fcio_t *)job->job_arg)->fcio_ibuf;
if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
mutex_enter(&fcsm->sm_mutex);
fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGGED_IN;
mutex_exit(&fcsm->sm_mutex);
}
if (job->job_retry_count >= fcsm_max_job_retries) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_job: job 0x%p max retries (%d) reached",
(void *)job, job->job_retry_count));
break;
}
/*
* Login is required again. Retry the command, so that
* login will get performed again.
*/
mutex_enter(&job->job_mutex);
job->job_retry_count++;
jobflag = job->job_flags;
mutex_exit(&job->job_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_job: retry(%d) job 0x%p",
job->job_retry_count, (void *)job));
/*
* This job should get picked up before the
* other jobs sitting in the queue.
* Requeue the command at the head and then
* reset the SERIALIZE flag.
*/
fcsm_enque_job(fcsm, job, 1);
if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
mutex_enter(&fcsm->sm_mutex);
ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
/* Signal the job thread to process the job */
cv_signal(&fcsm->sm_job_cv);
mutex_exit(&fcsm->sm_mutex);
}
/* Command is queued for retrying */
return (0);
}
default:
break;
}
return (1);
}
static void
fcsm_jobdone(fcsm_job_t *job)
{
fcsm_t *fcsm;
fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
ASSERT(fcsm != NULL);
if (job->job_result != FC_SUCCESS) {
if (fcsm_retry_job(fcsm, job) == 0) {
/* Job retried. so just return from here */
return;
}
}
if (job->job_comp) {
job->job_comp(job->job_comp_arg, job, job->job_result);
}
mutex_enter(&job->job_mutex);
if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
mutex_exit(&job->job_mutex);
mutex_enter(&fcsm->sm_mutex);
ASSERT(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD);
fcsm->sm_flags &= ~FCSM_SERIALIZE_JOBTHREAD;
/* Signal the job thread to process the job */
cv_signal(&fcsm->sm_job_cv);
mutex_exit(&fcsm->sm_mutex);
mutex_enter(&job->job_mutex);
}
if (job->job_flags & FCSM_JOBFLAG_SYNC) {
mutex_exit(&job->job_mutex);
sema_v(&job->job_sema);
} else {
mutex_exit(&job->job_mutex);
/* Async job, free the job structure */
fcsm_dealloc_job(job);
}
}
fcsm_job_t *
fcsm_deque_job(fcsm_t *fcsm)
{
fcsm_job_t *job;
ASSERT(MUTEX_HELD(&fcsm->sm_mutex));
if (fcsm->sm_job_head == NULL) {
ASSERT(fcsm->sm_job_tail == NULL);
job = NULL;
} else {
ASSERT(fcsm->sm_job_tail != NULL);
job = fcsm->sm_job_head;
if (job->job_next == NULL) {
ASSERT(fcsm->sm_job_tail == job);
fcsm->sm_job_tail = NULL;
}
fcsm->sm_job_head = job->job_next;
job->job_next = NULL;
}
return (job);
}
/* Dedicated per port thread to process various commands */
static void
fcsm_job_thread(fcsm_t *fcsm)
{
fcsm_job_t *job;
ASSERT(fcsm != NULL);
#ifndef __lock_lint
CALLB_CPR_INIT(&fcsm->sm_cpr_info, &fcsm->sm_mutex,
callb_generic_cpr, "fcsm_job_thread");
#endif /* __lock_lint */
for (;;) {
mutex_enter(&fcsm->sm_mutex);
while (fcsm->sm_job_head == NULL ||
fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD) {
CALLB_CPR_SAFE_BEGIN(&fcsm->sm_cpr_info);
cv_wait(&fcsm->sm_job_cv, &fcsm->sm_mutex);
CALLB_CPR_SAFE_END(&fcsm->sm_cpr_info, &fcsm->sm_mutex);
}
job = fcsm_deque_job(fcsm);
mutex_exit(&fcsm->sm_mutex);
mutex_enter(&job->job_mutex);
if (job->job_flags & FCSM_JOBFLAG_SERIALIZE) {
mutex_exit(&job->job_mutex);
mutex_enter(&fcsm->sm_mutex);
ASSERT(!(fcsm->sm_flags & FCSM_SERIALIZE_JOBTHREAD));
fcsm->sm_flags |= FCSM_SERIALIZE_JOBTHREAD;
mutex_exit(&fcsm->sm_mutex);
} else {
mutex_exit(&job->job_mutex);
}
ASSERT(fcsm->sm_instance == job->job_port_instance);
switch (job->job_code) {
case FCSM_JOB_NONE:
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"job_thread: uninitialized job code");
job->job_result = FC_FAILURE;
fcsm_jobdone(job);
break;
case FCSM_JOB_THREAD_SHUTDOWN:
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_thread: job code <JOB PORT SHUTDOWN>"));
/*
* There should not be any pending jobs, when this
* is being called.
*/
mutex_enter(&fcsm->sm_mutex);
ASSERT(fcsm->sm_job_head == NULL);
ASSERT(fcsm->sm_job_tail == NULL);
ASSERT(fcsm->sm_retry_head == NULL);
ASSERT(fcsm->sm_retry_tail == NULL);
job->job_result = FC_SUCCESS;
#ifndef __lock_lint
CALLB_CPR_EXIT(&fcsm->sm_cpr_info);
#endif
/* CPR_EXIT has also dropped the fcsm->sm_mutex */
fcsm_jobdone(job);
thread_exit();
/* NOTREACHED */
break;
case FCSM_JOB_LOGIN_NAME_SERVER:
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"job_thread: job code <LOGIN_NAME_SERVER>"));
job->job_result = FC_SUCCESS;
fcsm_jobdone(job);
break;
case FCSM_JOB_LOGIN_MGMT_SERVER:
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"job_thread: job code <LOGIN_MGMT_SERVER>"));
fcsm_job_login_mgmt_server(job);
break;
case FCSM_JOB_CT_PASSTHRU:
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"job_thread: job code <CT_PASSTHRU>"));
fcsm_job_ct_passthru(job);
break;
default:
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_thread: job code <UNKNOWN>"));
job->job_result = FC_FAILURE;
fcsm_jobdone(job);
break;
}
}
/* NOTREACHED */
}
static void
fcsm_ct_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, fc_ct_aiu_t *req_iu, size_t req_len,
void (*comp_func)())
{
fc_packet_t *pkt;
pkt = cmd->cmd_fp_pkt;
ASSERT(pkt != NULL);
ASSERT(req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE ||
(req_iu->aiu_header.ct_fcstype == FCSTYPE_DIRECTORY &&
req_iu->aiu_header.ct_fcssubtype == FCSSUB_DS_NAME_SERVER));
/* Set the pkt d_id properly */
if (req_iu->aiu_header.ct_fcstype == FCSTYPE_MGMTSERVICE) {
pkt->pkt_cmd_fhdr.d_id = FS_MANAGEMENT_SERVER;
} else {
pkt->pkt_cmd_fhdr.d_id = FS_NAME_SERVER;
}
pkt->pkt_cmd_fhdr.r_ctl = R_CTL_UNSOL_CONTROL;
pkt->pkt_cmd_fhdr.rsvd = 0;
pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
pkt->pkt_cmd_fhdr.type = FC_TYPE_FC_SERVICES;
pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE |
F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
pkt->pkt_cmd_fhdr.seq_id = 0;
pkt->pkt_cmd_fhdr.df_ctl = 0;
pkt->pkt_cmd_fhdr.seq_cnt = 0;
pkt->pkt_cmd_fhdr.ox_id = 0xffff;
pkt->pkt_cmd_fhdr.rx_id = 0xffff;
pkt->pkt_cmd_fhdr.ro = 0;
pkt->pkt_timeout = FCSM_MS_TIMEOUT;
pkt->pkt_comp = comp_func;
FCSM_REP_WR(pkt->pkt_cmd_acc, req_iu, pkt->pkt_cmd, req_len);
cmd->cmd_transport = fc_ulp_transport;
}
static void
fcsm_ct_intr(fcsm_cmd_t *cmd)
{
fc_packet_t *pkt;
fcsm_job_t *job;
fcio_t *fcio;
fcsm_t *fcsm;
pkt = cmd->cmd_fp_pkt;
job = cmd->cmd_job;
ASSERT(job != NULL);
fcio = job->job_arg;
ASSERT(fcio != NULL);
if (pkt->pkt_state != FC_PKT_SUCCESS) {
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
"ct_intr: CT command <0x%x> to did 0x%x failed",
((fc_ct_aiu_t *)fcio->fcio_ibuf)->aiu_header.ct_cmdrsp,
pkt->pkt_cmd_fhdr.d_id));
} else {
/* Get the CT response payload */
fcsm = cmd->cmd_fcsm;
FCSM_REP_RD(pkt->pkt_resp_acc, fcio->fcio_obuf,
pkt->pkt_resp, fcio->fcio_olen);
}
job->job_result =
fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
fcsm_free_cmd(cmd);
fcsm_jobdone(job);
}
static void
fcsm_job_ct_passthru(fcsm_job_t *job)
{
fcsm_t *fcsm;
fcio_t *fcio;
fcsm_cmd_t *cmd;
int status;
fc_ct_header_t *ct_header;
ASSERT(job != NULL);
ASSERT(job->job_port_instance != -1);
job->job_result = FC_FAILURE;
fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
if (fcsm == NULL) {
fcsm_jobdone(job);
return;
}
/*
* Process the CT Passthru job only if port is attached
* to a FABRIC.
*/
if (!FC_TOP_EXTERNAL(fcsm->sm_port_top)) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: end (non-fabric port)"));
job->job_result = FC_BADDEV;
fcsm_jobdone(job);
return;
}
fcio = job->job_arg;
ASSERT(fcio != NULL);
/*
* If it is NOT a Management Seriver (MS) or Name Server (NS) command
* then complete the command with failure.
*/
ct_header = (fc_ct_header_t *)fcio->fcio_ibuf;
/*
* According to libHBAAPI spec, CT header from libHBAAPI would always
* be big endian, so we must swap CT header before continue in little
* endian platforms.
*/
mutex_enter(&job->job_mutex);
if (!(job->job_flags & FCSM_JOBFLAG_CTHEADER_BE)) {
job->job_flags |= FCSM_JOBFLAG_CTHEADER_BE;
*((uint32_t *)((uint32_t *)ct_header + 0)) =
BE_32(*((uint32_t *)((uint32_t *)ct_header + 0)));
*((uint32_t *)((uint32_t *)ct_header + 1)) =
BE_32(*((uint32_t *)((uint32_t *)ct_header + 1)));
*((uint32_t *)((uint32_t *)ct_header + 2)) =
BE_32(*((uint32_t *)((uint32_t *)ct_header + 2)));
*((uint32_t *)((uint32_t *)ct_header + 3)) =
BE_32(*((uint32_t *)((uint32_t *)ct_header + 3)));
}
mutex_exit(&job->job_mutex);
if (ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: Management Server Cmd"));
} else if (ct_header->ct_fcstype == FCSTYPE_DIRECTORY) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: Name Server Cmd"));
} else {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: Unsupported Destination "
"gs_type <0x%x> gs_subtype <0x%x>",
ct_header->ct_fcstype, ct_header->ct_fcssubtype));
}
if (ct_header->ct_fcstype != FCSTYPE_MGMTSERVICE &&
(ct_header->ct_fcstype != FCSTYPE_DIRECTORY ||
ct_header->ct_fcssubtype != FCSSUB_DS_NAME_SERVER)) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: end (Not a Name Server OR "
"Mgmt Server Cmd)"));
job->job_result = FC_BADCMD;
fcsm_jobdone(job);
return;
}
/*
* If it is an MS command and we are not logged in to the management
* server, then start the login and requeue the command.
* If login to management server is in progress, then reque the
* command to wait for login to complete.
*/
mutex_enter(&fcsm->sm_mutex);
if ((ct_header->ct_fcstype == FCSTYPE_MGMTSERVICE) &&
!(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN)) {
mutex_exit(&fcsm->sm_mutex);
if (fcsm_login_and_process_job(fcsm, job) != FC_SUCCESS) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_ct_passthru: perform login failed"));
job->job_result = FC_FAILURE;
fcsm_jobdone(job);
}
return;
}
mutex_exit(&fcsm->sm_mutex);
/*
* We are already logged in to the management server.
* Issue the CT Passthru command
*/
cmd = fcsm_alloc_cmd(fcsm, fcio->fcio_ilen, fcio->fcio_olen, KM_SLEEP);
if (cmd == NULL) {
job->job_result = FC_NOMEM;
fcsm_jobdone(job);
return;
}
FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
fcsm_max_cmd_retries, fcsm_ct_intr);
fcsm_ct_init(fcsm, cmd, (fc_ct_aiu_t *)fcio->fcio_ibuf, fcio->fcio_ilen,
fcsm_pkt_common_intr);
if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
"job_ct_passthru: issue CT Passthru failed, status 0x%x",
status));
job->job_result = status;
fcsm_free_cmd(cmd);
fcsm_jobdone(job);
return;
}
}
static int
fcsm_login_and_process_job(fcsm_t *fcsm, fcsm_job_t *orig_job)
{
fcsm_job_t *login_job;
#ifdef DEBUG
int status;
#endif /* DEBUG */
if (orig_job->job_code != FCSM_JOB_CT_PASSTHRU) {
return (FC_FAILURE);
}
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"login_and_process_job: start login."));
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) {
/*
* Directory server login completed just now, while the
* mutex was dropped. Just queue the command again for
* processing.
*/
mutex_exit(&fcsm->sm_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"login_and_process_job: got job 0x%p. login just "
"completed", (void *)orig_job));
fcsm_enque_job(fcsm, orig_job, 0);
return (FC_SUCCESS);
}
if (fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG) {
/*
* Ideally we shouldn't have come here, since login
* job has the serialize flag set.
* Anyway, put the command back on the queue.
*/
mutex_exit(&fcsm->sm_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"login_and_process_job: got job 0x%p while login to "
"management server in progress", (void *)orig_job));
fcsm_enque_job(fcsm, orig_job, 0);
return (FC_SUCCESS);
}
fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGIN_IN_PROG;
mutex_exit(&fcsm->sm_mutex);
login_job = fcsm_alloc_job(KM_SLEEP);
ASSERT(login_job != NULL);
/*
* Mark the login job as SERIALIZE, so that all other jobs will
* be processed after completing the login.
* Save the original job (CT Passthru job) in the caller private
* field in the job structure, so that CT command can be issued
* after login has completed.
*/
fcsm_init_job(login_job, fcsm->sm_instance, FCSM_JOB_LOGIN_MGMT_SERVER,
FCSM_JOBFLAG_ASYNC | FCSM_JOBFLAG_SERIALIZE,
(opaque_t)NULL, (opaque_t)orig_job, fcsm_login_ms_comp, NULL);
orig_job->job_priv = (void *)login_job;
#ifdef DEBUG
status = fcsm_process_job(login_job, 1);
ASSERT(status == FC_SUCCESS);
#else /* DEBUG */
(void) fcsm_process_job(login_job, 1);
#endif /* DEBUG */
return (FC_SUCCESS);
}
/* ARGSUSED */
static void
fcsm_login_ms_comp(opaque_t comp_arg, fcsm_job_t *login_job, int result)
{
fcsm_t *fcsm;
fcsm_job_t *orig_job;
ASSERT(login_job != NULL);
orig_job = (fcsm_job_t *)login_job->job_caller_priv;
ASSERT(orig_job != NULL);
ASSERT(orig_job->job_priv == (void *)login_job);
orig_job->job_priv = NULL;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"login_ms_comp: result 0x%x", login_job->job_result));
/* Set the login flag in the per port fcsm structure */
ASSERT(login_job->job_port_instance == orig_job->job_port_instance);
fcsm = ddi_get_soft_state(fcsm_state, login_job->job_port_instance);
ASSERT(fcsm != NULL);
mutex_enter(&fcsm->sm_mutex);
ASSERT((fcsm->sm_flags & FCSM_MGMT_SERVER_LOGGED_IN) == 0);
ASSERT(fcsm->sm_flags & FCSM_MGMT_SERVER_LOGIN_IN_PROG);
fcsm->sm_flags &= ~FCSM_MGMT_SERVER_LOGIN_IN_PROG;
if (login_job->job_result != FC_SUCCESS) {
caddr_t msg;
/*
* Login failed. Complete the original job with FC_LOGINREQ
* status. Retry of that job will cause login to be
* retried.
*/
mutex_exit(&fcsm->sm_mutex);
orig_job->job_result = FC_LOGINREQ;
fcsm_jobdone(orig_job);
(void) fc_ulp_error(login_job->job_result, &msg);
fcsm_display(CE_WARN, SM_LOG, fcsm, NULL,
"login_ms_comp: Management server login failed: <%s>", msg);
return;
}
fcsm->sm_flags |= FCSM_MGMT_SERVER_LOGGED_IN;
mutex_exit(&fcsm->sm_mutex);
/*
* Queue the original job at the head of the queue for processing.
*/
fcsm_enque_job(fcsm, orig_job, 1);
}
static void
fcsm_els_init(fcsm_cmd_t *cmd, uint32_t d_id)
{
fc_packet_t *pkt;
fcsm_t *fcsm;
fcsm = cmd->cmd_fcsm;
pkt = cmd->cmd_fp_pkt;
ASSERT(fcsm != NULL && pkt != NULL);
pkt->pkt_cmd_fhdr.r_ctl = R_CTL_ELS_REQ;
pkt->pkt_cmd_fhdr.d_id = d_id;
pkt->pkt_cmd_fhdr.rsvd = 0;
pkt->pkt_cmd_fhdr.s_id = fcsm->sm_sid;
pkt->pkt_cmd_fhdr.type = FC_TYPE_EXTENDED_LS;
pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
pkt->pkt_cmd_fhdr.seq_id = 0;
pkt->pkt_cmd_fhdr.df_ctl = 0;
pkt->pkt_cmd_fhdr.seq_cnt = 0;
pkt->pkt_cmd_fhdr.ox_id = 0xffff;
pkt->pkt_cmd_fhdr.rx_id = 0xffff;
pkt->pkt_cmd_fhdr.ro = 0;
pkt->pkt_timeout = FCSM_ELS_TIMEOUT;
}
static int
fcsm_xlogi_init(fcsm_t *fcsm, fcsm_cmd_t *cmd, uint32_t d_id,
void (*comp_func)(), uchar_t ls_code)
{
ls_code_t payload;
fc_packet_t *pkt;
la_els_logi_t *login_params;
int status;
login_params = (la_els_logi_t *)
kmem_zalloc(sizeof (la_els_logi_t), KM_SLEEP);
if (login_params == NULL) {
return (FC_NOMEM);
}
status = fc_ulp_get_port_login_params(fcsm->sm_port_info.port_handle,
login_params);
if (status != FC_SUCCESS) {
kmem_free(login_params, sizeof (la_els_logi_t));
return (status);
}
pkt = cmd->cmd_fp_pkt;
fcsm_els_init(cmd, d_id);
pkt->pkt_comp = comp_func;
payload.ls_code = ls_code;
payload.mbz = 0;
FCSM_REP_WR(pkt->pkt_cmd_acc, login_params,
pkt->pkt_cmd, sizeof (la_els_logi_t));
FCSM_REP_WR(pkt->pkt_cmd_acc, &payload,
pkt->pkt_cmd, sizeof (payload));
cmd->cmd_transport = fc_ulp_issue_els;
kmem_free(login_params, sizeof (la_els_logi_t));
return (FC_SUCCESS);
}
static void
fcsm_xlogi_intr(fcsm_cmd_t *cmd)
{
fc_packet_t *pkt;
fcsm_job_t *job;
fcsm_t *fcsm;
pkt = cmd->cmd_fp_pkt;
job = cmd->cmd_job;
ASSERT(job != NULL);
fcsm = cmd->cmd_fcsm;
ASSERT(fcsm != NULL);
if (pkt->pkt_state != FC_PKT_SUCCESS) {
fcsm_display(CE_WARN, SM_LOG, fcsm, pkt,
"xlogi_intr: login to DID 0x%x failed",
pkt->pkt_cmd_fhdr.d_id);
} else {
/* Get the Login parameters of the Management Server */
FCSM_REP_RD(pkt->pkt_resp_acc, &fcsm->sm_ms_service_params,
pkt->pkt_resp, sizeof (la_els_logi_t));
}
job->job_result =
fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
fcsm_free_cmd(cmd);
fcsm_jobdone(job);
}
static void
fcsm_job_login_mgmt_server(fcsm_job_t *job)
{
fcsm_t *fcsm;
fcsm_cmd_t *cmd;
int status;
ASSERT(job != NULL);
ASSERT(job->job_port_instance != -1);
fcsm = ddi_get_soft_state(fcsm_state, job->job_port_instance);
if (fcsm == NULL) {
job->job_result = FC_NOMEM;
fcsm_jobdone(job);
return;
}
/*
* Issue the Login command to the management server.
*/
cmd = fcsm_alloc_cmd(fcsm, sizeof (la_els_logi_t),
sizeof (la_els_logi_t), KM_SLEEP);
if (cmd == NULL) {
job->job_result = FC_NOMEM;
fcsm_jobdone(job);
return;
}
FCSM_INIT_CMD(cmd, job, FC_TRAN_INTR | FC_TRAN_CLASS3, FC_PKT_EXCHANGE,
fcsm_max_cmd_retries, fcsm_xlogi_intr);
status = fcsm_xlogi_init(fcsm, cmd, FS_MANAGEMENT_SERVER,
fcsm_pkt_common_intr, LA_ELS_PLOGI);
if (status != FC_SUCCESS) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"job_login_mgmt_server: plogi init failed. status 0x%x",
status));
job->job_result = status;
fcsm_free_cmd(cmd);
fcsm_jobdone(job);
return;
}
if ((status = fcsm_issue_cmd(cmd)) != FC_SUCCESS) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
"job_ct_passthru: issue login cmd failed, status 0x%x",
status));
job->job_result = status;
fcsm_free_cmd(cmd);
fcsm_jobdone(job);
return;
}
}
int
fcsm_ct_passthru(int instance, fcio_t *fcio, int sleep, int job_flags,
void (*func)(fcio_t *))
{
fcsm_job_t *job;
int status;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"ct_passthru: instance 0x%x fcio 0x%p", instance, fcio));
job = fcsm_alloc_job(sleep);
ASSERT(sleep == KM_NOSLEEP || job != NULL);
fcsm_init_job(job, instance, FCSM_JOB_CT_PASSTHRU, job_flags,
(opaque_t)fcio, (opaque_t)func, fcsm_ct_passthru_comp, NULL);
status = fcsm_process_job(job, 0);
if (status != FC_SUCCESS) {
/* Job could not be issued. So free the job and return */
fcsm_dealloc_job(job);
return (status);
}
if (job_flags & FCSM_JOBFLAG_SYNC) {
status = job->job_result;
fcsm_dealloc_job(job);
}
return (status);
}
/* ARGSUSED */
static void
fcsm_ct_passthru_comp(opaque_t comp_arg, fcsm_job_t *job, int result)
{
ASSERT(job != NULL);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"ct_passthru_comp: result 0x%x port 0x%x",
job->job_result, job->job_port_instance));
}
static void
fcsm_pkt_common_intr(fc_packet_t *pkt)
{
fcsm_cmd_t *cmd;
int jobstatus;
fcsm_t *fcsm;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, NULL, NULL,
"pkt_common_intr"));
cmd = (fcsm_cmd_t *)pkt->pkt_ulp_private;
ASSERT(cmd != NULL);
if (pkt->pkt_state == FC_PKT_SUCCESS) {
/* Command completed successfully. Just complete the command */
cmd->cmd_comp(cmd);
return;
}
fcsm = cmd->cmd_fcsm;
ASSERT(fcsm != NULL);
FCSM_DEBUG(SMDL_ERR, (CE_NOTE, SM_LOG, cmd->cmd_fcsm, pkt,
"fc packet to DID 0x%x failed for pkt 0x%p",
pkt->pkt_cmd_fhdr.d_id, pkt));
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_LINK_DOWN) {
/*
* No need to retry the command. The link previously
* suffered an offline timeout.
*/
mutex_exit(&fcsm->sm_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
"pkt_common_intr: end. Link is down"));
cmd->cmd_comp(cmd);
return;
}
mutex_exit(&fcsm->sm_mutex);
jobstatus = fcsm_pkt_state_to_rval(pkt->pkt_state, pkt->pkt_reason);
if (jobstatus == FC_LOGINREQ) {
/*
* Login to the destination is required. No need to
* retry this cmd again.
*/
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, cmd->cmd_fcsm, NULL,
"pkt_common_intr: end. LOGIN required"));
cmd->cmd_comp(cmd);
return;
}
switch (pkt->pkt_state) {
case FC_PKT_PORT_OFFLINE:
case FC_PKT_LOCAL_RJT:
case FC_PKT_TIMEOUT: {
uchar_t pkt_state;
pkt_state = pkt->pkt_state;
cmd->cmd_retry_interval = fcsm_retry_interval;
if (fcsm_retry_cmd(cmd) != 0) {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
cmd->cmd_fcsm, NULL,
"common_intr: max retries(%d) reached, status 0x%x",
cmd->cmd_retry_count));
/*
* Restore the pkt_state to the actual failure status
* received at the time of pkt completion.
*/
pkt->pkt_state = pkt_state;
pkt->pkt_reason = 0;
cmd->cmd_comp(cmd);
} else {
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG,
cmd->cmd_fcsm, NULL,
"pkt_common_intr: retry(%d) on pkt state (0x%x)",
cmd->cmd_retry_count, pkt_state));
}
break;
}
default:
cmd->cmd_comp(cmd);
break;
}
}
static int
fcsm_issue_cmd(fcsm_cmd_t *cmd)
{
fc_packet_t *pkt;
fcsm_t *fcsm;
int status;
pkt = cmd->cmd_fp_pkt;
fcsm = cmd->cmd_fcsm;
/* Explicitly invalidate this field till fcsm decides to use it */
pkt->pkt_ulp_rscn_infop = NULL;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"issue_cmd: entry"));
ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_LINK_DOWN) {
/*
* Update the pkt_state/pkt_reason appropriately.
* Caller of this function can decide whether to call
* 'pkt->pkt_comp' or use the 'status' returned by this func.
*/
mutex_exit(&fcsm->sm_mutex);
pkt->pkt_state = FC_PKT_PORT_OFFLINE;
pkt->pkt_reason = FC_REASON_OFFLINE;
return (FC_OFFLINE);
}
mutex_exit(&fcsm->sm_mutex);
ASSERT(cmd->cmd_transport != NULL);
status = cmd->cmd_transport(fcsm->sm_port_info.port_handle, pkt);
if (status != FC_SUCCESS) {
switch (status) {
case FC_LOGINREQ:
/*
* No need to retry. Return the cause of failure.
* Also update the pkt_state/pkt_reason. Caller of
* this function can decide, whether to call
* 'pkt->pkt_comp' or use the 'status' code returned
* by this function.
*/
pkt->pkt_state = FC_PKT_LOCAL_RJT;
pkt->pkt_reason = FC_REASON_LOGIN_REQUIRED;
break;
case FC_DEVICE_BUSY_NEW_RSCN:
/*
* There was a newer RSCN than what fcsm knows about.
* So, just retry again
*/
cmd->cmd_retry_count = 0;
/*FALLTHROUGH*/
case FC_OFFLINE:
case FC_STATEC_BUSY:
/*
* TODO: set flag, so that command is retried after
* port is back online.
* FALL Through for now.
*/
case FC_TRAN_BUSY:
case FC_NOMEM:
case FC_DEVICE_BUSY:
cmd->cmd_retry_interval = fcsm_retry_interval;
if (fcsm_retry_cmd(cmd) != 0) {
FCSM_DEBUG(SMDL_TRACE,
(CE_WARN, SM_LOG, fcsm, NULL,
"issue_cmd: max retries (%d) reached",
cmd->cmd_retry_count));
/*
* status variable is not changed here.
* Return the cause of the original
* cmd_transport failure.
* Update the pkt_state/pkt_reason. Caller
* of this function can decide whether to
* call 'pkt->pkt_comp' or use the 'status'
* code returned by this function.
*/
pkt->pkt_state = FC_PKT_TRAN_BSY;
pkt->pkt_reason = 0;
} else {
FCSM_DEBUG(SMDL_TRACE,
(CE_WARN, SM_LOG, fcsm, NULL,
"issue_cmd: retry (%d) on fc status (0x%x)",
cmd->cmd_retry_count, status));
status = FC_SUCCESS;
}
break;
default:
FCSM_DEBUG(SMDL_TRACE, (CE_WARN, SM_LOG, fcsm, NULL,
"issue_cmd: failure status 0x%x", status));
pkt->pkt_state = FC_PKT_TRAN_ERROR;
pkt->pkt_reason = 0;
break;
}
}
return (status);
}
static int
fcsm_retry_cmd(fcsm_cmd_t *cmd)
{
if (cmd->cmd_retry_count < cmd->cmd_max_retries) {
cmd->cmd_retry_count++;
fcsm_enque_cmd(cmd->cmd_fcsm, cmd);
return (0);
}
return (1);
}
static void
fcsm_enque_cmd(fcsm_t *fcsm, fcsm_cmd_t *cmd)
{
ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "enque_cmd"));
cmd->cmd_next = NULL;
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_retry_tail) {
ASSERT(fcsm->sm_retry_head != NULL);
fcsm->sm_retry_tail->cmd_next = cmd;
fcsm->sm_retry_tail = cmd;
} else {
ASSERT(fcsm->sm_retry_tail == NULL);
fcsm->sm_retry_head = fcsm->sm_retry_tail = cmd;
/* Schedule retry thread, if not already running */
if (fcsm->sm_retry_tid == NULL) {
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"enque_cmd: schedule retry thread"));
fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
(caddr_t)fcsm, fcsm_retry_ticks);
}
}
mutex_exit(&fcsm->sm_mutex);
}
static fcsm_cmd_t *
fcsm_deque_cmd(fcsm_t *fcsm)
{
fcsm_cmd_t *cmd;
ASSERT(!MUTEX_HELD(&fcsm->sm_mutex));
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "deque_cmd"));
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_retry_head == NULL) {
ASSERT(fcsm->sm_retry_tail == NULL);
cmd = NULL;
} else {
cmd = fcsm->sm_retry_head;
fcsm->sm_retry_head = cmd->cmd_next;
if (fcsm->sm_retry_head == NULL) {
fcsm->sm_retry_tail = NULL;
}
cmd->cmd_next = NULL;
}
mutex_exit(&fcsm->sm_mutex);
return (cmd);
}
static void
fcsm_retry_timeout(void *handle)
{
fcsm_t *fcsm;
fcsm_cmd_t *curr_tail;
fcsm_cmd_t *cmd;
int done = 0;
int linkdown;
fcsm = (fcsm_t *)handle;
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL, "retry_timeout"));
/*
* If retry cmd queue is suspended, then go away.
* This retry thread will be restarted, when cmd queue resumes.
*/
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_flags & FCSM_CMD_RETRY_Q_SUSPENDED) {
/*
* Clear the retry_tid, to indicate that this routine is not
* currently being rescheduled.
*/
fcsm->sm_retry_tid = (timeout_id_t)NULL;
mutex_exit(&fcsm->sm_mutex);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_timeout: end. No processing. "
"Queue is currently suspended for this instance"));
return;
}
linkdown = (fcsm->sm_flags & FCSM_LINK_DOWN) ? 1 : 0;
/*
* Save the curr_tail, so that we only process the commands
* which are in the queue at this time.
*/
curr_tail = fcsm->sm_retry_tail;
mutex_exit(&fcsm->sm_mutex);
/*
* Check for done flag before dequeing the command.
* Dequeing before checking the done flag will cause a command
* to be lost.
*/
while ((!done) && ((cmd = fcsm_deque_cmd(fcsm)) != NULL)) {
if (cmd == curr_tail) {
done = 1;
}
cmd->cmd_retry_interval -= fcsm_retry_ticker;
if (linkdown) {
fc_packet_t *pkt;
/*
* No need to retry the command. The link has
* suffered an offline timeout.
*/
pkt = cmd->cmd_fp_pkt;
pkt->pkt_state = FC_PKT_PORT_OFFLINE;
pkt->pkt_reason = FC_REASON_OFFLINE;
pkt->pkt_comp(pkt);
continue;
}
if (cmd->cmd_retry_interval <= 0) {
/* Retry the command */
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_timeout: issue cmd 0x%p", (void *)cmd));
if (fcsm_issue_cmd(cmd) != FC_SUCCESS) {
cmd->cmd_fp_pkt->pkt_comp(cmd->cmd_fp_pkt);
}
} else {
/*
* Put the command back on the queue. Retry time
* has not yet reached.
*/
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_timeout: queue cmd 0x%p", (void *)cmd));
fcsm_enque_cmd(fcsm, cmd);
}
}
mutex_enter(&fcsm->sm_mutex);
if (fcsm->sm_retry_head) {
/* Activate timer */
fcsm->sm_retry_tid = timeout(fcsm_retry_timeout,
(caddr_t)fcsm, fcsm_retry_ticks);
FCSM_DEBUG(SMDL_TRACE, (CE_CONT, SM_LOG, fcsm, NULL,
"retry_timeout: retry thread rescheduled"));
} else {
/*
* Reset the tid variable. The first thread which queues the
* command, will restart the timer.
*/
fcsm->sm_retry_tid = (timeout_id_t)NULL;
}
mutex_exit(&fcsm->sm_mutex);
}