/*
* 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
* 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/byteorder.h>
/* Definitions */
/* Global Variables */
static int fcsm_num_attaching = 0;
static int fcsm_num_detaching = 0;
static int fcsm_detached = 0;
#ifdef DEBUG
#endif
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 */
NULL, /* streams info */
nodev, /* aread */
nodev /* awrite */
};
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 */
};
};
&modldrv,
};
&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 {
int xlat_rval;
} fcsm_xlat_pkt_state [] = {
{ FC_PKT_SUCCESS, FC_SUCCESS },
{ FC_PKT_REMOTE_STOP, FC_FAILURE },
{ 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_FAILURE, FC_FAILURE },
};
struct fcsm_xlat_port_state {
} 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 {
} 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 {
} 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;
"_init: ddi_soft_state_init failed");
return (ENOMEM);
}
if (fcsm_job_cache == NULL) {
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.
*/
if (rval != 0) {
switch (rval) {
case FC_ULP_SAMEMODULE:
"_init: FC SAN Management module is already "
"registered with transport layer");
break;
case FC_ULP_SAMETYPE:
"_init: Another module with same type 0x%x is "
"already registered with transport layer",
break;
case FC_BADULP:
"_init: Please upgrade this module. Current "
"version 0x%x is not the most recent version",
break;
default:
"_init: fc_ulp_add failed with status 0x%x", rval);
break;
}
return (rval);
}
"_init: mod_install failed with status 0x%x", rval));
(void) fc_ulp_remove(&fcsm_modinfo);
return (rval);
}
return (rval);
}
int
_fini(void)
{
int rval;
#ifdef DEBUG
int status;
#endif /* DEBUG */
/*
* 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
*/
return (rval);
}
#ifdef DEBUG
if (status != 0) {
"_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.
*/
return (rval);
}
int
{
}
/* ARGSUSED */
static int
{
"attach: cmd 0x%x", cmd));
switch (cmd) {
case DDI_ATTACH:
"attach: duplicate attach of fcsm!!"));
break;
}
/*
* 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;
"attach: rebinding to transport driver"));
(void) fc_ulp_remove(&fcsm_modinfo);
/*
* Reset the detached flag, so that ports can attach
*/
fcsm_detached = 0;
if (status != 0) {
/*
* ULP add failed. So set the
* detached flag again
*/
fcsm_detached = 1;
switch (status) {
case FC_ULP_SAMEMODULE:
NULL, "attach: FC SAN Management "
"module is already "
"registered with transport layer");
break;
case FC_ULP_SAMETYPE:
NULL, "attach: Another module with "
"same type 0x%x is already "
"registered with transport layer",
break;
case FC_BADULP:
NULL, "attach: Please upgrade this "
"module. Current version 0x%x is "
"not the most recent version",
break;
default:
NULL, "attach: fc_ulp_add failed "
"with status 0x%x", status);
break;
}
/* Return failure */
break;
}
}
/* Create a minor node */
/* Announce presence of the device */
rval = DDI_SUCCESS;
} else {
}
break;
case DDI_RESUME:
rval = DDI_SUCCESS;
break;
default:
break;
}
return (rval);
}
/* ARGSUSED */
static int
{
int instance;
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
break;
case DDI_INFO_DEVT2DEVINFO:
break;
default:
rval = DDI_FAILURE;
break;
}
return (rval);
}
/* ARGSUSED */
static int
{
int instance;
/*
* Set the attaching flag, so that fcsm_detach will fail, if
* port attach is in progress.
*/
if (fcsm_detached) {
"port_attach: end. detach in progress. failing attach "
"instance 0x%x", instance));
}
switch (cmd) {
case FC_CMD_ATTACH:
!= DDI_SUCCESS) {
break;
}
rval = FC_SUCCESS;
break;
case FC_CMD_RESUME:
case FC_CMD_POWER_UP: {
/* Get the soft state structure */
"port_attach: instance 0x%x, cmd 0x%x "
break;
}
/* If this instance is not attached, then return failure */
"port_detach: port is not attached");
break;
}
DDI_SUCCESS) {
break;
}
"attached to path %s", fcsm_pathname);
rval = FC_SUCCESS;
break;
}
default:
"port_attach: unknown cmd 0x%x for port 0x%x",
break;
}
return (rval);
}
static int
{
/* Allocate a soft state structure for the port */
"port_attach: instance 0x%x, soft state alloc failed",
instance);
return (DDI_FAILURE);
}
"port_attach: instance 0x%x, get soft state failed",
instance);
return (DDI_FAILURE);
}
/* Initialize the mutex */
/*
* Make a copy of the port_information structure, since fctl
* uses a temporary structure.
*/
"port_attach: pkt cache create failed");
return (DDI_FAILURE);
}
"port_attach: job thread create failed");
return (DDI_FAILURE);
}
/* Add this structure to fcsm global linked list */
if (fcsm_port_head == NULL) {
} else {
}
/*
* The corresponding FCA doesn't support DMA at all
*/
}
"attached to path %s", fcsm_pathname);
"port_attach: state <%s>(0x%x) topology <%s>(0x%x)",
return (DDI_SUCCESS);
}
static int
{
"port_resume: cmd 0x%x", cmd));
switch (cmd) {
case FC_CMD_RESUME:
break;
case FC_CMD_POWER_UP:
/* If port is suspended, then no need to resume */
return (DDI_SUCCESS);
}
break;
default:
return (DDI_FAILURE);
}
/*
* Make a copy of the new port_information structure
*/
/*
* 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.
*/
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_DETACH: {
"detach: start. cmd <DETACH>", cmd));
/*
* for them to complete.
*/
if (fcsm_num_attaching || fcsm_num_detaching) {
int count;
count = 0;
while ((count++ <= 30) &&
(fcsm_num_attaching || fcsm_num_detaching)) {
}
if (fcsm_num_attaching || fcsm_num_detaching) {
NULL, "detach: Failing detach. port "
rval = DDI_FAILURE;
break;
}
}
if (fcsm_port_head == NULL) {
/* Not much do, Succeed to detach. */
fcsm_detached = 0;
break;
}
/*
* Check to see, if any ports are active.
* If not, then set the DETACHING flag to indicate
* that they are being detached.
*/
/* port is busy. We can't detach */
break;
}
}
/*
* 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.
*/
/*
* Some ports were busy, so can't detach.
* Clear the DETACHING flag and return failure
*/
}
}
return (DDI_FAILURE);
} else {
fcsm_detached = 1;
/*
* Mark all the detaching ports as detached, as we
* will be detaching them
*/
}
}
/*
* Go ahead and detach the ports
*/
while (fcsm_port_head != NULL) {
/*
* Call fcsm_cleanup_port(). This cleansup and
* removes the fcsm structure from global linked list
*/
/*
* Soft state cleanup done.
* Remember that fcsm struct doesn't exist anymore.
*/
}
break;
}
case DDI_SUSPEND:
rval = DDI_SUCCESS;
break;
default:
"detach: unknown cmd 0x%x", cmd));
rval = DDI_FAILURE;
break;
}
return (rval);
}
/* ARGSUSED */
static void
{
while (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.
*/
}
}
/* ARGSUSED */
static int
{
int instance;
if (fcsm_detached) {
"port_detach: end. instance 0x%x, fcsm is detached",
instance));
return (FC_SUCCESS);
}
fcsm_num_detaching++; /* Set the flag */
/* Get the soft state structure */
"port_detach: instance 0x%x, cmd 0x%x get softstate failed",
return (rval);
}
/* If this instance is not attached, then fail the detach */
"port_detach: port is not attached");
return (rval);
}
/*
* 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:
"port_detach: port unknown cmd 0x%x", cmd));
return (rval);
};
rval = FC_SUCCESS;
}
/* If it was a detach, then fcsm state structure no longer exists */
return (rval);
}
static int
{
int count;
#ifdef DEBUG
#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.
*/
switch (cmd) {
case FC_CMD_DETACH:
break;
case FC_CMD_SUSPEND:
case FC_CMD_POWER_DOWN:
(flag = FCSM_POWER_DOWN));
(FCSM_POWER_DOWN | FCSM_SUSPENDED)) {
return (DDI_SUCCESS);
}
break;
default:
return (DDI_FAILURE);
};
/*
* 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;
}
"port_detach: Failing suspend, port is busy");
return (DDI_FAILURE);
}
if (flag == FCSM_DETACHING) {
}
"port_detach: cmd 0x%x pathname <%s>",
if (cmd == FC_CMD_DETACH) {
/*
* Soft state cleanup done.
* Always remember that fcsm struct doesn't exist anymore.
*/
} else {
}
return (DDI_SUCCESS);
}
static void
{
}
}
}
static void
{
/*
* If port if offline, link is not marked down and offline
* timer is not already running, then restart offline timer.
*/
}
}
/*
* If retry queue is not suspended and some cmds are waiting
* to be retried, then restart the retry timer
*/
}
}
}
static void
{
int status;
"fcsm_cleanup_port: entered"));
/*
* Kill the job thread
*/
/*
* 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.
*/
} else {
}
/* Remove from the fcsm state structure from global linked list */
}
} else {
}
}
/* Free the fcsm state structure */
}
/* ARGSUSED */
static void
{
if (fcsm_detached) {
return;
}
"statec_cb: instance 0x%x not found",
return;
}
"statec_cb: port not attached"));
return;
}
fcsm->sm_cb_count++;
"statec_cb: state <%s>(0x%x) topology <%s>(0x%x) dev_cnt %d",
/*
* Reset the Mgmt server Login flag, so that login is performed again.
*/
switch (port_state) {
case FC_STATE_OFFLINE:
case FC_STATE_RESET:
case FC_STATE_RESET_REQUESTED:
break;
case FC_STATE_ONLINE:
case FC_STATE_LOOP:
case FC_STATE_LIP:
case FC_STATE_LIP_LBIT_SET:
break;
case FC_STATE_NAMESERVICE:
case FC_STATE_DEVICE_CHANGE:
default:
/* Do nothing */
break;
}
/*
* Port is offline.
* Suspend cmd processing and start offline timeout thread.
*/
"statec_cb: schedule offline timeout thread"));
/* Stop the cmd retry thread */
}
} else {
/*
* Port is online.
* Cancel offline timeout thread and resume command processing.
*/
if (fcsm->sm_offline_tid) {
"statec_cb: cancel offline timeout thread"));
}
/* Start retry thread if needed */
}
}
if (offline_tid != NULL) {
(void) untimeout(offline_tid);
}
}
fcsm->sm_cb_count--;
}
static void
{
"offline_timeout"));
}
/* Start the retry thread if needed */
"offline_timeout: reschedule cmd retry thread"));
}
}
/* ARGSUSED */
static int
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
int *rval_p)
{
int retval = 0;
return (ENXIO);
}
/* Allow only root to talk */
"ioctl: end (disallowing underprivileged user)"));
return (EPERM);
}
switch (cmd) {
case FCSMIO_CMD: {
int status;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
break;
}
break;
}
case DDI_MODEL_NONE:
}
break;
}
#else /* _MULTI_DATAMODEL */
break;
}
#endif /* _MULTI_DATAMODEL */
if (!status) {
}
break;
}
default:
break;
}
return (retval);
}
/* ARGSUSED */
static int
{
return (FC_UNCLAIMED);
}
/* ARGSUSED */
static int
{
int retval = 0;
case FCSMIO_CT_CMD: {
break;
}
/*
* Get the destination port for which this ioctl
* is targeted. The abuf will have the fp_minor
* number.
*/
break;
}
if (instance < 0) {
"fciocmd: instance 0x%x, invalid instance",
instance));
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
*/
count = 0;
while (count++ <= 30) {
break;
}
}
if (count == 1) {
"fciocmd: instance 0x%x, "
"wait for port attach", instance));
}
}
if (count > 30) {
"fciocmd: instance 0x%x, port not attached",
instance));
break;
}
break;
}
if (status != FC_SUCCESS) {
}
"fciocmd: cmd 0x%x completion status 0x%x",
break;
}
}
break;
}
case FCSMIO_ADAPTER_LIST: {
int count;
break;
}
break;
}
break;
}
list->numAdapters);
if (count < 0) {
/* Did something go wrong? */
"Error fetching adapter list."));
break;
}
/* Sucess (or short buffer) */
}
break;
}
default:
break;
}
return (retval);
}
static int
{
int status;
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32: {
break;
}
case DDI_MODEL_NONE:
break;
}
#else /* _MULTI_DATAMODEL */
#endif /* _MULTI_DATAMODEL */
return (status);
}
/* ARGSUSED */
static int
{
"fcsm_open: failed. open type 0x%x for minor 0x%x is not "
return (EINVAL);
}
/*
* Allow anybody to open (both root and non-root users).
* Previlege level checks are made on the per ioctl basis.
*/
"fcsm_open: exclusive open of 0x%x failed",
return (EBUSY);
} else {
}
} else {
"fcsm_open: failed. Device minor 0x%x is in "
return (EBUSY);
}
}
return (0);
}
/* ARGSUSED */
static int
{
"fcsm_close: failed. close type 0x%x for minor 0x%x is not "
return (EINVAL);
}
"fcsm_close: failed. minor 0x%x is already closed",
return (ENODEV);
}
return (0);
}
/* ARGSUSED */
static void
{
uint32_t i;
if (dev_cnt == 0) {
return;
}
for (i = 0; i < dev_cnt; i++) {
"list[%d]: ID 0x%x WWN %x:%x:%x:%x:%x:%x:%x:%x "
"state (0x%x) "
"type <%s>(0x%x) "
"flags (0x%x)",
}
}
/* ARGSUSED */
static void
const char *fmt, ...)
{
return;
}
if (fcsm) {
} else {
}
if (pkt) {
" state: %s(0x%x); reason: %s(0x%x)",
}
switch (flags) {
case SM_LOG:
break;
case SM_CONSOLE:
break;
default:
break;
}
}
/*
* Convert FC packet state to FC errno
*/
int
{
int count;
reason == FC_REASON_LOGIN_REQUIRED)) {
return (FC_LOGINREQ);
} else if (state == FC_PKT_PORT_OFFLINE &&
return (FC_LOGINREQ);
}
sizeof (fcsm_xlat_pkt_state[0]); count++) {
}
}
return (FC_FAILURE);
}
/*
* Convert port state state to descriptive string
*/
{
int count;
sizeof (fcsm_xlat_port_state[0]); count++) {
}
}
return (NULL);
}
/*
* Convert port topology state to descriptive string
*/
{
int count;
sizeof (fcsm_xlat_topology[0]); count++) {
}
}
return (NULL);
}
/*
* Convert port topology state to descriptive string
*/
static caddr_t
{
int count;
sizeof (fcsm_xlat_dev_type[0]); count++) {
}
}
return (NULL);
}
static int
{
cmd->cmd_dma_flags = 0;
return (1);
}
return (1);
}
} else {
}
pkt->pkt_data_cookie_cnt = 0;
return (0);
}
/* ARGSUSED */
static void
{
}
}
}
static fcsm_cmd_t *
{
int rval;
"alloc_cmd: kmem_cache_alloc failed"));
return (NULL);
}
cmd->cmd_retry_count = 0;
cmd->cmd_max_retries = 0;
cmd->cmd_retry_interval = 0;
/* Zero out the important fc_packet fields */
pkt->pkt_datalen = 0;
pkt->pkt_action = 0;
pkt->pkt_reason = 0;
/*
* Now that pkt_pd is initialized, we can call fc_ulp_init_packet
*/
!= FC_SUCCESS) {
return (NULL);
}
&pkt->pkt_cmd_acc);
if (rval != DDI_SUCCESS) {
(void) fc_ulp_uninit_packet(
return (NULL);
}
(void) fc_ulp_uninit_packet(
return (NULL);
}
if (rval != DDI_DMA_MAPPED) {
(void) fc_ulp_uninit_packet(
return (NULL);
}
if (pkt->pkt_cmd_cookie_cnt >
(void) fc_ulp_uninit_packet(
return (NULL);
}
(void) fc_ulp_uninit_packet(
return (NULL);
}
*cp = pkt_cookie;
cp++;
*cp = pkt_cookie;
}
} else if (cmd_len != 0) {
}
&pkt->pkt_resp_acc);
if (rval != DDI_SUCCESS) {
(void) fc_ulp_uninit_packet(
return (NULL);
}
(void) fc_ulp_uninit_packet(
return (NULL);
}
if (rval != DDI_DMA_MAPPED) {
(void) fc_ulp_uninit_packet(
return (NULL);
}
if (pkt->pkt_resp_cookie_cnt >
(void) fc_ulp_uninit_packet(
return (NULL);
}
(void) fc_ulp_uninit_packet(
return (NULL);
}
*cp = pkt_cookie;
cp++;
*cp = pkt_cookie;
}
} else if (resp_len != 0) {
}
"alloc_cmd: cmd 0x%p", (void *)cmd));
return (cmd);
}
static void
{
"free_cmd: cmd 0x%p", (void *)cmd));
cmd->cmd_fp_pkt);
}
static void
{
}
}
}
pkt->pkt_cmdlen = 0;
pkt->pkt_rsplen = 0;
pkt->pkt_tran_type = 0;
pkt->pkt_tran_flags = 0;
sizeof (ddi_dma_cookie_t));
}
sizeof (ddi_dma_cookie_t));
}
}
if (pkt->pkt_cmd_acc) {
}
}
}
if (pkt->pkt_resp_acc) {
}
}
cmd->cmd_dma_flags = 0;
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
}
static fcsm_job_t *
{
job->job_priv_flags = 0;
}
return (job);
}
static void
{
}
static void
{
job->job_retry_count = 0;
}
static int
{
int sync;
"process_job: port instance 0x%x not found",
job->job_port_instance));
return (FC_BADDEV);
}
/* Both SYNC and ASYNC flags should not be set */
/*
* 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.
*/
/* Queue the job for processing by job thread */
/* 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.
*/
"process_job: Waiting for sync job <%p> completion",
(void *)job));
}
return (FC_SUCCESS);
}
static void
{
/* Queue the job at the head or tail depending on the job priority */
if (priority_flag) {
"enque_job: job 0x%p is high priority", job));
/* Queue at the head */
} else {
}
} else {
"enque_job: job 0x%p is normal", job));
/* Queue at the tail */
} else {
}
}
/* Signal the job thread to process the job */
}
static int
{
/*
* 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.
*/
case FCSM_JOB_CT_PASSTHRU: {
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.
*/
}
"retry_job: job 0x%p max retries (%d) reached",
break;
}
/*
* Login is required again. Retry the command, so that
* login will get performed again.
*/
job->job_retry_count++;
"retry_job: retry(%d) job 0x%p",
/*
* 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.
*/
if (jobflag & FCSM_JOBFLAG_SERIALIZE) {
/* Signal the job thread to process the job */
}
/* Command is queued for retrying */
return (0);
}
default:
break;
}
return (1);
}
static void
{
/* Job retried. so just return from here */
return;
}
}
}
/* Signal the job thread to process the job */
}
} else {
/* Async job, free the job structure */
}
}
{
} else {
}
}
return (job);
}
/* Dedicated per port thread to process various commands */
static void
{
#ifndef __lock_lint
callb_generic_cpr, "fcsm_job_thread");
#endif /* __lock_lint */
for (;;) {
}
} else {
}
case FCSM_JOB_NONE:
"job_thread: uninitialized job code");
break;
case FCSM_JOB_THREAD_SHUTDOWN:
"job_thread: job code <JOB PORT SHUTDOWN>"));
/*
* There should not be any pending jobs, when this
* is being called.
*/
#ifndef __lock_lint
#endif
/* CPR_EXIT has also dropped the fcsm->sm_mutex */
thread_exit();
/* NOTREACHED */
break;
"job_thread: job code <LOGIN_NAME_SERVER>"));
break;
"job_thread: job code <LOGIN_MGMT_SERVER>"));
break;
case FCSM_JOB_CT_PASSTHRU:
"job_thread: job code <CT_PASSTHRU>"));
break;
default:
"job_thread: job code <UNKNOWN>"));
break;
}
}
/* NOTREACHED */
}
static void
void (*comp_func)())
{
/* Set the pkt d_id properly */
} else {
}
}
static void
{
"ct_intr: CT command <0x%x> to did 0x%x failed",
} else {
/* Get the CT response payload */
}
job->job_result =
}
static void
{
int status;
return;
}
/*
* Process the CT Passthru job only if port is attached
* to a FABRIC.
*/
"job_ct_passthru: end (non-fabric port)"));
return;
}
/*
* If it is NOT a Management Seriver (MS) or Name Server (NS) command
* then complete the command with failure.
*/
/*
* 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.
*/
}
"job_ct_passthru: Management Server Cmd"));
"job_ct_passthru: Name Server Cmd"));
} else {
"job_ct_passthru: Unsupported Destination "
"gs_type <0x%x> gs_subtype <0x%x>",
}
"job_ct_passthru: end (Not a Name Server OR "
"Mgmt Server Cmd)"));
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.
*/
"job_ct_passthru: perform login failed"));
}
return;
}
/*
* We are already logged in to the management server.
* Issue the CT Passthru command
*/
return;
}
"job_ct_passthru: issue CT Passthru failed, status 0x%x",
status));
return;
}
}
static int
{
#ifdef DEBUG
int status;
#endif /* DEBUG */
return (FC_FAILURE);
}
"login_and_process_job: start login."));
/*
* Directory server login completed just now, while the
* mutex was dropped. Just queue the command again for
* processing.
*/
"login_and_process_job: got job 0x%p. login just "
"completed", (void *)orig_job));
return (FC_SUCCESS);
}
/*
* Ideally we shouldn't have come here, since login
* job has the serialize flag set.
* Anyway, put the command back on the queue.
*/
"login_and_process_job: got job 0x%p while login to "
"management server in progress", (void *)orig_job));
return (FC_SUCCESS);
}
/*
* 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.
*/
#ifdef DEBUG
#else /* DEBUG */
#endif /* DEBUG */
return (FC_SUCCESS);
}
/* ARGSUSED */
static void
{
/* Set the login flag in the per port fcsm structure */
/*
* Login failed. Complete the original job with FC_LOGINREQ
* status. Retry of that job will cause login to be
* retried.
*/
"login_ms_comp: Management server login failed: <%s>", msg);
return;
}
/*
* Queue the original job at the head of the queue for processing.
*/
}
static void
{
}
static int
{
int status;
login_params = (la_els_logi_t *)
if (login_params == NULL) {
return (FC_NOMEM);
}
if (status != FC_SUCCESS) {
return (status);
}
return (FC_SUCCESS);
}
static void
{
"xlogi_intr: login to DID 0x%x failed",
} else {
/* Get the Login parameters of the Management Server */
}
job->job_result =
}
static void
{
int status;
return;
}
/*
* Issue the Login command to the management server.
*/
sizeof (la_els_logi_t), KM_SLEEP);
return;
}
if (status != FC_SUCCESS) {
"job_login_mgmt_server: plogi init failed. status 0x%x",
status));
return;
}
"job_ct_passthru: issue login cmd failed, status 0x%x",
status));
return;
}
}
int
{
int status;
if (status != FC_SUCCESS) {
/* Job could not be issued. So free the job and return */
return (status);
}
if (job_flags & FCSM_JOBFLAG_SYNC) {
}
return (status);
}
/* ARGSUSED */
static void
{
"ct_passthru_comp: result 0x%x port 0x%x",
}
static void
{
int jobstatus;
"pkt_common_intr"));
/* Command completed successfully. Just complete the command */
return;
}
"fc packet to DID 0x%x failed for pkt 0x%p",
/*
* No need to retry the command. The link previously
* suffered an offline timeout.
*/
"pkt_common_intr: end. Link is down"));
return;
}
if (jobstatus == FC_LOGINREQ) {
/*
* Login to the destination is required. No need to
* retry this cmd again.
*/
"pkt_common_intr: end. LOGIN required"));
return;
}
case FC_PKT_PORT_OFFLINE:
case FC_PKT_LOCAL_RJT:
case FC_PKT_TIMEOUT: {
if (fcsm_retry_cmd(cmd) != 0) {
"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_reason = 0;
} else {
"pkt_common_intr: retry(%d) on pkt state (0x%x)",
}
break;
}
default:
break;
}
}
static int
{
int status;
/* Explicitly invalidate this field till fcsm decides to use it */
"issue_cmd: entry"));
/*
* 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.
*/
return (FC_OFFLINE);
}
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.
*/
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:
if (fcsm_retry_cmd(cmd) != 0) {
"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_reason = 0;
} else {
"issue_cmd: retry (%d) on fc status (0x%x)",
status = FC_SUCCESS;
}
break;
default:
"issue_cmd: failure status 0x%x", status));
pkt->pkt_reason = 0;
break;
}
}
return (status);
}
static int
{
cmd->cmd_retry_count++;
return (0);
}
return (1);
}
static void
{
if (fcsm->sm_retry_tail) {
} else {
/* Schedule retry thread, if not already running */
"enque_cmd: schedule retry thread"));
}
}
}
static fcsm_cmd_t *
{
} else {
}
}
return (cmd);
}
static void
{
int done = 0;
int linkdown;
/*
* If retry cmd queue is suspended, then go away.
* This retry thread will be restarted, when cmd queue resumes.
*/
/*
* Clear the retry_tid, to indicate that this routine is not
* currently being rescheduled.
*/
"retry_timeout: end. No processing. "
"Queue is currently suspended for this instance"));
return;
}
/*
* Save the curr_tail, so that we only process the commands
* which are in the queue at this time.
*/
/*
* Check for done flag before dequeing the command.
* Dequeing before checking the done flag will cause a command
* to be lost.
*/
done = 1;
}
if (linkdown) {
/*
* No need to retry the command. The link has
* suffered an offline timeout.
*/
continue;
}
if (cmd->cmd_retry_interval <= 0) {
/* Retry the command */
"retry_timeout: issue cmd 0x%p", (void *)cmd));
}
} else {
/*
* Put the command back on the queue. Retry time
* has not yet reached.
*/
"retry_timeout: queue cmd 0x%p", (void *)cmd));
}
}
if (fcsm->sm_retry_head) {
/* Activate timer */
"retry_timeout: retry thread rescheduled"));
} else {
/*
* Reset the tid variable. The first thread which queues the
* command, will restart the timer.
*/
}
}