changeset 6941:c5d83acec1bc

6459247 scsi_vhci does not return correct value for dma-max property 6475502 device specific load-balance setting not displayed by mpathadm 6621014 Support multipathing to tape devices 6646241 Error in MTFSR using MTIOCLTOP to space 5000000010 blocks 6683730 st is not warlock clean 6684754 scsi_vhci pgr out getting 4B 90 FCP_DL field not sufficient to complete the transfer 6693116 st when reading to EOD where there is no filemark at EOD system asserts as blkno > lgclblkno 6693591 Recovery of write command is issuing Read Position with invalid bit set 6695072 st uscsi write of zero bytes hits assert nblks != 0 6695099 st uscsi read/write that gets error returned asserts new_lastop != ST_OP_NIL 6695128 st_recov_cb does not handle TRAN_ERR status. 6698800 Driver does not recover jammed load command 6704414 read errors encountered during fault test
author rralphs
date Mon, 23 Jun 2008 13:41:43 -0700
parents f90064616a20
children 1b63ec2399bd
files usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c usr/src/pkgdefs/SUNWckr/prototype_i386 usr/src/pkgdefs/SUNWckr/prototype_sparc usr/src/pkgdefs/common_files/i.scsivhciconf usr/src/uts/common/Makefile.files usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/sym_hds.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tape.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/mpapi_impl.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.conf usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c usr/src/uts/common/io/scsi/targets/st.c usr/src/uts/common/sys/scsi/adapters/scsi_vhci_tpgs.h usr/src/uts/common/sys/scsi/generic/commands.h usr/src/uts/common/sys/scsi/generic/inquiry.h usr/src/uts/common/sys/scsi/generic/status.h usr/src/uts/common/sys/scsi/targets/stdef.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/scsi_vhci_f_tape/Makefile usr/src/uts/intel/scsi_vhci_f_tpgs_tape/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/scsi_vhci_f_tape/Makefile usr/src/uts/sparc/scsi_vhci_f_tpgs_tape/Makefile
diffstat 25 files changed, 2564 insertions(+), 1174 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c	Mon Jun 23 13:41:43 2008 -0700
@@ -134,10 +134,8 @@
 			 * As per SPC-3, Revision 23, Section 6.23
 			 */
 			switch ((cdb[1] & 0x1f)) {
-				/* SCMD_REPORT_SUPPORTED_OPERATION_CODES */
-				case 0x0c:
-				/* SCMD_REPORT_SUPPORTED_MANAGEMENT_FUNCTIONS */
-				case 0x0d:
+				case SSVC_ACTION_GET_SUPPORTED_OPERATIONS:
+				case SSVC_SCTION_GET_SUPPORTED_MANAGEMENT:
 
 					conflict = True;
 					break;
@@ -149,10 +147,10 @@
 			 * SPC-3, Revision 23, Section 6.29
 			 */
 			switch ((cdb[1] & 0x1F)) {
-				case SCMD_SET_DEVICE_IDENTIFIER:
-				case SCMD_SET_PRIORITY:
-				case SCMD_SET_TARGET_PORT_GROUPS:
-				case SCMD_SET_TIMESTAMP:
+				case SSVC_ACTION_SET_DEVICE_IDENTIFIER:
+				case SSVC_ACTION_SET_PRIORITY:
+				case SSVC_ACTION_SET_TARGET_PORT_GROUPS:
+				case SSVC_ACTION_SET_TIMESTAMP:
 				conflict = True;
 				break;
 			}
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Mon Jun 23 13:41:43 2008 -0700
@@ -182,6 +182,8 @@
 f none kernel/misc/scsi_vhci/scsi_vhci_f_sym_emc 755 root sys
 f none kernel/misc/scsi_vhci/scsi_vhci_f_sym_hds 755 root sys
 f none kernel/misc/scsi_vhci/scsi_vhci_f_tpgs 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_tape 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_tpgs_tape 755 root sys
 f none kernel/misc/acpica 755 root sys
 f none kernel/misc/agpmaster 755 root sys
 f none kernel/misc/bignum 755 root sys
@@ -385,6 +387,8 @@
 f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym_emc 755 root sys
 f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym_hds 755 root sys
 f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tpgs 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tape 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tpgs_tape 755 root sys
 
 d none kernel/misc/amd64 755 root sys
 f none kernel/misc/amd64/acpica 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Mon Jun 23 13:41:43 2008 -0700
@@ -179,6 +179,8 @@
 f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_sym_emc 755 root sys
 f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_sym_hds 755 root sys
 f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_tpgs 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_tape 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_tpgs_tape 755 root sys
 d none kernel/misc/sparcv9 755 root sys
 f none kernel/misc/sparcv9/busra 755 root sys
 f none kernel/misc/sparcv9/cardbus 755 root sys
--- a/usr/src/pkgdefs/common_files/i.scsivhciconf	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/pkgdefs/common_files/i.scsivhciconf	Mon Jun 23 13:41:43 2008 -0700
@@ -136,6 +136,8 @@
 	"misc/scsi_vhci/scsi_vhci_f_sym_emc",
 	"misc/scsi_vhci/scsi_vhci_f_sym_hds",
 	"misc/scsi_vhci/scsi_vhci_f_sym",
+#	"misc/scsi_vhci/scsi_vhci_f_tpgs_tape",
+#	"misc/scsi_vhci/scsi_vhci_f_tape",
 	"misc/scsi_vhci/scsi_vhci_f_tpgs";
 
 #
--- a/usr/src/uts/common/Makefile.files	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/Makefile.files	Mon Jun 23 13:41:43 2008 -0700
@@ -676,7 +676,7 @@
 		scsi_resource.o scsi_subr.o scsi_transport.o scsi_watch.o \
 		sas_transport.o
 
-SCSI_VHCI_OBJS +=		scsi_vhci.o mpapi_impl.o
+SCSI_VHCI_OBJS +=		scsi_vhci.o mpapi_impl.o scsi_vhci_tpgs.o
 
 SCSI_VHCI_F_SYM_OBJS +=		sym.o
 
@@ -686,6 +686,10 @@
 
 SCSI_VHCI_F_SYM_HDS_OBJS += 	sym_hds.o
 
+SCSI_VHCI_F_TAPE_OBJS +=	tape.o
+
+SCSI_VHCI_F_TPGS_TAPE_OBJS +=	tpgs_tape.o
+
 SGEN_OBJS +=	sgen.o
 
 SMP_OBJS +=	smp.o
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/sym_hds.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/sym_hds.c	Mon Jun 23 13:41:43 2008 -0700
@@ -82,7 +82,7 @@
 };
 
 static struct modlmisc modlmisc = {
-	&mod_miscops, "f_sym_hds 1.1"
+	&mod_miscops, "f_sym_hds %I%"
 };
 
 static struct modlinkage modlinkage = {
@@ -142,7 +142,7 @@
 /* ARGSUSED */
 static int
 hds_sym_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
-void **ctpriv)
+    void **ctpriv)
 {
 	char	**dt;
 	char	*dftype;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tape.c	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,204 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implementation of "scsi_vhci_f_tape" tape failover_ops.
+ *
+ * This file was historically meant for only tape implementation.  It has
+ * been extended to manage SUN "supported" tape controllers. The supported
+ * VID/PID shall be listed in the tape_dev_table.
+ */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+
+/* Supported device table entries.  */
+static char *tape_dev_table[] = {
+/*	"                  111111" */
+/*	"012345670123456789012345" */
+/*	"|-VID--||-----PID------|" */
+	"IBM     ULTRIUM-TD3",
+	"IBM     ULTRIUM-TD4",
+	NULL
+};
+
+/* Failover module plumbing. */
+SCSI_FAILOVER_OP("f_tape", tape, "%I%");
+
+/* ARGSUSED */
+static int
+tape_device_probe(struct scsi_device *sd, struct scsi_inquiry *inquiry,
+    void **ctpriv)
+{
+	int i;
+	int rval = SFO_DEVICE_PROBE_PHCI;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "!tape_device_probe: inquiry string %s\n",
+	    inquiry->inq_vid));
+	/*
+	 * See if this a device type that we want to care about.
+	 */
+	switch (inquiry->inq_dtype & DTYPE_MASK) {
+	case DTYPE_SEQUENTIAL:
+		break;
+	case DTYPE_CHANGER:
+		rval = SFO_DEVICE_PROBE_VHCI;
+		goto done;
+	default:
+		/*
+		 * Not interested.
+		 */
+		return (rval);
+	}
+
+	/*
+	 * If it reports that it supports tpgs and it got here it may have
+	 * failed the tpgs commands. It might or might not support dual port
+	 * but we'll go with it.
+	 */
+	if (inquiry->inq_tpgs) {
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!has tpgs bits: %s\n",
+		    inquiry->inq_vid));
+		rval = SFO_DEVICE_PROBE_VHCI;
+	} else if (inquiry->inq_dualp) {
+		/*
+		 * Looks like it claims to have more then one port.
+		 */
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!has dual port bits: %s\n",
+		    inquiry->inq_vid));
+		rval = SFO_DEVICE_PROBE_VHCI;
+	} else {
+
+		/*
+		 * See if this device is on the list.
+		 */
+		for (i = 0; tape_dev_table[i]; i++) {
+
+			if (strncmp(inquiry->inq_vid, tape_dev_table[i],
+			    strlen(tape_dev_table[i])) == 0) {
+				VHCI_DEBUG(6, (CE_NOTE, NULL,
+				    "!was on the list: %s\n",
+				    inquiry->inq_vid));
+				rval = SFO_DEVICE_PROBE_VHCI;
+				break;
+			}
+		}
+	}
+done:
+	if (rval == SFO_DEVICE_PROBE_VHCI) {
+		if (mdi_set_lb_policy(sd->sd_dev, LOAD_BALANCE_NONE) !=
+		    MDI_SUCCESS) {
+			VHCI_DEBUG(6, (CE_NOTE, NULL, "!fail load balance none"
+			    ": %s\n", inquiry->inq_vid));
+			return (SFO_DEVICE_PROBE_PHCI);
+		}
+
+	}
+	return (rval);
+}
+
+/* ARGSUSED */
+static void
+tape_device_unprobe(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * NO OP for tape.
+	 */
+
+}
+
+/* ARGSUSED */
+static int
+tape_path_activate(struct scsi_device *sd, char *pathclass, void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+tape_path_deactivate(struct scsi_device *sd, char *pathclass, void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+tape_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
+    void *ctpriv)
+{
+	opinfo->opinfo_rev = OPINFO_REV;
+	(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
+	opinfo->opinfo_path_state  = SCSI_PATH_ACTIVE;
+	opinfo->opinfo_pswtch_best = 0;		/* N/A */
+	opinfo->opinfo_pswtch_worst = 0;	/* N/A */
+	opinfo->opinfo_xlf_capable = 0;
+	opinfo->opinfo_mode = SCSI_NO_FAILOVER;
+	opinfo->opinfo_preferred = 1;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+tape_path_ping(struct scsi_device *sd, void *ctpriv)
+{
+	return (1);
+}
+
+/* ARGSUSED */
+static int
+tape_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense *sense,
+    void *ctpriv)
+{
+	if (sense->es_key == KEY_ABORTED_COMMAND &&
+	    sense->es_add_code == 0x4b &&
+	    sense->es_qual_code == 0x83) {
+		return (SCSI_SENSE_INACTIVE);
+	}
+	if (sense->es_key == KEY_NOT_READY &&
+	    sense->es_add_code == 0x4 &&
+	    sense->es_qual_code == 0x1) {
+		return (SCSI_SENSE_NOT_READY);
+	}
+	return (SCSI_SENSE_NOFAILOVER);
+}
+
+/* ARGSUSED */
+static int
+tape_pathclass_next(char *cur, char **nxt, void *ctpriv)
+{
+	if (cur == NULL) {
+		*nxt = PCLASS_PRIMARY;
+		return (0);
+	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
+		return (ENOENT);
+	}
+	return (EINVAL);
+}
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c	Mon Jun 23 13:41:43 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
@@ -36,6 +36,7 @@
 #include <sys/sunddi.h>
 #include <sys/scsi/scsi.h>
 #include <sys/scsi/adapters/scsi_vhci.h>
+#include <sys/scsi/adapters/scsi_vhci_tpgs.h>
 
 /* Supported device table entries.  */
 char	*std_dev_table[] = { NULL };
@@ -43,8 +44,6 @@
 /* Failover module plumbing. */
 SCSI_FAILOVER_OP(SFO_NAME_TPGS, std, "%I%");
 
-#define	STD_SCSI_CMD_LEN 0xff
-
 #define	STD_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
 #define	STD_FO_RETRY_DELAY	2000000 /* 2 seconds */
 /*
@@ -54,68 +53,24 @@
  */
 #define	STD_FO_MAX_RETRIES	(3*60*1000000)/STD_FO_RETRY_DELAY
 
-/*
- * max number of retries for std failover to complete where the ping
- * command is failing due to transport errors or commands being rejected by
- * std.
- * STD_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
- * std takes time to complete the failover.
- */
-#define	STD_FO_MAX_CMD_RETRIES	3
-
-#define	STD_ACTIVE_OPTIMIZED    0x0
-#define	STD_ACTIVE_NONOPTIMIZED 0x1
-#define	STD_STANDBY		0x2
-#define	STD_UNAVAILABLE		0x3
-#define	STD_TRANSITIONING	0xf
-
-#define	STD_SCSI_ASC_STATE_TRANS	0x04
-#define	STD_SCSI_ASCQ_STATE_TRANS_FAIL  0x0A
-#define	STD_SCSI_ASC_STATE_CHG		0x2A
-#define	STD_SCSI_ASCQ_STATE_CHG_SUCC	0x06
-#define	STD_SCSI_ASCQ_STATE_CHG_FAILED	0x07
-#define	STD_SCSI_ASC_INVAL_PARAM_LIST	0x26
-#define	STD_SCSI_ASC_INVAL_CMD_OPCODE	0x20
-#define	STD_LOGICAL_UNIT_NOT_ACCESSIBLE	0x04
-#define	STD_TGT_PORT_UNAVAILABLE	0x0C
-
-
-/* Special exported for direct use by MP-API */
-int std_set_target_groups(struct scsi_address *, int, int);
-
-/*
- * External function definitions
- */
-extern void vhci_mpapi_update_tpg_data(struct scsi_address *, char *);
-
-static int std_get_fo_mode(struct scsi_device *,
-		int *, int *, int *, int *);
-static int std_report_target_groups(struct scsi_address *, struct buf *,
-		int, int, int *, int *);
 
 /* ARGSUSED */
 static int
 std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
-void **ctpriv)
+    void **ctpriv)
 {
-	unsigned int	tpgs_bits;
-	unsigned char	*inqbuf = (unsigned char *)inq;
-	unsigned char	dtype = (inq->inq_dtype & DTYPE_MASK);
-
 	int		mode, state, xlf, preferred = 0;
 
 	VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n",
 	    inq->inq_vid));
 
-	tpgs_bits = ((inqbuf[5] & 0x30) >> 4);
-
-	if (tpgs_bits == 0) {
+	if (inq->inq_tpgs == 0) {
 		VHCI_DEBUG(4, (CE_WARN, NULL,
 		    "!std_device_probe: not a standard tpgs device"));
 		return (SFO_DEVICE_PROBE_PHCI);
 	}
 
-	if (dtype == DTYPE_SEQUENTIAL) {
+	if (inq->inq_dtype == DTYPE_SEQUENTIAL) {
 		VHCI_DEBUG(4, (CE_NOTE, NULL,
 		    "!std_device_probe: Detected a "
 		    "Standard Asymmetric device "
@@ -123,27 +78,27 @@
 		return (SFO_DEVICE_PROBE_PHCI);
 	}
 
-	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
+	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
 		VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
 		    "mode: sd(%p)", (void *) sd));
 		return (SFO_DEVICE_PROBE_PHCI);
 	}
 
-	if (tpgs_bits == SCSI_IMPLICIT_FAILOVER) {
+	if (inq->inq_tpgs == SCSI_IMPLICIT_FAILOVER) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL,
 		    "!std_device_probe: Detected a "
 		    "Standard Asymmetric device "
 		    "with implicit failover\n"));
 		return (SFO_DEVICE_PROBE_VHCI);
 	}
-	if (tpgs_bits == SCSI_EXPLICIT_FAILOVER) {
+	if (inq->inq_tpgs == SCSI_EXPLICIT_FAILOVER) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL,
 		    "!std_device_probe: Detected a "
 		    "Standard Asymmetric device "
 		    "with explicit failover\n"));
 		return (SFO_DEVICE_PROBE_VHCI);
 	}
-	if (tpgs_bits == SCSI_BOTH_FAILOVER) {
+	if (inq->inq_tpgs == SCSI_BOTH_FAILOVER) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL,
 		    "!std_device_probe: Detected a "
 		    "Standard Asymmetric device "
@@ -152,7 +107,7 @@
 	}
 	VHCI_DEBUG(1, (CE_WARN, NULL,
 	    "!std_device_probe: "
-	    "Unknown tpgs_bits: %x", tpgs_bits));
+	    "Unknown tpgs_bits: %x", inq->inq_tpgs));
 	return (SFO_DEVICE_PROBE_PHCI);
 }
 
@@ -165,412 +120,12 @@
 	 */
 }
 
-static int
-std_inquiry(struct scsi_address *ap, struct buf *bp, int *mode)
-{
-	struct scsi_pkt		*pkt;
-	char			buf[STD_SCSI_CMD_LEN];
-	int			buf_size = sizeof (buf);
-	unsigned int		tpgs_bits;
-	int			retval;
-
-	*mode = 0;
-	bp->b_un.b_addr = (caddr_t)&buf;
-	bp->b_flags = B_READ;
-	bp->b_bcount = buf_size;
-	bp->b_resid = 0;
-
-	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
-	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
-	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
-	pkt->pkt_cdbp[4] = (unsigned char)buf_size;
-	pkt->pkt_time = 60;
-
-	retval = vhci_do_scsi_cmd(pkt);
-	scsi_destroy_pkt(pkt);
-	if (retval == 0) {
-		VHCI_DEBUG(1, (CE_WARN, NULL,
-		    "!std_inquiry: Failure returned from vhci_do_scsi_cmd"));
-		return (1);
-	}
-
-	tpgs_bits = ((buf[5] & 0x30) >> 4);
-	if (tpgs_bits == 0) {
-		VHCI_DEBUG(1, (CE_WARN, NULL,
-		    "!std_inquiry: zero tpgs_bits"));
-		return (1);
-	}
-	retval = 0;
-	if (tpgs_bits == SCSI_IMPLICIT_FAILOVER) {
-		*mode = SCSI_IMPLICIT_FAILOVER;
-	} else if (tpgs_bits == SCSI_EXPLICIT_FAILOVER) {
-		*mode = SCSI_EXPLICIT_FAILOVER;
-	} else if (tpgs_bits == SCSI_BOTH_FAILOVER) {
-		*mode = SCSI_BOTH_FAILOVER;
-	} else {
-		VHCI_DEBUG(1, (CE_WARN, NULL,
-		    "!std_inquiry: Illegal mode returned: %x mode: %x",
-		    tpgs_bits, *mode));
-		retval = 1;
-	}
-
-	return (retval);
-}
-
-static int
-std_page83(struct scsi_address *ap, struct buf *bp,
-	int *rel_tgt_port, int *tgt_port, int *lu)
-{
-	char			*ptr, *end;
-	struct scsi_pkt		*pkt;
-	char			*bufp;
-	unsigned int		buf_len, rx_bsize;
-
-	/*
-	 * lets start the buf size with 512 bytes. If this
-	 * if found to be insufficient, we can allocate
-	 * appropriate size in the next iteration.
-	 */
-	buf_len = 512;
-
-once_again:
-	bufp = kmem_zalloc(buf_len, KM_NOSLEEP);
-	if (bufp == NULL) {
-		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_page83: "
-		    "request packet allocation for %d failed....",
-		    buf_len));
-		return (1);
-	}
-
-
-	bp->b_un.b_addr = bufp;
-	bp->b_flags = B_READ;
-	bp->b_bcount = buf_len;
-	bp->b_resid = 0;
-
-	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
-	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
-	if (pkt == NULL) {
-		VHCI_DEBUG(1, (CE_WARN, NULL,
-		    "!std_page83: Failure returned from scsi_init_pkt"));
-		kmem_free((void *)bufp, buf_len);
-		return (1);
-	}
-
-	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
-	pkt->pkt_cdbp[1] = 0x1;
-	pkt->pkt_cdbp[2] = 0x83;
-	pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff);
-	pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff);
-	pkt->pkt_time = 90;
-
-	if (vhci_do_scsi_cmd(pkt) == 0) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL,
-		    "!std_page83: vhci_do_scsi_cmd failed\n"));
-		kmem_free((void *)bufp, buf_len);
-		scsi_destroy_pkt(pkt);
-		return (1);
-	}
-
-	/*
-	 * Now lets check if the size that was provided was
-	 * sufficient. If not, allocate the appropriate size
-	 * and retry the command again.
-	 */
-	rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff));
-	rx_bsize += 4;
-	if (rx_bsize > buf_len) {
-		/*
-		 * Need to allocate more buf and retry again
-		 */
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_page83: "
-		    "bufsize: %d greater than allocated buf: %d\n",
-		    rx_bsize, buf_len));
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n",
-		    rx_bsize));
-		kmem_free((void *)bufp, buf_len);
-		buf_len = (unsigned int)(rx_bsize);
-		goto once_again;
-	}
-
-	ptr = bufp;
-	ptr += 4; /* identification descriptor 0 */
-	end = bufp + rx_bsize;
-	while (ptr < end) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "std_page83: desc[1/4/5/6/7]:"
-		    "%x %x %x %x %x\n",
-		    ptr[1], ptr[4], ptr[5], ptr[6], ptr[7]));
-		if ((ptr[1] & 0x0f) == 0x04) {
-			*rel_tgt_port = 0;
-			*rel_tgt_port |= ((ptr[6] & 0xff) << 8);
-			*rel_tgt_port |= (ptr[7] & 0xff);
-			VHCI_DEBUG(1, (CE_NOTE, NULL,
-			    "!std_page83: relative target port: %x\n",
-			    *rel_tgt_port));
-		} else if ((ptr[1] & 0x0f) == 0x05) {
-			*tgt_port = 0;
-			*tgt_port = ((ptr[6] & 0xff) << 8);
-			*tgt_port |= (ptr[7] & 0xff);
-			VHCI_DEBUG(1, (CE_NOTE, NULL,
-			    "!std_page83: target port: %x\n", *tgt_port));
-		} else if ((ptr[1] & 0x0f) == 0x06) {
-			*lu = 0;
-			*lu |= ((ptr[6] & 0xff)<< 8);
-			*lu |= (ptr[7] & 0xff);
-			VHCI_DEBUG(1, (CE_NOTE, NULL,
-			    "!std_page83: logical unit: %x\n", *lu));
-		}
-		ptr += ptr[3] + 4;  /* next identification descriptor */
-	}
-	kmem_free((void *)bufp, buf_len);
-	scsi_destroy_pkt(pkt);
-	return (0);
-}
-
-#ifdef DEBUG
-static void
-print_buf(char *buf, int buf_size)
-{
-	int		i = 0, j;
-	int		loop, left;
-
-	loop = buf_size / 8;
-	left = buf_size % 8;
-
-	VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x",
-	    buf_size, loop, left));
-
-	for (j = 0; j < loop; j++) {
-		VHCI_DEBUG(4, (CE_NOTE, NULL,
-		    "!buf[%d-%d]: %x %x %x %x %x %x %x %x",
-		    i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3],
-		    buf[i+4], buf[i+5], buf[i+6], buf[i+7]));
-		i += 8;
-	}
-
-	if (left) {
-		VHCI_DEBUG(4, (CE_CONT, NULL,
-		    "NOTICE: buf[%d-%d]:", i, i + left));
-		for (j = 0; j < left; j++) {
-			VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j]));
-		}
-		VHCI_DEBUG(4, (CE_CONT, NULL, "\n"));
-	}
-}
-#endif
-
-static int
-std_report_target_groups(struct scsi_address *ap, struct buf *bp,
-	int rel_tgt_port, int tgt_port, int *pstate, int *preferred)
-{
-	struct scsi_pkt		*pkt;
-	char			*ptr, *end, *bufp, *mpapi_ptr;
-	unsigned int		rtpg_len = 0;
-	unsigned int		l_tgt_port = 0, tpgs_state = 0;
-	unsigned int		tgt_port_cnt = 0, lr_tgt_port = 0;
-	int			i, len;
-
-	/*
-	 * Start with buffer size of 512.
-	 * If this is found to be insufficient, required size
-	 * will be allocated and the command will be retried.
-	 */
-	len = 512;
-
-try_again:
-	bufp = kmem_zalloc(len, KM_NOSLEEP);
-	if (bufp == NULL) {
-		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_report_target_groups: "
-		    "request packet allocation for %d failed....",
-		    len));
-		return (1);
-	}
-
-	bp->b_un.b_addr = bufp;
-	bp->b_flags = B_READ;
-	bp->b_bcount = len;
-	bp->b_resid = 0;
-
-	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
-	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
-
-	if (pkt == NULL) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL,
-		    "!std_report_target_groups: scsi_init_pkt error\n"));
-		kmem_free((void *)bufp, len);
-		return (1);
-	}
-
-	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_IN;
-	pkt->pkt_cdbp[1] = SCMD_SET_TARGET_PORT_GROUPS;
-	pkt->pkt_cdbp[6] = ((len >>  24) & 0xff);
-	pkt->pkt_cdbp[7] = ((len >> 16) & 0xff);
-	pkt->pkt_cdbp[8] = ((len >> 8) & 0xff);
-	pkt->pkt_cdbp[9] = len & 0xff;
-	pkt->pkt_time = 90;
-
-	VHCI_DEBUG(6, (CE_NOTE, NULL,
-	    "!std_report_target_groups: sending target port group:"
-	    " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6],
-	    pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
-	if (vhci_do_scsi_cmd(pkt) == 0) {
-		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_target_groups:"
-		    " vhci_do_scsi_cmd failed\n"));
-		kmem_free((void *)bufp, len);
-		scsi_destroy_pkt(pkt);
-		return (1);
-	}
-	ptr = bufp;
-	VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_report_target_groups:"
-	    " returned from target"
-	    " port group: buf[0/1/2/3]: %x/%x/%x/%x\n",
-	    ptr[0], ptr[1], ptr[2], ptr[3]));
-	rtpg_len = (unsigned int)((0xff & ptr[0]) << 24);
-	rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16);
-	rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8);
-	rtpg_len |= (unsigned int)(0xff & ptr[3]);
-	rtpg_len += 4;
-	if (rtpg_len > len) {
-		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_target_groups: "
-		    "bufsize: %d greater than allocated buf: %d\n",
-		    rtpg_len, len));
-		VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n",
-		    rtpg_len));
-		kmem_free((void *)bufp, len);
-		len = (unsigned int)(rtpg_len + 1);
-		goto try_again;
-	}
-#ifdef DEBUG
-	print_buf(bufp, rtpg_len);
-#endif
-	end = ptr + rtpg_len;
-	ptr += 4;
-	while (ptr < end) {
-		mpapi_ptr = ptr;
-		l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff);
-		tpgs_state = ptr[0] & 0x0f;
-		tgt_port_cnt = (ptr[7] & 0xff);
-		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_tgt_groups:"
-		    " tpgs state: %x"
-		    " tgt_group: %x count: %x\n", tpgs_state,
-		    l_tgt_port, tgt_port_cnt));
-		ptr += 8;
-		for (i = 0; i < tgt_port_cnt; i++) {
-			lr_tgt_port = 0;
-			lr_tgt_port |= ((ptr[2] & 0Xff) << 8);
-			lr_tgt_port |= (ptr[3] & 0xff);
-
-			if ((lr_tgt_port == rel_tgt_port) &&
-			    (l_tgt_port == tgt_port)) {
-				VHCI_DEBUG(4, (CE_NOTE, NULL,
-				    "!std_report_tgt_groups:"
-				    " found tgt_port: %x rel_tgt_port:%x"
-				    " tpgs_state: %x\n", tgt_port, rel_tgt_port,
-				    tpgs_state));
-				/*
-				 * once we have the preferred flag
-				 * and a non-optimized state flag
-				 * we will get preferred flag  from the
-				 * report target groups
-				 */
-				if (tpgs_state == STD_ACTIVE_OPTIMIZED) {
-					*pstate = STD_ACTIVE_OPTIMIZED;
-					*preferred = PCLASS_PREFERRED;
-				} else if (tpgs_state ==
-				    STD_ACTIVE_NONOPTIMIZED) {
-					*pstate = STD_ACTIVE_NONOPTIMIZED;
-					*preferred = PCLASS_NONPREFERRED;
-				} else if (tpgs_state == STD_STANDBY) {
-					*pstate = STD_STANDBY;
-					*preferred = PCLASS_NONPREFERRED;
-				} else {
-					*pstate = STD_UNAVAILABLE;
-					*preferred = PCLASS_NONPREFERRED;
-				}
-				vhci_mpapi_update_tpg_data(ap, mpapi_ptr);
-				kmem_free((void *)bufp, len);
-				scsi_destroy_pkt(pkt);
-				return (0);
-			}
-			VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_report_tgt_groups:"
-			    " tgt_port: %x rel_tgt_port:%x\n", tgt_port,
-			    rel_tgt_port));
-			ptr += 4;
-		}
-	}
-	*pstate = SCSI_PATH_INACTIVE;
-	*preferred = PCLASS_NONPREFERRED;
-	VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_report_tgt_groups: "
-	    "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x "
-	    "preferred: %d\n", *pstate, *preferred));
-	kmem_free((void *)bufp, len);
-	scsi_destroy_pkt(pkt);
-	return (1);
-}
-
-/*
- * get the failover mode, ownership and if it has extended failover
- * capability. The mode(bits5-4/byte5) is defined as implicit, explicit, or
- * both.  The state is defined as online-optimized(0h),
- * online-nonoptimized(1h), standby(2h), offline(3h),
- * and transitioning(fh). Currently, there is online,
- * standby, and offline(defined in sunmdi.h).
- * Online-nonoptimized will be a mode of secondary
- * and an ownership of online. Thought about using a different mode but
- * it appears the states are really for the states for secondary mode.
- * We currently have IS_ONLINING, IS_OFFLINING - should we have TRANSITIONING
- * to mean from online-optimized to online-nonoptimized or does onlining
- * cover this?
- */
-/* ARGSUSED */
-static int
-std_get_fo_mode(struct scsi_device *sd, int *mode,
-    int *state, int *xlf_capable, int *preferred)
-{
-	int			retval = 0;
-	struct buf		*bp;
-	struct scsi_address	*ap;
-	int			lu = 0, rel_tgt_port = 0, tgt_port = 0x0;
-
-	VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_get_fo_mode: enter\n"));
-	*mode = *state = *xlf_capable = 0;
-	bp = getrbuf(KM_NOSLEEP);
-	if (bp == NULL) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
-		    " failed getrbuf\n"));
-		return (1);
-	}
-
-	ap = &sd->sd_address;
-	if (std_inquiry(ap, bp, mode)) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
-		    " failed std_inquiry\n"));
-		retval = 1;
-	} else if (std_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
-		    " failed std_page83\n"));
-		retval = 1;
-	} else if (std_report_target_groups(ap, bp, rel_tgt_port, tgt_port,
-	    state, preferred)) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_get_fo_mode: "
-		    " failed std_report_target_groups\n"));
-		retval = 1;
-	}
-
-	freerbuf(bp);
-	if (retval == 0) {
-		VHCI_DEBUG(6, (CE_NOTE, NULL, "!std_get_fo_mode: "
-		    "SUCCESS\n"));
-	}
-	return (retval);
-}
-
 /* ARGSUSED */
 static int
 std_activate_explicit(struct scsi_device *sd, int xlf_capable)
 {
 	cmn_err(CE_NOTE, "Explicit Activation is done by "
-	    "std_set_target_groups() call from MPAPI");
+	    "vhci_tpgs_set_target_groups() call from MPAPI");
 	return (1);
 }
 
@@ -580,7 +135,7 @@
  */
 static int
 std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt,
-	int *retry_cnt)
+    int *retry_cnt)
 {
 	struct scsi_extended_sense	*sns;
 
@@ -666,7 +221,7 @@
 /* ARGSUSED */
 static int
 std_path_activate(struct scsi_device *sd, char *pathclass,
-void *ctpriv)
+    void *ctpriv)
 {
 	struct buf			*bp;
 	struct scsi_pkt			*pkt;
@@ -678,9 +233,9 @@
 
 	mode = state = 0;
 
-	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
+	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
-		    " failed std_get_fo_mode\n"));
+		    " failed vhci_tpgs_get_target_fo_mode\n"));
 		return (1);
 	}
 	if ((state == STD_ACTIVE_OPTIMIZED) ||
@@ -797,23 +352,23 @@
 
 /* ARGSUSED */
 static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
-void *ctpriv)
+    void *ctpriv)
 {
 	return (0);
 }
 
 /* ARGSUSED */
 static int
-std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo
-*opinfo, void *ctpriv)
+std_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo *opinfo,
+    void *ctpriv)
 {
 	int			mode, preferred, state, xlf;
 
 	opinfo->opinfo_rev = OPINFO_REV;
 
-	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
+	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
-		    " failed std_get_fo_mode\n"));
+		    " failed vhci_tpgs_get_target_fo_mode\n"));
 		return (1);
 	}
 
@@ -858,8 +413,8 @@
  */
 /* ARGSUSED */
 static int
-std_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense
-*sense, void *ctpriv)
+std_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense *sense,
+    void *ctpriv)
 {
 	int rval = SCSI_SENSE_UNKNOWN;
 
@@ -931,130 +486,3 @@
 		return (EINVAL);
 	}
 }
-
-int
-std_set_target_groups(struct scsi_address *ap, int set_state, int tpg_id)
-{
-	struct scsi_pkt			*pkt;
-	struct buf			*bp;
-	int				len, rval, ss = SCSI_SENSE_UNKNOWN;
-	char				*bufp;
-	struct scsi_extended_sense	*sns;
-
-	len = 8;
-
-	bp = getrbuf(KM_NOSLEEP);
-	if (bp == NULL) {
-		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_set_target_groups: "
-		    " failed getrbuf"));
-		return (1);
-	}
-
-	bufp = kmem_zalloc(len, KM_NOSLEEP);
-	if (bufp == NULL) {
-		VHCI_DEBUG(1, (CE_WARN, NULL, "!std_set_target_groups: "
-		    "request packet allocation for %d failed....", len));
-		freerbuf(bp);
-		return (1);
-	}
-
-	bp->b_un.b_addr = bufp;
-	bp->b_flags = B_READ;
-	bp->b_bcount = len;
-	bp->b_resid = 0;
-
-	bufp[4] = (0x0f & set_state);
-	bufp[6] = (0xff00 & tpg_id) >> 8;
-	bufp[7] = (0x00ff & tpg_id);
-
-	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
-	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
-
-	if (pkt == NULL) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL,
-		    "!std_set_target_groups: scsi_init_pkt error\n"));
-		freerbuf(bp);
-		kmem_free((void *)bufp, len);
-		return (1);
-	}
-
-	/*
-	 * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9]
-	 * is set to 8 bytes - Refer SPC3 for details.
-	 */
-	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_OUT;
-	pkt->pkt_cdbp[1] = SCMD_SET_TARGET_PORT_GROUPS;
-	pkt->pkt_cdbp[9] = 8;
-	pkt->pkt_time = 90;
-
-	VHCI_DEBUG(1, (CE_NOTE, NULL,
-	    "!std_set_target_groups: sending set target port group:"
-	    " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0],
-	    pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7],
-	    pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
-
-#ifdef DEBUG
-	print_buf(bufp, len);
-#endif
-	rval = vhci_do_scsi_cmd(pkt);
-
-	if (rval == 0) {
-		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
-		    " vhci_do_scsi_cmd failed\n"));
-		freerbuf(bp);
-		kmem_free((void *)bufp, len);
-		scsi_destroy_pkt(pkt);
-		return (-1);
-	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
-	    (SCBP_C(pkt) == STATUS_CHECK) &&
-	    (pkt->pkt_state & STATE_ARQ_DONE)) {
-		sns = &(((struct scsi_arq_status *)(uintptr_t)
-		    (pkt->pkt_scbp))->sts_sensedata);
-
-		if ((sns->es_key == KEY_UNIT_ATTENTION) &&
-		    (sns->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
-		    (sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
-			ss = SCSI_SENSE_STATE_CHANGED;
-			VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_set_target_groups:"
-			    " sense:%x, add_code: %x, qual_code:%x"
-			    " sense:%x\n", sns->es_key, sns->es_add_code,
-			    sns->es_qual_code, ss));
-		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
-		    (sns->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
-			ss = SCSI_SENSE_NOFAILOVER;
-			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
-			    " sense:%x, add_code: %x, qual_code:%x"
-			    " sense:%x\n", sns->es_key, sns->es_add_code,
-			    sns->es_qual_code, ss));
-		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
-		    (sns->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
-			ss = SCSI_SENSE_NOFAILOVER;
-			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups:"
-			    " sense_key:%x, add_code: %x, qual_code:%x"
-			    " sense:%x\n", sns->es_key, sns->es_add_code,
-			    sns->es_qual_code, rval));
-		} else {
-			/*
-			 * At this point sns data may be for power-on-reset
-			 * UNIT ATTN hardware errors, vendor unqiue sense etc.
-			 * For all these cases, sense is unknown.
-			 */
-			ss = SCSI_SENSE_NOFAILOVER;
-			VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_set_target_groups: "
-			    " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n",
-			    sns->es_key, sns->es_add_code, sns->es_qual_code));
-		}
-
-		if (ss == SCSI_SENSE_STATE_CHANGED) {
-			freerbuf(bp);
-			kmem_free((void *)bufp, len);
-			scsi_destroy_pkt(pkt);
-			return (0);
-		}
-	}
-
-	freerbuf(bp);
-	kmem_free((void *)bufp, len);
-	scsi_destroy_pkt(pkt);
-	return (1);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,172 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implementation of "scsi_vhci_f_tpgs_tape" T10 standard based failover_ops.
+ *
+ * NOTE: for sequential devices only.
+ */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+#include <sys/scsi/adapters/scsi_vhci_tpgs.h>
+
+/* Supported device table entries.  */
+char *tpgs_tape_dev_table[] = { NULL };
+static void tpgs_tape_init(void);
+static int tpgs_tape_device_probe(struct scsi_device *sd,
+    struct scsi_inquiry *inq, void **ctpriv);
+
+/* Failover module plumbing. */
+#ifdef	lint
+#define	scsi_vhci_failover_ops	scsi_vhci_failover_ops_f_tpgs_tape
+#endif	/* lint */
+struct scsi_failover_ops scsi_vhci_failover_ops = {
+	SFO_REV,
+	"f_tpgs_tape",
+	tpgs_tape_dev_table,
+	tpgs_tape_init,
+	tpgs_tape_device_probe,
+	/* The rest of the implementation comes from SFO_NAME_TPGS import  */
+};
+
+static struct modlmisc modlmisc = {
+	&mod_miscops, "f_tpgs_tape %I%"
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlmisc, NULL
+};
+
+
+
+/*
+ * External function definitions
+ */
+extern struct scsi_failover_ops	*vhci_failover_ops_by_name(char *);
+
+int
+_init()
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+	return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/* ARGSUSED */
+static int
+tpgs_tape_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq,
+    void **ctpriv)
+{
+	int		mode, state, xlf, preferred = 0;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "tpgs_tape_device_probe: vidpid %s\n",
+	    inq->inq_vid));
+
+	if (inq->inq_tpgs == 0) {
+		VHCI_DEBUG(4, (CE_WARN, NULL,
+		    "!tpgs_tape_device_probe: not a standard tpgs device"));
+		return (SFO_DEVICE_PROBE_PHCI);
+	}
+
+	if (inq->inq_dtype != DTYPE_SEQUENTIAL) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "!tpgs_tape_device_probe: Detected a "
+		    "Standard Asymmetric device "
+		    "not yet supported\n"));
+		return (SFO_DEVICE_PROBE_PHCI);
+	}
+
+	if (vhci_tpgs_get_target_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
+		VHCI_DEBUG(4, (CE_WARN, NULL, "!unable to fetch fo "
+		    "mode: sd(%p)", (void *) sd));
+		return (SFO_DEVICE_PROBE_PHCI);
+	}
+
+	if (inq->inq_tpgs == SCSI_IMPLICIT_FAILOVER) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!tpgs_tape_device_probe: Detected a "
+		    "Standard Asymmetric device "
+		    "with implicit failover\n"));
+		return (SFO_DEVICE_PROBE_VHCI);
+	}
+	if (inq->inq_tpgs == SCSI_EXPLICIT_FAILOVER) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!tpgs_tape_device_probe: Detected a "
+		    "Standard Asymmetric device "
+		    "with explicit failover\n"));
+		return (SFO_DEVICE_PROBE_VHCI);
+	}
+	if (inq->inq_tpgs == SCSI_BOTH_FAILOVER) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!tpgs_tape_device_probe: Detected a "
+		    "Standard Asymmetric device "
+		    "which supports both implicit and explicit failover\n"));
+		return (SFO_DEVICE_PROBE_VHCI);
+	}
+	VHCI_DEBUG(1, (CE_WARN, NULL,
+	    "!tpgs_tape_device_probe: "
+	    "Unknown tpgs_bits: %x", inq->inq_tpgs));
+	return (SFO_DEVICE_PROBE_PHCI);
+}
+
+static void
+tpgs_tape_init(void)
+{
+	struct scsi_failover_ops	*sfo, *ssfo, clone;
+
+	/* clone SFO_NAME_SYM implementation for most things */
+	ssfo = vhci_failover_ops_by_name(SFO_NAME_TPGS);
+	if (ssfo == NULL) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!tpgs_tape: "
+		    "can't import " SFO_NAME_SYM "\n"));
+		return;
+	}
+	sfo			= &scsi_vhci_failover_ops;
+	clone			= *ssfo;
+	clone.sfo_rev		= sfo->sfo_rev;
+	clone.sfo_name		= sfo->sfo_name;
+	clone.sfo_devices	= sfo->sfo_devices;
+	clone.sfo_init		= sfo->sfo_init;
+	clone.sfo_device_probe	= sfo->sfo_device_probe;
+	*sfo			= clone;
+}
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/mpapi_impl.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/mpapi_impl.c	Mon Jun 23 13:41:43 2008 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -151,7 +151,8 @@
  */
 extern void	*vhci_softstate;
 extern char	vhci_version_name[];
-extern int	(*tpgs_set_target_groups)(struct scsi_address *, int, int);
+extern int vhci_tpgs_set_target_groups(struct scsi_address *, int, int);
+
 
 extern void mdi_vhci_walk_phcis(dev_info_t *,
     int (*)(dev_info_t *, void *), void *);
@@ -1572,8 +1573,8 @@
 		ap = &svp->svp_psd->sd_address;
 		ASSERT(ap != NULL);
 
-		retval = (*tpgs_set_target_groups)
-		    (ap, desired_state, t10_tpgid);
+		retval = vhci_tpgs_set_target_groups(ap, desired_state,
+		    t10_tpgid);
 		if (retval != 0) {
 			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
 			    "state:(ALUA) FAILOVER FAILED: %x", retval));
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c	Mon Jun 23 13:41:43 2008 -0700
@@ -34,6 +34,7 @@
 #include <sys/sunddi.h>
 #include <sys/scsi/scsi.h>
 #include <sys/scsi/impl/scsi_reset_notify.h>
+#include <sys/scsi/impl/services.h>
 #include <sys/sunmdi.h>
 #include <sys/mdi_impldefs.h>
 #include <sys/scsi/adapters/scsi_vhci.h>
@@ -196,7 +197,7 @@
 static void vhci_intr(struct scsi_pkt *);
 static int vhci_do_prout(scsi_vhci_priv_t *);
 static void vhci_run_cmd(void *);
-static int vhci_do_prin(struct vhci_pkt **);
+static int vhci_do_prin(struct vhci_pkt *);
 static struct scsi_pkt *vhci_create_retry_pkt(struct vhci_pkt *);
 static struct vhci_pkt *vhci_sync_retry_pkt(struct vhci_pkt *);
 static struct scsi_vhci_lun *vhci_lun_lookup(dev_info_t *);
@@ -209,6 +210,10 @@
 #ifdef DEBUG
 static void vhci_print_prin_keys(vhci_prin_readkeys_t *, int);
 #endif
+static void vhci_print_cdb(dev_info_t *dip, uint_t level,
+    char *title, uchar_t *cdb);
+static void vhci_clean_print(dev_info_t *dev, uint_t level,
+    char *title, uchar_t *data, int len);
 static void vhci_print_prout_keys(scsi_vhci_lun_t *, char *);
 static void vhci_uscsi_iodone(struct scsi_pkt *pkt);
 
@@ -226,10 +231,7 @@
 extern int vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *,
     scsi_vhci_lun_t *);
 
-/* Special export to MP-API of tpgs non-'fops' entry point */
-int (*tpgs_set_target_groups)(struct scsi_address *, int, int);
-
-#define	VHCI_DMA_MAX_XFER_CAP	0xffffffffULL
+#define	VHCI_DMA_MAX_XFER_CAP	INT_MAX
 
 #define	VHCI_MAX_PGR_RETRIES	3
 
@@ -480,23 +482,6 @@
 		/* register vid/pid of devices supported with mpapi */
 		for (dt = sf->sf_sfo->sfo_devices; *dt; dt++)
 			vhci_mpapi_add_dev_prod(vhci, *dt);
-
-		/*
-		 * Special processing for SFO_NAME_TPGS module, which contains
-		 * the `tpgs_set_target_groups` implementation needed by the
-		 * MP-API code.
-		 */
-		if (strcmp(sf->sf_sfo->sfo_name, SFO_NAME_TPGS) == 0) {
-			tpgs_set_target_groups =
-			    (int (*)(struct scsi_address *, int, int))
-			    ddi_modsym(sf->sf_mod, "std_set_target_groups", &e);
-			if (tpgs_set_target_groups == NULL) {
-				cmn_err(CE_WARN, "scsi_vhci: "
-				    "unable to import 'std_set_target_groups' "
-				    "from '%s', error %d", module[i], e);
-			}
-		}
-
 		sf++;
 	}
 
@@ -513,7 +498,7 @@
 	/* call sfo_init for modules that need it */
 	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
 		if (sf->sf_sfo && sf->sf_sfo->sfo_init)
-			(*sf->sf_sfo->sfo_init)();
+			sf->sf_sfo->sfo_init();
 	}
 
 	ddi_prop_free(module);
@@ -1231,8 +1216,7 @@
 	 * is reading PR keys which requires filtering on completion.
 	 * Data cache sync must be guaranteed.
 	 */
-	if ((pkt->pkt_cdbp[0] == SCMD_PRIN) &&
-	    (pkt->pkt_cdbp[1] == 0) &&
+	if ((pkt->pkt_cdbp[0] == SCMD_PRIN) && (pkt->pkt_cdbp[1] == 0) &&
 	    (vpkt->vpkt_org_vpkt == NULL)) {
 		vpkt->vpkt_tgt_init_pkt_flags |= PKT_CONSISTENT;
 	}
@@ -1283,8 +1267,7 @@
 		}
 	}
 
-	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(
-	    vpkt->vpkt_path);
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
 	if (svp == NULL || reserve_failed) {
 		if (pkt_reserve_cmd) {
 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
@@ -1305,8 +1288,7 @@
 		}
 		if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
-		    ((pkt->pkt_cdbp[1] & 0x1f) ==
-		    VHCI_PROUT_R_AND_IGNORE))) {
+		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
 			sema_v(&vlun->svl_pgr_sema);
 		}
 		return (TRAN_BUSY);
@@ -1343,8 +1325,7 @@
 		mutex_exit(&vlun->svl_mutex);
 		if ((pkt->pkt_cdbp[0] == SCMD_PROUT) &&
 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
-		    ((pkt->pkt_cdbp[1] & 0x1f) ==
-		    VHCI_PROUT_R_AND_IGNORE))) {
+		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
 			if (rval = vhci_pgr_register_start(vlun, pkt)) {
 				/* an error */
 				sema_v(&vlun->svl_pgr_sema);
@@ -1404,7 +1385,7 @@
 			 */
 			ASSERT(vpkt->vpkt_org_vpkt == NULL);
 			if (tpkt->pkt_comp) {
-				(*tpkt->pkt_comp)(tpkt);
+				tpkt->pkt_comp(tpkt);
 			}
 		}
 		return (rval);
@@ -1544,7 +1525,7 @@
 		if (level == RESET_LUN) {
 			hba = ap->a_hba_tran;
 			ASSERT(hba != NULL);
-			return ((*hba->tran_reset)(ap, RESET_LUN));
+			return (hba->tran_reset(ap, RESET_LUN));
 		}
 		return (scsi_reset(ap, level));
 	}
@@ -1585,7 +1566,7 @@
 	ASSERT(hba != NULL);
 
 	if (hba->tran_reset != NULL) {
-		if ((*hba->tran_reset)(pap, level) == 0) {
+		if (hba->tran_reset(pap, level) == 0) {
 			pdip = mdi_pi_get_phci(pip);
 			vhci_log(CE_WARN, vdip, "!(%s%d):"
 			    " path (%s%d), reset %d failed",
@@ -1722,6 +1703,10 @@
 		 */
 		switch (cidx) {
 		case SCSI_CAP_DMA_MAX:
+			/*
+			 * For X86 this capability is caught in scsi_ifgetcap().
+			 * XXX Should this be getting the value from the pHCI?
+			 */
 			rval = (int)VHCI_DMA_MAX_XFER_CAP;
 			break;
 
@@ -1765,6 +1750,18 @@
 			mutex_exit(&vlun->svl_mutex);
 			break;
 
+		case SCSI_CAP_CDB_LEN:
+			rval = VHCI_SCSI_CDB_SIZE;
+			break;
+
+		case SCSI_CAP_DMA_MAX_ARCH:
+			/*
+			 * For X86 this capability is caught in scsi_ifgetcap().
+			 * XXX Should this be getting the value from the pHCI?
+			 */
+			rval = 0;
+			break;
+
 		default:
 			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
 			    "!vhci_getcap: unsupported %d", cidx));
@@ -2028,7 +2025,7 @@
  */
 static int
 vhci_scsi_reset_notify(struct scsi_address *ap, int flag,
-	void (*callback)(caddr_t), caddr_t arg)
+    void (*callback)(caddr_t), caddr_t arg)
 {
 	struct scsi_vhci *vhci = ADDR2VHCI(ap);
 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
@@ -2408,8 +2405,7 @@
 	if (pkt == NULL || (vpkt->vpkt_flags & CFLAG_DMA_PARTIAL)) {
 		pkt = scsi_init_pkt(address, pkt,
 		    vpkt->vpkt_tgt_init_bp, vpkt->vpkt_tgt_init_cdblen,
-		    vpkt->vpkt_tgt_init_scblen,
-		    0, flags, func, NULL);
+		    vpkt->vpkt_tgt_init_scblen, 0, flags, func, NULL);
 
 		if (pkt == NULL) {
 			VHCI_DEBUG(4, (CE_NOTE, NULL,
@@ -2461,16 +2457,17 @@
 
 	struct scsi_pkt			*new_pkt;
 	struct buf			*bp;
-	scsi_vhci_lun_t			*vlun;
+	scsi_vhci_lun_t			*vlun = svp->svp_svl;
 	int				rval, retry, nr_retry, ua_retry;
 	struct scsi_extended_sense	*sns;
 
 	bp = getrbuf(KM_SLEEP);
 	bp->b_flags = B_WRITE;
 	bp->b_resid = 0;
+	bp->b_un.b_addr = (caddr_t)&vlun->svl_prout;
+	bp->b_bcount = vlun->svl_bcount;
 
 	VHCI_INCR_PATH_CMDCOUNT(svp);
-	vlun = svp->svp_svl;
 
 	new_pkt = scsi_init_pkt(&svp->svp_psd->sd_address, NULL, bp,
 	    CDB_GROUP1, sizeof (struct scsi_arq_status), 0, 0,
@@ -2504,9 +2501,8 @@
 				int max_retry;
 				struct scsi_failover_ops *fops;
 				fops = vlun->svl_fops;
-				rval = (*fops->sfo_analyze_sense)
-				    (svp->svp_psd, sns,
-				    vlun->svl_fops_ctpriv);
+				rval = fops->sfo_analyze_sense(svp->svp_psd,
+				    sns, vlun->svl_fops_ctpriv);
 				if (rval == SCSI_SENSE_NOT_READY) {
 					max_retry = vhci_prout_not_ready_retry;
 					retry = nr_retry++;
@@ -2604,8 +2600,7 @@
 	vlun->svl_cdb[1] |= VHCI_PROUT_R_AND_IGNORE;
 
 	do {
-		nsvp = (scsi_vhci_priv_t *)
-		    mdi_pi_get_vhci_private(npip);
+		nsvp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
 		if (nsvp == NULL) {
 			VHCI_DEBUG(4, (CE_NOTE, NULL,
 			    "vhci_run_cmd: no "
@@ -2707,14 +2702,14 @@
  * keys to be returned.
  */
 static int
-vhci_do_prin(struct vhci_pkt **vpkt)
+vhci_do_prin(struct vhci_pkt *vpkt)
 {
 	scsi_vhci_priv_t *svp = (scsi_vhci_priv_t *)
-	    mdi_pi_get_vhci_private((*vpkt)->vpkt_path);
+	    mdi_pi_get_vhci_private(vpkt->vpkt_path);
 	vhci_prin_readkeys_t *prin;
 	scsi_vhci_lun_t *vlun = svp->svp_svl;
 	struct scsi_vhci *vhci =
-	    ADDR2VHCI(&((*vpkt)->vpkt_tgt_pkt->pkt_address));
+	    ADDR2VHCI(&(vpkt->vpkt_tgt_pkt->pkt_address));
 
 	struct buf		*new_bp = NULL;
 	struct scsi_pkt		*new_pkt = NULL;
@@ -2725,7 +2720,7 @@
 	uint32_t		svl_prin_length = 0;
 
 	prin = (vhci_prin_readkeys_t *)
-	    bp_mapin_common((*vpkt)->vpkt_tgt_init_bp, VM_NOSLEEP);
+	    bp_mapin_common(vpkt->vpkt_tgt_init_bp, VM_NOSLEEP);
 
 	if (prin != NULL) {
 		prin_length = BE_32(prin->length);
@@ -2763,10 +2758,10 @@
 	 * allocated to get all of the available registered keys.
 	 */
 	if (rval != VHCI_CMD_ERROR) {
-		if (((*vpkt)->vpkt_tgt_init_bp->b_bcount - hdr_len) <
+		if ((vpkt->vpkt_tgt_init_bp->b_bcount - hdr_len) <
 		    prin_length) {
-			if ((*vpkt)->vpkt_org_vpkt == NULL) {
-				new_pkt = vhci_create_retry_pkt(*vpkt);
+			if (vpkt->vpkt_org_vpkt == NULL) {
+				new_pkt = vhci_create_retry_pkt(vpkt);
 				if (new_pkt != NULL) {
 					new_vpkt = TGTPKT2VHCIPKT(new_pkt);
 
@@ -2778,7 +2773,7 @@
 					new_bp = scsi_alloc_consistent_buf(
 					    &svp->svp_psd->sd_address,
 					    NULL, (prin_length + hdr_len),
-					    ((*vpkt)->vpkt_tgt_init_bp->
+					    (vpkt->vpkt_tgt_init_bp->
 					    b_flags & (B_READ | B_WRITE)),
 					    NULL_FUNC, NULL);
 					if (new_bp != NULL) {
@@ -2820,9 +2815,9 @@
 		 * this command is sent down.  This allows the normal bind
 		 * transport mechanism to be used.
 		 */
-		if ((*vpkt)->vpkt_path != NULL) {
-			mdi_rele_path((*vpkt)->vpkt_path);
-			(*vpkt)->vpkt_path = NULL;
+		if (vpkt->vpkt_path != NULL) {
+			mdi_rele_path(vpkt->vpkt_path);
+			vpkt->vpkt_path = NULL;
 		}
 
 		/*
@@ -2902,12 +2897,12 @@
 		 * that we put everything back the way it originally was so
 		 * that the target driver can complete the command correctly.
 		 */
-		if ((*vpkt)->vpkt_org_vpkt != NULL) {
-			new_bp = (*vpkt)->vpkt_tgt_init_bp;
+		if (vpkt->vpkt_org_vpkt != NULL) {
+			new_bp = vpkt->vpkt_tgt_init_bp;
 
 			scsi_free_consistent_buf(new_bp);
 
-			*vpkt = vhci_sync_retry_pkt(*vpkt);
+			vpkt = vhci_sync_retry_pkt(vpkt);
 
 			/*
 			 * Make sure the original buffer is mapped into kernel
@@ -2915,7 +2910,7 @@
 			 * it.
 			 */
 			prin = (vhci_prin_readkeys_t *)bp_mapin_common(
-			    (*vpkt)->vpkt_tgt_init_bp, VM_NOSLEEP);
+			    vpkt->vpkt_tgt_init_bp, VM_NOSLEEP);
 		}
 
 		/*
@@ -2923,7 +2918,7 @@
 		 * target buffer.
 		 */
 		if (svl_prin_length <=
-		    ((*vpkt)->vpkt_tgt_init_bp->b_bcount - hdr_len)) {
+		    (vpkt->vpkt_tgt_init_bp->b_bcount - hdr_len)) {
 			/*
 			 * It is safe to return all of the available unique
 			 * keys
@@ -2935,7 +2930,7 @@
 			 * original command.
 			 */
 			bcopy(&vlun->svl_prin, prin,
-			    (*vpkt)->vpkt_tgt_init_bp->b_bcount);
+			    vpkt->vpkt_tgt_init_bp->b_bcount);
 		}
 #ifdef DEBUG
 		VHCI_DEBUG(5, (CE_NOTE, NULL,
@@ -2957,14 +2952,14 @@
 		 * complete the command correctly.
 		 */
 
-		if ((*vpkt)->vpkt_org_vpkt != NULL) {
-			new_bp = (*vpkt)->vpkt_tgt_init_bp;
+		if (vpkt->vpkt_org_vpkt != NULL) {
+			new_bp = vpkt->vpkt_tgt_init_bp;
 			if (new_bp != NULL) {
 				scsi_free_consistent_buf(new_bp);
 			}
 
-			new_vpkt = *vpkt;
-			*vpkt = (*vpkt)->vpkt_org_vpkt;
+			new_vpkt = vpkt;
+			vpkt = vpkt->vpkt_org_vpkt;
 
 			vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
 			    new_vpkt->vpkt_tgt_pkt);
@@ -2975,8 +2970,8 @@
 		 * ssd will retry the command.
 		 */
 
-		(*vpkt)->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
-		(*vpkt)->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
+		vpkt->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
+		vpkt->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
 
 		rval = VHCI_CMD_CMPLT;
 	}
@@ -3096,8 +3091,8 @@
 					    vpkt->vpkt_tgt_init_scblen);
 					break;
 				}
-				rval = (*fops->sfo_analyze_sense)
-				    (svp->svp_psd, sns, vlun->svl_fops_ctpriv);
+				rval = fops->sfo_analyze_sense(svp->svp_psd,
+				    sns, vlun->svl_fops_ctpriv);
 				if ((rval == SCSI_SENSE_NOFAILOVER) ||
 				    (rval == SCSI_SENSE_UNKNOWN) ||
 				    (rval == SCSI_SENSE_NOT_READY)) {
@@ -3208,8 +3203,7 @@
 		 */
 		if ((pkt->pkt_cdbp[0] ==  SCMD_PROUT) &&
 		    (((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_REGISTER) ||
-		    ((pkt->pkt_cdbp[1] & 0x1f) ==
-		    VHCI_PROUT_R_AND_IGNORE))) {
+		    ((pkt->pkt_cdbp[1] & 0x1f) == VHCI_PROUT_R_AND_IGNORE))) {
 			if (SCBP_C(pkt) == STATUS_GOOD) {
 				ASSERT(vlun->svl_taskq);
 				svp->svp_last_pkt_reason = pkt->pkt_reason;
@@ -3219,8 +3213,7 @@
 			}
 		}
 		if ((SCBP_C(pkt) == STATUS_GOOD) &&
-		    (pkt->pkt_cdbp[0] == SCMD_PRIN) &&
-		    vpkt->vpkt_tgt_init_bp) {
+		    (pkt->pkt_cdbp[0] == SCMD_PRIN) && vpkt->vpkt_tgt_init_bp) {
 			/*
 			 * If the action (value in byte 1 of the cdb) is zero,
 			 * we're reading keys, and that's the only condition
@@ -3257,7 +3250,7 @@
 				 * vpkt will contain the address of the
 				 * original vpkt
 				 */
-				if (vhci_do_prin(&vpkt) == VHCI_CMD_RETRY) {
+				if (vhci_do_prin(vpkt) == VHCI_CMD_RETRY) {
 					/*
 					 * The command has been resent to get
 					 * all the keys from the device.  Don't
@@ -3309,6 +3302,8 @@
 		break;
 
 	case CMD_DEV_GONE:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_intr received "
+		    "cmd_dev_gone\n"));
 		tpkt->pkt_reason = CMD_CMPLT;
 		tpkt->pkt_state = STATE_GOT_BUS |
 		    STATE_GOT_TARGET | STATE_SENT_CMD |
@@ -3523,8 +3518,8 @@
 		return (0);
 	}
 	if (*((unsigned char *)statusp) == STATUS_CHECK) {
-		rval = (*(vlun->svl_fops->sfo_analyze_sense))
-		    (svp->svp_psd, sensep, vlun->svl_fops_ctpriv);
+		rval = vlun->svl_fops->sfo_analyze_sense(svp->svp_psd, sensep,
+		    vlun->svl_fops_ctpriv);
 		switch (rval) {
 			/*
 			 * Only update path states in case path is definitely
@@ -3654,7 +3649,7 @@
 		pip = npip;
 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
 		psd = svp->svp_psd;
-		if ((*fo->sfo_path_get_opinfo)(psd, &opinfo,
+		if (fo->sfo_path_get_opinfo(psd, &opinfo,
 		    vlun->svl_fops_ctpriv) != 0) {
 			sps = mdi_select_path(dip, NULL,
 			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
@@ -4161,8 +4156,8 @@
 						 * path-class with class
 						 */
 						fo = vlun->svl_fops;
-						(*fo->sfo_pathclass_next)(NULL,
-						    &best_pclass,
+						(void) fo->sfo_pathclass_next(
+						    NULL, &best_pclass,
 						    vlun->svl_fops_ctpriv);
 						pclass = NULL;
 						rv = mdi_prop_lookup_string(pip,
@@ -4514,8 +4509,7 @@
 	struct scsi_path_opinfo		opinfo;
 	char				*pclass, *best_pclass;
 
-	if ((*fo->sfo_path_get_opinfo)(psd, &opinfo,
-	    vlun->svl_fops_ctpriv) != 0) {
+	if (fo->sfo_path_get_opinfo(psd, &opinfo, vlun->svl_fops_ctpriv) != 0) {
 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_update_pathinfo: "
 		    "Failed to get operation info for path:%p\n", (void *)pip));
 		return (MDI_FAILURE);
@@ -4647,7 +4641,7 @@
 		 * initiate auto-failback as the next IO shall take care of.
 		 * this. See comment above.
 		 */
-		(*fo->sfo_pathclass_next)(NULL, &best_pclass,
+		(void) fo->sfo_pathclass_next(NULL, &best_pclass,
 		    vlun->svl_fops_ctpriv);
 		if (((vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK) ==
 		    VHCI_CONF_FLAGS_AUTO_FAILBACK) &&
@@ -4934,9 +4928,8 @@
 		/* NULL: default: select based on sfo_device_probe results */
 		for (sf = scsi_failover_table; sf->sf_mod; sf++) {
 			if ((sf->sf_sfo == NULL) ||
-			    ((*sf->sf_sfo->sfo_device_probe) (psd,
-			    psd->sd_inq, &vlun->svl_fops_ctpriv) ==
-			    SFO_DEVICE_PROBE_PHCI))
+			    sf->sf_sfo->sfo_device_probe(psd, psd->sd_inq,
+			    &vlun->svl_fops_ctpriv) == SFO_DEVICE_PROBE_PHCI)
 				continue;
 
 			/* found failover module, supported under scsi_vhci */
@@ -4945,7 +4938,7 @@
 			    i_ddi_strdup(sfo->sfo_name, KM_SLEEP);
 			break;
 		}
-	} else if (strcmp(override, "NONE") && strcmp(override, "none")) {
+	} else if (strcasecmp(override, "NONE")) {
 		/* !"NONE": select based on driver.conf specified name */
 		for (sf = scsi_failover_table, sfo = NULL; sf->sf_mod; sf++) {
 			if ((sf->sf_sfo == NULL) ||
@@ -4988,6 +4981,12 @@
 	vhci_get_device_type_mpxio_options(vdip, tgt_dip, psd);
 
 	/*
+	 * The device probe or options in conf file may have set/changed the
+	 * lb policy, save the current value.
+	 */
+	vlun->svl_lb_policy_save = mdi_get_lb_policy(tgt_dip);
+
+	/*
 	 * if PGR is active, revalidate key and register on this path also,
 	 * if key is still valid
 	 */
@@ -5695,7 +5694,7 @@
 		 * arguments it accepts are PRIMARY and SECONDARY.
 		 */
 		fo = vlun->svl_fops;
-		if ((*fo->sfo_pathclass_next)(PCLASS_PRIMARY, &pclass,
+		if (fo->sfo_pathclass_next(PCLASS_PRIMARY, &pclass,
 		    vlun->svl_fops_ctpriv)) {
 			retval = ENOTSUP;
 			break;
@@ -6717,7 +6716,7 @@
 
 next_pathclass:
 
-	rval = (*sfo->sfo_pathclass_next)(pclass1, &pclass2,
+	rval = sfo->sfo_pathclass_next(pclass1, &pclass2,
 	    vlun->svl_fops_ctpriv);
 	if (rval == ENOENT) {
 		if (s_pclass == NULL) {
@@ -6725,7 +6724,7 @@
 			    "failed, no more pathclasses\n", guid));
 			goto done;
 		} else {
-			(*sfo->sfo_pathclass_next)(NULL, &pclass2,
+			(void) sfo->sfo_pathclass_next(NULL, &pclass2,
 			    vlun->svl_fops_ctpriv);
 		}
 	} else if (rval == EINVAL) {
@@ -6863,7 +6862,7 @@
 		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(6)(%s): "
 		    "activating path 0x%p(psd:%p)\n", guid, (void *)npip,
 		    (void *)svp->svp_psd));
-		if ((*sfo->sfo_path_activate)(svp->svp_psd, pclass2,
+		if (sfo->sfo_path_activate(svp->svp_psd, pclass2,
 		    vlun->svl_fops_ctpriv) == 0) {
 			activation_done = 1;
 			mdi_rele_path(npip);
@@ -6920,7 +6919,7 @@
 				    "!vhci_failover(8)(%s): "
 				    "pinging path 0x%p\n",
 				    guid, (void *)npip));
-				if ((*sfo->sfo_path_ping)(svp->svp_psd,
+				if (sfo->sfo_path_ping(svp->svp_psd,
 				    vlun->svl_fops_ctpriv) == 1) {
 					mdi_pi_set_state(npip,
 					    MDI_PATHINFO_STATE_ONLINE);
@@ -7201,6 +7200,11 @@
 	int	retry_cnt = 0;
 	struct scsi_extended_sense	*sns;
 
+#ifdef DEBUG
+	vhci_print_cdb(pkt->pkt_address.a_hba_tran->tran_hba_dip, CE_WARN,
+	    "Vhci command", pkt->pkt_cdbp);
+#endif
+
 retry:
 	err = scsi_poll(pkt);
 	if (err) {
@@ -7395,8 +7399,7 @@
 	success = 0;
 
 	/* Save the res key */
-	bcopy((const void *)prout->res_key,
-	    (void *)temp_res_key, MHIOC_RESV_KEY_SIZE);
+	bcopy(prout->res_key, temp_res_key, MHIOC_RESV_KEY_SIZE);
 
 	/*
 	 * Sometimes CDB from application can be a Register_And_Ignore.
@@ -7408,8 +7411,7 @@
 	vlun->svl_cdb[1] &= 0xe0;
 
 	do {
-		osvp = (scsi_vhci_priv_t *)
-		    mdi_pi_get_vhci_private(npip);
+		osvp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
 		if (osvp == NULL) {
 			VHCI_DEBUG(4, (CE_NOTE, NULL,
 			    "vhci_pgr_validate_and_register: no "
@@ -7433,8 +7435,7 @@
 		    (void *)curthread, vlun->svl_cdb[1]));
 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy:");
 
-		bcopy((const void *)prout->service_key,
-		    (void *)prout->res_key, MHIOC_RESV_KEY_SIZE);
+		bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
 
 		VHCI_DEBUG(4, (CE_WARN, NULL, "vlun 0x%p After bcopy",
 		    (void *)vlun));
@@ -7472,8 +7473,7 @@
 	vlun->svl_cdb[1] = cdb_1;
 
 	/* Restore the res_key */
-	bcopy((const void *)temp_res_key,
-	    (void *)prout->res_key, MHIOC_RESV_KEY_SIZE);
+	bcopy(temp_res_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
 
 	/*
 	 * If key could not be registered on any path for the first time,
@@ -7495,10 +7495,9 @@
 
 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: keys before bcopy: ");
 
-	bcopy((const void *)prout->active_service_key,
-	    (void *)prout->service_key, MHIOC_RESV_KEY_SIZE);
-	bcopy((const void *)prout->active_res_key,
-	    (void *)prout->res_key, MHIOC_RESV_KEY_SIZE);
+	bcopy(prout->active_service_key, prout->service_key,
+	    MHIOC_RESV_KEY_SIZE);
+	bcopy(prout->active_res_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
 
 	vhci_print_prout_keys(vlun, "v_pgr_val_reg:keys after bcopy: ");
 
@@ -7564,8 +7563,7 @@
 		    (void *)osvp, (void *)vlun, vlun->svl_cdb[1]));
 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
 
-		bcopy((const void *)prout->service_key,
-		    (void *)prout->res_key, MHIOC_RESV_KEY_SIZE);
+		bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
 
 		vhci_print_prout_keys(vlun, "v_pgr_val_reg: after bcopy: ");
 
@@ -7616,9 +7614,8 @@
 	    " svp 0x%p being done\n", (void *)svp));
 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
 
-	bcopy((const void *)prout->service_key, (void *)prout->res_key,
-	    MHIOC_RESV_KEY_SIZE);
-	bzero((void *)prout->service_key, MHIOC_RESV_KEY_SIZE);
+	bcopy(prout->service_key, prout->res_key, MHIOC_RESV_KEY_SIZE);
+	bzero(prout->service_key, MHIOC_RESV_KEY_SIZE);
 
 	vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
 
@@ -7673,9 +7670,9 @@
 static void
 vhci_dispatch_scsi_start(void *arg)
 {
-	struct vhci_pkt *vpkt = (struct vhci_pkt *)arg;
-	struct scsi_pkt *tpkt = vpkt->vpkt_tgt_pkt;
-	int			rval = TRAN_BUSY;
+	struct vhci_pkt *vpkt	= (struct vhci_pkt *)arg;
+	struct scsi_pkt *tpkt	= vpkt->vpkt_tgt_pkt;
+	int rval		= TRAN_BUSY;
 
 	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_dispatch_scsi_start: sending"
 	    " scsi-2 reserve for 0x%p\n",
@@ -7745,7 +7742,7 @@
 	}
 
 	if (tpkt->pkt_comp) {
-		(*tpkt->pkt_comp)(tpkt);
+		tpkt->pkt_comp(tpkt);
 	}
 }
 
@@ -7777,7 +7774,7 @@
 
 		fo = vlun->svl_fops;
 
-		(*fo->sfo_pathclass_next)(NULL, &best_pclass,
+		(void) fo->sfo_pathclass_next(NULL, &best_pclass,
 		    vlun->svl_fops_ctpriv);
 		if (strcmp(vlun->svl_active_pclass, best_pclass) == 0) {
 			mutex_exit(&vlun->svl_mutex);
@@ -7806,19 +7803,8 @@
 static void
 vhci_print_prin_keys(vhci_prin_readkeys_t *prin, int numkeys)
 {
-	uchar_t index = 0;
-	char buf[100];
-
-	VHCI_DEBUG(5, (CE_NOTE, NULL, "num keys %d\n", numkeys));
-
-	while (index < numkeys) {
-		bcopy(&prin->keylist[index], buf, MHIOC_RESV_KEY_SIZE);
-		VHCI_DEBUG(5, (CE_NOTE, NULL,
-		    "%02x%02x%02x%02x%02x%02x%02x%02x\t",
-		    buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
-		    buf[7]));
-		index++;
-	}
+	vhci_clean_print(NULL, 5, "Current PGR Keys",
+	    (uchar_t *)prin, numkeys * 8);
 }
 #endif
 
@@ -8041,9 +8027,7 @@
 	 */
 	pkt = vhci_scsi_init_pkt(&svp->svp_psd->sd_address, pkt,
 	    vpkt->vpkt_tgt_init_bp, vpkt->vpkt_tgt_init_cdblen,
-	    vpkt->vpkt_tgt_init_scblen, 0,
-	    PKT_CONSISTENT,
-	    NULL_FUNC, NULL);
+	    vpkt->vpkt_tgt_init_scblen, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
 	if (pkt != NULL) {
 		new_vpkt = TGTPKT2VHCIPKT(pkt);
 
@@ -8443,3 +8427,69 @@
 	    "vhci_uscsi_iostart: exit: rval: %d", rval));
 	return (rval);
 }
+
+#ifdef DEBUG
+
+extern struct scsi_key_strings scsi_cmds[];
+
+static char *
+vhci_print_scsi_cmd(char cmd)
+{
+	char tmp[64];
+	char *cpnt;
+
+	cpnt = scsi_cmd_name(cmd, scsi_cmds, tmp);
+	/* tmp goes out of scope on return and caller sees garbage */
+	if (cpnt == tmp) {
+		cpnt = "Unknown Command";
+	}
+	return (cpnt);
+}
+
+extern uchar_t	scsi_cdb_size[];
+
+static void
+vhci_print_cdb(dev_info_t *dip, uint_t level, char *title, uchar_t *cdb)
+{
+	int len = scsi_cdb_size[CDB_GROUPID(cdb[0])];
+	char buf[256];
+
+	if (level == CE_NOTE) {
+		vhci_log(level, dip, "path cmd %s\n",
+		    vhci_print_scsi_cmd(*cdb));
+		return;
+	}
+
+	(void) sprintf(buf, "%s for cmd(%s)", title, vhci_print_scsi_cmd(*cdb));
+	vhci_clean_print(dip, level, buf, cdb, len);
+}
+
+static void
+vhci_clean_print(dev_info_t *dev, uint_t level, char *title, uchar_t *data,
+    int len)
+{
+	int	i;
+	int 	c;
+	char	*format;
+	char	buf[256];
+	uchar_t	byte;
+
+	(void) sprintf(buf, "%s:\n", title);
+	vhci_log(level, dev, "%s", buf);
+	level = CE_CONT;
+	for (i = 0; i < len; ) {
+		buf[0] = 0;
+		for (c = 0; c < 8 && i < len; c++, i++) {
+			byte = (uchar_t)data[i];
+			if (byte < 0x10)
+				format = "0x0%x ";
+			else
+				format = "0x%x ";
+			(void) sprintf(&buf[(int)strlen(buf)], format, byte);
+		}
+		(void) sprintf(&buf[(int)strlen(buf)], "\n");
+
+		vhci_log(level, dev, "%s\n", buf);
+	}
+}
+#endif
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.conf	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.conf	Mon Jun 23 13:41:43 2008 -0700
@@ -57,6 +57,8 @@
 	"misc/scsi_vhci/scsi_vhci_f_sym_emc",
 	"misc/scsi_vhci/scsi_vhci_f_sym_hds",
 	"misc/scsi_vhci/scsi_vhci_f_sym",
+#	"misc/scsi_vhci/scsi_vhci_f_tpgs_tape",
+#	"misc/scsi_vhci/scsi_vhci_f_tape",
 	"misc/scsi_vhci/scsi_vhci_f_tpgs";
 
 #
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,579 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+#include <sys/scsi/adapters/scsi_vhci_tpgs.h>
+
+/*
+ * External function definitions
+ */
+extern void vhci_mpapi_update_tpg_data(struct scsi_address *, char *);
+
+
+
+static int vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp,
+    int *mode);
+static int vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
+    int *rel_tgt_port, int *tgt_port, int *lu);
+static void print_buf(char *buf, int buf_size);
+static int vhci_tpgs_report_target_groups(struct scsi_address *ap,
+    struct buf *bp, int rel_tgt_port, int tgt_port, int *pstate,
+    int *preferred);
+
+int
+vhci_tpgs_set_target_groups(struct scsi_address *ap, int set_state,
+    int tpg_id)
+{
+	struct scsi_pkt			*pkt;
+	struct buf			*bp;
+	int				len, rval, ss = SCSI_SENSE_UNKNOWN;
+	char				*bufp;
+	struct scsi_extended_sense	*sns;
+
+	len = 8;
+
+	bp = getrbuf(KM_NOSLEEP);
+	if (bp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
+		    " failed getrbuf"));
+		return (1);
+	}
+
+	bufp = kmem_zalloc(len, KM_NOSLEEP);
+	if (bufp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_set_target_groups: "
+		    "request packet allocation for %d failed....", len));
+		freerbuf(bp);
+		return (1);
+	}
+
+	bp->b_un.b_addr = bufp;
+	bp->b_flags = B_READ;
+	bp->b_bcount = len;
+	bp->b_resid = 0;
+
+	bufp[4] = (0x0f & set_state);
+	bufp[6] = (0xff00 & tpg_id) >> 8;
+	bufp[7] = (0x00ff & tpg_id);
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
+	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
+
+	if (pkt == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!vhci_tpgs_set_target_groups: scsi_init_pkt error\n"));
+		freerbuf(bp);
+		kmem_free((void *)bufp, len);
+		return (1);
+	}
+
+	/*
+	 * Sends 1 TPG descriptor only. Hence Parameter list length pkt_cdbp[9]
+	 * is set to 8 bytes - Refer SPC3 for details.
+	 */
+	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_OUT;
+	pkt->pkt_cdbp[1] = SSVC_ACTION_SET_TARGET_PORT_GROUPS;
+	pkt->pkt_cdbp[9] = 8;
+	pkt->pkt_time = 90;
+
+	VHCI_DEBUG(1, (CE_NOTE, NULL,
+	    "!vhci_tpgs_set_target_groups: sending set target port group:"
+	    " cdb[0/1/6/7/8/9]: %x/%x/%x/%x/%x/%x\n", pkt->pkt_cdbp[0],
+	    pkt->pkt_cdbp[1], pkt->pkt_cdbp[6], pkt->pkt_cdbp[7],
+	    pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
+
+#ifdef DEBUG
+	print_buf(bufp, len);
+#endif
+	rval = vhci_do_scsi_cmd(pkt);
+
+	if (rval == 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_set_target_groups:"
+		    " vhci_do_scsi_cmd failed\n"));
+		freerbuf(bp);
+		kmem_free((void *)bufp, len);
+		scsi_destroy_pkt(pkt);
+		return (-1);
+	} else if ((pkt->pkt_reason == CMD_CMPLT) &&
+	    (SCBP_C(pkt) == STATUS_CHECK) &&
+	    (pkt->pkt_state & STATE_ARQ_DONE)) {
+		sns = &(((struct scsi_arq_status *)(uintptr_t)
+		    (pkt->pkt_scbp))->sts_sensedata);
+
+		if ((sns->es_key == KEY_UNIT_ATTENTION) &&
+		    (sns->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
+		    (sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
+			ss = SCSI_SENSE_STATE_CHANGED;
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "!vhci_tpgs_set_target_groups:"
+			    " sense:%x, add_code: %x, qual_code:%x"
+			    " sense:%x\n", sns->es_key, sns->es_add_code,
+			    sns->es_qual_code, ss));
+		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
+		    (sns->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
+			ss = SCSI_SENSE_NOFAILOVER;
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_set_target_groups:"
+			    " sense:%x, add_code: %x, qual_code:%x"
+			    " sense:%x\n", sns->es_key, sns->es_add_code,
+			    sns->es_qual_code, ss));
+		} else if ((sns->es_key == KEY_ILLEGAL_REQUEST) &&
+		    (sns->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
+			ss = SCSI_SENSE_NOFAILOVER;
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_set_target_groups:"
+			    " sense_key:%x, add_code: %x, qual_code:%x"
+			    " sense:%x\n", sns->es_key, sns->es_add_code,
+			    sns->es_qual_code, rval));
+		} else {
+			/*
+			 * At this point sns data may be for power-on-reset
+			 * UNIT ATTN hardware errors, vendor unqiue sense etc.
+			 * For all these cases, sense is unknown.
+			 */
+			ss = SCSI_SENSE_NOFAILOVER;
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_set_target_groups: "
+			    " sense UNKNOWN: sense key:%x, ASC:%x, ASCQ:%x\n",
+			    sns->es_key, sns->es_add_code, sns->es_qual_code));
+		}
+
+		if (ss == SCSI_SENSE_STATE_CHANGED) {
+			freerbuf(bp);
+			kmem_free((void *)bufp, len);
+			scsi_destroy_pkt(pkt);
+			return (0);
+		}
+	}
+
+	freerbuf(bp);
+	kmem_free((void *)bufp, len);
+	scsi_destroy_pkt(pkt);
+	return (1);
+}
+
+/*
+ * get the failover mode, ownership and if it has extended failover
+ * capability. The mode(bits5-4/byte5) is defined as implicit, explicit, or
+ * both.  The state is defined as online-optimized(0h),
+ * online-nonoptimized(1h), standby(2h), offline(3h),
+ * and transitioning(fh). Currently, there is online,
+ * standby, and offline(defined in sunmdi.h).
+ * Online-nonoptimized will be a mode of secondary
+ * and an ownership of online. Thought about using a different mode but
+ * it appears the states are really for the states for secondary mode.
+ * We currently have IS_ONLINING, IS_OFFLINING - should we have TRANSITIONING
+ * to mean from online-optimized to online-nonoptimized or does onlining
+ * cover this?
+ */
+/* ARGSUSED */
+int
+vhci_tpgs_get_target_fo_mode(struct scsi_device *sd, int *mode,
+    int *state, int *xlf_capable, int *preferred)
+{
+	int			retval = 0;
+	struct buf		*bp;
+	struct scsi_address	*ap;
+	int			lu = 0, rel_tgt_port = 0, tgt_port = 0x0;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL,
+	    "!vhci_tpgs_get_target_fo_mode: enter\n"));
+	*mode = *state = *xlf_capable = 0;
+	bp = getrbuf(KM_NOSLEEP);
+	if (bp == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
+		    " failed getrbuf\n"));
+		return (1);
+	}
+
+	ap = &sd->sd_address;
+	if (vhci_tpgs_inquiry(ap, bp, mode)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
+		    " failed vhci_tpgs_inquiry\n"));
+		retval = 1;
+	} else if (vhci_tpgs_page83(ap, bp, &rel_tgt_port, &tgt_port, &lu)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
+		    " failed vhci_tpgs_page83\n"));
+		retval = 1;
+	} else if (vhci_tpgs_report_target_groups(ap, bp, rel_tgt_port,
+	    tgt_port, state, preferred)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
+		    " failed vhci_tpgs_report_target_groups\n"));
+		retval = 1;
+	}
+
+	freerbuf(bp);
+	if (retval == 0) {
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_get_target_fo_mode: "
+		    "SUCCESS\n"));
+	}
+	return (retval);
+}
+
+static int
+vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp, int *mode)
+{
+	struct scsi_pkt		*pkt;
+	struct scsi_inquiry	inq;
+	int			retval;
+
+	*mode = 0;
+	bp->b_un.b_addr = (caddr_t)&inq;
+	bp->b_flags = B_READ;
+	bp->b_bcount = sizeof (inq);
+	bp->b_resid = 0;
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
+	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
+	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
+	pkt->pkt_cdbp[4] = sizeof (inq);
+	pkt->pkt_time = 60;
+
+	retval = vhci_do_scsi_cmd(pkt);
+	scsi_destroy_pkt(pkt);
+	if (retval == 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: Failure"
+		    " returned from vhci_do_scsi_cmd"));
+		return (1);
+	}
+
+	if (inq.inq_tpgs == 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "!vhci_tpgs_inquiry: zero tpgs_bits"));
+		return (1);
+	}
+	retval = 0;
+	if (inq.inq_tpgs == SCSI_IMPLICIT_FAILOVER) {
+		*mode = SCSI_IMPLICIT_FAILOVER;
+	} else if (inq.inq_tpgs == SCSI_EXPLICIT_FAILOVER) {
+		*mode = SCSI_EXPLICIT_FAILOVER;
+	} else if (inq.inq_tpgs == SCSI_BOTH_FAILOVER) {
+		*mode = SCSI_BOTH_FAILOVER;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "!vhci_tpgs_inquiry: Illegal mode returned: %x mode: %x",
+		    inq.inq_tpgs, *mode));
+		retval = 1;
+	}
+
+	return (retval);
+}
+
+static int
+vhci_tpgs_page83(struct scsi_address *ap, struct buf *bp,
+	int *rel_tgt_port, int *tgt_port, int *lu)
+{
+	char			*ptr, *end;
+	struct scsi_pkt		*pkt;
+	char			*bufp;
+	unsigned int		buf_len, rx_bsize;
+
+	/*
+	 * lets start the buf size with 512 bytes. If this
+	 * if found to be insufficient, we can allocate
+	 * appropriate size in the next iteration.
+	 */
+	buf_len = 512;
+
+once_again:
+	bufp = kmem_zalloc(buf_len, KM_NOSLEEP);
+	if (bufp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_page83: "
+		    "request packet allocation for %d failed....",
+		    buf_len));
+		return (1);
+	}
+
+
+	bp->b_un.b_addr = bufp;
+	bp->b_flags = B_READ;
+	bp->b_bcount = buf_len;
+	bp->b_resid = 0;
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
+	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
+	if (pkt == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "!vhci_tpgs_page83: Failure returned from scsi_init_pkt"));
+		kmem_free((void *)bufp, buf_len);
+		return (1);
+	}
+
+	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
+	pkt->pkt_cdbp[1] = 0x1;
+	pkt->pkt_cdbp[2] = 0x83;
+	pkt->pkt_cdbp[3] = (unsigned char)((buf_len >> 8) & 0xff);
+	pkt->pkt_cdbp[4] = (unsigned char)(buf_len & 0xff);
+	pkt->pkt_time = 90;
+
+	if (vhci_do_scsi_cmd(pkt) == 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!vhci_tpgs_page83: vhci_do_scsi_cmd failed\n"));
+		kmem_free((void *)bufp, buf_len);
+		scsi_destroy_pkt(pkt);
+		return (1);
+	}
+
+	/*
+	 * Now lets check if the size that was provided was
+	 * sufficient. If not, allocate the appropriate size
+	 * and retry the command again.
+	 */
+	rx_bsize = (((bufp[2] & 0xff) << 8) | (bufp[3] & 0xff));
+	rx_bsize += 4;
+	if (rx_bsize > buf_len) {
+		/*
+		 * Need to allocate more buf and retry again
+		 */
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_page83: "
+		    "bufsize: %d greater than allocated buf: %d\n",
+		    rx_bsize, buf_len));
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "Retrying for size %d\n",
+		    rx_bsize));
+		kmem_free((void *)bufp, buf_len);
+		buf_len = (unsigned int)(rx_bsize);
+		goto once_again;
+	}
+
+	ptr = bufp;
+	ptr += 4; /* identification descriptor 0 */
+	end = bufp + rx_bsize;
+	while (ptr < end) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_tpgs_page83: "
+		    "desc[1/4/5/6/7]:%x %x %x %x %x\n",
+		    ptr[1], ptr[4], ptr[5], ptr[6], ptr[7]));
+		if ((ptr[1] & 0x0f) == 0x04) {
+			*rel_tgt_port = 0;
+			*rel_tgt_port |= ((ptr[6] & 0xff) << 8);
+			*rel_tgt_port |= (ptr[7] & 0xff);
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_page83: relative target port: %x\n",
+			    *rel_tgt_port));
+		} else if ((ptr[1] & 0x0f) == 0x05) {
+			*tgt_port = 0;
+			*tgt_port = ((ptr[6] & 0xff) << 8);
+			*tgt_port |= (ptr[7] & 0xff);
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_page83: target port: %x\n", *tgt_port));
+		} else if ((ptr[1] & 0x0f) == 0x06) {
+			*lu = 0;
+			*lu |= ((ptr[6] & 0xff)<< 8);
+			*lu |= (ptr[7] & 0xff);
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_tpgs_page83: logical unit: %x\n", *lu));
+		}
+		ptr += ptr[3] + 4;  /* next identification descriptor */
+	}
+	kmem_free((void *)bufp, buf_len);
+	scsi_destroy_pkt(pkt);
+	return (0);
+}
+
+#ifdef DEBUG
+static void
+print_buf(char *buf, int buf_size)
+{
+	int		i = 0, j;
+	int		loop, left;
+
+	loop = buf_size / 8;
+	left = buf_size % 8;
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!buf_size: %x loop: %x left: %x",
+	    buf_size, loop, left));
+
+	for (j = 0; j < loop; j++) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "!buf[%d-%d]: %x %x %x %x %x %x %x %x",
+		    i, i + 7, buf[i], buf[i+1], buf[i+2], buf[i+3],
+		    buf[i+4], buf[i+5], buf[i+6], buf[i+7]));
+		i += 8;
+	}
+
+	if (left) {
+		VHCI_DEBUG(4, (CE_CONT, NULL,
+		    "NOTICE: buf[%d-%d]:", i, i + left));
+		for (j = 0; j < left; j++) {
+			VHCI_DEBUG(4, (CE_CONT, NULL, " %x", buf[i + j]));
+		}
+		VHCI_DEBUG(4, (CE_CONT, NULL, "\n"));
+	}
+}
+#endif
+
+static int
+vhci_tpgs_report_target_groups(struct scsi_address *ap, struct buf *bp,
+	int rel_tgt_port, int tgt_port, int *pstate, int *preferred)
+{
+	struct scsi_pkt		*pkt;
+	char			*ptr, *end, *bufp, *mpapi_ptr;
+	unsigned int		rtpg_len = 0;
+	unsigned int		l_tgt_port = 0, tpgs_state = 0;
+	unsigned int		tgt_port_cnt = 0, lr_tgt_port = 0;
+	int			i, len;
+
+	/*
+	 * Start with buffer size of 512.
+	 * If this is found to be insufficient, required size
+	 * will be allocated and the command will be retried.
+	 */
+	len = 512;
+
+try_again:
+	bufp = kmem_zalloc(len, KM_NOSLEEP);
+	if (bufp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_report_target_groups:"
+		    " request packet allocation for %d failed....", len));
+		return (1);
+	}
+
+	bp->b_un.b_addr = bufp;
+	bp->b_flags = B_READ;
+	bp->b_bcount = len;
+	bp->b_resid = 0;
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP5,
+	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
+
+	if (pkt == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!vhci_tpgs_report_target_groups: scsi_init_pkt error\n"));
+		kmem_free((void *)bufp, len);
+		return (1);
+	}
+
+	pkt->pkt_cdbp[0] = SCMD_MAINTENANCE_IN;
+	pkt->pkt_cdbp[1] = SSVC_ACTION_GET_TARGET_PORT_GROUPS;
+	pkt->pkt_cdbp[6] = ((len >>  24) & 0xff);
+	pkt->pkt_cdbp[7] = ((len >> 16) & 0xff);
+	pkt->pkt_cdbp[8] = ((len >> 8) & 0xff);
+	pkt->pkt_cdbp[9] = len & 0xff;
+	pkt->pkt_time = 90;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL,
+	    "!vhci_tpgs_report_target_groups: sending target port group:"
+	    " cdb[6/7/8/9]: %x/%x/%x/%x\n", pkt->pkt_cdbp[6],
+	    pkt->pkt_cdbp[7], pkt->pkt_cdbp[8], pkt->pkt_cdbp[9]));
+	if (vhci_do_scsi_cmd(pkt) == 0) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
+		    " vhci_do_scsi_cmd failed\n"));
+		kmem_free((void *)bufp, len);
+		scsi_destroy_pkt(pkt);
+		return (1);
+	}
+	ptr = bufp;
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
+	    " returned from target"
+	    " port group: buf[0/1/2/3]: %x/%x/%x/%x\n",
+	    ptr[0], ptr[1], ptr[2], ptr[3]));
+	rtpg_len = (unsigned int)((0xff & ptr[0]) << 24);
+	rtpg_len |= (unsigned int)((0xff & ptr[1]) << 16);
+	rtpg_len |= (unsigned int)((0xff & ptr[2]) << 8);
+	rtpg_len |= (unsigned int)(0xff & ptr[3]);
+	rtpg_len += 4;
+	if (rtpg_len > len) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_target_groups:"
+		    " bufsize: %d greater than allocated buf: %d\n",
+		    rtpg_len, len));
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "Retrying for size %d\n",
+		    rtpg_len));
+		kmem_free((void *)bufp, len);
+		len = (unsigned int)(rtpg_len + 1);
+		goto try_again;
+	}
+#ifdef DEBUG
+	print_buf(bufp, rtpg_len);
+#endif
+	end = ptr + rtpg_len;
+	ptr += 4;
+	while (ptr < end) {
+		mpapi_ptr = ptr;
+		l_tgt_port = ((ptr[2] & 0xff) << 8) + (ptr[3] & 0xff);
+		tpgs_state = ptr[0] & 0x0f;
+		tgt_port_cnt = (ptr[7] & 0xff);
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups:"
+		    " tpgs state: %x"
+		    " tgt_group: %x count: %x\n", tpgs_state,
+		    l_tgt_port, tgt_port_cnt));
+		ptr += 8;
+		for (i = 0; i < tgt_port_cnt; i++) {
+			lr_tgt_port = 0;
+			lr_tgt_port |= ((ptr[2] & 0Xff) << 8);
+			lr_tgt_port |= (ptr[3] & 0xff);
+
+			if ((lr_tgt_port == rel_tgt_port) &&
+			    (l_tgt_port == tgt_port)) {
+				VHCI_DEBUG(4, (CE_NOTE, NULL,
+				    "!vhci_tpgs_report_tgt_groups:"
+				    " found tgt_port: %x rel_tgt_port:%x"
+				    " tpgs_state: %x\n", tgt_port, rel_tgt_port,
+				    tpgs_state));
+				/*
+				 * once we have the preferred flag
+				 * and a non-optimized state flag
+				 * we will get preferred flag  from the
+				 * report target groups
+				 */
+				if (tpgs_state == STD_ACTIVE_OPTIMIZED) {
+					*pstate = STD_ACTIVE_OPTIMIZED;
+					*preferred = PCLASS_PREFERRED;
+				} else if (tpgs_state ==
+				    STD_ACTIVE_NONOPTIMIZED) {
+					*pstate = STD_ACTIVE_NONOPTIMIZED;
+					*preferred = PCLASS_NONPREFERRED;
+				} else if (tpgs_state == STD_STANDBY) {
+					*pstate = STD_STANDBY;
+					*preferred = PCLASS_NONPREFERRED;
+				} else {
+					*pstate = STD_UNAVAILABLE;
+					*preferred = PCLASS_NONPREFERRED;
+				}
+				vhci_mpapi_update_tpg_data(ap, mpapi_ptr);
+				kmem_free((void *)bufp, len);
+				scsi_destroy_pkt(pkt);
+				return (0);
+			}
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "!vhci_tpgs_report_tgt_groups:"
+			    " tgt_port: %x rel_tgt_port:%x\n", tgt_port,
+			    rel_tgt_port));
+			ptr += 4;
+		}
+	}
+	*pstate = SCSI_PATH_INACTIVE;
+	*preferred = PCLASS_NONPREFERRED;
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_tpgs_report_tgt_groups: "
+	    "NO rel_TGTPRT MATCH!!! Assigning Default: state: %x "
+	    "preferred: %d\n", *pstate, *preferred));
+	kmem_free((void *)bufp, len);
+	scsi_destroy_pkt(pkt);
+	return (1);
+}
--- a/usr/src/uts/common/io/scsi/targets/st.c	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/io/scsi/targets/st.c	Mon Jun 23 13:41:43 2008 -0700
@@ -111,6 +111,14 @@
 
 #define	ONE_K	1024
 
+#define	MAX_SPACE_CNT(cnt) if (cnt >= 0) { \
+		if (cnt > MIN(SP_CNT_MASK, INT32_MAX)) \
+			return (EINVAL); \
+	} else { \
+		if (-(cnt) > MIN(SP_CNT_MASK, INT32_MAX)) \
+			return (EINVAL); \
+	} \
+
 /*
  * Global External Data Definitions
  */
@@ -122,8 +130,11 @@
  */
 static void *st_state;
 static char *const st_label = "st";
-static volatile dev_info_t *st_lastdev;
 static volatile int st_recov_sz = sizeof (recov_info);
+static const char mp_misconf[] = {
+	"St Tape is misconfigured, MPxIO enabled and "
+	"tape-command-recovery-disable set in st.conf\n"
+};
 
 #ifdef	__x86
 /*
@@ -241,6 +252,8 @@
 #ifdef STDEBUG
 static int st_soft_error_report_debug = 0;
 volatile int st_debug = 0;
+static volatile dev_info_t *st_lastdev;
+static kmutex_t st_debug_mutex;
 #endif
 
 #define	ST_MT02_NAME	"Emulex  MT02 QIC-11/24  "
@@ -317,6 +330,23 @@
 	"load and hold"		/* LD_LOAD | LD_HOLD	9 */
 };
 
+static const char *errstatenames[] = {
+	"COMMAND_DONE",
+	"COMMAND_DONE_ERROR",
+	"COMMAND_DONE_ERROR_RECOVERED",
+	"QUE_COMMAND",
+	"QUE_BUSY_COMMAND",
+	"QUE_SENSE",
+	"JUST_RETURN",
+	"COMMAND_DONE_EACCES",
+	"QUE_LAST_COMMAND",
+	"COMMAND_TIMEOUT",
+	"PATH_FAILED",
+	"DEVICE_RESET",
+	"DEVICE_TAMPER",
+	"ATTEMPT_RETRY"
+};
+
 const char *bogusID = "Unknown Media ID";
 
 /* default density offsets in the table above */
@@ -523,7 +553,7 @@
     struct scsi_arq_status *cmd);
 static void st_empty_error_stack(struct scsi_tape *un);
 static errstate st_decode_sense(struct scsi_tape *un, struct buf *bp, int amt,
-    struct scsi_status *, tapepos_t *);
+    struct scsi_arq_status *, tapepos_t *);
 static int st_report_soft_errors(dev_t dev, int flag);
 static void st_delayed_cv_broadcast(void *arg);
 static int st_check_media(dev_t dev, enum mtio_state state);
@@ -556,7 +586,7 @@
 static int st_check_cdb_for_need_to_reserve(struct scsi_tape *un, uchar_t *cdb);
 static int st_check_cmd_for_need_to_reserve(struct scsi_tape *un, uchar_t cmd,
     int count);
-static int st_take_ownership(struct scsi_tape *un);
+static int st_take_ownership(struct scsi_tape *un, ubufunc_t ubf);
 static int st_check_asc_ascq(struct scsi_tape *un);
 static int st_check_clean_bit(struct scsi_tape *un);
 static int st_check_alert_flags(struct scsi_tape *un);
@@ -576,20 +606,20 @@
 static int st_get_read_pos(struct scsi_tape *un, buf_t *bp);
 static int st_logical_block_locate(struct scsi_tape *un, ubufunc_t ubf,
     tapepos_t *pos, uint64_t lblk, uchar_t partition);
-static int st_mtfsf_ioctl(struct scsi_tape *un, int files);
-static int st_mtfsr_ioctl(struct scsi_tape *un, int count);
-static int st_mtbsf_ioctl(struct scsi_tape *un, int files);
-static int st_mtnbsf_ioctl(struct scsi_tape *un, int count);
-static int st_mtbsr_ioctl(struct scsi_tape *un, int num);
-static int st_mtfsfm_ioctl(struct scsi_tape *un, int cnt);
-static int st_mtbsfm_ioctl(struct scsi_tape *un, int cnt);
-static int st_backward_space_files(struct scsi_tape *un, int count,
+static int st_mtfsf_ioctl(struct scsi_tape *un, int64_t files);
+static int st_mtfsr_ioctl(struct scsi_tape *un, int64_t count);
+static int st_mtbsf_ioctl(struct scsi_tape *un, int64_t files);
+static int st_mtnbsf_ioctl(struct scsi_tape *un, int64_t count);
+static int st_mtbsr_ioctl(struct scsi_tape *un, int64_t num);
+static int st_mtfsfm_ioctl(struct scsi_tape *un, int64_t cnt);
+static int st_mtbsfm_ioctl(struct scsi_tape *un, int64_t cnt);
+static int st_backward_space_files(struct scsi_tape *un, int64_t count,
     int infront);
-static int st_forward_space_files(struct scsi_tape *un, int files);
+static int st_forward_space_files(struct scsi_tape *un, int64_t files);
 static int st_scenic_route_to_begining_of_file(struct scsi_tape *un,
     int32_t fileno);
 static int st_space_to_begining_of_file(struct scsi_tape *un);
-static int st_space_records(struct scsi_tape *un, int records);
+static int st_space_records(struct scsi_tape *un, int64_t records);
 static int st_get_media_identification(struct scsi_tape *un, ubufunc_t bufunc);
 static errstate st_command_recovery(struct scsi_tape *un, struct scsi_pkt *pkt,
     errstate onentry);
@@ -639,13 +669,14 @@
 
 #ifdef STDEBUG
 static void st_debug_cmds(struct scsi_tape *un, int com, int count, int wait);
+#endif /* STDEBUG */
 static char *st_dev_name(dev_t dev);
-#endif /* STDEBUG */
 
 #if !defined(lint)
 _NOTE(SCHEME_PROTECTS_DATA("unique per pkt",
     scsi_pkt buf uio scsi_cdb uscsi_cmd))
 _NOTE(SCHEME_PROTECTS_DATA("unique per pkt", scsi_extended_sense scsi_status))
+_NOTE(SCHEME_PROTECTS_DATA("unique per pkt", recov_info))
 _NOTE(SCHEME_PROTECTS_DATA("stable data", scsi_device))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(st_drivetype scsi_address))
 #endif
@@ -705,20 +736,24 @@
 
 	if ((e = mod_install(&modlinkage)) != 0) {
 		ddi_soft_state_fini(&st_state);
-	}
+	} else {
+#ifdef STDEBUG
+		mutex_init(&st_debug_mutex, NULL, MUTEX_DRIVER, NULL);
+#endif
 
 #if defined(__x86)
-	/* set the max physical address for iob allocs on x86 */
-	st_alloc_attr.dma_attr_addr_hi = st_max_phys_addr;
-
-	/*
-	 * set the sgllen for iob allocs on x86. If this is set less than
-	 * the number of pages the buffer will take (taking into account
-	 * alignment), it would force the allocator to try and allocate
-	 * contiguous pages.
-	 */
-	st_alloc_attr.dma_attr_sgllen = st_sgl_size;
+		/* set the max physical address for iob allocs on x86 */
+		st_alloc_attr.dma_attr_addr_hi = st_max_phys_addr;
+
+		/*
+		 * set the sgllen for iob allocs on x86. If this is set less
+		 * than the number of pages the buffer will take
+		 * (taking into account alignment), it would force the
+		 * allocator to try and allocate contiguous pages.
+		 */
+		st_alloc_attr.dma_attr_sgllen = st_sgl_size;
 #endif
+	}
 
 	return (e);
 }
@@ -732,6 +767,10 @@
 		return (e);
 	}
 
+#ifdef STDEBUG
+	mutex_destroy(&st_debug_mutex);
+#endif
+
 	ddi_soft_state_fini(&st_state);
 
 	return (e);
@@ -1055,6 +1094,12 @@
 		un->un_max_cdb_sz = CDB_GROUP4; /* optimistic default */
 	}
 
+	if (strcmp(ddi_driver_name(ddi_get_parent(ST_DEVINFO)), "scsi_vhci")) {
+		un->un_multipath = 0;
+	} else {
+		un->un_multipath = 1;
+	}
+
 	un->un_maxbsize = MAXBSIZE_UNKNOWN;
 
 	un->un_mediastate = MTIO_NONE;
@@ -1169,7 +1214,8 @@
 static int
 st_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 {
-	int 	instance;
+	int instance;
+	int result;
 	struct scsi_device *devp;
 	struct scsi_tape *un;
 	clock_t wait_cmds_complete;
@@ -1207,6 +1253,7 @@
 		    (void *)un);
 
 		if (((un->un_dp->options & ST_UNLOADABLE) == 0) ||
+		    ((un->un_rsvd_status & ST_APPLICATION_RESERVATIONS) != 0) ||
 		    (un->un_ncmds != 0) || (un->un_quef != NULL) ||
 		    (un->un_state != ST_STATE_CLOSED)) {
 			/*
@@ -1216,6 +1263,7 @@
 			 */
 			ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 			    "cannot unload instance %x\n", instance);
+			un->un_unit_attention_flags |= 4;
 			return (DDI_FAILURE);
 		}
 
@@ -1243,7 +1291,7 @@
 			 * to fail the detach till a user command fails
 			 * where after the detach will succead.
 			 */
-			(void) st_cmd(un, SCMD_TEST_UNIT_READY, 0, SYNC_CMD);
+			result = st_cmd(un, SCMD_TEST_UNIT_READY, 0, SYNC_CMD);
 			/*
 			 * After TUR un_state may be set to non-closed,
 			 * so reset it back.
@@ -1260,6 +1308,7 @@
 		 * if we are not at BOT then it is not safe to unload
 		 */
 		if ((un->un_dev) &&		/* Been opened since attach */
+		    (result != EACCES) &&	/* drive is use by somebody */
 		    (((un->un_pos.pmode == legacy) &&
 		    (un->un_pos.fileno > 0) ||	/* Known position not rewound */
 		    (un->un_pos.blkno != 0)) ||	/* Or within first file */
@@ -1271,6 +1320,7 @@
 			    " lgclblkno=0x%"PRIx64"\n", un->un_pos.pmode,
 			    un->un_pos.fileno, un->un_pos.blkno,
 			    un->un_pos.lgclblkno);
+			un->un_unit_attention_flags |= 4;
 			return (DDI_FAILURE);
 		}
 
@@ -1684,6 +1734,8 @@
 
 	un->un_suspend_pos.pmode = invalid;
 
+	st_add_recovery_info_to_pkt(un, un->un_rqs_bp, un->un_rqs);
+
 #ifdef	__x86
 	if (ddi_dma_alloc_handle(ST_DEVINFO, &st_contig_mem_dma_attr,
 	    DDI_DMA_SLEEP, NULL, &un->un_contig_mem_hdl) != DDI_SUCCESS) {
@@ -1863,7 +1915,7 @@
 		(void) st_reserve_release(un, reserved, st_uscsi_cmd);
 	}
 
-	un->un_unit_attention_flags = 1;
+	un->un_unit_attention_flags |= 1;
 
 	scsi_log(ST_DEVINFO, st_label, CE_NOTE, "?<%s>\n", dp->name);
 
@@ -2715,11 +2767,9 @@
 	 */
 	mutex_enter(ST_MUTEX);
 
-#ifdef	STDEBUG
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
 	    "st_open(node = %s dev = 0x%lx, flag = %d, otyp = %d)\n",
 	    st_dev_name(dev), *dev_p, flag, otyp);
-#endif
 
 	/*
 	 * All device accesss go thru st_strategy() where we check
@@ -2856,7 +2906,6 @@
 		}
 		un->un_err_resid = 0;
 		un->un_retry_ct = 0;
-		un->un_tran_retry_ct = 0;
 	}
 busy:
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
@@ -3349,6 +3398,8 @@
 	 */
 	if (((minor & (MT_BSD | MT_NOREWIND)) == MT_NOREWIND) &&
 	    (flag & FREAD) &&		/* reading or at least asked to */
+	    (un->un_mediastate == MTIO_INSERTED) &&	/* tape loaded */
+	    (un->un_pos.pmode != invalid) &&		/* XXX position known */
 	    ((un->un_pos.blkno != 0) && 		/* inside a file */
 	    (un->un_lastop != ST_OP_WRITE) &&		/* Didn't just write */
 	    (un->un_lastop != ST_OP_WEOF))) {		/* or write filemarks */
@@ -3369,7 +3420,7 @@
 					    SCSI_DEBUG,
 					    "st_close : EIO can't space\n");
 					err = EIO;
-					break;
+					goto error_out;
 				}
 				if (un->un_pos.eof >= ST_EOF_PENDING) {
 					un->un_pos.eof = ST_EOT_PENDING;
@@ -3382,6 +3433,7 @@
 				ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 				    "st_close: EIO can't space #2\n");
 				err = EIO;
+				goto error_out;
 			} else {
 				ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
 				    "st_close2: fileno=%x,blkno=%x,eof=%x\n",
@@ -3401,10 +3453,11 @@
 
 		case ST_EOT:
 		case ST_EOT_PENDING:
+		case ST_EOM:
 			/* nothing to do */
 			break;
 		default:
-			scsi_log(ST_DEVINFO, st_label, CE_PANIC,
+			ST_DEBUG(ST_DEVINFO, st_label, CE_PANIC,
 			    "Undefined state 0x%x", un->un_pos.eof);
 
 		}
@@ -3441,6 +3494,7 @@
 				ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 				    "st_close: EIO can't space #3\n");
 				err = EIO;
+				goto error_out;
 			} else {
 				un->un_pos.blkno = 0;
 				un->un_pos.eof = ST_EOT;
@@ -3483,6 +3537,7 @@
 				ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 				    "st_close : EIO can't wfm\n");
 				err = EIO;
+				goto error_out;
 			}
 			if ((un->un_dp->options & ST_REEL) &&
 			    (minor & MT_NOREWIND)) {
@@ -3491,6 +3546,7 @@
 					    SCSI_DEBUG,
 					    "st_close : EIO space fmk(-1)\n");
 					err = EIO;
+					goto error_out;
 				}
 				un->un_pos.eof = ST_NO_EOF;
 				/* fix up block number */
@@ -3519,7 +3575,10 @@
 	if (st_report_soft_errors_on_close &&
 	    (un->un_dp->options & ST_SOFT_ERROR_REPORTING) &&
 	    (last_state != ST_STATE_OFFLINE)) {
-		(void) st_report_soft_errors(dev, flag);
+		if (st_report_soft_errors(dev, flag)) {
+			err = EIO;
+			goto error_out;
+		}
 	}
 
 
@@ -3559,11 +3618,32 @@
 		 * the problems described above.
 		 */
 		if (un->un_sd->sd_inq->inq_ansi < 2) {
-			(void) st_cmd(un, SCMD_REWIND, 0, SYNC_CMD);
+			if (st_cmd(un, SCMD_REWIND, 0, SYNC_CMD)) {
+				err = EIO;
+			}
 		} else {
 			/* flush data for older drives per scsi spec. */
-			(void) st_cmd(un, SCMD_WRITE_FILE_MARK, 0, SYNC_CMD);
-			(void) st_cmd(un, SCMD_REWIND, 1, ASYNC_CMD);
+			if (st_cmd(un, SCMD_WRITE_FILE_MARK, 0, SYNC_CMD)) {
+				err = EIO;
+			} else if (st_cmd(un, SCMD_REWIND, 1, ASYNC_CMD)) {
+				err = EIO;
+			}
+		}
+		/*
+		 * Setting positions invalid in case the rewind doesn't
+		 * happen. Drives don't like to rewind if resets happen
+		 * they will tend to move back to where the rewind was
+		 * issued if a reset or something happens so that if a
+		 * write happens the data doesn't get clobbered.
+		 *
+		 * Not a big deal if the position is invalid when the
+		 * open occures it will do a read position.
+		 */
+		un->un_pos.pmode = invalid;
+		un->un_running.pmode = invalid;
+
+		if (err == EIO) {
+			goto error_out;
 		}
 	}
 
@@ -3575,6 +3655,8 @@
 		if (st_cmd(un, SCMD_LOAD, LD_UNLOAD, SYNC_CMD)) {
 			ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 			    "st_close : can't unload tape\n");
+			err = EIO;
+			goto error_out;
 		} else {
 			ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 			    "st_close : tape unloaded \n");
@@ -3590,7 +3672,7 @@
 	    (ST_RESERVE | ST_PRESERVE_RESERVE)) == ST_RESERVE) {
 		(void) st_reserve_release(un, ST_RELEASE, st_uscsi_cmd);
 	}
-
+error_out:
 	/*
 	 * clear up state
 	 */
@@ -3599,7 +3681,6 @@
 	un->un_lastop = ST_OP_NIL;
 	un->un_throttle = 1;	/* assume one request at time, for now */
 	un->un_retry_ct = 0;
-	un->un_tran_retry_ct = 0;
 	un->un_errno = 0;
 	un->un_swr_token = (opaque_t)NULL;
 	un->un_rsvd_status &= ~(ST_INIT_RESERVE);
@@ -3829,6 +3910,11 @@
 		rval = EINVAL;
 	}
 
+	if (st_recov_sz != sizeof (recov_info) && un->un_multipath) {
+		scsi_log(ST_DEVINFO, st_label, CE_WARN, mp_misconf);
+		rval = EFAULT;
+	}
+
 	if (rval != 0) {
 		un->un_errno = rval;
 		mutex_exit(ST_MUTEX);
@@ -3916,6 +4002,11 @@
 		rval = EINVAL;
 	}
 
+	if (st_recov_sz != sizeof (recov_info) && un->un_multipath) {
+		scsi_log(ST_DEVINFO, st_label, CE_WARN, mp_misconf);
+		rval = EFAULT;
+	}
+
 	if (rval != 0) {
 		un->un_errno = rval;
 		mutex_exit(ST_MUTEX);
@@ -4061,7 +4152,7 @@
 			un->un_pos.pmode = invalid;
 			goto b_done_err;
 		}
-		/* WTF un_restore_pos make invalid */
+		/* un_restore_pos make invalid */
 		un->un_state = ST_STATE_OPEN_PENDING_IO;
 		un->un_restore_pos = 0;
 	}
@@ -4373,14 +4464,15 @@
  * this routine spaces forward over filemarks
  */
 static int
-st_space_fmks(struct scsi_tape *un, long count)
+st_space_fmks(struct scsi_tape *un, int64_t count)
 {
 	int rval = 0;
 
 	ST_FUNC(ST_DEVINFO, st_space_fmks);
 
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_space_fmks(dev = 0x%lx, count = %ld)\n", un->un_dev, count);
+	    "st_space_fmks(dev = 0x%lx, count = %"PRIx64")\n",
+	    un->un_dev, count);
 
 	ASSERT(mutex_owned(ST_MUTEX));
 
@@ -4735,6 +4827,8 @@
 
 	mutex_enter(ST_MUTEX);
 
+	ASSERT(un->un_recov_buf_busy == 0);
+
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
 	    "st_ioctl(): fileno=%x, blkno=%x, eof=%x, state = %d, "
 	    "pe_flag = %d\n",
@@ -4897,6 +4991,15 @@
 		 * persistense. Fake the answer based on previous info.
 		 */
 		if (un->un_persistence) {
+			rval = 0;
+		} else {
+			rval = st_check_clean_bit(un);
+		}
+		if (rval == 0) {
+			/*
+			 * If zero is returned or in persistent mode,
+			 * use the old data.
+			 */
 			if ((un->un_HeadClean & (TAPE_ALERT_SUPPORTED |
 			    TAPE_SEQUENTIAL_SUPPORTED|TAPE_ALERT_NOT_SUPPORTED))
 			    != TAPE_ALERT_NOT_SUPPORTED) {
@@ -4906,13 +5009,6 @@
 			    TAPE_ALERT_STILL_DIRTY)) {
 				mtget->mt_flags |= MTF_TAPE_HEAD_DIRTY;
 			}
-			rval = 0;
-		} else {
-			rval = st_check_clean_bit(un);
-		}
-		if (rval == -1) {
-			rval = EIO;
-			goto exit;
 		} else {
 			mtget->mt_flags |= (ushort_t)rval;
 			rval = 0;
@@ -5239,7 +5335,7 @@
 		 */
 		(void) st_reserve_release(un, ST_RESERVE, st_uscsi_cmd);
 
-		rval = st_take_ownership(un);
+		rval = st_take_ownership(un, st_uscsi_cmd);
 
 		break;
 	}
@@ -5914,30 +6010,37 @@
 		break;
 
 	case MTFSF:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtfsf_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTFSR:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtfsr_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTBSF:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtbsf_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTNBSF:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtnbsf_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTBSR:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtbsr_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTBSSF:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtbsfm_ioctl(un, mtop->mt_count);
 		break;
 
 	case MTFSSF:
+		MAX_SPACE_CNT(mtop->mt_count);
 		rval = st_mtfsfm_ioctl(un, mtop->mt_count);
 		break;
 
@@ -6346,6 +6449,7 @@
 				    "Sending delayed start to st_runout()\n");
 				mutex_exit(ST_MUTEX);
 				(void) timeout(fnc, un, drv_usectohz(1000000));
+				mutex_enter(ST_MUTEX);
 			}
 			return;
 		}
@@ -6452,39 +6556,54 @@
 
 	if (status != TRAN_ACCEPT) {
 		ST_DO_KSTATS(bp, kstat_runq_back_to_waitq);
-		mutex_exit(ST_MUTEX);
+		ST_DEBUG(ST_DEVINFO, st_label, CE_WARN,
+		    "Unhappy transport packet status 0x%x\n", status);
 
 		if (status == TRAN_BUSY) {
-			/* if too many retries, fail the transport */
-			if (st_handle_start_busy(un, bp,
-			    ST_TRAN_BUSY_TIMEOUT, queued) == 0)
-				goto done;
+			pkt_info *pkti = BP_PKT(bp)->pkt_private;
+
+			/*
+			 * If command recovery is enabled and this isn't
+			 * a recovery command try command recovery.
+			 */
+			if (pkti->privatelen == sizeof (recov_info) &&
+			    bp != un->un_recov_buf) {
+				ST_RECOV(ST_DEVINFO, st_label, CE_WARN,
+				    "Command Recovery called on busy send\n");
+				if (st_command_recovery(un, BP_PKT(bp),
+				    ATTEMPT_RETRY) == JUST_RETURN) {
+					return;
+				}
+			} else {
+				mutex_exit(ST_MUTEX);
+				if (st_handle_start_busy(un, bp,
+				    ST_TRAN_BUSY_TIMEOUT, queued) == 0) {
+					mutex_enter(ST_MUTEX);
+					return;
+				}
+				/*
+				 * if too many retries, fail the transport
+				 */
+				mutex_enter(ST_MUTEX);
+			}
 		}
 		scsi_log(ST_DEVINFO, st_label, CE_WARN,
 		    "transport rejected %d\n", status);
 		bp->b_resid = bp->b_bcount;
 
-
-#ifndef __lock_lint
-		/*
-		 * warlock doesn't understand this potential
-		 * recursion?
-		 */
-		mutex_enter(ST_MUTEX);
 		ST_DO_KSTATS(bp, kstat_waitq_exit);
 		ST_DO_ERRSTATS(un, st_transerrs);
-		st_bioerror(bp, EIO);
-		st_set_pe_flag(un);
+		if ((bp == un->un_recov_buf) && (status == TRAN_BUSY)) {
+			st_bioerror(bp, EBUSY);
+		} else {
+			st_bioerror(bp, EIO);
+			st_set_pe_flag(un);
+		}
 		st_done_and_mutex_exit(un, bp);
-#endif
-	} else {
-		un->un_tran_retry_ct = 0;
-		mutex_exit(ST_MUTEX);
-	}
-
-done:
-
-	mutex_enter(ST_MUTEX);
+		mutex_enter(ST_MUTEX);
+	}
+
+	ASSERT(mutex_owned(ST_MUTEX));
 }
 
 /*
@@ -6495,6 +6614,8 @@
     clock_t timeout_interval, int queued)
 {
 
+	pkt_info *pktinfo = BP_PKT(bp)->pkt_private;
+
 	ST_FUNC(ST_DEVINFO, st_handle_start_busy);
 
 	mutex_enter(ST_MUTEX);
@@ -6507,7 +6628,7 @@
 	 * making sure this is the last on the runq, if it is not, we have
 	 * to fail
 	 */
-	if (((int)un->un_tran_retry_ct++ > st_retry_count) ||
+	if ((pktinfo->str_retry_cnt++ > st_retry_count) ||
 	    ((queued) && (un->un_runql != bp))) {
 		mutex_exit(ST_MUTEX);
 		return (-1);
@@ -6647,7 +6768,9 @@
 static void
 st_done_and_mutex_exit(struct scsi_tape *un, struct buf *bp)
 {
-	int	pe_flagged = 0;
+	int pe_flagged = 0;
+	struct scsi_pkt *pkt = BP_PKT(bp);
+	pkt_info *pktinfo = pkt->pkt_private;
 
 	ASSERT(MUTEX_HELD(&un->un_sd->sd_mutex));
 #if !defined(lint)
@@ -6665,7 +6788,7 @@
 
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
 	    "st_done_and_mutex_exit(): cmd=0x%x count=%ld resid=%ld  flags="
-	    "0x%x\n", (uchar_t)*((caddr_t)(BP_PKT(bp))->pkt_cdbp), bp->b_bcount,
+	    "0x%x\n", pkt->pkt_cdbp[0], bp->b_bcount,
 	    bp->b_resid, bp->b_flags);
 
 
@@ -6701,22 +6824,24 @@
 		st_start(un);
 	}
 
+	un->un_retry_ct = max(pktinfo->pkt_retry_cnt, pktinfo->str_retry_cnt);
+
 	if (bp == un->un_sbufp && (bp->b_flags & B_ASYNC)) {
 		/*
 		 * Since we marked this ourselves as ASYNC,
 		 * there isn't anybody around waiting for
 		 * completion any more.
 		 */
-		uchar_t *cdb = (uchar_t *)bp->b_forw;
-		if (*cdb == SCMD_READ || *cdb == SCMD_WRITE) {
+		uchar_t *cmd = pkt->pkt_cdbp;
+		if (*cmd == SCMD_READ || *cmd == SCMD_WRITE) {
 			bp->b_un.b_addr = (caddr_t)0;
 		}
-		scsi_log(ST_DEVINFO, st_label, CE_NOTE,
+		ST_DEBUG(ST_DEVINFO, st_label, CE_NOTE,
 		    "st_done_and_mutex_exit(async): freeing pkt\n");
 		st_print_cdb(ST_DEVINFO, st_label, CE_NOTE,
-		    "CDB sent with B_ASYNC",  (caddr_t)cdb);
-		if (BP_PKT(bp)) {
-			scsi_destroy_pkt(BP_PKT(bp));
+		    "CDB sent with B_ASYNC",  (caddr_t)cmd);
+		if (pkt) {
+			scsi_destroy_pkt(pkt);
 		}
 		un->un_sbuf_busy = 0;
 		cv_signal(&un->un_sbuf_cv);
@@ -6751,8 +6876,8 @@
 	ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
 	    "st_done_and_mutex_exit: freeing pkt\n");
 
-	if (BP_PKT(bp)) {
-		scsi_destroy_pkt(BP_PKT(bp));
+	if (pkt) {
+		scsi_destroy_pkt(pkt);
 	}
 
 	biodone(bp);
@@ -7120,8 +7245,6 @@
 
 	ST_FUNC(ST_DEVINFO, st_get_density);
 
-	ST_FUNC(ST_DEVINFO, st_get_density);
-
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG,
 	    "st_get_density(un = 0x%p)\n", (void*)un);
 
@@ -8128,6 +8251,7 @@
 	int r;
 	char	cdb[CDB_GROUP0];
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 
 	ST_FUNC(ST_DEVINFO, st_gen_mode_sense);
 
@@ -8142,8 +8266,10 @@
 	com->uscsi_cdblen = CDB_GROUP0;
 	com->uscsi_bufaddr = (caddr_t)page_data;
 	com->uscsi_buflen = page_size;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
-	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 
 	r = ubf(un, com, FKIOCTL);
 	kmem_free(com, sizeof (*com));
@@ -8163,6 +8289,7 @@
 	int r;
 	char cdb[CDB_GROUP0];
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 
 	ST_FUNC(ST_DEVINFO, st_gen_mode_select);
 
@@ -8193,8 +8320,10 @@
 	com->uscsi_cdblen = CDB_GROUP0;
 	com->uscsi_bufaddr = (caddr_t)page_data;
 	com->uscsi_buflen = page_size;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
-	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE;
+	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_WRITE;
 
 	r = ubf(un, com, FKIOCTL);
 
@@ -8208,6 +8337,7 @@
 	int rval;
 	char cdb[CDB_GROUP0];
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 
 	ST_FUNC(ST_DEVINFO, st_read_block_limits);
 
@@ -8220,8 +8350,10 @@
 	com->uscsi_cdblen = CDB_GROUP0;
 	com->uscsi_bufaddr = (caddr_t)read_blk;
 	com->uscsi_buflen = sizeof (struct read_blklim);
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
-	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
 	if (com->uscsi_status || com->uscsi_resid) {
@@ -8239,6 +8371,7 @@
 	int rval;
 	char cdb[CDB_GROUP1];
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 
 	ST_FUNC(ST_DEVINFO, st_report_density_support);
 
@@ -8253,8 +8386,10 @@
 	com->uscsi_cdblen = CDB_GROUP1;
 	com->uscsi_bufaddr = (caddr_t)density_data;
 	com->uscsi_buflen = buflen;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
-	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
 	if (com->uscsi_status || com->uscsi_resid) {
@@ -8272,6 +8407,7 @@
 	int rval;
 	char cdb[CDB_GROUP5];
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 	uint32_t allo_length;
 
 	ST_FUNC(ST_DEVINFO, st_report_supported_operation);
@@ -8282,7 +8418,7 @@
 
 	bzero(cdb, CDB_GROUP5);
 	cdb[0] = (char)SCMD_MAINTENANCE_IN;
-	cdb[1] = 0x0c; /* service action */
+	cdb[1] = SSVC_ACTION_GET_SUPPORTED_OPERATIONS;
 	if (service_action) {
 		cdb[2] = (char)(ONE_COMMAND_DATA_FORMAT | 0x80); /* RCTD */
 		cdb[4] = (service_action & 0xff00) >> 8;
@@ -8301,8 +8437,10 @@
 	com->uscsi_cdblen = CDB_GROUP5;
 	com->uscsi_bufaddr = (caddr_t)oper_data;
 	com->uscsi_buflen = allo_length;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
-	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	com->uscsi_flags = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
 	if (com->uscsi_status) {
@@ -8326,8 +8464,6 @@
 
 	ST_FUNC(ST_DEVINFO, st_change_block_size);
 
-	ST_FUNC(ST_DEVINFO, st_change_block_size);
-
 	current = kmem_zalloc(MSIZE, KM_SLEEP);
 
 	/*
@@ -8425,13 +8561,16 @@
 	struct uscsi_cmd *ucmd;
 	recov_info *ri;
 	int tval = 0;
-	uint64_t count;
-	uint32_t additional;
+	int64_t count;
+	uint32_t additional = 0;
+	uint32_t address = 0;
+	union scsi_cdb *ucdb;
 	int flags = 0;
 	int cdb_len = CDB_GROUP0; /* default */
 	uchar_t com;
 	char fixbit;
 	char short_fm = 0;
+	optype prev_op = un->un_lastop;
 	int stat_size =
 	    (un->un_arq_enabled ? sizeof (struct scsi_arq_status) : 1);
 
@@ -8586,7 +8725,22 @@
 
 		case SCMD_SPACE_G4:
 			cdb_len = CDB_GROUP4;
-		case SCMD_SPACE: /* FALL THROUGH */
+			fixbit = SPACE_TYPE(bp->b_bcount);
+			count = SPACE_CNT(bp->b_bcount);
+			ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
+			    " %s space %s %"PRId64" from file %d blk %d\n",
+			    bp->b_bcount & SP_BACKSP ? "backward" : "forward",
+			    space_strs[fixbit & 7], count,
+			    un->un_pos.fileno, un->un_pos.blkno);
+			address = (count >> 48) & 0x1fff;
+			additional = (count >> 16) & 0xffffffff;
+			count &= 0xffff;
+			count <<= 16;
+			un->un_lastop = ST_OP_CTL;
+			tval = un->un_dp->space_timeout;
+			break;
+
+		case SCMD_SPACE:
 			fixbit = SPACE_TYPE(bp->b_bcount);
 			count = SPACE_CNT(bp->b_bcount);
 			ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
@@ -8594,7 +8748,6 @@
 			    bp->b_bcount & SP_BACKSP ? "backward" : "forward",
 			    space_strs[fixbit & 7], count,
 			    un->un_pos.fileno, un->un_pos.blkno);
-			additional = count >> 32;
 			count &= 0xffffffff;
 			un->un_lastop = ST_OP_CTL;
 			tval = un->un_dp->space_timeout;
@@ -8729,6 +8882,7 @@
 			    "Unhandled scsi command 0x%x in st_make_cmd()\n",
 			    com);
 		}
+
 		pkt = scsi_init_pkt(ROUTE, NULL, allocbp, cdb_len, stat_size,
 		    st_recov_sz, 0, func, (caddr_t)un);
 		if (pkt == NULL) {
@@ -8750,15 +8904,15 @@
 
 	}
 
-
-	(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
-	    com, 0, (uint_t)count, additional);
+	ucdb = (union scsi_cdb *)pkt->pkt_cdbp;
+
+	(void) scsi_setup_cdb(ucdb, com, address, (uint_t)count, additional);
 	FILL_SCSI1_LUN(un->un_sd, pkt);
 	/*
 	 * Initialize the SILI/Fixed bits of the byte 1 of cdb.
 	 */
-	((union scsi_cdb *)(pkt->pkt_cdbp))->t_code = fixbit;
-	((union scsi_cdb *)pkt->pkt_cdbp)->g0_vu_1 = short_fm;
+	ucdb->t_code = fixbit;
+	ucdb->g0_vu_1 = short_fm;
 	pkt->pkt_flags = flags;
 
 	ASSERT(tval);
@@ -8771,6 +8925,24 @@
 
 	st_add_recovery_info_to_pkt(un, bp, pkt);
 
+	/*
+	 * If we just write data to tape and did a command that doesn't
+	 * change position, we still need to write a filemark.
+	 */
+	if ((prev_op == ST_OP_WRITE) || (prev_op == ST_OP_WEOF)) {
+		recov_info *rcvi = pkt->pkt_private;
+		cmd_attribute const *atrib;
+
+		if (rcvi->privatelen == sizeof (recov_info)) {
+			atrib = rcvi->cmd_attrib;
+		} else {
+			atrib = st_lookup_cmd_attribute(com);
+		}
+		if (atrib->chg_tape_direction == DIR_NONE) {
+			un->un_lastop = prev_op;
+		}
+	}
+
 exit:
 	ASSERT(mutex_owned(ST_MUTEX));
 }
@@ -8913,7 +9085,6 @@
 			 * not good, we don't want to requeue something after
 			 * another.
 			 */
-			mutex_exit(ST_MUTEX);
 			goto done_error;
 		} else {
 			un->un_runqf = bp;
@@ -8930,46 +9101,52 @@
 
 	if (status != TRAN_ACCEPT) {
 		ST_DO_KSTATS(bp, kstat_runq_back_to_waitq);
-		mutex_exit(ST_MUTEX);
 
 		if (status == TRAN_BUSY) {
+			pkt_info *pkti = BP_PKT(bp)->pkt_private;
+
+			if (pkti->privatelen == sizeof (recov_info) &&
+			    un->un_unit_attention_flags &&
+			    bp != un->un_recov_buf) {
+			un->un_unit_attention_flags = 0;
+				ST_RECOV(ST_DEVINFO, st_label, CE_WARN,
+				    "Command Recovery called on busy resend\n");
+				if (st_command_recovery(un, BP_PKT(bp),
+				    ATTEMPT_RETRY) == JUST_RETURN) {
+					mutex_exit(ST_MUTEX);
+					return;
+				}
+			}
+			mutex_exit(ST_MUTEX);
 			if (st_handle_intr_busy(un, bp,
 			    ST_TRAN_BUSY_TIMEOUT) == 0)
 				return;	/* timeout is setup again */
-		}
-
-	} else {
-		un->un_tran_retry_ct = 0;
+			mutex_enter(ST_MUTEX);
+		}
+
+done_error:
+		ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
+		    "restart transport rejected\n");
+		bp->b_resid = bp->b_bcount;
+
+		if (un->un_last_throttle) {
+			un->un_throttle = un->un_last_throttle;
+		}
+		if (status != TRAN_ACCEPT) {
+			ST_DO_ERRSTATS(un, st_transerrs);
+		}
+		ST_DO_KSTATS(bp, kstat_waitq_exit);
+		ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
+		    "busy restart aborted\n");
+		st_set_pe_flag(un);
+		st_bioerror(bp, EIO);
+		st_done_and_mutex_exit(un, bp);
+	} else {
 		if (un->un_last_throttle) {
 			un->un_throttle = un->un_last_throttle;
 		}
 		mutex_exit(ST_MUTEX);
-		return;
-	}
-
-done_error:
-	ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
-	    "restart transport rejected\n");
-	bp->b_resid = bp->b_bcount;
-
-#ifndef __lock_lint
-	/*
-	 * warlock doesn't understand this potential
-	 * recursion?
-	 */
-	mutex_enter(ST_MUTEX);
-	if (un->un_last_throttle) {
-		un->un_throttle = un->un_last_throttle;
-	}
-	if (status != TRAN_ACCEPT)
-		ST_DO_ERRSTATS(un, st_transerrs);
-	ST_DO_KSTATS(bp, kstat_waitq_exit);
-	st_set_pe_flag(un);
-	st_bioerror(bp, EIO);
-	st_done_and_mutex_exit(un, bp);
-#endif
-	ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
-	    "busy restart aborted\n");
+	}
 }
 
 /*
@@ -9377,8 +9554,7 @@
 			ASSERT(pkt == un->un_rqs);
 			ASSERT(un->un_state == ST_STATE_SENSING);
 			un->un_state = un->un_laststate;
-			((recov_info *)un->un_rqs->pkt_private)->cmd_bp =
-			    un->un_rqs_bp;
+			rcv->cmd_bp = un->un_rqs_bp;
 			ST_DO_ERRSTATS(un, st_transerrs);
 			action = COMMAND_DONE_ERROR;
 		} else {
@@ -9406,8 +9582,7 @@
 			 * to requeue it
 			 */
 			pkt = BP_PKT(bp);
-			((recov_info *)un->un_rqs->pkt_private)->cmd_bp =
-			    un->un_rqs_bp;
+			rcv->cmd_bp = un->un_rqs_bp;
 			/*
 			 * some actions are based on un_state, hence
 			 * restore the state st was in before ST_STATE_SENSING.
@@ -9456,6 +9631,22 @@
 	}
 
 	/*
+	 * check for undetected path failover.
+	 */
+	if ((un->un_multipath) &&
+	    (un->un_last_path_instance != pkt->pkt_path_instance)) {
+		if (un->un_state > ST_STATE_OPENING) {
+			ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
+			    "Failover detected, action is %s\n",
+			    errstatenames[action]);
+			if (action == COMMAND_DONE) {
+				action = PATH_FAILED;
+			}
+		}
+		un->un_last_path_instance = pkt->pkt_path_instance;
+	}
+
+	/*
 	 * Restore old state if we were sensing.
 	 */
 	if (un->un_state == ST_STATE_SENSING && action != QUE_SENSE) {
@@ -9463,8 +9654,8 @@
 	}
 
 	ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_intr: pkt=%p, bp=%p, action=%x, status=%x\n",
-	    (void *)pkt, (void *)bp, action, SCBP_C(pkt));
+	    "st_intr: pkt=%p, bp=%p, action=%s, status=%x\n",
+	    (void *)pkt, (void *)bp, errstatenames[action], SCBP_C(pkt));
 
 again:
 	switch (action) {
@@ -9628,6 +9819,10 @@
 	case DEVICE_RESET:
 	case DEVICE_TAMPER:
 	case ATTEMPT_RETRY:
+	case PATH_FAILED:
+		ST_RECOV(ST_DEVINFO, st_label, CE_WARN,
+		    "Command Recovery called on %s status\n",
+		    errstatenames[action]);
 		action = st_command_recovery(un, pkt, action);
 		goto again;
 
@@ -9688,8 +9883,8 @@
 		 * reduce probe time for nonexistant devices.
 		 */
 		if ((un->un_laststate > ST_STATE_OPENING) &&
-		    ((int)un->un_retry_ct < st_selection_retry_count)) {
-/* XXX check retriable? */
+		    (rinfo->pkt_retry_cnt < st_selection_retry_count)) {
+			/* XXX check retriable? */
 			rval = QUE_COMMAND;
 		}
 		ST_DO_ERRSTATS(un, st_transerrs);
@@ -9773,7 +9968,7 @@
 	}
 
 
-	if ((int)un->un_retry_ct++ < st_retry_count) {
+	if (rinfo->pkt_retry_cnt++ < st_retry_count) {
 		if (un->un_pwr_mgmt == ST_PWR_SUSPENDED) {
 			rval = QUE_COMMAND;
 		} else if (bp == un->un_sbufp) {
@@ -9824,6 +10019,7 @@
 
 	int queued;
 	int rval = 0;
+	pkt_info *pktinfo = BP_PKT(bp)->pkt_private;
 
 	mutex_enter(ST_MUTEX);
 
@@ -9846,7 +10042,7 @@
 	 * is the last we are in trouble anyway, as we are in the interrupt
 	 * context here.
 	 */
-	if (((int)un->un_tran_retry_ct++ > st_retry_count) ||
+	if ((pktinfo->str_retry_cnt++ > st_retry_count) ||
 	    ((un->un_runqf != bp) && (un->un_runql != bp) && (queued))) {
 		rval = -1;
 		goto exit;
@@ -10188,6 +10384,7 @@
 	struct scsi_pkt *pkt = BP_PKT(bp);
 	struct scsi_pkt *rqpkt = un->un_rqs;
 	struct scsi_arq_status arqstat;
+	recov_info *rcif = pkt->pkt_private;
 
 	errstate rval = COMMAND_DONE_ERROR;
 	int amt;
@@ -10200,9 +10397,13 @@
 	    "st_handle_sense()\n");
 
 	if (SCBP(rqpkt)->sts_busy) {
-		ST_DEBUG4(ST_DEVINFO, st_label, CE_WARN,
-		    "busy unit on request sense\n");
-		if ((int)un->un_retry_ct++ < st_retry_count) {
+		if (rcif->privatelen == sizeof (recov_info)) {
+			ST_RECOV(ST_DEVINFO, st_label, CE_WARN,
+			    "Attempt recovery of busy unit on request sense\n");
+			rval = ATTEMPT_RETRY;
+		} else if (rcif->pkt_retry_cnt++ < st_retry_count) {
+			ST_DEBUG4(ST_DEVINFO, st_label, CE_WARN,
+			    "Retry busy unit on request sense\n");
 			rval = QUE_BUSY_COMMAND;
 		}
 		return (rval);
@@ -10212,6 +10413,15 @@
 		return (rval);
 	}
 
+	/*
+	 * Make sure there is sense data to look at.
+	 */
+	if ((rqpkt->pkt_state & (STATE_GOT_BUS | STATE_GOT_TARGET |
+	    STATE_SENT_CMD | STATE_GOT_STATUS)) != (STATE_GOT_BUS |
+	    STATE_GOT_TARGET | STATE_SENT_CMD | STATE_GOT_STATUS)) {
+		return (rval);
+	}
+
 	/* was there enough data? */
 	amt = (int)MAX_SENSE_LENGTH - rqpkt->pkt_resid;
 	if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 ||
@@ -10235,7 +10445,7 @@
 	 * copy one arqstat entry in the sense data buffer
 	 */
 	st_update_error_stack(un, pkt, &arqstat);
-	return (st_decode_sense(un, bp, amt, SCBP(rqpkt), pos));
+	return (st_decode_sense(un, bp, amt, &arqstat, pos));
 }
 
 static errstate
@@ -10312,15 +10522,15 @@
 	 */
 	st_update_error_stack(un, pkt, arqstat);
 
-	return (st_decode_sense(un, bp, amt, &arqstat->sts_rqpkt_status, pos));
+	return (st_decode_sense(un, bp, amt, arqstat, pos));
 }
 
 static errstate
 st_decode_sense(struct scsi_tape *un, struct buf *bp, int amt,
-    struct scsi_status *statusp, tapepos_t *pos)
+    struct scsi_arq_status *statusp, tapepos_t *pos)
 {
 	struct scsi_pkt *pkt = BP_PKT(bp);
-	recov_info *ri = (recov_info *)pkt->pkt_private;
+	recov_info *ri = pkt->pkt_private;
 	errstate rval = COMMAND_DONE_ERROR;
 	cmd_attribute const *attrib;
 	long resid;
@@ -10372,8 +10582,8 @@
 	ST_CDB(ST_DEVINFO, "st_decode_sense failed CDB",
 	    (caddr_t)&CDBP(pkt)->scc_cmd);
 
-	ST_SENSE(ST_DEVINFO, "st_decode_sense sense data", (caddr_t)sensep,
-	    sizeof (*sensep));
+	ST_SENSE(ST_DEVINFO, "st_decode_sense sense data", (caddr_t)statusp,
+	    sizeof (*statusp));
 
 	/* for normal I/O check extract the resid values. */
 	if (bp != un->un_sbufp && bp != un->un_recov_buf) {
@@ -10417,6 +10627,7 @@
 		    (CDBP(pkt)->scc_cmd == SCMD_LOCATE) ||
 		    (CDBP(pkt)->scc_cmd == SCMD_LOCATE_G4) ||
 		    (CDBP(pkt)->scc_cmd == SCMD_SPACE) ||
+		    (CDBP(pkt)->scc_cmd == SCMD_SPACE_G4) ||
 		    (CDBP(pkt)->scc_cmd == SCMD_WRITE_FILE_MARK)) {
 			resid =
 			    (sensep->es_info_1 << 24) |
@@ -10593,14 +10804,6 @@
 	case KEY_MEDIUM_ERROR:
 		ST_DO_ERRSTATS(un, st_harderrs);
 		severity = SCSI_ERR_FATAL;
-
-		/*
-		 * for (buffered) writes, a medium error must be fatal
-		 */
-		if (CDBP(pkt)->scc_cmd != SCMD_WRITE) {
-			rval = COMMAND_DONE_ERROR_RECOVERED;
-		}
-
 check_keys:
 		/*
 		 * attempt to process the keys in the presence of
@@ -10676,6 +10879,7 @@
 			un->un_status = SUN_KEY_EOT;
 			pos->eof = ST_EOT;
 			rval = COMMAND_DONE_ERROR;
+			un->un_running.pmode = invalid;
 			st_set_pe_flag(un);
 			goto check_keys;
 		} else if (bp != un->un_sbufp &&
@@ -10724,7 +10928,7 @@
 		ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
 		    "KEY_UNIT_ATTENTION : un_state = %d\n", un->un_state);
 
-		un->un_unit_attention_flags = 1;
+		un->un_unit_attention_flags |= 1;
 		/*
 		 * If we have detected a Bus Reset and the tape
 		 * drive has been reserved.
@@ -10746,7 +10950,7 @@
 		if (bp == un->un_recov_buf) {
 			severity = SCSI_ERR_INFO;
 			if (attrib->retriable &&
-			    (int)un->un_retry_ct++ < st_retry_count) {
+			    ri->pkt_retry_cnt++ < st_retry_count) {
 				rval = QUE_COMMAND;
 			} else {
 				rval = COMMAND_DONE_ERROR;
@@ -10764,11 +10968,18 @@
 		 */
 		if ((un->un_rsvd_status &
 		    ST_APPLICATION_RESERVATIONS) != 0) {
+			/*
+			 * RESERVATIONS PREEMPTED
+			 * With MPxIO this could be a fail over? XXX
+			 */
 			if (ST_RQSENSE->es_add_code == 0x2a &&
 			    ST_RQSENSE->es_qual_code == 0x03) {
 				severity = SCSI_ERR_INFO;
 				rval = COMMAND_DONE_ERROR;
 				pos->pmode = invalid;
+			/*
+			 * RESERVATIONS RELEASED
+			 */
 			} else if (ST_RQSENSE->es_add_code == 0x2a &&
 			    ST_RQSENSE->es_qual_code == 0x04) {
 				rval = COMMAND_DONE;
@@ -10793,7 +11004,7 @@
 			}
 
 			if (attrib->retriable &&
-			    (int)un->un_retry_ct++ < st_retry_count) {
+			    ri->pkt_retry_cnt++ < st_retry_count) {
 				rval = QUE_COMMAND;
 			} else if (rval == DEVICE_RESET) {
 				break;
@@ -10805,7 +11016,8 @@
 		 * This is the result of a reset clearing settings or
 		 * another initiator changing what we set.
 		 */
-		} else if (ST_RQSENSE->es_add_code == 0x2a) {
+		}
+		if (ST_RQSENSE->es_add_code == 0x2a) {
 			if (ST_RQSENSE->es_qual_code == 0x1) {
 				/* Error recovery will modeselect and retry. */
 				rval = DEVICE_TAMPER;
@@ -10867,7 +11079,7 @@
 		 */
 		if (sensep->es_add_code  == 0x04 &&
 		    sensep->es_qual_code == 0x01 &&
-		    un->un_retry_ct++ < st_retry_count) {
+		    ri->pkt_retry_cnt++ < st_retry_count) {
 			rval = QUE_COMMAND;
 			severity = SCSI_ERR_INFO;
 		} else {
@@ -10901,7 +11113,7 @@
 		/* XXX Do drives return this when they see a lost light? */
 		/* Testing would say yes */
 
-		if (un->un_retry_ct++ < st_retry_count) {
+		if (ri->pkt_retry_cnt++ < st_retry_count) {
 			rval = ATTEMPT_RETRY;
 			severity = SCSI_ERR_RETRYABLE;
 			goto check_keys;
@@ -10973,6 +11185,7 @@
 st_handle_intr_retry_lcmd(struct scsi_tape *un, struct buf *bp)
 {
 	int status = TRAN_ACCEPT;
+	pkt_info *pktinfo = BP_PKT(bp)->pkt_private;
 
 	mutex_enter(ST_MUTEX);
 
@@ -10989,7 +11202,7 @@
 	 * is the last we are in trouble anyway, as we are in the interrupt
 	 * context here.
 	 */
-	if (((int)un->un_retry_ct > st_retry_count) ||
+	if ((pktinfo->pkt_retry_cnt > st_retry_count) ||
 	    ((un->un_runqf != bp) && (un->un_runql != bp))) {
 		goto exit;
 	}
@@ -11010,7 +11223,6 @@
 	status = st_transport(un, BP_PKT(bp));
 
 	if (status == TRAN_ACCEPT) {
-		un->un_tran_retry_ct = 0;
 		if (un->un_last_throttle) {
 			un->un_throttle = un->un_last_throttle;
 		}
@@ -11090,6 +11302,9 @@
 st_check_error(struct scsi_tape *un, struct scsi_pkt *pkt)
 {
 	errstate action;
+	recov_info *rcvi = pkt->pkt_private;
+	buf_t *bp = rcvi->cmd_bp;
+	struct scsi_arq_status *stat = (struct scsi_arq_status *)pkt->pkt_scbp;
 
 	ST_FUNC(ST_DEVINFO, st_check_error);
 
@@ -11097,12 +11312,47 @@
 
 	ST_DEBUG3(ST_DEVINFO, st_label, SCSI_DEBUG, "st_check_error()\n");
 
-	if (SCBP_C(pkt) == STATUS_RESERVATION_CONFLICT) {
-		action = COMMAND_DONE_EACCES;
-		un->un_rsvd_status |= ST_RESERVATION_CONFLICT;
-	} else if (SCBP(pkt)->sts_busy) {
+	switch (SCBP_C(pkt)) {
+	case STATUS_RESERVATION_CONFLICT:
+		/*
+		 * Command recovery is enabled, not just opening,
+		 * we had the drive reserved and we thing its ours.
+		 * Call recovery to attempt to take it back.
+		 */
+		if ((rcvi->privatelen == sizeof (recov_info)) &&
+		    (bp != un->un_recov_buf) &&
+		    (un->un_state > ST_STATE_OPEN_PENDING_IO) &&
+		    ((un->un_rsvd_status & (ST_RESERVE |
+		    ST_APPLICATION_RESERVATIONS)) != 0)) {
+			action = ATTEMPT_RETRY;
+			un->un_rsvd_status |= ST_LOST_RESERVE;
+		} else {
+			action = COMMAND_DONE_EACCES;
+			un->un_rsvd_status |= ST_RESERVATION_CONFLICT;
+		}
+		break;
+
+	case STATUS_BUSY:
 		ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG, "unit busy\n");
-		if ((int)un->un_retry_ct++ < st_retry_count) {
+		if (rcvi->privatelen == sizeof (recov_info) &&
+		    un->un_multipath && (pkt->pkt_state == (STATE_GOT_BUS |
+		    STATE_GOT_TARGET | STATE_SENT_CMD | STATE_GOT_STATUS))) {
+			/*
+			 * Status returned by scsi_vhci indicating path
+			 * has failed over.
+			 */
+			action = PATH_FAILED;
+			break;
+		}
+		/* FALLTHRU */
+	case STATUS_QFULL:
+		if (rcvi->privatelen == sizeof (recov_info)) {
+			/*
+			 * If recovery is inabled use it instead of
+			 * blind reties.
+			 */
+			action = ATTEMPT_RETRY;
+		} else if (rcvi->pkt_retry_cnt++ < st_retry_count) {
 			action = QUE_BUSY_COMMAND;
 		} else if ((un->un_rsvd_status &
 		    (ST_RESERVE | ST_APPLICATION_RESERVATIONS)) == 0) {
@@ -11117,21 +11367,45 @@
 			(void) st_reset(un, RESET_ALL);
 			action = COMMAND_DONE_ERROR;
 		}
-	} else if (SCBP(pkt)->sts_chk) {
+		break;
+
+	case STATUS_CHECK:
+	case STATUS_TERMINATED:
 		/*
 		 * we should only get here if the auto rqsense failed
 		 * thru a uscsi cmd without autorequest sense
 		 * so we just try again
 		 */
-		action = QUE_SENSE;
-	} else if (SCBP(pkt)->sts_vu7) {
+		if (un->un_arq_enabled &&
+		    stat->sts_rqpkt_reason == CMD_CMPLT &&
+		    (stat->sts_rqpkt_state & (STATE_GOT_BUS |
+		    STATE_GOT_TARGET | STATE_SENT_CMD | STATE_GOT_STATUS)) ==
+		    (STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD |
+		    STATE_GOT_STATUS)) {
+
+			ST_DEBUG2(ST_DEVINFO, st_label, CE_WARN,
+			    "Really got sense data\n");
+			action = st_decode_sense(un, bp, MAX_SENSE_LENGTH -
+			    pkt->pkt_resid, stat, &un->un_pos);
+		} else {
+			ST_DEBUG2(ST_DEVINFO, st_label, CE_WARN,
+			    "Trying to queue sense command\n");
+			action = QUE_SENSE;
+		}
+		break;
+
+	case STATUS_TASK_ABORT:
 		/*
 		 * This is an aborted task. This can be a reset on the other
 		 * port of a multiport drive. Lets try and recover it.
 		 */
 		action = DEVICE_RESET;
-	} else {
+		break;
+
+	default:
 		action = COMMAND_DONE;
+		ST_DEBUG(ST_DEVINFO, st_label, CE_PANIC,
+		    "Unexpected scsi status byte 0x%x\n", SCBP_C(pkt));
 	}
 	return (action);
 }
@@ -11140,8 +11414,9 @@
 st_calc_bnum(struct scsi_tape *un, struct buf *bp, struct scsi_pkt *pkt)
 {
 	int nblks;
+	int nfiles;
 	long count;
-	recov_info *ri = (recov_info *)pkt->pkt_private;
+	recov_info *ri = pkt->pkt_private;
 	cmd_attribute const *attrib;
 
 	ST_FUNC(ST_DEVINFO, st_calc_bnum);
@@ -11159,13 +11434,45 @@
 
 	count = bp->b_bcount - bp->b_resid;
 
-	/* If variable block mode */
-	if (un->un_bsize == 0) {
-		nblks = ((count == 0) ? 0 : 1);
-		un->un_kbytes_xferred += (count / ONE_K);
-	} else {
-		nblks = (count / un->un_bsize);
-		un->un_kbytes_xferred += (nblks * un->un_bsize) / ONE_K;
+	/* Command reads or writes data */
+	if (attrib->transfers_data != TRAN_NONE) {
+		if (count == 0) {
+			/* failed writes should not make it here */
+			ASSERT(attrib->transfers_data == TRAN_READ);
+			nblks = 0;
+			nfiles = 1;
+		} else if (un->un_bsize == 0) {
+			/*
+			 * If variable block mode.
+			 * Fixed bit in CBD should be zero.
+			 */
+			ASSERT((pkt->pkt_cdbp[1] & 1) == 0);
+			nblks = 1;
+			un->un_kbytes_xferred += (count / ONE_K);
+			nfiles = 0;
+		} else {
+			/*
+			 * If fixed block mode.
+			 * Fixed bit in CBD should be one.
+			 */
+			ASSERT((pkt->pkt_cdbp[1] & 1) == 1);
+			nblks = (count / un->un_bsize);
+			un->un_kbytes_xferred += (nblks * un->un_bsize) / ONE_K;
+			nfiles = 0;
+		}
+		/*
+		 * So its possable to read some blocks and hit a filemark.
+		 * Example reading in fixed block mode where more then one
+		 * block at a time is requested. In this case because the
+		 * filemark is hit something less then the requesed number
+		 * of blocks is read.
+		 */
+		if (un->un_pos.eof == ST_EOF_PENDING && bp->b_resid) {
+			nfiles = 1;
+		}
+	} else {
+		nblks = 0;
+		nfiles = count;
 	}
 
 	/*
@@ -11179,9 +11486,11 @@
 	if (attrib->chg_tape_direction == DIR_FORW) {
 		un->un_pos.blkno += nblks;
 		un->un_pos.lgclblkno += nblks;
+		un->un_pos.lgclblkno += nfiles;
 	} else if (attrib->chg_tape_direction == DIR_REVC) {
 		un->un_pos.blkno -= nblks;
 		un->un_pos.lgclblkno -= nblks;
+		un->un_pos.lgclblkno -= nfiles;
 	} else {
 		ASSERT(0);
 	}
@@ -11196,7 +11505,7 @@
 	 * If we didn't just read a filemark.
 	 */
 	if (un->un_pos.eof != ST_EOF_PENDING) {
-		ASSERT(nblks != 0);
+		ASSERT(nblks != 0 && nfiles == 0);
 		/*
 		 * If Previously calulated expected position does not match
 		 * debug the expected position.
@@ -11213,7 +11522,7 @@
 			un->un_running.pmode = invalid;
 		}
 	} else {
-		ASSERT(nblks == 0);
+		ASSERT(nfiles != 0);
 		if (un->un_running.pmode != invalid) {
 			/*
 			 * blkno and lgclblkno already counted in
@@ -11278,19 +11587,17 @@
 			un->un_throttle = un->un_max_throttle;
 		}
 	} else {
-		optype new_lastop;
+		optype new_lastop = ST_OP_NIL;
 		uchar_t cmd = (uchar_t)(intptr_t)bp->b_forw;
 
-		un->un_lastop = ST_OP_CTL;
-
 		switch (cmd) {
 		case SCMD_WRITE:
 		case SCMD_WRITE_G4:
 			bp->b_resid = sp->pkt_resid;
+			new_lastop = ST_OP_WRITE;
 			if (geterror(bp) == EIO) {
 				break;
 			}
-			new_lastop = ST_OP_WRITE;
 			st_calc_bnum(un, bp, sp);
 			if (un->un_dp->options & ST_REEL) {
 				un->un_fmneeded = 2;
@@ -11301,10 +11608,10 @@
 		case SCMD_READ:
 		case SCMD_READ_G4:
 			bp->b_resid = sp->pkt_resid;
+			new_lastop = ST_OP_READ;
 			if (geterror(bp) == EIO) {
 				break;
 			}
-			new_lastop = ST_OP_READ;
 			st_calc_bnum(un, bp, sp);
 			un->un_fmneeded = 0;
 			break;
@@ -11352,7 +11659,7 @@
 			int64_t resid;
 			int64_t done;
 			cmd_attribute const *attrib;
-			recov_info *ri = (recov_info *)sp->pkt_private;
+			recov_info *ri = sp->pkt_private;
 
 			if (ri->privatelen == sizeof (recov_info)) {
 				attrib = ri->cmd_attrib;
@@ -11375,7 +11682,7 @@
 				new_lastop = ST_OP_CTL;
 			}
 
-			ST_SPAC(ST_DEVINFO, st_label, SCSI_DEBUG,
+			ST_SPAC(ST_DEVINFO, st_label, CE_WARN,
 			    "space cmd: cdb[1] = %s\n"
 			    "space data:       = 0x%lx\n"
 			    "space count:      = %"PRId64"\n"
@@ -11469,6 +11776,16 @@
 			} else {
 				un->un_state = ST_STATE_OFFLINE;
 				un->un_pos.pmode = invalid;
+
+			}
+			/*
+			 * If we are loading or unloading we expect the media id
+			 * to change. Lets make it unknown.
+			 */
+			if (un->un_media_id != bogusID && un->un_media_id_len) {
+				kmem_free(un->un_media_id, un->un_media_id_len);
+				un->un_media_id = NULL;
+				un->un_media_id_len = 0;
 			}
 			un->un_density_known = 0;
 			un->un_pos.eof = ST_NO_EOF;
@@ -11489,7 +11806,7 @@
 			un->un_rsvd_status &=
 			    ~(ST_RELEASE | ST_LOST_RESERVE |
 			    ST_RESERVATION_CONFLICT | ST_INITIATED_RESET);
-			new_lastop = un->un_lastop;
+			new_lastop = ST_OP_CTL;
 			break;
 		case SCMD_RELEASE:
 			un->un_rsvd_status |= ST_RELEASE;
@@ -11501,6 +11818,7 @@
 		case SCMD_PERSISTENT_RESERVE_IN:
 			ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
 			    "PGR_IN command\n");
+			new_lastop = ST_OP_CTL;
 			break;
 		case SCMD_PERSISTENT_RESERVE_OUT:
 			switch (sp->pkt_cdbp[1] & ST_SA_MASK) {
@@ -11508,20 +11826,34 @@
 			case ST_SA_SCSI3_PREEMPT:
 			case ST_SA_SCSI3_PREEMPTANDABORT:
 				un->un_rsvd_status |=
-				    ST_APPLICATION_RESERVATIONS;
+				    (ST_APPLICATION_RESERVATIONS | ST_RESERVE);
+				un->un_rsvd_status &= ~(ST_RELEASE |
+				    ST_LOST_RESERVE | ST_RESERVATION_CONFLICT |
+				    ST_INITIATED_RESET);
 				ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
 				    "PGR Reserve and set: entering"
 				    " ST_APPLICATION_RESERVATIONS mode");
 				break;
-			case ST_SA_SCSI3_RELEASE:
+			case ST_SA_SCSI3_REGISTER:
+				ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
+				    "PGR Reserve register key");
+				un->un_rsvd_status |= ST_INIT_RESERVE;
+				break;
 			case ST_SA_SCSI3_CLEAR:
+				un->un_rsvd_status &= ~ST_INIT_RESERVE;
+				/* FALLTHROUGH */
+			case ST_SA_SCSI3_RELEASE:
 				un->un_rsvd_status &=
-				    ~ST_APPLICATION_RESERVATIONS;
+				    ~(ST_APPLICATION_RESERVATIONS | ST_RESERVE |
+				    ST_LOST_RESERVE | ST_RESERVATION_CONFLICT |
+				    ST_INITIATED_RESET);
+				un->un_rsvd_status |= ST_RELEASE;
 				ST_DEBUG6(ST_DEVINFO, st_label, CE_WARN,
 				    "PGR Release and reset: exiting"
 				    " ST_APPLICATION_RESERVATIONS mode");
 				break;
 			}
+			new_lastop = ST_OP_CTL;
 			break;
 		case SCMD_TEST_UNIT_READY:
 		case SCMD_READ_BLKLIM:
@@ -11558,6 +11890,16 @@
 			/* Locate makes position mode no longer legacy */
 			un->un_lastop = new_lastop = ST_OP_CTL;
 			break;
+		case SCMD_MAINTENANCE_IN:
+			switch (sp->pkt_cdbp[1]) {
+			case SSVC_ACTION_GET_SUPPORTED_OPERATIONS:
+			case SSVC_ACTION_SET_TARGET_PORT_GROUPS:
+				new_lastop = ST_OP_CTL;
+				break;
+			}
+			if (new_lastop != ST_OP_NIL) {
+				break;
+			}
 		default:
 			/*
 			 * Unknown command, If was USCSI and USCSI_SILENT
@@ -11569,6 +11911,12 @@
 				    "unknown cmd 0x%X caused loss of state\n",
 				    cmd);
 			} else {
+				/*
+				 * keep the old agreement to allow unknown
+				 * commands with the USCSI_SILENT set.
+				 * This prevents ASSERT below.
+				 */
+				new_lastop = ST_OP_CTL;
 				break;
 			}
 			/* FALLTHROUGH */
@@ -11604,7 +11952,6 @@
 	 */
 	un->un_err_resid = bp->b_resid;
 	COPY_POS(&un->un_err_pos, &un->un_pos);
-	un->un_retry_ct = 0;
 
 
 	/*
@@ -11781,15 +12128,21 @@
 
 	ST_FUNC(dip, st_print_cdb);
 
-#ifdef STDEBUG
+	/* force one line output so repeated commands are printed once */
 	if ((st_debug & 0x180) == 0x100) {
 		scsi_log(dip, label, level, "node %s cmd %s\n",
 		    st_dev_name(un->un_dev), st_print_scsi_cmd(*cdb));
 		return;
 	}
-#endif
-	(void) sprintf(buf, "%s for cmd(%s)", title, st_print_scsi_cmd(*cdb));
-	st_clean_print(dip, label, level, buf, cdb, len);
+
+	/* force one line output so repeated CDB's are printed once */
+	if ((st_debug & 0x180) == 0x80) {
+		st_clean_print(dip, label, level, NULL, cdb, len);
+	} else {
+		(void) sprintf(buf, "%s for cmd(%s)", title,
+		    st_print_scsi_cmd(*cdb));
+		st_clean_print(dip, label, level, buf, cdb, len);
+	}
 }
 
 static void
@@ -11805,9 +12158,12 @@
 	ST_FUNC(dev, st_clean_print);
 
 
-	(void) sprintf(buf, "%s:\n", title);
-	scsi_log(dev, label, level, "%s", buf);
-	level = CE_CONT;
+	if (title) {
+		(void) sprintf(buf, "%s:\n", title);
+		scsi_log(dev, label, level, "%s", buf);
+		level = CE_CONT;
+	}
+
 	for (i = 0; i < len; ) {
 		buf[0] = 0;
 		for (c = 0; c < 8 && i < len; c++, i++) {
@@ -11821,6 +12177,7 @@
 		(void) sprintf(&buf[(int)strlen(buf)], "\n");
 
 		scsi_log(dev, label, level, "%s\n", buf);
+		level = CE_CONT;
 	}
 }
 
@@ -11841,6 +12198,7 @@
 	    count, count,
 	    wait == ASYNC_CMD ? "a" : "");
 }
+#endif	/* STDEBUG */
 
 /*
  * Returns pointer to name of minor node name of device 'dev'.
@@ -11877,7 +12235,6 @@
 
 	return (name);
 }
-#endif	/* STDEBUG */
 
 /*
  * Soft error reporting, so far unique to each drive
@@ -12020,6 +12377,7 @@
 	int rval = 0;
 	char cdb[CDB_GROUP1], *c = cdb;
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 
 	GET_SOFT_STATE(dev);
 
@@ -12047,7 +12405,9 @@
 	com->uscsi_cdblen  = CDB_GROUP1;
 	com->uscsi_bufaddr = (caddr_t)sensep;
 	com->uscsi_buflen  = LOG_SENSE_LENGTH;
-	com->uscsi_flags   = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
+	com->uscsi_flags   = USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
 	if (rval) {
@@ -12330,6 +12690,7 @@
 st_do_reserve(struct scsi_tape *un)
 {
 	int rval;
+	int was_lost = un->un_rsvd_status & ST_LOST_RESERVE;
 
 	ST_FUNC(ST_DEVINFO, st_do_reserve);
 
@@ -12360,6 +12721,9 @@
 	if (rval == 0) {
 		un->un_rsvd_status |= ST_INIT_RESERVE;
 	}
+	if (was_lost) {
+		un->un_running.pmode = invalid;
+	}
 
 	return (rval);
 }
@@ -12522,7 +12886,7 @@
 }
 
 static int
-st_take_ownership(struct scsi_tape *un)
+st_take_ownership(struct scsi_tape *un, ubufunc_t ubf)
 {
 	int rval;
 
@@ -12534,7 +12898,7 @@
 	    "st_take_ownership: Entering ...\n");
 
 
-	rval = st_reserve_release(un, ST_RESERVE, st_uscsi_cmd);
+	rval = st_reserve_release(un, ST_RESERVE, ubf);
 	/*
 	 * XXX -> Should reset be done only if we get EACCES.
 	 * .
@@ -12552,10 +12916,10 @@
 		/*
 		 * remove the check condition.
 		 */
-		(void) st_reserve_release(un, ST_RESERVE, st_uscsi_cmd);
-		rval = st_reserve_release(un, ST_RESERVE, st_uscsi_cmd);
+		(void) st_reserve_release(un, ST_RESERVE, ubf);
+		rval = st_reserve_release(un, ST_RESERVE, ubf);
 		if (rval != 0) {
-			if ((st_reserve_release(un, ST_RESERVE, st_uscsi_cmd))
+			if ((st_reserve_release(un, ST_RESERVE, ubf))
 			    != 0) {
 				rval = (un->un_rsvd_status &
 				    ST_RESERVATION_CONFLICT) ? EACCES : EIO;
@@ -12795,6 +13159,7 @@
 	uchar_t *sp, *sensep;
 	unsigned length;
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 	int rval;
 	char cdb[CDB_GROUP1] = {
 		SCMD_LOG_SENSE_G1,
@@ -12820,8 +13185,10 @@
 	com->uscsi_cdblen = CDB_GROUP1;
 	com->uscsi_bufaddr = (caddr_t)sensep;
 	com->uscsi_buflen = LOG_SENSE_LENGTH;
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_flags =
-	    USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	    USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
 	if (rval || com->uscsi_status) {
@@ -12879,12 +13246,18 @@
 	if (un->un_HeadClean == TAPE_ALERT_SUPPORT_UNKNOWN) {
 
 		rval = st_logpage_supported(un, TAPE_SEQUENTIAL_PAGE);
+		if (rval == -1) {
+			return (0);
+		}
 		if (rval == 1) {
 
 			un->un_HeadClean |= TAPE_SEQUENTIAL_SUPPORTED;
 		}
 
 		rval = st_logpage_supported(un, TAPE_ALERT_PAGE);
+		if (rval == -1) {
+			return (0);
+		}
 		if (rval == 1) {
 
 			un->un_HeadClean |= TAPE_ALERT_SUPPORTED;
@@ -12901,20 +13274,25 @@
 	if (un->un_HeadClean & TAPE_SEQUENTIAL_SUPPORTED) {
 
 		rval = st_check_sequential_clean_bit(un);
-	}
-
-	if ((rval <= 0) && (un->un_HeadClean & TAPE_ALERT_SUPPORTED)) {
+		if (rval == -1) {
+			return (0);
+		}
+	}
+
+	if ((rval == 0) && (un->un_HeadClean & TAPE_ALERT_SUPPORTED)) {
 
 		rval = st_check_alert_flags(un);
-	}
-
-	if ((rval <= 0) && (un->un_dp->options & ST_CLN_MASK)) {
+		if (rval == -1) {
+			return (0);
+		}
+	}
+
+	if ((rval == 0) && (un->un_dp->options & ST_CLN_MASK)) {
 
 		rval = st_check_sense_clean_bit(un);
-	}
-
-	if (rval < 0) {
-		return (rval);
+		if (rval == -1) {
+			return (0);
+		}
 	}
 
 	/*
@@ -12958,6 +13336,7 @@
 	struct uscsi_cmd *cmd;
 	struct log_sequential_page *sp;
 	struct log_sequential_page_parameter *prm;
+	struct scsi_arq_status status;
 	char cdb[CDB_GROUP1] = {
 		SCMD_LOG_SENSE_G1,
 		0,
@@ -12977,12 +13356,14 @@
 	sp  = kmem_zalloc(sizeof (struct log_sequential_page), KM_SLEEP);
 
 	cmd->uscsi_flags   =
-	    USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	    USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 	cmd->uscsi_timeout = un->un_dp->non_motion_timeout;
 	cmd->uscsi_cdb	   = cdb;
 	cmd->uscsi_cdblen  = CDB_GROUP1;
 	cmd->uscsi_bufaddr = (caddr_t)sp;
 	cmd->uscsi_buflen  = sizeof (struct log_sequential_page);
+	cmd->uscsi_rqlen   = sizeof (status);
+	cmd->uscsi_rqbuf   = (caddr_t)&status;
 
 	rval = st_uscsi_cmd(un, cmd, FKIOCTL);
 
@@ -13032,6 +13413,7 @@
 {
 	struct st_tape_alert *ta;
 	struct uscsi_cmd *com;
+	struct scsi_arq_status status;
 	unsigned ix, length;
 	int rval;
 	tape_alert_flags flag;
@@ -13057,8 +13439,10 @@
 	com->uscsi_cdblen = CDB_GROUP1;
 	com->uscsi_bufaddr = (caddr_t)ta;
 	com->uscsi_buflen = sizeof (struct st_tape_alert);
+	com->uscsi_rqlen = sizeof (status);
+	com->uscsi_rqbuf = (caddr_t)&status;
 	com->uscsi_flags =
-	    USCSI_DIAGNOSE | USCSI_SILENT | USCSI_READ;
+	    USCSI_DIAGNOSE | USCSI_RQENABLE | USCSI_READ;
 	com->uscsi_timeout = un->un_dp->non_motion_timeout;
 
 	rval = st_uscsi_cmd(un, com, FKIOCTL);
@@ -13857,6 +14241,8 @@
 	char cdb[CDB_GROUP4];
 	int result;
 	struct uscsi_cmd *cmd;
+	struct scsi_arq_status status;
+
 	caddr_t buf = (caddr_t)pnt;
 
 	ST_FUNC(ST_DEVINFO, st_read_attributes);
@@ -13885,12 +14271,14 @@
 	cdb[15] = 0;
 
 
-	cmd->uscsi_flags = USCSI_READ | USCSI_DIAGNOSE;
+	cmd->uscsi_flags = USCSI_READ | USCSI_RQENABLE | USCSI_DIAGNOSE;
 	cmd->uscsi_timeout = un->un_dp->non_motion_timeout;
 	cmd->uscsi_cdb = &cdb[0];
 	cmd->uscsi_bufaddr = (caddr_t)buf;
 	cmd->uscsi_buflen = size;
 	cmd->uscsi_cdblen = sizeof (cdb);
+	cmd->uscsi_rqlen = sizeof (status);
+	cmd->uscsi_rqbuf = (caddr_t)&status;
 
 	result = bufunc(un, cmd, FKIOCTL);
 
@@ -14547,7 +14935,7 @@
 		rval = EIO;
 	}
 
-	if ((flag > 1) && (rval == 0)) {
+	if ((flag > 1) && (rval == 0) && (org.pmode != invalid)) {
 		st_print_position(ST_DEVINFO, st_label, CE_NOTE,
 		    "position read in", &org);
 		st_print_position(ST_DEVINFO, st_label, CE_NOTE,
@@ -14569,7 +14957,7 @@
 
 	ST_FUNC(ST_DEVINFO, st_logical_block_locate);
 	/*
-	 * WTF Not sure what to do when doing recovery and not wanting
+	 * Not sure what to do when doing recovery and not wanting
 	 * to update un_pos
 	 */
 
@@ -14677,7 +15065,7 @@
 		/* Worked as requested */
 		pos->lgclblkno = lblk;
 
-	} else if (((cmd->uscsi_status & STATUS_MASK) == STATUS_CHECK) &&
+	} else if (((cmd->uscsi_status & ST_STATUS_MASK) == STATUS_CHECK) &&
 	    (cmd->uscsi_resid != 0)) {
 		/* Got part way there but wasn't enough blocks on tape */
 		pos->lgclblkno = lblk - cmd->uscsi_resid;
@@ -14707,7 +15095,7 @@
 }
 
 static int
-st_mtfsf_ioctl(struct scsi_tape *un, int files)
+st_mtfsf_ioctl(struct scsi_tape *un, int64_t files)
 {
 	int rval;
 
@@ -14715,7 +15103,7 @@
 
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_mtfsf_ioctl: count=%x, eof=%x\n", files, un->un_pos.eof);
+	    "st_mtfsf_ioctl: count=%"PRIx64", eof=%x\n", files, un->un_pos.eof);
 #if 0
 	if ((IN_EOF(un->un_pos)) && (files == 1)) {
 		un->un_pos.fileno++;
@@ -14783,14 +15171,14 @@
 }
 
 static int
-st_forward_space_files(struct scsi_tape *un, int count)
+st_forward_space_files(struct scsi_tape *un, int64_t count)
 {
 	int rval;
 
 	ST_FUNC(ST_DEVINFO, st_forward_space_files);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "fspace: count=%x, eof=%x\n", count, un->un_pos.eof);
+	    "fspace: count=%"PRIx64", eof=%x\n", count, un->un_pos.eof);
 
 	ASSERT(count >= 0);
 	ASSERT(un->un_pos.pmode != invalid);
@@ -14936,7 +15324,7 @@
 }
 
 static int
-st_mtfsr_ioctl(struct scsi_tape *un, int count)
+st_mtfsr_ioctl(struct scsi_tape *un, int64_t count)
 {
 
 	ST_FUNC(ST_DEVINFO, st_mtfsr_ioctl);
@@ -14947,7 +15335,7 @@
 	 */
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_ioctl_fsr: count=%x, eof=%x\n", count, un->un_pos.eof);
+	    "st_ioctl_fsr: count=%"PRIx64", eof=%x\n", count, un->un_pos.eof);
 
 	if (un->un_pos.pmode == legacy) {
 		/*
@@ -15008,15 +15396,16 @@
 }
 
 static int
-st_space_records(struct scsi_tape *un, int count)
-{
-	int dblk;
+st_space_records(struct scsi_tape *un, int64_t count)
+{
+	int64_t dblk;
 	int rval = 0;
 
 	ST_FUNC(ST_DEVINFO, st_space_records);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_space_records: count=%x, eof=%x\n", count, un->un_pos.eof);
+	    "st_space_records: count=%"PRIx64", eof=%x\n",
+	    count, un->un_pos.eof);
 
 	if (un->un_pos.pmode == logical) {
 		rval = st_cmd(un, SCMD_SPACE, Blk(count), SYNC_CMD);
@@ -15026,7 +15415,7 @@
 		return (rval);
 	}
 
-	dblk = un->un_pos.blkno + count;
+	dblk = count + un->un_pos.blkno;
 
 	/* Already there */
 	if (dblk == un->un_pos.blkno) {
@@ -15124,12 +15513,12 @@
 }
 
 static int
-st_mtbsf_ioctl(struct scsi_tape *un, int files)
+st_mtbsf_ioctl(struct scsi_tape *un, int64_t files)
 {
 	ST_FUNC(ST_DEVINFO, st_mtbsf_ioctl);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_mtbsf_ioctl: count=%x, eof=%x\n", files, un->un_pos.eof);
+	    "st_mtbsf_ioctl: count=%"PRIx64", eof=%x\n", files, un->un_pos.eof);
 	/*
 	 * backward space of file filemark (1/2" and 8mm)
 	 * tape position will end on the beginning of tape side
@@ -15163,7 +15552,7 @@
 			un->un_pos.blkno = 0;
 			files++;
 			ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-			    "st_mtbsf_ioctl in eof: count=%d, op=%x\n",
+			    "st_mtbsf_ioctl in eof: count=%"PRIx64", op=%x\n",
 			    files, MTBSF);
 
 		}
@@ -15187,16 +15576,16 @@
 }
 
 static int
-st_backward_space_files(struct scsi_tape *un, int count, int infront)
-{
-	int end_fileno;
-	int skip_cnt;
+st_backward_space_files(struct scsi_tape *un, int64_t count, int infront)
+{
+	int64_t end_fileno;
+	int64_t skip_cnt;
 	int rval = 0;
 
 	ST_FUNC(ST_DEVINFO, st_backward_space_files);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_backward_space_files: count=%x eof=%x\n",
+	    "st_backward_space_files: count=%"PRIx64" eof=%x\n",
 	    count, un->un_pos.eof);
 	/*
 	 * Backspace files (MTNBSF): infront == 0
@@ -15224,7 +15613,7 @@
 	if (un->un_pos.pmode == logical) {
 
 		ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-		    "st_backward_space_files: mt_op=%x count=%x"
+		    "st_backward_space_files: mt_op=%x count=%"PRIx64
 		    "lgclblkno=%"PRIx64"\n", infront?MTBSF:MTNBSF, count,
 		    un->un_pos.lgclblkno);
 
@@ -15248,7 +15637,8 @@
 	}
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "st_backward_space_files: mt_op=%x count=%x fileno=%x blkno=%x\n",
+	    "st_backward_space_files: mt_op=%x count=%"PRIx64
+	    "fileno=%x blkno=%x\n",
 	    infront?MTBSF:MTNBSF, count, un->un_pos.fileno, un->un_pos.blkno);
 
 
@@ -15301,7 +15691,8 @@
 		 */
 		end_fileno = -(count + skip_cnt);
 		ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
-		    "skip_cnt=%x, tmp=%x\n", skip_cnt, end_fileno);
+		    "skip_cnt=%"PRIx64", tmp=%"PRIx64"\n",
+		    skip_cnt, end_fileno);
 		if (st_cmd(un, SCMD_SPACE, Fmk(end_fileno), SYNC_CMD)) {
 			ST_DEBUG2(ST_DEVINFO, st_label, SCSI_DEBUG,
 			    "st_backward_space_files:EIO:back space fm failed");
@@ -15319,7 +15710,7 @@
 	 * If we have to space forward, do so...
 	 */
 	ST_DEBUG6(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "space forward skip_cnt=%x, rval=%x\n", skip_cnt, rval);
+	    "space forward skip_cnt=%"PRIx64", rval=%x\n", skip_cnt, rval);
 
 	if (rval == 0 && skip_cnt) {
 		if (st_cmd(un, SCMD_SPACE, Fmk(skip_cnt), SYNC_CMD)) {
@@ -15350,14 +15741,14 @@
 }
 
 static int
-st_mtnbsf_ioctl(struct scsi_tape *un, int count)
+st_mtnbsf_ioctl(struct scsi_tape *un, int64_t count)
 {
 	int rval;
 
 	ST_FUNC(ST_DEVINFO, st_mtnbsf_ioctl);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "nbsf: count=%x, eof=%x\n", count, un->un_pos.eof);
+	    "nbsf: count=%"PRIx64", eof=%x\n", count, un->un_pos.eof);
 
 	if (un->un_pos.pmode == legacy) {
 		/*
@@ -15393,7 +15784,7 @@
 	}
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "mtnbsf: count=%x, eof=%x\n", count, un->un_pos.eof);
+	    "mtnbsf: count=%"PRIx64", eof=%x\n", count, un->un_pos.eof);
 
 	if (count <= 0) {
 		rval = st_forward_space_files(un, -count);
@@ -15404,12 +15795,12 @@
 }
 
 static int
-st_mtbsr_ioctl(struct scsi_tape *un, int num)
+st_mtbsr_ioctl(struct scsi_tape *un, int64_t num)
 {
 	ST_FUNC(ST_DEVINFO, st_mtbsr_ioctl);
 
 	ST_DEBUG4(ST_DEVINFO, st_label, SCSI_DEBUG,
-	    "bsr: count=%x, eof=%x\n", num, un->un_pos.eof);
+	    "bsr: count=%"PRIx64", eof=%x\n", num, un->un_pos.eof);
 
 	if (un->un_pos.pmode == legacy) {
 		/*
@@ -15474,7 +15865,7 @@
 }
 
 static int
-st_mtfsfm_ioctl(struct scsi_tape *un, int cnt)
+st_mtfsfm_ioctl(struct scsi_tape *un, int64_t cnt)
 {
 	int rval;
 
@@ -15500,7 +15891,7 @@
 }
 
 static int
-st_mtbsfm_ioctl(struct scsi_tape *un, int cnt)
+st_mtbsfm_ioctl(struct scsi_tape *un, int64_t cnt)
 {
 	int rval;
 
@@ -15938,18 +16329,18 @@
 st_handle_hex_media_id(struct scsi_tape *un, void *pnt, int size)
 {
 	int result;
-	int newsize = (size + 1) << 1;
+	int newsize = (size << 1) + 3; /* extra for leading 0x and null term */
 	int i;
-	char byte;
+	uchar_t byte;
 	char *format;
-	char *data = (char *)pnt;
+	uchar_t *data = (uchar_t *)pnt;
 	char *buf = kmem_alloc(newsize, KM_SLEEP);
 
 	ST_FUNC(ST_DEVINFO, st_handle_hex_media_id);
 
 	(void) sprintf(buf, "0x");
 	for (i = 0; i < size; i++) {
-		byte = (uchar_t)data[i];
+		byte = data[i];
 		if (byte < 0x10)
 			format = "0%x";
 		else
@@ -16035,7 +16426,7 @@
 	buf = kmem_alloc(size, KM_SLEEP);
 
 	cdb[0] = (char)SCMD_SVC_ACTION_IN_G5;
-	cdb[1] = 1; /* READ MEDIA SERIAL NUMBER */
+	cdb[1] = SSVC_ACTION_READ_MEDIA_SERIAL;
 	cdb[2] = 0;
 	cdb[3] = 0;
 	cdb[4] = 0;
@@ -16224,6 +16615,7 @@
 	return (JUST_RETURN); /* release calling thread */
 }
 
+
 static void
 st_recov_ret(struct scsi_tape *un, st_err_info *errinfo, errstate err)
 {
@@ -16234,6 +16626,9 @@
 	ST_FUNC(ST_DEVINFO, st_recov_ret);
 
 	ASSERT(MUTEX_HELD(&un->un_sd->sd_mutex));
+#if !defined(lint)
+	_NOTE(LOCK_RELEASED_AS_SIDE_EFFECT(&un->un_sd->sd_mutex))
+#endif
 
 	bp = errinfo->ei_failing_bp;
 	kmem_free(errinfo, ST_ERR_INFO_SIZE);
@@ -16262,10 +16657,12 @@
 		break;
 
 	}
+
 	st_bioerror(bp, error_number);
 	st_done_and_mutex_exit(un, bp);
 }
 
+
 static void
 st_recover(void *arg)
 {
@@ -16303,9 +16700,12 @@
 
 	rval = st_test_path_to_device(un);
 
+	ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
+	    "st_recover called with %s, TUR returned %d\n",
+	    errstatenames[errinfo->ei_error_type], rval);
 	/*
 	 * If the drive responed to the TUR lets try and get it to sync
-	 * any data it have in the buffer.
+	 * any data it might have in the buffer.
 	 */
 	if (rval == 0 && rcv->cmd_attrib->chg_tape_data) {
 		(void) st_rcmd(un, SCMD_WRITE_FILE_MARK, 0, SYNC_CMD);
@@ -16313,49 +16713,63 @@
 	switch (errinfo->ei_error_type) {
 	case ATTEMPT_RETRY:
 	case COMMAND_TIMEOUT:
-		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
-		    "st_recover called with COMMAND_TIMEOUT, TUR returned %d\n",
-		    rval);
-		if (rval != 0) {
-			/* ping failed, we're done. */
+	case DEVICE_RESET:
+	case PATH_FAILED:
+		/*
+		 * For now if we can't talk to the device we are done.
+		 * If the drive is reserved we can try to get it back.
+		 */
+		if (rval != 0 && rval != EACCES) {
 			st_recov_ret(un, errinfo, COMMAND_DONE_ERROR);
 			return;
 		}
 
 		/*
-		 * If a reset occured fall through.
-		 */
-		if (un->un_unit_attention_flags == 0) {
-			break;
-		}
-		/* FALLTHROUGH */
-	case DEVICE_RESET:
-		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
-		    "st_recover called with DEVICE_RESET, TUR returned %d\n",
-		    rval);
-		/*
-		 * For now if we can't talk to the device we are done.
-		 */
+		 * If scsi II lost reserve try and get it back.
+		 */
+		if ((((un->un_rsvd_status &
+		    (ST_LOST_RESERVE | ST_APPLICATION_RESERVATIONS)) ==
+		    ST_LOST_RESERVE)) &&
+		    (errinfo->ei_failed_pkt.pkt_cdbp[0] != SCMD_RELEASE)) {
+			rval = st_reserve_release(un, ST_RESERVE,
+			    st_uscsi_rcmd);
+			if (rval != 0) {
+				if (st_take_ownership(un, st_uscsi_rcmd) != 0) {
+					st_recov_ret(un, errinfo,
+					    COMMAND_DONE_EACCES);
+					return;
+				}
+			}
+			un->un_rsvd_status |= ST_RESERVE;
+			un->un_rsvd_status &= ~(ST_RELEASE | ST_LOST_RESERVE |
+			    ST_RESERVATION_CONFLICT | ST_INITIATED_RESET);
+		}
+		rval = st_check_mode_for_change(un, st_uscsi_rcmd);
+		if (rval) {
+			rval = st_gen_mode_select(un, st_uscsi_rcmd,
+			    un->un_mspl, sizeof (struct seq_mode));
+		}
 		if (rval) {
 			st_recov_ret(un, errinfo, COMMAND_DONE_ERROR);
 			return;
 		}
-
-		if ((un->un_rsvd_status & ST_LOST_RESERVE) &&
-		    (errinfo->ei_failed_pkt.pkt_cdbp[0] != SCMD_RELEASE)) {
-			rval = st_reserve_release(un, ST_RESERVE,
-			    st_uscsi_rcmd);
-			if (rval == 0) {
-				un->un_rsvd_status |= ST_RESERVE;
-				un->un_rsvd_status &= ~(ST_RELEASE |
-				    ST_LOST_RESERVE | ST_RESERVATION_CONFLICT |
-				    ST_INITIATED_RESET);
-			} else {
-				st_recov_ret(un, errinfo, COMMAND_DONE_EACCES);
-				return;
-			}
+		break;
+	case DEVICE_TAMPER:
+		/*
+		 * Check if the ASC/ASCQ says mode data has changed.
+		 */
+		if ((errinfo->ei_failing_status.sts_sensedata.es_add_code ==
+		    0x2a) &&
+		    (errinfo->ei_failing_status.sts_sensedata.es_qual_code ==
+		    0x01)) {
+			/*
+			 * See if mode sense changed.
+			 */
 			rval = st_check_mode_for_change(un, st_uscsi_rcmd);
 			if (rval) {
+				/*
+				 * If so change it back.
+				 */
 				rval = st_gen_mode_select(un, st_uscsi_rcmd,
 				    un->un_mspl, sizeof (struct seq_mode));
 			}
@@ -16364,51 +16778,6 @@
 				return;
 			}
 		}
-		break;
-	case PATH_FAILED:
-		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
-		    "st_recover called with PATH_FAILED, TUR returned %d\n",
-		    rval);
-		if (rval != 0) {
-			/* ping failed, we're done. */
-			st_recov_ret(un, errinfo, COMMAND_DONE_ERROR);
-			return;
-		}
-		break;
-	case DEVICE_TAMPER:
-		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
-		    "st_recover called with DEVICE_TAMPER, TUR returned %d\n",
-		    rval);
-		/*
-		 * Check if the ASC/ASCQ says mode data has changed.
-		 */
-		if (errinfo->ei_failing_status.sts_sensedata.es_add_code ==
-		    0x2a &&
-		    errinfo->ei_failing_status.sts_sensedata.es_qual_code ==
-		    0x01) {
-			/*
-			 * See if mode sense changed.
-			 */
-			rval = st_check_mode_for_change(un, st_uscsi_rcmd);
-			/*
-			 * if not cross your fingers and go for it.
-			 */
-			if (rval == 0) {
-				st_recov_ret(un, errinfo, COMMAND_DONE);
-				return;
-			}
-			/*
-			 * If so change it back.
-			 */
-			rval = st_gen_mode_select(un, st_uscsi_rcmd,
-			    un->un_mspl, sizeof (struct seq_mode));
-			if (rval) {
-				st_recov_ret(un, errinfo, COMMAND_DONE_ERROR);
-			} else {
-				st_recov_ret(un, errinfo, COMMAND_DONE);
-			}
-			return;
-		}
 		/*
 		 * if we have a media id and its not bogus.
 		 * Check to see if it the same.
@@ -16423,13 +16792,24 @@
 		break;
 	default:
 		ST_DEBUG(ST_DEVINFO, st_label, CE_PANIC,
-		    "Unhandled error type 0x%x in st_recover()\n", com);
-	}
-
-	/*
-	 * if command is retriable retry it
-	 */
-	if (rcv->cmd_attrib->retriable) {
+		    "Unhandled error type %s in st_recover() 0x%x\n",
+		    errstatenames[errinfo->ei_error_type], com);
+	}
+
+	/*
+	 * if command is retriable retry it.
+	 * Special case here. The command attribute for SCMD_REQUEST_SENSE
+	 * does not say that it is retriable. That because if you reissue a
+	 * request sense and the target responds the sense data will have
+	 * been consumed and no long be valid. If we get a busy status on
+	 * request sense while the state is ST_STATE_SENSING this will
+	 * reissue that pkt.
+	 *
+	 * XXX If this request sense gets sent to a different port then
+	 * the original command that failed was sent on it will not get
+	 * valid sense data for that command.
+	 */
+	if (rcv->cmd_attrib->retriable || un->un_rqs_bp == bp) {
 		status = st_recover_reissue_pkt(un, &errinfo->ei_failed_pkt);
 
 	/*
@@ -16472,7 +16852,7 @@
 	struct scsi_tape *un;
 	struct buf *bp;
 	recov_info *rcv;
-	errstate action = COMMAND_DONE;
+	errstate action = COMMAND_DONE_ERROR;
 	int timout = ST_TRAN_BUSY_TIMEOUT; /* short (default) timeout */
 
 	/*
@@ -16499,13 +16879,20 @@
 	case CMD_CMPLT:
 		if (un->un_arq_enabled && pkt->pkt_state & STATE_ARQ_DONE) {
 			action = st_handle_autosense(un, bp, &rcv->pos);
-		} else if (*pkt->pkt_scbp & (STATUS_BUSY | STATUS_CHECK)) {
+		} else  if ((SCBP(pkt)->sts_busy) ||
+		    (SCBP(pkt)->sts_chk) ||
+		    (SCBP(pkt)->sts_vu7)) {
 			action = st_check_error(un, pkt);
+		} else {
+			action = COMMAND_DONE;
 		}
 		break;
 	case CMD_TIMEOUT:
 		action = COMMAND_TIMEOUT;
 		break;
+	case CMD_TRAN_ERR:
+		action = QUE_COMMAND;
+		break;
 	default:
 		ST_DEBUG(ST_DEVINFO, st_label, CE_PANIC,
 		    "pkt_reason not handled yet %s",
@@ -16513,6 +16900,23 @@
 		action = COMMAND_DONE_ERROR;
 	}
 
+	/*
+	 * check for undetected path failover.
+	 */
+	if ((un->un_multipath) &&
+	    (un->un_last_path_instance != pkt->pkt_path_instance)) {
+		if (un->un_state > ST_STATE_OPENING) {
+			ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
+			    "Failover detected in recovery, action is %s\n",
+			    errstatenames[action]);
+		}
+		un->un_last_path_instance = pkt->pkt_path_instance;
+	}
+
+	ST_RECOV(ST_DEVINFO, st_label, CE_WARN,
+	    "Recovery call back got %s status on %s\n",
+	    errstatenames[action], st_print_scsi_cmd(pkt->pkt_cdbp[0]));
+
 	switch (action) {
 	case COMMAND_DONE:
 		break;
@@ -16521,6 +16925,10 @@
 		bioerror(bp, EACCES);
 		break;
 
+	case COMMAND_DONE_ERROR_RECOVERED: /* XXX maybe wrong */
+		ASSERT(0);
+		break;
+
 	case COMMAND_TIMEOUT:
 	case COMMAND_DONE_ERROR:
 		bioerror(bp, EIO);
@@ -16528,6 +16936,7 @@
 
 	case DEVICE_RESET:
 	case QUE_BUSY_COMMAND:
+	case PATH_FAILED:
 		/* longish timeout */
 		timout = ST_STATUS_BUSY_TIMEOUT;
 		/* FALLTHRU */
@@ -16972,7 +17381,8 @@
 static int
 st_test_path_to_device(struct scsi_tape *un)
 {
-	int rval;
+	int rval = 0;
+	int limit = st_retry_count;
 
 	ST_FUNC(ST_DEVINFO, st_test_path_to_device);
 
@@ -16980,8 +17390,19 @@
 	 * XXX Newer drives may not RESEVATION CONFLICT a TUR.
 	 */
 	do {
+		if (rval != 0) {
+			mutex_exit(ST_MUTEX);
+			delay(drv_usectohz(1000000));
+			mutex_enter(ST_MUTEX);
+		}
 		rval = st_rcmd(un, SCMD_TEST_UNIT_READY, 0, SYNC_CMD);
-	} while (rval == DEVICE_RESET);
+		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
+		    "ping TUR returned 0x%x", rval);
+		limit--;
+	} while (((rval == EACCES) || (rval == EBUSY)) && limit);
+
+	if (un->un_status == KEY_NOT_READY || un->un_mediastate == MTIO_EJECTED)
+		rval = 0;
 
 	return (rval);
 }
@@ -16996,6 +17417,7 @@
 {
 	int rval;
 	struct uscsi_cmd cmd;
+	struct scsi_arq_status status;
 	char cdb[CDB_GROUP1];
 
 	ST_FUNC(ST_DEVINFO, st_recovery_read_pos);
@@ -17009,13 +17431,15 @@
 	cdb[5] = 0;
 	cdb[6] = 0;
 	cdb[7] = 0;
-	cdb[8] =  (type == EXT_POS) ? 28 : 0;
+	cdb[8] = (type == EXT_POS) ? 28 : 0;
 	cdb[9] = 0;
 
-	cmd.uscsi_flags = USCSI_READ;
+	cmd.uscsi_flags = USCSI_READ | USCSI_RQENABLE;
 	cmd.uscsi_timeout = un->un_dp->non_motion_timeout;
 	cmd.uscsi_cdb = cdb;
 	cmd.uscsi_cdblen = sizeof (cdb);
+	cmd.uscsi_rqlen = sizeof (status);
+	cmd.uscsi_rqbuf = (caddr_t)&status;
 	cmd.uscsi_bufaddr = (caddr_t)raw;
 	switch (type) {
 	case SHORT_POS:
@@ -17047,13 +17471,39 @@
 
 	ST_FUNC(ST_DEVINFO, st_recovery_get_position);
 
-	rval = st_recovery_read_pos(un, type, raw);
-	if (rval != 0) {
-		return (rval);
-	}
-	rval = st_interpret_read_pos(un, read, type, sizeof (read_pos_data_t),
-	    (caddr_t)raw, 1);
-
+	do {
+		rval = st_recovery_read_pos(un, type, raw);
+		if (rval != 0) {
+			switch (type) {
+			case SHORT_POS:
+				type = NO_POS;
+				break;
+
+			case LONG_POS:
+				type = EXT_POS;
+				break;
+
+			case EXT_POS:
+				type = SHORT_POS;
+				break;
+
+			default:
+				type = LONG_POS;
+				break;
+
+			}
+		} else {
+			if (type != un->un_read_pos_type) {
+				un->un_read_pos_type = type;
+			}
+			break;
+		}
+	} while (type != NO_POS);
+
+	if (rval == 0) {
+		rval = st_interpret_read_pos(un, read, type,
+		    sizeof (read_pos_data_t), (caddr_t)raw, 1);
+	}
 	return (rval);
 }
 
@@ -17104,26 +17554,54 @@
 	if (cmd_att->recov_pos_type == POS_EXPECTED) {
 		uint32_t count;
 		int64_t difference;
-
-		/* At expected? */
-		if (read->lgclblkno == ei->ei_expected_pos.lgclblkno) {
-			ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
-			    "Found drive to be at expected position\n");
-			return (0); /* Good */
-		}
+		uchar_t reposition = 0;
+
 		ASSERT(cmd_att->get_cnt);
 		count = cmd_att->get_cnt(ei->ei_failed_pkt.pkt_cdbp);
 
-		ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
+		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 		    "Got count from CDB and it was %d\n", count);
+
+		/*
+		 * At expected?
+		 */
+		if (read->lgclblkno == ei->ei_expected_pos.lgclblkno) {
+			ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
+			    "Found drive to be at expected position\n");
+
+			/*
+			 * If the command should move tape and it got a busy
+			 * it shouldn't be in the expected position.
+			 */
+			if (ei->ei_failing_status.sts_status.sts_busy != 0) {
+				reposition = 1;
+
+			/*
+			 * If the command doesn't transfer data should be good.
+			 */
+			} else if (cmd_att->transfers_data == TRAN_NONE) {
+				return (0); /* Good */
+
+			/*
+			 * Command transfers data, should have done so.
+			 */
+			} else if (ei->ei_failed_pkt.pkt_state &
+			    STATE_XFERRED_DATA) {
+				return (0); /* Good */
+			} else {
+				reposition = 1;
+			}
+		}
+
 		if (cmd_att->chg_tape_direction == DIR_FORW) {
 			difference =
 			    ei->ei_expected_pos.lgclblkno - read->lgclblkno;
-			ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
+
+			ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 			    "difference between expected and actual is %"
 			    PRId64"\n", difference);
-			if (count == difference) {
-				ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
+			if (count == difference && reposition == 0) {
+				ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 				    "Found failed FORW command, retrying\n");
 				return (EAGAIN);
 			}
@@ -17142,20 +17620,24 @@
 				    ei->ei_expected_pos.partition);
 				if (rval == 0) {
 					ST_RECOV(ST_DEVINFO, st_label,
-					    SCSI_DEBUG, "reestablished FORW"
+					    CE_NOTE, "reestablished FORW"
 					    " command retrying\n");
 					return (EAGAIN);
 				}
-			/* This handles flushed read ahead on the drive */
+			/*
+			 * This handles flushed read ahead on the drive or
+			 * an aborted read that presents as a busy and advanced
+			 * the tape position.
+			 */
 			} else if ((cmd_att->transfers_data == TRAN_READ) &&
-			    (difference < 0)) {
+			    ((difference < 0) || (reposition == 1))) {
 				rval = st_logical_block_locate(un,
 				    st_uscsi_rcmd, read,
 				    ei->ei_expected_pos.lgclblkno - count,
 				    ei->ei_expected_pos.partition);
 				if (rval == 0) {
 					ST_RECOV(ST_DEVINFO, st_label,
-					    SCSI_DEBUG, "reestablished FORW"
+					    CE_NOTE, "reestablished FORW"
 					    " read command retrying\n");
 					return (EAGAIN);
 				}
@@ -17168,23 +17650,23 @@
 			 * The plot thickens. Now I am attempting to cover a
 			 * count of 1 and a differance of 2 on a write.
 			 */
-			} else if (difference > count) {
+			} else if ((difference > count) || (reposition == 1)) {
 				rval = st_logical_block_locate(un,
 				    st_uscsi_rcmd, read,
 				    ei->ei_expected_pos.lgclblkno - count,
 				    ei->ei_expected_pos.partition);
 				if (rval == 0) {
 					ST_RECOV(ST_DEVINFO, st_label,
-					    SCSI_DEBUG, "reestablished FORW"
+					    CE_NOTE, "reestablished FORW"
 					    " write command retrying\n");
 					return (EAGAIN);
 				}
-				scsi_log(ST_DEVINFO, st_label, CE_NOTE,
+				ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 				    "Seek to block %"PRId64" returned %d\n",
 				    ei->ei_expected_pos.lgclblkno - count,
 				    rval);
 			} else {
-				scsi_log(ST_DEVINFO, st_label, CE_NOTE,
+				ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 				    "Not expected transfers_data = %d "
 				    "difference = %"PRId64,
 				    cmd_att->transfers_data, difference);
@@ -17197,11 +17679,11 @@
 			ASSERT(cmd_att->transfers_data != TRAN_WRTE);
 			difference =
 			    read->lgclblkno - ei->ei_expected_pos.lgclblkno;
-			ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
+			ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 			    "difference between expected and actual is %"
 			    PRId64"\n", difference);
-			if (count == difference) {
-				ST_RECOV(ST_DEVINFO, st_label, SCSI_DEBUG,
+			if (count == difference && reposition == 0) {
+				ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 				    "Found failed REVC command, retrying\n");
 				return (EAGAIN);
 			}
@@ -17213,25 +17695,25 @@
 				    ei->ei_expected_pos.partition);
 				if (rval == 0) {
 					ST_RECOV(ST_DEVINFO, st_label,
-					    SCSI_DEBUG, "reestablished REVC"
+					    CE_NOTE, "reestablished REVC"
 					    " command retrying\n");
 					return (EAGAIN);
 				}
 			/* This handles read ahead in reverse direction */
 			} else if ((cmd_att->transfers_data == TRAN_READ) &&
-			    (difference < 0)) {
+			    (difference < 0) || (reposition == 1)) {
 				rval = st_logical_block_locate(un,
 				    st_uscsi_rcmd, read,
 				    ei->ei_expected_pos.lgclblkno - count,
 				    ei->ei_expected_pos.partition);
 				if (rval == 0) {
 					ST_RECOV(ST_DEVINFO, st_label,
-					    SCSI_DEBUG, "reestablished REVC"
+					    CE_NOTE, "reestablished REVC"
 					    " read command retrying\n");
 					return (EAGAIN);
 				}
 			} else {
-				scsi_log(ST_DEVINFO, st_label, CE_NOTE,
+				ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 				    "Not expected transfers_data = %d "
 				    "difference = %"PRId64,
 				    cmd_att->transfers_data, difference);
@@ -17246,7 +17728,7 @@
 			 */
 			ASSERT(0);
 		}
-		scsi_log(ST_DEVINFO, st_label, CE_NOTE,
+		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 		    "Didn't find a recoverable position, Failing\n");
 
 	/*
@@ -17302,7 +17784,9 @@
 	cmd_attribute const *attrib;
 	recov_info *rcv = oldpkt->pkt_private;
 	uint_t cdblen;
+	int queued = 0;
 	int rval;
+	int flags = 0;
 	int stat_size =
 	    (un->un_arq_enabled ? sizeof (struct scsi_arq_status) : 1);
 
@@ -17328,6 +17812,7 @@
 	} else {
 		pkt_bp = bp;
 	}
+
 	/*
 	 * if this is a queued command make sure it the only one in the
 	 * run queue.
@@ -17335,12 +17820,18 @@
 	if (bp != un->un_sbufp && bp != un->un_recov_buf) {
 		ASSERT(un->un_runqf == un->un_runql);
 		ASSERT(un->un_runqf == bp);
+		queued = 1;
 	}
 
 	cdblen = scsi_cdb_size[CDB_GROUPID(oldpkt->pkt_cdbp[0])];
 
+	if (pkt_bp == un->un_rqs_bp) {
+		flags |= PKT_CONSISTENT;
+		stat_size = 1;
+	}
+
 	newpkt = scsi_init_pkt(ROUTE, NULL, pkt_bp, cdblen,
-	    stat_size, rcv->privatelen, 0, NULL_FUNC, NULL);
+	    stat_size, rcv->privatelen, flags, NULL_FUNC, NULL);
 	if (newpkt == NULL) {
 		ST_RECOV(ST_DEVINFO, st_label, CE_NOTE,
 		    "Reissue pkt scsi_init_pkt() failure\n");
@@ -17363,8 +17854,17 @@
 	newpkt->pkt_state = 0;
 	newpkt->pkt_statistics = 0;
 
+	/*
+	 * oldpkt passed in was a copy of the original.
+	 * to distroy we need the address of the original.
+	 */
 	oldpkt = BP_PKT(bp);
 
+	if (oldpkt == un->un_rqs) {
+		ASSERT(bp == un->un_rqs_bp);
+		un->un_rqs = newpkt;
+	}
+
 	SET_BP_PKT(bp, newpkt);
 
 	scsi_destroy_pkt(oldpkt);
@@ -17379,7 +17879,7 @@
 		return (COMMAND_DONE_ERROR);
 	}
 	mutex_exit(ST_MUTEX);
-	rval = st_handle_start_busy(un, bp, ST_TRAN_BUSY_TIMEOUT, 0);
+	rval = st_handle_start_busy(un, bp, ST_TRAN_BUSY_TIMEOUT, queued);
 	mutex_enter(ST_MUTEX);
 	if (rval) {
 		return (COMMAND_DONE_ERROR);
@@ -17594,6 +18094,12 @@
 }
 
 static const cmd_attribute cmd_attributes[] = {
+	{ SCMD_READ,
+	    1, 0, 1, 0, 0, DIR_FORW, TRAN_READ, POS_EXPECTED,
+	    0, 0, 0, st_get_cdb_g0_rw_count },
+	{ SCMD_WRITE,
+	    1, 0, 1, 1, 0, DIR_FORW, TRAN_WRTE, POS_EXPECTED,
+	    0, 0, 0, st_get_cdb_g0_rw_count },
 	{ SCMD_TEST_UNIT_READY,
 	    0, 1, 0, 0, 0, DIR_NONE, TRAN_NONE, POS_EXPECTED,
 	    0, 0, 0 },
@@ -17606,12 +18112,6 @@
 	{ SCMD_READ_BLKLIM,
 	    0, 1, 0, 0, 0, DIR_NONE, TRAN_READ, POS_EXPECTED,
 	    0, 0, 0 },
-	{ SCMD_READ,
-	    1, 0, 1, 0, 0, DIR_FORW, TRAN_READ, POS_EXPECTED,
-	    0, 0, 0, st_get_cdb_g0_rw_count },
-	{ SCMD_WRITE,
-	    1, 0, 1, 1, 0, DIR_FORW, TRAN_WRTE, POS_EXPECTED,
-	    0, 0, 0, st_get_cdb_g0_rw_count },
 	{ SCMD_READ_G4,
 	    1, 0, 1, 0, 1, DIR_FORW, TRAN_READ, POS_EXPECTED,
 	    0, 0, 0, st_get_cdb_g5_rw_cnt, st_get_cdb_g4g5_cnt },
@@ -17791,7 +18291,7 @@
 	ST_FUNC(ST_DEVINFO, st_reset_notification);
 	mutex_enter(ST_MUTEX);
 
-	un->un_unit_attention_flags = 2;
+	un->un_unit_attention_flags |= 2;
 	if ((un->un_rsvd_status & (ST_RESERVE | ST_APPLICATION_RESERVATIONS)) ==
 	    ST_RESERVE) {
 		un->un_rsvd_status |= ST_LOST_RESERVE;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/scsi_vhci_tpgs.h	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,58 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef	_SYS_SCSI_ADAPTERS_SCSI_VHCI_TPGS_H
+#define	_SYS_SCSI_ADAPTERS_SCSI_VHCI_TPGS_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * max number of retries for std failover to complete where the ping
+ * command is failing due to transport errors or commands being rejected by
+ * std.
+ * STD_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
+ * std takes time to complete the failover.
+ */
+#define	STD_FO_MAX_CMD_RETRIES	3
+
+#define	STD_ACTIVE_OPTIMIZED    0x0
+#define	STD_ACTIVE_NONOPTIMIZED 0x1
+#define	STD_STANDBY		0x2
+#define	STD_UNAVAILABLE		0x3
+#define	STD_TRANSITIONING	0xf
+
+#define	STD_SCSI_ASC_STATE_TRANS	0x04
+#define	STD_SCSI_ASCQ_STATE_TRANS_FAIL  0x0A
+#define	STD_SCSI_ASC_STATE_CHG		0x2A
+#define	STD_SCSI_ASCQ_STATE_CHG_SUCC	0x06
+#define	STD_SCSI_ASCQ_STATE_CHG_FAILED	0x07
+#define	STD_SCSI_ASC_INVAL_PARAM_LIST	0x26
+#define	STD_SCSI_ASC_INVAL_CMD_OPCODE	0x20
+#define	STD_LOGICAL_UNIT_NOT_ACCESSIBLE	0x04
+#define	STD_TGT_PORT_UNAVAILABLE	0x0C
+
+extern int vhci_tpgs_get_target_fo_mode(struct scsi_device *sd, int *mode,
+    int *state, int *xlf_capable, int *preferred);
+
+#endif /* _SYS_SCSI_ADAPTERS_SCSI_VHCI_TPGS_H */
--- a/usr/src/uts/common/sys/scsi/generic/commands.h	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/sys/scsi/generic/commands.h	Mon Jun 23 13:41:43 2008 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -330,17 +330,13 @@
 #define	SCMD_LOCATE_G4		0x92
 
 /*
- * Define for Group 5 command.
+ * Group 5 commands.
  */
 #define	SCMD_GROUP5		0xA0
 #define	SCMD_REPORT_LUNS	0xA0
 #define	SCMD_SECURITY_PROTO_IN	0xA2
 #define	SCMD_MAINTENANCE_IN	0xA3
 #define	SCMD_MAINTENANCE_OUT	0xA4
-#define	SCMD_SET_DEVICE_IDENTIFIER	0x06
-#define	SCMD_SET_PRIORITY		0x0e
-#define	SCMD_SET_TARGET_PORT_GROUPS	0x0a
-#define	SCMD_SET_TIMESTAMP		0x0f
 #define	SCMD_READ_G5		0xA8
 #define	SCMD_WRITE_G5		0xAA
 #define	SCMD_SVC_ACTION_OUT_G5	0xA9
@@ -349,8 +345,27 @@
 #define	SCMD_VERIFY_G5		0xAF
 #define	SCMD_SECURITY_PROTO_OUT	0xB5
 
+/*
+ * Group 5 Service Actions for Maintenance In (12)
+ */
+#define	SSVC_ACTION_GET_TARGET_PORT_GROUPS	0x0a
+#define	SSVC_ACTION_GET_SUPPORTED_OPERATIONS	0x0c
+#define	SSVC_SCTION_GET_SUPPORTED_MANAGEMENT	0x0d
+#define	SSVC_ACTION_GET_TIMESTAMP		0x0f
 
 /*
+ * Group 5 Service Actions for Maintenance Out (12)
+ */
+#define	SSVC_ACTION_SET_DEVICE_IDENTIFIER	0x06
+#define	SSVC_ACTION_SET_PRIORITY		0x0e
+#define	SSVC_ACTION_SET_TARGET_PORT_GROUPS	0x0a
+#define	SSVC_ACTION_SET_TIMESTAMP		0x0f
+
+/*
+ * Group 5 Service Actions for Service Action In (12)
+ */
+#define	SSVC_ACTION_READ_MEDIA_SERIAL		0x01
+/*
  * scsi_key_strings for SCMD_ definitions
  *	NOTE: see SCSI_CMDS_KEY_STRINGS_CDIO in cdio.h for additional
  *	command-to-string translations.
--- a/usr/src/uts/common/sys/scsi/generic/inquiry.h	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/sys/scsi/generic/inquiry.h	Mon Jun 23 13:41:43 2008 -0700
@@ -73,8 +73,8 @@
 	uchar_t	inq_len;		/* additional length 		*/
 
 	uchar_t			: 4,	/* reserved 			*/
-		inq_tpgs	: 1,	/* supports Target Port Group set */
-				: 3;
+		inq_tpgs	: 2,	/* supports Target Port Group set */
+				: 2;
 	uchar_t	inq_addr16	: 1,	/* supports 16 bit wide SCSI addr */
 		inq_addr32	: 1,	/* supports 32 bit wide SCSI addr */
 		inq_ackqreqq	: 1,	/* data tranfer on Q cable */
@@ -149,8 +149,8 @@
 
 	uchar_t	inq_len;		/* additional length */
 
-	uchar_t			: 3,	/* reserved */
-		inq_tpgs	: 1,	/* supports Target Port Group Set */
+	uchar_t			: 2,	/* reserved */
+		inq_tpgs	: 2,	/* supports Target Port Group Set */
 				: 4;
 
 	uchar_t	inq_bque	: 1,	/* combined with cmdque */
--- a/usr/src/uts/common/sys/scsi/generic/status.h	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/sys/scsi/generic/status.h	Mon Jun 23 13:41:43 2008 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * 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.
@@ -20,8 +19,8 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 1996-1998 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
  */
 
 #ifndef	_SYS_SCSI_GENERIC_STATUS_H
@@ -99,6 +98,7 @@
 #define	STATUS_TERMINATED		0x22
 #define	STATUS_QFULL			0x28
 #define	STATUS_ACA_ACTIVE		0x30
+#define	STATUS_TASK_ABORT		0x40
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/sys/scsi/targets/stdef.h	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/common/sys/scsi/targets/stdef.h	Mon Jun 23 13:41:43 2008 -0700
@@ -873,11 +873,15 @@
 typedef struct {
 	buf_t *cmd_bp;
 	size_t privatelen;
+	int str_retry_cnt;
+	int pkt_retry_cnt;
 }pkt_info;
 
 typedef struct {
 	buf_t *cmd_bp;
 	size_t privatelen;
+	int str_retry_cnt;
+	int pkt_retry_cnt;
 	tapepos_t pos;
 	const cmd_attribute *cmd_attrib;
 }recov_info;
@@ -963,7 +967,6 @@
 	st_states un_state;		/* current state */
 	uchar_t	un_status;		/* status from last sense */
 	uchar_t	un_retry_ct;		/* retry count */
-	uchar_t	un_tran_retry_ct;	/* transport retry count */
 	writablity un_read_only;	/* RDWR, RDONLY, WORM, RDWORM */
 	uchar_t	un_test_append;		/* check writing at end of tape */
 	uchar_t un_arq_enabled;		/* auto request sense enabled */
@@ -1045,6 +1048,8 @@
 #endif
 	tapepos_t un_running;
 	uchar_t un_unit_attention_flags;
+	uchar_t un_multipath;
+	ulong_t un_last_path_instance;
 };
 
 typedef int (*bufunc_t)(struct scsi_tape *, int, int64_t, int);
@@ -1269,7 +1274,7 @@
  * 10 seconds is what we'll wait if we get a Busy Status back
  */
 #define	ST_STATUS_BUSY_TIMEOUT	10*hz	/* seconds Busy Waiting */
-#define	ST_TRAN_BUSY_TIMEOUT	1*hz	/* seconds retry on TRAN_BSY */
+#define	ST_TRAN_BUSY_TIMEOUT	4*hz	/* seconds retry on TRAN_BSY */
 #define	ST_INTERRUPT_CONTEXT	1
 #define	ST_START_CONTEXT	2
 
@@ -1325,8 +1330,9 @@
 
 #define	BSD_BEHAVIOR	(getminor(un->un_dev) & MT_BSD)
 #define	SVR4_BEHAVIOR	((getminor(un->un_dev) & MT_BSD) == 0)
+#define	ST_STATUS_MASK	(STATUS_MASK | STATUS_TASK_ABORT)
 #define	SCBP(pkt)		((struct scsi_status *)(pkt)->pkt_scbp)
-#define	SCBP_C(pkt)		((*(pkt)->pkt_scbp) & STATUS_MASK)
+#define	SCBP_C(pkt)		((*(pkt)->pkt_scbp) & ST_STATUS_MASK)
 #define	CDBP(pkt)		((union scsi_cdb *)(pkt)->pkt_cdbp)
 #define	BP_PKT(bp)		((struct scsi_pkt *)(bp)->av_back)
 #define	SET_BP_PKT(bp, pkt)	((bp)->av_back = (struct buf *)(pkt))
@@ -1355,7 +1361,16 @@
  * carried in the next 2 bits. The remaining bits a signed count of
  * how many of that direction and type to do.
  */
-#if (SIZE_MAX < UINT64_MAX)
+
+#if (defined(__lock_lint))
+/*
+ * This is a workaround for warlock not being able to parse an #ULL constant.
+ */
+#undef	UINT64_MAX
+#define	UINT64_MAX	(18446744073709551615UL)
+#endif /* __lock_lint */
+
+#if (defined(__lock_lint) || (SIZE_MAX < UINT64_MAX))
 
 #define	SP_BLK		UINT32_C(0x00000000)
 #define	SP_FLM		UINT32_C(0x20000000)
@@ -1424,7 +1439,11 @@
 #define	DEBUGGING\
 	((scsi_options & SCSI_DEBUG_TGT) || (st_debug & 0x7))
 
-#define	ST_DARGS(d) st_label, ((d == st_lastdev || d == 0) ?CE_CONT:CE_NOTE)
+#define	DEBLOCK(d) int lev = CE_NOTE; mutex_enter(&st_debug_mutex); \
+    if (d == st_lastdev || d == 0) lev = CE_CONT; mutex_exit(&st_debug_mutex);
+
+#define	DEBUNLOCK(d) mutex_enter(&st_debug_mutex); \
+    if (d != 0 && d != st_lastdev) st_lastdev = d; mutex_exit(&st_debug_mutex);
 
 	/* initialization */
 #define	ST_DEBUG1	if ((st_debug & 0x7) >= 1) scsi_log
@@ -1448,30 +1467,27 @@
 #define	ST_RECOV	if (st_debug & 0x8) scsi_log
 
 	/* Entry Point Functions */
-#define	ST_ENTR(d, fn)\
-    if (st_debug & 0x10) { scsi_log(d, ST_DARGS(d), #fn);\
-    if (d != 0 && d != st_lastdev) st_lastdev = d; }
+#define	ST_ENTR(d, fn) if (st_debug & 0x10) { DEBLOCK(d) \
+    scsi_log(d, st_label, lev, #fn); DEBUNLOCK(d) }
 
 	/* Non-Entry Point Functions */
-#define	ST_FUNC(d, fn)\
-    if (st_debug & 0x20) { scsi_log(d, ST_DARGS(d), #fn);\
-    if (d != 0 && d != st_lastdev) st_lastdev = d; }
+#define	ST_FUNC(d, fn) if (st_debug & 0x20) { DEBLOCK(d) \
+    scsi_log(d, st_label, lev, #fn); DEBUNLOCK(d) }
 
 	/* Space Information */
 #define	ST_SPAC		if (st_debug & 0x40) scsi_log
 
 	/* CDB's sent */
-#define	ST_CDB(d, cmnt, cdb) if (st_debug & 0x180) { \
-    st_print_cdb(d, ST_DARGS(d), cmnt, cdb);\
-    if (d != 0 && d != st_lastdev) st_lastdev = d; }
+#define	ST_CDB(d, cmnt, cdb) if (st_debug & 0x180) { DEBLOCK(d) \
+    st_print_cdb(d, st_label, lev, cmnt, cdb); DEBUNLOCK(d) }
+
 	/* sense data */
-#define	ST_SENSE(d, cmnt, sense, size) if (st_debug & 0x200) { \
-    st_clean_print(d, ST_DARGS(d), cmnt, sense, size);\
-    if (d != 0 && d != st_lastdev) st_lastdev = d; }
+#define	ST_SENSE(d, cmnt, sense, size) if (st_debug & 0x200) { DEBLOCK(d) \
+    st_clean_print(d, st_label, lev, cmnt, sense, size); DEBUNLOCK(d) }
+
 	/* position data */
-#define	ST_POS(d, cmnt, pdata) if (st_debug & 0x400) { \
-    st_print_position(d, ST_DARGS(d), cmnt, pdata);\
-    if (d != 0 && d != st_lastdev) st_lastdev = d; }
+#define	ST_POS(d, cmnt, pdata) if (st_debug & 0x400) { DEBLOCK(d) \
+    st_print_position(d, st_label, lev, cmnt, pdata); DEBUNLOCK(d) }
 
 
 #else
--- a/usr/src/uts/intel/Makefile.intel.shared	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Mon Jun 23 13:41:43 2008 -0700
@@ -573,6 +573,8 @@
 MISC_KMODS	+= scsi_vhci_f_sym
 MISC_KMODS	+= scsi_vhci_f_tpgs
 MISC_KMODS	+= scsi_vhci_f_asym_sun
+MISC_KMODS	+= scsi_vhci_f_tape
+MISC_KMODS	+= scsi_vhci_f_tpgs_tape
 
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= amsrc1
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= klmmod klmops
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/scsi_vhci_f_tape/Makefile	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,88 @@
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This makefile drives the production of misc/scsi_vhci/scsi_vhci_f_tape
+# intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_tape
+OBJECTS		= $(SCSI_VHCI_F_TAPE_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TAPE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_SCSI_VHCI_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi" -N"drv/scsi_vhci"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/scsi_vhci_f_tpgs_tape/Makefile	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,88 @@
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This makefile drives the production of misc/scsi_vhci/scsi_vhci_f_tpgs_tape
+# intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_tpgs_tape
+OBJECTS		= $(SCSI_VHCI_F_TPGS_TAPE_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TPGS_TAPE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_SCSI_VHCI_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi" -N"drv/scsi_vhci"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Mon Jun 23 13:16:14 2008 -0700
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Mon Jun 23 13:41:43 2008 -0700
@@ -390,6 +390,7 @@
 MISC_KMODS	+= pcicfg.e fcodem fcpci
 MISC_KMODS	+= scsi_vhci_f_sym scsi_vhci_f_tpgs scsi_vhci_f_asym_sun
 MISC_KMODS	+= scsi_vhci_f_sym_hds
+MISC_KMODS	+= scsi_vhci_f_tape scsi_vhci_f_tpgs_tape
 
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= amsrc1
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= klmmod klmops
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/scsi_vhci_f_tape/Makefile	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,88 @@
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This makefile drives the production of misc/scsi_vhci/scsi_vhci_f_tape
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_tape
+OBJECTS		= $(SCSI_VHCI_F_TAPE_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TAPE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_SCSI_VHCI_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi" -N"drv/scsi_vhci"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/scsi_vhci_f_tpgs_tape/Makefile	Mon Jun 23 13:41:43 2008 -0700
@@ -0,0 +1,88 @@
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This makefile drives the production of misc/scsi_vhci/scsi_vhci_f_tpgs_tape
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_tpgs_tape
+OBJECTS		= $(SCSI_VHCI_F_TPGS_TAPE_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TPGS_TAPE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_SCSI_VHCI_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi" -N"drv/scsi_vhci"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ