changeset 4851:5e98cf4c2164

PSARC 2006/708 ddi-forceload PSARC 2006/710 scsi_get_device_type_string PSARC 2007/379 MPXIO/scsi_vhci failover-ops configuration - revisited 6504975 ddi_modopen should allow open in subdirectory 6542649 step0: opensource mpxio
author cth
date Fri, 10 Aug 2007 17:00:37 -0700
parents 231048ceccbb
children 4e7870932cb2
files usr/src/pkgdefs/SUNWckr/prototype_i386 usr/src/pkgdefs/SUNWckr/prototype_sparc usr/src/pkgdefs/common_files/i.scsivhciconf usr/src/uts/Makefile.targ usr/src/uts/Makefile.uts usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/asym_sun.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/sym.c usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.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/impl/scsi_subr.c usr/src/uts/common/os/modctl.c usr/src/uts/common/os/swapgeneric.c usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h usr/src/uts/common/sys/scsi/impl/transport.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/scsi_vhci/Makefile usr/src/uts/intel/scsi_vhci_f_asym_sun/Makefile usr/src/uts/intel/scsi_vhci_f_sym/Makefile usr/src/uts/intel/scsi_vhci_f_tpgs/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/scsi_vhci/Makefile usr/src/uts/sparc/scsi_vhci_f_asym_sun/Makefile usr/src/uts/sparc/scsi_vhci_f_sym/Makefile usr/src/uts/sparc/scsi_vhci_f_tpgs/Makefile
diffstat 28 files changed, 15638 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Aug 10 17:00:37 2007 -0700
@@ -163,6 +163,13 @@
 d none boot/acpi/tables 755 root sys
 f none kernel/mac/mac_ether 755 root sys
 f none kernel/mac/mac_wifi 755 root sys
+d none kernel/misc/scsi_vhci 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_asym_emc 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_asym_lsi 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_asym_sun 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_sym 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_sym_emc 755 root sys
+f none kernel/misc/scsi_vhci/scsi_vhci_f_tpgs 755 root sys
 f none kernel/misc/acpica 755 root sys
 f none kernel/misc/agpmaster 755 root sys
 f none kernel/misc/bootdev 755 root sys
@@ -352,6 +359,14 @@
 d none kernel/mac/amd64 755 root sys
 f none kernel/mac/amd64/mac_ether 755 root sys
 f none kernel/mac/amd64/mac_wifi 755 root sys
+d none kernel/misc/scsi_vhci/amd64 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_asym_emc 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_asym_lsi 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_asym_sun 755 root sys
+f none kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym 755 root sys
+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_tpgs 755 root sys
+
 d none kernel/misc/amd64 755 root sys
 f none kernel/misc/amd64/acpica 755 root sys
 f none kernel/misc/amd64/agpmaster 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Aug 10 17:00:37 2007 -0700
@@ -161,6 +161,14 @@
 d none kernel/mac/sparcv9 755 root sys
 f none kernel/mac/sparcv9/mac_ether 755 root sys
 f none kernel/mac/sparcv9/mac_wifi 755 root sys
+d none kernel/misc/scsi_vhci 755 root sys
+d none kernel/misc/scsi_vhci/sparcv9 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_asym_emc 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_asym_lsi 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_asym_sun 755 root sys
+f none kernel/misc/scsi_vhci/sparcv9/scsi_vhci_f_sym 755 root sys
+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_tpgs 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	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/pkgdefs/common_files/i.scsivhciconf	Fri Aug 10 17:00:37 2007 -0700
@@ -3,9 +3,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,10 +19,9 @@
 #
 # CDDL HEADER END
 #
-#
 # ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -78,6 +76,100 @@
 /^# Force load driver to support hotplug activity (do not remove this property).$/d
 /^ddi-forceattach=1;$/d
 EOF
+	#
+	# convert old 'scsi-options' oriented device-type-scsi-options-list to
+	# new 'name' oriented scsi-vhci-failover-override
+	#
+	cat > $SEDSCRIPT << EOF
+/^# For enabling MPxIO support for 3rd party symmetric device need an$/d
+/^# entry similar to following in this file. Just replace the "SUN     SENA"$/d
+/^# part with the Vendor ID\/Product ID for the device, exactly as reported by$/d
+/^# Inquiry cmd.$/d
+/^# device-type-scsi-options-list =$/d
+/^# "SUN     SENA", "symmetric-option";$/d
+/^# symmetric-option = 0x1000000;$/d
+s/device-type-scsi-options-list/scsi-vhci-failover-override/
+EOF
+	# old 'symetric' scsi-options property of 1000000 -> f_sym name
+	SYMMETRIC_OPTION=`grep -v '^#' $2 | grep 1000000 | \
+		awk -F= '{print $1}'|awk '{print $1}'`
+	if [ "$SYMMETRIC_OPTION"x != "x" ]; then
+		echo "/^[ 	]*$SYMMETRIC_OPTION/d"	>> $SEDSCRIPT
+		echo "s/$SYMMETRIC_OPTION/f_sym/"	>> $SEDSCRIPT
+	fi
+
+	# old 'disable' scsi-options property of 7000000 -> NONE name
+	DISABLE_OPTION=`grep -v '^#' $2 | grep 7000000 | \
+		awk -F= '{print $1}'|awk '{print $1}'`
+	if [ "$DISABLE_OPTION"x != "x" ]; then
+		echo "/^[ 	]*$DISABLE_OPTION/d"	>> $SEDSCRIPT
+		echo "s/$DISABLE_OPTION/NONE/"		>> $SEDSCRIPT
+	fi
+}
+
+#
+# Make FAILOVER_MODULE_BLOCK file
+#
+make_fmbfile()
+{
+	#
+	# Form file with new scsi_vhci FAILOVER_MODULE_BLOCK information
+	#
+	cat > $FMBFILE << EOF
+#BEGIN: FAILOVER_MODULE_BLOCK (DO NOT MOVE OR DELETE)
+#
+# Declare scsi_vhci failover module paths with 'ddi-forceload' so that
+# they get loaded early enough to be available for scsi_vhci root use.
+#
+# NOTE: Correct operation depends on the value of 'ddi-forceload', this
+# value should not be changed. The ordering of entries is from
+# most-specific failover modules (with a "probe" implementation that is
+# completely VID/PID table based), to most generic (failover modules that
+# are based on T10 standards like TPGS). By convention the last part of a
+# failover module path, after "/scsi_vhci_", is called the
+# "failover-module-name", which begins with "f_" (like "f_asym_sun"). The
+# "failover-module-name" is also used in the override mechanism below.
+ddi-forceload =
+	"misc/scsi_vhci/scsi_vhci_f_asym_sun",
+	"misc/scsi_vhci/scsi_vhci_f_asym_lsi",
+	"misc/scsi_vhci/scsi_vhci_f_asym_emc",
+	"misc/scsi_vhci/scsi_vhci_f_sym_emc",
+	"misc/scsi_vhci/scsi_vhci_f_sym",
+	"misc/scsi_vhci/scsi_vhci_f_tpgs";
+
+#
+# For a device that has a GUID, discovered on a pHCI with mpxio enabled, vHCI
+# access also depends on one of the scsi_vhci failover modules accepting the
+# device.  The default way this occurs is by a failover module's "probe"
+# implementation (sfo_device_probe) indicating the device is supported under
+# scsi_vhci.  To override this default probe-oriented configuration in
+# order to
+#
+#    1)	establish support for a device not currently accepted under scsi_vhci
+#
+# or 2)	override the module selected by "probe"
+#
+# or 3)	disable scsi_vhci support for a device
+#
+# you can add a 'scsi-vhci-failover-override' tuple, as documented in
+# scsi_get_device_type_string(9F). For each tuple, the first part provides
+# basic device identity information (vid/pid) and the second part selects
+# the failover module by "failover-module-name". If you want to disable
+# scsi_vhci support for a device, use the special failover-module-name "NONE".
+# Currently, for each failover-module-name in 'scsi-vhci-failover-override'
+# (except "NONE") there needs to be a
+# "misc/scsi_vhci/scsi_vhci_<failover-module-name>" in 'ddi-forceload' above.
+#
+#	"                  111111"
+#	"012345670123456789012345",	"failover-module-name" or "NONE"
+#	"|-VID--||-----PID------|",
+# scsi-vhci-failover-override =
+#	"STK     FLEXLINE 400",		"f_asym_lsi",
+#	"SUN     T4",			"f_tpgs",
+#	"CME     XIRTEMMYS",		"NONE";
+#
+#END: FAILOVER_MODULE_BLOCK (DO NOT MOVE OR DELETE)
+EOF
 }
 
 #
@@ -93,9 +185,10 @@
 	SEDSCRIPT=$PREFIX.sed
 	NEWIDENT1=$PREFIX.ident1
 	NEWIDENT2=$PREFIX.ident2
+	FMBFILE=$PREFIX.fb
 
 	# make sed script to do most of the work of upgrading
-	make_sed_script
+	make_sed_script $1 $2
 
 	# make sed script to replace old ident with new ident
 	IDENT="^#.*ident.*SMI\"$"
@@ -109,8 +202,14 @@
 	retval=$?
 	if [ $retval -eq 0 ]; then
 		cp $TMPSCSIVHCI $2
+		grep "FAILOVER_MODULE_BLOCK" $2 >/dev/null 2>&1
+		if [ $? -ne 0 ]; then
+			make_fmbfile
+			cat $FMBFILE >> $2
+		fi
 	fi
-	rm -f $NEWIDENT1 $NEWIDENT2 $SEDSCRIPT $TMPSCSIVHCI
+
+	rm -f $NEWIDENT1 $NEWIDENT2 $SEDSCRIPT $TMPSCSIVHCI $FMBFILE
 	return $retval
 }
 
--- a/usr/src/uts/Makefile.targ	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/Makefile.targ	Fri Aug 10 17:00:37 2007 -0700
@@ -188,6 +188,9 @@
 $(ROOT_KGSS_DIR)/%:	$(OBJS_DIR)/% $(ROOT_KGSS_DIR) FRC
 	$(INS.file)
 
+$(ROOT_SCSI_VHCI_DIR)/%: $(OBJS_DIR)/% $(ROOT_SCSI_VHCI_DIR) FRC
+	$(INS.file)
+
 $(ROOT_MACH_DIR)/%:	$(OBJS_DIR)/% $(ROOT_MACH_DIR) FRC
 	$(INS.file)
 
--- a/usr/src/uts/Makefile.uts	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/Makefile.uts	Fri Aug 10 17:00:37 2007 -0700
@@ -425,6 +425,7 @@
 ROOT_SYS_DIR_32		= $(ROOT_MOD_DIR)/sys
 ROOT_MISC_DIR_32	= $(ROOT_MOD_DIR)/misc
 ROOT_KGSS_DIR_32	= $(ROOT_MOD_DIR)/misc/kgss
+ROOT_SCSI_VHCI_DIR_32	= $(ROOT_MOD_DIR)/misc/scsi_vhci
 ROOT_NLMISC_DIR_32	= $(ROOT_MOD_DIR)/misc
 ROOT_MACH_DIR_32	= $(ROOT_MOD_DIR)/mach
 ROOT_CPU_DIR_32		= $(ROOT_MOD_DIR)/cpu
@@ -447,6 +448,7 @@
 ROOT_SYS_DIR_64		= $(ROOT_MOD_DIR)/sys/$(SUBDIR64)
 ROOT_MISC_DIR_64	= $(ROOT_MOD_DIR)/misc/$(SUBDIR64)
 ROOT_KGSS_DIR_64	= $(ROOT_MOD_DIR)/misc/kgss/$(SUBDIR64)
+ROOT_SCSI_VHCI_DIR_64	= $(ROOT_MOD_DIR)/misc/scsi_vhci/$(SUBDIR64)
 ROOT_NLMISC_DIR_64	= $(ROOT_MOD_DIR)/misc/$(SUBDIR64)
 ROOT_MACH_DIR_64	= $(ROOT_MOD_DIR)/mach/$(SUBDIR64)
 ROOT_CPU_DIR_64		= $(ROOT_MOD_DIR)/cpu/$(SUBDIR64)
@@ -469,6 +471,7 @@
 ROOT_SYS_DIR		= $(ROOT_SYS_DIR_$(CLASS))
 ROOT_MISC_DIR		= $(ROOT_MISC_DIR_$(CLASS))
 ROOT_KGSS_DIR		= $(ROOT_KGSS_DIR_$(CLASS))
+ROOT_SCSI_VHCI_DIR	= $(ROOT_SCSI_VHCI_DIR_$(CLASS))
 ROOT_NLMISC_DIR		= $(ROOT_NLMISC_DIR_$(CLASS))
 ROOT_MACH_DIR		= $(ROOT_MACH_DIR_$(CLASS))
 ROOT_CPU_DIR		= $(ROOT_CPU_DIR_$(CLASS))
@@ -487,6 +490,7 @@
 ROOT_MOD_DIRS_32	+= $(ROOT_IPP_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_MISC_DIR_32) $(ROOT_MACH_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_KGSS_DIR_32)
+ROOT_MOD_DIRS_32	+= $(ROOT_SCSI_VHCI_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_CPU_DIR_32) $(ROOT_FONT_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_TOD_DIR_32) $(ROOT_DACF_DIR_32)
 ROOT_MOD_DIRS_32	+= $(ROOT_CRYPTO_DIR_32) $(ROOT_MAC_DIR_32)
--- a/usr/src/uts/common/Makefile.files	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Aug 10 17:00:37 2007 -0700
@@ -659,6 +659,14 @@
 		scsi_hba.o	scsi_transport.o	scsi_confsubr.o \
 		scsi_reset_notify.o
 
+SCSI_VHCI_OBJS +=		scsi_vhci.o mpapi_impl.o
+
+SCSI_VHCI_F_SYM_OBJS +=		sym.o
+
+SCSI_VHCI_F_TPGS_OBJS +=	tpgs.o
+
+SCSI_VHCI_F_ASYM_SUN_OBJS +=	asym_sun.o
+
 SGEN_OBJS +=	sgen.o
 
 SATA_OBJS +=	sata.o
--- a/usr/src/uts/common/Makefile.rules	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/Makefile.rules	Fri Aug 10 17:00:37 2007 -0700
@@ -62,7 +62,7 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o: 		$(COMMONBASE)/acl/%.c
+$(OBJS_DIR)/%.o:		$(COMMONBASE)/acl/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -287,7 +287,7 @@
 	$(COMPILE.c) $(KGSSDFLAGS) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o:  		$(UTSBASE)/common/gssapi/mechs/dummy/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/gssapi/mechs/dummy/%.c
 	$(COMPILE.c) $(KGSSDFLAGS) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -463,11 +463,11 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o: 		$(UTSBASE)/common/io/audio/sada/mixer/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/audio/sada/mixer/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o: 		$(UTSBASE)/common/io/bge/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/bge/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -650,7 +650,7 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
-$(OBJS_DIR)/%.o: 		$(UTSBASE)/common/io/rge/%.c
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/rge/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
@@ -691,6 +691,14 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/fops/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/clients/audio/usb_ac/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1151,7 +1159,7 @@
 $(LINTS_DIR)/%.ln:		$(COMMONBASE)/zfs/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
-$(LINTS_DIR)/%.ln: 		$(UTSBASE)/common/gssapi/%.c
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/gssapi/%.c
 	@($(LHEAD) $(LINT.c) $(KGSSDFLAGS) $< $(LTAIL))
 
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/gssapi/mechs/dummy/%.c
@@ -1443,6 +1451,12 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/fops/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/conf/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
@@ -1575,7 +1589,7 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/ktli/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
-$(LINTS_DIR)/%.ln:       	$(COMMONBASE)/lvm/%.c
+$(LINTS_DIR)/%.ln:		$(COMMONBASE)/lvm/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
 $(LINTS_DIR)/%.ln:		$(COMMONBASE)/crypto/md4/%.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/asym_sun.c	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,552 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implementation of "scsi_vhci_f_asym_sun" asymmetric failover_ops.
+ *
+ * Note : f_asym_sun method is the same as the one originally used by SUN's
+ * T3 (Purple) device.
+ */
+
+#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.  */
+char	*purple_dev_table[] = {
+/*	"                  111111" */
+/*	"012345670123456789012345" */
+/*	"|-VID--||-----PID------|" */
+
+	"SUN     T300            ",
+	"SUN     T4              ",
+	NULL,
+};
+
+/* Failover module plumbing. */
+SCSI_FAILOVER_OP("f_asym_sun", purple, "%I%");
+
+#define	PURPLE_FO_CMD_RETRY_DELAY	1000000 /* 1 seconds */
+#define	PURPLE_FO_RETRY_DELAY		2000000 /* 2 seconds */
+/*
+ * max time for failover to complete is 3 minutes.  Compute
+ * number of retries accordingly, to ensure we wait for at least
+ * 3 minutes
+ */
+#define	PURPLE_FO_MAX_RETRIES	(3*60*1000000)/PURPLE_FO_RETRY_DELAY
+
+/*
+ * max number of retries for purple failover to complete where the ping
+ * command is failing due to transport errors or commands being rejected by
+ * purple.
+ * PURPLE_FO_MAX_RETRIES takes into account the case where CMD_CMPLTs but
+ * purple takes time to complete the failover.
+ */
+#define	PURPLE_FO_MAX_CMD_RETRIES	3
+
+#define	T3_SCSI_ASC_FO_IN_PROGRESS	0x90
+#define	T3_SCSI_ASCQ_PATH_ACT2INACT	0x00
+#define	T3_SCSI_ASCQ_PATH_INACT2ACT	0x01
+#define	T3_SCSI_ASC_PATH_INACTIVE	0x04
+#define	T3_SCSI_ASCQ_PATH_INACTIVE	0x88
+
+static void purple_get_fo_mode(struct scsi_device *sd,
+		int *mode, int *ownership, int *xlf_capable);
+
+/* ARGSUSED */
+static int
+purple_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
+void **ctpriv)
+{
+	char	**dt;
+	int	xlf = 0, mode = 0, ownership = 0;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "purple_device_probe: vidpid %s\n",
+	    stdinq->inq_vid));
+
+	for (dt = purple_dev_table; *dt; dt++) {
+		if (strncmp(stdinq->inq_vid, *dt, strlen(*dt)))
+			continue;
+
+		/* match */
+		purple_get_fo_mode(sd, &mode, &ownership, &xlf);
+		if (mode == SCSI_EXPLICIT_FAILOVER)
+			return (SFO_DEVICE_PROBE_VHCI);
+		else
+			return (SFO_DEVICE_PROBE_PHCI);
+	}
+	return (SFO_DEVICE_PROBE_PHCI);
+}
+
+/* ARGSUSED */
+static void
+purple_device_unprobe(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * For future use
+	 */
+}
+
+/* ARGSUSED */
+static void
+purple_get_fo_mode(struct scsi_device *sd, int *mode,
+int *ownership, int *xlf_capable)
+{
+	char		inqbuf[0xff], *ptr, *end;
+	int		retval = 0;
+	struct buf	*bp;
+	struct scsi_pkt	*pkt;
+	struct scsi_address	*ap;
+
+	*mode = *ownership = *xlf_capable = 0;
+	bp = getrbuf(KM_NOSLEEP);
+	if (bp == NULL)
+		return;
+	bp->b_un.b_addr = inqbuf;
+	bp->b_flags = B_READ;
+	bp->b_bcount = 0xff;
+	bp->b_resid = 0;
+
+	ap = &sd->sd_address;
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
+	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
+	if (pkt == NULL) {
+		freerbuf(bp);
+		return;
+	}
+
+	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
+	pkt->pkt_cdbp[1] = 0x1;
+	pkt->pkt_cdbp[2] = 0x83;
+	pkt->pkt_cdbp[4] = 0xff;
+	pkt->pkt_time = 90;
+
+	retval = vhci_do_scsi_cmd(pkt);
+	scsi_destroy_pkt(pkt);
+	freerbuf(bp);
+	if (retval == 0) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!(sd:%p)failed to get mode"
+		    " and ownership info\n", (void *)sd));
+		return;
+	}
+
+	ptr = inqbuf;
+	ptr += 4; /* identification descriptor 0 */
+	end = inqbuf + 4 + inqbuf[3];
+	while (((ptr[1] & 0x0f) != 0xf) && (ptr < end))
+		ptr += ptr[3] + 4;  /* next identification descriptor */
+	if (ptr >= end) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!(sd:%p)p_g_m_a_o:assuming"
+		    " implicit mode\n", (void *)sd));
+		*mode = SCSI_IMPLICIT_FAILOVER;
+		*ownership = 0;
+		return;
+	}
+	ptr += 4; /* Port Failover Identifier */
+	*mode = ptr[0];
+	if ((ptr[1] & 0x3) == 0x01)
+		*ownership = 0;
+	else if ((ptr[1] & 0x3) == 0x00)
+		*ownership = 1;
+	if (ptr[1] & 0x4) {
+		*xlf_capable = 1;
+	} else {
+		*xlf_capable = 0;
+	}
+}
+
+static int
+purple_activate_explicit(struct scsi_device *sd, int xlf_capable)
+{
+	char			cdb[CDB_GROUP1];
+	struct scsi_address	*ap;
+	struct scsi_pkt		*pkt;
+	int			retval;
+
+	bzero(cdb, CDB_GROUP1);
+
+	ap = &sd->sd_address;
+	pkt = scsi_init_pkt(ap, NULL, NULL, CDB_GROUP1,
+	    sizeof (struct scsi_arq_status), 0, 0, NULL, NULL);
+	if (pkt == NULL)
+		return (0);
+
+	pkt->pkt_cdbp[0] = 0xD0;
+	if (xlf_capable) {
+		/*
+		 * Bit 2/1: 1/0: implicitly drop any reservation
+		 * Bit 0: Grab bit - 1 means an explicit failover will be
+		 * triggered
+		 */
+		pkt->pkt_cdbp[1] = 0x05;
+	} else {
+		pkt->pkt_cdbp[1] = 0x01; /* no reservation check, "grab" lun */
+	}
+
+	retval = vhci_do_scsi_cmd(pkt);
+	scsi_destroy_pkt(pkt);
+
+	return (retval);
+}
+
+/* ARGSUSED */
+static int
+purple_path_activate(struct scsi_device *sd, char *pathclass,
+void *ctpriv)
+{
+	struct buf		*bp;
+	struct scsi_pkt		*pkt;
+	struct scsi_address	*ap;
+	int			err, retry_cnt, retry_cmd_cnt;
+	int			mode, ownership, retval, xlf;
+	struct scsi_extended_sense	*sns;
+
+	ap = &sd->sd_address;
+
+	mode = ownership = 0;
+
+	purple_get_fo_mode(sd, &mode, &ownership, &xlf);
+	if (ownership == 1) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for 0x%p\n",
+		    (void *)sd));
+		return (0);
+	}
+
+	if (mode != SCSI_IMPLICIT_FAILOVER) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "!mode is EXPLICIT for 0x%p xlf %x\n",
+		    (void *)sd, xlf));
+		retval = purple_activate_explicit(sd, xlf);
+		if (retval == 0) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "!(sd:%p)purple_path_activate failed(1)\n",
+			    (void *)sd));
+			return (1);
+		}
+	} else {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!mode is IMPLICIT for 0x%p\n",
+		    (void *)sd));
+	}
+
+	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
+	    B_READ, NULL, NULL);
+	if (!bp) {
+		cmn_err(CE_WARN, "!No resources (buf) to initiate T3 path "
+		    "activation");
+		return (1);
+	}
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
+	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
+	if (!pkt) {
+		cmn_err(CE_WARN, "!Packet alloc failure during T3 "
+		    "path activation");
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+
+	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
+	    SCMD_READ, 1, 1, 0);
+	pkt->pkt_time = 3*30;
+	pkt->pkt_flags |= FLAG_NOINTR;
+
+	retry_cnt = 0;
+	retry_cmd_cnt = 0;
+retry:
+	err = scsi_transport(pkt);
+	if (err != TRAN_ACCEPT) {
+		/*
+		 * Retry TRAN_BUSY till PURPLE_FO_MAX_RETRIES is exhausted.
+		 * All other errors are fatal and should not be retried.
+		 */
+		if ((err == TRAN_BUSY) &&
+		    (retry_cnt++ < PURPLE_FO_MAX_RETRIES)) {
+			drv_usecwait(PURPLE_FO_RETRY_DELAY);
+			goto retry;
+		}
+		cmn_err(CE_WARN, "T3 failover failed, "
+		    "couldn't transport packet");
+		scsi_destroy_pkt(pkt);
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+
+	switch (pkt->pkt_reason) {
+		case CMD_TIMEOUT:
+			cmn_err(CE_WARN, "!T3 failover failed: timed out ");
+			scsi_destroy_pkt(pkt);
+			scsi_free_consistent_buf(bp);
+			return (1);
+		case CMD_CMPLT:
+			/*
+			 * Re-initialize retry_cmd_cnt. Allow transport and
+			 * cmd errors to go through a full retry count when
+			 * these are encountered.  This way TRAN/CMD errors
+			 * retry count is not exhausted due to CMD_CMPLTs
+			 * delay for a T3 fo to finish. This allows the system
+			 * to brave a hick-up on the link at any given time,
+			 * while waiting for the fo to complete.
+			 */
+			retry_cmd_cnt = 0;
+			if (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) {
+					/*
+					 * swallow unit attention
+					 */
+					goto retry;
+				} else if ((sns->es_key == KEY_NOT_READY) &&
+				    (sns->es_add_code ==
+				    T3_SCSI_ASC_FO_IN_PROGRESS) &&
+				    (sns->es_qual_code ==
+				    T3_SCSI_ASCQ_PATH_INACT2ACT)) {
+					if (retry_cnt++ >=
+					    PURPLE_FO_MAX_RETRIES) {
+						cmn_err(CE_WARN, "!T3 failover"
+						    " failed: timed out "
+						    "waiting for path to "
+						    "become active");
+						scsi_destroy_pkt(pkt);
+						scsi_free_consistent_buf(bp);
+						return (1);
+					}
+					VHCI_DEBUG(6, (CE_NOTE, NULL,
+					    "!(sd:%p)lun becoming active...\n",
+					    (void *)sd));
+					drv_usecwait(PURPLE_FO_RETRY_DELAY);
+					goto retry;
+				}
+				cmn_err(CE_NOTE, "!T3 failover failed;"
+				    " sense key:%x, ASC: %x, "
+				    "ASCQ:%x", sns->es_key,
+				    sns->es_add_code, sns->es_qual_code);
+				scsi_destroy_pkt(pkt);
+				scsi_free_consistent_buf(bp);
+				return (1);
+			}
+			switch (SCBP_C(pkt)) {
+				case STATUS_GOOD:
+					break;
+				case STATUS_CHECK:
+					VHCI_DEBUG(4, (CE_WARN, NULL,
+					    "!(sd:%p)T3:"
+					    " cont allegiance during purple "
+					    "activation", (void *)sd));
+					scsi_destroy_pkt(pkt);
+					scsi_free_consistent_buf(bp);
+					return (1);
+				case STATUS_QFULL:
+					VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
+					    "status returned during purple "
+					    "path activation for 0x%p\n",
+					    (void *)sd));
+					drv_usecwait(5000);
+					goto retry;
+				case STATUS_BUSY:
+					VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
+					    "status returned during purple "
+					    "path activation for 0x%p\n",
+					    (void *)sd));
+					drv_usecwait(5000);
+					goto retry;
+				default:
+					VHCI_DEBUG(4, (CE_WARN, NULL,
+					    "!(sd:%p) Bad status "
+					    "returned during purple "
+					    "activation (pkt 0x%p, "
+					    "status %x)",
+					    (void *)sd, (void *)pkt,
+					    SCBP_C(pkt)));
+					scsi_destroy_pkt(pkt);
+					scsi_free_consistent_buf(bp);
+					return (1);
+			}
+			break;
+		case CMD_INCOMPLETE:
+		case CMD_RESET:
+		case CMD_ABORTED:
+		case CMD_TRAN_ERR:
+			/*
+			 * Increased the number of retries when these error
+			 * cases are encountered.  Also added a 1 sec wait
+			 * before retrying.
+			 */
+			if (retry_cmd_cnt++ < PURPLE_FO_MAX_CMD_RETRIES) {
+				drv_usecwait(PURPLE_FO_CMD_RETRY_DELAY);
+				VHCI_DEBUG(4, (CE_WARN, NULL,
+				    "!Retrying T3 path activation due to "
+				    "pkt reason:%x, retry cnt:%d",
+				    pkt->pkt_reason, retry_cmd_cnt));
+				goto retry;
+			}
+			/* FALLTHROUGH */
+		default:
+			cmn_err(CE_WARN, "!T3 path activation did not "
+			    "complete successfully,"
+			    "(pkt reason %x)", pkt->pkt_reason);
+			scsi_destroy_pkt(pkt);
+			scsi_free_consistent_buf(bp);
+			return (1);
+	}
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!T3 path activation success\n"));
+	scsi_destroy_pkt(pkt);
+	scsi_free_consistent_buf(bp);
+	return (0);
+}
+
+/* ARGSUSED */
+static int purple_path_deactivate(struct scsi_device *sd, char *pathclass,
+void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+purple_path_get_opinfo(struct scsi_device *sd, struct scsi_path_opinfo
+*opinfo, void *ctpriv)
+{
+	struct scsi_inquiry	*inq;
+	struct buf		*bp;
+	struct scsi_pkt		*pkt;
+	struct scsi_address	*ap;
+	int			retval, mode, ownership, xlf;
+
+	ap = &sd->sd_address;
+
+	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, SUN_INQSIZE,
+	    B_READ, NULL, NULL);
+	if (!bp)
+		return (1);
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
+	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
+	if (!pkt) {
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
+	    SCMD_INQUIRY, 0, SUN_INQSIZE, 0);
+	pkt->pkt_time = 60;
+
+	retval = vhci_do_scsi_cmd(pkt);
+	if (retval == 0) {
+		scsi_destroy_pkt(pkt);
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+
+	inq = (struct scsi_inquiry *)bp->b_un.b_addr;
+
+	opinfo->opinfo_rev = OPINFO_REV;
+
+	/*
+	 * Ignore to check inquiry dual port bit.
+	 * T3 can return this bit as 0 when one of its controller goes down.
+	 * Instead relying on inquiry port bit only.
+	 */
+	if (inq->inq_port == 0) {
+		(void) strcpy(opinfo->opinfo_path_attr, "primary");
+	} else {
+		(void) strcpy(opinfo->opinfo_path_attr, "secondary");
+	}
+
+	scsi_destroy_pkt(pkt);
+	scsi_free_consistent_buf(bp);
+
+	purple_get_fo_mode(sd, &mode, &ownership, &xlf);
+
+	if (ownership == 1)
+		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
+	else
+		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
+	opinfo->opinfo_xlf_capable = xlf;
+	opinfo->opinfo_pswtch_best = 30;
+	opinfo->opinfo_pswtch_worst = 3*30;
+	opinfo->opinfo_mode = (uint16_t)mode;
+	opinfo->opinfo_preferred = 1;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int purple_path_ping(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * For future use
+	 */
+	return (1);
+}
+
+/* ARGSUSED */
+static int
+purple_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense
+*sense, void *ctpriv)
+{
+	if (sense->es_key == KEY_NOT_READY) {
+		if (sense->es_add_code == T3_SCSI_ASC_FO_IN_PROGRESS) {
+			if (sense->es_qual_code == T3_SCSI_ASCQ_PATH_INACT2ACT)
+				return (SCSI_SENSE_INACT2ACT);
+			else if (sense->es_qual_code ==
+			    T3_SCSI_ASCQ_PATH_ACT2INACT)
+				return (SCSI_SENSE_ACT2INACT);
+		} else if ((sense->es_add_code == T3_SCSI_ASC_PATH_INACTIVE) &&
+		    (sense->es_qual_code == T3_SCSI_ASCQ_PATH_INACTIVE)) {
+			return (SCSI_SENSE_INACTIVE);
+		}
+	}
+
+	/*
+	 * At this point sense data may be for power-on-reset UNIT ATTN or
+	 * hardware errors, vendor unique sense data etc.  For all these cases
+	 * return SCSI_SENSE_UNKNOWN.
+	 */
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "!T3 analyze sense UNKNOWN:"
+	    " sense key:%x, ASC: %x, ASCQ:%x\n", sense->es_key,
+	    sense->es_add_code, sense->es_qual_code));
+	return (SCSI_SENSE_UNKNOWN);
+}
+
+/* ARGSUSED */
+static int
+purple_pathclass_next(char *cur, char **nxt, void *ctpriv)
+{
+	if (cur == NULL) {
+		*nxt = PCLASS_PRIMARY;
+		return (0);
+	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
+		*nxt = PCLASS_SECONDARY;
+		return (0);
+	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
+		return (ENOENT);
+	} else {
+		return (EINVAL);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/sym.c	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,174 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implementation of "scsi_vhci_f_sym" symmetric failover_ops.
+ *
+ * This file was historically meant for only symmetric implementation.  It has
+ * been extended to manage SUN "supported" symmetric controllers. The supported
+ * VID/PID shall be listed in the symmetric_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.  */
+char *symmetric_dev_table[] = {
+/*	"                  111111" */
+/*	"012345670123456789012345" */
+/*	"|-VID--||-----PID------|" */
+				/* disks */
+	"IBM     DDYFT",
+	"IBM     IC",
+	"SEAGATE ST",
+				/* enclosures */
+	"SUN     SENA",			/* SES device */
+	"SUN     SESS01",		/* VICOM SVE box */
+	"SUNW    SUNWGS",		/* Daktari enclosure */
+				/* arrays */
+	"HITACHI OPEN",			/* Hitachi storage */
+	"SUN     PSX1000",		/* Pirus Matterhorn */
+	"SUN     SE6920",		/* Pirus */
+	"SUN     SE6940",		/* DSP - Nauset */
+	"SUN     StorEdge 3510",	/* Minnow FC */
+	"SUN     StorEdge 3511",	/* Minnow SATA RAID */
+	"SUN     StorageTek 6920",	/* DSP */
+	"SUN     StorageTek 6940",	/* DSP - Nauset */
+	"SUN     StorageTek NAS",	/* StorageTek NAS */
+	"SUN     MRA300_R",		/* Shamrock - Controller */
+	"SUN     MRA300_E",		/* Shamrock - Expansion */
+
+	NULL
+};
+
+/* Failover module plumbing. */
+SCSI_FAILOVER_OP(SFO_NAME_SYM, symmetric, "%I%");
+
+/* ARGSUSED */
+static int
+symmetric_device_probe(struct scsi_device *sd, struct scsi_inquiry *stdinq,
+void **ctpriv)
+{
+	char	**dt;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "!inq str: %s\n", stdinq->inq_vid));
+	for (dt = symmetric_dev_table; *dt; dt++)
+		if (strncmp(stdinq->inq_vid, *dt, strlen(*dt)) == 0)
+			return (SFO_DEVICE_PROBE_VHCI);
+
+	/*
+	 * No match, check for generic Sun supported disks:
+	 *
+	 *	"|-VID--||-----PID------|"
+	 *	"012345670123456789012345"
+	 *	".................SUN..G."
+	 *	".................SUN..T."
+	 *	".................SUN...G"
+	 *	".................SUN...T"
+	 */
+	if (bcmp(&stdinq->inq_pid[9], "SUN", 3) == 0) {
+		if ((stdinq->inq_pid[14] == 'G' || stdinq->inq_pid[15] == 'G' ||
+		    stdinq->inq_pid[14] == 'T' || stdinq->inq_pid[15] == 'T') &&
+		    (stdinq->inq_dtype == DTYPE_DIRECT)) {
+			return (SFO_DEVICE_PROBE_VHCI);
+		}
+	}
+	return (SFO_DEVICE_PROBE_PHCI);
+}
+
+/* ARGSUSED */
+static void
+symmetric_device_unprobe(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * NOP for symmetric
+	 */
+}
+
+/* ARGSUSED */
+static int
+symmetric_path_activate(struct scsi_device *sd, char *pathclass, void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+symmetric_path_deactivate(struct scsi_device *sd, char *pathclass,
+void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+symmetric_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, "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
+symmetric_path_ping(struct scsi_device *sd, void *ctpriv)
+{
+	return (1);
+}
+
+/* ARGSUSED */
+static int
+symmetric_analyze_sense(struct scsi_device *sd,
+struct scsi_extended_sense *sense, void *ctpriv)
+{
+	return (SCSI_SENSE_NOFAILOVER);
+}
+
+/* ARGSUSED */
+static int
+symmetric_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);
+	} else {
+		return (EINVAL);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,1063 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implementation of "scsi_vhci_f_tpgs" T10 standard based failover_ops.
+ *
+ * NOTE: for non-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>
+
+/* Supported device table entries.  */
+char	*std_dev_table[] = { NULL };
+
+/* 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 */
+/*
+ * max time for failover to complete is 3 minutes.  Compute
+ * number of retries accordingly, to ensure we wait for at least
+ * 3 minutes
+ */
+#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_TPG_SERVICE_ACTION	0x0A
+#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
+
+#define	SCMD_REPORT_TARGET_PORT_GROUP	0xA3;
+#define	SCMD_SET_TARGET_PORT_GROUP	0xA4;
+
+/* 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)
+{
+	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) {
+		VHCI_DEBUG(4, (CE_WARN, NULL,
+		    "!std_device_probe: not a standard tpgs device"));
+		return (SFO_DEVICE_PROBE_PHCI);
+	}
+
+	if (dtype == DTYPE_SEQUENTIAL) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "!std_device_probe: Detected a "
+		    "Standard Asymmetric device "
+		    "not yet supported\n"));
+		return (SFO_DEVICE_PROBE_PHCI);
+	}
+
+	if (std_get_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) {
+		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) {
+		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) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!std_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,
+	    "!std_device_probe: "
+	    "Unknown tpgs_bits: %x", tpgs_bits));
+	return (SFO_DEVICE_PROBE_PHCI);
+}
+
+/* ARGSUSED */
+static void
+std_device_unprobe(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * For future use
+	 */
+}
+
+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_REPORT_TARGET_PORT_GROUP;
+	pkt->pkt_cdbp[1] = STD_SCSI_TPG_SERVICE_ACTION;
+	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");
+	return (1);
+}
+
+/*
+ * Process the packet reason of CMD_PKT_CMPLT - return 0 if no
+ * retry and 1 if a retry should be done
+ */
+static int
+std_process_cmplt_pkt(struct scsi_device *sd, struct scsi_pkt *pkt,
+	int *retry_cnt)
+{
+	struct scsi_extended_sense	*sns;
+
+	/*
+	 * Re-initialize retry_cmd_cnt. Allow transport and
+	 * cmd errors to go through a full retry count when
+	 * these are encountered.  This way TRAN/CMD errors
+	 * retry count is not exhausted due to CMD_CMPLTs
+	 * delay. This allows the system
+	 * to brave a hick-up on the link at any given time,
+	 * while waiting for the fo to complete.
+	 */
+	if (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) {
+			/*
+			 * tpgs access state changed
+			 */
+			if (sns->es_add_code == STD_SCSI_ASC_STATE_CHG &&
+			    sns->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC) {
+				/* XXX: update path info? */
+				cmn_err(CE_WARN, "!Device failover"
+				    " state change");
+			}
+			return (1);
+		} else if (sns->es_key == KEY_NOT_READY) {
+			if ((*retry_cnt)++ >=
+			    STD_FO_MAX_RETRIES) {
+				cmn_err(CE_WARN, "!Device failover"
+				    " failed: timed out waiting "
+				    "for path to become active");
+				return (0);
+			}
+			VHCI_DEBUG(6, (CE_NOTE, NULL,
+			    "!(sd:%p)lun "
+			    "becoming active...\n", (void *)sd));
+			drv_usecwait(STD_FO_RETRY_DELAY);
+			return (1);
+		}
+		cmn_err(CE_NOTE, "!Failover failed;"
+		    " sense key:%x, ASC: %x, "
+		    "ASCQ:%x", sns->es_key,
+		    sns->es_add_code, sns->es_qual_code);
+		return (0);
+	}
+	switch (SCBP_C(pkt)) {
+		case STATUS_GOOD:
+			break;
+		case STATUS_CHECK:
+			VHCI_DEBUG(4, (CE_WARN, NULL,
+			    "!(sd:%p):"
+			    " status returned CHECK during std"
+			    " path activation", (void *)sd));
+			return (0);
+		case STATUS_QFULL:
+			VHCI_DEBUG(6, (CE_NOTE, NULL, "QFULL "
+			    "status returned QFULL during std "
+			    "path activation for %p\n", (void *)sd));
+			drv_usecwait(5000);
+			return (1);
+		case STATUS_BUSY:
+			VHCI_DEBUG(6, (CE_NOTE, NULL, "BUSY "
+			    "status returned BUSY during std "
+			    "path activation for %p\n", (void *)sd));
+			drv_usecwait(5000);
+			return (1);
+		default:
+			VHCI_DEBUG(4, (CE_WARN, NULL,
+			    "!(sd:%p) Bad status returned during std "
+			    "activation (pkt %p, status %x)",
+			    (void *)sd, (void *)pkt, SCBP_C(pkt)));
+			return (0);
+	}
+	return (0);
+}
+
+/*
+ * For now we are going to use primary/online and secondary/online.
+ * There is no standby path returned by the dsp and we may have
+ * to do something different for other devices that use standby
+ */
+/* ARGSUSED */
+static int
+std_path_activate(struct scsi_device *sd, char *pathclass,
+void *ctpriv)
+{
+	struct buf			*bp;
+	struct scsi_pkt			*pkt;
+	struct scsi_address		*ap;
+	int				err, retry_cnt, retry_cmd_cnt;
+	int				mode, state, retval, xlf, preferred;
+
+	ap = &sd->sd_address;
+
+	mode = state = 0;
+
+	if (std_get_fo_mode(sd, &mode, &state, &xlf, &preferred)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_activate:"
+		    " failed std_get_fo_mode\n"));
+		return (1);
+	}
+	if ((state == STD_ACTIVE_OPTIMIZED) ||
+	    (state == STD_ACTIVE_NONOPTIMIZED)) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!path already active for %p\n",
+		    (void *)sd));
+		return (0);
+	}
+
+	if (mode != SCSI_IMPLICIT_FAILOVER) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "!mode is EXPLICIT for %p xlf %x\n",
+		    (void *)sd, xlf));
+		retval = std_activate_explicit(sd, xlf);
+		if (retval != 0) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "!(sd:%p)std_path_activate failed(1)\n",
+			    (void *)sd));
+			return (1);
+		}
+	} else {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "STD mode is IMPLICIT for %p\n",
+		    (void *)sd));
+	}
+
+	bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL, DEV_BSIZE,
+	    B_READ, NULL, NULL);
+	if (!bp) {
+		VHCI_DEBUG(4, (CE_WARN, NULL,
+		    "!(sd:%p)std_path_activate failed to alloc buffer",
+		    (void *)sd));
+		return (1);
+	}
+
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP1,
+	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL);
+	if (!pkt) {
+		VHCI_DEBUG(4, (CE_WARN, NULL,
+		    "!(sd:%p)std_path_activate failed to initialize packet",
+		    (void *)sd));
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+
+	(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)pkt->pkt_cdbp,
+	    SCMD_READ, 1, 1, 0);
+	pkt->pkt_time = 3*30;
+	pkt->pkt_flags |= FLAG_NOINTR;
+
+	retry_cnt = 0;
+	retry_cmd_cnt = 0;
+retry:
+	err = scsi_transport(pkt);
+	if (err != TRAN_ACCEPT) {
+		/*
+		 * Retry TRAN_BUSY till STD_FO_MAX_RETRIES is exhausted.
+		 * All other errors are fatal and should not be retried.
+		 */
+		if ((err == TRAN_BUSY) &&
+		    (retry_cnt++ < STD_FO_MAX_RETRIES)) {
+			drv_usecwait(STD_FO_RETRY_DELAY);
+			goto retry;
+		}
+		cmn_err(CE_WARN, "Failover failed, "
+		    "couldn't transport packet");
+		scsi_destroy_pkt(pkt);
+		scsi_free_consistent_buf(bp);
+		return (1);
+	}
+	switch (pkt->pkt_reason) {
+		case CMD_CMPLT:
+			retry_cmd_cnt = 0;
+			retval = std_process_cmplt_pkt(sd, pkt, &retry_cnt);
+			if (retval != 0) {
+				goto retry;
+			}
+			break;
+		case CMD_TIMEOUT:
+			cmn_err(CE_WARN, "!Failover failed: timed out ");
+			retval = 1;
+			break;
+		case CMD_INCOMPLETE:
+		case CMD_RESET:
+		case CMD_ABORTED:
+		case CMD_TRAN_ERR:
+			/*
+			 * Increased the number of retries when these error
+			 * cases are encountered.  Also added a 1 sec wait
+			 * before retrying.
+			 */
+			if (retry_cmd_cnt++ < STD_FO_MAX_CMD_RETRIES) {
+				drv_usecwait(STD_FO_CMD_RETRY_DELAY);
+				VHCI_DEBUG(4, (CE_WARN, NULL,
+				    "!Retrying path activation due to "
+				    "pkt reason:%x, retry cnt:%d",
+				    pkt->pkt_reason, retry_cmd_cnt));
+				goto retry;
+			}
+			/* FALLTHROUGH */
+		default:
+			cmn_err(CE_WARN, "!Path activation did not "
+			    "complete successfully,"
+			    "(pkt reason %x)", pkt->pkt_reason);
+			retval = 1;
+			break;
+	}
+
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!Path activation success\n"));
+	scsi_destroy_pkt(pkt);
+	scsi_free_consistent_buf(bp);
+	return (retval);
+}
+
+/* ARGSUSED */
+static int std_path_deactivate(struct scsi_device *sd, char *pathclass,
+void *ctpriv)
+{
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+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)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_path_getopinfo:"
+		    " failed std_get_fo_mode\n"));
+		return (1);
+	}
+
+	if (state == STD_ACTIVE_OPTIMIZED) {
+		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE;
+	} else if (state == STD_ACTIVE_NONOPTIMIZED) {
+		opinfo->opinfo_path_state = SCSI_PATH_ACTIVE_NONOPT;
+	} else if (state == STD_STANDBY) {
+		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
+	} else if (state == STD_UNAVAILABLE) {
+		opinfo->opinfo_path_state = SCSI_PATH_INACTIVE;
+	}
+	if (preferred) {
+		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_PRIMARY);
+	} else {
+		(void) strcpy(opinfo->opinfo_path_attr, PCLASS_SECONDARY);
+	}
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "std_path_get_opinfo: "
+	    "class: %s state: %s\n", opinfo->opinfo_path_attr,
+	    opinfo->opinfo_path_state == SCSI_PATH_ACTIVE ?
+	    "ACTIVE" : "INACTIVE"));
+	opinfo->opinfo_xlf_capable = 0;
+	opinfo->opinfo_pswtch_best = 30;
+	opinfo->opinfo_pswtch_worst = 3*30;
+	opinfo->opinfo_preferred = (uint16_t)preferred;
+	opinfo->opinfo_mode = (uint16_t)mode;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int std_path_ping(struct scsi_device *sd, void *ctpriv)
+{
+	/*
+	 * For future use
+	 */
+	return (1);
+}
+
+/*
+ * Analyze the sense code to determine whether failover process
+ */
+/* ARGSUSED */
+static int
+std_analyze_sense(struct scsi_device *sd, struct scsi_extended_sense
+*sense, void *ctpriv)
+{
+	int rval = SCSI_SENSE_UNKNOWN;
+
+	if ((sense->es_key == KEY_UNIT_ATTENTION) &&
+	    (sense->es_add_code == STD_SCSI_ASC_STATE_CHG) &&
+	    (sense->es_qual_code == STD_SCSI_ASCQ_STATE_CHG_SUCC)) {
+		rval = SCSI_SENSE_STATE_CHANGED;
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
+		    " sense_key:%x, add_code: %x, qual_code:%x"
+		    " sense:%x\n", sense->es_key, sense->es_add_code,
+		    sense->es_qual_code, rval));
+	} else if ((sense->es_key == KEY_NOT_READY) &&
+	    (sense->es_add_code == STD_LOGICAL_UNIT_NOT_ACCESSIBLE) &&
+	    (sense->es_qual_code == STD_TGT_PORT_UNAVAILABLE)) {
+		rval = SCSI_SENSE_INACTIVE;
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!std_analyze_sense:"
+		    " sense_key:%x, add_code: %x, qual_code:%x"
+		    " sense:%x\n", sense->es_key, sense->es_add_code,
+		    sense->es_qual_code, rval));
+	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
+	    (sense->es_add_code == STD_SCSI_ASC_INVAL_PARAM_LIST)) {
+		rval = SCSI_SENSE_NOFAILOVER;
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
+		    " sense_key:%x, add_code: %x, qual_code:%x"
+		    " sense:%x\n", sense->es_key, sense->es_add_code,
+		    sense->es_qual_code, rval));
+	} else if ((sense->es_key == KEY_ILLEGAL_REQUEST) &&
+	    (sense->es_add_code == STD_SCSI_ASC_INVAL_CMD_OPCODE)) {
+		rval = SCSI_SENSE_NOFAILOVER;
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_analyze_sense:"
+		    " sense_key:%x, add_code: %x, qual_code:%x"
+		    " sense:%x\n", sense->es_key, sense->es_add_code,
+		    sense->es_qual_code, rval));
+	} else {
+		/*
+		 * At this point sense data may be for power-on-reset
+		 * UNIT ATTN hardware errors, vendor unqiue sense data etc.
+		 * For all these cases, return SCSI_SENSE_UNKNOWN.
+		 */
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Analyze sense UNKNOWN:"
+		    " sense key:%x, ASC:%x, ASCQ:%x\n", sense->es_key,
+		    sense->es_add_code, sense->es_qual_code));
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+std_pathclass_next(char *cur, char **nxt, void *ctpriv)
+{
+	/*
+	 * The first phase does not have a standby path so
+	 * there will be no explicit failover - when standard tpgs.
+	 * standard defines preferred flag then we should start
+	 * using this as the selection mechanism - there can be
+	 * preferred primary standby that we should fail to first and then
+	 * nonpreferred secondary standby.
+	 */
+	if (cur == NULL) {
+		*nxt = PCLASS_PRIMARY;
+		return (0);
+	} else if (strcmp(cur, PCLASS_PRIMARY) == 0) {
+		*nxt = PCLASS_SECONDARY;
+		return (0);
+	} else if (strcmp(cur, PCLASS_SECONDARY) == 0) {
+		return (ENOENT);
+	} else {
+		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_SET_TARGET_PORT_GROUP;
+	pkt->pkt_cdbp[1] = STD_SCSI_TPG_SERVICE_ACTION;
+	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/mpapi_impl.c	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,4188 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * SNIA Multipath Management API implementation
+ */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/disp.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunmdi.h>
+#include <sys/mdi_impldefs.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/impl/services.h>
+#include <sys/scsi/impl/scsi_reset_notify.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+
+/* used to manually force a request sense */
+int vhci_force_manual_sense = 0;
+
+#define	STD_ACTIVE_OPTIMIZED	0x0
+#define	STD_ACTIVE_NONOPTIMIZED	0x1
+#define	STD_STANDBY		0x2
+#define	STD_UNAVAILABLE		0x3
+#define	STD_TRANSITIONING	0xf
+
+/*
+ * MP-API Prototypes
+ */
+int vhci_mpapi_init(struct scsi_vhci *);
+void vhci_mpapi_add_dev_prod(struct scsi_vhci *, char *);
+int vhci_mpapi_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
+void vhci_update_mpapi_data(struct scsi_vhci *,
+    scsi_vhci_lun_t *, mdi_pathinfo_t *);
+void* vhci_get_mpapi_item(struct scsi_vhci *, mpapi_list_header_t *,
+    uint8_t, void*);
+int vhci_mpapi_sync_init_port_list(dev_info_t *, void *);
+int vhci_mpapi_get_vhci(dev_info_t *, void *);
+void vhci_mpapi_set_path_state(dev_info_t *, mdi_pathinfo_t *, int);
+void vhci_mpapi_synthesize_tpg_data(struct scsi_vhci *, scsi_vhci_lun_t *,
+    mdi_pathinfo_t *);
+void vhci_mpapi_update_tpg_data(struct scsi_address *, char *);
+int vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *,
+    scsi_vhci_lun_t *);
+
+/* Static Functions */
+static int vhci_get_driver_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_dev_prod_list(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_dev_prod_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_lu_list(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_lu_list_from_tpg(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_tpg_list_for_lu(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_lu_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_path_list_for_mp_lu(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_path_list_for_init_port(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_path_list_for_target_port(struct scsi_vhci *,
+    mp_iocdata_t *, void *, void *, int);
+static int vhci_get_path_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_init_port_list(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_init_port_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_target_port_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_tpg_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_target_port_list_for_tpg(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_set_tpg_access_state(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_prop_lb_list(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_get_prop_lb_prop(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_assign_lu_to_tpg(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_enable_auto_failback(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_disable_auto_failback(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_enable_path(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_disable_path(struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_send_uscsi_cmd(dev_t dev, struct scsi_vhci *, mp_iocdata_t *,
+    void *, void *, int);
+static int vhci_mpapi_validate(void *, mp_iocdata_t *, int, cred_t *);
+static uint64_t vhci_mpapi_create_oid(mpapi_priv_t *, uint8_t);
+static int vhci_mpapi_ioctl(dev_t dev, struct scsi_vhci *, void *,
+    mp_iocdata_t *, int, cred_t *);
+static int vhci_mpapi_add_to_list(mpapi_list_header_t *, mpapi_item_list_t *);
+static mpapi_item_list_t *vhci_mpapi_create_item(struct scsi_vhci *,
+    uint8_t, void *);
+static mpapi_item_list_t *vhci_mpapi_get_tpg_item(struct scsi_vhci *,
+    uint32_t, void *, char *, void *);
+static mpapi_list_header_t *vhci_mpapi_create_list_head();
+static int vhci_get_mpiocdata(const void *, mp_iocdata_t *, int);
+static int vhci_is_model_type32(int);
+static int vhci_mpapi_copyout_iocdata(void *, void *, int);
+static int vhci_mpapi_chk_last_path(mdi_pathinfo_t *);
+static int vhci_mpapi_sync_lu_oid_list(struct scsi_vhci *);
+static mpapi_item_list_t *vhci_mpapi_get_tpg_for_lun(struct scsi_vhci *,
+    char *, void *, void *);
+static int vhci_mpapi_check_tp_in_tpg(mpapi_tpg_data_t *tpgdata, void *tp);
+static void vhci_mpapi_log_sysevent(dev_info_t *, uint64_t *, char *);
+static mpapi_item_list_t *vhci_mpapi_match_pip(struct scsi_vhci *,
+    mpapi_item_list_t *, void *);
+static mpapi_item_list_t *vhci_mpapi_match_lu(struct scsi_vhci *,
+    mpapi_item_list_t *, void *);
+static void *vhci_mpapi_get_rel_tport_pair(struct scsi_vhci *vhci,
+    mpapi_list_header_t *list, void *tgt_port, uint32_t rel_tid);
+
+/*
+ * Extern variables, structures and functions
+ */
+extern void	*vhci_softstate;
+extern char	vhci_version_name[];
+extern int	(*tpgs_set_target_groups)(struct scsi_address *, int, int);
+
+extern void mdi_vhci_walk_phcis(dev_info_t *,
+    int (*)(dev_info_t *, void *), void *);
+extern void vhci_update_pathstates(void *);
+extern int vhci_uscsi_iostart(struct buf *bp);
+
+/*
+ * Routine for SCSI VHCI MPAPI IOCTL implementation.
+ */
+/* ARGSUSED */
+int
+vhci_mpapi_ctl(dev_t dev, int cm, intptr_t data, int mode,
+    cred_t *credp, int *rval)
+{
+	struct scsi_vhci		*vhci;
+	dev_info_t			*vdip;
+	int				retval = 0;
+	mp_iocdata_t			mpio_blk;
+	mp_iocdata_t			*mpioc = &mpio_blk;
+
+	/* Check for validity of vhci structure */
+	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
+	if (vhci == NULL) {
+		return (ENXIO);
+	}
+
+	mutex_enter(&vhci->vhci_mutex);
+	if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
+		mutex_exit(&vhci->vhci_mutex);
+		return (ENXIO);
+	}
+	mutex_exit(&vhci->vhci_mutex);
+
+	/* Get the vhci dip */
+	vdip = vhci->vhci_dip;
+	ASSERT(vdip != NULL);
+
+	/*
+	 * Get IOCTL parameters from userland
+	 */
+	if (vhci_get_mpiocdata((const void *)data, mpioc, mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_mpapi_ctl: "
+		    "vhci_get_mpiocdata() failed"));
+	}
+	if (mpioc->mp_cmd < MP_API_SUBCMD_MIN ||
+	    mpioc->mp_cmd > MP_API_SUBCMD_MAX) {
+		return (ENXIO);
+	}
+
+	retval = vhci_mpapi_ioctl(dev, vhci, (void *)data, mpioc, mode, credp);
+
+	return (retval);
+}
+
+/* ARGSUSED */
+static int
+vhci_mpapi_validate(void *udata, mp_iocdata_t *mpioc, int mode, cred_t *credp)
+{
+	int		rval = 0, olen = 0;
+	int		mode32 = 0;
+
+	if (vhci_is_model_type32(mode) == 1) {
+		mode32 = 1;
+	}
+
+	switch (mpioc->mp_cmd) {
+
+	case MP_GET_DEV_PROD_LIST:
+	case MP_GET_LU_LIST: /* XXX: This wont come; Plugin already has it */
+	case MP_GET_INIT_PORT_LIST: /* XXX: This call wont come either */
+	case MP_GET_TPG_LIST:
+	case MP_GET_PROPRIETARY_LOADBALANCE_LIST:
+	{
+		if ((mpioc->mp_olen == 0) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen == 0) {
+			/* We don't know alen yet, No point trying to set it */
+			mpioc->mp_errno = MP_MORE_DATA;
+			rval = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_DRIVER_PROP:
+	{
+		olen = sizeof (mp_driver_prop_t);
+		/* Adjust olen to account for the caddr_t in 32-bit mode */
+		if (mode32 == 1) {
+			olen -= 4;
+		}
+
+		if ((mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_DEV_PROD_PROP:
+	{
+		olen = sizeof (mp_dev_prod_prop_t);
+
+		if ((mpioc->mp_olen < olen) ||
+		    (mpioc->mp_ilen < sizeof (uint64_t)) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_LU_PROP:
+	{
+		olen = sizeof (mp_logical_unit_prop_t);
+		/* Adjust olen to account for the caddr_t in 32-bit mode */
+		if (mode32 == 1) {
+			olen -= 4;
+		}
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_PATH_PROP:
+	{
+		olen = sizeof (mp_path_prop_t);
+		/* Adjust olen to account for the caddr_t in 32-bit mode */
+		if (mode32 == 1) {
+			olen -= 4;
+		}
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_INIT_PORT_PROP:
+	{
+		olen = sizeof (mp_init_port_prop_t);
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_TARGET_PORT_PROP:
+	{
+		olen = sizeof (mp_target_port_prop_t);
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_TPG_PROP:
+	{
+		olen = sizeof (mp_tpg_prop_t);
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_PROPRIETARY_LOADBALANCE_PROP:
+	{
+		olen = sizeof (mp_proprietary_loadbalance_prop_t);
+		/* Adjust olen to account for the caddr_t in 32-bit mode */
+		if (mode32 == 1) {
+			olen -= 4;
+		}
+
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen < olen) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen < olen) {
+			mpioc->mp_alen = olen;
+			mpioc->mp_errno = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_GET_PATH_LIST_FOR_MP_LU:
+	case MP_GET_PATH_LIST_FOR_INIT_PORT:
+	case MP_GET_PATH_LIST_FOR_TARGET_PORT:
+	case MP_GET_LU_LIST_FROM_TPG:
+	case MP_GET_TPG_LIST_FOR_LU:
+	case MP_GET_TARGET_PORT_LIST_FOR_TPG:
+	{
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen == 0) ||
+		    (mpioc->mp_obuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_READ)) {
+			rval = EINVAL;
+		}
+		if (mpioc->mp_olen == 0) {
+			/* We don't know alen yet, No point trying to set it */
+			mpioc->mp_errno = MP_MORE_DATA;
+			rval = MP_MORE_DATA;
+		}
+	}
+	break;
+
+	case MP_SET_TPG_ACCESS_STATE:
+	{
+		if (drv_priv(credp) != 0) {
+			rval = EPERM;
+			break;
+		}
+		if ((mpioc->mp_ilen != sizeof (mp_set_tpg_state_req_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_xfer != MP_XFER_WRITE)) {
+			rval = EINVAL;
+		}
+	}
+	break;
+
+	case MP_ENABLE_AUTO_FAILBACK:
+	case MP_DISABLE_AUTO_FAILBACK:
+	{
+		if (drv_priv(credp) != 0) {
+			rval = EPERM;
+			break;
+		}
+		if ((mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_xfer !=  MP_XFER_WRITE)) {
+			rval = EINVAL;
+		}
+	}
+	break;
+
+	case MP_ENABLE_PATH:
+	case MP_DISABLE_PATH:
+	{
+		if (drv_priv(credp) != 0) {
+			rval = EPERM;
+			break;
+		}
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_xfer !=  MP_XFER_WRITE)) {
+			rval = EINVAL;
+		}
+	}
+	break;
+
+	case MP_SEND_SCSI_CMD:
+	{
+		cred_t	*cr;
+		int	olen = 0;
+
+		cr = ddi_get_cred();
+		if (drv_priv(credp) != 0 && drv_priv(cr) != 0) {
+			rval = EPERM;
+			break;
+		}
+		if (mode32 == 1) {
+			olen = sizeof (struct uscsi_cmd32);
+		} else {
+			olen = sizeof (struct uscsi_cmd);
+		}
+		/* oid is in the ibuf and the uscsi cmd is in the obuf */
+		if ((mpioc->mp_ilen != sizeof (uint64_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_olen != olen) ||
+		    (mpioc->mp_obuf == NULL)) {
+			rval = EINVAL;
+		}
+	}
+	break;
+
+	case MP_ASSIGN_LU_TO_TPG:
+	{
+		if (drv_priv(credp) != 0) {
+			rval = EPERM;
+			break;
+		}
+		if ((mpioc->mp_ilen != sizeof (mp_lu_tpg_pair_t)) ||
+		    (mpioc->mp_ibuf == NULL) ||
+		    (mpioc->mp_xfer !=  MP_XFER_WRITE)) {
+			rval = EINVAL;
+		}
+	}
+	break;
+
+	default:
+	{
+		rval = EINVAL;
+	}
+
+	} /* Closing the main switch */
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_driver_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	mp_driver_prop_t	*mpdp = (mp_driver_prop_t *)output_data;
+
+	if (output_data == NULL) {
+		return (EINVAL);
+	}
+
+	(void) strlcpy(mpdp->driverVersion, vhci_version_name,
+	    sizeof (mpdp->driverVersion));
+	mpdp->supportedLoadBalanceTypes =
+	    MP_DRVR_LOAD_BALANCE_TYPE_NONE |
+	    MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN |
+	    MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION;
+	mpdp->canSetTPGAccess = B_TRUE;
+	mpdp->canOverridePaths = B_FALSE;
+	mpdp->exposesPathDeviceFiles = B_FALSE;
+	(void) strlcpy(mpdp->deviceFileNamespace, "/devices/scsi_vhci",
+	    sizeof (mpdp->deviceFileNamespace));
+	mpdp->onlySupportsSpecifiedProducts = 1;
+	mpdp->maximumWeight = 1;
+	mpdp->failbackPollingRateMax = 0;
+	mpdp->currentFailbackPollingRate = 0;
+	mpdp->autoFailbackSupport = MP_DRVR_AUTO_FAILBACK_SUPPORT;
+	mutex_enter(&vhci->vhci_mutex);
+	mpdp->autoFailbackEnabled =
+	    ((vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK) ?
+	    1 : 0);
+	mutex_exit(&vhci->vhci_mutex);
+	mpdp->defaultLoadBalanceType =
+	    MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
+	mpdp->probingPollingRateMax = 0;
+	mpdp->currentProbingPollingRate = 0;
+	mpdp->autoProbingSupport = 0;
+	mpdp->autoProbingEnabled = 0;
+
+	if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    mpioc->mp_olen, mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_driver_prop: "
+		    "ddi_copyout() for 64-bit failed"));
+		mpioc->mp_errno = EFAULT;
+	} else {
+		mpioc->mp_errno = 0;
+		mpioc->mp_alen = sizeof (mp_iocdata_t);
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_dev_prod_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	mpapi_item_list_t	*ilist;
+
+	if (output_data == NULL) {
+		return (EINVAL);
+	}
+
+	/*
+	 * XXX: Get the Plugin OID from the input_data and apply below
+	 * Currently, we know we have only 1 plugin, so it ok to directly
+	 * return this only plugin's device product list.
+	 */
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_DEVICE_PRODUCT]->head;
+
+	while (ilist != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)ilist->item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		ilist = ilist->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_dev_prod_list: "
+		    "ddi_copyout() failed"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	} else {
+		mpioc->mp_errno = 0;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_dev_prod_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_dev_prod_prop_t	*dev_prop = NULL;
+	mpapi_item_list_t	*ilist;
+
+	if ((output_data == NULL) || (input_data == NULL)) {
+		return (EINVAL);
+	}
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_DEVICE_PRODUCT]->head;
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
+		ilist = ilist->next;
+	}
+	if (ilist != NULL) {
+		dev_prop = (mp_dev_prod_prop_t *)(ilist->item->idata);
+		if (dev_prop == NULL) {
+			return (EINVAL);
+		}
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_dev_prod_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)dev_prop, mpioc->mp_obuf,
+	    sizeof (mp_dev_prod_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_lu_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*ld;
+
+	if (output_data == NULL) {
+		return (EINVAL);
+	}
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while (ilist != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)(ilist->item->oid.raw_oid);
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		ld = ilist->item->idata;
+		if (ld->valid == 0) {
+			count--;
+		}
+		ilist = ilist->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	} else {
+		mpioc->mp_errno = 0;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_lu_list_from_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *tpg_lu_list = NULL;
+	mpapi_tpg_data_t	*mptpglu;
+	mpapi_lu_data_t		*ld;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
+	    ->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mptpglu = (mpapi_tpg_data_t *)(ilist->item->idata);
+		if (mptpglu->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_"
+			    "tpg: OID NOT FOUND - TPG IS INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		tpg_lu_list = mptpglu->lu_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
+		    "Unknown Error"));
+	}
+
+	while (tpg_lu_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)tpg_lu_list->
+			    item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		/*
+		 * Get rid of the latest entry if item is invalid
+		 */
+		ld = tpg_lu_list->item->idata;
+		if (ld->valid == 0) {
+			count--;
+		}
+		tpg_lu_list = tpg_lu_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_list_from_tpg: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_tpg_list_for_lu(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *mplu_tpg_list = NULL;
+	mpapi_lu_data_t		*mplutpg;
+	mpapi_tpg_data_t	*tpgd;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mplutpg = (mpapi_lu_data_t *)(ilist->item->idata);
+		if (mplutpg->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_"
+			    "lu: OID NOT FOUND - LU IS OFFLINE"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mplu_tpg_list = mplutpg->tpg_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
+		    "Unknown Error"));
+	}
+
+	while (mplu_tpg_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] =
+			    (uint64_t)mplu_tpg_list->item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		tpgd = mplu_tpg_list->item->idata;
+		if (tpgd->valid == 0) {
+			count--;
+		}
+		mplu_tpg_list = mplu_tpg_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_list_for_lu: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_lu_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_logical_unit_prop_t	*mplup_prop;
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*mplup;
+
+	mplup_prop = (mp_logical_unit_prop_t *)output_data;
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
+		ilist = ilist->next;
+	}
+
+	if (ilist != NULL) {
+		mplup = (mpapi_lu_data_t *)(ilist->item->idata);
+		if (mplup == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
+			    "idata in ilist is NULL"));
+			return (EINVAL);
+		} else if (mplup->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
+			    "OID NOT FOUND - LU GONE OFFLINE"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mplup_prop = (mp_logical_unit_prop_t *)(&mplup->prop);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_lu_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)mplup_prop, mpioc->mp_obuf,
+	    sizeof (mp_logical_unit_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_path_list_for_mp_lu(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *mplu_path_list = NULL;
+	mpapi_lu_data_t		*mplup;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mplup = (mpapi_lu_data_t *)(ilist->item->idata);
+		if (mplup->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
+			    "mp_lu: MP_DRVR_PATH_STATE_LU_ERR - LU OFFLINE"));
+			mpioc->mp_errno = MP_DRVR_PATH_STATE_LU_ERR;
+			return (EINVAL);
+		}
+		mplu_path_list = mplup->path_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
+		    "Unknown Error"));
+	}
+
+	while (mplu_path_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)mplu_path_list->
+			    item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		mplu_path_list = mplu_path_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_mp_lu: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_path_list_for_init_port(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *mpinit_path_list = NULL;
+	mpapi_initiator_data_t	*mpinitp;
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	/*
+	 * While walking the mpapi database for initiator ports invalidate all
+	 * initiator ports. The succeeding call to walk the phci list through
+	 * MDI walker will validate the currently existing pHCIS.
+	 */
+	while (ilist != NULL) {
+		mpinitp = ilist->item->idata;
+		mpinitp->valid = 0;
+		ilist = ilist->next;
+	}
+
+	mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
+	    vhci);
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
+		    "port: OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mpinitp = (mpapi_initiator_data_t *)(ilist->item->idata);
+		if (mpinitp->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
+			    "init_port: OID NOT FOUND - INIT PORT INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mpinit_path_list = mpinitp->path_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
+		    "port: Unknown Error"));
+	}
+
+	while (mpinit_path_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)mpinit_path_list->
+			    item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		mpinit_path_list = mpinit_path_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_init_"
+		    "port: ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_path_list_for_target_port(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *mptp_path_list = NULL;
+	mpapi_tport_data_t	*mptpp;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
+		    "port: OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mptpp = (mpapi_tport_data_t *)(ilist->item->idata);
+		if (mptpp->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_"
+			    "target_port: OID NOT FOUND - TGT PORT INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mptp_path_list = mptpp->path_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
+		    "port: Unknown Error"));
+	}
+
+	while (mptp_path_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] =
+			    (uint64_t)mptp_path_list->item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		mptp_path_list = mptp_path_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_list_for_target_"
+		    "port: ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_path_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		oid;
+	mp_path_prop_t		*mpp_prop = (mp_path_prop_t *)output_data;
+	mpapi_item_list_t	*ilist;
+	mpapi_path_data_t	*mpp;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_PATH_LU]->head;
+
+	rval = ddi_copyin(mpioc->mp_ibuf, &oid, mpioc->mp_ilen, mode);
+
+	while ((ilist != NULL) && (oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist != NULL) {
+		mpp = (mpapi_path_data_t *)(ilist->item->idata);
+		if (mpp == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_prop: "
+			    "idata in ilist is NULL"));
+			return (EINVAL);
+		}
+		mpp_prop = (mp_path_prop_t *)(&mpp->prop);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_path_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)mpp_prop, mpioc->mp_obuf,
+	    sizeof (mp_path_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_init_port_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	mpapi_item_list_t	*ilist;
+	mpapi_initiator_data_t	*initd;
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	/*
+	 * While walking the mpapi database for initiator ports invalidate all
+	 * initiator ports. The succeeding call to walk the phci list through
+	 * MDI walker will validate the currently existing pHCIS.
+	 */
+	while (ilist != NULL) {
+		initd = ilist->item->idata;
+		initd->valid = 0;
+		ilist = ilist->next;
+	}
+
+	mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
+	    vhci);
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	while (ilist != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)ilist->item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		/*
+		 * Get rid of the latest entry if item is invalid
+		 */
+		initd = ilist->item->idata;
+		if (initd->valid == 0) {
+			count--;
+		}
+		ilist = ilist->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_list: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	} else {
+		mpioc->mp_errno = 0;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_init_port_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_init_port_prop_t	*mpip_prop = (mp_init_port_prop_t *)output_data;
+	mpapi_item_list_t	*ilist;
+	mpapi_initiator_data_t	*mpip;
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	/*
+	 * While walking the mpapi database for initiator ports invalidate all
+	 * initiator ports. The succeeding call to walk the phci list through
+	 * MDI walker will validate the currently existing pHCIS.
+	 */
+	while (ilist != NULL) {
+		mpip = ilist->item->idata;
+		mpip->valid = 0;
+		ilist = ilist->next;
+	}
+
+	mdi_vhci_walk_phcis(vhci->vhci_dip, vhci_mpapi_sync_init_port_list,
+	    vhci);
+
+	ilist = vhci->mp_priv->
+	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
+		ilist = ilist->next;
+	}
+
+	if (ilist != NULL) {
+		mpip = (mpapi_initiator_data_t *)(ilist->item->idata);
+		if (mpip == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop:"
+			    " idata in ilist is NULL"));
+			return (EINVAL);
+		} else if (mpip->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop"
+			    ": OID NOT FOUND - INIT PORT IS INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mpip_prop = (mp_init_port_prop_t *)(&mpip->prop);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_init_port_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)mpip_prop, mpioc->mp_obuf,
+	    sizeof (mp_init_port_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_target_port_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_target_port_prop_t	*mptp_prop;
+	mpapi_item_list_t	*ilist;
+	mpapi_tport_data_t	*mptp;
+
+	mptp_prop = (mp_target_port_prop_t *)output_data;
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
+		ilist = ilist->next;
+	}
+
+	if (ilist != NULL) {
+		mptp = (mpapi_tport_data_t *)(ilist->item->idata);
+		if (mptp == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
+			    "prop: idata in ilist is NULL"));
+			return (EINVAL);
+		} else if (mptp->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
+			    "prop: OID NOT FOUND - TARGET PORT INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mptp_prop = (mp_target_port_prop_t *)(&mptp->prop);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)mptp_prop, mpioc->mp_obuf,
+	    sizeof (mp_target_port_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_tpg_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_tpg_prop_t		*mptpg_prop;
+	mpapi_item_list_t	*ilist;
+	mpapi_tpg_data_t	*mptpg;
+
+	mptpg_prop = (mp_tpg_prop_t *)output_data;
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]->
+	    head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid)) {
+		ilist = ilist->next;
+	}
+
+	if (ilist != NULL) {
+		mptpg = (mpapi_tpg_data_t *)(ilist->item->idata);
+		if (mptpg == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
+			    "idata in ilist is NULL"));
+			return (EINVAL);
+		} else if (mptpg->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
+			    "OID NOT FOUND - TPG INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		mptpg_prop = (mp_tpg_prop_t *)(&mptpg->prop);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_tpg_prop: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+	/*
+	 * Here were are not using the 'output_data' that is
+	 * passed as the required information is already
+	 * in the required format!
+	 */
+	if (ddi_copyout((void *)mptpg_prop, mpioc->mp_obuf,
+	    sizeof (mp_tpg_prop_t), mode) != 0) {
+		return (EFAULT);
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_target_port_list_for_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			count = 0, rval = 0;
+	int			list_len = mpioc->mp_olen/sizeof (uint64_t);
+	uint64_t		*oid_list = (uint64_t *)(output_data);
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mpapi_item_list_t	*ilist, *tpg_tp_list = NULL;
+	mpapi_tpg_data_t	*mptpgtp;
+	mpapi_tport_data_t	*mptpp;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
+	    ->head;
+
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
+		    "tpg: OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		rval = EINVAL;
+	} else if (*oid == ilist->item->oid.raw_oid) {
+		mptpgtp = (mpapi_tpg_data_t *)(ilist->item->idata);
+		if (mptpgtp->valid == 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_"
+			    "list_for_tpg: OID NOT FOUND - TPG INVALID"));
+			mpioc->mp_errno = MP_DRVR_INVALID_ID;
+			return (EINVAL);
+		}
+		tpg_tp_list = mptpgtp->tport_list->head;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
+		    "tpg: Unknown Error"));
+	}
+
+	while (tpg_tp_list != NULL) {
+		if (count < list_len) {
+			oid_list[count] = (uint64_t)tpg_tp_list->
+			    item->oid.raw_oid;
+		} else {
+			rval = MP_MORE_DATA;
+		}
+		mptpp = tpg_tp_list->item->idata;
+		if (mptpp->valid == 0) {
+			count--;
+		}
+		tpg_tp_list = tpg_tp_list->next;
+		count++;
+	}
+
+	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
+	if ((rval == MP_MORE_DATA) || (mpioc->mp_alen > mpioc->mp_olen)) {
+		mpioc->mp_errno = MP_MORE_DATA;
+		return (EINVAL);
+	}
+
+	if ((count > 0) && (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (count * sizeof (uint64_t)), mode) != 0)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_target_port_list_for_"
+		    "tpg: ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_set_tpg_access_state(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0, retval = 0, held = 0;
+	uint32_t		desired_state, t10_tpgid;
+	uint64_t		lu_oid, tpg_oid;
+	mp_set_tpg_state_req_t	mp_set_tpg;
+	mpapi_item_list_t	*lu_list, *tpg_list;
+	mpapi_tpg_data_t	*mptpgd;
+	scsi_vhci_lun_t		*svl;
+	scsi_vhci_priv_t	*svp;
+	mdi_pathinfo_t		*pip;
+	struct scsi_address	*ap = NULL;
+
+	lu_list = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]
+	    ->head;
+	tpg_list = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT_GROUP]
+	    ->head;
+
+	rval = ddi_copyin(mpioc->mp_ibuf, &mp_set_tpg, mpioc->mp_ilen, mode);
+	lu_oid = mp_set_tpg.luTpgPair.luId;
+	tpg_oid = mp_set_tpg.luTpgPair.tpgId;
+	desired_state = mp_set_tpg.desiredState;
+
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_set_tpg_access_state: lu_oid: %lx,"
+	    "tpg_oid: %lx, des_as: %x\n", (long)lu_oid, (long)tpg_oid,
+	    desired_state));
+
+	while ((lu_list != NULL) && (lu_oid != lu_list->item->oid.raw_oid))
+		lu_list = lu_list->next;
+	while ((tpg_list != NULL) && (tpg_oid != tpg_list->item->oid.raw_oid))
+		tpg_list = tpg_list->next;
+
+	if ((lu_list == NULL) || (tpg_list == NULL)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_state: "
+		    "OID NOT FOUND"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+	if ((desired_state != MP_DRVR_ACCESS_STATE_ACTIVE) &&
+	    (desired_state != MP_DRVR_ACCESS_STATE_ACTIVE_OPTIMIZED) &&
+	    (desired_state != MP_DRVR_ACCESS_STATE_ACTIVE_NONOPTIMIZED) &&
+	    (desired_state != MP_DRVR_ACCESS_STATE_STANDBY)) {
+		mpioc->mp_errno = MP_DRVR_ILLEGAL_ACCESS_STATE_REQUEST;
+		return (EINVAL);
+	}
+	mptpgd = (mpapi_tpg_data_t *)(tpg_list->item->idata);
+	if (desired_state == mptpgd->prop.accessState) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+		    "state: TPG already in desired State"));
+		return (EINVAL);
+	}
+	t10_tpgid = mptpgd->prop.tpgId;
+
+	/*
+	 * All input seems to be ok, Go ahead & change state.
+	 */
+	svl = ((mpapi_lu_data_t *)(lu_list->item->idata))->resp;
+	if (!SCSI_FAILOVER_IS_TPGS(svl->svl_fops)) {
+
+		VHCI_HOLD_LUN(svl, VH_SLEEP, held);
+		/*
+		 * retval specifically cares about failover
+		 * status and not about this routine's success.
+		 */
+		retval = mdi_failover(vhci->vhci_dip, svl->svl_dip,
+		    MDI_FAILOVER_SYNC);
+		if (retval != 0) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state: FAILOVER FAILED: %x", retval));
+			VHCI_RELEASE_LUN(svl);
+			return (EIO);
+		} else {
+			/*
+			 * Don't set TPG's accessState here. Let mdi_failover's
+			 * call-back routine "vhci_failover()" call
+			 * vhci_mpapi_update_tpg_acc_state_for_lu().
+			 */
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state: FAILOVER SUCCESS: %x", retval));
+		}
+		VHCI_RELEASE_LUN(svl);
+	} else {
+		/*
+		 * Send SET_TARGET_PORT_GROUP SCSI Command. This is supported
+		 * ONLY by devices which have TPGS EXPLICIT Failover support.
+		 */
+		retval = mdi_select_path(svl->svl_dip, NULL,
+		    MDI_SELECT_ONLINE_PATH, NULL, &pip);
+		if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state: Unable to find path: %x", retval));
+			return (EINVAL);
+		}
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		if (svp == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state: Unable to find vhci private data"));
+			mdi_rele_path(pip);
+			return (EINVAL);
+		}
+		if (svp->svp_psd == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state: Unable to find scsi device"));
+			mdi_rele_path(pip);
+			return (EINVAL);
+		}
+		mdi_rele_path(pip);
+		ap = &svp->svp_psd->sd_address;
+		ASSERT(ap != NULL);
+
+		retval = (*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));
+			return (EIO);
+		} else {
+			/*
+			 * Don't set accessState here.
+			 * std_report_target_groups() call needs to sync up
+			 * properly.
+			 */
+			VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_set_tpg_access_"
+			    "state:(ALUA) FAILOVER SUCCESS: %x", retval));
+
+			VHCI_HOLD_LUN(svl, VH_NOSLEEP, held);
+			if (!held) {
+				return (TRAN_BUSY);
+			} else {
+				vhci_update_pathstates((void *)svl);
+			}
+			if (desired_state != mptpgd->prop.accessState) {
+				VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_set_tpg_"
+				    "access_state: TPGAccessState NOT Set: "
+				    "des_state=%x, cur_state=%x", desired_state,
+				    mptpgd->prop.accessState));
+				return (EIO);
+			}
+
+		}
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_prop_lb_list(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int		rval = 0;
+	uint64_t	*oid_list = (uint64_t *)(output_data);
+
+	oid_list[0] = NULL;
+
+	if (ddi_copyout(output_data, (void *)mpioc->mp_obuf,
+	    (sizeof (uint64_t)), mode) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_prop_lb_list: "
+		    "ddi_copyout() FAILED"));
+		mpioc->mp_errno = EFAULT;
+		rval = EINVAL;
+	} else {
+		mpioc->mp_errno = 0;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_get_prop_lb_prop(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int rval = EINVAL;
+
+	return (rval);
+}
+
+/*
+ * Operation not supported currently as we do not know
+ * support any devices that allow this in the first place.
+ */
+/* ARGSUSED */
+static int
+vhci_assign_lu_to_tpg(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int rval = ENOTSUP;
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_enable_auto_failback(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*lud;
+
+	mutex_enter(&vhci->vhci_mutex);
+	vhci->vhci_conf_flags |= VHCI_CONF_FLAGS_AUTO_FAILBACK;
+	mutex_exit(&vhci->vhci_mutex);
+
+	/* Enable auto-failback for each lun in MPAPI database */
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+	while (ilist != NULL) {
+		lud = ilist->item->idata;
+		lud->prop.autoFailbackEnabled = 1;
+		ilist = ilist->next;
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_disable_auto_failback(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*lud;
+
+	mutex_enter(&vhci->vhci_mutex);
+	vhci->vhci_conf_flags &= ~VHCI_CONF_FLAGS_AUTO_FAILBACK;
+	mutex_exit(&vhci->vhci_mutex);
+
+	/* Disable auto-failback for each lun in MPAPI database */
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+	while (ilist != NULL) {
+		lud = ilist->item->idata;
+		lud->prop.autoFailbackEnabled = 0;
+		ilist = ilist->next;
+	}
+
+	return (rval);
+}
+
+/*
+ * Find the oid in the object type list. If found lock and return
+ * the item. If not found return NULL. The caller must unlock the item.
+ */
+void *
+vhci_mpapi_hold_item(struct scsi_vhci *vhci, uint64_t *oid, uint8_t obj_type)
+{
+	mpapi_item_list_t	*ilist;
+
+	ilist = vhci->mp_priv->obj_hdr_list[obj_type]->head;
+	while ((ilist != NULL) && (*oid != ilist->item->oid.raw_oid))
+		ilist = ilist->next;
+
+	if (ilist == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_item: "
+		    "OID NOT FOUND. oid: %p", (void *)oid));
+		return (NULL);
+	}
+	if (*oid == ilist->item->oid.raw_oid) {
+		mutex_enter(&ilist->item->item_mutex);
+		return (ilist);
+	}
+	VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_hold_item: "
+	    "Unknown Error. oid: %p", (void *)oid));
+	return (NULL);
+}
+
+/*
+ * Check that the pip sent in by the user is still associated with
+ * the same oid. This is done through checking the path name.
+ */
+mdi_pathinfo_t *
+vhci_mpapi_chk_path(struct scsi_vhci *vhci, mpapi_item_list_t *ilist)
+{
+	mdi_pathinfo_t		*pip;
+	mpapi_path_data_t	*mpp;
+
+	mpp = (mpapi_path_data_t *)(ilist->item->idata);
+	if (mpp == NULL || mpp->valid == 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_chk_path: "
+		    "pathinfo is not valid: %p", (void *)mpp));
+		return (NULL);
+	}
+	pip = mpp->resp;
+	/* make sure it is the same pip by checking path */
+	if (vhci_mpapi_match_pip(vhci, ilist, pip) == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_chk_path: "
+		    "Can not match pip: %p", (void *)pip));
+		return (NULL);
+	}
+	return (pip);
+}
+
+/*
+ * Get the pip from the oid passed in. the vhci_mpapi_chk_path
+ * will check the name with the passed in pip name.  the mdi_select_path()
+ * path will lock the pip and this should get released by the caller
+ */
+mdi_pathinfo_t *
+vhci_mpapi_hold_pip(struct scsi_vhci *vhci, mpapi_item_list_t *ilist, int flags)
+{
+	mdi_pathinfo_t		*pip, *opip, *npip;
+	scsi_vhci_lun_t		*svl;
+	int			rval;
+	mpapi_path_data_t	*mpp;
+
+	mpp = (mpapi_path_data_t *)(ilist->item->idata);
+	pip = mpp->resp;
+	/* make sure it is the same pip by checking path */
+	if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_pip: "
+		    "Can not match pip: %p", (void *)pip));
+		return (NULL);
+	}
+
+	svl = mdi_client_get_vhci_private(mdi_pi_get_client(pip));
+	opip = npip = NULL;
+
+	/*
+	 * use the select path to find the right pip since
+	 * it does all the state checking and locks the pip
+	 */
+	rval = mdi_select_path(svl->svl_dip, NULL,
+	    flags, NULL, &npip);
+	do {
+		if ((rval != MDI_SUCCESS) || (npip == NULL)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_hold_pip:"
+			    " Unable to find path: %x.", rval));
+			return (NULL);
+		}
+		if (npip == pip) {
+			break;
+		}
+		opip = npip;
+		rval = mdi_select_path(svl->svl_dip, NULL,
+		    flags, opip, &npip);
+		mdi_rele_path(opip);
+	} while ((npip != NULL) && (rval == MDI_SUCCESS));
+	return (npip);
+}
+
+/*
+ * Initialize the uscsi command. Lock the pip and the item in
+ * the item list.
+ */
+static mp_uscsi_cmd_t *
+vhci_init_uscsi_cmd(struct scsi_vhci *vhci,
+	mp_iocdata_t *mpioc, uint64_t *oid, mpapi_item_list_t **list)
+{
+	int			arq_enabled;
+	mp_uscsi_cmd_t		*mp_uscmdp;
+	scsi_vhci_priv_t	*svp;
+	struct scsi_address	*ap;
+	mdi_pathinfo_t		*pip;
+	mpapi_item_list_t	*ilist;
+	struct buf		*bp;
+
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_init_uscsi_cmd: enter"));
+
+	*list = NULL;
+	/* lock the item */
+	if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(
+	    vhci, oid, MP_OBJECT_TYPE_PATH_LU)) == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "vhci_init_uscsi_cmd: exit EINVAL"));
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (NULL);
+	}
+
+	/* lock the pip */
+	if ((pip = vhci_mpapi_hold_pip(vhci, ilist,
+	    (MDI_SELECT_STANDBY_PATH|MDI_SELECT_ONLINE_PATH))) == 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "vhci_init_uscsi_cmd: exit PATH_UNAVAIL"));
+		mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
+		mutex_exit(&ilist->item->item_mutex);
+		return (NULL);
+	};
+
+	/* get the address of the pip */
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_init_uscsi_cmd:"
+		    " Unable to find vhci private data"));
+		mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
+		mdi_rele_path(pip);
+		mutex_exit(&ilist->item->item_mutex);
+		return (NULL);
+	}
+	if (svp->svp_psd == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_init_uscsi_cmd:"
+		    " Unable to find scsi device"));
+		mpioc->mp_errno = MP_DRVR_PATH_UNAVAILABLE;
+		mdi_rele_path(pip);
+		mutex_exit(&ilist->item->item_mutex);
+		return (NULL);
+	}
+	ap = &svp->svp_psd->sd_address;
+	ASSERT(ap != NULL);
+
+	/* initialize the buffer */
+	bp = getrbuf(KM_SLEEP);
+	ASSERT(bp != NULL);
+
+	/* initialize the mp_uscsi_cmd */
+	mp_uscmdp = kmem_zalloc((size_t)sizeof (mp_uscsi_cmd_t), KM_SLEEP);
+	ASSERT(mp_uscmdp != NULL);
+	mp_uscmdp->ap = ap;
+	mp_uscmdp->pip = pip;
+	mp_uscmdp->cmdbp = bp;
+	mp_uscmdp->rqbp = NULL;
+
+	bp->b_private = mp_uscmdp;
+
+	/* used to debug a manual sense */
+	if (vhci_force_manual_sense) {
+		(void) scsi_ifsetcap(ap, "auto-rqsense", 0, 0);
+	} else {
+		if (scsi_ifgetcap(ap, "auto-rqsense", 1) != 1) {
+			(void) scsi_ifsetcap(ap, "auto-rqsense", 1, 1);
+		}
+	}
+	arq_enabled = scsi_ifgetcap(ap, "auto-rqsense", 1);
+	if (arq_enabled == 1) {
+		mp_uscmdp->arq_enabled = 1;
+	} else {
+		mp_uscmdp->arq_enabled = 0;
+	}
+	/* set the list pointer for the caller */
+	*list = ilist;
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_init_uscsi_cmd: mp_uscmdp: %p ilist: %p mp_errno: %d "
+	    "bp: %p arq: %d",
+	    (void *)mp_uscmdp, (void *)*list, mpioc->mp_errno,
+	    (void *)bp, arq_enabled));
+
+	return (mp_uscmdp);
+}
+
+
+/*
+ * Initialize the uscsi information and then issue the command.
+ */
+/* ARGSUSED */
+static int
+vhci_send_uscsi_cmd(dev_t dev, struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0, uioseg = 0;
+	struct uscsi_cmd	*uscmdp;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mp_uscsi_cmd_t		*mp_uscmdp;
+	mpapi_item_list_t	*ilist;
+
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_send_uscsi_cmd: enter: mode: %x", mode));
+	mpioc->mp_errno = 0;
+	mp_uscmdp = vhci_init_uscsi_cmd(vhci, mpioc, oid, &ilist);
+	if (mp_uscmdp == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "vhci_send_uscsi_cmd: exit INVALID_ID. rval: %d", rval));
+		return (EINVAL);
+	}
+	rval = scsi_uscsi_alloc_and_copyin((intptr_t)mpioc->mp_obuf,
+	    mode, mp_uscmdp->ap, &uscmdp);
+	if (rval != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
+		    "scsi_uscsi_alloc_and_copyin failed. rval: %d", rval));
+		mpioc->mp_errno = EINVAL;
+		mdi_rele_path(mp_uscmdp->pip);
+		mutex_exit(&ilist->item->item_mutex);
+		if (mp_uscmdp->cmdbp)
+			freerbuf(mp_uscmdp->cmdbp);
+		kmem_free(mp_uscmdp, sizeof (mp_uscsi_cmd_t));
+		return (EINVAL);
+	}
+	/* initialize the mp_uscsi_cmd with the uscsi_cmd from uscsi_alloc */
+	mp_uscmdp->uscmdp = uscmdp;
+
+	uioseg = (mode & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
+
+	/* start the command sending the buffer as an argument */
+	rval = scsi_uscsi_handle_cmd(dev, uioseg,
+	    uscmdp, vhci_uscsi_iostart, mp_uscmdp->cmdbp, mp_uscmdp);
+	if (rval != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
+		    "scsi_uscsi_handle_cmd failed. rval: %d", rval));
+		mpioc->mp_errno = EIO;
+	}
+
+	if (scsi_uscsi_copyout_and_free((intptr_t)mpioc->mp_obuf,
+	    uscmdp) != 0 && rval == 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_send_uscsi_cmd: "
+		    "scsi_uscsi_copyout_and_free failed. rval: %d", rval));
+		mpioc->mp_errno = EFAULT;
+		rval = EFAULT;
+	}
+	/* cleanup */
+	mdi_rele_path(mp_uscmdp->pip);
+	mutex_exit(&ilist->item->item_mutex);
+	if (mp_uscmdp->cmdbp)
+		freerbuf(mp_uscmdp->cmdbp);
+	kmem_free(mp_uscmdp, sizeof (mp_uscsi_cmd_t));
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_send_uscsi_cmd: rval: %d mp_errno: %d",
+	    rval, mpioc->mp_errno));
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_enable_path(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mdi_pathinfo_t		*pip;
+	mpapi_item_list_t	*ilist;
+	mpapi_path_data_t	*mpp;
+
+	if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(vhci, oid,
+	    MP_OBJECT_TYPE_PATH_LU)) == NULL) {
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	mpp = (mpapi_path_data_t *)(ilist->item->idata);
+	pip = (mdi_pathinfo_t *)mpp->resp;
+
+	if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
+		mutex_exit(&ilist->item->item_mutex);
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	if (mdi_pi_enable_path(pip, USER_DISABLE) != 0) {
+		rval = EFAULT;
+	} else {
+		mpp->prop.disabled = 0;
+		vhci_mpapi_log_sysevent(vhci->vhci_dip,
+		    &(((mpoid_t *)oid)->raw_oid), ESC_SUN_MP_PATH_CHANGE);
+	}
+	mutex_exit(&ilist->item->item_mutex);
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_disable_path(struct scsi_vhci *vhci, mp_iocdata_t *mpioc,
+    void *input_data, void *output_data, int mode)
+{
+	int			rval = 0;
+	uint64_t		*oid = (uint64_t *)(input_data);
+	mdi_pathinfo_t		*pip = NULL;
+	mpapi_item_list_t	*ilist;
+	mpapi_path_data_t	*mpp;
+
+	if ((ilist = (mpapi_item_list_t *)vhci_mpapi_hold_item(vhci, oid,
+	    MP_OBJECT_TYPE_PATH_LU)) == NULL) {
+		mpioc->mp_errno = MP_DRVR_INVALID_ID;
+		return (EINVAL);
+	}
+
+	mpp = (mpapi_path_data_t *)(ilist->item->idata);
+	pip = (mdi_pathinfo_t *)mpp->resp;
+
+	if (vhci_mpapi_chk_path(vhci, ilist) == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path: Request "
+		    "received to disable last path. Cant disable, Sorry!"));
+		mutex_exit(&ilist->item->item_mutex);
+		return (EINVAL);
+	}
+	if (vhci_mpapi_chk_last_path(pip) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path(1): Request "
+		    "received to disable last path. Cant disable, Sorry!"));
+		mutex_exit(&ilist->item->item_mutex);
+		return (EINVAL);
+	}
+
+	if (mdi_pi_disable_path(pip, USER_DISABLE) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_disable_path(2): Request "
+		    "received to disable last path. Cant disable, Sorry!"));
+		rval = EFAULT;
+	} else {
+		mpp->prop.disabled = 1;
+		vhci_mpapi_log_sysevent(vhci->vhci_dip,
+		    &(((mpoid_t *)oid)->raw_oid), ESC_SUN_MP_PATH_CHANGE);
+	}
+	mutex_exit(&ilist->item->item_mutex);
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_mpapi_ioctl(dev_t dev, struct scsi_vhci *vhci, void *udata,
+	mp_iocdata_t *mpioc, int mode, cred_t *credp)
+{
+	int		rval = 0;
+	uint64_t	oid;
+	void		*input_data = NULL, *output_data = NULL;
+
+	/* validate mpioc */
+	rval = vhci_mpapi_validate(udata, mpioc, mode, credp);
+
+	if (rval == EINVAL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
+		    " vhci_mpapi_validate() Returned %x: INVALID DATA", rval));
+		if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
+			    "vhci_mpapi_copyout_iocdata FAILED in EINVAL"));
+		}
+		return (rval);
+	} else if (rval == EPERM) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
+		    " vhci_mpapi_validate() Returned %x: NO CREDS", rval));
+		if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
+			    "vhci_mpapi_copyout_iocdata FAILED in EPERM"));
+		}
+		return (rval);
+	/* Process good cases & also cases where we need to get correct alen */
+	} else if ((rval == 0) || (rval == MP_MORE_DATA)) {
+		/* allocate an input buffer */
+		if ((mpioc->mp_ibuf) && (mpioc->mp_ilen != 0)) {
+			input_data = kmem_zalloc(mpioc->mp_ilen,
+			    KM_SLEEP);
+			ASSERT(input_data != NULL);
+			rval = ddi_copyin(mpioc->mp_ibuf,
+			    input_data, mpioc->mp_ilen, mode);
+			oid = (uint64_t)(*((uint64_t *)input_data));
+
+			VHCI_DEBUG(7, (CE_NOTE, NULL, "Requesting op for "
+			    "OID = %lx w/ mpioc = %p mp_cmd = %x\n",
+			    (long)oid, (void *)mpioc, mpioc->mp_cmd));
+
+		}
+		if ((mpioc->mp_xfer == MP_XFER_READ) && (mpioc->mp_olen != 0)) {
+			output_data = kmem_zalloc(mpioc->mp_olen, KM_SLEEP);
+			ASSERT(output_data != NULL);
+		}
+	}
+
+	if (vhci_mpapi_sync_lu_oid_list(vhci) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_mpapi_ioctl: "
+		    "vhci_mpapi_sync_lu_oid_list() failed"));
+	}
+	mdi_vhci_walk_phcis(vhci->vhci_dip,
+	    vhci_mpapi_sync_init_port_list, vhci);
+
+	/* process ioctls */
+	switch (mpioc->mp_cmd) {
+	case MP_GET_DRIVER_PROP:
+		rval = vhci_get_driver_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_DEV_PROD_LIST:
+		rval = vhci_get_dev_prod_list(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_DEV_PROD_PROP:
+		rval = vhci_get_dev_prod_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_LU_LIST:
+		rval = vhci_get_lu_list(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_LU_LIST_FROM_TPG:
+		rval = vhci_get_lu_list_from_tpg(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_TPG_LIST_FOR_LU:
+		rval = vhci_get_tpg_list_for_lu(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_LU_PROP:
+		rval = vhci_get_lu_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PATH_LIST_FOR_MP_LU:
+		rval = vhci_get_path_list_for_mp_lu(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PATH_LIST_FOR_INIT_PORT:
+		rval = vhci_get_path_list_for_init_port(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PATH_LIST_FOR_TARGET_PORT:
+		rval = vhci_get_path_list_for_target_port(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PATH_PROP:
+		rval = vhci_get_path_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_INIT_PORT_LIST: /* Not Required */
+		rval = vhci_get_init_port_list(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_INIT_PORT_PROP:
+		rval = vhci_get_init_port_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_TARGET_PORT_PROP:
+		rval = vhci_get_target_port_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_TPG_LIST: /* Not Required */
+		rval = vhci_get_tpg_list_for_lu(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_TPG_PROP:
+		rval = vhci_get_tpg_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_TARGET_PORT_LIST_FOR_TPG:
+		rval = vhci_get_target_port_list_for_tpg(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_SET_TPG_ACCESS_STATE:
+		rval = vhci_set_tpg_access_state(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_ASSIGN_LU_TO_TPG:
+		rval = vhci_assign_lu_to_tpg(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PROPRIETARY_LOADBALANCE_LIST:
+		rval = vhci_get_prop_lb_list(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_GET_PROPRIETARY_LOADBALANCE_PROP:
+		rval = vhci_get_prop_lb_prop(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_ENABLE_AUTO_FAILBACK:
+		rval = vhci_enable_auto_failback(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_DISABLE_AUTO_FAILBACK:
+		rval = vhci_disable_auto_failback(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_ENABLE_PATH:
+		rval = vhci_enable_path(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_DISABLE_PATH:
+		rval = vhci_disable_path(vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	case MP_SEND_SCSI_CMD:
+		rval = vhci_send_uscsi_cmd(dev, vhci, mpioc,
+		    input_data, output_data, mode);
+		break;
+	default:
+		rval = EINVAL;
+		break;
+	}
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_ioctl: output_data = %p, "
+	    "mp_obuf = %p, mp_olen = %lx, mp_alen = %lx, mp_errno = %x, "
+	    "mode = %x, rval=%x\n", (void *)output_data, (void *)mpioc->mp_obuf,
+	    mpioc->mp_olen, mpioc->mp_alen, mpioc->mp_errno, mode, rval));
+
+	if (vhci_mpapi_copyout_iocdata(mpioc, udata, mode)) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_ioctl: "
+		    "vhci_mpapi_copyout_iocdata FAILED"));
+		rval = EFAULT;
+	}
+
+	if (input_data) {
+		kmem_free(input_data, mpioc->mp_ilen);
+	}
+
+	if (output_data) {
+		kmem_free(output_data, mpioc->mp_olen);
+	}
+
+	return (rval);
+}
+
+/* ARGSUSED */
+int
+vhci_mpapi_init(struct scsi_vhci *vhci)
+{
+	mpapi_item_list_t	*ilist;
+	mpapi_item_t		*item;
+	mp_driver_prop_t	*drv;
+	uint8_t			i;
+
+	/*
+	 * This tstamp value is present in the upper 32-bits of all OIDs
+	 * that are issued in this boot session. Use it to identify
+	 * stale OIDs that an application/ioctl may pass to you and
+	 * reject it - Done in vhci_mpapi_validate() routine.
+	 */
+	mutex_enter(&tod_lock);
+	vhci->mp_priv->tstamp = (time32_t)(tod_get().tv_sec);
+	mutex_exit(&tod_lock);
+
+	for (i = 0; i < MP_MAX_OBJECT_TYPE; i++) {
+		vhci->mp_priv->obj_hdr_list[i] = vhci_mpapi_create_list_head();
+	}
+
+	/*
+	 * Let us now allocate and initialize the drv block.
+	 */
+	ilist = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
+	item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
+	ilist->item = item;
+	item->oid.raw_oid = vhci_mpapi_create_oid(vhci->mp_priv,
+	    MP_OBJECT_TYPE_PLUGIN);
+	drv = kmem_zalloc(sizeof (mp_driver_prop_t), KM_SLEEP);
+	drv->driverVersion[0] = '\0';
+	drv->supportedLoadBalanceTypes =
+	    (MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN |
+	    MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION);
+	drv->canSetTPGAccess = TRUE;
+	drv->canOverridePaths = FALSE;
+	drv->exposesPathDeviceFiles = FALSE;
+	drv->deviceFileNamespace[0] = '\0';
+	drv->onlySupportsSpecifiedProducts = 1;
+	drv->maximumWeight = 1;
+	drv->failbackPollingRateMax = 0;
+	drv->currentFailbackPollingRate = 0;
+	drv->autoFailbackSupport = 1;
+	drv->autoFailbackEnabled = 1;
+	drv->defaultLoadBalanceType = MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
+	drv->probingPollingRateMax = 0;
+	drv->currentProbingPollingRate = 0;
+	drv->autoProbingSupport = 0;
+	drv->autoProbingEnabled = 0;
+	item->idata = drv;
+	mutex_init(&item->item_mutex, NULL, MUTEX_DRIVER, NULL);
+	if (vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list
+	    [MP_OBJECT_TYPE_PLUGIN], ilist) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_init: "
+		    "vhci_mpapi_create_add_to_list() of PLUGIN failed"));
+		return (EFAULT);
+
+	}
+	return (0);
+}
+
+void
+vhci_mpapi_add_dev_prod(struct scsi_vhci *vhci, char *vidpid)
+{
+	mpapi_item_list_t	*dev_prod_list;
+	mpapi_item_t		*dev_prod_item;
+	mp_dev_prod_prop_t	*dev_prod;
+
+	/* add to list */
+	dev_prod_list = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
+	dev_prod_item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
+	dev_prod_list->item = dev_prod_item;
+	dev_prod_list->item->oid.raw_oid = vhci_mpapi_create_oid
+	    (vhci->mp_priv, MP_OBJECT_TYPE_DEVICE_PRODUCT);
+	dev_prod = kmem_zalloc(sizeof (mp_dev_prod_prop_t), KM_SLEEP);
+
+	(void) strncpy(dev_prod->prodInfo.vendor, vidpid, strlen(vidpid));
+	dev_prod->supportedLoadBalanceTypes =
+	    MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
+	dev_prod->id = dev_prod_list->item->oid.raw_oid;
+
+	dev_prod_list->item->idata = dev_prod;
+	(void) vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list
+	    [MP_OBJECT_TYPE_DEVICE_PRODUCT], (void *)dev_prod_list);
+	vhci_mpapi_log_sysevent(vhci->vhci_dip,
+	    &(dev_prod_list->item->oid.raw_oid),
+	    ESC_SUN_MP_DEV_PROD_ADD);
+}
+
+/* ARGSUSED */
+static uint64_t
+vhci_mpapi_create_oid(mpapi_priv_t *mp_priv, uint8_t obj_type)
+{
+	mpoid_t		oid;
+
+	oid.disc_oid.tstamp = mp_priv->tstamp;
+	oid.disc_oid.type = obj_type;
+	oid.disc_oid.seq_id = ++(mp_priv->oid_seq[obj_type]);
+	return (oid.raw_oid);
+}
+
+/* ARGSUSED */
+static int
+vhci_mpapi_add_to_list(mpapi_list_header_t *hdr, mpapi_item_list_t *item)
+{
+
+	mpapi_list_header_t	*tmp_hdr = hdr;
+	mpapi_item_list_t	*tmp_item = item;
+
+	if (item == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
+		    "NULL item passed"));
+		return (EFAULT);
+	}
+	if (hdr == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
+		    "NULL hdr passed"));
+		return (EFAULT);
+	}
+	/*
+	 * Check if the item is already there in the list.
+	 * Catches duplicates while assigning TPGs.
+	 */
+	tmp_item = tmp_hdr->head;
+	while (tmp_item != NULL) {
+		if (item == tmp_item) {
+			VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_add_to_list: "
+			    "Item already in list"));
+			return (1);
+		} else {
+			tmp_item = tmp_item->next;
+		}
+	}
+
+	item->next = NULL;
+	if (hdr->head == NULL) {
+		hdr->head = item;
+		hdr->tail = item;
+	} else {
+		hdr->tail->next = item;
+		hdr->tail = item;
+	}
+
+	return (0);
+}
+
+/*
+ * Local convenience routine to fetch reference to a mpapi item entry if it
+ * exits based on the pointer to the vhci resource that is passed.
+ * Returns NULL if no entry is found.
+ */
+/* ARGSUSED */
+void*
+vhci_get_mpapi_item(struct scsi_vhci *vhci,  mpapi_list_header_t *list,
+    uint8_t obj_type, void* res)
+{
+	mpapi_item_list_t	*ilist;
+
+	if (list == NULL) {
+		/*
+		 * Since the listhead is null, the search is being
+		 * performed in implicit mode - that is to use the
+		 * level one list.
+		 */
+		ilist = vhci->mp_priv->obj_hdr_list[obj_type]->head;
+	} else {
+		/*
+		 * The search is being performed on a sublist within
+		 * one of the toplevel list items. Use the listhead
+		 * that is passed in.
+		 */
+		ilist = list->head;
+	}
+
+	if (res == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item: "
+		    " Got Item w/ NULL resource ptr"));
+		return (NULL);
+	}
+
+	/*
+	 * Since the resource field within the item data is specific
+	 * to a particular object type, we need to use the object type
+	 * to enable us to perform the search and compare appropriately.
+	 */
+	switch (obj_type) {
+		case	MP_OBJECT_TYPE_INITIATOR_PORT:
+			while (ilist) {
+				void	*wwn = ((mpapi_initiator_data_t *)
+				    ilist->item->idata)->resp;
+				if (strncmp(wwn, res, strlen(res)) == 0) {
+					/* Found a match */
+					return ((void*)ilist);
+				}
+				ilist = ilist->next;
+			}
+		break;
+
+		case	MP_OBJECT_TYPE_TARGET_PORT:
+			while (ilist) {
+				void	*wwn = ((mpapi_tport_data_t *)ilist->
+				    item->idata)->resp;
+				if (strncmp(wwn, res, strlen(res)) == 0) {
+					/* Found a match */
+					return ((void*)ilist);
+				}
+				ilist = ilist->next;
+			}
+		break;
+
+		case	MP_OBJECT_TYPE_TARGET_PORT_GROUP:
+			/*
+			 * For TPG Synthesis, Use TPG specific routines
+			 * Use this case only for ALUA devices which give TPG ID
+			 */
+			while (ilist) {
+				void	*tpg_id = ((mpapi_tpg_data_t *)ilist->
+				    item->idata)->resp;
+				if (strncmp(tpg_id, res, strlen(res)) == 0) {
+					/* Found a match */
+					return ((void*)ilist);
+				}
+				ilist = ilist->next;
+			}
+		break;
+
+		case	MP_OBJECT_TYPE_MULTIPATH_LU:
+			return ((void *)(vhci_mpapi_match_lu
+			    (vhci, ilist, res)));
+
+		case	MP_OBJECT_TYPE_PATH_LU:
+			return ((void *)(vhci_mpapi_match_pip
+			    (vhci, ilist, res)));
+
+		default:
+			/*
+			 * This should not happen
+			 */
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item:"
+			    "Got Unsupported OBJECT TYPE"));
+			return (NULL);
+	}
+	return (NULL);
+}
+
+/*
+ * Local convenience routine to create and initialize mpapi item
+ * based on the object type passed.
+ */
+/* ARGSUSED */
+static mpapi_item_list_t *
+vhci_mpapi_create_item(struct scsi_vhci *vhci, uint8_t obj_type, void* res)
+{
+	int			major;
+	int			instance;
+	mpapi_item_list_t	*ilist;
+	mpapi_item_t		*item;
+	char			*pname = NULL;
+
+	ilist = kmem_zalloc(sizeof (mpapi_item_list_t), KM_SLEEP);
+	item = kmem_zalloc(sizeof (mpapi_item_t), KM_SLEEP);
+	mutex_init(&item->item_mutex, NULL, MUTEX_DRIVER, NULL);
+	ilist->item = item;
+	item->oid.raw_oid = 0;
+
+	switch (obj_type) {
+		case	MP_OBJECT_TYPE_INITIATOR_PORT:
+		{
+			mpapi_initiator_data_t	*init;
+			dev_info_t		*pdip = res;
+			char			*init_port_res;
+			char			*interconnect;
+			int			mp_interconnect_type, len;
+			int			prop_not_ddi_alloced = 0;
+
+			pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+			major = (int)ddi_driver_major(pdip);
+			instance = ddi_get_instance(pdip);
+			(void) ddi_pathname(pdip, pname);
+			item->oid.raw_oid =
+			    MP_STORE_INST_TO_ID(instance, item->oid.raw_oid);
+			item->oid.raw_oid =
+			    MP_STORE_MAJOR_TO_ID(major, item->oid.raw_oid);
+			/*
+			 * Just make a call to keep correct Sequence count.
+			 * Don't use the OID returned though.
+			 */
+			(void) vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
+			init_port_res = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+			(void) strlcpy(init_port_res, pname, MAXPATHLEN);
+
+			if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, 0,
+			    "initiator-interconnect-type",
+			    &interconnect) != DDI_PROP_SUCCESS)) {
+				/* XXX: initiator-interconnect-type not set */
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "vhci_mpapi_create_item: initiator-"
+				    "-interconnect-type prop not found"));
+				len = strlen("UNKNOWN")+1;
+				interconnect = kmem_zalloc(len, KM_SLEEP);
+				(void) strlcpy(interconnect, "UNKNOWN", len);
+				prop_not_ddi_alloced = 1;
+			}
+			/*
+			 * Map the initiator-interconnect-type values between
+			 * SCSA(as defined in services.h) and MPAPI
+			 * (as defined in mpapi_impl.h)
+			 */
+			if (strncmp(interconnect,
+			    INTERCONNECT_FABRIC_STR,
+			    strlen(interconnect)) == 0) {
+				mp_interconnect_type = 2;
+			} else if (strncmp(interconnect,
+			    INTERCONNECT_PARALLEL_STR,
+			    strlen(interconnect)) == 0) {
+				mp_interconnect_type = 3;
+			} else if (strncmp(interconnect,
+			    INTERCONNECT_ISCSI_STR,
+			    strlen(interconnect)) == 0) {
+				mp_interconnect_type = 4;
+			} else if (strncmp(interconnect,
+			    INTERCONNECT_IBSRP_STR,
+			    strlen(interconnect)) == 0) {
+				mp_interconnect_type = 5;
+			} else {
+				mp_interconnect_type = 0;
+			}
+
+			init = kmem_zalloc(
+			    sizeof (mpapi_initiator_data_t), KM_SLEEP);
+			init->resp = init_port_res;
+			init->valid = 1;
+			init->prop.id = item->oid.raw_oid;
+			init->prop.portType = mp_interconnect_type;
+			(void) strlcpy(init->prop.portID, pname,
+			    sizeof (init->prop.portID));
+			(void) strlcpy(init->prop.osDeviceFile, "/devices",
+			    sizeof (init->prop.osDeviceFile));
+			(void) strlcat(init->prop.osDeviceFile, pname,
+			    sizeof (init->prop.osDeviceFile));
+			init->path_list = vhci_mpapi_create_list_head();
+			item->idata = (void *)init;
+			vhci_mpapi_log_sysevent(vhci->vhci_dip,
+			    &(item->oid.raw_oid), ESC_SUN_MP_INIT_PORT_CHANGE);
+
+			if (prop_not_ddi_alloced != 1) {
+				ddi_prop_free(interconnect);
+			} else {
+				kmem_free(interconnect, len);
+			}
+			if (pname) {
+				kmem_free(pname, MAXPATHLEN);
+			}
+		}
+		break;
+
+		case	MP_OBJECT_TYPE_TARGET_PORT:
+		{
+			mpapi_tport_data_t	*tport;
+			char			*tgt_port_res;
+
+			item->oid.raw_oid =
+			    vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
+			tport = kmem_zalloc(sizeof (mpapi_tport_data_t),
+			    KM_SLEEP);
+			tgt_port_res = kmem_zalloc(strlen(res)+1, KM_SLEEP);
+			(void) strlcpy(tgt_port_res, res, strlen(res)+1);
+			tport->resp = tgt_port_res;
+			tport->valid = 1;
+			tport->prop.id = item->oid.raw_oid;
+			tport->prop.relativePortID = 0;
+			(void) strlcpy(tport->prop.portName, res,
+			    sizeof (tport->prop.portName));
+			tport->path_list = vhci_mpapi_create_list_head();
+			item->idata = (void *)tport;
+			vhci_mpapi_log_sysevent(vhci->vhci_dip,
+			    &(item->oid.raw_oid), ESC_SUN_MP_TARGET_PORT_ADD);
+		}
+		break;
+
+		case	MP_OBJECT_TYPE_TARGET_PORT_GROUP:
+		{
+			mpapi_tpg_data_t	*tpg;
+			char			*tpg_res;
+
+			item->oid.raw_oid =
+			    vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
+			tpg = kmem_zalloc(
+			    sizeof (mpapi_tpg_data_t), KM_SLEEP);
+			tpg_res = kmem_zalloc(strlen(res)+1, KM_SLEEP);
+			(void) strlcpy(tpg_res, res, strlen(res)+1);
+			tpg->resp = tpg_res;
+			tpg->valid = 1;
+			tpg->prop.id = item->oid.raw_oid;
+			/*
+			 * T10 TPG ID is a 2 byte value. Keep up with it.
+			 */
+			tpg->prop.tpgId =
+			    ((item->oid.raw_oid) & 0x000000000000ffff);
+			tpg->tport_list = vhci_mpapi_create_list_head();
+			tpg->lu_list = vhci_mpapi_create_list_head();
+			item->idata = (void *)tpg;
+			vhci_mpapi_log_sysevent(vhci->vhci_dip,
+			    &(item->oid.raw_oid), ESC_SUN_MP_TPG_ADD);
+		}
+		break;
+
+		case	MP_OBJECT_TYPE_MULTIPATH_LU:
+		{
+			mpapi_lu_data_t	*lu;
+			scsi_vhci_lun_t	*svl = res;
+			/*
+			 * We cant use ddi_get_instance(svl->svl_dip) at this
+			 * point because the dip is not yet in DS_READY state.
+			 */
+			item->oid.raw_oid =
+			    vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
+
+			lu = kmem_zalloc(sizeof (mpapi_lu_data_t), KM_SLEEP);
+			lu->resp = res;
+			lu->valid = 1;
+			lu->prop.id = (uint64_t)item->oid.raw_oid;
+			/*
+			 * XXX: luGroupID is currently unsupported
+			 */
+			lu->prop.luGroupID = 0xFFFFFFFF;
+
+			(void) strlcpy(lu->prop.name, svl->svl_lun_wwn,
+			    sizeof (lu->prop.name));
+
+			(void) strlcpy(lu->prop.deviceFileName,
+			    "/devices/scsi_vhci/ssd@g",
+			    sizeof (lu->prop.deviceFileName));
+			(void) strlcat(lu->prop.deviceFileName, lu->prop.name,
+			    sizeof (lu->prop.deviceFileName));
+
+			if ((svl->svl_fops != NULL) &&
+			    !SCSI_FAILOVER_IS_SYM(svl->svl_fops)) {
+				lu->prop.asymmetric = 1;
+			}
+
+			lu->prop.autoFailbackEnabled =
+			    ((VHCI_CONF_FLAGS_AUTO_FAILBACK & vhci->
+			    vhci_conf_flags) ? 1 : 0);
+
+			if (svl->svl_lb_policy_save == LOAD_BALANCE_NONE) {
+				lu->prop.currentLoadBalanceType =
+				    MP_DRVR_LOAD_BALANCE_TYPE_NONE;
+			} else if (svl->svl_lb_policy_save == LOAD_BALANCE_RR) {
+				lu->prop.currentLoadBalanceType =
+				    MP_DRVR_LOAD_BALANCE_TYPE_ROUNDROBIN;
+			} else if (svl->svl_lb_policy_save ==
+			    LOAD_BALANCE_LBA) {
+				lu->prop.currentLoadBalanceType =
+				    MP_DRVR_LOAD_BALANCE_TYPE_LBA_REGION;
+			} else {
+				/*
+				 * We still map Load Balance Type to UNKNOWN
+				 * although "none" also maps to the same case.
+				 * MPAPI spec does not have a "NONE" LB type.
+				 */
+				lu->prop.currentLoadBalanceType =
+				    MP_DRVR_LOAD_BALANCE_TYPE_UNKNOWN;
+			}
+			/*
+			 * Allocate header lists for cross reference
+			 */
+			lu->path_list = vhci_mpapi_create_list_head();
+			lu->tpg_list = vhci_mpapi_create_list_head();
+			item->idata = (void *)lu;
+			vhci_mpapi_log_sysevent(vhci->vhci_dip,
+			    &(item->oid.raw_oid), ESC_SUN_MP_LU_CHANGE);
+
+		}
+		break;
+
+		case	MP_OBJECT_TYPE_PATH_LU:
+		{
+			mpapi_path_data_t	*path;
+			mdi_pathinfo_t		*pip = res;
+			scsi_vhci_lun_t		*svl;
+			char			*iport, *tport;
+
+			item->oid.raw_oid =
+			    vhci_mpapi_create_oid(vhci->mp_priv, obj_type);
+			path = kmem_zalloc(
+			    sizeof (mpapi_path_data_t), KM_SLEEP);
+			pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+
+			iport = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+			(void) ddi_pathname(mdi_pi_get_phci(pip), iport);
+
+			if (mdi_prop_lookup_string(pip, "target-port",
+			    &tport) != DDI_PROP_SUCCESS) {
+				/* XXX: target-port prop not found */
+				tport = (char *)mdi_pi_get_addr(pip);
+				VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_"
+				    "create_item: mdi_prop_lookup_string() "
+				    "returned failure; "));
+			}
+
+			svl = mdi_client_get_vhci_private
+			    (mdi_pi_get_client(pip));
+
+			(void) strlcat(pname, iport, MAXPATHLEN);
+			(void) strlcat(pname, tport, MAXPATHLEN);
+			(void) strlcat(pname, svl->svl_lun_wwn, MAXPATHLEN);
+			kmem_free(iport, MAXPATHLEN);
+
+			path->resp = res;
+			path->path_name = pname;
+			path->valid = 1;
+			path->prop.id = item->oid.raw_oid;
+			item->idata = (void *)path;
+			vhci_mpapi_log_sysevent(vhci->vhci_dip,
+			    &(item->oid.raw_oid), ESC_SUN_MP_PATH_ADD);
+		}
+		break;
+
+		case	MP_OBJECT_TYPE_DEVICE_PRODUCT:
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_create_item:"
+			    " DEVICE PRODUCT not handled here."));
+		break;
+
+		default:
+			/*
+			 * This should not happen
+			 */
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_create_item:"
+			    "Got Unsupported OBJECT TYPE"));
+			return (NULL);
+	}
+
+	(void) vhci_mpapi_add_to_list(vhci->mp_priv->obj_hdr_list[obj_type],
+	    ilist);
+	return (ilist);
+}
+
+/*
+ * Local routine to allocate mpapi list header block
+ */
+/* ARGSUSED */
+static mpapi_list_header_t *
+vhci_mpapi_create_list_head()
+{
+	mpapi_list_header_t	*lh;
+
+	lh =  kmem_zalloc(sizeof (mpapi_list_header_t), KM_SLEEP);
+	lh->head = lh->tail = NULL;
+	return (lh);
+}
+
+/*
+ * Routine to create Level 1 mpapi_private data structure and also
+ * establish cross references between the resources being managed
+ */
+/* ARGSUSED */
+void
+vhci_update_mpapi_data(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun,
+    mdi_pathinfo_t *pip)
+{
+	char			*tmp_wwn = NULL, *init = NULL, *path_class;
+	dev_info_t		*pdip;
+	mpapi_item_list_t	*lu_list, *path_list, *init_list, *tgt_list;
+	mpapi_item_list_t	*tp_path_list, *init_path_list, *lu_path_list;
+	mpapi_lu_data_t		*ld;
+	mpapi_path_data_t	*pd;
+	mpapi_tport_data_t	*tpd;
+	mpapi_initiator_data_t	*initd;
+	int			path_class_not_mdi_alloced = 0;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_update_mpapi_data: vhci: %p, "
+	    "vlun: %p, pip: %p\n", (void *)vhci, (void *)vlun, (void *)pip));
+
+	/*
+	 * Check that the lun is not a TPGS device
+	 * TPGS devices create the same information in another routine.
+	 */
+	if (SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
+		return;
+	}
+	/*
+	 * LEVEL 1 - Actions:
+	 * Check if the appropriate resource pointers already
+	 * exist in the Level 1 list and add them if they are new.
+	 */
+
+	/*
+	 * Build MP LU list
+	 */
+	lu_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
+	if (lu_list == NULL) {
+		/* Need to create lu_list entry */
+		lu_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
+	} else {
+		/*
+		 * Matched this lu w/ an existing one in current lu list.
+		 * SAME LUN came online!! So, update the resp in main list.
+		 */
+		ld = lu_list->item->idata;
+		ld->valid = 1;
+		ld->resp = vlun;
+	}
+
+	/*
+	 * Find out the "path-class" property on the pip
+	 */
+	if (mdi_prop_lookup_string(pip, "path-class", &path_class)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: path-class prop not found */
+		path_class = kmem_zalloc(MPAPI_SCSI_MAXPCLASSLEN, KM_SLEEP);
+		(void) strlcpy(path_class, "NONE", MPAPI_SCSI_MAXPCLASSLEN);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_update_mpapi_data: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence path_class = NONE"));
+		path_class_not_mdi_alloced = 1;
+	}
+
+	/*
+	 * Build Path LU list
+	 */
+	path_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip);
+	if (path_list == NULL) {
+		/* Need to create path_list entry */
+		path_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_PATH_LU, (void*)pip);
+	} else {
+		/*
+		 * Matched this pip w/ an existing one in current pip list.
+		 * SAME PATH came online!! So, update the resp in main list.
+		 */
+		pd = path_list->item->idata;
+		pd->valid = 1;
+		pd->resp = pip;
+	}
+
+	if (MDI_PI_IS_ONLINE(pip)) {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_ACTIVE);
+	} else if (MDI_PI_IS_STANDBY(pip)) {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_PASSIVE);
+	} else {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_UNKNOWN);
+	}
+
+	/*
+	 * Build Initiator Port list
+	 */
+	pdip = mdi_pi_get_phci(pip);
+	init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) ddi_pathname(pdip, init);
+
+	init_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
+	if (init_list == NULL) {
+		/*
+		 * Need to create init_list entry
+		 * The resource ptr is no really pdip. It will be changed
+		 * in vhci_mpapi_create_item(). The real resource ptr
+		 * is the Port ID. But we pass the pdip, to create OID.
+		 */
+		init_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
+	} else {
+		initd = init_list->item->idata;
+		initd->valid = 1;
+	}
+	kmem_free(init, MAXPATHLEN);
+
+	/*
+	 * Build Target Port list
+	 * Can get the tdip: tdip = mdi_pi_get_client(pip);
+	 * But what's the use? We want TARGET_PORT.
+	 * So try getting Target Port's WWN which is unique per port.
+	 */
+	tmp_wwn = NULL;
+	if (mdi_prop_lookup_string(pip, "target-port", &tmp_wwn)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: target-port prop not found */
+		tmp_wwn = (char *)mdi_pi_get_addr(pip);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_update_mpapi_data: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence tmp_wwn = %p", (void *)tmp_wwn));
+	}
+
+	tgt_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
+	if (tgt_list == NULL) {
+		/* Need to create tgt_list entry */
+		tgt_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_TARGET_PORT, (void*)tmp_wwn);
+	} else {
+		tpd = tgt_list->item->idata;
+		tpd->valid = 1;
+	}
+
+	/*
+	 * LEVEL 2 - Actions:
+	 * Since all the Object type item lists are updated to account
+	 * for the new resources, now lets cross-reference these
+	 * resources (mainly through paths) to maintain the
+	 * relationship between them.
+	 */
+
+	ld = (mpapi_lu_data_t *)lu_list->item->idata;
+	if (vhci_get_mpapi_item(vhci, ld->path_list,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+		lu_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		lu_path_list->item = path_list->item;
+		(void) vhci_mpapi_add_to_list(ld->path_list, lu_path_list);
+	}
+
+	initd = (mpapi_initiator_data_t *)init_list->item->idata;
+	if (vhci_get_mpapi_item(vhci, initd->path_list,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+		init_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		init_path_list->item = path_list->item;
+		(void) vhci_mpapi_add_to_list(initd->path_list, init_path_list);
+	}
+
+	tpd = (mpapi_tport_data_t *)tgt_list->item->idata;
+	if (vhci_get_mpapi_item(vhci, tpd->path_list,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+		tp_path_list = kmem_zalloc(
+		    sizeof (mpapi_item_list_t), KM_SLEEP);
+		tp_path_list->item = path_list->item;
+		(void) vhci_mpapi_add_to_list(tpd->path_list, tp_path_list);
+	}
+
+	/*
+	 * Level-1: Fill-out Path Properties now, since we got all details.
+	 * Actually, It is a structure copy, rather than just filling details.
+	 */
+	pd = path_list->item->idata;
+	(void) strlcpy(pd->pclass, path_class, sizeof (pd->pclass));
+	bcopy(&(ld->prop), &(pd->prop.logicalUnit),
+	    sizeof (struct mp_logical_unit_prop));
+	bcopy(&(initd->prop), &(pd->prop.initPort),
+	    sizeof (struct mp_init_port_prop));
+	bcopy(&(tpd->prop), &(pd->prop.targetPort),
+	    sizeof (struct mp_target_port_prop));
+
+	vhci_mpapi_synthesize_tpg_data(vhci, vlun, pip);
+
+	if (path_class_not_mdi_alloced == 1) {
+		kmem_free(path_class, MPAPI_SCSI_MAXPCLASSLEN);
+	}
+
+}
+
+/*
+ * Routine to search (& return if found) a TPG object with a specified
+ * accessState for a specified vlun structure. Returns NULL if either
+ * TPG object or the lu item is not found.
+ * This routine is used for NON-TPGS devices.
+ */
+/* ARGSUSED */
+static mpapi_item_list_t *
+vhci_mpapi_get_tpg_item(struct scsi_vhci *vhci, uint32_t acc_state, void *vlun,
+    char *pclass, void *tp)
+{
+	mpapi_list_header_t	*tpghdr, *this_tpghdr;
+	mpapi_item_list_t	*lulist, *tpglist, *this_lulist, *this_tpglist;
+	mpapi_tpg_data_t	*tpgdata, *this_tpgdata;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_get_tpg_item: ENTER: vlun="
+	    "%p, acc_state=%x, pclass=%s, tp=%s\n",
+	    (void *)vlun, acc_state, pclass, (char *)tp));
+
+	lulist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while (lulist != NULL) {
+		tpghdr = ((mpapi_lu_data_t *)(lulist->item->idata))->tpg_list;
+		tpglist = tpghdr->head;
+		while (tpglist != NULL) {
+			tpgdata = tpglist->item->idata;
+
+			if ((tpgdata) &&
+			    (vhci_mpapi_check_tp_in_tpg(tpgdata, tp) == 1) &&
+			    (strncmp(tpgdata->pclass, pclass,
+			    strlen(pclass)) == 0)) {
+				return (tpglist);
+			} else {
+				tpglist = tpglist->next;
+			}
+		}
+		lulist = lulist->next;
+	}
+
+	this_lulist = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
+	if (this_lulist != NULL) {
+		this_tpghdr = ((mpapi_lu_data_t *)(this_lulist->item->idata))
+		    ->tpg_list;
+		this_tpglist = this_tpghdr->head;
+		while (this_tpglist != NULL) {
+			this_tpgdata = this_tpglist->item->idata;
+
+			if ((this_tpgdata) &&
+			    (strncmp(this_tpgdata->pclass, pclass,
+			    strlen(pclass)) == 0)) {
+				return (this_tpglist);
+			} else {
+				this_tpglist = this_tpglist->next;
+			}
+		}
+	}
+
+	VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_get_tpg_item: Returns NULL"));
+
+	return (NULL);
+}
+
+/*
+ * Routine to search (& return if found) a TPG object with a specified
+ * accessState for a specified vlun structure. Returns NULL if either
+ * TPG object or the lu item is not found.
+ * This routine is used for NON-TPGS devices.
+ */
+/* ARGSUSED */
+mpapi_item_list_t *
+vhci_mpapi_get_tpg_for_lun(struct scsi_vhci *vhci, char *pclass,
+    void *vlun, void *tp)
+{
+	mpapi_list_header_t	*this_tpghdr;
+	mpapi_item_list_t	*this_lulist, *this_tpglist;
+	mpapi_tpg_data_t	*this_tpgdata;
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_get_tpg_for_lun: ENTER: vlun="
+	    "%p, pclass=%s, tp=%s\n", (void *)vlun, pclass, (char *)tp));
+
+	this_lulist = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
+	if (this_lulist != NULL) {
+		this_tpghdr = ((mpapi_lu_data_t *)(this_lulist->item->idata))
+		    ->tpg_list;
+		this_tpglist = this_tpghdr->head;
+		while (this_tpglist != NULL) {
+			this_tpgdata = this_tpglist->item->idata;
+
+			if ((this_tpgdata) &&
+			    (vhci_mpapi_check_tp_in_tpg(this_tpgdata,
+			    tp) == 1) && (strncmp(this_tpgdata->pclass, pclass,
+			    strlen(pclass)) == 0)) {
+				return (this_tpglist);
+			}
+			this_tpglist = this_tpglist->next;
+		}
+	}
+
+	VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_mpapi_get_tpg_for_lun: Returns "
+	    "NULL"));
+
+	return (NULL);
+}
+
+/*
+ * Routine to search a Target Port in a TPG
+ */
+/* ARGSUSED */
+static int
+vhci_mpapi_check_tp_in_tpg(mpapi_tpg_data_t *tpgdata, void *tp)
+{
+	mpapi_item_list_t	*tplist;
+
+	if (tpgdata) {
+		tplist = tpgdata->tport_list->head;
+	} else {
+		return (0);
+	}
+
+	while (tplist != NULL) {
+		void	*resp = ((mpapi_tport_data_t *)tplist->
+		    item->idata)->resp;
+		if (strncmp(resp, tp, strlen(resp)) == 0) {
+			/* Found a match */
+			return (1);
+		}
+		tplist = tplist->next;
+	}
+
+	return (0);
+}
+
+/*
+ * Routine to create Level 1 mpapi_private data structure for TPG object &
+ * establish cross references between the TPG resources being managed.
+ * TPG SYNTHESIS MODE: Process for NON-SCSI_FAILOVER_IS_TPGS devices ONLY.
+ * SCSI_FAILOVER_IS_TPGS devices have TPGS(ALUA support) and provide
+ * REPORT_TARGET_PORT_GROUP data which we can parse directly in the next
+ * routine(vhci_mpapi_update_tpg_data) to create TPG list in mpapi_priv block.
+ */
+/* ARGSUSED */
+void
+vhci_mpapi_synthesize_tpg_data(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun,
+    mdi_pathinfo_t *pip)
+{
+	uint32_t		as;
+	char			*tmp_wwn = NULL, *path_class = NULL;
+	mpapi_item_list_t	*tpg_tport_list, *tpg_lu_list, *lu_list;
+	mpapi_item_list_t	*lu_tpg_list, *item_list, *tpg_list;
+	mpapi_tpg_data_t	*tpg_data;
+	int			path_class_not_mdi_alloced = 0;
+
+	/*
+	 * Build Target Port Group list
+	 * Start by finding out the affected Target Port.
+	 */
+	if (mdi_prop_lookup_string(pip, "target-port", &tmp_wwn)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: target-port prop not found */
+		tmp_wwn = (char *)mdi_pi_get_addr(pip);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence tmp_wwn = %p", (void *)tmp_wwn));
+	}
+
+	/*
+	 * Finding out the "path-class" property
+	 */
+	if (mdi_prop_lookup_string(pip, "path-class", &path_class)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: path-class prop not found */
+		path_class = kmem_zalloc(MPAPI_SCSI_MAXPCLASSLEN, KM_SLEEP);
+		(void) strlcpy(path_class, "NONE", MPAPI_SCSI_MAXPCLASSLEN);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence path_class = NONE"));
+		path_class_not_mdi_alloced = 1;
+	}
+
+	/*
+	 * Check the vlun's accessState through pip; we'll use it later.
+	 */
+	if (MDI_PI_IS_ONLINE(pip)) {
+		as = MP_DRVR_ACCESS_STATE_ACTIVE;
+	} else if (MDI_PI_IS_STANDBY(pip)) {
+		as = MP_DRVR_ACCESS_STATE_STANDBY;
+	} else {
+		as = MP_DRVR_ACCESS_STATE_UNAVAILABLE;
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_synthesize_tpg_data: "
+		    "Unknown pip state seen in TPG synthesis"));
+	}
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_synthesize_tpg_data: ENTER: "
+	    "vlun=%s, acc_state=%x, path_class=%s, tp=%s\n",
+	    vlun->svl_lun_wwn, as, path_class, tmp_wwn));
+
+	/*
+	 * Create Level 1 and Level 2 data structures for type
+	 */
+	if (!SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
+		/*
+		 * First check if the lun has a TPG list in its level 2
+		 * structure then, check if this lun is already
+		 * accounted for through a different Target Port.
+		 * If yes, get the ptr to the TPG & skip new TPG creation.
+		 */
+		lu_list = vhci_get_mpapi_item(vhci, NULL,
+		    MP_OBJECT_TYPE_MULTIPATH_LU, vlun);
+		tpg_list = vhci_mpapi_get_tpg_item(vhci, as, vlun, path_class,
+		    (void *)tmp_wwn);
+		if (tpg_list == NULL) {
+			tpg_list = vhci_mpapi_create_item(vhci,
+			    MP_OBJECT_TYPE_TARGET_PORT_GROUP, (void *)tmp_wwn);
+			tpg_data = tpg_list->item->idata;
+			(void) strlcpy(tpg_data->pclass, path_class,
+			    sizeof (tpg_data->pclass));
+			tpg_data->prop.accessState = as;
+		} else {
+			tpg_data = tpg_list->item->idata;
+		}
+
+		if (!SCSI_FAILOVER_IS_SYM(vlun->svl_fops)) {
+			tpg_data->prop.explicitFailover = 1;
+		}
+
+		/*
+		 * Level 2, Lun Cross referencing to TPG.
+		 */
+		if (vhci_get_mpapi_item(vhci, tpg_data->lu_list,
+		    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun) == NULL) {
+			tpg_lu_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+			    KM_SLEEP);
+			item_list = vhci_get_mpapi_item(vhci, NULL,
+			    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
+			tpg_lu_list->item = item_list->item;
+			(void) vhci_mpapi_add_to_list(tpg_data->lu_list,
+			    tpg_lu_list);
+		}
+
+		/*
+		 * Level 2, Target Port Cross referencing to TPG.
+		 */
+		if (vhci_get_mpapi_item(vhci, tpg_data->tport_list,
+		    MP_OBJECT_TYPE_TARGET_PORT, (void *)tmp_wwn) == NULL) {
+			tpg_tport_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+			    KM_SLEEP);
+			item_list = vhci_get_mpapi_item(vhci, NULL,
+			    MP_OBJECT_TYPE_TARGET_PORT, (void *)tmp_wwn);
+			tpg_tport_list->item = item_list->item;
+			(void) vhci_mpapi_add_to_list(tpg_data->tport_list,
+			    tpg_tport_list);
+		}
+
+		/*
+		 * Level 2, TPG Cross referencing to Lun.
+		 */
+		lu_tpg_list = vhci_mpapi_get_tpg_for_lun
+		    (vhci, path_class, vlun, tmp_wwn);
+		if (lu_tpg_list == NULL) {
+			lu_tpg_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+			    KM_SLEEP);
+			lu_tpg_list->item = tpg_list->item;
+			(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
+			    (lu_list->item->idata))->tpg_list, lu_tpg_list);
+		}
+
+		/*
+		 * Update the AccessState of related MPAPI TPGs
+		 * This takes care of a special case where a failover doesn't
+		 * happen but a TPG accessState needs to be updated from
+		 * Unavailable to Standby
+		 */
+		(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
+	}
+
+	if (path_class_not_mdi_alloced == 1) {
+		kmem_free(path_class, MPAPI_SCSI_MAXPCLASSLEN);
+	}
+
+}
+
+/*
+ * Routine to create Level 1 mpapi_private data structure for TPG object,
+ * for devices which support TPG and establish cross references between
+ * the TPG resources being managed. The RTPG response sent by std_asymmetric
+ * module is parsed in this routine and mpapi_priv data structure is updated.
+ */
+/* ARGSUSED */
+void
+vhci_mpapi_update_tpg_data(struct scsi_address *ap, char *ptr)
+{
+	struct scsi_vhci_lun	*vlun = ADDR2VLUN(ap);
+	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
+	struct scsi_device	*psd;
+	scsi_vhci_priv_t	*svp;
+	mdi_pathinfo_t		*pip;
+	dev_info_t		*pdip;
+	char			tpg_id[16], *tgt_port, *init = NULL;
+	unsigned char		*inqbuf;
+	uint32_t		int_tpg_id, rel_tid, as;
+	int			i, rel_tport_cnt;
+	mpapi_item_list_t	*path_list, *init_list;
+	mpapi_item_list_t	*tp_path_list, *init_path_list, *lu_path_list;
+	mpapi_item_list_t	*tpg_tport_list, *tpg_lu_list, *lu_list;
+	mpapi_item_list_t	*lu_tpg_list, *item_list, *tpg_list, *tgt_list;
+	mpapi_lu_data_t		*ld;
+	mpapi_tpg_data_t	*tpg_data;
+	mpapi_path_data_t	*pd;
+	mpapi_tport_data_t	*tpd;
+	mpapi_initiator_data_t	*initd;
+
+	/*
+	 * Find out the TPG ID (resource ptr for TPG is T10 TPG ID)
+	 */
+	int_tpg_id = ((ptr[2] & 0xff) << 8) | (ptr[3] & 0xff);
+	(void) sprintf(tpg_id, "%04x", int_tpg_id);
+
+	/*
+	 * Check the TPG's accessState; we'll use it later.
+	 */
+	as = (ptr[0] & 0x0f);
+	if (as == STD_ACTIVE_OPTIMIZED) {
+		as = MP_DRVR_ACCESS_STATE_ACTIVE_OPTIMIZED;
+	} else if (as == STD_ACTIVE_NONOPTIMIZED) {
+		as = MP_DRVR_ACCESS_STATE_ACTIVE_NONOPTIMIZED;
+	} else if (as == STD_STANDBY) {
+		as = MP_DRVR_ACCESS_STATE_STANDBY;
+	} else {
+		as = MP_DRVR_ACCESS_STATE_UNAVAILABLE;
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_update_tpg_data: "
+		    "UNAVAILABLE accessState seen in ALUA TPG setup"));
+	}
+
+	/*
+	 * Get the vlun through the following process;
+	 * ADDR2VLUN(ap) doesn't give the scsi_vhci lun
+	 */
+	psd = ap->a_hba_tran->tran_sd;
+	inqbuf = (unsigned char *)psd->sd_inq;
+	pip = (mdi_pathinfo_t *)(uintptr_t)(psd->sd_private);
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	vlun = svp->svp_svl;
+
+	/*
+	 * Now get the vhci ptr using the walker
+	 */
+	mdi_walk_vhcis(vhci_mpapi_get_vhci, &vhci);
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_update_tpg_data: vhci=%p, "
+	    "(vlun)wwn=(%p)%s, pip=%p, ap=%p, ptr=%p, as=%x, tpg_id=%s, fops="
+	    "%p\n", (void *)vhci, (void *)vlun, vlun->svl_lun_wwn, (void *)pip,
+	    (void *)ap, (void *)ptr, as, tpg_id, (void *)vlun->svl_fops));
+
+	if ((vhci == NULL) || (vlun == NULL) || (pip == NULL) ||
+	    !SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
+		/* Cant help, unfortunate situation */
+		return;
+	}
+
+	/*
+	 * LEVEL 1 - Actions:
+	 * Check if the appropriate resource pointers already
+	 * exist in the Level 1 list and add them if they are new.
+	 */
+
+	/*
+	 * Build MP LU list
+	 */
+	lu_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
+	if (lu_list == NULL) {
+		/* Need to create lu_list entry */
+		lu_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_MULTIPATH_LU, (void*)vlun);
+	} else {
+		/*
+		 * Matched this lu w/ an existing one in current lu list.
+		 * SAME LUN came online!! So, update the resp in main list.
+		 */
+		ld = lu_list->item->idata;
+		ld->valid = 1;
+		ld->resp = vlun;
+	}
+
+	/*
+	 * Build Path LU list
+	 */
+	path_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip);
+	if (path_list == NULL) {
+		/* Need to create path_list entry */
+		path_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_PATH_LU, (void*)pip);
+	} else {
+		/*
+		 * Matched this pip w/ an existing one in current pip list.
+		 * SAME PATH came online!! So, update the resp in main list.
+		 */
+		pd = path_list->item->idata;
+		pd->valid = 1;
+		pd->resp = pip;
+	}
+
+	if (MDI_PI_IS_ONLINE(pip)) {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_ACTIVE);
+	} else if (MDI_PI_IS_STANDBY(pip)) {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_PASSIVE);
+	} else {
+		vhci_mpapi_set_path_state(vhci->vhci_dip, pip,
+		    MP_DRVR_PATH_STATE_UNKNOWN);
+	}
+
+	/*
+	 * Build Initiator Port list
+	 */
+	pdip = mdi_pi_get_phci(pip);
+	init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) ddi_pathname(pdip, init);
+
+	init_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init);
+	if (init_list == NULL) {
+		/*
+		 * Need to create init_list entry
+		 * The resource ptr is no really pdip. It will be changed
+		 * in vhci_mpapi_create_item(). The real resource ptr
+		 * is the Port ID. But we pass the pdip, to create OID.
+		 */
+		init_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
+	} else {
+		initd = init_list->item->idata;
+		initd->valid = 1;
+	}
+	kmem_free(init, MAXPATHLEN);
+
+	/*
+	 * LEVEL 2 - Actions:
+	 * Since all the Object type item lists are updated to account
+	 * for the new resources, now lets cross-reference these
+	 * resources (mainly through paths) to maintain the
+	 * relationship between them.
+	 */
+
+	ld = (mpapi_lu_data_t *)lu_list->item->idata;
+	if (vhci_get_mpapi_item(vhci, ld->path_list,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+		lu_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		lu_path_list->item = path_list->item;
+		(void) vhci_mpapi_add_to_list(ld->path_list, lu_path_list);
+	}
+
+	initd = (mpapi_initiator_data_t *)init_list->item->idata;
+	if (vhci_get_mpapi_item(vhci, initd->path_list,
+	    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+		init_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		init_path_list->item = path_list->item;
+		(void) vhci_mpapi_add_to_list(initd->path_list, init_path_list);
+	}
+
+	/*
+	 * Create Level 1 & Level 2 data structures
+	 * Parse REPORT_TARGET_PORT_GROUP data & update mpapi database.
+	 */
+
+	tpg_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id);
+	if (tpg_list == NULL) {
+		tpg_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id);
+	}
+	tpg_data = tpg_list->item->idata;
+	tpg_data->prop.accessState = as;
+	tpg_data->prop.tpgId = int_tpg_id;
+
+	/*
+	 * Set explicitFailover for TPG -
+	 * based on tpgs_bits setting in Std Inquiry response.
+	 */
+	if ((((inqbuf[5] & 0x30) >> 4) == 2) ||
+	    (((inqbuf[5] & 0x30) >> 4) == 3)) {
+		tpg_data->prop.explicitFailover = 1;
+	} else if (((inqbuf[5] & 0x30) >> 4) == 1) {
+		tpg_data->prop.explicitFailover = 0;
+	} else if (((inqbuf[5] & 0x30) >> 4) == 0) {
+		tpg_data->prop.explicitFailover = 0;
+	}
+
+	/*
+	 * Level 2, Lun Cross referencing to TPG.
+	 */
+	if (vhci_get_mpapi_item(vhci, tpg_data->lu_list,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun) == NULL) {
+		tpg_lu_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		item_list = vhci_get_mpapi_item(vhci, NULL,
+		    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
+		tpg_lu_list->item = item_list->item;
+		(void) vhci_mpapi_add_to_list(tpg_data->lu_list,
+		    tpg_lu_list);
+	}
+
+	/*
+	 * Level 2, TPG Cross referencing to Lun.
+	 */
+	if (vhci_get_mpapi_item(vhci, ld->tpg_list,
+	    MP_OBJECT_TYPE_TARGET_PORT_GROUP, &tpg_id) == 0) {
+		lu_tpg_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+		    KM_SLEEP);
+		lu_tpg_list->item = tpg_list->item;
+		(void) vhci_mpapi_add_to_list(((mpapi_lu_data_t *)
+		    (lu_list->item->idata))->tpg_list, lu_tpg_list);
+	}
+
+	/*
+	 * Building Target Port list is different here.
+	 * For each different Relative Target Port. we have a new MPAPI
+	 * Target Port OID generated.
+	 * Just find out the main Target Port property here.
+	 */
+	tgt_port = NULL;
+	if (mdi_prop_lookup_string(pip, "target-port", &tgt_port)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: target-port prop not found */
+		tgt_port = (char *)mdi_pi_get_addr(pip);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_update_tpg_data: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence tgt_port = %p", (void *)tgt_port));
+	}
+
+	/*
+	 * Level 1, Relative Target Port + Target Port Creation
+	 */
+	rel_tport_cnt = (ptr[7] & 0xff);
+	ptr += 8;
+	for (i = 0; i < rel_tport_cnt; i++) {
+		rel_tid = 0;
+		rel_tid |= ((ptr[2] & 0Xff) << 8);
+		rel_tid |= (ptr[3] & 0xff);
+
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_update_tpg_data: "
+		    "TgtPort=%s, RelTgtPort=%x\n", tgt_port, rel_tid));
+
+		tgt_list = vhci_mpapi_get_rel_tport_pair(vhci, NULL,
+		    (void *)tgt_port, rel_tid);
+		if (tgt_list == NULL) {
+			/* Need to create tgt_list entry */
+			tgt_list = vhci_mpapi_create_item(vhci,
+			    MP_OBJECT_TYPE_TARGET_PORT,
+			    (void *)tgt_port);
+			tpd = tgt_list->item->idata;
+			tpd->valid = 1;
+			tpd->prop.relativePortID = rel_tid;
+		} else {
+			tpd = tgt_list->item->idata;
+			tpd->valid = 1;
+		}
+
+		tpd = (mpapi_tport_data_t *)tgt_list->item->idata;
+		if (vhci_get_mpapi_item(vhci, tpd->path_list,
+		    MP_OBJECT_TYPE_PATH_LU, (void*)pip) == NULL) {
+			tp_path_list = kmem_zalloc(sizeof (mpapi_item_list_t),
+			    KM_SLEEP);
+			tp_path_list->item = path_list->item;
+			(void) vhci_mpapi_add_to_list(tpd->path_list,
+			    tp_path_list);
+		}
+
+		if (vhci_mpapi_get_rel_tport_pair(vhci,
+		    tpg_data->tport_list, tgt_port, rel_tid) == NULL) {
+			tpg_tport_list = kmem_zalloc
+			    (sizeof (mpapi_item_list_t), KM_SLEEP);
+			tpg_tport_list->item = tgt_list->item;
+			(void) vhci_mpapi_add_to_list(tpg_data->
+			    tport_list, tpg_tport_list);
+		}
+		ptr += 4;
+	}
+
+	/*
+	 * Level-1: Fill-out Path Properties now, since we got all details.
+	 * Actually, It is a structure copy, rather than just filling details.
+	 */
+	pd = path_list->item->idata;
+	bcopy(&(ld->prop), &(pd->prop.logicalUnit),
+	    sizeof (struct mp_logical_unit_prop));
+	bcopy(&(initd->prop), &(pd->prop.initPort),
+	    sizeof (struct mp_init_port_prop));
+	bcopy(&(tpd->prop), &(pd->prop.targetPort),
+	    sizeof (struct mp_target_port_prop));
+}
+
+/*
+ * Routine to get mpapi ioctl argument structure from userland.
+ */
+/* ARGSUSED */
+static int
+vhci_get_mpiocdata(const void *data, mp_iocdata_t *mpioc, int mode)
+{
+	int	retval = 0;
+
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+	{
+		mp_iocdata32_t	ioc32;
+
+		VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_get_mpiocdata: "
+		    "Case DDI_MODEL_ILP32"));
+		if (ddi_copyin((void *)data, (void *)&ioc32,
+		    sizeof (mp_iocdata32_t), mode)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpiocdata: "
+			    "ddi_copyin() FAILED"));
+			retval = EFAULT;
+			break;
+		}
+		mpioc->mp_xfer	= (uint16_t)(uintptr_t)ioc32.mp_xfer;
+		mpioc->mp_cmd	= (uint16_t)(uintptr_t)ioc32.mp_cmd;
+		mpioc->mp_flags	= (uint16_t)(uintptr_t)ioc32.mp_flags;
+		mpioc->mp_cmd_flags	= (uint16_t)ioc32.mp_cmd_flags;
+		mpioc->mp_ilen	= (size_t)(uintptr_t)ioc32.mp_ilen;
+		mpioc->mp_ibuf	= (caddr_t)(uintptr_t)ioc32.mp_ibuf;
+		mpioc->mp_olen	= (size_t)(uintptr_t)ioc32.mp_olen;
+		mpioc->mp_obuf	= (caddr_t)(uintptr_t)ioc32.mp_obuf;
+		mpioc->mp_alen	= (size_t)(uintptr_t)ioc32.mp_alen;
+		mpioc->mp_abuf	= (caddr_t)(uintptr_t)ioc32.mp_abuf;
+		mpioc->mp_errno	= (int)(uintptr_t)ioc32.mp_errno;
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyin(data, (void*)mpioc, sizeof (*mpioc), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		break;
+
+	default:
+		if (ddi_copyin(data, (void*)mpioc, sizeof (*mpioc), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		break;
+	}
+#else   /* _MULTI_DATAMODEL */
+	if (ddi_copyin(data, (void *)mpioc, sizeof (*mpioc), mode)) {
+		retval = EFAULT;
+	}
+#endif  /* _MULTI_DATAMODEL */
+
+	if (retval) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "vhci_get_mpiocdata: cmd <%x> "
+		    "iocdata copyin failed", mpioc->mp_cmd));
+	}
+
+	return (retval);
+}
+
+/* ARGSUSED */
+static int
+vhci_is_model_type32(int mode)
+{
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+		case DDI_MODEL_ILP32:
+			return (1);
+		default:
+			return (0);
+	}
+#else   /* _MULTI_DATAMODEL */
+	return (0);
+#endif  /* _MULTI_DATAMODEL */
+}
+
+/*
+ * Convenience routine to copy mp_iocdata(32) to user land
+ */
+/* ARGSUSED */
+static int
+vhci_mpapi_copyout_iocdata(void *mpioc, void *udata, int mode)
+{
+	int	rval = 0;
+
+	if (vhci_is_model_type32(mode)) {
+		mp_iocdata32_t	*mpioc32;
+
+		mpioc32 = (mp_iocdata32_t *)kmem_zalloc
+		    (sizeof (mp_iocdata32_t), KM_SLEEP);
+		mpioc32->mp_xfer = (uint16_t)((mp_iocdata_t *)mpioc)->mp_xfer;
+		mpioc32->mp_cmd	 = (uint16_t)((mp_iocdata_t *)mpioc)->mp_cmd;
+		mpioc32->mp_flags = (uint16_t)((mp_iocdata_t *)mpioc)->mp_flags;
+		mpioc32->mp_cmd_flags = (uint16_t)((mp_iocdata_t *)
+		    mpioc)->mp_cmd_flags;
+		mpioc32->mp_ilen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_ilen;
+		mpioc32->mp_ibuf = (caddr32_t)((mp_iocdata32_t *)
+		    mpioc)->mp_ibuf;
+		mpioc32->mp_olen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_olen;
+		mpioc32->mp_obuf = (caddr32_t)((mp_iocdata32_t *)
+		    mpioc)->mp_obuf;
+		mpioc32->mp_alen = (uint32_t)((mp_iocdata_t *)mpioc)->mp_alen;
+		mpioc32->mp_abuf = (caddr32_t)((mp_iocdata32_t *)
+		    mpioc)->mp_abuf;
+		mpioc32->mp_errno = (int32_t)((mp_iocdata_t *)mpioc)->mp_errno;
+
+		if (ddi_copyout(mpioc32, udata, sizeof (mp_iocdata32_t), mode)
+		    != 0) {
+			rval = EFAULT;
+		}
+		kmem_free(mpioc32, sizeof (mp_iocdata32_t));
+	} else {
+		/* 64-bit ddicopyout */
+		if (ddi_copyout(mpioc, udata, sizeof (mp_iocdata_t), mode)
+		    != 0) {
+			rval = EFAULT;
+		}
+	}
+
+	return (rval);
+
+}
+
+/*
+ * Routine to sync OIDs of MPLU to match with the ssd instance# of the
+ * scsi_vhci lun, to accommodate the DINFOCACHE implementation of the plugin.
+ * ssd instance# = devi_instance from the dev_info structure.
+ * dev_info structure of the scsi_vhci lun is pointed by svl_dip field of
+ * scsi_vhci_lun structure.
+ */
+/* ARGSUSED */
+static int
+vhci_mpapi_sync_lu_oid_list(struct scsi_vhci *vhci)
+{
+	int			rval = 0;
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*lud;
+	mpapi_path_data_t	*pd;
+	scsi_vhci_lun_t		*svl;
+	dev_info_t		*lun_dip;
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while (ilist != NULL) {
+		if (MP_GET_MAJOR_FROM_ID((uint64_t)
+		    (ilist->item->oid.raw_oid)) != 0) {
+			lud = ilist->item->idata;
+			if (lud->valid == 1) {
+				svl = lud->resp;
+				ilist->item->oid.raw_oid =
+				    (uint64_t)ddi_get_instance(svl->svl_dip);
+				lud->prop.id =
+				    (uint64_t)ddi_get_instance(svl->svl_dip);
+			}
+		}
+		ilist = ilist->next;
+	}
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_PATH_LU]->head;
+	while (ilist != NULL) {
+		pd = ilist->item->idata;
+		if ((pd->valid == 1) && (MP_GET_MAJOR_FROM_ID((uint64_t)
+		    (pd->prop.logicalUnit.id)) != 0)) {
+			lun_dip = mdi_pi_get_client
+			    ((mdi_pathinfo_t *)(pd->resp));
+			pd->prop.logicalUnit.id =
+			    (uint64_t)ddi_get_instance(lun_dip);
+		}
+		ilist = ilist->next;
+	}
+
+	return (rval);
+}
+
+/*
+ * Routine to sync Initiator Port List with what MDI maintains. This means
+ * MP API knows about Initiator Ports which don't have a pip.
+ */
+/* ARGSUSED */
+int
+vhci_mpapi_sync_init_port_list(dev_info_t *pdip, void *arg)
+{
+	int			init_not_ddi_alloced = 0;
+	struct scsi_vhci	*vhci = arg;
+	char			*init, *init_port_res;
+	mpapi_item_list_t	*init_list;
+	mpapi_initiator_data_t	*initd;
+
+	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
+	    DDI_PROP_DONTPASS, "initiator-port", &init) != DDI_PROP_SUCCESS)) {
+		/* XXX: initiator-port prop not found */
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_sync_init_port_list: "
+		    "initiator-port prop not found"));
+		init = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+		init_not_ddi_alloced = 1;
+		(void) ddi_pathname(pdip, init);
+	}
+
+	init_port_res = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) ddi_pathname(pdip, init_port_res);
+
+	init_list = vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)init_port_res);
+	if (init_list == NULL) {
+		/*
+		 * Need to create init_list entry
+		 * The resource ptr is not really pdip. It will be changed
+		 * in vhci_mpapi_create_item(). The real resource ptr
+		 * is the Port ID. But we pass the pdip, to create OID.
+		 */
+		init_list = vhci_mpapi_create_item(vhci,
+		    MP_OBJECT_TYPE_INITIATOR_PORT, (void*)pdip);
+	}
+
+	initd = init_list->item->idata;
+	initd->valid = 1;
+	(void) strlcpy(initd->prop.portID, init, sizeof (initd->prop.portID));
+
+	if (init_not_ddi_alloced == 1) {
+		kmem_free(init, MAXPATHLEN);
+	} else if (init) {
+		ddi_prop_free(init);
+	}
+	kmem_free(init_port_res, MAXPATHLEN);
+
+	return (DDI_WALK_CONTINUE);
+}
+
+/* ARGSUSED */
+static void
+vhci_mpapi_log_sysevent(dev_info_t *dip, uint64_t *oid, char *subclass)
+{
+	nvlist_t	*attr_list;
+
+	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
+	    KM_SLEEP) != DDI_SUCCESS) {
+		goto alloc_failed;
+	}
+
+	if (nvlist_add_uint64_array(attr_list, "oid", oid, 1) != DDI_SUCCESS) {
+		goto error;
+	}
+
+	(void) ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_SUN_MP, subclass,
+	    attr_list, NULL, DDI_SLEEP);
+
+error:
+	nvlist_free(attr_list);
+	return;
+
+alloc_failed:
+	VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_log_sysevent: "
+	    "Unable to send sysevent"));
+
+}
+
+/* ARGSUSED */
+void
+vhci_mpapi_set_path_state(dev_info_t *vdip, mdi_pathinfo_t *pip, int state)
+{
+	struct scsi_vhci	*vhci;
+	struct scsi_vhci_lun	*svl;
+	scsi_vhci_priv_t	*svp;
+	mpapi_item_list_t	*ilist, *lu_list;
+	mpapi_path_data_t	*pp;
+	mpapi_lu_data_t		*ld;
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+
+	ilist = vhci_get_mpapi_item(vhci, NULL, MP_OBJECT_TYPE_PATH_LU, pip);
+
+	if (ilist != NULL) {
+		mutex_enter(&ilist->item->item_mutex);
+		pp = ilist->item->idata;
+		pp->prop.pathState = state;
+		pp->valid = 1;
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_set_path_state: "
+		    "pip(%p) not found", (void *)pip));
+		return;
+	}
+
+	/*
+	 * Find if there are any paths at all to the lun
+	 */
+	if ((state == MP_DRVR_PATH_STATE_REMOVED) || (state ==
+	    MP_DRVR_PATH_STATE_PATH_ERR) || (state ==
+	    MP_DRVR_PATH_STATE_LU_ERR) || (state ==
+	    MP_DRVR_PATH_STATE_UNKNOWN)) {
+		pp->valid = 0;
+
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		svl = svp->svp_svl;
+		/*
+		 * Update the AccessState of related MPAPI TPGs
+		 * This takes care of a special case where a path goes offline
+		 * & the TPG accessState may need an update from
+		 * Active/Standby to Unavailable.
+		 */
+		if (!SCSI_FAILOVER_IS_TPGS(svl->svl_fops)) {
+			(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci,
+			    svl);
+		}
+
+		/*
+		 * Following means the lun is offline
+		 */
+		if (vhci_mpapi_chk_last_path(pip) == -1) {
+			lu_list = vhci_get_mpapi_item(vhci, NULL,
+			    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)svl);
+			if (lu_list != NULL) {
+				ld = lu_list->item->idata;
+				ld->valid = 0;
+			}
+		}
+	}
+	mutex_exit(&ilist->item->item_mutex);
+
+}
+
+/* ARGSUSED */
+static mpapi_item_list_t *
+vhci_mpapi_match_pip(struct scsi_vhci *vhci, mpapi_item_list_t *ilist,
+    void *res)
+{
+	mpapi_path_data_t	*pd;
+	scsi_vhci_lun_t		*this_svl;
+	mdi_pathinfo_t		*this_pip;
+	char			*this_iport;
+	char			*this_tport;
+	char			*pname;
+
+	this_pip = (mdi_pathinfo_t *)res;
+	if ((this_pip == NULL) || (ilist == NULL)) {
+		return (NULL);
+	}
+
+	this_iport = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) ddi_pathname(mdi_pi_get_phci(this_pip), this_iport);
+
+	if (mdi_prop_lookup_string(this_pip, "target-port", &this_tport)
+	    != DDI_PROP_SUCCESS) {
+		/* XXX: target-port prop not found */
+		this_tport = (char *)mdi_pi_get_addr(this_pip);
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_mpapi_match_pip: "
+		    "mdi_prop_lookup_string() returned failure; "
+		    "Hence this_tport = %p", (void *)this_tport));
+	}
+
+	this_svl = mdi_client_get_vhci_private(mdi_pi_get_client(this_pip));
+
+	pname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	(void) strlcat(pname, this_iport, MAXPATHLEN);
+	(void) strlcat(pname, this_tport, MAXPATHLEN);
+	(void) strlcat(pname, this_svl->svl_lun_wwn, MAXPATHLEN);
+	kmem_free(this_iport, MAXPATHLEN);
+
+	while (ilist != NULL) {
+		pd = (mpapi_path_data_t *)(ilist->item->idata);
+		if ((pd != NULL) && (strncmp
+		    (pd->path_name, pname, strlen(pname)) == 0)) {
+			VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_mpapi_match_pip: "
+			    "path_name = %s", pd->path_name));
+			kmem_free(pname, MAXPATHLEN);
+			return (ilist);
+		}
+		ilist = ilist->next;
+	}
+
+	kmem_free(pname, MAXPATHLEN);
+	return (NULL);
+}
+
+/* ARGSUSED */
+static
+mpapi_item_list_t *vhci_mpapi_match_lu(struct scsi_vhci *vhci,
+    mpapi_item_list_t *ilist, void *res)
+{
+	mpapi_lu_data_t		*ld;
+	scsi_vhci_lun_t		*this_svl;
+
+	this_svl = (scsi_vhci_lun_t *)res;
+	if ((this_svl == NULL) || (ilist == NULL)) {
+		return (NULL);
+	}
+
+	while (ilist != NULL) {
+		ld = (mpapi_lu_data_t *)(ilist->item->idata);
+		if ((ld != NULL) && (strncmp
+		    (ld->prop.name, this_svl->svl_lun_wwn,
+		    strlen(this_svl->svl_lun_wwn)) == 0)) {
+			VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_mpapi_match_lu: "
+			    "this_wwn = %s", this_svl->svl_lun_wwn));
+			return (ilist);
+		}
+		ilist = ilist->next;
+	}
+
+	return (NULL);
+}
+
+/*
+ * Routine to handle TPG AccessState Change - Called after each LU failover
+ */
+int
+vhci_mpapi_update_tpg_acc_state_for_lu(struct scsi_vhci *vhci,
+    scsi_vhci_lun_t *vlun)
+{
+	int			rval = 0;
+	mpapi_item_list_t	*lu_list, *path_list, *tpg_list;
+	mpapi_lu_data_t		*lu_data;
+	mpapi_path_data_t	*path_data;
+	mpapi_tpg_data_t	*tpg_data;
+
+	lu_list = vhci_get_mpapi_item(vhci, NULL, MP_OBJECT_TYPE_MULTIPATH_LU,
+	    (void *)vlun);
+	if (lu_list == NULL) {
+		return (-1);
+	}
+	lu_data = lu_list->item->idata;
+	if (lu_data == NULL) {
+		return (-1);
+	}
+	lu_data->resp = vlun;
+	lu_data->valid = 1;
+
+	/*
+	 * For each "pclass of PATH" and "pclass of TPG" match of this LU,
+	 * Update the TPG AccessState to reflect the state of the path.
+	 * Exit the inner loop after the 1st successful ACTIVE/STANDBY update
+	 * is made, because subsequent matches also lead to the same TPG.
+	 */
+	tpg_list = lu_data->tpg_list->head;
+	while (tpg_list != NULL) {
+		tpg_data = tpg_list->item->idata;
+		path_list = lu_data->path_list->head;
+		while (path_list != NULL) {
+			path_data = path_list->item->idata;
+			if (strncmp(path_data->pclass, tpg_data->pclass,
+			    strlen(tpg_data->pclass)) == 0) {
+				VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_mpapi_"
+				    "update_tpg_acc_state_for_lu: Operating on "
+				    "LUN(%s), PATH(%p), TPG(%x: %s)\n",
+				    lu_data->prop.name, path_data->resp,
+				    tpg_data->prop.tpgId, tpg_data->pclass));
+				if (MDI_PI_IS_ONLINE(path_data->resp)) {
+					tpg_data->prop.accessState =
+					    MP_DRVR_ACCESS_STATE_ACTIVE;
+					break;
+				} else if (MDI_PI_IS_STANDBY(path_data->resp)) {
+					tpg_data->prop.accessState =
+					    MP_DRVR_ACCESS_STATE_STANDBY;
+					break;
+				} else {
+					tpg_data->prop.accessState =
+					    MP_DRVR_ACCESS_STATE_UNAVAILABLE;
+				}
+			}
+			path_list = path_list->next;
+		}
+		tpg_list = tpg_list->next;
+	}
+
+	return (rval);
+}
+
+int
+vhci_mpapi_get_vhci(dev_info_t *vdip, void *ptr2vhci)
+{
+	struct scsi_vhci	*local_vhci;
+
+	if (strncmp("scsi_vhci", ddi_get_name(vdip),
+	    strlen("scsi_vhci")) == 0) {
+		local_vhci = ddi_get_soft_state(vhci_softstate,
+		    ddi_get_instance(vdip));
+		bcopy(&local_vhci, ptr2vhci, sizeof (local_vhci));
+		return (DDI_WALK_TERMINATE);
+	}
+
+	return (DDI_WALK_CONTINUE);
+
+}
+
+/* ARGSUSED */
+void *
+vhci_mpapi_get_rel_tport_pair(struct scsi_vhci *vhci, mpapi_list_header_t *list,
+    void *tgt_port, uint32_t rel_tid)
+{
+	mpapi_item_list_t	*ilist;
+	mpapi_tport_data_t	*tpd;
+
+	if (list == NULL) {
+		/*
+		 * Since the listhead is null, the search is being
+		 * performed in implicit mode - that is to use the
+		 * level one list.
+		 */
+		ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]
+		    ->head;
+	} else {
+		/*
+		 * The search is being performed on a sublist within
+		 * one of the toplevel list items. Use the listhead
+		 * that is passed in.
+		 */
+		ilist = list->head;
+	}
+
+	if (tgt_port == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_get_mpapi_item: "
+		    " Got Target Port w/ NULL resource"));
+		return (NULL);
+	}
+
+	while (ilist) {
+		tpd = (mpapi_tport_data_t *)ilist->item->idata;
+		if ((strncmp(tpd->resp, tgt_port, strlen(tgt_port)) == 0) &&
+		    (tpd->prop.relativePortID == rel_tid)) {
+			/* Match */
+			return ((void*)ilist);
+		} else {
+			ilist = ilist->next;
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Returns 0, if 2 more paths are available to the lun;
+ * Returns 1, if ONLY 1 path is available to the lun;
+ * Return -1 for all other cases.
+ */
+static int
+vhci_mpapi_chk_last_path(mdi_pathinfo_t *pip)
+{
+	dev_info_t	*pdip = NULL, *cdip = NULL;
+	int		count = 0, circular;
+	mdi_pathinfo_t	*ret_pip;
+
+	if (pip == NULL) {
+		return (-1);
+	} else {
+		pdip = mdi_pi_get_phci(pip);
+		cdip = mdi_pi_get_client(pip);
+	}
+
+	if ((pdip == NULL) || (cdip == NULL)) {
+		return (-1);
+	}
+
+	ndi_devi_enter(cdip, &circular);
+	ret_pip = mdi_get_next_phci_path(cdip, NULL);
+
+	while ((ret_pip != NULL) && (count < 2)) {
+		mdi_pi_lock(ret_pip);
+		if ((MDI_PI_IS_ONLINE(ret_pip) ||
+		    MDI_PI_IS_STANDBY(ret_pip) ||
+		    MDI_PI_IS_INIT(ret_pip)) &&
+		    !(MDI_PI_IS_DISABLE(ret_pip) ||
+		    MDI_PI_IS_TRANSIENT(ret_pip))) {
+			count++;
+		}
+		mdi_pi_unlock(ret_pip);
+		ret_pip = mdi_get_next_phci_path(cdip, ret_pip);
+	}
+	ndi_devi_exit(cdip, circular);
+
+	if (count > 1) {
+		return (0);
+	} else if (count == 1) {
+		return (1);
+	}
+
+	return (-1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,8346 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Multiplexed I/O SCSI vHCI implementation
+ */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/impl/scsi_reset_notify.h>
+#include <sys/sunmdi.h>
+#include <sys/mdi_impldefs.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+#include <sys/disp.h>
+#include <sys/byteorder.h>
+
+extern uintptr_t scsi_callback_id;
+extern ddi_dma_attr_t scsi_alloc_attr;
+
+#ifdef	DEBUG
+int	vhci_debug = VHCI_DEBUG_DEFAULT_VAL;
+#endif
+
+/* retry for the vhci_do_prout command when a not ready is returned */
+int vhci_prout_not_ready_retry = 180;
+
+/*
+ * These values are defined to support the internal retry of
+ * SCSI packets for better sense code handling.
+ */
+#define	VHCI_CMD_CMPLT	0
+#define	VHCI_CMD_RETRY	1
+#define	VHCI_CMD_ERROR	-1
+
+#define	PROPFLAGS (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM)
+#define	VHCI_SCSI_PERR		0x47
+#define	VHCI_PGR_ILLEGALOP	-2
+#define	VHCI_NUM_UPDATE_TASKQ	8
+#define	VHCI_STD_INQ_SIZE	128
+
+/*
+ * Version Macros
+ */
+#define	VHCI_NAME_VERSION	"SCSI VHCI Driver %I%"
+char		vhci_version_name[] = VHCI_NAME_VERSION;
+
+int		vhci_first_time = 0;
+clock_t		vhci_to_ticks = 0;
+int		vhci_init_wait_timeout = VHCI_INIT_WAIT_TIMEOUT;
+kcondvar_t	vhci_cv;
+kmutex_t	vhci_global_mutex;
+void		*vhci_softstate = NULL; /* for soft state */
+
+/*
+ * Flag to delay the retry of the reserve command
+ */
+int		vhci_reserve_delay = 100000;
+static int	vhci_path_quiesce_timeout = 60;
+static uchar_t	zero_key[MHIOC_RESV_KEY_SIZE];
+
+/* uscsi delay for a TRAN_BUSY */
+static int vhci_uscsi_delay = 100000;
+static int vhci_uscsi_retry_count = 180;
+/* uscsi_restart_sense timeout id in case it needs to get canceled */
+static timeout_id_t vhci_restart_timeid = 0;
+
+/*
+ * Bidirectional map of 'target-port' to port id <pid> for support of
+ * iostat(1M) '-Xx' and '-Yx' output.
+ */
+static kmutex_t		vhci_targetmap_mutex;
+static uint_t		vhci_targetmap_pid = 1;
+static mod_hash_t	*vhci_targetmap_bypid;	/* <pid> -> 'target-port' */
+static mod_hash_t	*vhci_targetmap_byport;	/* 'target-port' -> <pid> */
+
+/*
+ * functions exported by scsi_vhci struct cb_ops
+ */
+static int vhci_open(dev_t *, int, int, cred_t *);
+static int vhci_close(dev_t, int, int, cred_t *);
+static int vhci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+/*
+ * functions exported by scsi_vhci struct dev_ops
+ */
+static int vhci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int vhci_attach(dev_info_t *, ddi_attach_cmd_t);
+static int vhci_detach(dev_info_t *, ddi_detach_cmd_t);
+
+/*
+ * functions exported by scsi_vhci scsi_hba_tran_t transport table
+ */
+static int vhci_scsi_tgt_init(dev_info_t *, dev_info_t *,
+    scsi_hba_tran_t *, struct scsi_device *);
+static void vhci_scsi_tgt_free(dev_info_t *, dev_info_t *, scsi_hba_tran_t *,
+    struct scsi_device *);
+static int vhci_pgr_register_start(scsi_vhci_lun_t *, struct scsi_pkt *);
+static int vhci_scsi_start(struct scsi_address *, struct scsi_pkt *);
+static int vhci_scsi_abort(struct scsi_address *, struct scsi_pkt *);
+static int vhci_scsi_reset(struct scsi_address *, int);
+static int vhci_scsi_reset_target(struct scsi_address *, int level,
+    uint8_t select_path);
+static int vhci_scsi_reset_bus(struct scsi_address *);
+static int vhci_scsi_getcap(struct scsi_address *, char *, int);
+static int vhci_scsi_setcap(struct scsi_address *, char *, int, int);
+static int vhci_commoncap(struct scsi_address *, char *, int, int, int);
+static int vhci_pHCI_cap(struct scsi_address *ap, char *cap, int val, int whom,
+    mdi_pathinfo_t *pip);
+static struct scsi_pkt *vhci_scsi_init_pkt(struct scsi_address *,
+    struct scsi_pkt *, struct buf *, int, int, int, int, int (*)(), caddr_t);
+static void vhci_scsi_destroy_pkt(struct scsi_address *, struct scsi_pkt *);
+static void vhci_scsi_dmafree(struct scsi_address *, struct scsi_pkt *);
+static void vhci_scsi_sync_pkt(struct scsi_address *, struct scsi_pkt *);
+static int vhci_scsi_reset_notify(struct scsi_address *, int, void (*)(caddr_t),
+    caddr_t);
+static int vhci_scsi_get_bus_addr(struct scsi_device *, char *, int);
+static int vhci_scsi_get_name(struct scsi_device *, char *, int);
+static int vhci_scsi_bus_power(dev_info_t *, void *, pm_bus_power_op_t,
+    void *, void *);
+static int vhci_scsi_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
+    void *, dev_info_t **);
+
+/*
+ * functions registered with the mpxio framework via mdi_vhci_ops_t
+ */
+static int vhci_pathinfo_init(dev_info_t *, mdi_pathinfo_t *, int);
+static int vhci_pathinfo_uninit(dev_info_t *, mdi_pathinfo_t *, int);
+static int vhci_pathinfo_state_change(dev_info_t *, mdi_pathinfo_t *,
+    mdi_pathinfo_state_t, uint32_t, int);
+static int vhci_pathinfo_online(dev_info_t *, mdi_pathinfo_t *, int);
+static int vhci_pathinfo_offline(dev_info_t *, mdi_pathinfo_t *, int);
+static int vhci_failover(dev_info_t *, dev_info_t *, int);
+static void vhci_client_attached(dev_info_t *);
+
+static int vhci_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int vhci_devctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int vhci_ioc_get_phci_path(sv_iocdata_t *, caddr_t, int, caddr_t);
+static int vhci_ioc_get_client_path(sv_iocdata_t *, caddr_t, int, caddr_t);
+static int vhci_ioc_get_paddr(sv_iocdata_t *, caddr_t, int, caddr_t);
+static int vhci_ioc_send_client_path(caddr_t, sv_iocdata_t *, int, caddr_t);
+static void vhci_ioc_devi_to_path(dev_info_t *, caddr_t);
+static int vhci_get_phci_path_list(dev_info_t *, sv_path_info_t *, uint_t);
+static int vhci_get_client_path_list(dev_info_t *, sv_path_info_t *, uint_t);
+static int vhci_get_iocdata(const void *, sv_iocdata_t *, int, caddr_t);
+static int vhci_get_iocswitchdata(const void *, sv_switch_to_cntlr_iocdata_t *,
+    int, caddr_t);
+static int vhci_ioc_alloc_pathinfo(sv_path_info_t **, sv_path_info_t **,
+    uint_t, sv_iocdata_t *, int, caddr_t);
+static void vhci_ioc_free_pathinfo(sv_path_info_t *, sv_path_info_t *, uint_t);
+static int vhci_ioc_send_pathinfo(sv_path_info_t *, sv_path_info_t *, uint_t,
+    sv_iocdata_t *, int, caddr_t);
+static int vhci_handle_ext_fo(struct scsi_pkt *, int);
+static int vhci_efo_watch_cb(caddr_t, struct scsi_watch_result *);
+static int vhci_quiesce_lun(struct scsi_vhci_lun *);
+static int vhci_pgr_validate_and_register(scsi_vhci_priv_t *);
+static void vhci_dispatch_scsi_start(void *);
+static void vhci_efo_done(void *);
+static void vhci_initiate_auto_failback(void *);
+static void vhci_update_pHCI_pkt(struct vhci_pkt *, struct scsi_pkt *);
+static int vhci_update_pathinfo(struct scsi_device *, mdi_pathinfo_t *,
+    struct scsi_failover_ops *, scsi_vhci_lun_t *, struct scsi_vhci *);
+static void vhci_kstat_create_pathinfo(mdi_pathinfo_t *);
+static int vhci_quiesce_paths(dev_info_t *, dev_info_t *,
+    scsi_vhci_lun_t *, char *, char *);
+
+static char *vhci_devnm_to_guid(char *);
+static int vhci_bind_transport(struct scsi_address *, struct vhci_pkt *,
+    int, int (*func)(caddr_t));
+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 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 *);
+static struct scsi_vhci_lun *vhci_lun_lookup_alloc(dev_info_t *, char *, int *);
+static void vhci_lun_free(dev_info_t *);
+static int vhci_recovery_reset(scsi_vhci_lun_t *, struct scsi_address *,
+    uint8_t, uint8_t);
+void vhci_update_pathstates(void *);
+
+#ifdef DEBUG
+static void vhci_print_prin_keys(vhci_prin_readkeys_t *, int);
+#endif
+static void vhci_print_prout_keys(scsi_vhci_lun_t *, char *);
+static void vhci_uscsi_iodone(struct scsi_pkt *pkt);
+
+/*
+ * MP-API related functions
+ */
+extern int vhci_mpapi_init(struct scsi_vhci *);
+extern void vhci_mpapi_add_dev_prod(struct scsi_vhci *, char *);
+extern int vhci_mpapi_ctl(dev_t, int, intptr_t, int, cred_t *, int *);
+extern void vhci_update_mpapi_data(struct scsi_vhci *,
+    scsi_vhci_lun_t *, mdi_pathinfo_t *);
+extern void* vhci_get_mpapi_item(struct scsi_vhci *, mpapi_list_header_t *,
+    uint8_t, void*);
+extern void vhci_mpapi_set_path_state(dev_info_t *, mdi_pathinfo_t *, int);
+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_MAX_PGR_RETRIES	3
+
+/*
+ * Macros for the device-type mpxio options
+ */
+#define	LOAD_BALANCE_OPTIONS		"load-balance-options"
+#define	LOGICAL_BLOCK_REGION_SIZE	"region-size"
+#define	MPXIO_OPTIONS_LIST		"device-type-mpxio-options-list"
+#define	DEVICE_TYPE_STR			"device-type"
+#define	isdigit(ch)			((ch) >= '0' && (ch) <= '9')
+
+static struct cb_ops vhci_cb_ops = {
+	vhci_open,			/* open */
+	vhci_close,			/* close */
+	nodev,				/* strategy */
+	nodev,				/* print */
+	nodev,				/* dump */
+	nodev,				/* read */
+	nodev,				/* write */
+	vhci_ioctl,			/* ioctl */
+	nodev,				/* devmap */
+	nodev,				/* mmap */
+	nodev,				/* segmap */
+	nochpoll,			/* chpoll */
+	ddi_prop_op,			/* cb_prop_op */
+	0,				/* streamtab */
+	D_NEW | D_MP,			/* cb_flag */
+	CB_REV,				/* rev */
+	nodev,				/* aread */
+	nodev				/* awrite */
+};
+
+static struct dev_ops vhci_ops = {
+	DEVO_REV,
+	0,
+	vhci_getinfo,
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	vhci_attach,		/* attach and detach are mandatory */
+	vhci_detach,
+	nodev,			/* reset */
+	&vhci_cb_ops,		/* cb_ops */
+	NULL,			/* bus_ops */
+	NULL,			/* power */
+};
+
+extern struct mod_ops mod_driverops;
+
+static struct modldrv modldrv = {
+	&mod_driverops,
+	vhci_version_name,	/* module name */
+	&vhci_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL
+};
+
+static mdi_vhci_ops_t vhci_opinfo = {
+	MDI_VHCI_OPS_REV,
+	vhci_pathinfo_init,		/* Pathinfo node init callback	*/
+	vhci_pathinfo_uninit,		/* Pathinfo uninit callback	*/
+	vhci_pathinfo_state_change,	/* Pathinfo node state change	*/
+	vhci_failover,			/* failover callback		*/
+	vhci_client_attached		/* client attached callback	*/
+};
+
+/*
+ * The scsi_failover table defines an ordered set of 'fops' modules supported
+ * by scsi_vhci.  Currently, initialize this table from the 'ddi-forceload'
+ * property specified in scsi_vhci.conf.
+ */
+struct scsi_failover {
+	ddi_modhandle_t			sf_mod;
+	struct scsi_failover_ops	*sf_sfo;
+} *scsi_failover_table;
+uint_t	scsi_nfailover;
+
+int
+_init(void)
+{
+	int	rval;
+
+	/*
+	 * Allocate soft state and prepare to do ddi_soft_state_zalloc()
+	 * before registering with the transport first.
+	 */
+	if ((rval = ddi_soft_state_init(&vhci_softstate,
+	    sizeof (struct scsi_vhci), 1)) != 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!_init:soft state init failed\n"));
+		return (rval);
+	}
+
+	if ((rval = scsi_hba_init(&modlinkage)) != 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!_init: scsi hba init failed\n"));
+		ddi_soft_state_fini(&vhci_softstate);
+		return (rval);
+	}
+
+	mutex_init(&vhci_global_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&vhci_cv, NULL, CV_DRIVER, NULL);
+
+	mutex_init(&vhci_targetmap_mutex, NULL, MUTEX_DRIVER, NULL);
+	vhci_targetmap_byport = mod_hash_create_strhash(
+	    "vhci_targetmap_byport", 256, mod_hash_null_valdtor);
+	vhci_targetmap_bypid = mod_hash_create_idhash(
+	    "vhci_targetmap_bypid", 256, mod_hash_null_valdtor);
+
+	if ((rval = mod_install(&modlinkage)) != 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!_init: mod_install failed\n"));
+		if (vhci_targetmap_bypid)
+			mod_hash_destroy_idhash(vhci_targetmap_bypid);
+		if (vhci_targetmap_byport)
+			mod_hash_destroy_strhash(vhci_targetmap_byport);
+		mutex_destroy(&vhci_targetmap_mutex);
+		cv_destroy(&vhci_cv);
+		mutex_destroy(&vhci_global_mutex);
+		scsi_hba_fini(&modlinkage);
+		ddi_soft_state_fini(&vhci_softstate);
+	}
+	return (rval);
+}
+
+
+/*
+ * the system is done with us as a driver, so clean up
+ */
+int
+_fini(void)
+{
+	int rval;
+
+	/*
+	 * don't start cleaning up until we know that the module remove
+	 * has worked  -- if this works, then we know that each instance
+	 * has successfully been DDI_DETACHed
+	 */
+	if ((rval = mod_remove(&modlinkage)) != 0) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "!_fini: mod_remove failed\n"));
+		return (rval);
+	}
+
+	if (vhci_targetmap_bypid)
+		mod_hash_destroy_idhash(vhci_targetmap_bypid);
+	if (vhci_targetmap_byport)
+		mod_hash_destroy_strhash(vhci_targetmap_byport);
+	mutex_destroy(&vhci_targetmap_mutex);
+	cv_destroy(&vhci_cv);
+	mutex_destroy(&vhci_global_mutex);
+	scsi_hba_fini(&modlinkage);
+	ddi_soft_state_fini(&vhci_softstate);
+
+	return (rval);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Lookup scsi_failover by "short name" of failover module.
+ */
+struct scsi_failover_ops *
+vhci_failover_ops_by_name(char *name)
+{
+	struct scsi_failover	*sf;
+
+	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
+		if (sf->sf_sfo == NULL)
+			continue;
+		if (strcmp(sf->sf_sfo->sfo_name, name) == 0)
+			return (sf->sf_sfo);
+	}
+	return (NULL);
+}
+
+/*
+ * Load all scsi_failover_ops 'fops' modules.
+ */
+static void
+vhci_failover_modopen(struct scsi_vhci *vhci)
+{
+	char			**module;
+	int			i;
+	struct scsi_failover	*sf;
+	char			**dt;
+	int			e;
+
+	if (scsi_failover_table)
+		return;
+
+	/* Get the list of modules from scsi_vhci.conf */
+	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY,
+	    vhci->vhci_dip, DDI_PROP_DONTPASS, "ddi-forceload",
+	    &module, &scsi_nfailover) != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN, "scsi_vhci: "
+		    "scsi_vhci.conf is missing 'ddi-forceload'");
+		return;
+	}
+	if (scsi_nfailover == 0) {
+		cmn_err(CE_WARN, "scsi_vhci: "
+		    "scsi_vhci.conf has empty 'ddi-forceload'");
+		return;
+	}
+
+	/* allocate failover table based on number of modules */
+	scsi_failover_table = (struct scsi_failover *)
+	    kmem_zalloc(sizeof (struct scsi_failover) * (scsi_nfailover + 1),
+	    KM_SLEEP);
+
+	/* loop over modules specified in scsi_vhci.conf and open each module */
+	for (i = 0, sf = scsi_failover_table; i < scsi_nfailover; i++) {
+		if (module[i] == NULL)
+			continue;
+
+		sf->sf_mod = ddi_modopen(module[i], KRTLD_MODE_FIRST, &e);
+		if (sf->sf_mod == NULL) {
+			/*
+			 * A module returns EEXIST if other software is
+			 * supporting the intended function: for example
+			 * the scsi_vhci_f_sum_emc module returns EEXIST
+			 * from _init if EMC powerpath software is installed.
+			 */
+			if (e != EEXIST)
+				cmn_err(CE_WARN, "scsi_vhci: unable to open "
+				    "module '%s', error %d", module[i], e);
+			continue;
+		}
+		sf->sf_sfo = ddi_modsym(sf->sf_mod,
+		    "scsi_vhci_failover_ops", &e);
+		if (sf->sf_sfo == NULL) {
+			cmn_err(CE_WARN, "scsi_vhci: "
+			    "unable to import 'scsi_failover_ops' from '%s', "
+			    "error %d", module[i], e);
+			(void) ddi_modclose(sf->sf_mod);
+			sf->sf_mod = NULL;
+			continue;
+		}
+
+		/* 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++;
+	}
+
+	/* verify that at least the "well-known" modules were there */
+	if (vhci_failover_ops_by_name(SFO_NAME_SYM) == NULL)
+		cmn_err(CE_WARN, "scsi_vhci: well-known module \""
+		    SFO_NAME_SYM "\" not defined in scsi_vhci.conf's "
+		    "'ddi-forceload'");
+	if (vhci_failover_ops_by_name(SFO_NAME_TPGS) == NULL)
+		cmn_err(CE_WARN, "scsi_vhci: well-known module \""
+		    SFO_NAME_TPGS "\" not defined in scsi_vhci.conf's "
+		    "'ddi-forceload'");
+
+	/* 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)();
+	}
+}
+
+/*
+ * unload all loaded scsi_failover_ops modules
+ */
+static void
+vhci_failover_modclose()
+{
+	struct scsi_failover	*sf;
+
+	for (sf = scsi_failover_table; sf->sf_mod; sf++) {
+		if ((sf->sf_mod == NULL) || (sf->sf_sfo == NULL))
+			continue;
+		(void) ddi_modclose(sf->sf_mod);
+		sf->sf_mod = NULL;
+		sf->sf_sfo = NULL;
+	}
+
+	if (scsi_failover_table && scsi_nfailover)
+		kmem_free(scsi_failover_table,
+		    sizeof (struct scsi_failover) * (scsi_nfailover + 1));
+	scsi_failover_table = NULL;
+	scsi_nfailover = 0;
+}
+
+/* ARGSUSED */
+static int
+vhci_open(dev_t *devp, int flag, int otype, cred_t *credp)
+{
+	struct scsi_vhci	*vhci;
+
+	if (otype != OTYP_CHR) {
+		return (EINVAL);
+	}
+
+	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(*devp)));
+	if (vhci == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_open: failed ENXIO\n"));
+		return (ENXIO);
+	}
+
+	mutex_enter(&vhci->vhci_mutex);
+	if ((flag & FEXCL) && (vhci->vhci_state & VHCI_STATE_OPEN)) {
+		mutex_exit(&vhci->vhci_mutex);
+		vhci_log(CE_NOTE, vhci->vhci_dip,
+		    "!vhci%d: Already open\n", getminor(*devp));
+		return (EBUSY);
+	}
+
+	vhci->vhci_state |= VHCI_STATE_OPEN;
+	mutex_exit(&vhci->vhci_mutex);
+	return (0);
+}
+
+
+/* ARGSUSED */
+static int
+vhci_close(dev_t dev, int flag, int otype, cred_t *credp)
+{
+	struct scsi_vhci	*vhci;
+
+	if (otype != OTYP_CHR) {
+		return (EINVAL);
+	}
+
+	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
+	if (vhci == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_close: failed ENXIO\n"));
+		return (ENXIO);
+	}
+
+	mutex_enter(&vhci->vhci_mutex);
+	vhci->vhci_state &= ~VHCI_STATE_OPEN;
+	mutex_exit(&vhci->vhci_mutex);
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+vhci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
+	cred_t *credp, int *rval)
+{
+	if (IS_DEVCTL(cmd)) {
+		return (vhci_devctl(dev, cmd, data, mode, credp, rval));
+	} else if (cmd == MP_CMD) {
+		return (vhci_mpapi_ctl(dev, cmd, data, mode, credp, rval));
+	} else {
+		return (vhci_ctl(dev, cmd, data, mode, credp, rval));
+	}
+}
+
+/*
+ * attach the module
+ */
+static int
+vhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int			rval = DDI_FAILURE;
+	int			scsi_hba_attached = 0;
+	int			vhci_attached = 0;
+	int			mutex_initted = 0;
+	int			instance;
+	struct scsi_vhci	*vhci;
+	scsi_hba_tran_t		*tran;
+	char			cache_name_buf[64];
+	char			*data;
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_attach: cmd=0x%x\n", cmd));
+
+	instance = ddi_get_instance(dip);
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		break;
+
+	case DDI_RESUME:
+	case DDI_PM_RESUME:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_attach: resume not yet"
+		    "implemented\n"));
+		return (rval);
+
+	default:
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!vhci_attach: unknown ddi command\n"));
+		return (rval);
+	}
+
+	/*
+	 * Allocate vhci data structure.
+	 */
+	if (ddi_soft_state_zalloc(vhci_softstate, instance) != DDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
+		    "soft state alloc failed\n"));
+		return (DDI_FAILURE);
+	}
+
+	if ((vhci = ddi_get_soft_state(vhci_softstate, instance)) == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
+		    "bad soft state\n"));
+		ddi_soft_state_free(vhci_softstate, instance);
+		return (DDI_FAILURE);
+	}
+
+	/* Allocate packet cache */
+	(void) snprintf(cache_name_buf, sizeof (cache_name_buf),
+	    "vhci%d_cache", instance);
+
+	mutex_init(&vhci->vhci_mutex, NULL, MUTEX_DRIVER, NULL);
+	mutex_initted++;
+
+	/*
+	 * Allocate a transport structure
+	 */
+	tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
+	ASSERT(tran != NULL);
+
+	vhci->vhci_tran		= tran;
+	vhci->vhci_dip		= dip;
+	vhci->vhci_instance	= instance;
+
+	tran->tran_hba_private	= vhci;
+	tran->tran_tgt_private	= NULL;
+	tran->tran_tgt_init	= vhci_scsi_tgt_init;
+	tran->tran_tgt_probe	= NULL;
+	tran->tran_tgt_free	= vhci_scsi_tgt_free;
+
+	tran->tran_start	= vhci_scsi_start;
+	tran->tran_abort	= vhci_scsi_abort;
+	tran->tran_reset	= vhci_scsi_reset;
+	tran->tran_getcap	= vhci_scsi_getcap;
+	tran->tran_setcap	= vhci_scsi_setcap;
+	tran->tran_init_pkt	= vhci_scsi_init_pkt;
+	tran->tran_destroy_pkt	= vhci_scsi_destroy_pkt;
+	tran->tran_dmafree	= vhci_scsi_dmafree;
+	tran->tran_sync_pkt	= vhci_scsi_sync_pkt;
+	tran->tran_reset_notify = vhci_scsi_reset_notify;
+
+	tran->tran_get_bus_addr	= vhci_scsi_get_bus_addr;
+	tran->tran_get_name	= vhci_scsi_get_name;
+	tran->tran_bus_reset	= NULL;
+	tran->tran_quiesce	= NULL;
+	tran->tran_unquiesce	= NULL;
+
+	/*
+	 * register event notification routines with scsa
+	 */
+	tran->tran_get_eventcookie = NULL;
+	tran->tran_add_eventcall = NULL;
+	tran->tran_remove_eventcall = NULL;
+	tran->tran_post_event = NULL;
+
+	tran->tran_bus_power = vhci_scsi_bus_power;
+
+	tran->tran_bus_config = vhci_scsi_bus_config;
+
+	/*
+	 * Attach this instance with the mpxio framework
+	 */
+	if (mdi_vhci_register(MDI_HCI_CLASS_SCSI, dip, &vhci_opinfo, 0)
+	    != MDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
+		    "mdi_vhci_register failed\n"));
+		goto attach_fail;
+	}
+	vhci_attached++;
+
+	/*
+	 * Attach this instance of the hba.
+	 *
+	 * Regarding dma attributes: Since scsi_vhci is a virtual scsi HBA
+	 * driver, it has nothing to do with DMA. However, when calling
+	 * scsi_hba_attach_setup() we need to pass something valid in the
+	 * dma attributes parameter. So we just use scsi_alloc_attr.
+	 * SCSA itself seems to care only for dma_attr_minxfer and
+	 * dma_attr_burstsizes fields of dma attributes structure.
+	 * It expects those fileds to be non-zero.
+	 */
+	if (scsi_hba_attach_setup(dip, &scsi_alloc_attr, tran,
+	    SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
+		    "hba attach failed\n"));
+		goto attach_fail;
+	}
+	scsi_hba_attached++;
+
+	if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
+	    INST2DEVCTL(instance), DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_NOTE, dip, "!vhci_attach:"
+		    " ddi_create_minor_node failed\n"));
+		goto attach_fail;
+	}
+
+	/*
+	 * Set pm-want-child-notification property for
+	 * power management of the phci and client
+	 */
+	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+	    "pm-want-child-notification?", NULL, NULL) != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN,
+		    "%s%d fail to create pm-want-child-notification? prop",
+		    ddi_driver_name(dip), ddi_get_instance(dip));
+		goto attach_fail;
+	}
+
+	vhci->vhci_taskq = taskq_create("vhci_taskq", 1, MINCLSYSPRI, 1, 4, 0);
+	vhci->vhci_update_pathstates_taskq =
+	    taskq_create("vhci_update_pathstates", VHCI_NUM_UPDATE_TASKQ,
+	    MINCLSYSPRI, 1, 4, 0);
+	ASSERT(vhci->vhci_taskq);
+	ASSERT(vhci->vhci_update_pathstates_taskq);
+
+	/*
+	 * Set appropriate configuration flags based on options set in
+	 * conf file.
+	 */
+	vhci->vhci_conf_flags = 0;
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, PROPFLAGS,
+	    "auto-failback", &data) == DDI_SUCCESS) {
+		if (strcmp(data, "enable") == 0)
+			vhci->vhci_conf_flags |= VHCI_CONF_FLAGS_AUTO_FAILBACK;
+		ddi_prop_free(data);
+	}
+
+	if (!(vhci->vhci_conf_flags & VHCI_CONF_FLAGS_AUTO_FAILBACK))
+		vhci_log(CE_NOTE, dip, "!Auto-failback capability "
+		    "disabled through scsi_vhci.conf file.");
+
+	/*
+	 * Allocate an mpapi private structure
+	 */
+	vhci->mp_priv = kmem_zalloc(sizeof (mpapi_priv_t), KM_SLEEP);
+	if (vhci_mpapi_init(vhci) != 0) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_attach: "
+		    "vhci_mpapi_init() failed"));
+	}
+
+	vhci_failover_modopen(vhci);		/* load failover modules */
+
+	ddi_report_dev(dip);
+	return (DDI_SUCCESS);
+
+attach_fail:
+	if (vhci_attached)
+		(void) mdi_vhci_unregister(dip, 0);
+
+	if (scsi_hba_attached)
+		(void) scsi_hba_detach(dip);
+
+	if (vhci->vhci_tran)
+		scsi_hba_tran_free(vhci->vhci_tran);
+
+	if (mutex_initted) {
+		mutex_destroy(&vhci->vhci_mutex);
+	}
+
+	ddi_soft_state_free(vhci_softstate, instance);
+	return (DDI_FAILURE);
+}
+
+
+/*ARGSUSED*/
+static int
+vhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	int			instance = ddi_get_instance(dip);
+	scsi_hba_tran_t		*tran;
+	struct scsi_vhci	*vhci;
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_detach: cmd=0x%x\n", cmd));
+
+	if ((tran = ddi_get_driver_private(dip)) == NULL)
+		return (DDI_FAILURE);
+
+	vhci = TRAN2HBAPRIVATE(tran);
+	if (!vhci) {
+		return (DDI_FAILURE);
+	}
+
+	switch (cmd) {
+	case DDI_DETACH:
+		break;
+
+	case DDI_SUSPEND:
+	case DDI_PM_SUSPEND:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_detach: suspend/pm not yet"
+		    "implemented\n"));
+		return (DDI_FAILURE);
+
+	default:
+		VHCI_DEBUG(1, (CE_NOTE, NULL,
+		    "!vhci_detach: unknown ddi command\n"));
+		return (DDI_FAILURE);
+	}
+
+	(void) mdi_vhci_unregister(dip, 0);
+	(void) scsi_hba_detach(dip);
+	scsi_hba_tran_free(tran);
+
+	if (ddi_prop_remove(DDI_DEV_T_NONE, dip,
+	    "pm-want-child-notification?") != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN,
+		    "%s%d unable to remove prop pm-want_child_notification?",
+		    ddi_driver_name(dip), ddi_get_instance(dip));
+	}
+	if (vhci_restart_timeid != 0) {
+		(void) untimeout(vhci_restart_timeid);
+	}
+	vhci_restart_timeid = 0;
+
+	mutex_destroy(&vhci->vhci_mutex);
+	vhci->vhci_dip = NULL;
+	vhci->vhci_tran = NULL;
+	taskq_destroy(vhci->vhci_taskq);
+	taskq_destroy(vhci->vhci_update_pathstates_taskq);
+	ddi_remove_minor_node(dip, NULL);
+	ddi_soft_state_free(vhci_softstate, instance);
+
+	vhci_failover_modclose();		/* unload failover modules */
+	return (DDI_SUCCESS);
+}
+
+/*
+ * vhci_getinfo()
+ * Given the device number, return the devinfo pointer or the
+ * instance number.
+ * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
+ */
+
+/*ARGSUSED*/
+static int
+vhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+	struct scsi_vhci	*vhcip;
+	int			instance = MINOR2INST(getminor((dev_t)arg));
+
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		vhcip = ddi_get_soft_state(vhci_softstate, instance);
+		if (vhcip != NULL)
+			*result = vhcip->vhci_dip;
+		else {
+			*result = NULL;
+			return (DDI_FAILURE);
+		}
+		break;
+
+	case DDI_INFO_DEVT2INSTANCE:
+		*result = (void *)(uintptr_t)instance;
+		break;
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+
+/*ARGSUSED*/
+static int
+vhci_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+	scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+	char			*guid;
+	scsi_vhci_lun_t		*vlun;
+	struct scsi_vhci	*vhci;
+	clock_t			from_ticks;
+	mdi_pathinfo_t		*pip;
+	int			rval;
+
+	ASSERT(hba_dip != NULL);
+	ASSERT(tgt_dip != NULL);
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(hba_dip));
+	ASSERT(vhci != NULL);
+
+	VHCI_DEBUG(4, (CE_NOTE, hba_dip,
+	    "!tgt_init: called for %s (instance %d)\n",
+	    ddi_driver_name(tgt_dip), ddi_get_instance(tgt_dip)));
+
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip, PROPFLAGS,
+	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
+		/*
+		 * This must be the .conf node.  The ssd node under
+		 * fp already inserts a delay, so we just return from here.
+		 * We rely on this delay to have all dips be posted to
+		 * the ndi hotplug thread's newdev list.  This is
+		 * necessary for the deferred attach mechanism to work
+		 * and opens() done soon after boot to succeed.
+		 */
+		VHCI_DEBUG(4, (CE_WARN, hba_dip, "tgt_init: lun guid "
+		    "property failed"));
+		return (DDI_NOT_WELL_FORMED);
+	}
+
+	vlun = vhci_lun_lookup(tgt_dip);
+
+	mutex_enter(&vhci_global_mutex);
+
+	from_ticks = ddi_get_lbolt();
+	if (vhci_to_ticks == 0) {
+		vhci_to_ticks = from_ticks +
+		    drv_usectohz(vhci_init_wait_timeout);
+	}
+
+#if DEBUG
+	if (vlun) {
+		VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
+		    "vhci_scsi_tgt_init: guid %s : found vlun 0x%p "
+		    "from_ticks %lx to_ticks %lx",
+		    guid, (void *)vlun, from_ticks, vhci_to_ticks));
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
+		    "vhci_scsi_tgt_init: guid %s : vlun not found "
+		    "from_ticks %lx to_ticks %lx", guid, from_ticks,
+		    vhci_to_ticks));
+	}
+#endif
+
+	rval = mdi_select_path(tgt_dip, NULL,
+	    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH), NULL, &pip);
+	if (rval == MDI_SUCCESS) {
+		mdi_rele_path(pip);
+	}
+
+	/*
+	 * Wait for the following conditions :
+	 *	1. no vlun available yet
+	 *	2. no path established
+	 *	3. timer did not expire
+	 */
+	while ((vlun == NULL) || (mdi_client_get_path_count(tgt_dip) == 0) ||
+	    (rval != MDI_SUCCESS)) {
+		if (vlun && vlun->svl_not_supported) {
+			VHCI_DEBUG(1, (CE_WARN, hba_dip, "tgt_init: "
+			    "vlun 0x%p lun guid %s not supported!",
+			    (void *)vlun, guid));
+			mutex_exit(&vhci_global_mutex);
+			ddi_prop_free(guid);
+			return (DDI_NOT_WELL_FORMED);
+		}
+		if ((vhci_first_time == 0) && (from_ticks >= vhci_to_ticks)) {
+			vhci_first_time = 1;
+		}
+		if (vhci_first_time == 1) {
+			VHCI_DEBUG(1, (CE_WARN, hba_dip, "vhci_scsi_tgt_init: "
+			    "no wait for %s. from_tick %lx, to_tick %lx",
+			    guid, from_ticks, vhci_to_ticks));
+			mutex_exit(&vhci_global_mutex);
+			ddi_prop_free(guid);
+			return (DDI_NOT_WELL_FORMED);
+		}
+
+		if (cv_timedwait(&vhci_cv,
+		    &vhci_global_mutex, vhci_to_ticks) == -1) {
+			/* Timed out */
+#ifdef DEBUG
+			if (vlun == NULL) {
+				VHCI_DEBUG(1, (CE_WARN, hba_dip,
+				    "tgt_init: no vlun for %s!", guid));
+			} else if (mdi_client_get_path_count(tgt_dip) == 0) {
+				VHCI_DEBUG(1, (CE_WARN, hba_dip,
+				    "tgt_init: client path count is "
+				    "zero for %s!", guid));
+			} else {
+				VHCI_DEBUG(1, (CE_WARN, hba_dip,
+				    "tgt_init: client path not "
+				    "available yet for %s!", guid));
+			}
+#endif /* DEBUG */
+			mutex_exit(&vhci_global_mutex);
+			ddi_prop_free(guid);
+			return (DDI_NOT_WELL_FORMED);
+		}
+		vlun = vhci_lun_lookup(tgt_dip);
+		rval = mdi_select_path(tgt_dip, NULL,
+		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+		    NULL, &pip);
+		if (rval == MDI_SUCCESS) {
+			mdi_rele_path(pip);
+		}
+		from_ticks = ddi_get_lbolt();
+	}
+	mutex_exit(&vhci_global_mutex);
+
+	ASSERT(vlun != NULL);
+	ddi_prop_free(guid);
+	hba_tran->tran_tgt_private = vlun;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static void
+vhci_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+	scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
+{
+}
+
+/*
+ * a PGR register command has started; copy the info we need
+ */
+int
+vhci_pgr_register_start(scsi_vhci_lun_t *vlun, struct scsi_pkt *pkt)
+{
+	struct vhci_pkt		*vpkt = TGTPKT2VHCIPKT(pkt);
+	void			*addr;
+
+	if (!vpkt->vpkt_tgt_init_bp)
+		return (TRAN_BADPKT);
+
+	addr = bp_mapin_common(vpkt->vpkt_tgt_init_bp,
+	    (vpkt->vpkt_flags & CFLAG_NOWAIT) ? VM_NOSLEEP : VM_SLEEP);
+	if (addr == NULL)
+		return (TRAN_BUSY);
+
+	mutex_enter(&vlun->svl_mutex);
+
+	vhci_print_prout_keys(vlun, "v_pgr_reg_start: before bcopy:");
+
+	bcopy(addr, &vlun->svl_prout, sizeof (vhci_prout_t) -
+	    (2 * MHIOC_RESV_KEY_SIZE*sizeof (char)));
+	bcopy(pkt->pkt_cdbp, vlun->svl_cdb, sizeof (vlun->svl_cdb));
+
+	vhci_print_prout_keys(vlun, "v_pgr_reg_start: after bcopy:");
+
+	vlun->svl_time = pkt->pkt_time;
+	vlun->svl_bcount = vpkt->vpkt_tgt_init_bp->b_bcount;
+	vlun->svl_first_path = vpkt->vpkt_path;
+	mutex_exit(&vlun->svl_mutex);
+	return (0);
+}
+
+/*
+ * Function name : vhci_scsi_start()
+ *
+ * Return Values : TRAN_FATAL_ERROR	- vhci has been shutdown
+ *					  or other fatal failure
+ *					  preventing packet transportation
+ *		   TRAN_BUSY		- request queue is full
+ *		   TRAN_ACCEPT		- pkt has been submitted to phci
+ *					  (or is held in the waitQ)
+ * Description	 : Implements SCSA's tran_start() entry point for
+ *		   packet transport
+ *
+ */
+static int
+vhci_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	int			rval = TRAN_ACCEPT;
+	int			instance, held;
+	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
+	struct scsi_vhci_lun	*vlun = ADDR2VLUN(ap);
+	struct vhci_pkt		*vpkt = TGTPKT2VHCIPKT(pkt);
+	int			flags = 0;
+	scsi_vhci_priv_t	*svp;
+	dev_info_t 		*cdip;
+	client_lb_t		lbp;
+	int			restore_lbp = 0;
+	/* set if pkt is SCSI-II RESERVE cmd */
+	int			pkt_reserve_cmd = 0;
+	int			reserve_failed = 0;
+
+	ASSERT(vhci != NULL);
+	ASSERT(vpkt != NULL);
+	ASSERT(vpkt->vpkt_state != VHCI_PKT_ISSUED);
+	cdip = ADDR2DIP(ap);
+
+	/*
+	 * Block IOs if LUN is held or QUIESCED for IOs.
+	 */
+	if ((VHCI_LUN_IS_HELD(vlun)) ||
+	    ((vlun->svl_flags & VLUN_QUIESCED_FLG) == VLUN_QUIESCED_FLG)) {
+		return (TRAN_BUSY);
+	}
+
+	/*
+	 * vhci_lun needs to be quiesced before SCSI-II RESERVE command
+	 * can be issued.  This may require a cv_timedwait, which is
+	 * dangerous to perform in an interrupt context.  So if this
+	 * is a RESERVE command a taskq is dispatched to service it.
+	 * This taskq shall again call vhci_scsi_start, but we shall be
+	 * sure its not in an interrupt context.
+	 */
+	if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
+	    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
+		if (!(vpkt->vpkt_state & VHCI_PKT_THRU_TASKQ)) {
+			if (taskq_dispatch(vhci->vhci_taskq,
+			    vhci_dispatch_scsi_start, (void *) vpkt,
+			    KM_NOSLEEP)) {
+				return (TRAN_ACCEPT);
+			} else {
+				return (TRAN_BUSY);
+			}
+		}
+
+		/*
+		 * Here we ensure that simultaneous SCSI-II RESERVE cmds don't
+		 * get serviced for a lun.
+		 */
+		VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
+		if (!held) {
+			return (TRAN_BUSY);
+		} else if ((vlun->svl_flags & VLUN_QUIESCED_FLG) ==
+		    VLUN_QUIESCED_FLG) {
+			VHCI_RELEASE_LUN(vlun);
+			return (TRAN_BUSY);
+		}
+
+		/*
+		 * To ensure that no IOs occur for this LUN for the duration
+		 * of this pkt set the VLUN_QUIESCED_FLG.
+		 * In case this routine needs to exit on error make sure that
+		 * this flag is cleared.
+		 */
+		vlun->svl_flags |= VLUN_QUIESCED_FLG;
+		pkt_reserve_cmd = 1;
+
+		/*
+		 * if this is a SCSI-II RESERVE command, set load balancing
+		 * policy to be ALTERNATE PATH to ensure that all subsequent
+		 * IOs are routed on the same path.  This is because if commands
+		 * are routed across multiple paths then IOs on paths other than
+		 * the one on which the RESERVE was executed will get a
+		 * RESERVATION CONFLICT
+		 */
+		lbp = mdi_get_lb_policy(cdip);
+		if (lbp != LOAD_BALANCE_NONE) {
+			if (vhci_quiesce_lun(vlun) != 1) {
+				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+				VHCI_RELEASE_LUN(vlun);
+				return (TRAN_FATAL_ERROR);
+			}
+			vlun->svl_lb_policy_save = lbp;
+			if (mdi_set_lb_policy(cdip, LOAD_BALANCE_NONE) !=
+			    MDI_SUCCESS) {
+				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+				VHCI_RELEASE_LUN(vlun);
+				return (TRAN_FATAL_ERROR);
+			}
+			restore_lbp = 1;
+		}
+		/*
+		 * See comments for VLUN_RESERVE_ACTIVE_FLG in scsi_vhci.h
+		 * To narrow this window where a reserve command may be sent
+		 * down an inactive path the path states first need to be
+		 * updated. Before calling vhci_update_pathstates reset
+		 * VLUN_RESERVE_ACTIVE_FLG, just in case it was already set
+		 * for this lun.  This shall prevent an unnecessary reset
+		 * from being sent out.
+		 */
+		vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
+		vhci_update_pathstates((void *)vlun);
+	}
+
+	instance = ddi_get_instance(vhci->vhci_dip);
+
+	/*
+	 * If the command is PRIN with action of zero, then the cmd
+	 * 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) &&
+	    (vpkt->vpkt_org_vpkt == NULL)) {
+		vpkt->vpkt_tgt_init_pkt_flags |= PKT_CONSISTENT;
+	}
+
+	/*
+	 * Do not defer bind for PKT_DMA_PARTIAL
+	 */
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+
+		/* This is a non pkt_dma_partial case */
+		if ((rval = vhci_bind_transport(
+		    ap, vpkt, vpkt->vpkt_tgt_init_pkt_flags, NULL_FUNC))
+		    != TRAN_ACCEPT) {
+			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
+			    "!vhci%d %x: failed to bind transport: "
+			    "vlun 0x%p pkt_reserved %x restore_lbp %x,"
+			    "lbp %x", instance, rval, (void *)vlun,
+			    pkt_reserve_cmd, restore_lbp, lbp));
+			if (restore_lbp)
+				(void) mdi_set_lb_policy(cdip, lbp);
+			if (pkt_reserve_cmd)
+				vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+			return (rval);
+		}
+		VHCI_DEBUG(8, (CE_NOTE, NULL,
+		    "vhci_scsi_start: v_b_t called 0x%p\n", (void *)vpkt));
+	}
+	ASSERT(vpkt->vpkt_hba_pkt != NULL);
+	ASSERT(vpkt->vpkt_path != NULL);
+
+	/*
+	 * This is the chance to adjust the pHCI's pkt and other information
+	 * from target driver's pkt.
+	 */
+	VHCI_DEBUG(8, (CE_NOTE, vhci->vhci_dip, "vhci_scsi_start vpkt %p\n",
+	    (void *)vpkt));
+	vhci_update_pHCI_pkt(vpkt, pkt);
+
+	if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
+		if (vpkt->vpkt_path != vlun->svl_resrv_pip) {
+			VHCI_DEBUG(1, (CE_WARN, vhci->vhci_dip,
+			    "!vhci_bind: reserve flag set for vlun 0x%p, but, "
+			    "pktpath 0x%p resrv path 0x%p differ. lb_policy %x",
+			    (void *)vlun, (void *)vpkt->vpkt_path,
+			    (void *)vlun->svl_resrv_pip,
+			    mdi_get_lb_policy(cdip)));
+			reserve_failed = 1;
+		}
+	}
+
+	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,
+			    "!vhci_bind returned null svp vlun 0x%p",
+			    (void *)vlun));
+			vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+			if (restore_lbp)
+				(void) mdi_set_lb_policy(cdip, lbp);
+		}
+pkt_cleanup:
+		if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+			scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
+			vpkt->vpkt_hba_pkt = NULL;
+			if (vpkt->vpkt_path) {
+				mdi_rele_path(vpkt->vpkt_path);
+				vpkt->vpkt_path = NULL;
+			}
+		}
+		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))) {
+			sema_v(&vlun->svl_pgr_sema);
+		}
+		return (TRAN_BUSY);
+	}
+
+	VHCI_INCR_PATH_CMDCOUNT(svp);
+
+	/*
+	 * Ensure that no other IOs raced ahead, while a RESERVE cmd was
+	 * QUIESCING the same lun.
+	 */
+	if ((!pkt_reserve_cmd) &&
+	    ((vlun->svl_flags & VLUN_QUIESCED_FLG) == VLUN_QUIESCED_FLG)) {
+		VHCI_DECR_PATH_CMDCOUNT(svp);
+		goto pkt_cleanup;
+	}
+
+	if ((pkt->pkt_cdbp[0] == SCMD_PRIN) ||
+	    (pkt->pkt_cdbp[0] == SCMD_PROUT)) {
+		/*
+		 * currently this thread only handles running PGR
+		 * commands, so don't bother creating it unless
+		 * something interesting is going to happen (like
+		 * either a PGR out, or a PGR in with enough space
+		 * to hold the keys that are getting returned)
+		 */
+		mutex_enter(&vlun->svl_mutex);
+		if (((vlun->svl_flags & VLUN_TASK_D_ALIVE_FLG) == 0) &&
+		    (pkt->pkt_cdbp[0] == SCMD_PROUT)) {
+			vlun->svl_taskq = taskq_create("vlun_pgr_task_daemon",
+			    1, MINCLSYSPRI, 1, 4, 0);
+			vlun->svl_flags |= VLUN_TASK_D_ALIVE_FLG;
+		}
+		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))) {
+			if (rval = vhci_pgr_register_start(vlun, pkt)) {
+				/* an error */
+				sema_v(&vlun->svl_pgr_sema);
+				return (rval);
+			}
+		}
+	}
+
+	/*
+	 * SCSI-II RESERVE cmd is not expected in polled mode.
+	 * If this changes it needs to be handled for the polled scenario.
+	 */
+	flags = vpkt->vpkt_hba_pkt->pkt_flags;
+	rval = scsi_transport(vpkt->vpkt_hba_pkt);
+	if (rval == TRAN_ACCEPT) {
+		if (flags & FLAG_NOINTR) {
+			struct scsi_pkt *tpkt = vpkt->vpkt_tgt_pkt;
+			struct scsi_pkt *pkt = vpkt->vpkt_hba_pkt;
+
+			ASSERT(tpkt != NULL);
+			*(tpkt->pkt_scbp) = *(pkt->pkt_scbp);
+			tpkt->pkt_resid = pkt->pkt_resid;
+			tpkt->pkt_state = pkt->pkt_state;
+			tpkt->pkt_statistics = pkt->pkt_statistics;
+			tpkt->pkt_reason = pkt->pkt_reason;
+
+			if ((*(pkt->pkt_scbp) == STATUS_CHECK) &&
+			    (pkt->pkt_state & STATE_ARQ_DONE)) {
+				bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
+				    vpkt->vpkt_tgt_init_scblen);
+			}
+
+			VHCI_DECR_PATH_CMDCOUNT(svp);
+			if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+				scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
+				vpkt->vpkt_hba_pkt = NULL;
+				if (vpkt->vpkt_path) {
+					mdi_rele_path(vpkt->vpkt_path);
+					vpkt->vpkt_path = NULL;
+				}
+			}
+			/*
+			 * This path will not automatically retry pkts
+			 * internally, therefore, vpkt_org_vpkt should
+			 * never be set.
+			 */
+			ASSERT(vpkt->vpkt_org_vpkt == NULL);
+			if (tpkt->pkt_comp) {
+				(*tpkt->pkt_comp)(tpkt);
+			}
+		}
+		return (rval);
+	} else 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))) {
+		/* the command exited with bad status */
+		sema_v(&vlun->svl_pgr_sema);
+	} else if (vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PRIN) {
+		/* the command exited with bad status */
+		sema_v(&vlun->svl_pgr_sema);
+	} else if (pkt_reserve_cmd) {
+		VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
+		    "!vhci_scsi_start: reserve failed vlun 0x%p",
+		    (void *)vlun));
+		vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+		if (restore_lbp)
+			(void) mdi_set_lb_policy(cdip, lbp);
+	}
+
+	ASSERT(vpkt->vpkt_hba_pkt != NULL);
+	VHCI_DECR_PATH_CMDCOUNT(svp);
+
+	/* Do not destroy phci packet information for PKT_DMA_PARTIAL */
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
+		vpkt->vpkt_hba_pkt = NULL;
+		if (vpkt->vpkt_path) {
+			MDI_PI_ERRSTAT(vpkt->vpkt_path, MDI_PI_TRANSERR);
+			mdi_rele_path(vpkt->vpkt_path);
+			vpkt->vpkt_path = NULL;
+		}
+	}
+	return (TRAN_BUSY);
+}
+
+/*
+ * Function name : vhci_scsi_reset()
+ *
+ * Return Values : 0 - reset failed
+ *		   1 - reset succeeded
+ */
+
+/* ARGSUSED */
+static int
+vhci_scsi_reset(struct scsi_address *ap, int level)
+{
+	int rval = 0;
+
+	cmn_err(CE_WARN, "!vhci_scsi_reset 0x%x", level);
+	if ((level == RESET_TARGET) || (level == RESET_LUN)) {
+		return (vhci_scsi_reset_target(ap, level, TRUE));
+	} else if (level == RESET_ALL) {
+		return (vhci_scsi_reset_bus(ap));
+	}
+
+	return (rval);
+}
+
+/*
+ * vhci_recovery_reset:
+ *	Issues reset to the device
+ * Input:
+ *	vlun - vhci lun pointer of the device
+ *	ap - address of the device
+ *	select_path:
+ *		If select_path is FALSE, then the address specified in ap is
+ *		the path on which reset will be issued.
+ *		If select_path is TRUE, then path is obtained by calling
+ *		mdi_select_path.
+ *
+ *	recovery_depth:
+ *		Caller can specify the level of reset.
+ *		VHCI_DEPTH_LUN -
+ *			Issues LUN RESET if device supports lun reset.
+ *		VHCI_DEPTH_TARGET -
+ *			If Lun Reset fails or the device does not support
+ *			Lun Reset, issues TARGET RESET
+ *		VHCI_DEPTH_ALL -
+ *			If Lun Reset fails or the device does not support
+ *			Lun Reset, issues TARGET RESET.
+ *			If TARGET RESET does not succeed, issues Bus Reset.
+ */
+
+static int
+vhci_recovery_reset(scsi_vhci_lun_t *vlun, struct scsi_address *ap,
+	uint8_t select_path, uint8_t recovery_depth)
+{
+	int	ret = 0;
+
+	ASSERT(ap != NULL);
+
+	if (vlun && vlun->svl_support_lun_reset == 1) {
+		ret = vhci_scsi_reset_target(ap, RESET_LUN,
+		    select_path);
+	}
+
+	recovery_depth--;
+
+	if ((ret == 0) && recovery_depth) {
+		ret = vhci_scsi_reset_target(ap, RESET_TARGET,
+		    select_path);
+		recovery_depth--;
+	}
+
+	if ((ret == 0) && recovery_depth) {
+		(void) scsi_reset(ap, RESET_ALL);
+	}
+
+	return (ret);
+}
+
+/*
+ * Note: The scsi_address passed to this routine could be the scsi_address
+ * for the virtual device or the physical device. No assumptions should be
+ * made in this routine about the ap structure and a_hba_tran->tran_tgt_private
+ * field of ap can not be assumed to be the vhci structure.
+ * Further note that the child dip would be the dip of the ssd node irrespective
+ * of the scsi_address passed.
+ */
+
+static int
+vhci_scsi_reset_target(struct scsi_address *ap, int level, uint8_t select_path)
+{
+	dev_info_t		*vdip, *pdip, *cdip = ADDR2DIP(ap);
+	mdi_pathinfo_t		*pip = NULL;
+	mdi_pathinfo_t		*npip = NULL;
+	int			rval = -1;
+	scsi_vhci_priv_t	*svp = NULL;
+	struct scsi_address	*pap = NULL;
+	scsi_hba_tran_t		*hba = NULL;
+	int			sps;
+	struct scsi_vhci	*vhci = NULL;
+
+	if (select_path != TRUE) {
+		ASSERT(ap != NULL);
+		if (level == RESET_LUN) {
+			hba = ap->a_hba_tran;
+			ASSERT(hba != NULL);
+			return ((*hba->tran_reset)(ap, RESET_LUN));
+		}
+		return (scsi_reset(ap, level));
+	}
+
+	ASSERT(cdip != NULL);
+	vdip = ddi_get_parent(cdip);
+	ASSERT(vdip != NULL);
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+	ASSERT(vhci != NULL);
+
+	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH, NULL, &pip);
+	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
+		    "Unable to get a path, dip 0x%p", (void *)cdip));
+		return (0);
+	}
+again:
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
+		    "priv is NULL, pip 0x%p", (void *)pip));
+		mdi_rele_path(pip);
+		return (0);
+	}
+
+	if (svp->svp_psd == NULL) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_scsi_reset_target: "
+		    "psd is NULL, pip 0x%p, svp 0x%p",
+		    (void *)pip, (void *)svp));
+		mdi_rele_path(pip);
+		return (0);
+	}
+
+	pap = &svp->svp_psd->sd_address;
+	hba = pap->a_hba_tran;
+
+	ASSERT(pap != NULL);
+	ASSERT(hba != NULL);
+
+	if (hba->tran_reset != NULL) {
+		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",
+			    ddi_driver_name(cdip), ddi_get_instance(cdip),
+			    ddi_driver_name(pdip), ddi_get_instance(pdip),
+			    level);
+
+			/*
+			 * Select next path and issue the reset, repeat
+			 * until all paths are exhausted
+			 */
+			sps = mdi_select_path(cdip, NULL,
+			    MDI_SELECT_ONLINE_PATH, pip, &npip);
+			if ((sps != MDI_SUCCESS) || (npip == NULL)) {
+				mdi_rele_path(pip);
+				return (0);
+			}
+			mdi_rele_path(pip);
+			pip = npip;
+			goto again;
+		}
+		mdi_rele_path(pip);
+		mutex_enter(&vhci->vhci_mutex);
+		scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
+		    &vhci->vhci_reset_notify_listf);
+		mutex_exit(&vhci->vhci_mutex);
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_scsi_reset_target: "
+		    "reset %d sent down pip:%p for cdip:%p\n", level,
+		    (void *)pip, (void *)cdip));
+		return (1);
+	}
+	mdi_rele_path(pip);
+	return (0);
+}
+
+
+/* ARGSUSED */
+static int
+vhci_scsi_reset_bus(struct scsi_address *ap)
+{
+	return (1);
+}
+
+
+/*
+ * called by vhci_getcap and vhci_setcap to get and set (respectively)
+ * SCSI capabilities
+ */
+/* ARGSUSED */
+static int
+vhci_commoncap(struct scsi_address *ap, char *cap,
+    int val, int tgtonly, int doset)
+{
+	struct scsi_vhci		*vhci = ADDR2VHCI(ap);
+	struct scsi_vhci_lun		*vlun = ADDR2VLUN(ap);
+	int			cidx;
+	int			rval = 0;
+
+	if (cap == (char *)0) {
+		VHCI_DEBUG(3, (CE_WARN, vhci->vhci_dip,
+		    "!vhci_commoncap: invalid arg"));
+		return (rval);
+	}
+
+	if ((cidx = scsi_hba_lookup_capstr(cap)) == -1) {
+		return (UNDEFINED);
+	}
+
+	/*
+	 * Process setcap request.
+	 */
+	if (doset) {
+		/*
+		 * At present, we can only set binary (0/1) values
+		 */
+		switch (cidx) {
+		case SCSI_CAP_ARQ:
+			if (val == 0) {
+				rval = 0;
+			} else {
+				rval = 1;
+			}
+			break;
+
+		case SCSI_CAP_LUN_RESET:
+			if (tgtonly == 0) {
+				VHCI_DEBUG(1, (CE_WARN, vhci->vhci_dip,
+				    "scsi_vhci_setcap: "
+				    "Returning error since whom = 0"));
+				rval = -1;
+				break;
+			}
+			/*
+			 * Set the capability accordingly.
+			 */
+			mutex_enter(&vlun->svl_mutex);
+			vlun->svl_support_lun_reset = val;
+			rval = val;
+			mutex_exit(&vlun->svl_mutex);
+			break;
+
+		case SCSI_CAP_SECTOR_SIZE:
+			mutex_enter(&vlun->svl_mutex);
+			vlun->svl_sector_size = val;
+			vlun->svl_setcap_done = 1;
+			mutex_exit(&vlun->svl_mutex);
+			(void) vhci_pHCI_cap(ap, cap, val, tgtonly, NULL);
+
+			/* Always return success */
+			rval = 1;
+			break;
+
+		default:
+			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
+			    "!vhci_setcap: unsupported %d", cidx));
+			rval = UNDEFINED;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_NOTE, vhci->vhci_dip,
+		    "!set cap: cap=%s, val/tgtonly/doset/rval = "
+		    "0x%x/0x%x/0x%x/%d\n",
+		    cap, val, tgtonly, doset, rval));
+
+	} else {
+		/*
+		 * Process getcap request.
+		 */
+		switch (cidx) {
+		case SCSI_CAP_DMA_MAX:
+			rval = (int)VHCI_DMA_MAX_XFER_CAP;
+			break;
+
+		case SCSI_CAP_INITIATOR_ID:
+			rval = 0x00;
+			break;
+
+		case SCSI_CAP_ARQ:
+		case SCSI_CAP_RESET_NOTIFICATION:
+		case SCSI_CAP_TAGGED_QING:
+			rval = 1;
+			break;
+
+		case SCSI_CAP_SCSI_VERSION:
+			rval = 3;
+			break;
+
+		case SCSI_CAP_INTERCONNECT_TYPE:
+			rval = INTERCONNECT_FABRIC;
+			break;
+
+		case SCSI_CAP_LUN_RESET:
+			/*
+			 * scsi_vhci will always return success for LUN reset.
+			 * When request for doing LUN reset comes
+			 * through scsi_reset entry point, at that time attempt
+			 * will be made to do reset through all the possible
+			 * paths.
+			 */
+			mutex_enter(&vlun->svl_mutex);
+			rval = vlun->svl_support_lun_reset;
+			mutex_exit(&vlun->svl_mutex);
+			VHCI_DEBUG(4, (CE_WARN, vhci->vhci_dip,
+			    "scsi_vhci_getcap:"
+			    "Getting the Lun reset capability %d", rval));
+			break;
+
+		case SCSI_CAP_SECTOR_SIZE:
+			mutex_enter(&vlun->svl_mutex);
+			rval = vlun->svl_sector_size;
+			mutex_exit(&vlun->svl_mutex);
+			break;
+
+		default:
+			VHCI_DEBUG(6, (CE_WARN, vhci->vhci_dip,
+			    "!vhci_getcap: unsupported %d", cidx));
+			rval = UNDEFINED;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_NOTE, vhci->vhci_dip,
+		    "!get cap: cap=%s, val/tgtonly/doset/rval = "
+		    "0x%x/0x%x/0x%x/%d\n",
+		    cap, val, tgtonly, doset, rval));
+	}
+	return (rval);
+}
+
+
+/*
+ * Function name : vhci_scsi_getcap()
+ *
+ */
+static int
+vhci_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
+{
+	return (vhci_commoncap(ap, cap, 0, whom, 0));
+}
+
+static int
+vhci_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
+{
+	return (vhci_commoncap(ap, cap, value, whom, 1));
+}
+
+/*
+ * Function name : vhci_scsi_abort()
+ */
+/* ARGSUSED */
+static int
+vhci_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	return (0);
+}
+
+/*
+ * Function name : vhci_scsi_init_pkt
+ *
+ * Return Values : pointer to scsi_pkt, or NULL
+ */
+/* ARGSUSED */
+static struct scsi_pkt *
+vhci_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
+	struct buf *bp, int cmdlen, int statuslen, int tgtlen,
+	int flags, int (*callback)(caddr_t), caddr_t arg)
+{
+	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
+	struct vhci_pkt		*vpkt;
+	int			rval;
+	int			newpkt = 0;
+	struct scsi_pkt		*pktp;
+
+
+	if (pkt == NULL) {
+		if (cmdlen > VHCI_SCSI_CDB_SIZE) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!init pkt: cdb size not supported\n"));
+			return (NULL);
+		}
+
+		pktp = scsi_hba_pkt_alloc(vhci->vhci_dip,
+		    ap, cmdlen, statuslen, tgtlen, sizeof (*vpkt), callback,
+		    arg);
+
+		if (pktp == NULL) {
+			return (NULL);
+		}
+
+		/* Get the vhci's private structure */
+		vpkt = (struct vhci_pkt *)(pktp->pkt_ha_private);
+		ASSERT(vpkt);
+
+		/* Save the target driver's packet */
+		vpkt->vpkt_tgt_pkt = pktp;
+
+		/*
+		 * Save pkt_tgt_init_pkt fields if deferred binding
+		 * is needed or for other purposes.
+		 */
+		vpkt->vpkt_tgt_init_pkt_flags = flags;
+		vpkt->vpkt_flags = (callback == NULL_FUNC) ? CFLAG_NOWAIT : 0;
+		vpkt->vpkt_state = VHCI_PKT_IDLE;
+		vpkt->vpkt_tgt_init_cdblen = cmdlen;
+		vpkt->vpkt_tgt_init_scblen = statuslen;
+		vpkt->vpkt_tgt_init_privlen = tgtlen;
+		newpkt = 1;
+	} else { /* pkt not NULL */
+		vpkt = pkt->pkt_ha_private;
+	}
+
+	VHCI_DEBUG(8, (CE_NOTE, NULL, "vhci_scsi_init_pkt "
+	    "vpkt %p flags %x\n", (void *)vpkt, flags));
+
+	/* Clear any stale error flags */
+	if (bp) {
+		bioerror(bp, 0);
+	}
+
+	vpkt->vpkt_tgt_init_bp = bp;
+
+	if (flags & PKT_DMA_PARTIAL) {
+
+		/*
+		 * Immediate binding is needed.
+		 * Target driver may not set this flag in next invocation.
+		 * vhci has to remember this flag was set during first
+		 * invocation of vhci_scsi_init_pkt.
+		 */
+		vpkt->vpkt_flags |= CFLAG_DMA_PARTIAL;
+	}
+
+	if (vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) {
+
+		/*
+		 * Re-initialize some of the target driver packet state
+		 * information.
+		 */
+		vpkt->vpkt_tgt_pkt->pkt_state = 0;
+		vpkt->vpkt_tgt_pkt->pkt_statistics = 0;
+		vpkt->vpkt_tgt_pkt->pkt_reason = 0;
+
+		/*
+		 * Binding a vpkt->vpkt_path for this IO at init_time.
+		 * If an IO error happens later, target driver will clear
+		 * this vpkt->vpkt_path binding before re-init IO again.
+		 */
+		VHCI_DEBUG(8, (CE_NOTE, NULL,
+		    "vhci_scsi_init_pkt: calling v_b_t %p, newpkt %d\n",
+		    (void *)vpkt, newpkt));
+		if (pkt && vpkt->vpkt_hba_pkt) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "v_s_i_p calling update_pHCI_pkt resid %ld\n",
+			    pkt->pkt_resid));
+			vhci_update_pHCI_pkt(vpkt, pkt);
+		}
+		if (callback == SLEEP_FUNC) {
+			rval = vhci_bind_transport(
+			    ap, vpkt, flags, callback);
+		} else {
+			rval = vhci_bind_transport(
+			    ap, vpkt, flags, NULL_FUNC);
+		}
+		VHCI_DEBUG(8, (CE_NOTE, NULL,
+		    "vhci_scsi_init_pkt: v_b_t called 0x%p rval 0x%x\n",
+		    (void *)vpkt, rval));
+		if (bp) {
+			if (rval == TRAN_FATAL_ERROR) {
+				/*
+				 * No paths available. Could not bind
+				 * any pHCI. Setting EFAULT as a way
+				 * to indicate no DMA is mapped.
+				 */
+				bioerror(bp, EFAULT);
+			} else {
+				/*
+				 * Do not indicate any pHCI errors to
+				 * target driver otherwise.
+				 */
+				bioerror(bp, 0);
+			}
+		}
+		if (rval != TRAN_ACCEPT) {
+			VHCI_DEBUG(8, (CE_NOTE, NULL,
+			    "vhci_scsi_init_pkt: "
+			    "v_b_t failed 0x%p newpkt %x\n",
+			    (void *)vpkt, newpkt));
+			if (newpkt) {
+				scsi_hba_pkt_free(ap,
+				    vpkt->vpkt_tgt_pkt);
+			}
+			return (NULL);
+		}
+		ASSERT(vpkt->vpkt_hba_pkt != NULL);
+		ASSERT(vpkt->vpkt_path != NULL);
+
+		/* Update the resid for the target driver */
+		vpkt->vpkt_tgt_pkt->pkt_resid =
+		    vpkt->vpkt_hba_pkt->pkt_resid;
+	}
+
+	return (vpkt->vpkt_tgt_pkt);
+}
+
+/*
+ * Function name : vhci_scsi_destroy_pkt
+ *
+ * Return Values : none
+ */
+static void
+vhci_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
+
+	VHCI_DEBUG(8, (CE_NOTE, NULL,
+	    "vhci_scsi_destroy_pkt: vpkt 0x%p\n", (void *)vpkt));
+
+	vpkt->vpkt_tgt_init_pkt_flags = 0;
+	if (vpkt->vpkt_hba_pkt) {
+		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
+		vpkt->vpkt_hba_pkt = NULL;
+	}
+	if (vpkt->vpkt_path) {
+		mdi_rele_path(vpkt->vpkt_path);
+		vpkt->vpkt_path = NULL;
+	}
+
+	ASSERT(vpkt->vpkt_state != VHCI_PKT_ISSUED);
+	scsi_hba_pkt_free(ap, vpkt->vpkt_tgt_pkt);
+}
+
+/*
+ * Function name : vhci_scsi_dmafree()
+ *
+ * Return Values : none
+ */
+/*ARGSUSED*/
+static void
+vhci_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	struct vhci_pkt	*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL,
+	    "vhci_scsi_dmafree: vpkt 0x%p\n", (void *)vpkt));
+
+	ASSERT(vpkt != NULL);
+	if (vpkt->vpkt_hba_pkt) {
+		scsi_destroy_pkt(vpkt->vpkt_hba_pkt);
+		vpkt->vpkt_hba_pkt = NULL;
+	}
+	if (vpkt->vpkt_path) {
+		mdi_rele_path(vpkt->vpkt_path);
+		vpkt->vpkt_path = NULL;
+	}
+}
+
+/*
+ * Function name : vhci_scsi_sync_pkt()
+ *
+ * Return Values : none
+ */
+/*ARGSUSED*/
+static void
+vhci_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	struct vhci_pkt	*vpkt = (struct vhci_pkt *)pkt->pkt_ha_private;
+
+	ASSERT(vpkt != NULL);
+	if (vpkt->vpkt_hba_pkt) {
+		scsi_sync_pkt(vpkt->vpkt_hba_pkt);
+	}
+}
+
+/*
+ * routine for reset notification setup, to register or cancel.
+ */
+static int
+vhci_scsi_reset_notify(struct scsi_address *ap, int flag,
+	void (*callback)(caddr_t), caddr_t arg)
+{
+	struct scsi_vhci *vhci = ADDR2VHCI(ap);
+	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
+	    &vhci->vhci_mutex, &vhci->vhci_reset_notify_listf));
+}
+
+static int
+vhci_scsi_get_name_bus_addr(struct scsi_device *sd,
+    char *name, int len, int bus_addr)
+{
+	dev_info_t		*cdip;
+	char			*guid;
+	scsi_vhci_lun_t		*vlun;
+
+	ASSERT(sd != NULL);
+	ASSERT(name != NULL);
+
+	cdip = sd->sd_dev;
+
+	ASSERT(cdip != NULL);
+
+	if (mdi_component_is_client(cdip, NULL) != MDI_SUCCESS) {
+		name[0] = '\0';
+		return (1);
+	}
+
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, PROPFLAGS,
+	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
+		name[0] = '\0';
+		return (1);
+	}
+
+	vlun = ADDR2VLUN(&sd->sd_address);
+	if (bus_addr && vlun && vlun->svl_fops_name) {
+		/* report the guid and the name of the failover module */
+		(void) snprintf(name, len, "g%s %s", guid, vlun->svl_fops_name);
+	} else {
+		/* report the guid */
+		(void) snprintf(name, len, "g%s", guid);
+	}
+
+	ddi_prop_free(guid);
+	return (1);
+}
+
+static int
+vhci_scsi_get_bus_addr(struct scsi_device *sd, char *name, int len)
+{
+	return (vhci_scsi_get_name_bus_addr(sd, name, len, 1));
+}
+
+static int
+vhci_scsi_get_name(struct scsi_device *sd, char *name, int len)
+{
+	return (vhci_scsi_get_name_bus_addr(sd, name, len, 0));
+}
+
+/*
+ * Return a pointer to the guid part of the devnm.
+ * devnm format is "nodename@busaddr", busaddr format is "gGUID".
+ */
+static char *
+vhci_devnm_to_guid(char *devnm)
+{
+	char *cp = devnm;
+
+	if (devnm == NULL)
+		return (NULL);
+
+	while (*cp != '\0' && *cp != '@')
+		cp++;
+	if (*cp == '@' && *(cp + 1) == 'g')
+		return (cp + 2);
+	return (NULL);
+}
+
+static int
+vhci_bind_transport(struct scsi_address *ap, struct vhci_pkt *vpkt, int flags,
+    int (*func)(caddr_t))
+{
+	struct scsi_vhci	*vhci = ADDR2VHCI(ap);
+	dev_info_t		*cdip = ADDR2DIP(ap);
+	mdi_pathinfo_t		*pip = NULL;
+	mdi_pathinfo_t		*npip = NULL;
+	scsi_vhci_priv_t	*svp = NULL;
+	struct scsi_device	*psd = NULL;
+	struct scsi_address	*address = NULL;
+	struct scsi_pkt		*pkt = NULL;
+	int			rval = -1;
+	int			pgr_sema_held = 0;
+	int			held;
+	int			mps_flag = MDI_SELECT_ONLINE_PATH;
+	struct scsi_vhci_lun	*vlun;
+	time_t			tnow;
+
+	vlun = ADDR2VLUN(ap);
+	ASSERT(vlun != 0);
+
+	if ((vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PROUT) &&
+	    (((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
+	    VHCI_PROUT_REGISTER) ||
+	    ((vpkt->vpkt_tgt_pkt->pkt_cdbp[1] & 0x1f) ==
+	    VHCI_PROUT_R_AND_IGNORE))) {
+		if (!sema_tryp(&vlun->svl_pgr_sema))
+			return (TRAN_BUSY);
+		pgr_sema_held = 1;
+		if (vlun->svl_first_path != NULL) {
+			rval = mdi_select_path(cdip, NULL,
+			    MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH,
+			    NULL, &pip);
+			if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+				VHCI_DEBUG(4, (CE_NOTE, NULL,
+				    "vhci_bind_transport: path select fail\n"));
+			} else {
+				npip = pip;
+				do {
+					if (npip == vlun->svl_first_path) {
+						VHCI_DEBUG(4, (CE_NOTE, NULL,
+						    "vhci_bind_transport: "
+						    "valid first path 0x%p\n",
+						    (void *)
+						    vlun->svl_first_path));
+						pip = vlun->svl_first_path;
+						goto bind_path;
+					}
+					pip = npip;
+					rval = mdi_select_path(cdip, NULL,
+					    MDI_SELECT_ONLINE_PATH |
+					    MDI_SELECT_STANDBY_PATH,
+					    pip, &npip);
+					mdi_rele_path(pip);
+				} while ((rval == MDI_SUCCESS) &&
+				    (npip != NULL));
+			}
+		}
+
+		if (vlun->svl_first_path) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_bind_transport: invalid first path 0x%p\n",
+			    (void *)vlun->svl_first_path));
+			vlun->svl_first_path = NULL;
+		}
+	} else if (vpkt->vpkt_tgt_pkt->pkt_cdbp[0] == SCMD_PRIN) {
+		if ((vpkt->vpkt_state & VHCI_PKT_THRU_TASKQ) == 0) {
+			if (!sema_tryp(&vlun->svl_pgr_sema))
+				return (TRAN_BUSY);
+		}
+		pgr_sema_held = 1;
+	}
+
+	/*
+	 * If the path is already bound for PKT_PARTIAL_DMA case,
+	 * try to use the same path.
+	 */
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) && vpkt->vpkt_path) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_bind_transport: PKT_PARTIAL_DMA "
+		    "vpkt 0x%p, path 0x%p\n",
+		    (void *)vpkt, (void *)vpkt->vpkt_path));
+		pip = vpkt->vpkt_path;
+		goto bind_path;
+	}
+
+	/*
+	 * If reservation is active bind the transport directly to the pip
+	 * with the reservation.
+	 */
+	if (vpkt->vpkt_hba_pkt == NULL) {
+		if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
+			if (MDI_PI_IS_ONLINE(vlun->svl_resrv_pip)) {
+				pip = vlun->svl_resrv_pip;
+				mdi_hold_path(pip);
+				vlun->svl_waiting_for_activepath = 0;
+				rval = MDI_SUCCESS;
+				goto bind_path;
+			} else {
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_BUSY);
+			}
+		}
+try_again:
+		rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp, 0, NULL,
+		    &pip);
+		if (rval == MDI_BUSY) {
+			if (pgr_sema_held) {
+				sema_v(&vlun->svl_pgr_sema);
+			}
+			return (TRAN_BUSY);
+		} else if (rval == MDI_DEVI_ONLINING) {
+			/*
+			 * if we are here then we are in the midst of
+			 * an attach/probe of the client device.
+			 * We attempt to bind to ONLINE path if available,
+			 * else it is OK to bind to a STANDBY path (instead
+			 * of triggering a failover) because IO associated
+			 * with attach/probe (eg. INQUIRY, block 0 read)
+			 * are completed by targets even on passive paths
+			 * If no ONLINE paths available, it is important
+			 * to set svl_waiting_for_activepath for two
+			 * reasons: (1) avoid sense analysis in the
+			 * "external failure detection" codepath in
+			 * vhci_intr().  Failure to do so will result in
+			 * infinite loop (unless an ONLINE path becomes
+			 * available at some point) (2) avoid
+			 * unnecessary failover (see "---Waiting For Active
+			 * Path---" comment below).
+			 */
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!%p in onlining "
+			    "state\n", (void *)cdip));
+			pip = NULL;
+			rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
+			    mps_flag, NULL, &pip);
+			if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+				if (vlun->svl_waiting_for_activepath == 0) {
+					vlun->svl_waiting_for_activepath = 1;
+					vlun->svl_wfa_time = ddi_get_time();
+				}
+				mps_flag |= MDI_SELECT_STANDBY_PATH;
+				rval = mdi_select_path(cdip,
+				    vpkt->vpkt_tgt_init_bp,
+				    mps_flag, NULL, &pip);
+				if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+					if (pgr_sema_held) {
+						sema_v(&vlun->svl_pgr_sema);
+					}
+					return (TRAN_FATAL_ERROR);
+				}
+				goto bind_path;
+			}
+		} else if (rval == MDI_FAILURE) {
+			if (pgr_sema_held) {
+				sema_v(&vlun->svl_pgr_sema);
+			}
+			return (TRAN_FATAL_ERROR);
+		}
+
+		if ((pip == NULL) || (rval == MDI_NOPATH)) {
+			while (vlun->svl_waiting_for_activepath) {
+				/*
+				 * ---Waiting For Active Path---
+				 * This device was discovered across a
+				 * passive path; lets wait for a little
+				 * bit, hopefully an active path will
+				 * show up obviating the need for a
+				 * failover
+				 */
+				tnow = ddi_get_time();
+				if (tnow - vlun->svl_wfa_time >= 60) {
+					vlun->svl_waiting_for_activepath = 0;
+				} else {
+					drv_usecwait(1000);
+					if (vlun->svl_waiting_for_activepath
+					    == 0) {
+						/*
+						 * an active path has come
+						 * online!
+						 */
+						goto try_again;
+					}
+				}
+			}
+			VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
+			if (!held) {
+				VHCI_DEBUG(4, (CE_NOTE, NULL,
+				    "!Lun not held\n"));
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_BUSY);
+			}
+			/*
+			 * now that the LUN is stable, one last check
+			 * to make sure no other changes sneaked in
+			 * (like a path coming online or a
+			 * failover initiated by another thread)
+			 */
+			pip = NULL;
+			rval = mdi_select_path(cdip, vpkt->vpkt_tgt_init_bp,
+			    0, NULL, &pip);
+			if (pip != NULL) {
+				VHCI_RELEASE_LUN(vlun);
+				vlun->svl_waiting_for_activepath = 0;
+				goto bind_path;
+			}
+
+			/*
+			 * Check if there is an ONLINE path OR a STANDBY path
+			 * available. If none is available, do not attempt
+			 * to do a failover, just return a fatal error at this
+			 * point.
+			 */
+			npip = NULL;
+			rval = mdi_select_path(cdip, NULL,
+			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+			    NULL, &npip);
+			if ((npip == NULL) || (rval != MDI_SUCCESS)) {
+				/*
+				 * No paths available, jus return FATAL error.
+				 */
+				VHCI_RELEASE_LUN(vlun);
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_FATAL_ERROR);
+			}
+			mdi_rele_path(npip);
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!invoking "
+			    "mdi_failover\n"));
+			rval = mdi_failover(vhci->vhci_dip, cdip,
+			    MDI_FAILOVER_ASYNC);
+			if (rval == MDI_FAILURE) {
+				VHCI_RELEASE_LUN(vlun);
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_FATAL_ERROR);
+			} else if (rval == MDI_BUSY) {
+				VHCI_RELEASE_LUN(vlun);
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_BUSY);
+			} else {
+				if (pgr_sema_held) {
+					sema_v(&vlun->svl_pgr_sema);
+				}
+				return (TRAN_BUSY);
+			}
+		}
+		vlun->svl_waiting_for_activepath = 0;
+bind_path:
+		vpkt->vpkt_path = pip;
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		ASSERT(svp != NULL);
+
+		psd = svp->svp_psd;
+		ASSERT(psd != NULL);
+		address = &psd->sd_address;
+	} else {
+		pkt = vpkt->vpkt_hba_pkt;
+		address = &pkt->pkt_address;
+	}
+
+	/*
+	 * For PKT_PARTIAL_DMA case, call pHCI's scsi_init_pkt whenever
+	 * target driver calls vhci_scsi_init_pkt.
+	 */
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) &&
+	    vpkt->vpkt_path && vpkt->vpkt_hba_pkt) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_bind_transport: PKT_PARTIAL_DMA "
+		    "vpkt 0x%p, path 0x%p hba_pkt 0x%p\n",
+		    (void *)vpkt, (void *)vpkt->vpkt_path, (void *)pkt));
+		pkt = vpkt->vpkt_hba_pkt;
+		address = &pkt->pkt_address;
+	}
+
+	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,
+		    vpkt->vpkt_tgt_init_privlen, flags, func, NULL);
+
+		if (pkt == NULL) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "!bind transport: 0x%p 0x%p 0x%p\n",
+			    (void *)vhci, (void *)psd, (void *)vpkt));
+			if ((vpkt->vpkt_hba_pkt == NULL) && vpkt->vpkt_path) {
+				MDI_PI_ERRSTAT(vpkt->vpkt_path,
+				    MDI_PI_TRANSERR);
+				mdi_rele_path(vpkt->vpkt_path);
+				vpkt->vpkt_path = NULL;
+			}
+			if (pgr_sema_held) {
+				sema_v(&vlun->svl_pgr_sema);
+			}
+			/*
+			 * Looks like a fatal error.
+			 * May be device disappeared underneath.
+			 * Give another chance to target driver for a retry to
+			 * get another path.
+			 */
+			return (TRAN_BUSY);
+		}
+	}
+
+	pkt->pkt_private = vpkt;
+	vpkt->vpkt_hba_pkt = pkt;
+	return (TRAN_ACCEPT);
+}
+
+
+/*PRINTFLIKE3*/
+void
+vhci_log(int level, dev_info_t *dip, const char *fmt, ...)
+{
+	char		buf[256];
+	va_list		ap;
+
+	va_start(ap, fmt);
+	(void) vsprintf(buf, fmt, ap);
+	va_end(ap);
+
+	scsi_log(dip, "scsi_vhci", level, buf);
+}
+
+/* do a PGR out with the information we've saved away */
+static int
+vhci_do_prout(scsi_vhci_priv_t *svp)
+{
+
+	struct scsi_pkt			*new_pkt;
+	struct buf			*bp;
+	scsi_vhci_lun_t			*vlun;
+	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;
+
+	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,
+	    SLEEP_FUNC, NULL);
+	if (new_pkt == NULL) {
+		VHCI_DECR_PATH_CMDCOUNT(svp);
+		freerbuf(bp);
+		cmn_err(CE_WARN, "!vhci_do_prout: scsi_init_pkt failed");
+		return (0);
+	}
+	mutex_enter(&vlun->svl_mutex);
+	bp->b_un.b_addr = (caddr_t)&vlun->svl_prout;
+	bp->b_bcount = vlun->svl_bcount;
+	bcopy(vlun->svl_cdb, new_pkt->pkt_cdbp,
+	    sizeof (vlun->svl_cdb));
+	new_pkt->pkt_time = vlun->svl_time;
+	mutex_exit(&vlun->svl_mutex);
+	new_pkt->pkt_flags = FLAG_NOINTR;
+
+	ua_retry = nr_retry = retry = 0;
+again:
+	rval = vhci_do_scsi_cmd(new_pkt);
+	if (rval != 1) {
+		if ((new_pkt->pkt_reason == CMD_CMPLT) &&
+		    (SCBP_C(new_pkt) == STATUS_CHECK) &&
+		    (new_pkt->pkt_state & STATE_ARQ_DONE)) {
+			sns = &(((struct scsi_arq_status *)(uintptr_t)
+			    (new_pkt->pkt_scbp))->sts_sensedata);
+			if ((sns->es_key == KEY_UNIT_ATTENTION) ||
+			    (sns->es_key == KEY_NOT_READY)) {
+				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);
+				if (rval == SCSI_SENSE_NOT_READY) {
+					max_retry = vhci_prout_not_ready_retry;
+					retry = nr_retry++;
+					delay(1*drv_usectohz(1000000));
+				} else {
+					/* chk for state change and update */
+					if (rval == SCSI_SENSE_STATE_CHANGED) {
+						int held;
+						VHCI_HOLD_LUN(vlun,
+						    VH_NOSLEEP, held);
+						if (!held) {
+							rval = TRAN_BUSY;
+						} else {
+							/* chk for alua first */
+							vhci_update_pathstates(
+							    (void *)vlun);
+						}
+					}
+					retry = ua_retry++;
+					max_retry = VHCI_MAX_PGR_RETRIES;
+				}
+				if (retry < max_retry) {
+					VHCI_DEBUG(4, (CE_WARN, NULL,
+					    "!vhci_do_prout retry 0x%x "
+					    "(0x%x 0x%x 0x%x)",
+					    SCBP_C(new_pkt),
+					    new_pkt->pkt_cdbp[0],
+					    new_pkt->pkt_cdbp[1],
+					    new_pkt->pkt_cdbp[2]));
+					goto again;
+				}
+				rval = 0;
+				VHCI_DEBUG(4, (CE_WARN, NULL,
+				    "!vhci_do_prout 0x%x "
+				    "(0x%x 0x%x 0x%x)",
+				    SCBP_C(new_pkt),
+				    new_pkt->pkt_cdbp[0],
+				    new_pkt->pkt_cdbp[1],
+				    new_pkt->pkt_cdbp[2]));
+			} else if (sns->es_key == KEY_ILLEGAL_REQUEST)
+				rval = VHCI_PGR_ILLEGALOP;
+		}
+	} else {
+		rval = 1;
+	}
+	scsi_destroy_pkt(new_pkt);
+	VHCI_DECR_PATH_CMDCOUNT(svp);
+	freerbuf(bp);
+	return (rval);
+}
+
+static void
+vhci_run_cmd(void *arg)
+{
+	struct scsi_pkt		*pkt = (struct scsi_pkt *)arg;
+	struct scsi_pkt		*tpkt;
+	scsi_vhci_priv_t	*svp;
+	mdi_pathinfo_t		*pip, *npip;
+	scsi_vhci_lun_t		*vlun;
+	dev_info_t		*cdip;
+	scsi_vhci_priv_t	*nsvp;
+	int			fail = 0;
+	int			rval;
+	struct vhci_pkt		*vpkt;
+	uchar_t			cdb_1;
+	vhci_prout_t		*prout;
+
+	vpkt = (struct vhci_pkt *)pkt->pkt_private;
+	tpkt = vpkt->vpkt_tgt_pkt;
+	pip = vpkt->vpkt_path;
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		tpkt->pkt_reason = CMD_TRAN_ERR;
+		tpkt->pkt_statistics = STAT_ABORTED;
+		goto done;
+	}
+	vlun = svp->svp_svl;
+	prout = &vlun->svl_prout;
+	if (SCBP_C(pkt) != STATUS_GOOD)
+		fail++;
+	cdip = vlun->svl_dip;
+	pip = npip = NULL;
+	rval = mdi_select_path(cdip, NULL,
+	    MDI_SELECT_ONLINE_PATH|MDI_SELECT_STANDBY_PATH, NULL, &npip);
+	if ((rval != MDI_SUCCESS) || (npip == NULL)) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_run_cmd: no path! 0x%p\n", (void *)svp));
+		tpkt->pkt_reason = CMD_TRAN_ERR;
+		tpkt->pkt_statistics = STAT_ABORTED;
+		goto done;
+	}
+
+	cdb_1 = vlun->svl_cdb[1];
+	vlun->svl_cdb[1] &= 0xe0;
+	vlun->svl_cdb[1] |= VHCI_PROUT_R_AND_IGNORE;
+
+	do {
+		nsvp = (scsi_vhci_priv_t *)
+		    mdi_pi_get_vhci_private(npip);
+		if (nsvp == NULL) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_run_cmd: no "
+			    "client priv! 0x%p offlined?\n",
+			    (void *)npip));
+			goto next_path;
+		}
+		if (vlun->svl_first_path == npip) {
+			goto next_path;
+		} else {
+			if (vhci_do_prout(nsvp) != 1)
+				fail++;
+		}
+next_path:
+		pip = npip;
+		rval = mdi_select_path(cdip, NULL,
+		    MDI_SELECT_ONLINE_PATH|MDI_SELECT_STANDBY_PATH,
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((rval == MDI_SUCCESS) && (npip != NULL));
+
+	vlun->svl_cdb[1] = cdb_1;
+
+	if (fail) {
+		VHCI_DEBUG(4, (CE_WARN, NULL, "%s%d: key registration failed, "
+		    "couldn't be replicated on all paths",
+		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
+		vhci_print_prout_keys(vlun, "vhci_run_cmd: ");
+
+		if (SCBP_C(pkt) != STATUS_GOOD) {
+			tpkt->pkt_reason = CMD_TRAN_ERR;
+			tpkt->pkt_statistics = STAT_ABORTED;
+		}
+	} else {
+		vlun->svl_pgr_active = 1;
+		vhci_print_prout_keys(vlun, "vhci_run_cmd: before bcopy:");
+
+		bcopy((const void *)prout->service_key,
+		    (void *)prout->active_service_key, MHIOC_RESV_KEY_SIZE);
+		bcopy((const void *)prout->res_key,
+		    (void *)prout->active_res_key, MHIOC_RESV_KEY_SIZE);
+
+		vhci_print_prout_keys(vlun, "vhci_run_cmd: after bcopy:");
+	}
+done:
+	if (SCBP_C(pkt) == STATUS_GOOD)
+		vlun->svl_first_path = NULL;
+
+	if (svp)
+		VHCI_DECR_PATH_CMDCOUNT(svp);
+
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+		scsi_destroy_pkt(pkt);
+		vpkt->vpkt_hba_pkt = NULL;
+		if (vpkt->vpkt_path) {
+			mdi_rele_path(vpkt->vpkt_path);
+			vpkt->vpkt_path = NULL;
+		}
+	}
+
+	sema_v(&vlun->svl_pgr_sema);
+	/*
+	 * The PROUT commands are not included in the automatic retry
+	 * mechanism, therefore, vpkt_org_vpkt should never be set here.
+	 */
+	ASSERT(vpkt->vpkt_org_vpkt == NULL);
+	if (tpkt->pkt_comp)
+		(*tpkt->pkt_comp)(tpkt);
+
+}
+
+/*
+ * Get the keys registered with this target.  Since we will have
+ * registered the same key with multiple initiators, strip out
+ * any duplicate keys.
+ *
+ * The pointers which will be used to filter the registered keys from
+ * the device will be stored in filter_prin and filter_pkt.  If the
+ * allocation length of the buffer was sufficient for the number of
+ * parameter data bytes available to be returned by the device then the
+ * key filtering will use the keylist returned from the original
+ * request.  If the allocation length of the buffer was not sufficient,
+ * then the filtering will use the keylist returned from the request
+ * that is resent below.
+ *
+ * If the device returns an additional length field that is greater than
+ * the allocation length of the buffer, then allocate a new buffer which
+ * can accommodate the number of parameter data bytes available to be
+ * returned.  Resend the scsi PRIN command, filter out the duplicate
+ * keys and return as many of the unique keys found that was originally
+ * requested and set the additional length field equal to the data bytes
+ * of unique reservation keys available to be returned.
+ *
+ * If the device returns an additional length field that is less than or
+ * equal to the allocation length of the buffer, then all the available
+ * keys registered were returned by the device.  Filter out the
+ * duplicate keys and return all of the unique keys found and set the
+ * additional length field equal to the data bytes of the reservation
+ * keys to be returned.
+ */
+static int
+vhci_do_prin(struct vhci_pkt **vpkt)
+{
+	scsi_vhci_priv_t *svp = (scsi_vhci_priv_t *)
+	    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));
+
+	struct buf		*new_bp = NULL;
+	struct scsi_pkt		*new_pkt = NULL;
+	struct vhci_pkt		*new_vpkt = NULL;
+	int			hdr_len = 0;
+	int			rval = VHCI_CMD_CMPLT;
+	uint32_t		prin_length = 0;
+	uint32_t		svl_prin_length = 0;
+
+	prin = (vhci_prin_readkeys_t *)
+	    bp_mapin_common((*vpkt)->vpkt_tgt_init_bp, VM_NOSLEEP);
+
+	if (prin != NULL) {
+		prin_length = BE_32(prin->length);
+	}
+
+	if (prin == NULL) {
+		VHCI_DEBUG(5, (CE_WARN, NULL,
+		    "vhci_do_prin: bp_mapin_common failed."));
+		rval = VHCI_CMD_ERROR;
+	} else {
+		/*
+		 * According to SPC-3r22, sec 4.3.4.6: "If the amount of
+		 * information to be transferred exceeds the maximum value
+		 * that the ALLOCATION LENGTH field is capable of specifying,
+		 * the device server shall...terminate the command with CHECK
+		 * CONDITION status".  The ALLOCATION LENGTH field of the
+		 * PERSISTENT RESERVE IN command is 2 bytes. We should never
+		 * get here with an ADDITIONAL LENGTH greater than 0xFFFF
+		 * so if we do, then it is an error!
+		 */
+
+		hdr_len = sizeof (prin->length) + sizeof (prin->generation);
+
+		if ((prin_length + hdr_len) > 0xFFFF) {
+			VHCI_DEBUG(5, (CE_NOTE, NULL,
+			    "vhci_do_prin: Device returned invalid "
+			    "length 0x%x\n", prin_length));
+			rval = VHCI_CMD_ERROR;
+		}
+	}
+
+	/*
+	 * If prin->length is greater than the byte count allocated in the
+	 * original buffer, then resend the request with enough buffer
+	 * allocated to get all of the available registered keys.
+	 */
+	if (rval != VHCI_CMD_ERROR) {
+		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 (new_pkt != NULL) {
+					new_vpkt = TGTPKT2VHCIPKT(new_pkt);
+
+					/*
+					 * This is the buf with buffer pointer
+					 * where the prin readkeys will be
+					 * returned from the device
+					 */
+					new_bp = scsi_alloc_consistent_buf(
+					    &svp->svp_psd->sd_address,
+					    NULL, (prin_length + hdr_len),
+					    ((*vpkt)->vpkt_tgt_init_bp->
+					    b_flags & (B_READ | B_WRITE)),
+					    NULL_FUNC, NULL);
+					if (new_bp != NULL) {
+						if (new_bp->b_un.b_addr !=
+						    NULL) {
+
+							new_bp->b_bcount =
+							    prin_length +
+							    hdr_len;
+
+							new_pkt->pkt_cdbp[7] =
+							    (uchar_t)(new_bp->
+							    b_bcount >> 8);
+							new_pkt->pkt_cdbp[8] =
+							    (uchar_t)new_bp->
+							    b_bcount;
+
+							rval = VHCI_CMD_RETRY;
+						} else {
+							rval = VHCI_CMD_ERROR;
+						}
+					} else {
+						rval = VHCI_CMD_ERROR;
+					}
+				} else {
+					rval = VHCI_CMD_ERROR;
+				}
+			} else {
+				rval = VHCI_CMD_ERROR;
+			}
+		}
+	}
+
+	if (rval == VHCI_CMD_RETRY) {
+		new_vpkt->vpkt_tgt_init_bp = new_bp;
+
+		/*
+		 * Release the old path because it does not matter which path
+		 * 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;
+		}
+
+		/*
+		 * Dispatch the retry command
+		 */
+		if (taskq_dispatch(vhci->vhci_taskq, vhci_dispatch_scsi_start,
+		    (void *) new_vpkt, KM_NOSLEEP) == NULL) {
+			rval = VHCI_CMD_ERROR;
+		} else {
+			/*
+			 * If we return VHCI_CMD_RETRY, that means the caller
+			 * is going to bail and wait for the reissued command
+			 * to complete.  In that case, we need to decrement
+			 * the path command count right now.  In any other
+			 * case, it'll be decremented by the caller.
+			 */
+			VHCI_DECR_PATH_CMDCOUNT(svp);
+		}
+	}
+
+	if ((rval != VHCI_CMD_ERROR) && (rval != VHCI_CMD_RETRY)) {
+		int new, old;
+		int data_len = 0;
+
+		data_len = prin_length / MHIOC_RESV_KEY_SIZE;
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_do_prin: %d keys read\n",
+		    data_len));
+
+#ifdef DEBUG
+		VHCI_DEBUG(5, (CE_NOTE, NULL, "vhci_do_prin: from storage\n"));
+		if (vhci_debug == 5)
+			vhci_print_prin_keys(prin, data_len);
+		VHCI_DEBUG(5, (CE_NOTE, NULL,
+		    "vhci_do_prin: MPxIO old keys:\n"));
+		if (vhci_debug == 5)
+			vhci_print_prin_keys(&vlun->svl_prin, data_len);
+#endif
+
+		/*
+		 * Filter out all duplicate keys returned from the device
+		 * We know that we use a different key for every host, so we
+		 * can simply strip out duplicates. Otherwise we would need to
+		 * do more bookkeeping to figure out which keys to strip out.
+		 */
+
+		new = 0;
+
+		if (data_len > 0) {
+			vlun->svl_prin.keylist[0] = prin->keylist[0];
+			new++;
+		}
+
+		for (old = 1; old < data_len; old++) {
+			int j;
+			int match = 0;
+			for (j = 0; j < new; j++) {
+				if (bcmp(&prin->keylist[old],
+				    &vlun->svl_prin.keylist[j],
+				    sizeof (mhioc_resv_key_t)) == 0) {
+					match = 1;
+					break;
+				}
+			}
+			if (!match) {
+				vlun->svl_prin.keylist[new] =
+				    prin->keylist[old];
+				new++;
+			}
+		}
+
+		vlun->svl_prin.generation = prin->generation;
+		svl_prin_length = new * MHIOC_RESV_KEY_SIZE;
+		vlun->svl_prin.length = BE_32(svl_prin_length);
+
+		/*
+		 * If we arrived at this point after issuing a retry, make sure
+		 * 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;
+
+			scsi_free_consistent_buf(new_bp);
+
+			*vpkt = vhci_sync_retry_pkt(*vpkt);
+
+			/*
+			 * Make sure the original buffer is mapped into kernel
+			 * space before we try to copy the filtered keys into
+			 * it.
+			 */
+			prin = (vhci_prin_readkeys_t *)bp_mapin_common(
+			    (*vpkt)->vpkt_tgt_init_bp, VM_NOSLEEP);
+		}
+
+		/*
+		 * Now copy the desired number of prin keys into the original
+		 * target buffer.
+		 */
+		if (svl_prin_length <=
+		    ((*vpkt)->vpkt_tgt_init_bp->b_bcount - hdr_len)) {
+			/*
+			 * It is safe to return all of the available unique
+			 * keys
+			 */
+			bcopy(&vlun->svl_prin, prin, svl_prin_length + hdr_len);
+		} else {
+			/*
+			 * Not all of the available keys were requested by the
+			 * original command.
+			 */
+			bcopy(&vlun->svl_prin, prin,
+			    (*vpkt)->vpkt_tgt_init_bp->b_bcount);
+		}
+#ifdef DEBUG
+		VHCI_DEBUG(5, (CE_NOTE, NULL,
+		    "vhci_do_prin: To Application:\n"));
+		if (vhci_debug == 5)
+			vhci_print_prin_keys(prin, new);
+		VHCI_DEBUG(5, (CE_NOTE, NULL,
+		    "vhci_do_prin: MPxIO new keys:\n"));
+		if (vhci_debug == 5)
+			vhci_print_prin_keys(&vlun->svl_prin, new);
+#endif
+	}
+
+	if (rval == VHCI_CMD_ERROR) {
+		/*
+		 * If we arrived at this point after issuing a
+		 * retry, make sure that we put everything back
+		 * the way it originally was so that ssd can
+		 * complete the command correctly.
+		 */
+
+		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;
+
+			vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
+			    new_vpkt->vpkt_tgt_pkt);
+		}
+
+		/*
+		 * Mark this command completion as having an error so that
+		 * ssd will retry the command.
+		 */
+
+		(*vpkt)->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
+		(*vpkt)->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
+
+		rval = VHCI_CMD_CMPLT;
+	}
+
+	/*
+	 * Make sure that the semaphore is only released once.
+	 */
+	if (rval == VHCI_CMD_CMPLT) {
+		sema_v(&vlun->svl_pgr_sema);
+	}
+
+	return (rval);
+}
+
+static void
+vhci_intr(struct scsi_pkt *pkt)
+{
+	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_private;
+	struct scsi_pkt		*tpkt;
+	scsi_vhci_priv_t	*svp;
+	scsi_vhci_lun_t		*vlun;
+	int			rval, held;
+	struct scsi_failover_ops	*fops;
+	struct scsi_extended_sense	*sns;
+	mdi_pathinfo_t		*lpath;
+	static char		*timeout_err = "Command Timeout";
+	static char		*parity_err = "Parity Error";
+	char			*err_str = NULL;
+	dev_info_t		*vdip, *cdip, *pdip;
+	char			*cpath, *dpath;
+
+	ASSERT(vpkt != NULL);
+	tpkt = vpkt->vpkt_tgt_pkt;
+	ASSERT(tpkt != NULL);
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
+	ASSERT(svp != NULL);
+	vlun = svp->svp_svl;
+	ASSERT(vlun != NULL);
+	lpath = vpkt->vpkt_path;
+
+	/*
+	 * sync up the target driver's pkt with the pkt that
+	 * we actually used
+	 */
+	*(tpkt->pkt_scbp) = *(pkt->pkt_scbp);
+	tpkt->pkt_resid = pkt->pkt_resid;
+	tpkt->pkt_state = pkt->pkt_state;
+	tpkt->pkt_statistics = pkt->pkt_statistics;
+	tpkt->pkt_reason = pkt->pkt_reason;
+
+	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)) {
+		if ((SCBP_C(pkt) != STATUS_GOOD) ||
+		    (pkt->pkt_reason != CMD_CMPLT)) {
+			sema_v(&vlun->svl_pgr_sema);
+		}
+	} else if (pkt->pkt_cdbp[0] == SCMD_PRIN) {
+		if (pkt->pkt_reason != CMD_CMPLT ||
+		    (SCBP_C(pkt) != STATUS_GOOD)) {
+			sema_v(&vlun->svl_pgr_sema);
+		}
+	}
+
+	switch (pkt->pkt_reason) {
+	case CMD_CMPLT:
+		/*
+		 * cmd completed successfully, check for scsi errors
+		 */
+		switch (*(pkt->pkt_scbp)) {
+		case STATUS_CHECK:
+			if (pkt->pkt_state & STATE_ARQ_DONE) {
+				sns = &(((struct scsi_arq_status *)(uintptr_t)
+				    (pkt->pkt_scbp))->sts_sensedata);
+				fops = vlun->svl_fops;
+				ASSERT(fops != NULL);
+				VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_intr: "
+				    "Received sns key %x  esc %x  escq %x\n",
+				    sns->es_key, sns->es_add_code,
+				    sns->es_qual_code));
+
+				if (vlun->svl_waiting_for_activepath == 1) {
+					/*
+					 * if we are here it means we are
+					 * in the midst of a probe/attach
+					 * through a passive path; this
+					 * case is exempt from sense analysis
+					 * for detection of ext. failover
+					 * because that would unnecessarily
+					 * increase attach time.
+					 */
+					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
+					    vpkt->vpkt_tgt_init_scblen);
+					break;
+				}
+				if (sns->es_add_code == VHCI_SCSI_PERR) {
+					/*
+					 * parity error
+					 */
+					err_str = parity_err;
+					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
+					    vpkt->vpkt_tgt_init_scblen);
+					break;
+				}
+				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)) {
+					bcopy(pkt->pkt_scbp, tpkt->pkt_scbp,
+					    vpkt->vpkt_tgt_init_scblen);
+					break;
+				} else if (rval == SCSI_SENSE_STATE_CHANGED) {
+					struct scsi_vhci	*vhci;
+					vhci = ADDR2VHCI(&tpkt->pkt_address);
+					VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
+					if (!held) {
+						/*
+						 * looks like some other thread
+						 * has already detected this
+						 * condition
+						 */
+						tpkt->pkt_state &=
+						    ~STATE_ARQ_DONE;
+						*(tpkt->pkt_scbp) =
+						    STATUS_BUSY;
+						break;
+					}
+					(void) taskq_dispatch(
+					    vhci->vhci_update_pathstates_taskq,
+					    vhci_update_pathstates,
+					    (void *)vlun, KM_SLEEP);
+				} else {
+					/*
+					 * externally initiated failover
+					 * has occurred or is in progress
+					 */
+					VHCI_HOLD_LUN(vlun, VH_NOSLEEP, held);
+					if (!held) {
+						/*
+						 * looks like some other thread
+						 * has already detected this
+						 * condition
+						 */
+						tpkt->pkt_state &=
+						    ~STATE_ARQ_DONE;
+						*(tpkt->pkt_scbp) =
+						    STATUS_BUSY;
+						break;
+					} else {
+						rval = vhci_handle_ext_fo
+						    (pkt, rval);
+						if (rval == BUSY_RETURN) {
+							tpkt->pkt_state &=
+							    ~STATE_ARQ_DONE;
+							*(tpkt->pkt_scbp) =
+							    STATUS_BUSY;
+							break;
+						}
+						bcopy(pkt->pkt_scbp,
+						    tpkt->pkt_scbp,
+						    vpkt->vpkt_tgt_init_scblen);
+						break;
+					}
+				}
+			}
+			break;
+
+		/*
+		 * If this is a good SCSI-II RELEASE cmd completion then restore
+		 * the load balancing policy and reset VLUN_RESERVE_ACTIVE_FLG.
+		 * If this is a good SCSI-II RESERVE cmd completion then set
+		 * VLUN_RESERVE_ACTIVE_FLG.
+		 */
+		case STATUS_GOOD:
+			if ((pkt->pkt_cdbp[0] == SCMD_RELEASE) ||
+			    (pkt->pkt_cdbp[0] == SCMD_RELEASE_G1)) {
+				(void) mdi_set_lb_policy(vlun->svl_dip,
+				    vlun->svl_lb_policy_save);
+				vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!vhci_intr: vlun 0x%p release path 0x%p",
+				    (void *)vlun, (void *)vpkt->vpkt_path));
+			}
+
+			if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
+			    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
+				vlun->svl_flags |= VLUN_RESERVE_ACTIVE_FLG;
+				vlun->svl_resrv_pip = vpkt->vpkt_path;
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!vhci_intr: vlun 0x%p reserved path 0x%p",
+				    (void *)vlun, (void *)vpkt->vpkt_path));
+			}
+			break;
+
+		case STATUS_RESERVATION_CONFLICT:
+			VHCI_DEBUG(1, (CE_WARN, NULL,
+			    "!vhci_intr: vlun 0x%p "
+			    "reserve conflict on path 0x%p",
+			    (void *)vlun, (void *)vpkt->vpkt_path));
+			/* FALLTHROUGH */
+		default:
+			break;
+		}
+
+		/*
+		 * Update I/O completion statistics for the path
+		 */
+		mdi_pi_kstat_iosupdate(vpkt->vpkt_path, vpkt->vpkt_tgt_init_bp);
+
+		/*
+		 * Command completed successfully, release the dma binding and
+		 * destroy the transport side of the packet.
+		 */
+		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))) {
+			if (SCBP_C(pkt) == STATUS_GOOD) {
+				ASSERT(vlun->svl_taskq);
+				svp->svp_last_pkt_reason = pkt->pkt_reason;
+				(void) taskq_dispatch(vlun->svl_taskq,
+				    vhci_run_cmd, pkt, KM_SLEEP);
+				return;
+			}
+		}
+		if ((SCBP_C(pkt) == STATUS_GOOD) &&
+		    (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
+			 * where we need to be concerned with filtering keys
+			 * and potential retries.  Otherwise, we simply signal
+			 * the semaphore and move on.
+			 */
+			if (pkt->pkt_cdbp[1] == 0) {
+				/*
+				 * If this is the completion of an internal
+				 * retry then we need to make sure that the
+				 * pkt and tpkt pointers are readjusted so
+				 * the calls to scsi_destroy_pkt and pkt_comp
+				 * below work * correctly.
+				 */
+				if (vpkt->vpkt_org_vpkt != NULL) {
+					pkt = vpkt->vpkt_org_vpkt->vpkt_hba_pkt;
+					tpkt = vpkt->vpkt_org_vpkt->
+					    vpkt_tgt_pkt;
+
+					/*
+					 * If this command was issued through
+					 * the taskq then we need to clear
+					 * this flag for proper processing in
+					 * the case of a retry from the target
+					 * driver.
+					 */
+					vpkt->vpkt_state &=
+					    ~VHCI_PKT_THRU_TASKQ;
+				}
+
+				/*
+				 * if vhci_do_prin returns VHCI_CMD_CMPLT then
+				 * vpkt will contain the address of the
+				 * original vpkt
+				 */
+				if (vhci_do_prin(&vpkt) == VHCI_CMD_RETRY) {
+					/*
+					 * The command has been resent to get
+					 * all the keys from the device.  Don't
+					 * complete the command with ssd until
+					 * the retry completes.
+					 */
+					return;
+				}
+			} else {
+				sema_v(&vlun->svl_pgr_sema);
+			}
+		}
+
+		break;
+
+	case CMD_TIMEOUT:
+		if ((pkt->pkt_statistics &
+		    (STAT_BUS_RESET|STAT_DEV_RESET|STAT_ABORTED)) == 0) {
+
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!scsi vhci timeout invoked\n"));
+
+			(void) vhci_recovery_reset(vlun, &pkt->pkt_address,
+			    FALSE, VHCI_DEPTH_ALL);
+		}
+		MDI_PI_ERRSTAT(lpath, MDI_PI_TRANSERR);
+		tpkt->pkt_statistics |= STAT_ABORTED;
+		err_str = timeout_err;
+		break;
+
+	case CMD_TRAN_ERR:
+		/*
+		 * This status is returned if the transport has sent the cmd
+		 * down the link to the target and then some error occurs.
+		 * In case of SCSI-II RESERVE cmd, we don't know if the
+		 * reservation been accepted by the target or not, so we need
+		 * to clear the reservation.
+		 */
+		if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
+		    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_intr received"
+			    " cmd_tran_err for scsi-2 reserve cmd\n"));
+			if (!vhci_recovery_reset(vlun, &pkt->pkt_address,
+			    TRUE, VHCI_DEPTH_TARGET)) {
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!vhci_intr cmd_tran_err reset failed!"));
+			}
+		}
+		break;
+
+	case CMD_DEV_GONE:
+		tpkt->pkt_reason = CMD_CMPLT;
+		tpkt->pkt_state = STATE_GOT_BUS |
+		    STATE_GOT_TARGET | STATE_SENT_CMD |
+		    STATE_GOT_STATUS;
+		*(tpkt->pkt_scbp) = STATUS_BUSY;
+		break;
+
+	default:
+		break;
+	}
+
+	/*
+	 * SCSI-II RESERVE cmd has been serviced by the lower layers clear
+	 * the flag so the lun is not QUIESCED any longer.
+	 * Also clear the VHCI_PKT_THRU_TASKQ flag, to ensure that if this pkt
+	 * is retried, a taskq shall again be dispatched to service it.  Else
+	 * it may lead to a system hang if the retry is within interrupt
+	 * context.
+	 */
+	if ((pkt->pkt_cdbp[0] == SCMD_RESERVE) ||
+	    (pkt->pkt_cdbp[0] == SCMD_RESERVE_G1)) {
+		vlun->svl_flags &= ~VLUN_QUIESCED_FLG;
+		vpkt->vpkt_state &= ~VHCI_PKT_THRU_TASKQ;
+	}
+
+	/*
+	 * vpkt_org_vpkt should always be NULL here if the retry command
+	 * has been successfully processed.  If vpkt_org_vpkt != NULL at
+	 * this point, it is an error so restore the original vpkt and
+	 * return an error to the target driver so it can retry the
+	 * command as appropriate.
+	 */
+	if (vpkt->vpkt_org_vpkt != NULL) {
+		struct vhci_pkt *new_vpkt = vpkt;
+		vpkt = vpkt->vpkt_org_vpkt;
+
+		vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
+		    new_vpkt->vpkt_tgt_pkt);
+
+		/*
+		 * Mark this command completion as having an error so that
+		 * ssd will retry the command.
+		 */
+		vpkt->vpkt_tgt_pkt->pkt_reason = CMD_ABORTED;
+		vpkt->vpkt_tgt_pkt->pkt_statistics |= STAT_ABORTED;
+
+		pkt = vpkt->vpkt_hba_pkt;
+		tpkt = vpkt->vpkt_tgt_pkt;
+	}
+
+	if ((err_str != NULL) && (pkt->pkt_reason !=
+	    svp->svp_last_pkt_reason)) {
+		cdip = vlun->svl_dip;
+		pdip = mdi_pi_get_phci(vpkt->vpkt_path);
+		vdip = ddi_get_parent(cdip);
+		cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+		dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+		vhci_log(CE_WARN, vdip, "!%s (%s%d): %s on path %s (%s%d)",
+		    ddi_pathname(cdip, cpath), ddi_driver_name(cdip),
+		    ddi_get_instance(cdip), err_str,
+		    ddi_pathname(pdip, dpath), ddi_driver_name(pdip),
+		    ddi_get_instance(pdip));
+		kmem_free(cpath, MAXPATHLEN);
+		kmem_free(dpath, MAXPATHLEN);
+	}
+	svp->svp_last_pkt_reason = pkt->pkt_reason;
+	VHCI_DECR_PATH_CMDCOUNT(svp);
+
+	/*
+	 * For PARTIAL_DMA, vhci should not free the path.
+	 * Target driver will call into vhci_scsi_dmafree or
+	 * destroy pkt to release this path.
+	 */
+	if ((vpkt->vpkt_flags & CFLAG_DMA_PARTIAL) == 0) {
+		scsi_destroy_pkt(pkt);
+		vpkt->vpkt_hba_pkt = NULL;
+		if (vpkt->vpkt_path) {
+			mdi_rele_path(vpkt->vpkt_path);
+			vpkt->vpkt_path = NULL;
+		}
+	}
+
+	if (tpkt->pkt_comp) {
+		(*tpkt->pkt_comp)(tpkt);
+	}
+}
+
+/*
+ * two possibilities: (1) failover has completed
+ * or (2) is in progress; update our path states for
+ * the former case; for the latter case,
+ * initiate a scsi_watch request to
+ * determine when failover completes - vlun is HELD
+ * until failover completes; BUSY is returned to upper
+ * layer in both the cases
+ */
+static int
+vhci_handle_ext_fo(struct scsi_pkt *pkt, int fostat)
+{
+	struct vhci_pkt		*vpkt = (struct vhci_pkt *)pkt->pkt_private;
+	struct scsi_pkt		*tpkt;
+	scsi_vhci_priv_t	*svp;
+	scsi_vhci_lun_t		*vlun;
+	struct scsi_vhci	*vhci;
+	scsi_vhci_swarg_t	*swarg;
+	char			*path;
+
+	ASSERT(vpkt != NULL);
+	tpkt = vpkt->vpkt_tgt_pkt;
+	ASSERT(tpkt != NULL);
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(vpkt->vpkt_path);
+	ASSERT(svp != NULL);
+	vlun = svp->svp_svl;
+	ASSERT(vlun != NULL);
+	ASSERT(VHCI_LUN_IS_HELD(vlun));
+
+	vhci = ADDR2VHCI(&tpkt->pkt_address);
+
+	if (fostat == SCSI_SENSE_INACTIVE) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Failover "
+		    "detected for %s; updating path states...\n",
+		    vlun->svl_lun_wwn));
+		/*
+		 * set the vlun flag to indicate to the task that the target
+		 * port group needs updating
+		 */
+		vlun->svl_flags |= VLUN_UPDATE_TPG;
+		(void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
+		    vhci_update_pathstates, (void *)vlun, KM_SLEEP);
+	} else {
+		path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+		vhci_log(CE_NOTE, ddi_get_parent(vlun->svl_dip),
+		    "!%s (%s%d): Waiting for externally initiated failover "
+		    "to complete", ddi_pathname(vlun->svl_dip, path),
+		    ddi_driver_name(vlun->svl_dip),
+		    ddi_get_instance(vlun->svl_dip));
+		kmem_free(path, MAXPATHLEN);
+		swarg = kmem_alloc(sizeof (*swarg), KM_NOSLEEP);
+		if (swarg == NULL) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_handle_ext_fo: "
+			    "request packet allocation for %s failed....\n",
+			    vlun->svl_lun_wwn));
+			VHCI_RELEASE_LUN(vlun);
+			return (PKT_RETURN);
+		}
+		swarg->svs_svp = svp;
+		swarg->svs_tos = ddi_get_time();
+		swarg->svs_pi = vpkt->vpkt_path;
+		swarg->svs_release_lun = 0;
+		swarg->svs_done = 0;
+		/*
+		 * place a hold on the path...we don't want it to
+		 * vanish while scsi_watch is in progress
+		 */
+		mdi_hold_path(vpkt->vpkt_path);
+		svp->svp_sw_token = scsi_watch_request_submit(svp->svp_psd,
+		    VHCI_FOWATCH_INTERVAL, SENSE_LENGTH, vhci_efo_watch_cb,
+		    (caddr_t)swarg);
+	}
+	return (BUSY_RETURN);
+}
+
+/*
+ * vhci_efo_watch_cb:
+ *	Callback from scsi_watch request to check the failover status.
+ *	Completion is either due to successful failover or timeout.
+ *	Upon successful completion, vhci_update_path_states is called.
+ *	For timeout condition, vhci_efo_done is called.
+ *	Always returns 0 to scsi_watch to keep retrying till vhci_efo_done
+ *	terminates this request properly in a separate thread.
+ */
+
+static int
+vhci_efo_watch_cb(caddr_t arg, struct scsi_watch_result *resultp)
+{
+	struct scsi_status		*statusp = resultp->statusp;
+	struct scsi_extended_sense	*sensep = resultp->sensep;
+	struct scsi_pkt			*pkt = resultp->pkt;
+	scsi_vhci_swarg_t		*swarg;
+	scsi_vhci_priv_t		*svp;
+	scsi_vhci_lun_t			*vlun;
+	struct scsi_vhci		*vhci;
+	dev_info_t			*vdip;
+	int				rval, updt_paths;
+
+	swarg = (scsi_vhci_swarg_t *)(uintptr_t)arg;
+	svp = swarg->svs_svp;
+	if (swarg->svs_done) {
+		/*
+		 * Already completed failover or timedout.
+		 * Waiting for vhci_efo_done to terminate this scsi_watch.
+		 */
+		return (0);
+	}
+
+	ASSERT(svp != NULL);
+	vlun = svp->svp_svl;
+	ASSERT(vlun != NULL);
+	ASSERT(VHCI_LUN_IS_HELD(vlun));
+	vlun->svl_efo_update_path = 0;
+	vdip = ddi_get_parent(vlun->svl_dip);
+	vhci = ddi_get_soft_state(vhci_softstate,
+	    ddi_get_instance(vdip));
+
+	updt_paths = 0;
+
+	if (pkt->pkt_reason != CMD_CMPLT) {
+		if ((ddi_get_time() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
+			swarg->svs_release_lun = 1;
+			goto done;
+		}
+		return (0);
+	}
+	if (*((unsigned char *)statusp) == STATUS_CHECK) {
+		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
+			 * inactive, or no failover occurred.  For all other
+			 * check conditions continue pinging.  A unexpected
+			 * check condition shouldn't cause pinging to complete
+			 * prematurely.
+			 */
+			case SCSI_SENSE_INACTIVE:
+			case SCSI_SENSE_NOFAILOVER:
+				updt_paths = 1;
+				break;
+			default:
+				if ((ddi_get_time() - swarg->svs_tos)
+				    >= VHCI_EXTFO_TIMEOUT) {
+					swarg->svs_release_lun = 1;
+					goto done;
+				}
+				return (0);
+		}
+	} else if (*((unsigned char *)statusp) ==
+	    STATUS_RESERVATION_CONFLICT) {
+		updt_paths = 1;
+	} else if ((*((unsigned char *)statusp)) &
+	    (STATUS_BUSY | STATUS_QFULL)) {
+		return (0);
+	}
+	if ((*((unsigned char *)statusp) == STATUS_GOOD) ||
+	    (updt_paths == 1)) {
+		/*
+		 * we got here because we had detected an
+		 * externally initiated failover; things
+		 * have settled down now, so let's
+		 * start up a task to update the
+		 * path states and target port group
+		 */
+		vlun->svl_efo_update_path = 1;
+		swarg->svs_done = 1;
+		vlun->svl_swarg = swarg;
+		vlun->svl_flags |= VLUN_UPDATE_TPG;
+		(void) taskq_dispatch(vhci->vhci_update_pathstates_taskq,
+		    vhci_update_pathstates, (void *)vlun,
+		    KM_SLEEP);
+		return (0);
+	}
+	if ((ddi_get_time() - swarg->svs_tos) >= VHCI_EXTFO_TIMEOUT) {
+		swarg->svs_release_lun = 1;
+		goto done;
+	}
+	return (0);
+done:
+	swarg->svs_done = 1;
+	(void) taskq_dispatch(vhci->vhci_taskq,
+	    vhci_efo_done, (void *)swarg, KM_SLEEP);
+	return (0);
+}
+
+/*
+ * vhci_efo_done:
+ *	cleanly terminates scsi_watch and free up resources.
+ *	Called as taskq function in vhci_efo_watch_cb for EFO timeout condition
+ *	or by vhci_update_path_states invoked during external initiated
+ *	failover completion.
+ */
+static void
+vhci_efo_done(void *arg)
+{
+	scsi_vhci_lun_t			*vlun;
+	scsi_vhci_swarg_t		*swarg = (scsi_vhci_swarg_t *)arg;
+	scsi_vhci_priv_t		*svp = swarg->svs_svp;
+	ASSERT(svp);
+
+	vlun = svp->svp_svl;
+	ASSERT(vlun);
+
+	/* Wait for clean termination of scsi_watch */
+	(void) scsi_watch_request_terminate(svp->svp_sw_token,
+	    SCSI_WATCH_TERMINATE_WAIT);
+	svp->svp_sw_token = NULL;
+
+	/* release path and freeup resources to indicate failover completion */
+	mdi_rele_path(swarg->svs_pi);
+	if (swarg->svs_release_lun) {
+		VHCI_RELEASE_LUN(vlun);
+	}
+	kmem_free((void *)swarg, sizeof (*swarg));
+}
+
+/*
+ * Update the path states
+ * vlun should be HELD when this is invoked.
+ * Calls vhci_efo_done to cleanup resources allocated for EFO.
+ */
+void
+vhci_update_pathstates(void *arg)
+{
+	mdi_pathinfo_t			*pip, *npip;
+	dev_info_t			*dip, *pdip;
+	struct scsi_failover_ops	*fo;
+	struct scsi_vhci_priv		*svp;
+	struct scsi_device		*psd;
+	struct scsi_path_opinfo		opinfo;
+	char				*pclass, *tptr;
+	struct scsi_vhci_lun		*vlun = (struct scsi_vhci_lun *)arg;
+	int				sps; /* mdi_select_path() status */
+	char				*cpath, *dpath;
+	struct scsi_vhci		*vhci;
+	struct scsi_pkt			*pkt;
+	struct buf			*bp;
+	int				reserve_conflict = 0;
+
+	ASSERT(VHCI_LUN_IS_HELD(vlun));
+	dip  = vlun->svl_dip;
+	pip = npip = NULL;
+
+	vhci = ddi_get_soft_state(vhci_softstate,
+	    ddi_get_instance(ddi_get_parent(dip)));
+
+	sps = mdi_select_path(dip, NULL, (MDI_SELECT_ONLINE_PATH |
+	    MDI_SELECT_STANDBY_PATH), NULL, &npip);
+	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
+		goto done;
+	}
+
+	fo = vlun->svl_fops;
+	do {
+		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,
+		    vlun->svl_fops_ctpriv) != 0) {
+			sps = mdi_select_path(dip, NULL,
+			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+			    pip, &npip);
+			mdi_rele_path(pip);
+			continue;
+		}
+
+		if (mdi_prop_lookup_string(pip, "path-class", &pclass) !=
+		    MDI_SUCCESS) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_update_pathstates: prop lookup failed for "
+			    "path 0x%p\n", (void *)pip));
+			sps = mdi_select_path(dip, NULL,
+			    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+			    pip, &npip);
+			mdi_rele_path(pip);
+			continue;
+		}
+
+		/*
+		 * Need to update the "path-class" property
+		 * value in the device tree if different
+		 * from the existing value.
+		 */
+		if (strcmp(pclass, opinfo.opinfo_path_attr) != 0) {
+			(void) mdi_prop_update_string(pip, "path-class",
+			    opinfo.opinfo_path_attr);
+		}
+
+		/*
+		 * Only change the state if needed. i.e. Don't call
+		 * mdi_pi_set_state to ONLINE a path if its already
+		 * ONLINE. Same for STANDBY paths.
+		 */
+
+		if ((opinfo.opinfo_path_state == SCSI_PATH_ACTIVE ||
+		    opinfo.opinfo_path_state == SCSI_PATH_ACTIVE_NONOPT)) {
+			if (!(MDI_PI_IS_ONLINE(pip))) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_update_pathstates: marking path"
+				    " 0x%p as ONLINE\n", (void *)pip));
+				pdip = mdi_pi_get_phci(pip);
+				cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+				dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+				vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s"
+				    " (%s%d): path %s (%s%d) target address %s"
+				    " is now ONLINE because of"
+				    " an externally initiated failover",
+				    ddi_pathname(dip, cpath),
+				    ddi_driver_name(dip),
+				    ddi_get_instance(dip),
+				    ddi_pathname(pdip, dpath),
+				    ddi_driver_name(pdip),
+				    ddi_get_instance(pdip),
+				    mdi_pi_get_addr(pip));
+				kmem_free(cpath, MAXPATHLEN);
+				kmem_free(dpath, MAXPATHLEN);
+				mdi_pi_set_state(pip,
+				    MDI_PATHINFO_STATE_ONLINE);
+				mdi_pi_set_preferred(pip,
+				    opinfo.opinfo_preferred);
+				tptr = kmem_alloc(strlen
+				    (opinfo.opinfo_path_attr)+1, KM_SLEEP);
+				(void) strlcpy(tptr, opinfo.opinfo_path_attr,
+				    (strlen(opinfo.opinfo_path_attr)+1));
+				mutex_enter(&vlun->svl_mutex);
+				if (vlun->svl_active_pclass != NULL) {
+					kmem_free(vlun->svl_active_pclass,
+					    strlen(vlun->svl_active_pclass)+1);
+				}
+				vlun->svl_active_pclass = tptr;
+				if (vlun->svl_waiting_for_activepath) {
+					vlun->svl_waiting_for_activepath = 0;
+				}
+				mutex_exit(&vlun->svl_mutex);
+				/* Check for Reservation Conflict */
+				bp = scsi_alloc_consistent_buf(
+				    &svp->svp_psd->sd_address,
+				    (struct buf *)NULL, DEV_BSIZE, B_READ,
+				    NULL, NULL);
+				if (!bp) {
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "vhci_update_pathstates: "
+					    "!No resources (buf)\n"));
+					mdi_rele_path(pip);
+					goto done;
+				}
+				pkt = scsi_init_pkt(&svp->svp_psd->sd_address,
+				    NULL, bp, CDB_GROUP1,
+				    sizeof (struct scsi_arq_status), 0,
+				    PKT_CONSISTENT, NULL, NULL);
+				if (pkt) {
+					(void) scsi_setup_cdb((union scsi_cdb *)
+					    (uintptr_t)pkt->pkt_cdbp,
+					    SCMD_READ, 1, 1, 0);
+					pkt->pkt_time = 3*30;
+					pkt->pkt_flags = FLAG_NOINTR;
+					if ((scsi_transport(pkt) ==
+					    TRAN_ACCEPT) && (pkt->pkt_reason
+					    == CMD_CMPLT) && (SCBP_C(pkt) ==
+					    STATUS_RESERVATION_CONFLICT)) {
+						reserve_conflict = 1;
+					}
+					scsi_destroy_pkt(pkt);
+				}
+				scsi_free_consistent_buf(bp);
+			} else if (MDI_PI_IS_ONLINE(pip)) {
+				if (strcmp(pclass, opinfo.opinfo_path_attr)
+				    != 0) {
+					mdi_pi_set_preferred(pip,
+					    opinfo.opinfo_preferred);
+					mutex_enter(&vlun->svl_mutex);
+					if (vlun->svl_active_pclass == NULL ||
+					    strcmp(opinfo.opinfo_path_attr,
+					    vlun->svl_active_pclass) != 0) {
+						mutex_exit(&vlun->svl_mutex);
+						tptr = kmem_alloc(strlen
+						    (opinfo.opinfo_path_attr)+1,
+						    KM_SLEEP);
+						(void) strlcpy(tptr,
+						    opinfo.opinfo_path_attr,
+						    (strlen
+						    (opinfo.opinfo_path_attr)
+						    +1));
+						mutex_enter(&vlun->svl_mutex);
+					} else {
+						/*
+						 * No need to update
+						 * svl_active_pclass
+						 */
+						tptr = NULL;
+						mutex_exit(&vlun->svl_mutex);
+					}
+					if (tptr) {
+						if (vlun->svl_active_pclass
+						    != NULL) {
+							kmem_free(vlun->
+							    svl_active_pclass,
+							    strlen(vlun->
+							    svl_active_pclass)
+							    +1);
+						}
+						vlun->svl_active_pclass = tptr;
+						mutex_exit(&vlun->svl_mutex);
+					}
+				}
+			}
+		} else if ((opinfo.opinfo_path_state == SCSI_PATH_INACTIVE) &&
+		    !(MDI_PI_IS_STANDBY(pip))) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_update_pathstates: marking path"
+			    " 0x%p as STANDBY\n", (void *)pip));
+			pdip = mdi_pi_get_phci(pip);
+			cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+			dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+			vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s"
+			    " (%s%d): path %s (%s%d) target address %s"
+			    " is now STANDBY because of"
+			    " an externally initiated failover",
+			    ddi_pathname(dip, cpath),
+			    ddi_driver_name(dip),
+			    ddi_get_instance(dip),
+			    ddi_pathname(pdip, dpath),
+			    ddi_driver_name(pdip),
+			    ddi_get_instance(pdip),
+			    mdi_pi_get_addr(pip));
+			kmem_free(cpath, MAXPATHLEN);
+			kmem_free(dpath, MAXPATHLEN);
+			mdi_pi_set_state(pip,
+			    MDI_PATHINFO_STATE_STANDBY);
+			mdi_pi_set_preferred(pip,
+			    opinfo.opinfo_preferred);
+			mutex_enter(&vlun->svl_mutex);
+			if (vlun->svl_active_pclass != NULL) {
+				if (strcmp(vlun->svl_active_pclass,
+				    opinfo.opinfo_path_attr) == 0) {
+					kmem_free(vlun->
+					    svl_active_pclass,
+					    strlen(vlun->
+					    svl_active_pclass)+1);
+					vlun->svl_active_pclass = NULL;
+				}
+			}
+			mutex_exit(&vlun->svl_mutex);
+		}
+		(void) mdi_prop_free(pclass);
+		sps = mdi_select_path(dip, NULL,
+		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+		    pip, &npip);
+		mdi_rele_path(pip);
+
+	} while ((npip != NULL) && (sps == MDI_SUCCESS));
+
+	/*
+	 * Check to see if this vlun has an active SCSI-II RESERVE.  If so
+	 * clear the reservation by sending a reset, so the host doesn't
+	 * receive a reservation conflict.
+	 * Reset VLUN_RESERVE_ACTIVE_FLG for this vlun. Also notify ssd
+	 * of the reset, explicitly.
+	 */
+	if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
+		if (reserve_conflict && (vlun->svl_xlf_capable == 0)) {
+			(void) vhci_recovery_reset(vlun,
+			    &svp->svp_psd->sd_address, FALSE,
+			    VHCI_DEPTH_TARGET);
+		}
+		vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
+		mutex_enter(&vhci->vhci_mutex);
+		scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
+		    &vhci->vhci_reset_notify_listf);
+		mutex_exit(&vhci->vhci_mutex);
+	}
+	if (vlun->svl_flags & VLUN_UPDATE_TPG) {
+		/*
+		 * Update the AccessState of related MP-API TPGs
+		 */
+		(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
+		vlun->svl_flags &= ~VLUN_UPDATE_TPG;
+	}
+done:
+	if (vlun->svl_efo_update_path) {
+		vlun->svl_efo_update_path = 0;
+		vhci_efo_done(vlun->svl_swarg);
+		vlun->svl_swarg = 0;
+	}
+	VHCI_RELEASE_LUN(vlun);
+}
+
+/* ARGSUSED */
+static int
+vhci_pathinfo_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
+{
+	scsi_hba_tran_t		*hba = NULL;
+	struct scsi_device	*psd = NULL;
+	scsi_vhci_lun_t		*vlun = NULL;
+	dev_info_t		*pdip = NULL;
+	dev_info_t		*tgt_dip;
+	struct scsi_vhci	*vhci;
+	char			*guid;
+	scsi_vhci_priv_t	*svp = NULL;
+	int			rval = MDI_FAILURE;
+	int			vlun_alloced = 0;
+
+	ASSERT(vdip != NULL);
+	ASSERT(pip != NULL);
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+	ASSERT(vhci != NULL);
+
+	pdip = mdi_pi_get_phci(pip);
+	ASSERT(pdip != NULL);
+
+	hba = ddi_get_driver_private(pdip);
+	ASSERT(hba != NULL);
+
+	tgt_dip = mdi_pi_get_client(pip);
+	ASSERT(tgt_dip != NULL);
+
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip, PROPFLAGS,
+	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "vhci_pathinfo_init: lun guid property failed"));
+		goto failure;
+	}
+
+	vlun = vhci_lun_lookup_alloc(tgt_dip, guid, &vlun_alloced);
+	ddi_prop_free(guid);
+
+	vlun->svl_dip = tgt_dip;
+
+	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
+	svp->svp_svl = vlun;
+
+	vlun->svl_lb_policy_save = mdi_get_lb_policy(tgt_dip);
+	mutex_init(&svp->svp_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&svp->svp_cv, NULL, CV_DRIVER, NULL);
+
+	psd = kmem_zalloc(sizeof (*psd), KM_SLEEP);
+	mutex_init(&psd->sd_mutex, NULL, MUTEX_DRIVER, NULL);
+
+	/*
+	 * Clone transport structure if requested, so
+	 * Self enumerating HBAs always need to use cloning
+	 */
+
+	if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
+		scsi_hba_tran_t	*clone =
+		    kmem_alloc(sizeof (scsi_hba_tran_t), KM_SLEEP);
+		bcopy(hba, clone, sizeof (scsi_hba_tran_t));
+		hba = clone;
+		hba->tran_sd = psd;
+	} else {
+		ASSERT(hba->tran_sd == NULL);
+	}
+	psd->sd_dev = tgt_dip;
+	psd->sd_address.a_hba_tran = hba;
+	psd->sd_private = (caddr_t)pip;
+	svp->svp_psd = psd;
+	mdi_pi_set_vhci_private(pip, (caddr_t)svp);
+
+	/*
+	 * call hba's target init entry point if it exists
+	 */
+	if (hba->tran_tgt_init != NULL) {
+		if ((rval = (*hba->tran_tgt_init)(pdip, tgt_dip,
+		    hba, psd)) != DDI_SUCCESS) {
+			VHCI_DEBUG(1, (CE_WARN, pdip,
+			    "!vhci_pathinfo_init: tran_tgt_init failed for "
+			    "path=0x%p rval=%x", (void *)pip, rval));
+			goto failure;
+		}
+	}
+
+	svp->svp_new_path = 1;
+
+	psd->sd_inq = NULL;
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_pathinfo_init: path:%p\n",
+	    (void *)pip));
+	return (MDI_SUCCESS);
+
+failure:
+	if (psd) {
+		mutex_destroy(&psd->sd_mutex);
+		kmem_free(psd, sizeof (*psd));
+	}
+	if (svp) {
+		mdi_pi_set_vhci_private(pip, NULL);
+		mutex_destroy(&svp->svp_mutex);
+		cv_destroy(&svp->svp_cv);
+		kmem_free(svp, sizeof (*svp));
+	}
+	if (hba && hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE)
+		kmem_free(hba, sizeof (scsi_hba_tran_t));
+
+	if (vlun_alloced)
+		vhci_lun_free(tgt_dip);
+
+	return (rval);
+}
+
+/* ARGSUSED */
+static int
+vhci_pathinfo_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
+{
+	scsi_hba_tran_t		*hba = NULL;
+	struct scsi_device	*psd = NULL;
+	dev_info_t		*pdip = NULL;
+	dev_info_t		*cdip = NULL;
+	scsi_vhci_priv_t	*svp = NULL;
+
+	ASSERT(vdip != NULL);
+	ASSERT(pip != NULL);
+
+	pdip = mdi_pi_get_phci(pip);
+	ASSERT(pdip != NULL);
+
+	cdip = mdi_pi_get_client(pip);
+	ASSERT(cdip != NULL);
+
+	hba = ddi_get_driver_private(pdip);
+	ASSERT(hba != NULL);
+
+	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_REMOVED);
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		/* path already freed. Nothing to do. */
+		return (MDI_SUCCESS);
+	}
+
+	psd = svp->svp_psd;
+	ASSERT(psd != NULL);
+
+	if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
+		hba = psd->sd_address.a_hba_tran;
+		ASSERT(hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE);
+		ASSERT(hba->tran_sd == psd);
+	} else {
+		ASSERT(hba->tran_sd == NULL);
+	}
+
+	if (hba->tran_tgt_free != NULL) {
+		(*hba->tran_tgt_free) (pdip, cdip, hba, psd);
+	}
+	mutex_destroy(&psd->sd_mutex);
+	if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) {
+		kmem_free(hba, sizeof (*hba));
+	}
+
+	mdi_pi_set_vhci_private(pip, NULL);
+	kmem_free((caddr_t)psd, sizeof (*psd));
+
+	mutex_destroy(&svp->svp_mutex);
+	cv_destroy(&svp->svp_cv);
+	kmem_free((caddr_t)svp, sizeof (*svp));
+
+	/*
+	 * If this is the last path to the client,
+	 * then free up the vlun as well.
+	 */
+	if (mdi_client_get_path_count(cdip) == 1) {
+		vhci_lun_free(cdip);
+	}
+
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_pathinfo_uninit: path=0x%p\n",
+	    (void *)pip));
+	return (MDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+vhci_pathinfo_state_change(dev_info_t *vdip, mdi_pathinfo_t *pip,
+    mdi_pathinfo_state_t state, uint32_t ext_state, int flags)
+{
+	int			rval = MDI_SUCCESS;
+	scsi_vhci_priv_t	*svp;
+	scsi_vhci_lun_t		*vlun;
+	int			held;
+	int			op = (flags & 0xf00) >> 8;
+	struct scsi_vhci	*vhci;
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+
+	if (flags & MDI_EXT_STATE_CHANGE) {
+		/*
+		 * We do not want to issue any commands down the path in case
+		 * sync flag is set. Lower layers might not be ready to accept
+		 * any I/O commands.
+		 */
+		if (op == DRIVER_DISABLE)
+			return (MDI_SUCCESS);
+
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		if (svp == NULL) {
+			return (MDI_FAILURE);
+		}
+		vlun = svp->svp_svl;
+
+		if (flags & MDI_BEFORE_STATE_CHANGE) {
+			/*
+			 * Hold the LUN.
+			 */
+			VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
+			if (flags & MDI_DISABLE_OP)  {
+				/*
+				 * Issue scsi reset if it happens to be
+				 * reserved path.
+				 */
+				if (vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
+					/*
+					 * if reservation pending on
+					 * this path, dont' mark the
+					 * path busy
+					 */
+					if (op == DRIVER_DISABLE_TRANSIENT) {
+						VHCI_DEBUG(1, (CE_NOTE, NULL,
+						    "!vhci_pathinfo"
+						    "_state_change (pip:%p): "
+						    " reservation: fail busy\n",
+						    (void *)pip));
+						return (MDI_FAILURE);
+					}
+					if (pip == vlun->svl_resrv_pip) {
+						if (vhci_recovery_reset(
+						    svp->svp_svl,
+						    &svp->svp_psd->sd_address,
+						    TRUE,
+						    VHCI_DEPTH_TARGET) == 0) {
+							VHCI_DEBUG(1,
+							    (CE_NOTE, NULL,
+							    "!vhci_pathinfo"
+							    "_state_change "
+							    " (pip:%p): "
+							    "reset failed, "
+							    "give up!\n",
+							    (void *)pip));
+						}
+						vlun->svl_flags &=
+						    ~VLUN_RESERVE_ACTIVE_FLG;
+					}
+				}
+			} else if (flags & MDI_ENABLE_OP)  {
+				if (((vhci->vhci_conf_flags &
+				    VHCI_CONF_FLAGS_AUTO_FAILBACK) ==
+				    VHCI_CONF_FLAGS_AUTO_FAILBACK) &&
+				    MDI_PI_IS_USER_DISABLE(pip) &&
+				    MDI_PI_IS_STANDBY(pip)) {
+					struct scsi_failover_ops	*fo;
+					char *best_pclass, *pclass = NULL;
+					int  best_class, rv;
+					/*
+					 * Failback if enabling a standby path
+					 * and it is the primary class or
+					 * preferred class
+					 */
+					best_class = mdi_pi_get_preferred(pip);
+					if (best_class == 0) {
+						/*
+						 * if not preferred - compare
+						 * path-class with class
+						 */
+						fo = vlun->svl_fops;
+						(*fo->sfo_pathclass_next)(NULL,
+						    &best_pclass,
+						    vlun->svl_fops_ctpriv);
+						pclass = NULL;
+						rv = mdi_prop_lookup_string(pip,
+						    "path-class", &pclass);
+						if (rv != MDI_SUCCESS ||
+						    pclass == NULL) {
+							vhci_log(CE_NOTE, vdip,
+							    "!path-class "
+							    " lookup "
+							    "failed. rv: %d"
+							    "class: %p", rv,
+							    (void *)pclass);
+						} else if (strncmp(pclass,
+						    best_pclass,
+						    strlen(best_pclass)) == 0) {
+							best_class = 1;
+						}
+						if (rv == MDI_SUCCESS &&
+						    pclass != NULL) {
+							rv = mdi_prop_free(
+							    pclass);
+							if (rv !=
+							    DDI_PROP_SUCCESS) {
+								vhci_log(
+								    CE_NOTE,
+								    vdip,
+								    "!path-"
+								    "class"
+								    " free"
+								    " failed"
+								    " rv: %d"
+								    " class: "
+								    "%p",
+								    rv,
+								    (void *)
+								    pclass);
+							}
+						}
+					}
+					if (best_class == 1) {
+						VHCI_DEBUG(1, (CE_NOTE, NULL,
+						    "preferred path: %p "
+						    "USER_DISABLE->USER_ENABLE "
+						    "transition for lun %s\n",
+						    (void *)pip,
+						    vlun->svl_lun_wwn));
+						(void) taskq_dispatch(
+						    vhci->vhci_taskq,
+						    vhci_initiate_auto_failback,
+						    (void *) vlun, KM_SLEEP);
+					}
+				}
+				/*
+				 * if PGR is active, revalidate key and
+				 * register on this path also, if key is
+				 * still valid
+				 */
+				sema_p(&vlun->svl_pgr_sema);
+				if (vlun->svl_pgr_active)
+					(void)
+					    vhci_pgr_validate_and_register(svp);
+				sema_v(&vlun->svl_pgr_sema);
+				/*
+				 * Inform target driver about any
+				 * reservations to be reinstated if target
+				 * has dropped reservation during the busy
+				 * period.
+				 */
+				mutex_enter(&vhci->vhci_mutex);
+				scsi_hba_reset_notify_callback(
+				    &vhci->vhci_mutex,
+				    &vhci->vhci_reset_notify_listf);
+				mutex_exit(&vhci->vhci_mutex);
+			}
+		}
+		if (flags & MDI_AFTER_STATE_CHANGE) {
+			if (flags & MDI_ENABLE_OP)  {
+				mutex_enter(&vhci_global_mutex);
+				cv_broadcast(&vhci_cv);
+				mutex_exit(&vhci_global_mutex);
+			}
+			if (vlun->svl_setcap_done) {
+				(void) vhci_pHCI_cap(&svp->svp_psd->sd_address,
+				    "sector-size", vlun->svl_sector_size,
+				    1, pip);
+			}
+
+			/*
+			 * Release the LUN
+			 */
+			VHCI_RELEASE_LUN(vlun);
+
+			/*
+			 * Path transition is complete.
+			 * Run callback to indicate target driver to
+			 * retry to prevent IO starvation.
+			 */
+			if (scsi_callback_id != 0) {
+				ddi_run_callback(&scsi_callback_id);
+			}
+		}
+	} else {
+		switch (state) {
+		case MDI_PATHINFO_STATE_ONLINE:
+			rval = vhci_pathinfo_online(vdip, pip, flags);
+			break;
+
+		case MDI_PATHINFO_STATE_OFFLINE:
+			rval = vhci_pathinfo_offline(vdip, pip, flags);
+			break;
+
+		default:
+			break;
+		}
+		/*
+		 * Path transition is complete.
+		 * Run callback to indicate target driver to
+		 * retry to prevent IO starvation.
+		 */
+		if ((rval == MDI_SUCCESS) && (scsi_callback_id != 0)) {
+			ddi_run_callback(&scsi_callback_id);
+		}
+		return (rval);
+	}
+
+	return (MDI_SUCCESS);
+}
+
+/*
+ * Parse the mpxio load balancing options. The datanameptr
+ * will point to a string containing the load-balance-options value.
+ * The load-balance-options value will be a property that
+ * defines the load-balance algorithm and any arguments to that
+ * algorithm.
+ * For example:
+ * device-type-mpxio-options-list=
+ * "device-type=SUN    SENA", "load-balance-options=logical-block-options"
+ * "device-type=SUN     SE6920", "round-robin-options";
+ * logical-block-options="load-balance=logical-block", "region-size=15";
+ * round-robin-options="load-balance=round-robin";
+ *
+ * If the load-balance is not defined the load balance algorithm will
+ * default to the global setting. There will be default values assigned
+ * to the arguments (region-size=18) and if an argument is one
+ * that is not known, it will be ignored.
+ */
+static void
+vhci_parse_mpxio_lb_options(dev_info_t *dip, dev_info_t *cdip,
+	caddr_t datanameptr)
+{
+	char			*dataptr, *next_entry;
+	caddr_t			config_list	= NULL;
+	int			config_list_len = 0, list_len = 0;
+	int			region_size = -1;
+	client_lb_t		load_balance;
+
+	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, datanameptr,
+	    (caddr_t)&config_list, &config_list_len) != DDI_PROP_SUCCESS) {
+		return;
+	}
+
+	list_len = config_list_len;
+	next_entry = config_list;
+	while (config_list_len > 0) {
+		dataptr = next_entry;
+
+		if (strncmp(mdi_load_balance, dataptr,
+		    strlen(mdi_load_balance)) == 0) {
+			/* get the load-balance scheme */
+			dataptr += strlen(mdi_load_balance) + 1;
+			if (strcmp(dataptr, LOAD_BALANCE_PROP_RR) == 0) {
+				(void) mdi_set_lb_policy(cdip, LOAD_BALANCE_RR);
+				load_balance = LOAD_BALANCE_RR;
+			} else if (strcmp(dataptr,
+			    LOAD_BALANCE_PROP_LBA) == 0) {
+				(void) mdi_set_lb_policy(cdip,
+				    LOAD_BALANCE_LBA);
+				load_balance = LOAD_BALANCE_LBA;
+			} else if (strcmp(dataptr,
+			    LOAD_BALANCE_PROP_NONE) == 0) {
+				(void) mdi_set_lb_policy(cdip,
+				    LOAD_BALANCE_NONE);
+				load_balance = LOAD_BALANCE_NONE;
+			}
+		} else if (strncmp(dataptr, LOGICAL_BLOCK_REGION_SIZE,
+		    strlen(LOGICAL_BLOCK_REGION_SIZE)) == 0) {
+			int	i = 0;
+			char	*ptr;
+			char	*tmp;
+
+			tmp = dataptr + (strlen(LOGICAL_BLOCK_REGION_SIZE) + 1);
+			/* check for numeric value */
+			for (ptr = tmp; i < strlen(tmp); i++, ptr++) {
+				if (!isdigit(*ptr)) {
+					cmn_err(CE_WARN,
+					    "Illegal region size: %s."
+					    " Setting to default value: %d",
+					    tmp,
+					    LOAD_BALANCE_DEFAULT_REGION_SIZE);
+					region_size =
+					    LOAD_BALANCE_DEFAULT_REGION_SIZE;
+					break;
+				}
+			}
+			if (i >= strlen(tmp)) {
+				region_size = stoi(&tmp);
+			}
+			(void) mdi_set_lb_region_size(cdip, region_size);
+		}
+		config_list_len -= (strlen(next_entry) + 1);
+		next_entry += strlen(next_entry) + 1;
+	}
+#ifdef DEBUG
+	if ((region_size >= 0) && (load_balance != LOAD_BALANCE_LBA)) {
+		VHCI_DEBUG(1, (CE_NOTE, dip,
+		    "!vhci_parse_mpxio_lb_options: region-size: %d"
+		    "only valid for load-balance=logical-block\n",
+		    region_size));
+	}
+#endif
+	if ((region_size == -1) && (load_balance == LOAD_BALANCE_LBA)) {
+		VHCI_DEBUG(1, (CE_NOTE, dip,
+		    "!vhci_parse_mpxio_lb_options: No region-size"
+		    " defined load-balance=logical-block."
+		    " Default to: %d\n", LOAD_BALANCE_DEFAULT_REGION_SIZE));
+		(void) mdi_set_lb_region_size(cdip,
+		    LOAD_BALANCE_DEFAULT_REGION_SIZE);
+	}
+	if (list_len > 0) {
+		kmem_free(config_list, list_len);
+	}
+}
+
+/*
+ * Parse the device-type-mpxio-options-list looking for the key of
+ * "load-balance-options". If found, parse the load balancing options.
+ * Check the comment of the vhci_get_device_type_mpxio_options()
+ * for the device-type-mpxio-options-list.
+ */
+static void
+vhci_parse_mpxio_options(dev_info_t *dip, dev_info_t *cdip,
+		caddr_t datanameptr, int list_len)
+{
+	char		*dataptr;
+	int		len;
+
+	/*
+	 * get the data list
+	 */
+	dataptr = datanameptr;
+	len = 0;
+	while (len < list_len &&
+	    strncmp(dataptr, DEVICE_TYPE_STR, strlen(DEVICE_TYPE_STR))
+	    != 0) {
+		if (strncmp(dataptr, LOAD_BALANCE_OPTIONS,
+		    strlen(LOAD_BALANCE_OPTIONS)) == 0) {
+			len += strlen(LOAD_BALANCE_OPTIONS) + 1;
+			dataptr += strlen(LOAD_BALANCE_OPTIONS) + 1;
+			vhci_parse_mpxio_lb_options(dip, cdip, dataptr);
+		}
+		len += strlen(dataptr) + 1;
+		dataptr += strlen(dataptr) + 1;
+	}
+}
+
+/*
+ * Check the inquriy string returned from the device wiith the device-type
+ * Check for the existence of the device-type-mpxio-options-list and
+ * if found parse the list checking for a match with the device-type
+ * value and the inquiry string returned from the device. If a match
+ * is found, parse the mpxio options list. The format of the
+ * device-type-mpxio-options-list is:
+ * device-type-mpxio-options-list=
+ * "device-type=SUN    SENA", "load-balance-options=logical-block-options"
+ * "device-type=SUN     SE6920", "round-robin-options";
+ * logical-block-options="load-balance=logical-block", "region-size=15";
+ * round-robin-options="load-balance=round-robin";
+ */
+void
+vhci_get_device_type_mpxio_options(dev_info_t *dip, dev_info_t *cdip,
+	struct scsi_device *devp)
+{
+
+	caddr_t			config_list	= NULL;
+	caddr_t			vidptr, datanameptr;
+	int			vidlen, dupletlen = 0;
+	int			config_list_len = 0, len;
+	struct scsi_inquiry	*inq = devp->sd_inq;
+
+	/*
+	 * look up the device-type-mpxio-options-list and walk thru
+	 * the list compare the vendor ids of the earlier inquiry command and
+	 * with those vids in the list if there is a match, lookup
+	 * the mpxio-options value
+	 */
+	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+	    MPXIO_OPTIONS_LIST,
+	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
+
+		/*
+		 * Compare vids in each duplet - if it matches,
+		 * parse the mpxio options list.
+		 */
+		for (len = config_list_len, vidptr = config_list; len > 0;
+		    len -= dupletlen) {
+
+			dupletlen = 0;
+
+			if (strlen(vidptr) != 0 &&
+			    strncmp(vidptr, DEVICE_TYPE_STR,
+			    strlen(DEVICE_TYPE_STR)) == 0) {
+				/* point to next duplet */
+				datanameptr = vidptr + strlen(vidptr) + 1;
+				/* add len of this duplet */
+				dupletlen += strlen(vidptr) + 1;
+				/* get to device type */
+				vidptr += strlen(DEVICE_TYPE_STR) + 1;
+				vidlen = strlen(vidptr);
+				if ((vidlen != 0) &&
+				    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
+					vhci_parse_mpxio_options(dip, cdip,
+					    datanameptr, len - dupletlen);
+					break;
+				}
+				/* get to next duplet */
+				vidptr += strlen(vidptr) + 1;
+			}
+			/* get to the next device-type */
+			while (len - dupletlen > 0 &&
+			    strlen(vidptr) != 0 &&
+			    strncmp(vidptr, DEVICE_TYPE_STR,
+			    strlen(DEVICE_TYPE_STR)) != 0) {
+				dupletlen += strlen(vidptr) + 1;
+				vidptr += strlen(vidptr) + 1;
+			}
+		}
+		if (config_list_len > 0) {
+			kmem_free(config_list, config_list_len);
+		}
+	}
+}
+
+static int
+vhci_update_pathinfo(struct scsi_device *psd,  mdi_pathinfo_t *pip,
+	struct scsi_failover_ops *fo,
+	scsi_vhci_lun_t		*vlun,
+	struct scsi_vhci	*vhci)
+{
+	struct scsi_path_opinfo		opinfo;
+	char				*pclass, *best_pclass;
+
+	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);
+	}
+	/* set the xlf capable flag in the vlun for future use */
+	vlun->svl_xlf_capable = opinfo.opinfo_xlf_capable;
+	(void) mdi_prop_update_string(pip, "path-class",
+	    opinfo.opinfo_path_attr);
+
+	pclass = opinfo.opinfo_path_attr;
+	if (opinfo.opinfo_path_state == SCSI_PATH_ACTIVE) {
+		mutex_enter(&vlun->svl_mutex);
+		if (vlun->svl_active_pclass != NULL) {
+			if (strcmp(vlun->svl_active_pclass, pclass) != 0) {
+				mutex_exit(&vlun->svl_mutex);
+				/*
+				 * Externally initiated failover has happened;
+				 * force the path state to be STANDBY/ONLINE,
+				 * next IO will trigger failover and thus
+				 * sync-up the pathstates.  Reason we don't
+				 * sync-up immediately by invoking
+				 * vhci_update_pathstates() is because it
+				 * needs a VHCI_HOLD_LUN() and we don't
+				 * want to block here.
+				 *
+				 * Further, if the device is an ALUA device,
+				 * then failure to exactly match 'pclass' and
+				 * 'svl_active_pclass'(as is the case here)
+				 * indicates that the currently active path
+				 * is a 'non-optimized' path - which means
+				 * that 'svl_active_pclass' needs to be
+				 * replaced with opinfo.opinfo_path_state
+				 * value.
+				 */
+
+				if (SCSI_FAILOVER_IS_TPGS(vlun->svl_fops)) {
+					char	*tptr;
+
+					/*
+					 * The device is ALUA compliant. The
+					 * state need to be changed to online
+					 * rather than standby state which is
+					 * done typically for a asymmetric
+					 * device that is non ALUA compliant.
+					 */
+					mdi_pi_set_state(pip,
+					    MDI_PATHINFO_STATE_ONLINE);
+					tptr = kmem_alloc(strlen
+					    (opinfo.opinfo_path_attr)+1,
+					    KM_SLEEP);
+					(void) strlcpy(tptr,
+					    opinfo.opinfo_path_attr,
+					    (strlen(opinfo.opinfo_path_attr)
+					    +1));
+					mutex_enter(&vlun->svl_mutex);
+					kmem_free(vlun->svl_active_pclass,
+					    strlen(vlun->svl_active_pclass)+1);
+					vlun->svl_active_pclass = tptr;
+					mutex_exit(&vlun->svl_mutex);
+				} else {
+					/*
+					 * Non ALUA device case.
+					 */
+					mdi_pi_set_state(pip,
+					    MDI_PATHINFO_STATE_STANDBY);
+				}
+				vlun->svl_fo_support = opinfo.opinfo_mode;
+				mdi_pi_set_preferred(pip,
+				    opinfo.opinfo_preferred);
+				return (MDI_SUCCESS);
+			}
+		} else {
+			char	*tptr;
+
+			/*
+			 * lets release the mutex before we try to
+			 * allocate since the potential to sleep is
+			 * possible.
+			 */
+			mutex_exit(&vlun->svl_mutex);
+			tptr = kmem_alloc(strlen(pclass)+1, KM_SLEEP);
+			(void) strlcpy(tptr, pclass, (strlen(pclass)+1));
+			mutex_enter(&vlun->svl_mutex);
+			vlun->svl_active_pclass = tptr;
+		}
+		mutex_exit(&vlun->svl_mutex);
+		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_ONLINE);
+		vlun->svl_waiting_for_activepath = 0;
+	} else if (opinfo.opinfo_path_state == SCSI_PATH_ACTIVE_NONOPT) {
+		mutex_enter(&vlun->svl_mutex);
+		if (vlun->svl_active_pclass == NULL) {
+			char	*tptr;
+
+			mutex_exit(&vlun->svl_mutex);
+			tptr = kmem_alloc(strlen(pclass)+1, KM_SLEEP);
+			(void) strlcpy(tptr, pclass, (strlen(pclass)+1));
+			mutex_enter(&vlun->svl_mutex);
+			vlun->svl_active_pclass = tptr;
+		}
+		mutex_exit(&vlun->svl_mutex);
+		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_ONLINE);
+		vlun->svl_waiting_for_activepath = 0;
+	} else if (opinfo.opinfo_path_state == SCSI_PATH_INACTIVE) {
+		mutex_enter(&vlun->svl_mutex);
+		if (vlun->svl_active_pclass != NULL) {
+			if (strcmp(vlun->svl_active_pclass, pclass) == 0) {
+				mutex_exit(&vlun->svl_mutex);
+				/*
+				 * externally initiated failover has happened;
+				 * force state to ONLINE (see comment above)
+				 */
+				mdi_pi_set_state(pip,
+				    MDI_PATHINFO_STATE_ONLINE);
+				vlun->svl_fo_support = opinfo.opinfo_mode;
+				mdi_pi_set_preferred(pip,
+				    opinfo.opinfo_preferred);
+				return (MDI_SUCCESS);
+			}
+		}
+		mutex_exit(&vlun->svl_mutex);
+		mdi_pi_set_state(pip, MDI_PATHINFO_STATE_STANDBY);
+
+		/*
+		 * Initiate auto-failback, if enabled, for path if path-state
+		 * is transitioning from OFFLINE->STANDBY and pathclass is the
+		 * prefered pathclass for this storage.
+		 * NOTE: In case where opinfo_path_state is SCSI_PATH_ACTIVE
+		 * (above), where the pi state is set to STANDBY, we don't
+		 * initiate auto-failback as the next IO shall take care of.
+		 * this. See comment above.
+		 */
+		(*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) &&
+		    ((strcmp(pclass, best_pclass) == 0) ||
+		    mdi_pi_get_preferred(pip) == 1) &&
+		    ((MDI_PI_OLD_STATE(pip) == MDI_PATHINFO_STATE_OFFLINE)||
+		    (MDI_PI_OLD_STATE(pip) == MDI_PATHINFO_STATE_INIT))) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "%s pathclass path: %p"
+			    " OFFLINE->STANDBY transition for lun %s\n",
+			    best_pclass, (void *)pip, vlun->svl_lun_wwn));
+			(void) taskq_dispatch(vhci->vhci_taskq,
+			    vhci_initiate_auto_failback, (void *) vlun,
+			    KM_SLEEP);
+		}
+	}
+	vlun->svl_fo_support = opinfo.opinfo_mode;
+	mdi_pi_set_preferred(pip, opinfo.opinfo_preferred);
+
+	VHCI_DEBUG(8, (CE_NOTE, NULL, "vhci_update_pathinfo: opinfo_rev = %x,"
+	    " opinfo_path_state = %x opinfo_preferred = %x, opinfo_mode = %x\n",
+	    opinfo.opinfo_rev, opinfo.opinfo_path_state,
+	    opinfo.opinfo_preferred, opinfo.opinfo_mode));
+
+	return (MDI_SUCCESS);
+}
+
+/*
+ * Form the kstat name and and call mdi_pi_kstat_create()
+ */
+void
+vhci_kstat_create_pathinfo(mdi_pathinfo_t *pip)
+{
+	dev_info_t	*tgt_dip;
+	dev_info_t	*pdip;
+	char		*guid;
+	char		*target_port, *target_port_dup;
+	char		ks_name[KSTAT_STRLEN];
+	uint_t		pid;
+	int		by_id;
+	mod_hash_val_t	hv;
+
+
+	/* return if we have already allocated kstats */
+	if (mdi_pi_kstat_exists(pip))
+		return;
+
+	/*
+	 * We need instance numbers to create a kstat name, return if we don't
+	 * have instance numbers assigned yet.
+	 */
+	tgt_dip = mdi_pi_get_client(pip);
+	pdip = mdi_pi_get_phci(pip);
+	if ((ddi_get_instance(tgt_dip) == -1) || (ddi_get_instance(pdip) == -1))
+		return;
+
+	/*
+	 * A path oriented kstat has a ks_name of the form:
+	 *
+	 * <client-driver><instance>.t<pid>.<pHCI-driver><instance>
+	 *
+	 * We maintain a bidirectional 'target-port' to <pid> map,
+	 * called targetmap. All pathinfo nodes with the same
+	 * 'target-port' map to the same <pid>. The iostat(1M) code,
+	 * when parsing a path oriented kstat name, uses the <pid> as
+	 * a SCSI_VHCI_GET_TARGET_LONGNAME ioctl argument in order
+	 * to get the 'target-port'. For KSTAT_FLAG_PERSISTENT kstats,
+	 * this ioctl needs to translate a <pid> to a 'target-port'
+	 * even after all pathinfo nodes associated with the
+	 * 'target-port' have been destroyed. This is needed to support
+	 * consistent first-iteration activity-since-boot iostat(1M)
+	 * output. Because of this requirement, the mapping can't be
+	 * based on pathinfo information in a devinfo snapshot.
+	 */
+
+	/* determine 'target-port' */
+	if (mdi_prop_lookup_string(pip,
+	    "target-port", &target_port) == MDI_SUCCESS) {
+		target_port_dup = i_ddi_strdup(target_port, KM_SLEEP);
+		(void) mdi_prop_free(target_port);
+		by_id = 1;
+	} else {
+		/*
+		 * If the pHCI did not set up 'target-port' on this
+		 * pathinfo node, assume that our client is the only
+		 * one with paths to the device by using the guid
+		 * value as the 'target-port'. Since no other client
+		 * will have the same guid, no other client will use
+		 * the same <pid>.  NOTE: a client with an instance
+		 * number always has a guid.
+		 */
+		(void) ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip,
+		    PROPFLAGS, MDI_CLIENT_GUID_PROP, &guid);
+		target_port_dup = i_ddi_strdup(guid, KM_SLEEP);
+		ddi_prop_free(guid);
+
+		/*
+		 * For this type of mapping we don't want the
+		 * <id> -> 'target-port' mapping to be made.  This
+		 * will cause the SCSI_VHCI_GET_TARGET_LONGNAME ioctl
+		 * to fail, and the iostat(1M) long '-n' output will
+		 * still use the <pid>.  We do this because we just
+		 * made up the 'target-port' using the guid, and we
+		 * don't want to expose that fact in iostat output.
+		 */
+		by_id = 0;
+	}
+
+	/* find/establish <pid> given 'target-port' */
+	mutex_enter(&vhci_targetmap_mutex);
+	if (mod_hash_find(vhci_targetmap_byport,
+	    (mod_hash_key_t)target_port_dup, &hv) == 0) {
+		pid = (int)(intptr_t)hv;	/* mapping exists */
+	} else {
+		pid = vhci_targetmap_pid++;	/* new mapping */
+
+		(void) mod_hash_insert(vhci_targetmap_byport,
+		    (mod_hash_key_t)target_port_dup,
+		    (mod_hash_val_t)(intptr_t)pid);
+		if (by_id) {
+			(void) mod_hash_insert(vhci_targetmap_bypid,
+			    (mod_hash_key_t)(uintptr_t)pid,
+			    (mod_hash_val_t)(uintptr_t)target_port_dup);
+		}
+		target_port_dup = NULL;		/* owned by hash */
+	}
+	mutex_exit(&vhci_targetmap_mutex);
+
+	/* form kstat name */
+	(void) snprintf(ks_name, KSTAT_STRLEN, "%s%d.t%d.%s%d",
+	    ddi_driver_name(tgt_dip), ddi_get_instance(tgt_dip),
+	    pid, ddi_driver_name(pdip), ddi_get_instance(pdip));
+
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_path_online: path:%p "
+	    "kstat %s: pid %x <-> port %s\n", (void *)pip,
+	    ks_name, pid, target_port_dup));
+	if (target_port_dup)
+		kmem_free(target_port_dup, strlen(target_port_dup) + 1);
+
+	/* call mdi to create kstats with the name we built */
+	(void) mdi_pi_kstat_create(pip, ks_name);
+}
+
+/* ARGSUSED */
+static int
+vhci_pathinfo_online(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
+{
+	scsi_hba_tran_t			*hba = NULL;
+	struct scsi_device		*psd = NULL;
+	scsi_vhci_lun_t			*vlun = NULL;
+	dev_info_t			*pdip = NULL;
+	dev_info_t			*tgt_dip;
+	struct scsi_vhci		*vhci;
+	char				*guid;
+	struct scsi_failover		*sf;
+	struct scsi_failover_ops	*sfo;
+	char				*override;
+	scsi_vhci_priv_t		*svp = NULL;
+	struct buf			*bp;
+	struct scsi_address		*ap;
+	struct scsi_pkt			*pkt;
+	int				rval = MDI_FAILURE;
+	uint_t				inq_size = VHCI_STD_INQ_SIZE;
+	mpapi_item_list_t		*list_ptr;
+	mpapi_lu_data_t			*ld;
+
+	ASSERT(vdip != NULL);
+	ASSERT(pip != NULL);
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+	ASSERT(vhci != NULL);
+
+	pdip = mdi_pi_get_phci(pip);
+	hba = ddi_get_driver_private(pdip);
+	ASSERT(hba != NULL);
+
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	ASSERT(svp != NULL);
+
+	tgt_dip = mdi_pi_get_client(pip);
+	ASSERT(tgt_dip != NULL);
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip, PROPFLAGS,
+	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "vhci_path_online: lun guid "
+		    "property failed"));
+		goto failure;
+	}
+
+	vlun = vhci_lun_lookup(tgt_dip);
+	ASSERT(vlun != NULL);
+
+	ddi_prop_free(guid);
+
+	vlun->svl_dip = mdi_pi_get_client(pip);
+	ASSERT(vlun->svl_dip != NULL);
+
+	psd = svp->svp_psd;
+	ASSERT(psd != NULL);
+
+	/*
+	 * For INQUIRY response buffer size, we use VHCI_STD_INQ_SIZE(128bytes)
+	 * instead of SUN_INQSIZE(48bytes) which is used in sd layer. This is
+	 * because we could get the Vendor specific parameters(present 97th
+	 * byte onwards) which are required to process Vendor specific data
+	 * based on array type.
+	 * This INQUIRY buffer is freed in vhci_pathinfo_offline but NEVER
+	 * in a different layer like sd/phci transport. In other words, vhci
+	 * maintains its own copy of scsi_device and scsi_inquiry data on a
+	 * per-path basis.
+	 */
+	if (psd->sd_inq == NULL) {
+		psd->sd_inq = (struct scsi_inquiry *)
+		    kmem_zalloc(inq_size, KM_SLEEP);
+	}
+
+	tgt_dip = psd->sd_dev;
+	ASSERT(tgt_dip != NULL);
+
+	/*
+	 * do inquiry to pass into probe routine; this
+	 * will avoid each probe routine doing scsi inquiry
+	 */
+	bp = getrbuf(KM_SLEEP);
+	bp->b_un.b_addr = (caddr_t)psd->sd_inq;
+	bp->b_flags = B_READ;
+	bp->b_bcount = inq_size;
+	bp->b_resid = 0;
+
+	ap = &psd->sd_address;
+	pkt = scsi_init_pkt(ap, NULL, bp, CDB_GROUP0,
+	    sizeof (struct scsi_arq_status), 0, 0, SLEEP_FUNC, NULL);
+	if (pkt == NULL) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_pathinfo_online: "
+		    "Inquiry init_pkt failed :%p\n", (void *)pip));
+		rval = MDI_FAILURE;
+		goto failure;
+	}
+	pkt->pkt_cdbp[0] = SCMD_INQUIRY;
+	pkt->pkt_cdbp[4] = (uchar_t)inq_size;
+	pkt->pkt_time = 60;
+
+	rval = vhci_do_scsi_cmd(pkt);
+	scsi_destroy_pkt(pkt);
+	freerbuf(bp);
+	if (rval == 0) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_pathinfo_online: "
+		    "Failover Inquiry failed path:%p rval:%x\n",
+		    (void *)pip, rval));
+		rval = MDI_FAILURE;
+		goto failure;
+	}
+
+	/*
+	 * Determine if device is supported under scsi_vhci, and select
+	 * failover module.
+	 *
+	 * See if there is a scsi_vhci.conf file override for this devices's
+	 * VID/PID. The following values can be returned:
+	 *
+	 * NULL		If the NULL is returned then there is no scsi_vhci.conf
+	 *		override.  For NULL, we determine the failover_ops for
+	 *		this device by checking the sfo_device_probe entry
+	 *		point for each 'fops' module, in order.
+	 *
+	 *		NOTE: Correct operation may depend on module ordering
+	 *		of 'specific' (failover modules that are completely
+	 *		VID/PID table based) to 'generic' (failover modules
+	 *		that based on T10 standards like TPGS).  Currently,
+	 *		the value of 'ddi-forceload' in scsi_vhci.conf is used
+	 *		to establish the module list and probe order.
+	 *
+	 * "NONE"	If value "NONE" is returned then there is a
+	 *		scsi_vhci.conf VID/PID override to indicate the device
+	 *		should not be supported under scsi_vhci (even if there
+	 *		is an 'fops' module supporting the device).
+	 *
+	 * "<other>"	If another value is returned then that value is the
+	 *		name of the 'fops' module that should be used.
+	 */
+	sfo = NULL;	/* "NONE" */
+	override = scsi_get_device_type_string(
+	    "scsi-vhci-failover-override", vdip, psd);
+
+	if (override == NULL) {
+		/* 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))
+				continue;
+
+			/* found failover module, supported under scsi_vhci */
+			sfo = sf->sf_sfo;
+			vlun->svl_fops_name =
+			    i_ddi_strdup(sfo->sfo_name, KM_SLEEP);
+			break;
+		}
+	} else if (strcmp(override, "NONE") && strcmp(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) ||
+			    (sf->sf_sfo->sfo_name == NULL) ||
+			    strcmp(override, sf->sf_sfo->sfo_name))
+				continue;
+
+			/* found failover module, supported under scsi_vhci */
+			sfo = sf->sf_sfo;
+			vlun->svl_fops_name = kmem_alloc(strlen("conf ") +
+			    strlen(sfo->sfo_name) + 1, KM_SLEEP);
+			(void) sprintf(vlun->svl_fops_name, "conf %s",
+			    sfo->sfo_name);
+			break;
+		}
+	}
+	if (override)
+		kmem_free(override, strlen(override) + 1);
+
+	if (sfo == NULL) {
+		/* no failover module - device not supported */
+		VHCI_DEBUG(1, (CE_NOTE, vhci->vhci_dip,
+		    "!vhci_pathinfo_online: dev (path 0x%p) not "
+		    "supported\n", (void *)pip));
+		vlun->svl_not_supported = 1;
+		rval = MDI_NOT_SUPPORTED;
+		goto done;
+	}
+
+	/* failover supported for device - save failover_ops in vlun */
+	vlun->svl_fops = sfo;
+
+	/*
+	 * Obtain the device-type based mpxio options as specified in
+	 * scsi_vhci.conf file.
+	 *
+	 * NOTE: currently, the end result is a call to
+	 * mdi_set_lb_region_size().
+	 */
+	vhci_get_device_type_mpxio_options(vdip, tgt_dip, psd);
+
+	/*
+	 * if PGR is active, revalidate key and register on this path also,
+	 * if key is still valid
+	 */
+	sema_p(&vlun->svl_pgr_sema);
+	if (vlun->svl_pgr_active) {
+		rval = vhci_pgr_validate_and_register(svp);
+		if (rval != 1) {
+			rval = MDI_FAILURE;
+			sema_v(&vlun->svl_pgr_sema);
+			goto failure;
+		}
+	}
+	sema_v(&vlun->svl_pgr_sema);
+
+	if (svp->svp_new_path) {
+		/*
+		 * Last chance to perform any cleanup operations on this
+		 * new path before making this path completely online.
+		 */
+		svp->svp_new_path = 0;
+
+		/*
+		 * If scsi_vhci knows the lun is alread RESERVE'd,
+		 * then skip the issue of RELEASE on new path.
+		 */
+		if ((vlun->svl_flags & VLUN_RESERVE_ACTIVE_FLG) == 0) {
+			/*
+			 * Issue SCSI-2 RELEASE only for the first time on
+			 * a new path just in case the host rebooted and
+			 * a reservation is still pending on this path.
+			 * IBM Shark storage does not clear RESERVE upon
+			 * host reboot.
+			 */
+			ap = &psd->sd_address;
+			pkt = scsi_init_pkt(ap, NULL, NULL, CDB_GROUP0,
+			    sizeof (struct scsi_arq_status), 0, 0,
+			    SLEEP_FUNC, NULL);
+			if (pkt == NULL) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_pathinfo_online: "
+				    "Release init_pkt failed :%p\n",
+				    (void *)pip));
+				rval = MDI_FAILURE;
+				goto failure;
+			}
+			pkt->pkt_cdbp[0] = SCMD_RELEASE;
+			pkt->pkt_time = 60;
+
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_path_online: path:%p "
+			    "Issued SCSI-2 RELEASE\n", (void *)pip));
+
+			/* Ignore the return value */
+			(void) vhci_do_scsi_cmd(pkt);
+			scsi_destroy_pkt(pkt);
+		}
+	}
+
+	rval = vhci_update_pathinfo(psd, pip, sfo, vlun, vhci);
+	if (rval == MDI_FAILURE) {
+		goto failure;
+	}
+
+	/* Initialize MP-API data */
+	vhci_update_mpapi_data(vhci, vlun, pip);
+
+	/*
+	 * MP-API also needs the Inquiry data to be maintained in the
+	 * mp_vendor_prop_t structure, so find the lun and update its
+	 * structure with this data.
+	 */
+	list_ptr = (mpapi_item_list_t *)vhci_get_mpapi_item(vhci, NULL,
+	    MP_OBJECT_TYPE_MULTIPATH_LU, (void *)vlun);
+	ld = (mpapi_lu_data_t *)list_ptr->item->idata;
+	if (ld != NULL) {
+		bcopy(psd->sd_inq->inq_vid, ld->prop.prodInfo.vendor, 8);
+		bcopy(psd->sd_inq->inq_pid, ld->prop.prodInfo.product, 16);
+		bcopy(psd->sd_inq->inq_revision, ld->prop.prodInfo.revision, 4);
+	} else {
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_pathinfo_online: "
+		    "mpapi_lu_data_t is NULL"));
+	}
+
+	/* create kstats for path */
+	vhci_kstat_create_pathinfo(pip);
+
+done:
+	mutex_enter(&vhci_global_mutex);
+	cv_broadcast(&vhci_cv);
+	mutex_exit(&vhci_global_mutex);
+
+	if (vlun->svl_setcap_done) {
+		(void) vhci_pHCI_cap(ap, "sector-size",
+		    vlun->svl_sector_size, 1, pip);
+	}
+
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_path_online: path:%p\n",
+	    (void *)pip));
+
+failure:
+	if ((rval != MDI_SUCCESS) && psd->sd_inq) {
+		kmem_free((caddr_t)psd->sd_inq, inq_size);
+		psd->sd_inq = (struct scsi_inquiry *)NULL;
+	}
+	return (rval);
+}
+
+/*
+ * path offline handler.  Release all bindings that will not be
+ * released by the normal packet transport/completion code path.
+ * Since we don't (presently) keep any bindings alive outside of
+ * the in-transport packets (which will be released on completion)
+ * there is not much to do here.
+ */
+/* ARGSUSED */
+static int
+vhci_pathinfo_offline(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
+{
+	scsi_hba_tran_t		*hba = NULL;
+	struct scsi_device	*psd = NULL;
+	dev_info_t		*pdip = NULL;
+	dev_info_t		*cdip = NULL;
+	scsi_vhci_priv_t	*svp = NULL;
+	uint_t			inq_size = VHCI_STD_INQ_SIZE;
+
+	ASSERT(vdip != NULL);
+	ASSERT(pip != NULL);
+
+	pdip = mdi_pi_get_phci(pip);
+	ASSERT(pdip != NULL);
+	if (pdip == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, vdip, "Invalid path 0x%p: NULL "
+		    "phci dip", (void *)pip));
+		return (MDI_FAILURE);
+	}
+
+	cdip = mdi_pi_get_client(pip);
+	ASSERT(cdip != NULL);
+	if (cdip == NULL) {
+		VHCI_DEBUG(1, (CE_WARN, vdip, "Invalid path 0x%p: NULL "
+		    "client dip", (void *)pip));
+		return (MDI_FAILURE);
+	}
+
+	hba = ddi_get_driver_private(pdip);
+	ASSERT(hba != NULL);
+
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		/*
+		 * mdi_pathinfo node in INIT state can have vHCI private
+		 * information set to null
+		 */
+		VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
+		    "svp is NULL for pip 0x%p\n", (void *)pip));
+		return (MDI_SUCCESS);
+	}
+
+	psd = svp->svp_psd;
+	ASSERT(psd != NULL);
+
+	mutex_enter(&svp->svp_mutex);
+
+	VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
+	    "%d cmds pending on path: 0x%p\n", svp->svp_cmds, (void *)pip));
+	while (svp->svp_cmds != 0) {
+		if (cv_timedwait(&svp->svp_cv, &svp->svp_mutex,
+		    ddi_get_lbolt() +
+		    drv_usectohz(vhci_path_quiesce_timeout * 1000000)) == -1) {
+			/*
+			 * The timeout time reached without the condition
+			 * being signaled.
+			 */
+			VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
+			    "Timeout reached on path 0x%p without the cond\n",
+			    (void *)pip));
+			VHCI_DEBUG(1, (CE_NOTE, vdip, "!vhci_pathinfo_offline: "
+			    "%d cmds still pending on path: 0x%p\n",
+			    svp->svp_cmds, (void *)pip));
+			break;
+		}
+	}
+	mutex_exit(&svp->svp_mutex);
+
+	/*
+	 * Check to see if this vlun has an active SCSI-II RESERVE. And this
+	 * is the pip for the path that has been reserved.
+	 * If so clear the reservation by sending a reset, so the host will not
+	 * get a reservation conflict.  Reset the flag VLUN_RESERVE_ACTIVE_FLG
+	 * for this lun.  Also a reset notify is sent to the target driver
+	 * just in case the POR check condition is cleared by some other layer
+	 * in the stack.
+	 */
+	if (svp->svp_svl->svl_flags & VLUN_RESERVE_ACTIVE_FLG) {
+		if (pip == svp->svp_svl->svl_resrv_pip) {
+			if (vhci_recovery_reset(svp->svp_svl,
+			    &svp->svp_psd->sd_address, TRUE,
+			    VHCI_DEPTH_TARGET) == 0) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_pathinfo_offline (pip:%p):"
+				    "reset failed, retrying\n", (void *)pip));
+				delay(1*drv_usectohz(1000000));
+				if (vhci_recovery_reset(svp->svp_svl,
+				    &svp->svp_psd->sd_address, TRUE,
+				    VHCI_DEPTH_TARGET) == 0) {
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "!vhci_pathinfo_offline "
+					    "(pip:%p): reset failed, "
+					    "giving up!\n", (void *)pip));
+				}
+			}
+			svp->svp_svl->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
+		}
+	}
+
+	mdi_pi_set_state(pip, MDI_PATHINFO_STATE_OFFLINE);
+	if (psd->sd_inq) {
+		kmem_free((caddr_t)psd->sd_inq, inq_size);
+		psd->sd_inq = (struct scsi_inquiry *)NULL;
+	}
+	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_REMOVED);
+
+	VHCI_DEBUG(1, (CE_NOTE, NULL,
+	    "!vhci_pathinfo_offline: offlined path 0x%p\n", (void *)pip));
+	return (MDI_SUCCESS);
+}
+
+
+/*
+ * routine for SCSI VHCI IOCTL implementation.
+ */
+/* ARGSUSED */
+static int
+vhci_ctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval)
+{
+	struct scsi_vhci		*vhci;
+	dev_info_t			*vdip;
+	mdi_pathinfo_t			*pip;
+	int				instance, held;
+	int				retval = 0;
+	caddr_t				phci_path = NULL, client_path = NULL;
+	caddr_t				paddr = NULL;
+	sv_iocdata_t			ioc;
+	sv_iocdata_t			*pioc = &ioc;
+	sv_switch_to_cntlr_iocdata_t	iocsc;
+	sv_switch_to_cntlr_iocdata_t	*piocsc = &iocsc;
+	caddr_t				s;
+	scsi_vhci_lun_t			*vlun;
+	struct scsi_failover_ops	*fo;
+	char				*pclass;
+
+	/* Check for validity of vhci structure */
+	vhci = ddi_get_soft_state(vhci_softstate, MINOR2INST(getminor(dev)));
+	if (vhci == NULL) {
+		return (ENXIO);
+	}
+
+	mutex_enter(&vhci->vhci_mutex);
+	if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
+		mutex_exit(&vhci->vhci_mutex);
+		return (ENXIO);
+	}
+	mutex_exit(&vhci->vhci_mutex);
+
+	/* Get the vhci dip */
+	vdip = vhci->vhci_dip;
+	ASSERT(vdip != NULL);
+	instance = ddi_get_instance(vdip);
+
+	/* Allocate memory for getting parameters from userland */
+	phci_path	= kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	client_path	= kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+	paddr		= kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+
+	/*
+	 * Set a local variable indicating the ioctl name. Used for
+	 * printing debug strings.
+	 */
+	switch (cmd) {
+	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
+		s = "GET_CLIENT_MULTIPATH_INFO";
+		break;
+
+	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
+		s = "GET_PHCI_MULTIPATH_INFO";
+		break;
+
+	case SCSI_VHCI_GET_CLIENT_NAME:
+		s = "GET_CLIENT_NAME";
+		break;
+
+	case SCSI_VHCI_PATH_ONLINE:
+		s = "PATH_ONLINE";
+		break;
+
+	case SCSI_VHCI_PATH_OFFLINE:
+		s = "PATH_OFFLINE";
+		break;
+
+	case SCSI_VHCI_PATH_STANDBY:
+		s = "PATH_STANDBY";
+		break;
+
+	case SCSI_VHCI_PATH_TEST:
+		s = "PATH_TEST";
+		break;
+
+	case SCSI_VHCI_SWITCH_TO_CNTLR:
+		s = "SWITCH_TO_CNTLR";
+		break;
+	case SCSI_VHCI_PATH_DISABLE:
+		s = "PATH_DISABLE";
+		break;
+	case SCSI_VHCI_PATH_ENABLE:
+		s = "PATH_ENABLE";
+		break;
+
+	case SCSI_VHCI_GET_TARGET_LONGNAME:
+		s = "GET_TARGET_LONGNAME";
+		break;
+
+#ifdef	DEBUG
+	case SCSI_VHCI_CONFIGURE_PHCI:
+		s = "CONFIGURE_PHCI";
+		break;
+
+	case SCSI_VHCI_UNCONFIGURE_PHCI:
+		s = "UNCONFIGURE_PHCI";
+		break;
+#endif
+
+	default:
+		s = "Unknown";
+		vhci_log(CE_NOTE, vdip,
+		    "!vhci%d: ioctl %x (unsupported ioctl)", instance, cmd);
+		retval = ENOTSUP;
+		break;
+	}
+	if (retval != 0) {
+		goto end;
+	}
+
+	VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci%d: ioctl <%s>", instance, s));
+
+	/*
+	 * Get IOCTL parameters from userland
+	 */
+	switch (cmd) {
+	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
+	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
+	case SCSI_VHCI_GET_CLIENT_NAME:
+	case SCSI_VHCI_PATH_ONLINE:
+	case SCSI_VHCI_PATH_OFFLINE:
+	case SCSI_VHCI_PATH_STANDBY:
+	case SCSI_VHCI_PATH_TEST:
+	case SCSI_VHCI_PATH_DISABLE:
+	case SCSI_VHCI_PATH_ENABLE:
+	case SCSI_VHCI_GET_TARGET_LONGNAME:
+#ifdef	DEBUG
+	case SCSI_VHCI_CONFIGURE_PHCI:
+	case SCSI_VHCI_UNCONFIGURE_PHCI:
+#endif
+		retval = vhci_get_iocdata((const void *)data, pioc, mode, s);
+		break;
+
+	case SCSI_VHCI_SWITCH_TO_CNTLR:
+		retval = vhci_get_iocswitchdata((const void *)data, piocsc,
+		    mode, s);
+		break;
+	}
+	if (retval != 0) {
+		goto end;
+	}
+
+
+	/*
+	 * Process the IOCTL
+	 */
+	switch (cmd) {
+	case SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO:
+	{
+		uint_t		num_paths;	/* Num paths to client dev */
+		sv_path_info_t	*upibuf = NULL;	/* To keep userland values */
+		sv_path_info_t	*kpibuf = NULL; /* Kernel data for ioctls */
+		dev_info_t	*cdip;		/* Client device dip */
+
+		if (pioc->ret_elem == NULL) {
+			retval = EINVAL;
+			break;
+		}
+
+		/* Get client device path from user land */
+		if (vhci_ioc_get_client_path(pioc, client_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "client <%s>", s, client_path));
+
+		/* Get number of paths to this client device */
+		if ((cdip = mdi_client_path2devinfo(vdip, client_path))
+		    == NULL) {
+			retval = ENXIO;
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "client dip doesn't exist. invalid path <%s>",
+			    s, client_path));
+			break;
+		}
+		num_paths = mdi_client_get_path_count(cdip);
+
+		if (ddi_copyout(&num_paths, pioc->ret_elem,
+		    sizeof (num_paths), mode)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "num_paths copyout failed", s));
+			retval = EFAULT;
+			break;
+		}
+
+		/* If  user just wanted num_paths, then return */
+		if (pioc->buf_elem == 0 || pioc->ret_buf == NULL ||
+		    num_paths == 0) {
+			break;
+		}
+
+		/* Set num_paths to value as much as can be sent to userland */
+		if (num_paths > pioc->buf_elem) {
+			num_paths = pioc->buf_elem;
+		}
+
+		/* Allocate memory and get userland pointers */
+		if (vhci_ioc_alloc_pathinfo(&upibuf, &kpibuf, num_paths,
+		    pioc, mode, s) != 0) {
+			retval = EFAULT;
+			break;
+		}
+		ASSERT(upibuf != NULL);
+		ASSERT(kpibuf != NULL);
+
+		/*
+		 * Get the path information and send it to userland.
+		 */
+		if (vhci_get_client_path_list(cdip, kpibuf, num_paths)
+		    != MDI_SUCCESS) {
+			retval = ENXIO;
+			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+			break;
+		}
+
+		if (vhci_ioc_send_pathinfo(upibuf, kpibuf, num_paths,
+		    pioc, mode, s)) {
+			retval = EFAULT;
+			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+			break;
+		}
+
+		/* Free the memory allocated for path information */
+		vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+		break;
+	}
+
+	case SCSI_VHCI_GET_PHCI_MULTIPATH_INFO:
+	{
+		uint_t		num_paths;	/* Num paths to client dev */
+		sv_path_info_t	*upibuf = NULL;	/* To keep userland values */
+		sv_path_info_t	*kpibuf = NULL; /* Kernel data for ioctls */
+		dev_info_t	*pdip;		/* PHCI device dip */
+
+		if (pioc->ret_elem == NULL) {
+			retval = EINVAL;
+			break;
+		}
+
+		/* Get PHCI device path from user land */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip,
+		    "!vhci_ioctl: ioctl <%s> phci <%s>", s, phci_path));
+
+		/* Get number of devices associated with this PHCI device */
+		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		num_paths = mdi_phci_get_path_count(pdip);
+
+		if (ddi_copyout(&num_paths, pioc->ret_elem,
+		    sizeof (num_paths), mode)) {
+			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "num_paths copyout failed", s));
+			retval = EFAULT;
+			break;
+		}
+
+		/* If  user just wanted num_paths, then return */
+		if (pioc->buf_elem == 0 || pioc->ret_buf == NULL ||
+		    num_paths == 0) {
+			break;
+		}
+
+		/* Set num_paths to value as much as can be sent to userland */
+		if (num_paths > pioc->buf_elem) {
+			num_paths = pioc->buf_elem;
+		}
+
+		/* Allocate memory and get userland pointers */
+		if (vhci_ioc_alloc_pathinfo(&upibuf, &kpibuf, num_paths,
+		    pioc, mode, s) != 0) {
+			retval = EFAULT;
+			break;
+		}
+		ASSERT(upibuf != NULL);
+		ASSERT(kpibuf != NULL);
+
+		/*
+		 * Get the path information and send it to userland.
+		 */
+		if (vhci_get_phci_path_list(pdip, kpibuf, num_paths)
+		    != MDI_SUCCESS) {
+			retval = ENXIO;
+			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+			break;
+		}
+
+		if (vhci_ioc_send_pathinfo(upibuf, kpibuf, num_paths,
+		    pioc, mode, s)) {
+			retval = EFAULT;
+			vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+			break;
+		}
+
+		/* Free the memory allocated for path information */
+		vhci_ioc_free_pathinfo(upibuf, kpibuf, num_paths);
+		break;
+	}
+
+	case SCSI_VHCI_GET_CLIENT_NAME:
+	{
+		dev_info_t		*cdip, *pdip;
+
+		/* Get PHCI path and device address from user land */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s) ||
+		    vhci_ioc_get_paddr(pioc, paddr, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "phci <%s>, paddr <%s>", s, phci_path, paddr));
+
+		/* Get the PHCI dip */
+		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		if ((pip = mdi_pi_find(pdip, NULL, paddr)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+			    "pathinfo doesn't exist. invalid device addr", s));
+			retval = ENXIO;
+			break;
+		}
+
+		/* Get the client device pathname and send to userland */
+		cdip = mdi_pi_get_client(pip);
+		vhci_ioc_devi_to_path(cdip, client_path);
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "client <%s>", s, client_path));
+
+		if (vhci_ioc_send_client_path(client_path, pioc, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+		break;
+	}
+
+	case SCSI_VHCI_PATH_ONLINE:
+	case SCSI_VHCI_PATH_OFFLINE:
+	case SCSI_VHCI_PATH_STANDBY:
+	case SCSI_VHCI_PATH_TEST:
+	{
+		dev_info_t		*pdip;	/* PHCI dip */
+
+		/* Get PHCI path and device address from user land */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s) ||
+		    vhci_ioc_get_paddr(pioc, paddr, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "phci <%s>, paddr <%s>", s, phci_path, paddr));
+
+		/* Get the PHCI dip */
+		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		if ((pip = mdi_pi_find(pdip, NULL, paddr)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+			    "pathinfo doesn't exist. invalid device addr", s));
+			retval = ENXIO;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "Calling MDI function to change device state", s));
+
+		switch (cmd) {
+		case SCSI_VHCI_PATH_ONLINE:
+			retval = mdi_pi_online(pip, 0);
+			break;
+
+		case SCSI_VHCI_PATH_OFFLINE:
+			retval = mdi_pi_offline(pip, 0);
+			break;
+
+		case SCSI_VHCI_PATH_STANDBY:
+			retval = mdi_pi_standby(pip, 0);
+			break;
+
+		case SCSI_VHCI_PATH_TEST:
+			break;
+		}
+		break;
+	}
+
+	case SCSI_VHCI_SWITCH_TO_CNTLR:
+	{
+		dev_info_t *cdip;
+		struct scsi_device *devp;
+
+		/* Get the client device pathname */
+		if (ddi_copyin(piocsc->client, client_path,
+		    MAXPATHLEN, mode)) {
+			VHCI_DEBUG(2, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+			    "client_path copyin failed", s));
+			retval = EFAULT;
+			break;
+		}
+
+		/* Get the path class to which user wants to switch */
+		if (ddi_copyin(piocsc->class, paddr, MAXNAMELEN, mode)) {
+			VHCI_DEBUG(2, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+			    "controller_class copyin failed", s));
+			retval = EFAULT;
+			break;
+		}
+
+		/* Perform validity checks */
+		if ((cdip = mdi_client_path2devinfo(vdip,
+		    client_path)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "client dip doesn't exist. invalid path <%s>",
+			    s, client_path));
+			retval = ENXIO;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: Calling MDI func "
+		    "to switch controller"));
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: client <%s> "
+		    "class <%s>", client_path, paddr));
+
+		if (strcmp(paddr, PCLASS_PRIMARY) &&
+		    strcmp(paddr, PCLASS_SECONDARY)) {
+			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "invalid path class <%s>", s, paddr));
+			retval = ENXIO;
+			break;
+		}
+
+		devp = ddi_get_driver_private(cdip);
+		if (devp == NULL) {
+			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "invalid scsi device <%s>", s, client_path));
+			retval = ENXIO;
+			break;
+		}
+		vlun = ADDR2VLUN(&devp->sd_address);
+		ASSERT(vlun);
+
+		/*
+		 * Checking to see if device has only one pclass, PRIMARY.
+		 * If so this device doesn't support failovers.  Assumed
+		 * that the devices with one pclass is PRIMARY, as thats the
+		 * case today.  If this is not true and in future other
+		 * symmetric devices are supported with other pclass, this
+		 * IOCTL shall have to be overhauled anyways as now the only
+		 * arguments it accepts are PRIMARY and SECONDARY.
+		 */
+		fo = vlun->svl_fops;
+		if ((*fo->sfo_pathclass_next)(PCLASS_PRIMARY, &pclass,
+		    vlun->svl_fops_ctpriv)) {
+			retval = ENOTSUP;
+			break;
+		}
+
+		VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
+		mutex_enter(&vlun->svl_mutex);
+		if (vlun->svl_active_pclass != NULL) {
+			if (strcmp(vlun->svl_active_pclass, paddr) == 0) {
+				mutex_exit(&vlun->svl_mutex);
+				retval = EALREADY;
+				VHCI_RELEASE_LUN(vlun);
+				break;
+			}
+		}
+		mutex_exit(&vlun->svl_mutex);
+		/* Call mdi function to cause  a switch over */
+		retval = mdi_failover(vdip, cdip, MDI_FAILOVER_SYNC);
+		if (retval == MDI_SUCCESS) {
+			retval = 0;
+		} else if (retval == MDI_BUSY) {
+			retval = EBUSY;
+		} else {
+			retval = EIO;
+		}
+		VHCI_RELEASE_LUN(vlun);
+		break;
+	}
+
+	case SCSI_VHCI_PATH_ENABLE:
+	case SCSI_VHCI_PATH_DISABLE:
+	{
+		dev_info_t	*cdip, *pdip;
+
+		/*
+		 * Get client device path from user land
+		 */
+		if (vhci_ioc_get_client_path(pioc, client_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		/*
+		 * Get Phci device path from user land
+		 */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		/*
+		 * Get the devinfo for the Phci.
+		 */
+		if ((pdip = mdi_phci_path2devinfo(vdip, phci_path)) == NULL) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		/*
+		 * If the client path is set to /scsi_vhci then we need
+		 * to do the operation on all the clients so set cdip to NULL.
+		 * Else, try to get the client dip.
+		 */
+		if (strcmp(client_path, "/scsi_vhci") == 0) {
+			cdip = NULL;
+		} else {
+			if ((cdip = mdi_client_path2devinfo(vdip,
+			    client_path)) == NULL) {
+				retval = ENXIO;
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!vhci_ioctl: ioctl <%s> client dip "
+				    "doesn't exist. invalid path <%s>",
+				    s, client_path));
+				break;
+			}
+		}
+
+		if (cmd == SCSI_VHCI_PATH_ENABLE)
+			retval = mdi_pi_enable(cdip, pdip, USER_DISABLE);
+		else
+			retval = mdi_pi_disable(cdip, pdip, USER_DISABLE);
+
+		break;
+	}
+
+	case SCSI_VHCI_GET_TARGET_LONGNAME:
+	{
+		uint_t		pid = pioc->buf_elem;
+		char		*target_port;
+		mod_hash_val_t	hv;
+
+		/* targetmap lookup of 'target-port' by <pid> */
+		if (mod_hash_find(vhci_targetmap_bypid,
+		    (mod_hash_key_t)(uintptr_t)pid, &hv) != 0) {
+			/*
+			 * NOTE: failure to find the mapping is OK for guid
+			 * based 'target-port' values.
+			 */
+			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "targetport mapping doesn't exist: pid %d",
+			    s, pid));
+			retval = ENXIO;
+			break;
+		}
+
+		/* copyout 'target-port' result */
+		target_port = (char *)hv;
+		if (copyoutstr(target_port, pioc->addr, MAXNAMELEN, NULL)) {
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "targetport copyout failed: len: %d",
+			    s, (int)strlen(target_port)));
+			retval = EFAULT;
+		}
+		break;
+	}
+
+#ifdef	DEBUG
+	case SCSI_VHCI_CONFIGURE_PHCI:
+	{
+		dev_info_t		*pdip;
+
+		/* Get PHCI path and device address from user land */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "phci <%s>", s, phci_path));
+
+		/* Get the PHCI dip */
+		if ((pdip = e_ddi_hold_devi_by_path(phci_path, 0)) == NULL) {
+			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		if (ndi_devi_config(pdip,
+		    NDI_DEVFS_CLEAN|NDI_DEVI_PERSIST) != NDI_SUCCESS) {
+			retval = EIO;
+		}
+
+		ddi_release_devi(pdip);
+		break;
+	}
+
+	case SCSI_VHCI_UNCONFIGURE_PHCI:
+	{
+		dev_info_t		*pdip;
+
+		/* Get PHCI path and device address from user land */
+		if (vhci_ioc_get_phci_path(pioc, phci_path, mode, s)) {
+			retval = EFAULT;
+			break;
+		}
+
+		VHCI_DEBUG(6, (CE_WARN, vdip, "!vhci_ioctl: ioctl <%s> "
+		    "phci <%s>", s, phci_path));
+
+		/* Get the PHCI dip */
+		if ((pdip = e_ddi_hold_devi_by_path(phci_path, 0)) == NULL) {
+			VHCI_DEBUG(3, (CE_WARN, NULL, "!vhci_ioctl: ioctl <%s> "
+			    "phci dip doesn't exist. invalid path <%s>",
+			    s, phci_path));
+			retval = ENXIO;
+			break;
+		}
+
+		if (ndi_devi_unconfig(pdip,
+		    NDI_DEVI_REMOVE|NDI_DEVFS_CLEAN) != NDI_SUCCESS) {
+			retval = EBUSY;
+		}
+
+		ddi_release_devi(pdip);
+		break;
+	}
+#endif
+	}
+
+end:
+	/* Free the memory allocated above */
+	if (phci_path != NULL) {
+		kmem_free(phci_path, MAXPATHLEN);
+	}
+	if (client_path != NULL) {
+		kmem_free(client_path, MAXPATHLEN);
+	}
+	if (paddr != NULL) {
+		kmem_free(paddr, MAXNAMELEN);
+	}
+	return (retval);
+}
+
+/*
+ * devctl IOCTL support for client device DR
+ */
+/* ARGSUSED */
+int
+vhci_devctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+    int *rvalp)
+{
+	dev_info_t *self;
+	dev_info_t *child;
+	scsi_hba_tran_t *hba;
+	struct devctl_iocdata *dcp;
+	struct scsi_vhci *vhci;
+	int rv = 0;
+	int retval = 0;
+	scsi_vhci_priv_t *svp;
+	mdi_pathinfo_t  *pip;
+
+	if ((vhci = ddi_get_soft_state(vhci_softstate,
+	    MINOR2INST(getminor(dev)))) == NULL)
+		return (ENXIO);
+
+	/*
+	 * check if :devctl minor device has been opened
+	 */
+	mutex_enter(&vhci->vhci_mutex);
+	if ((vhci->vhci_state & VHCI_STATE_OPEN) == 0) {
+		mutex_exit(&vhci->vhci_mutex);
+		return (ENXIO);
+	}
+	mutex_exit(&vhci->vhci_mutex);
+
+	self = vhci->vhci_dip;
+	hba = ddi_get_driver_private(self);
+	if (hba == NULL)
+		return (ENXIO);
+
+	/*
+	 * We can use the generic implementation for these ioctls
+	 */
+	switch (cmd) {
+	case DEVCTL_DEVICE_GETSTATE:
+	case DEVCTL_DEVICE_ONLINE:
+	case DEVCTL_DEVICE_OFFLINE:
+	case DEVCTL_DEVICE_REMOVE:
+	case DEVCTL_BUS_GETSTATE:
+		return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
+	}
+
+	/*
+	 * read devctl ioctl data
+	 */
+	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
+		return (EFAULT);
+
+	switch (cmd) {
+
+	case DEVCTL_DEVICE_RESET:
+		/*
+		 * lookup and hold child device
+		 */
+		if ((child = ndi_devi_find(self, ndi_dc_getname(dcp),
+		    ndi_dc_getaddr(dcp))) == NULL) {
+			rv = ENXIO;
+			break;
+		}
+		retval = mdi_select_path(child, NULL,
+		    (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH),
+		    NULL, &pip);
+		if ((retval != MDI_SUCCESS) || (pip == NULL)) {
+			VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioctl:"
+			    "Unable to get a path, dip 0x%p", (void *)child));
+			rv = ENXIO;
+			break;
+		}
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		if (vhci_recovery_reset(svp->svp_svl,
+		    &svp->svp_psd->sd_address, TRUE,
+		    VHCI_DEPTH_TARGET) == 0) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_ioctl(pip:%p): "
+			    "reset failed\n", (void *)pip));
+			rv = ENXIO;
+		}
+		mdi_rele_path(pip);
+		break;
+
+	case DEVCTL_BUS_QUIESCE:
+	case DEVCTL_BUS_UNQUIESCE:
+	case DEVCTL_BUS_RESET:
+	case DEVCTL_BUS_RESETALL:
+#ifdef	DEBUG
+	case DEVCTL_BUS_CONFIGURE:
+	case DEVCTL_BUS_UNCONFIGURE:
+#endif
+		rv = ENOTSUP;
+		break;
+
+	default:
+		rv = ENOTTY;
+	} /* end of outer switch */
+
+	ndi_dc_freehdl(dcp);
+	return (rv);
+}
+
+/*
+ * Routine to get the PHCI pathname from ioctl structures in userland
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_get_phci_path(sv_iocdata_t *pioc, caddr_t phci_path,
+	int mode, caddr_t s)
+{
+	int retval = 0;
+
+	if (ddi_copyin(pioc->phci, phci_path, MAXPATHLEN, mode)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_phci: ioctl <%s> "
+		    "phci_path copyin failed", s));
+		retval = EFAULT;
+	}
+	return (retval);
+
+}
+
+
+/*
+ * Routine to get the Client device pathname from ioctl structures in userland
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_get_client_path(sv_iocdata_t *pioc, caddr_t client_path,
+	int mode, caddr_t s)
+{
+	int retval = 0;
+
+	if (ddi_copyin(pioc->client, client_path, MAXPATHLEN, mode)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_client: "
+		    "ioctl <%s> client_path copyin failed", s));
+		retval = EFAULT;
+	}
+	return (retval);
+}
+
+
+/*
+ * Routine to get physical device address from ioctl structure in userland
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_get_paddr(sv_iocdata_t *pioc, caddr_t paddr, int mode, caddr_t s)
+{
+	int retval = 0;
+
+	if (ddi_copyin(pioc->addr, paddr, MAXNAMELEN, mode)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_get_paddr: "
+		    "ioctl <%s> device addr copyin failed", s));
+		retval = EFAULT;
+	}
+	return (retval);
+}
+
+
+/*
+ * Routine to send client device pathname to userland.
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_send_client_path(caddr_t client_path, sv_iocdata_t *pioc,
+	int mode, caddr_t s)
+{
+	int retval = 0;
+
+	if (ddi_copyout(client_path, pioc->client, MAXPATHLEN, mode)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_ioc_send_client: "
+		    "ioctl <%s> client_path copyout failed", s));
+		retval = EFAULT;
+	}
+	return (retval);
+}
+
+
+/*
+ * Routine to translated dev_info pointer (dip) to device pathname.
+ */
+static void
+vhci_ioc_devi_to_path(dev_info_t *dip, caddr_t path)
+{
+	(void) ddi_pathname(dip, path);
+}
+
+
+/*
+ * vhci_get_phci_path_list:
+ *		get information about devices associated with a
+ *		given PHCI device.
+ *
+ * Return Values:
+ *		path information elements
+ */
+int
+vhci_get_phci_path_list(dev_info_t *pdip, sv_path_info_t *pibuf,
+	uint_t num_elems)
+{
+	uint_t			count, done;
+	mdi_pathinfo_t		*pip;
+	sv_path_info_t		*ret_pip;
+	int			status;
+	size_t			prop_size;
+	int			circular;
+
+	/*
+	 * Get the PHCI structure and retrieve the path information
+	 * from the GUID hash table.
+	 */
+
+	ret_pip = pibuf;
+	count = 0;
+
+	ndi_devi_enter(pdip, &circular);
+
+	done = (count >= num_elems);
+	pip = mdi_get_next_client_path(pdip, NULL);
+	while (pip && !done) {
+		mdi_pi_lock(pip);
+		(void) ddi_pathname(mdi_pi_get_phci(pip),
+		    ret_pip->device.ret_phci);
+		(void) strcpy(ret_pip->ret_addr, mdi_pi_get_addr(pip));
+		(void) mdi_pi_get_state2(pip, &ret_pip->ret_state,
+		    &ret_pip->ret_ext_state);
+
+		status = mdi_prop_size(pip, &prop_size);
+		if (status == MDI_SUCCESS && ret_pip->ret_prop.ret_buf_size) {
+			*ret_pip->ret_prop.ret_buf_size = (uint_t)prop_size;
+		}
+
+#ifdef DEBUG
+		if (status != MDI_SUCCESS) {
+			VHCI_DEBUG(2, (CE_WARN, NULL,
+			    "!vhci_get_phci_path_list: "
+			    "phci <%s>, prop size failure 0x%x",
+			    ret_pip->device.ret_phci, status));
+		}
+#endif /* DEBUG */
+
+
+		if (status == MDI_SUCCESS && ret_pip->ret_prop.buf &&
+		    prop_size && ret_pip->ret_prop.buf_size >= prop_size) {
+			status = mdi_prop_pack(pip,
+			    &ret_pip->ret_prop.buf,
+			    ret_pip->ret_prop.buf_size);
+
+#ifdef DEBUG
+			if (status != MDI_SUCCESS) {
+				VHCI_DEBUG(2, (CE_WARN, NULL,
+				    "!vhci_get_phci_path_list: "
+				    "phci <%s>, prop pack failure 0x%x",
+				    ret_pip->device.ret_phci, status));
+			}
+#endif /* DEBUG */
+		}
+
+		mdi_pi_unlock(pip);
+		pip = mdi_get_next_client_path(pdip, pip);
+		ret_pip++;
+		count++;
+		done = (count >= num_elems);
+	}
+
+	ndi_devi_exit(pdip, circular);
+
+	return (MDI_SUCCESS);
+}
+
+
+/*
+ * vhci_get_client_path_list:
+ *		get information about various paths associated with a
+ *		given client device.
+ *
+ * Return Values:
+ *		path information elements
+ */
+int
+vhci_get_client_path_list(dev_info_t *cdip, sv_path_info_t *pibuf,
+	uint_t num_elems)
+{
+	uint_t			count, done;
+	mdi_pathinfo_t		*pip;
+	sv_path_info_t		*ret_pip;
+	int			status;
+	size_t			prop_size;
+	int			circular;
+
+	ret_pip = pibuf;
+	count = 0;
+
+	ndi_devi_enter(cdip, &circular);
+
+	done = (count >= num_elems);
+	pip = mdi_get_next_phci_path(cdip, NULL);
+	while (pip && !done) {
+		mdi_pi_lock(pip);
+		(void) ddi_pathname(mdi_pi_get_phci(pip),
+		    ret_pip->device.ret_phci);
+		(void) strcpy(ret_pip->ret_addr, mdi_pi_get_addr(pip));
+		(void) mdi_pi_get_state2(pip, &ret_pip->ret_state,
+		    &ret_pip->ret_ext_state);
+
+		status = mdi_prop_size(pip, &prop_size);
+		if (status == MDI_SUCCESS && ret_pip->ret_prop.ret_buf_size) {
+			*ret_pip->ret_prop.ret_buf_size = (uint_t)prop_size;
+		}
+
+#ifdef DEBUG
+		if (status != MDI_SUCCESS) {
+			VHCI_DEBUG(2, (CE_WARN, NULL,
+			    "!vhci_get_client_path_list: "
+			    "phci <%s>, prop size failure 0x%x",
+			    ret_pip->device.ret_phci, status));
+		}
+#endif /* DEBUG */
+
+
+		if (status == MDI_SUCCESS && ret_pip->ret_prop.buf &&
+		    prop_size && ret_pip->ret_prop.buf_size >= prop_size) {
+			status = mdi_prop_pack(pip,
+			    &ret_pip->ret_prop.buf,
+			    ret_pip->ret_prop.buf_size);
+
+#ifdef DEBUG
+			if (status != MDI_SUCCESS) {
+				VHCI_DEBUG(2, (CE_WARN, NULL,
+				    "!vhci_get_client_path_list: "
+				    "phci <%s>, prop pack failure 0x%x",
+				    ret_pip->device.ret_phci, status));
+			}
+#endif /* DEBUG */
+		}
+
+		mdi_pi_unlock(pip);
+		pip = mdi_get_next_phci_path(cdip, pip);
+		ret_pip++;
+		count++;
+		done = (count >= num_elems);
+	}
+
+	ndi_devi_exit(cdip, circular);
+
+	return (MDI_SUCCESS);
+}
+
+
+/*
+ * Routine to get ioctl argument structure from userland.
+ */
+/* ARGSUSED */
+static int
+vhci_get_iocdata(const void *data, sv_iocdata_t *pioc, int mode, caddr_t s)
+{
+	int	retval = 0;
+
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+	{
+		sv_iocdata32_t	ioc32;
+
+		if (ddi_copyin(data, &ioc32, sizeof (ioc32), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		pioc->client	= (caddr_t)(uintptr_t)ioc32.client;
+		pioc->phci	= (caddr_t)(uintptr_t)ioc32.phci;
+		pioc->addr	= (caddr_t)(uintptr_t)ioc32.addr;
+		pioc->buf_elem	= (uint_t)ioc32.buf_elem;
+		pioc->ret_buf	= (sv_path_info_t *)(uintptr_t)ioc32.ret_buf;
+		pioc->ret_elem	= (uint_t *)(uintptr_t)ioc32.ret_elem;
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyin(data, pioc, sizeof (*pioc), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		break;
+	}
+#else   /* _MULTI_DATAMODEL */
+	if (ddi_copyin(data, pioc, sizeof (*pioc), mode)) {
+		retval = EFAULT;
+	}
+#endif  /* _MULTI_DATAMODEL */
+
+#ifdef DEBUG
+	if (retval) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: cmd <%s> "
+		    "iocdata copyin failed", s));
+	}
+#endif
+
+	return (retval);
+}
+
+
+/*
+ * Routine to get the ioctl argument for ioctl causing controller switchover.
+ */
+/* ARGSUSED */
+static int
+vhci_get_iocswitchdata(const void *data, sv_switch_to_cntlr_iocdata_t *piocsc,
+    int mode, caddr_t s)
+{
+	int	retval = 0;
+
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+	{
+		sv_switch_to_cntlr_iocdata32_t	ioc32;
+
+		if (ddi_copyin(data, &ioc32, sizeof (ioc32), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		piocsc->client	= (caddr_t)(uintptr_t)ioc32.client;
+		piocsc->class	= (caddr_t)(uintptr_t)ioc32.class;
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyin(data, piocsc, sizeof (*piocsc), mode)) {
+			retval = EFAULT;
+		}
+		break;
+	}
+#else   /* _MULTI_DATAMODEL */
+	if (ddi_copyin(data, piocsc, sizeof (*piocsc), mode)) {
+		retval = EFAULT;
+	}
+#endif  /* _MULTI_DATAMODEL */
+
+#ifdef DEBUG
+	if (retval) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: cmd <%s> "
+		    "switch_to_cntlr_iocdata copyin failed", s));
+	}
+#endif
+
+	return (retval);
+}
+
+
+/*
+ * Routine to allocate memory for the path information structures.
+ * It allocates two chunks of memory - one for keeping userland
+ * pointers/values for path information and path properties, second for
+ * keeping allocating kernel memory for path properties. These path
+ * properties are finally copied to userland.
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_alloc_pathinfo(sv_path_info_t **upibuf, sv_path_info_t **kpibuf,
+    uint_t num_paths, sv_iocdata_t *pioc, int mode, caddr_t s)
+{
+	sv_path_info_t	*pi;
+	uint_t		bufsize;
+	int		retval = 0;
+	int		index;
+
+	/* Allocate memory */
+	*upibuf = (sv_path_info_t *)
+	    kmem_zalloc(sizeof (sv_path_info_t) * num_paths, KM_SLEEP);
+	ASSERT(*upibuf != NULL);
+	*kpibuf = (sv_path_info_t *)
+	    kmem_zalloc(sizeof (sv_path_info_t) * num_paths, KM_SLEEP);
+	ASSERT(*kpibuf != NULL);
+
+	/*
+	 * Get the path info structure from the user space.
+	 * We are interested in the following fields:
+	 *	- user size of buffer for per path properties.
+	 *	- user address of buffer for path info properties.
+	 *	- user pointer for returning actual buffer size
+	 * Keep these fields in the 'upibuf' structures.
+	 * Allocate buffer for per path info properties in kernel
+	 * structure ('kpibuf').
+	 * Size of these buffers will be equal to the size of buffers
+	 * in the user space.
+	 */
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+	{
+		sv_path_info32_t	*src;
+		sv_path_info32_t	pi32;
+
+		src  = (sv_path_info32_t *)pioc->ret_buf;
+		pi = (sv_path_info_t *)*upibuf;
+		for (index = 0; index < num_paths; index++, src++, pi++) {
+			if (ddi_copyin(src, &pi32, sizeof (pi32), mode)) {
+				retval = EFAULT;
+				break;
+			}
+
+			pi->ret_prop.buf_size	=
+			    (uint_t)pi32.ret_prop.buf_size;
+			pi->ret_prop.ret_buf_size =
+			    (uint_t *)(uintptr_t)pi32.ret_prop.ret_buf_size;
+			pi->ret_prop.buf	=
+			    (caddr_t)(uintptr_t)pi32.ret_prop.buf;
+		}
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyin(pioc->ret_buf, *upibuf,
+		    sizeof (sv_path_info_t) * num_paths, mode)) {
+			retval = EFAULT;
+		}
+		break;
+	}
+#else   /* _MULTI_DATAMODEL */
+	if (ddi_copyin(pioc->ret_buf, *upibuf,
+	    sizeof (sv_path_info_t) * num_paths, mode)) {
+		retval = EFAULT;
+	}
+#endif  /* _MULTI_DATAMODEL */
+
+	if (retval != 0) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_alloc_path_info: "
+		    "ioctl <%s> normal: path_info copyin failed", s));
+		kmem_free(*upibuf, sizeof (sv_path_info_t) * num_paths);
+		kmem_free(*kpibuf, sizeof (sv_path_info_t) * num_paths);
+		*upibuf = NULL;
+		*kpibuf = NULL;
+		return (retval);
+	}
+
+	/*
+	 * Allocate memory for per path properties.
+	 */
+	for (index = 0, pi = *kpibuf; index < num_paths; index++, pi++) {
+		bufsize = (*upibuf)[index].ret_prop.buf_size;
+
+		if (bufsize && bufsize <= SV_PROP_MAX_BUF_SIZE) {
+			pi->ret_prop.buf_size = bufsize;
+			pi->ret_prop.buf = (caddr_t)
+			    kmem_zalloc(bufsize, KM_SLEEP);
+			ASSERT(pi->ret_prop.buf != NULL);
+		} else {
+			pi->ret_prop.buf_size = 0;
+			pi->ret_prop.buf = NULL;
+		}
+
+		if ((*upibuf)[index].ret_prop.ret_buf_size != NULL) {
+			pi->ret_prop.ret_buf_size = (uint_t *)kmem_zalloc(
+			    sizeof (*pi->ret_prop.ret_buf_size), KM_SLEEP);
+			ASSERT(pi->ret_prop.ret_buf_size != NULL);
+		} else {
+			pi->ret_prop.ret_buf_size = NULL;
+		}
+	}
+
+	return (0);
+}
+
+
+/*
+ * Routine to free memory for the path information structures.
+ * This is the memory which was allocated earlier.
+ */
+/* ARGSUSED */
+static void
+vhci_ioc_free_pathinfo(sv_path_info_t *upibuf, sv_path_info_t *kpibuf,
+    uint_t num_paths)
+{
+	sv_path_info_t	*pi;
+	int		index;
+
+	/* Free memory for per path properties */
+	for (index = 0, pi = kpibuf; index < num_paths; index++, pi++) {
+		if (pi->ret_prop.ret_buf_size != NULL) {
+			kmem_free(pi->ret_prop.ret_buf_size,
+			    sizeof (*pi->ret_prop.ret_buf_size));
+		}
+
+		if (pi->ret_prop.buf != NULL) {
+			kmem_free(pi->ret_prop.buf, pi->ret_prop.buf_size);
+		}
+	}
+
+	/* Free memory for path info structures */
+	kmem_free(upibuf, sizeof (sv_path_info_t) * num_paths);
+	kmem_free(kpibuf, sizeof (sv_path_info_t) * num_paths);
+}
+
+
+/*
+ * Routine to copy path information and path properties to userland.
+ */
+/* ARGSUSED */
+static int
+vhci_ioc_send_pathinfo(sv_path_info_t *upibuf, sv_path_info_t *kpibuf,
+    uint_t num_paths, sv_iocdata_t *pioc, int mode, caddr_t s)
+{
+	int			retval = 0, index;
+	sv_path_info_t		*upi_ptr;
+	sv_path_info32_t	*upi32_ptr;
+
+#ifdef  _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(mode & FMODELS)) {
+	case DDI_MODEL_ILP32:
+		goto copy_32bit;
+
+	case DDI_MODEL_NONE:
+		goto copy_normal;
+	}
+#else   /* _MULTI_DATAMODEL */
+
+	goto copy_normal;
+
+#endif  /* _MULTI_DATAMODEL */
+
+copy_normal:
+
+	/*
+	 * Copy path information and path properties to user land.
+	 * Pointer fields inside the path property structure were
+	 * saved in the 'upibuf' structure earlier.
+	 */
+	upi_ptr = pioc->ret_buf;
+	for (index = 0; index < num_paths; index++) {
+		if (ddi_copyout(kpibuf[index].device.ret_ct,
+		    upi_ptr[index].device.ret_ct, MAXPATHLEN, mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(kpibuf[index].ret_addr,
+		    upi_ptr[index].ret_addr, MAXNAMELEN, mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(&kpibuf[index].ret_state,
+		    &upi_ptr[index].ret_state, sizeof (kpibuf[index].ret_state),
+		    mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(&kpibuf[index].ret_ext_state,
+		    &upi_ptr[index].ret_ext_state,
+		    sizeof (kpibuf[index].ret_ext_state), mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if ((kpibuf[index].ret_prop.ret_buf_size != NULL) &&
+		    ddi_copyout(kpibuf[index].ret_prop.ret_buf_size,
+		    upibuf[index].ret_prop.ret_buf_size,
+		    sizeof (*upibuf[index].ret_prop.ret_buf_size), mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if ((kpibuf[index].ret_prop.buf != NULL) &&
+		    ddi_copyout(kpibuf[index].ret_prop.buf,
+		    upibuf[index].ret_prop.buf,
+		    upibuf[index].ret_prop.buf_size, mode)) {
+			retval = EFAULT;
+			break;
+		}
+	}
+
+#ifdef DEBUG
+	if (retval) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: ioctl <%s> "
+		    "normal: path_info copyout failed", s));
+	}
+#endif
+
+	return (retval);
+
+copy_32bit:
+	/*
+	 * Copy path information and path properties to user land.
+	 * Pointer fields inside the path property structure were
+	 * saved in the 'upibuf' structure earlier.
+	 */
+	upi32_ptr = (sv_path_info32_t *)pioc->ret_buf;
+	for (index = 0; index < num_paths; index++) {
+		if (ddi_copyout(kpibuf[index].device.ret_ct,
+		    upi32_ptr[index].device.ret_ct, MAXPATHLEN, mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(kpibuf[index].ret_addr,
+		    upi32_ptr[index].ret_addr, MAXNAMELEN, mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(&kpibuf[index].ret_state,
+		    &upi32_ptr[index].ret_state,
+		    sizeof (kpibuf[index].ret_state), mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if (ddi_copyout(&kpibuf[index].ret_ext_state,
+		    &upi32_ptr[index].ret_ext_state,
+		    sizeof (kpibuf[index].ret_ext_state), mode)) {
+			retval = EFAULT;
+			break;
+		}
+		if ((kpibuf[index].ret_prop.ret_buf_size != NULL) &&
+		    ddi_copyout(kpibuf[index].ret_prop.ret_buf_size,
+		    upibuf[index].ret_prop.ret_buf_size,
+		    sizeof (*upibuf[index].ret_prop.ret_buf_size), mode)) {
+			retval = EFAULT;
+			break;
+		}
+
+		if ((kpibuf[index].ret_prop.buf != NULL) &&
+		    ddi_copyout(kpibuf[index].ret_prop.buf,
+		    upibuf[index].ret_prop.buf,
+		    upibuf[index].ret_prop.buf_size, mode)) {
+			retval = EFAULT;
+			break;
+		}
+	}
+
+#ifdef DEBUG
+	if (retval) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_get_ioc: ioctl <%s> "
+		    "normal: path_info copyout failed", s));
+	}
+#endif
+
+	return (retval);
+}
+
+
+/*
+ * vhci_failover()
+ * This routine expects VHCI_HOLD_LUN before being invoked.  It can be invoked
+ * as MDI_FAILOVER_ASYNC or MDI_FAILOVER_SYNC.  For Asynchronous failovers
+ * this routine shall VHCI_RELEASE_LUN on exiting.  For synchronous failovers
+ * it is the callers responsibility to release lun.
+ */
+
+/* ARGSUSED */
+static int
+vhci_failover(dev_info_t *vdip, dev_info_t *cdip, int flags)
+{
+	char			*guid;
+	scsi_vhci_lun_t		*vlun = NULL;
+	struct scsi_vhci	*vhci;
+	mdi_pathinfo_t		*pip, *npip;
+	char			*s_pclass, *pclass1, *pclass2, *pclass;
+	char			active_pclass_copy[255], *active_pclass_ptr;
+	char			*ptr1, *ptr2;
+	mdi_pathinfo_state_t	pi_state;
+	uint32_t		pi_ext_state;
+	scsi_vhci_priv_t	*svp;
+	struct scsi_device	*sd;
+	struct scsi_failover_ops	*sfo;
+	int			sps; /* mdi_select_path() status */
+	int			activation_done = 0;
+	int			rval, retval = MDI_FAILURE;
+	int			reserve_pending, check_condition, UA_condition;
+	struct scsi_pkt		*pkt;
+	struct buf		*bp;
+
+	vhci = ddi_get_soft_state(vhci_softstate, ddi_get_instance(vdip));
+	sd = ddi_get_driver_private(cdip);
+	vlun = ADDR2VLUN(&sd->sd_address);
+	ASSERT(vlun != 0);
+	ASSERT(VHCI_LUN_IS_HELD(vlun));
+	guid = vlun->svl_lun_wwn;
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(1): guid %s\n", guid));
+	vhci_log(CE_NOTE, vdip, "!Initiating failover for device %s "
+	    "(GUID %s)", ddi_node_name(cdip), guid);
+
+	/*
+	 * Lets maintain a local copy of the vlun->svl_active_pclass
+	 * for the rest of the processing. Accessing the field
+	 * directly in the loop below causes loop logic to break
+	 * especially when the field gets updated by other threads
+	 * update path status etc and causes 'paths are not currently
+	 * available' condition to be declared prematurely.
+	 */
+	mutex_enter(&vlun->svl_mutex);
+	if (vlun->svl_active_pclass != NULL) {
+		(void) strlcpy(active_pclass_copy, vlun->svl_active_pclass,
+		    sizeof (active_pclass_copy));
+		active_pclass_ptr = &active_pclass_copy[0];
+		mutex_exit(&vlun->svl_mutex);
+		if (vhci_quiesce_paths(vdip, cdip, vlun, guid,
+		    active_pclass_ptr) != 0) {
+			retval = MDI_FAILURE;
+		}
+	} else {
+		/*
+		 * can happen only when the available path to device
+		 * discovered is a STANDBY path.
+		 */
+		mutex_exit(&vlun->svl_mutex);
+		active_pclass_copy[0] = '\0';
+		active_pclass_ptr = NULL;
+	}
+
+	sfo = vlun->svl_fops;
+	ASSERT(sfo != NULL);
+	pclass1 = s_pclass = active_pclass_ptr;
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!(%s)failing over from %s\n", guid,
+	    (s_pclass == NULL ? "<none>" : s_pclass)));
+
+next_pathclass:
+
+	rval = (*sfo->sfo_pathclass_next)(pclass1, &pclass2,
+	    vlun->svl_fops_ctpriv);
+	if (rval == ENOENT) {
+		if (s_pclass == NULL) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(4)(%s): "
+			    "failed, no more pathclasses\n", guid));
+			goto done;
+		} else {
+			(*sfo->sfo_pathclass_next)(NULL, &pclass2,
+			    vlun->svl_fops_ctpriv);
+		}
+	} else if (rval == EINVAL) {
+		vhci_log(CE_NOTE, vdip, "!Failover operation failed for "
+		    "device %s (GUID %s): Invalid path-class %s",
+		    ddi_node_name(cdip), guid,
+		    ((pclass1 == NULL) ? "<none>" : pclass1));
+		goto done;
+	}
+	if ((s_pclass != NULL) && (strcmp(pclass2, s_pclass) == 0)) {
+		/*
+		 * paths are not currently available
+		 */
+		vhci_log(CE_NOTE, vdip, "!Failover path currently unavailable"
+		    " for device %s (GUID %s)",
+		    ddi_node_name(cdip), guid);
+		goto done;
+	}
+	pip = npip = NULL;
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(5.2)(%s): considering "
+	    "%s as failover destination\n", guid, pclass2));
+	sps = mdi_select_path(cdip, NULL, MDI_SELECT_STANDBY_PATH, NULL, &npip);
+	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(%s): no "
+		    "STANDBY paths found (status:%x)!\n", guid, sps));
+		pclass1 = pclass2;
+		goto next_pathclass;
+	}
+	do {
+		pclass = NULL;
+		if ((mdi_prop_lookup_string(npip, "path-class",
+		    &pclass) != MDI_SUCCESS) || (strcmp(pclass2,
+		    pclass) != 0)) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_failover(5.5)(%s): skipping path "
+			    "%p(%s)...\n", guid, (void *)npip, pclass));
+			pip = npip;
+			sps = mdi_select_path(cdip, NULL,
+			    MDI_SELECT_STANDBY_PATH, pip, &npip);
+			mdi_rele_path(pip);
+			(void) mdi_prop_free(pclass);
+			continue;
+		}
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(npip);
+
+		/*
+		 * Issue READ at non-zer block on this STANDBY path.
+		 * Purple returns
+		 * 1. RESERVATION_CONFLICT if reservation is pending
+		 * 2. POR check condition if it reset happened.
+		 * 2. failover Check Conditions if one is already in progress.
+		 */
+		reserve_pending = 0;
+		check_condition = 0;
+		UA_condition = 0;
+
+		bp = scsi_alloc_consistent_buf(&svp->svp_psd->sd_address,
+		    (struct buf *)NULL, DEV_BSIZE, B_READ, NULL, NULL);
+		if (!bp) {
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "vhci_failover !No resources (buf)\n"));
+			mdi_rele_path(npip);
+			goto done;
+		}
+		pkt = scsi_init_pkt(&svp->svp_psd->sd_address, NULL, bp,
+		    CDB_GROUP1, sizeof (struct scsi_arq_status), 0,
+		    PKT_CONSISTENT, NULL, NULL);
+		if (pkt) {
+			(void) scsi_setup_cdb((union scsi_cdb *)(uintptr_t)
+			    pkt->pkt_cdbp, SCMD_READ, 1, 1, 0);
+			pkt->pkt_flags = FLAG_NOINTR;
+check_path_again:
+			pkt->pkt_time = 3*30;
+			if (scsi_transport(pkt) == TRAN_ACCEPT) {
+				switch (pkt->pkt_reason) {
+				case CMD_CMPLT:
+					switch (SCBP_C(pkt)) {
+					case STATUS_GOOD:
+						/* Already failed over */
+						activation_done = 1;
+						break;
+					case STATUS_RESERVATION_CONFLICT:
+						reserve_pending = 1;
+						break;
+					case STATUS_CHECK:
+						check_condition = 1;
+						break;
+					}
+				}
+			}
+			if (check_condition &&
+			    (pkt->pkt_state & STATE_ARQ_DONE)) {
+				struct scsi_extended_sense *sns =
+				    &(((struct scsi_arq_status *)(uintptr_t)
+				    (pkt->pkt_scbp))->sts_sensedata);
+				if (sns->es_key == KEY_UNIT_ATTENTION &&
+				    sns->es_add_code == 0x29) {
+					/* Already failed over */
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "!vhci_failover(7)(%s): "
+					    "path 0x%p POR UA condition\n",
+					    guid, (void *)npip));
+					if (UA_condition == 0) {
+						UA_condition = 1;
+						goto check_path_again;
+					}
+				} else {
+					activation_done = 0;
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "!vhci_failover(%s): path 0x%p "
+					    "unhandled chkcond %x %x %x\n",
+					    guid, (void *)npip, sns->es_key,
+					    sns->es_add_code,
+					    sns->es_qual_code));
+				}
+			}
+			scsi_destroy_pkt(pkt);
+		}
+		scsi_free_consistent_buf(bp);
+
+		if (activation_done) {
+			mdi_rele_path(npip);
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(7)(%s): "
+			    "path 0x%p already failedover\n", guid,
+			    (void *)npip));
+			break;
+		}
+		if (reserve_pending && (vlun->svl_xlf_capable == 0)) {
+			(void) vhci_recovery_reset(vlun,
+			    &svp->svp_psd->sd_address,
+			    FALSE, VHCI_DEPTH_ALL);
+		}
+		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,
+		    vlun->svl_fops_ctpriv) == 0) {
+			activation_done = 1;
+			mdi_rele_path(npip);
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(7)(%s): "
+			    "path 0x%p successfully activated\n", guid,
+			    (void *)npip));
+			break;
+		}
+		pip = npip;
+		sps = mdi_select_path(cdip, NULL, MDI_SELECT_STANDBY_PATH,
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((npip != NULL) && (sps == MDI_SUCCESS));
+	if (activation_done == 0) {
+		pclass1 = pclass2;
+		goto next_pathclass;
+	}
+
+	/*
+	 * if we are here, we have succeeded in activating path npip of
+	 * pathclass pclass2; let us validate all paths of pclass2 by
+	 * "ping"-ing each one and mark the good ones ONLINE
+	 * Also, set the state of the paths belonging to the previously
+	 * active pathclass to STANDBY
+	 */
+	pip = npip = NULL;
+	sps = mdi_select_path(cdip, NULL, (MDI_SELECT_ONLINE_PATH |
+	    MDI_SELECT_STANDBY_PATH | MDI_SELECT_USER_DISABLE_PATH),
+	    NULL, &npip);
+	if (npip == NULL || sps != MDI_SUCCESS) {
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "!Failover operation failed for "
+		    "device %s (GUID %s): paths may be busy\n",
+		    ddi_node_name(cdip), guid));
+		goto done;
+	}
+	do {
+		(void) mdi_pi_get_state2(npip, &pi_state, &pi_ext_state);
+		if (mdi_prop_lookup_string(npip, "path-class", &pclass)
+		    != MDI_SUCCESS) {
+			pip = npip;
+			sps = mdi_select_path(cdip, NULL,
+			    (MDI_SELECT_ONLINE_PATH |
+			    MDI_SELECT_STANDBY_PATH |
+			    MDI_SELECT_USER_DISABLE_PATH),
+			    pip, &npip);
+			mdi_rele_path(pip);
+			continue;
+		}
+		if (strcmp(pclass, pclass2) == 0) {
+			if (pi_state == MDI_PATHINFO_STATE_STANDBY) {
+				svp = (scsi_vhci_priv_t *)
+				    mdi_pi_get_vhci_private(npip);
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_failover(8)(%s): "
+				    "pinging path 0x%p\n",
+				    guid, (void *)npip));
+				if ((*sfo->sfo_path_ping)(svp->svp_psd,
+				    vlun->svl_fops_ctpriv) == 1) {
+					mdi_pi_set_state(npip,
+					    MDI_PATHINFO_STATE_ONLINE);
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "!vhci_failover(9)(%s): "
+					    "path 0x%p ping successful, "
+					    "marked online\n", guid,
+					    (void *)npip));
+					MDI_PI_ERRSTAT(npip, MDI_PI_FAILTO);
+				}
+			}
+		} else if ((s_pclass != NULL) && (strcmp(pclass, s_pclass)
+		    == 0)) {
+			if (pi_state == MDI_PATHINFO_STATE_ONLINE) {
+				mdi_pi_set_state(npip,
+				    MDI_PATHINFO_STATE_STANDBY);
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_failover(10)(%s): path 0x%p marked "
+				    "STANDBY\n", guid, (void *)npip));
+				MDI_PI_ERRSTAT(npip, MDI_PI_FAILFROM);
+			}
+		}
+		(void) mdi_prop_free(pclass);
+		pip = npip;
+		sps = mdi_select_path(cdip, NULL, (MDI_SELECT_ONLINE_PATH |
+		    MDI_SELECT_STANDBY_PATH|MDI_SELECT_USER_DISABLE_PATH),
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((npip != NULL) && (sps == MDI_SUCCESS));
+
+	/*
+	 * Update the AccessState of related MP-API TPGs
+	 */
+	(void) vhci_mpapi_update_tpg_acc_state_for_lu(vhci, vlun);
+
+	vhci_log(CE_NOTE, vdip, "!Failover operation completed successfully "
+	    "for device %s (GUID %s): failed over from %s to %s",
+	    ddi_node_name(cdip), guid, ((s_pclass == NULL) ? "<none>" :
+	    s_pclass), pclass2);
+	ptr1 = kmem_alloc(strlen(pclass2)+1, KM_SLEEP);
+	(void) strlcpy(ptr1, pclass2, (strlen(pclass2)+1));
+	mutex_enter(&vlun->svl_mutex);
+	ptr2 = vlun->svl_active_pclass;
+	vlun->svl_active_pclass = ptr1;
+	mutex_exit(&vlun->svl_mutex);
+	if (ptr2) {
+		kmem_free(ptr2, strlen(ptr2)+1);
+	}
+	mutex_enter(&vhci->vhci_mutex);
+	scsi_hba_reset_notify_callback(&vhci->vhci_mutex,
+	    &vhci->vhci_reset_notify_listf);
+	/* All reservations are cleared upon these resets. */
+	vlun->svl_flags &= ~VLUN_RESERVE_ACTIVE_FLG;
+	mutex_exit(&vhci->vhci_mutex);
+	VHCI_DEBUG(1, (CE_NOTE, NULL, "!vhci_failover(11): DONE! Active "
+	    "pathclass for %s is now %s\n", guid, pclass2));
+	retval = MDI_SUCCESS;
+
+done:
+	if (flags == MDI_FAILOVER_ASYNC) {
+		VHCI_RELEASE_LUN(vlun);
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_failover(12): DONE! "
+		    "releasing lun, as failover was ASYNC\n"));
+	} else {
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_failover(12): DONE! "
+		    "NOT releasing lun, as failover was SYNC\n"));
+	}
+	return (retval);
+}
+
+/*
+ * vhci_client_attached is called after the successful attach of a
+ * client devinfo node.
+ */
+static void
+vhci_client_attached(dev_info_t *cdip)
+{
+	mdi_pathinfo_t	*pip;
+	int		circular;
+
+	/*
+	 * At this point the client has attached and it's instance number is
+	 * valid, so we can set up kstats.  We need to do this here because it
+	 * is possible for paths to go online prior to client attach, in which
+	 * case the call to vhci_kstat_create_pathinfo in vhci_pathinfo_online
+	 * was a noop.
+	 */
+	ndi_devi_enter(cdip, &circular);
+	for (pip = mdi_get_next_phci_path(cdip, NULL); pip;
+	    pip = mdi_get_next_phci_path(cdip, pip))
+		vhci_kstat_create_pathinfo(pip);
+	ndi_devi_exit(cdip, circular);
+}
+
+/*
+ * quiesce all of the online paths
+ */
+static int
+vhci_quiesce_paths(dev_info_t *vdip, dev_info_t *cdip, scsi_vhci_lun_t *vlun,
+	char *guid, char *active_pclass_ptr)
+{
+	scsi_vhci_priv_t	*svp;
+	char			*s_pclass = NULL;
+	mdi_pathinfo_t		*npip, *pip;
+	int			sps;
+
+	/* quiesce currently active paths */
+	s_pclass = NULL;
+	pip = npip = NULL;
+	sps = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH, NULL, &npip);
+	if ((npip == NULL) || (sps != MDI_SUCCESS)) {
+		return (1);
+	}
+	do {
+		if (mdi_prop_lookup_string(npip, "path-class",
+		    &s_pclass) != MDI_SUCCESS) {
+			mdi_rele_path(npip);
+			vhci_log(CE_NOTE, vdip, "!Failover operation failed "
+			    "for device %s (GUID %s) due to an internal "
+			    "error", ddi_node_name(cdip), guid);
+			return (1);
+		}
+		if (strcmp(s_pclass, active_pclass_ptr) == 0) {
+			/*
+			 * quiesce path. Free s_pclass since
+			 * we don't need it anymore
+			 */
+			VHCI_DEBUG(1, (CE_NOTE, NULL,
+			    "!vhci_failover(2)(%s): failing over "
+			    "from %s; quiescing path %p\n",
+			    guid, s_pclass, (void *)npip));
+			(void) mdi_prop_free(s_pclass);
+			svp = (scsi_vhci_priv_t *)
+			    mdi_pi_get_vhci_private(npip);
+			if (svp == NULL) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_failover(2.5)(%s): no "
+				    "client priv! %p offlined?\n",
+				    guid, (void *)npip));
+				pip = npip;
+				sps = mdi_select_path(cdip, NULL,
+				    MDI_SELECT_ONLINE_PATH, pip, &npip);
+				mdi_rele_path(pip);
+				continue;
+			}
+			if (scsi_abort(&svp->svp_psd->sd_address, NULL)
+			    == 0) {
+				(void) vhci_recovery_reset(vlun,
+				    &svp->svp_psd->sd_address, FALSE,
+				    VHCI_DEPTH_TARGET);
+			}
+			mutex_enter(&svp->svp_mutex);
+			if (svp->svp_cmds == 0) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!vhci_failover(3)(%s):"
+				    "quiesced path %p\n", guid, (void *)npip));
+			} else {
+				while (svp->svp_cmds != 0) {
+					cv_wait(&svp->svp_cv, &svp->svp_mutex);
+					VHCI_DEBUG(1, (CE_NOTE, NULL,
+					    "!vhci_failover(3.cv)(%s):"
+					    "quiesced path %p\n", guid,
+					    (void *)npip));
+				}
+			}
+			mutex_exit(&svp->svp_mutex);
+		} else {
+			/*
+			 * make sure we freeup the memory
+			 */
+			(void) mdi_prop_free(s_pclass);
+		}
+		pip = npip;
+		sps = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH,
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((npip != NULL) && (sps == MDI_SUCCESS));
+	return (0);
+}
+
+static struct scsi_vhci_lun *
+vhci_lun_lookup(dev_info_t *tgt_dip)
+{
+	return ((struct scsi_vhci_lun *)
+	    mdi_client_get_vhci_private(tgt_dip));
+}
+
+static struct scsi_vhci_lun *
+vhci_lun_lookup_alloc(dev_info_t *tgt_dip, char *guid, int *didalloc)
+{
+	struct scsi_vhci_lun *svl;
+
+	if (svl = vhci_lun_lookup(tgt_dip)) {
+		return (svl);
+	}
+
+	svl = kmem_zalloc(sizeof (*svl), KM_SLEEP);
+	svl->svl_lun_wwn = kmem_zalloc(strlen(guid)+1, KM_SLEEP);
+	(void) strcpy(svl->svl_lun_wwn,  guid);
+	mutex_init(&svl->svl_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&svl->svl_cv, NULL, CV_DRIVER, NULL);
+	sema_init(&svl->svl_pgr_sema, 1, NULL, SEMA_DRIVER, NULL);
+	svl->svl_waiting_for_activepath = 1;
+	svl->svl_sector_size = 1;
+	mdi_client_set_vhci_private(tgt_dip, svl);
+	*didalloc = 1;
+	VHCI_DEBUG(1, (CE_NOTE, NULL,
+	    "vhci_lun_lookup_alloc: guid %s vlun 0x%p\n",
+	    guid, (void *)svl));
+	return (svl);
+}
+
+static void
+vhci_lun_free(dev_info_t *tgt_dip)
+{
+	struct scsi_vhci_lun *dvlp;
+	char *guid;
+
+	dvlp = (struct scsi_vhci_lun *)
+	    mdi_client_get_vhci_private(tgt_dip);
+	ASSERT(dvlp != NULL);
+
+	mdi_client_set_vhci_private(tgt_dip, NULL);
+
+	guid = dvlp->svl_lun_wwn;
+	ASSERT(guid != NULL);
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "!vhci_lun_free: %s\n", guid));
+
+	mutex_enter(&dvlp->svl_mutex);
+	if (dvlp->svl_active_pclass != NULL) {
+		kmem_free(dvlp->svl_active_pclass,
+		    strlen(dvlp->svl_active_pclass)+1);
+	}
+	dvlp->svl_active_pclass = NULL;
+	mutex_exit(&dvlp->svl_mutex);
+
+	if (dvlp->svl_lun_wwn != NULL) {
+		kmem_free(dvlp->svl_lun_wwn, strlen(dvlp->svl_lun_wwn)+1);
+	}
+	dvlp->svl_lun_wwn = NULL;
+
+	if (dvlp->svl_fops_name) {
+		kmem_free(dvlp->svl_fops_name, strlen(dvlp->svl_fops_name)+1);
+	}
+	dvlp->svl_fops_name = NULL;
+
+	if (dvlp->svl_flags & VLUN_TASK_D_ALIVE_FLG)
+		taskq_destroy(dvlp->svl_taskq);
+
+	mutex_destroy(&dvlp->svl_mutex);
+	cv_destroy(&dvlp->svl_cv);
+	sema_destroy(&dvlp->svl_pgr_sema);
+	kmem_free(dvlp, sizeof (*dvlp));
+}
+
+
+int
+vhci_do_scsi_cmd(struct scsi_pkt *pkt)
+{
+	int	err = 0;
+	int	retry_cnt = 0;
+	struct scsi_extended_sense	*sns;
+
+retry:
+	err = scsi_poll(pkt);
+	if (err) {
+		if (pkt->pkt_cdbp[0] == SCMD_RELEASE) {
+			if (SCBP_C(pkt) == STATUS_RESERVATION_CONFLICT) {
+				VHCI_DEBUG(1, (CE_NOTE, NULL,
+				    "!v_s_do_s_c: RELEASE conflict\n"));
+				return (0);
+			}
+		}
+		if (retry_cnt++ < 3) {
+			VHCI_DEBUG(1, (CE_WARN, NULL,
+			    "!v_s_do_s_c:retry packet 0x%p "
+			    "status 0x%x reason %s",
+			    (void *)pkt, SCBP_C(pkt),
+			    scsi_rname(pkt->pkt_reason)));
+			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);
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!v_s_do_s_c:retry "
+				    "packet 0x%p  sense data %s", (void *)pkt,
+				    scsi_sname(sns->es_key)));
+			}
+			goto retry;
+		}
+		VHCI_DEBUG(1, (CE_WARN, NULL,
+		    "!v_s_do_s_c: failed transport 0x%p 0x%x",
+		    (void *)pkt, SCBP_C(pkt)));
+		return (0);
+	}
+
+	switch (pkt->pkt_reason) {
+		case CMD_TIMEOUT:
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!pkt timed "
+			    "out (pkt 0x%p)", (void *)pkt));
+			return (0);
+		case CMD_CMPLT:
+			switch (SCBP_C(pkt)) {
+				case STATUS_GOOD:
+					break;
+				case STATUS_CHECK:
+					if (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_key ==
+						    KEY_NOT_READY)) {
+							/*
+							 * clear unit attn.
+							 */
+
+							VHCI_DEBUG(1,
+							    (CE_WARN, NULL,
+							    "!v_s_do_s_c: "
+							    "retry "
+							    "packet 0x%p sense "
+							    "data %s",
+							    (void *)pkt,
+							    scsi_sname
+							    (sns->es_key)));
+							goto retry;
+						}
+						VHCI_DEBUG(4, (CE_WARN, NULL,
+						    "!ARQ while "
+						    "transporting "
+						    "(pkt 0x%p)",
+						    (void *)pkt));
+						return (0);
+					}
+					return (0);
+				default:
+					VHCI_DEBUG(1, (CE_WARN, NULL,
+					    "!Bad status returned "
+					    "(pkt 0x%p, status %x)",
+					    (void *)pkt, SCBP_C(pkt)));
+					return (0);
+			}
+			break;
+		case CMD_INCOMPLETE:
+		case CMD_RESET:
+		case CMD_ABORTED:
+		case CMD_TRAN_ERR:
+			if (retry_cnt++ < 1) {
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "!v_s_do_s_c: retry packet 0x%p %s",
+				    (void *)pkt, scsi_rname(pkt->pkt_reason)));
+				goto retry;
+			}
+			/* FALLTHROUGH */
+		default:
+			VHCI_DEBUG(1, (CE_WARN, NULL, "!pkt did not "
+			    "complete successfully (pkt 0x%p,"
+			    "reason %x)", (void *)pkt, pkt->pkt_reason));
+			return (0);
+	}
+	return (1);
+}
+
+static int
+vhci_quiesce_lun(struct scsi_vhci_lun *vlun)
+{
+	mdi_pathinfo_t		*pip, *spip;
+	dev_info_t		*cdip;
+	struct scsi_vhci_priv	*svp;
+	mdi_pathinfo_state_t	pstate;
+	uint32_t		p_ext_state;
+	int			circular;
+
+	cdip = vlun->svl_dip;
+	pip = spip = NULL;
+	ndi_devi_enter(cdip, &circular);
+	pip = mdi_get_next_phci_path(cdip, NULL);
+	while (pip != NULL) {
+		(void) mdi_pi_get_state2(pip, &pstate, &p_ext_state);
+		if (pstate != MDI_PATHINFO_STATE_ONLINE) {
+			spip = pip;
+			pip = mdi_get_next_phci_path(cdip, spip);
+			continue;
+		}
+		mdi_hold_path(pip);
+		ndi_devi_exit(cdip, circular);
+		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+		mutex_enter(&svp->svp_mutex);
+		while (svp->svp_cmds != 0) {
+			if (cv_timedwait(&svp->svp_cv, &svp->svp_mutex,
+			    ddi_get_lbolt() + drv_usectohz
+			    (vhci_path_quiesce_timeout * 1000000)) == -1) {
+				mutex_exit(&svp->svp_mutex);
+				mdi_rele_path(pip);
+				VHCI_DEBUG(1, (CE_WARN, NULL,
+				    "Quiesce of lun is not successful "
+				    "vlun: 0x%p.", (void *)vlun));
+				return (0);
+			}
+		}
+		mutex_exit(&svp->svp_mutex);
+		ndi_devi_enter(cdip, &circular);
+		spip = pip;
+		pip = mdi_get_next_phci_path(cdip, spip);
+		mdi_rele_path(spip);
+	}
+	ndi_devi_exit(cdip, circular);
+	return (1);
+}
+
+static int
+vhci_pgr_validate_and_register(scsi_vhci_priv_t *svp)
+{
+	scsi_vhci_lun_t		*vlun;
+	vhci_prout_t		*prout;
+	int			rval, success;
+	mdi_pathinfo_t		*pip, *npip;
+	scsi_vhci_priv_t	*osvp;
+	dev_info_t		*cdip;
+	uchar_t			cdb_1;
+	uchar_t			temp_res_key[MHIOC_RESV_KEY_SIZE];
+
+
+	/*
+	 * see if there are any other paths available; if none,
+	 * then there is nothing to do.
+	 */
+	cdip = svp->svp_svl->svl_dip;
+	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH |
+	    MDI_SELECT_STANDBY_PATH, NULL, &pip);
+	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "%s%d: vhci_pgr_validate_and_register: first path\n",
+		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
+		return (1);
+	}
+
+	vlun = svp->svp_svl;
+	prout = &vlun->svl_prout;
+	ASSERT(vlun->svl_pgr_active != 0);
+
+	/*
+	 * When the path was busy/offlined, some other host might have
+	 * cleared this key. Validate key on some other path first.
+	 * If it fails, return failure.
+	 */
+
+	npip = pip;
+	pip = NULL;
+	success = 0;
+
+	/* Save the res key */
+	bcopy((const void *)prout->res_key,
+	    (void *)temp_res_key, MHIOC_RESV_KEY_SIZE);
+
+	/*
+	 * Sometimes CDB from application can be a Register_And_Ignore.
+	 * Instead of validation, this cdb would result in force registration.
+	 * Convert it to normal cdb for validation.
+	 * After that be sure to restore the cdb.
+	 */
+	cdb_1 = vlun->svl_cdb[1];
+	vlun->svl_cdb[1] &= 0xe0;
+
+	do {
+		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 "
+			    "client priv! 0x%p offlined?\n",
+			    (void *)npip));
+			goto next_path_1;
+		}
+
+		if (osvp == svp) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_pgr_validate_and_register: same svp 0x%p"
+			    " npip 0x%p vlun 0x%p\n",
+			    (void *)svp, (void *)npip, (void *)vlun));
+			goto next_path_1;
+		}
+
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_pgr_validate_and_register: First validate on"
+		    " osvp 0x%p being done. vlun 0x%p thread 0x%p Before bcopy"
+		    " cdb1 %x\n", (void *)osvp, (void *)vlun,
+		    (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);
+
+		VHCI_DEBUG(4, (CE_WARN, NULL, "vlun 0x%p After bcopy",
+		    (void *)vlun));
+		vhci_print_prout_keys(vlun, "v_pgr_val_reg: after bcopy: ");
+
+		rval = vhci_do_prout(osvp);
+		if (rval == 1) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "%s%d: vhci_pgr_validate_and_register: key"
+			    " validated thread 0x%p\n", ddi_driver_name(cdip),
+			    ddi_get_instance(cdip), (void *)curthread));
+			pip = npip;
+			success = 1;
+			break;
+		} else {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_pgr_validate_and_register: First validation"
+			    " on osvp 0x%p failed %x\n", (void *)osvp, rval));
+			vhci_print_prout_keys(vlun, "v_pgr_val_reg: failed:");
+		}
+
+		/*
+		 * Try other paths
+		 */
+next_path_1:
+		pip = npip;
+		rval = mdi_select_path(cdip, NULL,
+		    MDI_SELECT_ONLINE_PATH|MDI_SELECT_STANDBY_PATH,
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((rval == MDI_SUCCESS) && (npip != NULL));
+
+
+	/* Be sure to restore original cdb */
+	vlun->svl_cdb[1] = cdb_1;
+
+	/* Restore the res_key */
+	bcopy((const void *)temp_res_key,
+	    (void *)prout->res_key, MHIOC_RESV_KEY_SIZE);
+
+	/*
+	 * If key could not be registered on any path for the first time,
+	 * return success as online should still continue.
+	 */
+	if (success == 0) {
+		return (1);
+	}
+
+	ASSERT(pip != NULL);
+
+	/*
+	 * Force register on new path
+	 */
+	cdb_1 = vlun->svl_cdb[1];		/* store the cdb */
+
+	vlun->svl_cdb[1] &= 0xe0;
+	vlun->svl_cdb[1] |= VHCI_PROUT_R_AND_IGNORE;
+
+	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);
+
+	vhci_print_prout_keys(vlun, "v_pgr_val_reg:keys after bcopy: ");
+
+	rval = vhci_do_prout(svp);
+	vlun->svl_cdb[1] = cdb_1;		/* restore the cdb */
+	if (rval != 1) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_pgr_validate_and_register: register on new"
+		    " path 0x%p svp 0x%p failed %x\n",
+		    (void *)pip, (void *)svp, rval));
+		vhci_print_prout_keys(vlun, "v_pgr_val_reg: reg failed: ");
+		mdi_rele_path(pip);
+		return (0);
+	}
+
+	if (bcmp(prout->service_key, zero_key, MHIOC_RESV_KEY_SIZE) == 0) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_pgr_validate_and_register: zero service key\n"));
+		mdi_rele_path(pip);
+		return (rval);
+	}
+
+	/*
+	 * While the key was force registered, some other host might have
+	 * cleared the key. Re-validate key on another pre-existing path
+	 * before declaring success.
+	 */
+	npip = pip;
+	pip = NULL;
+
+	/*
+	 * Sometimes CDB from application can be Register and Ignore.
+	 * Instead of validation, it would result in force registration.
+	 * Convert it to normal cdb for validation.
+	 * After that be sure to restore the cdb.
+	 */
+	cdb_1 = vlun->svl_cdb[1];
+	vlun->svl_cdb[1] &= 0xe0;
+	success = 0;
+
+	do {
+		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 "
+			    "client priv! 0x%p offlined?\n",
+			    (void *)npip));
+			goto next_path_2;
+		}
+
+		if (osvp == svp) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_pgr_validate_and_register: same osvp 0x%p"
+			    " npip 0x%p vlun 0x%p\n",
+			    (void *)svp, (void *)npip, (void *)vlun));
+			goto next_path_2;
+		}
+
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_pgr_validate_and_register: Re-validation on"
+		    " osvp 0x%p being done. vlun 0x%p Before bcopy cdb1 %x\n",
+		    (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);
+
+		vhci_print_prout_keys(vlun, "v_pgr_val_reg: after bcopy: ");
+
+		rval = vhci_do_prout(osvp);
+		if (rval == 1) {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "%s%d: vhci_pgr_validate_and_register: key"
+			    " validated thread 0x%p\n", ddi_driver_name(cdip),
+			    ddi_get_instance(cdip), (void *)curthread));
+			pip = npip;
+			success = 1;
+			break;
+		} else {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_pgr_validate_and_register: Re-validation on"
+			    " osvp 0x%p failed %x\n", (void *)osvp, rval));
+			vhci_print_prout_keys(vlun,
+			    "v_pgr_val_reg: reval failed: ");
+		}
+
+		/*
+		 * Try other paths
+		 */
+next_path_2:
+		pip = npip;
+		rval = mdi_select_path(cdip, NULL,
+		    MDI_SELECT_ONLINE_PATH|MDI_SELECT_STANDBY_PATH,
+		    pip, &npip);
+		mdi_rele_path(pip);
+	} while ((rval == MDI_SUCCESS) && (npip != NULL));
+
+	/* Be sure to restore original cdb */
+	vlun->svl_cdb[1] = cdb_1;
+
+	if (success == 1) {
+		/* Successfully validated registration */
+		mdi_rele_path(pip);
+		return (1);
+	}
+
+	VHCI_DEBUG(4, (CE_WARN, NULL, "key validation failed"));
+
+	/*
+	 * key invalid, back out by registering key value of 0
+	 */
+	VHCI_DEBUG(4, (CE_NOTE, NULL,
+	    "vhci_pgr_validate_and_register: backout on"
+	    " 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);
+
+	vhci_print_prout_keys(vlun, "v_pgr_val_reg: before bcopy: ");
+
+	/*
+	 * Get a new path
+	 */
+	rval = mdi_select_path(cdip, NULL, MDI_SELECT_ONLINE_PATH |
+	    MDI_SELECT_STANDBY_PATH, NULL, &pip);
+	if ((rval != MDI_SUCCESS) || (pip == NULL)) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "%s%d: vhci_pgr_validate_and_register: no valid pip\n",
+		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
+		return (0);
+	}
+
+	if ((rval = vhci_do_prout(svp)) != 1) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_pgr_validate_and_register: backout on"
+		    " svp 0x%p failed\n", (void *)svp));
+		vhci_print_prout_keys(vlun, "backout failed");
+
+		VHCI_DEBUG(4, (CE_WARN, NULL,
+		    "%s%d: vhci_pgr_validate_and_register: key"
+		    " validation and backout failed", ddi_driver_name(cdip),
+		    ddi_get_instance(cdip)));
+		if (rval == VHCI_PGR_ILLEGALOP) {
+			VHCI_DEBUG(4, (CE_WARN, NULL,
+			    "%s%d: vhci_pgr_validate_and_register: key"
+			    " already cleared", ddi_driver_name(cdip),
+			    ddi_get_instance(cdip)));
+			rval = 1;
+		} else
+			rval = 0;
+	} else {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "%s%d: vhci_pgr_validate_and_register: key"
+		    " validation failed, key backed out\n",
+		    ddi_driver_name(cdip), ddi_get_instance(cdip)));
+		vhci_print_prout_keys(vlun, "v_pgr_val_reg: key backed out: ");
+	}
+	mdi_rele_path(pip);
+
+	return (rval);
+}
+
+/*
+ * taskq routine to dispatch a scsi cmd to vhci_scsi_start.  This ensures
+ * that vhci_scsi_start is not called in interrupt context.
+ * As the upper layer gets TRAN_ACCEPT when the command is dispatched, we
+ * need to complete the command if something goes wrong.
+ */
+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;
+
+	VHCI_DEBUG(6, (CE_NOTE, NULL, "!vhci_dispatch_scsi_start: sending"
+	    " scsi-2 reserve for 0x%p\n",
+	    (void *)ADDR2DIP(&(vpkt->vpkt_tgt_pkt->pkt_address))));
+
+	/*
+	 * To prevent the taskq from being called recursively we set the
+	 * the VHCI_PKT_THRU_TASKQ bit in the vhci_pkt_states.
+	 */
+	vpkt->vpkt_state |= VHCI_PKT_THRU_TASKQ;
+
+	/*
+	 * Wait for the transport to get ready to send packets
+	 * and if it times out, it will return something other than
+	 * TRAN_BUSY. The vhci_reserve_delay may want to
+	 * get tuned for other transports and is therefore a global.
+	 * Using delay since this routine is called by taskq dispatch
+	 * and not called during interrupt context.
+	 */
+	while ((rval = vhci_scsi_start(&(vpkt->vpkt_tgt_pkt->pkt_address),
+	    vpkt->vpkt_tgt_pkt)) == TRAN_BUSY) {
+		delay(drv_usectohz(vhci_reserve_delay));
+	}
+
+	switch (rval) {
+	case TRAN_ACCEPT:
+		return;
+
+	default:
+		/*
+		 * This pkt shall be retried, and to ensure another taskq
+		 * is dispatched for it, clear the VHCI_PKT_THRU_TASKQ
+		 * flag.
+		 */
+		vpkt->vpkt_state &= ~VHCI_PKT_THRU_TASKQ;
+
+		/* Ensure that the pkt is retried without a reset */
+		tpkt->pkt_reason = CMD_ABORTED;
+		tpkt->pkt_statistics |= STAT_ABORTED;
+		VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_dispatch_scsi_start: "
+		    "TRAN_rval %d returned for dip 0x%p", rval,
+		    (void *)ADDR2DIP(&(vpkt->vpkt_tgt_pkt->pkt_address))));
+		break;
+	}
+
+	/*
+	 * vpkt_org_vpkt should always be NULL here if the retry command
+	 * has been successfully dispatched.  If vpkt_org_vpkt != NULL at
+	 * this point, it is an error so restore the original vpkt and
+	 * return an error to the target driver so it can retry the
+	 * command as appropriate.
+	 */
+	if (vpkt->vpkt_org_vpkt != NULL) {
+		struct vhci_pkt		*new_vpkt = vpkt;
+		scsi_vhci_priv_t	*svp = (scsi_vhci_priv_t *)
+		    mdi_pi_get_vhci_private(vpkt->vpkt_path);
+
+		vpkt = vpkt->vpkt_org_vpkt;
+
+		vpkt->vpkt_tgt_pkt->pkt_reason = tpkt->pkt_reason;
+		vpkt->vpkt_tgt_pkt->pkt_statistics = tpkt->pkt_statistics;
+
+		vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
+		    new_vpkt->vpkt_tgt_pkt);
+
+		tpkt = vpkt->vpkt_tgt_pkt;
+	}
+
+	if (tpkt->pkt_comp) {
+		(*tpkt->pkt_comp)(tpkt);
+	}
+}
+
+static void
+vhci_initiate_auto_failback(void *arg)
+{
+	struct scsi_vhci_lun	*vlun = (struct scsi_vhci_lun *)arg;
+	dev_info_t		*vdip, *cdip;
+	int			held;
+
+	cdip = vlun->svl_dip;
+	vdip = ddi_get_parent(cdip);
+
+	VHCI_HOLD_LUN(vlun, VH_SLEEP, held);
+
+	/*
+	 * Perform a final check to see if the active path class is indeed
+	 * not the prefered path class.  As in the time the auto failback
+	 * was dispatched, an external failover could have been detected.
+	 * [Some other host could have detected this condition and triggered
+	 *  the auto failback before].
+	 * In such a case if we go ahead with failover we will be negating the
+	 * whole purpose of auto failback.
+	 */
+	mutex_enter(&vlun->svl_mutex);
+	if (vlun->svl_active_pclass != NULL) {
+		char				*best_pclass;
+		struct scsi_failover_ops	*fo;
+
+		fo = vlun->svl_fops;
+
+		(*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);
+			VHCI_RELEASE_LUN(vlun);
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "Not initiating "
+			    "auto failback for %s as %s pathclass already "
+			    "active.\n", vlun->svl_lun_wwn, best_pclass));
+			return;
+		}
+	}
+	mutex_exit(&vlun->svl_mutex);
+	if (mdi_failover(vdip, vlun->svl_dip, MDI_FAILOVER_SYNC)
+	    == MDI_SUCCESS) {
+		vhci_log(CE_NOTE, vdip, "!Auto failback operation "
+		    "succeeded for device %s (GUID %s)",
+		    ddi_node_name(cdip), vlun->svl_lun_wwn);
+	} else {
+		vhci_log(CE_NOTE, vdip, "!Auto failback operation "
+		    "failed for device %s (GUID %s)",
+		    ddi_node_name(cdip), vlun->svl_lun_wwn);
+	}
+	VHCI_RELEASE_LUN(vlun);
+}
+
+#ifdef DEBUG
+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++;
+	}
+}
+#endif
+
+static void
+vhci_print_prout_keys(scsi_vhci_lun_t *vlun, char *msg)
+{
+	int			i;
+	vhci_prout_t		*prout;
+	char			buf1[4*MHIOC_RESV_KEY_SIZE + 1];
+	char			buf2[4*MHIOC_RESV_KEY_SIZE + 1];
+	char			buf3[4*MHIOC_RESV_KEY_SIZE + 1];
+	char			buf4[4*MHIOC_RESV_KEY_SIZE + 1];
+
+	prout = &vlun->svl_prout;
+
+	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
+		(void) sprintf(&buf1[4*i], "[%02x]", prout->res_key[i]);
+	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
+		(void) sprintf(&buf2[(4*i)], "[%02x]", prout->service_key[i]);
+	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
+		(void) sprintf(&buf3[4*i], "[%02x]", prout->active_res_key[i]);
+	for (i = 0; i < MHIOC_RESV_KEY_SIZE; i++)
+		(void) sprintf(&buf4[4*i], "[%02x]",
+		    prout->active_service_key[i]);
+
+	/* Printing all in one go. Otherwise it will jumble up */
+	VHCI_DEBUG(5, (CE_CONT, NULL, "%s vlun 0x%p, thread 0x%p\n"
+	    "res_key:          : %s\n"
+	    "service_key       : %s\n"
+	    "active_res_key    : %s\n"
+	    "active_service_key: %s\n",
+	    msg, (void *)vlun, (void *)curthread, buf1, buf2, buf3, buf4));
+}
+
+/*
+ * Called from vhci_scsi_start to update the pHCI pkt with target packet.
+ */
+static void
+vhci_update_pHCI_pkt(struct vhci_pkt *vpkt, struct scsi_pkt *pkt)
+{
+
+	ASSERT(vpkt->vpkt_hba_pkt);
+
+	vpkt->vpkt_hba_pkt->pkt_flags = pkt->pkt_flags;
+	vpkt->vpkt_hba_pkt->pkt_flags |= FLAG_NOQUEUE;
+
+	if ((vpkt->vpkt_hba_pkt->pkt_flags & FLAG_NOINTR) ||
+	    MDI_PI_IS_SUSPENDED(vpkt->vpkt_path)) {
+		/*
+		 * Polled Command is requested or HBA is in
+		 * suspended state
+		 */
+		vpkt->vpkt_hba_pkt->pkt_flags |= FLAG_NOINTR;
+		vpkt->vpkt_hba_pkt->pkt_comp = NULL;
+	} else {
+		vpkt->vpkt_hba_pkt->pkt_comp = vhci_intr;
+	}
+	vpkt->vpkt_hba_pkt->pkt_time = pkt->pkt_time;
+	bcopy(pkt->pkt_cdbp, vpkt->vpkt_hba_pkt->pkt_cdbp,
+	    vpkt->vpkt_tgt_init_cdblen);
+	vpkt->vpkt_hba_pkt->pkt_resid = pkt->pkt_resid;
+
+	/* Re-initialize the following pHCI packet state information */
+	vpkt->vpkt_hba_pkt->pkt_state = 0;
+	vpkt->vpkt_hba_pkt->pkt_statistics = 0;
+	vpkt->vpkt_hba_pkt->pkt_reason = 0;
+}
+
+static int
+vhci_scsi_bus_power(dev_info_t *parent, void *impl_arg, pm_bus_power_op_t op,
+    void *arg, void *result)
+{
+	int ret = DDI_SUCCESS;
+
+	/*
+	 * Generic processing in MPxIO framework
+	 */
+	ret = mdi_bus_power(parent, impl_arg, op, arg, result);
+
+	switch (ret) {
+	case MDI_SUCCESS:
+		ret = DDI_SUCCESS;
+		break;
+	case MDI_FAILURE:
+		ret = DDI_FAILURE;
+		break;
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+static int
+vhci_pHCI_cap(struct scsi_address *ap, char *cap, int val, int whom,
+    mdi_pathinfo_t *pip)
+{
+	dev_info_t		*cdip;
+	mdi_pathinfo_t		*npip = NULL;
+	scsi_vhci_priv_t	*svp = NULL;
+	struct scsi_address	*pap = NULL;
+	scsi_hba_tran_t		*hba = NULL;
+	int			sps;
+	int			mps_flag;
+	int			rval = 0;
+
+	mps_flag = (MDI_SELECT_ONLINE_PATH | MDI_SELECT_STANDBY_PATH);
+	if (pip) {
+		/*
+		 * If the call is from vhci_pathinfo_state_change,
+		 * then this path was busy and is becoming ready to accept IO.
+		 */
+		ASSERT(ap != NULL);
+		hba = ap->a_hba_tran;
+		ASSERT(hba != NULL);
+		rval = scsi_ifsetcap(ap, cap, val, whom);
+
+		VHCI_DEBUG(2, (CE_NOTE, NULL,
+		    "!vhci_pHCI_cap: only on path %p, ap %p, rval %x\n",
+		    (void *)pip, (void *)ap, rval));
+
+		return (rval);
+	}
+
+	/*
+	 * Set capability on all the pHCIs.
+	 * If any path is busy, then the capability would be set by
+	 * vhci_pathinfo_state_change.
+	 */
+
+	cdip = ADDR2DIP(ap);
+	ASSERT(cdip != NULL);
+	sps = mdi_select_path(cdip, NULL, mps_flag, NULL, &pip);
+	if ((sps != MDI_SUCCESS) || (pip == NULL)) {
+		VHCI_DEBUG(2, (CE_WARN, NULL,
+		    "!vhci_pHCI_cap: Unable to get a path, dip 0x%p",
+		    (void *)cdip));
+		return (0);
+	}
+
+again:
+	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
+	if (svp == NULL) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_pHCI_cap: "
+		    "priv is NULL, pip 0x%p", (void *)pip));
+		mdi_rele_path(pip);
+		return (rval);
+	}
+
+	if (svp->svp_psd == NULL) {
+		VHCI_DEBUG(2, (CE_WARN, NULL, "!vhci_pHCI_cap: "
+		    "psd is NULL, pip 0x%p, svp 0x%p",
+		    (void *)pip, (void *)svp));
+		mdi_rele_path(pip);
+		return (rval);
+	}
+
+	pap = &svp->svp_psd->sd_address;
+	ASSERT(pap != NULL);
+	hba = pap->a_hba_tran;
+	ASSERT(hba != NULL);
+
+	if (hba->tran_setcap != NULL) {
+		rval = scsi_ifsetcap(pap, cap, val, whom);
+
+		VHCI_DEBUG(2, (CE_NOTE, NULL,
+		    "!vhci_pHCI_cap: path %p, ap %p, rval %x\n",
+		    (void *)pip, (void *)ap, rval));
+
+		/*
+		 * Select next path and issue the setcap, repeat
+		 * until all paths are exhausted
+		 */
+		sps = mdi_select_path(cdip, NULL, mps_flag, pip, &npip);
+		if ((sps != MDI_SUCCESS) || (npip == NULL)) {
+			mdi_rele_path(pip);
+			return (1);
+		}
+		mdi_rele_path(pip);
+		pip = npip;
+		goto again;
+	}
+	mdi_rele_path(pip);
+	return (rval);
+}
+
+static int
+vhci_scsi_bus_config(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op,
+    void *arg, dev_info_t **child)
+{
+	char *guid;
+
+	if (op == BUS_CONFIG_ONE || op == BUS_UNCONFIG_ONE)
+		guid = vhci_devnm_to_guid((char *)arg);
+	else
+		guid = NULL;
+
+	if (mdi_vhci_bus_config(pdip, flags, op, arg, child, guid)
+	    == MDI_SUCCESS)
+		return (NDI_SUCCESS);
+	else
+		return (NDI_FAILURE);
+}
+
+/*
+ * Take the original vhci_pkt, create a duplicate of the pkt for resending
+ * as though it originated in ssd.
+ */
+static struct scsi_pkt *
+vhci_create_retry_pkt(struct vhci_pkt *vpkt)
+{
+	struct vhci_pkt *new_vpkt = NULL;
+	struct scsi_pkt	*pkt = NULL;
+
+	scsi_vhci_priv_t *svp = (scsi_vhci_priv_t *)
+	    mdi_pi_get_vhci_private(vpkt->vpkt_path);
+
+	/*
+	 * Ensure consistent data at completion time by setting PKT_CONSISTENT
+	 */
+	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, vpkt->vpkt_tgt_init_privlen,
+	    PKT_CONSISTENT,
+	    NULL_FUNC, NULL);
+	if (pkt != NULL) {
+		new_vpkt = TGTPKT2VHCIPKT(pkt);
+
+		pkt->pkt_address = vpkt->vpkt_tgt_pkt->pkt_address;
+		pkt->pkt_flags = vpkt->vpkt_tgt_pkt->pkt_flags;
+		pkt->pkt_time = vpkt->vpkt_tgt_pkt->pkt_time;
+		pkt->pkt_comp = vpkt->vpkt_tgt_pkt->pkt_comp;
+
+		pkt->pkt_resid = 0;
+		pkt->pkt_statistics = 0;
+		pkt->pkt_reason = 0;
+
+		bcopy(vpkt->vpkt_tgt_pkt->pkt_cdbp,
+		    pkt->pkt_cdbp, vpkt->vpkt_tgt_init_cdblen);
+
+		/*
+		 * Save a pointer to the original vhci_pkt
+		 */
+		new_vpkt->vpkt_org_vpkt = vpkt;
+	}
+
+	return (pkt);
+}
+
+/*
+ * Copy the successful completion information from the hba packet into
+ * the original target pkt from the upper layer.  Returns the original
+ * vpkt and destroys the new vpkt from the internal retry.
+ */
+static struct vhci_pkt *
+vhci_sync_retry_pkt(struct vhci_pkt *vpkt)
+{
+	struct vhci_pkt		*ret_vpkt = NULL;
+	struct scsi_pkt		*tpkt = NULL;
+	struct scsi_pkt		*hba_pkt = NULL;
+	scsi_vhci_priv_t	*svp = (scsi_vhci_priv_t *)
+	    mdi_pi_get_vhci_private(vpkt->vpkt_path);
+
+	ASSERT(vpkt->vpkt_org_vpkt != NULL);
+	VHCI_DEBUG(0, (CE_NOTE, NULL, "vhci_sync_retry_pkt: Retry pkt "
+	    "completed successfully!\n"));
+
+	ret_vpkt = vpkt->vpkt_org_vpkt;
+	tpkt = ret_vpkt->vpkt_tgt_pkt;
+	hba_pkt = vpkt->vpkt_hba_pkt;
+
+	/*
+	 * Copy the good status into the target driver's packet
+	 */
+	*(tpkt->pkt_scbp) = *(hba_pkt->pkt_scbp);
+	tpkt->pkt_resid = hba_pkt->pkt_resid;
+	tpkt->pkt_state = hba_pkt->pkt_state;
+	tpkt->pkt_statistics = hba_pkt->pkt_statistics;
+	tpkt->pkt_reason = hba_pkt->pkt_reason;
+
+	/*
+	 * Destroy the internally created vpkt for the retry
+	 */
+	vhci_scsi_destroy_pkt(&svp->svp_psd->sd_address,
+	    vpkt->vpkt_tgt_pkt);
+
+	return (ret_vpkt);
+}
+
+/* restart the request sense request */
+static void
+vhci_uscsi_restart_sense(void *arg)
+{
+	struct buf 	*rqbp;
+	struct buf 	*bp;
+	struct scsi_pkt *rqpkt = (struct scsi_pkt *)arg;
+	mp_uscsi_cmd_t 	*mp_uscmdp;
+
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_uscsi_restart_sense: enter: rqpkt: %p", (void *)rqpkt));
+
+	if (scsi_transport(rqpkt) != TRAN_ACCEPT) {
+		/* if it fails - need to wakeup the original command */
+		mp_uscmdp = rqpkt->pkt_private;
+		bp = mp_uscmdp->cmdbp;
+		rqbp = mp_uscmdp->rqbp;
+		ASSERT(mp_uscmdp && bp && rqbp);
+		scsi_free_consistent_buf(rqbp);
+		scsi_destroy_pkt(rqpkt);
+		bp->b_resid = bp->b_bcount;
+		bioerror(bp, EIO);
+		biodone(bp);
+	}
+}
+
+/*
+ * auto-rqsense is not enabled so we have to retrieve the request sense
+ * manually.
+ */
+static int
+vhci_uscsi_send_sense(struct scsi_pkt *pkt, mp_uscsi_cmd_t *mp_uscmdp)
+{
+	struct buf 		*rqbp, *cmdbp;
+	struct scsi_pkt 	*rqpkt;
+	int			rval = 0;
+
+	cmdbp = mp_uscmdp->cmdbp;
+	ASSERT(cmdbp != NULL);
+
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_uscsi_send_sense: enter: bp: %p pkt: %p scmd: %p",
+	    (void *)cmdbp, (void *)pkt, (void *)mp_uscmdp));
+	/* set up the packet information and cdb */
+	if ((rqbp = scsi_alloc_consistent_buf(mp_uscmdp->ap, NULL,
+	    SENSE_LENGTH, B_READ, NULL, NULL)) == NULL) {
+		return (-1);
+	}
+
+	if ((rqpkt = scsi_init_pkt(mp_uscmdp->ap, NULL, rqbp,
+	    CDB_GROUP0, 1, 0, PKT_CONSISTENT, NULL, NULL)) == NULL) {
+		scsi_free_consistent_buf(rqbp);
+		return (-1);
+	}
+
+	(void) scsi_setup_cdb((union scsi_cdb *)(intptr_t)rqpkt->pkt_cdbp,
+	    SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
+
+	mp_uscmdp->rqbp = rqbp;
+	rqbp->b_private = mp_uscmdp;
+	rqpkt->pkt_flags |= FLAG_SENSING;
+	rqpkt->pkt_time = 60;
+	rqpkt->pkt_comp = vhci_uscsi_iodone;
+	rqpkt->pkt_private = mp_uscmdp;
+
+	/* get her done */
+	switch (scsi_transport(rqpkt)) {
+	case TRAN_ACCEPT:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
+		    "transport accepted."));
+		break;
+	case TRAN_BUSY:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
+		    "transport busy, setting timeout."));
+		vhci_restart_timeid = timeout(vhci_uscsi_restart_sense, rqpkt,
+		    (drv_usectohz(5 * 1000000)));
+		break;
+	default:
+		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_uscsi_send_sense: "
+		    "transport failed"));
+		scsi_free_consistent_buf(rqbp);
+		scsi_destroy_pkt(rqpkt);
+		rval = -1;
+	}
+
+	return (rval);
+}
+
+/*
+ * done routine for the mpapi uscsi command - this is behaving as though
+ * FLAG_DIAGNOSE is set meaning there are no retries except for a manual
+ * request sense.
+ */
+void
+vhci_uscsi_iodone(struct scsi_pkt *pkt)
+{
+	struct buf 			*bp;
+	mp_uscsi_cmd_t 			*mp_uscmdp;
+	struct uscsi_cmd 		*uscmdp;
+	struct scsi_arq_status 		*arqstat;
+	int 				err;
+
+	mp_uscmdp = (mp_uscsi_cmd_t *)pkt->pkt_private;
+	uscmdp = mp_uscmdp->uscmdp;
+	bp = mp_uscmdp->cmdbp;
+	ASSERT(bp != NULL);
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_uscsi_iodone: enter: bp: %p pkt: %p scmd: %p",
+	    (void *)bp, (void *)pkt, (void *)mp_uscmdp));
+	/* Save the status and the residual into the uscsi_cmd struct */
+	uscmdp->uscsi_status = ((*(pkt)->pkt_scbp) & STATUS_MASK);
+	uscmdp->uscsi_resid = bp->b_resid;
+
+	/* return on a very successful command */
+	if (pkt->pkt_reason == CMD_CMPLT &&
+	    SCBP_C(pkt) == 0 && ((pkt->pkt_flags & FLAG_SENSING) == 0) &&
+	    pkt->pkt_resid == 0) {
+		mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
+		scsi_destroy_pkt(pkt);
+		biodone(bp);
+		return;
+	}
+	VHCI_DEBUG(4, (CE_NOTE, NULL, "iodone: reason=0x%x "
+	    " pkt_resid=%ld pkt_state: 0x%x b_count: %ld b_resid: %ld",
+	    pkt->pkt_reason, pkt->pkt_resid,
+	    pkt->pkt_state, bp->b_bcount, bp->b_resid));
+
+	err = EIO;
+
+	arqstat = (struct scsi_arq_status *)(intptr_t)(pkt->pkt_scbp);
+	if (pkt->pkt_reason != CMD_CMPLT) {
+		/*
+		 * The command did not complete.
+		 */
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iodone: command did not complete."
+		    " reason: %x flag: %x", pkt->pkt_reason, pkt->pkt_flags));
+		if (pkt->pkt_flags & FLAG_SENSING) {
+			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
+		} else if (pkt->pkt_reason == CMD_TIMEOUT) {
+			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_HARDERR);
+			err = ETIMEDOUT;
+		}
+	} else if (pkt->pkt_state & STATE_ARQ_DONE && mp_uscmdp->arq_enabled) {
+		/*
+		 * The auto-rqsense happened, and the packet has a filled-in
+		 * scsi_arq_status structure, pointed to by pkt_scbp.
+		 */
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iodone: received auto-requested sense"));
+		if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
+			/* get the amount of data to copy into rqbuf */
+			int rqlen = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
+			rqlen = min(((int)uscmdp->uscsi_rqlen), rqlen);
+			uscmdp->uscsi_rqresid = uscmdp->uscsi_rqlen - rqlen;
+			uscmdp->uscsi_rqstatus =
+			    *((char *)&arqstat->sts_rqpkt_status);
+			if (uscmdp->uscsi_rqbuf && uscmdp->uscsi_rqlen &&
+			    rqlen != 0) {
+				bcopy(&(arqstat->sts_sensedata),
+				    uscmdp->uscsi_rqbuf, rqlen);
+			}
+			mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_uscsi_iodone: ARQ "
+			    "uscsi_rqstatus=0x%x uscsi_rqresid=%d rqlen: %d "
+			    "xfer: %d rqpkt_resid: %d\n",
+			    uscmdp->uscsi_rqstatus, uscmdp->uscsi_rqresid,
+			    uscmdp->uscsi_rqlen, rqlen,
+			    arqstat->sts_rqpkt_resid));
+		}
+	} else if (pkt->pkt_flags & FLAG_SENSING) {
+		struct buf *rqbp;
+		struct scsi_status *rqstatus;
+
+		rqstatus = (struct scsi_status *)pkt->pkt_scbp;
+		/* a manual request sense was done - get the information */
+		if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
+			int rqlen = SENSE_LENGTH - pkt->pkt_resid;
+
+			rqbp = mp_uscmdp->rqbp;
+			/* get the amount of data to copy into rqbuf */
+			rqlen = min(((int)uscmdp->uscsi_rqlen), rqlen);
+			uscmdp->uscsi_rqresid = uscmdp->uscsi_rqlen - rqlen;
+			uscmdp->uscsi_rqstatus = *((char *)rqstatus);
+			if (uscmdp->uscsi_rqlen && uscmdp->uscsi_rqbuf) {
+				bcopy(rqbp->b_un.b_addr, uscmdp->uscsi_rqbuf,
+				    rqlen);
+			}
+			MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
+			scsi_free_consistent_buf(rqbp);
+		}
+		VHCI_DEBUG(4, (CE_NOTE, NULL, "vhci_uscsi_iodone: FLAG_SENSING"
+		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
+		    uscmdp->uscsi_rqstatus, uscmdp->uscsi_rqresid));
+	} else {
+		struct scsi_status *status =
+		    (struct scsi_status *)pkt->pkt_scbp;
+		/*
+		 * Command completed and we're not getting sense. Check for
+		 * errors and decide what to do next.
+		 */
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iodone: command appears complete: reason: %x",
+		    pkt->pkt_reason));
+		if (status->sts_chk) {
+			/* need to manually get the request sense */
+			if (vhci_uscsi_send_sense(pkt, mp_uscmdp) == 0) {
+				scsi_destroy_pkt(pkt);
+				return;
+			}
+		} else {
+			VHCI_DEBUG(4, (CE_NOTE, NULL,
+			    "vhci_chk_err: appears complete"));
+			err = 0;
+			mdi_pi_kstat_iosupdate(mp_uscmdp->pip, bp);
+			if (pkt->pkt_resid) {
+				bp->b_resid += pkt->pkt_resid;
+			}
+		}
+	}
+
+	if (err) {
+		if (bp->b_resid == 0)
+			bp->b_resid = bp->b_bcount;
+		bioerror(bp, err);
+		bp->b_flags |= B_ERROR;
+	}
+
+	scsi_destroy_pkt(pkt);
+	biodone(bp);
+
+	VHCI_DEBUG(4, (CE_WARN, NULL, "vhci_uscsi_iodone: exit"));
+}
+
+/*
+ * start routine for the mpapi uscsi command
+ */
+int
+vhci_uscsi_iostart(struct buf *bp)
+{
+	struct scsi_pkt 	*pkt;
+	struct uscsi_cmd	*uscmdp;
+	mp_uscsi_cmd_t 		*mp_uscmdp;
+	int			stat_size, rval;
+	int			retry = 0;
+
+	ASSERT(bp->b_private != NULL);
+
+	mp_uscmdp = (mp_uscsi_cmd_t *)bp->b_private;
+	uscmdp = mp_uscmdp->uscmdp;
+	if (uscmdp->uscsi_flags & USCSI_RQENABLE) {
+		stat_size = SENSE_LENGTH;
+	} else {
+		stat_size = 1;
+	}
+
+	pkt = scsi_init_pkt(mp_uscmdp->ap, NULL, bp, uscmdp->uscsi_cdblen,
+	    stat_size, 0, 0, SLEEP_FUNC, NULL);
+	if (pkt == NULL) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iostart: rval: EINVAL"));
+		bp->b_resid = bp->b_bcount;
+		uscmdp->uscsi_resid = bp->b_bcount;
+		bioerror(bp, EINVAL);
+		biodone(bp);
+		return (EINVAL);
+	}
+
+	pkt->pkt_time = uscmdp->uscsi_timeout;
+	bcopy(uscmdp->uscsi_cdb, pkt->pkt_cdbp, (size_t)uscmdp->uscsi_cdblen);
+	pkt->pkt_comp = vhci_uscsi_iodone;
+	pkt->pkt_private = mp_uscmdp;
+	if (uscmdp->uscsi_flags & USCSI_SILENT)
+		pkt->pkt_flags |= FLAG_SILENT;
+	if (uscmdp->uscsi_flags & USCSI_ISOLATE)
+		pkt->pkt_flags |= FLAG_ISOLATE;
+	if (uscmdp->uscsi_flags & USCSI_DIAGNOSE)
+		pkt->pkt_flags |= FLAG_DIAGNOSE;
+	if (uscmdp->uscsi_flags & USCSI_RENEGOT) {
+		pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
+	}
+	VHCI_DEBUG(4, (CE_WARN, NULL,
+	    "vhci_uscsi_iostart: ap: %p pkt: %p pcdbp: %p uscmdp: %p"
+	    " ucdbp: %p pcdblen: %d bp: %p count: %ld pip: %p"
+	    " stat_size: %d",
+	    (void *)mp_uscmdp->ap, (void *)pkt, (void *)pkt->pkt_cdbp,
+	    (void *)uscmdp, (void *)uscmdp->uscsi_cdb, pkt->pkt_cdblen,
+	    (void *)bp, bp->b_bcount, (void *)mp_uscmdp->pip, stat_size));
+
+	while (((rval = scsi_transport(pkt)) == TRAN_BUSY) &&
+	    retry < vhci_uscsi_retry_count) {
+		delay(drv_usectohz(vhci_uscsi_delay));
+		retry++;
+	}
+	if (retry >= vhci_uscsi_retry_count) {
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iostart: tran_busy - retry: %d", retry));
+	}
+	switch (rval) {
+	case TRAN_ACCEPT:
+		rval =  0;
+		break;
+
+	default:
+		VHCI_DEBUG(4, (CE_NOTE, NULL,
+		    "vhci_uscsi_iostart: rval: %d count: %ld res: %ld",
+		    rval, bp->b_bcount, bp->b_resid));
+		bp->b_resid = bp->b_bcount;
+		uscmdp->uscsi_resid = bp->b_bcount;
+		bioerror(bp, EIO);
+		scsi_destroy_pkt(pkt);
+		biodone(bp);
+		rval = EIO;
+		MDI_PI_ERRSTAT(mp_uscmdp->pip, MDI_PI_TRANSERR);
+		break;
+	}
+	VHCI_DEBUG(4, (CE_NOTE, NULL,
+	    "vhci_uscsi_iostart: exit: rval: %d", rval));
+	return (rval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.conf	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,92 @@
+# 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 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+name="scsi_vhci" class="root";
+
+#
+# Load balancing global configuration: setting load-balance="none" will cause
+# all I/O to a given device (which supports multipath I/O) to occur via one
+# path.  Setting load-balance="round-robin" will cause each path to the device
+# to be used in turn.
+#
+load-balance="round-robin";
+
+#
+# Automatic failback configuration
+# possible values are auto-failback="enable" or auto-failback="disable"
+auto-failback="enable";
+
+#BEGIN: FAILOVER_MODULE_BLOCK (DO NOT MOVE OR DELETE)
+#
+# Declare scsi_vhci failover module paths with 'ddi-forceload' so that
+# they get loaded early enough to be available for scsi_vhci root use.
+#
+# NOTE: Correct operation depends on the value of 'ddi-forceload', this
+# value should not be changed. The ordering of entries is from
+# most-specific failover modules (with a "probe" implementation that is
+# completely VID/PID table based), to most generic (failover modules that
+# are based on T10 standards like TPGS). By convention the last part of a
+# failover module path, after "/scsi_vhci_", is called the
+# "failover-module-name", which begins with "f_" (like "f_asym_sun"). The
+# "failover-module-name" is also used in the override mechanism below.
+ddi-forceload =
+	"misc/scsi_vhci/scsi_vhci_f_asym_sun",
+	"misc/scsi_vhci/scsi_vhci_f_asym_lsi",
+	"misc/scsi_vhci/scsi_vhci_f_asym_emc",
+	"misc/scsi_vhci/scsi_vhci_f_sym_emc",
+	"misc/scsi_vhci/scsi_vhci_f_sym",
+	"misc/scsi_vhci/scsi_vhci_f_tpgs";
+
+#
+# For a device that has a GUID, discovered on a pHCI with mpxio enabled, vHCI
+# access also depends on one of the scsi_vhci failover modules accepting the
+# device.  The default way this occurs is by a failover module's "probe"
+# implementation (sfo_device_probe) indicating the device is supported under
+# scsi_vhci.  To override this default probe-oriented configuration in
+# order to
+#
+#    1)	establish support for a device not currently accepted under scsi_vhci
+#
+# or 2)	override the module selected by "probe"
+#
+# or 3)	disable scsi_vhci support for a device
+#
+# you can add a 'scsi-vhci-failover-override' tuple, as documented in
+# scsi_get_device_type_string(9F). For each tuple, the first part provides
+# basic device identity information (vid/pid) and the second part selects
+# the failover module by "failover-module-name". If you want to disable
+# scsi_vhci support for a device, use the special failover-module-name "NONE".
+# Currently, for each failover-module-name in 'scsi-vhci-failover-override'
+# (except "NONE") there needs to be a
+# "misc/scsi_vhci/scsi_vhci_<failover-module-name>" in 'ddi-forceload' above.
+#
+#	"                  111111"
+#	"012345670123456789012345",	"failover-module-name" or "NONE"
+#	"|-VID--||-----PID------|",
+# scsi-vhci-failover-override =
+#	"STK     FLEXLINE 400",		"f_asym_lsi",
+#	"SUN     T4",			"f_tpgs",
+#	"CME     XIRTEMMYS",		"NONE";
+#
+#END: FAILOVER_MODULE_BLOCK (DO NOT MOVE OR DELETE)
--- a/usr/src/uts/common/io/scsi/impl/scsi_subr.c	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/io/scsi/impl/scsi_subr.c	Fri Aug 10 17:00:37 2007 -0700
@@ -115,7 +115,7 @@
 	 * every second as this status takes a while to change.
 	 */
 	for (busy_count = 0; busy_count < (pkt->pkt_time * SEC_TO_CSEC);
-		busy_count++) {
+	    busy_count++) {
 		int rc;
 		int poll_delay;
 
@@ -310,9 +310,8 @@
 		local.b_flags = B_READ;
 	local.b_bcount = datalen;
 	pkt = (*tran->tran_init_pkt) (ap, NULL, &local,
-		cdblen, statuslen, 0, PKT_CONSISTENT,
-		(func == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC,
-		NULL);
+	    cdblen, statuslen, 0, PKT_CONSISTENT,
+	    (func == SLEEP_FUNC) ? SLEEP_FUNC : NULL_FUNC, NULL);
 	if (!pkt) {
 		i_ddi_mem_free(local.b_un.b_addr, NULL);
 		if (func != NULL_FUNC) {
@@ -1181,21 +1180,22 @@
 				(void) sprintf(&buf1[strlen(buf1)],
 				    "ASC: 0x%x (%s)", asc,
 				    scsi_asc_ascq_name(asc, ascq,
-					tmpbuf, asc_list));
+				    tmpbuf, asc_list));
 				buflen = strlen(buf1);
 				if (buflen < SCSI_ERRMSG_COLUMN_LEN) {
-				    pad[SCSI_ERRMSG_COLUMN_LEN - buflen] = '\0';
-				    (void) sprintf(&buf1[buflen],
-				    "%s ASCQ: 0x%x", pad, ascq);
+					pad[SCSI_ERRMSG_COLUMN_LEN - buflen] =
+					    '\0';
+					(void) sprintf(&buf1[buflen],
+					    "%s ASCQ: 0x%x", pad, ascq);
 				} else {
-				    (void) sprintf(&buf1[buflen], " ASCQ: 0x%x",
-					ascq);
+					(void) sprintf(&buf1[buflen],
+					    " ASCQ: 0x%x", ascq);
 				}
 				impl_scsi_log(dev,
-					label, CE_CONT, "%s\n", buf1);
-				impl_scsi_log(dev, label, CE_CONT,
-					"FRU: 0x%x (%s)\n",
-						fru_code, buf);
+				    label, CE_CONT, "%s\n", buf1);
+				impl_scsi_log(dev,
+				    label, CE_CONT, "FRU: 0x%x (%s)\n",
+				    fru_code, buf);
 				mutex_exit(&scsi_log_mutex);
 				return;
 			}
@@ -1215,7 +1215,7 @@
     struct scsi_key_strings *cmdlist, struct scsi_extended_sense *sensep)
 {
 	scsi_vu_errmsg(devp, pkt, label, severity, blkno,
-		err_blkno, cmdlist, sensep, NULL, NULL);
+	    err_blkno, cmdlist, sensep, NULL, NULL);
 }
 
 /*PRINTFLIKE4*/
@@ -1265,8 +1265,8 @@
 		if (level == CE_PANIC || level == CE_WARN ||
 		    level == CE_NOTE) {
 			(void) sprintf(name, "%s (%s%d):\n",
-				ddi_pathname(dev, scsi_log_buffer),
-				label, ddi_get_instance(dev));
+			    ddi_pathname(dev, scsi_log_buffer),
+			    label, ddi_get_instance(dev));
 		} else if (level >= (uint_t)SCSI_DEBUG) {
 			(void) sprintf(name,
 			    "%s%d:", label, ddi_get_instance(dev));
@@ -1299,83 +1299,120 @@
 	case CE_WARN:
 	case CE_PANIC:
 		if (boot_only) {
-			cmn_err(level, "?%s\t%s", name,
-				&scsi_log_buffer[1]);
+			cmn_err(level, "?%s\t%s", name, &scsi_log_buffer[1]);
 		} else if (console_only) {
-			cmn_err(level, "^%s\t%s", name,
-				&scsi_log_buffer[1]);
+			cmn_err(level, "^%s\t%s", name, &scsi_log_buffer[1]);
 		} else if (log_only) {
-			cmn_err(level, "!%s\t%s", name,
-				&scsi_log_buffer[1]);
+			cmn_err(level, "!%s\t%s", name, &scsi_log_buffer[1]);
 		} else {
-			cmn_err(level, "%s\t%s", name,
-				scsi_log_buffer);
+			cmn_err(level, "%s\t%s", name, scsi_log_buffer);
 		}
 		break;
 	case (uint_t)SCSI_DEBUG:
 	default:
-		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name,
-				scsi_log_buffer);
+		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name, scsi_log_buffer);
 		break;
 	}
 }
 
-int
-scsi_get_device_type_scsi_options(dev_info_t *dip,
-    struct scsi_device *devp, int default_scsi_options)
+/*
+ * Lookup the 'prop_name' string array property and walk thru its list of
+ * tuple values looking for a tuple who's VID/PID string (first part of tuple)
+ * matches the inquiry VID/PID information for the scsi_device.  On a match,
+ * return a duplicate of the second part of the tuple.  If no match is found,
+ * return NULL. On non-NULL return, caller is responsible for freeing return
+ * result via:
+ *	kmem_free(string, strlen(string) + 1);
+ *
+ * This interface can either be used directly, or indirectly by
+ * scsi_get_device_type_scsi_options.
+ */
+char	*
+scsi_get_device_type_string(char *prop_name,
+    dev_info_t *dip, struct scsi_device *devp)
 {
+	struct scsi_inquiry	*inq = devp->sd_inq;
+	char			**tuples;
+	uint_t			ntuples;
+	int			i;
+	char			*tvp;		/* tuple vid/pid */
+	char			*trs;		/* tuple return string */
+	int			tvp_len;
 
-	caddr_t config_list	= NULL;
-	int options		= default_scsi_options;
-	struct scsi_inquiry  *inq = devp->sd_inq;
-	caddr_t vidptr, datanameptr;
-	int	vidlen, dupletlen;
-	int config_list_len, len;
+	/* if we have no inquiry data then we can't do this */
+	if (inq == NULL)
+		return (NULL);
 
 	/*
-	 * look up the device-type-scsi-options-list and walk thru
-	 * the list
-	 * compare the vendor ids of the earlier inquiry command and
-	 * with those vids in the list
-	 * if there is a match, lookup the scsi-options value
+	 * So that we can establish a 'prop_name' for all instances of a
+	 * device in the system in a single place if needed (via options.conf),
+	 * we loop going up to the root ourself. This way root lookup does
+	 * *not* specify DDI_PROP_DONTPASS, and the code will look on the
+	 * options node.
 	 */
-	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
-	    "device-type-scsi-options-list",
-	    (caddr_t)&config_list, &config_list_len) == DDI_PROP_SUCCESS) {
+	do {
+		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
+		    (ddi_get_parent(dip) ? DDI_PROP_DONTPASS : 0) |
+		    DDI_PROP_NOTPROM, prop_name, &tuples, &ntuples) ==
+		    DDI_PROP_SUCCESS) {
+
+			/* loop over tuples */
+			for (i = 0;  i < (ntuples/2); i++) {
+				/* split into vid/pid and return-string */
+				tvp = tuples[i * 2];
+				trs = tuples[(i * 2) + 1];
+				tvp_len = strlen(tvp);
 
-		/*
-		 * Compare vids in each duplet - if it matches, get value for
-		 * dataname and then lookup scsi_options
-		 * dupletlen is calculated later.
-		 */
-		for (len = config_list_len, vidptr = config_list; len > 0;
-		    vidptr += dupletlen, len -= dupletlen) {
+				/* check for vid/pid match */
+				if ((tvp_len == 0) ||
+				    bcmp(tvp, inq->inq_vid, tvp_len))
+					continue;	/* no match */
+
+				/* match, dup return-string */
+				trs = i_ddi_strdup(trs, KM_SLEEP);
+				ddi_prop_free(tuples);
+				return (trs);
+			}
+			ddi_prop_free(tuples);
+		}
 
-			vidlen = strlen(vidptr);
-			datanameptr = vidptr + vidlen + 1;
+		/* climb up to root one step at a time */
+		dip = ddi_get_parent(dip);
+	} while (dip);
+
+	return (NULL);
+}
 
-			if ((vidlen != 0) &&
-			    bcmp(inq->inq_vid, vidptr, vidlen) == 0) {
-				/*
-				 * get the data list
-				 */
-				options = ddi_prop_get_int(DDI_DEV_T_ANY,
-				    dip, 0,
-				    datanameptr, default_scsi_options);
-				break;
-			}
-			dupletlen = vidlen + strlen(datanameptr) + 2;
-		}
-		kmem_free(config_list, config_list_len);
+/*
+ * The 'device-type-scsi-options' mechanism can be used to establish a device
+ * specific scsi_options value for a particular device. This mechanism uses
+ * paired strings ("vendor_info", "options_property_name") from the string
+ * array "device-type-scsi-options" definition. A bcmp of the vendor info is
+ * done against the inquiry data (inq_vid). Here is an example of use:
+ *
+ * device-type-scsi-options-list =
+ *	"FOOLCO  Special x1000", "foolco-scsi-options",
+ *	"FOOLCO  Special y1000", "foolco-scsi-options";
+ * foolco-scsi-options = 0xXXXXXXXX;
+ */
+int
+scsi_get_device_type_scsi_options(dev_info_t *dip,
+    struct scsi_device *devp, int options)
+{
+	char	*string;
+
+	if ((string = scsi_get_device_type_string(
+	    "device-type-scsi-options-list", dip, devp)) != NULL) {
+		options = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
+		    string, options);
+		kmem_free(string, strlen(string) + 1);
 	}
-
 	return (options);
 }
 
 /*
  * Functions for format-neutral sense data functions
  */
-
 int
 scsi_validate_sense(uint8_t *sense_buffer, int sense_buf_len, int *flags)
 {
@@ -1396,9 +1433,9 @@
 	 */
 	if ((es->es_class != CLASS_EXTENDED_SENSE) ||
 	    ((es->es_code != CODE_FMT_FIXED_CURRENT) &&
-		(es->es_code != CODE_FMT_FIXED_DEFERRED) &&
-		(es->es_code != CODE_FMT_DESCR_CURRENT) &&
-		(es->es_code != CODE_FMT_DESCR_DEFERRED))) {
+	    (es->es_code != CODE_FMT_FIXED_DEFERRED) &&
+	    (es->es_code != CODE_FMT_DESCR_CURRENT) &&
+	    (es->es_code != CODE_FMT_DESCR_DEFERRED))) {
 		/*
 		 * Sense data (if there's actually anything here) is not
 		 * in a format we can handle).
@@ -1411,7 +1448,7 @@
 	 */
 	if ((flags != NULL) &&
 	    ((es->es_code == CODE_FMT_FIXED_DEFERRED) ||
-		(es->es_code == CODE_FMT_DESCR_DEFERRED))) {
+	    (es->es_code == CODE_FMT_DESCR_DEFERRED))) {
 		*flags |= SNS_BUF_DEFERRED;
 	}
 
@@ -1427,7 +1464,7 @@
 		 */
 		if ((sense_buf_len < MIN_FIXED_SENSE_LEN) ||
 		    ((es->es_add_len + ADDL_SENSE_ADJUST) <
-			MIN_FIXED_SENSE_LEN)) {
+		    MIN_FIXED_SENSE_LEN)) {
 			result = SENSE_UNUSABLE;
 		} else {
 			/*
@@ -1436,7 +1473,7 @@
 			 */
 			if ((flags != NULL) &&
 			    (sense_buf_len <
-				(es->es_add_len + ADDL_SENSE_ADJUST))) {
+			    (es->es_add_len + ADDL_SENSE_ADJUST))) {
 				*flags |= SNS_BUF_OVERFLOW;
 			}
 
@@ -1458,7 +1495,7 @@
 			 */
 			if ((flags != NULL) &&
 			    (sense_buf_len <
-				(ds->ds_addl_sense_length + sizeof (*ds)))) {
+			    (ds->ds_addl_sense_length + sizeof (*ds)))) {
 				*flags |= SNS_BUF_OVERFLOW;
 			}
 
@@ -1686,7 +1723,7 @@
 
 		isd = (struct scsi_information_sense_descr *)
 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
-			DESCR_INFORMATION);
+		    DESCR_INFORMATION);
 
 		if (isd) {
 			*information = SCSI_READ64(isd->isd_information);
@@ -1728,7 +1765,7 @@
 
 		c = (struct scsi_cmd_specific_sense_descr *)
 		    scsi_find_sense_descr(sense_buffer, sense_buf_len,
-			DESCR_COMMAND_SPECIFIC);
+		    DESCR_COMMAND_SPECIFIC);
 
 		if (c) {
 			valid = 1;
--- a/usr/src/uts/common/os/modctl.c	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/os/modctl.c	Fri Aug 10 17:00:37 2007 -0700
@@ -807,7 +807,8 @@
 	ASSERT(constraints != NULL);
 	ASSERT(len > 0);
 
-	for (i = 0, p = constraints; strlen(p) > 0; i++, p += strlen(p) + 1);
+	for (i = 0, p = constraints; strlen(p) > 0; i++, p += strlen(p) + 1)
+		;
 
 	n = i;
 
@@ -1518,7 +1519,7 @@
 		break;
 	case MODEVENTS_POST_EVENT:
 		error = log_usr_sysevent((sysevent_t *)a2, (uint32_t)a3,
-			(sysevent_id_t *)a4);
+		    (sysevent_id_t *)a4);
 		break;
 	case MODEVENTS_REGISTER_EVENT:
 		error = log_sysevent_register((char *)a2, (char *)a3,
@@ -1568,7 +1569,7 @@
 	LOCK_DEV_OPS(&dnp->dn_lock);
 	if (strcmp(mp->mp_minorname, "*") == 0) {
 		wildmp = ((is_clone == 0) ?
-			&dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
+		    &dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
 		if (*wildmp)
 			freemp = *wildmp;
 		*wildmp = NULL;
@@ -1597,7 +1598,7 @@
 	} else {
 		if (moddebug & MODDEBUG_MINORPERM) {
 			cmn_err(CE_CONT, MP_NO_MINOR,
-				drvname, mp->mp_minorname);
+			    drvname, mp->mp_minorname);
 		}
 	}
 
@@ -1624,7 +1625,7 @@
 	LOCK_DEV_OPS(&dnp->dn_lock);
 	if (strcmp(mp->mp_minorname, "*") == 0) {
 		wildmp = ((is_clone == 0) ?
-			&dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
+		    &dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
 		if (*wildmp)
 			freemp = *wildmp;
 		*wildmp = mp;
@@ -1869,7 +1870,7 @@
 	for (pe = list_head(&wargs->wa_pathlist); pe != NULL;
 	    pe = list_next(&wargs->wa_pathlist, pe)) {
 		err = devfs_remdrv_cleanup((const char *)pe->pe_dir,
-			(const char *)pe->pe_nodename);
+		    (const char *)pe->pe_nodename);
 		if (rval == 0)
 			rval = err;
 	}
@@ -2199,12 +2200,12 @@
 
 	case MODGETDEVFSPATH_MI_LEN:	/* sizeof path nm of (major,instance) */
 		error = modctl_devfspath_mi_len((major_t)a1, (int)a2,
-			    (uint_t *)a3);
+		    (uint_t *)a3);
 		break;
 
 	case MODGETDEVFSPATH_MI:   	/* get path name of (major,instance) */
 		error = modctl_devfspath_mi((major_t)a1, (int)a2,
-			    (uint_t)a3, (char *)a4);
+		    (uint_t)a3, (char *)a4);
 		break;
 
 
@@ -2243,7 +2244,7 @@
 
 	case MODGETDEVPOLICY:	/* get device policy */
 		error = devpolicy_get((int *)a1, (size_t)a2,
-				(devplcysys_t *)a3);
+		    (devplcysys_t *)a3);
 		break;
 
 	case MODALLOCPRIV:
@@ -3026,7 +3027,7 @@
 	/*CONSTANTCONDITION*/
 	while (1) {
 		printf("Name of system file [%s]:  ",
-			systemfile ? systemfile : "/dev/null");
+		    systemfile ? systemfile : "/dev/null");
 
 		console_gets(s0, sizeof (s0));
 
@@ -3072,7 +3073,7 @@
 	    mod_sysctl(SYS_CHECK_EXCLUDE, mp->mod_filename) != 0) {
 		if (moddebug & MODDEBUG_LOADMSG) {
 			printf(mod_excl_msg, mp->mod_filename,
-				mp->mod_modname);
+			    mp->mod_modname);
 		}
 		return (ENXIO);
 	}
@@ -3103,10 +3104,10 @@
 		mp->mod_loadcnt++;
 		if (moddebug & MODDEBUG_LOADMSG) {
 			printf(load_msg, mp->mod_filename, mp->mod_id,
-				(void *)((struct module *)mp->mod_mp)->text,
-				(void *)((struct module *)mp->mod_mp)->data,
-				((struct module *)mp->mod_mp)->text_size,
-				((struct module *)mp->mod_mp)->data_size);
+			    (void *)((struct module *)mp->mod_mp)->text,
+			    (void *)((struct module *)mp->mod_mp)->data,
+			    ((struct module *)mp->mod_mp)->text_size,
+			    ((struct module *)mp->mod_mp)->data_size);
 		}
 
 		/*
@@ -3179,7 +3180,7 @@
 
 	if (moddebug & MODDEBUG_LOADMSG)
 		printf(unload_msg, mp->mod_modname,
-			mp->mod_id, mp->mod_loadcnt);
+		    mp->mod_id, mp->mod_loadcnt);
 
 	/*
 	 * If mod_ref is not zero, it means some modules might still refer
@@ -3271,7 +3272,7 @@
 
 	if (moddebug & MODDEBUG_LOADMSG)
 		printf("installing %s, module id %d.\n",
-			mp->mod_modname, mp->mod_id);
+		    mp->mod_modname, mp->mod_id);
 
 	ASSERT(mp->mod_mp != NULL);
 	if (mod_install_requisites(mp) != 0) {
@@ -3286,11 +3287,11 @@
 
 	if (moddebug & MODDEBUG_ERRMSG) {
 		printf("init '%s' id %d loaded @ 0x%p/0x%p size %lu/%lu\n",
-			mp->mod_filename, mp->mod_id,
-			(void *)((struct module *)mp->mod_mp)->text,
-			(void *)((struct module *)mp->mod_mp)->data,
-			((struct module *)mp->mod_mp)->text_size,
-			((struct module *)mp->mod_mp)->data_size);
+		    mp->mod_filename, mp->mod_id,
+		    (void *)((struct module *)mp->mod_mp)->text,
+		    (void *)((struct module *)mp->mod_mp)->data,
+		    ((struct module *)mp->mod_mp)->text_size,
+		    ((struct module *)mp->mod_mp)->data_size);
 	}
 
 	func = (int (*)())kobj_lookup(mp->mod_mp, "_init");
@@ -3574,9 +3575,9 @@
 		 */
 		if (mod_uninstall_interval) {
 			ticks = ddi_get_lbolt() +
-				drv_usectohz(mod_uninstall_interval * 1000000);
+			    drv_usectohz(mod_uninstall_interval * 1000000);
 			(void) cv_timedwait(&mod_uninstall_cv,
-				&mod_uninstall_lock, ticks);
+			    &mod_uninstall_lock, ticks);
 		} else {
 			cv_wait(&mod_uninstall_cv, &mod_uninstall_lock);
 		}
@@ -4025,7 +4026,7 @@
 		mod_make_requisite(dep, on_mod);
 	} else if (moddebug & MODDEBUG_ERRMSG) {
 		printf("error processing %s on which module %s depends\n",
-			on, dep->mod_modname);
+		    on, dep->mod_modname);
 	}
 	return (on_mod);
 }
@@ -4069,7 +4070,7 @@
 #define	popchar(p, c) \
 	c = *p++; \
 	if (c == 0) \
-		return (0);
+		return (0)
 
 int
 gmatch(const char *s, const char *p)
@@ -4085,7 +4086,7 @@
 	switch (c) {
 	case '\\':
 		/* skip to quoted character */
-		popchar(p, c)
+		popchar(p, c);
 		/*FALLTHRU*/
 
 	default:
@@ -4127,14 +4128,14 @@
 			notflag = 1;
 			p++;
 		}
-		popchar(p, c)
+		popchar(p, c);
 
 		do {
 			if (c == '-' && lc && *p != ']') {
 				/* test sc against range [c1-c2] */
-				popchar(p, c)
+				popchar(p, c);
 				if (c == '\\') {
-					popchar(p, c)
+					popchar(p, c);
 				}
 
 				if (notflag) {
@@ -4148,7 +4149,7 @@
 				/* keep going, may get a match next */
 			} else if (c == '\\') {
 				/* skip to quoted character */
-				popchar(p, c)
+				popchar(p, c);
 			}
 			lc = c;
 			if (notflag) {
@@ -4158,7 +4159,7 @@
 			} else if (sc == lc) {
 				ok++;
 			}
-			popchar(p, c)
+			popchar(p, c);
 		} while (c != ']');
 
 		/* recurse on remainder of string */
@@ -4383,10 +4384,8 @@
 	if ((modname == NULL) || (mode != KRTLD_MODE_FIRST))
 		goto out;
 
-	/* find optional first '/' in modname */
-	mod = strchr(modname, '/');
-	if (mod != strrchr(modname, '/'))
-		goto out;		/* only one '/' is legal */
+	/* find last '/' in modname */
+	mod = strrchr(modname, '/');
 
 	if (mod) {
 		/* for subdir string without modification to argument */
--- a/usr/src/uts/common/os/swapgeneric.c	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/os/swapgeneric.c	Fri Aug 10 17:00:37 2007 -0700
@@ -238,10 +238,10 @@
 
 	if (error) {
 		cmn_err(CE_CONT, "Cannot remount root on %s fstype %s\n",
-			rootfs.bo_name, rootfs.bo_fstype);
+		    rootfs.bo_name, rootfs.bo_fstype);
 	} else {
 		cmn_err(CE_CONT, "?root remounted on %s fstype %s\n",
-			rootfs.bo_name, rootfs.bo_fstype);
+		    rootfs.bo_name, rootfs.bo_fstype);
 	}
 	return (error);
 }
@@ -305,7 +305,7 @@
 {
 	struct vfssw	*vsw;
 	char		*this;
-	char 		*name;
+	char		*name;
 	int		err;
 /* ONC_PLUS EXTRACT END */
 	int		i, proplen, dhcacklen;
@@ -685,8 +685,16 @@
 static int
 load_boot_driver(char *drv)
 {
-	char	*drvname;
-	major_t	major;
+	char		*drvname;
+	major_t		major;
+#ifdef	sparc
+	struct devnames *dnp;
+	ddi_prop_t	*propp;
+	char		*module;
+	char		*dir, *mf;
+	int		plen;
+	int		mlen;
+#endif	/* sparc */
 
 	if ((major = ddi_name_to_major(drv)) == (major_t)-1) {
 		cmn_err(CE_CONT, "%s: no major number\n", drv);
@@ -710,6 +718,49 @@
 		return (-1);
 	}
 
+#ifdef	sparc
+	/*
+	 * NOTE: this can be removed when newboot-sparc is delivered.
+	 *
+	 * Check to see if the driver had a 'ddi-forceload' global driver.conf
+	 * property to identify additional modules that need to be loaded.
+	 * The driver still needs to use ddi_modopen() to open these modules,
+	 * but the 'ddi-forceload' property allows the modules to be loaded
+	 * into memory prior to lights-out, so that driver ddi_modopen()
+	 * calls during lights-out (when mounting root) will work correctly.
+	 * Use of 'ddi-forceload' is only required for drivers involved in
+	 * getting root mounted.
+	 */
+	dnp = &devnamesp[major];
+	if (dnp->dn_global_prop_ptr && dnp->dn_global_prop_ptr->prop_list &&
+	    ((propp = i_ddi_prop_search(DDI_DEV_T_ANY,
+	    "ddi-forceload", DDI_PROP_TYPE_STRING,
+	    &dnp->dn_global_prop_ptr->prop_list)) != NULL)) {
+
+		module = (char *)propp->prop_val;
+		plen = propp->prop_len;
+		while (plen > 0) {
+			mlen = strlen(module);
+			mf = strrchr(module, '/');
+			if (mf) {
+				dir = module;
+				*mf++ = '\0';		/* '/' -> '\0' */
+			} else {
+				dir = "misc";
+				mf = module;
+			}
+			if (modloadonly(dir, mf) == -1)
+				cmn_err(CE_CONT,
+				    "misc/%s: can't load module\n", mf);
+			if (mf != module)
+				*(mf - 1) = '/';	/* '\0' -> '/' */
+
+			module += mlen + 1;
+			plen -= mlen + 1;
+		}
+	}
+#endif	/* sparc */
+
 	return (0);
 }
 
@@ -800,7 +851,7 @@
 		*p = 0;
 
 		BMDPRINTF(("load_bootpath_drivers: parent=%s leaf=%s\n",
-			bootpath, leaf));
+		    bootpath, leaf));
 
 		dip = path_to_devinfo(pathcopy);
 		if (leaf) {
@@ -815,7 +866,7 @@
 
 	if (dip == NULL) {
 		cmn_err(CE_WARN, "can't bind driver for boot path <%s>",
-			bootpath);
+		    bootpath);
 		kmem_free(pathcopy, pathcopy_len);
 		return (NULL);
 	}
@@ -884,7 +935,7 @@
 		BMDPRINTF(("load_boot_platform_modules: %s\n", drv));
 	} else {
 		BMDPRINTF(("load_boot_platform_modules: %s -> %s\n",
-			drv, drvname));
+		    drv, drvname));
 	}
 #endif	/* DEBUG */
 
--- a/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/sys/scsi/adapters/scsi_vhci.h	Fri Aug 10 17:00:37 2007 -0700
@@ -72,6 +72,7 @@
 #define	SCBP_C(pkt)	((*(pkt)->pkt_scbp) & STATUS_MASK)
 
 int vhci_do_scsi_cmd(struct scsi_pkt *);
+/*PRINTFLIKE3*/
 void vhci_log(int, dev_info_t *, const char *, ...);
 
 /*
@@ -200,12 +201,6 @@
 #define	VHCI_SCSI_CDB_SIZE		16
 #define	VHCI_SCSI_SCB_SIZE		(sizeof (struct scsi_arq_status))
 
-#define	SCSI_OPTIONS_VHCI_MASK		0x7000000
-#define	SCSI_OPTIONS_DISABLE_DEV_TYPE	0x7000000
-#define	SCSI_OPTIONS_SYMMETRIC		0x1000000
-#define	SCSI_OPTIONS_ASYMMETRIC		0x2000000
-#define	SCSI_OPTIONS_SUN_T3_ASYMMETRIC	0x3000000
-
 /*
  * flag to determine failover support
  */
@@ -215,15 +210,6 @@
 #define	SCSI_BOTH_FAILOVER \
 	(SCSI_IMPLICIT_FAILOVER |  SCSI_EXPLICIT_FAILOVER)
 
-#define	SCSI_OPTIONS_VHCI(n)	((n) & SCSI_OPTIONS_VHCI_MASK)
-
-typedef struct vhci_dev_vidpid_entry {
-	char	vid[10];
-	int	vid_len;
-	char	pid[20];
-	int	pid_len;
-} vhci_dev_vidpid_entry_t;
-
 struct	scsi_vhci_swarg;
 
 typedef struct vhci_prin_readkeys {
@@ -269,7 +255,7 @@
 
 struct vhci_pkt {
 	struct scsi_pkt			*vpkt_tgt_pkt;
-	mdi_pathinfo_t			*vpkt_path; 	/* path pkt bound to */
+	mdi_pathinfo_t			*vpkt_path;	/* path pkt bound to */
 
 	/*
 	 * pHCI packet that does the actual work.
@@ -319,8 +305,14 @@
 	 */
 	client_lb_t		svl_lb_policy_save;
 
+	/*
+	 * Failover ops and ops name selected for the lun.
+	 */
 	struct scsi_failover_ops	*svl_fops;
+	char			*svl_fops_name;
+
 	void			*svl_fops_ctpriv;
+
 	struct scsi_vhci_lun	*svl_hash_next;
 	char			*svl_lun_wwn;
 
@@ -365,7 +357,6 @@
 	int			svl_xlf_capable; /* XLF implementation */
 	int			svl_sector_size;
 	int			svl_setcap_done;
-	uint16_t		svl_fo_type;	 /* failover type */
 	uint16_t		svl_fo_support;	 /* failover mode */
 } scsi_vhci_lun_t;
 
@@ -533,70 +524,168 @@
 	int	sfo_rev;
 
 	/*
+	 * failover module name, begins with "f_"
+	 */
+	char	*sfo_name;
+
+	/*
+	 * devices supported by failover module
+	 *
+	 * NOTE: this is an aproximation, sfo_device_probe has the final say.
+	 */
+	char	**sfo_devices;
+
+	/*
+	 * initialize the failover module
+	 */
+	void	(*sfo_init)();
+
+	/*
 	 * identify device
 	 */
 	int	(*sfo_device_probe)(
 			struct scsi_device	*sd,
-			struct scsi_inquiry 	*stdinq,
-			void 			**ctpriv);
+			struct scsi_inquiry	*stdinq,
+			void			**ctpriv);
 
 	/*
 	 * housekeeping (free memory etc alloc'ed during probe
 	 */
 	void	(*sfo_device_unprobe)(
-			struct scsi_device 	*sd,
-			void 			*ctpriv);
+			struct scsi_device	*sd,
+			void			*ctpriv);
 
 	/*
 	 * bring a path ONLINE (ie make it ACTIVE)
 	 */
 	int	(*sfo_path_activate)(
-			struct scsi_device 	*sd,
-			char 			*pathclass,
-			void 			*ctpriv);
+			struct scsi_device	*sd,
+			char			*pathclass,
+			void			*ctpriv);
 
 	/*
 	 * inverse of above
 	 */
-	int 	(*sfo_path_deactivate)(
-			struct scsi_device 	*sd,
-			char 			*pathclass,
-			void 			*ctpriv);
+	int	(*sfo_path_deactivate)(
+			struct scsi_device	*sd,
+			char			*pathclass,
+			void			*ctpriv);
 
 	/*
 	 * returns operational characteristics of path
 	 */
 	int	(*sfo_path_get_opinfo)(
-			struct scsi_device 	*sd,
+			struct scsi_device	*sd,
 			struct scsi_path_opinfo *opinfo,
-			void 			*ctpriv);
+			void			*ctpriv);
 
 	/*
 	 * verify path is operational
 	 */
 	int	(*sfo_path_ping)(
-			struct scsi_device 	*sd,
-			void 			*ctpriv);
+			struct scsi_device	*sd,
+			void			*ctpriv);
 
 	/*
 	 * analyze SENSE data to detect externally initiated
 	 * failovers
 	 */
 	int	(*sfo_analyze_sense)(
-			struct scsi_device 		*sd,
-			struct scsi_extended_sense 	*sense,
-			void 				*ctpriv);
+			struct scsi_device		*sd,
+			struct scsi_extended_sense	*sense,
+			void				*ctpriv);
 
 	/*
 	 * return the next pathclass in order of preference
 	 * eg. "secondary" comes after "primary"
 	 */
 	int	(*sfo_pathclass_next)(
-			char 			*cur,
-			char 			**nxt,
-			void 			*ctpriv);
+			char			*cur,
+			char			**nxt,
+			void			*ctpriv);
 };
 
+/*
+ * Names of (too) 'well-known' failover ops.
+ *   NOTE: consumers of these names should look for a better way...
+ */
+#define	SFO_NAME_SYM		"f_sym"
+#define	SFO_NAME_TPGS		"f_tpgs"
+#define	SCSI_FAILOVER_IS_SYM(sfo)	\
+	((sfo) ? (strcmp((sfo)->sfo_name, SFO_NAME_SYM) == 0) : 0)
+#define	SCSI_FAILOVER_IS_TPGS(sfo)	\
+	((sfo) ? (strcmp((sfo)->sfo_name, SFO_NAME_TPGS) == 0) : 0)
+
+/*
+ * Macro to provide plumbing for basic failover module
+ */
+#define	_SCSI_FAILOVER_OP(sfo_name, local_name, ops_name, vers)		\
+	static struct modlmisc modlmisc = {				\
+		&mod_miscops, sfo_name  " " vers			\
+	};								\
+	static struct modlinkage modlinkage = {				\
+		MODREV_1, (void *)&modlmisc, NULL			\
+	};								\
+	int	_init()							\
+	{								\
+		return (mod_install(&modlinkage));			\
+	}								\
+	int	_fini()							\
+	{								\
+		return (mod_remove(&modlinkage));			\
+	}								\
+	int	_info(struct modinfo *modinfop)				\
+	{								\
+		return (mod_info(&modlinkage, modinfop));		\
+	}								\
+	static int	local_name##_device_probe(			\
+				struct scsi_device *,			\
+				struct scsi_inquiry *, void **);	\
+	static void	local_name##_device_unprobe(			\
+				struct scsi_device *, void *);		\
+	static int	local_name##_path_activate(			\
+				struct scsi_device *, char *, void *);	\
+	static int	local_name##_path_deactivate(			\
+				struct scsi_device *, char *, void *);	\
+	static int	local_name##_path_get_opinfo(			\
+				struct scsi_device *,			\
+				struct scsi_path_opinfo *, void *);	\
+	static int	local_name##_path_ping(				\
+				struct scsi_device *, void *);		\
+	static int	local_name##_analyze_sense(			\
+				struct scsi_device *,			\
+				struct scsi_extended_sense *, void *);	\
+	static int	local_name##_pathclass_next(			\
+				char *, char **, void *);		\
+	struct scsi_failover_ops ops_name##_failover_ops = {		\
+		SFO_REV,						\
+		sfo_name,						\
+		local_name##_dev_table,					\
+		NULL,							\
+		local_name##_device_probe,				\
+		local_name##_device_unprobe,				\
+		local_name##_path_activate,				\
+		local_name##_path_deactivate,				\
+		local_name##_path_get_opinfo,				\
+		local_name##_path_ping,					\
+		local_name##_analyze_sense,				\
+		local_name##_pathclass_next				\
+	}
+
+#ifdef	lint
+#define	SCSI_FAILOVER_OP(sfo_name, local_name, vers)			\
+	_SCSI_FAILOVER_OP(sfo_name, local_name, local_name, vers)
+#else	/* lint */
+#define	SCSI_FAILOVER_OP(sfo_name, local_name, vers)			\
+	_SCSI_FAILOVER_OP(sfo_name, local_name, scsi_vhci, vers)
+#endif	/* lint */
+
+/*
+ * Return values for sfo_device_probe
+ */
+#define	SFO_DEVICE_PROBE_VHCI	1	/* supported under scsi_vhci */
+#define	SFO_DEVICE_PROBE_PHCI	0	/* not supported under scsi_vhci */
+
 /* return values for sfo_analyze_sense() */
 #define	SCSI_SENSE_NOFAILOVER		0
 #define	SCSI_SENSE_FAILOVER_INPROG	1
--- a/usr/src/uts/common/sys/scsi/impl/transport.h	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/common/sys/scsi/impl/transport.h	Fri Aug 10 17:00:37 2007 -0700
@@ -336,6 +336,11 @@
 				struct scsi_device	*sd,
 				int			(*callback)(void));
 
+char			*scsi_get_device_type_string(
+				char			*prop_name,
+				dev_info_t		*hba_dip,
+				struct scsi_device	*devp);
+
 extern int		scsi_get_device_type_scsi_options(
 				dev_info_t		*hba_dip,
 				struct scsi_device	*devp,
--- a/usr/src/uts/intel/Makefile.intel.shared	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Fri Aug 10 17:00:37 2007 -0700
@@ -371,7 +371,7 @@
 #	Machine Specific Driver Modules (/kernel/drv):
 #
 DRV_KMODS	+= options
-$(CLOSED_BUILD)CLOSED_DRV_KMODS	+= scsi_vhci
+DRV_KMODS	+= scsi_vhci
 
 #
 #	PCMCIA specific module(s)
@@ -550,10 +550,16 @@
 MISC_KMODS	+= tem
 MISC_KMODS	+= tlimod
 MISC_KMODS	+= usba usba10 usbs49_fw
+MISC_KMODS	+= scsi_vhci_f_sym
+MISC_KMODS	+= scsi_vhci_f_tpgs
+MISC_KMODS	+= scsi_vhci_f_asym_sun
 
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= amsrc1
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= klmmod klmops
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= phx
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_asym_lsi
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_asym_emc
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_sym_emc
 
 #
 #	Software Cryptographic Providers (/kernel/crypto):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/scsi_vhci/Makefile	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,89 @@
+#
+# 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 2007 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 SCSI vHCI Driver
+# intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci
+OBJECTS		= $(SCSI_VHCI_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/scsi/adapters/scsi_vhci
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# 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_asym_sun/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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_asym_sun
+# intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_asym_sun
+OBJECTS		= $(SCSI_VHCI_F_ASYM_SUN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_ASYM_SUN_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_sym/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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_sym
+# intel architecture dependent module
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_sym
+OBJECTS		= $(SCSI_VHCI_F_SYM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_SYM_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/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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
+# 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
+OBJECTS		= $(SCSI_VHCI_F_TPGS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TPGS_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	Fri Aug 10 11:19:25 2007 -0700
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Fri Aug 10 17:00:37 2007 -0700
@@ -286,11 +286,11 @@
 DRV_KMODS	+= socal
 DRV_KMODS	+= sgen
 DRV_KMODS	+= dad
+DRV_KMODS	+= scsi_vhci
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= audioens
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= audiovia823x
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= ifp
-$(CLOSED_BUILD)CLOSED_DRV_KMODS	+= scsi_vhci
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= uata
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= usbser_edge
 
@@ -385,10 +385,14 @@
 MISC_KMODS	+= cmlb
 MISC_KMODS	+= tem
 MISC_KMODS	+= pcicfg.e fcodem fcpci
+MISC_KMODS	+= scsi_vhci_f_sym scsi_vhci_f_tpgs scsi_vhci_f_asym_sun
 
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= amsrc1
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= klmmod klmops
 $(CLOSED_BUILD)CLOSED_MISC_KMODS	+= phx
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_asym_lsi
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_asym_emc
+$(CLOSED_BUILD)CLOSED_MISC_KMODS	+= scsi_vhci_f_sym_emc
 
 #
 #	Software Cryptographic Providers (/kernel/crypto):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/scsi_vhci/Makefile	Fri Aug 10 17:00:37 2007 -0700
@@ -0,0 +1,89 @@
+#
+# 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 2007 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 SCSI vHCI Driver
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci
+OBJECTS		= $(SCSI_VHCI_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/scsi/adapters/scsi_vhci
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets.
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# 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_asym_sun/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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_asym_sun
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_asym_sun
+OBJECTS		= $(SCSI_VHCI_F_ASYM_SUN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_ASYM_SUN_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_sym/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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_sym
+# sparc architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= scsi_vhci_f_sym
+OBJECTS		= $(SCSI_VHCI_F_SYM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_SYM_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/Makefile	Fri Aug 10 17:00:37 2007 -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 2007 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
+# 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
+OBJECTS		= $(SCSI_VHCI_F_TPGS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(SCSI_VHCI_F_TPGS_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