tpgs.c revision 3c1daac4bec6560b90a1e9046e8279b4aaec609f
/*
* 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.
*/
/*
* Implementation of "scsi_vhci_f_tpgs" T10 standard based failover_ops.
*
* NOTE: for non-sequential devices only.
*/
/* Supported device table entries. */
char *std_dev_table[] = { NULL };
/* Failover module plumbing. */
/*
* max time for failover to complete is 3 minutes. Compute
* number of retries accordingly, to ensure we wait for at least
* 3 minutes
*/
/* ARGSUSED */
static int
void **ctpriv)
{
"!std_device_probe: not a standard tpgs device"));
return (SFO_DEVICE_PROBE_PHCI);
}
"!std_device_probe: Detected a "
"Standard Asymmetric device "
"not yet supported\n"));
return (SFO_DEVICE_PROBE_PHCI);
}
"mode: sd(%p)", (void *) sd));
return (SFO_DEVICE_PROBE_PHCI);
}
"!std_device_probe: Detected a "
"Standard Asymmetric device "
"with implicit failover\n"));
return (SFO_DEVICE_PROBE_VHCI);
}
"!std_device_probe: Detected a "
"Standard Asymmetric device "
"with explicit failover\n"));
return (SFO_DEVICE_PROBE_VHCI);
}
"!std_device_probe: Detected a "
"Standard Asymmetric device "
"which supports both implicit and explicit failover\n"));
return (SFO_DEVICE_PROBE_VHCI);
}
"!std_device_probe: "
return (SFO_DEVICE_PROBE_PHCI);
}
/* ARGSUSED */
static void
{
/*
* For future use
*/
}
/* ARGSUSED */
static int
{
"vhci_tpgs_set_target_groups() call from MPAPI");
return (1);
}
/*
* Process the packet reason of CMD_PKT_CMPLT - return 0 if no
* retry and 1 if a retry should be done
*/
static int
int *retry_cnt)
{
/*
* Re-initialize retry_cmd_cnt. Allow transport and
* cmd errors to go through a full retry count when
* retry count is not exhausted due to CMD_CMPLTs
* delay. This allows the system
* to brave a hick-up on the link at any given time,
* while waiting for the fo to complete.
*/
&(((struct scsi_arq_status *)(uintptr_t)
if (skey == KEY_UNIT_ATTENTION) {
/*
* tpgs access state changed
*/
if (asc == STD_SCSI_ASC_STATE_CHG &&
/* XXX: update path info? */
" state change");
}
return (1);
} else if (skey == KEY_NOT_READY) {
if ((*retry_cnt)++ >=
" failed: timed out waiting "
"for path to become active");
return (0);
}
"!(sd:%p)lun "
"becoming active...\n", (void *)sd));
return (1);
}
" sense key:%x, ASC: %x, "
return (0);
}
case STATUS_GOOD:
break;
case STATUS_CHECK:
"!(sd:%p):"
" status returned CHECK during std"
" path activation", (void *)sd));
return (0);
case STATUS_QFULL:
"status returned QFULL during std "
"path activation for %p\n", (void *)sd));
drv_usecwait(5000);
return (1);
case STATUS_BUSY:
"status returned BUSY during std "
"path activation for %p\n", (void *)sd));
drv_usecwait(5000);
return (1);
default:
"!(sd:%p) Bad status returned during std "
"activation (pkt %p, status %x)",
return (0);
}
return (0);
}
/*
* There is no standby path returned by the dsp and we may have
* to do something different for other devices that use standby
*/
/* ARGSUSED */
static int
void *ctpriv)
{
struct scsi_address *ap;
" failed vhci_tpgs_get_target_fo_mode\n"));
return (1);
}
if ((state == STD_ACTIVE_OPTIMIZED) ||
(state == STD_ACTIVE_NONOPTIMIZED)) {
(void *)sd));
return (0);
}
if (mode == SCSI_EXPLICIT_FAILOVER) {
"!mode is EXPLICIT for %p xlf %x\n",
if (retval != 0) {
"!(sd:%p)std_path_activate failed(1)\n",
(void *)sd));
return (1);
}
} else {
(void *)sd));
}
if (!bp) {
"!(sd:%p)std_path_activate failed to alloc buffer",
(void *)sd));
return (1);
}
if (!pkt) {
"!(sd:%p)std_path_activate failed to initialize packet",
(void *)sd));
return (1);
}
retry_cnt = 0;
retry_cmd_cnt = 0;
if (err != TRAN_ACCEPT) {
/*
* Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
* All other errors are fatal and should not be retried.
*/
(retry_cnt++ < STD_FO_MAX_RETRIES)) {
goto retry;
}
"couldn't transport packet");
return (1);
}
switch (pkt->pkt_reason) {
case CMD_CMPLT:
retry_cmd_cnt = 0;
if (retval != 0) {
goto retry;
}
break;
case CMD_TIMEOUT:
retval = 1;
break;
case CMD_INCOMPLETE:
case CMD_RESET:
case CMD_ABORTED:
case CMD_TRAN_ERR:
/*
* Increased the number of retries when these error
* cases are encountered. Also added a 1 sec wait
* before retrying.
*/
if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
"!Retrying path activation due to "
"pkt reason:%x, retry cnt:%d",
goto retry;
}
/* FALLTHROUGH */
default:
"complete successfully,"
retval = 1;
break;
}
return (retval);
}
/* ARGSUSED */
void *ctpriv)
{
return (0);
}
/* ARGSUSED */
static int
void *ctpriv)
{
" failed vhci_tpgs_get_target_fo_mode\n"));
return (1);
}
if (state == STD_ACTIVE_OPTIMIZED) {
} else if (state == STD_ACTIVE_NONOPTIMIZED) {
} else if (state == STD_STANDBY) {
} else if (state == STD_UNAVAILABLE) {
}
if (preferred) {
} else {
}
"ACTIVE" : "INACTIVE"));
opinfo->opinfo_xlf_capable = 0;
return (0);
}
/* ARGSUSED */
{
/*
* For future use
*/
return (1);
}
/*
* Analyze the sense code to determine whether failover process
*/
/* ARGSUSED */
static int
void *ctpriv)
{
int rval = SCSI_SENSE_UNKNOWN;
if ((skey == KEY_UNIT_ATTENTION) &&
(asc == STD_SCSI_ASC_STATE_CHG) &&
(ascq == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
" sense_key:%x, add_code: %x, qual_code:%x"
} else if ((skey == KEY_NOT_READY) &&
(asc == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
((ascq == STD_TGT_PORT_UNAVAILABLE) ||
(ascq == STD_TGT_PORT_STANDBY))) {
" sense_key:%x, add_code: %x, qual_code:%x"
} else if ((skey == KEY_ILLEGAL_REQUEST) &&
(asc == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
" sense_key:%x, add_code: %x, qual_code:%x"
} else if ((skey == KEY_ILLEGAL_REQUEST) &&
(asc == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
" sense_key:%x, add_code: %x, qual_code:%x"
} else {
/*
* At this point sense data may be for power-on-reset
* UNIT ATTN hardware errors, vendor unqiue sense data etc.
* For all these cases, return SCSI_SENSE_UNKNOWN.
*/
}
return (rval);
}
/* ARGSUSED */
static int
{
/*
* The first phase does not have a standby path so
* there will be no explicit failover - when standard tpgs.
* standard defines preferred flag then we should start
* using this as the selection mechanism - there can be
* preferred primary standby that we should fail to first and then
* nonpreferred secondary standby.
*/
*nxt = PCLASS_PRIMARY;
return (0);
*nxt = PCLASS_SECONDARY;
return (0);
return (ENOENT);
} else {
return (EINVAL);
}
}