changeset 10725:cd144ed668c5

PSARC/2009/465 COMSTAR ALUA active/standby support 6862774 Add support for ALUA to COMSTAR 6878583 mem leak in stmfGetLuResource() 6850890 stmfTargetProperties.protocol is not populated for stmfGetTargetProperties()
author John Forte <John.Forte@Sun.COM>
date Tue, 06 Oct 2009 19:56:15 -0700
parents 46ad20cc31ef
children dd40023f03e8
files usr/src/cmd/Makefile usr/src/cmd/Makefile.check usr/src/cmd/sbdadm/sbdadm.c usr/src/cmd/stmfadm/stmfadm.c usr/src/cmd/stmfproxy/Makefile usr/src/cmd/stmfproxy/aluaadm/Makefile usr/src/cmd/stmfproxy/aluaadm/aluaadm.c usr/src/cmd/stmfproxy/stmfproxy/Makefile usr/src/cmd/stmfproxy/stmfproxy/stmfproxy.c usr/src/cmd/stmfproxy/stmfproxy/stmfproxy.xml usr/src/lib/Makefile usr/src/lib/libstmf/common/libstmf.h usr/src/lib/libstmf/common/libstmf_impl.h usr/src/lib/libstmf/common/mapfile-vers usr/src/lib/libstmf/common/stmf.c usr/src/lib/libstmfproxy/Makefile usr/src/lib/libstmfproxy/Makefile.com usr/src/lib/libstmfproxy/amd64/Makefile usr/src/lib/libstmfproxy/common/libstmfproxy.h usr/src/lib/libstmfproxy/common/llib-lstmfproxy usr/src/lib/libstmfproxy/common/mapfile-vers usr/src/lib/libstmfproxy/common/stmftransport.c usr/src/lib/libstmfproxy/i386/Makefile usr/src/lib/libstmfproxy/sparc/Makefile usr/src/lib/libstmfproxy/sparcv9/Makefile usr/src/pkgdefs/SUNWstmf/postinstall usr/src/pkgdefs/SUNWstmf/preremove usr/src/pkgdefs/SUNWstmf/prototype_com usr/src/pkgdefs/SUNWstmf/prototype_i386 usr/src/pkgdefs/SUNWstmf/prototype_sparc usr/src/pkgdefs/SUNWstmfu/prototype_com usr/src/pkgdefs/SUNWstmfu/prototype_i386 usr/src/pkgdefs/SUNWstmfu/prototype_sparc usr/src/pkgdefs/etc/exception_list_i386 usr/src/pkgdefs/etc/exception_list_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_impl.h usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_pgr.c usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h usr/src/uts/common/io/comstar/port/pppt/alua_ic_if.c usr/src/uts/common/io/comstar/port/pppt/pppt.c usr/src/uts/common/io/comstar/port/pppt/pppt.conf usr/src/uts/common/io/comstar/port/pppt/pppt.h usr/src/uts/common/io/comstar/port/pppt/pppt_msg.c usr/src/uts/common/io/comstar/port/pppt/pppt_tgt.c usr/src/uts/common/io/comstar/stmf/lun_map.c usr/src/uts/common/io/comstar/stmf/stmf.c usr/src/uts/common/io/comstar/stmf/stmf_impl.h usr/src/uts/common/io/comstar/stmf/stmf_state.h usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/lpif.h usr/src/uts/common/sys/portif.h usr/src/uts/common/sys/pppt_ic_if.h usr/src/uts/common/sys/pppt_ioctl.h usr/src/uts/common/sys/stmf_defines.h usr/src/uts/common/sys/stmf_ioctl.h usr/src/uts/common/sys/stmf_sbd_ioctl.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/pppt/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/pppt/Makefile
diffstat 64 files changed, 9841 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/Makefile	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/cmd/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -383,6 +383,7 @@
 	ssh		\
 	stat		\
 	stmfadm		\
+	stmfproxy	\
 	stmfsvc		\
 	stmsboot	\
 	streams		\
--- a/usr/src/cmd/Makefile.check	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/cmd/Makefile.check	Tue Oct 06 19:56:15 2009 -0700
@@ -67,6 +67,7 @@
 	sckmd				\
 	sf880drd			\
 	smserverd			\
+	stmfproxy			\
 	stmfsvc				\
 	stmsboot			\
 	syseventd			\
--- a/usr/src/cmd/sbdadm/sbdadm.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/cmd/sbdadm/sbdadm.c	Tue Oct 06 19:56:15 2009 -0700
@@ -665,7 +665,7 @@
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
 	} else {
-		(void) printf("<error retrieving property>");
+		(void) printf("<error retrieving property>\n");
 		ret++;
 	}
 
@@ -676,7 +676,7 @@
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
 	} else {
-		(void) printf("<error retrieving property>");
+		(void) printf("<error retrieving property>\n");
 		ret++;
 	}
 
--- a/usr/src/cmd/stmfadm/stmfadm.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/cmd/stmfadm/stmfadm.c	Tue Oct 06 19:56:15 2009 -0700
@@ -1113,6 +1113,11 @@
 			    gettext("could not set write cache"));
 			ret++;
 			break;
+		case STMF_ERROR_ACCESS_STATE_SET:
+			(void) fprintf(stderr, "%s: %s\n", cmdName,
+			    gettext("cannot modify while in standby mode"));
+			ret++;
+			break;
 		default:
 			(void) fprintf(stderr, "%s: %s: %s: %d\n", cmdName,
 			    gettext("could not set property"), propString,
@@ -2063,6 +2068,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2075,6 +2082,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2087,6 +2096,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2099,6 +2110,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2111,6 +2124,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2123,6 +2138,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2135,6 +2152,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2147,6 +2166,8 @@
 		(void) printf("%s\n", propVal);
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2160,6 +2181,8 @@
 		    strcasecmp(propVal, "true") ? "Disabled" : "Enabled");
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
@@ -2173,12 +2196,38 @@
 		    strcasecmp(propVal, "true") ? "Enabled" : "Disabled");
 	} else if (stmfRet == STMF_ERROR_NO_PROP) {
 		(void) printf("not set\n");
+	} else if (stmfRet == STMF_ERROR_NO_PROP_STANDBY) {
+		(void) printf("prop unavailable in standby\n");
 	} else {
 		(void) printf("<error retrieving property>\n");
 		ret++;
 	}
 
+	stmfRet = stmfGetLuProp(hdl, STMF_LU_PROP_ACCESS_STATE, propVal,
+	    &propValSize);
+	(void) printf(PROPS_FORMAT, "Access State");
+	if (stmfRet == STMF_STATUS_SUCCESS) {
+		if (strcmp(propVal, STMF_ACCESS_ACTIVE) == 0) {
+			(void) printf("%s\n", "Active");
+		} else if (strcmp(propVal,
+		    STMF_ACCESS_ACTIVE_TO_STANDBY) == 0) {
+			(void) printf("%s\n", "Active->Standby");
+		} else if (strcmp(propVal, STMF_ACCESS_STANDBY) == 0) {
+			(void) printf("%s\n", "Standby");
+		} else if (strcmp(propVal,
+		    STMF_ACCESS_STANDBY_TO_ACTIVE) == 0) {
+			(void) printf("%s\n", "Standby->Active");
+		} else {
+			(void) printf("%s\n", "Unknown");
+		}
+	} else if (stmfRet == STMF_ERROR_NO_PROP) {
+		(void) printf("not set\n");
+	} else {
+		(void) printf("<error retrieving property>\n");
+		ret++;
+	}
 
+done:
 	(void) stmfFreeLuResource(hdl);
 	return (ret);
 
@@ -2274,6 +2323,26 @@
 		(void) printf("-");
 	}
 	(void) printf("\n");
+	(void) printf(PROPS_FORMAT, "Protocol");
+	switch (targetProps->protocol) {
+		case STMF_PROTOCOL_FIBRE_CHANNEL:
+			(void) printf("%s", "Fibre Channel");
+			break;
+		case STMF_PROTOCOL_ISCSI:
+			(void) printf("%s", "iSCSI");
+			break;
+		case STMF_PROTOCOL_SRP:
+			(void) printf("%s", "SRP");
+			break;
+		case STMF_PROTOCOL_SAS:
+			(void) printf("%s", "SAS");
+			break;
+		default:
+			(void) printf("%s", "unknown");
+			break;
+	}
+
+	(void) printf("\n");
 }
 
 /*
@@ -2361,6 +2430,8 @@
 {
 	int ret;
 	stmfState state;
+	boolean_t aluaEnabled;
+	uint32_t node;
 
 	if ((ret = getStmfState(&state)) != STMF_STATUS_SUCCESS)
 		return (ret);
@@ -2400,7 +2471,43 @@
 			break;
 	}
 	(void) printf("\n");
-	return (0);
+	ret = stmfGetAluaState(&aluaEnabled, &node);
+	switch (ret) {
+		case STMF_STATUS_SUCCESS:
+			break;
+		case STMF_ERROR_PERM:
+			(void) fprintf(stderr, "%s: %s\n", cmdName,
+			    gettext("permission denied"));
+			break;
+		case STMF_ERROR_BUSY:
+			(void) fprintf(stderr, "%s: %s\n", cmdName,
+			    gettext("resource busy"));
+			break;
+		default:
+			(void) fprintf(stderr, "%s: %s: %d\n", cmdName,
+			    gettext("unknown error"), ret);
+			break;
+	}
+	(void) printf("%-18s: ", "ALUA Status");
+	if (ret == STMF_STATUS_SUCCESS) {
+		if (aluaEnabled == B_TRUE) {
+			(void) printf("enabled");
+		} else {
+			(void) printf("disabled");
+		}
+	} else {
+		(void) printf("unknown");
+	}
+
+	(void) printf("\n");
+	(void) printf("%-18s: ", "ALUA Node");
+	if (ret == STMF_STATUS_SUCCESS) {
+		(void) printf("%d", node);
+	} else {
+		(void) printf("unknown");
+	}
+	(void) printf("\n");
+	return (ret);
 }
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,55 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/aluademo/Makefile
+#
+
+include ../Makefile.cmd
+
+SUBDIRS   = stmfproxy aluaadm
+
+all :=		TARGET= all
+install :=	TARGET= install
+clean :=	TARGET= clean
+clobber :=	TARGET= clobber
+lint :=		TARGET= lint
+cstyle :=	TARGET= cstyle
+check :=	TARGET= check
+
+.KEEP_STATE:
+
+all install cstyle lint : $(SUBDIRS)
+
+check: stmfproxy
+
+clean: $(SUBDIRS)
+
+clobber: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/aluaadm/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,80 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+# cmd/iscsid/Makefile
+#
+
+PROG	= aluaadm
+
+include ../../Makefile.cmd
+
+COMMONBASE = ../../../common
+
+DEMOBINFILES = \
+	$(PROG)
+
+ROOTDEMODIR	= $(ROOT)/usr/demo/comstar
+ROOTDEMOBINDIR	= $(ROOTDEMODIR)/bin
+ROOTDEMOFILES = $(DEMOFILES:%=$(ROOTDEMODIR)/%)
+ROOTDEMOBINFILES = $(DEMOBINFILES:%=$(ROOTDEMOBINDIR)/%)
+
+LOCAL_OBJS =	aluaadm.o
+LOCAL_SRCS =	$(LOCAL_OBJS:%.o=%.c)
+COMMON_OBJS =	cmdparse.o
+COMMON_SRCS =	$(COMMON_OBJS:%.o=$(COMMONBASE)/cmdparse/%.c)
+OBJS =		$(LOCAL_OBJS) $(COMMON_OBJS)
+SRCS =		$(LOCAL_SRCS) $(COMMON_SRCS)
+
+CPPFLAGS += -I. -I$(COMMONBASE)/cmdparse
+LDLIBS += -lstmf
+
+.KEEP_STATE:
+
+all:	$(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+clean:
+	$(RM) $(OBJS)
+
+install: all $(ROOTDEMOBINFILES)
+
+$(ROOTDEMODIR) $(ROOTDEMOBINDIR):
+	$(INS.dir)
+
+$(ROOTDEMODIR)/% $(ROOTDEMOBINDIR)/% : %
+	$(INS.file)
+
+$(ROOTDEMOBINFILES): $(ROOTDEMOBINDIR)
+
+cmdparse.o: $(COMMONBASE)/cmdparse/cmdparse.c
+	    $(COMPILE.c) -o $@ $(COMMONBASE)/cmdparse/cmdparse.c
+	    $(POST_PROCESS_O)
+
+lint:	lint_SRCS
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/aluaadm/aluaadm.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,240 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <libintl.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include <cmdparse.h>
+#include <libstmf.h>
+#include <signal.h>
+#include <pthread.h>
+#include <locale.h>
+
+static char *getExecBasename(char *);
+static int setLuStandbyFunc(int, char **, cmdOptions_t *, void *);
+static int disableAluaFunc(int, char **, cmdOptions_t *, void *);
+static int enableAluaFunc(int, char **, cmdOptions_t *, void *);
+
+#define	OPERANDSTRING_LU	    "LU-name"
+#define	OPERANDSTRING_NODE_ID	    "node ID (0 or 1)"
+
+#define	VERSION_STRING_MAJOR	    "1"
+#define	VERSION_STRING_MINOR	    "0"
+#define	VERSION_STRING_MAX_LEN	    10
+
+#define	GUID_INPUT		    32
+
+/* tables set up based on cmdparse instructions */
+
+/* add new options here */
+optionTbl_t longOptions[] = {
+	{NULL, 0, 0, 0}
+};
+
+/*
+ * Add new subcommands here
+ */
+subCommandProps_t subcommands[] = {
+	{"standby", setLuStandbyFunc, NULL, NULL, NULL,
+		OPERAND_MANDATORY_SINGLE, OPERANDSTRING_LU, NULL},
+	{"disable", disableAluaFunc, NULL, NULL, NULL,
+		OPERAND_NONE, NULL, NULL},
+	{"enable", enableAluaFunc, NULL, NULL, NULL,
+		OPERAND_MANDATORY_SINGLE, OPERANDSTRING_NODE_ID, NULL},
+	{NULL, 0, NULL, NULL, 0, NULL, 0, NULL, NULL}
+};
+
+/* globals */
+char *cmdName;
+
+/*
+ * setLuStandbyFunc
+ *
+ * Purpose: set lu to standby
+ *
+ */
+/*ARGSUSED*/
+static int
+setLuStandbyFunc(int operandLen, char *operands[], cmdOptions_t *options,
+    void *args)
+{
+	char sGuid[GUID_INPUT + 1];
+	stmfGuid inGuid;
+	unsigned int guid[sizeof (stmfGuid)];
+	int i;
+	int ret = 0;
+
+	if (strlen(operands[0]) != GUID_INPUT) {
+		(void) fprintf(stderr, "%s: %s: %s %d %s\n", cmdName,
+		    operands[0], gettext("must be"), GUID_INPUT,
+		    gettext("hexadecimal digits long"));
+		return (1);
+	}
+
+	bcopy(operands[0], sGuid, GUID_INPUT);
+
+	for (i = 0; i < GUID_INPUT; i++)
+		sGuid[i] = tolower(sGuid[i]);
+	sGuid[i] = 0;
+
+	(void) sscanf(sGuid, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
+	    &guid[0], &guid[1], &guid[2], &guid[3], &guid[4], &guid[5],
+	    &guid[6], &guid[7], &guid[8], &guid[9], &guid[10], &guid[11],
+	    &guid[12], &guid[13], &guid[14], &guid[15]);
+
+	for (i = 0; i < sizeof (stmfGuid); i++) {
+		inGuid.guid[i] = guid[i];
+	}
+
+	ret = stmfLuStandby(&inGuid);
+	if (ret != STMF_STATUS_SUCCESS) {
+		switch (ret) {
+			case STMF_ERROR_PERM:
+				(void) fprintf(stderr, "%s: %s\n", cmdName,
+				    gettext("permission denied"));
+				break;
+			case STMF_ERROR_SERVICE_NOT_FOUND:
+				(void) fprintf(stderr, "%s: %s\n", cmdName,
+				    gettext("STMF service not found"));
+				break;
+			case STMF_ERROR_NOT_FOUND:
+				(void) fprintf(stderr, "%s: %s: %s\n", cmdName,
+				    operands[0], gettext("not found"));
+				break;
+			case STMF_ERROR_SERVICE_DATA_VERSION:
+				(void) fprintf(stderr, "%s: %s\n", cmdName,
+				    gettext("STMF service version incorrect"));
+				break;
+			default:
+				(void) fprintf(stderr, "%s: %s\n", cmdName,
+				    gettext("unknown error"));
+				break;
+		}
+	}
+	return (ret);
+}
+
+/*
+ * disableAluaFunc
+ *
+ * Purpose: disable alua mode
+ *
+ */
+/*ARGSUSED*/
+static int
+disableAluaFunc(int operandLen, char *operands[], cmdOptions_t *options,
+    void *args)
+{
+	return (stmfSetAluaState(B_FALSE, 0));
+}
+
+/*
+ * enableAluaFunc
+ *
+ * Purpose: enable alua mode
+ *
+ */
+/*ARGSUSED*/
+static int
+enableAluaFunc(int operandLen, char *operands[], cmdOptions_t *options,
+    void *args)
+{
+	uint8_t node_id = 0;
+	if (operands[0][1] == '1') {
+		node_id = 1;
+	}
+	return (stmfSetAluaState(B_TRUE, node_id));
+}
+
+
+/*
+ * input:
+ *  execFullName - exec name of program (argv[0])
+ *
+ *  copied from usr/src/cmd/zoneadm/zoneadm.c in OS/Net
+ *  (changed name to lowerCamelCase to keep consistent with this file)
+ *
+ * Returns:
+ *  command name portion of execFullName
+ */
+static char *
+getExecBasename(char *execFullname)
+{
+	char *lastSlash, *execBasename;
+
+	/* guard against '/' at end of command invocation */
+	for (;;) {
+		lastSlash = strrchr(execFullname, '/');
+		if (lastSlash == NULL) {
+			execBasename = execFullname;
+			break;
+		} else {
+			execBasename = lastSlash + 1;
+			if (*execBasename == '\0') {
+				*lastSlash = '\0';
+				continue;
+			}
+			break;
+		}
+	}
+	return (execBasename);
+}
+
+int
+main(int argc, char *argv[])
+{
+	synTables_t synTables;
+	char versionString[VERSION_STRING_MAX_LEN];
+	int ret;
+	int funcRet;
+	void *subcommandArgs = NULL;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+	/* set global command name */
+	cmdName = getExecBasename(argv[0]);
+
+	(void) snprintf(versionString, VERSION_STRING_MAX_LEN, "%s.%s",
+	    VERSION_STRING_MAJOR, VERSION_STRING_MINOR);
+	synTables.versionString = versionString;
+	synTables.longOptionTbl = &longOptions[0];
+	synTables.subCommandPropsTbl = &subcommands[0];
+
+	ret = cmdParse(argc, argv, synTables, subcommandArgs, &funcRet);
+	if (ret != 0) {
+		return (ret);
+	}
+
+	return (funcRet);
+} /* end main */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/stmfproxy/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,103 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+# cmd/stmfproxy/stmfproxy/Makefile
+#
+
+PROG	= svc-stmfproxy
+
+COMMONBASE = ../../common
+
+include ../../Makefile.cmd
+
+DEMOFILES = \
+	stmfproxy.xml
+
+DEMOBINFILES = \
+	$(PROG)
+
+ROOTDEMODIR	= $(ROOT)/usr/demo/comstar
+ROOTDEMOBINDIR	= $(ROOTDEMODIR)/bin
+ROOTDEMOFILES = $(DEMOFILES:%=$(ROOTDEMODIR)/%)
+ROOTDEMOBINFILES = $(DEMOBINFILES:%=$(ROOTDEMOBINDIR)/%)
+
+PRODUCT=	$(PROG)
+SRCS=		$(OBJS:%.o=./%.c)
+OBJS=		stmfproxy.o
+LLOBJS=		$(OBJS:%.o=%.ll)
+#POFILES= $(OBJS:%.o=%.po)
+#POFILE= stmfproxy.po
+
+$(ROOTDEMODIR)/stmfproxy.xml	:= FILEMODE = 0444
+
+
+CCVERBOSE	=
+LDLIBS += -lnsl -lstmf -lstmfproxy
+CPPFLAGS += -I. -I$(COMMONBASE)/cmdparse
+
+# Uncomment the following to help with debugging
+#CFLAGS += -g 
+#i386_COPTFLAG=
+#i386_CCOPTFLAG=
+
+.KEEP_STATE:
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+LINTFLAGS += -erroff=E_FUNC_HAS_NO_RETURN_STMT
+LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
+.PARALLEL: $(OBJS)
+
+all:	$(PROG)
+
+clean:
+	$(RM) $(PROG) $(OBJS) $(LLOBJS)
+
+lint:	lint_SRCS
+
+#$(POFILE): $(POFILES)
+#	$(RM) $@
+#	cat $(POFILES) > $@
+
+install: all $(ROOTDEMOFILES) $(ROOTDEMOBINFILES)
+
+$(ROOTDEMODIR) $(ROOTDEMOBINDIR):
+	$(INS.dir)
+
+$(ROOTDEMODIR)/% $(ROOTDEMOBINDIR)/% : %
+	$(INS.file)
+
+$(ROOTDEMOFILES): $(ROOTDEMODIR)
+
+$(ROOTDEMOBINFILES): $(ROOTDEMOBINDIR)
+
+check:	$(CHKMANIFEST)
+	$(CSTYLE) -pPc $(SRCS:%=%)
+
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/stmfproxy/stmfproxy.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,541 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <sys/sdt.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <libstmf.h>
+#include <door.h>
+#include <pthread.h>
+#include <libscf.h>
+#include <locale.h>
+#include <sys/stmf_ioctl.h>
+#include <sys/pppt_ioctl.h>
+#include <libstmfproxy.h>
+
+#define	PPPT_NODE	"/devices/pseudo/pppt@0:pppt"
+#define	USAGE	"Usage: %s [-d][-f][-n nodeid] nodename\n" \
+		"Note: nodename must be the same on both nodes\n"
+
+
+/*
+ * static functions
+ */
+static void daemonInit(void);
+static void killHandler();
+static int postMsg(uint_t nelem, uchar_t *aluaMsg);
+
+
+/*
+ * globals
+ */
+void *t_handle;			/* transport handle */
+char aluaNode[256];		/* one of the two alua peers */
+char myNode[256];		/* this hostname */
+int log_debug = 0;
+int fore_ground = 0;
+int proxy_hdl;
+pt_ops_t *pt_ops;
+
+/*
+ * killHandler
+ *
+ * Terminates this process on SIGQUIT, SIGINT, SIGTERM
+ */
+/* ARGSUSED */
+static void
+killHandler(int sig)
+{
+	exit(0);
+}
+
+/*
+ * doorHandler
+ *
+ * Recieve data from the local proxy port provider and relay
+ * it to the peer node.
+ */
+/* ARGSUSED */
+void
+doorHandler(
+	void		*cookie,
+	char		*args,
+	size_t		alen,
+	door_desc_t	*ddp,
+	uint_t		ndid)
+{
+	uint32_t result = 0;
+
+	if (ddp != NULL || ndid != 0) {
+		syslog(LOG_DAEMON|LOG_WARNING,
+		    "descriptor passed to door %p %d", ddp, ndid);
+		result = EINVAL;
+	}
+
+	if (args == NULL || alen == 0) {
+		syslog(LOG_DAEMON|LOG_WARNING,
+		    "empty message passed to door %p %d", args, alen);
+		result = EFAULT;
+	}
+
+	if (result == 0)
+		result = postMsg((uint_t)alen, (uchar_t *)args);
+	(void) door_return((char *)&result, sizeof (result), NULL, 0);
+
+	syslog(LOG_DAEMON|LOG_WARNING, "door_return FAILED %d", errno);
+	exit(errno);
+}
+
+static int
+postMsg(uint_t nelem, uchar_t *aluaMsg)
+{
+	uint32_t buflen;
+	uchar_t *buf;
+	int ret = 0;
+	int ns;
+
+	if (t_handle == NULL) {
+		syslog(LOG_DAEMON|LOG_WARNING,
+		    "postMsg() no transport handle");
+		exit(1);
+	}
+
+	buf = malloc(nelem + sizeof (buflen));
+
+	buflen = htonl(nelem);	/* length in network byte order */
+	bcopy(&buflen, buf, sizeof (buflen));
+	bcopy(aluaMsg, buf + sizeof (buflen), nelem);
+
+	ns = pt_ops->stmf_proxy_send(t_handle, buf, nelem + sizeof (buflen));
+	if (ns != nelem + sizeof (buflen)) {
+		ret = errno;
+		if (ret == 0)
+			ret = ENOTTY;	/* something bogus */
+		syslog(LOG_DAEMON|LOG_CRIT, "send() call failed: %d", ret);
+	}
+	free(buf);
+	return (ret);
+}
+
+/*
+ * Multi-thread the data path from the peer node to the local
+ * proxy port provider. During discover, there can be a large
+ * burst of messages from the peer node proportional to the number
+ * of LUs. Multiple threads allow these messages to be processed
+ * simultaneously.
+ */
+typedef struct pppt_drv_queue {
+	struct pppt_drv_queue	*next;
+	uint32_t		buflen;
+	uchar_t			*buf;
+} pppt_drv_queue_t;
+
+pppt_drv_queue_t *pq_head = NULL;
+pthread_mutex_t pq_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t pq_cond = PTHREAD_COND_INITIALIZER;
+int pq_num_threads = 0;
+int pq_avail_threads = 0;
+
+/*ARGSUSED*/
+void *
+push_to_drv(void *arg)
+{
+	pppt_drv_queue_t	*pq;
+	int rc;
+
+	(void) pthread_mutex_lock(&pq_mutex);
+	pq_num_threads++;
+	(void) pthread_mutex_unlock(&pq_mutex);
+	for (;;) {
+		(void) pthread_mutex_lock(&pq_mutex);
+		while (pq_head == NULL) {
+			pq_avail_threads++;
+			(void) pthread_cond_wait(&pq_cond, &pq_mutex);
+			pq_avail_threads--;
+		}
+		pq = pq_head;
+		pq_head = pq->next;
+		pq->next = NULL;
+		(void) pthread_mutex_unlock(&pq_mutex);
+		/* Relay the message to the local kernel */
+		rc = stmfPostProxyMsg(proxy_hdl, (void *)pq->buf, pq->buflen);
+		if (rc != STMF_STATUS_SUCCESS) {
+			/* XXX die ? */
+			syslog(LOG_DAEMON|LOG_CRIT, "ioctl failed - %d", errno);
+		}
+		free(pq->buf);
+		free(pq);
+	}
+	/*NOTREACHED*/
+	return (NULL);
+}
+
+/*
+ * Receive data from peer and queue it up for the proxy driver.
+ */
+int message_count = 0;
+static void
+relay_peer_msg()
+{
+	uint32_t		buflen;
+	pppt_drv_queue_t	*pq, *tmpq;
+	pthread_t		tid;
+	int			rc;
+
+
+	/* first receive the length of the message */
+	if ((pt_ops->stmf_proxy_recv(t_handle, (uchar_t *)&buflen,
+	    sizeof (buflen))) != sizeof (buflen)) {
+		syslog(LOG_DAEMON|LOG_WARNING, "recv() call failed: %d",
+		    errno);
+		exit(1);
+	}
+
+	pq = malloc(sizeof (*pq));
+	pq->next = NULL;
+	pq->buflen = ntohl(buflen);
+	pq->buf = malloc(pq->buflen+4);
+	if (log_debug) {
+		syslog(LOG_DAEMON|LOG_DEBUG,
+		    "recvMsg: size of buffer - %d", (int)pq->buflen);
+	}
+
+	if ((pt_ops->stmf_proxy_recv(t_handle, pq->buf, pq->buflen)) !=
+	    pq->buflen) {
+		syslog(LOG_DAEMON|LOG_WARNING, "recv() call failed: %d",
+		    errno);
+		exit(1);
+	}
+
+	/* Eat the first message from peer */
+	if (message_count++ == 0) {
+		*(pq->buf+pq->buflen) = 0;
+		free(pq->buf);
+		free(pq);
+		return;
+	}
+
+	/* Queue the message to the driver */
+	(void) pthread_mutex_lock(&pq_mutex);
+	if (pq_head == NULL) {
+		pq_head = pq;
+	} else {
+		/* add to the tail */
+		tmpq = pq_head;
+		while (tmpq->next != NULL)
+			tmpq = tmpq->next;
+		tmpq->next = pq;
+	}
+
+	/* Make sure there is a thread to service this message */
+	if (pq_avail_threads) {
+		/* wake an available thread */
+		(void) pthread_cond_signal(&pq_cond);
+		(void) pthread_mutex_unlock(&pq_mutex);
+	} else {
+		/* no threads available, create a new thread */
+		(void) pthread_mutex_unlock(&pq_mutex);
+		rc = pthread_create(&tid, NULL, push_to_drv, NULL);
+		if (rc != 0) {
+			syslog(LOG_DAEMON|LOG_WARNING,
+			    "pthread_create() call failed: %d", rc);
+			if (pq_num_threads == 0) {
+				/* never created a thread */
+				exit(rc);
+			}
+		}
+	}
+}
+
+/*
+ * Initialization for a daemon process
+ */
+static void
+daemonInit(void)
+{
+	pid_t	pid;
+	int	devnull;
+
+	if (fore_ground)
+		return;
+
+	if ((pid = fork()) < 0) {
+		syslog(LOG_DAEMON|LOG_CRIT, "Could not fork(). Exiting");
+		exit(1);
+	} else if (pid != 0) {
+		/*
+		 * XXX
+		 * Simple approach for now - let the service go online.
+		 * Later, set-up a pipe to the child and wait until the
+		 * child indicates service is setup.
+		 */
+		exit(SMF_EXIT_OK);
+	}
+
+	(void) setsid();
+
+	(void) chdir("/");
+
+	(void) umask(0);
+
+
+	devnull = open("/dev/null", O_RDWR);
+	if (devnull < 0) {
+		syslog(LOG_DAEMON|LOG_CRIT,
+		    "Failed to open /dev/null. Exiting");
+		exit(1);
+	}
+
+	(void) dup2(devnull, STDIN_FILENO);
+	(void) dup2(devnull, STDOUT_FILENO);
+	(void) dup2(devnull, STDERR_FILENO);
+	(void) close(devnull);
+}
+
+void
+daemon_fini(int rc)
+{
+	/*
+	 * XXX inform the parent about the service state
+	 * For now, just exit on error.
+	 */
+	if (rc != 0)
+		exit(rc);
+}
+
+static int
+open_proxy_driver()
+{
+	int drv_door_fd;
+	int stmf_ret;
+
+	/*
+	 * Create communication channel for the driver.
+	 */
+	if ((drv_door_fd = door_create(doorHandler, NULL, 0)) < 0) {
+		perror("door_create");
+		syslog(LOG_DAEMON|LOG_DEBUG,
+		    "could not create door: errno %d", errno);
+		return (SMF_EXIT_ERR_FATAL);
+	}
+
+	stmf_ret = stmfInitProxyDoor(&proxy_hdl, drv_door_fd);
+	if (stmf_ret != STMF_STATUS_SUCCESS) {
+		perror("pppt ioctl: door install");
+		syslog(LOG_DAEMON|LOG_DEBUG,
+		    "could not install door: errno %d", errno);
+		return (SMF_EXIT_ERR_FATAL);
+	}
+
+	return (SMF_EXIT_OK);
+}
+
+/*
+ * daemon entry
+ *
+ * parse arguments
+ * create resources to talk to child
+ * if !foreground
+ *    daemonize, run as child
+ * open proxy driver
+ * install door in proxy driver
+ * create socket
+ * if server-side
+ *     bind socket
+ * if !foreground
+ *    inform parent things aok
+ * if parent
+ *    exit(SMF_EXIT_OK)
+ * if server-side
+ *    accept
+ * if client-side
+ *    connect
+ * send hello
+ * recv hello
+ * loop on recieve
+ * XXX anyway to check in envp that we are started by SMF?
+ */
+int
+main(int argc, char *argv[])
+{
+	struct	sockaddr_in sin;
+	int	rc;
+	struct sigaction 	act;
+	sigset_t		sigmask;
+	int	c;
+	int	node = 0;
+	int	node_override = 0;
+	int	server_node = 0;
+	int	server_match = 0;
+	extern char *optarg;
+	int stmf_ret;
+
+	(void) setlocale(LC_ALL, "");
+	openlog("stmfproxy", LOG_PID, LOG_DAEMON);
+	(void) setlogmask(LOG_UPTO(LOG_INFO));
+
+	while ((c = getopt(argc, argv, "dfn:")) != -1) {
+		switch (c) {
+			case 'd':
+				(void) setlogmask(LOG_UPTO(LOG_DEBUG));
+				log_debug = 1;
+				break;
+			case 'f':
+				fore_ground = 1;
+				break;
+			case 'n':
+				node_override = 1;
+				node = atoi(optarg);
+				break;
+			default:
+				/*
+				 * Should never happen from smf
+				 */
+				(void) fprintf(stderr, USAGE, argv[0]);
+				exit(SMF_EXIT_ERR_CONFIG);
+				break;
+		}
+	}
+	/*
+	 * After the options, only the server argument should remain.
+	 */
+	if (optind != argc-1) {
+		(void) fprintf(stderr, USAGE, argv[0]);
+		exit(SMF_EXIT_ERR_CONFIG);
+	}
+	(void) strcpy(aluaNode, argv[optind]);
+	syslog(LOG_DAEMON|LOG_DEBUG, "aluaNode %s", aluaNode);
+	if (gethostname(myNode, 255)) {
+		perror("gethostname");
+		exit(1);
+	}
+	if ((inet_aton(aluaNode, &sin.sin_addr)) == 0) {
+		/*
+		 * Not ipaddr, try hostname match.
+		 */
+		server_match = (strcmp(aluaNode, myNode)) ? 0 : 1;
+	} else {
+		/*
+		 * see if this is our ip address
+		 */
+		(void) fprintf(stderr, "Sorry, cannot use ip adress format\n");
+	}
+	if (server_match) {
+		server_node = 1;
+		if (!node_override)
+			node = 1;
+	}
+
+
+	/*
+	 * Allow SIGQUIT, SIGINT and SIGTERM signals to terminate us
+	 */
+	act.sa_handler = killHandler;
+	(void) sigemptyset(&act.sa_mask);
+	act.sa_flags = 0;
+
+	/* Install the signal handler */
+	(void) sigaction(SIGQUIT, &act, NULL);
+	(void) sigaction(SIGINT, &act, NULL);
+	(void) sigaction(SIGTERM, &act, NULL);
+	(void) sigaction(SIGHUP, &act, NULL);
+
+	/* block all signals */
+	(void) sigfillset(&sigmask);
+
+	/* unblock SIGQUIT, SIGINT, SIGTERM */
+	(void) sigdelset(&sigmask, SIGQUIT);
+	(void) sigdelset(&sigmask, SIGINT);
+	(void) sigdelset(&sigmask, SIGTERM);
+	(void) sigdelset(&sigmask, SIGHUP);
+
+	(void) sigprocmask(SIG_SETMASK, &sigmask, NULL);
+
+	/* time to go backstage */
+	daemonInit();
+
+	if ((rc = open_proxy_driver()) != 0)
+		daemon_fini(rc);
+
+	if ((rc = stmf_proxy_transport_init("sockets", &pt_ops)) != 0)
+		daemon_fini(rc);
+
+	/*
+	 * Establish connection
+	 *
+	 * At this point, the parent has exited and the service
+	 * is online. But there are no real proxy services until
+	 * this connect call succeeds. That could take a long time if
+	 * the peer node is down.
+	 */
+	t_handle = pt_ops->stmf_proxy_connect(server_node, aluaNode);
+	if (t_handle == NULL) {
+		syslog(LOG_DAEMON|LOG_WARNING,
+		    "socket() call failed: %d", errno);
+		exit(1);
+	}
+
+	/* The first message is a greeting */
+	(void) postMsg((uint_t)strlen(myNode)+1, (uchar_t *)myNode);
+	/* Read the greeting from peer node */
+	relay_peer_msg();
+	/*
+	 * Set the alua state in stmf. No need to keep
+	 * the device open since the proxy driver has a reference.
+	 */
+	stmf_ret = stmfSetAluaState(B_TRUE, node);
+	if (stmf_ret != STMF_STATUS_SUCCESS) {
+		syslog(LOG_DAEMON|LOG_CRIT, "stmf ioctl failed - %x", stmf_ret);
+		exit(1);
+	}
+
+	/* service is online */
+	daemon_fini(0);
+
+	/*
+	 * Loop relaying data from the peer daemon to the local kernel.
+	 * Data coming from the local kernel is handled asynchronously
+	 * by the door server.
+	 */
+	for (;;) { /* loop forever */
+		relay_peer_msg();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stmfproxy/stmfproxy/stmfproxy.xml	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,112 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+
+<!--
+
+ 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ Use is subject to license terms.
+
+	Service manifests for the stmfproxy daemon
+-->
+
+<service_bundle type='manifest' name='SUNWstmfu:svc-stmfproxy'>
+
+<service
+    name='system/stmfproxy'
+    type='service'
+    version='1'>
+
+    <single_instance/>
+
+    <dependency
+        name='stmf'
+        grouping='require_all'
+        restart_on='none'
+        type='service'>
+        <service_fmri value='svc:/system/stmf' />
+    </dependency>
+
+	<!--
+	   Set a timeout of -1 to signify to inetd that we don't want
+	   to timeout this service, since the forked process is the
+	   one that does the services work. This is the case for most/all
+	   legacy inetd services; for services written to take advantage
+	   of Greenlines capabilities, the start method should fork
+	   off a process to handle the request and return a success code.
+	-->
+	<exec_method
+		type='method'
+		name='start'
+		exec='/usr/demo/comstar/bin/svc-stmfproxy %{config/proxy_host}'
+		timeout_seconds='600'>
+		<method_context>
+			<method_credential
+			user='root'
+			group='root'
+			privileges='basic,sys_devices'
+			/>
+		</method_context>
+	</exec_method>
+        
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':kill'
+		timeout_seconds='600'>
+		<method_context>
+			<method_credential
+			user='root'
+			group='root'
+			privileges='basic,sys_devices'
+			/>
+		</method_context>
+	</exec_method>
+
+        <property_group name='config' type='application' >
+                <stability value='Unstable' />
+                <propval name='proxy_host' type='astring'
+                        value='no:host:set' />
+                <propval name='proxy_port' type='integer'
+                        value='6543' />
+        </property_group>
+        
+	<!--
+	    Create an enabled instance.
+	-->
+	<instance
+        name='default'
+        enabled='false' >
+	</instance>
+
+	<stability value='Evolving' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+				stmfproxy daemon
+			</loctext>
+		</common_name>
+
+	</template>
+</service>
+
+</service_bundle>
--- a/usr/src/lib/Makefile	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/lib/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -156,6 +156,7 @@
 	libsmbfs	\
 	libfcoe		\
 	libstmf		\
+	libstmfproxy	\
 	libnsctl	\
 	libunistat	\
 	libdscfg	\
@@ -449,6 +450,7 @@
 	libsqlite	\
 	libfcoe		\
 	libstmf		\
+	libstmfproxy	\
 	libsum		\
 	libsysevent	\
 	libtecla	\
@@ -595,6 +597,7 @@
 libsip:		libmd5
 libsmbfs:	libsocket libnsl libkrb5
 libsocket:	libnsl
+libstmfproxy: 	libstmf libsocket libnsl libpthread
 libsum: 	libast
 libldap5:	libsasl libsocket libnsl libmd
 libsldap:	libldap5 libtsol libnsl libc libscf libresolv
--- a/usr/src/lib/libstmf/common/libstmf.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/lib/libstmf/common/libstmf.h	Tue Oct 06 19:56:15 2009 -0700
@@ -89,6 +89,10 @@
 #define	STMF_ERROR_INVALID_PROP		(STMF_STATUS_ERROR | 0x20)
 #define	STMF_ERROR_PERSIST_TYPE		(STMF_STATUS_ERROR | 0x21)
 #define	STMF_ERROR_TG_ONLINE		(STMF_STATUS_ERROR | 0x22)
+#define	STMF_ERROR_ACCESS_STATE_SET	(STMF_STATUS_ERROR | 0x23)
+#define	STMF_ERROR_NO_PROP_STANDBY	(STMF_STATUS_ERROR | 0x24)
+#define	STMF_ERROR_POST_MSG_FAILED	(STMF_STATUS_ERROR | 0x25)
+#define	STMF_ERROR_DOOR_INSTALLED	(STMF_STATUS_ERROR | 0x26)
 
 /* Failures for stmfCreateLu */
 #define	STMF_ERROR_FILE_IN_USE		(STMF_STATUS_ERROR | 0x100)
@@ -118,6 +122,12 @@
 #define	STMF_PERSIST_SMF	1
 #define	STMF_PERSIST_NONE	2
 
+/* Logical unit access states */
+#define	STMF_ACCESS_ACTIVE		"0"
+#define	STMF_ACCESS_ACTIVE_TO_STANDBY   "1"
+#define	STMF_ACCESS_STANDBY		"2"
+#define	STMF_ACCESS_STANDBY_TO_ACTIVE	"3"
+
 /*
  * LU Disk Properties
  */
@@ -136,7 +146,8 @@
 	STMF_LU_PROP_WRITE_CACHE_DISABLE,
 	STMF_LU_PROP_VID,
 	STMF_LU_PROP_PID,
-	STMF_LU_PROP_SERIAL_NUM
+	STMF_LU_PROP_SERIAL_NUM,
+	STMF_LU_PROP_ACCESS_STATE
 };
 
 
@@ -152,8 +163,12 @@
 typedef enum _stmfProtocol
 {
 	STMF_PROTOCOL_FIBRE_CHANNEL =	0,
-	STMF_PROTOCOL_ISCSI =		1,
-	STMF_PROTOCOL_SAS =		2
+	STMF_PROTOCOL_SCSI =		1,
+	STMF_PROTOCOL_SSA =		2,
+	STMF_PROTOCOL_IEEE_1394 =	3,
+	STMF_PROTOCOL_SRP =		4,
+	STMF_PROTOCOL_ISCSI =		5,
+	STMF_PROTOCOL_SAS =		6
 } stmfProtocol;
 
 
@@ -289,7 +304,6 @@
 	uchar_t	    rsvd[64];
 } stmfLocalPortProviderProperties;
 
-
 /* API prototypes */
 int stmfAddToHostGroup(stmfGroupName *hostGroupName, stmfDevid *name);
 int stmfAddToTargetGroup(stmfGroupName *targetGroupName, stmfDevid *targetName);
@@ -302,10 +316,12 @@
 int stmfDeleteHostGroup(stmfGroupName *hostGroupName);
 int stmfDeleteLu(stmfGuid *luGuid);
 int stmfDeleteTargetGroup(stmfGroupName *targetGroupName);
+void stmfDestroyProxyDoor(int hdl);
 int stmfDevidFromIscsiName(char *iscsiName, stmfDevid *devid);
 int stmfDevidFromWwn(uchar_t wwn[8], stmfDevid *devid);
 int stmfFreeLuResource(luResource hdl);
 void stmfFreeMemory(void *);
+int stmfGetAluaState(boolean_t *enabled, uint32_t *node);
 int stmfGetHostGroupList(stmfGroupList **initiatorGroupList);
 int stmfGetHostGroupMembers(stmfGroupName *hostGroupName,
     stmfGroupProperties **groupProperties);
@@ -335,7 +351,9 @@
     stmfTargetProperties *targetProps);
 int stmfGetViewEntryList(stmfGuid *lu, stmfViewEntryList **viewEntryList);
 int stmfImportLu(uint16_t dType, char *fname, stmfGuid *luGuid);
+int stmfInitProxyDoor(int *hdl, int fd);
 int stmfLoadConfig(void);
+int stmfLuStandby(stmfGuid *luGuid);
 int stmfModifyLu(stmfGuid *luGuid, uint32_t prop, const char *propVal);
 int stmfModifyLuByFname(uint16_t dType, const char *fname, uint32_t prop,
     const char *propVal);
@@ -345,11 +363,13 @@
 int stmfOnline(void);
 int stmfOnlineTarget(stmfDevid *devid);
 int stmfOnlineLogicalUnit(stmfGuid *logicalUnit);
+int stmfPostProxyMsg(int hdl, void *buf, uint32_t buflen);
 int stmfRemoveFromHostGroup(stmfGroupName *hostGroupName,
     stmfDevid *initiatorName);
 int stmfRemoveFromTargetGroup(stmfGroupName *targetGroupName,
     stmfDevid *targetName);
 int stmfRemoveViewEntry(stmfGuid *lu, uint32_t viewEntryIndex);
+int stmfSetAluaState(boolean_t enabled, uint32_t node);
 int stmfSetLuProp(luResource hdl, uint32_t propType, const char *propVal);
 int stmfSetPersistMethod(uint8_t persistType, boolean_t serviceSet);
 int stmfSetProviderData(char *providerName, nvlist_t *nvl, int providerType);
--- a/usr/src/lib/libstmf/common/libstmf_impl.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/lib/libstmf/common/libstmf_impl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -67,6 +67,7 @@
 	boolean_t   writeProtectEnable;
 	boolean_t   writebackCacheDisableValid;
 	boolean_t   writebackCacheDisable;
+	uint16_t    accessState;
 } diskResource;
 
 
--- a/usr/src/lib/libstmf/common/mapfile-vers	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/lib/libstmf/common/mapfile-vers	Tue Oct 06 19:56:15 2009 -0700
@@ -48,6 +48,7 @@
 	    stmfDeleteHostGroup;
 	    stmfDeleteLu;
 	    stmfDeleteTargetGroup;
+	    stmfDestroyProxyDoor;
 	    stmfDevidFromIscsiName;
 	    stmfDevidFromWwn;
 	    stmfFreeLuResource;
@@ -70,20 +71,25 @@
 	    stmfGetState;
 	    stmfGetViewEntryList;
 	    stmfImportLu;
+	    stmfInitProxyDoor;
+	    stmfLuStandby;
 	    stmfModifyLu;
 	    stmfModifyLuByFname;
 	    stmfOfflineTarget;
 	    stmfOfflineLogicalUnit;
 	    stmfOnlineTarget;
 	    stmfOnlineLogicalUnit;
+	    stmfPostProxyMsg;
 	    stmfRemoveFromHostGroup;
 	    stmfRemoveFromTargetGroup;
 	    stmfRemoveViewEntry;
+	    stmfSetAluaState;
+	    stmfGetAluaState;
 	    stmfSetPersistMethod;
 	    stmfSetProviderData;
 	    stmfSetProviderDataProt;
 	    stmfSetLuProp;
-	    stmfValidateView;
+            stmfValidateView;
 	local:
 		*;
 };
--- a/usr/src/lib/libstmf/common/stmf.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/lib/libstmf/common/stmf.c	Tue Oct 06 19:56:15 2009 -0700
@@ -47,9 +47,11 @@
 #include <libstmf_impl.h>
 #include <sys/stmf_ioctl.h>
 #include <sys/stmf_sbd_ioctl.h>
+#include <sys/pppt_ioctl.h>
 
 #define	STMF_PATH    "/devices/pseudo/stmf@0:admin"
 #define	SBD_PATH    "/devices/pseudo/stmf_sbd@0:admin"
+#define	PPPT_PATH    "/devices/pseudo/pppt@0:pppt"
 
 #define	EUI "eui."
 #define	WWN "wwn."
@@ -81,6 +83,9 @@
 #define	OPEN_SBD 0
 #define	OPEN_EXCL_SBD O_EXCL
 
+#define	OPEN_PPPT 0
+#define	OPEN_EXCL_PPPT O_EXCL
+
 #define	LOGICAL_UNIT_TYPE 0
 #define	TARGET_TYPE 1
 #define	STMF_SERVICE_TYPE 2
@@ -95,6 +100,7 @@
 
 static int openStmf(int, int *fd);
 static int openSbd(int, int *fd);
+static int openPppt(int, int *fd);
 static int groupIoctl(int fd, int cmd, stmfGroupName *);
 static int loadStore(int fd);
 static int initializeConfig();
@@ -129,7 +135,9 @@
 static int iLoadGroupFromPs(stmfGroupList **, int);
 static int groupMemberListIoctl(stmfGroupName *, stmfGroupProperties **, int);
 static int getProviderData(char *, nvlist_t **, int, uint64_t *);
+static int setDiskStandby(stmfGuid *luGuid);
 static int viewEntryCompare(const void *, const void *);
+static void deleteNonActiveLus();
 
 static pthread_mutex_t persistenceTypeLock = PTHREAD_MUTEX_INITIALIZER;
 static int iPersistType = 0;
@@ -167,7 +175,7 @@
 /*
  * Open for sbd module
  *
- * flag - open flag (OPEN_STMF, OPEN_EXCL_STMF)
+ * flag - open flag (OPEN_SBD, OPEN_EXCL_SBD)
  * fd - pointer to integer. On success, contains the stmf file descriptor
  */
 static int
@@ -193,6 +201,34 @@
 }
 
 /*
+ * Open for pppt module
+ *
+ * flag - open flag (OPEN_PPPT, OPEN_EXCL_PPPT)
+ * fd - pointer to integer. On success, contains the stmf file descriptor
+ */
+static int
+openPppt(int flag, int *fd)
+{
+	int ret = STMF_STATUS_ERROR;
+
+	if ((*fd = open(PPPT_PATH, O_RDONLY | flag)) != -1) {
+		ret = STMF_STATUS_SUCCESS;
+	} else {
+		if (errno == EBUSY) {
+			ret = STMF_ERROR_BUSY;
+		} else if (errno == EACCES) {
+			ret = STMF_ERROR_PERM;
+		} else {
+			ret = STMF_STATUS_ERROR;
+		}
+		syslog(LOG_DEBUG, "openPppt:open failure:%s:errno(%d)",
+		    PPPT_PATH, errno);
+	}
+
+	return (ret);
+}
+
+/*
  * initializeConfig
  *
  * This routine should be called before any ioctl requiring initialization
@@ -1459,6 +1495,9 @@
 		case SBD_RET_WRITE_CACHE_SET_FAILED:
 			*ret = STMF_ERROR_WRITE_CACHE_SET;
 			break;
+		case SBD_RET_ACCESS_STATE_FAILED:
+			*ret = STMF_ERROR_ACCESS_STATE_SET;
+			break;
 		default:
 			*ret = STMF_STATUS_ERROR;
 			break;
@@ -1578,6 +1617,88 @@
 }
 
 /*
+ * stmfLuStandby
+ *
+ * Purpose: Sets access state to standby
+ *
+ * luGuid - guid of registered logical unit
+ *
+ */
+int
+stmfLuStandby(stmfGuid *luGuid)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	stmfLogicalUnitProperties luProps;
+
+	if (luGuid == NULL) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	/* Check logical unit provider name to call correct dtype function */
+	if ((ret = stmfGetLogicalUnitProperties(luGuid, &luProps))
+	    != STMF_STATUS_SUCCESS) {
+		return (ret);
+	} else {
+		if (strcmp(luProps.providerName, "sbd") == 0) {
+			ret = setDiskStandby(luGuid);
+		} else if (luProps.status == STMF_LOGICAL_UNIT_UNREGISTERED) {
+			return (STMF_ERROR_NOT_FOUND);
+		} else {
+			return (STMF_ERROR_INVALID_ARG);
+		}
+	}
+
+	return (ret);
+}
+
+static int
+setDiskStandby(stmfGuid *luGuid)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	stmf_iocdata_t sbdIoctl = {0};
+	sbd_set_lu_standby_t sbdLu = {0};
+	int ioctlRet;
+	int savedErrno;
+	int fd = 0;
+
+	/*
+	 * Open control node for sbd
+	 */
+	if ((ret = openSbd(OPEN_SBD, &fd)) != STMF_STATUS_SUCCESS)
+		return (ret);
+
+	bcopy(luGuid, &sbdLu.stlu_guid, sizeof (stmfGuid));
+
+	sbdIoctl.stmf_version = STMF_VERSION_1;
+	sbdIoctl.stmf_ibuf_size = sizeof (sbd_set_lu_standby_t);
+	sbdIoctl.stmf_ibuf = (uint64_t)(unsigned long)&sbdLu;
+
+	ioctlRet = ioctl(fd, SBD_IOCTL_SET_LU_STANDBY, &sbdIoctl);
+	if (ioctlRet != 0) {
+		savedErrno = errno;
+		switch (savedErrno) {
+			case EBUSY:
+				ret = STMF_ERROR_BUSY;
+				break;
+			case EPERM:
+			case EACCES:
+				ret = STMF_ERROR_PERM;
+				break;
+			default:
+				diskError(sbdIoctl.stmf_error, &ret);
+				if (ret == STMF_STATUS_ERROR) {
+					syslog(LOG_DEBUG,
+					"setDiskStandby:ioctl "
+					"error(%d) (%d) (%d)", ioctlRet,
+					    sbdIoctl.stmf_error, savedErrno);
+				}
+				break;
+		}
+	}
+	return (ret);
+}
+
+/*
  * stmfModifyLu
  *
  * Purpose: Modify properties of a logical unit
@@ -1900,7 +2021,13 @@
 				}
 				newData = B_TRUE;
 			} else {
-				ret = stmfRet;
+				/*
+				 * if we're persisting the data, it's
+				 * an error. Otherwise, just return
+				 */
+				if (persist) {
+					ret = stmfRet;
+				}
 				goto done;
 			}
 		}
@@ -2065,6 +2192,7 @@
 	ret = createDiskResource((luResourceImpl *)*hdl);
 	if (ret != STMF_STATUS_SUCCESS) {
 		free(*hdl);
+		free(sbdProps);
 		(void) close(fd);
 		return (ret);
 	}
@@ -2104,6 +2232,7 @@
 		ret = loadDiskPropsFromDriver((luResourceImpl *)*hdl, sbdProps);
 	}
 
+	free(sbdProps);
 	(void) close(fd);
 	return (ret);
 }
@@ -2208,6 +2337,8 @@
 	diskLu->luSizeValid = B_TRUE;
 	diskLu->luSize = sbdProps->slp_lu_size;
 
+	diskLu->accessState = sbdProps->slp_access_state;
+
 	return (ret);
 }
 
@@ -2255,8 +2386,38 @@
 {
 	int ret = STMF_STATUS_SUCCESS;
 	diskResource *diskLu = hdl->resource;
+	char accessState[20];
 	size_t reqLen;
 
+	if (prop == STMF_LU_PROP_ACCESS_STATE) {
+		if (diskLu->accessState == SBD_LU_ACTIVE) {
+			(void) strlcpy(accessState, STMF_ACCESS_ACTIVE,
+			    sizeof (accessState));
+		} else if (diskLu->accessState == SBD_LU_TRANSITION_TO_ACTIVE) {
+			(void) strlcpy(accessState,
+			    STMF_ACCESS_STANDBY_TO_ACTIVE,
+			    sizeof (accessState));
+		} else if (diskLu->accessState == SBD_LU_STANDBY) {
+			(void) strlcpy(accessState, STMF_ACCESS_STANDBY,
+			    sizeof (accessState));
+		} else if (diskLu->accessState ==
+		    SBD_LU_TRANSITION_TO_STANDBY) {
+			(void) strlcpy(accessState,
+			    STMF_ACCESS_ACTIVE_TO_STANDBY,
+			    sizeof (accessState));
+		}
+		if ((reqLen = strlcpy(propVal, accessState,
+		    *propLen)) >= *propLen) {
+			*propLen = reqLen + 1;
+			return (STMF_ERROR_INVALID_ARG);
+		}
+		return (0);
+	}
+
+	if (diskLu->accessState != SBD_LU_ACTIVE) {
+		return (STMF_ERROR_NO_PROP_STANDBY);
+	}
+
 	switch (prop) {
 		case STMF_LU_PROP_BLOCK_SIZE:
 			if (diskLu->blkSizeValid == B_FALSE) {
@@ -2570,6 +2731,9 @@
 			}
 			diskLu->writebackCacheDisableValid = B_TRUE;
 			break;
+		case STMF_LU_PROP_ACCESS_STATE:
+			ret = STMF_ERROR_INVALID_PROP;
+			break;
 		default:
 			ret = STMF_ERROR_NO_PROP;
 			break;
@@ -3869,6 +4033,7 @@
 	int ioctlRet;
 	stmf_iocdata_t stmfIoctl;
 	sioc_target_port_props_t targetProperties;
+	scsi_devid_desc_t *scsiDevid;
 
 	if (devid == NULL || targetProps == NULL) {
 		return (STMF_ERROR_INVALID_ARG);
@@ -3936,6 +4101,10 @@
 	}
 	bcopy(targetProperties.tgt_alias, targetProps->alias,
 	    sizeof (targetProps->alias));
+
+	scsiDevid = (scsi_devid_desc_t *)&targetProperties.tgt_id;
+	targetProps->protocol = scsiDevid->protocol_id;
+
 done:
 	(void) close(fd);
 	return (ret);
@@ -4720,6 +4889,172 @@
 }
 
 /*
+ * stmfGetAluaState
+ *
+ * Purpose - Get the alua state
+ *
+ */
+int
+stmfGetAluaState(boolean_t *enabled, uint32_t *node)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	int fd;
+	stmf_iocdata_t stmfIoctl = {0};
+	stmf_alua_state_desc_t alua_state = {0};
+	int ioctlRet;
+
+	if (enabled == NULL || node == NULL) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	/*
+	 * Open control node for stmf
+	 */
+	if ((ret = openStmf(OPEN_STMF, &fd)) != STMF_STATUS_SUCCESS)
+		return (ret);
+
+	/*
+	 * Issue ioctl to get the stmf state
+	 */
+	stmfIoctl.stmf_version = STMF_VERSION_1;
+	stmfIoctl.stmf_obuf_size = sizeof (alua_state);
+	stmfIoctl.stmf_obuf = (uint64_t)(unsigned long)&alua_state;
+	ioctlRet = ioctl(fd, STMF_IOCTL_GET_ALUA_STATE, &stmfIoctl);
+
+	(void) close(fd);
+
+	if (ioctlRet != 0) {
+		switch (errno) {
+			case EBUSY:
+				ret = STMF_ERROR_BUSY;
+				break;
+			case EPERM:
+			case EACCES:
+				ret = STMF_ERROR_PERM;
+				break;
+			default:
+				syslog(LOG_DEBUG,
+				    "getStmfState:ioctl errno(%d)", errno);
+				ret = STMF_STATUS_ERROR;
+				break;
+		}
+	} else {
+		if (alua_state.alua_state == 1) {
+			*enabled = B_TRUE;
+		} else {
+			*enabled = B_FALSE;
+		}
+		*node = alua_state.alua_node;
+	}
+
+	return (ret);
+}
+
+/*
+ * stmfSetAluaState
+ *
+ * Purpose - set the alua state to enabled/disabled
+ *
+ */
+int
+stmfSetAluaState(boolean_t enabled, uint32_t node)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	int fd;
+	stmf_iocdata_t stmfIoctl = {0};
+	stmf_alua_state_desc_t alua_state = {0};
+	int ioctlRet;
+
+	if ((enabled != B_TRUE && enabled != B_FALSE) || (node > 1)) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	if (enabled) {
+		alua_state.alua_state = 1;
+	}
+
+	alua_state.alua_node = node;
+
+	/*
+	 * Open control node for stmf
+	 */
+	if ((ret = openStmf(OPEN_STMF, &fd)) != STMF_STATUS_SUCCESS)
+		return (ret);
+
+	/*
+	 * Issue ioctl to get the stmf state
+	 */
+	stmfIoctl.stmf_version = STMF_VERSION_1;
+	stmfIoctl.stmf_ibuf_size = sizeof (alua_state);
+	stmfIoctl.stmf_ibuf = (uint64_t)(unsigned long)&alua_state;
+	ioctlRet = ioctl(fd, STMF_IOCTL_SET_ALUA_STATE, &stmfIoctl);
+
+	(void) close(fd);
+
+	if (ioctlRet != 0) {
+		switch (errno) {
+			case EBUSY:
+				ret = STMF_ERROR_BUSY;
+				break;
+			case EPERM:
+			case EACCES:
+				ret = STMF_ERROR_PERM;
+				break;
+			default:
+				syslog(LOG_DEBUG,
+				    "getStmfState:ioctl errno(%d)", errno);
+				ret = STMF_STATUS_ERROR;
+				break;
+		}
+	}
+	if (ret == STMF_STATUS_SUCCESS) {
+		deleteNonActiveLus();
+	}
+
+	return (ret);
+}
+
+static void
+deleteNonActiveLus()
+{
+	int stmfRet;
+	int i;
+	stmfGuidList *luList;
+	luResource hdl = NULL;
+	char propVal[10];
+	size_t propValSize = sizeof (propVal);
+
+	stmfRet = stmfGetLogicalUnitList(&luList);
+	if (stmfRet != STMF_STATUS_SUCCESS) {
+		return;
+	}
+
+	for (i = 0; i < luList->cnt; i++) {
+		stmfRet = stmfGetLuResource(&luList->guid[i], &hdl);
+		if (stmfRet != STMF_STATUS_SUCCESS) {
+			goto err;
+		}
+		stmfRet = stmfGetLuProp(hdl, STMF_LU_PROP_ACCESS_STATE, propVal,
+		    &propValSize);
+		if (stmfRet != STMF_STATUS_SUCCESS) {
+			goto err;
+		}
+		if (propVal[0] == '0') {
+			(void) stmfFreeLuResource(hdl);
+			hdl = NULL;
+			continue;
+		}
+		(void) stmfDeleteLu(&luList->guid[i]);
+		(void) stmfFreeLuResource(hdl);
+		hdl = NULL;
+	}
+
+err:
+	stmfFreeMemory(luList);
+	(void) stmfFreeLuResource(hdl);
+}
+
+/*
  * stmfLoadConfig
  *
  * Purpose - load the configuration data from smf into stmf
@@ -5824,6 +6159,109 @@
 }
 
 /*
+ * stmfPostProxyMsg
+ *
+ * Purpose: Post a message to the proxy port provider
+ *
+ * buf - buffer containing message to post
+ * buflen - buffer length
+ */
+int
+stmfPostProxyMsg(int hdl, void *buf, uint32_t buflen)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	int ioctlRet;
+	pppt_iocdata_t ppptIoctl = {0};
+
+	if (buf == NULL) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	/*
+	 * Issue ioctl to post the message
+	 */
+	ppptIoctl.pppt_version = PPPT_VERSION_1;
+	ppptIoctl.pppt_buf_size = buflen;
+	ppptIoctl.pppt_buf = (uint64_t)(unsigned long)buf;
+	ioctlRet = ioctl(hdl, PPPT_MESSAGE, &ppptIoctl);
+	if (ioctlRet != 0) {
+		switch (errno) {
+			case EPERM:
+			case EACCES:
+				ret = STMF_ERROR_PERM;
+				break;
+			default:
+				ret = STMF_ERROR_POST_MSG_FAILED;
+				break;
+		}
+	}
+
+	return (ret);
+}
+
+/*
+ * stmfInitProxyDoor
+ *
+ * Purpose: Install door in proxy
+ *
+ * hdl - pointer to returned handle
+ * fd - door from door_create()
+ */
+int
+stmfInitProxyDoor(int *hdl, int door)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	int ioctlRet;
+	int fd;
+	pppt_iocdata_t ppptIoctl = {0};
+
+	if (hdl == NULL) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	/*
+	 * Open control node for pppt
+	 */
+	if ((ret = openPppt(OPEN_PPPT, &fd)) != STMF_STATUS_SUCCESS) {
+		return (ret);
+	}
+
+	/*
+	 * Issue ioctl to install the door
+	 */
+	ppptIoctl.pppt_version = PPPT_VERSION_1;
+	ppptIoctl.pppt_door_fd = (uint32_t)door;
+	ioctlRet = ioctl(fd, PPPT_INSTALL_DOOR, &ppptIoctl);
+	if (ioctlRet != 0) {
+		switch (errno) {
+			case EPERM:
+			case EACCES:
+				ret = STMF_ERROR_PERM;
+				break;
+			case EINVAL:
+				ret = STMF_ERROR_INVALID_ARG;
+				break;
+			case EBUSY:
+				ret = STMF_ERROR_DOOR_INSTALLED;
+				break;
+			default:
+				ret = STMF_STATUS_ERROR;
+				break;
+		}
+	}
+
+	/* return driver fd to caller */
+	*hdl = fd;
+	return (ret);
+}
+
+void
+stmfDestroyProxyDoor(int hdl)
+{
+	(void) close(hdl);
+}
+
+/*
  * validateLunNumIoctl
  *
  * Purpose: Issues ioctl to check and get available lun# in view entry
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,55 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all :=		TARGET= all
+clean :=	TARGET= clean
+clobber :=	TARGET= clobber
+install :=	TARGET= install
+install_h :=	TARGET= install_h
+lint :=		TARGET= lint
+
+
+HDRS=		libstmfproxy.h
+HDRDIR=         common
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/Makefile.com	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,52 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= libstmfproxy.a
+VERS= .1
+
+OBJECTS= stmftransport.o
+
+include ../../Makefile.lib
+
+LIBS=	$(DYNLIB) $(LINTLIB)
+
+SRCDIR =	../common
+
+INCS += -I$(SRCDIR)
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+LDLIBS +=	-lc -lsocket -lnsl
+CPPFLAGS +=	$(INCS) -D_REENTRANT
+
+$(LINTLIB) := SRCS=	$(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+#lint: lintcheck
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/amd64/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,30 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/common/libstmfproxy.h	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,50 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_LIBSTMFPROXY_H
+#define	_LIBSTMFPROXY_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+struct _pt_ops {
+	void *(*stmf_proxy_connect)(int server_node, char *server);
+	ssize_t (*stmf_proxy_send)(void *, void *, size_t);
+	ssize_t (*stmf_proxy_recv)(void *, void *, size_t);
+};
+
+typedef struct _pt_ops pt_ops_t;
+
+int
+stmf_proxy_transport_init(char *transport, pt_ops_t **pt_ops);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LIBSTMFPROXY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/common/llib-lstmfproxy	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,29 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libstmfproxy.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/common/mapfile-vers	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,50 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+SUNW_1.1 {
+	global:
+		stmf_proxy_transport_init;
+	local:
+		*;
+};
+
+SUNWprivate {
+	global:
+	local:
+	    *;
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/common/stmftransport.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,191 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <sys/sdt.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <libstmfproxy.h>
+
+/*
+ * NOTE:
+ * This is demo code to be used with the existing demo proxy daemon
+ * svc-stmfproxy in /usr/demo/comstar.
+ */
+
+struct _s_handle {
+	int	sockfd;
+};
+
+typedef struct _s_handle s_handle_t;
+
+static ssize_t
+pt_socket_recv(void *handle, void *buf, size_t len)
+{
+	s_handle_t *sh = handle;
+
+	return (recv(sh->sockfd, buf, len, MSG_WAITALL));
+}
+
+static ssize_t
+pt_socket_send(void *handle, void *buf, size_t len)
+{
+	s_handle_t *sh = handle;
+
+	return (send(sh->sockfd, buf, len, 0));
+}
+
+static void *
+pt_socket_connect(int server_node, char *server)
+{
+	int sfd, new_sfd;
+	s_handle_t *sh = NULL;
+	int on = 1;
+	struct sockaddr_in cli_addr, serv_addr;
+	struct	sockaddr_in sin;
+	int cliLen = sizeof (cli_addr);
+
+	if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
+		syslog(LOG_DAEMON|LOG_WARNING,
+		    "socket() call failed: %d", errno);
+		return (NULL);
+	}
+
+	if (server_node) {
+
+		if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on,
+		    sizeof (on)) < 0) {
+			syslog(LOG_DAEMON|LOG_WARNING,
+			    "setsockopt() failed: %d", errno);
+			goto serv_out;
+		}
+
+		bzero(&serv_addr, sizeof (serv_addr));
+		serv_addr.sin_family = AF_INET;
+		serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+		/* XXX get from smf? */
+		serv_addr.sin_port = htons(6543);
+
+		if (bind(sfd, (struct sockaddr *)&serv_addr,
+		    sizeof (serv_addr)) < 0) {
+			syslog(LOG_DAEMON|LOG_WARNING, "bind() call failed: %d",
+			    errno);
+			goto serv_out;
+		}
+
+		(void) listen(sfd, 5);
+
+		new_sfd = accept(sfd, (struct sockaddr *)&cli_addr, &cliLen);
+
+		if (new_sfd < 0) {
+			syslog(LOG_DAEMON|LOG_WARNING, "accept failed: %d",
+			    errno);
+			goto serv_out;
+		}
+		sh = malloc(sizeof (*sh));
+		sh->sockfd = new_sfd;
+serv_out:
+		close(sfd);
+	} else {
+		struct	hostent *hp;
+
+		/*
+		 * Assume IP dot notation or if that fails, gethostbyname()
+		 * If that fails, return
+		 */
+		if ((inet_aton(server, &sin.sin_addr)) == 0) {
+			if ((hp = gethostbyname(server)) != NULL) {
+				memcpy(&sin.sin_addr.s_addr, hp->h_addr,
+				    hp->h_length);
+			} else {
+				syslog(LOG_DAEMON|LOG_CRIT,
+				    "Cannot get IP address for %s", server);
+				(void) close(sfd);
+				return (NULL);
+			}
+		} else {
+			fprintf(stderr,
+			    "Sorry, cannot use ip address format\n");
+			(void) close(sfd);
+			return (NULL);
+		}
+		sin.sin_family = AF_INET;
+		/* XXX pass in from smf */
+		sin.sin_port = htons(6543);
+
+		while (connect(sfd, (struct sockaddr *)&sin,
+		    sizeof (sin)) < 0) {
+			close(sfd);
+			if (errno == ECONNREFUSED) {
+				/* get a fresh socket and retry */
+				sfd = socket(AF_INET, SOCK_STREAM, 0);
+				if (sfd < 0) {
+					syslog(LOG_DAEMON|LOG_WARNING,
+					    "socket() call failed: %d", errno);
+					return (NULL);
+				}
+				sleep(2);
+			} else {
+				syslog(LOG_DAEMON|LOG_CRIT,
+				    "Cannot connect %s - %d", server, errno);
+				return (NULL);
+			}
+		}
+		sh = malloc(sizeof (*sh));
+		sh->sockfd = sfd;
+	}
+	return (sh);
+}
+
+pt_ops_t pt_socket_ops = {
+	pt_socket_connect,
+	pt_socket_send,
+	pt_socket_recv
+};
+
+int
+stmf_proxy_transport_init(char *transport, pt_ops_t **pt_ops)
+{
+	if (strcmp(transport, "sockets") == 0) {
+		*pt_ops = &pt_socket_ops;
+		return (0);
+	} else {
+		return (-1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/i386/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,29 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/sparc/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,27 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libstmfproxy/sparcv9/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,30 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- a/usr/src/pkgdefs/SUNWstmf/postinstall	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmf/postinstall	Tue Oct 06 19:56:15 2009 -0700
@@ -84,4 +84,6 @@
 	driver_add qlt
 fi
 
+add_drv -n -b "${BASEDIR}" -m '* 0666 root sys' pppt > /dev/null 2>&1
+
 exit 0
--- a/usr/src/pkgdefs/SUNWstmf/preremove	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmf/preremove	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -48,6 +48,7 @@
 
 remove_drv qlt
 remove_drv fct
+remove_drv pppt
 remove_drv stmf_sbd
 echo 'stmf_allow_modunload/W 1' | /bin/mdb -kw >/dev/null 2>&1
 remove_drv stmf
--- a/usr/src/pkgdefs/SUNWstmf/prototype_com	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmf/prototype_com	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -46,6 +46,7 @@
 f none kernel/drv/qlt.conf 0644 root sys
 f none kernel/drv/stmf_sbd.conf 0644 root sys
 f none kernel/drv/stmf.conf 0644 root sys
+f none kernel/drv/pppt.conf 0644 root sys
 d none kernel/kmdb 755 root sys
 d none lib 755 root bin
 d none lib/svc 755 root bin
--- a/usr/src/pkgdefs/SUNWstmf/prototype_i386	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmf/prototype_i386	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -54,3 +54,5 @@
 f none kernel/drv/amd64/fct 0755 root sys
 f none kernel/drv/qlt 0755 root sys
 f none kernel/drv/amd64/qlt 0755 root sys
+f none kernel/drv/pppt 0755 root sys
+f none kernel/drv/amd64/pppt 0755 root sys
--- a/usr/src/pkgdefs/SUNWstmf/prototype_sparc	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmf/prototype_sparc	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -48,3 +48,4 @@
 f none kernel/drv/sparcv9/stmf 0755 root sys
 f none kernel/drv/sparcv9/fct 0755 root sys
 f none kernel/drv/sparcv9/qlt 0755 root sys
+f none kernel/drv/sparcv9/pppt 0755 root sys
--- a/usr/src/pkgdefs/SUNWstmfu/prototype_com	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmfu/prototype_com	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -44,10 +44,13 @@
 s none usr/lib/libstmf.so=libstmf.so.1
 f none usr/lib/llib-lstmf 644 root bin
 f none usr/lib/llib-lstmf.ln 644 root bin
+f none usr/lib/libstmfproxy.so.1 755 root bin
+s none usr/lib/libstmfproxy.so=libstmfproxy.so.1
 d none usr/lib/mdb 755 root sys
 d none usr/lib/mdb/kvm 755 root sys
 d none usr/include 755 root bin
 f none usr/include/libstmf.h 644 root bin
+f none usr/include/libstmfproxy.h 644 root bin
 d none usr/include/sys 755 root bin
 f none usr/include/sys/fct_defines.h 644 root bin
 f none usr/include/sys/fctio.h 644 root bin
@@ -57,3 +60,9 @@
 f none usr/include/sys/portif.h 644 root bin
 f none usr/include/sys/stmf_defines.h 644 root bin
 f none usr/include/sys/stmf_ioctl.h 644 root bin
+d none usr/demo 755 root bin
+d none usr/demo/comstar 755 root bin
+f none usr/demo/comstar/stmfproxy.xml 444 root bin
+d none usr/demo/comstar/bin 755 root bin
+f none usr/demo/comstar/bin/svc-stmfproxy 555 root bin
+f none usr/demo/comstar/bin/aluaadm 555 root bin
--- a/usr/src/pkgdefs/SUNWstmfu/prototype_i386	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmfu/prototype_i386	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -49,3 +49,5 @@
 f none usr/lib/amd64/libstmf.so.1 755 root bin
 s none usr/lib/amd64/libstmf.so=libstmf.so.1
 f none usr/lib/amd64/llib-lstmf.ln 644 root bin
+f none usr/lib/amd64/libstmfproxy.so.1 755 root bin
+s none usr/lib/amd64/libstmfproxy.so=libstmfproxy.so.1
--- a/usr/src/pkgdefs/SUNWstmfu/prototype_sparc	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/SUNWstmfu/prototype_sparc	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -47,3 +47,5 @@
 f none usr/lib/sparcv9/libstmf.so.1 755 root bin
 s none usr/lib/sparcv9/libstmf.so=libstmf.so.1
 f none usr/lib/sparcv9/llib-lstmf.ln 644 root bin
+f none usr/lib/sparcv9/libstmfproxy.so.1 755 root bin
+s none usr/lib/sparcv9/libstmfproxy.so=libstmfproxy.so.1
--- a/usr/src/pkgdefs/etc/exception_list_i386	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Tue Oct 06 19:56:15 2009 -0700
@@ -1242,6 +1242,17 @@
 usr/include/sys/stmf_sbd_ioctl.h	i386
 
 #
+# proxy port provider interface
+#
+usr/include/sys/pppt_ic_if.h		i386
+usr/include/sys/pppt_ioctl.h		i386
+
+# proxy daemon lint library
+usr/lib/llib-lstmfproxy			i386
+usr/lib/llib-lstmfproxy.ln		i386
+usr/lib/amd64/llib-lstmfproxy.ln	i386
+
+#
 # portable object file and dictionary used by libfmd_msg test
 #
 usr/lib/fm/dict/TEST.dict		i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Tue Oct 06 19:56:15 2009 -0700
@@ -1336,6 +1336,17 @@
 usr/include/sys/stmf_sbd_ioctl.h	sparc
 
 #
+# proxy port provider interface
+#
+usr/include/sys/pppt_ic_if.h		sparc
+usr/include/sys/pppt_ioctl.h		sparc
+
+# proxy daemon lint library
+usr/lib/llib-lstmfproxy			sparc
+usr/lib/llib-lstmfproxy.ln		sparc
+usr/lib/sparcv9/llib-lstmfproxy.ln	sparc
+
+#
 # portable object file and dictionary used by libfmd_msg test
 #
 usr/lib/fm/dict/TEST.dict		sparc
--- a/usr/src/uts/common/Makefile.files	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/Makefile.files	Tue Oct 06 19:56:15 2009 -0700
@@ -922,6 +922,8 @@
 		iscsit_text.o iscsit_isns.o iscsit_radiusauth.o \
 		iscsit_radiuspacket.o iscsit_auth.o iscsit_authclient.o
 
+PPPT_OBJS +=	alua_ic_if.o pppt.o pppt_msg.o pppt_tgt.o
+
 STMF_OBJS += lun_map.o stmf.o
 
 STMF_SBD_OBJS += sbd.o sbd_scsi.o sbd_pgr.o
--- a/usr/src/uts/common/Makefile.rules	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/Makefile.rules	Tue Oct 06 19:56:15 2009 -0700
@@ -664,6 +664,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/comstar/port/pppt/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/comstar/lu/stmf_sbd/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1924,6 +1928,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/comstar/port/iscsit/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/comstar/port/pppt/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/comstar/stmf/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd.c	Tue Oct 06 19:56:15 2009 -0700
@@ -51,6 +51,7 @@
 
 extern sbd_status_t sbd_pgr_meta_init(sbd_lu_t *sl);
 extern sbd_status_t sbd_pgr_meta_load(sbd_lu_t *sl);
+extern void sbd_pgr_reset(sbd_lu_t *sl);
 
 static int sbd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
     void **result);
@@ -61,10 +62,19 @@
 static int stmf_sbd_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
     cred_t *credp, int *rval);
 void sbd_lp_cb(stmf_lu_provider_t *lp, int cmd, void *arg, uint32_t flags);
+stmf_status_t sbd_proxy_reg_lu(uint8_t *luid, void *proxy_reg_arg,
+    uint32_t proxy_reg_arg_len);
+stmf_status_t sbd_proxy_dereg_lu(uint8_t *luid, void *proxy_reg_arg,
+    uint32_t proxy_reg_arg_len);
+stmf_status_t sbd_proxy_msg(uint8_t *luid, void *proxy_arg,
+    uint32_t proxy_arg_len, uint32_t type);
 int sbd_create_register_lu(sbd_create_and_reg_lu_t *slu, int struct_sz,
     uint32_t *err_ret);
+int sbd_create_standby_lu(sbd_create_standby_lu_t *slu, uint32_t *err_ret);
+int sbd_set_lu_standby(sbd_set_lu_standby_t *stlu, uint32_t *err_ret);
 int sbd_import_lu(sbd_import_lu_t *ilu, int struct_sz, uint32_t *err_ret,
     int no_register, sbd_lu_t **slr);
+int sbd_import_active_lu(sbd_import_lu_t *ilu, sbd_lu_t *sl, uint32_t *err_ret);
 int sbd_delete_lu(sbd_delete_lu_t *dlu, int struct_sz, uint32_t *err_ret);
 int sbd_modify_lu(sbd_modify_lu_t *mlu, int struct_sz, uint32_t *err_ret);
 int sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
@@ -80,6 +90,7 @@
 int sbd_zvolget(char *zvol_name, char **comstarprop);
 int sbd_zvolset(char *zvol_name, char *comstarprop);
 char sbd_ctoi(char c);
+void sbd_close_lu(sbd_lu_t *sl);
 
 static ldi_ident_t	sbd_zfs_ident;
 static stmf_lu_provider_t *sbd_lp;
@@ -151,10 +162,12 @@
 		return (ret);
 	sbd_lp = (stmf_lu_provider_t *)stmf_alloc(STMF_STRUCT_LU_PROVIDER,
 	    0, 0);
-	sbd_lp->lp_lpif_rev = LPIF_REV_1;
+	sbd_lp->lp_lpif_rev = LPIF_REV_2;
 	sbd_lp->lp_instance = 0;
 	sbd_lp->lp_name = sbd_name;
 	sbd_lp->lp_cb = sbd_lp_cb;
+	sbd_lp->lp_alua_support = 1;
+	sbd_lp->lp_proxy_msg = sbd_proxy_msg;
 	sbd_zfs_ident = ldi_ident_from_anon();
 
 	if (stmf_register_lu_provider(sbd_lp) != STMF_SUCCESS) {
@@ -320,6 +333,18 @@
 		    ibuf, iocd->stmf_ibuf_size, &iocd->stmf_error);
 		bcopy(ibuf, obuf, iocd->stmf_obuf_size);
 		break;
+	case SBD_IOCTL_SET_LU_STANDBY:
+		if (iocd->stmf_ibuf_size < sizeof (sbd_set_lu_standby_t)) {
+			ret = EFAULT;
+			break;
+		}
+		if (iocd->stmf_obuf_size) {
+			ret = EINVAL;
+			break;
+		}
+		ret = sbd_set_lu_standby((sbd_set_lu_standby_t *)ibuf,
+		    &iocd->stmf_error);
+		break;
 	case SBD_IOCTL_IMPORT_LU:
 		if (iocd->stmf_ibuf_size <
 		    (sizeof (sbd_import_lu_t) - 8)) {
@@ -592,9 +617,24 @@
 	io_buf = (uint8_t *)kmem_zalloc(io_size, KM_SLEEP);
 	ASSERT((starting_off + io_size) <= sl->sl_total_meta_size);
 
+	/*
+	 * Don't proceed if the device has been closed
+	 * This can occur on an access state change to standby or
+	 * a delete. The writer lock is acquired before closing the
+	 * lu. If importing, reading the metadata is valid, hence
+	 * the check on SL_OP_IMPORT_LU.
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_READER);
+	if ((sl->sl_flags & SL_MEDIA_LOADED) == 0 &&
+	    sl->sl_trans_op != SL_OP_IMPORT_LU) {
+		rw_exit(&sl->sl_access_state_lock);
+		ret = SBD_FILEIO_FAILURE;
+		goto sbd_read_meta_failure;
+	}
 	if (sl->sl_flags & SL_ZFS_META) {
 		if ((ret = sbd_read_zfs_meta(sl, io_buf, io_size,
 		    starting_off)) != SBD_SUCCESS) {
+			rw_exit(&sl->sl_access_state_lock);
 			goto sbd_read_meta_failure;
 		}
 	} else {
@@ -604,9 +644,11 @@
 
 		if (vret || resid) {
 			ret = SBD_FILEIO_FAILURE | vret;
+			rw_exit(&sl->sl_access_state_lock);
 			goto sbd_read_meta_failure;
 		}
 	}
+	rw_exit(&sl->sl_access_state_lock);
 
 	bcopy(io_buf + data_off, buf, size);
 	ret = SBD_SUCCESS;
@@ -652,9 +694,24 @@
 		goto sbd_write_meta_failure;
 	}
 	bcopy(buf, io_buf + data_off, size);
+	/*
+	 * Don't proceed if the device has been closed
+	 * This can occur on an access state change to standby or
+	 * a delete. The writer lock is acquired before closing the
+	 * lu. If importing, reading the metadata is valid, hence
+	 * the check on SL_OP_IMPORT_LU.
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_READER);
+	if ((sl->sl_flags & SL_MEDIA_LOADED) == 0 &&
+	    sl->sl_trans_op != SL_OP_IMPORT_LU) {
+		rw_exit(&sl->sl_access_state_lock);
+		ret = SBD_FILEIO_FAILURE;
+		goto sbd_write_meta_failure;
+	}
 	if (sl->sl_flags & SL_ZFS_META) {
 		if ((ret = sbd_write_zfs_meta(sl, io_buf, io_size,
 		    starting_off)) != SBD_SUCCESS) {
+			rw_exit(&sl->sl_access_state_lock);
 			goto sbd_write_meta_failure;
 		}
 	} else {
@@ -664,9 +721,11 @@
 
 		if (vret || resid) {
 			ret = SBD_FILEIO_FAILURE | vret;
+			rw_exit(&sl->sl_access_state_lock);
 			goto sbd_write_meta_failure;
 		}
 	}
+	rw_exit(&sl->sl_access_state_lock);
 
 	ret = SBD_SUCCESS;
 
@@ -1287,6 +1346,22 @@
 	} else {
 		lu->lu_alias = sl->sl_name;
 	}
+	if (sl->sl_access_state == SBD_LU_STANDBY) {
+		/* call set access state */
+		ret = stmf_set_lu_access(lu, STMF_LU_STANDBY);
+		if (ret != STMF_SUCCESS) {
+			*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+			return (EIO);
+		}
+	}
+	/* set proxy_reg_cb_arg to meta filename */
+	if (sl->sl_meta_filename) {
+		lu->lu_proxy_reg_arg = sl->sl_meta_filename;
+		lu->lu_proxy_reg_arg_len = strlen(sl->sl_meta_filename) + 1;
+	} else {
+		lu->lu_proxy_reg_arg = sl->sl_data_filename;
+		lu->lu_proxy_reg_arg_len = strlen(sl->sl_data_filename) + 1;
+	}
 	lu->lu_lp = sbd_lp;
 	lu->lu_task_alloc = sbd_task_alloc;
 	lu->lu_new_task = sbd_new_task;
@@ -1434,8 +1509,8 @@
 	return (ret);
 }
 
-int
-sbd_close_delete_lu(sbd_lu_t *sl, int ret)
+void
+sbd_close_lu(sbd_lu_t *sl)
 {
 	int flag;
 
@@ -1445,6 +1520,7 @@
 			rw_destroy(&sl->sl_zfs_meta_lock);
 			if (sl->sl_zfs_meta) {
 				kmem_free(sl->sl_zfs_meta, ZAP_MAXVALUELEN / 2);
+				sl->sl_zfs_meta = NULL;
 			}
 		} else {
 			flag = FREAD | FWRITE | FOFFMAX | FEXCL;
@@ -1467,11 +1543,68 @@
 			sl->sl_flags &= ~SL_META_OPENED;
 		}
 	}
+}
+
+int
+sbd_set_lu_standby(sbd_set_lu_standby_t *stlu, uint32_t *err_ret)
+{
+	sbd_lu_t *sl;
+	sbd_status_t sret;
+	stmf_status_t stret;
+
+	sret = sbd_find_and_lock_lu(stlu->stlu_guid, NULL,
+	    SL_OP_MODIFY_LU, &sl);
+	if (sret != SBD_SUCCESS) {
+		if (sret == SBD_BUSY) {
+			*err_ret = SBD_RET_LU_BUSY;
+			return (EBUSY);
+		} else if (sret == SBD_NOT_FOUND) {
+			*err_ret = SBD_RET_NOT_FOUND;
+			return (ENOENT);
+		}
+		*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+		return (EIO);
+	}
+
+	sl->sl_access_state = SBD_LU_TRANSITION_TO_STANDBY;
+	stret = stmf_set_lu_access((stmf_lu_t *)sl->sl_lu, STMF_LU_STANDBY);
+	if (stret != STMF_SUCCESS) {
+		sl->sl_trans_op = SL_OP_NONE;
+		*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+		sl->sl_access_state = SBD_LU_TRANSITION_TO_STANDBY;
+		return (EIO);
+	}
+
+	/*
+	 * acquire the writer lock here to ensure we're not pulling
+	 * the rug from the vn_rdwr to the backing store
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_WRITER);
+	sbd_close_lu(sl);
+	rw_exit(&sl->sl_access_state_lock);
+
+	sl->sl_trans_op = SL_OP_NONE;
+	return (0);
+}
+
+int
+sbd_close_delete_lu(sbd_lu_t *sl, int ret)
+{
+
+	/*
+	 * acquire the writer lock here to ensure we're not pulling
+	 * the rug from the vn_rdwr to the backing store
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_WRITER);
+	sbd_close_lu(sl);
+	rw_exit(&sl->sl_access_state_lock);
+
 	if (sl->sl_flags & SL_LINKED)
 		sbd_unlink_lu(sl);
 	mutex_destroy(&sl->sl_metadata_lock);
 	mutex_destroy(&sl->sl_lock);
 	rw_destroy(&sl->sl_pgr->pgr_lock);
+	rw_destroy(&sl->sl_access_state_lock);
 	if (sl->sl_serial_no_alloc_size) {
 		kmem_free(sl->sl_serial_no, sl->sl_serial_no_alloc_size);
 	}
@@ -1553,11 +1686,13 @@
 	rw_init(&sl->sl_pgr->pgr_lock, NULL, RW_DRIVER, NULL);
 	mutex_init(&sl->sl_lock, NULL, MUTEX_DRIVER, NULL);
 	mutex_init(&sl->sl_metadata_lock, NULL, MUTEX_DRIVER, NULL);
+	rw_init(&sl->sl_access_state_lock, NULL, RW_DRIVER, NULL);
 	p = ((char *)sl) + sizeof (sbd_lu_t) + sizeof (sbd_pgr_t);
 	sl->sl_data_filename = p;
 	(void) strcpy(sl->sl_data_filename, namebuf + slu->slu_data_fname_off);
 	p += strlen(sl->sl_data_filename) + 1;
 	sl->sl_meta_offset = SBD_META_OFFSET;
+	sl->sl_access_state = SBD_LU_ACTIVE;
 	if (slu->slu_meta_fname_valid) {
 		sl->sl_alias = sl->sl_name = sl->sl_meta_filename = p;
 		(void) strcpy(sl->sl_meta_filename, namebuf +
@@ -1778,6 +1913,180 @@
 	return (sbd_close_delete_lu(sl, ret));
 }
 
+stmf_status_t
+sbd_proxy_msg(uint8_t *luid, void *proxy_arg, uint32_t proxy_arg_len,
+    uint32_t type)
+{
+	switch (type) {
+		case STMF_MSG_LU_ACTIVE:
+			return (sbd_proxy_reg_lu(luid, proxy_arg,
+			    proxy_arg_len));
+		case STMF_MSG_LU_REGISTER:
+			return (sbd_proxy_reg_lu(luid, proxy_arg,
+			    proxy_arg_len));
+		case STMF_MSG_LU_DEREGISTER:
+			return (sbd_proxy_dereg_lu(luid, proxy_arg,
+			    proxy_arg_len));
+		default:
+			return (STMF_INVALID_ARG);
+	}
+}
+
+
+/*
+ * register a standby logical unit
+ * proxy_reg_arg contains the meta filename
+ */
+stmf_status_t
+sbd_proxy_reg_lu(uint8_t *luid, void *proxy_reg_arg, uint32_t proxy_reg_arg_len)
+{
+	sbd_lu_t *sl;
+	sbd_status_t sret;
+	sbd_create_standby_lu_t *stlu;
+	int alloc_sz;
+	uint32_t err_ret = 0;
+	stmf_status_t stret = STMF_SUCCESS;
+
+	if (luid == NULL) {
+		return (STMF_INVALID_ARG);
+	}
+
+	do {
+		sret = sbd_find_and_lock_lu(luid, NULL, SL_OP_MODIFY_LU, &sl);
+	} while (sret == SBD_BUSY);
+
+	if (sret == SBD_NOT_FOUND) {
+		alloc_sz = sizeof (*stlu) + proxy_reg_arg_len - 8;
+		stlu = (sbd_create_standby_lu_t *)kmem_zalloc(alloc_sz,
+		    KM_SLEEP);
+		bcopy(luid, stlu->stlu_guid, 16);
+		if (proxy_reg_arg_len) {
+			bcopy(proxy_reg_arg, stlu->stlu_meta_fname,
+			    proxy_reg_arg_len);
+			stlu->stlu_meta_fname_size = proxy_reg_arg_len;
+		}
+		if (sbd_create_standby_lu(stlu, &err_ret) != 0) {
+			cmn_err(CE_WARN,
+			    "Unable to create standby logical unit for %s",
+			    stlu->stlu_meta_fname);
+			stret = STMF_FAILURE;
+		}
+		kmem_free(stlu, alloc_sz);
+		return (stret);
+	} else if (sret == SBD_SUCCESS) {
+		/*
+		 * if the lu is already registered, then the lu should now
+		 * be in standby mode
+		 */
+		sbd_it_data_t *it;
+		if (sl->sl_access_state != SBD_LU_STANDBY) {
+			mutex_enter(&sl->sl_lock);
+			sl->sl_access_state = SBD_LU_STANDBY;
+			for (it = sl->sl_it_list; it != NULL;
+			    it = it->sbd_it_next) {
+				it->sbd_it_ua_conditions |=
+				    SBD_UA_ASYMMETRIC_ACCESS_CHANGED;
+				it->sbd_it_flags &=
+				    ~SBD_IT_HAS_SCSI2_RESERVATION;
+				sl->sl_flags &= ~SL_LU_HAS_SCSI2_RESERVATION;
+			}
+			mutex_exit(&sl->sl_lock);
+			sbd_pgr_reset(sl);
+		}
+		sl->sl_trans_op = SL_OP_NONE;
+	} else {
+		cmn_err(CE_WARN, "could not find and lock logical unit");
+		stret = STMF_FAILURE;
+	}
+out:
+	return (stret);
+}
+
+/* ARGSUSED */
+stmf_status_t
+sbd_proxy_dereg_lu(uint8_t *luid, void *proxy_reg_arg,
+    uint32_t proxy_reg_arg_len)
+{
+	sbd_delete_lu_t dlu = {0};
+	uint32_t err_ret;
+
+	if (luid == NULL) {
+		cmn_err(CE_WARN, "de-register lu request had null luid");
+		return (STMF_INVALID_ARG);
+	}
+
+	bcopy(luid, &dlu.dlu_guid, 16);
+
+	if (sbd_delete_lu(&dlu, (int)sizeof (dlu), &err_ret) != 0) {
+		cmn_err(CE_WARN, "failed to delete de-register lu request");
+		return (STMF_FAILURE);
+	}
+
+	return (STMF_SUCCESS);
+}
+
+int
+sbd_create_standby_lu(sbd_create_standby_lu_t *slu, uint32_t *err_ret)
+{
+	sbd_lu_t *sl;
+	stmf_lu_t *lu;
+	int ret = EIO;
+	int alloc_sz;
+
+	alloc_sz = sizeof (sbd_lu_t) + sizeof (sbd_pgr_t) +
+	    slu->stlu_meta_fname_size;
+	lu = (stmf_lu_t *)stmf_alloc(STMF_STRUCT_STMF_LU, alloc_sz, 0);
+	if (lu == NULL) {
+		return (ENOMEM);
+	}
+	sl = (sbd_lu_t *)lu->lu_provider_private;
+	bzero(sl, alloc_sz);
+	sl->sl_lu = lu;
+	sl->sl_alloc_size = alloc_sz;
+
+	sl->sl_pgr = (sbd_pgr_t *)(sl + 1);
+	sl->sl_meta_filename = ((char *)sl) + sizeof (sbd_lu_t) +
+	    sizeof (sbd_pgr_t);
+
+	if (slu->stlu_meta_fname_size > 0) {
+		(void) strcpy(sl->sl_meta_filename, slu->stlu_meta_fname);
+	}
+	sl->sl_name = sl->sl_meta_filename;
+
+	sl->sl_device_id[3] = 16;
+	sl->sl_device_id[0] = 0xf1;
+	sl->sl_device_id[1] = 3;
+	sl->sl_device_id[2] = 0;
+	bcopy(slu->stlu_guid, sl->sl_device_id + 4, 16);
+	lu->lu_id = (scsi_devid_desc_t *)sl->sl_device_id;
+	sl->sl_access_state = SBD_LU_STANDBY;
+
+	rw_init(&sl->sl_pgr->pgr_lock, NULL, RW_DRIVER, NULL);
+	mutex_init(&sl->sl_lock, NULL, MUTEX_DRIVER, NULL);
+	mutex_init(&sl->sl_metadata_lock, NULL, MUTEX_DRIVER, NULL);
+	rw_init(&sl->sl_access_state_lock, NULL, RW_DRIVER, NULL);
+
+	sl->sl_trans_op = SL_OP_CREATE_REGISTER_LU;
+
+	if (sbd_link_lu(sl) != SBD_SUCCESS) {
+		*err_ret = SBD_RET_FILE_ALREADY_REGISTERED;
+		ret = EALREADY;
+		goto scs_err_out;
+	}
+
+	ret = sbd_populate_and_register_lu(sl, err_ret);
+	if (ret) {
+		goto scs_err_out;
+	}
+
+	sl->sl_trans_op = SL_OP_NONE;
+	atomic_add_32(&sbd_lu_count, 1);
+	return (0);
+
+scs_err_out:
+	return (sbd_close_delete_lu(sl, ret));
+}
+
 int
 sbd_load_sli_1_0(sbd_lu_t *sl, uint32_t *err_ret)
 {
@@ -1820,36 +2129,88 @@
 	sbd_lu_info_1_1_t *sli = NULL;
 	int asz;
 	int ret = 0;
+	stmf_status_t stret;
 	int flag;
 	int wcd = 0;
 	int data_opened;
 	uint16_t sli_buf_sz;
 	uint8_t *sli_buf_copy = NULL;
 	enum vtype vt;
+	int standby = 0;
 	sbd_status_t sret;
 
 	if (no_register && slr == NULL) {
 		return (EINVAL);
 	}
 	ilu->ilu_meta_fname[struct_sz - sizeof (*ilu) + 8 - 1] = 0;
-	asz = strlen(ilu->ilu_meta_fname) + 1;
+	/*
+	 * check whether logical unit is already registered ALUA
+	 * For a standby logical unit, the meta filename is set. Use
+	 * that to search for an existing logical unit.
+	 */
+	sret = sbd_find_and_lock_lu(NULL, (uint8_t *)&(ilu->ilu_meta_fname),
+	    SL_OP_IMPORT_LU, &sl);
 
-	lu = (stmf_lu_t *)stmf_alloc(STMF_STRUCT_STMF_LU,
-	    sizeof (sbd_lu_t) + sizeof (sbd_pgr_t) + asz, 0);
-	if (lu == NULL) {
-		return (ENOMEM);
+	if (sret == SBD_SUCCESS) {
+		if (sl->sl_access_state != SBD_LU_ACTIVE) {
+			no_register = 1;
+			standby = 1;
+			lu = sl->sl_lu;
+			if (sl->sl_alias_alloc_size) {
+				kmem_free(sl->sl_alias,
+				    sl->sl_alias_alloc_size);
+				sl->sl_alias_alloc_size = 0;
+				sl->sl_alias = NULL;
+				lu->lu_alias = NULL;
+			}
+			if (sl->sl_meta_filename == NULL) {
+				sl->sl_meta_filename = sl->sl_data_filename;
+			} else if (sl->sl_data_fname_alloc_size) {
+				kmem_free(sl->sl_data_filename,
+				    sl->sl_data_fname_alloc_size);
+				sl->sl_data_fname_alloc_size = 0;
+			}
+			if (sl->sl_serial_no_alloc_size) {
+				kmem_free(sl->sl_serial_no,
+				    sl->sl_serial_no_alloc_size);
+				sl->sl_serial_no_alloc_size = 0;
+			}
+			if (sl->sl_mgmt_url_alloc_size) {
+				kmem_free(sl->sl_mgmt_url,
+				    sl->sl_mgmt_url_alloc_size);
+				sl->sl_mgmt_url_alloc_size = 0;
+			}
+		} else {
+			*err_ret = SBD_RET_FILE_ALREADY_REGISTERED;
+			sl->sl_trans_op = SL_OP_NONE;
+			return (EALREADY);
+		}
+	} else if (sret == SBD_NOT_FOUND) {
+		asz = strlen(ilu->ilu_meta_fname) + 1;
+
+		lu = (stmf_lu_t *)stmf_alloc(STMF_STRUCT_STMF_LU,
+		    sizeof (sbd_lu_t) + sizeof (sbd_pgr_t) + asz, 0);
+		if (lu == NULL) {
+			return (ENOMEM);
+		}
+		sl = (sbd_lu_t *)lu->lu_provider_private;
+		bzero(sl, sizeof (*sl));
+		sl->sl_lu = lu;
+		sl->sl_pgr = (sbd_pgr_t *)(sl + 1);
+		sl->sl_meta_filename = ((char *)sl) + sizeof (*sl) +
+		    sizeof (sbd_pgr_t);
+		(void) strcpy(sl->sl_meta_filename, ilu->ilu_meta_fname);
+		sl->sl_name = sl->sl_meta_filename;
+		rw_init(&sl->sl_pgr->pgr_lock, NULL, RW_DRIVER, NULL);
+		rw_init(&sl->sl_access_state_lock, NULL, RW_DRIVER, NULL);
+		mutex_init(&sl->sl_lock, NULL, MUTEX_DRIVER, NULL);
+		mutex_init(&sl->sl_metadata_lock, NULL, MUTEX_DRIVER, NULL);
+		sl->sl_trans_op = SL_OP_IMPORT_LU;
+	} else {
+		*err_ret = SBD_RET_META_FILE_LOOKUP_FAILED;
+		return (EIO);
 	}
-	sl = (sbd_lu_t *)lu->lu_provider_private;
-	bzero(sl, sizeof (*sl));
-	sl->sl_lu = lu;
-	sl->sl_pgr = (sbd_pgr_t *)(sl + 1);
-	sl->sl_meta_filename = ((char *)sl) + sizeof (*sl) + sizeof (sbd_pgr_t);
-	(void) strcpy(sl->sl_meta_filename, ilu->ilu_meta_fname);
-	sl->sl_name = sl->sl_meta_filename;
-	rw_init(&sl->sl_pgr->pgr_lock, NULL, RW_DRIVER, NULL);
-	mutex_init(&sl->sl_lock, NULL, MUTEX_DRIVER, NULL);
-	mutex_init(&sl->sl_metadata_lock, NULL, MUTEX_DRIVER, NULL);
-	sl->sl_trans_op = SL_OP_IMPORT_LU;
+
 	/* we're only loading the metadata */
 	if (!no_register) {
 		if (sbd_link_lu(sl) != SBD_SUCCESS) {
@@ -1917,8 +2278,9 @@
 	    SMS_ID_LU_INFO_1_1);
 	if ((sret == SBD_NOT_FOUND) && ((sl->sl_flags & SL_ZFS_META) == 0)) {
 		ret = sbd_load_sli_1_0(sl, err_ret);
-		if (ret)
+		if (ret) {
 			goto sim_err_out;
+		}
 		goto sim_sli_loaded;
 	}
 	if (sret != SBD_SUCCESS) {
@@ -2040,8 +2402,9 @@
 	}
 
 	ret = sbd_open_data_file(sl, err_ret, 1, data_opened, 0);
-	if (ret)
+	if (ret) {
 		goto sim_err_out;
+	}
 
 	/*
 	 * set write cache disable on the device
@@ -2071,13 +2434,15 @@
 	/* we're only loading the metadata */
 	if (!no_register) {
 		ret = sbd_populate_and_register_lu(sl, err_ret);
-		if (ret)
+		if (ret) {
 			goto sim_err_out;
+		}
 		atomic_add_32(&sbd_lu_count, 1);
 	}
 
 	bcopy(sl->sl_device_id + 4, ilu->ilu_ret_guid, 16);
 	sl->sl_trans_op = SL_OP_NONE;
+
 	if (sli) {
 		kmem_free(sli, sli->sli_sms_header.sms_size);
 		sli = NULL;
@@ -2086,9 +2451,39 @@
 		kmem_free(sli_buf_copy, sli_buf_sz + 1);
 		sli_buf_copy = NULL;
 	}
-	if (no_register) {
+	if (no_register && !standby) {
 		*slr = sl;
 	}
+
+	/*
+	 * if this was imported from standby, set the access state
+	 * to active.
+	 */
+	if (standby) {
+		sbd_it_data_t *it;
+		mutex_enter(&sl->sl_lock);
+		sl->sl_access_state = SBD_LU_ACTIVE;
+		for (it = sl->sl_it_list; it != NULL;
+		    it = it->sbd_it_next) {
+			it->sbd_it_ua_conditions |=
+			    SBD_UA_ASYMMETRIC_ACCESS_CHANGED;
+			it->sbd_it_ua_conditions |= SBD_UA_POR;
+		}
+		mutex_exit(&sl->sl_lock);
+		/* call set access state */
+		stret = stmf_set_lu_access(lu, STMF_LU_ACTIVE);
+		if (stret != STMF_SUCCESS) {
+			*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+			sl->sl_access_state = SBD_LU_STANDBY;
+			goto sim_err_out;
+		}
+		if (sl->sl_alias) {
+			lu->lu_alias = sl->sl_alias;
+		} else {
+			lu->lu_alias = sl->sl_name;
+		}
+	}
+	sl->sl_access_state = SBD_LU_ACTIVE;
 	return (0);
 
 sim_err_out:
@@ -2100,7 +2495,14 @@
 		kmem_free(sli_buf_copy, sli_buf_sz + 1);
 		sli_buf_copy = NULL;
 	}
-	return (sbd_close_delete_lu(sl, ret));
+
+	if (standby) {
+		*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+		sl->sl_trans_op = SL_OP_NONE;
+		return (EIO);
+	} else {
+		return (sbd_close_delete_lu(sl, ret));
+	}
 }
 
 int
@@ -2180,6 +2582,12 @@
 		modify_unregistered = 1;
 	}
 
+	if (sl->sl_access_state != SBD_LU_ACTIVE) {
+		*err_ret = SBD_RET_ACCESS_STATE_FAILED;
+		ret = EINVAL;
+		goto smm_err_out;
+	}
+
 	/* check for write cache change */
 	if (mlu->mlu_writeback_cache_disable_valid) {
 		/* set wce on device */
@@ -2435,9 +2843,21 @@
 	DTRACE_PROBE4(backing__store__read__start, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset);
 
+	/*
+	 * Don't proceed if the device has been closed
+	 * This can occur on an access state change to standby or
+	 * a delete. The writer lock is acquired before closing the
+	 * lu.
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_READER);
+	if ((sl->sl_flags & SL_MEDIA_LOADED) == 0) {
+		rw_exit(&sl->sl_access_state_lock);
+		return (SBD_FAILURE);
+	}
 	ret = vn_rdwr(UIO_READ, sl->sl_data_vp, (caddr_t)buf, (ssize_t)size,
 	    (offset_t)offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, CRED(),
 	    &resid);
+	rw_exit(&sl->sl_access_state_lock);
 
 	DTRACE_PROBE5(backing__store__read__end, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
@@ -2477,9 +2897,21 @@
 	DTRACE_PROBE4(backing__store__write__start, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset);
 
+	/*
+	 * Don't proceed if the device has been closed
+	 * This can occur on an access state change to standby or
+	 * a delete. The writer lock is acquired before closing the
+	 * lu.
+	 */
+	rw_enter(&sl->sl_access_state_lock, RW_READER);
+	if ((sl->sl_flags & SL_MEDIA_LOADED) == 0) {
+		rw_exit(&sl->sl_access_state_lock);
+		return (SBD_FAILURE);
+	}
 	ret = vn_rdwr(UIO_WRITE, sl->sl_data_vp, (caddr_t)buf, (ssize_t)size,
 	    (offset_t)offset, UIO_SYSSPACE, ioflag, RLIM64_INFINITY, CRED(),
 	    &resid);
+	rw_exit(&sl->sl_access_state_lock);
 
 	DTRACE_PROBE5(backing__store__write__end, sbd_lu_t *, sl,
 	    uint8_t *, buf, uint64_t, size, uint64_t, offset,
@@ -2605,6 +3037,8 @@
 	oslp->slp_lu_size = sl->sl_lu_size;
 	oslp->slp_blksize = ((uint16_t)1) << sl->sl_data_blocksize_shift;
 
+	oslp->slp_access_state = sl->sl_access_state;
+
 	if (sl->sl_flags & SL_VID_VALID) {
 		oslp->slp_lu_vid = 1;
 		bcopy(sl->sl_vendor_id, oslp->slp_vid, 8);
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_impl.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_impl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -230,15 +230,24 @@
 	sbd_pgr_key_t		*pgr_key_ptr;
 } sbd_it_data_t;
 
+typedef struct sbd_create_standby_lu {
+	uint32_t	stlu_meta_fname_size;
+	uint32_t	stlu_rsvd;
+	uint8_t		stlu_guid[16];
+	char		stlu_meta_fname[8];
+} sbd_create_standby_lu_t;
+
 /*
  * Different UA conditions
  */
-#define	SBD_UA_POR			0x01
-#define	SBD_UA_CAPACITY_CHANGED		0x02
-#define	SBD_UA_MODE_PARAMETERS_CHANGED	0x04
-#define	SBD_UA_REGISTRATIONS_PREEMPTED	0x10
-#define	SBD_UA_RESERVATIONS_PREEMPTED	0x20
-#define	SBD_UA_RESERVATIONS_RELEASED	0x40
+#define	SBD_UA_POR			    0x01
+#define	SBD_UA_CAPACITY_CHANGED		    0x02
+#define	SBD_UA_MODE_PARAMETERS_CHANGED	    0x04
+#define	SBD_UA_ACCESS_STATE_TRANSITION	    0x08
+#define	SBD_UA_REGISTRATIONS_PREEMPTED	    0x10
+#define	SBD_UA_RESERVATIONS_PREEMPTED	    0x20
+#define	SBD_UA_RESERVATIONS_RELEASED	    0x40
+#define	SBD_UA_ASYMMETRIC_ACCESS_CHANGED    0x80
 
 /*
  * sbd_it_flags
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_pgr.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_pgr.c	Tue Oct 06 19:56:15 2009 -0700
@@ -40,6 +40,7 @@
 #define	MAX_PGR_PARAM_LIST_LENGTH	(256 * 1024)
 
 int  sbd_pgr_reservation_conflict(scsi_task_t *);
+void sbd_pgr_reset(sbd_lu_t *);
 void sbd_pgr_initialize_it(scsi_task_t *);
 void sbd_handle_pgr_in_cmd(scsi_task_t *, stmf_data_buf_t *);
 void sbd_handle_pgr_out_cmd(scsi_task_t *, stmf_data_buf_t *);
@@ -533,6 +534,25 @@
 	}
 }
 
+/*
+ * Reset and clear the keys, Can be used in the case of Lun Reset
+ */
+void
+sbd_pgr_reset(sbd_lu_t *slu)
+{
+	sbd_pgr_t	*pgr  = slu->sl_pgr;
+
+	rw_enter(&pgr->pgr_lock, RW_WRITER);
+	if (!(pgr->pgr_flags & SBD_PGR_APTPL)) {
+		sbd_pgr_keylist_dealloc(slu);
+		pgr->pgr_PRgeneration	= 0;
+		pgr->pgr_rsvholder	= NULL;
+		pgr->pgr_rsv_type	= 0;
+		pgr->pgr_flags		= 0;
+	}
+	rw_exit(&pgr->pgr_lock);
+}
+
 static void
 sbd_pgr_remove_key(sbd_lu_t *slu, sbd_pgr_key_t *key)
 {
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c	Tue Oct 06 19:56:15 2009 -0700
@@ -101,6 +101,7 @@
 
 extern void sbd_pgr_initialize_it(scsi_task_t *);
 extern int sbd_pgr_reservation_conflict(scsi_task_t *);
+extern void sbd_pgr_reset(sbd_lu_t *);
 extern void sbd_pgr_remove_it_handle(sbd_lu_t *, sbd_it_data_t *);
 extern void sbd_handle_pgr_in_cmd(scsi_task_t *, stmf_data_buf_t *);
 extern void sbd_handle_pgr_out_cmd(scsi_task_t *, stmf_data_buf_t *);
@@ -696,6 +697,8 @@
     stmf_data_buf_t *dbuf)
 {
 	sbd_cmd_t *scmd;
+	stmf_status_t st_ret;
+	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
 
 	/*
 	 * For now lets assume we will get only one sglist element
@@ -717,11 +720,27 @@
 	switch (task->task_cdb[0]) {
 	case SCMD_MODE_SELECT:
 	case SCMD_MODE_SELECT_G1:
-		sbd_handle_mode_select_xfer(task,
-		    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
+		if (sl->sl_access_state == SBD_LU_STANDBY) {
+			st_ret = stmf_proxy_scsi_cmd(task, dbuf);
+			if (st_ret != STMF_SUCCESS) {
+				stmf_scsilib_send_status(task, STATUS_CHECK,
+				    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
+			}
+		} else {
+			sbd_handle_mode_select_xfer(task,
+			    dbuf->db_sglist[0].seg_addr, dbuf->db_data_size);
+		}
 		break;
 	case SCMD_PERSISTENT_RESERVE_OUT:
-		sbd_handle_pgr_out_data(task, dbuf);
+		if (sl->sl_access_state == SBD_LU_STANDBY) {
+			st_ret = stmf_proxy_scsi_cmd(task, dbuf);
+			if (st_ret != STMF_SUCCESS) {
+				stmf_scsilib_send_status(task, STATUS_CHECK,
+				    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
+			}
+		} else {
+			sbd_handle_pgr_out_data(task, dbuf);
+		}
 		break;
 	default:
 		/* This should never happen */
@@ -1458,6 +1477,7 @@
 	sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private;
 	sbd_it_data_t *it;
 	uint8_t cdb0, cdb1;
+	stmf_status_t st_ret;
 
 	if ((it = task->task_lu_itl_handle) == NULL) {
 		mutex_enter(&sl->sl_lock);
@@ -1492,7 +1512,9 @@
 			return;
 		}
 		task->task_lu_itl_handle = it;
-		it->sbd_it_ua_conditions = SBD_UA_POR;
+		if (sl->sl_access_state != SBD_LU_STANDBY) {
+			it->sbd_it_ua_conditions = SBD_UA_POR;
+		}
 	} else if (it->sbd_it_flags & SBD_IT_PGR_CHECK_FLAG) {
 		sbd_pgr_initialize_it(task);
 		mutex_enter(&sl->sl_lock);
@@ -1505,6 +1527,17 @@
 		return;
 	}
 
+	/*
+	 * if we're transitioning between access
+	 * states, return NOT READY
+	 */
+	if (sl->sl_access_state == SBD_LU_TRANSITION_TO_STANDBY ||
+	    sl->sl_access_state == SBD_LU_TRANSITION_TO_ACTIVE) {
+		stmf_scsilib_send_status(task, STATUS_CHECK,
+		    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
+		return;
+	}
+
 	/* Checking ua conditions as per SAM3R14 5.3.2 specified order */
 	if ((it->sbd_it_ua_conditions) && (task->task_cdb[0] != SCMD_INQUIRY)) {
 		uint32_t saa = 0;
@@ -1522,18 +1555,20 @@
 	}
 
 	/* Reservation conflict checks */
-	if (SBD_PGR_RSVD(sl->sl_pgr)) {
-		if (sbd_pgr_reservation_conflict(task)) {
-			stmf_scsilib_send_status(task,
-			    STATUS_RESERVATION_CONFLICT, 0);
-			return;
-		}
-	} else if ((sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) &&
-	    ((it->sbd_it_flags & SBD_IT_HAS_SCSI2_RESERVATION) == 0)) {
-		if (!(SCSI2_CONFLICT_FREE_CMDS(task->task_cdb))) {
-			stmf_scsilib_send_status(task,
-			    STATUS_RESERVATION_CONFLICT, 0);
-			return;
+	if (sl->sl_access_state != SBD_LU_STANDBY) {
+		if (SBD_PGR_RSVD(sl->sl_pgr)) {
+			if (sbd_pgr_reservation_conflict(task)) {
+				stmf_scsilib_send_status(task,
+				    STATUS_RESERVATION_CONFLICT, 0);
+				return;
+			}
+		} else if ((sl->sl_flags & SL_LU_HAS_SCSI2_RESERVATION) &&
+		    ((it->sbd_it_flags & SBD_IT_HAS_SCSI2_RESERVATION) == 0)) {
+			if (!(SCSI2_CONFLICT_FREE_CMDS(task->task_cdb))) {
+				stmf_scsilib_send_status(task,
+				    STATUS_RESERVATION_CONFLICT, 0);
+				return;
+			}
 		}
 	}
 
@@ -1557,6 +1592,16 @@
 			it->sbd_it_ua_conditions &=
 			    ~SBD_UA_MODE_PARAMETERS_CHANGED;
 			saa = STMF_SAA_MODE_PARAMETERS_CHANGED;
+		} else if (it->sbd_it_ua_conditions &
+		    SBD_UA_ASYMMETRIC_ACCESS_CHANGED) {
+			it->sbd_it_ua_conditions &=
+			    ~SBD_UA_ASYMMETRIC_ACCESS_CHANGED;
+			saa = STMF_SAA_ASYMMETRIC_ACCESS_CHANGED;
+		} else if (it->sbd_it_ua_conditions &
+		    SBD_UA_ACCESS_STATE_TRANSITION) {
+			it->sbd_it_ua_conditions &=
+			    ~SBD_UA_ACCESS_STATE_TRANSITION;
+			saa = STMF_SAA_LU_NO_ACCESS_TRANSITION;
 		} else {
 			it->sbd_it_ua_conditions = 0;
 			saa = 0;
@@ -1568,6 +1613,49 @@
 		}
 	}
 
+	cdb0 = task->task_cdb[0];
+	cdb1 = task->task_cdb[1];
+
+	if (sl->sl_access_state == SBD_LU_STANDBY) {
+		if (cdb0 != SCMD_INQUIRY &&
+		    cdb0 != SCMD_MODE_SENSE &&
+		    cdb0 != SCMD_MODE_SENSE_G1 &&
+		    cdb0 != SCMD_MODE_SELECT &&
+		    cdb0 != SCMD_MODE_SELECT_G1 &&
+		    cdb0 != SCMD_RESERVE &&
+		    cdb0 != SCMD_RELEASE &&
+		    cdb0 != SCMD_PERSISTENT_RESERVE_OUT &&
+		    cdb0 != SCMD_PERSISTENT_RESERVE_IN &&
+		    cdb0 != SCMD_REQUEST_SENSE &&
+		    !(cdb0 == SCMD_MAINTENANCE_IN &&
+		    (cdb1 & 0x1F) == 0x0A)) {
+			stmf_scsilib_send_status(task, STATUS_CHECK,
+			    STMF_SAA_LU_NO_ACCESS_STANDBY);
+			return;
+		}
+
+		/*
+		 * is this a short write?
+		 * if so, we'll need to wait until we have the buffer
+		 * before proxying the command
+		 */
+		switch (cdb0) {
+			case SCMD_MODE_SELECT:
+			case SCMD_MODE_SELECT_G1:
+			case SCMD_PERSISTENT_RESERVE_OUT:
+				break;
+			default:
+				st_ret = stmf_proxy_scsi_cmd(task,
+				    initial_dbuf);
+				if (st_ret != STMF_SUCCESS) {
+					stmf_scsilib_send_status(task,
+					    STATUS_CHECK,
+					    STMF_SAA_LU_NO_ACCESS_UNAVAIL);
+				}
+				return;
+		}
+	}
+
 	cdb0 = task->task_cdb[0] & 0x1F;
 
 	if ((cdb0 == SCMD_READ) || (cdb0 == SCMD_WRITE)) {
@@ -1886,6 +1974,7 @@
 			    SL_LU_HAS_SCSI2_RESERVATION);
 			sl->sl_state = STMF_STATE_OFFLINE;
 			sl->sl_state_not_acked = 1;
+			sbd_pgr_reset(sl);
 		}
 		(void) stmf_ctl(STMF_CMD_LU_OFFLINE_COMPLETE, lu, &st);
 		break;
@@ -1916,12 +2005,17 @@
 	if (sl->sl_flags & SL_SAVED_WRITE_CACHE_DISABLE) {
 		sl->sl_flags |= SL_WRITEBACK_CACHE_DISABLE;
 		mutex_exit(&sl->sl_lock);
-		(void) sbd_wcd_set(1, sl);
+		if (sl->sl_access_state == SBD_LU_ACTIVE) {
+			(void) sbd_wcd_set(1, sl);
+		}
 	} else {
 		sl->sl_flags &= ~SL_WRITEBACK_CACHE_DISABLE;
 		mutex_exit(&sl->sl_lock);
-		(void) sbd_wcd_set(0, sl);
+		if (sl->sl_access_state == SBD_LU_ACTIVE) {
+			(void) sbd_wcd_set(0, sl);
+		}
 	}
+	sbd_pgr_reset(sl);
 	sbd_check_and_clear_scsi2_reservation(sl, NULL);
 	if (stmf_deregister_all_lu_itl_handles(lu) != STMF_SUCCESS) {
 		return (STMF_FAILURE);
--- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/stmf_sbd.h	Tue Oct 06 19:56:15 2009 -0700
@@ -190,6 +190,7 @@
 
 	/* Metadata */
 	kmutex_t	sl_metadata_lock;
+	krwlock_t	sl_access_state_lock;
 	char		*sl_alias;
 	char		*sl_meta_filename;	/* If applicable */
 	char		*sl_mgmt_url;
@@ -210,6 +211,7 @@
 	uint16_t	sl_alias_alloc_size;
 	uint16_t	sl_mgmt_url_alloc_size;
 	uint8_t		sl_serial_no_alloc_size;
+	uint8_t		sl_access_state;
 	uint64_t	sl_meta_offset;
 
 	/* zfs metadata */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/alua_ic_if.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,2171 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * XXX TODO
+ * #includes cribbed from stmf.c -- undoubtedly only a small subset of these
+ * are actually needed.
+ */
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/scsi/scsi.h>
+#include <sys/byteorder.h>
+#include <sys/nvpair.h>
+#include <sys/door.h>
+
+#include <sys/stmf.h>
+#include <sys/lpif.h>
+#include <sys/stmf_ioctl.h>
+#include <sys/portif.h>
+#include <pppt.h>
+#include <sys/pppt_ic_if.h>
+
+
+/*
+ * Macros
+ */
+
+/* Free a struct if it was allocated */
+#define	FREE_IF_ALLOC(m)					\
+	do {							\
+		if ((m)) kmem_free((m), sizeof (*(m)));		\
+		_NOTE(CONSTCOND)				\
+	} while (0)
+
+/*
+ * Macros to simplify the addition of struct fields to an nvlist.
+ * The name of the fields in the nvlist is the same as the name
+ * of the struct field.
+ *
+ * These macros require an int rc and a "done:" return retval label;
+ * they assume that the nvlist is named "nvl".
+ */
+#define	NVLIST_ADD_FIELD(type, structure, field)			\
+	do {								\
+		rc = nvlist_add_##type(nvl, #field, structure->field);  \
+		if (rc) goto done;					\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+/* use this macro when the array is defined as part of the struct */
+#define	NVLIST_ADD_ARRAY(type, structure, field)			\
+	do {								\
+		rc = nvlist_add_##type##_array(nvl, #field,		\
+		    structure->field, sizeof (structure->field));	\
+		if (rc) goto done;					\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+/*
+ * use this macro when the array field is a ptr or you need to explictly
+ * call out the size.
+ */
+#define	NVLIST_ADD_ARRAY_LEN(type, structure, field, len)		\
+	do {								\
+		rc = nvlist_add_##type##_array(nvl, #field,		\
+		    structure->field, len);				\
+		if (rc) goto done;					\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+#define	NVLIST_ADD_DEVID(structure, field)				\
+	do {								\
+		rc = stmf_ic_scsi_devid_desc_marshal(nvl, #field,	\
+		    structure->field);					\
+		if (rc) goto done;					\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+#define	NVLIST_ADD_FIELD_UINT8(structure, field)			\
+	NVLIST_ADD_FIELD(structure, field, uint8)
+
+/*
+ * Macros to simplify the extraction of struct fields from an nvlist.
+ * The name of the fields in the nvlist is the same as the name
+ * of the struct field.
+ *
+ * Requires an int rc and a "done:" return retval label.
+ * Assumes that the nvlist is named "nvl".
+ *
+ * Sample usage: NVLIST_LOOKUP_FIELD(uint8, structname, fieldname);
+ */
+#define	NVLIST_LOOKUP_FIELD(type, structure, field)			\
+	do {								\
+		rc = nvlist_lookup_##type(nvl, #field,			\
+		    &(structure->field));				\
+		if (rc) { 						\
+			stmf_ic_nvlookup_warn(__func__, #field);	\
+			goto done;					\
+		}							\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+/*
+ * Look up a field which gets stored into a structure bit field.
+ * The type passed is a uint type which can hold the largest value
+ * in the bit field.
+ *
+ * Requires an int rc and a "done:" return retval label.
+ * Assumes that the nvlist is named "nvl".
+ *
+ * Sample usage: NVLIST_LOOKUP_BIT_FIELD(uint8, structname, fieldname);
+ */
+#define	NVLIST_LOOKUP_BIT_FIELD(type, structure, field)			\
+	do {								\
+		type##_t tmp;						\
+		rc = nvlist_lookup_##type(nvl, #field, &tmp);		\
+		if (rc) { 						\
+			stmf_ic_nvlookup_warn(__func__, #field);	\
+			goto done;					\
+		}							\
+		structure->field = tmp;					\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+/*
+ * Look up a boolean field which gets stored into a structure bit field.
+ *
+ * Requires an int rc and a "done:" return retval label.
+ * Assumes that the nvlist is named "nvl".
+ */
+#define	NVLIST_LOOKUP_BIT_FIELD_BOOLEAN(structure, field)		\
+	do {								\
+		boolean_t tmp;						\
+		rc = nvlist_lookup_boolean_value(nvl, #field, &tmp);	\
+		if (rc) { 						\
+			stmf_ic_nvlookup_warn(__func__, #field);	\
+			goto done;					\
+		}							\
+		structure->field = (tmp ?  1 : 0);			\
+		_NOTE(CONSTCOND)					\
+	} while (0)
+
+/* shorthand  for nvlist_lookup_pairs() args */
+#define	NV_PAIR(type, strct, field) #field, DATA_TYPE_##type, &(strct->field)
+
+/* number of times to retry the upcall to transmit */
+#define	STMF_MSG_TRANSMIT_RETRY	    3
+
+/*
+ * How was the message constructed?
+ *
+ * We need to know this when we free the message in order to
+ * determine what to do with pointers in the message:
+ *
+ * - messages which were unmarshaled from an nvlist may point to
+ *   memory within that nvlist; this memory should not be freed since
+ *   it will be deallocated when we free the nvlist.
+ *
+ * - messages which built using a constructor (alloc) function may
+ *   point to memory which was explicitly allocated by the constructor;
+ *   it should be freed when the message is freed.
+ *
+ */
+typedef enum {
+	STMF_CONSTRUCTOR = 0,
+	STMF_UNMARSHAL
+} stmf_ic_msg_construction_method_t;
+
+
+/*
+ * Function prototypes.
+ */
+
+/*
+ * Helpers for msg_alloc routines, used when the msg payload is
+ * the same for multiple types of messages.
+ */
+static stmf_ic_msg_t *stmf_ic_reg_dereg_lun_msg_alloc(
+    stmf_ic_msg_type_t msg_type, uint8_t *lun_id,
+    char *lu_provider_name, uint16_t cb_arg_len,
+    uint8_t *cb_arg, stmf_ic_msgid_t msgid);
+
+static stmf_ic_msg_t *stmf_ic_session_create_destroy_msg_alloc(
+    stmf_ic_msg_type_t msg_type,
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid);
+
+static stmf_ic_msg_t *stmf_ic_echo_request_reply_msg_alloc(
+    stmf_ic_msg_type_t msg_type,
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid);
+
+/*
+ * Msg free routines.
+ */
+static void stmf_ic_reg_port_msg_free(stmf_ic_reg_port_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_dereg_port_msg_free(stmf_ic_dereg_port_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_reg_dereg_lun_msg_free(stmf_ic_reg_dereg_lun_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_scsi_cmd_msg_free(stmf_ic_scsi_cmd_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_scsi_data_msg_free(stmf_ic_scsi_data_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_scsi_data_xfer_done_msg_free(
+    stmf_ic_scsi_data_xfer_done_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_scsi_status_msg_free(stmf_ic_scsi_status_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_r2t_msg_free(stmf_ic_r2t_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_status_msg_free(stmf_ic_status_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_session_create_destroy_msg_free(
+    stmf_ic_session_create_destroy_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+static void stmf_ic_echo_request_reply_msg_free(
+    stmf_ic_echo_request_reply_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod);
+
+/*
+ * Marshaling routines.
+ */
+static nvlist_t *stmf_ic_msg_marshal(stmf_ic_msg_t *msg);
+static int stmf_ic_reg_port_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_dereg_port_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_reg_dereg_lun_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_scsi_cmd_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_scsi_data_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_scsi_data_xfer_done_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_scsi_status_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_r2t_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_status_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_session_create_destroy_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_echo_request_reply_msg_marshal(nvlist_t *nvl, void *msg);
+static int stmf_ic_scsi_devid_desc_marshal(nvlist_t *parent_nvl,
+	char *sdid_name, scsi_devid_desc_t *sdid);
+
+/*
+ * Unmarshaling routines.
+ */
+static stmf_ic_msg_t *stmf_ic_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_reg_port_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_dereg_port_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_reg_dereg_lun_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_scsi_cmd_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_scsi_data_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_scsi_data_xfer_done_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_scsi_status_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_r2t_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_status_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_session_create_destroy_msg_unmarshal(nvlist_t *nvl);
+static void *stmf_ic_echo_request_reply_msg_unmarshal(nvlist_t *nvl);
+static scsi_devid_desc_t *stmf_ic_lookup_scsi_devid_desc_and_unmarshal(
+    nvlist_t *nvl, char *field_name);
+static scsi_devid_desc_t *stmf_ic_scsi_devid_desc_unmarshal(
+    nvlist_t *nvl_devid);
+static uint8_t *stmf_ic_uint8_array_unmarshal(nvlist_t *nvl, char *field_name,
+	uint16_t len, uint8_t *buf);
+static char *stmf_ic_string_unmarshal(nvlist_t *nvl, char *field_name);
+
+/*
+ * Transmit and recieve routines.
+ */
+stmf_ic_msg_status_t stmf_ic_transmit(char *buf, size_t size);
+
+/*
+ * Utilities.
+ */
+static stmf_ic_msg_t *stmf_ic_alloc_msg_header(stmf_ic_msg_type_t msg_type,
+	stmf_ic_msgid_t msgid);
+static size_t sizeof_scsi_devid_desc(int ident_length);
+static char *stmf_ic_strdup(char *str);
+static scsi_devid_desc_t *scsi_devid_desc_dup(scsi_devid_desc_t *did);
+static void scsi_devid_desc_free(scsi_devid_desc_t *did);
+static inline void stmf_ic_nvlookup_warn(const char *func, char *field);
+
+/*
+ * Send a message out over the interconnect, in the process marshalling
+ * the arguments.
+ *
+ * After being sent, the message is freed.
+ */
+stmf_ic_msg_status_t
+stmf_ic_tx_msg(stmf_ic_msg_t *msg)
+{
+	size_t size = 0;
+	nvlist_t *nvl = NULL;
+	char *buf = NULL;
+	int err = 0;
+	stmf_ic_msg_status_t status = STMF_IC_MSG_SUCCESS;
+
+	nvl = stmf_ic_msg_marshal(msg);
+	if (!nvl) {
+		cmn_err(CE_WARN, "stmf_ic_tx_msg: marshal failed");
+		status = STMF_IC_MSG_INTERNAL_ERROR;
+		goto done;
+	}
+
+	err = nvlist_size(nvl, &size, NV_ENCODE_XDR);
+	if (err) {
+		status = STMF_IC_MSG_INTERNAL_ERROR;
+		goto done;
+	}
+
+	buf = kmem_alloc(size, KM_SLEEP);
+	err = nvlist_pack(nvl, &buf, &size, NV_ENCODE_XDR, 0);
+	if (err) {
+		status = STMF_IC_MSG_INTERNAL_ERROR;
+		goto done;
+	}
+
+	/* push the bits out on the wire */
+
+	status = stmf_ic_transmit(buf, size);
+
+done:
+	if (nvl)
+		nvlist_free(nvl);
+
+	if (buf)
+		kmem_free(buf, size);
+
+	stmf_ic_msg_free(msg);
+
+
+	return (status);
+}
+
+/*
+ * Pass the command to the daemon for transmission to the other node.
+ */
+stmf_ic_msg_status_t
+stmf_ic_transmit(char *buf, size_t size)
+{
+	int i;
+	int rc;
+	door_arg_t arg;
+	door_handle_t door;
+	uint32_t result;
+
+	mutex_enter(&pppt_global.global_door_lock);
+	if (pppt_global.global_door == NULL) {
+		/* daemon not listening */
+		mutex_exit(&pppt_global.global_door_lock);
+		return (STMF_IC_MSG_INTERNAL_ERROR);
+	}
+	door = pppt_global.global_door;
+	door_ki_hold(door);
+	mutex_exit(&pppt_global.global_door_lock);
+
+	arg.data_ptr = buf;
+	arg.data_size = size;
+	arg.desc_ptr = NULL;
+	arg.desc_num = 0;
+	arg.rbuf = (char *)&result;
+	arg.rsize = sizeof (result);
+	/*
+	 * Retry a few times if there is a shortage of threads to
+	 * service the upcall. This shouldn't happen unless a large
+	 * number of initiators issue commands at once.
+	 */
+	for (i = 0; i < STMF_MSG_TRANSMIT_RETRY; i++) {
+		rc = door_ki_upcall(door, &arg);
+		if (rc != EAGAIN)
+			break;
+		delay(hz);
+	}
+	door_ki_rele(door);
+	if (rc != 0) {
+		cmn_err(CE_WARN,
+		    "stmf_ic_transmit door_ki_upcall failed %d", rc);
+		return (STMF_IC_MSG_INTERNAL_ERROR);
+	}
+	if (result != 0) {
+		/* XXX Just warn for now */
+		cmn_err(CE_WARN,
+		    "stmf_ic_transmit bad result from daemon %d", result);
+	}
+
+	return (STMF_IC_MSG_SUCCESS);
+}
+
+/*
+ * This is a low-level upcall which is called when a message has
+ * been received on the interconnect.
+ *
+ * The caller is responsible for freeing the buffer which is passed in.
+ */
+/*ARGSUSED*/
+void
+stmf_ic_rx_msg(char *buf, size_t len)
+{
+	nvlist_t *nvl = NULL;
+	stmf_ic_msg_t *m = NULL;
+	stmf_ic_echo_request_reply_msg_t *icerr;
+	stmf_ic_msg_t *echo_msg;
+	int rc = 0;
+
+	rc = nvlist_unpack(buf, len, &nvl, 0);
+	if (rc) {
+		cmn_err(CE_WARN, "stmf_ic_rx_msg: unpack failed");
+		return;
+	}
+
+	m = stmf_ic_msg_unmarshal(nvl);
+	if (m == NULL) {
+		cmn_err(CE_WARN, "stmf_ic_rx_msg: unmarshal failed");
+		nvlist_free(nvl);
+		return;
+	}
+
+	switch (m->icm_msg_type) {
+
+	case STMF_ICM_REGISTER_PROXY_PORT:
+	case STMF_ICM_DEREGISTER_PROXY_PORT:
+	case STMF_ICM_SCSI_CMD:
+	case STMF_ICM_SCSI_DATA_XFER_DONE:
+	case STMF_ICM_SESSION_CREATE:
+	case STMF_ICM_SESSION_DESTROY:
+		/*
+		 * These messages are all received by pppt.
+		 * Currently, pppt will parse the message for type
+		 */
+		(void) pppt_msg_rx(m);
+		break;
+
+	case STMF_ICM_LUN_ACTIVE:
+	case STMF_ICM_REGISTER_LUN:
+	case STMF_ICM_DEREGISTER_LUN:
+	case STMF_ICM_SCSI_DATA:
+	case STMF_ICM_SCSI_STATUS:
+		/*
+		 * These messages are all received by stmf.
+		 * Currently, stmf will parse the message for type
+		 */
+		(void) stmf_msg_rx(m);
+		break;
+
+	case STMF_ICM_ECHO_REQUEST:
+		icerr = m->icm_msg;
+		echo_msg = stmf_ic_echo_reply_msg_alloc(icerr->icerr_datalen,
+		    icerr->icerr_data, 0);
+		if (echo_msg != NULL) {
+			(void) stmf_ic_tx_msg(echo_msg);
+		}
+		stmf_ic_msg_free(m);
+		break;
+
+	case STMF_ICM_ECHO_REPLY:
+		stmf_ic_msg_free(m);
+		break;
+
+	case STMF_ICM_R2T:
+		/*
+		 * XXX currently not supported
+		 */
+		stmf_ic_msg_free(m);
+		break;
+
+	case STMF_ICM_STATUS:
+		(void) stmf_msg_rx(m);
+		break;
+
+	default:
+		ASSERT(0);
+	}
+}
+
+/*
+ * IC message allocation routines.
+ */
+
+stmf_ic_msg_t *
+stmf_ic_reg_port_msg_alloc(
+    scsi_devid_desc_t *port_id,
+    uint16_t relative_port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_reg_port_msg_t *icrp = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_REGISTER_PROXY_PORT, msgid);
+	icrp = (stmf_ic_reg_port_msg_t *)kmem_zalloc(sizeof (*icrp), KM_SLEEP);
+	icm->icm_msg = (void *)icrp;
+
+	icrp->icrp_port_id = scsi_devid_desc_dup(port_id);
+	icrp->icrp_relative_port_id = relative_port_id;
+
+	if (cb_arg_len) {
+		icrp->icrp_cb_arg_len = cb_arg_len;
+		icrp->icrp_cb_arg = cb_arg;
+	}
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_dereg_port_msg_alloc(
+    scsi_devid_desc_t *port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_dereg_port_msg_t *icdp = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_DEREGISTER_PROXY_PORT, msgid);
+	icdp = (stmf_ic_dereg_port_msg_t *)kmem_zalloc(sizeof (*icdp),
+	    KM_SLEEP);
+	icm->icm_msg = (void *)icdp;
+
+	icdp->icdp_port_id = scsi_devid_desc_dup(port_id);
+
+	if (cb_arg_len) {
+		icdp->icdp_cb_arg_len = cb_arg_len;
+		icdp->icdp_cb_arg = cb_arg;
+	}
+
+	return (icm);
+}
+
+
+stmf_ic_msg_t *
+stmf_ic_reg_lun_msg_alloc(
+    uint8_t *lun_id,
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_reg_dereg_lun_msg_alloc(STMF_ICM_REGISTER_LUN, lun_id,
+	    lu_provider_name, cb_arg_len, cb_arg, msgid));
+}
+
+stmf_ic_msg_t *
+stmf_ic_lun_active_msg_alloc(
+    uint8_t *lun_id,
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_reg_dereg_lun_msg_alloc(STMF_ICM_LUN_ACTIVE, lun_id,
+	    lu_provider_name, cb_arg_len, cb_arg, msgid));
+}
+
+stmf_ic_msg_t *
+stmf_ic_dereg_lun_msg_alloc(
+    uint8_t *lun_id,
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_reg_dereg_lun_msg_alloc(STMF_ICM_DEREGISTER_LUN, lun_id,
+	    lu_provider_name, cb_arg_len, cb_arg, msgid));
+}
+
+/*
+ * Guts of lun register/deregister/active alloc routines.
+ */
+static stmf_ic_msg_t *
+stmf_ic_reg_dereg_lun_msg_alloc(
+    stmf_ic_msg_type_t msg_type,
+    uint8_t *lun_id,
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_reg_dereg_lun_msg_t *icrl = NULL;
+
+	icm = stmf_ic_alloc_msg_header(msg_type, msgid);
+	icrl = (stmf_ic_reg_dereg_lun_msg_t *)
+	    kmem_zalloc(sizeof (*icrl), KM_SLEEP);
+	icm->icm_msg = (void *)icrl;
+
+	icrl->icrl_lu_provider_name = stmf_ic_strdup(lu_provider_name);
+
+	bcopy(lun_id, icrl->icrl_lun_id, sizeof (icrl->icrl_lun_id));
+
+	if (cb_arg_len) {
+		icrl->icrl_cb_arg_len = cb_arg_len;
+		icrl->icrl_cb_arg = cb_arg;
+	}
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_scsi_cmd_msg_alloc(
+    stmf_ic_msgid_t task_msgid,
+    scsi_task_t *task,
+    uint32_t immed_data_len,
+    uint8_t *immed_data,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_scsi_cmd_msg_t *icsc = NULL;
+	scsi_devid_desc_t *ini_devid = task->task_session->ss_rport_id;
+	scsi_devid_desc_t *tgt_devid = task->task_lport->lport_id;
+	uint8_t *lun_id = task->task_lu->lu_id->ident;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_SCSI_CMD, msgid);
+	icsc = (stmf_ic_scsi_cmd_msg_t *)kmem_zalloc(sizeof (*icsc), KM_SLEEP);
+	icm->icm_msg = (void *)icsc;
+
+	icsc->icsc_task_msgid = task_msgid;
+	icsc->icsc_ini_devid = scsi_devid_desc_dup(ini_devid);
+	icsc->icsc_tgt_devid = scsi_devid_desc_dup(tgt_devid);
+	icsc->icsc_session_id = task->task_session->ss_session_id;
+
+	bcopy(lun_id, icsc->icsc_lun_id, sizeof (icsc->icsc_lun_id));
+
+	bcopy(task->task_lun_no, icsc->icsc_task_lun_no,
+	    sizeof (icsc->icsc_task_lun_no));
+
+	icsc->icsc_task_expected_xfer_length = task->task_expected_xfer_length;
+	icsc->icsc_task_cdb_length = task->task_cdb_length;
+
+	icsc->icsc_task_cdb = (uint8_t *)kmem_zalloc(task->task_cdb_length,
+	    KM_SLEEP);
+	bcopy(task->task_cdb, icsc->icsc_task_cdb, task->task_cdb_length);
+
+	icsc->icsc_task_flags = task->task_flags;
+	icsc->icsc_task_priority = task->task_priority;
+	icsc->icsc_task_mgmt_function = task->task_mgmt_function;
+
+	icsc->icsc_immed_data_len = immed_data_len;
+	icsc->icsc_immed_data = immed_data;
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_scsi_data_msg_alloc(
+    stmf_ic_msgid_t task_msgid,
+    uint64_t session_id,
+    uint8_t *lun_id,
+    uint64_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_scsi_data_msg_t *icsd = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_SCSI_DATA, msgid);
+	icsd = (stmf_ic_scsi_data_msg_t *)kmem_zalloc(sizeof (*icsd), KM_SLEEP);
+	icm->icm_msg = (void *)icsd;
+
+	icsd->icsd_task_msgid = task_msgid;
+	icsd->icsd_session_id = session_id;
+	bcopy(lun_id, icsd->icsd_lun_id, sizeof (icsd->icsd_lun_id));
+	icsd->icsd_data_len = data_len;
+	icsd->icsd_data = data;
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_scsi_data_xfer_done_msg_alloc(
+    stmf_ic_msgid_t task_msgid,
+    uint64_t session_id,
+    stmf_status_t status,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_scsi_data_xfer_done_msg_t *icsx = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_SCSI_DATA_XFER_DONE, msgid);
+	icsx = (stmf_ic_scsi_data_xfer_done_msg_t *)kmem_zalloc(
+	    sizeof (*icsx), KM_SLEEP);
+	icm->icm_msg = (void *)icsx;
+
+	icsx->icsx_task_msgid = task_msgid;
+	icsx->icsx_session_id = session_id;
+	icsx->icsx_status = status;
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_scsi_status_msg_alloc(
+    stmf_ic_msgid_t task_msgid,
+    uint64_t session_id,
+    uint8_t *lun_id,
+    uint8_t response,
+    uint8_t status,
+    uint8_t flags,
+    uint32_t resid,
+    uint8_t sense_len,
+    uint8_t *sense,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_scsi_status_msg_t *icss = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_SCSI_STATUS, msgid);
+	icss = (stmf_ic_scsi_status_msg_t *)kmem_zalloc(sizeof (*icss),
+	    KM_SLEEP);
+	icm->icm_msg = (void *)icss;
+
+	icss->icss_task_msgid = task_msgid;
+	icss->icss_session_id = session_id;
+	bcopy(lun_id, icss->icss_lun_id, sizeof (icss->icss_lun_id));
+	icss->icss_response = response;
+	icss->icss_status = status;
+	icss->icss_flags = flags;
+	icss->icss_resid = resid;
+	icss->icss_sense_len = sense_len;
+	icss->icss_sense = sense;
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_r2t_msg_alloc(
+    stmf_ic_msgid_t task_msgid,
+    uint64_t session_id,
+    uint32_t offset,
+    uint32_t length,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_r2t_msg_t *icrt = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_R2T, msgid);
+	icrt = (stmf_ic_r2t_msg_t *)kmem_zalloc(sizeof (*icrt), KM_SLEEP);
+	icm->icm_msg = (void *)icrt;
+
+	icrt->icrt_task_msgid = task_msgid;
+	icrt->icrt_session_id = session_id;
+	icrt->icrt_offset = offset;
+	icrt->icrt_length = length;
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_status_msg_alloc(
+    stmf_status_t status,
+    stmf_ic_msg_type_t msg_type,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_status_msg_t *ics = NULL;
+
+	icm = stmf_ic_alloc_msg_header(STMF_ICM_STATUS, msgid);
+	ics = (stmf_ic_status_msg_t *)kmem_zalloc(sizeof (*ics), KM_SLEEP);
+	icm->icm_msg = (void *)ics;
+
+	ics->ics_status = status;
+	ics->ics_msg_type = msg_type;
+	ics->ics_msgid = msgid;		/* XXX same as msgid in header */
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_session_create_msg_alloc(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_session_create_destroy_msg_alloc(
+	    STMF_ICM_SESSION_CREATE, session, msgid));
+}
+
+stmf_ic_msg_t *
+stmf_ic_session_destroy_msg_alloc(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_session_create_destroy_msg_alloc(
+	    STMF_ICM_SESSION_DESTROY, session, msgid));
+}
+
+/*
+ * Guts of session create/destroy routines.
+ */
+static stmf_ic_msg_t *
+stmf_ic_session_create_destroy_msg_alloc(
+    stmf_ic_msg_type_t msg_type,
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_session_create_destroy_msg_t *icscd = NULL;
+	scsi_devid_desc_t *ini_devid = session->ss_rport_id;
+	scsi_devid_desc_t *tgt_devid = session->ss_lport->lport_id;
+
+	icm = stmf_ic_alloc_msg_header(msg_type, msgid);
+	icscd = (stmf_ic_session_create_destroy_msg_t *)
+	    kmem_zalloc(sizeof (*icscd), KM_SLEEP);
+	icm->icm_msg = (void *)icscd;
+
+	icscd->icscd_session_id = session->ss_session_id;
+	icscd->icscd_ini_devid = scsi_devid_desc_dup(ini_devid);
+	icscd->icscd_tgt_devid = scsi_devid_desc_dup(tgt_devid);
+
+	return (icm);
+}
+
+stmf_ic_msg_t *
+stmf_ic_echo_request_msg_alloc(
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_echo_request_reply_msg_alloc(
+	    STMF_ICM_ECHO_REQUEST, data_len, data, msgid));
+}
+
+stmf_ic_msg_t *
+stmf_ic_echo_reply_msg_alloc(
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid)
+{
+	return (stmf_ic_echo_request_reply_msg_alloc(
+	    STMF_ICM_ECHO_REPLY, data_len, data, msgid));
+}
+
+
+static stmf_ic_msg_t *
+stmf_ic_echo_request_reply_msg_alloc(
+    stmf_ic_msg_type_t msg_type,
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm = NULL;
+	stmf_ic_echo_request_reply_msg_t *icerr = NULL;
+
+	icm = stmf_ic_alloc_msg_header(msg_type, msgid);
+	icerr = kmem_zalloc(sizeof (*icerr), KM_SLEEP);
+	icm->icm_msg = (void *)icerr;
+
+	icerr->icerr_data = data;
+	icerr->icerr_datalen = data_len;
+
+	return (icm);
+}
+
+/*
+ * msg free routines.
+ */
+void
+stmf_ic_msg_free(stmf_ic_msg_t *msg)
+{
+	stmf_ic_msg_construction_method_t cmethod =
+	    (msg->icm_nvlist ? STMF_UNMARSHAL : STMF_CONSTRUCTOR);
+
+	switch (msg->icm_msg_type) {
+	case STMF_ICM_REGISTER_PROXY_PORT:
+		stmf_ic_reg_port_msg_free(
+		    (stmf_ic_reg_port_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_DEREGISTER_PROXY_PORT:
+		stmf_ic_dereg_port_msg_free(
+		    (stmf_ic_dereg_port_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_LUN_ACTIVE:
+	case STMF_ICM_REGISTER_LUN:
+	case STMF_ICM_DEREGISTER_LUN:
+		stmf_ic_reg_dereg_lun_msg_free(
+		    (stmf_ic_reg_dereg_lun_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_SCSI_CMD:
+		stmf_ic_scsi_cmd_msg_free(
+		    (stmf_ic_scsi_cmd_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_SCSI_DATA:
+		stmf_ic_scsi_data_msg_free(
+		    (stmf_ic_scsi_data_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_SCSI_DATA_XFER_DONE:
+		stmf_ic_scsi_data_xfer_done_msg_free(
+		    (stmf_ic_scsi_data_xfer_done_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_SCSI_STATUS:
+		stmf_ic_scsi_status_msg_free(
+		    (stmf_ic_scsi_status_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_R2T:
+		stmf_ic_r2t_msg_free(
+		    (stmf_ic_r2t_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_STATUS:
+		stmf_ic_status_msg_free(
+		    (stmf_ic_status_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_SESSION_CREATE:
+	case STMF_ICM_SESSION_DESTROY:
+		stmf_ic_session_create_destroy_msg_free(
+		    (stmf_ic_session_create_destroy_msg_t *)msg->icm_msg,
+		    cmethod);
+		break;
+
+	case STMF_ICM_ECHO_REQUEST:
+	case STMF_ICM_ECHO_REPLY:
+		stmf_ic_echo_request_reply_msg_free(
+		    (stmf_ic_echo_request_reply_msg_t *)msg->icm_msg, cmethod);
+		break;
+
+	case STMF_ICM_MAX_MSG_TYPE:
+		ASSERT(0);
+		break;
+
+	default:
+		ASSERT(0);
+	}
+
+	if (msg->icm_nvlist)
+		nvlist_free(msg->icm_nvlist);
+
+	kmem_free(msg, sizeof (*msg));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_reg_port_msg_free(stmf_ic_reg_port_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	scsi_devid_desc_free(m->icrp_port_id);
+
+	kmem_free(m, sizeof (*m));
+}
+
+
+/*ARGSUSED*/
+static void
+stmf_ic_dereg_port_msg_free(stmf_ic_dereg_port_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	scsi_devid_desc_free(m->icdp_port_id);
+
+	kmem_free(m, sizeof (*m));
+}
+
+
+/*
+ * Works for both reg_lun_msg and dereg_lun_msg, since the message
+ * payload is the same.
+ */
+static void
+stmf_ic_reg_dereg_lun_msg_free(stmf_ic_reg_dereg_lun_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	if (cmethod == STMF_CONSTRUCTOR) {
+		kmem_free(m->icrl_lu_provider_name,
+		    strlen(m->icrl_lu_provider_name) + 1);
+	}
+
+	kmem_free(m, sizeof (*m));
+}
+
+static void
+stmf_ic_scsi_cmd_msg_free(stmf_ic_scsi_cmd_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	scsi_devid_desc_free(m->icsc_ini_devid);
+	scsi_devid_desc_free(m->icsc_tgt_devid);
+	if (cmethod == STMF_CONSTRUCTOR) {
+		kmem_free(m->icsc_task_cdb, m->icsc_task_cdb_length);
+	}
+
+	kmem_free(m, sizeof (*m));
+
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_scsi_data_msg_free(stmf_ic_scsi_data_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_scsi_data_xfer_done_msg_free(stmf_ic_scsi_data_xfer_done_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_scsi_status_msg_free(stmf_ic_scsi_status_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_r2t_msg_free(stmf_ic_r2t_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_status_msg_free(stmf_ic_status_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+/*
+ * Works for both session_create and session_destroy msgs, since the message
+ * payload is the same.
+ */
+/*ARGSUSED*/
+static void
+stmf_ic_session_create_destroy_msg_free(stmf_ic_session_create_destroy_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	scsi_devid_desc_free(m->icscd_ini_devid);
+	scsi_devid_desc_free(m->icscd_tgt_devid);
+
+	kmem_free(m, sizeof (*m));
+}
+
+/*ARGSUSED*/
+static void
+stmf_ic_echo_request_reply_msg_free(stmf_ic_echo_request_reply_msg_t *m,
+    stmf_ic_msg_construction_method_t cmethod)
+{
+	kmem_free(m, sizeof (*m));
+}
+
+
+/*
+ * Marshaling routines.
+ */
+
+static nvlist_t *
+stmf_ic_msg_marshal(stmf_ic_msg_t *msg)
+{
+	nvlist_t *nvl = NULL;
+	int rc = 0;
+
+	rc = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
+	if (rc)
+		goto done;
+
+	NVLIST_ADD_FIELD(uint8, msg, icm_msg_type);
+	NVLIST_ADD_FIELD(uint64, msg, icm_msgid);
+
+	switch (msg->icm_msg_type) {
+	case STMF_ICM_REGISTER_PROXY_PORT:
+		rc = stmf_ic_reg_port_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+
+	case STMF_ICM_DEREGISTER_PROXY_PORT:
+		rc = stmf_ic_dereg_port_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_LUN_ACTIVE:
+	case STMF_ICM_REGISTER_LUN:
+	case STMF_ICM_DEREGISTER_LUN:
+		rc = stmf_ic_reg_dereg_lun_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_SCSI_CMD:
+		rc = stmf_ic_scsi_cmd_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_SCSI_DATA:
+		rc = stmf_ic_scsi_data_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_SCSI_DATA_XFER_DONE:
+		rc = stmf_ic_scsi_data_xfer_done_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_SCSI_STATUS:
+		rc = stmf_ic_scsi_status_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_R2T:
+		rc = stmf_ic_r2t_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_STATUS:
+		rc = stmf_ic_status_msg_marshal(nvl, msg->icm_msg);
+		break;
+
+	case STMF_ICM_SESSION_CREATE:
+	case STMF_ICM_SESSION_DESTROY:
+		rc = stmf_ic_session_create_destroy_msg_marshal(nvl,
+		    msg->icm_msg);
+		break;
+
+	case STMF_ICM_ECHO_REQUEST:
+	case STMF_ICM_ECHO_REPLY:
+		rc = stmf_ic_echo_request_reply_msg_marshal(nvl,
+		    msg->icm_msg);
+		break;
+
+	case STMF_ICM_MAX_MSG_TYPE:
+		ASSERT(0);
+		break;
+
+	default:
+		ASSERT(0);
+	}
+
+done:
+	if (!rc)
+		return (nvl);
+
+	if (nvl)
+		nvlist_free(nvl);
+
+	return (NULL);
+}
+
+
+static int
+stmf_ic_reg_port_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_reg_port_msg_t *m = (stmf_ic_reg_port_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_DEVID(m, icrp_port_id);
+	NVLIST_ADD_FIELD(uint16, m, icrp_relative_port_id);
+
+	NVLIST_ADD_FIELD(uint16, m, icrp_cb_arg_len);
+	/* only add the callback arg if necessary */
+	if (m->icrp_cb_arg_len) {
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icrp_cb_arg, m->icrp_cb_arg_len);
+	}
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_dereg_port_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_dereg_port_msg_t *m = (stmf_ic_dereg_port_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_DEVID(m, icdp_port_id);
+	NVLIST_ADD_FIELD(uint16, m, icdp_cb_arg_len);
+
+	/* only add the callback arg if necessary */
+	if (m->icdp_cb_arg_len) {
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icdp_cb_arg, m->icdp_cb_arg_len);
+	}
+
+done:
+	return (rc);
+}
+
+/*
+ * Handles STMF_ICM_LUN_ACTIVE, STMF_ICM_REGISTER_LUN and
+ * STMF_ICM_DEREGISTER_LUN;
+ * msg payload is the same for all.
+ */
+static int
+stmf_ic_reg_dereg_lun_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_reg_dereg_lun_msg_t *m = (stmf_ic_reg_dereg_lun_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_ARRAY(uint8, m, icrl_lun_id);
+	NVLIST_ADD_FIELD(string, m, icrl_lu_provider_name);
+	NVLIST_ADD_FIELD(uint16, m, icrl_cb_arg_len);
+
+	/* only add the callback arg if necessary */
+	if (m->icrl_cb_arg_len) {
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icrl_cb_arg, m->icrl_cb_arg_len);
+	}
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_scsi_cmd_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_scsi_cmd_msg_t *m = (stmf_ic_scsi_cmd_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint64, m, icsc_task_msgid);
+	NVLIST_ADD_DEVID(m, icsc_ini_devid);
+	NVLIST_ADD_DEVID(m, icsc_tgt_devid);
+	NVLIST_ADD_ARRAY(uint8, m, icsc_lun_id);
+	NVLIST_ADD_FIELD(uint64, m, icsc_session_id);
+	NVLIST_ADD_ARRAY_LEN(uint8, m, icsc_task_lun_no, 8);
+	NVLIST_ADD_FIELD(uint32, m, icsc_task_expected_xfer_length);
+	NVLIST_ADD_FIELD(uint16, m, icsc_task_cdb_length);
+	NVLIST_ADD_ARRAY_LEN(uint8, m, icsc_task_cdb, m->icsc_task_cdb_length);
+	NVLIST_ADD_FIELD(uint8, m, icsc_task_flags);
+	NVLIST_ADD_FIELD(uint8, m, icsc_task_priority);
+	NVLIST_ADD_FIELD(uint8, m, icsc_task_mgmt_function);
+
+	NVLIST_ADD_FIELD(uint32, m, icsc_immed_data_len);
+	/* only add immediate data if necessary */
+	if (m->icsc_immed_data_len) {
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icsc_immed_data,
+		    m->icsc_immed_data_len);
+	}
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_scsi_data_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_scsi_data_msg_t *m = (stmf_ic_scsi_data_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint64, m, icsd_task_msgid);
+	NVLIST_ADD_FIELD(uint64, m, icsd_session_id);
+	NVLIST_ADD_ARRAY(uint8, m, icsd_lun_id);
+	NVLIST_ADD_FIELD(uint64, m, icsd_data_len);
+	NVLIST_ADD_ARRAY_LEN(uint8, m, icsd_data, m->icsd_data_len);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_scsi_data_xfer_done_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_scsi_data_xfer_done_msg_t *m =
+	    (stmf_ic_scsi_data_xfer_done_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint64, m, icsx_task_msgid);
+	NVLIST_ADD_FIELD(uint64, m, icsx_session_id);
+	NVLIST_ADD_FIELD(uint64, m, icsx_status);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_scsi_status_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_scsi_status_msg_t *m = (stmf_ic_scsi_status_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint64, m, icss_task_msgid);
+	NVLIST_ADD_FIELD(uint64, m, icss_session_id);
+	NVLIST_ADD_ARRAY(uint8, m, icss_lun_id);
+	NVLIST_ADD_FIELD(uint8, m, icss_response);
+	NVLIST_ADD_FIELD(uint8, m, icss_status);
+	NVLIST_ADD_FIELD(uint8, m, icss_flags);
+	NVLIST_ADD_FIELD(uint32, m, icss_resid);
+
+	NVLIST_ADD_FIELD(uint8, m, icss_sense_len);
+
+	if (m->icss_sense_len)
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icss_sense, m->icss_sense_len);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_r2t_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_r2t_msg_t *m = (stmf_ic_r2t_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint64, m, icrt_task_msgid);
+	NVLIST_ADD_FIELD(uint64, m, icrt_session_id);
+	NVLIST_ADD_FIELD(uint32, m, icrt_offset);
+	NVLIST_ADD_FIELD(uint32, m, icrt_length);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_status_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_status_msg_t *m = (stmf_ic_status_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint8, m, ics_msg_type);
+	NVLIST_ADD_FIELD(uint64, m, ics_msgid);
+	NVLIST_ADD_FIELD(uint8, m, ics_status);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_session_create_destroy_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_session_create_destroy_msg_t *m =
+	    (stmf_ic_session_create_destroy_msg_t *)msg;
+	int rc = 0;
+
+	NVLIST_ADD_DEVID(m, icscd_ini_devid);
+	NVLIST_ADD_DEVID(m, icscd_tgt_devid);
+	NVLIST_ADD_FIELD(uint64, m, icscd_session_id);
+
+done:
+	return (rc);
+}
+
+static int
+stmf_ic_echo_request_reply_msg_marshal(nvlist_t *nvl, void *msg)
+{
+	stmf_ic_echo_request_reply_msg_t *m = msg;
+	int rc = 0;
+
+	NVLIST_ADD_FIELD(uint32, m, icerr_datalen);
+	if (m->icerr_datalen)
+		NVLIST_ADD_ARRAY_LEN(uint8, m, icerr_data, m->icerr_datalen);
+
+done:
+	return (rc);
+}
+
+/*
+ * Allocate a new nvlist representing the scsi_devid_desc and add it
+ * to the nvlist.
+ */
+static int
+stmf_ic_scsi_devid_desc_marshal(nvlist_t *parent_nvl,
+	char *sdid_name,
+	scsi_devid_desc_t *sdid)
+{
+	int rc = 0;
+	nvlist_t *nvl = NULL;
+
+	rc = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
+	if (rc)
+		goto done;
+
+	NVLIST_ADD_FIELD(uint8, sdid, protocol_id);
+	NVLIST_ADD_FIELD(uint8, sdid, code_set);
+	NVLIST_ADD_FIELD(uint8, sdid, piv);
+	NVLIST_ADD_FIELD(uint8, sdid, association);
+	NVLIST_ADD_FIELD(uint8, sdid, ident_type);
+	NVLIST_ADD_FIELD(uint8, sdid, ident_length);
+
+	rc = nvlist_add_uint8_array(nvl, "ident", sdid->ident,
+	    sdid->ident_length);
+	if (rc)
+		goto done;
+
+	rc = nvlist_add_nvlist(parent_nvl, sdid_name, nvl);
+
+done:
+	if (nvl) {
+		nvlist_free(nvl);
+	}
+	return (rc);
+}
+
+/*
+ * Unmarshaling routines.
+ */
+
+static stmf_ic_msg_t *
+stmf_ic_msg_unmarshal(nvlist_t *nvl)
+{
+	stmf_ic_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+	uint8_t msg_type;
+	int rc = 0;
+
+	/*
+	 * We'd like to do this:
+	 *
+	 *   NVLIST_LOOKUP_FIELD(uint8, m, icm_msg_type);
+	 *
+	 * but the fact that msg type is an enum causes type problems.
+	 */
+	rc = nvlist_lookup_uint8(nvl, "icm_msg_type", &msg_type);
+	if (rc) {
+		stmf_ic_nvlookup_warn(__func__, "icm_msg_type");
+		goto done;
+	}
+
+	m->icm_msg_type = msg_type;
+	m->icm_nvlist = nvl;
+
+	NVLIST_LOOKUP_FIELD(uint64, m, icm_msgid);
+
+	switch (m->icm_msg_type) {
+
+	case STMF_ICM_REGISTER_PROXY_PORT:
+		m->icm_msg = stmf_ic_reg_port_msg_unmarshal(nvl);
+		break;
+
+
+	case STMF_ICM_DEREGISTER_PROXY_PORT:
+		m->icm_msg = stmf_ic_dereg_port_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_LUN_ACTIVE:
+	case STMF_ICM_REGISTER_LUN:
+	case STMF_ICM_DEREGISTER_LUN:
+		m->icm_msg = stmf_ic_reg_dereg_lun_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_SCSI_CMD:
+		m->icm_msg = stmf_ic_scsi_cmd_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_SCSI_DATA:
+		m->icm_msg = stmf_ic_scsi_data_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_SCSI_DATA_XFER_DONE:
+		m->icm_msg = stmf_ic_scsi_data_xfer_done_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_SCSI_STATUS:
+		m->icm_msg = stmf_ic_scsi_status_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_R2T:
+		m->icm_msg = stmf_ic_r2t_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_STATUS:
+		m->icm_msg = stmf_ic_status_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_SESSION_CREATE:
+	case STMF_ICM_SESSION_DESTROY:
+		m->icm_msg = stmf_ic_session_create_destroy_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_ECHO_REQUEST:
+	case STMF_ICM_ECHO_REPLY:
+		m->icm_msg = stmf_ic_echo_request_reply_msg_unmarshal(nvl);
+		break;
+
+	case STMF_ICM_MAX_MSG_TYPE:
+		ASSERT(0);
+		break;
+
+	default:
+		ASSERT(0);
+	}
+
+done:
+
+	if (!m->icm_msg) {
+		kmem_free(m, sizeof (*m));
+		return (NULL);
+	}
+
+	return (m);
+}
+
+static void *
+stmf_ic_reg_port_msg_unmarshal(nvlist_t *nvl)
+{
+	nvlist_t *nvl_port_id = NULL;
+	int rc = 0;
+	stmf_ic_reg_port_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	rc = nvlist_lookup_nvlist(nvl, "icrp_port_id", &nvl_port_id);
+	if (rc) {
+		stmf_ic_nvlookup_warn(__func__, "icrp_port_id nvl");
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+	m->icrp_port_id = stmf_ic_scsi_devid_desc_unmarshal(nvl_port_id);
+	if (m->icrp_port_id == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icrp_port_id");
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+	NVLIST_LOOKUP_FIELD(uint16, m, icrp_relative_port_id);
+	NVLIST_LOOKUP_FIELD(uint16, m, icrp_cb_arg_len);
+
+	if (m->icrp_cb_arg_len) {
+		m->icrp_cb_arg = stmf_ic_uint8_array_unmarshal(nvl,
+		    "icrp_cb_arg", m->icrp_cb_arg_len, NULL);
+		if (m->icrp_cb_arg == NULL) {
+			stmf_ic_nvlookup_warn(__func__, "icrp_cb_arg");
+			rc = ENOMEM; /* XXX */
+			goto done;
+		}
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_reg_port_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+/*
+ * XXX largely the same as stmf_ic_reg_port_msg_unmarshal()
+ * Common stuff should be factored out.  Type issues may make this
+ * painful.
+ */
+static void *
+stmf_ic_dereg_port_msg_unmarshal(nvlist_t *nvl)
+{
+	nvlist_t *nvl_port_id = NULL;
+	int rc = 0;
+	stmf_ic_dereg_port_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	rc = nvlist_lookup_nvlist(nvl, "icdp_port_id", &nvl_port_id);
+	if (rc) {
+		stmf_ic_nvlookup_warn(__func__, "icdp_port_id nvl");
+		goto done;
+	}
+
+	m->icdp_port_id = stmf_ic_scsi_devid_desc_unmarshal(nvl_port_id);
+	if (m->icdp_port_id == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icdp_port_id");
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+	NVLIST_LOOKUP_FIELD(uint16, m, icdp_cb_arg_len);
+
+	if (m->icdp_cb_arg_len) {
+		m->icdp_cb_arg = stmf_ic_uint8_array_unmarshal(nvl,
+		    "icdp_cb_arg", m->icdp_cb_arg_len, NULL);
+		if (m->icdp_cb_arg == NULL) {
+			stmf_ic_nvlookup_warn(__func__, "icdp_cb_arg");
+			rc = ENOMEM; /* XXX */
+			goto done;
+		}
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_dereg_port_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_reg_dereg_lun_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_reg_dereg_lun_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (! stmf_ic_uint8_array_unmarshal(nvl, "icrl_lun_id",
+	    sizeof (m->icrl_lun_id), m->icrl_lun_id)) {
+		stmf_ic_nvlookup_warn(__func__, "icrl_lun_id");
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+	m->icrl_lu_provider_name = stmf_ic_string_unmarshal(nvl,
+	    "icrl_lu_provider_name");
+
+	if (!m->icrl_lu_provider_name) {
+		stmf_ic_nvlookup_warn(__func__, "icrl_lu_provider_name");
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+	NVLIST_LOOKUP_FIELD(uint16, m, icrl_cb_arg_len);
+
+	if (m->icrl_cb_arg_len) {
+		m->icrl_cb_arg = stmf_ic_uint8_array_unmarshal(nvl,
+		    "icrl_cb_arg", m->icrl_cb_arg_len, NULL);
+		if (m->icrl_cb_arg == NULL) {
+			stmf_ic_nvlookup_warn(__func__, "icrl_cb_arg");
+			rc = ENOMEM; /* XXX */
+			goto done;
+		}
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_reg_dereg_lun_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_scsi_cmd_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_scsi_cmd_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icsc_task_msgid),
+	    NV_PAIR(UINT64, m, icsc_session_id),
+	    NV_PAIR(UINT32, m, icsc_task_expected_xfer_length),
+	    NV_PAIR(UINT16, m, icsc_task_cdb_length),
+	    NV_PAIR(UINT8, m, icsc_task_flags),
+	    NV_PAIR(UINT8, m, icsc_task_mgmt_function),
+	    NV_PAIR(UINT32, m, icsc_immed_data_len),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_task_msgid and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+	m->icsc_ini_devid = stmf_ic_lookup_scsi_devid_desc_and_unmarshal(
+	    nvl, "icsc_ini_devid");
+	if (m->icsc_ini_devid == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_ini_devid");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	m->icsc_tgt_devid = stmf_ic_lookup_scsi_devid_desc_and_unmarshal(
+	    nvl, "icsc_tgt_devid");
+	if (m->icsc_tgt_devid == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_tgt_devid");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	/* icsc_lun_id */
+	if (!stmf_ic_uint8_array_unmarshal(nvl, "icsc_lun_id",
+	    sizeof (m->icsc_lun_id), m->icsc_lun_id)) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_lun_id");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	/* icsc_task_lun_no */
+	if (!stmf_ic_uint8_array_unmarshal(nvl, "icsc_task_lun_no",
+	    sizeof (m->icsc_task_lun_no), m->icsc_task_lun_no)) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_task_lun_no");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	/* icsc_task_cdb */
+	m->icsc_task_cdb = stmf_ic_uint8_array_unmarshal(nvl, "icsc_task_cdb",
+	    m->icsc_task_cdb_length, NULL);
+	if (!m->icsc_task_cdb) {
+		stmf_ic_nvlookup_warn(__func__, "icsc_task_cdb");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	/* immediate data, if there is any */
+	if (m->icsc_immed_data_len) {
+		m->icsc_immed_data = stmf_ic_uint8_array_unmarshal(nvl,
+		    "icsc_immed_data", m->icsc_immed_data_len, NULL);
+		if (!m->icsc_immed_data) {
+			stmf_ic_nvlookup_warn(__func__, "icsc_immed_data");
+			rc = ENOMEM;
+			goto done;
+		}
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_scsi_cmd_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_scsi_data_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_scsi_data_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icsd_task_msgid),
+	    NV_PAIR(UINT64, m, icsd_session_id),
+	    NV_PAIR(UINT64, m, icsd_data_len),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_task_msgid and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+	if (!stmf_ic_uint8_array_unmarshal(nvl, "icsd_lun_id",
+	    sizeof (m->icsd_lun_id), m->icsd_lun_id)) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_lun_id");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	m->icsd_data = stmf_ic_uint8_array_unmarshal(nvl, "icsd_data",
+	    m->icsd_data_len, NULL);
+	if (!m->icsd_data) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_data");
+		rc = ENOMEM;
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_scsi_data_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_scsi_data_xfer_done_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_scsi_data_xfer_done_msg_t *m =
+	    kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icsx_task_msgid),
+	    NV_PAIR(UINT64, m, icsx_session_id),
+	    NV_PAIR(UINT64, m, icsx_status),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icsx_task_msgid and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_scsi_data_xfer_done_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_scsi_status_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_scsi_status_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icss_task_msgid),
+	    NV_PAIR(UINT64, m, icss_session_id),
+	    NV_PAIR(UINT8, m, icss_response),
+	    NV_PAIR(UINT8, m, icss_status),
+	    NV_PAIR(UINT8, m, icss_flags),
+	    NV_PAIR(UINT32, m, icss_resid),
+	    NV_PAIR(UINT8, m, icss_sense_len),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icss_task_msgid and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+	if (!stmf_ic_uint8_array_unmarshal(nvl, "icss_lun_id",
+	    sizeof (m->icss_lun_id), m->icss_lun_id)) {
+		stmf_ic_nvlookup_warn(__func__, "icss_lun_id");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	if (m->icss_sense_len) {
+		m->icss_sense = stmf_ic_uint8_array_unmarshal(nvl, "icss_sense",
+		    m->icss_sense_len, NULL);
+		if (!m->icss_sense) {
+			stmf_ic_nvlookup_warn(__func__, "icss_sense");
+			rc = ENOMEM;
+			goto done;
+		}
+	}
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_scsi_status_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_r2t_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_r2t_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icrt_task_msgid),
+	    NV_PAIR(UINT64, m, icrt_session_id),
+	    NV_PAIR(UINT32, m, icrt_offset),
+	    NV_PAIR(UINT32, m, icrt_length),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icrt_task_msgid and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_r2t_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_session_create_destroy_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_session_create_destroy_msg_t *m = kmem_zalloc(sizeof (*m),
+	    KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT64, m, icscd_session_id),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_session_id");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+	m->icscd_ini_devid = stmf_ic_lookup_scsi_devid_desc_and_unmarshal(
+	    nvl, "icscd_ini_devid");
+	if (m->icscd_ini_devid == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_ini_devid");
+		rc = ENOMEM;
+		goto done;
+	}
+
+	m->icscd_tgt_devid = stmf_ic_lookup_scsi_devid_desc_and_unmarshal(
+	    nvl, "icscd_tgt_devid");
+	if (m->icscd_tgt_devid == NULL) {
+		stmf_ic_nvlookup_warn(__func__, "icsd_tgt_devid");
+		rc = ENOMEM;
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_session_create_destroy_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_echo_request_reply_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_echo_request_reply_msg_t *m = kmem_zalloc(sizeof (*m),
+	    KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT32, m, icerr_datalen),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "icerr_datalen");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+	/* immediate data, if there is any */
+	if (m->icerr_datalen) {
+		m->icerr_data = stmf_ic_uint8_array_unmarshal(nvl,
+		    "icerr_data", m->icerr_datalen, NULL);
+		if (!m->icerr_data) {
+			stmf_ic_nvlookup_warn(__func__, "icerr_data");
+			rc = ENOMEM;
+			goto done;
+		}
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	stmf_ic_echo_request_reply_msg_free(m, STMF_UNMARSHAL);
+
+	return (NULL);
+}
+
+static void *
+stmf_ic_status_msg_unmarshal(nvlist_t *nvl)
+{
+	int rc = 0;
+	stmf_ic_status_msg_t *m = kmem_zalloc(sizeof (*m), KM_SLEEP);
+
+	if (nvlist_lookup_pairs(nvl, 0,
+	    NV_PAIR(UINT8, m, ics_msg_type),
+	    NV_PAIR(UINT64, m, ics_msgid),
+	    NV_PAIR(UINT8, m, ics_status),
+	    NULL) != 0) {
+		stmf_ic_nvlookup_warn(__func__, "ics_msg_type and friends");
+		rc = ENOMEM; /* XXX need something better */
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (m);
+
+	kmem_free(m, sizeof (*m));
+	return (NULL);
+}
+
+
+static scsi_devid_desc_t *
+stmf_ic_lookup_scsi_devid_desc_and_unmarshal(nvlist_t *nvl, char *field_name)
+{
+	nvlist_t *nvl_devid = NULL;
+	scsi_devid_desc_t *did = NULL;
+	int rc;
+
+	rc = nvlist_lookup_nvlist(nvl, field_name, &nvl_devid);
+	if (rc) {
+		goto done;
+	}
+
+	did = stmf_ic_scsi_devid_desc_unmarshal(nvl_devid);
+
+done:
+	return (did);
+}
+
+
+static scsi_devid_desc_t *
+stmf_ic_scsi_devid_desc_unmarshal(nvlist_t *nvl)
+{
+	scsi_devid_desc_t *sdid = NULL;
+	uint8_t ident_length = 0;
+	size_t sdid_size;
+	int rc = 0;
+
+	/*
+	 * we get the ident_length first, since that's the only
+	 * variable-sized field in the struct.
+	 */
+	rc = nvlist_lookup_uint8(nvl, "ident_length", &ident_length);
+	if (rc)
+		goto done;
+
+	sdid_size = sizeof_scsi_devid_desc(ident_length);
+	sdid = kmem_zalloc(sdid_size, KM_SLEEP);
+
+	NVLIST_LOOKUP_BIT_FIELD(uint8, sdid, protocol_id);
+	NVLIST_LOOKUP_BIT_FIELD(uint8, sdid, code_set);
+	NVLIST_LOOKUP_BIT_FIELD(uint8, sdid, piv);
+	NVLIST_LOOKUP_BIT_FIELD(uint8, sdid, association);
+	NVLIST_LOOKUP_BIT_FIELD(uint8, sdid, ident_type);
+
+	sdid->ident_length = ident_length;
+
+	if (!stmf_ic_uint8_array_unmarshal(nvl, "ident",
+	    sdid->ident_length, sdid->ident)) {
+		rc = ENOMEM; /* XXX */
+		goto done;
+	}
+
+done:
+	if (!rc)
+		return (sdid);
+
+	kmem_free(sdid, sdid_size);
+
+	return (NULL);
+}
+
+/*
+ * Unmarshal a uint8_t array.
+ *
+ * Takes a buf argument:
+ *
+ * - if non-null, the array contents are copied into the buf,
+ *   and we return a pointer to the buffer.
+ *
+ * - if null, we return a pointer to the unmarshaled data, which
+ *   resides in the nvlist.
+ *
+ * Returns NULL on failure.
+ */
+static uint8_t *
+stmf_ic_uint8_array_unmarshal(
+    nvlist_t *nvl,
+    char *field_name,
+    uint16_t len,
+    uint8_t *buf)	/* non-NULL: copy array into buf */
+{
+	uint8_t *array = NULL;
+	uint_t actual_len;
+	int rc = 0;
+
+	rc = nvlist_lookup_uint8_array(nvl, field_name, &array, &actual_len);
+	if (rc) {
+		return (NULL);
+	}
+
+	if (len != actual_len) {
+		cmn_err(CE_WARN,
+		    "stmf_ic_uint8_array_unmarshal: wrong len (%d != %d)",
+		    len, actual_len);
+		return (NULL);
+	}
+
+	if (buf) {
+		/* preallocated buf, copy in */
+		bcopy(array, buf, len);
+	} else {
+		/* return a pointer to the underlying array in the nvlist */
+		buf = array;
+	}
+
+	return (buf);
+}
+
+/*
+ * Unmarshal a string.
+ *
+ * Returns NULL on failure.
+ */
+static char *
+stmf_ic_string_unmarshal(
+    nvlist_t *nvl,
+    char *field_name)
+{
+	char *s = NULL;
+	int rc = 0;
+
+	rc = nvlist_lookup_string(nvl, field_name, &s);
+	if (rc) {
+		return (NULL);
+	}
+
+	return (s);
+}
+
+/*
+ * Utility routines.
+ */
+
+static stmf_ic_msg_t *
+stmf_ic_alloc_msg_header(
+    stmf_ic_msg_type_t msg_type,
+    stmf_ic_msgid_t msgid)
+{
+	stmf_ic_msg_t *icm;
+
+	icm = (stmf_ic_msg_t *)kmem_zalloc(sizeof (*icm), KM_SLEEP);
+	icm->icm_msg_type = msg_type;
+	icm->icm_msgid = msgid;
+
+	return (icm);
+}
+
+static size_t
+sizeof_scsi_devid_desc(int ident_length)
+{
+	int num_ident_elems;
+	size_t size;
+
+	ASSERT(ident_length > 0);
+
+	/*
+	 * Need to account for the fact that there's
+	 * already a single element in scsi_devid_desc_t.
+	 *
+	 * XXX would really like to have a way to determine the
+	 * sizeof (struct scsi_devid_desc.ident[0]), but
+	 * it's not clear that can be done.
+	 * Thus, this code relies on the knowledge of the type of
+	 * that field.
+	 */
+	num_ident_elems = ident_length - 1;
+	size = sizeof (scsi_devid_desc_t) +
+	    (num_ident_elems * sizeof (uint8_t));
+
+	return (size);
+}
+
+
+/*
+ * Duplicate the scsi_devid_desc_t.
+ */
+static scsi_devid_desc_t *
+scsi_devid_desc_dup(scsi_devid_desc_t *did)
+{
+	scsi_devid_desc_t *dup;
+	size_t dup_size;
+
+	ASSERT(did->ident_length > 0);
+
+	dup_size = sizeof_scsi_devid_desc(did->ident_length);
+	dup = (scsi_devid_desc_t *)kmem_zalloc(dup_size, KM_SLEEP);
+	bcopy(did, dup, dup_size);
+	return (dup);
+}
+
+/*
+ * May be called with a null pointer.
+ */
+static void
+scsi_devid_desc_free(scsi_devid_desc_t *did)
+{
+	if (!did)
+		return;
+
+	kmem_free(did, sizeof_scsi_devid_desc(did->ident_length));
+}
+
+/*
+ * Helper functions, returns NULL if no memory.
+ */
+static char *
+stmf_ic_strdup(char *str)
+{
+	char *copy;
+
+	ASSERT(str);
+
+	copy = kmem_zalloc(strlen(str) + 1, KM_SLEEP);
+	(void) strcpy(copy, str);
+	return (copy);
+}
+
+static inline void
+stmf_ic_nvlookup_warn(const char *func, char *field)
+{
+	cmn_err(CE_WARN, "%s: nvlist lookup of %s failed", func, field);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/pppt.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,1453 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cpuvar.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/sysmacros.h>
+#include <sys/nvpair.h>
+#include <sys/door.h>
+
+#include <sys/stmf.h>
+#include <sys/stmf_ioctl.h>
+#include <sys/pppt_ioctl.h>
+#include <sys/portif.h>
+#include <pppt.h>
+
+#define	PPPT_VERSION		BUILD_DATE "-1.18dev"
+#define	PPPT_NAME_VERSION	"COMSTAR PPPT v" PPPT_VERSION
+
+/*
+ * DDI entry points.
+ */
+static int pppt_drv_attach(dev_info_t *, ddi_attach_cmd_t);
+static int pppt_drv_detach(dev_info_t *, ddi_detach_cmd_t);
+static int pppt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int pppt_drv_open(dev_t *, int, int, cred_t *);
+static int pppt_drv_close(dev_t, int, int, cred_t *);
+static boolean_t pppt_drv_busy(void);
+static int pppt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+extern pppt_status_t pppt_ic_so_enable(boolean_t);
+extern void pppt_ic_so_disable();
+extern void stmf_ic_rx_msg(char *, size_t);
+
+extern struct mod_ops mod_miscops;
+
+static struct cb_ops pppt_cb_ops = {
+	pppt_drv_open,	/* cb_open */
+	pppt_drv_close,	/* cb_close */
+	nodev,			/* cb_strategy */
+	nodev,			/* cb_print */
+	nodev,			/* cb_dump */
+	nodev,			/* cb_read */
+	nodev,			/* cb_write */
+	pppt_drv_ioctl,		/* cb_ioctl */
+	nodev,			/* cb_devmap */
+	nodev,			/* cb_mmap */
+	nodev,			/* cb_segmap */
+	nochpoll,		/* cb_chpoll */
+	ddi_prop_op,		/* cb_prop_op */
+	NULL,			/* cb_streamtab */
+	D_MP,			/* cb_flag */
+	CB_REV,			/* cb_rev */
+	nodev,			/* cb_aread */
+	nodev,			/* cb_awrite */
+};
+
+static struct dev_ops pppt_dev_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* devo_refcnt */
+	pppt_drv_getinfo,	/* devo_getinfo */
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	pppt_drv_attach,	/* devo_attach */
+	pppt_drv_detach,	/* devo_detach */
+	nodev,			/* devo_reset */
+	&pppt_cb_ops,		/* devo_cb_ops */
+	NULL,			/* devo_bus_ops */
+	NULL,			/* devo_power */
+	ddi_quiesce_not_needed,	/* quiesce */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,
+	"Proxy Port Provider",
+	&pppt_dev_ops,
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL,
+};
+
+pppt_global_t pppt_global;
+
+int pppt_logging = 0;
+
+static int pppt_enable_svc(void);
+
+static void pppt_disable_svc(void);
+
+static int pppt_task_avl_compare(const void *tgt1, const void *tgt2);
+
+static stmf_data_buf_t *pppt_dbuf_alloc(scsi_task_t *task,
+    uint32_t size, uint32_t *pminsize, uint32_t flags);
+
+static void pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf);
+
+static void pppt_sess_destroy_task(void *ps_void);
+
+static void pppt_task_sent_status(pppt_task_t *ptask);
+
+static pppt_status_t pppt_task_try_abort(pppt_task_t *ptask);
+
+static pppt_status_t pppt_task_hold(pppt_task_t *ptask);
+
+static void pppt_task_rele(pppt_task_t *ptask);
+
+static void pppt_task_update_state(pppt_task_t *ptask,
+    pppt_task_state_t new_state);
+
+/*
+ * Lock order:  global --> target --> session --> task
+ */
+
+int
+_init(void)
+{
+	int rc;
+
+	mutex_init(&pppt_global.global_lock, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&pppt_global.global_door_lock, NULL, MUTEX_DEFAULT, NULL);
+	pppt_global.global_svc_state = PSS_DETACHED;
+
+	if ((rc = mod_install(&modlinkage)) != 0) {
+		mutex_destroy(&pppt_global.global_door_lock);
+		mutex_destroy(&pppt_global.global_lock);
+		return (rc);
+	}
+
+	return (rc);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int rc;
+
+	rc = mod_remove(&modlinkage);
+
+	if (rc == 0) {
+		mutex_destroy(&pppt_global.global_lock);
+		mutex_destroy(&pppt_global.global_door_lock);
+	}
+
+	return (rc);
+}
+
+/*
+ * DDI entry points.
+ */
+
+/* ARGSUSED */
+static int
+pppt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+    void **result)
+{
+	ulong_t instance = getminor((dev_t)arg);
+
+	switch (cmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*result = pppt_global.global_dip;
+		return (DDI_SUCCESS);
+
+	case DDI_INFO_DEVT2INSTANCE:
+		*result = (void *)instance;
+		return (DDI_SUCCESS);
+
+	default:
+		break;
+	}
+
+	return (DDI_FAILURE);
+}
+
+static int
+pppt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	if (cmd != DDI_ATTACH) {
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_get_instance(dip) != 0) {
+		/* we only allow instance 0 to attach */
+		return (DDI_FAILURE);
+	}
+
+	/* create the minor node */
+	if (ddi_create_minor_node(dip, PPPT_MODNAME, S_IFCHR, 0,
+	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "pppt_drv_attach: "
+		    "failed creating minor node");
+		return (DDI_FAILURE);
+	}
+
+	pppt_global.global_svc_state = PSS_DISABLED;
+	pppt_global.global_dip = dip;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pppt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	PPPT_GLOBAL_LOCK();
+	if (pppt_drv_busy()) {
+		PPPT_GLOBAL_UNLOCK();
+		return (EBUSY);
+	}
+
+	ddi_remove_minor_node(dip, NULL);
+	ddi_prop_remove_all(dip);
+
+	pppt_global.global_svc_state = PSS_DETACHED;
+
+	PPPT_GLOBAL_UNLOCK();
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pppt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+	int	rc = 0;
+
+	PPPT_GLOBAL_LOCK();
+
+	switch (pppt_global.global_svc_state) {
+	case PSS_DISABLED:
+		pppt_global.global_svc_state = PSS_ENABLING;
+		PPPT_GLOBAL_UNLOCK();
+		rc = pppt_enable_svc();
+		PPPT_GLOBAL_LOCK();
+		if (rc == 0) {
+			pppt_global.global_svc_state = PSS_ENABLED;
+		} else {
+			pppt_global.global_svc_state = PSS_DISABLED;
+		}
+		break;
+	case PSS_DISABLING:
+	case PSS_ENABLING:
+	case PSS_ENABLED:
+		rc = EBUSY;
+		break;
+	default:
+		rc = EFAULT;
+		break;
+	}
+
+	PPPT_GLOBAL_UNLOCK();
+
+	return (rc);
+}
+
+/* ARGSUSED */
+static int
+pppt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+	int rc = 0;
+
+	PPPT_GLOBAL_LOCK();
+
+	switch (pppt_global.global_svc_state) {
+	case PSS_ENABLED:
+		pppt_global.global_svc_state = PSS_DISABLING;
+		PPPT_GLOBAL_UNLOCK();
+		pppt_disable_svc();
+		PPPT_GLOBAL_LOCK();
+		pppt_global.global_svc_state = PSS_DISABLED;
+		/*
+		 * release the door to the daemon
+		 */
+		mutex_enter(&pppt_global.global_door_lock);
+		if (pppt_global.global_door != NULL) {
+			door_ki_rele(pppt_global.global_door);
+			pppt_global.global_door = NULL;
+		}
+		mutex_exit(&pppt_global.global_door_lock);
+		break;
+	default:
+		rc = EFAULT;
+		break;
+	}
+
+	PPPT_GLOBAL_UNLOCK();
+
+	return (rc);
+}
+
+static boolean_t
+pppt_drv_busy(void)
+{
+	switch (pppt_global.global_svc_state) {
+	case PSS_DISABLED:
+	case PSS_DETACHED:
+		return (B_FALSE);
+	default:
+		return (B_TRUE);
+	}
+	/* NOTREACHED */
+}
+
+/* ARGSUSED */
+static int
+pppt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
+    int *retval)
+{
+	int				rc;
+	void				*buf;
+	size_t				buf_size;
+	pppt_iocdata_t			iocd;
+	door_handle_t			new_handle;
+
+	if (drv_priv(cred) != 0) {
+		return (EPERM);
+	}
+
+	rc = ddi_copyin((void *)argp, &iocd, sizeof (iocd), flag);
+	if (rc)
+		return (EFAULT);
+
+	if (iocd.pppt_version != PPPT_VERSION_1)
+		return (EINVAL);
+
+	switch (cmd) {
+	case PPPT_MESSAGE:
+
+		/* XXX limit buf_size ? */
+		buf_size = (size_t)iocd.pppt_buf_size;
+		buf = kmem_alloc(buf_size, KM_SLEEP);
+		if (buf == NULL)
+			return (ENOMEM);
+
+		rc = ddi_copyin((void *)(unsigned long)iocd.pppt_buf,
+		    buf, buf_size, flag);
+		if (rc) {
+			kmem_free(buf, buf_size);
+			return (EFAULT);
+		}
+
+		stmf_ic_rx_msg(buf, buf_size);
+
+		kmem_free(buf, buf_size);
+		break;
+	case PPPT_INSTALL_DOOR:
+
+		cmn_err(CE_NOTE, "RECEIVE DOOR HANDLE");
+
+		new_handle = door_ki_lookup((int)iocd.pppt_door_fd);
+		if (new_handle == NULL)
+			return (EINVAL);
+
+		mutex_enter(&pppt_global.global_door_lock);
+		ASSERT(pppt_global.global_svc_state == PSS_ENABLED);
+		if (pppt_global.global_door != NULL) {
+			/*
+			 * There can only be one door installed
+			 */
+			mutex_exit(&pppt_global.global_door_lock);
+			door_ki_rele(new_handle);
+			return (EBUSY);
+		}
+		pppt_global.global_door = new_handle;
+		mutex_exit(&pppt_global.global_door_lock);
+		break;
+	}
+
+	return (rc);
+}
+
+/*
+ * pppt_enable_svc
+ *
+ * registers all the configured targets and target portals with STMF
+ */
+static int
+pppt_enable_svc(void)
+{
+	stmf_port_provider_t	*pp;
+	stmf_dbuf_store_t	*dbuf_store;
+	int			rc = 0;
+
+	ASSERT(pppt_global.global_svc_state == PSS_ENABLING);
+
+	/*
+	 * Make sure that can tell if we have partially allocated
+	 * in case we need to exit and tear down anything allocated.
+	 */
+	pppt_global.global_dbuf_store = NULL;
+	pp = NULL;
+	pppt_global.global_pp = NULL;
+	pppt_global.global_dispatch_taskq = NULL;
+	pppt_global.global_sess_taskq = NULL;
+
+	avl_create(&pppt_global.global_target_list,
+	    pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
+	    offsetof(pppt_tgt_t, target_global_ln));
+
+	avl_create(&pppt_global.global_sess_list,
+	    pppt_sess_avl_compare_by_id, sizeof (pppt_sess_t),
+	    offsetof(pppt_sess_t, ps_global_ln));
+
+	/*
+	 * Setup STMF dbuf store.  Tf buffers are associated with a particular
+	 * lport (FC, SRP) then the dbuf_store should stored in the lport
+	 * context, otherwise (iSCSI) the dbuf_store should be global.
+	 */
+	dbuf_store = stmf_alloc(STMF_STRUCT_DBUF_STORE, 0, 0);
+	if (dbuf_store == NULL) {
+		rc = ENOMEM;
+		goto tear_down_and_return;
+	}
+	dbuf_store->ds_alloc_data_buf = pppt_dbuf_alloc;
+	dbuf_store->ds_free_data_buf = pppt_dbuf_free;
+	dbuf_store->ds_port_private = NULL;
+	pppt_global.global_dbuf_store = dbuf_store;
+
+	/* Register port provider */
+	pp = stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
+	if (pp == NULL) {
+		rc = ENOMEM;
+		goto tear_down_and_return;
+	}
+
+	pp->pp_portif_rev = PORTIF_REV_1;
+	pp->pp_instance = 0;
+	pp->pp_name = PPPT_MODNAME;
+	pp->pp_cb = NULL;
+
+	pppt_global.global_pp = pp;
+
+	if (stmf_register_port_provider(pp) != STMF_SUCCESS) {
+		rc = EIO;
+		goto tear_down_and_return;
+	}
+
+	pppt_global.global_dispatch_taskq = taskq_create("pppt_dispatch",
+	    1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
+
+	pppt_global.global_sess_taskq = taskq_create("pppt_session",
+	    1, minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE);
+
+	return (0);
+
+tear_down_and_return:
+
+	if (pppt_global.global_sess_taskq) {
+		taskq_destroy(pppt_global.global_sess_taskq);
+		pppt_global.global_sess_taskq = NULL;
+	}
+
+	if (pppt_global.global_dispatch_taskq) {
+		taskq_destroy(pppt_global.global_dispatch_taskq);
+		pppt_global.global_dispatch_taskq = NULL;
+	}
+
+	if (pppt_global.global_pp)
+		pppt_global.global_pp = NULL;
+
+	if (pp)
+		stmf_free(pp);
+
+	if (pppt_global.global_dbuf_store) {
+		stmf_free(pppt_global.global_dbuf_store);
+		pppt_global.global_dbuf_store = NULL;
+	}
+
+	avl_destroy(&pppt_global.global_sess_list);
+	avl_destroy(&pppt_global.global_target_list);
+
+	return (rc);
+}
+
+/*
+ * pppt_disable_svc
+ *
+ * clean up all existing sessions and deregister targets from STMF
+ */
+static void
+pppt_disable_svc(void)
+{
+	pppt_tgt_t	*tgt, *next_tgt;
+	avl_tree_t	delete_target_list;
+
+	ASSERT(pppt_global.global_svc_state == PSS_DISABLING);
+
+	avl_create(&delete_target_list,
+	    pppt_tgt_avl_compare, sizeof (pppt_tgt_t),
+	    offsetof(pppt_tgt_t, target_global_ln));
+
+	PPPT_GLOBAL_LOCK();
+	for (tgt = avl_first(&pppt_global.global_target_list);
+	    tgt != NULL;
+	    tgt = next_tgt) {
+		next_tgt = AVL_NEXT(&pppt_global.global_target_list, tgt);
+		avl_remove(&pppt_global.global_target_list, tgt);
+		avl_add(&delete_target_list, tgt);
+		pppt_tgt_async_delete(tgt);
+	}
+	PPPT_GLOBAL_UNLOCK();
+
+	for (tgt = avl_first(&delete_target_list);
+	    tgt != NULL;
+	    tgt = next_tgt) {
+		next_tgt = AVL_NEXT(&delete_target_list, tgt);
+		mutex_enter(&tgt->target_mutex);
+		while ((tgt->target_refcount > 0) ||
+		    (tgt->target_state != TS_DELETING)) {
+			cv_wait(&tgt->target_cv, &tgt->target_mutex);
+		}
+		mutex_exit(&tgt->target_mutex);
+
+		avl_remove(&delete_target_list, tgt);
+		pppt_tgt_destroy(tgt);
+	}
+
+	taskq_destroy(pppt_global.global_sess_taskq);
+
+	taskq_destroy(pppt_global.global_dispatch_taskq);
+
+	avl_destroy(&pppt_global.global_sess_list);
+	avl_destroy(&pppt_global.global_target_list);
+
+	(void) stmf_deregister_port_provider(pppt_global.global_pp);
+
+	stmf_free(pppt_global.global_dbuf_store);
+	pppt_global.global_dbuf_store = NULL;
+
+	stmf_free(pppt_global.global_pp);
+	pppt_global.global_pp = NULL;
+}
+
+/*
+ * STMF callbacks
+ */
+
+/*ARGSUSED*/
+static stmf_data_buf_t *
+pppt_dbuf_alloc(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
+    uint32_t flags)
+{
+	stmf_data_buf_t	*result;
+	pppt_buf_t	*pbuf;
+	uint8_t		*buf;
+
+	/* Get buffer */
+	buf = kmem_alloc(size, KM_SLEEP);
+
+	/*
+	 *  Allocate stmf buf with private port provider section
+	 * (pppt_buf_t)
+	 */
+	result = stmf_alloc(STMF_STRUCT_DATA_BUF, sizeof (pppt_buf_t), 0);
+	if (result != NULL) {
+		/* Fill in pppt_buf_t */
+		pbuf = result->db_port_private;
+		pbuf->pbuf_stmf_buf = result;
+		pbuf->pbuf_is_immed = B_FALSE;
+
+		/*
+		 * Fill in stmf_data_buf_t.  DB_DONT CACHE tells
+		 * stmf not to cache buffers but STMF doesn't do
+		 * that yet so it's a no-op.  Port providers like
+		 * FC and SRP that have buffers associated with the
+		 * target port would want to let STMF cache
+		 * the buffers.  Port providers like iSCSI would
+		 * not want STMF to cache because the buffers are
+		 * really associated with a connection, not an
+		 * STMF target port so there is no way for STMF
+		 * to cache the buffers effectively.  These port
+		 * providers should cache buffers internally if
+		 * there is significant buffer setup overhead.
+		 *
+		 * And of course, since STMF doesn't do any internal
+		 * caching right now anyway, all port providers should
+		 * do what they can to minimize buffer setup overhead.
+		 */
+		result->db_flags = DB_DONT_CACHE;
+		result->db_buf_size = size;
+		result->db_data_size = size;
+		result->db_sglist_length = 1;
+		result->db_sglist[0].seg_addr = buf;
+		result->db_sglist[0].seg_length = size;
+		return (result);
+	} else {
+		/*
+		 * Couldn't get the stmf_data_buf_t so free the
+		 * buffer
+		 */
+		kmem_free(buf, size);
+	}
+
+	return (NULL);
+}
+
+/*ARGSUSED*/
+static void
+pppt_dbuf_free(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
+{
+	pppt_buf_t *pbuf = dbuf->db_port_private;
+
+	if (pbuf->pbuf_is_immed) {
+		stmf_ic_msg_free(pbuf->pbuf_immed_msg);
+	} else {
+		kmem_free(dbuf->db_sglist[0].seg_addr,
+		    dbuf->db_sglist[0].seg_length);
+		stmf_free(dbuf);
+	}
+}
+
+/*ARGSUSED*/
+stmf_status_t
+pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
+    uint32_t ioflags)
+{
+	pppt_task_t		*pppt_task = task->task_port_private;
+	pppt_buf_t		*pbuf = dbuf->db_port_private;
+	stmf_ic_msg_t		*msg;
+	stmf_ic_msg_status_t	ic_msg_status;
+
+	/*
+	 * If we are aborting then we can ignore this request, otherwise
+	 * add a reference.
+	 */
+	if (pppt_task_hold(pppt_task) != PPPT_STATUS_SUCCESS) {
+		return (STMF_SUCCESS);
+	}
+
+	/*
+	 * If it's not immediate data then start the transfer
+	 */
+	ASSERT(pbuf->pbuf_is_immed == B_FALSE);
+	if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) {
+
+		/* Send read data */
+		msg = stmf_ic_scsi_data_msg_alloc(
+		    pppt_task->pt_task_id,
+		    pppt_task->pt_sess->ps_session_id,
+		    pppt_task->pt_lun_id,
+		    dbuf->db_sglist[0].seg_length,
+		    dbuf->db_sglist[0].seg_addr, 0);
+
+		pppt_task->pt_read_buf = pbuf;
+		pppt_task->pt_read_xfer_msgid = msg->icm_msgid;
+
+		ic_msg_status = stmf_ic_tx_msg(msg);
+		pppt_task_rele(pppt_task);
+		if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
+			return (STMF_FAILURE);
+		} else {
+			return (STMF_SUCCESS);
+		}
+	} else if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) {
+		pppt_task_rele(pppt_task);
+		return (STMF_FAILURE);
+	}
+
+	pppt_task_rele(pppt_task);
+
+	return (STMF_INVALID_ARG);
+}
+
+void
+pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status)
+{
+	pppt_buf_t		*pppt_buf;
+	stmf_data_buf_t		*dbuf;
+
+	/*
+	 * Caller should have taken a task hold (likely via pppt_task_lookup)
+	 *
+	 * Get pppt_buf_t and stmf_data_buf_t pointers
+	 */
+	pppt_buf = pppt_task->pt_read_buf;
+	dbuf = pppt_buf->pbuf_stmf_buf;
+	dbuf->db_xfer_status = (status == STMF_SUCCESS) ?
+	    STMF_SUCCESS : STMF_FAILURE;
+
+	/*
+	 * COMSTAR currently requires port providers to support
+	 * the DB_SEND_STATUS_GOOD flag even if phase collapse is
+	 * not supported.  So we will roll our own... pretend we are
+	 * COMSTAR and ask for a status message.
+	 */
+	if ((dbuf->db_flags & DB_SEND_STATUS_GOOD) &&
+	    (status == STMF_SUCCESS)) {
+		/*
+		 * It's possible the task has been aborted since the time we
+		 * looked it up.  We need to release the hold before calling
+		 * pppt_lport_send_status and as soon as we release the hold
+		 * the task may disappear.  Calling pppt_task_done allows us
+		 * to determine whether the task has been aborted (in which
+		 * case we will stop processing and return) and mark the task
+		 * "done" which will prevent the task from being aborted while
+		 * we are trying to send the status.
+		 */
+		if (pppt_task_done(pppt_task) != PPPT_STATUS_SUCCESS) {
+			/* STMF will free task and buffer(s) */
+			pppt_task_rele(pppt_task);
+			return;
+		}
+		pppt_task_rele(pppt_task);
+
+		if (pppt_lport_send_status(pppt_task->pt_stmf_task, 0)
+		    != STMF_SUCCESS) {
+			/* Failed to send status */
+			dbuf->db_xfer_status = STMF_FAILURE;
+			stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf,
+			    STMF_IOF_LPORT_DONE);
+		}
+	} else {
+		pppt_task_rele(pppt_task);
+		stmf_data_xfer_done(pppt_task->pt_stmf_task, dbuf, 0);
+	}
+}
+
+/*ARGSUSED*/
+stmf_status_t
+pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags)
+{
+	pppt_task_t *ptask =		task->task_port_private;
+	stmf_ic_msg_t			*msg;
+	stmf_ic_msg_status_t		ic_msg_status;
+
+	/*
+	 * Mark task completed.  If the state indicates it was aborted
+	 * then we don't need to respond.
+	 */
+	if (pppt_task_done(ptask) == PPPT_STATUS_ABORTED) {
+		return (STMF_SUCCESS);
+	}
+
+	/*
+	 * Send status.
+	 */
+	msg = stmf_ic_scsi_status_msg_alloc(
+	    ptask->pt_task_id,
+	    ptask->pt_sess->ps_session_id,
+	    ptask->pt_lun_id,
+	    0,
+	    task->task_scsi_status,
+	    task->task_status_ctrl, task->task_resid,
+	    task->task_sense_length, task->task_sense_data, 0);
+
+	ic_msg_status = stmf_ic_tx_msg(msg);
+
+	if (ic_msg_status != STMF_IC_MSG_SUCCESS) {
+		pppt_task_sent_status(ptask);
+		stmf_send_status_done(ptask->pt_stmf_task,
+		    STMF_FAILURE, STMF_IOF_LPORT_DONE);
+		return (STMF_FAILURE);
+	} else {
+		pppt_task_sent_status(ptask);
+		stmf_send_status_done(ptask->pt_stmf_task,
+		    STMF_SUCCESS, STMF_IOF_LPORT_DONE);
+		return (STMF_SUCCESS);
+	}
+}
+
+void
+pppt_lport_task_free(scsi_task_t *task)
+{
+	pppt_task_t *ptask = task->task_port_private;
+	pppt_sess_t *ps = ptask->pt_sess;
+
+	pppt_task_free(ptask);
+	pppt_sess_rele(ps);
+}
+
+/*ARGSUSED*/
+stmf_status_t
+pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
+    uint32_t flags)
+{
+	scsi_task_t	*st = (scsi_task_t *)arg;
+	pppt_task_t	*ptask;
+
+	ptask = st->task_port_private;
+
+	if (pppt_task_try_abort(ptask) == PPPT_STATUS_DONE) {
+		/*
+		 * This task is beyond the point where abort makes sense
+		 * and we will soon be sending status.  Tell STMF to
+		 * go away.
+		 */
+		return (STMF_BUSY);
+	} else {
+		return (STMF_ABORT_SUCCESS);
+	}
+	/*NOTREACHED*/
+}
+
+/*ARGSUSED*/
+void
+pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg)
+{
+	switch (cmd) {
+	case STMF_CMD_LPORT_ONLINE:
+	case STMF_CMD_LPORT_OFFLINE:
+	case STMF_ACK_LPORT_ONLINE_COMPLETE:
+	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
+		pppt_tgt_sm_ctl(lport, cmd, arg);
+		break;
+
+	default:
+		ASSERT(0);
+		break;
+	}
+}
+
+pppt_sess_t *
+pppt_sess_lookup_locked(uint64_t session_id,
+    scsi_devid_desc_t *lport_devid,
+    scsi_devid_desc_t *rport_devid)
+{
+	pppt_tgt_t				*tgt;
+	pppt_sess_t				*ps;
+	int					lport_cmp;
+	int					rport_cmp;
+
+	ASSERT(mutex_owned(&pppt_global.global_lock));
+
+	/*
+	 * Look for existing session for this ID
+	 */
+	ps = pppt_sess_lookup_by_id_locked(session_id);
+	if (ps == NULL) {
+		PPPT_INC_STAT(es_sess_lookup_no_session);
+		return (NULL);
+	}
+
+	tgt = ps->ps_target;
+
+	mutex_enter(&tgt->target_mutex);
+
+	/* Validate local/remote port names */
+	if ((lport_devid->ident_length !=
+	    tgt->target_stmf_lport->lport_id->ident_length) ||
+	    (rport_devid->ident_length !=
+	    ps->ps_stmf_sess->ss_rport_id->ident_length)) {
+		mutex_exit(&tgt->target_mutex);
+		PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
+		return (NULL);
+	} else {
+		lport_cmp = bcmp(lport_devid->ident,
+		    tgt->target_stmf_lport->lport_id->ident,
+		    lport_devid->ident_length);
+		rport_cmp = bcmp(rport_devid->ident,
+		    ps->ps_stmf_sess->ss_rport_id->ident,
+		    rport_devid->ident_length);
+		if (lport_cmp || rport_cmp) {
+			mutex_exit(&tgt->target_mutex);
+			PPPT_INC_STAT(es_sess_lookup_ident_mismatch);
+			return (NULL);
+		}
+
+		if (tgt->target_state != TS_STMF_ONLINE) {
+			mutex_exit(&tgt->target_mutex);
+			PPPT_INC_STAT(es_sess_lookup_bad_tgt_state);
+			return (NULL);
+		}
+	}
+	mutex_exit(&tgt->target_mutex);
+
+	return (ps);
+}
+
+pppt_sess_t *
+pppt_sess_lookup_by_id_locked(uint64_t session_id)
+{
+	pppt_sess_t		tmp_ps;
+	pppt_sess_t		*ps;
+
+	ASSERT(mutex_owned(&pppt_global.global_lock));
+	tmp_ps.ps_session_id = session_id;
+	tmp_ps.ps_closed = 0;
+	ps = avl_find(&pppt_global.global_sess_list, &tmp_ps, NULL);
+	if (ps != NULL) {
+		mutex_enter(&ps->ps_mutex);
+		if (!ps->ps_closed) {
+			ps->ps_refcnt++;
+			mutex_exit(&ps->ps_mutex);
+			return (ps);
+		}
+		mutex_exit(&ps->ps_mutex);
+	}
+
+	return (NULL);
+}
+
+/* New session */
+pppt_sess_t *
+pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
+    scsi_devid_desc_t *rport_devid, uint64_t session_id,
+    stmf_status_t *statusp)
+{
+	pppt_tgt_t		*tgt;
+	pppt_sess_t		*ps;
+	stmf_scsi_session_t	*ss;
+	pppt_sess_t		tmp_ps;
+	stmf_scsi_session_t	tmp_ss;
+	*statusp = STMF_SUCCESS;
+
+	PPPT_GLOBAL_LOCK();
+
+	/*
+	 * Look for existing session for this ID
+	 */
+	ps = pppt_sess_lookup_locked(session_id, lport_devid, rport_devid);
+
+	if (ps != NULL) {
+		PPPT_GLOBAL_UNLOCK();
+		return (ps);
+	}
+
+	/*
+	 * No session with that ID, look for another session corresponding
+	 * to the same IT nexus.
+	 */
+	tgt = pppt_tgt_lookup_locked(lport_devid);
+	if (tgt == NULL) {
+		*statusp = STMF_NOT_FOUND;
+		PPPT_GLOBAL_UNLOCK();
+		return (NULL);
+	}
+
+	mutex_enter(&tgt->target_mutex);
+	if (tgt->target_state != TS_STMF_ONLINE) {
+		*statusp = STMF_NOT_FOUND;
+		mutex_exit(&tgt->target_mutex);
+		PPPT_GLOBAL_UNLOCK();
+		/* Can't create session to offline target */
+		return (NULL);
+	}
+
+	bzero(&tmp_ps, sizeof (tmp_ps));
+	bzero(&tmp_ss, sizeof (tmp_ss));
+	tmp_ps.ps_stmf_sess = &tmp_ss;
+	tmp_ss.ss_rport_id = rport_devid;
+
+	/*
+	 * Look for an existing session on this IT nexus
+	 */
+	ps = avl_find(&tgt->target_sess_list, &tmp_ps, NULL);
+
+	if (ps != NULL) {
+		/*
+		 * Now check the session ID.  It should not match because if
+		 * it did we would have found it on the global session list.
+		 * If the session ID in the command is higher than the existing
+		 * session ID then we need to tear down the existing session.
+		 */
+		mutex_enter(&ps->ps_mutex);
+		ASSERT(ps->ps_session_id != session_id);
+		if (ps->ps_session_id > session_id) {
+			/* Invalid session ID */
+			mutex_exit(&ps->ps_mutex);
+			mutex_exit(&tgt->target_mutex);
+			PPPT_GLOBAL_UNLOCK();
+			*statusp = STMF_INVALID_ARG;
+			return (NULL);
+		} else {
+			/* Existing session needs to be invalidated */
+			if (!ps->ps_closed) {
+				pppt_sess_close_locked(ps);
+			}
+		}
+		mutex_exit(&ps->ps_mutex);
+
+		/* Fallthrough and create new session */
+	}
+
+	/*
+	 * Allocate and fill in pppt_session_t with the appropriate data
+	 * for the protocol.
+	 */
+	ps = kmem_zalloc(sizeof (*ps), KM_SLEEP);
+
+	/* Fill in session fields */
+	ps->ps_target = tgt;
+	ps->ps_session_id = session_id;
+
+	ss = stmf_alloc(STMF_STRUCT_SCSI_SESSION, 0,
+	    0);
+	if (ss == NULL) {
+		mutex_exit(&tgt->target_mutex);
+		PPPT_GLOBAL_UNLOCK();
+		kmem_free(ps, sizeof (*ps));
+		*statusp = STMF_ALLOC_FAILURE;
+		return (NULL);
+	}
+
+	ss->ss_rport_id = kmem_zalloc(sizeof (scsi_devid_desc_t) +
+	    rport_devid->ident_length + 1, KM_SLEEP);
+	bcopy(rport_devid, ss->ss_rport_id,
+	    sizeof (scsi_devid_desc_t) + rport_devid->ident_length + 1);
+
+	ss->ss_lport = tgt->target_stmf_lport;
+
+	if (stmf_register_scsi_session(tgt->target_stmf_lport, ss) !=
+	    STMF_SUCCESS) {
+		mutex_exit(&tgt->target_mutex);
+		PPPT_GLOBAL_UNLOCK();
+		kmem_free(ss->ss_rport_id,
+		    sizeof (scsi_devid_desc_t) +
+		    rport_devid->ident_length + 1);
+		stmf_free(ss);
+		kmem_free(ps, sizeof (*ps));
+		*statusp = STMF_TARGET_FAILURE;
+		return (NULL);
+	}
+
+	ss->ss_port_private = ps;
+	mutex_init(&ps->ps_mutex, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&ps->ps_cv, NULL, CV_DEFAULT, NULL);
+	avl_create(&ps->ps_task_list, pppt_task_avl_compare,
+	    sizeof (pppt_task_t), offsetof(pppt_task_t, pt_sess_ln));
+	ps->ps_refcnt = 1;
+	ps->ps_stmf_sess = ss;
+	avl_add(&tgt->target_sess_list, ps);
+	avl_add(&pppt_global.global_sess_list, ps);
+	mutex_exit(&tgt->target_mutex);
+	PPPT_GLOBAL_UNLOCK();
+	PPPT_LOG(CE_NOTE, "New session %p", (void *)ps);
+
+	return (ps);
+}
+
+void
+pppt_sess_rele(pppt_sess_t *ps)
+{
+	mutex_enter(&ps->ps_mutex);
+	pppt_sess_rele_locked(ps);
+	mutex_exit(&ps->ps_mutex);
+}
+
+void
+pppt_sess_rele_locked(pppt_sess_t *ps)
+{
+	ASSERT(mutex_owned(&ps->ps_mutex));
+	ps->ps_refcnt--;
+	if (ps->ps_refcnt == 0) {
+		cv_signal(&ps->ps_cv);
+	}
+}
+
+static void pppt_sess_destroy_task(void *ps_void)
+{
+	pppt_sess_t *ps = ps_void;
+	stmf_scsi_session_t	*ss;
+
+	PPPT_LOG(CE_NOTE, "Session destroy task %p", (void *)ps);
+
+	ss = ps->ps_stmf_sess;
+	mutex_enter(&ps->ps_mutex);
+	stmf_deregister_scsi_session(ss->ss_lport, ss);
+	kmem_free(ss->ss_rport_id,
+	    sizeof (scsi_devid_desc_t) +
+	    ss->ss_rport_id->ident_length + 1);
+	avl_destroy(&ps->ps_task_list);
+	mutex_exit(&ps->ps_mutex);
+	cv_destroy(&ps->ps_cv);
+	mutex_destroy(&ps->ps_mutex);
+	stmf_free(ps->ps_stmf_sess);
+	kmem_free(ps, sizeof (*ps));
+
+	PPPT_LOG(CE_NOTE, "Session destroy task complete %p", (void *)ps);
+}
+
+int
+pppt_sess_avl_compare_by_id(const void *void_sess1, const void *void_sess2)
+{
+	const	pppt_sess_t	*psess1 = void_sess1;
+	const	pppt_sess_t	*psess2 = void_sess2;
+
+	if (psess1->ps_session_id < psess2->ps_session_id)
+		return (-1);
+	else if (psess1->ps_session_id > psess2->ps_session_id)
+		return (1);
+
+	/* Allow multiple duplicate sessions if one is closed */
+	ASSERT(!(psess1->ps_closed && psess2->ps_closed));
+	if (psess1->ps_closed)
+		return (-1);
+	else if (psess2->ps_closed)
+		return (1);
+
+	return (0);
+}
+
+int
+pppt_sess_avl_compare_by_name(const void *void_sess1, const void *void_sess2)
+{
+	const	pppt_sess_t	*psess1 = void_sess1;
+	const	pppt_sess_t	*psess2 = void_sess2;
+	int			result;
+
+	/* Sort by code set then ident */
+	if (psess1->ps_stmf_sess->ss_rport_id->code_set <
+	    psess2->ps_stmf_sess->ss_rport_id->code_set) {
+		return (-1);
+	} else if (psess1->ps_stmf_sess->ss_rport_id->code_set >
+	    psess2->ps_stmf_sess->ss_rport_id->code_set) {
+		return (1);
+	}
+
+	/* Next by ident length */
+	if (psess1->ps_stmf_sess->ss_rport_id->ident_length <
+	    psess2->ps_stmf_sess->ss_rport_id->ident_length) {
+		return (-1);
+	} else if (psess1->ps_stmf_sess->ss_rport_id->ident_length >
+	    psess2->ps_stmf_sess->ss_rport_id->ident_length) {
+		return (1);
+	}
+
+	/* Code set and ident length both match, now compare idents */
+	result = memcmp(psess1->ps_stmf_sess->ss_rport_id->ident,
+	    psess2->ps_stmf_sess->ss_rport_id->ident,
+	    psess1->ps_stmf_sess->ss_rport_id->ident_length);
+
+	if (result < 0) {
+		return (-1);
+	} else if (result > 0) {
+		return (1);
+	}
+
+	return (0);
+}
+
+void
+pppt_sess_close_locked(pppt_sess_t *ps)
+{
+	pppt_tgt_t	*tgt = ps->ps_target;
+	pppt_task_t	*ptask;
+
+	PPPT_LOG(CE_NOTE, "Session close %p", (void *)ps);
+
+	ASSERT(mutex_owned(&pppt_global.global_lock));
+	ASSERT(mutex_owned(&tgt->target_mutex));
+	ASSERT(mutex_owned(&ps->ps_mutex));
+	ASSERT(!ps->ps_closed); /* Caller should ensure session is not closed */
+
+	ps->ps_closed = B_TRUE;
+	for (ptask = avl_first(&ps->ps_task_list); ptask != NULL;
+	    ptask = AVL_NEXT(&ps->ps_task_list, ptask)) {
+		mutex_enter(&ptask->pt_mutex);
+		if (ptask->pt_state == PTS_ACTIVE) {
+			stmf_abort(STMF_QUEUE_TASK_ABORT, ptask->pt_stmf_task,
+			    STMF_ABORTED, NULL);
+		}
+		mutex_exit(&ptask->pt_mutex);
+	}
+
+	/*
+	 * Now that all the tasks are aborting the session refcnt should
+	 * go to 0.
+	 */
+	while (ps->ps_refcnt != 0) {
+		cv_wait(&ps->ps_cv, &ps->ps_mutex);
+	}
+
+	avl_remove(&tgt->target_sess_list, ps);
+	avl_remove(&pppt_global.global_sess_list, ps);
+	(void) taskq_dispatch(pppt_global.global_sess_taskq,
+	    &pppt_sess_destroy_task, ps, KM_SLEEP);
+
+	PPPT_LOG(CE_NOTE, "Session close complete %p", (void *)ps);
+}
+
+pppt_task_t *
+pppt_task_alloc(void)
+{
+	pppt_task_t	*ptask;
+	pppt_buf_t	*immed_pbuf;
+
+	ptask = kmem_alloc(sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
+	    sizeof (stmf_data_buf_t), KM_NOSLEEP);
+	if (ptask != NULL) {
+		ptask->pt_state = PTS_INIT;
+		ptask->pt_read_buf = NULL;
+		ptask->pt_read_xfer_msgid = 0;
+		cv_init(&ptask->pt_cv, NULL, CV_DRIVER, NULL);
+		mutex_init(&ptask->pt_mutex, NULL, MUTEX_DRIVER, NULL);
+		immed_pbuf = (pppt_buf_t *)(ptask + 1);
+		bzero(immed_pbuf, sizeof (*immed_pbuf));
+		immed_pbuf->pbuf_is_immed = B_TRUE;
+		immed_pbuf->pbuf_stmf_buf = (stmf_data_buf_t *)(immed_pbuf + 1);
+
+		bzero(immed_pbuf->pbuf_stmf_buf, sizeof (stmf_data_buf_t));
+		immed_pbuf->pbuf_stmf_buf->db_port_private = immed_pbuf;
+		immed_pbuf->pbuf_stmf_buf->db_sglist_length = 1;
+		immed_pbuf->pbuf_stmf_buf->db_flags = DB_DIRECTION_FROM_RPORT |
+		    DB_DONT_CACHE;
+		ptask->pt_immed_data = immed_pbuf;
+	}
+
+	return (ptask);
+
+}
+
+void
+pppt_task_free(pppt_task_t *ptask)
+{
+	mutex_enter(&ptask->pt_mutex);
+	mutex_destroy(&ptask->pt_mutex);
+	cv_destroy(&ptask->pt_cv);
+	kmem_free(ptask, sizeof (pppt_task_t) + sizeof (pppt_buf_t) +
+	    sizeof (stmf_data_buf_t));
+}
+
+pppt_status_t
+pppt_task_start(pppt_task_t *ptask)
+{
+	avl_index_t		where;
+
+	ASSERT(ptask->pt_state == PTS_INIT);
+
+	mutex_enter(&ptask->pt_sess->ps_mutex);
+	mutex_enter(&ptask->pt_mutex);
+	if (avl_find(&ptask->pt_sess->ps_task_list, ptask, &where) == NULL) {
+		pppt_task_update_state(ptask, PTS_ACTIVE);
+		avl_insert(&ptask->pt_sess->ps_task_list, ptask, where);
+		mutex_exit(&ptask->pt_mutex);
+		mutex_exit(&ptask->pt_sess->ps_mutex);
+		return (PPPT_STATUS_SUCCESS);
+	}
+	mutex_exit(&ptask->pt_mutex);
+	mutex_exit(&ptask->pt_sess->ps_mutex);
+
+	return (PPPT_STATUS_FAIL);
+}
+
+pppt_status_t
+pppt_task_done(pppt_task_t *ptask)
+{
+	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
+	boolean_t	remove = B_FALSE;
+
+	mutex_enter(&ptask->pt_mutex);
+
+	switch (ptask->pt_state) {
+	case PTS_ACTIVE:
+		remove = B_TRUE;
+		pppt_task_update_state(ptask, PTS_DONE);
+		break;
+	case PTS_ABORTED:
+		pppt_status = PPPT_STATUS_ABORTED;
+		break;
+	case PTS_DONE:
+		/* Repeat calls are OK.  Do nothing, return success */
+		break;
+	default:
+		ASSERT(0);
+	}
+
+	mutex_exit(&ptask->pt_mutex);
+
+	if (remove) {
+		mutex_enter(&ptask->pt_sess->ps_mutex);
+		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
+		mutex_exit(&ptask->pt_sess->ps_mutex);
+	}
+
+	return (pppt_status);
+}
+
+void
+pppt_task_sent_status(pppt_task_t *ptask)
+{
+	/*
+	 * If STMF tries to abort a task after the task state changed to
+	 * PTS_DONE (meaning all task processing is complete from
+	 * the port provider perspective) then we return STMF_BUSY
+	 * from pppt_lport_abort.  STMF will return after a short interval
+	 * but our calls to stmf_send_status_done will be ignored since
+	 * STMF is aborting the task.  That's where this state comes in.
+	 * This state essentially says we are calling stmf_send_status_done
+	 * so we will not be touching the task again.  The next time
+	 * STMF calls pppt_lport_abort we will return a success full
+	 * status and the abort will succeed.
+	 */
+	mutex_enter(&ptask->pt_mutex);
+	pppt_task_update_state(ptask, PTS_SENT_STATUS);
+	mutex_exit(&ptask->pt_mutex);
+}
+
+pppt_task_t *
+pppt_task_lookup(stmf_ic_msgid_t msgid)
+{
+	pppt_tgt_t	*tgt;
+	pppt_sess_t	*sess;
+	pppt_task_t	lookup_task;
+	pppt_task_t	*result;
+
+	bzero(&lookup_task, sizeof (lookup_task));
+	lookup_task.pt_task_id = msgid;
+	PPPT_GLOBAL_LOCK();
+	for (tgt = avl_first(&pppt_global.global_target_list); tgt != NULL;
+	    tgt = AVL_NEXT(&pppt_global.global_target_list, tgt)) {
+
+		mutex_enter(&tgt->target_mutex);
+		for (sess = avl_first(&tgt->target_sess_list); sess != NULL;
+		    sess = AVL_NEXT(&tgt->target_sess_list, sess)) {
+			mutex_enter(&sess->ps_mutex);
+			if ((result = avl_find(&sess->ps_task_list,
+			    &lookup_task, NULL)) != NULL) {
+				if (pppt_task_hold(result) !=
+				    PPPT_STATUS_SUCCESS) {
+					result = NULL;
+				}
+				mutex_exit(&sess->ps_mutex);
+				mutex_exit(&tgt->target_mutex);
+				PPPT_GLOBAL_UNLOCK();
+				return (result);
+			}
+			mutex_exit(&sess->ps_mutex);
+		}
+		mutex_exit(&tgt->target_mutex);
+	}
+	PPPT_GLOBAL_UNLOCK();
+
+	return (NULL);
+}
+
+static int
+pppt_task_avl_compare(const void *void_task1, const void *void_task2)
+{
+	const pppt_task_t	*ptask1 = void_task1;
+	const pppt_task_t	*ptask2 = void_task2;
+
+	if (ptask1->pt_task_id < ptask2->pt_task_id)
+		return (-1);
+	else if (ptask1->pt_task_id > ptask2->pt_task_id)
+		return (1);
+
+	return (0);
+}
+
+static pppt_status_t
+pppt_task_try_abort(pppt_task_t *ptask)
+{
+	boolean_t	remove = B_FALSE;
+	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
+
+	mutex_enter(&ptask->pt_mutex);
+
+	switch (ptask->pt_state) {
+	case PTS_ACTIVE:
+		remove = B_TRUE;
+		pppt_task_update_state(ptask, PTS_ABORTED);
+		break;
+	case PTS_DONE:
+		pppt_status = PPPT_STATUS_DONE;
+		break;
+	case PTS_SENT_STATUS:
+		/*
+		 * Already removed so leave remove set to B_FALSE
+		 * and leave status set to PPPT_STATUS_SUCCESS.
+		 */
+		pppt_task_update_state(ptask, PTS_ABORTED);
+		break;
+	case PTS_ABORTED:
+		break;
+	default:
+		ASSERT(0);
+	}
+
+	mutex_exit(&ptask->pt_mutex);
+
+	if (remove) {
+		mutex_enter(&ptask->pt_sess->ps_mutex);
+		avl_remove(&ptask->pt_sess->ps_task_list, ptask);
+		mutex_exit(&ptask->pt_sess->ps_mutex);
+	}
+
+	return (pppt_status);
+}
+
+static pppt_status_t
+pppt_task_hold(pppt_task_t *ptask)
+{
+	pppt_status_t	pppt_status = PPPT_STATUS_SUCCESS;
+
+	mutex_enter(&ptask->pt_mutex);
+	if (ptask->pt_state == PTS_ACTIVE) {
+		ptask->pt_refcnt++;
+	} else {
+		pppt_status = PPPT_STATUS_FAIL;
+	}
+	mutex_exit(&ptask->pt_mutex);
+
+	return (pppt_status);
+}
+
+static void
+pppt_task_rele(pppt_task_t *ptask)
+{
+	mutex_enter(&ptask->pt_mutex);
+	ptask->pt_refcnt--;
+	cv_signal(&ptask->pt_cv);
+	mutex_exit(&ptask->pt_mutex);
+}
+
+static void
+pppt_task_update_state(pppt_task_t *ptask,
+    pppt_task_state_t new_state)
+{
+	PPPT_LOG(CE_NOTE, "task %p %d -> %d", (void *)ptask,
+	    ptask->pt_state, new_state);
+
+	ASSERT(mutex_owned(&ptask->pt_mutex));
+	ptask->pt_state = new_state;
+	cv_signal(&ptask->pt_cv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/pppt.conf	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,30 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+name="pppt" parent="/pseudo";
+
+pppt-node-id=0;
+pppt-ic-remote-ip="0.0.0.0";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/pppt.h	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,304 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef _PPPT_H
+#define	_PPPT_H
+
+#include <sys/pppt_ic_if.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	PPPT_GLOBAL_LOCK() mutex_enter(&pppt_global.global_lock)
+#define	PPPT_GLOBAL_UNLOCK() mutex_exit(&pppt_global.global_lock)
+
+extern int pppt_logging;
+
+#define	PPPT_LOG if (pppt_logging) cmn_err
+
+#define	TGT_DEREG_RETRY_SECONDS	1
+
+typedef enum {
+	PPPT_STATUS_SUCCESS = 0,
+	PPPT_STATUS_FAIL,
+	PPPT_STATUS_ABORTED,
+	PPPT_STATUS_DONE
+} pppt_status_t;
+
+#define	PPPT_MODNAME "pppt"
+
+/* Target states and events, update pppt_ts_name table whenever modified */
+typedef enum {
+	TS_UNDEFINED = 0,
+	TS_CREATED,
+	TS_ONLINING,
+	TS_ONLINE,
+	TS_STMF_ONLINE,
+	TS_DELETING_NEED_OFFLINE,
+	TS_OFFLINING,
+	TS_OFFLINE,
+	TS_STMF_OFFLINE,
+	TS_DELETING_STMF_DEREG,
+	TS_DELETING_STMF_DEREG_FAIL,
+	TS_DELETING,
+	TS_MAX_STATE
+} pppt_tgt_state_t;
+
+#ifdef PPPT_TGT_SM_STRINGS
+static const char *pppt_ts_name[TS_MAX_STATE+1] = {
+	"TS_UNDEFINED",
+	"TS_CREATED",
+	"TS_ONLINING",
+	"TS_ONLINE",
+	"TS_STMF_ONLINE",
+	"TS_DELETING_NEED_OFFLINE",
+	"TS_OFFLINING",
+	"TS_OFFLINE",
+	"TS_STMF_OFFLINE",
+	"TS_DELETING_STMF_DEREG",
+	"TS_DELETING_STMF_DEREG_FAIL",
+	"TS_DELETING",
+	"TS_MAX_STATE"
+};
+#endif
+
+typedef enum {
+	TE_UNDEFINED = 0,
+	TE_STMF_ONLINE_REQ,
+	TE_ONLINE_SUCCESS,
+	TE_ONLINE_FAIL,
+	TE_STMF_ONLINE_COMPLETE_ACK,
+	TE_STMF_OFFLINE_REQ,
+	TE_OFFLINE_COMPLETE,
+	TE_STMF_OFFLINE_COMPLETE_ACK,
+	TE_DELETE,
+	TE_STMF_DEREG_SUCCESS,
+	TE_STMF_DEREG_FAIL,
+	TE_STMF_DEREG_RETRY,
+	TE_WAIT_REF_COMPLETE, /* XXX */
+	TE_MAX_EVENT
+} pppt_tgt_event_t;
+
+#ifdef PPPT_TGT_SM_STRINGS
+static const char *pppt_te_name[TE_MAX_EVENT+1] = {
+	"TE_UNDEFINED",
+	"TE_STMF_ONLINE_REQ",
+	"TE_ONLINE_SUCCESS",
+	"TE_ONLINE_FAIL",
+	"TE_STMF_ONLINE_COMPLETE_ACK",
+	"TE_STMF_OFFLINE_REQ",
+	"TE_OFFLINE_COMPLETE",
+	"TE_STMF_OFFLINE_COMPLETE_ACK",
+	"TE_DELETE",
+	"TE_STMF_DEREG_SUCCESS",
+	"TE_STMF_DEREG_FAIL",
+	"TE_STMF_DEREG_RETRY",
+	"TE_WAIT_REF_COMPLETE",
+	"TE_MAX_EVENT"
+};
+#endif
+
+typedef struct pppt_tgt_s {
+	kmutex_t		target_mutex;
+	kcondvar_t		target_cv;
+	avl_node_t		target_global_ln;
+	scsi_devid_desc_t	*target_devid;
+	stmf_local_port_t	*target_stmf_lport;
+	avl_tree_t		target_sess_list;
+
+	/* Target state */
+	boolean_t		target_sm_busy;
+	boolean_t		target_deleting;
+	pppt_tgt_state_t	target_state;
+	pppt_tgt_state_t	target_last_state;
+	int			target_refcount;
+	list_t			target_events;
+} pppt_tgt_t;
+
+typedef struct {
+	struct pppt_tgt_s	*ps_target;
+	uint64_t		ps_session_id;
+	int			ps_refcnt;
+	kmutex_t		ps_mutex;
+	kcondvar_t		ps_cv;
+	boolean_t		ps_closed;
+	avl_node_t		ps_global_ln;
+	avl_node_t		ps_target_ln;
+	avl_tree_t		ps_task_list;
+	stmf_scsi_session_t	*ps_stmf_sess;
+} pppt_sess_t;
+
+typedef struct {
+	stmf_data_buf_t		*pbuf_stmf_buf;
+	boolean_t		pbuf_is_immed;
+	stmf_ic_msg_t		*pbuf_immed_msg;
+} pppt_buf_t;
+
+typedef enum {
+	PTS_INIT = 0,
+	PTS_ACTIVE,
+	PTS_DONE,
+	PTS_SENT_STATUS,
+	PTS_ABORTED
+} pppt_task_state_t;
+
+typedef struct {
+	pppt_sess_t		*pt_sess;
+	avl_node_t		pt_sess_ln;
+	int			pt_refcnt;
+	kmutex_t		pt_mutex;
+	kcondvar_t		pt_cv;
+	stmf_ic_msgid_t		pt_task_id;
+	uint8_t			pt_lun_id[16];
+	pppt_task_state_t	pt_state;
+	scsi_task_t		*pt_stmf_task;
+	pppt_buf_t		*pt_immed_data;
+	pppt_buf_t		*pt_read_buf;
+	stmf_ic_msgid_t		pt_read_xfer_msgid;
+} pppt_task_t;
+
+/*
+ * Error statistics
+ */
+typedef struct {
+	uint64_t		es_tgt_reg_svc_disabled;
+	uint64_t		es_tgt_reg_duplicate;
+	uint64_t		es_tgt_reg_create_fail;
+	uint64_t		es_tgt_dereg_svc_disabled;
+	uint64_t		es_tgt_dereg_not_found;
+	uint64_t		es_sess_destroy_no_session;
+	uint64_t		es_sess_lookup_no_session;
+	uint64_t		es_sess_lookup_ident_mismatch;
+	uint64_t		es_sess_lookup_bad_tgt_state;
+	uint64_t		es_scmd_ptask_alloc_fail;
+	uint64_t		es_scmd_sess_create_fail;
+	uint64_t		es_scmd_stask_alloc_fail;
+	uint64_t		es_scmd_dup_task_count;
+} pppt_error_stats_t;
+
+#define	PPPT_INC_STAT(stat_field) \
+	atomic_inc_64(&pppt_global.global_error_stats.stat_field);
+
+/*
+ * State values for the iscsit service
+ */
+typedef enum {
+	PSS_UNDEFINED = 0,
+	PSS_DETACHED,
+	PSS_DISABLED,
+	PSS_ENABLING,
+	PSS_ENABLED,
+	PSS_BUSY,
+	PSS_DISABLING
+} pppt_service_state_t;
+
+
+typedef struct {
+	pppt_service_state_t	global_svc_state;
+	dev_info_t		*global_dip;
+	stmf_port_provider_t	*global_pp;
+	stmf_dbuf_store_t	*global_dbuf_store;
+	taskq_t			*global_dispatch_taskq;
+	taskq_t			*global_sess_taskq;
+	avl_tree_t		global_sess_list;
+	avl_tree_t		global_target_list;
+	kmutex_t		global_lock;
+	door_handle_t		global_door;
+	kmutex_t		global_door_lock;
+	pppt_error_stats_t	global_error_stats;
+} pppt_global_t;
+
+extern pppt_global_t pppt_global;
+
+stmf_status_t pppt_lport_xfer_data(scsi_task_t *task, stmf_data_buf_t *dbuf,
+    uint32_t ioflags);
+
+void pppt_xfer_read_complete(pppt_task_t *pppt_task, stmf_status_t status);
+
+stmf_status_t pppt_lport_send_status(scsi_task_t *task, uint32_t ioflags);
+
+void pppt_lport_task_free(scsi_task_t *task);
+
+stmf_status_t pppt_lport_abort(stmf_local_port_t *lport, int abort_cmd,
+    void *arg, uint32_t flags);
+
+void pppt_lport_ctl(stmf_local_port_t *lport, int cmd, void *arg);
+
+pppt_sess_t *pppt_sess_lookup_locked(uint64_t session_id,
+    scsi_devid_desc_t *lport_devid,
+    scsi_devid_desc_t *rport_devid);
+
+pppt_sess_t *pppt_sess_lookup_by_id_locked(uint64_t session_id);
+
+pppt_sess_t *pppt_sess_lookup_create(scsi_devid_desc_t *lport_devid,
+    scsi_devid_desc_t *rport_devid, uint64_t session_id,
+    stmf_status_t *statusp);
+
+void pppt_sess_rele(pppt_sess_t *sks);
+
+void pppt_sess_rele_locked(pppt_sess_t *sks);
+
+void pppt_sess_close_locked(pppt_sess_t *ps);
+
+int pppt_sess_avl_compare_by_id(const void *void_sess1,
+    const void *void_sess2);
+
+int pppt_sess_avl_compare_by_name(const void *void_sess1,
+    const void *void_sess2);
+
+pppt_task_t *pppt_task_alloc(void);
+
+void pppt_task_free(pppt_task_t *ptask);
+
+pppt_status_t pppt_task_start(pppt_task_t *ptask);
+
+pppt_status_t pppt_task_done(pppt_task_t *ptask);
+
+pppt_task_t *pppt_task_lookup(stmf_ic_msgid_t msgid);
+
+void pppt_msg_rx(stmf_ic_msg_t *msg);
+
+void pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status);
+
+pppt_tgt_t *pppt_tgt_lookup(scsi_devid_desc_t *tgt_devid);
+
+pppt_tgt_t *pppt_tgt_lookup_locked(scsi_devid_desc_t *tgt_devid);
+
+pppt_tgt_t *pppt_tgt_create(stmf_ic_reg_port_msg_t *reg_port,
+    stmf_status_t *errcode);
+
+void pppt_tgt_async_delete(pppt_tgt_t *tgt);
+
+void pppt_tgt_destroy(pppt_tgt_t *tgt);
+
+int pppt_tgt_avl_compare(const void *void_tgt1, const void *void_tgt2);
+
+void pppt_tgt_sm_ctl(stmf_local_port_t *lport, int cmd, void *arg);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _PPPT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/pppt_msg.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,405 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cpuvar.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/sysmacros.h>
+
+#include <sys/socket.h>
+#include <sys/strsubr.h>
+#include <sys/door.h>
+
+#include <sys/stmf.h>
+#include <sys/stmf_ioctl.h>
+#include <sys/portif.h>
+#include <pppt.h>
+
+static void pppt_msg_tgt_register(stmf_ic_msg_t *reg_port);
+
+static void pppt_msg_tgt_deregister(stmf_ic_msg_t *msg);
+
+static void pppt_msg_session_destroy(stmf_ic_msg_t *msg);
+
+static void pppt_msg_scsi_cmd(stmf_ic_msg_t *msg);
+
+static void pppt_msg_data_xfer_done(stmf_ic_msg_t *msg);
+
+static void pppt_msg_handle_status(stmf_ic_msg_t *msg);
+
+void
+pppt_msg_rx(stmf_ic_msg_t *msg)
+{
+	switch (msg->icm_msg_type) {
+	case STMF_ICM_REGISTER_PROXY_PORT:
+		pppt_msg_tgt_register(msg);
+		break;
+	case STMF_ICM_DEREGISTER_PROXY_PORT:
+		pppt_msg_tgt_deregister(msg);
+		break;
+	case STMF_ICM_SESSION_CREATE:
+		pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
+		stmf_ic_msg_free(msg);
+		break;
+	case STMF_ICM_SESSION_DESTROY:
+		pppt_msg_session_destroy(msg);
+		break;
+	case STMF_ICM_SCSI_CMD:
+		pppt_msg_scsi_cmd(msg);
+		break;
+	case STMF_ICM_SCSI_DATA_XFER_DONE:
+		pppt_msg_data_xfer_done(msg);
+		break;
+	case STMF_ICM_SCSI_DATA:
+		/* Ignore, all proxy data will be immediate for now */
+		pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
+		stmf_ic_msg_free(msg);
+		break;
+	case STMF_ICM_STATUS:
+		pppt_msg_handle_status(msg);
+		break;
+	default:
+		/* Other message types are not allowed */
+		ASSERT(0);
+		break;
+	}
+}
+
+void
+pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status)
+{
+	stmf_ic_msg_t	*msg;
+
+	/*
+	 * If TX of status fails it should be treated the same as a loss of
+	 * connection.  We expect the remote node to handle it.
+	 */
+	msg = stmf_ic_status_msg_alloc(status, orig_msg->icm_msg_type,
+	    orig_msg->icm_msgid);
+
+	if (msg != NULL) {
+		(void) stmf_ic_tx_msg(msg);
+	}
+}
+
+static void
+pppt_msg_tgt_register(stmf_ic_msg_t *msg)
+{
+	stmf_ic_reg_port_msg_t	*reg_port;
+	pppt_tgt_t		*result;
+	stmf_status_t		stmf_status;
+
+	reg_port = msg->icm_msg;
+
+
+	PPPT_GLOBAL_LOCK();
+	if (pppt_global.global_svc_state != PSS_ENABLED) {
+		stmf_status = STMF_FAILURE;
+		PPPT_INC_STAT(es_tgt_reg_svc_disabled);
+		goto pppt_register_tgt_done;
+	}
+
+	/*
+	 * For now we assume that the marshall/unmarshall code is responsible
+	 * for validating the message length and ensuring the resulting
+	 * request structure is self consistent.  Make sure this
+	 * target doesn't already exist.
+	 */
+	if ((result = pppt_tgt_lookup_locked(reg_port->icrp_port_id)) != NULL) {
+		stmf_status = STMF_ALREADY;
+		PPPT_INC_STAT(es_tgt_reg_duplicate);
+		goto pppt_register_tgt_done;
+	}
+
+	result = pppt_tgt_create(reg_port, &stmf_status);
+
+	if (result == NULL) {
+		stmf_status = STMF_TARGET_FAILURE;
+		PPPT_INC_STAT(es_tgt_reg_create_fail);
+		goto pppt_register_tgt_done;
+	}
+
+	avl_add(&pppt_global.global_target_list, result);
+
+	stmf_status = STMF_SUCCESS;
+
+pppt_register_tgt_done:
+	PPPT_GLOBAL_UNLOCK();
+	pppt_msg_tx_status(msg, stmf_status);
+	stmf_ic_msg_free(msg);
+}
+
+static void
+pppt_msg_tgt_deregister(stmf_ic_msg_t *msg)
+{
+	stmf_ic_dereg_port_msg_t	*dereg_port;
+	stmf_status_t			stmf_status;
+	pppt_tgt_t			*tgt;
+
+	PPPT_GLOBAL_LOCK();
+	if (pppt_global.global_svc_state != PSS_ENABLED) {
+		PPPT_GLOBAL_UNLOCK();
+		stmf_status = STMF_FAILURE;
+		PPPT_INC_STAT(es_tgt_dereg_svc_disabled);
+		goto pppt_deregister_tgt_done;
+	}
+
+	dereg_port = msg->icm_msg;
+
+	/* Lookup target */
+	if ((tgt = pppt_tgt_lookup_locked(dereg_port->icdp_port_id)) == NULL) {
+		PPPT_GLOBAL_UNLOCK();
+		stmf_status = STMF_NOT_FOUND;
+		PPPT_INC_STAT(es_tgt_dereg_not_found);
+		goto pppt_deregister_tgt_done;
+	}
+	avl_remove(&pppt_global.global_target_list, tgt);
+	pppt_tgt_async_delete(tgt);
+
+	PPPT_GLOBAL_UNLOCK();
+
+	/* Wait for delete to complete */
+	mutex_enter(&tgt->target_mutex);
+	while ((tgt->target_refcount > 0) ||
+	    (tgt->target_state != TS_DELETING)) {
+		cv_wait(&tgt->target_cv, &tgt->target_mutex);
+	}
+	mutex_exit(&tgt->target_mutex);
+
+	pppt_tgt_destroy(tgt);
+	stmf_status = STMF_SUCCESS;
+
+pppt_deregister_tgt_done:
+	pppt_msg_tx_status(msg, stmf_status);
+	stmf_ic_msg_free(msg);
+}
+
+static void
+pppt_msg_session_destroy(stmf_ic_msg_t *msg)
+{
+	stmf_ic_session_create_destroy_msg_t	*sess_destroy;
+	pppt_tgt_t				*tgt;
+	pppt_sess_t				*ps;
+
+	sess_destroy = msg->icm_msg;
+
+	PPPT_GLOBAL_LOCK();
+
+	/*
+	 * Look for existing session for this ID
+	 */
+	ps = pppt_sess_lookup_locked(sess_destroy->icscd_session_id,
+	    sess_destroy->icscd_tgt_devid, sess_destroy->icscd_ini_devid);
+
+	if (ps == NULL) {
+		PPPT_GLOBAL_UNLOCK();
+		stmf_ic_msg_free(msg);
+		PPPT_INC_STAT(es_sess_destroy_no_session);
+		return;
+	}
+
+	tgt = ps->ps_target;
+
+	mutex_enter(&tgt->target_mutex);
+	mutex_enter(&ps->ps_mutex);
+
+	/* Release the reference from the lookup */
+	pppt_sess_rele_locked(ps);
+
+	/* Make sure another thread is not already closing the session */
+	if (!ps->ps_closed) {
+		/* Found matching open session, quiesce... */
+		pppt_sess_close_locked(ps);
+	}
+	mutex_exit(&ps->ps_mutex);
+	mutex_exit(&tgt->target_mutex);
+	PPPT_GLOBAL_UNLOCK();
+
+	stmf_ic_msg_free(msg);
+}
+
+static void
+pppt_msg_scsi_cmd(stmf_ic_msg_t *msg)
+{
+	pppt_sess_t			*pppt_sess;
+	pppt_buf_t			*pbuf;
+	stmf_ic_scsi_cmd_msg_t		*scmd;
+	pppt_task_t			*ptask;
+	scsi_task_t			*task;
+	pppt_status_t			pppt_status;
+	stmf_local_port_t		*lport;
+	stmf_scsi_session_t		*stmf_sess;
+	stmf_status_t			stmf_status;
+
+	/*
+	 * Get a task context
+	 */
+	ptask = pppt_task_alloc();
+	if (ptask == NULL) {
+		/*
+		 * We must be very low on memory.  Just free the message
+		 * and let the command timeout.
+		 */
+		stmf_ic_msg_free(msg);
+		PPPT_INC_STAT(es_scmd_ptask_alloc_fail);
+		return;
+	}
+
+	scmd = msg->icm_msg;
+
+	/*
+	 * Session are created implicitly on the first use of an
+	 * IT nexus
+	 */
+	pppt_sess = pppt_sess_lookup_create(scmd->icsc_tgt_devid,
+	    scmd->icsc_ini_devid, scmd->icsc_session_id, &stmf_status);
+	if (pppt_sess == NULL) {
+		pppt_task_free(ptask);
+		pppt_msg_tx_status(msg, stmf_status);
+		stmf_ic_msg_free(msg);
+		PPPT_INC_STAT(es_scmd_sess_create_fail);
+		return;
+	}
+
+	ptask->pt_sess = pppt_sess;
+	ptask->pt_task_id = scmd->icsc_task_msgid;
+	stmf_sess = pppt_sess->ps_stmf_sess;
+	lport = stmf_sess->ss_lport;
+
+	/*
+	 * Add task to our internal task set.
+	 */
+	pppt_status = pppt_task_start(ptask);
+
+	if (pppt_status != 0) {
+		/* Release hold from pppt_sess_lookup_create() */
+		PPPT_LOG(CE_WARN, "Duplicate taskid from remote node 0x%llx",
+		    (longlong_t)scmd->icsc_task_msgid);
+		pppt_task_free(ptask);
+		pppt_sess_rele(pppt_sess);
+		pppt_msg_tx_status(msg, STMF_ALREADY);
+		stmf_ic_msg_free(msg);
+		PPPT_INC_STAT(es_scmd_dup_task_count);
+		return;
+	}
+
+	/*
+	 * Allocate STMF task context
+	 */
+	ptask->pt_stmf_task = stmf_task_alloc(lport, stmf_sess,
+	    scmd->icsc_task_lun_no,
+	    scmd->icsc_task_cdb_length, 0);
+	if (ptask->pt_stmf_task == NULL) {
+		(void) pppt_task_done(ptask);
+		pppt_task_free(ptask);
+		pppt_sess_rele(pppt_sess);
+		pppt_msg_tx_status(msg, STMF_ALLOC_FAILURE);
+		stmf_ic_msg_free(msg);
+		PPPT_INC_STAT(es_scmd_stask_alloc_fail);
+		return;
+	}
+
+	task = ptask->pt_stmf_task;
+	task->task_port_private = ptask;
+	task->task_flags = scmd->icsc_task_flags;
+	task->task_additional_flags = 0;
+	task->task_priority = 0;
+
+	/*
+	 * Set task->task_mgmt_function to TM_NONE for a normal SCSI task
+	 * or one of these values for a task management command:
+	 *
+	 * TM_ABORT_TASK ***
+	 * TM_ABORT_TASK_SET
+	 * TM_CLEAR_ACA
+	 * TM_CLEAR_TASK_SET
+	 * TM_LUN_RESET
+	 * TM_TARGET_WARM_RESET
+	 * TM_TARGET_COLD_RESET
+	 *
+	 * *** Note that STMF does not currently support TM_ABORT_TASK so
+	 * port providers must implement this command on their own
+	 * (e.g. lookup the desired task and call stmf_abort).
+	 */
+	task->task_mgmt_function = scmd->icsc_task_mgmt_function;
+
+	task->task_max_nbufs = STMF_BUFS_MAX; /* Or protocol value */
+	task->task_cmd_seq_no = msg->icm_msgid;
+	task->task_expected_xfer_length =
+	    scmd->icsc_task_expected_xfer_length;
+
+	bcopy(scmd->icsc_task_cdb, task->task_cdb,
+	    scmd->icsc_task_cdb_length);
+	bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16);
+
+	if (scmd->icsc_immed_data_len) {
+		pbuf = ptask->pt_immed_data;
+		pbuf->pbuf_immed_msg = msg;
+		pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len;
+		pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len;
+		pbuf->pbuf_stmf_buf->db_relative_offset = 0;
+		pbuf->pbuf_stmf_buf->db_sglist[0].seg_length =
+		    scmd->icsc_immed_data_len;
+		pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr =
+		    scmd->icsc_immed_data;
+
+		stmf_post_task(task, pbuf->pbuf_stmf_buf);
+	} else {
+		stmf_post_task(task, NULL);
+		stmf_ic_msg_free(msg);
+	}
+}
+
+static void
+pppt_msg_data_xfer_done(stmf_ic_msg_t *msg)
+{
+	pppt_task_t				*pppt_task;
+	stmf_ic_scsi_data_xfer_done_msg_t	*data_xfer_done;
+
+	data_xfer_done = msg->icm_msg;
+
+	/*
+	 * Find task
+	 */
+	pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid);
+
+	/* If we found one, complete the transfer */
+	if (pppt_task != NULL) {
+		pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status);
+	}
+
+	stmf_ic_msg_free(msg);
+}
+
+static void
+pppt_msg_handle_status(stmf_ic_msg_t *msg)
+{
+	/* Don't care for now */
+	stmf_ic_msg_free(msg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/comstar/port/pppt/pppt_tgt.c	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,890 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cpuvar.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/sysmacros.h>
+
+#include <sys/socket.h>
+#include <sys/strsubr.h>
+#include <sys/door.h>
+#include <sys/note.h>
+#include <sys/sdt.h>
+
+#include <sys/stmf.h>
+#include <sys/stmf_ioctl.h>
+#include <sys/portif.h>
+#define	PPPT_TGT_SM_STRINGS
+#include <pppt.h>
+
+typedef struct {
+	list_node_t		te_ctx_node;
+	pppt_tgt_event_t	te_ctx_event;
+} tgt_event_ctx_t;
+
+static void
+pppt_tgt_sm_event(pppt_tgt_t *tgt, pppt_tgt_event_t event);
+
+static void
+tgt_sm_event_locked(pppt_tgt_t *tgt, pppt_tgt_event_t event);
+
+static void
+tgt_sm_event_dispatch(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_created(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_onlining(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_online(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_stmf_online(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_deleting_need_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_offlining(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_stmf_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_deleting_stmf_dereg(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_deleting_stmf_dereg_fail(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+tgt_sm_deleting(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx);
+
+static void
+pppt_tgt_dereg_retry(void *arg);
+
+static void
+pppt_tgt_dereg_task(void *arg);
+
+static void
+tgt_sm_new_state(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx,
+    pppt_tgt_state_t new_state);
+
+/*ARGSUSED*/
+void
+pppt_tgt_sm_ctl(stmf_local_port_t *lport, int cmd, void *arg)
+{
+	pppt_tgt_t		*pppt_tgt;
+
+	pppt_tgt = (pppt_tgt_t *)lport->lport_port_private;
+
+	switch (cmd) {
+	case STMF_CMD_LPORT_ONLINE:
+		pppt_tgt_sm_event(pppt_tgt, TE_STMF_ONLINE_REQ);
+		break;
+	case STMF_CMD_LPORT_OFFLINE:
+		pppt_tgt_sm_event(pppt_tgt, TE_STMF_OFFLINE_REQ);
+		break;
+	case STMF_ACK_LPORT_ONLINE_COMPLETE:
+		pppt_tgt_sm_event(pppt_tgt, TE_STMF_ONLINE_COMPLETE_ACK);
+		break;
+	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
+		pppt_tgt_sm_event(pppt_tgt, TE_STMF_OFFLINE_COMPLETE_ACK);
+		break;
+
+	default:
+		ASSERT(0);
+		break;
+	}
+}
+
+pppt_tgt_t *
+pppt_tgt_create(stmf_ic_reg_port_msg_t *reg_port, stmf_status_t *msg_errcode)
+{
+	pppt_tgt_t		*result;
+	stmf_local_port_t	*lport;
+	int			total_devid_len;
+
+	total_devid_len = sizeof (scsi_devid_desc_t) +
+	    reg_port->icrp_port_id->ident_length - 1;
+
+	/*
+	 * Each target is an STMF local port.  Allocate an STMF local port
+	 * including enough space to store a scsi_devid_desc_t for this target.
+	 */
+	lport = stmf_alloc(STMF_STRUCT_STMF_LOCAL_PORT,
+	    sizeof (pppt_tgt_t) + total_devid_len, 0);
+	if (lport == NULL) {
+		*msg_errcode = STMF_ALLOC_FAILURE;
+		return (NULL);
+	}
+
+	result = lport->lport_port_private;
+	result->target_state = TS_CREATED;
+	/* Use pointer arithmetic to find scsi_devid_desc_t */
+	result->target_devid = (scsi_devid_desc_t *)(result + 1);
+	bcopy(reg_port->icrp_port_id, result->target_devid, total_devid_len);
+	result->target_devid->piv = 1;
+	result->target_devid->code_set = CODE_SET_ASCII;
+	result->target_devid->association = ID_IS_TARGET_PORT;
+
+	mutex_init(&result->target_mutex, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&result->target_cv, NULL, CV_DEFAULT, NULL);
+	list_create(&result->target_events, sizeof (tgt_event_ctx_t),
+	    offsetof(tgt_event_ctx_t, te_ctx_node));
+	avl_create(&result->target_sess_list, pppt_sess_avl_compare_by_name,
+	    sizeof (pppt_sess_t), offsetof(pppt_sess_t, ps_target_ln));
+
+	lport->lport_abort_timeout = 120; /* seconds */
+	lport->lport_id = result->target_devid;
+	lport->lport_pp = pppt_global.global_pp;
+	lport->lport_ds = pppt_global.global_dbuf_store;
+	lport->lport_xfer_data = &pppt_lport_xfer_data;
+	lport->lport_send_status = &pppt_lport_send_status;
+	lport->lport_task_free = &pppt_lport_task_free;
+	lport->lport_abort = &pppt_lport_abort;
+	lport->lport_ctl = &pppt_lport_ctl;
+	result->target_stmf_lport = lport;
+
+	/*
+	 * Since this is a proxy port we need to do set the relative
+	 * target port identifier before registering it with STMF.
+	 */
+	stmf_set_port_standby(lport, reg_port->icrp_relative_port_id);
+
+	/*
+	 * Register the target with STMF.  STMF may immediately ask us to go
+	 * online so insure any additional config setup is complete.
+	 */
+	if (stmf_register_local_port(lport) != STMF_SUCCESS) {
+		*msg_errcode = STMF_FAILURE;
+		pppt_tgt_destroy(result);
+		return (NULL);
+	}
+
+	return (result);
+
+}
+
+void
+pppt_tgt_destroy(pppt_tgt_t *tgt)
+{
+	/* Destroy target */
+	avl_destroy(&tgt->target_sess_list);
+	list_destroy(&tgt->target_events);
+	cv_destroy(&tgt->target_cv);
+	mutex_destroy(&tgt->target_mutex);
+	stmf_free(tgt->target_stmf_lport); /* Also frees "tgt' */
+}
+
+pppt_tgt_t *
+pppt_tgt_lookup(scsi_devid_desc_t *tgt_devid)
+{
+	pppt_tgt_t	*result;
+	PPPT_GLOBAL_LOCK();
+	result = pppt_tgt_lookup_locked(tgt_devid);
+	PPPT_GLOBAL_UNLOCK();
+
+	return (result);
+}
+
+pppt_tgt_t *
+pppt_tgt_lookup_locked(scsi_devid_desc_t *tgt_devid)
+{
+	pppt_tgt_t	*result;
+	pppt_tgt_t	tmptgt;
+
+	bzero(&tmptgt, sizeof (tmptgt));
+	tmptgt.target_devid = tgt_devid;
+
+	result = avl_find(&pppt_global.global_target_list, &tmptgt, NULL);
+
+	return (result);
+}
+
+void
+pppt_tgt_async_delete(pppt_tgt_t *tgt)
+{
+	/* Generate TE_DELETE event to target state machine */
+	pppt_tgt_sm_event(tgt, TE_DELETE);
+}
+
+int
+pppt_tgt_avl_compare(const void *void_tgt1, const void *void_tgt2)
+{
+	const	pppt_tgt_t	*ptgt1 = void_tgt1;
+	const	pppt_tgt_t	*ptgt2 = void_tgt2;
+	int			result;
+
+	/* Sort by code set then ident */
+	if (ptgt1->target_devid->code_set <
+	    ptgt2->target_devid->code_set) {
+		return (-1);
+	} else if (ptgt1->target_devid->code_set >
+	    ptgt2->target_devid->code_set) {
+		return (1);
+	}
+
+	/* Next by ident length */
+	if (ptgt1->target_devid->ident_length <
+	    ptgt2->target_devid->ident_length) {
+		return (-1);
+	} else if (ptgt1->target_devid->ident_length >
+	    ptgt2->target_devid->ident_length) {
+		return (1);
+	}
+
+	/* Code set and ident length both match, now compare idents */
+	result = memcmp(ptgt1->target_devid->ident, ptgt2->target_devid->ident,
+	    ptgt1->target_devid->ident_length);
+
+	if (result < 0) {
+		return (-1);
+	} else if (result > 0) {
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Target state machine
+ */
+
+static void
+pppt_tgt_sm_event(pppt_tgt_t *tgt, pppt_tgt_event_t event)
+{
+	mutex_enter(&tgt->target_mutex);
+	tgt_sm_event_locked(tgt, event);
+	mutex_exit(&tgt->target_mutex);
+}
+
+static void
+tgt_sm_event_locked(pppt_tgt_t *tgt, pppt_tgt_event_t event)
+{
+	tgt_event_ctx_t *ctx;
+
+	tgt->target_refcount++;
+
+	ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
+
+	ctx->te_ctx_event = event;
+
+	list_insert_tail(&tgt->target_events, ctx);
+
+	/*
+	 * Use the target_sm_busy flag to keep the state machine single
+	 * threaded.  This also serves as recursion avoidance since this
+	 * flag will always be set if we call pppt_tgt_sm_event from
+	 * within the state machine code.
+	 */
+	if (!tgt->target_sm_busy) {
+		tgt->target_sm_busy = B_TRUE;
+		while (!list_is_empty(&tgt->target_events)) {
+			ctx = list_head(&tgt->target_events);
+			list_remove(&tgt->target_events, ctx);
+			mutex_exit(&tgt->target_mutex);
+			tgt_sm_event_dispatch(tgt, ctx);
+			mutex_enter(&tgt->target_mutex);
+		}
+		tgt->target_sm_busy = B_FALSE;
+
+	}
+
+	tgt->target_refcount--;
+	cv_signal(&tgt->target_cv);
+}
+
+static void
+tgt_sm_event_dispatch(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	DTRACE_PROBE2(pppt__tgt__event, pppt_tgt_t *, tgt,
+	    tgt_event_ctx_t *, ctx);
+
+	PPPT_LOG(CE_NOTE, "tgt_sm_event_dispatch: tgt %p event %s(%d)",
+	    (void *)tgt, pppt_te_name[ctx->te_ctx_event], ctx->te_ctx_event);
+
+	/* State independent actions */
+	switch (ctx->te_ctx_event) {
+	case TE_DELETE:
+		tgt->target_deleting = B_TRUE;
+		break;
+	}
+
+	/* State dependent actions */
+	switch (tgt->target_state) {
+	case TS_CREATED:
+		tgt_sm_created(tgt, ctx);
+		break;
+	case TS_ONLINING:
+		tgt_sm_onlining(tgt, ctx);
+		break;
+	case TS_ONLINE:
+		tgt_sm_online(tgt, ctx);
+		break;
+	case TS_STMF_ONLINE:
+		tgt_sm_stmf_online(tgt, ctx);
+		break;
+	case TS_DELETING_NEED_OFFLINE:
+		tgt_sm_deleting_need_offline(tgt, ctx);
+		break;
+	case TS_OFFLINING:
+		tgt_sm_offlining(tgt, ctx);
+		break;
+	case TS_OFFLINE:
+		tgt_sm_offline(tgt, ctx);
+		break;
+	case TS_STMF_OFFLINE:
+		tgt_sm_stmf_offline(tgt, ctx);
+		break;
+	case TS_DELETING_STMF_DEREG:
+		tgt_sm_deleting_stmf_dereg(tgt, ctx);
+		break;
+	case TS_DELETING_STMF_DEREG_FAIL:
+		tgt_sm_deleting_stmf_dereg_fail(tgt, ctx);
+		break;
+	case TS_DELETING:
+		tgt_sm_deleting(tgt, ctx);
+		break;
+	default:
+		ASSERT(0);
+	}
+
+	kmem_free(ctx, sizeof (*ctx));
+}
+
+static void
+tgt_sm_created(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_REQ:
+		tgt_sm_new_state(tgt, ctx, TS_ONLINING);
+		break;
+	case TE_DELETE:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
+		break;
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We're already offline but update to an equivelant
+		 * state just to note that STMF talked to us.
+		 */
+		scs.st_completion_status = STMF_SUCCESS;
+		scs.st_additional_info = NULL;
+		tgt_sm_new_state(tgt, ctx, TS_OFFLINE);
+		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+static void
+tgt_sm_onlining(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_ONLINE_SUCCESS:
+		tgt_sm_new_state(tgt, ctx, TS_ONLINE);
+		break;
+	case TE_ONLINE_FAIL:
+		tgt_sm_new_state(tgt, ctx, TS_STMF_OFFLINE);
+		break;
+	case TE_DELETE:
+		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
+		break;
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are busy going
+		 * online.
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+static void
+tgt_sm_online(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+		if (tgt->target_deleting) {
+			tgt_sm_new_state(tgt, ctx, TS_DELETING_NEED_OFFLINE);
+		} else {
+			tgt_sm_new_state(tgt, ctx, TS_STMF_ONLINE);
+		}
+		break;
+	case TE_DELETE:
+		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
+		break;
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are busy going
+		 * online (waiting for acknowlegement from STMF)
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_stmf_online(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_DELETE:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING_NEED_OFFLINE);
+		break;
+	case TE_STMF_OFFLINE_REQ:
+		tgt_sm_new_state(tgt, ctx, TS_OFFLINING);
+		break;
+	case TE_STMF_ONLINE_REQ:
+		/* Already online */
+		scs.st_completion_status = STMF_ALREADY;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_deleting_need_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_OFFLINE_REQ:
+		tgt_sm_new_state(tgt, ctx, TS_OFFLINING);
+		break;
+	case TE_DELETE:
+		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
+		break;
+	case TE_STMF_ONLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we need to be offlined
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_offlining(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_OFFLINE_COMPLETE:
+		tgt_sm_new_state(tgt, ctx, TS_OFFLINE);
+		break;
+	case TE_DELETE:
+		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
+		break;
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are busy going
+		 * offline.
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		if (tgt->target_deleting) {
+			tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
+		} else {
+			tgt_sm_new_state(tgt, ctx, TS_STMF_OFFLINE);
+		}
+		break;
+	case TE_DELETE:
+		/* TE_DELETE is handled in tgt_sm_event_dispatch() */
+		break;
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are busy going
+		 * offline.
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_stmf_offline(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_REQ:
+		tgt_sm_new_state(tgt, ctx, TS_ONLINING);
+		break;
+	case TE_DELETE:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
+		break;
+	case TE_STMF_OFFLINE_REQ:
+		/* Already offline */
+		scs.st_completion_status = STMF_ALREADY;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+
+static void
+tgt_sm_deleting_stmf_dereg(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	/* Terminal state, no events */
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are being deleted
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	case TE_STMF_DEREG_SUCCESS:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING);
+		break;
+	case TE_STMF_DEREG_FAIL:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG_FAIL);
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+static void
+tgt_sm_deleting_stmf_dereg_fail(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	/* Terminal state, no events */
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are being deleted
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	case TE_STMF_DEREG_RETRY:
+		tgt_sm_new_state(tgt, ctx, TS_DELETING_STMF_DEREG);
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+static void
+tgt_sm_deleting(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx)
+{
+	stmf_change_status_t	scs;
+
+	/* Terminal state, no events */
+	switch (ctx->te_ctx_event) {
+	case TE_STMF_ONLINE_REQ:
+	case TE_STMF_OFFLINE_REQ:
+		/*
+		 * We can't complete STMF's request since we are being deleted
+		 */
+		scs.st_completion_status = STMF_INVALID_ARG;
+		scs.st_additional_info = NULL;
+		(void) stmf_ctl((ctx->te_ctx_event == TE_STMF_ONLINE_REQ) ?
+		    STMF_CMD_LPORT_ONLINE_COMPLETE :
+		    STMF_CMD_LPORT_OFFLINE_COMPLETE,
+		    tgt->target_stmf_lport, &scs);
+		break;
+	case TE_STMF_ONLINE_COMPLETE_ACK:
+	case TE_STMF_OFFLINE_COMPLETE_ACK:
+		/* Ignore */
+		break;
+	default:
+		ASSERT(0);
+	}
+}
+
+static void
+pppt_tgt_offline(void *arg)
+{
+	pppt_tgt_t		*tgt = arg;
+	pppt_sess_t		*ps, *next_ps;
+	stmf_change_status_t	scs;
+
+	PPPT_LOG(CE_NOTE, "pppt_tgt_offline %p", (void *)tgt);
+
+	PPPT_GLOBAL_LOCK();
+	mutex_enter(&tgt->target_mutex);
+	for (ps = avl_first(&tgt->target_sess_list); ps != NULL; ps = next_ps) {
+		next_ps = AVL_NEXT(&tgt->target_sess_list, ps);
+		mutex_enter(&ps->ps_mutex);
+		PPPT_LOG(CE_NOTE, "pppt_tgt_offline closing session %p(%d)",
+		    (void *)ps, ps->ps_closed);
+		if (!ps->ps_closed) {
+			pppt_sess_close_locked(ps);
+		}
+		mutex_exit(&ps->ps_mutex);
+	}
+	mutex_exit(&tgt->target_mutex);
+	PPPT_GLOBAL_UNLOCK();
+
+	pppt_tgt_sm_event(tgt, TE_OFFLINE_COMPLETE);
+
+	scs.st_completion_status = STMF_SUCCESS;
+	scs.st_additional_info = NULL;
+	(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
+	    tgt->target_stmf_lport, &scs);
+
+	PPPT_LOG(CE_NOTE, "pppt_tgt_offline complete %p", (void *)tgt);
+}
+
+static void
+pppt_tgt_dereg_retry(void *arg)
+{
+	pppt_tgt_t *tgt = arg;
+
+	/*
+	 * Rather than guaranteeing the target state machine code will not
+	 * block for long periods of time (tying up this callout thread)
+	 * we will queue a task on the taskq to send the retry event.
+	 * If it fails we'll setup another timeout and try again later.
+	 */
+	if (taskq_dispatch(pppt_global.global_dispatch_taskq,
+	    pppt_tgt_dereg_task, tgt, KM_NOSLEEP) == NULL) {
+		/* Dispatch failed, try again later */
+		(void) timeout(pppt_tgt_dereg_retry, tgt,
+		    drv_usectohz(TGT_DEREG_RETRY_SECONDS * 1000000));
+	}
+}
+
+static void
+pppt_tgt_dereg_task(void *arg)
+{
+	pppt_tgt_t *tgt = arg;
+
+	pppt_tgt_sm_event(tgt, TE_STMF_DEREG_RETRY);
+}
+
+/*ARGSUSED*/
+static void
+tgt_sm_new_state(pppt_tgt_t *tgt, tgt_event_ctx_t *ctx,
+    pppt_tgt_state_t new_state)
+{
+	stmf_local_port_t		*lport = tgt->target_stmf_lport;
+	stmf_change_status_t		scs;
+	stmf_state_change_info_t	sci;
+	stmf_status_t			stmfrc;
+
+	scs.st_completion_status = STMF_SUCCESS;
+	scs.st_additional_info = NULL;
+
+	/*
+	 * Validate new state
+	 */
+	ASSERT(new_state != TS_UNDEFINED);
+	ASSERT3U(new_state, <, TS_MAX_STATE);
+
+	new_state = (new_state < TS_MAX_STATE) ?
+	    new_state : TS_UNDEFINED;
+
+	PPPT_LOG(CE_NOTE, "tgt_sm_new_state: tgt %p, %s(%d) --> %s(%d)\n",
+	    (void *) tgt, pppt_ts_name[tgt->target_state], tgt->target_state,
+	    pppt_ts_name[new_state], new_state);
+	DTRACE_PROBE3(pppt__target__state__change,
+	    pppt_tgt_t *, tgt, tgt_event_ctx_t *, ctx,
+	    pppt_tgt_state_t, new_state);
+
+	mutex_enter(&tgt->target_mutex);
+	tgt->target_last_state = tgt->target_state;
+	tgt->target_state = new_state;
+	cv_signal(&tgt->target_cv);
+	mutex_exit(&tgt->target_mutex);
+
+	switch (tgt->target_state) {
+	case TS_ONLINING:
+		pppt_tgt_sm_event(tgt, TE_ONLINE_SUCCESS);
+
+		/*
+		 * Let STMF know the how the online operation completed.
+		 * STMF will respond with an acknowlege later
+		 */
+		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, &scs);
+		break;
+	case TS_ONLINE:
+		break;
+	case TS_STMF_ONLINE:
+		break;
+	case TS_DELETING_NEED_OFFLINE:
+		sci.st_rflags = STMF_RFLAG_STAY_OFFLINED;
+		sci.st_additional_info = "Offline for delete";
+		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE, lport, &sci);
+		break;
+	case TS_OFFLINING:
+		/* Async callback generates completion event */
+		pppt_tgt_offline(tgt);
+		break;
+	case TS_OFFLINE:
+		break;
+	case TS_STMF_OFFLINE:
+		break;
+	case TS_DELETING_STMF_DEREG:
+		stmfrc = stmf_deregister_local_port(tgt->target_stmf_lport);
+		if (stmfrc == STMF_SUCCESS) {
+			pppt_tgt_sm_event(tgt, TE_STMF_DEREG_SUCCESS);
+		} else {
+			pppt_tgt_sm_event(tgt, TE_STMF_DEREG_FAIL);
+		}
+		break;
+	case TS_DELETING_STMF_DEREG_FAIL:
+		/* Retry dereg in 1 second */
+		(void) timeout(pppt_tgt_dereg_retry, tgt,
+		    drv_usectohz(TGT_DEREG_RETRY_SECONDS * 1000000));
+		break;
+	case TS_DELETING:
+		break;
+	default:
+		ASSERT(0);
+	}
+}
--- a/usr/src/uts/common/io/comstar/stmf/lun_map.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/stmf/lun_map.c	Tue Oct 06 19:56:15 2009 -0700
@@ -426,7 +426,13 @@
 	ret = stmf_add_ent_to_map(sm, (void *)lun_map_ent, lu_nbr);
 	ASSERT(ret == STMF_SUCCESS);
 	atomic_add_32(&ilu->ilu_ref_cnt, 1);
-	new_flags |= ISS_LUN_INVENTORY_CHANGED;
+	/*
+	 * do not set lun inventory flag for standby port
+	 * as this would be handled from peer
+	 */
+	if (ilport->ilport_standby == 0) {
+		new_flags |= ISS_LUN_INVENTORY_CHANGED;
+	}
 	atomic_or_32(&iss->iss_flags, new_flags);
 	return (STMF_SUCCESS);
 }
--- a/usr/src/uts/common/io/comstar/stmf/stmf.c	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/stmf/stmf.c	Tue Oct 06 19:56:15 2009 -0700
@@ -45,9 +45,12 @@
 #include <stmf_impl.h>
 #include <lun_map.h>
 #include <stmf_state.h>
+#include <pppt_ic_if.h>
 
 static uint64_t stmf_session_counter = 0;
 static uint16_t stmf_rtpid_counter = 0;
+/* start messages at 1 */
+static uint64_t stmf_proxy_msg_id = 1;
 
 static int stmf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
 static int stmf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
@@ -61,6 +64,9 @@
 static int stmf_set_stmf_state(stmf_state_desc_t *std);
 static void stmf_abort_task_offline(scsi_task_t *task, int offline_lu,
     char *info);
+static int stmf_set_alua_state(stmf_alua_state_desc_t *alua_state);
+static void stmf_get_alua_state(stmf_alua_state_desc_t *alua_state);
+stmf_xfer_data_t *stmf_prepare_tpgs_data(uint8_t ilu_alua);
 void stmf_svc_init();
 stmf_status_t stmf_svc_fini();
 void stmf_svc(void *arg);
@@ -85,6 +91,30 @@
 stmf_status_t stmf_worker_fini();
 void stmf_worker_mgmt();
 void stmf_worker_task(void *arg);
+static void stmf_task_lu_free(scsi_task_t *task, stmf_i_scsi_session_t *iss);
+static stmf_status_t stmf_ic_lu_reg(stmf_ic_reg_dereg_lun_msg_t *msg,
+    uint32_t type);
+static stmf_status_t stmf_ic_lu_dereg(stmf_ic_reg_dereg_lun_msg_t *msg);
+static stmf_status_t stmf_ic_rx_scsi_status(stmf_ic_scsi_status_msg_t *msg);
+static stmf_status_t stmf_ic_rx_status(stmf_ic_status_msg_t *msg);
+static stmf_status_t stmf_ic_rx_scsi_data(stmf_ic_scsi_data_msg_t *msg);
+void stmf_task_lu_killall(stmf_lu_t *lu, scsi_task_t *tm_task, stmf_status_t s);
+
+/* pppt modhandle */
+ddi_modhandle_t pppt_mod;
+
+/* pppt modload imported functions */
+stmf_ic_reg_port_msg_alloc_func_t ic_reg_port_msg_alloc;
+stmf_ic_dereg_port_msg_alloc_func_t ic_dereg_port_msg_alloc;
+stmf_ic_reg_lun_msg_alloc_func_t ic_reg_lun_msg_alloc;
+stmf_ic_dereg_lun_msg_alloc_func_t ic_dereg_lun_msg_alloc;
+stmf_ic_lun_active_msg_alloc_func_t ic_lun_active_msg_alloc;
+stmf_ic_scsi_cmd_msg_alloc_func_t ic_scsi_cmd_msg_alloc;
+stmf_ic_scsi_data_xfer_done_msg_alloc_func_t ic_scsi_data_xfer_done_msg_alloc;
+stmf_ic_session_create_msg_alloc_func_t ic_session_reg_msg_alloc;
+stmf_ic_session_destroy_msg_alloc_func_t ic_session_dereg_msg_alloc;
+stmf_ic_tx_msg_func_t ic_tx_msg;
+stmf_ic_msg_free_func_t ic_msg_free;
 
 static void stmf_update_kstat_lu_q(scsi_task_t *, void());
 static void stmf_update_kstat_lport_q(scsi_task_t *, void());
@@ -689,6 +719,24 @@
 		ret = stmf_get_stmf_state((stmf_state_desc_t *)obuf);
 		break;
 
+	case STMF_IOCTL_SET_ALUA_STATE:
+		if ((ibuf == NULL) ||
+		    (iocd->stmf_ibuf_size < sizeof (stmf_alua_state_desc_t))) {
+			ret = EINVAL;
+			break;
+		}
+		ret = stmf_set_alua_state((stmf_alua_state_desc_t *)ibuf);
+		break;
+
+	case STMF_IOCTL_GET_ALUA_STATE:
+		if ((obuf == NULL) ||
+		    (iocd->stmf_obuf_size < sizeof (stmf_alua_state_desc_t))) {
+			ret = EINVAL;
+			break;
+		}
+		stmf_get_alua_state((stmf_alua_state_desc_t *)obuf);
+		break;
+
 	case STMF_IOCTL_SET_LU_STATE:
 		ssi.st_rflags = STMF_RFLAG_USER_REQUEST;
 		ssi.st_additional_info = NULL;
@@ -1431,6 +1479,545 @@
 	return (0);
 }
 
+/*
+ * handles registration message from pppt for a logical unit
+ */
+stmf_status_t
+stmf_ic_lu_reg(stmf_ic_reg_dereg_lun_msg_t *msg, uint32_t type)
+{
+	stmf_i_lu_provider_t	*ilp;
+	stmf_lu_provider_t	*lp;
+	mutex_enter(&stmf_state.stmf_lock);
+	for (ilp = stmf_state.stmf_ilplist; ilp != NULL; ilp = ilp->ilp_next) {
+		if (strcmp(msg->icrl_lu_provider_name,
+		    ilp->ilp_lp->lp_name) == 0) {
+			lp = ilp->ilp_lp;
+			mutex_exit(&stmf_state.stmf_lock);
+			lp->lp_proxy_msg(msg->icrl_lun_id, msg->icrl_cb_arg,
+			    msg->icrl_cb_arg_len, type);
+			return (STMF_SUCCESS);
+		}
+	}
+	mutex_exit(&stmf_state.stmf_lock);
+	return (STMF_SUCCESS);
+}
+
+/*
+ * handles de-registration message from pppt for a logical unit
+ */
+stmf_status_t
+stmf_ic_lu_dereg(stmf_ic_reg_dereg_lun_msg_t *msg)
+{
+	stmf_i_lu_provider_t	*ilp;
+	stmf_lu_provider_t	*lp;
+	mutex_enter(&stmf_state.stmf_lock);
+	for (ilp = stmf_state.stmf_ilplist; ilp != NULL; ilp = ilp->ilp_next) {
+		if (strcmp(msg->icrl_lu_provider_name,
+		    ilp->ilp_lp->lp_name) == 0) {
+			lp = ilp->ilp_lp;
+			mutex_exit(&stmf_state.stmf_lock);
+			lp->lp_proxy_msg(msg->icrl_lun_id, NULL, 0,
+			    STMF_MSG_LU_DEREGISTER);
+			return (STMF_SUCCESS);
+		}
+	}
+	mutex_exit(&stmf_state.stmf_lock);
+	return (STMF_SUCCESS);
+}
+
+/*
+ * helper function to find a task that matches a task_msgid
+ */
+scsi_task_t *
+find_task_from_msgid(uint8_t *lu_id, stmf_ic_msgid_t task_msgid)
+{
+	stmf_i_lu_t *ilu;
+	stmf_i_scsi_task_t *itask;
+
+	mutex_enter(&stmf_state.stmf_lock);
+	for (ilu = stmf_state.stmf_ilulist; ilu != NULL; ilu = ilu->ilu_next) {
+		if (bcmp(lu_id, ilu->ilu_lu->lu_id->ident, 16) == 0) {
+			break;
+		}
+	}
+
+	if (ilu == NULL) {
+		mutex_exit(&stmf_state.stmf_lock);
+		return (NULL);
+	}
+
+	mutex_enter(&ilu->ilu_task_lock);
+	for (itask = ilu->ilu_tasks; itask != NULL;
+	    itask = itask->itask_lu_next) {
+		if (itask->itask_flags & (ITASK_IN_FREE_LIST |
+		    ITASK_BEING_ABORTED)) {
+			continue;
+		}
+		if (itask->itask_proxy_msg_id == task_msgid) {
+			break;
+		}
+	}
+	mutex_exit(&ilu->ilu_task_lock);
+	mutex_exit(&stmf_state.stmf_lock);
+
+	if (itask != NULL) {
+		return (itask->itask_task);
+	} else {
+		/* task not found. Likely already aborted. */
+		return (NULL);
+	}
+}
+
+/*
+ * message received from pppt/ic
+ */
+stmf_status_t
+stmf_msg_rx(stmf_ic_msg_t *msg)
+{
+	mutex_enter(&stmf_state.stmf_lock);
+	if (stmf_state.stmf_alua_state != 1) {
+		mutex_exit(&stmf_state.stmf_lock);
+		cmn_err(CE_WARN, "stmf alua state is disabled");
+		ic_msg_free(msg);
+		return (STMF_FAILURE);
+	}
+	mutex_exit(&stmf_state.stmf_lock);
+
+	switch (msg->icm_msg_type) {
+		case STMF_ICM_REGISTER_LUN:
+			(void) stmf_ic_lu_reg(
+			    (stmf_ic_reg_dereg_lun_msg_t *)msg->icm_msg,
+			    STMF_MSG_LU_REGISTER);
+			break;
+		case STMF_ICM_LUN_ACTIVE:
+			(void) stmf_ic_lu_reg(
+			    (stmf_ic_reg_dereg_lun_msg_t *)msg->icm_msg,
+			    STMF_MSG_LU_ACTIVE);
+			break;
+		case STMF_ICM_DEREGISTER_LUN:
+			(void) stmf_ic_lu_dereg(
+			    (stmf_ic_reg_dereg_lun_msg_t *)msg->icm_msg);
+			break;
+		case STMF_ICM_SCSI_DATA:
+			(void) stmf_ic_rx_scsi_data(
+			    (stmf_ic_scsi_data_msg_t *)msg->icm_msg);
+			break;
+		case STMF_ICM_SCSI_STATUS:
+			(void) stmf_ic_rx_scsi_status(
+			    (stmf_ic_scsi_status_msg_t *)msg->icm_msg);
+			break;
+		case STMF_ICM_STATUS:
+			(void) stmf_ic_rx_status(
+			    (stmf_ic_status_msg_t *)msg->icm_msg);
+			break;
+		default:
+			cmn_err(CE_WARN, "unknown message received %d",
+			    msg->icm_msg_type);
+			ic_msg_free(msg);
+			return (STMF_FAILURE);
+	}
+	ic_msg_free(msg);
+	return (STMF_SUCCESS);
+}
+
+stmf_status_t
+stmf_ic_rx_status(stmf_ic_status_msg_t *msg)
+{
+	stmf_i_local_port_t *ilport;
+
+	if (msg->ics_msg_type != STMF_ICM_REGISTER_PROXY_PORT) {
+		/* for now, ignore other message status */
+		return (STMF_SUCCESS);
+	}
+
+	if (msg->ics_status != STMF_SUCCESS) {
+		return (STMF_SUCCESS);
+	}
+
+	mutex_enter(&stmf_state.stmf_lock);
+	for (ilport = stmf_state.stmf_ilportlist; ilport != NULL;
+	    ilport = ilport->ilport_next) {
+		if (msg->ics_msgid == ilport->ilport_reg_msgid) {
+			ilport->ilport_proxy_registered = 1;
+			break;
+		}
+	}
+	mutex_exit(&stmf_state.stmf_lock);
+	return (STMF_SUCCESS);
+}
+
+/*
+ * handles scsi status message from pppt
+ */
+stmf_status_t
+stmf_ic_rx_scsi_status(stmf_ic_scsi_status_msg_t *msg)
+{
+	scsi_task_t *task;
+
+	task = find_task_from_msgid(msg->icss_lun_id, msg->icss_task_msgid);
+
+	if (task == NULL) {
+		return (STMF_SUCCESS);
+	}
+
+	task->task_scsi_status = msg->icss_status;
+	task->task_sense_data = msg->icss_sense;
+	task->task_sense_length = msg->icss_sense_len;
+	(void) stmf_send_scsi_status(task, STMF_IOF_LU_DONE);
+
+	return (STMF_SUCCESS);
+}
+
+/*
+ * handles scsi data message from pppt
+ */
+stmf_status_t
+stmf_ic_rx_scsi_data(stmf_ic_scsi_data_msg_t *msg)
+{
+	stmf_i_scsi_task_t *itask;
+	scsi_task_t *task;
+	stmf_xfer_data_t *xd = NULL;
+	stmf_data_buf_t *dbuf;
+	uint32_t sz, minsz, xd_sz, asz;
+
+	task = find_task_from_msgid(msg->icsd_lun_id, msg->icsd_task_msgid);
+	if (task == NULL) {
+		stmf_ic_msg_t *ic_xfer_done_msg = NULL;
+		static uint64_t data_msg_id;
+		stmf_status_t ic_ret = STMF_FAILURE;
+		mutex_enter(&stmf_state.stmf_lock);
+		data_msg_id = stmf_proxy_msg_id++;
+		mutex_exit(&stmf_state.stmf_lock);
+		/*
+		 * send xfer done status to pppt
+		 * for now, set the session id to 0 as we cannot
+		 * ascertain it since we cannot find the task
+		 */
+		ic_xfer_done_msg = ic_scsi_data_xfer_done_msg_alloc(
+		    msg->icsd_task_msgid, 0, STMF_FAILURE, data_msg_id);
+		if (ic_xfer_done_msg) {
+			ic_ret = ic_tx_msg(ic_xfer_done_msg);
+			if (ic_ret != STMF_IC_MSG_SUCCESS) {
+				cmn_err(CE_WARN, "unable to xmit proxy msg");
+			}
+		}
+		return (STMF_FAILURE);
+	}
+
+	itask = (stmf_i_scsi_task_t *)task->task_stmf_private;
+	dbuf = itask->itask_proxy_dbuf;
+	/*
+	 * stmf will now take over the task handling for this task
+	 * but it still needs to be treated differently from other
+	 * default handled tasks, hence the ITASK_PROXY_TASK
+	 */
+	itask->itask_flags |= ITASK_DEFAULT_HANDLING | ITASK_PROXY_TASK;
+
+	task->task_cmd_xfer_length = msg->icsd_data_len;
+
+	if (task->task_additional_flags &
+	    TASK_AF_NO_EXPECTED_XFER_LENGTH) {
+		task->task_expected_xfer_length =
+		    task->task_cmd_xfer_length;
+	}
+
+	sz = min(task->task_expected_xfer_length,
+	    task->task_cmd_xfer_length);
+
+	xd_sz = msg->icsd_data_len;
+	asz = xd_sz + sizeof (*xd) - 4;
+	xd = (stmf_xfer_data_t *)kmem_zalloc(asz, KM_NOSLEEP);
+
+	if (xd == NULL) {
+		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
+		    STMF_ALLOC_FAILURE, NULL);
+		return (STMF_FAILURE);
+	}
+
+	xd->alloc_size = asz;
+	xd->size_left = xd_sz;
+	bcopy(msg->icsd_data, xd->buf, xd_sz);
+
+	sz = min(sz, xd->size_left);
+	xd->size_left = sz;
+	minsz = min(512, sz);
+
+	if (dbuf == NULL)
+		dbuf = stmf_alloc_dbuf(task, sz, &minsz, 0);
+	if (dbuf == NULL) {
+		kmem_free(xd, xd->alloc_size);
+		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
+		    STMF_ALLOC_FAILURE, NULL);
+		return (STMF_FAILURE);
+	}
+	dbuf->db_lu_private = xd;
+	stmf_xd_to_dbuf(dbuf);
+
+	dbuf->db_flags = DB_DIRECTION_TO_RPORT;
+	(void) stmf_xfer_data(task, dbuf, 0);
+	return (STMF_SUCCESS);
+}
+
+stmf_status_t
+stmf_proxy_scsi_cmd(scsi_task_t *task, stmf_data_buf_t *dbuf)
+{
+	stmf_i_scsi_task_t *itask =
+	    (stmf_i_scsi_task_t *)task->task_stmf_private;
+	stmf_i_local_port_t *ilport =
+	    (stmf_i_local_port_t *)task->task_lport->lport_stmf_private;
+	stmf_ic_msg_t *ic_cmd_msg;
+	stmf_ic_msg_status_t ic_ret;
+	stmf_status_t ret = STMF_FAILURE;
+
+	if (ilport->ilport_proxy_registered == 0) {
+		cmn_err(CE_WARN, "proxy port not registered");
+		return (STMF_FAILURE);
+	}
+
+	if (stmf_state.stmf_alua_state != 1) {
+		cmn_err(CE_WARN, "stmf alua state is disabled");
+		return (STMF_FAILURE);
+	}
+
+	mutex_enter(&stmf_state.stmf_lock);
+	itask->itask_proxy_msg_id = stmf_proxy_msg_id++;
+	mutex_exit(&stmf_state.stmf_lock);
+	itask->itask_proxy_dbuf = dbuf;
+	if (dbuf) {
+		ic_cmd_msg = ic_scsi_cmd_msg_alloc(itask->itask_proxy_msg_id,
+		    task, dbuf->db_data_size, dbuf->db_sglist[0].seg_addr,
+		    itask->itask_proxy_msg_id);
+	} else {
+		ic_cmd_msg = ic_scsi_cmd_msg_alloc(itask->itask_proxy_msg_id,
+		    task, 0, NULL, itask->itask_proxy_msg_id);
+	}
+	if (ic_cmd_msg) {
+		ic_ret = ic_tx_msg(ic_cmd_msg);
+		if (ic_ret == STMF_IC_MSG_SUCCESS) {
+			ret = STMF_SUCCESS;
+		}
+	}
+	return (ret);
+}
+
+
+stmf_status_t
+pppt_modload()
+{
+	int error;
+
+	if (pppt_mod == NULL && ((pppt_mod =
+	    ddi_modopen("drv/pppt", KRTLD_MODE_FIRST, &error)) == NULL)) {
+		cmn_err(CE_WARN, "Unable to load pppt");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_reg_port_msg_alloc == NULL && ((ic_reg_port_msg_alloc =
+	    (stmf_ic_reg_port_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_reg_port_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_reg_port_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+
+	if (ic_dereg_port_msg_alloc == NULL && ((ic_dereg_port_msg_alloc =
+	    (stmf_ic_dereg_port_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_dereg_port_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_dereg_port_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_reg_lun_msg_alloc == NULL && ((ic_reg_lun_msg_alloc =
+	    (stmf_ic_reg_lun_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_reg_lun_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_reg_lun_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_lun_active_msg_alloc == NULL && ((ic_lun_active_msg_alloc =
+	    (stmf_ic_lun_active_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_lun_active_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_lun_active_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_dereg_lun_msg_alloc == NULL && ((ic_dereg_lun_msg_alloc =
+	    (stmf_ic_dereg_lun_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_dereg_lun_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_dereg_lun_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_scsi_cmd_msg_alloc == NULL && ((ic_scsi_cmd_msg_alloc =
+	    (stmf_ic_scsi_cmd_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_scsi_cmd_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol - stmf_ic_scsi_cmd_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_scsi_data_xfer_done_msg_alloc == NULL &&
+	    ((ic_scsi_data_xfer_done_msg_alloc =
+	    (stmf_ic_scsi_data_xfer_done_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_scsi_data_xfer_done_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol -"
+		    "stmf_ic_scsi_data_xfer_done_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_session_reg_msg_alloc == NULL &&
+	    ((ic_session_reg_msg_alloc =
+	    (stmf_ic_session_create_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_session_create_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol -"
+		    "stmf_ic_session_create_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_session_dereg_msg_alloc == NULL &&
+	    ((ic_session_dereg_msg_alloc =
+	    (stmf_ic_session_destroy_msg_alloc_func_t)
+	    ddi_modsym(pppt_mod, "stmf_ic_session_destroy_msg_alloc",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN,
+		    "Unable to find symbol -"
+		    "stmf_ic_session_destroy_msg_alloc");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_tx_msg == NULL && ((ic_tx_msg =
+	    (stmf_ic_tx_msg_func_t)ddi_modsym(pppt_mod, "stmf_ic_tx_msg",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN, "Unable to find symbol - stmf_ic_tx_msg");
+		return (STMF_FAILURE);
+	}
+
+	if (ic_msg_free == NULL && ((ic_msg_free =
+	    (stmf_ic_msg_free_func_t)ddi_modsym(pppt_mod, "stmf_ic_msg_free",
+	    &error)) == NULL)) {
+		cmn_err(CE_WARN, "Unable to find symbol - stmf_ic_msg_free");
+		return (STMF_FAILURE);
+	}
+	return (STMF_SUCCESS);
+}
+
+static void
+stmf_get_alua_state(stmf_alua_state_desc_t *alua_state)
+{
+	mutex_enter(&stmf_state.stmf_lock);
+	alua_state->alua_node = stmf_state.stmf_alua_node;
+	alua_state->alua_state = stmf_state.stmf_alua_state;
+	mutex_exit(&stmf_state.stmf_lock);
+}
+
+
+static int
+stmf_set_alua_state(stmf_alua_state_desc_t *alua_state)
+{
+	stmf_i_local_port_t *ilport;
+	stmf_i_lu_t *ilu;
+	stmf_lu_t *lu;
+	stmf_ic_msg_status_t ic_ret;
+	stmf_ic_msg_t *ic_reg_lun, *ic_reg_port;
+	stmf_local_port_t *lport;
+	int ret = 0;
+
+	if (alua_state->alua_state > 1 || alua_state->alua_node > 1) {
+		return (EINVAL);
+	}
+
+	mutex_enter(&stmf_state.stmf_lock);
+	if (alua_state->alua_state == 1) {
+		if (pppt_modload() == STMF_FAILURE) {
+			ret = EIO;
+			goto err;
+		}
+		if (alua_state->alua_node != 0) {
+			/* reset existing rtpids to new base */
+			cmn_err(CE_NOTE, "non-zero alua node set");
+			stmf_rtpid_counter = 255;
+		}
+		stmf_state.stmf_alua_node = alua_state->alua_node;
+		stmf_state.stmf_alua_state = 1;
+		/* register existing local ports with ppp */
+		for (ilport = stmf_state.stmf_ilportlist; ilport != NULL;
+		    ilport = ilport->ilport_next) {
+			/* skip standby ports */
+			if (ilport->ilport_standby == 1) {
+				continue;
+			}
+			if (alua_state->alua_node != 0) {
+				ilport->ilport_rtpid =
+				    atomic_add_16_nv(&stmf_rtpid_counter, 1);
+			}
+			lport = ilport->ilport_lport;
+			ic_reg_port = ic_reg_port_msg_alloc(
+			    lport->lport_id, ilport->ilport_rtpid,
+			    0, NULL, stmf_proxy_msg_id);
+			if (ic_reg_port) {
+				ic_ret = ic_tx_msg(ic_reg_port);
+				if (ic_ret == STMF_IC_MSG_SUCCESS) {
+					ilport->ilport_reg_msgid =
+					    stmf_proxy_msg_id++;
+				} else {
+					cmn_err(CE_WARN,
+					    "error on port registration "
+					    "port - %s",
+					    ilport->ilport_kstat_tgt_name);
+				}
+			}
+		}
+		/* register existing logical units */
+		for (ilu = stmf_state.stmf_ilulist; ilu != NULL;
+		    ilu = ilu->ilu_next) {
+			if (ilu->ilu_access != STMF_LU_ACTIVE) {
+				continue;
+			}
+			/* register with proxy module */
+			lu = ilu->ilu_lu;
+			if (lu->lu_lp && lu->lu_lp->lp_lpif_rev == LPIF_REV_2 &&
+			    lu->lu_lp->lp_alua_support) {
+				ilu->ilu_alua = 1;
+				/* allocate the register message */
+				ic_reg_lun = ic_reg_lun_msg_alloc(
+				    lu->lu_id->ident, lu->lu_lp->lp_name,
+				    lu->lu_proxy_reg_arg_len,
+				    (uint8_t *)lu->lu_proxy_reg_arg,
+				    stmf_proxy_msg_id);
+				/* send the message */
+				if (ic_reg_lun) {
+					ic_ret = ic_tx_msg(ic_reg_lun);
+					if (ic_ret == STMF_IC_MSG_SUCCESS) {
+						stmf_proxy_msg_id++;
+					}
+				}
+			}
+		}
+	} else {
+		stmf_state.stmf_alua_state = 0;
+	}
+
+err:
+	mutex_exit(&stmf_state.stmf_lock);
+	return (ret);
+}
+
+
 typedef struct {
 	void	*bp;	/* back pointer from internal struct to main struct */
 	int	alloc_size;
@@ -1571,7 +2158,7 @@
 	stmf_pp_data_t *ppd;
 	uint32_t cb_flags;
 
-	if (lp->lp_lpif_rev != LPIF_REV_1)
+	if (lp->lp_lpif_rev != LPIF_REV_1 && lp->lp_lpif_rev != LPIF_REV_2)
 		return (STMF_FAILURE);
 
 	mutex_enter(&stmf_state.stmf_lock);
@@ -2216,6 +2803,83 @@
 	kstat_install(ilport->ilport_kstat_io);
 }
 
+/*
+ * set the asymmetric access state for a logical unit
+ * caller is responsible for establishing SCSI unit attention on
+ * state change
+ */
+stmf_status_t
+stmf_set_lu_access(stmf_lu_t *lu, uint8_t access_state)
+{
+	stmf_i_lu_t *ilu;
+	uint8_t *p1, *p2;
+
+	if ((access_state != STMF_LU_STANDBY) &&
+	    (access_state != STMF_LU_ACTIVE)) {
+		return (STMF_INVALID_ARG);
+	}
+
+	p1 = &lu->lu_id->ident[0];
+	mutex_enter(&stmf_state.stmf_lock);
+	if (stmf_state.stmf_inventory_locked) {
+		mutex_exit(&stmf_state.stmf_lock);
+		return (STMF_BUSY);
+	}
+
+	for (ilu = stmf_state.stmf_ilulist; ilu != NULL; ilu = ilu->ilu_next) {
+		p2 = &ilu->ilu_lu->lu_id->ident[0];
+		if (bcmp(p1, p2, 16) == 0) {
+			break;
+		}
+	}
+
+	if (!ilu) {
+		ilu = (stmf_i_lu_t *)lu->lu_stmf_private;
+	} else {
+		/*
+		 * We're changing access state on an existing logical unit
+		 * Send the proxy registration message for this logical unit
+		 * if we're in alua mode.
+		 * If the requested state is STMF_LU_ACTIVE, we want to register
+		 * this logical unit.
+		 * If the requested state is STMF_LU_STANDBY, we're going to
+		 * abort all tasks for this logical unit.
+		 */
+		if (stmf_state.stmf_alua_state == 1 &&
+		    access_state == STMF_LU_ACTIVE) {
+			stmf_ic_msg_status_t ic_ret = STMF_IC_MSG_SUCCESS;
+			stmf_ic_msg_t *ic_reg_lun;
+			if (lu->lu_lp && lu->lu_lp->lp_lpif_rev == LPIF_REV_2 &&
+			    lu->lu_lp->lp_alua_support) {
+				ilu->ilu_alua = 1;
+				/* allocate the register message */
+				ic_reg_lun = ic_lun_active_msg_alloc(p1,
+				    lu->lu_lp->lp_name,
+				    lu->lu_proxy_reg_arg_len,
+				    (uint8_t *)lu->lu_proxy_reg_arg,
+				    stmf_proxy_msg_id);
+				/* send the message */
+				if (ic_reg_lun) {
+					ic_ret = ic_tx_msg(ic_reg_lun);
+					if (ic_ret == STMF_IC_MSG_SUCCESS) {
+						stmf_proxy_msg_id++;
+					}
+				}
+			}
+		} else if (stmf_state.stmf_alua_state == 1 &&
+		    access_state == STMF_LU_STANDBY) {
+			/* abort all tasks for this lu */
+			stmf_task_lu_killall(lu, NULL, STMF_ABORTED);
+		}
+	}
+
+	ilu->ilu_access = access_state;
+
+	mutex_exit(&stmf_state.stmf_lock);
+	return (STMF_SUCCESS);
+}
+
+
 stmf_status_t
 stmf_register_lu(stmf_lu_t *lu)
 {
@@ -2266,6 +2930,30 @@
 	ilu->ilu_cur_task_cntr = &ilu->ilu_task_cntr1;
 	STMF_EVENT_ALLOC_HANDLE(ilu->ilu_event_hdl);
 	stmf_create_kstat_lu(ilu);
+	/*
+	 * register with proxy module if available and logical unit
+	 * is in active state
+	 */
+	if (stmf_state.stmf_alua_state == 1 &&
+	    ilu->ilu_access == STMF_LU_ACTIVE) {
+		stmf_ic_msg_status_t ic_ret = STMF_IC_MSG_SUCCESS;
+		stmf_ic_msg_t *ic_reg_lun;
+		if (lu->lu_lp && lu->lu_lp->lp_lpif_rev == LPIF_REV_2 &&
+		    lu->lu_lp->lp_alua_support) {
+			ilu->ilu_alua = 1;
+			/* allocate the register message */
+			ic_reg_lun = ic_reg_lun_msg_alloc(p1,
+			    lu->lu_lp->lp_name, lu->lu_proxy_reg_arg_len,
+			    (uint8_t *)lu->lu_proxy_reg_arg, stmf_proxy_msg_id);
+			/* send the message */
+			if (ic_reg_lun) {
+				ic_ret = ic_tx_msg(ic_reg_lun);
+				if (ic_ret == STMF_IC_MSG_SUCCESS) {
+					stmf_proxy_msg_id++;
+				}
+			}
+		}
+	}
 	mutex_exit(&stmf_state.stmf_lock);
 
 	/* XXX we should probably check if this lu can be brought online */
@@ -2314,6 +3002,28 @@
 			ilu->ilu_tasks = ilu->ilu_free_tasks = NULL;
 			ilu->ilu_ntasks = ilu->ilu_ntasks_free = 0;
 		}
+		/* de-register with proxy if available */
+		if (ilu->ilu_access == STMF_LU_ACTIVE &&
+		    stmf_state.stmf_alua_state == 1) {
+			/* de-register with proxy module */
+			stmf_ic_msg_status_t ic_ret = STMF_IC_MSG_SUCCESS;
+			stmf_ic_msg_t *ic_dereg_lun;
+			if (lu->lu_lp && lu->lu_lp->lp_lpif_rev == LPIF_REV_2 &&
+			    lu->lu_lp->lp_alua_support) {
+				ilu->ilu_alua = 1;
+				/* allocate the de-register message */
+				ic_dereg_lun = ic_dereg_lun_msg_alloc(
+				    lu->lu_id->ident, lu->lu_lp->lp_name, 0,
+				    NULL, stmf_proxy_msg_id);
+				/* send the message */
+				if (ic_dereg_lun) {
+					ic_ret = ic_tx_msg(ic_dereg_lun);
+					if (ic_ret == STMF_IC_MSG_SUCCESS) {
+						stmf_proxy_msg_id++;
+					}
+				}
+			}
+		}
 
 		if (ilu->ilu_next)
 			ilu->ilu_next->ilu_prev = ilu->ilu_prev;
@@ -2356,6 +3066,15 @@
 	return (STMF_SUCCESS);
 }
 
+void
+stmf_set_port_standby(stmf_local_port_t *lport, uint16_t rtpid)
+{
+	stmf_i_local_port_t *ilport =
+	    (stmf_i_local_port_t *)lport->lport_stmf_private;
+	ilport->ilport_rtpid = rtpid;
+	ilport->ilport_standby = 1;
+}
+
 stmf_status_t
 stmf_register_local_port(stmf_local_port_t *lport)
 {
@@ -2384,7 +3103,34 @@
 	ilport->ilport_tg =
 	    stmf_lookup_group_for_target(lport->lport_id->ident,
 	    lport->lport_id->ident_length);
-	ilport->ilport_rtpid = atomic_add_16_nv(&stmf_rtpid_counter, 1);
+
+	/*
+	 * rtpid will/must be set if this is a standby port
+	 * only register ports that are not standby (proxy) ports
+	 */
+	if (ilport->ilport_standby == 0) {
+		ilport->ilport_rtpid = atomic_add_16_nv(&stmf_rtpid_counter, 1);
+	}
+
+	if (stmf_state.stmf_alua_state == 1 &&
+	    ilport->ilport_standby == 0) {
+		stmf_ic_msg_t *ic_reg_port;
+		stmf_ic_msg_status_t ic_ret;
+		stmf_local_port_t *lport;
+		lport = ilport->ilport_lport;
+		ic_reg_port = ic_reg_port_msg_alloc(
+		    lport->lport_id, ilport->ilport_rtpid,
+		    0, NULL, stmf_proxy_msg_id);
+		if (ic_reg_port) {
+			ic_ret = ic_tx_msg(ic_reg_port);
+			if (ic_ret == STMF_IC_MSG_SUCCESS) {
+				ilport->ilport_reg_msgid = stmf_proxy_msg_id++;
+			} else {
+				cmn_err(CE_WARN, "error on port registration "
+				"port - %s", ilport->ilport_kstat_tgt_name);
+			}
+		}
+	}
 	STMF_EVENT_ALLOC_HANDLE(ilport->ilport_event_hdl);
 	stmf_create_kstat_lport(ilport);
 	if (stmf_workers_state == STMF_WORKERS_DISABLED) {
@@ -2418,7 +3164,26 @@
 		mutex_exit(&stmf_state.stmf_lock);
 		return (STMF_BUSY);
 	}
+
 	ilport = (stmf_i_local_port_t *)lport->lport_stmf_private;
+
+	/*
+	 * deregister ports that are not standby (proxy)
+	 */
+	if (stmf_state.stmf_alua_state == 1 &&
+	    ilport->ilport_standby == 0) {
+		stmf_ic_msg_t *ic_dereg_port;
+		stmf_ic_msg_status_t ic_ret;
+		ic_dereg_port = ic_dereg_port_msg_alloc(
+		    lport->lport_id, 0, NULL, stmf_proxy_msg_id);
+		if (ic_dereg_port) {
+			ic_ret = ic_tx_msg(ic_dereg_port);
+			if (ic_ret == STMF_IC_MSG_SUCCESS) {
+				stmf_proxy_msg_id++;
+			}
+		}
+	}
+
 	if (ilport->ilport_nsessions == 0) {
 		if (ilport->ilport_next)
 			ilport->ilport_next->ilport_prev = ilport->ilport_prev;
@@ -2495,6 +3260,8 @@
 	iss->iss_creation_time = ddi_get_time();
 	ss->ss_session_id = atomic_add_64_nv(&stmf_session_counter, 1);
 	iss->iss_flags &= ~ISS_BEING_CREATED;
+	/* XXX should we remove ISS_LUN_INVENTORY_CHANGED on new session? */
+	iss->iss_flags &= ~ISS_LUN_INVENTORY_CHANGED;
 	DTRACE_PROBE2(session__online, stmf_local_port_t *, lport,
 	    stmf_scsi_session_t *, ss);
 	return (STMF_SUCCESS);
@@ -2507,6 +3274,8 @@
 	    lport->lport_stmf_private;
 	stmf_i_scsi_session_t *iss, **ppss;
 	int found = 0;
+	stmf_ic_msg_t *ic_session_dereg;
+	stmf_status_t ic_ret = STMF_FAILURE;
 
 	DTRACE_PROBE2(session__offline, stmf_local_port_t *, lport,
 	    stmf_scsi_session_t *, ss);
@@ -2525,7 +3294,21 @@
 		delay(1);
 		goto try_dereg_ss_again;
 	}
+
+	/* dereg proxy session if not standby port */
+	if (stmf_state.stmf_alua_state == 1 && ilport->ilport_standby == 0) {
+		ic_session_dereg = ic_session_dereg_msg_alloc(
+		    ss, stmf_proxy_msg_id);
+		if (ic_session_dereg) {
+			ic_ret = ic_tx_msg(ic_session_dereg);
+			if (ic_ret == STMF_IC_MSG_SUCCESS) {
+				stmf_proxy_msg_id++;
+			}
+		}
+	}
+
 	mutex_exit(&stmf_state.stmf_lock);
+
 	rw_enter(&ilport->ilport_lock, RW_WRITER);
 	for (ppss = &ilport->ilport_ss_list; *ppss != NULL;
 	    ppss = &((*ppss)->iss_next)) {
@@ -3032,16 +3815,14 @@
 	return (task);
 }
 
-void
-stmf_task_lu_free(scsi_task_t *task)
+static void
+stmf_task_lu_free(scsi_task_t *task, stmf_i_scsi_session_t *iss)
 {
 	stmf_i_scsi_task_t *itask =
 	    (stmf_i_scsi_task_t *)task->task_stmf_private;
-	stmf_i_scsi_session_t *iss = (stmf_i_scsi_session_t *)
-	    task->task_session->ss_stmf_private;
 	stmf_i_lu_t *ilu = (stmf_i_lu_t *)task->task_lu->lu_stmf_private;
 
-	rw_enter(iss->iss_lockp, RW_READER);
+	ASSERT(rw_lock_held(iss->iss_lockp));
 	itask->itask_flags = ITASK_IN_FREE_LIST;
 	mutex_enter(&ilu->ilu_task_lock);
 	itask->itask_lu_free_next = ilu->ilu_free_tasks;
@@ -3049,7 +3830,6 @@
 	ilu->ilu_ntasks_free++;
 	mutex_exit(&ilu->ilu_task_lock);
 	atomic_add_32(itask->itask_ilu_task_cntr, -1);
-	rw_exit(iss->iss_lockp);
 }
 
 void
@@ -3245,6 +4025,8 @@
 	stmf_local_port_t *lport = task->task_lport;
 	stmf_i_scsi_task_t *itask = (stmf_i_scsi_task_t *)
 	    task->task_stmf_private;
+	stmf_i_scsi_session_t *iss = (stmf_i_scsi_session_t *)
+	    task->task_session->ss_stmf_private;
 
 	DTRACE_PROBE1(stmf__task__end, scsi_task_t *, task);
 	stmf_free_task_bufs(itask, lport);
@@ -3255,6 +4037,8 @@
 			    itask->itask_itl_datap);
 		}
 	}
+
+	rw_enter(iss->iss_lockp, RW_READER);
 	lport->lport_task_free(task);
 	if (itask->itask_worker) {
 		atomic_add_32(&stmf_cur_ntasks, -1);
@@ -3264,7 +4048,8 @@
 	 * After calling stmf_task_lu_free, the task pointer can no longer
 	 * be trusted.
 	 */
-	stmf_task_lu_free(task);
+	stmf_task_lu_free(task, iss);
+	rw_exit(iss->iss_lockp);
 }
 
 void
@@ -4305,17 +5090,31 @@
 
 
 stmf_xfer_data_t *
-stmf_prepare_tpgs_data()
+stmf_prepare_tpgs_data(uint8_t ilu_alua)
 {
 	stmf_xfer_data_t *xd;
 	stmf_i_local_port_t *ilport;
 	uint8_t *p;
-	uint32_t sz, asz, nports;
+	uint32_t sz, asz, nports = 0, nports_standby = 0;
 
 	mutex_enter(&stmf_state.stmf_lock);
-	/* The spec only allows for 255 ports to be reported */
-	nports = min(stmf_state.stmf_nlports, 255);
+	/* check if any ports are standby and create second group */
+	for (ilport = stmf_state.stmf_ilportlist; ilport;
+	    ilport = ilport->ilport_next) {
+		if (ilport->ilport_standby == 1) {
+			nports_standby++;
+		} else {
+			nports++;
+		}
+	}
+
+	/* The spec only allows for 255 ports to be reported per group */
+	nports = min(nports, 255);
+	nports_standby = min(nports_standby, 255);
 	sz = (nports * 4) + 12;
+	if (nports_standby && ilu_alua) {
+		sz += (nports_standby * 4) + 8;
+	}
 	asz = sz + sizeof (*xd) - 4;
 	xd = (stmf_xfer_data_t *)kmem_zalloc(asz, KM_NOSLEEP);
 	if (xd == NULL) {
@@ -4330,13 +5129,42 @@
 	*((uint32_t *)p) = BE_32(sz - 4);
 	p += 4;
 	p[0] = 0x80;	/* PREF */
-	p[1] = 1;	/* AO_SUP */
+	p[1] = 5;	/* AO_SUP, S_SUP */
+	if (stmf_state.stmf_alua_node == 1) {
+		p[3] = 1;	/* Group 1 */
+	} else {
+		p[3] = 0;	/* Group 0 */
+	}
 	p[7] = nports & 0xff;
 	p += 8;
-	for (ilport = stmf_state.stmf_ilportlist; ilport && nports;
-	    nports++, ilport = ilport->ilport_next, p += 4) {
+	for (ilport = stmf_state.stmf_ilportlist; ilport;
+	    ilport = ilport->ilport_next) {
+		if (ilport->ilport_standby == 1) {
+			continue;
+		}
 		((uint16_t *)p)[1] = BE_16(ilport->ilport_rtpid);
-	}
+		p += 4;
+	}
+	if (nports_standby && ilu_alua) {
+		p[0] = 0x02;	/* Non PREF, Standby */
+		p[1] = 5;	/* AO_SUP, S_SUP */
+		if (stmf_state.stmf_alua_node == 1) {
+			p[3] = 0;	/* Group 0 */
+		} else {
+			p[3] = 1;	/* Group 1 */
+		}
+		p[7] = nports_standby & 0xff;
+		p += 8;
+		for (ilport = stmf_state.stmf_ilportlist; ilport;
+		    ilport = ilport->ilport_next) {
+			if (ilport->ilport_standby == 0) {
+				continue;
+			}
+			((uint16_t *)p)[1] = BE_16(ilport->ilport_rtpid);
+			p += 4;
+		}
+	}
+
 	mutex_exit(&stmf_state.stmf_lock);
 
 	return (xd);
@@ -4501,13 +5329,18 @@
 			p = (uint8_t *)task->task_lport->lport_id;
 			continue;
 		} else if (vpd_mask & STMF_VPD_TP_GROUP) {
+			stmf_i_local_port_t *ilport;
 			last_bit = STMF_VPD_TP_GROUP;
 			p = small_buf;
 			bzero(p, 8);
 			p[0] = 1;
 			p[1] = 0x15;
 			p[3] = 4;
-			/* Group ID is always 0 */
+			ilport = (stmf_i_local_port_t *)
+			    task->task_lport->lport_stmf_private;
+			if (ilport->ilport_rtpid > 255) {
+				p[7] = 1;	/* Group 1 */
+			}
 			sz = 8;
 			continue;
 		} else if (vpd_mask & STMF_VPD_RELATIVE_TP_ID) {
@@ -4542,6 +5375,8 @@
 {
 	stmf_i_scsi_task_t *itask =
 	    (stmf_i_scsi_task_t *)task->task_stmf_private;
+	stmf_i_lu_t *ilu =
+	    (stmf_i_lu_t *)task->task_lu->lu_stmf_private;
 	stmf_xfer_data_t *xd;
 	uint32_t sz, minsz;
 
@@ -4571,7 +5406,7 @@
 	sz = min(task->task_expected_xfer_length,
 	    task->task_cmd_xfer_length);
 
-	xd = stmf_prepare_tpgs_data();
+	xd = stmf_prepare_tpgs_data(ilu->ilu_alua);
 
 	if (xd == NULL) {
 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
@@ -4602,6 +5437,7 @@
 void
 stmf_scsilib_handle_task_mgmt(scsi_task_t *task)
 {
+
 	switch (task->task_mgmt_function) {
 	/*
 	 * For now we will abort all I/Os on the LU in case of ABORT_TASK_SET
@@ -4614,6 +5450,8 @@
 	case TM_CLEAR_TASK_SET:
 	case TM_LUN_RESET:
 		stmf_handle_lun_reset(task);
+		/* issue the reset to the proxy node as well */
+		(void) stmf_proxy_scsi_cmd(task, NULL);
 		return;
 	case TM_TARGET_RESET:
 	case TM_TARGET_COLD_RESET:
@@ -5392,6 +6230,9 @@
 void
 stmf_dlun0_dbuf_done(scsi_task_t *task, stmf_data_buf_t *dbuf)
 {
+	stmf_i_scsi_task_t *itask =
+	    (stmf_i_scsi_task_t *)task->task_stmf_private;
+
 	if (dbuf->db_xfer_status != STMF_SUCCESS) {
 		stmf_abort(STMF_QUEUE_TASK_ABORT, task,
 		    dbuf->db_xfer_status, NULL);
@@ -5404,6 +6245,33 @@
 		(void) stmf_xfer_data(task, dbuf, 0);
 		return;
 	}
+	/*
+	 * If this is a proxy task, it will need to be completed from the
+	 * proxy port provider. This message lets pppt know that the xfer
+	 * is complete. When we receive the status from pppt, we will
+	 * then relay that status back to the lport.
+	 */
+	if (itask->itask_flags & ITASK_PROXY_TASK) {
+		stmf_ic_msg_t *ic_xfer_done_msg = NULL;
+		stmf_status_t ic_ret = STMF_FAILURE;
+		uint64_t session_msg_id;
+		mutex_enter(&stmf_state.stmf_lock);
+		session_msg_id = stmf_proxy_msg_id++;
+		mutex_exit(&stmf_state.stmf_lock);
+		/* send xfer done status to pppt */
+		ic_xfer_done_msg = ic_scsi_data_xfer_done_msg_alloc(
+		    itask->itask_proxy_msg_id,
+		    task->task_session->ss_session_id,
+		    STMF_SUCCESS, session_msg_id);
+		if (ic_xfer_done_msg) {
+			ic_ret = ic_tx_msg(ic_xfer_done_msg);
+			if (ic_ret != STMF_IC_MSG_SUCCESS) {
+				cmn_err(CE_WARN, "unable to xmit session msg");
+			}
+		}
+		/* task will be completed from pppt */
+		return;
+	}
 	stmf_scsilib_send_status(task, STATUS_GOOD, 0);
 }
 
--- a/usr/src/uts/common/io/comstar/stmf/stmf_impl.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/stmf/stmf_impl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -75,6 +75,8 @@
 	uint32_t	ilu_ref_cnt;
 	uint8_t		ilu_state;
 	uint8_t		ilu_prev_state;
+	uint8_t		ilu_access;
+	uint8_t		ilu_alua;
 	stmf_event_handle_t ilu_event_hdl;
 	struct stmf_i_lu *ilu_next;
 	struct stmf_i_lu *ilu_prev;
@@ -87,6 +89,9 @@
 	uint32_t	ilu_ntasks;	 /* # of tasks in the ilu_task list */
 	uint32_t	ilu_ntasks_free;	/* # of tasks that are free */
 	uint32_t	ilu_ntasks_min_free; /* # minimal free tasks */
+	uint32_t	rsvd1;
+	uint32_t	ilu_proxy_registered;
+	uint64_t	ilu_reg_msgid;
 	struct stmf_i_scsi_task	*ilu_tasks;
 	struct stmf_i_scsi_task *ilu_free_tasks;
 	struct stmf_itl_data	*ilu_itl_list;
@@ -124,7 +129,12 @@
 	struct stmf_i_local_port *ilport_prev;
 	uint8_t			ilport_state;
 	uint8_t			ilport_prev_state;
+	uint8_t			ilport_standby;
+	uint8_t			ilport_alua;
 	uint16_t		ilport_rtpid; /* relative tpid */
+	uint16_t		ilport_proxy_registered;
+	uint64_t		ilport_reg_msgid;
+	uint8_t			ilport_no_standby_lu;
 	stmf_event_handle_t	ilport_event_hdl;
 	clock_t			ilport_last_online_clock;
 	clock_t			ilport_avg_interval;
@@ -183,6 +193,8 @@
 	scsi_task_t		*itask_task;
 	uint32_t		itask_alloc_size;
 	uint32_t		itask_flags;
+	uint64_t		itask_proxy_msg_id;
+	stmf_data_buf_t		*itask_proxy_dbuf;
 	struct stmf_worker	*itask_worker;
 	uint32_t		*itask_ilu_task_cntr;
 	struct stmf_i_scsi_task	*itask_worker_next;
@@ -219,6 +231,7 @@
 #define	ITASK_CAUSING_LU_RESET		0x0400
 #define	ITASK_CAUSING_TARGET_RESET	0x0800
 #define	ITASK_KSTAT_IN_RUNQ		0x1000
+#define	ITASK_PROXY_TASK		0x2000
 
 /*
  * itask cmds.
@@ -310,7 +323,6 @@
 void stmf_worker_init();
 stmf_status_t stmf_worker_fini();
 void stmf_task_free(scsi_task_t *task);
-void stmf_task_lu_free(scsi_task_t *task);
 void stmf_do_task_abort(scsi_task_t *task);
 void stmf_do_itl_dereg(stmf_lu_t *lu, stmf_itl_data_t *itl,
 		uint8_t hdlrm_reason);
--- a/usr/src/uts/common/io/comstar/stmf/stmf_state.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/io/comstar/stmf/stmf_state.h	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #ifndef	_STMF_STATE_H
@@ -48,6 +48,8 @@
 				stmf_process_initial_luns:1,
 				rsvd:3;
 	uint8_t			stmf_config_state; /* See stmf_ioctl.h */
+	uint8_t			stmf_alua_state;
+	uint16_t		stmf_alua_node;
 	ddi_taskq_t		*stmf_svc_taskq;
 	uint32_t		stmf_svc_flags;
 	stmf_i_lu_t		*stmf_svc_ilu_draining;
--- a/usr/src/uts/common/sys/Makefile	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -438,6 +438,8 @@
 	port_kernel.h		\
 	portif.h		\
 	ppmio.h                 \
+	pppt_ic_if.h		\
+	pppt_ioctl.h		\
 	priocntl.h		\
 	priv.h			\
 	priv_impl.h		\
--- a/usr/src/uts/common/sys/lpif.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/lpif.h	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #ifndef	_LPIF_H
@@ -30,12 +30,14 @@
  */
 
 #include <sys/stmf_defines.h>
+#include <sys/stmf.h>
 
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
 #define	LPIF_REV_1	0x00010000
+#define	LPIF_REV_2	0x00020000
 
 typedef struct stmf_lu {
 	void			*lu_stmf_private;
@@ -70,6 +72,8 @@
 		void *arg, uint8_t *buf, uint32_t *bufsizep);
 	void			(*lu_event_handler)(struct stmf_lu *lu,
 		int eventid, void *arg, uint32_t flags);
+	void			*lu_proxy_reg_arg;
+	uint32_t		lu_proxy_reg_arg_len;
 } stmf_lu_t;
 
 /*
@@ -80,6 +84,23 @@
 #define	STMF_LU_ITL_HANDLE_REMOVED	3
 
 /*
+ * Asymmetric access state
+ */
+#define	STMF_LU_ACTIVE			0
+#define	STMF_LU_STANDBY			1
+
+/*
+ * proxy register msg types
+ */
+#define	STMF_MSG_LU_REGISTER		0
+#define	STMF_MSG_LU_ACTIVE		1
+#define	STMF_MSG_LU_DEREGISTER		2
+
+
+#define	STMF_PROXY_READ			1
+#define	STMF_PROXY_WRITE		2
+
+/*
  * Reasons for itl handle removal. Passed in flags.
  */
 #define	STMF_ITL_REASON_MASK		0x0f
@@ -92,17 +113,23 @@
 	void			*lp_stmf_private;
 	void			*lp_private;
 
-	uint32_t		lp_lpif_rev;	/* Currently LPIF_REV_1 */
+	uint32_t		lp_lpif_rev;	/* Currently LPIF_REV_2 */
 	int			lp_instance;
 	char			*lp_name;
 	void			(*lp_cb)(struct stmf_lu_provider *lp,
 	    int cmd, void *arg, uint32_t flags);
+	uint8_t			lp_alua_support;
+	stmf_status_t		(*lp_proxy_msg)(uint8_t *luid,
+	    void *proxy_reg_arg, uint32_t proxy_reg_arg_len, uint32_t type);
 } stmf_lu_provider_t;
 
 stmf_status_t stmf_deregister_lu_provider(stmf_lu_provider_t *lp);
 stmf_status_t stmf_register_lu_provider(stmf_lu_provider_t *lp);
 stmf_status_t stmf_register_lu(stmf_lu_t *lup);
 stmf_status_t stmf_deregister_lu(stmf_lu_t *lup);
+stmf_status_t stmf_set_lu_access(stmf_lu_t *lup, uint8_t access_state);
+stmf_status_t stmf_proxy_scsi_cmd(scsi_task_t *, stmf_data_buf_t *dbuf);
+int stmf_is_standby_port(scsi_task_t *);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/sys/portif.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/portif.h	Tue Oct 06 19:56:15 2009 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #ifndef	_PORTIF_H
@@ -112,6 +112,7 @@
 				stmf_scsi_session_t *ss);
 void stmf_deregister_scsi_session(stmf_local_port_t *lport,
 				stmf_scsi_session_t *ss);
+void stmf_set_port_standby(stmf_local_port_t *lport, uint16_t rtpid);
 
 #ifdef	__cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/pppt_ic_if.h	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,442 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef _SYS_PPPT_IC_IF_H
+#define	_SYS_PPPT_IC_IF_H
+
+#include <sys/stmf_defines.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * ALUA messaging and interconnect API.
+ */
+
+/*
+ * Message type.
+ */
+typedef enum {
+	STMF_ICM_REGISTER_PROXY_PORT = 0,
+	STMF_ICM_DEREGISTER_PROXY_PORT,
+	STMF_ICM_REGISTER_LUN,
+	STMF_ICM_DEREGISTER_LUN,
+	STMF_ICM_SCSI_CMD,
+	STMF_ICM_SCSI_DATA,
+	STMF_ICM_SCSI_DATA_XFER_DONE,
+	STMF_ICM_SCSI_STATUS,
+	STMF_ICM_R2T,
+	STMF_ICM_STATUS,
+	STMF_ICM_SESSION_CREATE,
+	STMF_ICM_SESSION_DESTROY,
+	STMF_ICM_ECHO_REQUEST,
+	STMF_ICM_ECHO_REPLY,
+	STMF_ICM_LUN_ACTIVE,
+	STMF_ICM_MAX_MSG_TYPE
+} stmf_ic_msg_type_t;
+
+/*
+ * Message id: uniquely identifies a message.
+ * This need not be a sequence number since we don't depend on
+ * messages being delivered in sequence.
+ */
+typedef uint64_t stmf_ic_msgid_t;
+
+/*
+ * IC message.  This is a container for the various specific message types.
+ *
+ * Note that the message contains a pointer to an nvlist.  This pointer
+ * is valid only in the case of messages which are unmarshaled from
+ * nvlists.  In that case, it's important to retain a pointer to the nvlist,
+ * since the message and the nvlist share data in the case of strings
+ * and array elements, and data in the message may be invalid if used
+ * after the nvlist is freed.
+ */
+typedef struct stmf_ic_msg {
+	stmf_ic_msg_type_t icm_msg_type;
+	stmf_ic_msgid_t icm_msgid;
+	nvlist_t *icm_nvlist;		/* nvlist associated with the msg */
+	void *icm_msg;			/* ptr to the specific msg */
+} stmf_ic_msg_t;
+
+/*
+ * Register port message.
+ */
+typedef struct {
+	scsi_devid_desc_t 	*icrp_port_id;
+	uint16_t 		icrp_relative_port_id;
+	/* opaque callback data */
+	uint16_t		icrp_cb_arg_len;
+	uint8_t			*icrp_cb_arg;
+} stmf_ic_reg_port_msg_t;
+
+/*
+ * Deregister port message.
+ */
+typedef struct {
+	scsi_devid_desc_t 	*icdp_port_id;
+	/* opaque callback data */
+	uint16_t		icdp_cb_arg_len;
+	uint8_t			*icdp_cb_arg;
+} stmf_ic_dereg_port_msg_t;
+
+/*
+ * Register/deregister lun message.
+ */
+typedef struct {
+	uint8_t 		icrl_lun_id[16];
+	char			*icrl_lu_provider_name;
+	/* opaque callback data */
+	uint16_t		icrl_cb_arg_len;
+	uint8_t			*icrl_cb_arg;
+} stmf_ic_reg_dereg_lun_msg_t;
+
+/*
+ * SCSI cmd msg.
+ */
+typedef struct {
+	stmf_ic_msgid_t		icsc_task_msgid;
+	scsi_devid_desc_t	*icsc_ini_devid;
+	scsi_devid_desc_t	*icsc_tgt_devid;
+	uint8_t 		icsc_lun_id[16];
+	/*
+	 * fields from scsi_task_t
+	 */
+	uint64_t	icsc_session_id;
+	uint8_t		icsc_task_lun_no[8];
+	uint32_t	icsc_task_expected_xfer_length;
+	uint16_t	icsc_task_cdb_length;
+	uint8_t 	*icsc_task_cdb;
+	uint8_t		icsc_task_flags;	/* See def. for task flags */
+	uint8_t		icsc_task_priority;	/* As per SAM-3 */
+	uint8_t		icsc_task_mgmt_function;	/* if is a TM req */
+	uint32_t	icsc_immed_data_len;
+	uint8_t		*icsc_immed_data;
+} stmf_ic_scsi_cmd_msg_t;
+
+/*
+ * SCSI data message.
+ */
+typedef struct {
+	stmf_ic_msgid_t icsd_task_msgid;	/* matches msgid of cmd */
+	uint64_t icsd_session_id;
+	uint8_t icsd_lun_id[16];
+	uint64_t icsd_data_len;
+	uint8_t *icsd_data;
+} stmf_ic_scsi_data_msg_t;
+
+/*
+ * SCSI data xfer done msg
+ */
+typedef struct {
+	stmf_ic_msgid_t icsx_task_msgid;	/* matches msgid of cmd */
+	uint64_t icsx_session_id;
+	stmf_status_t	icsx_status;
+} stmf_ic_scsi_data_xfer_done_msg_t;
+
+/*
+ * SCSI status msg.
+ */
+typedef struct {
+	stmf_ic_msgid_t icss_task_msgid;	/* matches msgid of cmd */
+	uint64_t icss_session_id;
+	uint8_t icss_lun_id[16];
+	uint8_t icss_response;		/* was command processed? */
+	uint8_t icss_status;
+	uint8_t	icss_flags;		/* TASK_SCTRL_OVER, TASK_SCTRL_UNDER */
+	uint32_t icss_resid;
+	uint8_t	icss_sense_len;
+	uint8_t	*icss_sense;
+} stmf_ic_scsi_status_msg_t;
+
+/*
+ * Ready to transfer (r2t) msg.
+ */
+typedef struct {
+	stmf_ic_msgid_t icrt_task_msgid;	/* matches msgid of cmd */
+	uint64_t icrt_session_id;
+	uint32_t icrt_offset;
+	uint32_t icrt_length;
+} stmf_ic_r2t_msg_t;
+
+/*
+ * Status message: sent in response to messages other than SCSI messages.
+ */
+typedef struct {
+	stmf_ic_msg_type_t ics_msg_type;	/* msg type rpting status on */
+	stmf_ic_msgid_t ics_msgid;		/* msgid reporting status on */
+	stmf_status_t ics_status;
+} stmf_ic_status_msg_t;
+
+/*
+ * Session create/destroy message.
+ */
+typedef struct {
+	uint64_t		icscd_session_id;
+	scsi_devid_desc_t	*icscd_ini_devid;
+	scsi_devid_desc_t	*icscd_tgt_devid;
+} stmf_ic_session_create_destroy_msg_t;
+
+/*
+ * Echo request/reply message
+ */
+typedef struct {
+	uint8_t			*icerr_data;
+	uint32_t		icerr_datalen;
+} stmf_ic_echo_request_reply_msg_t;
+
+typedef enum {
+	STMF_IC_MSG_SUCCESS = 0,
+	STMF_IC_MSG_IC_DOWN,
+	STMF_IC_MSG_TIMED_OUT,
+	STMF_IC_MSG_INTERNAL_ERROR
+} stmf_ic_msg_status_t;
+
+/*
+ * Function prototypes.
+ *
+ * Note: Functions which are exported to other modules must have a function
+ * typedef and a prototype; the function type definition is used by
+ * the other module to import the symbol using ddi_modsym().
+ */
+
+void stmf_ic_ioctl_cmd(void *ibuf, uint32_t ibuf_size);
+
+/* Allocate a register port message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_reg_port_msg_alloc_func_t)(
+    scsi_devid_desc_t *port_id,
+    uint16_t relative_port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_reg_port_msg_alloc(
+    scsi_devid_desc_t *port_id,
+    uint16_t relative_port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a deregister port message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_dereg_port_msg_alloc_func_t)(
+    scsi_devid_desc_t *port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_dereg_port_msg_alloc(
+    scsi_devid_desc_t *port_id,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+
+/* Allocate a register lun message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_reg_lun_msg_alloc_func_t)(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_reg_lun_msg_alloc(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a lun active message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_lun_active_msg_alloc_func_t)(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_lun_active_msg_alloc(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a deregister lun message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_dereg_lun_msg_alloc_func_t)(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_dereg_lun_msg_alloc(
+    uint8_t *icrl_lun_id,	/* should be 16 bytes */
+    char *lu_provider_name,
+    uint16_t cb_arg_len,
+    uint8_t *cb_arg,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a scsi cmd message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_scsi_cmd_msg_alloc_func_t)(
+    stmf_ic_msgid_t 	task_msgid,
+    scsi_task_t 	*scsi_task,
+    uint32_t		immed_data_len,
+    uint8_t		*immed_data,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_scsi_cmd_msg_alloc(
+    stmf_ic_msgid_t 	task_msgid,
+    scsi_task_t 	*scsi_task,
+    uint32_t		immed_data_len,
+    uint8_t		*immed_data,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a scsi data message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_scsi_data_msg_alloc_func_t)(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    uint8_t		*lun_id,
+    uint64_t		data_len,
+    uint8_t		*data,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_scsi_data_msg_alloc(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    uint8_t		*lun_id,
+    uint64_t		data_len,
+    uint8_t		*data,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a scsi transfer done message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_scsi_data_xfer_done_msg_alloc_func_t)(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    stmf_status_t	status,
+    stmf_ic_msgid_t	msgid);
+
+stmf_ic_msg_t *stmf_ic_scsi_data_xfer_done_msg_alloc(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    stmf_status_t	status,
+    stmf_ic_msgid_t	msgid);
+
+
+/* Allocate a scsi status message */
+stmf_ic_msg_t *stmf_ic_scsi_status_msg_alloc(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    uint8_t		*lun_id,
+    uint8_t		response,		/* was command processed? */
+    uint8_t		status,
+    uint8_t		flags,
+    uint32_t 		resid,
+    uint8_t		sense_len,
+    uint8_t		*sense,
+    stmf_ic_msgid_t msgid);	/* must match corresponding scsi cmd msgid */
+
+
+/* Allocate a scsi ready to transfer (r2t) message */
+stmf_ic_msg_t *stmf_ic_r2t_msg_alloc(
+    stmf_ic_msgid_t 	task_msgid,
+    uint64_t		session_id,
+    uint32_t		offset,
+    uint32_t		length,
+    stmf_ic_msgid_t msgid);	/* must match corresponding scsi cmd msgid */
+
+/* Allocate a status message */
+stmf_ic_msg_t *stmf_ic_status_msg_alloc(
+    stmf_status_t	status,
+    stmf_ic_msg_type_t	msg_type,	/* msg type reporting status on */
+    stmf_ic_msgid_t 	msgid);		/* id of msg reporting status on */
+
+/* Allocate a session create message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_session_create_msg_alloc_func_t)(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_session_create_msg_alloc(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate a session destroy message */
+typedef
+stmf_ic_msg_t *(*stmf_ic_session_destroy_msg_alloc_func_t)(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid);
+
+stmf_ic_msg_t *stmf_ic_session_destroy_msg_alloc(
+    stmf_scsi_session_t *session,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate an echo request message */
+stmf_ic_msg_t *stmf_ic_echo_request_msg_alloc(
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid);
+
+/* Allocate an echo reply message */
+stmf_ic_msg_t *stmf_ic_echo_reply_msg_alloc(
+    uint32_t data_len,
+    uint8_t *data,
+    stmf_ic_msgid_t msgid);
+
+/*
+ * Free a msg.
+ */
+typedef void (*stmf_ic_msg_free_func_t)(stmf_ic_msg_t *msg);
+void stmf_ic_msg_free(stmf_ic_msg_t *msg);
+
+/*
+ * Send a message out over the interconnect, in the process marshalling
+ * the arguments.
+ *
+ * After being sent, the message is freed by tx_msg().
+ */
+typedef stmf_ic_msg_status_t (*stmf_ic_tx_msg_func_t)(stmf_ic_msg_t *msg);
+stmf_ic_msg_status_t stmf_ic_tx_msg(stmf_ic_msg_t *msg);
+
+/*
+ * This is a low-level upcall which is called when a message has
+ * been received on the interconnect.
+ */
+void stmf_ic_rx_msg(char *buf, size_t len);
+
+stmf_status_t stmf_msg_rx(stmf_ic_msg_t *msg);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_PPPT_IC_IF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/pppt_ioctl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,55 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_PPPT_IOCTL_H
+#define	_SYS_PPPT_IOCTL_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	PPPT_VERSION_1	1
+
+/*
+ * map ioctls
+ */
+#define	PPPT_IOC (('P' << 24) | ('P' << 16) | ('P' << 8))
+#define	PPPT_INSTALL_DOOR	(PPPT_IOC|1)	/* to talk to daemon */
+#define	PPPT_MESSAGE		(PPPT_IOC|2)	/* data from peer */
+
+
+typedef struct _pppt_iocdata {
+	uint32_t	pppt_version;
+	uint32_t	pppt_error;
+	uint32_t	pppt_door_fd;
+	uint32_t	pppt_buf_size;
+	uint64_t	pppt_buf;
+} pppt_iocdata_t;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_PPPT_IOCTL_H */
--- a/usr/src/uts/common/sys/stmf_defines.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/stmf_defines.h	Tue Oct 06 19:56:15 2009 -0700
@@ -108,6 +108,9 @@
  * Common key, asc, ascq for stmf_scsilib_send_status
  */
 #define	STMF_SAA_MEDIUM_NOT_PRESENT		0X023A00
+#define	STMF_SAA_LU_NO_ACCESS_TRANSITION	0X02040A
+#define	STMF_SAA_LU_NO_ACCESS_STANDBY		0X02040B
+#define	STMF_SAA_LU_NO_ACCESS_UNAVAIL		0X02040C
 #define	STMF_SAA_WRITE_ERROR			0x030C00
 #define	STMF_SAA_READ_ERROR			0x031100
 #define	STMF_SAA_OPERATION_IN_PROGRESS		0x050016
@@ -122,8 +125,9 @@
 #define	STMF_SAA_MEDIUM_REMOVAL_PREVENTED	0x055302
 #define	STMF_SAA_INSUFFICIENT_REG_RESOURCES	0x055504
 #define	STMF_SAA_POR				0x062900
+#define	STMF_SAA_MODE_PARAMETERS_CHANGED	0x062A01
+#define	STMF_SAA_ASYMMETRIC_ACCESS_CHANGED	0x062A06
 #define	STMF_SAA_CAPACITY_DATA_HAS_CHANGED	0x062A09
-#define	STMF_SAA_MODE_PARAMETERS_CHANGED	0x062A01
 #define	STMF_SAA_REPORT_LUN_DATA_HAS_CHANGED	0x063F0E
 #define	STMF_SAA_WRITE_PROTECTED		0X072700
 
--- a/usr/src/uts/common/sys/stmf_ioctl.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/stmf_ioctl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -67,6 +67,8 @@
 #define	STMF_IOCTL_VE_LU_LIST			(STMF_IOCTL | 33)
 #define	STMF_IOCTL_LU_VE_LIST			(STMF_IOCTL | 34)
 #define	STMF_IOCTL_VALIDATE_VIEW		(STMF_IOCTL | 35)
+#define	STMF_IOCTL_SET_ALUA_STATE		(STMF_IOCTL | 36)
+#define	STMF_IOCTL_GET_ALUA_STATE		(STMF_IOCTL | 37)
 
 typedef	struct stmf_iocdata {
 	uint32_t	stmf_version;
@@ -136,6 +138,14 @@
 	uint8_t		config_state;	/* N/A for LU/LPORTs */
 } stmf_state_desc_t;
 
+/*
+ * This struct is used for setting the alua state
+ */
+typedef struct stmf_alua_state_desc {
+	uint8_t		alua_state;
+	uint16_t	alua_node;
+} stmf_alua_state_desc_t;
+
 /* Error definitions for group/view entry/provider dataioctls */
 #define	STMF_IOCERR_NONE			0
 #define	STMF_IOCERR_HG_EXISTS			1
--- a/usr/src/uts/common/sys/stmf_sbd_ioctl.h	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/common/sys/stmf_sbd_ioctl.h	Tue Oct 06 19:56:15 2009 -0700
@@ -61,6 +61,7 @@
 	SBD_RET_NOT_FOUND,
 	SBD_RET_INSUFFICIENT_BUF_SPACE,
 	SBD_RET_WRITE_CACHE_SET_FAILED,
+	SBD_RET_ACCESS_STATE_FAILED,
 
 	SBD_RET_MAX_VAL
 } sbd_ret_t;
@@ -72,6 +73,7 @@
 #define	SBD_IOCTL_MODIFY_LU				SBD_IOCTL_DEF(4)
 #define	SBD_IOCTL_GET_LU_PROPS				SBD_IOCTL_DEF(5)
 #define	SBD_IOCTL_GET_LU_LIST				SBD_IOCTL_DEF(6)
+#define	SBD_IOCTL_SET_LU_STANDBY			SBD_IOCTL_DEF(7)
 
 typedef struct sbd_create_and_reg_lu {
 	uint32_t	slu_struct_size;
@@ -89,7 +91,6 @@
 			slu_writeback_cache_disable_valid:1,
 			slu_writeback_cache_disable:1,
 			slu_write_protected:1;
-
 	uint16_t	slu_meta_fname_off;
 	uint64_t	slu_lu_size;
 	uint16_t	slu_data_fname_off;
@@ -108,6 +109,11 @@
 	char		slu_buf[8];	/* likely more than 8 */
 } sbd_create_and_reg_lu_t;
 
+typedef struct sbd_set_lu_standby {
+	uint8_t		stlu_guid[16];
+} sbd_set_lu_standby_t;
+
+
 typedef struct sbd_import_lu {
 	uint32_t	ilu_struct_size;
 	uint32_t	ilu_rsvd;
@@ -126,7 +132,9 @@
 			mlu_write_protected_valid:1,
 			mlu_write_protected:1,
 			mlu_by_guid:1,
-			mlu_by_fname:1;
+			mlu_by_fname:1,
+			mlu_standby_valid:1,
+			mlu_standby:1;
 	uint64_t	mlu_lu_size;
 	uint16_t	mlu_alias_off;
 	uint16_t	mlu_mgmt_url_off;
@@ -148,6 +156,14 @@
 	uint8_t		dlu_meta_name[8];
 } sbd_delete_lu_t;
 
+/*
+ * sbd access states
+ */
+#define	SBD_LU_ACTIVE			1
+#define	SBD_LU_TRANSITION_TO_ACTIVE	2
+#define	SBD_LU_STANDBY			3
+#define	SBD_LU_TRANSITION_TO_STANDBY	4
+
 typedef struct sbd_lu_props {
 	uint32_t	slp_input_guid:1,	/* GUID or meta filename */
 			slp_separate_meta:1,
@@ -163,7 +179,6 @@
 			slp_writeback_cache_disable_cur:1,
 			slp_writeback_cache_disable_saved:1,
 			slp_write_protected:1;
-
 	uint16_t	slp_meta_fname_off;
 	uint16_t	slp_data_fname_off;
 	uint64_t	slp_lu_size;
@@ -173,7 +188,7 @@
 	uint16_t	slp_mgmt_url_off;
 	uint32_t	slp_buf_size_needed;	/* Upon return */
 	uint16_t	slp_serial_size;
-	uint16_t	slp_rsvd;
+	uint16_t	slp_access_state;
 	char		slp_rev[4];
 	char		slp_vid[8];
 	char		slp_pid[16];
--- a/usr/src/uts/intel/Makefile.intel.shared	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Tue Oct 06 19:56:15 2009 -0700
@@ -363,6 +363,7 @@
 DRV_KMODS	+= fcoei
 DRV_KMODS	+= qlt
 DRV_KMODS	+= iscsit
+DRV_KMODS	+= pppt
 DRV_KMODS	+= ncall nsctl sdbc nskern sv
 DRV_KMODS	+= ii rdc rdcsrv rdcstub 
 DRV_KMODS	+= iptun
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/pppt/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,93 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the pppt pseudo-driver for 
+#	COMSTAR.
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+
+
+UTSBASE	= ../..
+
+ARCHDIR:sh = cd ..; basename `pwd`
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pppt
+OBJECTS		= $(PPPT_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PPPT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/comstar/port/pppt
+
+#
+#	Include common rules.
+#
+include ../Makefile.$(ARCHDIR)
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Overrides and depends_on
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+LDFLAGS		+= -dy -Ndrv/stmf
+
+INC_PATH	+= -I$(UTSBASE)/common/io/comstar/port/pppt
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
+#
+#	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 ../Makefile.targ
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Tue Oct 06 12:44:54 2009 -0700
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Tue Oct 06 19:56:15 2009 -0700
@@ -315,6 +315,7 @@
 DRV_KMODS	+= fcoei
 DRV_KMODS	+= qlt
 DRV_KMODS	+= iscsit
+DRV_KMODS	+= pppt
 DRV_KMODS	+= ncall nsctl sdbc nskern sv
 DRV_KMODS	+= ii rdc rdcsrv rdcstub 
 DRV_KMODS	+= iscsi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/pppt/Makefile	Tue Oct 06 19:56:15 2009 -0700
@@ -0,0 +1,93 @@
+#
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the pppt pseudo-driver for 
+#	COMSTAR.
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+
+
+UTSBASE	= ../..
+
+ARCHDIR:sh = cd ..; basename `pwd`
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pppt
+OBJECTS		= $(PPPT_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PPPT_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/comstar/port/pppt
+
+#
+#	Include common rules.
+#
+include ../Makefile.$(ARCHDIR)
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Overrides and depends_on
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+LDFLAGS		+= -dy -Ndrv/stmf
+
+INC_PATH	+= -I$(UTSBASE)/common/io/comstar/port/pppt
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
+#
+#	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 ../Makefile.targ