summaryrefslogtreecommitdiff
path: root/usr/src/lib/scsi/libses/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/scsi/libses/common')
-rw-r--r--usr/src/lib/scsi/libses/common/libses.h132
-rw-r--r--usr/src/lib/scsi/libses/common/libses_plugin.h161
-rw-r--r--usr/src/lib/scsi/libses/common/mkerrno.sh86
-rw-r--r--usr/src/lib/scsi/libses/common/ses_enclosure.c293
-rw-r--r--usr/src/lib/scsi/libses/common/ses_impl.h170
-rw-r--r--usr/src/lib/scsi/libses/common/ses_node.c399
-rw-r--r--usr/src/lib/scsi/libses/common/ses_plugin.c398
-rw-r--r--usr/src/lib/scsi/libses/common/ses_snap.c700
-rw-r--r--usr/src/lib/scsi/libses/common/ses_subr.c376
9 files changed, 2715 insertions, 0 deletions
diff --git a/usr/src/lib/scsi/libses/common/libses.h b/usr/src/lib/scsi/libses/common/libses.h
new file mode 100644
index 0000000000..7e95cb4b3a
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/libses.h
@@ -0,0 +1,132 @@
+/*
+ * 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 _LIBSES_H
+#define _LIBSES_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <libnvpair.h>
+#include <pthread.h>
+
+#include <scsi/libscsi.h>
+#include <scsi/plugins/ses/framework/ses2.h>
+#include <scsi/plugins/ses/framework/libses.h>
+
+#define LIBSES_VERSION 1
+
+typedef enum ses_node_type {
+ SES_NODE_NONE = 0x0,
+ SES_NODE_TARGET = 0x1,
+ SES_NODE_ENCLOSURE = 0x2,
+ SES_NODE_AGGREGATE = 0x4,
+ SES_NODE_ELEMENT = 0x8
+} ses_node_type_t;
+
+typedef enum ses_errno {
+ ESES_NONE, /* no error */
+ ESES_NOMEM, /* no memory */
+ ESES_ZERO_LENGTH, /* zero-length allocation requested */
+ ESES_VERSION, /* library version mismatch */
+ ESES_NVL, /* nvlist manipulation error */
+ ESES_BAD_NODE, /* bad node */
+ ESES_INVALID_OP, /* invalid operation */
+ ESES_RANGE, /* value out of range */
+ ESES_INVALID_PROP, /* nonexistent or immutable property */
+ ESES_BAD_TYPE, /* incorrect property type */
+ ESES_BAD_PAGE, /* bad page number */
+ ESES_BAD_RESPONSE, /* bad response from target */
+ ESES_BUSY, /* target busy */
+ ESES_TOOMUCHCHANGE, /* target configuration changing too rapidly */
+ ESES_LIBSCSI, /* SCSI error */
+ ESES_NOTSUP, /* operation not supported */
+ ESES_UNKNOWN, /* error of unknown type */
+ ESES_CHANGED, /* generation count has changed */
+ ESES_PLUGIN, /* invalid or missing plugin */
+ ESES_MAX /* maximum libses errno value */
+} ses_errno_t;
+
+struct ses_target;
+typedef struct ses_target ses_target_t;
+
+struct ses_snap;
+typedef struct ses_snap ses_snap_t;
+
+struct ses_node;
+typedef struct ses_node ses_node_t;
+
+extern ses_target_t *ses_open(uint_t, const char *);
+extern ses_target_t *ses_open_scsi(uint_t, libscsi_target_t *);
+extern void ses_close(ses_target_t *);
+
+extern libscsi_target_t *ses_scsi_target(ses_target_t *);
+
+typedef enum ses_walk_action {
+ SES_WALK_ACTION_CONTINUE,
+ SES_WALK_ACTION_PRUNE,
+ SES_WALK_ACTION_TERMINATE
+} ses_walk_action_t;
+
+typedef ses_walk_action_t (*ses_walk_f)(ses_node_t *, void *);
+
+extern uint64_t ses_node_id(ses_node_t *);
+extern ses_node_t *ses_node_lookup(ses_snap_t *, uint64_t);
+
+extern ses_node_t *ses_root_node(ses_snap_t *);
+extern ses_node_t *ses_node_sibling(ses_node_t *);
+extern ses_node_t *ses_node_prev_sibling(ses_node_t *);
+extern ses_node_t *ses_node_child(ses_node_t *);
+extern ses_node_t *ses_node_parent(ses_node_t *);
+extern int ses_walk(ses_snap_t *, ses_walk_f, void *);
+
+extern ses_snap_t *ses_snap_hold(ses_target_t *);
+extern void ses_snap_rele(ses_snap_t *);
+extern ses_snap_t *ses_snap_new(ses_target_t *);
+extern uint32_t ses_snap_generation(ses_snap_t *);
+
+extern ses_node_type_t ses_node_type(ses_node_t *);
+extern nvlist_t *ses_node_props(ses_node_t *);
+extern int ses_node_ctl(ses_node_t *, const char *, nvlist_t *);
+extern ses_snap_t *ses_node_snapshot(ses_node_t *);
+extern ses_target_t *ses_node_target(ses_node_t *);
+
+extern ses_errno_t ses_errno(void);
+extern const char *ses_errmsg(void);
+extern const char *ses_strerror(ses_errno_t);
+extern const char *ses_nv_error_member(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSES_H */
diff --git a/usr/src/lib/scsi/libses/common/libses_plugin.h b/usr/src/lib/scsi/libses/common/libses_plugin.h
new file mode 100644
index 0000000000..7156da7bdf
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/libses_plugin.h
@@ -0,0 +1,161 @@
+/*
+ * 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 _LIBSES_PLUGIN_H
+#define _LIBSES_PLUGIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBSES_PLUGIN_VERSION 1
+
+/*
+ * These are the primary APIs for plugins to interact with libses.
+ */
+
+struct ses_plugin;
+typedef struct ses_plugin ses_plugin_t;
+
+typedef enum {
+ SES_PAGE_DIAG,
+ SES_PAGE_CTL
+} ses_pagetype_t;
+
+typedef struct ses_pagedesc {
+ int spd_pagenum;
+ size_t (*spd_ctl_len)(uint_t, int, size_t);
+ void *(*spd_ctl_fill)(ses_plugin_t *, void *, size_t,
+ ses_node_t *);
+ void *(*spd_index)(ses_plugin_t *, ses_node_t *,
+ void *, size_t, size_t *);
+ int spd_gcoff;
+} ses_pagedesc_t;
+
+typedef struct ses_plugin_config {
+ ses_pagedesc_t *spc_pages;
+ int (*spc_node_parse)(ses_plugin_t *, ses_node_t *);
+ int (*spc_node_ctl)(ses_plugin_t *, ses_node_t *,
+ const char *, nvlist_t *);
+} ses_plugin_config_t;
+
+extern int ses_plugin_register(ses_plugin_t *, int, ses_plugin_config_t *);
+
+extern void *ses_plugin_page_lookup(ses_plugin_t *, ses_snap_t *, int,
+ ses_node_t *, size_t *);
+
+extern void *ses_plugin_ctlpage_lookup(ses_plugin_t *, ses_snap_t *, int,
+ size_t, ses_node_t *, boolean_t);
+
+extern void ses_plugin_setspecific(ses_plugin_t *, void *);
+extern void *ses_plugin_getspecific(ses_plugin_t *);
+
+/*
+ * The following are support functions provided by libses.
+ */
+
+extern int ses_assert(const char *, const char *, int);
+
+#define VERIFY(x) ((void)((x) || ses_assert(#x, __FILE__, __LINE__)))
+
+#ifdef DEBUG
+#define ASSERT(x) VERIFY(x)
+#else
+#define ASSERT(x)
+#endif
+
+#define SES_NV_ADD(_t, _e, _l, _n, ...) \
+ if (((_e) = nvlist_add_##_t((_l), (_n), __VA_ARGS__)) != 0) \
+ return (ses_set_nverrno((_e), (_n)))
+
+#define SES_NV_ADD_OR_FREE(_t, _e, _l, _n, ...) \
+ if (((_e) = nvlist_add_##_t((_l), (_n), __VA_ARGS__)) != 0) { \
+ nvlist_free(_l); return (ses_set_nverrno((_e), (_n))); }
+
+#define SES_NV_ADD_FS(_e, _l, _name, _buf) \
+ SES_NV_ADD(fixed_string, (_e), (_l), (_name), (_buf), sizeof (_buf))
+
+#define SES_NV_ADD_FS_TRUNC(_e, _l, _name, _buf) \
+ SES_NV_ADD(fixed_string_trunc, (_e), (_l), (_name), (_buf), \
+ sizeof (_buf))
+
+#define SES_NV_CTLBOOL(_l, _n, _b) \
+ { \
+ boolean_t v = B_FALSE; \
+ (void) nvlist_lookup_boolean_value((_l), (_n), &v); \
+ (_b) = v; \
+ }
+
+#define SES_NV_CTLBOOL_INVERT(_l, _n, _b) \
+ { \
+ boolean_t v = B_FALSE; \
+ (void) nvlist_lookup_boolean_value((_l), (_n), &v); \
+ (_b) = !v; \
+ }
+
+#define SES_NV_CTL64(_l, _n, _v) \
+ { \
+ uint64_t v = 0; \
+ (void) nvlist_lookup_uint64((_l), (_n), &v); \
+ (_v) = v; \
+ }
+
+#define SES_NV_CTL16(_l, _n, _v) \
+ { \
+ uint16_t v = 0; \
+ (void) nvlist_lookup_uint16((_l), (_n), &v); \
+ SCSI_WRITE16(&(_v), v); \
+ }
+
+extern void *ses_alloc(size_t);
+extern void *ses_zalloc(size_t);
+extern char *ses_strdup(const char *);
+extern void *ses_realloc(void *, size_t);
+extern void ses_free(void *);
+
+extern int ses_set_errno(ses_errno_t);
+extern int ses_set_nverrno(int, const char *);
+extern int ses_error(ses_errno_t, const char *, ...);
+extern int ses_nverror(int, const char *, const char *, ...);
+extern void ses_panic(const char *, ...) __NORETURN;
+
+extern int nvlist_add_fixed_string(nvlist_t *, const char *,
+ const char *, size_t);
+extern int nvlist_add_fixed_string_trunc(nvlist_t *, const char *,
+ const char *, size_t);
+
+#define SES_WITHIN_PAGE(sp, size, data, len) \
+ ((char *)(sp) <= (char *)(data) + (len) - (size))
+#define SES_WITHIN_PAGE_STRUCT(sp, data, len) \
+ SES_WITHIN_PAGE((sp), sizeof (*(sp)), (data), (len))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSES_PLUGIN_H */
diff --git a/usr/src/lib/scsi/libses/common/mkerrno.sh b/usr/src/lib/scsi/libses/common/mkerrno.sh
new file mode 100644
index 0000000000..a796ddee67
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/mkerrno.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# 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"
+
+echo "/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident\t\"%Z%%M%\t%I%\t%E% SMI\"
+
+#include <strings.h>
+#include <scsi/libses.h>
+
+static const struct {
+\tchar *se_name;\t\t/* error name */
+\tchar *se_msg;\t\t/* error message */
+} _ses_errstr[] = {"
+
+pattern='^ \(ESES_[A-Z0-9_]*\),*'
+replace=' { "\1",'
+open=' \/\* '
+openrepl='"'
+close=' \*\/$'
+closerepl='" },'
+
+( sed -n "s/$pattern/$replace/p" | sed -n "s/$open/$openrepl/p" |
+ sed -n "s/$close/$closerepl/p" ) || exit 1
+
+echo "\
+};\n\
+\n\
+static int _ses_nerrno = sizeof (_ses_errstr) / sizeof (_ses_errstr[0]);\n\
+\n\
+const char *
+ses_strerror(ses_errno_t err)
+{
+ return (err < 0 || err >= _ses_nerrno ? \"unknown error\" :
+ _ses_errstr[err].se_msg);
+}
+
+const char *
+ses_errname(ses_errno_t err)
+{
+ return (err < 0 || err >= _ses_nerrno ? NULL :
+ _ses_errstr[err].se_name);
+}
+
+ses_errno_t
+ses_errcode(const char *name)
+{
+ ses_errno_t err;
+
+ for (err = 0; err < _ses_nerrno; err++) {
+ if (strcmp(name, _ses_errstr[err].se_name) == 0)
+ return (err);
+ }
+
+ return (ESES_UNKNOWN);
+}"
+
+exit 0
diff --git a/usr/src/lib/scsi/libses/common/ses_enclosure.c b/usr/src/lib/scsi/libses/common/ses_enclosure.c
new file mode 100644
index 0000000000..4d2af5cc55
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_enclosure.c
@@ -0,0 +1,293 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <scsi/libses.h>
+#include "ses_impl.h"
+
+int
+enc_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl)
+{
+ int nverr;
+
+ if (tp != NULL)
+ SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION,
+ tp, tip->sthi_text_len);
+
+ return (0);
+}
+
+static int
+enc_eid(const ses2_ed_impl_t *tp, nvlist_t *nvl, const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD(uint64, nverr, nvl, name, tp->st_hdr.sehi_subenclosure_id);
+
+ return (0);
+}
+
+static int
+enc_espid(const ses2_ed_impl_t *tp, nvlist_t *nvl, const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD(uint64, nverr, nvl, name, tp->st_hdr.sehi_rel_esp_id);
+
+ return (0);
+}
+
+static int
+enc_nesp(const ses2_ed_impl_t *tp, nvlist_t *nvl, const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD(uint64, nverr, nvl, name, tp->st_hdr.sehi_n_esps);
+
+ return (0);
+}
+
+static int
+enc_lid(const ses2_ed_impl_t *tp, nvlist_t *nvl, const char *name)
+{
+ nvlist_t *lid;
+ int nverr;
+
+ if ((nverr = nvlist_alloc(&lid, NV_UNIQUE_NAME, 0)) != 0)
+ return (ses_set_nverrno(nverr, NULL));
+
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_INT,
+ SCSI_READ64(&tp->st_logical_id));
+
+ switch (tp->st_logical_id.sni8i_naa) {
+ case NAA_IEEE_EXT:
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_ID_TYPE,
+ NAA_IEEE_EXT);
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_COMPANY_ID,
+ NAA_IEEE_EXT_COMPANY_ID(&tp->st_logical_id.sni8i_ext_id));
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_VS_A,
+ NAA_IEEE_EXT_VENDOR_A(&tp->st_logical_id.sni8i_ext_id));
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_VS_B,
+ NAA_IEEE_EXT_VENDOR_B(&tp->st_logical_id.sni8i_ext_id));
+ break;
+ case NAA_IEEE_REG:
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_ID_TYPE,
+ NAA_IEEE_REG);
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_COMPANY_ID,
+ NAA_IEEE_REG_COMPANY_ID(&tp->st_logical_id.sni8i_reg_id));
+ SES_NV_ADD_OR_FREE(uint64, nverr, lid, SPC3_NAA_VS_A,
+ NAA_IEEE_REG_VENDOR_ID(&tp->st_logical_id.sni8i_reg_id));
+ break;
+ default:
+ break;
+ }
+
+ if ((nverr = nvlist_add_nvlist(nvl, name, lid)) != 0) {
+ nvlist_free(lid);
+ return (ses_set_nverrno(nverr, name));
+ }
+
+ nvlist_free(lid);
+
+ return (0);
+}
+
+static int
+enc_vid(const ses2_ed_impl_t *tp, nvlist_t *nvl,
+ const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD_FS_TRUNC(nverr, nvl, name, tp->st_vendor_id);
+
+ return (0);
+}
+
+static int
+enc_pid(const ses2_ed_impl_t *tp, nvlist_t *nvl,
+ const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD_FS_TRUNC(nverr, nvl, name, tp->st_product_id);
+
+ return (0);
+}
+
+static int
+enc_rev(const ses2_ed_impl_t *tp, nvlist_t *nvl,
+ const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD_FS_TRUNC(nverr, nvl, name, tp->st_product_revision);
+
+ return (0);
+}
+
+static int
+enc_vs(const ses2_ed_impl_t *tp, nvlist_t *nvl, const char *name)
+{
+ int nverr;
+
+ SES_NV_ADD(byte_array, nverr, nvl, name, (uchar_t *)tp->st_priv,
+ tp->st_hdr.sehi_ed_len - offsetof(ses2_ed_impl_t, st_priv[0]));
+
+ return (0);
+}
+
+/* LINTED - unused */
+static const ses2_ed_impl_t __ed = { 0 };
+
+#define ED_REQ_LEN(member) \
+ (offsetof(ses2_ed_impl_t, member) - sizeof (ses2_ed_hdr_impl_t) + \
+ sizeof (__ed.member))
+
+static const struct config_member {
+ const char *name;
+ size_t minsz;
+ int (*func)(const ses2_ed_impl_t *, nvlist_t *, const char *);
+} config_members[] = {
+ { SES_EN_PROP_EID, 0, enc_eid },
+ { SES_EN_PROP_ESPID, 0, enc_espid },
+ { SES_EN_PROP_NESP, 0, enc_nesp },
+ { SES_EN_PROP_LID, ED_REQ_LEN(st_logical_id), enc_lid },
+ { SES_EN_PROP_VID, ED_REQ_LEN(st_vendor_id), enc_vid },
+ { SES_EN_PROP_PID, ED_REQ_LEN(st_product_id), enc_pid },
+ { SES_EN_PROP_REV, ED_REQ_LEN(st_product_revision), enc_rev },
+ { SES_EN_PROP_VS, ED_REQ_LEN(st_priv), enc_vs },
+ { NULL, 0, NULL }
+};
+
+int
+enc_parse_ed(ses2_ed_impl_t *tp, nvlist_t *nvl)
+{
+ const struct config_member *mp;
+ int err;
+
+ if (tp == NULL)
+ return (0);
+
+ for (mp = &config_members[0]; mp->name != NULL; mp++) {
+ if (mp->func != NULL && tp->st_hdr.sehi_ed_len >= mp->minsz) {
+ err = mp->func(tp, nvl, mp->name);
+ if (err != 0)
+ return (err);
+ }
+ }
+
+ return (0);
+}
+
+ses_target_t *
+ses_open_scsi(uint_t version, libscsi_target_t *stp)
+{
+ ses_target_t *tp;
+ ses_snap_t *sp;
+
+ if (version != LIBSES_VERSION) {
+ (void) ses_set_errno(ESES_VERSION);
+ return (NULL);
+ }
+
+ if ((tp = ses_zalloc(sizeof (ses_target_t))) == NULL)
+ return (NULL);
+
+ tp->st_target = stp;
+ tp->st_scsi_hdl = libscsi_get_handle(stp);
+ tp->st_truncate = (getenv("LIBSES_TRUNCATE") != NULL);
+ if (tp->st_truncate)
+ srand48(gethrtime());
+
+ (void) pthread_mutex_init(&tp->st_lock, NULL);
+
+ if (ses_plugin_load(tp) != 0) {
+ ses_close(tp);
+ return (NULL);
+ }
+
+ if ((sp = ses_snap_new(tp)) == NULL) {
+ ses_close(tp);
+ return (NULL);
+ }
+
+ ses_snap_rele(sp);
+
+ return (tp);
+}
+
+ses_target_t *
+ses_open(uint_t version, const char *target)
+{
+ ses_target_t *tp;
+ libscsi_errno_t serr;
+ libscsi_target_t *stp;
+ libscsi_hdl_t *hp;
+
+ if ((hp = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
+ (void) ses_error(ESES_LIBSCSI, "failed to initialize "
+ "libscsi: %s", libscsi_strerror(serr));
+ return (NULL);
+ }
+
+ if ((stp = libscsi_open(hp, NULL, target)) == NULL) {
+ (void) ses_libscsi_error(hp, "failed to open SES target");
+ libscsi_fini(hp);
+ return (NULL);
+ }
+
+ if ((tp = ses_open_scsi(version, stp)) == NULL) {
+ libscsi_close(hp, stp);
+ libscsi_fini(hp);
+ return (NULL);
+ }
+
+ tp->st_closescsi = B_TRUE;
+
+ return (tp);
+}
+
+libscsi_target_t *
+ses_scsi_target(ses_target_t *tp)
+{
+ return (tp->st_target);
+}
+
+void
+ses_close(ses_target_t *tp)
+{
+ if (tp->st_snapshots != NULL)
+ ses_snap_rele(tp->st_snapshots);
+ if (tp->st_snapshots != NULL)
+ ses_panic("attempt to close SES target with active snapshots");
+ ses_plugin_unload(tp);
+ if (tp->st_closescsi) {
+ libscsi_close(tp->st_scsi_hdl, tp->st_target);
+ libscsi_fini(tp->st_scsi_hdl);
+ }
+ ses_free(tp);
+}
diff --git a/usr/src/lib/scsi/libses/common/ses_impl.h b/usr/src/lib/scsi/libses/common/ses_impl.h
new file mode 100644
index 0000000000..a820f31787
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_impl.h
@@ -0,0 +1,170 @@
+/*
+ * 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 _SES_IMPL_H
+#define _SES_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <alloca.h>
+#include <errno.h>
+#include <assert.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libnvpair.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+
+#include <scsi/libscsi.h>
+#include <scsi/libses_plugin.h>
+#include <scsi/plugins/ses/framework/ses2_impl.h>
+
+#define LIBSES_ERRMSGLEN 512
+
+#define LIBSES_DEFAULT_PLUGINDIR "/usr/lib/scsi/plugins/ses"
+#define LIBSES_PLUGIN_FRAMEWORK "framework"
+#define LIBSES_PLUGIN_VENDOR "vendor"
+
+#define LIBSES_PLUGIN_EXT ".so"
+
+struct ses_plugin {
+ struct ses_plugin *sp_next; /* next plugin in list */
+ struct ses_plugin *sp_prev; /* previous plugin in list */
+ uint64_t sp_priority; /* plugin priority */
+ struct ses_target *sp_target; /* corresponding target */
+ void *sp_object; /* shared object */
+ void *sp_data; /* module-specific data */
+ boolean_t sp_initialized; /* successfully initialized */
+ ses_pagedesc_t *sp_pages; /* pages */
+ int (*sp_init)(ses_plugin_t *); /* plugin init */
+ void (*sp_fini)(ses_plugin_t *); /* plugin fini */
+ int (*sp_node_parse)(ses_plugin_t *, ses_node_t *); /* parse node */
+ int (*sp_node_ctl)(ses_plugin_t *, ses_node_t *, const char *,
+ nvlist_t *); /* node control */
+};
+
+struct ses_target {
+ libscsi_hdl_t *st_scsi_hdl;
+ libscsi_target_t *st_target;
+ struct ses_plugin *st_plugin_first;
+ struct ses_plugin *st_plugin_last;
+ struct ses_snap *st_snapshots;
+ boolean_t st_closescsi;
+ boolean_t st_truncate;
+ pthread_mutex_t st_lock;
+};
+
+/*
+ * Maximum number of snapshot retries triggered by generation count changes
+ */
+#define LIBSES_MAX_GC_RETRIES 10
+
+/*
+ * Maximum number of Enclosure Busy retries
+ */
+#define LIBSES_MAX_BUSY_RETRIES 3
+
+typedef struct ses_snap_page {
+ ses2_diag_page_t ssp_num;
+ boolean_t ssp_control;
+ boolean_t ssp_initialized;
+ size_t ssp_alloc;
+ size_t ssp_len;
+ void *ssp_page;
+ char *ssp_mmap_base;
+ size_t ssp_mmap_len;
+ struct ses_snap_page *ssp_next;
+ struct ses_snap_page *ssp_unique;
+} ses_snap_page_t;
+
+struct ses_snap {
+ struct ses_target *ss_target;
+ uint32_t ss_generation;
+ hrtime_t ss_time;
+ struct ses_node *ss_root;
+ size_t ss_n_elem;
+ ses_snap_page_t *ss_pages;
+ size_t ss_n_nodes;
+ struct ses_node **ss_nodes;
+ struct ses_snap *ss_next;
+ struct ses_snap *ss_prev;
+ uint32_t ss_refcnt;
+};
+
+struct ses_node {
+ ses_node_type_t sn_type;
+ uint64_t sn_rootidx; /* Relative index for enclosure/aggregate */
+ size_t sn_id; /* Unique global ID */
+ uint64_t sn_enc_num;
+ struct ses_snap *sn_snapshot;
+ struct ses_node *sn_parent;
+ struct ses_node *sn_next_sibling;
+ struct ses_node *sn_prev_sibling;
+ struct ses_node *sn_first_child;
+ struct ses_node *sn_last_child;
+ nvlist_t *sn_props;
+};
+
+extern int ses_fill_snap(ses_snap_t *);
+extern void ses_node_teardown(ses_node_t *);
+extern ses_snap_page_t *ses_snap_find_page(ses_snap_t *, ses2_diag_page_t,
+ boolean_t);
+extern ses_snap_page_t *ses_snap_ctl_page(ses_snap_t *,
+ ses2_diag_page_t, size_t, boolean_t);
+extern int ses_snap_do_ctl(ses_snap_t *);
+
+extern int ses_libscsi_error(libscsi_hdl_t *, const char *, ...);
+extern int ses_scsi_error(libscsi_action_t *, const char *, ...);
+
+extern int ses_plugin_load(ses_target_t *);
+extern void ses_plugin_unload(ses_target_t *);
+
+extern ses_pagedesc_t *ses_get_pagedesc(ses_target_t *, int, ses_pagetype_t);
+extern int ses_fill_node(ses_node_t *);
+
+extern int enc_parse_ed(ses2_ed_impl_t *, nvlist_t *);
+extern int enc_parse_td(ses2_td_hdr_impl_t *, const char *, nvlist_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SES_IMPL_H */
diff --git a/usr/src/lib/scsi/libses/common/ses_node.c b/usr/src/lib/scsi/libses/common/ses_node.c
new file mode 100644
index 0000000000..6e08a20874
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_node.c
@@ -0,0 +1,399 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <scsi/libses.h>
+#include "ses_impl.h"
+
+#define NEXT_ED(eip) \
+ ((ses2_ed_impl_t *)((uint8_t *)(eip) + \
+ ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t))))
+
+static ses_node_t *
+ses_find_enclosure(ses_snap_t *sp, uint64_t number)
+{
+ ses_node_t *np;
+
+ for (np = sp->ss_root->sn_first_child; np != NULL;
+ np = np->sn_next_sibling) {
+ ASSERT(np->sn_type == SES_NODE_ENCLOSURE);
+ if (np->sn_enc_num == number)
+ return ((ses_node_t *)np);
+ }
+
+ return (NULL);
+}
+
+void
+ses_node_teardown(ses_node_t *np)
+{
+ ses_node_t *rp;
+
+ if (np == NULL)
+ return;
+
+ for (; np != NULL; np = rp) {
+ ses_node_teardown(np->sn_first_child);
+ rp = np->sn_next_sibling;
+ nvlist_free(np->sn_props);
+ ses_free(np);
+ }
+}
+
+static ses_node_t *
+ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp)
+{
+ ses_node_t *np;
+
+ np = ses_zalloc(sizeof (ses_node_t));
+ if (np == NULL)
+ goto fail;
+ if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0)
+ goto fail;
+
+ np->sn_snapshot = sp;
+ np->sn_id = sp->ss_n_nodes++;
+
+ if (pnp == NULL) {
+ ASSERT(sp->ss_root == NULL);
+ sp->ss_root = np;
+ } else {
+ np->sn_parent = pnp;
+ np->sn_prev_sibling = pnp->sn_last_child;
+
+ if (pnp->sn_first_child == NULL)
+ pnp->sn_first_child = np;
+ else
+ pnp->sn_last_child->sn_next_sibling = np;
+
+ pnp->sn_last_child = np;
+ }
+
+ return (np);
+
+fail:
+ ses_free(np);
+ ses_node_teardown(sp->ss_root);
+ sp->ss_root = NULL;
+ return (NULL);
+}
+
+/*
+ * Parse element type descriptor.
+ */
+static int
+elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl)
+{
+ int nverr;
+
+ if (tp != NULL)
+ SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION,
+ tp, tip->sthi_text_len);
+
+ return (0);
+}
+
+
+/*
+ * Build a skeleton tree of nodes in the given snapshot. This is the heart of
+ * libses, and is responsible for parsing the config page into a tree and
+ * populating nodes with data from the config page.
+ */
+static int
+ses_build_snap_skel(ses_snap_t *sp)
+{
+ ses2_ed_impl_t *eip;
+ ses2_td_hdr_impl_t *tip, *ftip;
+ ses_node_t *np, *pnp, *cnp, *root;
+ ses_snap_page_t *pp;
+ ses2_config_page_impl_t *pip;
+ int i, j, n_etds = 0;
+ off_t toff;
+ char *tp, *text;
+ int err;
+ uint64_t idx;
+
+ pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE);
+ if (pp == NULL)
+ return (ses_error(ESES_BAD_RESPONSE, "target does not support "
+ "configuration diagnostic page"));
+ pip = (ses2_config_page_impl_t *)pp->ssp_page;
+
+ if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data))
+ return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
+ "descriptors found"));
+
+ /*
+ * Start with the root of the tree, which is a target node, containing
+ * just the SCSI inquiry properties.
+ */
+ if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL)
+ return (-1);
+
+ root->sn_type = SES_NODE_TARGET;
+ SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR,
+ libscsi_vendor(sp->ss_target->st_target));
+ SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT,
+ libscsi_product(sp->ss_target->st_target));
+ SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION,
+ libscsi_revision(sp->ss_target->st_target));
+
+ for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
+ i < pip->scpi_n_subenclosures + 1;
+ i++, eip = NEXT_ED(eip)) {
+ if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
+ break;
+
+ n_etds += eip->st_hdr.sehi_n_etd_hdrs;
+ }
+ ftip = (ses2_td_hdr_impl_t *)eip;
+
+ /*
+ * There should really be only one Enclosure element possible for a
+ * give subenclosure ID. The standard never comes out and says this,
+ * but it does describe this element as "managing the enclosure itself"
+ * which implies rather strongly that the subenclosure ID field is that
+ * of, well, the enclosure itself. Since an enclosure can't contain
+ * itself, it follows logically that each subenclosure has at most one
+ * Enclosure type descriptor elements matching its ID. Of course, some
+ * enclosure firmware is buggy, so this may not always work out; in
+ * this case we just ignore all but the first Enclosure-type element
+ * with our subenclosure ID.
+ */
+ for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
+ i < pip->scpi_n_subenclosures + 1;
+ i++, eip = NEXT_ED(eip)) {
+ if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
+ break;
+
+ if ((np = ses_node_alloc(sp, root)) == NULL)
+ return (-1);
+
+ np->sn_type = SES_NODE_ENCLOSURE;
+ np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id;
+
+ if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len +
+ sizeof (ses2_ed_hdr_impl_t),
+ pp->ssp_page, pp->ssp_len))
+ break;
+
+ if (enc_parse_ed(eip, np->sn_props) != 0)
+ return (-1);
+ }
+
+ if (root->sn_first_child == NULL)
+ return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
+ "descriptors found"));
+
+ tp = (char *)(ftip + n_etds);
+
+ for (i = 0, toff = 0, idx = 0; i < n_etds; i++) {
+ tip = ftip + i;
+
+ if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len))
+ break;
+
+ pnp = ses_find_enclosure(sp,
+ tip->sthi_subenclosure_id);
+ if (pnp == NULL) {
+ idx += tip->sthi_max_elements + 1;
+ toff += tip->sthi_text_len;
+ continue;
+ }
+
+ if (tip->sthi_element_type == SES_ET_ENCLOSURE) {
+ if (tip->sthi_max_elements == 0) {
+ SES_NV_ADD(uint64, err, pnp->sn_props,
+ SES_PROP_ELEMENT_INDEX, idx);
+ pnp->sn_rootidx = idx;
+ } else {
+ SES_NV_ADD(uint64, err, pnp->sn_props,
+ SES_PROP_ELEMENT_INDEX, idx + 1);
+ pnp->sn_rootidx = idx + 1;
+ }
+
+ if (tip->sthi_text_len > 0 &&
+ SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
+ pp->ssp_page, pp->ssp_len)) {
+ text = tp + toff;
+ toff += tip->sthi_text_len;
+ } else {
+ text = NULL;
+ }
+
+ SES_NV_ADD(uint64, err, pnp->sn_props,
+ SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE);
+ if (enc_parse_td(tip, text, pnp->sn_props) != 0)
+ return (-1);
+
+ idx += tip->sthi_max_elements + 1;
+ continue;
+ }
+
+ if ((np = ses_node_alloc(sp, pnp)) == NULL)
+ return (-1);
+
+ np->sn_type = SES_NODE_AGGREGATE;
+ np->sn_enc_num = tip->sthi_subenclosure_id;
+ np->sn_parent = pnp;
+ np->sn_rootidx = idx;
+
+ SES_NV_ADD(uint64, err, np->sn_props,
+ SES_PROP_ELEMENT_INDEX, idx);
+ SES_NV_ADD(uint64, err, np->sn_props,
+ SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
+
+ if (tip->sthi_text_len > 0 &&
+ SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
+ pp->ssp_page, pp->ssp_len)) {
+ text = tp + toff;
+ toff += tip->sthi_text_len;
+ } else {
+ text = NULL;
+ }
+
+ if (elem_parse_td(tip, text, np->sn_props) != 0)
+ return (-1);
+
+ idx += tip->sthi_max_elements + 1;
+
+ if (tip->sthi_max_elements == 0)
+ continue;
+
+ for (j = 0; j < tip->sthi_max_elements; j++) {
+ cnp = ses_node_alloc(sp, np);
+ if (cnp == NULL)
+ return (-1);
+
+ cnp->sn_type = SES_NODE_ELEMENT;
+ SES_NV_ADD(uint64, err, cnp->sn_props,
+ SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1);
+ SES_NV_ADD(uint64, err, cnp->sn_props,
+ SES_PROP_ELEMENT_CLASS_INDEX, j);
+ SES_NV_ADD(uint64, err, cnp->sn_props,
+ SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
+ }
+ }
+
+ np->sn_snapshot->ss_n_elem = idx;
+
+ return (0);
+}
+
+static int
+ses_fill_tree(ses_node_t *np)
+{
+ if (np == NULL)
+ return (0);
+
+ for (; np != NULL; np = np->sn_next_sibling) {
+ if (ses_fill_node(np) != 0)
+ return (-1);
+ if (ses_fill_tree(np->sn_first_child) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+ses_fill_snap(ses_snap_t *sp)
+{
+ if (ses_build_snap_skel(sp) != 0)
+ return (-1);
+
+ if (ses_fill_tree(sp->ss_root) != 0)
+ return (-1);
+
+ return (0);
+}
+
+ses_node_t *
+ses_root_node(ses_snap_t *sp)
+{
+ return (sp->ss_root);
+}
+
+ses_node_t *
+ses_node_sibling(ses_node_t *np)
+{
+ return (np->sn_next_sibling);
+}
+
+ses_node_t *
+ses_node_prev_sibling(ses_node_t *np)
+{
+ return (np->sn_prev_sibling);
+}
+
+ses_node_t *
+ses_node_parent(ses_node_t *np)
+{
+ return (np->sn_parent);
+}
+
+ses_node_t *
+ses_node_child(ses_node_t *np)
+{
+ return (np->sn_first_child);
+}
+
+ses_node_type_t
+ses_node_type(ses_node_t *np)
+{
+ return (np->sn_type);
+}
+
+ses_snap_t *
+ses_node_snapshot(ses_node_t *np)
+{
+ return ((ses_snap_t *)np->sn_snapshot);
+}
+
+ses_target_t *
+ses_node_target(ses_node_t *np)
+{
+ return (np->sn_snapshot->ss_target);
+}
+
+nvlist_t *
+ses_node_props(ses_node_t *np)
+{
+ return (np->sn_props);
+}
+
+/*
+ * A node identifier is a (generation, index) tuple that can be used to lookup a
+ * node within this target at a later point. This will be valid across
+ * snapshots, though it will return failure if the generation count has changed.
+ */
+uint64_t
+ses_node_id(ses_node_t *np)
+{
+ return (((uint64_t)np->sn_snapshot->ss_generation << 32) |
+ np->sn_id);
+}
diff --git a/usr/src/lib/scsi/libses/common/ses_plugin.c b/usr/src/lib/scsi/libses/common/ses_plugin.c
new file mode 100644
index 0000000000..e419a74475
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_plugin.c
@@ -0,0 +1,398 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <scsi/libses.h>
+#include "ses_impl.h"
+
+static boolean_t ses_plugin_dlclose;
+
+/*ARGSUSED*/
+void *
+ses_plugin_ctlpage_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
+ size_t len, ses_node_t *np, boolean_t unique)
+{
+ ses_target_t *tp = snap->ss_target;
+ ses_snap_page_t *pp;
+ ses_pagedesc_t *dp;
+
+ if ((pp = ses_snap_ctl_page(snap, pagenum, len, unique)) == NULL)
+ return (NULL);
+
+ if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_CTL)) == NULL)
+ return (NULL);
+
+ if (dp->spd_ctl_fill != NULL) {
+ return (dp->spd_ctl_fill(sp, pp->ssp_page,
+ pp->ssp_len, np));
+ } else {
+ return (pp->ssp_page);
+ }
+}
+
+int
+ses_fill_node(ses_node_t *np)
+{
+ ses_target_t *tp = np->sn_snapshot->ss_target;
+ ses_plugin_t *sp;
+
+ for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
+ if (sp->sp_node_parse == NULL)
+ continue;
+
+ if (sp->sp_node_parse(sp, np) != 0)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+ses_node_ctl(ses_node_t *np, const char *op, nvlist_t *arg)
+{
+ ses_target_t *tp = np->sn_snapshot->ss_target;
+ ses_plugin_t *sp;
+ nvlist_t *nvl;
+ nvpair_t *nvp;
+ int ret;
+
+ if (nvlist_dup(arg, &nvl, 0) != 0)
+ return (ses_set_errno(ESES_NOMEM));
+
+ /*
+ * Technically we could get away with a per-snapshot lock while we fill
+ * the control page contents, but this doesn't take much time and we
+ * want actual control operations to be protected per-target, so we just
+ * take the target lock.
+ */
+ (void) pthread_mutex_lock(&tp->st_lock);
+
+ /*
+ * We walk the list of plugins backwards, so that a product-specific
+ * plugin can rewrite the nvlist to control operations in terms of the
+ * standard mechanisms, if desired.
+ */
+ for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
+ if (sp->sp_node_ctl == NULL)
+ continue;
+
+ if (sp->sp_node_ctl(sp, np, op, nvl) != 0) {
+ nvlist_free(nvl);
+ (void) pthread_mutex_unlock(&tp->st_lock);
+ return (-1);
+ }
+ }
+
+ if ((nvp = nvlist_next_nvpair(nvl, NULL)) != NULL) {
+ (void) ses_error(ESES_NOTSUP, "property '%s' invalid for "
+ "this node", nvpair_name(nvp));
+ nvlist_free(nvl);
+ (void) pthread_mutex_unlock(&tp->st_lock);
+ return (-1);
+ }
+
+ nvlist_free(nvl);
+
+ ret = ses_snap_do_ctl(np->sn_snapshot);
+ (void) pthread_mutex_unlock(&tp->st_lock);
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+void *
+ses_plugin_page_lookup(ses_plugin_t *sp, ses_snap_t *snap, int pagenum,
+ ses_node_t *np, size_t *lenp)
+{
+ ses_snap_page_t *pp;
+ ses_target_t *tp = sp->sp_target;
+ ses_pagedesc_t *dp;
+
+ if ((dp = ses_get_pagedesc(tp, pagenum, SES_PAGE_DIAG)) == NULL)
+ return (NULL);
+
+ if ((pp = ses_snap_find_page(snap, pagenum, B_FALSE)) == NULL)
+ return (NULL);
+
+ if (dp->spd_index != NULL) {
+ return (dp->spd_index(sp, np, pp->ssp_page, pp->ssp_len,
+ lenp));
+ } else {
+ *lenp = pp->ssp_len;
+ return (pp->ssp_page);
+ }
+}
+
+ses_pagedesc_t *
+ses_get_pagedesc(ses_target_t *tp, int pagenum, ses_pagetype_t type)
+{
+ ses_plugin_t *sp;
+ ses_pagedesc_t *dp;
+
+ for (sp = tp->st_plugin_first; sp != NULL; sp = sp->sp_next) {
+ if (sp->sp_pages == NULL)
+ continue;
+
+ for (dp = &sp->sp_pages[0]; dp->spd_pagenum != -1;
+ dp++) {
+ if ((type == SES_PAGE_CTL && dp->spd_ctl_len == NULL) ||
+ (type == SES_PAGE_DIAG && dp->spd_ctl_len != NULL))
+ continue;
+
+ if (dp->spd_pagenum == pagenum)
+ return (dp);
+ }
+ }
+
+ (void) ses_error(ESES_BAD_PAGE, "failed to find page 0x%x", pagenum);
+ return (NULL);
+}
+
+int
+ses_plugin_register(ses_plugin_t *sp, int version, ses_plugin_config_t *scp)
+{
+ if (version != LIBSES_PLUGIN_VERSION)
+ return (ses_set_errno(ESES_VERSION));
+
+ sp->sp_pages = scp->spc_pages;
+ sp->sp_node_parse = scp->spc_node_parse;
+ sp->sp_node_ctl = scp->spc_node_ctl;
+
+ return (0);
+}
+
+void
+ses_plugin_setspecific(ses_plugin_t *sp, void *data)
+{
+ sp->sp_data = data;
+}
+
+void *
+ses_plugin_getspecific(ses_plugin_t *sp)
+{
+ return (sp->sp_data);
+}
+
+static void
+ses_plugin_cleanstr(char *s)
+{
+ while (*s != '\0') {
+ if (*s == ' ' || *s == '/')
+ *s = '-';
+ s++;
+ }
+}
+
+static void
+ses_plugin_destroy(ses_plugin_t *sp)
+{
+ if (sp->sp_initialized && sp->sp_fini != NULL)
+ sp->sp_fini(sp);
+
+ if (!ses_plugin_dlclose)
+ (void) dlclose(sp->sp_object);
+
+ ses_free(sp);
+}
+
+static int
+ses_plugin_loadone(ses_target_t *tp, const char *path, uint32_t pass)
+{
+ ses_plugin_t *sp, **loc;
+ void *obj;
+ int (*ses_priority)(void);
+
+ if ((obj = dlopen(path, RTLD_PARENT | RTLD_LOCAL | RTLD_LAZY)) == NULL)
+ return (0);
+
+ if ((sp = ses_zalloc(sizeof (ses_plugin_t))) == NULL) {
+ (void) dlclose(obj);
+ return (-1);
+ }
+
+ sp->sp_object = obj;
+ sp->sp_init = (int (*)())dlsym(obj, "_ses_init");
+ sp->sp_fini = (void (*)())dlsym(obj, "_ses_fini");
+ sp->sp_target = tp;
+
+ if (sp->sp_init == NULL) {
+ ses_plugin_destroy(sp);
+ return (0);
+ }
+
+ /*
+ * Framework modules can establish an explicit prioritying by declaring
+ * the '_ses_priority' symbol, which returns an integer used to create
+ * an explicit ordering between plugins.
+ */
+ if ((ses_priority = (int (*)())dlsym(obj, "_ses_priority")) != NULL)
+ sp->sp_priority = ses_priority();
+
+ sp->sp_priority |= (uint64_t)pass << 32;
+
+ for (loc = &tp->st_plugin_first; *loc != NULL; loc = &(*loc)->sp_next) {
+ if ((*loc)->sp_priority > sp->sp_priority)
+ break;
+ }
+
+ if (*loc != NULL)
+ (*loc)->sp_prev = sp;
+ else
+ tp->st_plugin_last = sp;
+
+ sp->sp_next = *loc;
+ *loc = sp;
+
+ if (sp->sp_init(sp) != 0)
+ return (-1);
+ sp->sp_initialized = B_TRUE;
+
+ return (0);
+}
+
+static int
+ses_plugin_load_dir(ses_target_t *tp, const char *pluginroot)
+{
+ char path[PATH_MAX];
+ DIR *dirp;
+ struct dirent64 *dp;
+ char *vendor, *product, *revision;
+ char isa[257];
+
+ (void) snprintf(path, sizeof (path), "%s/%s",
+ pluginroot, LIBSES_PLUGIN_FRAMEWORK);
+
+#if defined(_LP64)
+ if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
+ isa[0] = '\0';
+#else
+ isa[0] = '\0';
+#endif
+
+ if ((dirp = opendir(path)) != NULL) {
+ while ((dp = readdir64(dirp)) != NULL) {
+ if (strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0)
+ continue;
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s/%s",
+ pluginroot, LIBSES_PLUGIN_FRAMEWORK,
+ isa, dp->d_name);
+
+ if (ses_plugin_loadone(tp, path, 0) != 0) {
+ (void) closedir(dirp);
+ return (-1);
+ }
+ }
+
+ (void) closedir(dirp);
+ }
+
+ /*
+ * Create a local copy of the vendor/product/revision, strip out any
+ * questionable characters, and then attempt to load each plugin.
+ */
+ vendor = alloca(strlen(libscsi_vendor(tp->st_target)) + 1);
+ product = alloca(strlen(libscsi_product(tp->st_target)) + 1);
+ revision = alloca(strlen(libscsi_revision(tp->st_target)) + 1);
+ (void) strcpy(vendor, libscsi_vendor(tp->st_target));
+ (void) strcpy(product, libscsi_product(tp->st_target));
+ (void) strcpy(revision, libscsi_revision(tp->st_target));
+
+ ses_plugin_cleanstr(vendor);
+ ses_plugin_cleanstr(product);
+ ses_plugin_cleanstr(revision);
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s/%s%s", pluginroot,
+ LIBSES_PLUGIN_VENDOR, isa, vendor,
+ LIBSES_PLUGIN_EXT);
+ if (ses_plugin_loadone(tp, path, 1) != 0)
+ return (-1);
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s%s", pluginroot,
+ LIBSES_PLUGIN_VENDOR, isa, vendor, product,
+ LIBSES_PLUGIN_EXT);
+ if (ses_plugin_loadone(tp, path, 2) != 0)
+ return (-1);
+
+ (void) snprintf(path, sizeof (path), "%s/%s/%s/%s-%s-%s%s", pluginroot,
+ LIBSES_PLUGIN_VENDOR, isa, vendor, product,
+ revision, LIBSES_PLUGIN_EXT);
+ if (ses_plugin_loadone(tp, path, 3) != 0)
+ return (-1);
+
+ return (0);
+}
+
+int
+ses_plugin_load(ses_target_t *tp)
+{
+ char pluginroot[PATH_MAX];
+ const char *pluginpath, *p, *q;
+
+ if ((pluginpath = getenv("SES_PLUGINPATH")) == NULL)
+ pluginpath = LIBSES_DEFAULT_PLUGINDIR;
+ ses_plugin_dlclose = (getenv("SES_NODLCLOSE") == NULL);
+
+ for (p = pluginpath, q = strchr(p, ':'); p != NULL; p = q) {
+ if (q != NULL) {
+ ptrdiff_t len = q - p;
+ (void) strncpy(pluginroot, p, len);
+ pluginroot[len] = '\0';
+ while (*q == ':')
+ ++q;
+ if (*q == '\0')
+ q = NULL;
+ if (len == 0)
+ continue;
+ } else {
+ (void) strcpy(pluginroot, p);
+ }
+
+ if (pluginroot[0] != '/')
+ continue;
+
+ if (ses_plugin_load_dir(tp, pluginpath) != 0)
+ return (-1);
+ }
+
+ if (tp->st_plugin_first == NULL)
+ return (ses_error(ESES_PLUGIN, "no plugins found"));
+
+ return (0);
+}
+
+void
+ses_plugin_unload(ses_target_t *tp)
+{
+ ses_plugin_t *sp;
+
+ while ((sp = tp->st_plugin_first) != NULL) {
+ tp->st_plugin_first = sp->sp_next;
+ ses_plugin_destroy(sp);
+ }
+}
diff --git a/usr/src/lib/scsi/libses/common/ses_snap.c b/usr/src/lib/scsi/libses/common/ses_snap.c
new file mode 100644
index 0000000000..78e7927061
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_snap.c
@@ -0,0 +1,700 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <scsi/libses.h>
+#include "ses_impl.h"
+
+ses_snap_page_t *
+ses_snap_find_page(ses_snap_t *sp, ses2_diag_page_t page, boolean_t ctl)
+{
+ ses_snap_page_t *pp;
+
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
+ if (pp->ssp_num == page && pp->ssp_control == ctl &&
+ (pp->ssp_len > 0 || pp->ssp_control))
+ return (pp);
+
+ return (NULL);
+}
+
+static int
+grow_snap_page(ses_snap_page_t *pp, size_t min)
+{
+ uint8_t *newbuf;
+
+ if (min == 0 || min < pp->ssp_alloc)
+ min = pp->ssp_alloc * 2;
+
+ if ((newbuf = ses_realloc(pp->ssp_page, min)) == NULL)
+ return (-1);
+
+ pp->ssp_page = newbuf;
+ pp->ssp_alloc = min;
+
+ bzero(newbuf + pp->ssp_len, pp->ssp_alloc - pp->ssp_len);
+
+ return (0);
+}
+
+static ses_snap_page_t *
+alloc_snap_page(void)
+{
+ ses_snap_page_t *pp;
+
+ if ((pp = ses_zalloc(sizeof (ses_snap_page_t))) == NULL)
+ return (NULL);
+
+ if ((pp->ssp_page = ses_zalloc(SES2_MIN_DIAGPAGE_ALLOC)) == NULL) {
+ ses_free(pp);
+ return (NULL);
+ }
+
+ pp->ssp_num = -1;
+ pp->ssp_alloc = SES2_MIN_DIAGPAGE_ALLOC;
+
+ return (pp);
+}
+
+static void
+free_snap_page(ses_snap_page_t *pp)
+{
+ if (pp == NULL)
+ return;
+
+ if (pp->ssp_mmap_base)
+ (void) munmap(pp->ssp_mmap_base, pp->ssp_mmap_len);
+ else
+ ses_free(pp->ssp_page);
+ ses_free(pp);
+}
+
+static void
+free_all_snap_pages(ses_snap_t *sp)
+{
+ ses_snap_page_t *pp, *np;
+
+ for (pp = sp->ss_pages; pp != NULL; pp = np) {
+ np = pp->ssp_next;
+ free_snap_page(pp);
+ }
+
+ sp->ss_pages = NULL;
+}
+
+/*
+ * Grow (if needed) the control page buffer, fill in the page code, page
+ * length, and generation count, and return a pointer to the page. The
+ * caller is responsible for filling in the rest of the page data. If 'unique'
+ * is specified, then a new page instance is created instead of sharing the
+ * current one.
+ */
+ses_snap_page_t *
+ses_snap_ctl_page(ses_snap_t *sp, ses2_diag_page_t page, size_t dlen,
+ boolean_t unique)
+{
+ ses_target_t *tp = sp->ss_target;
+ spc3_diag_page_impl_t *pip;
+ ses_snap_page_t *pp, *up, **loc;
+ ses_pagedesc_t *dp;
+ size_t len;
+
+ pp = ses_snap_find_page(sp, page, B_TRUE);
+ if (pp == NULL) {
+ (void) ses_set_errno(ESES_NOTSUP);
+ return (NULL);
+ }
+
+ if (pp->ssp_initialized && !unique)
+ return (pp);
+
+ if (unique) {
+ /*
+ * The user has requested a unique instance of the page. Create
+ * a new ses_snap_page_t instance and chain it off the
+ * 'ssp_instances' list of the master page. These must be
+ * appended to the end of the chain, as the order of operations
+ * may be important (i.e. microcode download).
+ */
+ if ((up = alloc_snap_page()) == NULL)
+ return (NULL);
+
+ up->ssp_num = pp->ssp_num;
+ up->ssp_control = B_TRUE;
+
+ for (loc = &pp->ssp_unique; *loc != NULL;
+ loc = &(*loc)->ssp_next)
+ ;
+
+ *loc = up;
+ pp = up;
+ }
+
+ dp = ses_get_pagedesc(tp, page, SES_PAGE_CTL);
+ ASSERT(dp != NULL);
+
+ len = dp->spd_ctl_len(sp->ss_n_elem, page, dlen);
+ if (pp->ssp_alloc < dlen && grow_snap_page(pp, len) != 0)
+ return (NULL);
+ pp->ssp_len = len;
+ bzero(pp->ssp_page, len);
+ pp->ssp_initialized = B_TRUE;
+
+ pip = (spc3_diag_page_impl_t *)pp->ssp_page;
+ pip->sdpi_page_code = (uint8_t)page;
+ SCSI_WRITE16(&pip->sdpi_page_length,
+ len - offsetof(spc3_diag_page_impl_t, sdpi_data[0]));
+ if (dp->spd_gcoff != -1)
+ SCSI_WRITE32((uint8_t *)pip + dp->spd_gcoff, sp->ss_generation);
+
+ return (pp);
+}
+
+static int
+read_status_page(ses_snap_t *sp, ses2_diag_page_t page)
+{
+ libscsi_action_t *ap;
+ ses_snap_page_t *pp;
+ ses_target_t *tp;
+ spc3_diag_page_impl_t *pip;
+ spc3_receive_diagnostic_results_cdb_t *cp;
+ uint_t flags;
+ uint8_t *buf;
+ size_t alloc;
+ uint_t retries = 0;
+ ses2_diag_page_t retpage;
+
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next)
+ if (pp->ssp_num == page && !pp->ssp_control)
+ break;
+
+ /*
+ * No matching page. Since the page number is not under consumer or
+ * device control, this must be a bug.
+ */
+ ASSERT(pp != NULL);
+
+ tp = sp->ss_target;
+
+ flags = LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
+ LIBSCSI_AF_RQSENSE;
+
+again:
+ ap = libscsi_action_alloc(tp->st_scsi_hdl,
+ SPC3_CMD_RECEIVE_DIAGNOSTIC_RESULTS, flags, pp->ssp_page,
+ pp->ssp_alloc);
+
+ if (ap == NULL)
+ return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
+ "allocate SCSI action"));
+
+ cp = (spc3_receive_diagnostic_results_cdb_t *)
+ libscsi_action_get_cdb(ap);
+
+ cp->rdrc_page_code = pp->ssp_num;
+ cp->rdrc_pcv = 1;
+ SCSI_WRITE16(&cp->rdrc_allocation_length,
+ MIN(pp->ssp_alloc, UINT16_MAX));
+
+ if (libscsi_exec(ap, tp->st_target) != 0) {
+ libscsi_action_free(ap);
+ return (ses_libscsi_error(tp->st_scsi_hdl,
+ "receive diagnostic results failed"));
+ }
+
+ if (libscsi_action_get_status(ap) != 0) {
+ (void) ses_scsi_error(ap,
+ "receive diagnostic results failed");
+ libscsi_action_free(ap);
+ return (-1);
+ }
+
+ (void) libscsi_action_get_buffer(ap, &buf, &alloc, &pp->ssp_len);
+ libscsi_action_free(ap);
+
+ ASSERT(buf == pp->ssp_page);
+ ASSERT(alloc == pp->ssp_alloc);
+
+ if (pp->ssp_len == pp->ssp_alloc && pp->ssp_alloc < UINT16_MAX) {
+ bzero(pp->ssp_page, pp->ssp_len);
+ pp->ssp_len = 0;
+ if (grow_snap_page(pp, 0) != 0)
+ return (-1);
+ goto again;
+ }
+
+ pip = (spc3_diag_page_impl_t *)buf;
+
+ if (pip->sdpi_page_code == page)
+ return (0);
+
+ retpage = pip->sdpi_page_code;
+
+ bzero(pp->ssp_page, pp->ssp_len);
+ pp->ssp_len = 0;
+
+ if (retpage == SES2_DIAGPAGE_ENCLOSURE_BUSY) {
+ if (++retries > LIBSES_MAX_BUSY_RETRIES)
+ return (ses_error(ESES_BUSY, "too many "
+ "enclosure busy responses for page 0x%x", page));
+ goto again;
+ }
+
+ return (ses_error(ESES_BAD_RESPONSE, "target returned page 0x%x "
+ "instead of the requested page 0x%x", retpage, page));
+}
+
+static int
+send_control_page(ses_snap_t *sp, ses_snap_page_t *pp)
+{
+ ses_target_t *tp;
+ libscsi_action_t *ap;
+ spc3_send_diagnostic_cdb_t *cp;
+ uint_t flags;
+
+ tp = sp->ss_target;
+
+ flags = LIBSCSI_AF_WRITE | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE |
+ LIBSCSI_AF_RQSENSE;
+
+ ap = libscsi_action_alloc(tp->st_scsi_hdl, SPC3_CMD_SEND_DIAGNOSTIC,
+ flags, pp->ssp_page, pp->ssp_len);
+
+ if (ap == NULL)
+ return (ses_libscsi_error(tp->st_scsi_hdl, "failed to "
+ "allocate SCSI action"));
+
+ cp = (spc3_send_diagnostic_cdb_t *)libscsi_action_get_cdb(ap);
+
+ cp->sdc_pf = 1;
+ SCSI_WRITE16(&cp->sdc_parameter_list_length, pp->ssp_len);
+
+ if (libscsi_exec(ap, tp->st_target) != 0) {
+ libscsi_action_free(ap);
+ return (ses_libscsi_error(tp->st_scsi_hdl,
+ "SEND DIAGNOSTIC command failed for page 0x%x",
+ pp->ssp_num));
+ }
+
+ if (libscsi_action_get_status(ap) != 0) {
+ (void) ses_scsi_error(ap, "SEND DIAGNOSTIC command "
+ "failed for page 0x%x", pp->ssp_num);
+ libscsi_action_free(ap);
+ return (-1);
+ }
+
+ libscsi_action_free(ap);
+
+ return (0);
+}
+
+static int
+pages_skel_create(ses_snap_t *sp)
+{
+ ses_snap_page_t *pp, *np;
+ ses_target_t *tp = sp->ss_target;
+ ses2_supported_ses_diag_page_impl_t *pip;
+ ses2_diag_page_t page;
+ size_t npages;
+ size_t pagelen;
+ off_t i;
+
+ ASSERT(sp->ss_pages == NULL);
+
+ if ((pp = alloc_snap_page()) == NULL)
+ return (-1);
+
+ pp->ssp_num = SES2_DIAGPAGE_SUPPORTED_PAGES;
+ pp->ssp_control = B_FALSE;
+ sp->ss_pages = pp;
+
+ if (read_status_page(sp, SES2_DIAGPAGE_SUPPORTED_PAGES) != 0) {
+ free_snap_page(pp);
+ sp->ss_pages = NULL;
+ return (-1);
+ }
+
+ pip = pp->ssp_page;
+ pagelen = pp->ssp_len;
+
+ npages = SCSI_READ16(&pip->sssdpi_page_length);
+
+ for (i = 0; i < npages; i++) {
+ if (!SES_WITHIN_PAGE(pip->sssdpi_pages + i, 1, pip,
+ pagelen))
+ break;
+
+ page = (ses2_diag_page_t)pip->sssdpi_pages[i];
+ /*
+ * Skip the page we already added during the bootstrap.
+ */
+ if (page == SES2_DIAGPAGE_SUPPORTED_PAGES)
+ continue;
+ /*
+ * The end of the page list may be padded with zeros; ignore
+ * them all.
+ */
+ if (page == 0 && i > 0)
+ break;
+ if ((np = alloc_snap_page()) == NULL) {
+ free_all_snap_pages(sp);
+ return (-1);
+ }
+ np->ssp_num = page;
+ pp->ssp_next = np;
+ pp = np;
+
+ /*
+ * Allocate a control page as well, if we can use it.
+ */
+ if (ses_get_pagedesc(tp, page, SES_PAGE_CTL) != NULL) {
+ if ((np = alloc_snap_page()) == NULL) {
+ free_all_snap_pages(sp);
+ return (-1);
+ }
+ np->ssp_num = page;
+ np->ssp_control = B_TRUE;
+ pp->ssp_next = np;
+ pp = np;
+ }
+ }
+
+ return (0);
+}
+
+static void
+ses_snap_free(ses_snap_t *sp)
+{
+ free_all_snap_pages(sp);
+ ses_node_teardown(sp->ss_root);
+ ses_free(sp->ss_nodes);
+ ses_free(sp);
+}
+
+static void
+ses_snap_rele_unlocked(ses_snap_t *sp)
+{
+ ses_target_t *tp = sp->ss_target;
+
+ if (--sp->ss_refcnt != 0)
+ return;
+
+ if (sp->ss_next != NULL)
+ sp->ss_next->ss_prev = sp->ss_prev;
+
+ if (sp->ss_prev != NULL)
+ sp->ss_prev->ss_next = sp->ss_next;
+ else
+ tp->st_snapshots = sp->ss_next;
+
+ ses_snap_free(sp);
+}
+
+ses_snap_t *
+ses_snap_hold(ses_target_t *tp)
+{
+ ses_snap_t *sp;
+
+ (void) pthread_mutex_lock(&tp->st_lock);
+ sp = tp->st_snapshots;
+ sp->ss_refcnt++;
+ (void) pthread_mutex_unlock(&tp->st_lock);
+
+ return (sp);
+}
+
+void
+ses_snap_rele(ses_snap_t *sp)
+{
+ ses_target_t *tp = sp->ss_target;
+
+ (void) pthread_mutex_lock(&tp->st_lock);
+ ses_snap_rele_unlocked(sp);
+ (void) pthread_mutex_unlock(&tp->st_lock);
+}
+
+ses_snap_t *
+ses_snap_new(ses_target_t *tp)
+{
+ ses_snap_t *sp;
+ ses_snap_page_t *pp;
+ uint32_t gc;
+ uint_t retries = 0;
+ ses_pagedesc_t *dp;
+ size_t pages, pagesize, pagelen;
+ char *scratch;
+
+ if ((sp = ses_zalloc(sizeof (ses_snap_t))) == NULL)
+ return (NULL);
+
+ sp->ss_target = tp;
+
+again:
+ free_all_snap_pages(sp);
+
+ if (pages_skel_create(sp) != 0) {
+ free(sp);
+ return (NULL);
+ }
+
+ sp->ss_generation = (uint32_t)-1;
+ sp->ss_time = gethrtime();
+
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
+ /*
+ * We skip all of:
+ *
+ * - Control pages
+ * - Pages we've already filled in
+ * - Pages we don't understand (those with no descriptor)
+ */
+ if (pp->ssp_len > 0 || pp->ssp_control)
+ continue;
+ if ((dp = ses_get_pagedesc(tp, pp->ssp_num,
+ SES_PAGE_DIAG)) == NULL)
+ continue;
+
+ if (read_status_page(sp, pp->ssp_num) != 0)
+ continue;
+
+ /*
+ * If the generation code has changed, we don't have a valid
+ * snapshot. Start over.
+ */
+ if (dp->spd_gcoff != -1 &&
+ dp->spd_gcoff + 4 <= pp->ssp_len) {
+ gc = SCSI_READ32((uint8_t *)pp->ssp_page +
+ dp->spd_gcoff);
+ if (sp->ss_generation == (uint32_t)-1) {
+ sp->ss_generation = gc;
+ } else if (sp->ss_generation != gc) {
+ if (++retries > LIBSES_MAX_GC_RETRIES) {
+ (void) ses_error(ESES_TOOMUCHCHANGE,
+ "too many generation count "
+ "mismatches: page 0x%x gc %u "
+ "previous page %u", dp->spd_gcoff,
+ gc, sp->ss_generation);
+ ses_snap_free((ses_snap_t *)sp);
+ return (NULL);
+ }
+ goto again;
+ }
+ }
+ }
+
+ /*
+ * The LIBSES_TRUNCATE environment variable is a debugging tool which,
+ * if set, randomly truncates all pages (except
+ * SES2_DIAGPAGE_SUPPORTED_PAGES). In order to be truly evil, we
+ * mmap() each page with enough space after it so we can move the data
+ * up to the end of a page and unmap the following page so that any
+ * attempt to read past the end of the page results in a segfault.
+ */
+ if (sp->ss_target->st_truncate) {
+ pagesize = PAGESIZE;
+
+ /*
+ * Count the maximum number of pages we will need and allocate
+ * the necessary space.
+ */
+ pages = 0;
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
+ if (pp->ssp_control || pp->ssp_len == 0)
+ continue;
+
+ pages += (P2ROUNDUP(pp->ssp_len, pagesize) /
+ pagesize) + 1;
+ }
+
+ if ((scratch = mmap(NULL, pages * pagesize,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0)) == MAP_FAILED) {
+ (void) ses_error(ESES_NOMEM,
+ "failed to mmap() pages for truncation");
+ ses_snap_free(sp);
+ return (NULL);
+ }
+
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
+ if (pp->ssp_control || pp->ssp_len == 0)
+ continue;
+
+ pages = P2ROUNDUP(pp->ssp_len, pagesize) / pagesize;
+ pp->ssp_mmap_base = scratch;
+ pp->ssp_mmap_len = pages * pagesize;
+
+ pagelen = lrand48() % pp->ssp_len;
+ (void) memcpy(pp->ssp_mmap_base + pp->ssp_mmap_len -
+ pagelen, pp->ssp_page, pagelen);
+ ses_free(pp->ssp_page);
+ pp->ssp_page = pp->ssp_mmap_base + pp->ssp_mmap_len -
+ pagelen;
+ pp->ssp_len = pagelen;
+
+ (void) munmap(pp->ssp_mmap_base + pages * pagesize,
+ pagesize);
+ scratch += (pages + 1) * pagesize;
+ }
+ }
+
+
+ if (ses_fill_snap(sp) != 0) {
+ ses_snap_free(sp);
+ return (NULL);
+ }
+
+ (void) pthread_mutex_lock(&tp->st_lock);
+ if (tp->st_snapshots != NULL)
+ ses_snap_rele_unlocked(tp->st_snapshots);
+ sp->ss_next = tp->st_snapshots;
+ if (tp->st_snapshots != NULL)
+ tp->st_snapshots->ss_prev = sp;
+ tp->st_snapshots = sp;
+ sp->ss_refcnt = 2;
+ (void) pthread_mutex_unlock(&tp->st_lock);
+
+ return (sp);
+}
+
+int
+ses_snap_do_ctl(ses_snap_t *sp)
+{
+ ses_snap_page_t *pp, *up;
+ int ret = -1;
+
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
+ if (!pp->ssp_control)
+ continue;
+
+ if (pp->ssp_initialized && send_control_page(sp, pp) != 0)
+ goto error;
+
+ for (up = pp->ssp_unique; up != NULL; up = up->ssp_next) {
+ if (send_control_page(sp, up) != 0)
+ goto error;
+ }
+ }
+
+ ret = 0;
+error:
+ for (pp = sp->ss_pages; pp != NULL; pp = pp->ssp_next) {
+ if (!pp->ssp_control)
+ continue;
+
+ pp->ssp_initialized = B_FALSE;
+ while ((up = pp->ssp_unique) != NULL) {
+ pp->ssp_unique = up->ssp_next;
+ free_snap_page(up);
+ }
+ }
+
+
+ return (ret);
+}
+
+uint32_t
+ses_snap_generation(ses_snap_t *sp)
+{
+ return (sp->ss_generation);
+}
+
+static ses_walk_action_t
+ses_walk_node(ses_node_t *np, ses_walk_f func, void *arg)
+{
+ ses_walk_action_t action;
+
+ for (; np != NULL; np = ses_node_sibling(np)) {
+ action = func(np, arg);
+ if (action == SES_WALK_ACTION_TERMINATE)
+ return (SES_WALK_ACTION_TERMINATE);
+ if (action == SES_WALK_ACTION_PRUNE ||
+ ses_node_child(np) == NULL)
+ continue;
+ if (ses_walk_node(ses_node_child(np), func, arg) ==
+ SES_WALK_ACTION_TERMINATE)
+ return (SES_WALK_ACTION_TERMINATE);
+ }
+
+ return (SES_WALK_ACTION_CONTINUE);
+}
+
+int
+ses_walk(ses_snap_t *sp, ses_walk_f func, void *arg)
+{
+ (void) ses_walk_node(ses_root_node(sp), func, arg);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static ses_walk_action_t
+ses_fill_nodes(ses_node_t *np, void *unused)
+{
+ np->sn_snapshot->ss_nodes[np->sn_id] = np;
+
+ return (SES_WALK_ACTION_CONTINUE);
+}
+
+/*
+ * Given an ID returned by ses_node_id(), lookup and return the corresponding
+ * node in the snapshot. If the snapshot generation count has changed, then
+ * return failure.
+ */
+ses_node_t *
+ses_node_lookup(ses_snap_t *sp, uint64_t id)
+{
+ uint32_t gen = (id >> 32);
+ uint32_t idx = (id & 0xFFFFFFFF);
+
+ if (sp->ss_generation != gen) {
+ (void) ses_set_errno(ESES_CHANGED);
+ return (NULL);
+ }
+
+ if (idx >= sp->ss_n_nodes) {
+ (void) ses_error(ESES_BAD_NODE,
+ "no such node in snapshot");
+ return (NULL);
+ }
+
+ /*
+ * If this is our first lookup attempt, construct the array for fast
+ * lookups.
+ */
+ if (sp->ss_nodes == NULL) {
+ if ((sp->ss_nodes = ses_zalloc(
+ sp->ss_n_nodes * sizeof (void *))) == NULL)
+ return (NULL);
+
+ (void) ses_walk(sp, ses_fill_nodes, NULL);
+ }
+
+ if (sp->ss_nodes[idx] == NULL)
+ (void) ses_error(ESES_BAD_NODE,
+ "no such node in snapshot");
+ return (sp->ss_nodes[idx]);
+}
diff --git a/usr/src/lib/scsi/libses/common/ses_subr.c b/usr/src/lib/scsi/libses/common/ses_subr.c
new file mode 100644
index 0000000000..aa1013e678
--- /dev/null
+++ b/usr/src/lib/scsi/libses/common/ses_subr.c
@@ -0,0 +1,376 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <scsi/libses.h>
+#include "ses_impl.h"
+
+__thread ses_errno_t _ses_errno;
+__thread char _ses_errmsg[1024];
+__thread char _ses_nverr_member[256];
+
+static void ses_vpanic(const char *, va_list) __NORETURN;
+
+static void
+ses_vpanic(const char *fmt, va_list ap)
+{
+ int oserr = errno;
+ char msg[BUFSIZ];
+ size_t len;
+
+ (void) snprintf(msg, sizeof (msg), "ABORT: ");
+ len = strlen(msg);
+ (void) vsnprintf(msg + len, sizeof (msg) - len, fmt, ap);
+
+ if (strchr(fmt, '\n') == NULL) {
+ len = strlen(msg);
+ (void) snprintf(msg + len, sizeof (msg) - len, ": %s\n",
+ strerror(oserr));
+ }
+
+ (void) write(STDERR_FILENO, msg, strlen(msg));
+
+abort:
+ abort();
+ _exit(1);
+}
+
+/*PRINTFLIKE1*/
+void
+ses_panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ses_vpanic(fmt, ap);
+ va_end(ap);
+}
+
+int
+ses_assert(const char *expr, const char *file, int line)
+{
+ ses_panic("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+int
+nvlist_add_fixed_string(nvlist_t *nvl, const char *name,
+ const char *buf, size_t len)
+{
+ char *str = alloca(len + 1);
+ bcopy(buf, str, len);
+ str[len] = '\0';
+
+ return (nvlist_add_string(nvl, name, str));
+}
+
+/*
+ * Like fixed_string, but clears any leading or trailing spaces.
+ */
+int
+nvlist_add_fixed_string_trunc(nvlist_t *nvl, const char *name,
+ const char *buf, size_t len)
+{
+ while (buf[0] == ' ' && len > 0) {
+ buf++;
+ len--;
+ }
+
+ while (len > 0 && buf[len - 1] == ' ')
+ len--;
+
+ return (nvlist_add_fixed_string(nvl, name, buf, len));
+}
+
+ses_errno_t
+ses_errno(void)
+{
+ return (_ses_errno);
+}
+
+const char *
+ses_errmsg(void)
+{
+ if (_ses_errmsg[0] == '\0')
+ (void) snprintf(_ses_errmsg, sizeof (_ses_errmsg), "%s",
+ ses_strerror(_ses_errno));
+
+ return (_ses_errmsg);
+}
+
+const char *
+ses_nv_error_member(void)
+{
+ if (_ses_nverr_member[0] != '\0')
+ return (_ses_nverr_member);
+ else
+ return (NULL);
+}
+
+static int
+__ses_set_errno(ses_errno_t err, const char *nvm)
+{
+ if (nvm == NULL) {
+ _ses_nverr_member[0] = '\0';
+ } else {
+ (void) strlcpy(_ses_nverr_member, nvm,
+ sizeof (_ses_nverr_member));
+ }
+ _ses_errmsg[0] = '\0';
+ _ses_errno = err;
+
+ return (-1);
+}
+
+int
+ses_set_errno(ses_errno_t err)
+{
+ return (__ses_set_errno(err, NULL));
+}
+
+int
+ses_set_nverrno(int err, const char *member)
+{
+ ses_errno_t se = (err == ENOMEM || err == EAGAIN) ?
+ ESES_NOMEM : ESES_NVL;
+
+ /*
+ * If the error is ESES_NVL, then we should always have a member
+ * available. The only time 'member' is NULL is when nvlist_alloc()
+ * fails, which should only be possible if memory allocation fails.
+ */
+ assert(se == ESES_NOMEM || member != NULL);
+
+ return (__ses_set_errno(se, member));
+}
+
+static int
+ses_verror(ses_errno_t err, const char *fmt, va_list ap)
+{
+ int syserr = errno;
+ size_t n;
+ char *errmsg;
+
+ errmsg = alloca(sizeof (_ses_errmsg));
+ (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
+ (void) ses_set_errno(err);
+
+ n = strlen(errmsg);
+
+ while (n != 0 && errmsg[n - 1] == '\n')
+ errmsg[--n] = '\0';
+
+ bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
+ errno = syserr;
+
+ return (-1);
+}
+
+static int
+ses_vnverror(int err, const char *member, const char *fmt,
+ va_list ap)
+{
+ int syserr = errno;
+ size_t n;
+ char *errmsg;
+
+ errmsg = alloca(sizeof (_ses_errmsg));
+ (void) vsnprintf(errmsg, sizeof (_ses_errmsg), fmt, ap);
+ (void) ses_set_nverrno(err, member);
+
+ n = strlen(errmsg);
+
+ while (n != 0 && errmsg[n - 1] == '\n')
+ errmsg[--n] = '\0';
+
+ (void) snprintf(errmsg + n, sizeof (_ses_errmsg) - n, ": %s",
+ strerror(err));
+
+ bcopy(errmsg, _ses_errmsg, sizeof (_ses_errmsg));
+ errno = syserr;
+
+ return (-1);
+}
+
+int
+ses_error(ses_errno_t err, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = ses_verror(err, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+ses_nverror(int err, const char *member, const char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = ses_vnverror(err, member, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+int
+ses_libscsi_error(libscsi_hdl_t *shp, const char *fmt, ...)
+{
+ va_list ap;
+ char errmsg[LIBSES_ERRMSGLEN];
+ libscsi_errno_t se = libscsi_errno(shp);
+ ses_errno_t e;
+
+ switch (se) {
+ case ESCSI_NONE:
+ return (0);
+ case ESCSI_NOMEM:
+ e = ESES_NOMEM;
+ break;
+ case ESCSI_NOTSUP:
+ e = ESES_NOTSUP;
+ break;
+ case ESCSI_ZERO_LENGTH:
+ case ESCSI_VERSION:
+ case ESCSI_BADFLAGS:
+ case ESCSI_BOGUSFLAGS:
+ case ESCSI_BADLENGTH:
+ case ESCSI_NEEDBUF:
+ va_start(ap, fmt);
+ (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
+ va_end(ap);
+ ses_panic("%s: unexpected libscsi error %s: %s", errmsg,
+ libscsi_errname(se), libscsi_errmsg(shp));
+ break;
+ case ESCSI_UNKNOWN:
+ e = ESES_UNKNOWN;
+ break;
+ default:
+ e = ESES_LIBSCSI;
+ break;
+ }
+
+ va_start(ap, fmt);
+ (void) vsnprintf(errmsg, sizeof (errmsg), fmt, ap);
+ va_end(ap);
+
+ return (ses_error(e, "%s: %s", errmsg, libscsi_errmsg(shp)));
+}
+
+int
+ses_scsi_error(libscsi_action_t *ap, const char *fmt, ...)
+{
+ va_list args;
+ char errmsg[LIBSES_ERRMSGLEN];
+ uint64_t asc = 0, ascq = 0, key = 0;
+ const char *code, *keystr;
+
+ va_start(args, fmt);
+ (void) vsnprintf(errmsg, sizeof (errmsg), fmt, args);
+ va_end(args);
+
+ if (libscsi_action_parse_sense(ap, &key, &asc, &ascq, NULL) != 0)
+ return (ses_error(ESES_LIBSCSI,
+ "%s: SCSI status %d (no sense data available)", errmsg,
+ libscsi_action_get_status(ap)));
+
+ code = libscsi_sense_code_name(asc, ascq);
+ keystr = libscsi_sense_key_name(key);
+
+ return (ses_error(ESES_LIBSCSI, "%s: SCSI status %d sense key %llu "
+ "(%s) additional sense code 0x%llx/0x%llx (%s)", errmsg,
+ libscsi_action_get_status(ap), key, keystr ? keystr : "<unknown>",
+ asc, ascq, code ? code : "<unknown>"));
+}
+
+void *
+ses_alloc(size_t sz)
+{
+ void *p;
+
+ if (sz == 0)
+ ses_panic("attempted zero-length allocation");
+
+ if ((p = malloc(sz)) == NULL)
+ (void) ses_set_errno(ESES_NOMEM);
+
+ return (p);
+}
+
+void *
+ses_zalloc(size_t sz)
+{
+ void *p;
+
+ if ((p = ses_alloc(sz)) != NULL)
+ bzero(p, sz);
+
+ return (p);
+}
+
+char *
+ses_strdup(const char *s)
+{
+ char *p;
+ size_t len;
+
+ if (s == NULL)
+ ses_panic("attempted zero-length allocation");
+
+ len = strlen(s) + 1;
+
+ if ((p = ses_alloc(len)) != NULL)
+ bcopy(s, p, len);
+
+ return (p);
+}
+
+void *
+ses_realloc(void *p, size_t sz)
+{
+ if (sz == 0)
+ ses_panic("attempted zero-length allocation");
+
+ if ((p = realloc(p, sz)) == NULL)
+ (void) ses_set_errno(ESES_NOMEM);
+
+ return (p);
+}
+
+/*ARGSUSED*/
+void
+ses_free(void *p)
+{
+ free(p);
+}