Mercurial > illumos > illumos-gate
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()
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