asym_sun.c revision 1e1ddd6cc98ab5af8293f7ebd132be62900730fd
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Implementation of "scsi_vhci_f_asym_sun" asymmetric failover_ops.
*
* Note : f_asym_sun method is the same as the one originally used by SUN's
* T3 (Purple) device.
*/
/* Supported device table entries. */
char *purple_dev_table[] = {
/* " 111111" */
/* "012345670123456789012345" */
/* "|-VID--||-----PID------|" */
"SUN T300 ",
"SUN T4 ",
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
*/
/*
* max number of retries for purple failover to complete where the ping
* command is failing due to transport errors or commands being rejected by
* purple.
* PURPLE_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
* purple takes time to complete the failover.
*/
#define PURPLE_FO_MAX_CMD_RETRIES 3
#define T3_SCSI_ASC_FO_IN_PROGRESS 0x90
#define T3_SCSI_ASCQ_PATH_ACT2INACT 0x00
#define T3_SCSI_ASCQ_PATH_INACT2ACT 0x01
#define T3_SCSI_ASC_PATH_INACTIVE 0x04
#define T3_SCSI_ASCQ_PATH_INACTIVE 0x88
/* ARGSUSED */
static int
void **ctpriv)
{
char **dt;
continue;
/* match */
if (mode == SCSI_EXPLICIT_FAILOVER)
return (SFO_DEVICE_PROBE_VHCI);
else
return (SFO_DEVICE_PROBE_PHCI);
}
return (SFO_DEVICE_PROBE_PHCI);
}
/* ARGSUSED */
static void
{
/*
* For future use
*/
}
/* ARGSUSED */
static void
int *ownership, int *xlf_capable)
{
int retval = 0;
struct scsi_address *ap;
return;
return;
}
if (retval == 0) {
" and ownership info\n", (void *)sd));
return;
}
" implicit mode\n", (void *)sd));
*ownership = 0;
return;
}
*ownership = 0;
*ownership = 1;
*xlf_capable = 1;
} else {
*xlf_capable = 0;
}
}
static int
{
char cdb[CDB_GROUP1];
struct scsi_address *ap;
int retval;
return (0);
if (xlf_capable) {
/*
* Bit 2/1: 1/0: implicitly drop any reservation
* Bit 0: Grab bit - 1 means an explicit failover will be
* triggered
*/
} else {
}
return (retval);
}
/* ARGSUSED */
static int
void *ctpriv)
{
struct scsi_address *ap;
struct scsi_extended_sense *sns;
if (ownership == 1) {
(void *)sd));
return (0);
}
if (mode != SCSI_IMPLICIT_FAILOVER) {
"!mode is EXPLICIT for 0x%p xlf %x\n",
if (retval == 0) {
"!(sd:%p)purple_path_activate failed(1)\n",
(void *)sd));
return (1);
}
} else {
(void *)sd));
}
if (!bp) {
"activation");
return (1);
}
if (!pkt) {
"path activation");
return (1);
}
retry_cnt = 0;
retry_cmd_cnt = 0;
if (err != TRAN_ACCEPT) {
/*
* Retry TRAN_BUSY till PURPLE_FO_MAX_RETRIES is exhausted.
* All other errors are fatal and should not be retried.
*/
(retry_cnt++ < PURPLE_FO_MAX_RETRIES)) {
goto retry;
}
"couldn't transport packet");
return (1);
}
switch (pkt->pkt_reason) {
case CMD_TIMEOUT:
return (1);
case CMD_CMPLT:
/*
* 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 for a T3 fo to finish. This allows the system
* to brave a hick-up on the link at any given time,
* while waiting for the fo to complete.
*/
retry_cmd_cnt = 0;
/*
* swallow unit attention
*/
goto retry;
(sns->es_add_code ==
(sns->es_qual_code ==
if (retry_cnt++ >=
" failed: timed out "
"waiting for path to "
"become active");
return (1);
}
"!(sd:%p)lun becoming active...\n",
(void *)sd));
goto retry;
}
" sense key:%x, ASC: %x, "
return (1);
}
case STATUS_GOOD:
break;
case STATUS_CHECK:
"!(sd:%p)T3:"
" cont allegiance during purple "
"activation", (void *)sd));
return (1);
case STATUS_QFULL:
"status returned during purple "
"path activation for 0x%p\n",
(void *)sd));
drv_usecwait(5000);
goto retry;
case STATUS_BUSY:
"status returned during purple "
"path activation for 0x%p\n",
(void *)sd));
drv_usecwait(5000);
goto retry;
default:
"!(sd:%p) Bad status "
"returned during purple "
"activation (pkt 0x%p, "
"status %x)",
return (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++ < PURPLE_FO_MAX_CMD_RETRIES) {
"!Retrying T3 path activation due to "
"pkt reason:%x, retry cnt:%d",
goto retry;
}
/* FALLTHROUGH */
default:
"complete successfully,"
return (1);
}
return (0);
}
/* ARGSUSED */
void *ctpriv)
{
return (0);
}
/* ARGSUSED */
static int
{
struct scsi_inquiry *inq;
struct scsi_address *ap;
if (!bp)
return (1);
if (!pkt) {
return (1);
}
SCMD_INQUIRY, 0, SUN_INQSIZE, 0);
if (retval == 0) {
return (1);
}
/*
* Ignore to check inquiry dual port bit.
* T3 can return this bit as 0 when one of its controller goes down.
* Instead relying on inquiry port bit only.
*/
} else {
}
if (ownership == 1)
else
return (0);
}
/* ARGSUSED */
{
/*
* For future use
*/
return (1);
}
/* ARGSUSED */
static int
{
return (SCSI_SENSE_INACT2ACT);
else if (sense->es_qual_code ==
return (SCSI_SENSE_ACT2INACT);
return (SCSI_SENSE_INACTIVE);
}
}
/*
* At this point sense data may be for power-on-reset UNIT ATTN or
* hardware errors, vendor unique sense data etc. For all these cases
* return SCSI_SENSE_UNKNOWN.
*/
return (SCSI_SENSE_UNKNOWN);
}
/* ARGSUSED */
static int
{
*nxt = PCLASS_PRIMARY;
return (0);
*nxt = PCLASS_SECONDARY;
return (0);
return (ENOENT);
} else {
return (EINVAL);
}
}