summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/libds/Makefile58
-rw-r--r--usr/src/lib/libds/Makefile.com64
-rw-r--r--usr/src/lib/libds/common/libds.c710
-rw-r--r--usr/src/lib/libds/common/libds.h95
-rw-r--r--usr/src/lib/libds/common/llib-lds29
-rw-r--r--usr/src/lib/libds/common/mapfile-vers44
-rw-r--r--usr/src/lib/libds/sparc/Makefile29
-rw-r--r--usr/src/lib/libds/sparcv9/Makefile30
-rw-r--r--usr/src/pkgdefs/SUNWldomr.v/postinstall4
-rw-r--r--usr/src/pkgdefs/SUNWldomr.v/preremove4
-rw-r--r--usr/src/pkgdefs/SUNWldomr.v/prototype_sparc2
-rw-r--r--usr/src/pkgdefs/SUNWldomu.v/prototype_sparc3
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc14
-rw-r--r--usr/src/uts/sun4v/Makefile.files3
-rw-r--r--usr/src/uts/sun4v/Makefile.sun4v.shared1
-rw-r--r--usr/src/uts/sun4v/io/ds.c2962
-rw-r--r--usr/src/uts/sun4v/io/ds_common.c3141
-rw-r--r--usr/src/uts/sun4v/io/ds_drv.c1098
-rw-r--r--usr/src/uts/sun4v/io/vlds.c1576
-rw-r--r--usr/src/uts/sun4v/sys/Makefile6
-rw-r--r--usr/src/uts/sun4v/sys/ds.h3
-rw-r--r--usr/src/uts/sun4v/sys/ds_impl.h249
-rw-r--r--usr/src/uts/sun4v/sys/vlds.h245
-rw-r--r--usr/src/uts/sun4v/vlds/Makefile97
25 files changed, 7482 insertions, 2987 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 4905a502e7..09dd4bff22 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -246,6 +246,7 @@ SUBDIRS += \
sparc_SUBDIRS= .WAIT \
efcode \
libc_psr .WAIT \
+ libds \
libdscp \
libprtdiag .WAIT \
libprtdiag_psr \
@@ -448,6 +449,7 @@ $(CLOSED_BUILD)HDRSUBDIRS += \
$(CLOSED)/lib/smartcard
sparc_HDRSUBDIRS= \
+ libds \
libdscp \
libpri
diff --git a/usr/src/lib/libds/Makefile b/usr/src/lib/libds/Makefile
new file mode 100644
index 0000000000..059b9076c4
--- /dev/null
+++ b/usr/src/lib/libds/Makefile
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.lib
+
+HDRS = libds.h
+HDRDIR = common
+
+ROOTHDRDIR = $(ROOT)/usr/platform/sun4v/include/sys
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint: $(SUBDIRS)
+
+check: $(CHECKHDRS)
+
+install: all .WAIT $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libds/Makefile.com b/usr/src/lib/libds/Makefile.com
new file mode 100644
index 0000000000..9168e65ed2
--- /dev/null
+++ b/usr/src/lib/libds/Makefile.com
@@ -0,0 +1,64 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+LIBRARY = libds.a
+VERS = .1
+
+LIBSRCS = libds.c
+OBJECTS = $(LIBSRCS:%.c=%.o)
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+SRCDIR = ../common
+SRCS = $(LIBSRCS:%.c=$(SRCDIR)/%.c)
+
+CPPFLAGS += -I. -I$(SRC)/uts/sun4v
+CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS)
+CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS)
+
+LDLIBS += -lsysevent -lnvpair -lc
+
+LINTFLAGS = -msux
+LINTFLAGS64 = -msux -Xarch=$(MACH64:sparcv9=v9)
+
+$(LINTLIB) := SRCS = $(LINTSRC:%=$(SRCDIR)/%)
+$(LINTLIB) := LINTFLAGS = -nsvx -I$(ROOT)/usr/platform/sun4v/include
+$(LINTLIB) := LINTFLAGS64 = -nsvx -Xarch=$(MACH64:sparcv9=v9) \
+ -I$(ROOT)/usr/platform/sun4v/include
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: $(LINTLIB) lintcheck
+
+pics/%.o: $(SRCDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libds/common/libds.c b/usr/src/lib/libds/common/libds.c
new file mode 100644
index 0000000000..bacdd14f5a
--- /dev/null
+++ b/usr/src/lib/libds/common/libds.c
@@ -0,0 +1,710 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/sysevent.h>
+#include <libsysevent.h>
+#include <sys/vlds.h>
+#include "libds.h"
+
+#define PTRTOUINT64(ptr) ((uint64_t)((uintptr_t)(ptr)))
+static char vlds_device[] =
+ "/devices/virtual-devices@100/channel-devices@200/"
+ "virtual-domain-service@0:vlds";
+
+typedef struct dslibentry {
+ ds_hdl_t dsl_hdl;
+ uint32_t dsl_flags;
+ char *dsl_service;
+ ds_ops_t dsl_ops;
+} dslibentry_t;
+
+#define MIN_DSLIB_ENTRIES 64
+static dslibentry_t *dslibtab;
+static int ndslib;
+
+/*
+ * Lock to protect the dslibtab table. We only need to protect this
+ * table for those functions which actually look at or modify the table:
+ * service registration (ds_svc_reg/ds_clnt_reg), service unregistration
+ * (ds_hdl_unreg) or during callbacks (ds_recv)
+ */
+static mutex_t dslib_lock;
+
+static int ds_fd = -1;
+
+static char *ds_sid_name = "vlds";
+
+static evchan_t *ds_evchan;
+
+/*
+ * Static functions internal to dslib.
+ */
+static dslibentry_t *ds_hdl_to_dslibentry(ds_hdl_t hdl);
+static dslibentry_t *ds_lookup_dslibentry(char *service, boolean_t is_client);
+static dslibentry_t *ds_register_dslibentry(ds_hdl_t hdl, char *service,
+ boolean_t is_client);
+static void ds_free_dslibentry(dslibentry_t *dsp, int force_unreg);
+static int ds_recv(sysevent_t *sep, void *arg);
+static void ds_string_arg(vlds_string_t *dsp, char *str);
+static int ds_register(ds_capability_t *cap, ds_ops_t *ops, uint_t flags);
+
+static dslibentry_t *
+ds_hdl_to_dslibentry(ds_hdl_t hdl)
+{
+ int i;
+ dslibentry_t *dsp;
+
+ for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) {
+ if (hdl == dsp->dsl_hdl)
+ return (dsp);
+ }
+ return (NULL);
+}
+
+static dslibentry_t *
+ds_new_dslibentry(void)
+{
+ int newndslib;
+ dslibentry_t *dsp;
+
+ if ((dsp = ds_hdl_to_dslibentry(NULL)) != NULL)
+ return (dsp);
+
+ /* double the size */
+ newndslib = ndslib << 1;
+ if ((dslibtab = realloc(dslibtab, newndslib * sizeof (dslibentry_t)))
+ == NULL)
+ return (NULL);
+ dsp = &dslibtab[ndslib];
+ (void) memset(dsp, 0, (newndslib - ndslib) * sizeof (dslibentry_t));
+ ndslib = newndslib;
+ return (dsp);
+}
+
+static dslibentry_t *
+ds_lookup_dslibentry(char *service, boolean_t is_client)
+{
+ int i;
+ dslibentry_t *dsp;
+ uint_t is_client_flag = is_client ? VLDS_REG_CLIENT : 0;
+
+ for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) {
+ if (dsp->dsl_hdl != NULL &&
+ strcmp(dsp->dsl_service, service) == 0 &&
+ (dsp->dsl_flags & VLDS_REG_CLIENT) == is_client_flag) {
+ return (dsp);
+ }
+ }
+ return (NULL);
+}
+
+static dslibentry_t *
+ds_register_dslibentry(ds_hdl_t hdl, char *service, boolean_t is_client)
+{
+ dslibentry_t *dsp, *orig_dsp;
+ uint_t nhdls;
+
+ if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL)
+ return (dsp);
+
+ if ((orig_dsp = ds_lookup_dslibentry(service, is_client)) == NULL) {
+ return (NULL);
+ }
+
+ /*
+ * Find out if we have 1 or 2 or more handles. Having one implies
+ * that we can reuse the current one handle for this service.
+ * Having two or more implies we need to allocate a new handle.
+ */
+ if (ds_hdl_lookup(service, is_client, NULL, 2, &nhdls) != 0)
+ return (NULL);
+
+ if (nhdls == 1) {
+ /* reuse the original structure entry */
+ dsp = orig_dsp;
+ } else if (nhdls == 2) {
+ /* allocate a new structure entry */
+ if ((dsp = ds_new_dslibentry()) == NULL)
+ return (NULL);
+ *dsp = *orig_dsp;
+ dsp->dsl_service = strdup(orig_dsp->dsl_service);
+ } else {
+ /* can't happen... */
+ return (NULL);
+ }
+ dsp->dsl_hdl = hdl;
+ return (dsp);
+}
+
+/*
+ * Want to leave an entry in the dslib table even though all the
+ * handles may have been unregistered for it.
+ */
+static void
+ds_free_dslibentry(dslibentry_t *dsp, int force_unreg)
+{
+ uint_t nhdls;
+
+ /*
+ * Find out if we have 1 or 2 or more handles. Having one implies
+ * that we want to leave the entry alone unless this is a ds_unreg_hdl
+ * (force_unreg is true).
+ */
+ if (ds_hdl_lookup(dsp->dsl_service,
+ (dsp->dsl_flags & VLDS_REG_CLIENT) != 0, NULL, 2, &nhdls) != 0) {
+ /* should never happen */
+ return;
+ }
+
+ if ((nhdls == 1 && force_unreg) || nhdls == 2) {
+ dsp->dsl_hdl = NULL;
+ if (dsp->dsl_service) {
+ free(dsp->dsl_service);
+ }
+ (void) memset(dsp, 0, sizeof (dslibentry_t));
+ }
+}
+
+/*ARGSUSED*/
+static int
+ds_recv(sysevent_t *sep, void *arg)
+{
+ nvlist_t *nvl;
+ uint64_t hdl;
+ ds_ver_t ver;
+ ds_domain_hdl_t dhdl;
+ uchar_t *bufp;
+ boolean_t is_client;
+ uint_t buflen;
+ char *subclass;
+ char *servicep;
+ dslibentry_t *dsp;
+ ds_cb_arg_t cb_arg;
+
+ subclass = sysevent_get_subclass_name(sep);
+ if (sysevent_get_attr_list(sep, &nvl) != 0) {
+ return (0);
+ }
+
+ if (nvlist_lookup_uint64(nvl, VLDS_HDL, &hdl) == 0) {
+ if (strcmp(subclass, ESC_VLDS_REGISTER) == 0) {
+ void (*reg_cb)(ds_hdl_t, ds_cb_arg_t, ds_ver_t *,
+ ds_domain_hdl_t) = NULL;
+
+ if (nvlist_lookup_string(nvl, VLDS_SERVICE_ID,
+ &servicep) == 0 &&
+ nvlist_lookup_boolean_value(nvl, VLDS_ISCLIENT,
+ &is_client) == 0) {
+ (void) mutex_lock(&dslib_lock);
+ if ((dsp = ds_register_dslibentry(hdl,
+ servicep, is_client)) != NULL) {
+ reg_cb = dsp->dsl_ops.ds_reg_cb;
+ cb_arg = dsp->dsl_ops.cb_arg;
+ }
+ (void) mutex_unlock(&dslib_lock);
+ if (reg_cb != NULL &&
+ nvlist_lookup_uint64(nvl, VLDS_DOMAIN_HDL,
+ &dhdl) == 0 &&
+ nvlist_lookup_uint16(nvl, VLDS_VER_MAJOR,
+ &ver.major) == 0 &&
+ nvlist_lookup_uint16(nvl, VLDS_VER_MINOR,
+ &ver.minor) == 0) {
+ (reg_cb)((ds_hdl_t)hdl, cb_arg, &ver,
+ dhdl);
+ }
+ }
+ } else if (strcmp(subclass, ESC_VLDS_UNREGISTER) == 0) {
+ void (*unreg_cb)(ds_hdl_t, ds_cb_arg_t) = NULL;
+
+ (void) mutex_lock(&dslib_lock);
+ if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) {
+ unreg_cb = dsp->dsl_ops.ds_unreg_cb;
+ cb_arg = dsp->dsl_ops.cb_arg;
+ ds_free_dslibentry(dsp, 0);
+ }
+ (void) mutex_unlock(&dslib_lock);
+ if (unreg_cb != NULL) {
+ (unreg_cb)((ds_hdl_t)hdl, cb_arg);
+ }
+ } else if (strcmp(subclass, ESC_VLDS_DATA) == 0) {
+ void (*data_cb)(ds_hdl_t, ds_cb_arg_t, void *,
+ size_t) = NULL;
+
+ (void) mutex_lock(&dslib_lock);
+ if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) {
+ data_cb = dsp->dsl_ops.ds_data_cb;
+ cb_arg = dsp->dsl_ops.cb_arg;
+ }
+ (void) mutex_unlock(&dslib_lock);
+ if (data_cb != NULL &&
+ nvlist_lookup_byte_array(nvl, VLDS_DATA, &bufp,
+ &buflen) == 0) {
+ (data_cb)((ds_hdl_t)hdl, cb_arg, bufp, buflen);
+ }
+ }
+ }
+ nvlist_free(nvl);
+ return (0);
+}
+
+static void
+ds_string_arg(vlds_string_t *dsp, char *str)
+{
+ if (str == NULL) {
+ dsp->vlds_strp = NULL;
+ dsp->vlds_strlen = 0;
+ } else {
+ dsp->vlds_strp = PTRTOUINT64(str);
+ dsp->vlds_strlen = strlen(str) + 1;
+ }
+}
+
+static int
+ds_init_sysev(void)
+{
+ char evchan_name[MAX_CHNAME_LEN];
+
+ (void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, (int)getpid());
+ if (sysevent_evc_bind(evchan_name, &ds_evchan, 0) != 0) {
+ return (errno);
+ }
+ if (sysevent_evc_subscribe(ds_evchan, ds_sid_name, EC_VLDS,
+ ds_recv, NULL, 0) != 0) {
+ sysevent_evc_unbind(ds_evchan);
+ ds_evchan = NULL;
+ return (errno);
+ }
+ return (0);
+}
+
+int
+ds_init(void)
+{
+ if (ds_fd >= 0)
+ return (0);
+
+ if ((ds_fd = open(vlds_device, 0)) < 0)
+ return (errno);
+
+ if (dslibtab == NULL) {
+ if ((dslibtab = malloc(sizeof (dslibentry_t) * ndslib)) == NULL)
+ return (errno = ENOMEM);
+ ndslib = MIN_DSLIB_ENTRIES;
+ (void) memset(dslibtab, 0, sizeof (dslibentry_t) * ndslib);
+ }
+
+ (void) mutex_init(&dslib_lock, USYNC_THREAD, NULL);
+ return (0);
+}
+
+static int
+ds_register(ds_capability_t *cap, ds_ops_t *ops, uint_t flags)
+{
+ dslibentry_t *dsp;
+ vlds_svc_reg_arg_t vlds_arg;
+ vlds_cap_t vlds_cap;
+ vlds_ver_t vlds_vers[VLDS_MAX_VERS];
+ uint64_t hdl_arg;
+ ds_hdl_t hdl;
+ uint_t nhdls;
+ int i;
+
+ if (cap == NULL || ops == NULL || cap->svc_id == NULL ||
+ cap->vers == NULL || (flags & (~VLDS_REG_CLIENT)) != 0) {
+ return (errno = EINVAL);
+ }
+
+ if (cap->nvers > VLDS_MAX_VERS) {
+ return (errno = EINVAL);
+ }
+
+ if (ds_fd < 0 && (errno = ds_init()) != 0) {
+ return (errno);
+ }
+
+ if (ds_hdl_lookup(cap->svc_id, (flags & VLDS_REG_CLIENT), NULL, 1,
+ &nhdls) == 0 && nhdls == 1) {
+ return (errno = EALREADY);
+ }
+
+ (void) mutex_lock(&dslib_lock);
+ if ((dsp = ds_new_dslibentry()) == NULL) {
+ (void) mutex_unlock(&dslib_lock);
+ return (errno = ENOMEM);
+ }
+
+ /* Setup device driver capability structure. */
+
+ /* service string */
+ ds_string_arg(&vlds_cap.vlds_service, cap->svc_id);
+
+ /* version array */
+ for (i = 0; i < cap->nvers; i++) {
+ vlds_vers[i].vlds_major = cap->vers[i].major;
+ vlds_vers[i].vlds_minor = cap->vers[i].minor;
+ }
+ vlds_cap.vlds_versp = PTRTOUINT64(vlds_vers);
+ vlds_cap.vlds_nver = cap->nvers;
+
+ /*
+ * Format args for VLDS_SVC_REG ioctl.
+ */
+
+ vlds_arg.vlds_capp = PTRTOUINT64(&vlds_cap);
+
+ /* op flags */
+ if (ops->ds_reg_cb != NULL)
+ flags |= VLDS_REGCB_VALID;
+ if (ops->ds_unreg_cb != NULL)
+ flags |= VLDS_UNREGCB_VALID;
+ if (ops->ds_data_cb != NULL)
+ flags |= VLDS_DATACB_VALID;
+ vlds_arg.vlds_reg_flags = flags;
+
+ /* returned handle */
+ vlds_arg.vlds_hdlp = PTRTOUINT64(&hdl_arg);
+
+ if (ioctl(ds_fd, VLDS_SVC_REG, &vlds_arg) < 0) {
+ (void) mutex_unlock(&dslib_lock);
+ return (errno);
+ }
+
+ /*
+ * Setup user callback sysevent channel.
+ */
+ if ((flags & VLDS_ANYCB_VALID) != 0 && ds_evchan == NULL &&
+ ds_init_sysev() != 0) {
+ (void) mutex_unlock(&dslib_lock);
+ (void) ioctl(ds_fd, VLDS_UNREG_HDL, &vlds_arg);
+ return (errno);
+ }
+
+ hdl = hdl_arg;
+
+ /*
+ * Set entry values in dslibtab.
+ */
+ dsp->dsl_hdl = hdl;
+ dsp->dsl_flags = flags;
+ dsp->dsl_service = strdup(cap->svc_id);
+ dsp->dsl_ops = *ops;
+ (void) mutex_unlock(&dslib_lock);
+ return (0);
+}
+
+/*
+ * Registers a service provider. Kicks off the handshake with other
+ * domain(s) to announce servce. Callback events are as described above.
+ */
+int
+ds_svc_reg(ds_capability_t *cap, ds_ops_t *ops)
+{
+ return (ds_register(cap, ops, 0));
+}
+
+/*
+ * Registers interest in a service from a specific domain. When that
+ * service is registered, the register callback is invoked. When that
+ * service is unregistered, the unregister callback is invoked. When
+ * data is received, the receive data callback is invoked.
+ */
+int
+ds_clnt_reg(ds_capability_t *cap, ds_ops_t *ops)
+{
+ return (ds_register(cap, ops, VLDS_REG_CLIENT));
+}
+
+/*
+ * Given a service name and type, returns the existing handle(s), if
+ * one or more exist. This could be used to poll for the connection being
+ * registered or unregistered, rather than using the register/unregister
+ * callbacks.
+ */
+int
+ds_hdl_lookup(char *service, boolean_t is_client, ds_hdl_t *hdlsp,
+ uint_t maxhdls, uint_t *nhdlsp)
+{
+ vlds_hdl_lookup_arg_t vlds_arg;
+ uint64_t nhdls_arg;
+
+ errno = 0;
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ if (service == NULL) {
+ return (errno = EINVAL);
+ }
+
+ ds_string_arg(&vlds_arg.vlds_service, service);
+ vlds_arg.vlds_isclient = is_client ? VLDS_REG_CLIENT : 0;
+ vlds_arg.vlds_hdlsp = PTRTOUINT64(hdlsp);
+ vlds_arg.vlds_maxhdls = maxhdls;
+ vlds_arg.vlds_nhdlsp = PTRTOUINT64(&nhdls_arg);
+
+ if (ioctl(ds_fd, VLDS_HDL_LOOKUP, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ *nhdlsp = nhdls_arg;
+ return (0);
+}
+
+/*
+ * Given a handle, return its associated domain.
+ */
+int
+ds_domain_lookup(ds_hdl_t hdl, ds_domain_hdl_t *dhdlp)
+{
+ vlds_dmn_lookup_arg_t vlds_arg;
+ uint64_t dhdl_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ vlds_arg.vlds_hdl = hdl;
+ vlds_arg.vlds_dhdlp = PTRTOUINT64(&dhdl_arg);
+
+ if (ioctl(ds_fd, VLDS_DMN_LOOKUP, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ if (dhdlp) {
+ *dhdlp = dhdl_arg;
+ }
+
+ return (0);
+}
+
+/*
+ * Unregisters either a service or an interest in that service
+ * indicated by the supplied handle.
+ */
+int
+ds_unreg_hdl(ds_hdl_t hdl)
+{
+ dslibentry_t *dsp;
+ vlds_unreg_hdl_arg_t vlds_arg;
+
+ (void) mutex_lock(&dslib_lock);
+ if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) {
+ ds_free_dslibentry(dsp, 1);
+ }
+ (void) mutex_unlock(&dslib_lock);
+
+ if (ds_fd >= 0) {
+ vlds_arg.vlds_hdl = hdl;
+ (void) ioctl(ds_fd, VLDS_UNREG_HDL, &vlds_arg);
+ }
+
+ return (0);
+}
+
+/*
+ * Send data to the appropriate service provider or client
+ * indicated by the provided handle. The sender will block
+ * until the message has been sent. There is no guarantee
+ * that multiple calls to ds_send_msg by the same thread
+ * will result in the data showing up at the receiver in
+ * the same order as sent. If multiple messages are required,
+ * it will be up to the sender and receiver to implement a
+ * protocol.
+ */
+int
+ds_send_msg(ds_hdl_t hdl, void *buf, size_t buflen)
+{
+ vlds_send_msg_arg_t vlds_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ vlds_arg.vlds_hdl = hdl;
+ vlds_arg.vlds_bufp = PTRTOUINT64(buf);
+ vlds_arg.vlds_buflen = buflen;
+
+ if (ioctl(ds_fd, VLDS_SEND_MSG, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ return (0);
+}
+
+/*
+ * Receive data from the appropriate service provider or client
+ * indicated by the provided handle. The sender will block
+ * until a message has been received.
+ */
+int
+ds_recv_msg(ds_hdl_t hdl, void *buf, size_t buflen, size_t *msglen)
+{
+ vlds_recv_msg_arg_t vlds_arg;
+ uint64_t msglen_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ vlds_arg.vlds_hdl = hdl;
+ vlds_arg.vlds_bufp = PTRTOUINT64(buf);
+ vlds_arg.vlds_buflen = buflen;
+ vlds_arg.vlds_msglenp = PTRTOUINT64(&msglen_arg);
+
+ if (ioctl(ds_fd, VLDS_RECV_MSG, &vlds_arg) < 0) {
+ if (errno == EFBIG && msglen) {
+ *msglen = msglen_arg;
+ }
+ return (errno);
+ }
+
+ if (msglen) {
+ *msglen = msglen_arg;
+ }
+
+ return (0);
+}
+
+int
+ds_isready(ds_hdl_t hdl, boolean_t *is_ready)
+{
+ vlds_hdl_isready_arg_t vlds_arg;
+ uint64_t is_ready_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ vlds_arg.vlds_hdl = hdl;
+ vlds_arg.vlds_isreadyp = PTRTOUINT64(&is_ready_arg);
+
+ if (ioctl(ds_fd, VLDS_HDL_ISREADY, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ *is_ready = (is_ready_arg != 0);
+ return (0);
+}
+
+/*
+ * Given a domain name, return its associated domain handle.
+ */
+int
+ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp)
+{
+ vlds_dom_nam2hdl_arg_t vlds_arg;
+ uint64_t dhdl_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ ds_string_arg(&vlds_arg.vlds_domain_name, domain_name);
+ vlds_arg.vlds_dhdlp = PTRTOUINT64(&dhdl_arg);
+
+ if (ioctl(ds_fd, VLDS_DOM_NAM2HDL, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ if (dhdlp) {
+ *dhdlp = dhdl_arg;
+ }
+
+ return (0);
+}
+
+/*
+ * Given a domain handle, return its associated domain name.
+ */
+int
+ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char *domain_name, uint_t maxnamlen)
+{
+ vlds_dom_hdl2nam_arg_t vlds_arg;
+
+ if (ds_fd < 0) {
+ return (errno = EBADF);
+ }
+
+ vlds_arg.vlds_dhdl = dhdl;
+ vlds_arg.vlds_domain_name.vlds_strp = PTRTOUINT64(domain_name);
+ vlds_arg.vlds_domain_name.vlds_strlen = maxnamlen;
+
+ if (ioctl(ds_fd, VLDS_DOM_HDL2NAM, &vlds_arg) < 0) {
+ return (errno);
+ }
+
+ return (0);
+}
+
+void
+ds_unreg_svc(char *service, boolean_t is_client)
+{
+ ds_hdl_t hdl;
+ uint_t nhdls;
+
+ while (ds_hdl_lookup(service, is_client, &hdl, 1, &nhdls) == 0 &&
+ nhdls == 1) {
+ (void) ds_unreg_hdl(hdl);
+ }
+}
+
+void
+ds_fini(void)
+{
+ int i;
+ dslibentry_t *dsp;
+
+ if (ds_fd >= 0) {
+ (void) close(ds_fd);
+ ds_fd = -1;
+ }
+ if (ds_evchan) {
+ (void) sysevent_evc_unsubscribe(ds_evchan, ds_sid_name);
+ (void) sysevent_evc_unbind(ds_evchan);
+ ds_evchan = NULL;
+ }
+ if (ndslib > 0) {
+ (void) mutex_lock(&dslib_lock);
+ for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) {
+ if (dsp->dsl_hdl == NULL)
+ continue;
+ if (dsp->dsl_service) {
+ free(dsp->dsl_service);
+ }
+ }
+ free(dslibtab);
+ ndslib = 0;
+ dslibtab = NULL;
+ (void) mutex_unlock(&dslib_lock);
+ (void) mutex_destroy(&dslib_lock);
+ }
+}
diff --git a/usr/src/lib/libds/common/libds.h b/usr/src/lib/libds/common/libds.h
new file mode 100644
index 0000000000..149e53c91a
--- /dev/null
+++ b/usr/src/lib/libds/common/libds.h
@@ -0,0 +1,95 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBDS_H
+#define _LIBDS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * LDOMs User Domain Services library Interfaces
+ */
+
+typedef uint64_t ds_hdl_t; /* service handle */
+typedef uint64_t ds_domain_hdl_t; /* domain handle */
+typedef void *ds_cb_arg_t; /* client callback arg */
+
+#define DS_INVALID_HDL (0) /* a ds handle cannot be zero */
+
+/*
+ * LDOMs User Domain Services versioning
+ */
+typedef struct ds_ver {
+ uint16_t major;
+ uint16_t minor;
+} ds_ver_t;
+
+/*
+ * LDOMs User Domain Services capability
+ */
+typedef struct ds_capability {
+ char *svc_id; /* service identifier */
+ ds_ver_t *vers; /* list of supported versions */
+ uint_t nvers; /* number of supported versions */
+} ds_capability_t;
+
+/*
+ * LDOMs User Domain Services event callbacks
+ */
+typedef struct ds_ops {
+ void (*ds_reg_cb)(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
+ ds_domain_hdl_t dhdl);
+ void (*ds_unreg_cb)(ds_hdl_t hdl, ds_cb_arg_t arg);
+ void (*ds_data_cb)(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf,
+ size_t buflen);
+ ds_cb_arg_t cb_arg;
+} ds_ops_t;
+
+extern int ds_init(void);
+extern int ds_svc_reg(ds_capability_t *cap, ds_ops_t *ops);
+extern int ds_clnt_reg(ds_capability_t *cap, ds_ops_t *ops);
+extern int ds_hdl_lookup(char *service, boolean_t is_client, ds_hdl_t *hdlsp,
+ uint_t maxhdls, uint_t *nhdlsp);
+extern int ds_domain_lookup(ds_hdl_t hdl, ds_domain_hdl_t *dhdlp);
+extern int ds_unreg_hdl(ds_hdl_t hdl);
+extern int ds_send_msg(ds_hdl_t hdl, void *buf, size_t buflen);
+extern int ds_recv_msg(ds_hdl_t hdl, void *buf, size_t buflen,
+ size_t *msglen);
+extern int ds_isready(ds_hdl_t hdl, boolean_t *is_ready);
+extern int ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp);
+extern int ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char *domain_name,
+ uint_t maxnamlen);
+extern void ds_unreg_svc(char *service, boolean_t is_client);
+extern void ds_fini(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDS_H */
diff --git a/usr/src/lib/libds/common/llib-lds b/usr/src/lib/libds/common/llib-lds
new file mode 100644
index 0000000000..83867420d8
--- /dev/null
+++ b/usr/src/lib/libds/common/llib-lds
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include "libds.h"
diff --git a/usr/src/lib/libds/common/mapfile-vers b/usr/src/lib/libds/common/mapfile-vers
new file mode 100644
index 0000000000..889d1c47f0
--- /dev/null
+++ b/usr/src/lib/libds/common/mapfile-vers
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+SUNWprivate {
+ global:
+ ds_init;
+ ds_svc_reg;
+ ds_clnt_reg;
+ ds_hdl_lookup;
+ ds_domain_lookup;
+ ds_unreg_hdl;
+ ds_send_msg;
+ ds_recv_msg;
+ ds_isready;
+ ds_dom_name_to_hdl;
+ ds_dom_hdl_to_name;
+ ds_unreg_svc;
+ ds_fini;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libds/sparc/Makefile b/usr/src/lib/libds/sparc/Makefile
new file mode 100644
index 0000000000..ea09428ea0
--- /dev/null
+++ b/usr/src/lib/libds/sparc/Makefile
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libds/sparcv9/Makefile b/usr/src/lib/libds/sparcv9/Makefile
new file mode 100644
index 0000000000..40e4cef06c
--- /dev/null
+++ b/usr/src/lib/libds/sparcv9/Makefile
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/pkgdefs/SUNWldomr.v/postinstall b/usr/src/pkgdefs/SUNWldomr.v/postinstall
index ac5b3a9c67..aefe149431 100644
--- a/usr/src/pkgdefs/SUNWldomr.v/postinstall
+++ b/usr/src/pkgdefs/SUNWldomr.v/postinstall
@@ -21,10 +21,9 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
#
# Function: check_add_drv()
@@ -127,6 +126,7 @@ check_add_drv()
check_add_drv -b "${BASEDIR}" -i '"SUNW,sun4v-channel-devices"' cnex
check_add_drv -b "${BASEDIR}" drctl
+check_add_drv -b "${BASEDIR}" -i '"SUNW,sun4v-domain-service"' vlds
check_add_drv -b "${BASEDIR}" -i '"SUNW,sun4v-console-concentrator"' vcc
check_add_drv -b "${BASEDIR}" -i '"SUNW,sun4v-disk"' vdc
check_add_drv -b "${BASEDIR}" -i '"SUNW,sun4v-disk-server"' vds
diff --git a/usr/src/pkgdefs/SUNWldomr.v/preremove b/usr/src/pkgdefs/SUNWldomr.v/preremove
index 0b99d3f15b..898a7de959 100644
--- a/usr/src/pkgdefs/SUNWldomr.v/preremove
+++ b/usr/src/pkgdefs/SUNWldomr.v/preremove
@@ -20,10 +20,9 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
#
PATH=/usr/bin:/usr/sbin:${PATH}
@@ -49,6 +48,7 @@ not_installed()
#
not_installed cnex || rem_drv -b "${BASEDIR}" cnex || EXIT=1
not_installed drctl || rem_drv -b "${BASEDIR}" drctl || EXIT=1
+not_installed vlds || rem_drv -b "${BASEDIR}" vlds || EXIT=1
not_installed vcc || rem_drv -b "${BASEDIR}" vcc || EXIT=1
not_installed vdc || rem_drv -b "${BASEDIR}" vdc || EXIT=1
not_installed vds || rem_drv -b "${BASEDIR}" vds || EXIT=1
diff --git a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
index 825734cc4a..0b9d9fceef 100644
--- a/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWldomr.v/prototype_sparc
@@ -23,7 +23,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -65,6 +64,7 @@ f none platform/sun4v/kernel/drv/sparcv9/cnex 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/drctl 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/ds_pri 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/ds_snmp 755 root sys
+f none platform/sun4v/kernel/drv/sparcv9/vlds 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vcc 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vdc 755 root sys
f none platform/sun4v/kernel/drv/sparcv9/vds 755 root sys
diff --git a/usr/src/pkgdefs/SUNWldomu.v/prototype_sparc b/usr/src/pkgdefs/SUNWldomu.v/prototype_sparc
index 5b7dd635bb..413badcb41 100644
--- a/usr/src/pkgdefs/SUNWldomu.v/prototype_sparc
+++ b/usr/src/pkgdefs/SUNWldomu.v/prototype_sparc
@@ -23,7 +23,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
@@ -54,8 +53,10 @@ d none usr/lib/ldoms 755 root bin
f none usr/lib/ldoms/drd 555 root bin
f none usr/lib/ldoms/vntsd 555 root bin
f none usr/lib/libpri.so.1 755 root bin
+f none usr/lib/libds.so.1 755 root bin
d none usr/lib/rcm 755 root bin
d none usr/lib/rcm/scripts 755 root bin
f none usr/lib/rcm/scripts/SUNW,vdevices.pl 555 root bin
d none usr/lib/sparcv9 755 root bin
f none usr/lib/sparcv9/libpri.so.1 755 root bin
+f none usr/lib/sparcv9/libds.so.1 755 root bin
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index bfbd483227..f01b8e1277 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -977,6 +977,20 @@ usr/lib/llib-lpri.ln sparc
usr/lib/sparcv9/libpri.so sparc
usr/lib/sparcv9/llib-lpri.ln sparc
#
+# These files are installed in the proto area by the build of libds for
+# the benefit of the builds of sun4v IO FMA and/or other libds
+# consumers. However, the libds interfaces are private to Sun
+# (Consolidation Private) and not intended for customer use. So these
+# files (the symlink and the lint library) are excluded from packaging.
+#
+usr/lib/libds.so sparc
+usr/lib/llib-lds sparc
+usr/lib/llib-lds.ln sparc
+usr/lib/sparcv9/libds.so sparc
+usr/lib/sparcv9/llib-lds.ln sparc
+usr/platform/sun4v/include/sys/vlds.h sparc
+usr/platform/sun4v/include/sys/libds.h sparc
+#
# Private/Internal u8_textprep header file. Do not ship.
#
usr/include/sys/u8_textprep_data.h sparc
diff --git a/usr/src/uts/sun4v/Makefile.files b/usr/src/uts/sun4v/Makefile.files
index 4055ca40a2..b237268bf9 100644
--- a/usr/src/uts/sun4v/Makefile.files
+++ b/usr/src/uts/sun4v/Makefile.files
@@ -151,6 +151,7 @@ VDC_OBJS = vdc.o
VDS_OBJS = vds.o
DS_PRI_OBJS = ds_pri.o
DS_SNMP_OBJS = ds_snmp.o
+VLDS_OBJS = vlds.o
#
# Misc modules
@@ -159,7 +160,7 @@ BOOTDEV_OBJS += bootdev.o
DR_CPU_OBJS += dr_cpu.o
DR_IO_OBJS += dr_io.o
DRCTL_OBJS = drctl.o drctl_impl.o dr_util.o
-DS_OBJS = ds.o
+DS_OBJS = ds_common.o ds_drv.o
FAULT_ISO_OBJS = fault_iso.o
OBPSYM_OBJS += obpsym.o obpsym_1275.o
PLATSVC_OBJS = platsvc.o mdeg.o
diff --git a/usr/src/uts/sun4v/Makefile.sun4v.shared b/usr/src/uts/sun4v/Makefile.sun4v.shared
index bf0b0d0daf..11b7c41d75 100644
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared
+++ b/usr/src/uts/sun4v/Makefile.sun4v.shared
@@ -348,6 +348,7 @@ DRV_KMODS += vcc
DRV_KMODS += vdc
DRV_KMODS += vds
DRV_KMODS += vldc
+DRV_KMODS += vlds
DRV_KMODS += vnet
DRV_KMODS += vnex
DRV_KMODS += vsw
diff --git a/usr/src/uts/sun4v/io/ds.c b/usr/src/uts/sun4v/io/ds.c
deleted file mode 100644
index 4e81501ff9..0000000000
--- a/usr/src/uts/sun4v/io/ds.c
+++ /dev/null
@@ -1,2962 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-/*
- * Domain Services Module
- *
- * The Domain Services (DS) module is responsible for communication
- * with external service entities. It provides an API for clients to
- * publish capabilities and handles the low level communication and
- * version negotiation required to export those capabilities to any
- * interested service entity. Once a capability has been successfully
- * registered with a service entity, the DS module facilitates all
- * data transfers between the service entity and the client providing
- * that particular capability.
- */
-
-#include <sys/modctl.h>
-#include <sys/ksynch.h>
-#include <sys/taskq.h>
-#include <sys/disp.h>
-#include <sys/cmn_err.h>
-#include <sys/note.h>
-
-#include <sys/mach_descrip.h>
-#include <sys/mdesc.h>
-#include <sys/ldc.h>
-
-#include <sys/ds.h>
-#include <sys/ds_impl.h>
-
-/*
- * All DS ports in the system
- *
- * The list of DS ports is read in from the MD when the DS module is
- * initialized and is never modified. This eliminates the need for
- * locking to access the port array itself. Access to the individual
- * ports are synchronized at the port level.
- */
-static ds_port_t ds_ports[DS_MAX_PORTS];
-static ds_portset_t ds_allports; /* all DS ports in the system */
-
-/*
- * Table of registered services
- *
- * Locking: Accesses to the table of services are synchronized using
- * a RW lock. The reader lock must be held when looking up service
- * information in the table. The writer lock must be held when any
- * service information is being modified.
- */
-static struct ds_svcs {
- ds_svc_t **tbl; /* the table itself */
- krwlock_t rwlock; /* table lock */
- uint_t maxsvcs; /* size of the table */
- uint_t nsvcs; /* current number of items */
-} ds_svcs;
-
-/* initial size of the table */
-#define DS_MAXSVCS_INIT 32
-
-/*
- * Lock Usage
- *
- * ds_svcs.rwlock
- *
- * See comment just above definition of ds_svcs structure above.
- *
- * ds_port mutex
- *
- * Protects the elements of each port structure. Must be acquired for
- * access to any of the elements.
- *
- * ds_log mutex
- *
- * See comment above definition of ds_log structure.
- *
- * Multiple lock requirements:
- *
- * Some code will need to access both a ds_svc_t structure and
- * a ds_port_t. In that case, the acquisition order must be:
- *
- * ds_svcs.rwlock -> port lock
- */
-
-/*
- * Taskq for internal task processing
- */
-static taskq_t *ds_taskq;
-static boolean_t ds_enabled; /* enable/disable taskq processing */
-
-/*
- * The actual required number of parallel threads is not expected
- * to be very large. Use the maximum number of CPUs in the system
- * as a rough upper bound.
- */
-#define DS_MAX_TASKQ_THR NCPU
-#define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP)
-
-/*
- * Retry count and delay for LDC reads and writes
- */
-#define DS_DEFAULT_RETRIES 10000 /* number of times to retry */
-#define DS_DEFAULT_DELAY 1000 /* usecs to wait between retries */
-
-static int ds_retries = DS_DEFAULT_RETRIES;
-static clock_t ds_delay = DS_DEFAULT_DELAY;
-
-/*
- * Supported versions of the DS message protocol
- *
- * The version array must be sorted in order from the highest
- * supported version to the lowest. Support for a particular
- * <major>.<minor> version implies all lower minor versions of
- * that same major version are supported as well.
- */
-static ds_ver_t ds_vers[] = { { 1, 0 } };
-
-#define DS_NUM_VER (sizeof (ds_vers) / sizeof (ds_vers[0]))
-
-/*
- * Results of checking version array with ds_vers_isvalid()
- */
-typedef enum {
- DS_VERS_OK,
- DS_VERS_INCREASING_MAJOR_ERR,
- DS_VERS_INCREASING_MINOR_ERR
-} ds_vers_check_t;
-
-/* incoming message handling functions */
-typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
-static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
-
-/*
- * DS Message Handler Dispatch Table
- *
- * A table used to dispatch all incoming messages. This table
- * contains handlers for all the fixed message types, as well as
- * the the messages defined in the 1.0 version of the DS protocol.
- */
-static const ds_msg_handler_t ds_msg_handlers[] = {
- ds_handle_init_req, /* DS_INIT_REQ */
- ds_handle_init_ack, /* DS_INIT_ACK */
- ds_handle_init_nack, /* DS_INIT_NACK */
- ds_handle_reg_req, /* DS_REG_REQ */
- ds_handle_reg_ack, /* DS_REG_ACK */
- ds_handle_reg_nack, /* DS_REG_NACK */
- ds_handle_unreg_req, /* DS_UNREG */
- ds_handle_unreg_ack, /* DS_UNREG_ACK */
- ds_handle_unreg_nack, /* DS_UNREG_NACK */
- ds_handle_data, /* DS_DATA */
- ds_handle_nack /* DS_NACK */
-};
-
-/*
- * DS message log
- *
- * Locking: The message log is protected by a single mutex. This
- * protects all fields in the log structure itself as well as
- * everything in the entry structures on both the log and the
- * free list.
- */
-static struct log {
- ds_log_entry_t *head; /* head of the log */
- ds_log_entry_t *freelist; /* head of the free list */
- size_t size; /* size of the log in bytes */
- uint32_t nentry; /* number of entries */
- kmutex_t lock; /* log lock */
-} ds_log;
-
-/* log soft limit */
-uint_t ds_log_sz = DS_LOG_DEFAULT_SZ;
-
-/* initial pool of log entry structures */
-static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL];
-
-/*
- * Error message features
- */
-#define DS_EBUFSIZE 80
-
-/*
- * Debugging Features
- */
-#ifdef DEBUG
-
-#define DS_DBG_FLAG_LDC 0x1
-#define DS_DBG_FLAG_LOG 0x2
-#define DS_DBG_FLAG_MSG 0x4
-#define DS_DBG_FLAG_ALL 0xf
-
-#define DS_DBG if (ds_debug) printf
-#define DS_DBG_LDC if (ds_debug & DS_DBG_FLAG_LDC) printf
-#define DS_DBG_LOG if (ds_debug & DS_DBG_FLAG_LOG) printf
-#define DS_DBG_MSG if (ds_debug & DS_DBG_FLAG_MSG) printf
-#define DS_DUMP_MSG(buf, len) ds_dump_msg(buf, len)
-
-uint_t ds_debug = 0;
-static void ds_dump_msg(void *buf, size_t len);
-
-#else /* DEBUG */
-
-#define DS_DBG _NOTE(CONSTCOND) if (0) printf
-#define DS_DBG_LDC DS_DBG
-#define DS_DBG_LOG DS_DBG
-#define DS_DUMP_MSG(buf, len)
-
-#endif /* DEBUG */
-
-
-/* initialization functions */
-static void ds_init(void);
-static void ds_fini(void);
-static int ds_ports_init(void);
-static int ds_ports_fini(void);
-static int ds_ldc_init(ds_port_t *port);
-static int ds_ldc_fini(ds_port_t *port);
-
-/* event processing functions */
-static uint_t ds_ldc_reconnect(ds_port_t *port);
-static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
-static void ds_dispatch_event(void *arg);
-static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
-static void ds_handle_recv(void *arg);
-
-/* message sending functions */
-static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
-static void ds_send_init_req(ds_port_t *port);
-static int ds_send_reg_req(ds_svc_t *svc);
-static int ds_send_unreg_req(ds_svc_t *svc);
-static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
-static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
-
-/* walker functions */
-typedef int (*svc_cb_t)(ds_svc_t *svc, void *arg);
-static int ds_walk_svcs(svc_cb_t svc_cb, void *arg);
-static int ds_svc_isfree(ds_svc_t *svc, void *arg);
-static int ds_svc_ismatch(ds_svc_t *svc, void *arg);
-static int ds_svc_free(ds_svc_t *svc, void *arg);
-static int ds_svc_register(ds_svc_t *svc, void *arg);
-static int ds_svc_unregister(ds_svc_t *svc, void *arg);
-static int ds_svc_port_up(ds_svc_t *svc, void *arg);
-
-/* service utilities */
-static ds_svc_t *ds_alloc_svc(void);
-static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
-static ds_svc_t *ds_get_svc(ds_svc_hdl_t hdl);
-
-/* port utilities */
-static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan);
-static void ds_port_reset(ds_port_t *port);
-
-/* misc utilities */
-static ds_vers_check_t ds_vers_isvalid(ds_ver_t *vers, int nvers);
-static char *ds_errno_to_str(int errno, char *ebuf);
-
-/* log functions */
-static void ds_log_init(void);
-static void ds_log_fini(void);
-static int ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz);
-static int ds_log_remove(void);
-static void ds_log_purge(void *arg);
-
-
-static struct modlmisc modlmisc = {
- &mod_miscops,
- "Domain Services 1.8"
-};
-
-static struct modlinkage modlinkage = {
- MODREV_1,
- (void *)&modlmisc,
- NULL
-};
-
-int
-_init(void)
-{
- int rv;
-
- /*
- * Perform all internal setup before initializing
- * the DS ports. This ensures that events can be
- * processed as soon as the port comes up.
- */
- ds_init();
-
- /* force attach channel nexus */
- (void) i_ddi_attach_hw_nodes("cnex");
-
- if ((rv = ds_ports_init()) != 0) {
- cmn_err(CE_WARN, "Domain Services initialization failed");
- ds_fini();
- return (rv);
- }
-
- if ((rv = mod_install(&modlinkage)) != 0) {
- (void) ds_ports_fini();
- ds_fini();
- }
-
- return (rv);
-}
-
-int
-_info(struct modinfo *modinfop)
-{
- return (mod_info(&modlinkage, modinfop));
-}
-
-int
-_fini(void)
-{
- int rv;
-
- if ((rv = mod_remove(&modlinkage)) == 0) {
- (void) ds_ports_fini();
- ds_fini();
- }
-
- return (rv);
-}
-
-static void
-ds_init(void)
-{
- int tblsz;
-
- /*
- * Initialize table of registered service classes
- */
- ds_svcs.maxsvcs = DS_MAXSVCS_INIT;
-
- tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
- ds_svcs.tbl = kmem_zalloc(tblsz, KM_SLEEP);
-
- rw_init(&ds_svcs.rwlock, NULL, RW_DRIVER, NULL);
-
- ds_svcs.nsvcs = 0;
-
- /*
- * Initialize the message log.
- */
- ds_log_init();
-
- /*
- * Create taskq for internal processing threads. This
- * includes processing incoming request messages and
- * sending out of band registration messages.
- */
- ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1,
- DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
-
- ds_enabled = B_TRUE;
-
- /* catch problems with the version array */
- ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
-}
-
-static void
-ds_fini(void)
-{
- int idx;
-
- /*
- * Flip the enabled switch to make sure that no
- * incoming events get dispatched while things
- * are being torn down.
- */
- ds_enabled = B_FALSE;
-
- /*
- * Destroy the taskq.
- */
- taskq_destroy(ds_taskq);
-
- /*
- * Destroy the message log.
- */
- ds_log_fini();
-
- /*
- * Deallocate the table of registered services
- */
-
- /* clear out all entries */
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
- idx = ds_walk_svcs(ds_svc_free, NULL);
- rw_exit(&ds_svcs.rwlock);
-
- /* should have gone through the whole table */
- ASSERT(idx == ds_svcs.maxsvcs);
-
- /* destroy the table itself */
- kmem_free(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
- rw_destroy(&ds_svcs.rwlock);
- bzero(&ds_svcs, sizeof (ds_svcs));
-}
-
-/*
- * Initialize the list of ports based on the MD.
- */
-static int
-ds_ports_init(void)
-{
- int idx;
- int rv = 0;
- md_t *mdp;
- int num_nodes;
- int listsz;
- mde_cookie_t rootnode;
- mde_cookie_t dsnode;
- mde_cookie_t *portp = NULL;
- mde_cookie_t *chanp = NULL;
- int nport;
- int nchan;
- ds_port_t *port;
-
- if ((mdp = md_get_handle()) == NULL) {
- cmn_err(CE_WARN, "unable to initialize machine description");
- return (-1);
- }
-
- num_nodes = md_node_count(mdp);
- ASSERT(num_nodes > 0);
-
- listsz = num_nodes * sizeof (mde_cookie_t);
-
- /* allocate temporary storage for MD scans */
- portp = kmem_zalloc(listsz, KM_SLEEP);
- chanp = kmem_zalloc(listsz, KM_SLEEP);
-
- rootnode = md_root_node(mdp);
- ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
-
- /*
- * The root of the search for DS port nodes is the
- * DS node. Perform a scan to find that node.
- */
- nport = md_scan_dag(mdp, rootnode, md_find_name(mdp, DS_MD_ROOT_NAME),
- md_find_name(mdp, "fwd"), portp);
-
- if (nport <= 0) {
- DS_DBG("No '%s' node in MD\n", DS_MD_ROOT_NAME);
- goto done;
- }
-
- /* expecting only one DS node */
- if (nport != 1) {
- DS_DBG("expected one '%s' node in the MD, found %d\n",
- DS_MD_ROOT_NAME, nport);
- }
-
- dsnode = portp[0];
-
- /* find all the DS ports in the MD */
- nport = md_scan_dag(mdp, dsnode, md_find_name(mdp, DS_MD_PORT_NAME),
- md_find_name(mdp, "fwd"), portp);
-
- if (nport <= 0) {
- DS_DBG("No '%s' nodes in MD\n", DS_MD_PORT_NAME);
- goto done;
- }
-
- /*
- * Initialize all the ports found in the MD.
- */
- for (idx = 0; idx < nport; idx++) {
-
- /* get the channels for this port */
- nchan = md_scan_dag(mdp, portp[idx],
- md_find_name(mdp, DS_MD_CHAN_NAME),
- md_find_name(mdp, "fwd"), chanp);
-
- if (nchan <= 0) {
- cmn_err(CE_NOTE, "No '%s' node for DS port",
- DS_MD_CHAN_NAME);
- rv = -1;
- goto done;
- }
-
- /* expecting only one channel */
- if (nchan != 1) {
- DS_DBG("expected one '%s' node for DS port, found %d\n",
- DS_MD_CHAN_NAME, nchan);
- }
-
- if (ds_port_add(mdp, portp[idx], chanp[0]) != 0) {
- rv = -1;
- goto done;
- }
- }
-
- /*
- * Initialize the LDC channel for each port.
- */
- for (idx = 0; idx < DS_MAX_PORTS; idx++) {
-
- if (!DS_PORT_IN_SET(ds_allports, idx))
- continue;
-
- port = &ds_ports[idx];
-
- mutex_enter(&port->lock);
-
- if (ds_ldc_init(port)) {
- cmn_err(CE_WARN, "ds@%lx: ports_init: failed to "
- "initialize LDC %ld", port->id, port->ldc.id);
- } else {
- DS_DBG("ds@%lx: ports_init: initialization complete\n",
- port->id);
- }
-
- mutex_exit(&port->lock);
- }
-
- rv = 0;
-
-done:
- if (rv != 0)
- (void) ds_ports_fini();
-
- kmem_free(portp, listsz);
- kmem_free(chanp, listsz);
-
- (void) md_fini_handle(mdp);
-
- return (rv);
-}
-
-static int
-ds_ports_fini(void)
-{
- int idx;
- ds_port_t *port;
-
- /*
- * Tear down each initialized port.
- */
- for (idx = 0; idx < DS_MAX_PORTS; idx++) {
-
- if (!DS_PORT_IN_SET(ds_allports, idx))
- continue;
-
- port = &ds_ports[idx];
-
- mutex_enter(&port->lock);
-
- if (port->state >= DS_PORT_LDC_INIT) {
- /* shut down the LDC for this port */
- (void) ds_ldc_fini(port);
- }
-
- port->state = DS_PORT_FREE;
-
- mutex_exit(&port->lock);
-
- /* clean up the port structure */
- mutex_destroy(&port->lock);
- DS_PORTSET_DEL(ds_allports, idx);
- }
-
- return (0);
-}
-
-static int
-ds_ldc_init(ds_port_t *port)
-{
- int rv;
- ldc_attr_t ldc_attr;
- caddr_t cb_arg = (caddr_t)port;
- char ebuf[DS_EBUFSIZE];
-
- ASSERT(MUTEX_HELD(&port->lock));
-
- DS_DBG("ds@%lx: ldc_init: ldc_id=%ld\n", port->id, port->ldc.id);
-
- ldc_attr.devclass = LDC_DEV_GENERIC;
- ldc_attr.instance = 0;
- ldc_attr.mode = LDC_MODE_RELIABLE;
- ldc_attr.mtu = DS_STREAM_MTU;
-
- if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_init: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- goto done;
- }
-
- /* register the LDC callback */
- if ((rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, cb_arg)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_reg_callback: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- goto done;
- }
-
- if ((rv = ldc_open(port->ldc.hdl)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_open: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- goto done;
- }
-
- (void) ldc_up(port->ldc.hdl);
-
- (void) ldc_status(port->ldc.hdl, &port->ldc.state);
-
- DS_DBG_LDC("ds@%lx: ldc_init: initial LDC state 0x%x\n",
- port->id, port->ldc.state);
-
- port->state = DS_PORT_LDC_INIT;
-
- /* if port is up, send init message */
- if (port->ldc.state == LDC_UP) {
- ds_send_init_req(port);
- }
-
-done:
- return (rv);
-}
-
-static int
-ds_ldc_fini(ds_port_t *port)
-{
- int rv;
- char ebuf[DS_EBUFSIZE];
-
- ASSERT(port->state >= DS_PORT_LDC_INIT);
-
- DS_DBG("ds@%lx: ldc_fini: ldc_id=%ld\n", port->id, port->ldc.id);
-
- if ((rv = ldc_close(port->ldc.hdl)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_close: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- return (rv);
- }
-
- if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_unreg_callback: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- return (rv);
- }
-
- if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
- cmn_err(CE_WARN, "ds@%lx: ldc_fini: %s", port->id,
- ds_errno_to_str(rv, ebuf));
- return (rv);
- }
-
- return (rv);
-}
-
-static uint_t
-ds_ldc_reconnect(ds_port_t *port)
-{
- ldc_status_t ldc_state;
- int rv;
- ldc_handle_t ldc_hdl;
- int write_held;
- char ebuf[DS_EBUFSIZE];
-
- /*
- * We can enter this code holding write lock via ds_handle_init_ack,
- * ds_handle_reg_nack, ds_handle_unreg_req, and ds_handle_nack. We
- * do not enter this code ever holding the read lock.
- */
- write_held = RW_WRITE_HELD(&ds_svcs.rwlock);
- if (!write_held) {
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
- }
-
- mutex_enter(&port->lock);
-
- ldc_hdl = port->ldc.hdl;
-
- /* reset the port state */
- ds_port_reset(port);
- (void) ldc_up(ldc_hdl);
-
- /* read status after bringing LDC up */
- if ((rv = ldc_status(ldc_hdl, &ldc_state)) != 0) {
- cmn_err(CE_NOTE, "ds@%lx: ds_ldc_reconnect: ldc_status: %s",
- port->id, ds_errno_to_str(rv, ebuf));
- } else {
- port->ldc.state = ldc_state;
-
- /*
- * If the channel is already up, initiate
- * the handshake.
- */
- if (ldc_state == LDC_UP)
- ds_send_init_req(port);
-
- DS_DBG_LDC("ds@%lx: ds_ldc_reconnect: succeeded", port->id);
- }
-
- mutex_exit(&port->lock);
- if (!write_held) {
- rw_exit(&ds_svcs.rwlock);
- }
-
- return (rv);
-}
-
-/*
- * A DS event consists of a buffer on a port.
- */
-typedef struct ds_event {
- ds_port_t *port;
- char *buf;
- size_t buflen;
-} ds_event_t;
-
-static uint_t
-ds_ldc_cb(uint64_t event, caddr_t arg)
-{
- ldc_status_t ldc_state;
- int rv;
- ds_port_t *port = (ds_port_t *)arg;
- ldc_handle_t ldc_hdl;
- char ebuf[DS_EBUFSIZE];
-
- DS_DBG("ds@%lx: LDC event received: 0x%lx\n", port->id, event);
-
- if (!ds_enabled) {
- DS_DBG("ds@%lx: callback handling is disabled\n", port->id);
- return (LDC_SUCCESS);
- }
-
- ldc_hdl = port->ldc.hdl;
-
- /*
- * Check the LDC event.
- */
- if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
-
- ASSERT((event & (LDC_EVT_UP | LDC_EVT_READ)) == 0);
-
- rv = ds_ldc_reconnect(port);
-
- return (rv);
- }
-
- mutex_enter(&port->lock);
-
- if (event & LDC_EVT_UP) {
- if ((rv = ldc_status(ldc_hdl, &ldc_state)) != 0) {
- cmn_err(CE_NOTE, "ds@%lx: ds_ldc_cb: ldc_status: %s\n",
- port->id, ds_errno_to_str(rv, ebuf));
- goto done;
- }
- port->ldc.state = ldc_state;
-
- /* initiate the handshake */
- ds_send_init_req(port);
- }
-
- if (event & LDC_EVT_READ) {
- /* dispatch a thread to handle the read event */
- if (DS_DISPATCH(ds_handle_recv, port) == NULL) {
- cmn_err(CE_WARN, "error initiating event handler");
- }
- }
-
- if (event & LDC_EVT_WRITE) {
- cmn_err(CE_NOTE, "ds@%lx: LDC write event received, not"
- " supported\n", port->id);
- goto done;
- }
-
- /* report any unknown LDC events */
- if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
- cmn_err(CE_NOTE, "ds@%lx: Unexpected LDC event received: "
- "0x%lx\n", port->id, event);
- }
-
-done:
- mutex_exit(&port->lock);
-
- return (LDC_SUCCESS);
-}
-
-/*
- * Attempt to read a specified number of bytes from a particular LDC.
- * Returns zero for success or the return code from the LDC read on
- * failure. The actual number of bytes read from the LDC is returned
- * in the size parameter.
- */
-static int
-ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
-{
- int rv = 0;
- size_t bytes_req = *sizep;
- size_t bytes_left = bytes_req;
- size_t nbytes;
- int retry_count = 0;
- char ebuf[DS_EBUFSIZE];
-
- *sizep = 0;
-
- DS_DBG_LDC("ds@%lx: attempting to read %ld bytes\n", port->id,
- bytes_req);
-
- while (bytes_left > 0) {
-
- nbytes = bytes_left;
-
- if ((rv = ldc_read(port->ldc.hdl, msgp, &nbytes)) != 0) {
- if (rv == ECONNRESET) {
- break;
- } else if (rv != EAGAIN) {
- cmn_err(CE_NOTE, "ds@%lx: ds_recv_msg: %s",
- port->id, ds_errno_to_str(rv, ebuf));
- break;
- }
- } else {
- if (nbytes != 0) {
- DS_DBG_LDC("ds@%lx: read %ld bytes, %d "
- "retries\n", port->id, nbytes, retry_count);
-
- *sizep += nbytes;
- msgp += nbytes;
- bytes_left -= nbytes;
-
- /* reset counter on a successful read */
- retry_count = 0;
- continue;
- }
-
- /*
- * No data was read. Check if this is the
- * first attempt. If so, just return since
- * nothing has been read yet.
- */
- if (bytes_left == bytes_req) {
- DS_DBG_LDC("ds@%lx: read zero bytes, no data "
- "available\n", port->id);
- break;
- }
- }
-
- /*
- * A retry is necessary because the read returned
- * EAGAIN, or a zero length read occurred after
- * reading a partial message.
- */
- if (retry_count++ >= ds_retries) {
- DS_DBG_LDC("ds@%lx: timed out waiting for "
- "message\n", port->id);
- break;
- }
-
- drv_usecwait(ds_delay);
- }
-
- return (rv);
-}
-
-static void
-ds_handle_recv(void *arg)
-{
- ds_port_t *port = (ds_port_t *)arg;
- char *hbuf;
- size_t msglen;
- size_t read_size;
- boolean_t hasdata;
- ds_hdr_t hdr;
- uint8_t *msg;
- char *currp;
- int rv;
- ldc_handle_t ldc_hdl;
- ds_event_t *devent;
-
- DS_DBG("ds@%lx: ds_handle_recv...\n", port->id);
-
- ldc_hdl = port->ldc.hdl;
-
- mutex_enter(&port->lock);
-
- /*
- * Read messages from the channel until there are none
- * pending. Valid messages are dispatched to be handled
- * by a separate thread while any malformed messages are
- * dropped.
- */
- while ((rv = ldc_chkq(ldc_hdl, &hasdata)) == 0 && hasdata) {
-
- DS_DBG("ds@%lx: reading next message\n", port->id);
-
- /*
- * Read in the next message.
- */
- hbuf = (char *)&hdr;
- bzero(hbuf, DS_HDR_SZ);
- read_size = DS_HDR_SZ;
- currp = hbuf;
-
- /* read in the message header */
- if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
- break;
- }
-
- if (read_size < DS_HDR_SZ) {
- /*
- * A zero length read is a valid signal that
- * there is no data left on the channel.
- */
- if (read_size != 0) {
- cmn_err(CE_NOTE, "ds@%lx: invalid message "
- "length, received %ld bytes, expected %ld",
- port->id, read_size, DS_HDR_SZ);
- }
- continue;
- }
-
- /* get payload size and allocate a buffer */
- read_size = ((ds_hdr_t *)hbuf)->payload_len;
- msglen = DS_HDR_SZ + read_size;
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- /* move message header into buffer */
- bcopy(hbuf, msg, DS_HDR_SZ);
- currp = (char *)(msg) + DS_HDR_SZ;
-
- /* read in the message body */
- if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
- kmem_free(msg, msglen);
- break;
- }
-
- /* validate the size of the message */
- if ((DS_HDR_SZ + read_size) != msglen) {
- cmn_err(CE_NOTE, "ds@%lx: invalid message length, "
- "received %ld bytes, expected %ld", port->id,
- (DS_HDR_SZ + read_size), msglen);
- kmem_free(msg, msglen);
- continue;
- }
-
- DS_DUMP_MSG(msg, msglen);
-
- /*
- * Send the message for processing, and store it
- * in the log. The memory is deallocated only when
- * the message is removed from the log.
- */
-
- devent = kmem_zalloc(sizeof (ds_event_t), KM_SLEEP);
- devent->port = port;
- devent->buf = (char *)msg;
- devent->buflen = msglen;
-
- /* log the message */
- (void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
-
- /* send the message off to get processed in a new thread */
- if (DS_DISPATCH(ds_dispatch_event, devent) == NULL) {
- cmn_err(CE_WARN, "error initiating event handler");
- kmem_free(devent, sizeof (ds_event_t));
- continue;
- }
-
- }
-
- mutex_exit(&port->lock);
-
- if (rv == ECONNRESET) {
- (void) ds_ldc_reconnect(port);
- }
-}
-
-static void
-ds_dispatch_event(void *arg)
-{
- ds_event_t *event = (ds_event_t *)arg;
- ds_hdr_t *hdr;
- ds_port_t *port;
-
- port = event->port;
-
- hdr = (ds_hdr_t *)event->buf;
-
- if (!DS_MSG_TYPE_VALID(hdr->msg_type)) {
- cmn_err(CE_NOTE, "ds@%lx: dispatch_event: invalid msg "
- "type (%d)", port->id, hdr->msg_type);
- goto done;
- }
-
- DS_DBG("ds@%lx: dispatch_event: msg_type=%d\n", port->id,
- hdr->msg_type);
-
- (*ds_msg_handlers[hdr->msg_type])(port, event->buf, event->buflen);
-
-done:
- kmem_free(event->buf, event->buflen);
- kmem_free(event, sizeof (ds_event_t));
-}
-
-/*
- * Version negotiation is always initiated by the guest. Any
- * attempt by a remote party to initiate the handshake gets
- * nack'd with a major number equal to zero. This indicates
- * that no version is supported since an init request is not
- * expected.
- */
-static void
-ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_hdr_t *hdr;
- ds_init_nack_t *nack;
- char *msg;
- size_t msglen;
- ds_init_req_t *req;
- size_t explen = DS_MSG_LEN(ds_init_req_t);
-
- req = (ds_init_req_t *)(buf + DS_HDR_SZ);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <init_req: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- } else {
- DS_DBG("ds@%lx: <init_req: ver=%d.%d\n", port->id,
- req->major_vers, req->minor_vers);
- }
-
- DS_DBG("ds@%lx: init_nack>: major=0\n", port->id);
-
- msglen = DS_MSG_LEN(ds_init_nack_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_INIT_NACK;
- hdr->payload_len = sizeof (ds_init_nack_t);
-
- nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
- nack->major_vers = 0;
-
- /* send message */
- mutex_enter(&port->lock);
- (void) ds_send_msg(port, msg, msglen);
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-}
-
-static void
-ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_init_ack_t *ack;
- ds_ver_t *ver;
- size_t explen = DS_MSG_LEN(ds_init_ack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <init_ack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
-
- mutex_enter(&port->lock);
-
- if (port->state != DS_PORT_INIT_REQ) {
- cmn_err(CE_NOTE, "ds@%lx: <init_ack: invalid state for msg "
- "(%d)", port->id, port->state);
- mutex_exit(&port->lock);
- return;
- }
-
- ver = &(ds_vers[port->ver_idx]);
-
- DS_DBG("ds@%lx: <init_ack: req=v%d.%d, ack=v%d.%d\n", port->id,
- ver->major, ver->minor, ver->major, ack->minor_vers);
-
- /* agreed upon a major version */
- port->ver.major = ver->major;
-
- /*
- * If the returned minor version is larger than
- * the requested minor version, use the lower of
- * the two, i.e. the requested version.
- */
- if (ack->minor_vers >= ver->minor) {
- /*
- * Use the minor version specified in the
- * original request.
- */
- port->ver.minor = ver->minor;
- } else {
- /*
- * Use the lower minor version returned in
- * the ack. By definition, all lower minor
- * versions must be supported.
- */
- port->ver.minor = ack->minor_vers;
- }
-
- port->state = DS_PORT_READY;
-
- DS_DBG("ds@%lx: <init_ack: port ready v%d.%d\n", port->id,
- port->ver.major, port->ver.minor);
-
- mutex_exit(&port->lock);
-
- /*
- * The port came up, so update all the services
- * with this information. Follow that up with an
- * attempt to register any service that is not
- * already registered.
- */
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- (void) ds_walk_svcs(ds_svc_port_up, port);
- (void) ds_walk_svcs(ds_svc_register, NULL);
-
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
-{
- int idx;
- ds_init_nack_t *nack;
- ds_ver_t *ver;
- size_t explen = DS_MSG_LEN(ds_init_nack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <init_nack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
-
- mutex_enter(&port->lock);
-
- if (port->state != DS_PORT_INIT_REQ) {
- cmn_err(CE_NOTE, "ds@%lx: <init_nack: invalid state for msg "
- "(%d)", port->id, port->state);
- mutex_exit(&port->lock);
- return;
- }
-
- ver = &(ds_vers[port->ver_idx]);
-
- DS_DBG("ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x\n", port->id,
- ver->major, ver->minor, nack->major_vers);
-
- if (nack->major_vers == 0) {
- /* no supported protocol version */
- cmn_err(CE_NOTE, "ds@%lx: <init_nack: DS not supported",
- port->id);
- mutex_exit(&port->lock);
- return;
- }
-
- /*
- * Walk the version list, looking for a major version
- * that is as close to the requested major version as
- * possible.
- */
- for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
- if (ds_vers[idx].major <= nack->major_vers) {
- /* found a version to try */
- goto done;
- }
- }
-
- if (idx == DS_NUM_VER) {
- /* no supported version */
- cmn_err(CE_NOTE, "ds@%lx: <init_nack: DS v%d.x not supported",
- port->id, nack->major_vers);
-
- mutex_exit(&port->lock);
- return;
- }
-
-done:
- /* start the handshake again */
- port->ver_idx = idx;
- port->state = DS_PORT_LDC_INIT;
-
- ds_send_init_req(port);
-
- mutex_exit(&port->lock);
-}
-
-static void
-ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_hdr_t *hdr;
- ds_reg_req_t *req;
- ds_reg_nack_t *nack;
- char *msg;
- size_t msglen;
- size_t explen = DS_MSG_LEN(ds_reg_req_t);
-
- /* the request information */
- req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
-
- /* sanity check the incoming message */
- if (len < explen) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_req: invalid message length "
- "(%ld), expected at least %ld", port->id, len, explen);
- } else {
- DS_DBG("ds@%lx: <reg_req: id='%s', ver=%d.%d, hdl=0x%09lx\n",
- port->id, req->svc_id, req->major_vers, req->minor_vers,
- req->svc_handle);
- }
-
- DS_DBG("ds@%lx: reg_nack>: major=0\n", port->id);
-
- msglen = DS_MSG_LEN(ds_reg_nack_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_REG_NACK;
- hdr->payload_len = sizeof (ds_reg_nack_t);
-
- nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
- nack->svc_handle = req->svc_handle;
- nack->result = DS_REG_VER_NACK;
- nack->major_vers = 0;
-
- /* send message */
- mutex_enter(&port->lock);
- (void) ds_send_msg(port, msg, msglen);
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-}
-
-static void
-ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_reg_ack_t *ack;
- ds_ver_t *ver;
- ds_ver_t tmpver;
- ds_svc_t *svc;
- size_t explen = DS_MSG_LEN(ds_reg_ack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_ack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /* lookup appropriate client */
- if ((svc = ds_get_svc(ack->svc_handle)) == NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_ack: invalid handle 0x%lx",
- port->id, ack->svc_handle);
- goto done;
- }
-
- /* make sure the message makes sense */
- if (svc->state != DS_SVC_REG_PENDING) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_ack: invalid state for message "
- "(%d)", port->id, svc->state);
- goto done;
- }
-
- ver = &(svc->cap.vers[svc->ver_idx]);
-
- DS_DBG("ds@%lx: <reg_ack: hdl=0x%09lx, ack=v%d.%d\n", port->id,
- ack->svc_handle, ver->major, ack->minor_vers);
-
- /* major version has been agreed upon */
- svc->ver.major = ver->major;
-
- if (ack->minor_vers >= ver->minor) {
- /*
- * Use the minor version specified in the
- * original request.
- */
- svc->ver.minor = ver->minor;
- } else {
- /*
- * Use the lower minor version returned in
- * the ack. By definition, all lower minor
- * versions must be supported.
- */
- svc->ver.minor = ack->minor_vers;
- }
-
- svc->state = DS_SVC_ACTIVE;
-
- DS_DBG("ds@%lx: <reg_ack: %s v%d.%d ready, hdl=0x%09lx\n", port->id,
- svc->cap.svc_id, svc->ver.major, svc->ver.minor, svc->hdl);
-
- /* notify the client that registration is complete */
- if (svc->ops.ds_reg_cb) {
- /*
- * Use a temporary version structure so that
- * the copy in the svc structure cannot be
- * modified by the client.
- */
- tmpver.major = svc->ver.major;
- tmpver.minor = svc->ver.minor;
-
- (*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
- }
-
-done:
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_reg_nack_t *nack;
- ds_svc_t *svc;
- int idx;
- boolean_t reset_svc = B_FALSE;
- size_t explen = DS_MSG_LEN(ds_reg_nack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_nack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /* lookup appropriate client */
- if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_nack: invalid handle 0x%lx",
- port->id, nack->svc_handle);
- goto done;
- }
-
- /* make sure the message makes sense */
- if (svc->state != DS_SVC_REG_PENDING) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_nack: invalid state for message "
- "(%d)", port->id, svc->state);
- goto done;
- }
-
- if (nack->result == DS_REG_DUP) {
- cmn_err(CE_NOTE, "ds@%lx: <reg_nack: duplicate registration "
- "for %s", port->id, svc->cap.svc_id);
- reset_svc = B_TRUE;
- goto done;
- }
-
- /*
- * A major version of zero indicates that the
- * service is not supported at all.
- */
- if (nack->major_vers == 0) {
- DS_DBG("ds@%lx: <reg_nack: %s not supported\n", port->id,
- svc->cap.svc_id);
- reset_svc = B_TRUE;
- goto done;
- }
-
- DS_DBG("ds@%lx: <reg_nack: hdl=0x%09lx, nack=%d.x\n", port->id,
- nack->svc_handle, nack->major_vers);
-
- /*
- * Walk the version list for the service, looking for
- * a major version that is as close to the requested
- * major version as possible.
- */
- for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
- if (svc->cap.vers[idx].major <= nack->major_vers) {
- /* found a version to try */
- break;
- }
- }
-
- if (idx == svc->cap.nvers) {
- /* no supported version */
- DS_DBG("ds@%lx: <reg_nack: %s v%d.x not supported\n",
- port->id, svc->cap.svc_id, nack->major_vers);
- reset_svc = B_TRUE;
- goto done;
- }
-
- /* start the handshake again */
- svc->state = DS_SVC_INACTIVE;
- svc->ver_idx = idx;
-
- (void) ds_svc_register(svc, NULL);
-
-done:
- if (reset_svc)
- ds_reset_svc(svc, port);
-
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_hdr_t *hdr;
- ds_unreg_req_t *req;
- ds_unreg_ack_t *ack;
- ds_svc_t *svc;
- char *msg;
- size_t msglen;
- size_t explen = DS_MSG_LEN(ds_unreg_req_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_req: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- /* the request information */
- req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /* lookup appropriate client */
- if ((svc = ds_get_svc(req->svc_handle)) == NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_req: invalid handle "
- "0x%lx", port->id, req->svc_handle);
- ds_send_unreg_nack(port, req->svc_handle);
- goto done;
- }
-
- /* unregister the service */
- (void) ds_svc_unregister(svc, svc->port);
-
- DS_DBG("ds@%lx: unreg_ack>: hdl=0x%09lx\n", port->id, req->svc_handle);
-
- msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_UNREG_ACK;
- hdr->payload_len = sizeof (ds_unreg_ack_t);
-
- ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
- ack->svc_handle = req->svc_handle;
-
- /* send message */
- mutex_enter(&port->lock);
- (void) ds_send_msg(port, msg, msglen);
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-
-done:
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_unreg_ack_t *ack;
- size_t explen = DS_MSG_LEN(ds_unreg_ack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_ack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
-
- DS_DBG("ds@%lx: <unreg_ack: hdl=0x%09lx\n", port->id, ack->svc_handle);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /*
- * Since the unregister request was initiated locally,
- * the service structure has already been torn down.
- * Just perform a sanity check to make sure the message
- * is appropriate.
- */
- if (ds_get_svc(ack->svc_handle) != NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_ack: handle 0x%lx still "
- "in use", port->id, ack->svc_handle);
- }
-
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_unreg_nack_t *nack;
- size_t explen = DS_MSG_LEN(ds_unreg_nack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_nack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
-
- DS_DBG("ds@%lx: <unreg_nack: hdl=0x%09lx\n", port->id,
- nack->svc_handle);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /*
- * Since the unregister request was initiated locally,
- * the service structure has already been torn down.
- * Just perform a sanity check to make sure the message
- * is appropriate.
- */
- if (ds_get_svc(nack->svc_handle) != NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <unreg_nack: handle 0x%lx still "
- "in use", port->id, nack->svc_handle);
- }
-
- rw_exit(&ds_svcs.rwlock);
-}
-
-static void
-ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_data_handle_t *data;
- ds_svc_t *svc;
- char *msg;
- int msgsz;
- int hdrsz;
- size_t explen = DS_MSG_LEN(ds_data_handle_t);
-
- /* sanity check the incoming message */
- if (len < explen) {
- cmn_err(CE_NOTE, "ds@%lx: <data: invalid message length "
- "(%ld), expected at least %ld", port->id, len, explen);
- return;
- }
-
- data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
-
- hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
- msgsz = len - hdrsz;
-
- /* strip off the header for the client */
- msg = (msgsz) ? (buf + hdrsz) : NULL;
-
- rw_enter(&ds_svcs.rwlock, RW_READER);
-
- /* lookup appropriate client */
- if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
- cmn_err(CE_NOTE, "ds@%lx: <data: invalid handle 0x%lx",
- port->id, data->svc_handle);
- rw_exit(&ds_svcs.rwlock);
- ds_send_data_nack(port, data->svc_handle);
- return;
- }
-
- rw_exit(&ds_svcs.rwlock);
-
- DS_DBG("ds@%lx: <data: client=%s hdl=0x%09lx\n", port->id,
- (svc->cap.svc_id) ? svc->cap.svc_id : "NULL", svc->hdl);
-
- /* dispatch this message to the client */
- (*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
-}
-
-static void
-ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
-{
- ds_svc_t *svc;
- ds_data_nack_t *nack;
- size_t explen = DS_MSG_LEN(ds_data_nack_t);
-
- /* sanity check the incoming message */
- if (len != explen) {
- cmn_err(CE_NOTE, "ds@%lx: <data_nack: invalid message length "
- "(%ld), expected %ld", port->id, len, explen);
- return;
- }
-
- nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
-
- DS_DBG("ds@%lx: data_nack: hdl=0x%09lx, result=0x%lx\n", port->id,
- nack->svc_handle, nack->result);
-
- if (nack->result == DS_INV_HDL) {
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
- rw_exit(&ds_svcs.rwlock);
- return;
- }
-
- cmn_err(CE_NOTE, "ds@%lx: <data_nack: handle 0x%lx reported "
- "as invalid", port->id, nack->svc_handle);
-
- (void) ds_svc_unregister(svc, svc->port);
-
- rw_exit(&ds_svcs.rwlock);
- }
-}
-
-static int
-ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
-{
- int rv;
- caddr_t currp = msg;
- size_t amt_left = msglen;
- int loopcnt = 0;
- char ebuf[DS_EBUFSIZE];
-
- DS_DUMP_MSG(msg, msglen);
-
- (void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen);
-
- /*
- * ensure that no other messages can be sent on this port in case
- * the write doesn't get sent with one write to guarantee that the
- * message doesn't become fragmented.
- */
- ASSERT(MUTEX_HELD(&port->lock));
-
- /* send the message */
- do {
- if ((rv = ldc_write(port->ldc.hdl, currp, &msglen)) != 0) {
- if (rv == ECONNRESET) {
- mutex_exit(&port->lock);
- (void) ds_ldc_reconnect(port);
- mutex_enter(&port->lock);
- return (rv);
- } else if ((rv == EWOULDBLOCK) &&
- (loopcnt++ < ds_retries)) {
- drv_usecwait(ds_delay);
- } else {
- cmn_err(CE_WARN, "ds@%lx: ldc_write: %s",
- port->id, ds_errno_to_str(rv, ebuf));
- return (rv);
- }
- } else {
- amt_left -= msglen;
- currp += msglen;
- msglen = amt_left;
- loopcnt = 0;
- }
- } while (amt_left > 0);
-
- return (rv);
-}
-
-static void
-ds_send_init_req(ds_port_t *port)
-{
- ds_hdr_t *hdr;
- ds_init_req_t *init_req;
- size_t msglen;
- ds_ver_t *vers = &ds_vers[port->ver_idx];
-
- ASSERT(MUTEX_HELD(&port->lock));
-
- if (port->state != DS_PORT_LDC_INIT) {
- cmn_err(CE_NOTE, "ds@%lx: init_req>: invalid port state (%d)",
- port->id, port->state);
- return;
- }
-
- DS_DBG("ds@%lx: init_req>: req=v%d.%d\n", port->id, vers->major,
- vers->minor);
-
- msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
- hdr = kmem_zalloc(msglen, KM_SLEEP);
-
- hdr->msg_type = DS_INIT_REQ;
- hdr->payload_len = sizeof (ds_init_req_t);
-
- init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
- init_req->major_vers = vers->major;
- init_req->minor_vers = vers->minor;
-
- /* send the message */
- if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
- port->state = DS_PORT_INIT_REQ;
- }
-
- kmem_free(hdr, msglen);
-}
-
-static int
-ds_send_reg_req(ds_svc_t *svc)
-{
- int rv = 0;
- ds_port_t *port = svc->port;
- ds_ver_t *ver;
- ds_hdr_t *hdr;
- caddr_t msg;
- size_t msglen;
- ds_reg_req_t *req;
- size_t idlen;
-
- /* assumes some checking has already occurred */
- ASSERT(svc->state == DS_SVC_INACTIVE);
-
- mutex_enter(&port->lock);
-
- /* check on the LDC to Zeus */
- if (port->ldc.state != LDC_UP) {
- /* can not send message */
- DS_DBG("ds@%lx: reg_req>: channel %ld is not up\n", port->id,
- port->ldc.id);
- mutex_exit(&port->lock);
- return (-1);
- }
-
- /* make sure port is ready */
- if (port->state != DS_PORT_READY) {
- /* can not send message */
- DS_DBG("ds@%lx: reg_req>: port is not ready\n", port->id);
- mutex_exit(&port->lock);
- return (-1);
- }
-
- mutex_exit(&port->lock);
-
- /* allocate the message buffer */
- idlen = strlen(svc->cap.svc_id);
- msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- /* copy in the header data */
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_REG_REQ;
- hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
-
- req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
- req->svc_handle = svc->hdl;
- ver = &(svc->cap.vers[svc->ver_idx]);
- req->major_vers = ver->major;
- req->minor_vers = ver->minor;
-
- /* copy in the service id */
- bcopy(svc->cap.svc_id, req->svc_id, idlen + 1);
-
- /* send the message */
- DS_DBG("ds@%lx: reg_req>: id='%s', ver=%d.%d, hdl=0x%09lx\n", port->id,
- svc->cap.svc_id, ver->major, ver->minor, svc->hdl);
-
- mutex_enter(&port->lock);
- if (ds_send_msg(port, msg, msglen) != 0) {
- rv = -1;
- } else {
- svc->state = DS_SVC_REG_PENDING;
- }
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
- return (rv);
-}
-
-static int
-ds_send_unreg_req(ds_svc_t *svc)
-{
- int rv = 0;
- caddr_t msg;
- size_t msglen;
- ds_hdr_t *hdr;
- ds_unreg_req_t *req;
- ds_port_t *port = svc->port;
-
- if (port == NULL) {
- DS_DBG("send_unreg_req: service '%s' not associated with "
- "a port\n", svc->cap.svc_id);
- return (-1);
- }
-
- mutex_enter(&port->lock);
-
- /* check on the LDC to Zeus */
- if (port->ldc.state != LDC_UP) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: unreg_req>: channel %ld is not up\n",
- port->id, port->ldc.id);
- mutex_exit(&port->lock);
- return (-1);
- }
-
- /* make sure port is ready */
- if (port->state != DS_PORT_READY) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: unreg_req>: port is not ready\n",
- port->id);
- mutex_exit(&port->lock);
- return (-1);
- }
-
- mutex_exit(&port->lock);
-
- msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- /* copy in the header data */
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_UNREG;
- hdr->payload_len = sizeof (ds_unreg_req_t);
-
- req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
- req->svc_handle = svc->hdl;
-
- /* send the message */
- DS_DBG("ds@%lx: unreg_req>: id='%s', hdl=0x%09lx\n", port->id,
- (svc->cap.svc_id) ? svc->cap.svc_id : "NULL", svc->hdl);
-
- mutex_enter(&port->lock);
-
- if (ds_send_msg(port, msg, msglen) != 0)
- rv = -1;
-
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
- return (rv);
-}
-
-static void
-ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
-{
- caddr_t msg;
- size_t msglen;
- ds_hdr_t *hdr;
- ds_unreg_nack_t *nack;
-
- mutex_enter(&port->lock);
-
- /* check on the LDC to Zeus */
- if (port->ldc.state != LDC_UP) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: unreg_nack>: channel %ld is not up",
- port->id, port->ldc.id);
- mutex_exit(&port->lock);
- return;
- }
-
- /* make sure port is ready */
- if (port->state != DS_PORT_READY) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: unreg_nack>: port is not ready",
- port->id);
- mutex_exit(&port->lock);
- return;
- }
-
- mutex_exit(&port->lock);
-
- msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- /* copy in the header data */
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_UNREG_NACK;
- hdr->payload_len = sizeof (ds_unreg_nack_t);
-
- nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
- nack->svc_handle = bad_hdl;
-
- /* send the message */
- DS_DBG("ds@%lx: unreg_nack>: hdl=0x%09lx\n", port->id, bad_hdl);
-
- mutex_enter(&port->lock);
- (void) ds_send_msg(port, msg, msglen);
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-}
-
-static void
-ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
-{
- caddr_t msg;
- size_t msglen;
- ds_hdr_t *hdr;
- ds_data_nack_t *nack;
-
- mutex_enter(&port->lock);
-
- /* check on the LDC to Zeus */
- if (port->ldc.state != LDC_UP) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: data_nack>: channel %ld is not up",
- port->id, port->ldc.id);
- mutex_exit(&port->lock);
- return;
- }
-
- /* make sure port is ready */
- if (port->state != DS_PORT_READY) {
- /* can not send message */
- cmn_err(CE_NOTE, "ds@%lx: data_nack>: port is not ready",
- port->id);
- mutex_exit(&port->lock);
- return;
- }
-
- mutex_exit(&port->lock);
-
- msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
- msg = kmem_zalloc(msglen, KM_SLEEP);
-
- /* copy in the header data */
- hdr = (ds_hdr_t *)msg;
- hdr->msg_type = DS_NACK;
- hdr->payload_len = sizeof (ds_data_nack_t);
-
- nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
- nack->svc_handle = bad_hdl;
- nack->result = DS_INV_HDL;
-
- /* send the message */
- DS_DBG("ds@%lx: data_nack>: hdl=0x%09lx\n", port->id, bad_hdl);
-
- mutex_enter(&port->lock);
- (void) ds_send_msg(port, msg, msglen);
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-}
-
-#ifdef DEBUG
-
-#define BYTESPERLINE 8
-#define LINEWIDTH ((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
-#define ASCIIOFFSET ((BYTESPERLINE * 3) + 2)
-#define ISPRINT(c) ((c >= ' ') && (c <= '~'))
-
-/*
- * Output a buffer formatted with a set number of bytes on
- * each line. Append each line with the ASCII equivalent of
- * each byte if it falls within the printable ASCII range,
- * and '.' otherwise.
- */
-static void
-ds_dump_msg(void *vbuf, size_t len)
-{
- int i, j;
- char *curr;
- char *aoff;
- char line[LINEWIDTH];
- uint8_t *buf = vbuf;
-
- /* abort if not debugging ldc */
- if (!(ds_debug & DS_DBG_FLAG_MSG)) {
- return;
- }
-
- /* walk the buffer one line at a time */
- for (i = 0; i < len; i += BYTESPERLINE) {
-
- bzero(line, LINEWIDTH);
-
- curr = line;
- aoff = line + ASCIIOFFSET;
-
- /*
- * Walk the bytes in the current line, storing
- * the hex value for the byte as well as the
- * ASCII representation in a temporary buffer.
- * All ASCII values are placed at the end of
- * the line.
- */
- for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
- (void) sprintf(curr, " %02x", buf[i + j]);
- *aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
- curr += 3;
- aoff++;
- }
-
- /*
- * Fill in to the start of the ASCII translation
- * with spaces. This will only be necessary if
- * this is the last line and there are not enough
- * bytes to fill the whole line.
- */
- while (curr != (line + ASCIIOFFSET))
- *curr++ = ' ';
-
- DS_DBG_MSG("%s\n", line);
- }
-}
-#endif /* DEBUG */
-
-
-/*
- * Walk the table of registered services, executing the specified
- * callback function for each service. A non-zero return value from
- * the callback is used to terminate the walk, not to indicate an
- * error. Returns the index of the last service visited.
- */
-static int
-ds_walk_svcs(svc_cb_t svc_cb, void *arg)
-{
- int idx;
- ds_svc_t *svc;
-
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
-
- /* walk every table entry */
- for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
-
- svc = ds_svcs.tbl[idx];
-
- /* execute the callback */
- if ((*svc_cb)(svc, arg) != 0)
- break;
- }
-
- return (idx);
-}
-
-static int
-ds_svc_isfree(ds_svc_t *svc, void *arg)
-{
- _NOTE(ARGUNUSED(arg))
-
- /*
- * Looking for a free service. This may be a NULL entry
- * in the table, or an unused structure that could be
- * reused.
- */
-
- if (DS_SVC_ISFREE(svc)) {
- /* yes, it is free */
- return (1);
- }
-
- /* not a candidate */
- return (0);
-}
-
-static int
-ds_svc_ismatch(ds_svc_t *svc, void *arg)
-{
- if (DS_SVC_ISFREE(svc)) {
- return (0);
- }
-
- if (strcmp(svc->cap.svc_id, arg) == 0) {
- /* found a match */
- return (1);
- }
-
- return (0);
-}
-
-static int
-ds_svc_free(ds_svc_t *svc, void *arg)
-{
- _NOTE(ARGUNUSED(arg))
-
- if (svc == NULL) {
- return (0);
- }
-
- if (svc->cap.svc_id) {
- kmem_free(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
- svc->cap.svc_id = NULL;
- }
-
- if (svc->cap.vers) {
- kmem_free(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
- svc->cap.vers = NULL;
- }
-
- kmem_free(svc, sizeof (ds_svc_t));
-
- return (0);
-}
-
-static int
-ds_svc_register(ds_svc_t *svc, void *arg)
-{
- _NOTE(ARGUNUSED(arg))
-
- int idx;
-
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
-
- /* check the state of the service */
- if (DS_SVC_ISFREE(svc) || (svc->state != DS_SVC_INACTIVE))
- return (0);
-
- /* check if there are any ports to try */
- if (DS_PORTSET_ISNULL(svc->avail))
- return (0);
-
- /*
- * Attempt to register the service. Start with the lowest
- * numbered port and continue until a registration message
- * is sent successfully, or there are no ports left to try.
- */
- for (idx = 0; idx < DS_MAX_PORTS; idx++) {
-
- /*
- * If the port is not in the available list,
- * it is not a candidate for registration.
- */
- if (!DS_PORT_IN_SET(svc->avail, idx)) {
- continue;
- }
-
- svc->port = &ds_ports[idx];
- if (ds_send_reg_req(svc) == 0) {
- /* register sent successfully */
- break;
- }
-
- /* reset the service to try the next port */
- ds_reset_svc(svc, svc->port);
- }
-
- return (0);
-}
-
-static int
-ds_svc_unregister(ds_svc_t *svc, void *arg)
-{
- ds_port_t *port = (ds_port_t *)arg;
-
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
-
- if (DS_SVC_ISFREE(svc)) {
- return (0);
- }
-
- /* make sure the service is using this port */
- if (svc->port != port) {
- return (0);
- }
-
- DS_DBG("ds@%lx: svc_unreg: id='%s', ver=%d.%d, hdl=0x%09lx\n", port->id,
- svc->cap.svc_id, svc->ver.major, svc->ver.minor, svc->hdl);
-
- /* reset the service structure */
- ds_reset_svc(svc, port);
-
- /* increment the count in the handle to prevent reuse */
- svc->hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
-
- /* call the client unregister callback */
- if (svc->ops.ds_unreg_cb)
- (*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
-
- /* try to initiate a new registration */
- (void) ds_svc_register(svc, NULL);
-
- return (0);
-}
-
-static int
-ds_svc_port_up(ds_svc_t *svc, void *arg)
-{
- ds_port_t *port = (ds_port_t *)arg;
-
- if (DS_SVC_ISFREE(svc)) {
- /* nothing to do */
- return (0);
- }
-
- DS_PORTSET_ADD(svc->avail, port->id);
-
- return (0);
-}
-
-static ds_svc_t *
-ds_alloc_svc(void)
-{
- int idx;
- uint_t newmaxsvcs;
- ds_svc_t **newtbl;
- ds_svc_t *newsvc;
-
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
-
- idx = ds_walk_svcs(ds_svc_isfree, NULL);
-
- if (idx != ds_svcs.maxsvcs) {
- goto found;
- }
-
- /*
- * There was no free space in the table. Grow
- * the table to double its current size.
- */
- newmaxsvcs = ds_svcs.maxsvcs * 2;
- newtbl = kmem_zalloc(newmaxsvcs * sizeof (ds_svc_t *), KM_SLEEP);
-
- /* copy old table data to the new table */
- for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
- newtbl[idx] = ds_svcs.tbl[idx];
- }
-
- /* clean up the old table */
- kmem_free(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
- ds_svcs.tbl = newtbl;
- ds_svcs.maxsvcs = newmaxsvcs;
-
- /* search for a free space again */
- idx = ds_walk_svcs(ds_svc_isfree, NULL);
-
- /* the table is locked so should find a free slot */
- ASSERT(idx != ds_svcs.maxsvcs);
-
-found:
- /* allocate a new svc structure if necessary */
- if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
- /* allocate a new service */
- newsvc = kmem_zalloc(sizeof (ds_svc_t), KM_SLEEP);
- ds_svcs.tbl[idx] = newsvc;
- }
-
- /* fill in the handle */
- newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
-
- return (newsvc);
-}
-
-static void
-ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
-{
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
-
- svc->state = DS_SVC_INACTIVE;
- svc->ver_idx = 0;
- svc->ver.major = 0;
- svc->ver.minor = 0;
- svc->port = NULL;
- DS_PORTSET_DEL(svc->avail, port->id);
-}
-
-static ds_svc_t *
-ds_get_svc(ds_svc_hdl_t hdl)
-{
- int idx;
- ds_svc_t *svc;
-
- ASSERT(RW_LOCK_HELD(&ds_svcs.rwlock));
-
- if (hdl == DS_INVALID_HDL)
- return (NULL);
-
- idx = DS_HDL2IDX(hdl);
-
- /* check if index is out of bounds */
- if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
- return (NULL);
-
- svc = ds_svcs.tbl[idx];
-
- /* check for a valid service */
- if (DS_SVC_ISFREE(svc))
- return (NULL);
-
- /* make sure the handle is an exact match */
- if (svc->hdl != hdl)
- return (NULL);
-
- return (svc);
-}
-
-static int
-ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan)
-{
- ds_port_t *newport;
- uint64_t port_id;
- uint64_t ldc_id;
-
- /* get the ID for this port */
- if (md_get_prop_val(mdp, port, "id", &port_id) != 0) {
- cmn_err(CE_NOTE, "ds_port_add: port 'id' property not found");
- return (-1);
- }
-
- /* sanity check the port id */
- if (port_id > DS_MAX_PORT_ID) {
- cmn_err(CE_WARN, "ds_port_add: port ID %ld out of range",
- port_id);
- return (-1);
- }
-
- DS_DBG("ds_port_add: adding port ds@%ld\n", port_id);
-
- /* get the channel ID for this port */
- if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) {
- cmn_err(CE_NOTE, "ds@%lx: add_port: no channel 'id' property",
- port_id);
- return (-1);
- }
-
- /* get the port structure from the array of ports */
- newport = &ds_ports[port_id];
-
- /* check for a duplicate port in the MD */
- if (newport->state != DS_PORT_FREE) {
- cmn_err(CE_NOTE, "ds@%lx: add_port: port already exists",
- port_id);
- return (-1);
- }
-
- /* initialize the port lock */
- mutex_init(&newport->lock, NULL, MUTEX_DRIVER, NULL);
-
- /* initialize the port */
- newport->id = port_id;
- newport->state = DS_PORT_INIT;
- newport->ldc.id = ldc_id;
-
- /* add the port to the set of all ports */
- DS_PORTSET_ADD(ds_allports, port_id);
-
- return (0);
-}
-
-static void
-ds_port_reset(ds_port_t *port)
-{
- ASSERT(RW_WRITE_HELD(&ds_svcs.rwlock));
- ASSERT(MUTEX_HELD(&port->lock));
-
- /* connection went down, mark everything inactive */
- (void) ds_walk_svcs(ds_svc_unregister, port);
-
- port->ver_idx = 0;
- port->ver.major = 0;
- port->ver.minor = 0;
- port->state = DS_PORT_LDC_INIT;
-}
-
-/*
- * Verify that a version array is sorted as expected for the
- * version negotiation to work correctly.
- */
-static ds_vers_check_t
-ds_vers_isvalid(ds_ver_t *vers, int nvers)
-{
- uint16_t curr_major;
- uint16_t curr_minor;
- int idx;
-
- curr_major = vers[0].major;
- curr_minor = vers[0].minor;
-
- /*
- * Walk the version array, verifying correct ordering.
- * The array must be sorted from highest supported
- * version to lowest supported version.
- */
- for (idx = 0; idx < nvers; idx++) {
- if (vers[idx].major > curr_major) {
- DS_DBG("vers_isvalid: version array has increasing "
- "major versions\n");
- return (DS_VERS_INCREASING_MAJOR_ERR);
- }
-
- if (vers[idx].major < curr_major) {
- curr_major = vers[idx].major;
- curr_minor = vers[idx].minor;
- continue;
- }
-
- if (vers[idx].minor > curr_minor) {
- DS_DBG("vers_isvalid: version array has increasing "
- "minor versions\n");
- return (DS_VERS_INCREASING_MINOR_ERR);
- }
-
- curr_minor = vers[idx].minor;
- }
-
- return (DS_VERS_OK);
-}
-
-/*
- * Logging Support
- */
-static void
-ds_log_init(void)
-{
- ds_log_entry_t *new;
-
- /* initialize global lock */
- mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL);
-
- mutex_enter(&ds_log.lock);
-
- /* initialize the log */
- ds_log.head = NULL;
- ds_log.size = 0;
- ds_log.nentry = 0;
-
- /* initialize the free list */
- for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) {
- new->next = ds_log.freelist;
- ds_log.freelist = new;
- }
-
- mutex_exit(&ds_log.lock);
-
- DS_DBG_LOG("ds_log initialized: size=%d bytes, limit=%d bytes, "
- "ninit=%ld\n", ds_log_sz, DS_LOG_LIMIT, DS_LOG_NPOOL);
-}
-
-static void
-ds_log_fini(void)
-{
- ds_log_entry_t *next;
-
- mutex_enter(&ds_log.lock);
-
- /* clear out the log */
- while (ds_log.nentry > 0)
- (void) ds_log_remove();
-
- /*
- * Now all the entries are on the free list.
- * Clear out the free list, deallocating any
- * entry that was dynamically allocated.
- */
- while (ds_log.freelist != NULL) {
- next = ds_log.freelist->next;
-
- if (!DS_IS_POOL_ENTRY(ds_log.freelist)) {
- kmem_free(ds_log.freelist, sizeof (ds_log_entry_t));
- }
-
- ds_log.freelist = next;
- }
-
- mutex_exit(&ds_log.lock);
-
- mutex_destroy(&ds_log.lock);
-}
-
-static ds_log_entry_t *
-ds_log_entry_alloc(void)
-{
- ds_log_entry_t *new = NULL;
-
- ASSERT(MUTEX_HELD(&ds_log.lock));
-
- if (ds_log.freelist != NULL) {
- new = ds_log.freelist;
- ds_log.freelist = ds_log.freelist->next;
- }
-
- if (new == NULL) {
- /* free list was empty */
- new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP);
- }
-
- ASSERT(new);
-
- return (new);
-}
-
-static void
-ds_log_entry_free(ds_log_entry_t *entry)
-{
- ASSERT(MUTEX_HELD(&ds_log.lock));
-
- if (entry == NULL)
- return;
-
- if (entry->data != NULL) {
- kmem_free(entry->data, entry->datasz);
- entry->data = NULL;
- }
-
- /* place entry on the free list */
- entry->next = ds_log.freelist;
- ds_log.freelist = entry;
-}
-
-/*
- * Add a message to the end of the log
- */
-static int
-ds_log_add(ds_log_entry_t *new)
-{
- ASSERT(MUTEX_HELD(&ds_log.lock));
-
- if (ds_log.head == NULL) {
-
- new->prev = new;
- new->next = new;
-
- ds_log.head = new;
- } else {
- ds_log_entry_t *head = ds_log.head;
- ds_log_entry_t *tail = ds_log.head->prev;
-
- new->next = head;
- new->prev = tail;
- tail->next = new;
- head->prev = new;
- }
-
- /* increase the log size, including the metadata size */
- ds_log.size += DS_LOG_ENTRY_SZ(new);
- ds_log.nentry++;
-
- DS_DBG_LOG("ds_log: added %ld data bytes, %ld total bytes\n",
- new->datasz, DS_LOG_ENTRY_SZ(new));
-
- return (0);
-}
-
-/*
- * Remove an entry from the head of the log
- */
-static int
-ds_log_remove(void)
-{
- ds_log_entry_t *head;
-
- ASSERT(MUTEX_HELD(&ds_log.lock));
-
- head = ds_log.head;
-
- /* empty list */
- if (head == NULL)
- return (0);
-
- if (head->next == ds_log.head) {
- /* one element list */
- ds_log.head = NULL;
- } else {
- head->next->prev = head->prev;
- head->prev->next = head->next;
- ds_log.head = head->next;
- }
-
- DS_DBG_LOG("ds_log: removed %ld data bytes, %ld total bytes\n",
- head->datasz, DS_LOG_ENTRY_SZ(head));
-
- ds_log.size -= DS_LOG_ENTRY_SZ(head);
- ds_log.nentry--;
-
- ds_log_entry_free(head);
-
- return (0);
-}
-
-/*
- * Replace the data in the entry at the front of the list with then
- * new data. This has the effect of removing the oldest entry and
- * adding the new entry.
- */
-static int
-ds_log_replace(int32_t dest, uint8_t *msg, size_t sz)
-{
- ds_log_entry_t *head;
-
- ASSERT(MUTEX_HELD(&ds_log.lock));
-
- head = ds_log.head;
-
- DS_DBG_LOG("ds_log: replaced %ld data bytes (%ld total) with %ld data "
- "bytes (%ld total)\n", head->datasz, DS_LOG_ENTRY_SZ(head),
- sz, sz + sizeof (ds_log_entry_t));
-
- ds_log.size -= DS_LOG_ENTRY_SZ(head);
-
- kmem_free(head->data, head->datasz);
-
- head->data = msg;
- head->datasz = sz;
- head->timestamp = ddi_get_time();
- head->dest = dest;
-
- ds_log.size += DS_LOG_ENTRY_SZ(head);
-
- ds_log.head = head->next;
-
- return (0);
-}
-
-static void
-ds_log_purge(void *arg)
-{
- _NOTE(ARGUNUSED(arg))
-
- mutex_enter(&ds_log.lock);
-
- DS_DBG_LOG("ds_log: purging oldest log entries\n");
-
- while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) {
- (void) ds_log_remove();
- }
-
- mutex_exit(&ds_log.lock);
-}
-
-static int
-ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz)
-{
- int rv = 0;
- void *data;
-
- mutex_enter(&ds_log.lock);
-
- /* allocate a local copy of the data */
- data = kmem_alloc(sz, KM_SLEEP);
- bcopy(msg, data, sz);
-
- /* check if the log is larger than the soft limit */
- if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) {
- /*
- * The log is larger than the soft limit.
- * Swap the oldest entry for the newest.
- */
- DS_DBG_LOG("ds_log: replacing oldest entry with new entry\n");
- (void) ds_log_replace(dest, data, sz);
- } else {
- /*
- * Still have headroom under the soft limit.
- * Add the new entry to the log.
- */
- ds_log_entry_t *new;
-
- new = ds_log_entry_alloc();
-
- /* fill in message data */
- new->data = data;
- new->datasz = sz;
- new->timestamp = ddi_get_time();
- new->dest = dest;
-
- rv = ds_log_add(new);
- }
-
- /* check if the log is larger than the hard limit */
- if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) {
- /*
- * Wakeup the thread to remove entries
- * from the log until it is smaller than
- * the soft limit.
- */
- DS_DBG_LOG("ds_log: log exceeded %d bytes, scheduling a "
- "purge...\n", DS_LOG_LIMIT);
-
- if (DS_DISPATCH(ds_log_purge, NULL) == NULL) {
- cmn_err(CE_NOTE, "ds_log: purge thread failed to "
- "start");
- }
- }
-
- mutex_exit(&ds_log.lock);
-
- return (rv);
-}
-
-/*
- * Client Interface
- */
-
-int
-ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
-{
- int idx;
- ds_vers_check_t status;
- ds_svc_t *svc;
-
- /* sanity check the args */
- if ((cap == NULL) || (ops == NULL)) {
- cmn_err(CE_NOTE, "ds_cap_init: invalid arguments");
- return (EINVAL);
- }
-
- /* sanity check the capability specifier */
- if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
- cmn_err(CE_NOTE, "ds_cap_init: invalid capability specifier");
- return (EINVAL);
- }
-
- /* sanity check the version array */
- if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
- cmn_err(CE_NOTE, "ds_cap_init: invalid capability "
- "version array for %s service: %s", cap->svc_id,
- (status == DS_VERS_INCREASING_MAJOR_ERR) ?
- "increasing major versions" :
- "increasing minor versions");
- return (EINVAL);
- }
-
- /* data and register callbacks are required */
- if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
- cmn_err(CE_NOTE, "ds_cap_init: invalid ops specifier for "
- "%s service", cap->svc_id);
- return (EINVAL);
- }
-
- DS_DBG("ds_cap_init: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx\n",
- cap->svc_id, (uint64_t)ops->ds_data_cb, (uint64_t)ops->cb_arg);
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /* check if the service is already registered */
- idx = ds_walk_svcs(ds_svc_ismatch, cap->svc_id);
- if (idx != ds_svcs.maxsvcs) {
- /* already registered */
- cmn_err(CE_NOTE, "service '%s' already registered",
- cap->svc_id);
- rw_exit(&ds_svcs.rwlock);
- return (EALREADY);
- }
-
- svc = ds_alloc_svc();
-
- /* copy over all the client information */
- bcopy(cap, &svc->cap, sizeof (ds_capability_t));
-
- /* make a copy of the service name */
- svc->cap.svc_id = kmem_zalloc(strlen(cap->svc_id) + 1, KM_SLEEP);
- (void) strncpy(svc->cap.svc_id, cap->svc_id, strlen(cap->svc_id));
-
- /* make a copy of the version array */
- svc->cap.vers = kmem_zalloc(cap->nvers * sizeof (ds_ver_t), KM_SLEEP);
- bcopy(cap->vers, svc->cap.vers, cap->nvers * sizeof (ds_ver_t));
-
- /* copy the client ops vector */
- bcopy(ops, &svc->ops, sizeof (ds_clnt_ops_t));
-
- svc->state = DS_SVC_INACTIVE;
- svc->ver_idx = 0;
- DS_PORTSET_DUP(svc->avail, ds_allports);
-
- ds_svcs.nsvcs++;
-
- /* attempt to register the service */
- (void) ds_svc_register(svc, NULL);
-
- rw_exit(&ds_svcs.rwlock);
-
- DS_DBG("ds_cap_init: service '%s' assigned handle 0x%09lx\n",
- svc->cap.svc_id, svc->hdl);
-
- return (0);
-}
-
-int
-ds_cap_fini(ds_capability_t *cap)
-{
- int idx;
- ds_svc_t *svc;
- ds_svc_hdl_t tmp_hdl;
-
- rw_enter(&ds_svcs.rwlock, RW_WRITER);
-
- /* make sure the service is registered */
- idx = ds_walk_svcs(ds_svc_ismatch, cap->svc_id);
- if (idx == ds_svcs.maxsvcs) {
- /* service is not registered */
- cmn_err(CE_NOTE, "ds_cap_fini: unknown service '%s'",
- cap->svc_id);
- rw_exit(&ds_svcs.rwlock);
- return (EINVAL);
- }
-
- svc = ds_svcs.tbl[idx];
-
- DS_DBG("ds_cap_fini: svcid='%s', hdl=0x%09lx\n", svc->cap.svc_id,
- svc->hdl);
-
- /*
- * Attempt to send an unregister notification. Even
- * if sending the message fails, the local unregister
- * request must be honored, since this indicates that
- * the client will no longer handle incoming requests.
- */
- (void) ds_send_unreg_req(svc);
-
- /*
- * Clear out the structure, but do not deallocate the
- * memory. It can be reused for the next registration.
- */
- kmem_free(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
- kmem_free(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
-
- /* save the handle to prevent reuse */
- tmp_hdl = svc->hdl;
- bzero(svc, sizeof (ds_svc_t));
-
- /* initialize for next use */
- svc->hdl = tmp_hdl;
- svc->state = DS_SVC_FREE;
-
- ds_svcs.nsvcs--;
-
- rw_exit(&ds_svcs.rwlock);
-
- return (0);
-}
-
-int
-ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
-{
- int rv;
- ds_hdr_t *hdr;
- caddr_t msg;
- size_t msglen;
- size_t hdrlen;
- caddr_t payload;
- ds_svc_t *svc;
- ds_port_t *port;
- ds_data_handle_t *data;
-
- rw_enter(&ds_svcs.rwlock, RW_READER);
-
- if ((hdl == DS_INVALID_HDL) || (svc = ds_get_svc(hdl)) == NULL) {
- cmn_err(CE_NOTE, "ds_cap_send: invalid handle 0x%09lx", hdl);
- rw_exit(&ds_svcs.rwlock);
- return (EINVAL);
- }
-
- if ((port = svc->port) == NULL) {
- cmn_err(CE_NOTE, "ds_cap_send: service '%s' not associated "
- "with a port", svc->cap.svc_id);
- rw_exit(&ds_svcs.rwlock);
- return (ECONNRESET);
- }
-
- mutex_enter(&port->lock);
-
- /* check that the LDC channel is ready */
- if (port->ldc.state != LDC_UP) {
- cmn_err(CE_NOTE, "ds_cap_send: LDC channel is not up");
- mutex_exit(&port->lock);
- rw_exit(&ds_svcs.rwlock);
- return (ECONNRESET);
- }
-
-
- if (svc->state != DS_SVC_ACTIVE) {
- /* channel is up, but svc is not registered */
- cmn_err(CE_NOTE, "ds_cap_send: invalid service state 0x%x",
- svc->state);
- mutex_exit(&port->lock);
- rw_exit(&ds_svcs.rwlock);
- return (EINVAL);
- }
-
- mutex_exit(&port->lock);
- rw_exit(&ds_svcs.rwlock);
-
- hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
-
- msg = kmem_zalloc(len + hdrlen, KM_SLEEP);
- hdr = (ds_hdr_t *)msg;
- payload = msg + hdrlen;
- msglen = len + hdrlen;
-
- hdr->payload_len = len + sizeof (ds_data_handle_t);
- hdr->msg_type = DS_DATA;
-
- data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
- data->svc_handle = hdl;
-
- if ((buf != NULL) && (len != 0)) {
- bcopy(buf, payload, len);
- }
-
- DS_DBG("ds@%lx: data>: hdl=0x%09lx, len=%ld, payload_len=%d\n",
- port->id, svc->hdl, msglen, hdr->payload_len);
-
- mutex_enter(&port->lock);
-
- if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
- rv = (rv == EIO) ? ECONNRESET : rv;
- }
-
- mutex_exit(&port->lock);
-
- kmem_free(msg, msglen);
-
-
- return (rv);
-}
-
-/*
- * Specific errno's that are used by ds.c and ldc.c
- */
-static struct {
- int errno;
- char *estr;
-} ds_errno_to_str_tab[] = {
- EIO, "I/O error",
- EAGAIN, "Resource temporarily unavailable",
- ENOMEM, "Not enough space",
- EACCES, "Permission denied",
- EFAULT, "Bad address",
- EBUSY, "Device busy",
- EINVAL, "Invalid argument",
- ENOSPC, "No space left on device",
- ECHRNG, "Channel number out of range",
- ENOTSUP, "Operation not supported",
- EMSGSIZE, "Message too long",
- EADDRINUSE, "Address already in use",
- ECONNRESET, "Connection reset by peer",
- ENOBUFS, "No buffer space available",
- ECONNREFUSED, "Connection refused",
- EALREADY, "Operation already in progress",
- 0,
-};
-
-static char *
-ds_errno_to_str(int errno, char *ebuf)
-{
- int i, en;
-
- for (i = 0; (en = ds_errno_to_str_tab[i].errno) != 0; i++) {
- if (en == errno) {
- (void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
- return (ebuf);
- }
- }
-
- (void) sprintf(ebuf, "errno (%d)", errno);
- return (ebuf);
-}
diff --git a/usr/src/uts/sun4v/io/ds_common.c b/usr/src/uts/sun4v/io/ds_common.c
new file mode 100644
index 0000000000..0d3a354780
--- /dev/null
+++ b/usr/src/uts/sun4v/io/ds_common.c
@@ -0,0 +1,3141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Domain Services Module Common Code.
+ *
+ * This module is intended to be used by both Solaris and the VBSC
+ * module.
+ */
+
+#include <sys/modctl.h>
+#include <sys/ksynch.h>
+#include <sys/taskq.h>
+#include <sys/disp.h>
+#include <sys/cmn_err.h>
+#include <sys/note.h>
+#include <sys/mach_descrip.h>
+#include <sys/mdesc.h>
+#include <sys/ldc.h>
+#include <sys/ds.h>
+#include <sys/ds_impl.h>
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define DS_DECODE_BUF_LEN 30
+
+/*
+ * All DS ports in the system
+ *
+ * The list of DS ports is read in from the MD when the DS module is
+ * initialized and is never modified. This eliminates the need for
+ * locking to access the port array itself. Access to the individual
+ * ports are synchronized at the port level.
+ */
+ds_port_t ds_ports[DS_MAX_PORTS];
+ds_portset_t ds_allports; /* all DS ports in the system */
+
+/*
+ * Table of registered services
+ *
+ * Locking: Accesses to the table of services are synchronized using
+ * a mutex lock. The reader lock must be held when looking up service
+ * information in the table. The writer lock must be held when any
+ * service information is being modified.
+ */
+ds_svcs_t ds_svcs;
+
+/*
+ * Flag to prevent callbacks while in the middle of DS teardown.
+ */
+boolean_t ds_enabled = B_FALSE; /* enable/disable taskq processing */
+
+/*
+ * Retry count and delay for LDC reads and writes
+ */
+#ifndef DS_DEFAULT_RETRIES
+#define DS_DEFAULT_RETRIES 10000 /* number of times to retry */
+#endif
+#ifndef DS_DEFAULT_DELAY
+#define DS_DEFAULT_DELAY 1000 /* usecs to wait between retries */
+#endif
+
+static int ds_retries = DS_DEFAULT_RETRIES;
+static clock_t ds_delay = DS_DEFAULT_DELAY;
+
+/*
+ * Supported versions of the DS message protocol
+ *
+ * The version array must be sorted in order from the highest
+ * supported version to the lowest. Support for a particular
+ * <major>.<minor> version implies all lower minor versions of
+ * that same major version are supported as well.
+ */
+static ds_ver_t ds_vers[] = { { 1, 0 } };
+
+#define DS_NUM_VER (sizeof (ds_vers) / sizeof (ds_vers[0]))
+
+
+/* incoming message handling functions */
+typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
+static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
+
+/*
+ * DS Message Handler Dispatch Table
+ *
+ * A table used to dispatch all incoming messages. This table
+ * contains handlers for all the fixed message types, as well as
+ * the the messages defined in the 1.0 version of the DS protocol.
+ * The handlers are indexed based on the DS header msg_type values
+ */
+static const ds_msg_handler_t ds_msg_handlers[] = {
+ ds_handle_init_req, /* DS_INIT_REQ */
+ ds_handle_init_ack, /* DS_INIT_ACK */
+ ds_handle_init_nack, /* DS_INIT_NACK */
+ ds_handle_reg_req, /* DS_REG_REQ */
+ ds_handle_reg_ack, /* DS_REG_ACK */
+ ds_handle_reg_nack, /* DS_REG_NACK */
+ ds_handle_unreg_req, /* DS_UNREG */
+ ds_handle_unreg_ack, /* DS_UNREG_ACK */
+ ds_handle_unreg_nack, /* DS_UNREG_NACK */
+ ds_handle_data, /* DS_DATA */
+ ds_handle_nack /* DS_NACK */
+};
+
+
+
+/* initialization functions */
+static int ds_ldc_init(ds_port_t *port);
+
+/* event processing functions */
+static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
+static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
+static void ds_handle_up_event(ds_port_t *port);
+static void ds_handle_down_reset_events(ds_port_t *port);
+static void ds_handle_recv(void *arg);
+static void ds_dispatch_event(void *arg);
+
+/* message sending functions */
+static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
+static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
+static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
+static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
+
+/* walker functions */
+static int ds_svc_isfree(ds_svc_t *svc, void *arg);
+static int ds_svc_unregister(ds_svc_t *svc, void *arg);
+static int ds_svc_port_up(ds_svc_t *svc, void *arg);
+
+/* service utilities */
+static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
+static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
+
+/* port utilities */
+static void ds_port_reset(ds_port_t *port);
+static ldc_status_t ds_update_ldc_state(ds_port_t *port);
+
+/* misc utilities */
+static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
+ uint16_t *min_major, uint16_t *max_major);
+
+/* debug */
+static char *decode_ldc_events(uint64_t event, char *buf);
+
+/* loopback */
+static void ds_loopback_register(ds_svc_hdl_t hdl);
+static void ds_loopback_unregister(ds_svc_hdl_t hdl);
+static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
+static int ds_loopback_set_svc(ds_svc_t *svc, ds_svc_hdl_t lb_hdl);
+
+/* client handling */
+static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
+ uint_t maxhdls);
+static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
+ ds_port_t *port);
+static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
+ ds_port_t *port);
+static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
+static void ds_portset_del_active_clients(char *service, ds_portset_t *portsp);
+static void ds_check_for_dup_services(ds_svc_t *svc);
+static void ds_delete_svc_entry(ds_svc_t *svc);
+
+char *
+ds_strdup(char *str)
+{
+ char *newstr;
+
+ newstr = DS_MALLOC(strlen(str) + 1);
+ (void) strcpy(newstr, str);
+ return (newstr);
+}
+
+void
+ds_common_init(void)
+{
+ /* Validate version table */
+ ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
+
+ /* Initialize services table */
+ ds_init_svcs_tbl(DS_MAXSVCS_INIT);
+
+ /* enable callback processing */
+ ds_enabled = B_TRUE;
+}
+
+/* BEGIN LDC SUPPORT FUNCTIONS */
+
+static char *
+decode_ldc_events(uint64_t event, char *buf)
+{
+ buf[0] = 0;
+ if (event & LDC_EVT_DOWN) (void) strcat(buf, " DOWN");
+ if (event & LDC_EVT_RESET) (void) strcat(buf, " RESET");
+ if (event & LDC_EVT_UP) (void) strcat(buf, " UP");
+ if (event & LDC_EVT_READ) (void) strcat(buf, " READ");
+ if (event & LDC_EVT_WRITE) (void) strcat(buf, " WRITE");
+ return (buf);
+}
+
+static ldc_status_t
+ds_update_ldc_state(ds_port_t *port)
+{
+ ldc_status_t ldc_state;
+ int rv;
+ char ebuf[DS_EBUFSIZE];
+
+ ASSERT(MUTEX_HELD(&port->lock));
+
+ /*
+ * Read status and update ldc state info in port structure.
+ */
+ if ((rv = ldc_status(port->ldc.hdl, &ldc_state)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_status error: %s" DS_EOL,
+ PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ ldc_state = port->ldc.state;
+ } else {
+ port->ldc.state = ldc_state;
+ }
+
+ return (ldc_state);
+}
+
+static void
+ds_handle_down_reset_events(ds_port_t *port)
+{
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
+ __func__);
+
+ mutex_enter(&ds_svcs.lock);
+ mutex_enter(&port->lock);
+
+ ds_sys_drain_events(port);
+
+ (void) ds_update_ldc_state(port);
+
+ /* reset the port state */
+ ds_port_reset(port);
+
+ /* acknowledge the reset */
+ (void) ldc_up(port->ldc.hdl);
+
+ mutex_exit(&port->lock);
+ mutex_exit(&ds_svcs.lock);
+
+ ds_handle_up_event(port);
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
+}
+
+static void
+ds_handle_up_event(ds_port_t *port)
+{
+ ldc_status_t ldc_state;
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
+ __func__);
+
+ mutex_enter(&port->lock);
+
+ ldc_state = ds_update_ldc_state(port);
+
+ mutex_exit(&port->lock);
+
+ if ((ldc_state == LDC_UP) && IS_DS_PORT(port)) {
+ /*
+ * Initiate the handshake.
+ */
+ ds_send_init_req(port);
+ }
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
+}
+
+static uint_t
+ds_ldc_cb(uint64_t event, caddr_t arg)
+{
+ ds_port_t *port = (ds_port_t *)arg;
+ char evstring[DS_DECODE_BUF_LEN];
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: %s event (%llx) received" DS_EOL,
+ PORTID(port), __func__, decode_ldc_events(event, evstring),
+ (u_longlong_t)event);
+
+ if (!ds_enabled) {
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: callback handling is disabled"
+ DS_EOL, PORTID(port), __func__);
+ return (LDC_SUCCESS);
+ }
+
+ if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
+ ds_handle_down_reset_events(port);
+ goto done;
+ }
+
+ if (event & LDC_EVT_UP) {
+ ds_handle_up_event(port);
+ }
+
+ if (event & LDC_EVT_READ) {
+ if (port->ldc.state != LDC_UP) {
+ cmn_err(CE_WARN, "ds@%lx: %s: LDC READ event while "
+ "port not up" DS_EOL, PORTID(port), __func__);
+ goto done;
+ }
+
+ if (ds_sys_dispatch_func(ds_handle_recv, port)) {
+ cmn_err(CE_WARN, "ds@%lx: error initiating LDC READ "
+ " event", PORTID(port));
+ }
+ }
+
+ if (event & LDC_EVT_WRITE) {
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: LDC WRITE event received, "
+ "not supported" DS_EOL, PORTID(port), __func__);
+ }
+
+ if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
+ cmn_err(CE_WARN, "ds@%lx: %s: Unexpected LDC event received: "
+ "0x%llx" DS_EOL, PORTID(port), __func__,
+ (u_longlong_t)event);
+ }
+done:
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
+
+ return (LDC_SUCCESS);
+}
+
+static int
+ds_ldc_init(ds_port_t *port)
+{
+ int rv;
+ ldc_attr_t ldc_attr;
+ caddr_t ldc_cb_arg = (caddr_t)port;
+ char ebuf[DS_EBUFSIZE];
+
+ ASSERT(MUTEX_HELD(&port->lock));
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%lld" DS_EOL,
+ PORTID(port), __func__, (u_longlong_t)port->ldc.id);
+
+ ldc_attr.devclass = LDC_DEV_GENERIC;
+ ldc_attr.instance = 0;
+ ldc_attr.mode = LDC_MODE_RELIABLE;
+ ldc_attr.mtu = DS_STREAM_MTU;
+
+ if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_id: %lx, ldc_init error: %s"
+ DS_EOL, PORTID(port), __func__, port->ldc.id,
+ ds_errno_to_str(rv, ebuf));
+ return (rv);
+ }
+
+ rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, ldc_cb_arg);
+ if (rv != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_reg_callback error: %s"
+ DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ return (rv);
+ }
+
+ ds_sys_ldc_init(port);
+ return (0);
+}
+
+int
+ds_ldc_fini(ds_port_t *port)
+{
+ int rv;
+ char ebuf[DS_EBUFSIZE];
+
+ ASSERT(port->state >= DS_PORT_LDC_INIT);
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%ld" DS_EOL, PORTID(port),
+ __func__, port->ldc.id);
+
+ if ((rv = ldc_close(port->ldc.hdl)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_close error: %s" DS_EOL,
+ PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ return (rv);
+ }
+
+ if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_unreg_callback error: %s"
+ DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ return (rv);
+ }
+
+ if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_fini error: %s" DS_EOL,
+ PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ return (rv);
+ }
+
+ return (rv);
+}
+
+/*
+ * Attempt to read a specified number of bytes from a particular LDC.
+ * Returns zero for success or the return code from the LDC read on
+ * failure. The actual number of bytes read from the LDC is returned
+ * in the size parameter.
+ */
+static int
+ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
+{
+ int rv = 0;
+ size_t bytes_req = *sizep;
+ size_t bytes_left = bytes_req;
+ size_t nbytes;
+ int retry_count = 0;
+ char ebuf[DS_EBUFSIZE];
+
+ ASSERT(MUTEX_HELD(&port->rcv_lock));
+
+ *sizep = 0;
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: attempting to read %ld bytes" DS_EOL,
+ PORTID(port), bytes_req);
+
+ while (bytes_left > 0) {
+
+ nbytes = bytes_left;
+
+ if ((rv = ldc_read(port->ldc.hdl, msgp, &nbytes)) != 0) {
+ if (rv == ECONNRESET) {
+ break;
+ } else if (rv != EAGAIN) {
+ cmn_err(CE_NOTE, "ds@%lx: %s: %s" DS_EOL,
+ PORTID(port), __func__,
+ ds_errno_to_str(rv, ebuf));
+ break;
+ }
+ } else {
+ if (nbytes != 0) {
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: "
+ "read %ld bytes, %d retries" DS_EOL,
+ PORTID(port), nbytes, retry_count);
+
+ *sizep += nbytes;
+ msgp += nbytes;
+ bytes_left -= nbytes;
+
+ /* reset counter on a successful read */
+ retry_count = 0;
+ continue;
+ }
+
+ /*
+ * No data was read. Check if this is the
+ * first attempt. If so, just return since
+ * nothing has been read yet.
+ */
+ if (bytes_left == bytes_req) {
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: read zero bytes, "
+ " no data available" DS_EOL, PORTID(port));
+ break;
+ }
+ }
+
+ /*
+ * A retry is necessary because the read returned
+ * EAGAIN, or a zero length read occurred after
+ * reading a partial message.
+ */
+ if (retry_count++ >= ds_retries) {
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: timed out waiting for "
+ "message" DS_EOL, PORTID(port));
+ break;
+ }
+
+ drv_usecwait(ds_delay);
+ }
+
+ return (rv);
+}
+
+static void
+ds_handle_recv(void *arg)
+{
+ ds_port_t *port = (ds_port_t *)arg;
+ char *hbuf;
+ size_t msglen;
+ size_t read_size;
+ boolean_t hasdata;
+ ds_hdr_t hdr;
+ uint8_t *msg;
+ char *currp;
+ int rv;
+ ds_event_t *devent;
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s..." DS_EOL, PORTID(port), __func__);
+
+ /*
+ * Read messages from the channel until there are none
+ * pending. Valid messages are dispatched to be handled
+ * by a separate thread while any malformed messages are
+ * dropped.
+ */
+
+ mutex_enter(&port->rcv_lock);
+
+ while (((rv = ldc_chkq(port->ldc.hdl, &hasdata)) == 0) && hasdata) {
+
+ DS_DBG(CE_NOTE, "ds@%lx: %s: reading next message" DS_EOL,
+ PORTID(port), __func__);
+
+ /*
+ * Read in the next message.
+ */
+ hbuf = (char *)&hdr;
+ bzero(hbuf, DS_HDR_SZ);
+ read_size = DS_HDR_SZ;
+ currp = hbuf;
+
+ /* read in the message header */
+ if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
+ break;
+ }
+
+ if (read_size < DS_HDR_SZ) {
+ /*
+ * A zero length read is a valid signal that
+ * there is no data left on the channel.
+ */
+ if (read_size != 0) {
+ cmn_err(CE_WARN, "ds@%lx: invalid message "
+ "length, received %ld bytes, expected %ld"
+ DS_EOL, PORTID(port), read_size, DS_HDR_SZ);
+ }
+ continue;
+ }
+
+ /* get payload size and allocate a buffer */
+ read_size = ((ds_hdr_t *)hbuf)->payload_len;
+ msglen = DS_HDR_SZ + read_size;
+ msg = DS_MALLOC(msglen);
+ if (!msg) {
+ cmn_err(CE_WARN, "Memory allocation failed attempting "
+ " to allocate %d bytes." DS_EOL, (int)msglen);
+ continue;
+ }
+
+ DS_DBG(CE_NOTE, "ds@%lx: %s: message payload len %d" DS_EOL,
+ PORTID(port), __func__, (int)read_size);
+
+ /* move message header into buffer */
+ (void) memcpy(msg, hbuf, DS_HDR_SZ);
+ currp = (char *)(msg) + DS_HDR_SZ;
+
+ /* read in the message body */
+ if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
+ DS_FREE(msg, msglen);
+ break;
+ }
+
+ /* validate the size of the message */
+ if ((DS_HDR_SZ + read_size) != msglen) {
+ cmn_err(CE_WARN, "ds@%lx: %s: invalid message length, "
+ "received %ld bytes, expected %ld" DS_EOL,
+ PORTID(port), __func__, (DS_HDR_SZ + read_size),
+ msglen);
+ DS_FREE(msg, msglen);
+ continue;
+ }
+
+ DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
+
+ /*
+ * Send the message for processing, and store it
+ * in the log. The memory is deallocated only when
+ * the message is removed from the log.
+ */
+
+ devent = DS_MALLOC(sizeof (ds_event_t));
+ devent->port = port;
+ devent->buf = (char *)msg;
+ devent->buflen = msglen;
+
+ /* log the message */
+ (void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
+
+ if (ds_sys_dispatch_func(ds_dispatch_event, devent)) {
+ cmn_err(CE_WARN, "ds@%lx: error initiating "
+ "event handler", PORTID(port));
+ DS_FREE(devent, sizeof (ds_event_t));
+ }
+ }
+
+ mutex_exit(&port->rcv_lock);
+
+ /* handle connection reset errors returned from ds_recv_msg */
+ if (rv == ECONNRESET) {
+ ds_handle_down_reset_events(port);
+ }
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s done" DS_EOL, PORTID(port), __func__);
+}
+
+static void
+ds_dispatch_event(void *arg)
+{
+ ds_event_t *event = (ds_event_t *)arg;
+ ds_hdr_t *hdr;
+ ds_port_t *port;
+
+ port = event->port;
+
+ hdr = (ds_hdr_t *)event->buf;
+
+ if (DS_MSG_TYPE_VALID(hdr->msg_type)) {
+ DS_DBG(CE_NOTE, "ds@%lx: dispatch_event: msg_type=%d" DS_EOL,
+ PORTID(port), hdr->msg_type);
+
+ (*ds_msg_handlers[hdr->msg_type])(port, event->buf,
+ event->buflen);
+ } else {
+ cmn_err(CE_WARN, "ds@%lx: dispatch_event: invalid msg "
+ "type (%d)" DS_EOL, PORTID(port), hdr->msg_type);
+ }
+
+ DS_FREE(event->buf, event->buflen);
+ DS_FREE(event, sizeof (ds_event_t));
+}
+
+int
+ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
+{
+ int rv;
+ caddr_t currp = msg;
+ size_t amt_left = msglen;
+ int loopcnt = 0;
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s msglen: %ld" DS_EOL, PORTID(port),
+ __func__, msglen);
+ DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
+
+ /*
+ * Ensure that no other messages can be sent on this port by holding
+ * the tx_lock mutex in case the write doesn't get sent with one write.
+ * This guarantees that the message doesn't become fragmented.
+ */
+ mutex_enter(&port->tx_lock);
+
+ do {
+ if ((rv = ldc_write(port->ldc.hdl, currp, &msglen)) != 0) {
+ if (rv == ECONNRESET) {
+ mutex_exit(&port->tx_lock);
+ ds_handle_down_reset_events(port);
+ return (rv);
+ } else if ((rv == EWOULDBLOCK) &&
+ (loopcnt++ < ds_retries)) {
+ drv_usecwait(ds_delay);
+ } else {
+ cmn_err(CE_WARN, "ds@%lx: send_msg: ldc_write "
+ "failed (%d), %d bytes remaining" DS_EOL,
+ PORTID(port), rv, (int)amt_left);
+ goto error;
+ }
+ } else {
+ amt_left -= msglen;
+ currp += msglen;
+ msglen = amt_left;
+ loopcnt = 0;
+ }
+ } while (amt_left > 0);
+error:
+ mutex_exit(&port->tx_lock);
+
+ return (rv);
+}
+
+/* END LDC SUPPORT FUNCTIONS */
+
+
+/* BEGIN DS PROTOCOL SUPPORT FUNCTIONS */
+
+static void
+ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_hdr_t *hdr;
+ ds_init_ack_t *ack;
+ ds_init_nack_t *nack;
+ char *msg;
+ size_t msglen;
+ ds_init_req_t *req;
+ size_t explen = DS_MSG_LEN(ds_init_req_t);
+ uint16_t new_major;
+ uint16_t new_minor;
+ boolean_t match;
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <init_req: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ req = (ds_init_req_t *)(buf + DS_HDR_SZ);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_req: ver=%d.%d" DS_EOL,
+ PORTID(port), req->major_vers, req->minor_vers);
+
+ match = negotiate_version(DS_NUM_VER, &ds_vers[0],
+ req->major_vers, &new_major, &new_minor);
+
+ /*
+ * Check version info. ACK only if the major numbers exactly
+ * match. The service entity can retry with a new minor
+ * based on the response sent as part of the NACK.
+ */
+ if (match) {
+ msglen = DS_MSG_LEN(ds_init_ack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_INIT_ACK;
+ hdr->payload_len = sizeof (ds_init_ack_t);
+
+ ack = (ds_init_ack_t *)(msg + DS_HDR_SZ);
+ ack->minor_vers = MIN(new_minor, req->minor_vers);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_ack>: minor=0x%04X" DS_EOL,
+ PORTID(port), MIN(new_minor, req->minor_vers));
+ } else {
+ msglen = DS_MSG_LEN(ds_init_nack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_INIT_NACK;
+ hdr->payload_len = sizeof (ds_init_nack_t);
+
+ nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
+ nack->major_vers = new_major;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_nack>: major=0x%04X" DS_EOL,
+ PORTID(port), new_major);
+ }
+
+ /*
+ * Send the response
+ */
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+}
+
+static void
+ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_init_ack_t *ack;
+ ds_ver_t *ver;
+ size_t explen = DS_MSG_LEN(ds_init_ack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <init_ack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
+
+ mutex_enter(&port->lock);
+
+ if (port->state != DS_PORT_INIT_REQ) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
+ DS_EOL, PORTID(port), port->state);
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ ver = &(ds_vers[port->ver_idx]);
+
+ /* agreed upon a major version */
+ port->ver.major = ver->major;
+
+ /*
+ * If the returned minor version is larger than
+ * the requested minor version, use the lower of
+ * the two, i.e. the requested version.
+ */
+ if (ack->minor_vers >= ver->minor) {
+ /*
+ * Use the minor version specified in the
+ * original request.
+ */
+ port->ver.minor = ver->minor;
+ } else {
+ /*
+ * Use the lower minor version returned in
+ * the ack. By definition, all lower minor
+ * versions must be supported.
+ */
+ port->ver.minor = ack->minor_vers;
+ }
+
+ port->state = DS_PORT_READY;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
+ PORTID(port), port->ver.major, port->ver.minor);
+
+ mutex_exit(&port->lock);
+
+ /*
+ * The port came up, so update all the services
+ * with this information. Follow that up with an
+ * attempt to register any service that is not
+ * already registered.
+ */
+ mutex_enter(&ds_svcs.lock);
+
+ (void) ds_walk_svcs(ds_svc_port_up, port);
+ (void) ds_walk_svcs(ds_svc_register, NULL);
+
+ mutex_exit(&ds_svcs.lock);
+}
+
+static void
+ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ int idx;
+ ds_init_nack_t *nack;
+ ds_ver_t *ver;
+ size_t explen = DS_MSG_LEN(ds_init_nack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
+
+ mutex_enter(&port->lock);
+
+ if (port->state != DS_PORT_INIT_REQ) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: invalid state: %d"
+ DS_EOL, PORTID(port), port->state);
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ ver = &(ds_vers[port->ver_idx]);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x"
+ DS_EOL, PORTID(port), ver->major, ver->minor, nack->major_vers);
+
+ if (nack->major_vers == 0) {
+ /* no supported protocol version */
+ DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS not supported"
+ DS_EOL, PORTID(port));
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ /*
+ * Walk the version list, looking for a major version
+ * that is as close to the requested major version as
+ * possible.
+ */
+ for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
+ if (ds_vers[idx].major <= nack->major_vers) {
+ /* found a version to try */
+ goto done;
+ }
+ }
+
+ if (idx == DS_NUM_VER) {
+ /* no supported version */
+ DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS v%d.x not "
+ "supported" DS_EOL, PORTID(port), nack->major_vers);
+
+ mutex_exit(&port->lock);
+ return;
+ }
+
+done:
+ /* start the handshake again */
+ port->ver_idx = idx;
+ port->state = DS_PORT_LDC_INIT;
+ mutex_exit(&port->lock);
+
+ ds_send_init_req(port);
+
+}
+
+static ds_svc_t *
+ds_find_svc_by_id_port(char *svc_id, int is_client, ds_port_t *port)
+{
+ int idx;
+ ds_svc_t *svc, *found_svc = 0;
+ uint32_t flag_match = is_client ? DSSF_ISCLIENT : 0;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ /* walk every table entry */
+ for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+ svc = ds_svcs.tbl[idx];
+ if (DS_SVC_ISFREE(svc))
+ continue;
+ if (strcmp(svc->cap.svc_id, svc_id) != 0)
+ continue;
+ if ((svc->flags & DSSF_ISCLIENT) != flag_match)
+ continue;
+ if (port != NULL && svc->port == port) {
+ return (svc);
+ } else if (svc->state == DS_SVC_INACTIVE) {
+ found_svc = svc;
+ } else if (!found_svc) {
+ found_svc = svc;
+ }
+ }
+
+ return (found_svc);
+}
+
+static void
+ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_reg_req_t *req;
+ ds_hdr_t *hdr;
+ ds_reg_ack_t *ack;
+ ds_reg_nack_t *nack;
+ char *msg;
+ size_t msglen;
+ size_t explen = DS_MSG_LEN(ds_reg_req_t);
+ ds_svc_t *svc = NULL;
+ ds_ver_t version;
+ uint16_t new_major;
+ uint16_t new_minor;
+ boolean_t match;
+
+ /* sanity check the incoming message */
+ if (len < explen) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_req: invalid message "
+ "length (%ld), expected at least %ld" DS_EOL,
+ PORTID(port), len, explen);
+ return;
+ }
+
+ req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' ver=%d.%d, hdl=0x%llx"
+ DS_EOL, PORTID(port), req->svc_id, req->major_vers, req->minor_vers,
+ (u_longlong_t)req->svc_handle);
+
+ mutex_enter(&ds_svcs.lock);
+ svc = ds_find_svc_by_id_port(req->svc_id,
+ DS_HDL_ISCLIENT(req->svc_handle) == 0, port);
+ if (svc == NULL) {
+
+do_reg_nack:
+ mutex_exit(&ds_svcs.lock);
+
+ msglen = DS_MSG_LEN(ds_reg_nack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_REG_NACK;
+ hdr->payload_len = sizeof (ds_reg_nack_t);
+
+ nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
+ nack->svc_handle = req->svc_handle;
+ nack->result = DS_REG_VER_NACK;
+ nack->major_vers = 0;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s'" DS_EOL,
+ PORTID(port), req->svc_id);
+ /*
+ * Send the response
+ */
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+ return;
+ }
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' found, hdl: 0x%llx" DS_EOL,
+ PORTID(port), req->svc_id, (u_longlong_t)svc->hdl);
+
+ /*
+ * A client sends out a reg req in order to force service providers to
+ * initiate a reg req from their end (limitation in the protocol). If a
+ * service provider already thinks it's talking to someone on that port,
+ * something's gone wrong (probably an unreg req request was dropped).
+ * If we see that the service is in the wrong state, reset it.
+ */
+
+ if (DS_HDL_ISCLIENT(req->svc_handle)) {
+ if (svc->state != DS_SVC_INACTIVE) {
+ (void) ds_svc_unregister(svc, port);
+ }
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging client"
+ DS_EOL, PORTID(port), req->svc_id);
+ (void) ds_svc_port_up(svc, port);
+ (void) ds_svc_register_onport(svc, port);
+ mutex_exit(&ds_svcs.lock);
+ return;
+ }
+
+ /*
+ * Only remote service providers can initiate a registration. The
+ * local sevice from here must be a client service.
+ */
+
+ match = negotiate_version(svc->cap.nvers, svc->cap.vers,
+ req->major_vers, &new_major, &new_minor);
+
+ /*
+ * Check version info. ACK only if the major numbers exactly
+ * match. The service entity can retry with a new minor
+ * based on the response sent as part of the NACK.
+ */
+ if (match) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' svc%d: state: %x "
+ "svc_portid: %d" DS_EOL, PORTID(port), req->svc_id,
+ (int)DS_HDL2IDX(svc->hdl), svc->state,
+ (int)(svc->port == NULL ? -1 : PORTID(svc->port)));
+ /*
+ * If the current local service is already in use and
+ * it's not on this port, clone it.
+ */
+ if (svc->state != DS_SVC_INACTIVE) {
+ if (svc->port != NULL && port == svc->port) {
+ /*
+ * Someone probably dropped an unreg req
+ * somewhere. Force a local unreg.
+ */
+ (void) ds_svc_unregister(svc, port);
+ } else if (!DS_HDL_ISCLIENT(svc->hdl)) {
+ /*
+ * Can't clone a non-client (service provider)
+ * handle. This is because old in-kernel
+ * service providers can't deal with multiple
+ * handles.
+ */
+ goto do_reg_nack;
+ } else {
+ svc = ds_svc_clone(svc);
+ }
+ }
+ svc->port = port;
+ svc->svc_hdl = req->svc_handle;
+ svc->state = DS_SVC_ACTIVE;
+
+ msglen = DS_MSG_LEN(ds_reg_ack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_REG_ACK;
+ hdr->payload_len = sizeof (ds_reg_ack_t);
+
+ ack = (ds_reg_ack_t *)(msg + DS_HDR_SZ);
+ ack->svc_handle = req->svc_handle;
+ ack->minor_vers = MIN(new_minor, req->minor_vers);
+
+
+ if (svc->ops.ds_reg_cb) {
+ /* Call the registration callback */
+ version.major = req->major_vers;
+ version.minor = ack->minor_vers;
+ (*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &version,
+ svc->hdl);
+ }
+ mutex_exit(&ds_svcs.lock);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_ack>: '%s' minor=0x%04X"
+ DS_EOL, PORTID(port), svc->cap.svc_id,
+ MIN(new_minor, req->minor_vers));
+ } else {
+ mutex_exit(&ds_svcs.lock);
+
+ msglen = DS_MSG_LEN(ds_reg_nack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_REG_NACK;
+ hdr->payload_len = sizeof (ds_reg_nack_t);
+
+ nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
+ nack->svc_handle = req->svc_handle;
+ nack->result = DS_REG_VER_NACK;
+ nack->major_vers = new_major;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s' major=0x%04X"
+ DS_EOL, PORTID(port), svc->cap.svc_id, new_major);
+ }
+
+ /* send message */
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+}
+
+static void
+ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_reg_ack_t *ack;
+ ds_ver_t *ver;
+ ds_ver_t tmpver;
+ ds_svc_t *svc;
+ size_t explen = DS_MSG_LEN(ds_reg_ack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
+
+ mutex_enter(&ds_svcs.lock);
+
+ /*
+ * This searches for service based on how we generate handles
+ * and so only works because this is a reg ack.
+ */
+ if (DS_HDL_ISCLIENT(ack->svc_handle) ||
+ (svc = ds_get_svc(ack->svc_handle)) == NULL) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid handle 0x%llx"
+ DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
+ goto done;
+ }
+
+ /* make sure the message makes sense */
+ if (svc->state != DS_SVC_REG_PENDING) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid state (%d)" DS_EOL,
+ PORTID(port), svc->state);
+ goto done;
+ }
+
+ ver = &(svc->cap.vers[svc->ver_idx]);
+
+ /* major version has been agreed upon */
+ svc->ver.major = ver->major;
+
+ if (ack->minor_vers >= ver->minor) {
+ /*
+ * Use the minor version specified in the
+ * original request.
+ */
+ svc->ver.minor = ver->minor;
+ } else {
+ /*
+ * Use the lower minor version returned in
+ * the ack. By defninition, all lower minor
+ * versions must be supported.
+ */
+ svc->ver.minor = ack->minor_vers;
+ }
+
+ svc->state = DS_SVC_ACTIVE;
+ svc->port = port;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_ack: '%s' v%d.%d ready, hdl=0x%llx"
+ DS_EOL, PORTID(port), svc->cap.svc_id, svc->ver.major,
+ svc->ver.minor, (u_longlong_t)svc->hdl);
+
+ /* notify the client that registration is complete */
+ if (svc->ops.ds_reg_cb) {
+ /*
+ * Use a temporary version structure so that
+ * the copy in the svc structure cannot be
+ * modified by the client.
+ */
+ tmpver.major = svc->ver.major;
+ tmpver.minor = svc->ver.minor;
+
+ (*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
+ }
+
+done:
+ mutex_exit(&ds_svcs.lock);
+}
+
+static void
+ds_try_next_port(ds_svc_t *svc, int portid)
+{
+ ds_port_t *port;
+ ds_portset_t totry;
+ int i;
+
+ DS_DBG_LDC(CE_NOTE, "ds@%x %s" DS_EOL, portid, __func__);
+ DS_PORTSET_NOT(totry, svc->tried);
+ DS_PORTSET_AND(totry, svc->avail);
+ if (DS_PORTSET_ISNULL(totry))
+ return;
+
+ for (i = 0; i < DS_MAX_PORTS; i++, portid++) {
+ if (portid >= DS_MAX_PORTS) {
+ portid = 0;
+ }
+
+ /*
+ * If the port is not in the available list,
+ * it is not a candidate for registration.
+ */
+ if (!DS_PORT_IN_SET(totry, portid)) {
+ continue;
+ }
+
+ port = &ds_ports[portid];
+ DS_DBG_LDC(CE_NOTE, "ds@%x: %s trying ldc.id: %d" DS_EOL,
+ portid, __func__, (uint_t)(port->ldc.id));
+ if (ds_send_reg_req(svc, port) == 0) {
+ DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send OK" DS_EOL,
+ portid, __func__);
+ /* register sent successfully */
+ break;
+ }
+ DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send FAIL" DS_EOL,
+ portid, __func__);
+
+ /* reset the service to try the next port */
+ ds_reset_svc(svc, port);
+ }
+}
+
+static void
+ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_reg_nack_t *nack;
+ ds_svc_t *svc;
+ int idx;
+ size_t explen = DS_MSG_LEN(ds_reg_nack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
+
+ mutex_enter(&ds_svcs.lock);
+
+ /*
+ * We expect a reg_nack for a client ping.
+ */
+ if (DS_HDL_ISCLIENT(nack->svc_handle)) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: ping hdl: 0x%llx"
+ DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
+ goto done;
+ }
+
+ /*
+ * This searches for service based on how we generate handles
+ * and so only works because this is a reg nack.
+ */
+ if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid handle 0x%llx"
+ DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
+ goto done;
+ }
+
+ /* make sure the message makes sense */
+ if (svc->state != DS_SVC_REG_PENDING) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid state (%d)" DS_EOL,
+ PORTID(port), svc->state);
+ goto done;
+ }
+
+ if (nack->result == DS_REG_DUP) {
+ cmn_err(CE_WARN, "ds@%lx: <reg_nack: duplicate registration "
+ " for %s" DS_EOL, PORTID(port), svc->cap.svc_id);
+ ds_reset_svc(svc, port);
+ goto done;
+ }
+
+ /*
+ * A major version of zero indicates that the
+ * service is not supported at all.
+ */
+ if (nack->major_vers == 0) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' not supported"
+ DS_EOL, PORTID(port), svc->cap.svc_id);
+ ds_reset_svc(svc, port);
+ if ((svc->flags & DSSF_ISCLIENT) == 0)
+ ds_try_next_port(svc, PORTID(port) + 1);
+ goto done;
+ }
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' hdl=0x%llx, nack=%d.x"
+ DS_EOL, PORTID(port), svc->cap.svc_id,
+ (u_longlong_t)nack->svc_handle, nack->major_vers);
+
+ /*
+ * Walk the version list for the service, looking for
+ * a major version that is as close to the requested
+ * major version as possible.
+ */
+ for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
+ if (svc->cap.vers[idx].major <= nack->major_vers) {
+ /* found a version to try */
+ break;
+ }
+ }
+
+ if (idx == svc->cap.nvers) {
+ /* no supported version */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: %s v%d.x not supported"
+ DS_EOL, PORTID(port), svc->cap.svc_id, nack->major_vers);
+ ds_reset_svc(svc, port);
+ if ((svc->flags & DSSF_ISCLIENT) == 0)
+ ds_try_next_port(svc, PORTID(port) + 1);
+ goto done;
+ }
+
+ /* start the handshake again */
+ svc->state = DS_SVC_INACTIVE;
+ svc->ver_idx = idx;
+
+ (void) ds_svc_register(svc, NULL);
+
+done:
+ mutex_exit(&ds_svcs.lock);
+}
+
+static void
+ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_hdr_t *hdr;
+ ds_unreg_req_t *req;
+ ds_unreg_ack_t *ack;
+ ds_svc_t *svc;
+ char *msg;
+ size_t msglen;
+ size_t explen = DS_MSG_LEN(ds_unreg_req_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
+
+ mutex_enter(&ds_svcs.lock);
+
+ /* lookup appropriate client or service */
+ if (DS_HDL_ISCLIENT(req->svc_handle) ||
+ ((svc = ds_find_clnt_svc_by_hdl_port(req->svc_handle, port))
+ == NULL && ((svc = ds_get_svc(req->svc_handle)) == NULL ||
+ svc->port != port))) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid handle 0x%llx"
+ DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
+ ds_send_unreg_nack(port, req->svc_handle);
+ mutex_exit(&ds_svcs.lock);
+ return;
+ }
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: '%s' handle 0x%llx" DS_EOL,
+ PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
+
+ (void) ds_svc_unregister(svc, svc->port);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_ack>: '%s' hdl=0x%llx" DS_EOL,
+ PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
+
+ ds_check_for_dup_services(svc);
+
+ mutex_exit(&ds_svcs.lock);
+
+ msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
+ msg = DS_MALLOC(msglen);
+
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_UNREG_ACK;
+ hdr->payload_len = sizeof (ds_unreg_ack_t);
+
+ ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
+ ack->svc_handle = req->svc_handle;
+
+ /* send message */
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+
+}
+
+static void
+ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_unreg_ack_t *ack;
+ size_t explen = DS_MSG_LEN(ds_unreg_ack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_ack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: hdl=0x%llx" DS_EOL,
+ PORTID(port), (u_longlong_t)ack->svc_handle);
+
+ mutex_enter(&ds_svcs.lock);
+
+ /*
+ * Since the unregister request was initiated locally,
+ * the service structure has already been torn down.
+ * Just perform a sanity check to make sure the message
+ * is appropriate.
+ */
+ if (ds_get_svc(ack->svc_handle) != NULL) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_ack: handle 0x%llx in use"
+ DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
+ }
+
+ mutex_exit(&ds_svcs.lock);
+}
+
+static void
+ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_unreg_nack_t *nack;
+ size_t explen = DS_MSG_LEN(ds_unreg_nack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_nack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: hdl=0x%llx" DS_EOL,
+ PORTID(port), (u_longlong_t)nack->svc_handle);
+
+ mutex_enter(&ds_svcs.lock);
+
+ /*
+ * Since the unregister request was initiated locally,
+ * the service structure has already been torn down.
+ * Just perform a sanity check to make sure the message
+ * is appropriate.
+ */
+ if (ds_get_svc(nack->svc_handle) != NULL) {
+ cmn_err(CE_WARN, "ds@%lx: <unreg_nack: handle 0x%llx in use"
+ DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
+ }
+
+ mutex_exit(&ds_svcs.lock);
+}
+
+static void
+ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_data_handle_t *data;
+ ds_svc_t *svc;
+ char *msg;
+ int msgsz;
+ int hdrsz;
+ size_t explen = DS_MSG_LEN(ds_data_handle_t);
+
+ /* sanity check the incoming message */
+ if (len < explen) {
+ cmn_err(CE_WARN, "ds@%lx: <data: invalid message length "
+ "(%ld), expected at least %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
+
+ hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
+ msgsz = len - hdrsz;
+
+ /* strip off the header for the client */
+ msg = (msgsz) ? (buf + hdrsz) : NULL;
+
+ mutex_enter(&ds_svcs.lock);
+
+ if ((svc = ds_find_clnt_svc_by_hdl_port(data->svc_handle, port))
+ == NULL) {
+ if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ cmn_err(CE_WARN, "ds@%lx: <data: invalid handle 0x%llx"
+ DS_EOL, PORTID(port),
+ (u_longlong_t)data->svc_handle);
+ ds_send_data_nack(port, data->svc_handle);
+ return;
+ }
+ }
+
+ mutex_exit(&ds_svcs.lock);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: <data: '%s' hdl=0x%llx" DS_EOL,
+ PORTID(port), svc->cap.svc_id, (u_longlong_t)svc->hdl);
+ DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msgsz);
+
+ /* dispatch this message to the client */
+ (*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
+}
+
+static void
+ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
+{
+ ds_svc_t *svc;
+ ds_data_nack_t *nack;
+ size_t explen = DS_MSG_LEN(ds_data_nack_t);
+
+ /* sanity check the incoming message */
+ if (len != explen) {
+ cmn_err(CE_WARN, "ds@%lx: <data_nack: invalid message "
+ "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
+ explen);
+ return;
+ }
+
+ nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack: hdl=0x%llx, result=0x%llx"
+ DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle,
+ (u_longlong_t)nack->result);
+
+ if (nack->result == DS_INV_HDL) {
+
+ mutex_enter(&ds_svcs.lock);
+
+ if ((svc = ds_find_clnt_svc_by_hdl_port(nack->svc_handle,
+ port)) == NULL) {
+ if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ return;
+ }
+ }
+
+ cmn_err(CE_WARN, "ds@%lx: <data_nack: handle 0x%llx reported "
+ " as invalid" DS_EOL, PORTID(port),
+ (u_longlong_t)nack->svc_handle);
+
+ (void) ds_svc_unregister(svc, svc->port);
+
+ mutex_exit(&ds_svcs.lock);
+ }
+}
+
+/* Initialize the port */
+void
+ds_send_init_req(ds_port_t *port)
+{
+ ds_hdr_t *hdr;
+ ds_init_req_t *init_req;
+ size_t msglen;
+ ds_ver_t *vers = &ds_vers[port->ver_idx];
+
+ mutex_enter(&port->lock);
+ if (port->state != DS_PORT_LDC_INIT) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: invalid state: %d"
+ DS_EOL, PORTID(port), port->state);
+ mutex_exit(&port->lock);
+ return;
+ }
+ mutex_exit(&port->lock);
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: req=v%d.%d" DS_EOL,
+ PORTID(port), vers->major, vers->minor);
+
+ msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
+ hdr = DS_MALLOC(msglen);
+
+ hdr->msg_type = DS_INIT_REQ;
+ hdr->payload_len = sizeof (ds_init_req_t);
+
+ init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
+ init_req->major_vers = vers->major;
+ init_req->minor_vers = vers->minor;
+
+ if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
+ /*
+ * We've left the port state unlocked over the malloc/send,
+ * make sure no one has changed the state under us before
+ * we update the state.
+ */
+ mutex_enter(&port->lock);
+ if (port->state == DS_PORT_LDC_INIT)
+ port->state = DS_PORT_INIT_REQ;
+ mutex_exit(&port->lock);
+ }
+ DS_FREE(hdr, msglen);
+}
+
+static int
+ds_send_reg_req(ds_svc_t *svc, ds_port_t *port)
+{
+ ds_ver_t *ver;
+ ds_hdr_t *hdr;
+ caddr_t msg;
+ size_t msglen;
+ ds_reg_req_t *req;
+ size_t idlen;
+ int rv;
+
+ ASSERT(svc->state == DS_SVC_INACTIVE ||
+ (svc->flags & DSSF_ISCLIENT) != 0);
+
+ mutex_enter(&port->lock);
+
+ /* check on the LDC to Zeus */
+ if (port->ldc.state != LDC_UP) {
+ /* can not send message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: channel %ld is not up"
+ DS_EOL, PORTID(port), port->ldc.id);
+ mutex_exit(&port->lock);
+ return (-1);
+ }
+
+ /* make sure port is ready */
+ if (port->state != DS_PORT_READY) {
+ /* can not send message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: port is not ready"
+ DS_EOL, PORTID(port));
+ mutex_exit(&port->lock);
+ return (-1);
+ }
+
+ mutex_exit(&port->lock);
+
+ /* allocate the message buffer */
+ idlen = strlen(svc->cap.svc_id);
+ msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
+ msg = DS_MALLOC(msglen);
+
+ /* copy in the header data */
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_REG_REQ;
+ hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
+
+ req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
+ req->svc_handle = svc->hdl;
+ ver = &(svc->cap.vers[svc->ver_idx]);
+ req->major_vers = ver->major;
+ req->minor_vers = ver->minor;
+
+ /* copy in the service id */
+ (void) memcpy(req->svc_id, svc->cap.svc_id, idlen + 1);
+
+ /* send the message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: '%s' ver=%d.%d, hdl=0x%llx"
+ DS_EOL, PORTID(port), svc->cap.svc_id, ver->major, ver->minor,
+ (u_longlong_t)svc->hdl);
+
+ if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
+ svc->port = port;
+ rv = -1;
+ } else if ((svc->flags & DSSF_ISCLIENT) == 0) {
+ svc->state = DS_SVC_REG_PENDING;
+ }
+ DS_FREE(msg, msglen);
+
+ return (rv);
+}
+
+/*
+ * Keep around in case we want this later
+ */
+int
+ds_send_unreg_req(ds_svc_t *svc)
+{
+ caddr_t msg;
+ size_t msglen;
+ ds_hdr_t *hdr;
+ ds_unreg_req_t *req;
+ ds_port_t *port = svc->port;
+ int rv;
+
+ if (port == NULL) {
+ DS_DBG(CE_NOTE, "send_unreg_req: service '%s' not "
+ "associated with a port" DS_EOL, svc->cap.svc_id);
+ return (-1);
+ }
+
+ mutex_enter(&port->lock);
+
+ /* check on the LDC to Zeus */
+ if (port->ldc.state != LDC_UP) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: unreg_req>: channel %ld is not up"
+ DS_EOL, PORTID(port), port->ldc.id);
+ mutex_exit(&port->lock);
+ return (-1);
+ }
+
+ /* make sure port is ready */
+ if (port->state != DS_PORT_READY) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: unreg_req>: port is not ready" DS_EOL,
+ PORTID(port));
+ mutex_exit(&port->lock);
+ return (-1);
+ }
+
+ mutex_exit(&port->lock);
+
+ msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
+ msg = DS_MALLOC(msglen);
+
+ /* copy in the header data */
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_UNREG;
+ hdr->payload_len = sizeof (ds_unreg_req_t);
+
+ req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
+ if (svc->flags & DSSF_ISCLIENT) {
+ req->svc_handle = svc->svc_hdl;
+ } else {
+ req->svc_handle = svc->hdl;
+ }
+
+ /* send the message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_req>: '%s' hdl=0x%llx" DS_EOL,
+ PORTID(port), (svc->cap.svc_id) ? svc->cap.svc_id : "NULL",
+ (u_longlong_t)svc->hdl);
+
+ if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
+ rv = -1;
+ }
+ DS_FREE(msg, msglen);
+
+ return (rv);
+}
+
+static void
+ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
+{
+ caddr_t msg;
+ size_t msglen;
+ ds_hdr_t *hdr;
+ ds_unreg_nack_t *nack;
+
+ mutex_enter(&port->lock);
+
+ /* check on the LDC to Zeus */
+ if (port->ldc.state != LDC_UP) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: unreg_nack>: channel %ld is not up"
+ DS_EOL, PORTID(port), port->ldc.id);
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ /* make sure port is ready */
+ if (port->state != DS_PORT_READY) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: unreg_nack>: port is not ready"
+ DS_EOL, PORTID(port));
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ mutex_exit(&port->lock);
+
+ msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
+ msg = DS_MALLOC(msglen);
+
+ /* copy in the header data */
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_UNREG_NACK;
+ hdr->payload_len = sizeof (ds_unreg_nack_t);
+
+ nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
+ nack->svc_handle = bad_hdl;
+
+ /* send the message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_nack>: hdl=0x%llx" DS_EOL,
+ PORTID(port), (u_longlong_t)bad_hdl);
+
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+}
+
+static void
+ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
+{
+ caddr_t msg;
+ size_t msglen;
+ ds_hdr_t *hdr;
+ ds_data_nack_t *nack;
+
+ mutex_enter(&port->lock);
+
+ /* check on the LDC to Zeus */
+ if (port->ldc.state != LDC_UP) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: data_nack>: channel %ld is not up"
+ DS_EOL, PORTID(port), port->ldc.id);
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ /* make sure port is ready */
+ if (port->state != DS_PORT_READY) {
+ /* can not send message */
+ cmn_err(CE_WARN, "ds@%lx: data_nack>: port is not ready" DS_EOL,
+ PORTID(port));
+ mutex_exit(&port->lock);
+ return;
+ }
+
+ mutex_exit(&port->lock);
+
+ msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
+ msg = DS_MALLOC(msglen);
+
+ /* copy in the header data */
+ hdr = (ds_hdr_t *)msg;
+ hdr->msg_type = DS_NACK;
+ hdr->payload_len = sizeof (ds_data_nack_t);
+
+ nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
+ nack->svc_handle = bad_hdl;
+ nack->result = DS_INV_HDL;
+
+ /* send the message */
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack>: hdl=0x%llx" DS_EOL,
+ PORTID(port), (u_longlong_t)bad_hdl);
+
+ (void) ds_send_msg(port, msg, msglen);
+ DS_FREE(msg, msglen);
+}
+
+/* END DS PROTOCOL SUPPORT FUNCTIONS */
+
+#ifdef DEBUG
+
+#define BYTESPERLINE 8
+#define LINEWIDTH ((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
+#define ASCIIOFFSET ((BYTESPERLINE * 3) + 2)
+#define ISPRINT(c) ((c >= ' ') && (c <= '~'))
+
+/*
+ * Output a buffer formatted with a set number of bytes on
+ * each line. Append each line with the ASCII equivalent of
+ * each byte if it falls within the printable ASCII range,
+ * and '.' otherwise.
+ */
+void
+ds_dump_msg(void *vbuf, size_t len)
+{
+ int i, j;
+ char *curr;
+ char *aoff;
+ char line[LINEWIDTH];
+ uint8_t *buf = vbuf;
+
+ if (len > 128)
+ len = 128;
+
+ /* walk the buffer one line at a time */
+ for (i = 0; i < len; i += BYTESPERLINE) {
+
+ bzero(line, LINEWIDTH);
+
+ curr = line;
+ aoff = line + ASCIIOFFSET;
+
+ /*
+ * Walk the bytes in the current line, storing
+ * the hex value for the byte as well as the
+ * ASCII representation in a temporary buffer.
+ * All ASCII values are placed at the end of
+ * the line.
+ */
+ for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
+ (void) sprintf(curr, " %02x", buf[i + j]);
+ *aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
+ curr += 3;
+ aoff++;
+ }
+
+ /*
+ * Fill in to the start of the ASCII translation
+ * with spaces. This will only be necessary if
+ * this is the last line and there are not enough
+ * bytes to fill the whole line.
+ */
+ while (curr != (line + ASCIIOFFSET))
+ *curr++ = ' ';
+
+ cmn_err(CE_NOTE, "%s" DS_EOL, line);
+ }
+}
+#endif /* DEBUG */
+
+
+/*
+ * Walk the table of registered services, executing the specified callback
+ * function for each service on a port. A non-zero return value from the
+ * callback is used to terminate the walk, not to indicate an error. Returns
+ * the index of the last service visited.
+ */
+int
+ds_walk_svcs(svc_cb_t svc_cb, void *arg)
+{
+ int idx;
+ ds_svc_t *svc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ /* walk every table entry */
+ for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+ svc = ds_svcs.tbl[idx];
+
+ /* execute the callback */
+ if ((*svc_cb)(svc, arg) != 0)
+ break;
+ }
+
+ return (idx);
+}
+
+static int
+ds_svc_isfree(ds_svc_t *svc, void *arg)
+{
+ _NOTE(ARGUNUSED(arg))
+
+ /*
+ * Looking for a free service. This may be a NULL entry
+ * in the table, or an unused structure that could be
+ * reused.
+ */
+
+ if (DS_SVC_ISFREE(svc)) {
+ /* yes, it is free */
+ return (1);
+ }
+
+ /* not a candidate */
+ return (0);
+}
+
+int
+ds_svc_ismatch(ds_svc_t *svc, void *arg)
+{
+ if (DS_SVC_ISFREE(svc)) {
+ return (0);
+ }
+
+ if (strcmp(svc->cap.svc_id, arg) == 0 &&
+ (svc->flags & DSSF_ISCLIENT) == 0) {
+ /* found a match */
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+ds_svc_clnt_ismatch(ds_svc_t *svc, void *arg)
+{
+ if (DS_SVC_ISFREE(svc)) {
+ return (0);
+ }
+
+ if (strcmp(svc->cap.svc_id, arg) == 0 &&
+ (svc->flags & DSSF_ISCLIENT) != 0) {
+ /* found a match */
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+ds_svc_free(ds_svc_t *svc, void *arg)
+{
+ _NOTE(ARGUNUSED(arg))
+
+ if (svc == NULL) {
+ return (0);
+ }
+
+ if (svc->cap.svc_id) {
+ DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
+ svc->cap.svc_id = NULL;
+ }
+
+ if (svc->cap.vers) {
+ DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
+ svc->cap.vers = NULL;
+ }
+
+ DS_FREE(svc, sizeof (ds_svc_t));
+
+ return (0);
+}
+
+static int
+ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
+{
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ if (DS_SVC_ISFREE(svc))
+ return (0);
+
+ if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
+ return (0);
+
+ DS_PORTSET_ADD(svc->tried, PORTID(port));
+
+ if (ds_send_reg_req(svc, port) == 0) {
+ /* register sent successfully */
+ return (1);
+ }
+
+ if ((svc->flags & DSSF_ISCLIENT) == 0) {
+ /* reset the service */
+ ds_reset_svc(svc, port);
+ }
+ return (0);
+}
+
+int
+ds_svc_register(ds_svc_t *svc, void *arg)
+{
+ _NOTE(ARGUNUSED(arg))
+ ds_portset_t ports;
+ ds_port_t *port;
+ int idx;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ if (DS_SVC_ISFREE(svc))
+ return (0);
+
+ ports = svc->avail;
+ if (svc->flags & DSSF_ISCLIENT) {
+ ds_portset_del_active_clients(svc->cap.svc_id, &ports);
+ } else if (svc->state != DS_SVC_INACTIVE)
+ return (0);
+
+ if (DS_PORTSET_ISNULL(ports))
+ return (0);
+
+ /*
+ * Attempt to register the service. Start with the lowest
+ * numbered port and continue until a registration message
+ * is sent successfully, or there are no ports left to try.
+ */
+ for (idx = 0; idx < DS_MAX_PORTS; idx++) {
+
+ /*
+ * If the port is not in the available list,
+ * it is not a candidate for registration.
+ */
+ if (!DS_PORT_IN_SET(ports, idx)) {
+ continue;
+ }
+
+ port = &ds_ports[idx];
+ if (ds_svc_register_onport(svc, port)) {
+ if ((svc->flags & DSSF_ISCLIENT) == 0)
+ break;
+ DS_PORTSET_DEL(svc->avail, idx);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ds_svc_unregister(ds_svc_t *svc, void *arg)
+{
+ ds_port_t *port = (ds_port_t *)arg;
+ ds_svc_hdl_t hdl;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ if (DS_SVC_ISFREE(svc)) {
+ return (0);
+ }
+
+ /* make sure the service is using this port */
+ if (svc->port != port) {
+ return (0);
+ }
+
+ if (port) {
+ DS_DBG(CE_NOTE, "ds@%lx: svc_unreg: id='%s', ver=%d.%d, "
+ " hdl=0x%09lx" DS_EOL, PORTID(port), svc->cap.svc_id,
+ svc->ver.major, svc->ver.minor, svc->hdl);
+ } else {
+ DS_DBG(CE_NOTE, "port=NULL: svc_unreg: id='%s', ver=%d.%d, "
+ " hdl=0x%09lx" DS_EOL, svc->cap.svc_id, svc->ver.major,
+ svc->ver.minor, svc->hdl);
+ }
+
+ /* reset the service structure */
+ ds_reset_svc(svc, port);
+
+ /* call the client unregister callback */
+ if (svc->ops.ds_unreg_cb) {
+ (*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
+ }
+
+ /* increment the count in the handle to prevent reuse */
+ hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
+ if (DS_HDL_ISCLIENT(svc->hdl)) {
+ DS_HDL_SET_ISCLIENT(hdl);
+ }
+ svc->hdl = hdl;
+
+ if (svc->state != DS_SVC_UNREG_PENDING) {
+ /* try to initiate a new registration */
+ (void) ds_svc_register(svc, NULL);
+ }
+
+ return (0);
+}
+
+static int
+ds_svc_port_up(ds_svc_t *svc, void *arg)
+{
+ ds_port_t *port = (ds_port_t *)arg;
+
+ if (DS_SVC_ISFREE(svc)) {
+ /* nothing to do */
+ return (0);
+ }
+
+ DS_PORTSET_ADD(svc->avail, port->id);
+ DS_PORTSET_DEL(svc->tried, port->id);
+
+ return (0);
+}
+
+ds_svc_t *
+ds_alloc_svc(void)
+{
+ int idx;
+ uint_t newmaxsvcs;
+ ds_svc_t **newtbl;
+ ds_svc_t *newsvc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ idx = ds_walk_svcs(ds_svc_isfree, NULL);
+
+ if (idx != ds_svcs.maxsvcs) {
+ goto found;
+ }
+
+ /*
+ * There was no free space in the table. Grow
+ * the table to double its current size.
+ */
+ newmaxsvcs = ds_svcs.maxsvcs * 2;
+ newtbl = DS_MALLOC(newmaxsvcs * sizeof (ds_svc_t *));
+
+ /* copy old table data to the new table */
+ (void) memcpy(newtbl, ds_svcs.tbl,
+ ds_svcs.maxsvcs * sizeof (ds_svc_t *));
+
+ /* clean up the old table */
+ DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
+ ds_svcs.tbl = newtbl;
+ ds_svcs.maxsvcs = newmaxsvcs;
+
+ /* search for a free space again */
+ idx = ds_walk_svcs(ds_svc_isfree, NULL);
+
+ /* the table is locked so should find a free slot */
+ ASSERT(idx != ds_svcs.maxsvcs);
+
+found:
+ /* allocate a new svc structure if necessary */
+ if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
+ /* allocate a new service */
+ newsvc = DS_MALLOC(sizeof (ds_svc_t));
+ ds_svcs.tbl[idx] = newsvc;
+ }
+
+ /* fill in the handle */
+ newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
+ newsvc->state = DS_SVC_FREE; /* Mark as free temporarily */
+
+ return (newsvc);
+}
+
+static void
+ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
+{
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ if (svc->state != DS_SVC_UNREG_PENDING)
+ svc->state = DS_SVC_INACTIVE;
+ svc->ver_idx = 0;
+ svc->ver.major = 0;
+ svc->ver.minor = 0;
+ svc->port = NULL;
+ if (port) {
+ DS_PORTSET_DEL(svc->avail, port->id);
+ }
+}
+
+ds_svc_t *
+ds_get_svc(ds_svc_hdl_t hdl)
+{
+ int idx;
+ ds_svc_t *svc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ if (hdl == DS_INVALID_HDL)
+ return (NULL);
+
+ idx = DS_HDL2IDX(hdl);
+
+ /* check if index is out of bounds */
+ if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
+ return (NULL);
+
+ svc = ds_svcs.tbl[idx];
+
+ /* check for a valid service */
+ if (DS_SVC_ISFREE(svc))
+ return (NULL);
+
+ /* make sure the handle is an exact match */
+ if (svc->hdl != hdl)
+ return (NULL);
+
+ return (svc);
+}
+
+static void
+ds_port_reset(ds_port_t *port)
+{
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+ ASSERT(MUTEX_HELD(&port->lock));
+
+ /* connection went down, mark everything inactive */
+ (void) ds_walk_svcs(ds_svc_unregister, port);
+
+ port->ver_idx = 0;
+ port->ver.major = 0;
+ port->ver.minor = 0;
+ port->state = DS_PORT_LDC_INIT;
+}
+
+/*
+ * Verify that a version array is sorted as expected for the
+ * version negotiation to work correctly.
+ */
+ds_vers_check_t
+ds_vers_isvalid(ds_ver_t *vers, int nvers)
+{
+ uint16_t curr_major;
+ uint16_t curr_minor;
+ int idx;
+
+ curr_major = vers[0].major;
+ curr_minor = vers[0].minor;
+
+ /*
+ * Walk the version array, verifying correct ordering.
+ * The array must be sorted from highest supported
+ * version to lowest supported version.
+ */
+ for (idx = 0; idx < nvers; idx++) {
+ if (vers[idx].major > curr_major) {
+ DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
+ " increasing major versions" DS_EOL);
+ return (DS_VERS_INCREASING_MAJOR_ERR);
+ }
+
+ if (vers[idx].major < curr_major) {
+ curr_major = vers[idx].major;
+ curr_minor = vers[idx].minor;
+ continue;
+ }
+
+ if (vers[idx].minor > curr_minor) {
+ DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
+ " increasing minor versions" DS_EOL);
+ return (DS_VERS_INCREASING_MINOR_ERR);
+ }
+
+ curr_minor = vers[idx].minor;
+ }
+
+ return (DS_VERS_OK);
+}
+
+/*
+ * Extended user capability init.
+ */
+int
+ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint32_t flags,
+ int instance, ds_svc_hdl_t *hdlp)
+{
+ ds_vers_check_t status;
+ ds_svc_t *svc;
+ int rv = 0;
+ ds_svc_hdl_t lb_hdl, hdl;
+ int is_loopback;
+ int is_client;
+
+ /* sanity check the args */
+ if ((cap == NULL) || (ops == NULL)) {
+ cmn_err(CE_NOTE, "%s: invalid arguments" DS_EOL, __func__);
+ return (EINVAL);
+ }
+
+ /* sanity check the capability specifier */
+ if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
+ cmn_err(CE_NOTE, "%s: invalid capability specifier" DS_EOL,
+ __func__);
+ return (EINVAL);
+ }
+
+ /* sanity check the version array */
+ if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
+ cmn_err(CE_NOTE, "%s: invalid capability version array "
+ "for %s service: %s" DS_EOL, __func__, cap->svc_id,
+ (status == DS_VERS_INCREASING_MAJOR_ERR) ?
+ "increasing major versions" :
+ "increasing minor versions");
+ return (EINVAL);
+ }
+
+ /* data and register callbacks are required */
+ if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
+ cmn_err(CE_NOTE, "%s: invalid ops specifier for %s service"
+ DS_EOL, __func__, cap->svc_id);
+ return (EINVAL);
+ }
+
+ flags &= DSSF_USERFLAGS;
+ is_client = flags & DSSF_ISCLIENT;
+
+ DS_DBG_USR(CE_NOTE, "%s: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx"
+ DS_EOL, __func__, cap->svc_id, PTR_TO_LONG(ops->ds_data_cb),
+ PTR_TO_LONG(ops->cb_arg));
+
+ mutex_enter(&ds_svcs.lock);
+
+ /* check if the service is already registered */
+ if (i_ds_hdl_lookup(cap->svc_id, is_client, NULL, 1) == 1) {
+ /* already registered */
+ cmn_err(CE_NOTE, "Service '%s'/%s already registered" DS_EOL,
+ cap->svc_id,
+ (flags & DSSF_ISCLIENT) ? "client" : "service");
+ mutex_exit(&ds_svcs.lock);
+ return (EALREADY);
+ }
+
+ svc = ds_alloc_svc();
+ if (is_client) {
+ DS_HDL_SET_ISCLIENT(svc->hdl);
+ }
+
+ svc->state = DS_SVC_FREE;
+ svc->svc_hdl = DS_BADHDL1;
+
+ svc->flags = flags;
+ svc->drvi = instance;
+ svc->drv_psp = NULL;
+
+ /*
+ * Check for loopback.
+ */
+ if (i_ds_hdl_lookup(cap->svc_id, is_client == 0, &lb_hdl, 1) == 1) {
+ if ((rv = ds_loopback_set_svc(svc, lb_hdl)) != 0) {
+ cmn_err(CE_WARN, "%s: ds_loopback_set_svc err (%d)"
+ DS_EOL, __func__, rv);
+ mutex_exit(&ds_svcs.lock);
+ return (rv);
+ }
+ is_loopback = 1;
+ } else
+ is_loopback = 0;
+
+ /* copy over all the client information */
+ (void) memcpy(&svc->cap, cap, sizeof (ds_capability_t));
+
+ /* make a copy of the service name */
+ svc->cap.svc_id = ds_strdup(cap->svc_id);
+
+ /* make a copy of the version array */
+ svc->cap.vers = DS_MALLOC(cap->nvers * sizeof (ds_ver_t));
+ (void) memcpy(svc->cap.vers, cap->vers, cap->nvers * sizeof (ds_ver_t));
+
+ /* copy the client ops vector */
+ (void) memcpy(&svc->ops, ops, sizeof (ds_clnt_ops_t));
+
+ svc->state = DS_SVC_INACTIVE;
+ svc->ver_idx = 0;
+ DS_PORTSET_DUP(svc->avail, ds_allports);
+ DS_PORTSET_SETNULL(svc->tried);
+
+ ds_svcs.nsvcs++;
+
+ hdl = svc->hdl;
+
+ /*
+ * kludge to allow user callback code to get handle and user args.
+ * Make sure the callback arg points to the svc structure.
+ */
+ if ((flags & DSSF_ISUSER) != 0) {
+ ds_cbarg_set_cookie(svc);
+ }
+
+ if (is_loopback) {
+ ds_loopback_register(hdl);
+ ds_loopback_register(lb_hdl);
+ }
+
+ /*
+ * If this is a client or a non-loopback service provider, send
+ * out register requests.
+ */
+ if (!is_loopback || (flags & DSSF_ISCLIENT) != 0)
+ (void) ds_svc_register(svc, NULL);
+
+ if (hdlp) {
+ *hdlp = hdl;
+ }
+
+ mutex_exit(&ds_svcs.lock);
+
+ DS_DBG_USR(CE_NOTE, "%s: service '%s' assigned handle 0x%09lx" DS_EOL,
+ __func__, svc->cap.svc_id, hdl);
+
+ return (0);
+}
+
+/*
+ * ds_cap_init interface for previous revision.
+ */
+int
+ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
+{
+ return (ds_ucap_init(cap, ops, 0, DS_INVALID_INSTANCE, NULL));
+}
+
+/*
+ * Interface for ds_unreg_hdl in lds driver.
+ */
+int
+ds_unreg_hdl(ds_svc_hdl_t hdl)
+{
+ ds_svc_t *svc;
+ int is_loopback;
+ ds_svc_hdl_t lb_hdl;
+
+ DS_DBG_USR(CE_NOTE, "%s: hdl=0x%09lx" DS_EOL, __func__, hdl);
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ DS_DBG_USR(CE_NOTE, "%s: unknown hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ return (ENXIO);
+ }
+
+ DS_DBG_USR(CE_NOTE, "%s: svcid='%s', hdl=0x%llx" DS_EOL, __func__,
+ svc->cap.svc_id, (u_longlong_t)svc->hdl);
+
+ svc->state = DS_SVC_UNREG_PENDING;
+
+ is_loopback = ((svc->flags & DSSF_LOOPBACK) != 0);
+ lb_hdl = svc->svc_hdl;
+
+ if (svc->port) {
+ (void) ds_send_unreg_req(svc);
+ }
+
+ (void) ds_svc_unregister(svc, svc->port);
+
+ ds_delete_svc_entry(svc);
+
+ if (is_loopback) {
+ ds_loopback_unregister(lb_hdl);
+ }
+
+ mutex_exit(&ds_svcs.lock);
+
+ return (0);
+}
+
+int
+ds_cap_fini(ds_capability_t *cap)
+{
+ ds_svc_hdl_t hdl;
+ int rv;
+ uint_t nhdls = 0;
+
+ DS_DBG(CE_NOTE, "%s: '%s'" DS_EOL, __func__, cap->svc_id);
+ if ((rv = ds_hdl_lookup(cap->svc_id, 0, &hdl, 1, &nhdls)) != 0) {
+ DS_DBG(CE_NOTE, "%s: ds_hdl_lookup '%s' err (%d)" DS_EOL,
+ __func__, cap->svc_id, rv);
+ return (rv);
+ }
+
+ if (nhdls == 0) {
+ DS_DBG(CE_NOTE, "%s: no such service '%s'" DS_EOL,
+ __func__, cap->svc_id);
+ return (ENXIO);
+ }
+
+ if ((rv = ds_is_my_hdl(hdl, DS_INVALID_INSTANCE)) != 0) {
+ DS_DBG(CE_NOTE, "%s: ds_is_my_handle err (%d)" DS_EOL, __func__,
+ rv);
+ return (rv);
+ }
+
+ if ((rv = ds_unreg_hdl(hdl)) != 0) {
+ DS_DBG(CE_NOTE, "%s: ds_unreg_hdl err (%d)" DS_EOL, __func__,
+ rv);
+ return (rv);
+ }
+
+ return (0);
+}
+
+int
+ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
+{
+ int rv;
+ ds_hdr_t *hdr;
+ caddr_t msg;
+ size_t msglen;
+ size_t hdrlen;
+ caddr_t payload;
+ ds_svc_t *svc;
+ ds_port_t *port;
+ ds_data_handle_t *data;
+ ds_svc_hdl_t svc_hdl;
+ int is_client = 0;
+
+ DS_DBG(CE_NOTE, "%s: hdl: 0x%llx, buf: %lx, len: %ld" DS_EOL, __func__,
+ (u_longlong_t)hdl, (ulong_t)buf, len);
+
+ mutex_enter(&ds_svcs.lock);
+
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ cmn_err(CE_WARN, "%s: invalid handle 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ mutex_exit(&ds_svcs.lock);
+ return (ENXIO);
+ }
+
+ if (svc->state != DS_SVC_ACTIVE) {
+ /* channel is up, but svc is not registered */
+ DS_DBG(CE_NOTE, "%s: invalid service state 0x%x" DS_EOL,
+ __func__, svc->state);
+ mutex_exit(&ds_svcs.lock);
+ return (ENOTCONN);
+ }
+
+ if (svc->flags & DSSF_LOOPBACK) {
+ hdl = svc->svc_hdl;
+ mutex_exit(&ds_svcs.lock);
+ ds_loopback_send(hdl, buf, len);
+ return (0);
+ }
+
+ if ((port = svc->port) == NULL) {
+ DS_DBG(CE_NOTE, "%s: service '%s' not associated with a port"
+ DS_EOL, __func__, svc->cap.svc_id);
+ mutex_exit(&ds_svcs.lock);
+ return (ECONNRESET);
+ }
+
+ if (svc->flags & DSSF_ISCLIENT) {
+ is_client = 1;
+ svc_hdl = svc->svc_hdl;
+ }
+
+ mutex_exit(&ds_svcs.lock);
+
+ /* check that the LDC channel is ready */
+ if (port->ldc.state != LDC_UP) {
+ DS_DBG(CE_NOTE, "%s: LDC channel is not up" DS_EOL, __func__);
+ return (ECONNRESET);
+ }
+
+ hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
+
+ msg = DS_MALLOC(len + hdrlen);
+ hdr = (ds_hdr_t *)msg;
+ payload = msg + hdrlen;
+ msglen = len + hdrlen;
+
+ hdr->payload_len = len + sizeof (ds_data_handle_t);
+ hdr->msg_type = DS_DATA;
+
+ data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
+ if (is_client) {
+ data->svc_handle = svc_hdl;
+ } else {
+ data->svc_handle = hdl;
+ }
+
+ if ((buf != NULL) && (len != 0)) {
+ (void) memcpy(payload, buf, len);
+ }
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: data>: hdl=0x%llx, len=%ld, "
+ " payload_len=%d" DS_EOL, PORTID(port), (u_longlong_t)svc->hdl,
+ msglen, hdr->payload_len);
+ DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msglen);
+
+ if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
+ rv = (rv == EIO) ? ECONNRESET : rv;
+ }
+ DS_FREE(msg, msglen);
+
+ return (rv);
+}
+
+void
+ds_port_common_init(ds_port_t *port)
+{
+ int rv;
+
+ if ((port->flags & DS_PORT_MUTEX_INITED) == 0) {
+ mutex_init(&port->lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&port->rcv_lock, NULL, MUTEX_DRIVER, NULL);
+ port->flags |= DS_PORT_MUTEX_INITED;
+ }
+
+ port->state = DS_PORT_INIT;
+ DS_PORTSET_ADD(ds_allports, port->id);
+
+ ds_sys_port_init(port);
+
+ mutex_enter(&port->lock);
+ rv = ds_ldc_init(port);
+ mutex_exit(&port->lock);
+
+ /*
+ * If LDC successfully init'ed, try to kick off protocol for this port.
+ */
+ if (rv == 0) {
+ ds_handle_up_event(port);
+ }
+}
+
+void
+ds_port_common_fini(ds_port_t *port, int is_fini)
+{
+ port->state = DS_PORT_FREE;
+
+ if (is_fini && (port->flags & DS_PORT_MUTEX_INITED) != 0) {
+ mutex_destroy(&port->lock);
+ mutex_destroy(&port->tx_lock);
+ mutex_destroy(&port->rcv_lock);
+ port->flags &= ~DS_PORT_MUTEX_INITED;
+ }
+
+ DS_PORTSET_DEL(ds_allports, port->id);
+
+ ds_sys_port_fini(port);
+}
+
+/*
+ * Initialize table of registered service classes
+ */
+void
+ds_init_svcs_tbl(uint_t nentries)
+{
+ int tblsz;
+
+ ds_svcs.maxsvcs = nentries;
+
+ tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
+ ds_svcs.tbl = (ds_svc_t **)DS_MALLOC(tblsz);
+
+ ds_svcs.nsvcs = 0;
+}
+
+/*
+ * Find the max and min version supported.
+ * Hacked from zeus workspace, support.c
+ */
+static void
+min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
+ uint16_t *min_major, uint16_t *max_major)
+{
+ int i;
+
+ *min_major = sup_versionsp[0].major;
+ *max_major = *min_major;
+
+ for (i = 1; i < num_versions; i++) {
+ if (sup_versionsp[i].major < *min_major)
+ *min_major = sup_versionsp[i].major;
+
+ if (sup_versionsp[i].major > *max_major)
+ *max_major = sup_versionsp[i].major;
+ }
+}
+
+/*
+ * Check whether the major and minor numbers requested by the peer can be
+ * satisfied. If the requested major is supported, true is returned, and the
+ * agreed minor is returned in new_minor. If the requested major is not
+ * supported, the routine returns false, and the closest major is returned in
+ * *new_major, upon which the peer should re-negotiate. The closest major is
+ * the just lower that the requested major number.
+ *
+ * Hacked from zeus workspace, support.c
+ */
+boolean_t
+negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
+ uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp)
+{
+ int i;
+ uint16_t major, lower_major;
+ uint16_t min_major = 0, max_major;
+ boolean_t found_match = B_FALSE;
+
+ min_max_versions(num_versions, sup_versionsp, &min_major, &max_major);
+
+ DS_DBG(CE_NOTE, "negotiate_version: req_major = %u, min = %u, max = %u"
+ DS_EOL, req_major, min_major, max_major);
+
+ /*
+ * If the minimum version supported is greater than
+ * the version requested, return the lowest version
+ * supported
+ */
+ if (min_major > req_major) {
+ *new_majorp = min_major;
+ return (B_FALSE);
+ }
+
+ /*
+ * If the largest version supported is lower than
+ * the version requested, return the largest version
+ * supported
+ */
+ if (max_major < req_major) {
+ *new_majorp = max_major;
+ return (B_FALSE);
+ }
+
+ /*
+ * Now we know that the requested version lies between the
+ * min and max versions supported. Check if the requested
+ * major can be found in supported versions.
+ */
+ lower_major = min_major;
+ for (i = 0; i < num_versions; i++) {
+ major = sup_versionsp[i].major;
+ if (major == req_major) {
+ found_match = B_TRUE;
+ *new_majorp = req_major;
+ *new_minorp = sup_versionsp[i].minor;
+ break;
+ } else {
+ if ((major < req_major) && (major > lower_major))
+ lower_major = major;
+ }
+ }
+
+ /*
+ * If no match is found, return the closest available number
+ */
+ if (!found_match)
+ *new_majorp = lower_major;
+
+ return (found_match);
+}
+
+/*
+ * Specific errno's that are used by ds.c and ldc.c
+ */
+static struct {
+ int ds_errno;
+ char *estr;
+} ds_errno_to_str_tab[] = {
+ { EIO, "I/O error" },
+ { ENXIO, "No such device or address" },
+ { EAGAIN, "Resource temporarily unavailable" },
+ { ENOMEM, "Not enough space" },
+ { EACCES, "Permission denied" },
+ { EFAULT, "Bad address" },
+ { EBUSY, "Device busy" },
+ { EINVAL, "Invalid argument" },
+ { ENOSPC, "No space left on device" },
+ { ENOMSG, "No message of desired type" },
+#ifdef ECHRNG
+ { ECHRNG, "Channel number out of range" },
+#endif
+ { ENOTSUP, "Operation not supported" },
+ { EMSGSIZE, "Message too long" },
+ { EADDRINUSE, "Address already in use" },
+ { ECONNRESET, "Connection reset by peer" },
+ { ENOBUFS, "No buffer space available" },
+ { ENOTCONN, "Socket is not connected" },
+ { ECONNREFUSED, "Connection refused" },
+ { EALREADY, "Operation already in progress" },
+ { 0, NULL },
+};
+
+char *
+ds_errno_to_str(int ds_errno, char *ebuf)
+{
+ int i, en;
+
+ for (i = 0; (en = ds_errno_to_str_tab[i].ds_errno) != 0; i++) {
+ if (en == ds_errno) {
+ (void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
+ return (ebuf);
+ }
+ }
+
+ (void) sprintf(ebuf, "ds_errno (%d)", ds_errno);
+ return (ebuf);
+}
+
+static void
+ds_loopback_register(ds_svc_hdl_t hdl)
+{
+ ds_ver_t ds_ver = { 1, 0};
+ ds_svc_t *svc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+ DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ return;
+ }
+ svc->state = DS_SVC_ACTIVE;
+
+ if (svc->ops.ds_reg_cb) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback regcb: hdl: 0x%llx" DS_EOL,
+ __func__, (u_longlong_t)hdl);
+ ds_ver.major = svc->ver.major;
+ ds_ver.minor = svc->ver.minor;
+ (*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &ds_ver, hdl);
+ }
+}
+
+static void
+ds_loopback_unregister(ds_svc_hdl_t hdl)
+{
+ ds_svc_t *svc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ return;
+ }
+
+ DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+
+ svc->flags &= ~DSSF_LOOPBACK;
+ svc->svc_hdl = DS_BADHDL2;
+
+ if (svc->ops.ds_unreg_cb) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback unregcb: hdl: 0x%llx" DS_EOL,
+ __func__, (u_longlong_t)hdl);
+ (*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
+ }
+}
+
+static void
+ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen)
+{
+ ds_svc_t *svc;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+ return;
+ }
+ mutex_exit(&ds_svcs.lock);
+
+ DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
+ (u_longlong_t)hdl);
+
+ if (svc->ops.ds_data_cb) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback datacb hdl: 0x%llx" DS_EOL,
+ __func__, (u_longlong_t)hdl);
+ (*svc->ops.ds_data_cb)(svc->ops.cb_arg, buf, buflen);
+ }
+}
+
+static int
+ds_loopback_set_svc(ds_svc_t *svc, ds_svc_hdl_t lb_hdl)
+{
+ ds_svc_t *lb_svc;
+
+ if ((lb_svc = ds_get_svc(lb_hdl)) == NULL) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback: hdl: 0x%llx invalid" DS_EOL,
+ __func__, (u_longlong_t)lb_hdl);
+ return (ENXIO);
+ }
+ if (lb_svc->state != DS_SVC_INACTIVE) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback inactive: hdl: 0x%llx"
+ DS_EOL, __func__, (u_longlong_t)lb_hdl);
+ if ((lb_svc->flags & DSSF_ISCLIENT) == 0) {
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback busy hdl: 0x%llx"
+ DS_EOL, __func__, (u_longlong_t)lb_hdl);
+ return (EBUSY);
+ }
+ lb_svc = ds_svc_clone(lb_svc);
+ DS_DBG_LOOP(CE_NOTE, "%s: loopback clone: ohdl: 0x%llx "
+ "nhdl: 0x%llx" DS_EOL, __func__, (u_longlong_t)lb_hdl,
+ (u_longlong_t)lb_svc->hdl);
+ }
+
+ svc->flags |= DSSF_LOOPBACK;
+ svc->svc_hdl = lb_svc->hdl;
+ svc->port = NULL;
+
+ lb_svc->flags |= DSSF_LOOPBACK;
+ lb_svc->svc_hdl = svc->hdl;
+ lb_svc->port = NULL;
+
+ DS_DBG_LOOP(CE_NOTE, "%s: setting loopback between: 0x%llx and 0x%llx"
+ DS_EOL, __func__, (u_longlong_t)svc->hdl,
+ (u_longlong_t)lb_svc->hdl);
+ return (0);
+}
+
+static ds_svc_t *
+ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl, ds_port_t *port)
+{
+ int idx;
+ ds_svc_t *svc;
+
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s looking up clnt hdl: 0x%llx" DS_EOL,
+ PORTID(port), __func__, (u_longlong_t)hdl);
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ /* walk every table entry */
+ for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+ svc = ds_svcs.tbl[idx];
+ if (DS_SVC_ISFREE(svc))
+ continue;
+ if ((svc->flags & DSSF_ISCLIENT) != 0 &&
+ svc->svc_hdl == hdl && svc->port == port) {
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s found clnt hdl "
+ "0x%llx: svc%d" DS_EOL, PORTID(port), __func__,
+ (u_longlong_t)hdl, (uint_t)DS_HDL2IDX(svc->hdl));
+ return (svc);
+ }
+ }
+ DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s clnt hdl: 0x%llx not found" DS_EOL,
+ PORTID(port), __func__, (u_longlong_t)hdl);
+
+ return (NULL);
+}
+
+static ds_svc_t *
+ds_svc_clone(ds_svc_t *svc)
+{
+ ds_svc_t *newsvc;
+ ds_svc_hdl_t hdl;
+
+ ASSERT(svc->flags & DSSF_ISCLIENT);
+
+ newsvc = ds_alloc_svc();
+
+ /* Can only clone clients for now */
+ hdl = newsvc->hdl | DS_HDL_ISCLIENT_BIT;
+ DS_DBG_USR(CE_NOTE, "%s: cloning client: old hdl: 0x%llx new hdl: "
+ "0x%llx" DS_EOL, __func__, (u_longlong_t)svc->hdl,
+ (u_longlong_t)hdl);
+ (void) memcpy(newsvc, svc, sizeof (ds_svc_t));
+ newsvc->hdl = hdl;
+ newsvc->flags &= ~DSSF_LOOPBACK;
+ newsvc->port = NULL;
+ newsvc->svc_hdl = DS_BADHDL2;
+ newsvc->cap.svc_id = ds_strdup(svc->cap.svc_id);
+ newsvc->cap.vers = DS_MALLOC(svc->cap.nvers * sizeof (ds_ver_t));
+ (void) memcpy(newsvc->cap.vers, svc->cap.vers,
+ svc->cap.nvers * sizeof (ds_ver_t));
+
+ /*
+ * Kludge to allow lds driver user callbacks to get access to current
+ * svc structure. Arg could be index to svc table or some other piece
+ * of info to get to the svc table entry.
+ */
+ if (newsvc->flags & DSSF_ISUSER) {
+ newsvc->ops.cb_arg = (ds_cb_arg_t)(newsvc);
+ }
+ return (newsvc);
+}
+
+/*
+ * Internal handle lookup function.
+ */
+static int
+i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
+ uint_t maxhdls)
+{
+ int idx;
+ int nhdls = 0;
+ ds_svc_t *svc;
+ uint32_t client_flag = is_client ? DSSF_ISCLIENT : 0;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ for (idx = 0; idx < ds_svcs.maxsvcs && nhdls < maxhdls; idx++) {
+ svc = ds_svcs.tbl[idx];
+ if (DS_SVC_ISFREE(svc))
+ continue;
+ if (strcmp(svc->cap.svc_id, service) == 0 &&
+ (svc->flags & DSSF_ISCLIENT) == client_flag) {
+ if (hdlp != NULL && nhdls < maxhdls) {
+ hdlp[nhdls] = svc->hdl;
+ nhdls++;
+ } else {
+ nhdls++;
+ }
+ }
+ }
+ return (nhdls);
+}
+
+/*
+ * Interface for ds_hdl_lookup in lds driver.
+ */
+int
+ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
+ uint_t maxhdls, uint_t *nhdlsp)
+{
+ mutex_enter(&ds_svcs.lock);
+ *nhdlsp = i_ds_hdl_lookup(service, is_client, hdlp, maxhdls);
+ mutex_exit(&ds_svcs.lock);
+ return (0);
+}
+
+static void
+ds_portset_del_active_clients(char *service, ds_portset_t *portsp)
+{
+ ds_portset_t ports = *portsp;
+ int idx;
+ ds_svc_t *svc;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+ svc = ds_svcs.tbl[idx];
+ if (DS_SVC_ISFREE(svc))
+ continue;
+ if (strcmp(svc->cap.svc_id, service) == 0 &&
+ (svc->flags & DSSF_ISCLIENT) != 0 &&
+ svc->state != DS_SVC_INACTIVE &&
+ svc->port != NULL) {
+ DS_PORTSET_DEL(ports, PORTID(svc->port));
+ }
+ }
+ *portsp = ports;
+}
+
+/*
+ * After an UNREG REQ, check if this is a client service with multiple
+ * handles. If it is, then we can eliminate this entry.
+ */
+static void
+ds_check_for_dup_services(ds_svc_t *svc)
+{
+ if ((svc->flags & DSSF_ISCLIENT) != 0 &&
+ svc->state == DS_SVC_INACTIVE &&
+ i_ds_hdl_lookup(svc->cap.svc_id, 1, NULL, 2) == 2) {
+ ds_delete_svc_entry(svc);
+ }
+}
+
+static void
+ds_delete_svc_entry(ds_svc_t *svc)
+{
+ ds_svc_hdl_t tmp_hdl;
+
+ ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+ /*
+ * Clear out the structure, but do not deallocate the
+ * memory. It can be reused for the next registration.
+ */
+ DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
+ DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
+
+ /* save the handle to prevent reuse */
+ tmp_hdl = svc->hdl;
+ bzero((void *)svc, sizeof (ds_svc_t));
+
+ /* initialize for next use */
+ svc->hdl = tmp_hdl;
+ svc->state = DS_SVC_FREE;
+
+ ds_svcs.nsvcs--;
+}
diff --git a/usr/src/uts/sun4v/io/ds_drv.c b/usr/src/uts/sun4v/io/ds_drv.c
new file mode 100644
index 0000000000..d4ffeb6fc6
--- /dev/null
+++ b/usr/src/uts/sun4v/io/ds_drv.c
@@ -0,0 +1,1098 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Domain Services Module System Specific Code.
+ *
+ * The Domain Services (DS) module is responsible for communication
+ * with external service entities. It provides a kernel API for clients to
+ * publish capabilities and handles the low level communication and
+ * version negotiation required to export those capabilities to any
+ * interested service entity. Once a capability has been successfully
+ * registered with a service entity, the DS module facilitates all
+ * data transfers between the service entity and the client providing
+ * that particular capability.
+ *
+ * This file provides the system interfaces that are required for
+ * the ds.c module, which is common to both Solaris and VBSC (linux).
+ */
+
+#include <sys/modctl.h>
+#include <sys/ksynch.h>
+#include <sys/taskq.h>
+#include <sys/disp.h>
+#include <sys/cmn_err.h>
+#include <sys/note.h>
+#include <sys/mach_descrip.h>
+#include <sys/mdesc.h>
+#include <sys/mdeg.h>
+#include <sys/ldc.h>
+#include <sys/ds.h>
+#include <sys/ds_impl.h>
+
+/*
+ * All DS ports in the system
+ *
+ * The list of DS ports is read in from the MD when the DS module is
+ * initialized and is never modified. This eliminates the need for
+ * locking to access the port array itself. Access to the individual
+ * ports are synchronized at the port level.
+ */
+ds_port_t ds_ports[DS_MAX_PORTS];
+ds_portset_t ds_allports; /* all DS ports in the system */
+
+/*
+ * Table of registered services
+ *
+ * Locking: Accesses to the table of services are synchronized using
+ * a mutex lock. The reader lock must be held when looking up service
+ * information in the table. The writer lock must be held when any
+ * service information is being modified.
+ */
+ds_svcs_t ds_svcs;
+
+/*
+ * Taskq for internal task processing
+ */
+static taskq_t *ds_taskq;
+
+/*
+ * The actual required number of parallel threads is not expected
+ * to be very large. Use the maximum number of CPUs in the system
+ * as a rough upper bound.
+ */
+#define DS_MAX_TASKQ_THR NCPU
+#define DS_DISPATCH(fn, arg) taskq_dispatch(ds_taskq, fn, arg, TQ_SLEEP)
+
+ds_domain_hdl_t ds_my_domain_hdl = NULL;
+
+#ifdef DEBUG
+/*
+ * Debug Flag
+ */
+uint_t ds_debug = 0;
+#endif /* DEBUG */
+
+/* initialization functions */
+static void ds_init(void);
+static void ds_fini(void);
+static int ds_ports_init(void);
+static int ds_ports_fini(void);
+
+/* port utilities */
+static int ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan);
+
+/* log functions */
+static void ds_log_init(void);
+static void ds_log_fini(void);
+static int ds_log_remove(void);
+static void ds_log_purge(void *arg);
+
+static struct modlmisc modlmisc = {
+ &mod_miscops,
+ "Domain Services 1.9"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modlmisc,
+ NULL
+};
+
+int
+_init(void)
+{
+ int rv;
+
+ /*
+ * Perform all internal setup before initializing
+ * the DS ports. This ensures that events can be
+ * processed as soon as the port comes up.
+ */
+ ds_init();
+
+ /* force attach channel nexus */
+ (void) i_ddi_attach_hw_nodes("cnex");
+
+ if ((rv = ds_ports_init()) != 0) {
+ cmn_err(CE_WARN, "Domain Services initialization failed");
+ ds_fini();
+ return (rv);
+ }
+
+ if ((rv = mod_install(&modlinkage)) != 0) {
+ (void) ds_ports_fini();
+ ds_fini();
+ }
+
+ return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int rv;
+
+ if ((rv = mod_remove(&modlinkage)) == 0) {
+ (void) ds_ports_fini();
+ ds_fini();
+ }
+
+ return (rv);
+}
+
+static void
+ds_fini(void)
+{
+ /*
+ * Flip the enabled switch to make sure that no
+ * incoming events get dispatched while things
+ * are being torn down.
+ */
+ ds_enabled = B_FALSE;
+
+ /*
+ * Destroy the taskq.
+ */
+ taskq_destroy(ds_taskq);
+
+ /*
+ * Destroy the message log.
+ */
+ ds_log_fini();
+
+ /*
+ * Deallocate the table of registered services
+ */
+
+ /* clear out all entries */
+ mutex_enter(&ds_svcs.lock);
+ (void) ds_walk_svcs(ds_svc_free, NULL);
+ mutex_exit(&ds_svcs.lock);
+
+ /* destroy the table itself */
+ DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
+ mutex_destroy(&ds_svcs.lock);
+ bzero(&ds_svcs, sizeof (ds_svcs));
+}
+
+/*
+ * Initialize the list of ports based on the MD.
+ */
+static int
+ds_ports_init(void)
+{
+ int idx;
+ int rv = 0;
+ md_t *mdp;
+ int num_nodes;
+ int listsz;
+ mde_cookie_t rootnode;
+ mde_cookie_t dsnode;
+ mde_cookie_t *portp = NULL;
+ mde_cookie_t *chanp = NULL;
+ int nport;
+ int nchan;
+
+ if ((mdp = md_get_handle()) == NULL) {
+ cmn_err(CE_WARN, "Unable to initialize machine description");
+ return (-1);
+ }
+
+ num_nodes = md_node_count(mdp);
+ ASSERT(num_nodes > 0);
+
+ listsz = num_nodes * sizeof (mde_cookie_t);
+
+ /* allocate temporary storage for MD scans */
+ portp = kmem_zalloc(listsz, KM_SLEEP);
+ chanp = kmem_zalloc(listsz, KM_SLEEP);
+
+ rootnode = md_root_node(mdp);
+ ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
+
+ /*
+ * The root of the search for DS port nodes is the
+ * DS node. Perform a scan to find that node.
+ */
+ nport = md_scan_dag(mdp, rootnode, md_find_name(mdp, DS_MD_ROOT_NAME),
+ md_find_name(mdp, "fwd"), portp);
+
+ if (nport <= 0) {
+ DS_DBG_MD(CE_NOTE, "No '%s' node in MD", DS_MD_ROOT_NAME);
+ goto done;
+ }
+
+ /* expecting only one DS node */
+ if (nport != 1) {
+ DS_DBG_MD(CE_NOTE, "Expected one '%s' node in the MD, found %d",
+ DS_MD_ROOT_NAME, nport);
+ }
+
+ dsnode = portp[0];
+
+ /* find all the DS ports in the MD */
+ nport = md_scan_dag(mdp, dsnode, md_find_name(mdp, DS_MD_PORT_NAME),
+ md_find_name(mdp, "fwd"), portp);
+
+ if (nport <= 0) {
+ DS_DBG_MD(CE_NOTE, "No '%s' nodes in MD", DS_MD_PORT_NAME);
+ goto done;
+ }
+
+ /*
+ * Initialize all the ports found in the MD.
+ */
+ for (idx = 0; idx < nport; idx++) {
+
+ /* get the channels for this port */
+ nchan = md_scan_dag(mdp, portp[idx],
+ md_find_name(mdp, DS_MD_CHAN_NAME),
+ md_find_name(mdp, "fwd"), chanp);
+
+ if (nchan <= 0) {
+ cmn_err(CE_WARN, "No '%s' node for DS port",
+ DS_MD_CHAN_NAME);
+ rv = -1;
+ goto done;
+ }
+
+ /* expecting only one channel */
+ if (nchan != 1) {
+ DS_DBG_MD(CE_NOTE, "Expected one '%s' node for DS "
+ " port, found %d", DS_MD_CHAN_NAME, nchan);
+ }
+
+ if (ds_port_add(mdp, portp[idx], chanp[0]) != 0) {
+ rv = -1;
+ goto done;
+ }
+ }
+
+done:
+ if (rv != 0)
+ (void) ds_ports_fini();
+
+ DS_FREE(portp, listsz);
+ DS_FREE(chanp, listsz);
+
+ (void) md_fini_handle(mdp);
+
+ return (rv);
+}
+
+static int
+ds_ports_fini(void)
+{
+ int idx;
+
+ /*
+ * Tear down each initialized port.
+ */
+ for (idx = 0; idx < DS_MAX_PORTS; idx++) {
+ if (DS_PORT_IN_SET(ds_allports, idx)) {
+ (void) ds_remove_port(idx, 1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ds_port_add(md_t *mdp, mde_cookie_t port, mde_cookie_t chan)
+{
+ uint64_t port_id;
+ uint64_t ldc_id;
+ uint64_t dhdl;
+ char *dom_name;
+
+ /* get the ID for this port */
+ if (md_get_prop_val(mdp, port, "id", &port_id) != 0) {
+ cmn_err(CE_WARN, "%s: port 'id' property not found",
+ __func__);
+ return (-1);
+ }
+
+ /* sanity check the port id */
+ if (port_id > DS_MAX_PORT_ID) {
+ cmn_err(CE_WARN, "%s: port ID %ld out of range",
+ __func__, port_id);
+ return (-1);
+ }
+
+ /* get the channel ID for this port */
+ if (md_get_prop_val(mdp, chan, "id", &ldc_id) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: no channel 'id' property",
+ port_id, __func__);
+ return (-1);
+ }
+
+ /* get the remote-domain-id property if it's there */
+ if (md_get_prop_val(mdp, port, "remote-domain-id", &dhdl) != 0) {
+ DS_DBG_MD(CE_NOTE, "ds@%lx: %s: 'remote-domain-id' prop "
+ " not found", port_id, __func__);
+ dhdl = DS_DHDL_INVALID;
+ }
+
+ /* get the remote-domain-name property if it's there */
+ if (md_get_prop_str(mdp, port, "remote-domain-name", &dom_name) != 0) {
+ DS_DBG_MD(CE_NOTE, "ds@%lx: %s: 'remote-domain-name' prop "
+ " not found", port_id, __func__);
+ dom_name = NULL;
+ }
+
+ if (ds_add_port(port_id, ldc_id, dhdl, dom_name, 1) != 0)
+ return (-1);
+
+ return (0);
+}
+
+void
+ds_init()
+{
+ ds_common_init();
+
+ /*
+ * Create taskq for internal processing threads. This
+ * includes processing incoming request messages and
+ * sending out of band registration messages.
+ */
+ ds_taskq = taskq_create("ds_taskq", 1, minclsyspri, 1,
+ DS_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
+
+ /*
+ * Initialize the message log.
+ */
+ ds_log_init();
+}
+
+int
+ds_sys_dispatch_func(void (func)(void *), void *arg)
+{
+ return (DS_DISPATCH(func, arg) == NULL);
+}
+
+/*
+ * Drain event queue, if necessary.
+ */
+void
+ds_sys_drain_events(ds_port_t *port)
+{
+ _NOTE(ARGUNUSED(port))
+}
+
+/*
+ * System specific port initalization.
+ */
+void
+ds_sys_port_init(ds_port_t *port)
+{
+ _NOTE(ARGUNUSED(port))
+}
+
+/*
+ * System specific port teardown.
+ */
+void
+ds_sys_port_fini(ds_port_t *port)
+{
+ _NOTE(ARGUNUSED(port))
+}
+
+/*
+ * System specific LDC channel initialization.
+ */
+void
+ds_sys_ldc_init(ds_port_t *port)
+{
+ int rv;
+ char ebuf[DS_EBUFSIZE];
+
+ ASSERT(MUTEX_HELD(&port->lock));
+
+ if ((rv = ldc_open(port->ldc.hdl)) != 0) {
+ cmn_err(CE_WARN, "ds@%lx: %s: ldc_open: %s",
+ PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
+ return;
+ }
+
+ (void) ldc_up(port->ldc.hdl);
+
+ (void) ldc_status(port->ldc.hdl, &port->ldc.state);
+
+ DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: initial LDC state 0x%x",
+ PORTID(port), __func__, port->ldc.state);
+
+ port->state = DS_PORT_LDC_INIT;
+}
+
+/*
+ * DS message log
+ *
+ * Locking: The message log is protected by a single mutex. This
+ * protects all fields in the log structure itself as well as
+ * everything in the entry structures on both the log and the
+ * free list.
+ */
+static struct log {
+ ds_log_entry_t *head; /* head of the log */
+ ds_log_entry_t *freelist; /* head of the free list */
+ size_t size; /* size of the log in bytes */
+ uint32_t nentry; /* number of entries */
+ kmutex_t lock; /* log lock */
+} ds_log;
+
+/* log soft limit */
+uint_t ds_log_sz = DS_LOG_DEFAULT_SZ;
+
+/* initial pool of log entry structures */
+static ds_log_entry_t ds_log_entry_pool[DS_LOG_NPOOL];
+
+/*
+ * Logging Support
+ */
+static void
+ds_log_init(void)
+{
+ ds_log_entry_t *new;
+
+ /* initialize global lock */
+ mutex_init(&ds_log.lock, NULL, MUTEX_DRIVER, NULL);
+
+ mutex_enter(&ds_log.lock);
+
+ /* initialize the log */
+ ds_log.head = NULL;
+ ds_log.size = 0;
+ ds_log.nentry = 0;
+
+ /* initialize the free list */
+ for (new = ds_log_entry_pool; new < DS_LOG_POOL_END; new++) {
+ new->next = ds_log.freelist;
+ ds_log.freelist = new;
+ }
+
+ mutex_exit(&ds_log.lock);
+
+ DS_DBG_LOG(CE_NOTE, "ds_log initialized: size=%d bytes, "
+ " limit=%d bytes, ninit=%ld", ds_log_sz, DS_LOG_LIMIT,
+ DS_LOG_NPOOL);
+}
+
+static void
+ds_log_fini(void)
+{
+ ds_log_entry_t *next;
+
+ mutex_enter(&ds_log.lock);
+
+ /* clear out the log */
+ while (ds_log.nentry > 0)
+ (void) ds_log_remove();
+
+ /*
+ * Now all the entries are on the free list.
+ * Clear out the free list, deallocating any
+ * entry that was dynamically allocated.
+ */
+ while (ds_log.freelist != NULL) {
+ next = ds_log.freelist->next;
+
+ if (!DS_IS_POOL_ENTRY(ds_log.freelist)) {
+ kmem_free(ds_log.freelist, sizeof (ds_log_entry_t));
+ }
+
+ ds_log.freelist = next;
+ }
+
+ mutex_exit(&ds_log.lock);
+
+ mutex_destroy(&ds_log.lock);
+}
+
+static ds_log_entry_t *
+ds_log_entry_alloc(void)
+{
+ ds_log_entry_t *new = NULL;
+
+ ASSERT(MUTEX_HELD(&ds_log.lock));
+
+ if (ds_log.freelist != NULL) {
+ new = ds_log.freelist;
+ ds_log.freelist = ds_log.freelist->next;
+ }
+
+ if (new == NULL) {
+ /* free list was empty */
+ new = kmem_zalloc(sizeof (ds_log_entry_t), KM_SLEEP);
+ }
+
+ ASSERT(new);
+
+ return (new);
+}
+
+static void
+ds_log_entry_free(ds_log_entry_t *entry)
+{
+ ASSERT(MUTEX_HELD(&ds_log.lock));
+
+ if (entry == NULL)
+ return;
+
+ if (entry->data != NULL) {
+ kmem_free(entry->data, entry->datasz);
+ entry->data = NULL;
+ }
+
+ /* place entry on the free list */
+ entry->next = ds_log.freelist;
+ ds_log.freelist = entry;
+}
+
+/*
+ * Add a message to the end of the log
+ */
+static int
+ds_log_add(ds_log_entry_t *new)
+{
+ ASSERT(MUTEX_HELD(&ds_log.lock));
+
+ if (ds_log.head == NULL) {
+
+ new->prev = new;
+ new->next = new;
+
+ ds_log.head = new;
+ } else {
+ ds_log_entry_t *head = ds_log.head;
+ ds_log_entry_t *tail = ds_log.head->prev;
+
+ new->next = head;
+ new->prev = tail;
+ tail->next = new;
+ head->prev = new;
+ }
+
+ /* increase the log size, including the metadata size */
+ ds_log.size += DS_LOG_ENTRY_SZ(new);
+ ds_log.nentry++;
+
+ DS_DBG_LOG(CE_NOTE, "ds_log: added %ld data bytes, %ld total bytes",
+ new->datasz, DS_LOG_ENTRY_SZ(new));
+
+ return (0);
+}
+
+/*
+ * Remove an entry from the head of the log
+ */
+static int
+ds_log_remove(void)
+{
+ ds_log_entry_t *head;
+
+ ASSERT(MUTEX_HELD(&ds_log.lock));
+
+ head = ds_log.head;
+
+ /* empty list */
+ if (head == NULL)
+ return (0);
+
+ if (head->next == ds_log.head) {
+ /* one element list */
+ ds_log.head = NULL;
+ } else {
+ head->next->prev = head->prev;
+ head->prev->next = head->next;
+ ds_log.head = head->next;
+ }
+
+ DS_DBG_LOG(CE_NOTE, "ds_log: removed %ld data bytes, %ld total bytes",
+ head->datasz, DS_LOG_ENTRY_SZ(head));
+
+ ds_log.size -= DS_LOG_ENTRY_SZ(head);
+ ds_log.nentry--;
+
+ ds_log_entry_free(head);
+
+ return (0);
+}
+
+/*
+ * Replace the data in the entry at the front of the list with then
+ * new data. This has the effect of removing the oldest entry and
+ * adding the new entry.
+ */
+static int
+ds_log_replace(int32_t dest, uint8_t *msg, size_t sz)
+{
+ ds_log_entry_t *head;
+
+ ASSERT(MUTEX_HELD(&ds_log.lock));
+
+ head = ds_log.head;
+
+ DS_DBG_LOG(CE_NOTE, "ds_log: replaced %ld data bytes (%ld total) with "
+ " %ld data bytes (%ld total)", head->datasz,
+ DS_LOG_ENTRY_SZ(head), sz, sz + sizeof (ds_log_entry_t));
+
+ ds_log.size -= DS_LOG_ENTRY_SZ(head);
+
+ kmem_free(head->data, head->datasz);
+
+ head->data = msg;
+ head->datasz = sz;
+ head->timestamp = ddi_get_time();
+ head->dest = dest;
+
+ ds_log.size += DS_LOG_ENTRY_SZ(head);
+
+ ds_log.head = head->next;
+
+ return (0);
+}
+
+static void
+ds_log_purge(void *arg)
+{
+ _NOTE(ARGUNUSED(arg))
+
+ mutex_enter(&ds_log.lock);
+
+ DS_DBG_LOG(CE_NOTE, "ds_log: purging oldest log entries");
+
+ while ((ds_log.nentry) && (ds_log.size >= ds_log_sz)) {
+ (void) ds_log_remove();
+ }
+
+ mutex_exit(&ds_log.lock);
+}
+
+int
+ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz)
+{
+ int rv = 0;
+ void *data;
+
+ mutex_enter(&ds_log.lock);
+
+ /* allocate a local copy of the data */
+ data = kmem_alloc(sz, KM_SLEEP);
+ bcopy(msg, data, sz);
+
+ /* check if the log is larger than the soft limit */
+ if ((ds_log.nentry) && ((ds_log.size + sz) >= ds_log_sz)) {
+ /*
+ * The log is larger than the soft limit.
+ * Swap the oldest entry for the newest.
+ */
+ DS_DBG_LOG(CE_NOTE, "%s: replacing oldest entry with new entry",
+ __func__);
+ (void) ds_log_replace(dest, data, sz);
+ } else {
+ /*
+ * Still have headroom under the soft limit.
+ * Add the new entry to the log.
+ */
+ ds_log_entry_t *new;
+
+ new = ds_log_entry_alloc();
+
+ /* fill in message data */
+ new->data = data;
+ new->datasz = sz;
+ new->timestamp = ddi_get_time();
+ new->dest = dest;
+
+ rv = ds_log_add(new);
+ }
+
+ /* check if the log is larger than the hard limit */
+ if ((ds_log.nentry > 1) && (ds_log.size >= DS_LOG_LIMIT)) {
+ /*
+ * Wakeup the thread to remove entries
+ * from the log until it is smaller than
+ * the soft limit.
+ */
+ DS_DBG_LOG(CE_NOTE, "%s: log exceeded %d bytes, scheduling"
+ " a purge...", __func__, DS_LOG_LIMIT);
+
+ if (DS_DISPATCH(ds_log_purge, NULL) == NULL) {
+ cmn_err(CE_NOTE, "%s: purge thread failed to start",
+ __func__);
+ }
+ }
+
+ mutex_exit(&ds_log.lock);
+
+ return (rv);
+}
+
+int
+ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl,
+ char *dom_name, int verbose)
+{
+ ds_port_t *newport;
+
+ /* sanity check the port id */
+ if (port_id > DS_MAX_PORT_ID) {
+ cmn_err(CE_WARN, "%s: port ID %ld out of range",
+ __func__, port_id);
+ return (EINVAL);
+ }
+
+ DS_DBG_MD(CE_NOTE, "%s: adding port ds@%ld, LDC: 0x%lx, dhdl: 0x%lx",
+ __func__, port_id, ldc_id, dhdl);
+
+ /* get the port structure from the array of ports */
+ newport = &ds_ports[port_id];
+
+ /* check for a duplicate port in the MD */
+ if (newport->state != DS_PORT_FREE) {
+ if (verbose) {
+ cmn_err(CE_WARN, "ds@%lx: %s: port already exists",
+ port_id, __func__);
+ }
+ if (newport->domain_hdl == DS_DHDL_INVALID) {
+ newport->domain_hdl = dhdl;
+ }
+ if (newport->domain_name == NULL && dom_name != NULL) {
+ newport->domain_name = ds_strdup(dom_name);
+ }
+ return (EBUSY);
+ }
+
+ /* initialize the port */
+ newport->id = port_id;
+ newport->ldc.id = ldc_id;
+ newport->domain_hdl = dhdl;
+ if (dom_name) {
+ newport->domain_name = ds_strdup(dom_name);
+ } else
+ newport->domain_name = NULL;
+ ds_port_common_init(newport);
+
+ return (0);
+}
+
+int
+ds_remove_port(uint64_t port_id, int is_fini)
+{
+ ds_port_t *port;
+
+ if (port_id >= DS_MAX_PORTS || !DS_PORT_IN_SET(ds_allports, port_id)) {
+ DS_DBG_MD(CE_NOTE, "%s: invalid port %lx", __func__,
+ port_id);
+ return (EINVAL);
+ }
+
+ DS_DBG_MD(CE_NOTE, "%s: removing port ds@%lx", __func__, port_id);
+
+ port = &ds_ports[port_id];
+
+ mutex_enter(&port->lock);
+
+ if (port->state >= DS_PORT_LDC_INIT) {
+ /* shut down the LDC for this port */
+ (void) ds_ldc_fini(port);
+ }
+
+ mutex_exit(&port->lock);
+
+ if (port->domain_name) {
+ DS_FREE(port->domain_name, strlen(port->domain_name) + 1);
+ port->domain_name = NULL;
+ }
+ port->domain_hdl = DS_DHDL_INVALID;
+
+ /* clean up the port structure */
+ ds_port_common_fini(port, is_fini);
+ return (0);
+}
+
+/*
+ * Interface for ds_service_lookup in lds driver.
+ */
+int
+ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client)
+{
+ ds_svc_t *svc;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
+ (u_longlong_t)hdl);
+ return (ENXIO);
+ }
+ *servicep = svc->cap.svc_id;
+ *is_client = svc->flags & DSSF_ISCLIENT;
+ mutex_exit(&ds_svcs.lock);
+ return (0);
+}
+
+/*
+ * Interface for ds_domain_lookup in lds driver.
+ */
+int
+ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp)
+{
+ ds_svc_t *svc;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
+ (u_longlong_t)hdl);
+ return (ENXIO);
+ }
+ if (svc->port == NULL)
+ *dhdlp = ds_my_domain_hdl;
+ else
+ *dhdlp = svc->port->domain_hdl;
+ mutex_exit(&ds_svcs.lock);
+ return (0);
+}
+
+/*
+ * Interface for ds_hdl_isready in lds driver.
+ */
+int
+ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready)
+{
+ ds_svc_t *svc;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ mutex_exit(&ds_svcs.lock);
+ DS_DBG(CE_NOTE, "%s: handle 0x%llx not found", __func__,
+ (u_longlong_t)hdl);
+ return (ENXIO);
+ }
+ *is_ready = (svc->state == DS_SVC_ACTIVE);
+ mutex_exit(&ds_svcs.lock);
+ return (0);
+}
+
+/*
+ * Interface for ds_dom_name_to_hdl in lds driver.
+ */
+int
+ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp)
+{
+ int i;
+ ds_port_t *port;
+
+ for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
+ if (port->state != DS_PORT_FREE &&
+ port->domain_name != NULL &&
+ strcmp(port->domain_name, domain_name) == 0) {
+ *dhdlp = port->domain_hdl;
+ return (0);
+ }
+ }
+ return (ENXIO);
+}
+
+/*
+ * Interface for ds_dom_hdl_to_name in lds driver.
+ */
+int
+ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep)
+{
+ int i;
+ ds_port_t *port;
+
+ for (i = 0, port = ds_ports; i < DS_MAX_PORTS; i++, port++) {
+ if (port->state != DS_PORT_FREE &&
+ port->domain_hdl == dhdl) {
+ *domain_namep = port->domain_name;
+ return (0);
+ }
+ }
+ return (ENXIO);
+}
+
+/*
+ * Unregister all handles related to device open instance.
+ */
+void
+ds_unreg_all(int instance)
+{
+ int idx;
+ ds_svc_t *svc;
+ ds_svc_hdl_t hdl;
+
+ DS_DBG_USR(CE_NOTE, "%s: entered", __func__);
+
+ /* walk every table entry */
+ mutex_enter(&ds_svcs.lock);
+ for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+ svc = ds_svcs.tbl[idx];
+ if (DS_SVC_ISFREE(svc))
+ continue;
+ if ((svc->flags & DSSF_ISUSER) != 0 && svc->drvi == instance) {
+ hdl = svc->hdl;
+ mutex_exit(&ds_svcs.lock);
+ (void) ds_unreg_hdl(hdl);
+ mutex_enter(&ds_svcs.lock);
+ DS_DBG_USR(CE_NOTE, "%s: ds_unreg_hdl(0x%llx):",
+ __func__, (u_longlong_t)hdl);
+ }
+ }
+ mutex_exit(&ds_svcs.lock);
+}
+
+/*
+ * Special callbacks to allow the lds module revision-independent access
+ * to service structure data in the callback routines. This assumes that
+ * we put a special "cookie" in the arg argument passed to those
+ * routines (for now, a ptr to the svc structure, but it could be a svc
+ * table index or something that we could get back to the svc table entry).
+ */
+void
+ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *hdlp = svc->hdl;
+}
+
+void
+ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *flagsp = svc->flags;
+}
+
+void
+ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *drvip = svc->drvi;
+}
+
+void
+ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *dpspp = svc->drv_psp;
+}
+
+void
+ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ if (svc->port == NULL)
+ *dhdlp = ds_my_domain_hdl;
+ else
+ *dhdlp = svc->port->domain_hdl;
+}
+
+void
+ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *servicep = svc->cap.svc_id;
+}
+
+void
+ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp)
+{
+ ds_svc_t *svc = (ds_svc_t *)arg;
+
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ svc->drv_psp = dpsp;
+}
+
+void
+ds_cbarg_set_cookie(ds_svc_t *svc)
+{
+ svc->ops.cb_arg = (ds_cb_arg_t)(svc);
+}
+
+int
+ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp)
+{
+ ds_svc_t *svc;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) != NULL &&
+ (svc->flags & DSSF_ISUSER) != 0) {
+ ASSERT(svc == (ds_svc_t *)svc->ops.cb_arg);
+ *cbargp = svc->ops.cb_arg;
+ mutex_exit(&ds_svcs.lock);
+ return (0);
+ }
+ mutex_exit(&ds_svcs.lock);
+ return (ENXIO);
+}
+
+int
+ds_is_my_hdl(ds_svc_hdl_t hdl, int instance)
+{
+ ds_svc_t *svc;
+ int rv = 0;
+
+ mutex_enter(&ds_svcs.lock);
+ if ((svc = ds_get_svc(hdl)) == NULL) {
+ DS_DBG_USR(CE_NOTE, "%s: invalid hdl: 0x%llx\n", __func__,
+ (u_longlong_t)hdl);
+ rv = ENXIO;
+ } else if (instance == DS_INVALID_INSTANCE) {
+ if ((svc->flags & DSSF_ISUSER) != 0) {
+ DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n",
+ __func__, (u_longlong_t)hdl);
+ rv = EACCES;
+ }
+ } else if ((svc->flags & DSSF_ISUSER) == 0 || svc->drvi != instance) {
+ DS_DBG_USR(CE_NOTE, "%s: unowned hdl: 0x%llx\n", __func__,
+ (u_longlong_t)hdl);
+ rv = EACCES;
+ }
+ mutex_exit(&ds_svcs.lock);
+ return (rv);
+}
diff --git a/usr/src/uts/sun4v/io/vlds.c b/usr/src/uts/sun4v/io/vlds.c
new file mode 100644
index 0000000000..25b1e3edb4
--- /dev/null
+++ b/usr/src/uts/sun4v/io/vlds.c
@@ -0,0 +1,1576 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * LDOMs Domain Services Device Driver
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/ksynch.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/debug.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/taskq.h>
+#include <sys/disp.h>
+#include <sys/note.h>
+#include <sys/mach_descrip.h>
+#include <sys/mdesc.h>
+#include <sys/mdeg.h>
+#include <sys/ldc.h>
+#include <sys/ds.h>
+#include <sys/ds_impl.h>
+#include <sys/vlds.h>
+#include <sys/bitmap.h>
+#include <sys/sysevent.h>
+
+static dev_info_t *vlds_devi;
+
+
+typedef struct vlds_state {
+ dev_info_t *dip;
+ int instance;
+ evchan_t *evchan;
+} vlds_state_t;
+
+static void *vlds_statep;
+
+typedef struct vlds_recv_hdr {
+ struct vlds_recv_hdr *next; /* next in recv list */
+ void *data; /* the data itself */
+ size_t datasz; /* size of the data */
+} vlds_recv_hdr_t;
+
+typedef struct vlds_svc_info {
+ int state; /* driver svc info state VLDS_RECV* */
+ vlds_recv_hdr_t *recv_headp; /* ptr to head of recv queue */
+ vlds_recv_hdr_t *recv_tailp; /* ptr to tail of recv queue */
+ size_t recv_size; /* no. of bytes in recv queue */
+ kmutex_t recv_lock; /* lock for recv queue */
+ kcondvar_t recv_cv; /* condition variable for recv queue */
+ int recv_nreaders; /* no of currently waiting readers */
+} vlds_svc_info_t;
+
+#define VLDS_RECV_OK 1
+#define VLDS_RECV_UNREG_PENDING 2
+
+static int vlds_ports_inited = 0;
+
+static uint_t vlds_flags_to_svc(uint64_t flags);
+
+
+#define VLDS_NAME "vlds"
+static int vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp);
+static int vlds_close(dev_t dev, int flag, int otyp, cred_t *credp);
+static int vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp);
+static int vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+ void **resultp);
+static int vlds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int vlds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+/* mdeg register functions */
+static int ds_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
+static int ds_mdeg_register(void);
+static int ds_mdeg_unregister(void);
+static int ds_add_mdeg_port(md_t *mdp, mde_cookie_t node);
+
+/* driver utilities */
+static void vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
+static void vlds_user_unreg_cb(ds_cb_arg_t arg);
+static void vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen);
+static void vlds_recvq_init(vlds_svc_info_t *dpsp);
+static void vlds_recvq_destroy(vlds_svc_info_t *dpsp);
+static int vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
+ size_t *msglenp, int mode);
+static void vlds_recvq_drain(vlds_svc_info_t *dpsp);
+static int vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen);
+static int vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen,
+ size_t *msglenp, int mode);
+
+/*
+ * DS driver Ops Vector
+ */
+static struct cb_ops vlds_cb_ops = {
+ vlds_open, /* cb_open */
+ vlds_close, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ vlds_ioctl, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ (struct streamtab *)NULL, /* cb_str */
+ D_MP | D_64BIT, /* cb_flag */
+ CB_REV, /* cb_rev */
+ nodev, /* cb_aread */
+ nodev /* cb_awrite */
+};
+
+static struct dev_ops vlds_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ vlds_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ vlds_attach, /* devo_attach */
+ vlds_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &vlds_cb_ops, /* devo_cb_ops */
+ (struct bus_ops *)NULL, /* devo_bus_ops */
+ nulldev /* devo_power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "Domain Services Driver 1.0",
+ &vlds_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+/*
+ * Callback ops for user-land services.
+ */
+static ds_clnt_ops_t ds_user_ops = {
+ vlds_user_reg_cb, /* register */
+ vlds_user_unreg_cb, /* unregister */
+ vlds_user_data_cb, /* data */
+ NULL /* ds_ucap_init will fill in */
+};
+
+#define VLDS_MINOR_MAX SHRT_MAX
+
+/* Definitions for binding handle array */
+static ulong_t vlds_bitmap_initial = 1; /* index 0 indicates error */
+static ulong_t *vlds_minor_bitmap = &vlds_bitmap_initial;
+static size_t vlds_minor_bits = BT_NBIPUL;
+static kmutex_t vlds_minor_mutex;
+
+/*
+ * Following vlds_minor_* routines map a binding handle to a minor number.
+ * Has to be called w/ locks held.
+ */
+static ulong_t *
+vlds_minor_alloc(void)
+{
+ ulong_t *bhst = vlds_minor_bitmap;
+
+ /* Increase bitmap by one BT_NBIPUL */
+ if (vlds_minor_bits + BT_NBIPUL > VLDS_MINOR_MAX) {
+ return ((ulong_t *)NULL);
+ }
+ vlds_minor_bitmap = kmem_zalloc(
+ BT_SIZEOFMAP(vlds_minor_bits + BT_NBIPUL), KM_SLEEP);
+ bcopy(bhst, vlds_minor_bitmap, BT_SIZEOFMAP(vlds_minor_bits));
+ if (bhst != &vlds_bitmap_initial)
+ kmem_free(bhst, BT_SIZEOFMAP(vlds_minor_bits));
+ vlds_minor_bits += BT_NBIPUL;
+
+ return (vlds_minor_bitmap);
+}
+
+static void
+vlds_minor_free(ulong_t *bitmap)
+{
+ if (bitmap != &vlds_bitmap_initial)
+ kmem_free(bitmap, BT_SIZEOFMAP(vlds_minor_bits));
+}
+
+static index_t
+vlds_minor_get(void)
+{
+ index_t idx;
+ ulong_t *bhst;
+
+ /* Search for an available index */
+ mutex_enter(&vlds_minor_mutex);
+ if ((idx = bt_availbit(vlds_minor_bitmap,
+ vlds_minor_bits)) == -1) {
+ /* All busy - allocate additional binding handle bitmap space */
+ if ((bhst = vlds_minor_alloc()) == NULL) {
+ /* Reached our maximum of id's == SHRT_MAX */
+ mutex_exit(&vlds_minor_mutex);
+ return (0);
+ } else {
+ vlds_minor_bitmap = bhst;
+ }
+ idx = bt_availbit(vlds_minor_bitmap, vlds_minor_bits);
+ }
+ BT_SET(vlds_minor_bitmap, idx);
+ mutex_exit(&vlds_minor_mutex);
+ return (idx);
+}
+
+static void
+vlds_minor_rele(index_t idx)
+{
+ mutex_enter(&vlds_minor_mutex);
+ ASSERT(BT_TEST(vlds_minor_bitmap, idx) == 1);
+ BT_CLEAR(vlds_minor_bitmap, idx);
+ mutex_exit(&vlds_minor_mutex);
+}
+
+static void
+vlds_minor_init(void)
+{
+ mutex_init(&vlds_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
+}
+
+int
+_init(void)
+{
+ int s;
+
+ if ((s = ddi_soft_state_init(&vlds_statep, sizeof (vlds_state_t), 0))
+ != 0)
+ return (s);
+
+ if ((s = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&vlds_statep);
+ return (s);
+ }
+
+ (void) ds_mdeg_register();
+
+ return (s);
+}
+
+int
+_fini(void)
+{
+ int s;
+
+ if ((s = mod_remove(&modlinkage)) != 0)
+ return (s);
+
+ ddi_soft_state_fini(&vlds_statep);
+
+ (void) ds_mdeg_unregister();
+
+ return (s);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/*ARGSUSED*/
+static int
+vlds_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resultp = vlds_devi;
+ return (DDI_SUCCESS);
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = 0;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+
+static int
+vlds_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ if (cmd != DDI_ATTACH) {
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, VLDS_NAME, S_IFCHR,
+ 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+ vlds_devi = devi;
+
+ vlds_minor_init();
+
+ return (DDI_SUCCESS);
+}
+
+
+/*ARGSUSED*/
+static int
+vlds_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH) {
+ return (DDI_FAILURE);
+ }
+
+ vlds_minor_free(vlds_minor_bitmap);
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_SUCCESS);
+}
+
+
+/*ARGSUSED*/
+static int
+vlds_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ int minor;
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ if (getminor(*devp) != 0)
+ return (ENXIO);
+
+ minor = vlds_minor_get();
+ if (minor == 0)
+ /* All minors are busy */
+ return (EBUSY);
+
+ if (ddi_soft_state_zalloc(vlds_statep, minor) != DDI_SUCCESS) {
+ vlds_minor_rele(minor);
+ return (ENOMEM);
+ }
+
+ *devp = makedevice(getmajor(*devp), minor);
+
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+vlds_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ int minor = (int)getminor(dev);
+ vlds_state_t *sp;
+
+ DS_DBG_VLDS(CE_NOTE, "vlds_close");
+
+ /*
+ * Unregister all handles associated with this process.
+ */
+ ds_unreg_all(minor);
+
+ if (otyp != OTYP_CHR)
+ return (EINVAL);
+
+ sp = ddi_get_soft_state(vlds_statep, minor);
+ if (sp == NULL) {
+ return (ENXIO);
+ }
+
+ if (sp->evchan) {
+ sysevent_evc_unbind(sp->evchan);
+ sp->evchan = NULL;
+ }
+
+ ddi_soft_state_free(vlds_statep, minor);
+ vlds_minor_rele(minor);
+
+ return (0);
+}
+
+int
+vlds_init_sysevent(vlds_state_t *sp, uint32_t flags)
+{
+ char evchan_name[MAX_CHNAME_LEN];
+ int rv;
+
+ if (flags & DSSF_ANYCB_VALID) {
+ if (sp->evchan) {
+ DS_DBG_VLDS(CE_NOTE, "%s: sysevent already bound",
+ __func__);
+ return (0);
+ }
+ (void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, ddi_get_pid());
+ if ((rv = sysevent_evc_bind(evchan_name, &sp->evchan,
+ EVCH_CREAT|EVCH_HOLD_PEND)) != 0) {
+ cmn_err(CE_WARN, "%s: can't bind to '%s' (%d)",
+ __func__, evchan_name, rv);
+ return (rv);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: sysevent bind to '%s' successful",
+ __func__, evchan_name);
+ }
+ return (0);
+}
+
+#define ARGTOPTR(x) ((void *)((uintptr_t)(x)))
+#define ARGTOUINT(x) ((uint_t)(x))
+#define ARGTOINT(x) ((int)(x))
+
+static int
+vlds_get_string(vlds_string_t *strp, char **rstrp, int mode)
+{
+ char *str;
+ uint_t len = strp->vlds_strlen;
+ uint_t slen;
+
+ if (len == 0) {
+ *rstrp = NULL;
+ return (0);
+ }
+ if (len > MAXNAMELEN) {
+ DS_DBG_VLDS(CE_NOTE, "%s: invalid string length: %d", __func__,
+ len);
+ return (EINVAL);
+ }
+ str = DS_MALLOC(len);
+ if (ddi_copyin(ARGTOPTR(strp->vlds_strp), str, len, mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: ddi copyin failed (%p)", __func__,
+ ARGTOPTR(strp->vlds_strp));
+ DS_FREE(str, len);
+ return (EFAULT);
+ }
+ slen = strlen(str) + 1;
+ if (slen != len) {
+ DS_DBG_VLDS(CE_NOTE, "%s: invalid string len: %d != len: %d",
+ __func__, slen, len);
+ DS_FREE(str, len);
+ return (EINVAL);
+ }
+ *rstrp = str;
+ return (0);
+}
+
+static int
+vlds_put_string(char *str, vlds_string_t *strp, int mode)
+{
+ uint_t len;
+ char *tstr = NULL;
+ int rv;
+
+ if (str == NULL) {
+ str = "";
+ }
+ len = strlen(str) + 1;
+
+ /*
+ * If string is longer than user buffer, return a
+ * truncated, null-terminated string.
+ */
+ if (len > strp->vlds_strlen) {
+ len = strp->vlds_strlen;
+ if (len > 0) {
+ tstr = DS_MALLOC(len);
+ (void) memcpy(tstr, str, len - 1);
+ tstr[len - 1] = '\0';
+ str = tstr;
+ }
+ }
+ rv = ddi_copyout(str, ARGTOPTR(strp->vlds_strp), len, mode);
+ if (tstr) {
+ DS_FREE(tstr, len);
+ }
+ if (rv) {
+ DS_DBG_VLDS(CE_NOTE, "%s: copyout (%p) failed", __func__,
+ ARGTOPTR(strp->vlds_strp));
+ return (EFAULT);
+ }
+ return (0);
+}
+
+static int
+vlds_get_ucap(vlds_cap_t *capp, ds_capability_t *ucap, int mode)
+{
+ char *servp;
+ vlds_ver_t *dsvp;
+ vlds_cap_t vlds_cap;
+ uint_t n;
+ uint_t nver;
+ int i;
+ int rv;
+
+ if (ddi_copyin(capp, &vlds_cap, sizeof (vlds_cap), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: cap copyin failed (%p)", __func__,
+ (void *)capp);
+ return (EFAULT);
+ }
+
+ nver = ARGTOUINT(vlds_cap.vlds_nver);
+
+ if (nver > VLDS_MAX_VERS) {
+ DS_DBG_VLDS(CE_NOTE, "%s: vlds_nver (%d) invalid", __func__,
+ nver);
+ return (EINVAL);
+ }
+
+ if ((rv = vlds_get_string(&vlds_cap.vlds_service, &servp, mode)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service failed "
+ "(%d)", __func__, rv);
+ return (rv);
+ } else if (servp == NULL) {
+ DS_DBG_VLDS(CE_NOTE, "%s: vlds_get_string vlds_service is NULL",
+ __func__);
+ return (EINVAL);
+ }
+
+ n = nver * sizeof (vlds_ver_t);
+ dsvp = DS_MALLOC(n);
+
+ if (ddi_copyin(ARGTOPTR(vlds_cap.vlds_versp), dsvp, n, mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: copyin of vers (%p, %d) failed",
+ __func__, ARGTOPTR(vlds_cap.vlds_versp), n);
+ DS_FREE(servp, strlen(servp) + 1);
+ DS_FREE(dsvp, n);
+ return (EFAULT);
+ }
+
+ ucap->svc_id = servp;
+ ucap->vers = DS_MALLOC(nver * sizeof (ds_ver_t));
+ for (i = 0; i < nver; i++) {
+ ucap->vers[i].major = dsvp[i].vlds_major;
+ ucap->vers[i].minor = dsvp[i].vlds_minor;
+ }
+ ucap->nvers = nver;
+ DS_FREE(dsvp, n);
+ return (0);
+}
+
+static void
+vlds_free_ucap(ds_capability_t *ucap)
+{
+ kmem_free(ucap->svc_id, strlen(ucap->svc_id) + 1);
+ kmem_free(ucap->vers, ucap->nvers * sizeof (ds_ver_t));
+}
+
+/*ARGSUSED*/
+static int
+vlds_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ vlds_state_t *sp;
+ ds_svc_hdl_t hdl;
+ ds_domain_hdl_t dhdl;
+ char *servicep;
+ int rv;
+ int minor = (int)getminor(dev);
+
+ if ((sp = ddi_get_soft_state(vlds_statep, minor)) == NULL)
+ return (ENXIO);
+
+ switch (cmd) {
+
+ case VLDS_SVC_REG:
+ {
+ vlds_svc_reg_arg_t vlds_arg;
+ ds_capability_t ucap;
+ uint64_t hdl_arg;
+ uint_t flags;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SVC REG arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ if ((rv = vlds_get_ucap(ARGTOPTR(vlds_arg.vlds_capp), &ucap,
+ mode)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SVC REG get_ucap failed (%d)",
+ __func__, rv);
+ return (rv);
+ }
+
+ flags = vlds_flags_to_svc(vlds_arg.vlds_reg_flags);
+ if ((rv = vlds_init_sysevent(sp, flags)) != 0) {
+ vlds_free_ucap(&ucap);
+ return (rv);
+ }
+
+ rv = ds_ucap_init(&ucap, &ds_user_ops,
+ vlds_flags_to_svc(vlds_arg.vlds_reg_flags) | DSSF_ISUSER,
+ minor, &hdl);
+
+ vlds_free_ucap(&ucap);
+
+ if (rv) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SVC REG ds_ucap_init failed "
+ "(%d)", __func__, rv);
+ return (rv);
+ }
+
+ hdl_arg = hdl;
+ if (ddi_copyout(&hdl_arg, ARGTOPTR(vlds_arg.vlds_hdlp),
+ sizeof (hdl_arg), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SVC REG copyout failed",
+ __func__);
+ return (EFAULT);
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: SVC REG succeeded: hdl: %lx",
+ __func__, hdl);
+ break;
+ }
+
+ case VLDS_UNREG_HDL:
+ {
+ vlds_unreg_hdl_arg_t vlds_arg;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ hdl = vlds_arg.vlds_hdl;
+
+ if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_is_my_hdl "
+ " hdl: %lx inst: %d failed (%d)", __func__,
+ hdl, rv, minor);
+ return (rv);
+ }
+
+ if ((rv = ds_unreg_hdl(hdl)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL ds_cap_unreg "
+ " hdl: %lx failed (%d)", __func__, hdl, rv);
+ return (rv);
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: UNREG_HDL hdl: %lx succeeded",
+ __func__, hdl);
+ break;
+ }
+
+ case VLDS_HDL_LOOKUP:
+ {
+ vlds_hdl_lookup_arg_t vlds_arg;
+ ds_svc_hdl_t *hdlsp;
+ uint_t is_client, maxhdls, nhdls;
+ uint64_t nhdls_arg;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ is_client = ARGTOUINT(vlds_arg.vlds_isclient);
+ maxhdls = ARGTOUINT(vlds_arg.vlds_maxhdls);
+ if (maxhdls == 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP invalid maxhdls "
+ "%d", __func__, maxhdls);
+ return (EINVAL);
+ }
+
+ if ((rv = vlds_get_string(&vlds_arg.vlds_service, &servicep,
+ mode)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
+ "(service) failed (%d)", __func__, rv);
+ return (EFAULT);
+ } else if (servicep == NULL) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP vlds_get_string "
+ " service is NULL", __func__);
+ return (EINVAL);
+ }
+
+ if (ARGTOPTR(vlds_arg.vlds_hdlsp) == 0) {
+ hdlsp = NULL;
+ } else {
+ hdlsp = DS_MALLOC(maxhdls * sizeof (*hdlsp));
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP (%s, %d) entered",
+ __func__, servicep, is_client);
+ rv = ds_hdl_lookup(servicep, is_client, hdlsp, maxhdls, &nhdls);
+
+ DS_FREE(servicep, strlen(servicep) + 1);
+ if (rv) {
+ if (hdlsp) {
+ DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP failed: (%d)",
+ __func__, rv);
+ return (rv);
+ }
+
+ if (hdlsp != NULL && nhdls > 0 &&
+ ddi_copyout(hdlsp, ARGTOPTR(vlds_arg.vlds_hdlsp),
+ nhdls * sizeof (ds_svc_hdl_t), mode) != 0) {
+ if (hdlsp) {
+ DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of hdls "
+ " failed", __func__);
+ return (EFAULT);
+ }
+ if (hdlsp) {
+ DS_FREE(hdlsp, maxhdls * sizeof (*hdlsp));
+ }
+
+ nhdls_arg = nhdls;
+ if (ddi_copyout(&nhdls_arg, ARGTOPTR(vlds_arg.vlds_nhdlsp),
+ sizeof (nhdls_arg), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP copyout of nhdls "
+ " failed", __func__);
+ return (EFAULT);
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_LOOKUP succeeded: nhdls: %d",
+ __func__, nhdls);
+ break;
+ }
+
+ case VLDS_DMN_LOOKUP:
+ {
+ vlds_dmn_lookup_arg_t vlds_arg;
+ uint64_t dhdl_arg;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ hdl = vlds_arg.vlds_hdl;
+
+ if ((rv = ds_domain_lookup(hdl, &dhdl)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP lookup hdl: 0x%lx "
+ "failed (%d)", __func__, hdl, rv);
+ return (rv);
+ }
+
+ dhdl_arg = dhdl;
+
+ if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
+ sizeof (dhdl_arg), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP copyout "
+ "failed (%d)", __func__, rv);
+ return (rv);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: DMN_LOOKUP hdl: 0x%lx, dhdl: 0x%lx "
+ "succeeded", __func__, hdl, dhdl);
+ break;
+ }
+
+ case VLDS_SEND_MSG:
+ {
+ vlds_send_msg_arg_t vlds_arg;
+ size_t buflen;
+ char *bufp;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ hdl = vlds_arg.vlds_hdl;
+ if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_is_my_hdl "
+ " hdl: %lx inst: %d failed (%d)", __func__,
+ hdl, rv, minor);
+ return (rv);
+ }
+
+ buflen = ARGTOUINT(vlds_arg.vlds_buflen);
+ bufp = DS_MALLOC(buflen);
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG (hdl: %lx, bufp: %p, "
+ "buflen: %ld", __func__, hdl, ARGTOPTR(vlds_arg.vlds_bufp),
+ buflen);
+
+ if (ddi_copyin(ARGTOPTR(vlds_arg.vlds_bufp), bufp, buflen,
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG buf (%p, %ld) "
+ "copyin failed", __func__,
+ ARGTOPTR(vlds_arg.vlds_bufp), buflen);
+ DS_FREE(bufp, buflen);
+ return (EFAULT);
+ }
+
+ if ((rv = ds_cap_send(hdl, bufp, buflen)) != 0) {
+ DS_FREE(bufp, buflen);
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG ds_cap_send failed "
+ "(%d)", __func__, rv);
+ return (rv);
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: SEND_MSG hdl: %lx, bufp: %p, "
+ "buflen: %ld succeeded", __func__, hdl, (void *)bufp,
+ buflen);
+ DS_DUMP_MSG(DS_DBG_FLAG_VLDS, bufp, buflen);
+ DS_FREE(bufp, buflen);
+ break;
+ }
+
+ case VLDS_RECV_MSG:
+ {
+ vlds_recv_msg_arg_t vlds_arg;
+ size_t buflen, msglen;
+ uint64_t msglen_arg;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG arg copyin failed",
+ __func__);
+ return (EFAULT);
+ }
+
+ hdl = vlds_arg.vlds_hdl;
+ if ((rv = ds_is_my_hdl(hdl, minor)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG ds_is_my_hdl "
+ " hdl: %lx inst: %d failed (%d)", __func__,
+ hdl, rv, minor);
+ return (rv);
+ }
+
+ buflen = ARGTOUINT(vlds_arg.vlds_buflen);
+
+ if ((rv = vlds_recv_msg(hdl, ARGTOPTR(vlds_arg.vlds_bufp),
+ buflen, &msglen, mode)) != 0 && rv != EFBIG) {
+ DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG vlds_recv_msg "
+ " failed (%d)", __func__, rv);
+ return (rv);
+ }
+
+ msglen_arg = msglen;
+ if (ddi_copyout(&msglen_arg, ARGTOPTR(vlds_arg.vlds_msglenp),
+ sizeof (msglen_arg), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG copyout of msglen "
+ "failed", __func__);
+ return (EFAULT);
+ }
+
+ if (rv == EFBIG) {
+ return (EFBIG);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: RECV_MSG hdl: %lx, "
+ "msglen: %ld succeeded", __func__, hdl, buflen);
+ break;
+ }
+
+ case VLDS_HDL_ISREADY:
+ {
+ vlds_hdl_isready_arg_t vlds_arg;
+ ds_svc_hdl_t hdl;
+ uint64_t is_ready_arg;
+ uint_t is_ready;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY arg copyin "
+ "failed", __func__);
+ return (EFAULT);
+ }
+
+ hdl = vlds_arg.vlds_hdl;
+ if ((rv = ds_hdl_isready(hdl, &is_ready)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY ds_hdl_isready "
+ "error (%d)", __func__, rv);
+ return (rv);
+ }
+
+ is_ready_arg = is_ready;
+ if (ddi_copyout(&is_ready_arg, ARGTOPTR(vlds_arg.vlds_isreadyp),
+ sizeof (is_ready_arg), mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY copyout of "
+ "vlds_isready failed", __func__);
+ return (EFAULT);
+ }
+ DS_DBG_VLDS(CE_NOTE, "%s: HDL_ISREADY succeeded hdl: %lx, "
+ "is_ready: %d", __func__, hdl, is_ready);
+ break;
+ }
+
+ case VLDS_DOM_NAM2HDL:
+ {
+ vlds_dom_nam2hdl_arg_t vlds_arg;
+ char *domain_name;
+ uint64_t dhdl_arg;
+ ds_domain_hdl_t dhdl;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL arg copyin "
+ "failed", __func__);
+ return (EFAULT);
+ }
+
+ if ((rv = vlds_get_string(&vlds_arg.vlds_domain_name,
+ &domain_name, mode)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
+ "domain_name failed (%d)", __func__, rv);
+ return (EFAULT);
+ } else if (servicep == NULL) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL vlds_get_string "
+ " domain_name is NULL", __func__);
+ return (EINVAL);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL (%s) entered", __func__,
+ domain_name);
+
+ if ((rv = ds_dom_name_to_hdl(domain_name, &dhdl)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL name: '%s' "
+ "failed: (%d)", __func__, domain_name, rv);
+ DS_FREE(domain_name, strlen(domain_name) + 1);
+ return (rv);
+ }
+
+ dhdl_arg = dhdl;
+ if (ddi_copyout(&dhdl_arg, ARGTOPTR(vlds_arg.vlds_dhdlp),
+ sizeof (dhdl_arg), mode) != 0) {
+ DS_FREE(domain_name, strlen(domain_name) + 1);
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL copyout of dhdl "
+ " failed", __func__);
+ return (EFAULT);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_NAM2HDL succeeded: name: '%s', "
+ "dhdl: 0x%lx", __func__, domain_name, dhdl);
+ DS_FREE(domain_name, strlen(domain_name) + 1);
+ break;
+ }
+
+ case VLDS_DOM_HDL2NAM:
+ {
+ vlds_dom_hdl2nam_arg_t vlds_arg;
+ ds_domain_hdl_t dhdl;
+ char *domain_name;
+
+ if (ddi_copyin((void *)arg, &vlds_arg, sizeof (vlds_arg),
+ mode) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM arg copyin "
+ "failed", __func__);
+ return (EFAULT);
+ }
+
+ dhdl = vlds_arg.vlds_dhdl;
+ if ((rv = ds_dom_hdl_to_name(hdl, &domain_name)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM lookup dhdl: %lx "
+ "failed (%d)", __func__, dhdl, rv);
+ return (rv);
+ }
+
+ if ((rv = vlds_put_string(domain_name,
+ &vlds_arg.vlds_domain_name, mode)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM vlds_put_string "
+ "'%s' failed (%d)", __func__, domain_name, rv);
+ return (rv);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: DOM_HDL2NAM dhdl: 0x%lx name: '%s'",
+ __func__, dhdl, domain_name);
+ break;
+ }
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static uint_t
+vlds_flags_to_svc(uint64_t flags)
+{
+ uint_t sflags = 0;
+
+ if (flags & VLDS_REG_CLIENT)
+ sflags |= DSSF_ISCLIENT;
+ if (flags & VLDS_REGCB_VALID)
+ sflags |= DSSF_REGCB_VALID;
+ if (flags & VLDS_UNREGCB_VALID)
+ sflags |= DSSF_UNREGCB_VALID;
+ if (flags & VLDS_DATACB_VALID)
+ sflags |= DSSF_DATACB_VALID;
+ return (sflags);
+}
+
+/*
+ * MD registration code.
+ * Placed in vlds rather than ds module due to cirular dependency of
+ * platsvc module which contains the mdeg code.
+ */
+mdeg_handle_t ds_mdeg_hdl;
+
+/*
+ * There's only one domain services node, so we don't
+ * need to specify any match conditions. However, we
+ * have to supply a non-NULL property spec.
+ */
+static mdeg_prop_spec_t ds_prop_template[] = {
+ { MDET_LIST_END, NULL, NULL }
+};
+
+static mdeg_node_spec_t ds_node_template =
+ { VLDS_MD_ROOT_NODE_NAME, ds_prop_template };
+
+/*
+ * Matching criteria passed to the MDEG to register interest
+ * in changes to domain services port nodes identified by their
+ * 'id' property.
+ */
+static md_prop_match_t ds_port_prop_match[] = {
+ { MDET_PROP_VAL, "id" },
+ { MDET_LIST_END, NULL }
+};
+
+static mdeg_node_match_t ds_port_match = { VLDS_MD_PORT_NODE_NAME,
+ ds_port_prop_match };
+
+/* mdeg callback */
+static int
+ds_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
+{
+ _NOTE(ARGUNUSED(cb_argp))
+ int idx;
+ uint64_t portno;
+ int rv;
+ md_t *mdp;
+ mde_cookie_t node;
+
+ if (resp == NULL) {
+ DS_DBG_VLDS(CE_NOTE, "ds_mdeg_cb: no result returned");
+ return (MDEG_FAILURE);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: added=%d, removed=%d, matched=%d", __func__,
+ resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
+
+ /* process added ports */
+ for (idx = 0; idx < resp->added.nelem; idx++) {
+ mdp = resp->added.mdp;
+ node = resp->added.mdep[idx];
+
+ DS_DBG_VLDS(CE_NOTE, "%s: processing added node 0x%lx",
+ __func__, node);
+
+ /* attempt to add a port */
+ if ((rv = ds_add_mdeg_port(mdp, node)) != MDEG_SUCCESS) {
+ if (vlds_ports_inited) {
+ cmn_err(CE_NOTE, "%s: unable to add port, "
+ "err = %d", __func__, rv);
+ }
+ }
+ }
+
+ /* process removed ports */
+ for (idx = 0; idx < resp->removed.nelem; idx++) {
+ mdp = resp->removed.mdp;
+ node = resp->removed.mdep[idx];
+
+ DS_DBG_VLDS(CE_NOTE, "%s: processing removed node 0x%lx",
+ __func__, node);
+
+ /* read in the port's id property */
+ if (md_get_prop_val(mdp, node, "id", &portno)) {
+ cmn_err(CE_NOTE, "%s: node 0x%lx of removed list "
+ "has no 'id' property", __func__, node);
+ continue;
+ }
+
+ /* attempt to remove a port */
+ if ((rv = ds_remove_port(portno, 0)) != 0) {
+ cmn_err(CE_NOTE, "%s: unable to remove port %lu, "
+ " err %d", __func__, portno, rv);
+ }
+ }
+
+ vlds_ports_inited = 1;
+
+ return (MDEG_SUCCESS);
+}
+
+/* register callback to mdeg */
+static int
+ds_mdeg_register(void)
+{
+ int rv;
+
+ DS_DBG_VLDS(CE_NOTE, "ds_mdeg_register: entered");
+
+ /* perform the registration */
+ rv = mdeg_register(&ds_node_template, &ds_port_match, ds_mdeg_cb,
+ NULL, &ds_mdeg_hdl);
+
+ if (rv != MDEG_SUCCESS) {
+ cmn_err(CE_NOTE, "ds_mdeg_register: mdeg_register "
+ "failed, err = %d", rv);
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/* unregister callback from mdeg */
+static int
+ds_mdeg_unregister(void)
+{
+ DS_DBG_VLDS(CE_NOTE, "ds_mdeg_unregister: hdl=0x%lx", ds_mdeg_hdl);
+
+ return (mdeg_unregister(ds_mdeg_hdl));
+}
+
+static int
+ds_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
+{
+ int num_nodes, nchan;
+ size_t listsz;
+ mde_cookie_t *listp;
+
+ /*
+ * Find the channel-endpoint node(s) (which should be under this
+ * port node) which contain the channel id(s).
+ */
+ if ((num_nodes = md_node_count(mdp)) <= 0) {
+ cmn_err(CE_NOTE, "%s: invalid number of channel-endpoint nodes "
+ "found (%d)", __func__, num_nodes);
+ return (-1);
+ }
+
+ /* allocate space for node list */
+ listsz = num_nodes * sizeof (mde_cookie_t);
+ listp = kmem_alloc(listsz, KM_SLEEP);
+
+ nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
+ md_find_name(mdp, "fwd"), listp);
+
+ if (nchan <= 0) {
+ cmn_err(CE_NOTE, "%s: no channel-endpoint nodes found",
+ __func__);
+ kmem_free(listp, listsz);
+ return (-1);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "%s: %d channel-endpoint nodes found", __func__,
+ nchan);
+
+ /* use property from first node found */
+ if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
+ cmn_err(CE_NOTE, "%s: channel-endpoint has no 'id' property",
+ __func__);
+ kmem_free(listp, listsz);
+ return (-1);
+ }
+
+ kmem_free(listp, listsz);
+
+ return (0);
+}
+
+/* add a DS services port */
+static int
+ds_add_mdeg_port(md_t *mdp, mde_cookie_t node)
+{
+ uint64_t portno;
+ uint64_t ldc_id;
+ int rv;
+ uint64_t dhdl;
+ char *dom_name;
+
+ /* read in the port's id property */
+ if (md_get_prop_val(mdp, node, "id", &portno)) {
+ cmn_err(CE_NOTE, "%s: node 0x%lx of added list has no "
+ "'id' property", __func__, node);
+ return (MDEG_FAILURE);
+ }
+
+ if (portno >= DS_MAX_PORTS) {
+ cmn_err(CE_NOTE, "%s: found port number (%lu) "
+ "larger than maximum supported number of ports", __func__,
+ portno);
+ return (MDEG_FAILURE);
+ }
+
+ /* get all channels for this device (currently only one) */
+ if (ds_get_port_channel(mdp, node, &ldc_id) == -1) {
+ return (MDEG_FAILURE);
+ }
+
+ if (md_get_prop_val(mdp, node, "remote-domain-id", &dhdl) != 0) {
+ dhdl = DS_DHDL_INVALID;
+ }
+
+ if (md_get_prop_str(mdp, node, "remote-domain-name", &dom_name) != 0) {
+ dom_name = NULL;
+ }
+
+ rv = ds_add_port(portno, ldc_id, dhdl, dom_name, vlds_ports_inited);
+
+ if (rv != 0) {
+ if (vlds_ports_inited) {
+ DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx "
+ "failed err = %d", portno, __func__, ldc_id, rv);
+ }
+ return (MDEG_FAILURE);
+ }
+
+ DS_DBG_VLDS(CE_NOTE, "ds%lx: %s LDC chan: %lx inited", portno,
+ __func__, ldc_id);
+
+ return (MDEG_SUCCESS);
+}
+
+static void
+vlds_user_reg_cb(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
+{
+ nvlist_t *nvl = NULL;
+ ds_domain_hdl_t dhdl;
+ char *servicep;
+ uint32_t flags;
+ int minor;
+ vlds_state_t *sp;
+ vlds_svc_info_t *dpsp;
+
+ ds_cbarg_get_flags(arg, &flags);
+ ASSERT((flags & DSSF_ISUSER) != 0);
+
+ if ((flags & DSSF_DATACB_VALID) == 0) {
+ /*
+ * must allocate and init the svc read queue.
+ */
+ DS_DBG_VLDS(CE_NOTE, "%s: hdl: 0x%lx initing recvq", __func__,
+ hdl);
+ dpsp = DS_MALLOC(sizeof (vlds_svc_info_t));
+ vlds_recvq_init(dpsp);
+ ds_cbarg_set_drv_per_svc_ptr(arg, dpsp);
+ }
+
+ if ((flags & DSSF_REGCB_VALID) != 0) {
+ ds_cbarg_get_drv_info(arg, &minor);
+ sp = ddi_get_soft_state(vlds_statep, minor);
+ ASSERT(sp != NULL);
+ ASSERT(sp->evchan != NULL);
+ ds_cbarg_get_domain(arg, &dhdl);
+ ds_cbarg_get_service_id(arg, &servicep);
+ DS_DBG_VLDS(CE_NOTE, "%s: regcb: hdl: 0x%lx, ver%d.%d, "
+ " dhdl: 0x%lx", __func__, hdl, ver->major,
+ ver->minor, dhdl);
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
+ nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
+ nvlist_add_uint16(nvl, VLDS_VER_MAJOR, ver->major) ||
+ nvlist_add_uint16(nvl, VLDS_VER_MINOR, ver->minor) ||
+ nvlist_add_uint64(nvl, VLDS_DOMAIN_HDL, dhdl) ||
+ nvlist_add_string(nvl, VLDS_SERVICE_ID, servicep) ||
+ nvlist_add_boolean_value(nvl, VLDS_ISCLIENT,
+ (flags & DSSF_ISCLIENT) != 0) ||
+ sysevent_evc_publish(sp->evchan, EC_VLDS,
+ ESC_VLDS_REGISTER, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
+ cmn_err(CE_WARN, "Failed to send REG Callback");
+ } else {
+ DS_DBG_VLDS(CE_NOTE, "%s: sysevent_evc_publish "
+ "succeeded", __func__);
+ }
+ nvlist_free(nvl);
+ }
+}
+
+static void
+vlds_user_unreg_cb(ds_cb_arg_t arg)
+{
+ nvlist_t *nvl = NULL;
+ int minor;
+ ds_svc_hdl_t hdl;
+ vlds_state_t *sp;
+ void *dpsp;
+ uint32_t flags;
+
+ ds_cbarg_get_flags(arg, &flags);
+ ASSERT((flags & DSSF_ISUSER) != 0);
+
+ if ((flags & DSSF_DATACB_VALID) == 0) {
+ ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
+ if (dpsp) {
+ DS_DBG_VLDS(CE_NOTE, "%s: unregcb draining recvq",
+ __func__);
+ vlds_recvq_drain(dpsp);
+ vlds_recvq_destroy(dpsp);
+ ds_cbarg_set_drv_per_svc_ptr(arg, NULL);
+ }
+ }
+
+ if ((flags & DSSF_UNREGCB_VALID) != 0) {
+ ds_cbarg_get_hdl(arg, &hdl);
+ DS_DBG_VLDS(CE_NOTE, "%s: unregcb hdl: 0x%lx", __func__,
+ hdl);
+ ds_cbarg_get_drv_info(arg, &minor);
+ sp = ddi_get_soft_state(vlds_statep, minor);
+ ASSERT(sp != NULL);
+ ASSERT(sp->evchan != NULL);
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
+ nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
+ sysevent_evc_publish(sp->evchan, EC_VLDS,
+ ESC_VLDS_UNREGISTER, "sun.com", "kernel", nvl,
+ EVCH_SLEEP)) {
+ cmn_err(CE_WARN, "Failed to send UNREG Callback");
+ }
+ nvlist_free(nvl);
+ }
+}
+
+static void
+vlds_user_data_cb(ds_cb_arg_t arg, void *buf, size_t buflen)
+{
+ nvlist_t *nvl = NULL;
+ ds_svc_hdl_t hdl;
+ int minor;
+ void *dpsp;
+ vlds_state_t *sp;
+ uint32_t flags;
+
+ ds_cbarg_get_flags(arg, &flags);
+ ASSERT((flags & DSSF_ISUSER) != 0);
+
+ if ((flags & DSSF_DATACB_VALID) == 0) {
+ ds_cbarg_get_drv_per_svc_ptr(arg, &dpsp);
+ ASSERT(dpsp != NULL);
+ DS_DBG_VLDS(CE_NOTE, "%s: datacb: to recvq: buflen: %ld",
+ __func__, buflen);
+ (void) vlds_recvq_put_data(dpsp, buf, buflen);
+ } else {
+ ds_cbarg_get_hdl(arg, &hdl);
+ DS_DBG_VLDS(CE_NOTE, "%s: datacb: usercb: hdl: 0x%lx, "
+ " buflen: %ld", __func__, hdl, buflen);
+ ds_cbarg_get_drv_info(arg, &minor);
+ sp = ddi_get_soft_state(vlds_statep, minor);
+ ASSERT(sp != NULL);
+ ASSERT(sp->evchan != NULL);
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
+ nvlist_add_uint64(nvl, VLDS_HDL, hdl) ||
+ nvlist_add_byte_array(nvl, VLDS_DATA, buf, buflen) ||
+ sysevent_evc_publish(sp->evchan, EC_VLDS,
+ ESC_VLDS_DATA, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
+ cmn_err(CE_WARN, "Failed to send DATA Callback");
+ }
+ }
+ nvlist_free(nvl);
+}
+
+/*
+ * Initialize receive queue if request is from user land but
+ * data callback is null (implying user will be using ds_recv_msg).
+ */
+static void
+vlds_recvq_init(vlds_svc_info_t *dpsp)
+{
+ dpsp->state = VLDS_RECV_OK;
+ mutex_init(&dpsp->recv_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&dpsp->recv_cv, NULL, CV_DRIVER, NULL);
+ dpsp->recv_headp = NULL;
+ dpsp->recv_tailp = NULL;
+ dpsp->recv_size = 0;
+}
+
+static void
+vlds_recvq_destroy(vlds_svc_info_t *dpsp)
+{
+ ASSERT(dpsp->state == VLDS_RECV_UNREG_PENDING);
+ ASSERT(dpsp->recv_size == 0);
+ ASSERT(dpsp->recv_headp == NULL);
+ ASSERT(dpsp->recv_tailp == NULL);
+
+ mutex_destroy(&dpsp->recv_lock);
+ cv_destroy(&dpsp->recv_cv);
+ DS_FREE(dpsp, sizeof (vlds_svc_info_t));
+}
+
+static int
+vlds_recvq_get_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen,
+ size_t *msglenp, int mode)
+{
+ vlds_recv_hdr_t *rhp;
+ int rv;
+ size_t msglen;
+
+ mutex_enter(&dpsp->recv_lock);
+ while (dpsp->recv_size == 0) {
+ if (dpsp->state == VLDS_RECV_UNREG_PENDING)
+ break;
+ /*
+ * Passing in a buflen of 0 allows user to poll for msgs.
+ */
+ if (buflen == 0) {
+ mutex_exit(&dpsp->recv_lock);
+ *msglenp = 0;
+ return (EFBIG);
+ }
+ dpsp->recv_nreaders += 1;
+ rv = cv_wait_sig(&dpsp->recv_cv, &dpsp->recv_lock);
+ dpsp->recv_nreaders -= 1;
+ if (rv == 0) {
+ DS_DBG_RCVQ(CE_NOTE, "%s: signal EINTR", __func__);
+ mutex_exit(&dpsp->recv_lock);
+ return (EINTR);
+ }
+ }
+ if (dpsp->state == VLDS_RECV_UNREG_PENDING) {
+ DS_DBG_RCVQ(CE_NOTE, "%s: unreg pending", __func__);
+ cv_broadcast(&dpsp->recv_cv);
+ mutex_exit(&dpsp->recv_lock);
+ return (EINVAL);
+ }
+ ASSERT(dpsp->recv_headp != NULL);
+ rhp = dpsp->recv_headp;
+
+ /*
+ * Don't transfer truncated data, return EFBIG error if user-supplied
+ * buffer is too small.
+ */
+ if (rhp->datasz > buflen) {
+ *msglenp = rhp->datasz;
+ mutex_exit(&dpsp->recv_lock);
+ return (EFBIG);
+ }
+ if (rhp == dpsp->recv_tailp) {
+ dpsp->recv_headp = NULL;
+ dpsp->recv_tailp = NULL;
+ } else {
+ dpsp->recv_headp = rhp->next;
+ ASSERT(dpsp->recv_headp != NULL);
+ }
+ dpsp->recv_size -= rhp->datasz;
+ mutex_exit(&dpsp->recv_lock);
+
+ msglen = rhp->datasz;
+ rv = ddi_copyout(rhp->data, buf, msglen, mode);
+
+ if (rv == 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: user data dequeued msglen: %ld",
+ __func__, rhp->datasz);
+ DS_DUMP_MSG(DS_DBG_FLAG_VLDS, rhp->data, rhp->datasz);
+ }
+
+ DS_FREE(rhp->data, rhp->datasz);
+ DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
+
+ if (rv != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: copyout failed", __func__);
+ return (EFAULT);
+ }
+
+ *msglenp = msglen;
+ return (0);
+}
+
+uint64_t vlds_recv_drain_delay_time = 1 * MILLISEC;
+
+static void
+vlds_recvq_drain(vlds_svc_info_t *dpsp)
+{
+ vlds_recv_hdr_t *rhp, *nextp;
+
+ mutex_enter(&dpsp->recv_lock);
+ dpsp->state = VLDS_RECV_UNREG_PENDING;
+ for (rhp = dpsp->recv_tailp; rhp != NULL; rhp = nextp) {
+ nextp = rhp->next;
+ DS_FREE(rhp->data, rhp->datasz);
+ DS_FREE(rhp, sizeof (vlds_recv_hdr_t));
+ }
+ dpsp->recv_headp = NULL;
+ dpsp->recv_tailp = NULL;
+ dpsp->recv_size = 0;
+
+ /*
+ * Make sure other readers have exited.
+ */
+ while (dpsp->recv_nreaders > 0) {
+ cv_broadcast(&dpsp->recv_cv);
+ mutex_exit(&dpsp->recv_lock);
+ delay(vlds_recv_drain_delay_time);
+ mutex_enter(&dpsp->recv_lock);
+ }
+
+ mutex_exit(&dpsp->recv_lock);
+}
+
+static int
+vlds_recvq_put_data(vlds_svc_info_t *dpsp, void *buf, size_t buflen)
+{
+ vlds_recv_hdr_t *rhp;
+
+ mutex_enter(&dpsp->recv_lock);
+ if (dpsp->state != VLDS_RECV_UNREG_PENDING) {
+ DS_DBG_RCVQ(CE_NOTE, "%s: user data enqueued msglen: %ld",
+ __func__, buflen);
+ DS_DUMP_MSG(DS_DBG_FLAG_RCVQ, buf, buflen);
+ rhp = DS_MALLOC(sizeof (vlds_recv_hdr_t));
+ rhp->data = DS_MALLOC(buflen);
+ (void) memcpy(rhp->data, buf, buflen);
+ rhp->datasz = buflen;
+ rhp->next = NULL;
+ if (dpsp->recv_headp == NULL) {
+ dpsp->recv_headp = rhp;
+ dpsp->recv_tailp = rhp;
+ } else {
+ dpsp->recv_tailp->next = rhp;
+ dpsp->recv_tailp = rhp;
+ }
+ dpsp->recv_size += rhp->datasz;
+ cv_broadcast(&dpsp->recv_cv);
+ }
+ mutex_exit(&dpsp->recv_lock);
+ return (0);
+}
+
+static int
+vlds_recv_msg(ds_svc_hdl_t hdl, void *buf, size_t buflen, size_t *msglenp,
+ int mode)
+{
+ void *dpsp;
+ ds_cb_arg_t cbarg;
+ uint32_t flags;
+ int rv;
+
+ if ((rv = ds_hdl_get_cbarg(hdl, &cbarg)) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: handle %lx not found (%d)", __func__,
+ hdl, rv);
+ return (rv);
+ }
+ ds_cbarg_get_flags(cbarg, &flags);
+ if ((flags & DSSF_ISUSER) == 0 || (flags & DSSF_DATACB_VALID) != 0) {
+ DS_DBG_VLDS(CE_NOTE, "%s: invalid flags: %x", __func__, flags);
+ return (EINVAL);
+ }
+ ds_cbarg_get_drv_per_svc_ptr(cbarg, &dpsp);
+ if (dpsp == NULL) {
+ DS_DBG_VLDS(CE_NOTE, "%s: recv on non-ready handle: %x",
+ __func__, flags);
+ return (ENXIO);
+ }
+ rv = vlds_recvq_get_data(dpsp, buf, buflen, msglenp, mode);
+ return (rv);
+}
diff --git a/usr/src/uts/sun4v/sys/Makefile b/usr/src/uts/sun4v/sys/Makefile
index 87babddd2c..e08bd67f5a 100644
--- a/usr/src/uts/sun4v/sys/Makefile
+++ b/usr/src/uts/sun4v/sys/Makefile
@@ -19,10 +19,9 @@
# CDDL HEADER END
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
#
# uts/sun4v/sys/Makefile
#
@@ -91,7 +90,8 @@ HDRS= \
prom_plat.h \
qcn.h \
soft_state.h \
- traptrace.h
+ traptrace.h \
+ vlds.h
CLOSED_HDRS= \
memtestio_ni.h \
diff --git a/usr/src/uts/sun4v/sys/ds.h b/usr/src/uts/sun4v/sys/ds.h
index cd5efa807f..e5f479638c 100644
--- a/usr/src/uts/sun4v/sys/ds.h
+++ b/usr/src/uts/sun4v/sys/ds.h
@@ -20,14 +20,13 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _DS_H
#define _DS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Domain Services Client Interface
diff --git a/usr/src/uts/sun4v/sys/ds_impl.h b/usr/src/uts/sun4v/sys/ds_impl.h
index 101918b903..8d0e68fbb3 100644
--- a/usr/src/uts/sun4v/sys/ds_impl.h
+++ b/usr/src/uts/sun4v/sys/ds_impl.h
@@ -20,19 +20,19 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _DS_IMPL_H
#define _DS_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
+
+
/*
* The Domain Services Protocol
*
@@ -56,7 +56,7 @@ typedef struct ds_hdr {
* DS Fixed Message Types
*/
#define DS_INIT_REQ 0x0 /* initiate DS connection */
-#define DS_INIT_ACK 0x1 /* initiation acknowledgment */
+#define DS_INIT_ACK 0x1 /* initiation acknowledgement */
#define DS_INIT_NACK 0x2 /* initiation negative acknowledgment */
/*
@@ -79,10 +79,10 @@ typedef struct ds_init_nack {
* DS Message Types for Version 1.0
*/
#define DS_REG_REQ 0x3 /* register a service */
-#define DS_REG_ACK 0x4 /* register acknowledgment */
+#define DS_REG_ACK 0x4 /* register acknowledgement */
#define DS_REG_NACK 0x5 /* register failed */
#define DS_UNREG 0x6 /* unregister a service */
-#define DS_UNREG_ACK 0x7 /* unregister acknowledgment */
+#define DS_UNREG_ACK 0x7 /* unregister acknowledgement */
#define DS_UNREG_NACK 0x8 /* unregister failed */
#define DS_DATA 0x9 /* data message */
#define DS_NACK 0xa /* data error */
@@ -176,15 +176,32 @@ typedef struct ds_ldc {
ldc_status_t state; /* current LDC state */
} ds_ldc_t;
+typedef uint64_t ds_domain_hdl_t;
+
+#define DS_DHDL_INVALID ((ds_domain_hdl_t)0xffffffff)
+
+/* port flags */
+#define DS_PORT_MUTEX_INITED 0x1 /* mutexes inited? */
+
typedef struct ds_port {
- kmutex_t lock; /* port lock */
+ uint32_t flags; /* port flags */
+ kmutex_t lock; /* port and service state lock */
+ kmutex_t tx_lock; /* tx port lock */
+ kmutex_t rcv_lock; /* rcv port lock */
uint64_t id; /* port id from MD */
ds_port_state_t state; /* state of the port */
ds_ver_t ver; /* DS protocol version in use */
uint32_t ver_idx; /* index of version during handshake */
ds_ldc_t ldc; /* LDC for this port */
+ ds_domain_hdl_t domain_hdl; /* LDOMs domain hdl assoc. with port */
+ char *domain_name; /* LDOMs domain name assoc. with port */
} ds_port_t;
+#define IS_DS_PORT(port) 1 /* VBSC code compatability */
+#define PORTID(port) ((ulong_t)((port)->id))
+#define PTR_TO_LONG(ptr) ((uint64_t)(ptr))
+
+
/*
* A DS portset is a bitmap that represents a collection of DS
* ports. Each bit represent a particular port id. The current
@@ -192,7 +209,9 @@ typedef struct ds_port {
*/
typedef uint64_t ds_portset_t;
+#ifndef DS_MAX_PORTS
#define DS_MAX_PORTS ((sizeof (ds_portset_t)) * 8)
+#endif
#define DS_MAX_PORT_ID (DS_MAX_PORTS - 1)
#define DS_PORT_SET(port) (1UL << (port))
@@ -200,12 +219,25 @@ typedef uint64_t ds_portset_t;
#define DS_PORTSET_ADD(set, port) ((void)((set) |= DS_PORT_SET(port)))
#define DS_PORTSET_DEL(set, port) ((void)((set) &= ~DS_PORT_SET(port)))
#define DS_PORTSET_ISNULL(set) ((set) == 0)
+#define DS_PORTSET_SETNULL(set) ((void)((set) = 0))
#define DS_PORTSET_DUP(set1, set2) ((void)((set1) = (set2)))
+#define DS_PORTSET_NOT(set1, set2) ((void)((set1) = ~(set2)))
+#define DS_PORTSET_AND(set1, set2) ((void)((set1) &= (set2)))
+
+/*
+ * A DS event consists of a buffer on a port. We explictly use a link to
+ * enequeue/dequeue on non-Solaris environments. On Solaris we use taskq.
+ */
+typedef struct ds_event {
+ ds_port_t *port;
+ char *buf;
+ size_t buflen;
+} ds_event_t;
/*
* LDC Information
*/
-#define DS_STREAM_MTU 4096
+#define DS_STREAM_MTU 4096
/*
* Machine Description Constants
@@ -229,36 +261,75 @@ typedef enum {
DS_SVC_FREE, /* svc structure not in use */
DS_SVC_INACTIVE, /* svc not registered */
DS_SVC_REG_PENDING, /* register message sent */
- DS_SVC_ACTIVE /* register message acknowledged */
+ DS_SVC_ACTIVE, /* register message acknowledged */
+ DS_SVC_UNREG_PENDING /* unregister is pending */
} ds_svc_state_t;
+/* ds_svc flags bits */
+#define DSSF_ISCLIENT 0x0001 /* client service */
+#define DSSF_ISUSER 0x0002 /* user land service */
+#define DSSF_REGCB_VALID 0x0004 /* ops register callback is valid */
+#define DSSF_UNREGCB_VALID 0x0008 /* ops unregister callback is valid */
+#define DSSF_DATACB_VALID 0x0010 /* ops data callback is valid */
+#define DSSF_LOOPBACK 0x0020 /* loopback */
+#define DSSF_PEND_UNREG 0x0040 /* pending unregister */
+#define DSSF_ANYCB_VALID (DSSF_REGCB_VALID | DSSF_UNREGCB_VALID | \
+ DSSF_DATACB_VALID)
+#define DSSF_USERFLAGS (DSSF_ISCLIENT | DSSF_ISUSER | DSSF_ANYCB_VALID)
+
typedef struct ds_svc {
ds_capability_t cap; /* capability information */
ds_clnt_ops_t ops; /* client ops vector */
ds_svc_hdl_t hdl; /* handle assigned by DS */
+ ds_svc_hdl_t svc_hdl; /* remote svc hdl if client svc */
ds_svc_state_t state; /* current service state */
ds_ver_t ver; /* svc protocol version in use */
uint_t ver_idx; /* index into client version array */
ds_port_t *port; /* port for this service */
ds_portset_t avail; /* ports available to this service */
+ ds_portset_t tried; /* ports tried by this service */
+ int fixed; /* is svc fixed to port */
+ uint_t flags; /* service flags */
+ ds_cb_arg_t uarg; /* user arg for user callbacks */
+ uint_t drvi; /* driver instance */
+ void *drv_psp; /* driver per svc ptr */
} ds_svc_t;
+typedef struct ds_svcs {
+ ds_svc_t **tbl; /* ptr to table */
+ kmutex_t lock;
+ uint_t maxsvcs; /* size of the table */
+ uint_t nsvcs; /* current number of items */
+} ds_svcs_t;
+
#define DS_SVC_ISFREE(svc) ((svc == NULL) || (svc->state == DS_SVC_FREE))
+#ifndef DS_MAXSVCS_INIT
+#define DS_MAXSVCS_INIT 32
+#endif
/*
- * A service handle is a 64 bit value with two pieces of information
+ * A service handle is a 64 bit value with three pieces of information
* encoded in it. The upper 32 bits is the index into the table of
- * a particular service structure. The lower 32 bits is a counter
- * that is incremented each time a service structure is reused.
+ * a particular service structure. Bit 31 indicates whether the handle
+ * represents a service privider or service client. The lower 31 bits is
+ * a counter that is incremented each time a service structure is reused.
*/
#define DS_IDX_SHIFT 32
-#define DS_COUNT_MASK 0xfffffffful
+#define DS_COUNT_MASK 0x7fffffffull
+#define DS_HDL_ISCLIENT_BIT 0x80000000ull
#define DS_ALLOC_HDL(_idx, _count) (((uint64_t)_idx << DS_IDX_SHIFT) | \
((uint64_t)(_count + 1) & \
DS_COUNT_MASK))
#define DS_HDL2IDX(hdl) (hdl >> DS_IDX_SHIFT)
#define DS_HDL2COUNT(hdl) (hdl & DS_COUNT_MASK)
+#define DS_HDL_ISCLIENT(hdl) ((hdl) & DS_HDL_ISCLIENT_BIT)
+#define DS_HDL_SET_ISCLIENT(hdl) ((hdl) |= DS_HDL_ISCLIENT_BIT)
+
+#define DS_INVALID_INSTANCE (-1)
+
+/* enable/disable taskq processing */
+extern boolean_t ds_enabled;
/*
* DS Message Logging
@@ -326,6 +397,158 @@ typedef struct ds_log_entry {
#define DS_IS_POOL_ENTRY(ep) (((ep) >= ds_log_entry_pool) && \
((ep) <= &(ds_log_entry_pool[DS_LOG_NPOOL])))
+/* VBSC code compatability related defines */
+
+/* VBSC malloc/free are similar to user malloc/free */
+#define DS_MALLOC(size) kmem_zalloc(size, KM_SLEEP)
+#define DS_FREE(ptr, size) kmem_free(ptr, size)
+
+/* VBSC debug print needs newline, Solaris cmn_err doesn't */
+#define DS_EOL
+
+/*
+ * Results of checking version array with ds_vers_isvalid()
+ */
+typedef enum {
+ DS_VERS_OK,
+ DS_VERS_INCREASING_MAJOR_ERR,
+ DS_VERS_INCREASING_MINOR_ERR
+} ds_vers_check_t;
+
+/* System specific interfaces */
+extern void ds_sys_port_init(ds_port_t *port);
+extern void ds_sys_port_fini(ds_port_t *port);
+extern void ds_sys_drain_events(ds_port_t *port);
+extern int ds_sys_dispatch_func(void (func)(void *), void *arg);
+extern void ds_sys_ldc_init(ds_port_t *port);
+
+/* vlds cb access to svc structure */
+void ds_cbarg_get_hdl(ds_cb_arg_t arg, ds_svc_hdl_t *hdlp);
+void ds_cbarg_get_flags(ds_cb_arg_t arg, uint32_t *flagsp);
+void ds_cbarg_get_drv_info(ds_cb_arg_t arg, int *drvip);
+void ds_cbarg_get_drv_per_svc_ptr(ds_cb_arg_t arg, void **dpspp);
+void ds_cbarg_get_domain(ds_cb_arg_t arg, ds_domain_hdl_t *dhdlp);
+void ds_cbarg_get_service_id(ds_cb_arg_t arg, char **servicep);
+void ds_cbarg_set_drv_per_svc_ptr(ds_cb_arg_t arg, void *dpsp);
+int ds_hdl_get_cbarg(ds_svc_hdl_t hdl, ds_cb_arg_t *cbargp);
+void ds_cbarg_set_cookie(ds_svc_t *svc);
+int ds_is_my_hdl(ds_svc_hdl_t hdl, int instance);
+
+/* initialization functions */
+void ds_common_init(void);
+int ds_ldc_fini(ds_port_t *port);
+void ds_init_svcs_tbl(uint_t nentries);
+
+/* message sending functions */
+void ds_send_init_req(ds_port_t *port);
+int ds_send_unreg_req(ds_svc_t *svc);
+
+/* walker functions */
+typedef int (*svc_cb_t)(ds_svc_t *svc, void *arg);
+int ds_walk_svcs(svc_cb_t svc_cb, void *arg);
+int ds_svc_ismatch(ds_svc_t *svc, void *arg);
+int ds_svc_free(ds_svc_t *svc, void *arg);
+int ds_svc_register(ds_svc_t *svc, void *arg);
+
+/* service utilities */
+ds_svc_t *ds_alloc_svc(void);
+ds_svc_t *ds_sys_find_svc_by_id_port(char *svc_id, ds_port_t *port,
+ int is_client);
+ds_svc_t *ds_get_svc(ds_svc_hdl_t hdl);
+
+/* port utilities */
+void ds_port_common_init(ds_port_t *port);
+void ds_port_common_fini(ds_port_t *port, int is_fini);
+
+/* misc utilities */
+ds_vers_check_t ds_vers_isvalid(ds_ver_t *vers, int nvers);
+char *ds_errno_to_str(int ds_errno, char *ebuf);
+char *ds_strdup(char *str);
+boolean_t negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
+ uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp);
+
+/* log functions */
+int ds_log_add_msg(int32_t dest, uint8_t *msg, size_t sz);
+
+/* vlds driver interfaces to ds module */
+int ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint_t flags,
+ int instance, ds_svc_hdl_t *hdlp);
+int ds_unreg_hdl(ds_svc_hdl_t hdl);
+int ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
+ uint_t maxhdls, uint_t *nhdlsp);
+int ds_service_lookup(ds_svc_hdl_t hdl, char **servicep, uint_t *is_client);
+int ds_domain_lookup(ds_svc_hdl_t hdl, ds_domain_hdl_t *dhdlp);
+int ds_hdl_isready(ds_svc_hdl_t hdl, uint_t *is_ready);
+void ds_unreg_all(int instance);
+int ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp);
+int ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char **domain_namep);
+int ds_add_port(uint64_t port_id, uint64_t ldc_id, ds_domain_hdl_t dhdl,
+ char *dom_name, int verbose);
+int ds_remove_port(uint64_t portid, int is_fini);
+
+/* ds_ucap_init flags */
+#define DS_UCAP_CLNT 0x0 /* Service is Client */
+#define DS_UCAP_SVC 0x1 /* Service is Server */
+
+/*
+ * Error buffer size for ds_errno_to_str
+ */
+#define DS_EBUFSIZE 80
+
+/*
+ * Debugging Features
+ */
+#ifdef DEBUG
+
+#define DS_DBG_BASIC 0x001
+#define DS_DBG_FLAG_LDC 0x002
+#define DS_DBG_FLAG_LOG 0x004
+#define DS_DBG_DUMP_LDC_MSG 0x008
+#define DS_DBG_FLAG_MD 0x010
+#define DS_DBG_FLAG_USR 0x020
+#define DS_DBG_FLAG_VLDS 0x040
+#define DS_DBG_FLAG_PRCL 0x080
+#define DS_DBG_FLAG_RCVQ 0x100
+#define DS_DBG_FLAG_LOOP 0x200
+
+#define DS_DBG if (ds_debug & DS_DBG_BASIC) cmn_err
+#define DS_DBG_LDC if (ds_debug & DS_DBG_FLAG_LDC) cmn_err
+#define DS_DBG_LOG if (ds_debug & DS_DBG_FLAG_LOG) cmn_err
+#define DS_DBG_MD if (ds_debug & DS_DBG_FLAG_MD) cmn_err
+#define DS_DBG_USR if (ds_debug & DS_DBG_FLAG_USR) cmn_err
+#define DS_DBG_VLDS if (ds_debug & DS_DBG_FLAG_VLDS) cmn_err
+#define DS_DBG_PRCL if (ds_debug & DS_DBG_FLAG_PRCL) cmn_err
+#define DS_DBG_RCVQ if (ds_debug & DS_DBG_FLAG_RCVQ) cmn_err
+#define DS_DBG_LOOP if (ds_debug & DS_DBG_FLAG_LOOP) cmn_err
+
+#define DS_DUMP_MSG(flags, buf, len) if (ds_debug & (flags)) \
+ ds_dump_msg(buf, len)
+
+extern uint_t ds_debug;
+void ds_dump_msg(void *buf, size_t len);
+
+#define DS_BADHDL1 (ds_svc_hdl_t)(0xdeadbed1deadbed1ull)
+#define DS_BADHDL2 (ds_svc_hdl_t)(0x2deadbed2deadbedull)
+
+#else /* DEBUG */
+
+#define DS_DBG if (0) cmn_err
+#define DS_DBG_LDC DS_DBG
+#define DS_DBG_LOG DS_DBG
+#define DS_DBG_MD DS_DBG
+#define DS_DBG_USR DS_DBG
+#define DS_DBG_VLDS DS_DBG
+#define DS_DBG_PRCL DS_DBG
+#define DS_DBG_RCVQ DS_DBG
+#define DS_DBG_LOOP DS_DBG
+#define DS_DUMP_MSG(flags, buf, len)
+#define DS_DUMP_LDC_MSG(buf, len)
+
+#define DS_BADHDL1 NULL
+#define DS_BADHDL2 NULL
+
+#endif /* DEBUG */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/sun4v/sys/vlds.h b/usr/src/uts/sun4v/sys/vlds.h
new file mode 100644
index 0000000000..3ad53ca7d3
--- /dev/null
+++ b/usr/src/uts/sun4v/sys/vlds.h
@@ -0,0 +1,245 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_VLDS_H_
+#define _SYS_VLDS_H_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * LDOMS Domain Services Device Driver
+ */
+
+/*
+ * ioctl info for vlds device
+ */
+
+#define VLDSIOC ('d' << 16 | 's' << 8)
+
+#define VLDS_SVC_REG (VLDSIOC | 1) /* Register DS Service */
+#define VLDS_UNREG_HDL (VLDSIOC | 2) /* Unregister DS Service by Handle */
+#define VLDS_HDL_LOOKUP (VLDSIOC | 3) /* Lookup DS Handle(s) by Service id */
+#define VLDS_DMN_LOOKUP (VLDSIOC | 4) /* Lookup DS Domain id by Handle */
+#define VLDS_SEND_MSG (VLDSIOC | 5) /* Send DS Message by Handle */
+#define VLDS_RECV_MSG (VLDSIOC | 6) /* Receive DS Message by Handle */
+#define VLDS_HDL_ISREADY (VLDSIOC | 7) /* Handle ready for data transfers */
+#define VLDS_DOM_NAM2HDL (VLDSIOC | 8) /* Domain Name to Handle translation */
+#define VLDS_DOM_HDL2NAM (VLDSIOC | 9) /* Handle ready for data transfers */
+
+/* vlds_reg_flags */
+#define VLDS_REG_CLIENT 0x01 /* Register as client */
+#define VLDS_REGCB_VALID 0x02 /* User supplied Register callback */
+#define VLDS_UNREGCB_VALID 0x04 /* User supplied Unregister callback */
+#define VLDS_DATACB_VALID 0x08 /* User supplied Data callback */
+#define VLDS_ANYCB_VALID (VLDS_REGCB_VALID | VLDS_UNREGCB_VALID | \
+ VLDS_DATACB_VALID)
+
+#define VLDS_MAX_VERS 20 /* Max no. of vlds_ver_t entries */
+
+/*
+ * The following are declared so that they are size-invariant.
+ */
+
+/* String arguments to ioctl */
+typedef struct vlds_string_arg {
+ uint64_t vlds_strp;
+ uint64_t vlds_strlen;
+} vlds_string_t;
+
+/* Version array (used by VLDS_SVC_REG) */
+typedef struct vlds_ver {
+ uint16_t vlds_major;
+ uint16_t vlds_minor;
+} vlds_ver_t;
+
+/* Capability structure (used by VLDS_SVC_REG) */
+typedef struct vlds_cap {
+ vlds_string_t vlds_service;
+ uint64_t vlds_nver;
+ uint64_t vlds_versp;
+} vlds_cap_t;
+
+/*
+ * VLDS_SVC_REG
+ */
+typedef struct vlds_svc_reg_arg {
+ uint64_t vlds_hdlp; /* DS Service Handle ptr. (returned) */
+ uint64_t vlds_capp; /* DS Capability Structure ptr. */
+ uint64_t vlds_reg_flags; /* DS reg flags */
+} vlds_svc_reg_arg_t;
+
+/*
+ * VLDS_UNREG_HDL
+ */
+typedef struct vlds_unreg_hdl_arg {
+ uint64_t vlds_hdl; /* DS Service Handle */
+} vlds_unreg_hdl_arg_t;
+
+/*
+ * VLDS_DMN_LOOKUP
+ */
+typedef struct vlds_dmn_lookup_arg {
+ uint64_t vlds_hdl; /* DS Service Handle */
+ uint64_t vlds_dhdlp; /* DS Domain hdl ptr. (returned) */
+} vlds_dmn_lookup_arg_t;
+
+/*
+ * VLDS_HDL_LOOKUP
+ */
+typedef struct vlds_hdl_lookup_arg {
+ vlds_string_t vlds_service; /* DS Service Name */
+ uint64_t vlds_isclient; /* DS Client flag */
+ uint64_t vlds_hdlsp; /* DS Handle array ptr */
+ uint64_t vlds_maxhdls; /* DS Max no. of hdls to return */
+ uint64_t vlds_nhdlsp; /* DS No. of hdls returned */
+} vlds_hdl_lookup_arg_t;
+
+/*
+ * VLDS_SEND_MSG
+ */
+typedef struct vlds_send_msg_arg {
+ uint64_t vlds_hdl; /* DS Service Handle */
+ uint64_t vlds_bufp; /* buffer */
+ uint64_t vlds_buflen; /* message length/buffer size */
+} vlds_send_msg_arg_t;
+
+/*
+ * VLDS_RECV_MSG
+ */
+typedef struct vlds_recv_msg_arg {
+ uint64_t vlds_hdl; /* DS Service Handle */
+ uint64_t vlds_bufp; /* buffer */
+ uint64_t vlds_buflen; /* message length/buffer size */
+ uint64_t vlds_msglenp; /* ptr to returned message length */
+} vlds_recv_msg_arg_t;
+
+/*
+ * VLDS_HDL_ISREADY
+ */
+typedef struct vlds_hdl_isready_arg {
+ uint64_t vlds_hdl; /* DS Service Handle */
+ uint64_t vlds_isreadyp; /* Ptr to isready flag */
+} vlds_hdl_isready_arg_t;
+
+/*
+ * VLDS_DOM_NAM2HDL
+ */
+typedef struct vlds_dom_nam2hdl_arg {
+ vlds_string_t vlds_domain_name; /* Domain Name string */
+ uint64_t vlds_dhdlp; /* ptr to returned Domain Handle */
+} vlds_dom_nam2hdl_arg_t;
+
+/*
+ * VLDS_DOM_HDL2NAM
+ */
+typedef struct vlds_dom_hdl2nam_arg {
+ uint64_t vlds_dhdl; /* Domain Handle */
+ vlds_string_t vlds_domain_name; /* returned Domain Name string */
+} vlds_dom_hdl2nam_arg_t;
+
+/*
+ * Machine Description Constants for vlds driver.
+ */
+#define VLDS_MD_ROOT_NODE_NAME "domain-services"
+#define VLDS_MD_PORT_NODE_NAME "domain-services-port"
+
+/*
+ * VLDS Sysevent defines.
+ * VLDS System Event Channel names are of the form:
+ * sun.com:vlds:pid<pid_number>
+ */
+#define VLDS_SYSEV_CHAN_FMT "sun.com:vlds:pid%06d"
+#define VLDS_SYSEV_MAX_CHAN_NAME 32
+
+#define EC_VLDS "EC_vlds" /* LDOMS Domain Services event class */
+
+/*
+ * EC_VLDS subclass definitions - supporting attributes (name/value pairs) are
+ * found in sys/sysevent/vlds.h
+ */
+#define ESC_VLDS_REGISTER "ESC_VLDS_register"
+#define ESC_VLDS_UNREGISTER "ESC_VLDS_unregister"
+#define ESC_VLDS_DATA "ESC_VLDS_data"
+
+/*
+ * Event type EC_VLDS
+ * Event Class - EC_VLDS
+ * Event Sub-Class - ESC_VLDS_REGISTER
+ * Event Publisher - SUNW:kern:[ds_module_name]
+ * Attribute Name - VLDS_HDL
+ * Attribute Type - SE_DATA_TYPE_UINT64
+ * Attribute Value - [Domain Service Handle]
+ * Attribute Name - VLDS_VER_MAJOR
+ * Attribute Type - SE_DATA_TYPE_UINT16
+ * Attribute Value - [major version of the DS interface]
+ * Attribute Name - VLDS_VER_MINOR
+ * Attribute Type - SE_DATA_TYPE_UINT16
+ * Attribute Value - [minor version of the DS interface]
+ * Attribute Name - VLDS_DOMAIN_HDL
+ * Attribute Type - SE_DATA_TYPE_UINT64
+ * Attribute Value - [Domain handle of registered service]
+ * Attribute Name - VLDS_SERVICE_ID
+ * Attribute Type - SE_DATA_TYPE_STRING
+ * Attribute Value - [Service name of registered service]
+ * Attribute Name - VLDS_ISCLIENT
+ * Attribute Type - SE_DATA_TYPE_BOOLEAN_VALUE
+ * Attribute Value - [Service is client or provider]
+ *
+ * Event Class - EC_VLDS
+ * Event Sub-Class - ESC_VLDS_UNREGISTER
+ * Event Publisher - SUNW:kern:[ds_module_name]
+ * Attribute Name - VLDS_HDL
+ * Attribute Type - SE_DATA_TYPE_UINT64
+ * Attribute Value - [Domain Service Handle]
+ *
+ * Event Class - EC_VLDS
+ * Event Sub-Class - ESC_VLDS_DATA
+ * Event Publisher - SUNW:kern:[ds_module_name]
+ * Attribute Name - VLDS_HDL
+ * Attribute Type - SE_DATA_TYPE_UINT64
+ * Attribute Value - [Domain Service Handle]
+ * Attribute Name - VLDS_DATA
+ * Attribute Type - SE_DATA_TYPE_BYTE_ARRAY
+ * Attribute Value - [Data array passed to user]
+ */
+
+#define VLDS_HDL "vlds_hdl" /* service handle */
+#define VLDS_VER_MAJOR "vlds_ver_major" /* major version */
+#define VLDS_VER_MINOR "vlds_ver_minor" /* minor version */
+#define VLDS_DOMAIN_HDL "vlds_domain_hdl" /* domain handle */
+#define VLDS_SERVICE_ID "vlds_service_id" /* service id */
+#define VLDS_ISCLIENT "vlds_isclient" /* service is client */
+#define VLDS_DATA "vlds_data" /* data buffer */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VLDS_H_ */
diff --git a/usr/src/uts/sun4v/vlds/Makefile b/usr/src/uts/sun4v/vlds/Makefile
new file mode 100644
index 0000000000..6c10e8c9ea
--- /dev/null
+++ b/usr/src/uts/sun4v/vlds/Makefile
@@ -0,0 +1,97 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# This makefile drives the production of the pseudo device
+# to access the sun4v PRI
+#
+# sun4v implementation architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = vlds
+OBJECTS = $(VLDS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(VLDS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sun4v/Makefile.sun4v
+
+#
+# Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+
+CLEANFILES += $(MODSTUBS_O)
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += -v
+
+#
+# Module Dependencies
+LDFLAGS += -dy -Nmisc/ds -Nmisc/platsvc
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/$(PLATFORM)/Makefile.targ