summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4v/io/mdeg.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4v/io/mdeg.c')
-rw-r--r--usr/src/uts/sun4v/io/mdeg.c914
1 files changed, 914 insertions, 0 deletions
diff --git a/usr/src/uts/sun4v/io/mdeg.c b/usr/src/uts/sun4v/io/mdeg.c
new file mode 100644
index 0000000000..879f8b9725
--- /dev/null
+++ b/usr/src/uts/sun4v/io/mdeg.c
@@ -0,0 +1,914 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * MD Event Generator (MDEG) Module
+ */
+
+#include <sys/machsystm.h>
+#include <sys/taskq.h>
+#include <sys/disp.h>
+#include <sys/cmn_err.h>
+#include <sys/note.h>
+
+#include <sys/mdeg.h>
+#include <sys/mach_descrip.h>
+#include <sys/mdesc.h>
+
+/*
+ * A single client registration
+ */
+typedef struct mdeg_clnt {
+ boolean_t valid; /* structure is in active use */
+ mdeg_node_match_t *nmatch; /* node match filter */
+ mdeg_node_spec_t *pspec; /* parent match filter */
+ mdeg_cb_t cb; /* the client callback */
+ caddr_t cb_arg; /* argument to the callback */
+ uint64_t magic; /* sanity checking magic */
+ mdeg_handle_t hdl; /* handle assigned by MDEG */
+} mdeg_clnt_t;
+
+/*
+ * Global MDEG data
+ *
+ * Locking Strategy:
+ *
+ * mdeg.lock - lock used to sychronize system wide MD updates. An
+ * MD update must be treated as an atomic event. The lock is
+ * taken when notification that a new MD is available and held
+ * until all clients have been notified.
+ *
+ * mdeg.rwlock - lock used to sychronize access to the table of
+ * registered clients. The reader lock must be held when looking
+ * up client information in the table. The writer lock must be
+ * held when modifying any client information.
+ */
+static struct mdeg {
+ taskq_t *taskq; /* for internal processing */
+ boolean_t enabled; /* enable/disable taskq processing */
+ kmutex_t lock; /* synchronize MD updates */
+ md_t *md_prev; /* previous MD */
+ md_t *md_curr; /* current MD */
+ mdeg_clnt_t *tbl; /* table of registered clients */
+ krwlock_t rwlock; /* client table lock */
+ uint_t maxclnts; /* client table size */
+ uint_t nclnts; /* current number of clients */
+} mdeg;
+
+/*
+ * Debugging routines
+ */
+#ifdef DEBUG
+uint_t mdeg_debug = 0x0;
+
+static void mdeg_dump_clnt(mdeg_clnt_t *clnt);
+static void mdeg_dump_table(void);
+
+#define MDEG_DBG if (mdeg_debug) printf
+#define MDEG_DUMP_CLNT mdeg_dump_clnt
+#define MDEG_DUMP_TABLE mdeg_dump_table
+
+#else /* DEBUG */
+
+#define MDEG_DBG _NOTE(CONSTCOND) if (0) printf
+#define MDEG_DUMP_CLNT
+#define MDEG_DUMP_TABLE()
+
+#endif /* DEBUG */
+
+/*
+ * Global constants
+ */
+#define MDEG_MAX_TASKQ_THR 512 /* maximum number of taskq threads */
+#define MDEG_MAX_CLNTS_INIT 64 /* initial client table size */
+
+#define MDEG_MAGIC 0x4D4445475F48444Cull /* 'MDEG_HDL' */
+
+/*
+ * A client handle is a 64 bit value with two pieces of
+ * information encoded in it. The upper 32 bits are the
+ * index into the table of a particular client structure.
+ * The lower 32 bits are a counter that is incremented
+ * each time a client structure is reused.
+ */
+#define MDEG_IDX_SHIFT 32
+#define MDEG_COUNT_MASK 0xfffffffful
+
+#define MDEG_ALLOC_HDL(_idx, _count) (((uint64_t)_idx << MDEG_IDX_SHIFT) | \
+ ((uint64_t)(_count + 1) & \
+ MDEG_COUNT_MASK))
+#define MDEG_HDL2IDX(hdl) (hdl >> MDEG_IDX_SHIFT)
+#define MDEG_HDL2COUNT(hdl) (hdl & MDEG_COUNT_MASK)
+
+static const char trunc_str[] = " ... }";
+
+/*
+ * Utility routines
+ */
+static mdeg_clnt_t *mdeg_alloc_clnt(void);
+static void mdeg_notify_client(void *);
+static mde_cookie_t mdeg_find_start_node(md_t *, mdeg_node_spec_t *);
+static boolean_t mdeg_node_spec_match(md_t *, mde_cookie_t, mdeg_node_spec_t *);
+static void mdeg_get_diff_results(md_diff_cookie_t, mdeg_result_t *);
+
+int
+mdeg_init(void)
+{
+ int tblsz;
+
+ /*
+ * Grab the current MD
+ */
+ if ((mdeg.md_curr = md_get_handle()) == NULL) {
+ cmn_err(CE_WARN, "unable to cache snapshot of MD");
+ return (-1);
+ }
+
+ /*
+ * Initialize table of registered clients
+ */
+ mdeg.maxclnts = MDEG_MAX_CLNTS_INIT;
+
+ tblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
+ mdeg.tbl = kmem_zalloc(tblsz, KM_SLEEP);
+
+ rw_init(&mdeg.rwlock, NULL, RW_DRIVER, NULL);
+
+ mdeg.nclnts = 0;
+
+ /*
+ * Initialize global lock
+ */
+ mutex_init(&mdeg.lock, NULL, MUTEX_DRIVER, NULL);
+
+ /*
+ * Initialize the task queue
+ */
+ mdeg.taskq = taskq_create("mdeg_taskq", 1, minclsyspri, 1,
+ MDEG_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
+
+ /* ready to begin handling clients */
+ mdeg.enabled = B_TRUE;
+
+ return (0);
+}
+
+void
+mdeg_fini(void)
+{
+ /*
+ * Flip the enabled switch off to make sure that
+ * no events get dispatched while things are being
+ * torn down.
+ */
+ mdeg.enabled = B_FALSE;
+
+ /* destroy the task queue */
+ taskq_destroy(mdeg.taskq);
+
+ /*
+ * Deallocate the table of registered clients
+ */
+ kmem_free(mdeg.tbl, mdeg.maxclnts * sizeof (mdeg_clnt_t));
+ rw_destroy(&mdeg.rwlock);
+
+ /*
+ * Free up the cached MDs.
+ */
+ if (mdeg.md_curr)
+ (void) md_fini_handle(mdeg.md_curr);
+
+ if (mdeg.md_prev)
+ (void) md_fini_handle(mdeg.md_prev);
+
+ mutex_destroy(&mdeg.lock);
+}
+
+static mdeg_clnt_t *
+mdeg_alloc_clnt(void)
+{
+ mdeg_clnt_t *clnt;
+ int idx;
+ mdeg_clnt_t *newtbl;
+ uint_t newmaxclnts;
+ uint_t newtblsz;
+ uint_t oldtblsz;
+
+ ASSERT(RW_WRITE_HELD(&mdeg.rwlock));
+
+ /* search for an unused slot in the table */
+ for (idx = 0; idx < mdeg.maxclnts; idx++) {
+ clnt = &mdeg.tbl[idx];
+ if (!clnt->valid) {
+ break;
+ }
+ }
+
+ /* found any empty slot */
+ if (idx != mdeg.maxclnts) {
+ goto found;
+ }
+
+ /*
+ * There was no free space in the table. Grow
+ * the table to double its current size.
+ */
+
+ MDEG_DBG("client table full:\n");
+ MDEG_DUMP_TABLE();
+
+ newmaxclnts = mdeg.maxclnts * 2;
+ newtblsz = newmaxclnts * sizeof (mdeg_clnt_t);
+
+ newtbl = kmem_zalloc(newtblsz, KM_SLEEP);
+
+ /* copy old table data to the new table */
+ oldtblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
+ bcopy(mdeg.tbl, newtbl, oldtblsz);
+
+ /*
+ * Since the old table was full, the first free entry
+ * will be just past the end of the old table.
+ */
+ clnt = &mdeg.tbl[mdeg.maxclnts];
+
+ /* clean up the old table */
+ kmem_free(mdeg.tbl, oldtblsz);
+ mdeg.tbl = newtbl;
+ mdeg.maxclnts = newmaxclnts;
+
+found:
+ ASSERT(clnt->valid == 0);
+
+ clnt->hdl = MDEG_ALLOC_HDL(idx, MDEG_HDL2COUNT(clnt->hdl));
+
+ return (clnt);
+}
+
+static mdeg_clnt_t *
+mdeg_get_client(mdeg_handle_t hdl)
+{
+ int idx;
+ mdeg_clnt_t *clnt;
+
+ idx = MDEG_HDL2IDX(hdl);
+
+ /* check if index is out of bounds */
+ if ((idx < 0) || (idx >= mdeg.maxclnts)) {
+ MDEG_DBG("mdeg_get_client: index out of bounds\n");
+ return (NULL);
+ }
+
+ clnt = &mdeg.tbl[idx];
+
+ /* check for a valid client */
+ if (!clnt->valid) {
+ MDEG_DBG("mdeg_get_client: client is not valid\n");
+ return (NULL);
+ }
+
+ /* make sure the handle is an exact match */
+ if (clnt->hdl != hdl) {
+ MDEG_DBG("mdeg_get_client: bad handle\n");
+ return (NULL);
+ }
+
+ if (clnt->magic != MDEG_MAGIC) {
+ MDEG_DBG("mdeg_get_client: bad magic\n");
+ return (NULL);
+ }
+
+ return (clnt);
+}
+
+/*
+ * Send a notification to a client immediately after it registers.
+ * The result_t is a list of all the nodes that match their specified
+ * nodes of interest, all returned on the added list. This serves
+ * as a base of reference to the client. All future MD updates are
+ * relative to this list.
+ */
+static int
+mdeg_notify_client_reg(mdeg_clnt_t *clnt)
+{
+ md_t *mdp = NULL;
+ mde_str_cookie_t nname;
+ mde_str_cookie_t aname;
+ mde_cookie_t startnode;
+ int nnodes;
+ int nodechk;
+ mde_cookie_t *listp = NULL;
+ mdeg_result_t *mdeg_res = NULL;
+ int rv = MDEG_SUCCESS;
+
+ mutex_enter(&mdeg.lock);
+
+ /*
+ * Handle the special case where the node specification
+ * is NULL. In this case, call the client callback without
+ * any results. All processing is left to the client.
+ */
+ if (clnt->pspec == NULL) {
+ /* call the client callback */
+ (*clnt->cb)(clnt->cb_arg, NULL);
+ goto done;
+ }
+
+ if ((mdp = md_get_handle()) == NULL) {
+ cmn_err(CE_WARN, "unable to retrieve current MD");
+ rv = MDEG_FAILURE;
+ goto done;
+ }
+
+ startnode = mdeg_find_start_node(mdp, clnt->pspec);
+ if (startnode == MDE_INVAL_ELEM_COOKIE) {
+ /* not much we can do */
+ cmn_err(CE_WARN, "unable to match node specifier");
+ rv = MDEG_FAILURE;
+ goto done;
+ }
+
+ /*
+ * Use zalloc to provide correct default values for the
+ * unused removed, match_prev, and match_curr lists.
+ */
+ mdeg_res = kmem_zalloc(sizeof (mdeg_result_t), KM_SLEEP);
+
+ nname = md_find_name(mdp, clnt->nmatch->namep);
+ aname = md_find_name(mdp, "fwd");
+
+ nnodes = md_scan_dag(mdp, startnode, nname, aname, NULL);
+
+ if (nnodes == 0) {
+ MDEG_DBG("mdeg_notify_client_reg: no nodes of interest\n");
+ rv = MDEG_SUCCESS;
+ goto done;
+ } else if (nnodes == -1) {
+ MDEG_DBG("error scanning DAG\n");
+ rv = MDEG_FAILURE;
+ goto done;
+ }
+
+ MDEG_DBG("mdeg_notify_client_reg: %d node%s of interest\n",
+ nnodes, (nnodes == 1) ? "" : "s");
+
+ /* get the list of nodes of interest */
+ listp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
+ nodechk = md_scan_dag(mdp, startnode, nname, aname, listp);
+
+ ASSERT(nodechk == nnodes);
+
+ mdeg_res->added.mdp = mdp;
+ mdeg_res->added.mdep = listp;
+ mdeg_res->added.nelem = nnodes;
+
+ /* call the client callback */
+ (*clnt->cb)(clnt->cb_arg, mdeg_res);
+
+done:
+ mutex_exit(&mdeg.lock);
+
+ if (mdp)
+ (void) md_fini_handle(mdp);
+
+ if (listp)
+ kmem_free(listp, sizeof (mde_cookie_t) * nnodes);
+
+ if (mdeg_res)
+ kmem_free(mdeg_res, sizeof (mdeg_result_t));
+
+ return (rv);
+}
+
+/*
+ * Register to receive an event notification when the system
+ * machine description is updated.
+ *
+ * Passing NULL for the node specification parameter is valid
+ * as long as the match specification is also NULL. In this
+ * case, the client will receive a notification when the MD
+ * has been updated, but the callback will not include any
+ * information. The client is then responsible for obtaining
+ * its own copy of the system MD and performing any processing
+ * manually.
+ */
+int
+mdeg_register(mdeg_node_spec_t *pspecp, mdeg_node_match_t *nmatchp,
+ mdeg_cb_t cb, void *cb_arg, mdeg_handle_t *hdlp)
+{
+ mdeg_clnt_t *clnt;
+
+ /*
+ * If the RW lock is held, a client is calling
+ * register from its own callback.
+ */
+ if (RW_LOCK_HELD(&mdeg.rwlock)) {
+ MDEG_DBG("mdeg_register: rwlock already held\n");
+ return (MDEG_FAILURE);
+ }
+
+ /* node spec and node match must both be valid, or both NULL */
+ if (((pspecp != NULL) && (nmatchp == NULL)) ||
+ ((pspecp == NULL) && (nmatchp != NULL))) {
+ MDEG_DBG("mdeg_register: invalid parameters\n");
+ return (MDEG_FAILURE);
+ }
+
+ rw_enter(&mdeg.rwlock, RW_WRITER);
+
+ clnt = mdeg_alloc_clnt();
+
+ ASSERT(clnt);
+
+ /*
+ * Fill in the rest of the data
+ */
+ clnt->nmatch = nmatchp;
+ clnt->pspec = pspecp;
+ clnt->cb = cb;
+ clnt->cb_arg = cb_arg;
+ clnt->magic = MDEG_MAGIC;
+
+ /* do this last */
+ clnt->valid = B_TRUE;
+
+ MDEG_DBG("client registered (0x%lx):\n", clnt->hdl);
+ MDEG_DUMP_CLNT(clnt);
+
+ mdeg.nclnts++;
+
+ if (mdeg_notify_client_reg(clnt) != MDEG_SUCCESS) {
+ bzero(clnt, sizeof (mdeg_clnt_t));
+ rw_exit(&mdeg.rwlock);
+ return (MDEG_FAILURE);
+ }
+
+ rw_exit(&mdeg.rwlock);
+
+ *hdlp = clnt->hdl;
+
+ return (MDEG_SUCCESS);
+}
+
+int
+mdeg_unregister(mdeg_handle_t hdl)
+{
+ mdeg_clnt_t *clnt;
+ mdeg_handle_t mdh;
+
+ /*
+ * If the RW lock is held, a client is calling
+ * unregister from its own callback.
+ */
+ if (RW_LOCK_HELD(&mdeg.rwlock)) {
+ MDEG_DBG("mdeg_unregister: rwlock already held\n");
+ return (MDEG_FAILURE);
+ }
+
+ /* lookup the client */
+ if ((clnt = mdeg_get_client(hdl)) == NULL) {
+ return (MDEG_FAILURE);
+ }
+
+ rw_enter(&mdeg.rwlock, RW_WRITER);
+
+ MDEG_DBG("client unregistered (0x%lx):\n", hdl);
+ MDEG_DUMP_CLNT(clnt);
+
+ /* save the handle to prevent reuse */
+ mdh = clnt->hdl;
+ bzero(clnt, sizeof (mdeg_clnt_t));
+
+ clnt->hdl = mdh;
+
+ mdeg.nclnts--;
+
+ rw_exit(&mdeg.rwlock);
+
+ return (MDEG_SUCCESS);
+}
+
+/*
+ * Simple algorithm for now, grab the global lock and let all
+ * the clients update themselves in parallel. There is a lot of
+ * room for improvement here. We could eliminate some scans of
+ * the DAG by imcrementally scanning at lower levels of the DAG
+ * rather than having each client start its own scan from the root.
+ */
+void
+mdeg_notify_clients(void)
+{
+ md_t *md_new;
+ mdeg_clnt_t *clnt;
+ int idx;
+ int nclnt;
+
+ rw_enter(&mdeg.rwlock, RW_READER);
+ mutex_enter(&mdeg.lock);
+
+ /*
+ * Rotate the MDs
+ */
+ if ((md_new = md_get_handle()) == NULL) {
+ cmn_err(CE_WARN, "unable to retrieve new MD");
+ goto done;
+ }
+
+ if (mdeg.md_prev) {
+ (void) md_fini_handle(mdeg.md_prev);
+ }
+
+ mdeg.md_prev = mdeg.md_curr;
+ mdeg.md_curr = md_new;
+
+ if (mdeg.nclnts == 0) {
+ MDEG_DBG("mdeg_notify_clients: no clients registered\n");
+ goto done;
+ }
+
+ /* dispatch the update notification to all clients */
+ for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) {
+ clnt = &mdeg.tbl[idx];
+
+ if (!clnt->valid)
+ continue;
+
+ MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl,
+ ++nclnt, mdeg.nclnts);
+
+ (void) taskq_dispatch(mdeg.taskq, mdeg_notify_client,
+ (void *)clnt, TQ_SLEEP);
+ }
+
+ taskq_wait(mdeg.taskq);
+
+done:
+ mutex_exit(&mdeg.lock);
+ rw_exit(&mdeg.rwlock);
+}
+
+static void
+mdeg_notify_client(void *arg)
+{
+ mdeg_clnt_t *clnt = (mdeg_clnt_t *)arg;
+ md_diff_cookie_t mdd = MD_INVAL_DIFF_COOKIE;
+ mdeg_result_t mdeg_res;
+ mde_cookie_t md_prev_start;
+ mde_cookie_t md_curr_start;
+
+ rw_enter(&mdeg.rwlock, RW_READER);
+
+ if (!mdeg.enabled) {
+ /* trying to shutdown */
+ MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n");
+ goto cleanup;
+ }
+
+ /*
+ * Handle the special case where the node specification
+ * is NULL. In this case, call the client callback without
+ * any results. All processing is left to the client.
+ */
+ if (clnt->pspec == NULL) {
+ /* call the client callback */
+ (*clnt->cb)(clnt->cb_arg, NULL);
+
+ MDEG_DBG("MDEG client callback done\n");
+ goto cleanup;
+ }
+
+ /* find our start nodes */
+ md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec);
+ if (md_prev_start == MDE_INVAL_ELEM_COOKIE) {
+ goto cleanup;
+ }
+
+ md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec);
+ if (md_curr_start == MDE_INVAL_ELEM_COOKIE) {
+ goto cleanup;
+ }
+
+ /* diff the MDs */
+ mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr,
+ md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp);
+
+ if (mdd == MD_INVAL_DIFF_COOKIE) {
+ MDEG_DBG("unable to diff MDs\n");
+ goto cleanup;
+ }
+
+ /*
+ * Cache the results of the diff
+ */
+ mdeg_get_diff_results(mdd, &mdeg_res);
+
+ /* call the client callback */
+ (*clnt->cb)(clnt->cb_arg, &mdeg_res);
+
+ MDEG_DBG("MDEG client callback done\n");
+
+cleanup:
+ rw_exit(&mdeg.rwlock);
+
+ if (mdd != MD_INVAL_DIFF_COOKIE)
+ (void) md_diff_fini(mdd);
+}
+
+static mde_cookie_t
+mdeg_find_start_node(md_t *md, mdeg_node_spec_t *nspec)
+{
+ mde_cookie_t *nodesp;
+ mde_str_cookie_t nname;
+ mde_str_cookie_t aname;
+ int nnodes;
+ int idx;
+
+ if ((md == NULL) || (nspec == NULL))
+ return (MDE_INVAL_ELEM_COOKIE);
+
+ nname = md_find_name(md, nspec->namep);
+ aname = md_find_name(md, "fwd");
+
+ nnodes = md_scan_dag(md, NULL, nname, aname, NULL);
+ if (nnodes == 0)
+ return (MDE_INVAL_ELEM_COOKIE);
+
+ nodesp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
+
+ (void) md_scan_dag(md, NULL, nname, aname, nodesp);
+
+ for (idx = 0; idx < nnodes; idx++) {
+
+ if (mdeg_node_spec_match(md, nodesp[idx], nspec)) {
+ mde_cookie_t res = nodesp[idx];
+
+ kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
+ return (res);
+ }
+ }
+
+ kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
+ return (MDE_INVAL_ELEM_COOKIE);
+}
+
+static boolean_t
+mdeg_node_spec_match(md_t *md, mde_cookie_t node, mdeg_node_spec_t *nspec)
+{
+ mdeg_prop_spec_t *prop;
+
+ ASSERT(md && nspec);
+ ASSERT(node != MDE_INVAL_ELEM_COOKIE);
+
+ prop = nspec->specp;
+
+ while (prop->type != MDET_LIST_END) {
+
+ switch (prop->type) {
+ case MDET_PROP_VAL: {
+ uint64_t val;
+
+ if (md_get_prop_val(md, node, prop->namep, &val) != 0)
+ return (B_FALSE);
+
+ if (prop->ps_val != val)
+ return (B_FALSE);
+
+ break;
+ }
+ case MDET_PROP_STR: {
+ char *str;
+
+ if (md_get_prop_str(md, node, prop->namep, &str) != 0)
+ return (B_FALSE);
+
+ if (strcmp(prop->ps_str, str) != 0)
+ return (B_FALSE);
+
+ break;
+ }
+
+ default:
+ return (B_FALSE);
+ }
+
+ prop++;
+ }
+
+ return (B_TRUE);
+}
+
+static void
+mdeg_get_diff_results(md_diff_cookie_t mdd, mdeg_result_t *res)
+{
+ /*
+ * Cache added nodes.
+ */
+ res->added.mdp = mdeg.md_curr;
+ res->added.nelem = md_diff_added(mdd, &(res->added.mdep));
+
+ if (res->added.nelem == -1) {
+ bzero(&(res->added), sizeof (mdeg_diff_t));
+ }
+
+ /*
+ * Cache removed nodes.
+ */
+ res->removed.mdp = mdeg.md_prev;
+ res->removed.nelem = md_diff_removed(mdd, &(res->removed.mdep));
+
+ if (res->removed.nelem == -1) {
+ bzero(&(res->removed), sizeof (mdeg_diff_t));
+ }
+
+ /*
+ * Cache matching node pairs.
+ */
+ res->match_curr.mdp = mdeg.md_curr;
+ res->match_prev.mdp = mdeg.md_prev;
+ res->match_curr.nelem = md_diff_matched(mdd, &(res->match_prev.mdep),
+ &(res->match_curr.mdep));
+ res->match_prev.nelem = res->match_curr.nelem;
+
+ if (res->match_prev.nelem == -1) {
+ bzero(&(res->match_prev), sizeof (mdeg_diff_t));
+ bzero(&(res->match_curr), sizeof (mdeg_diff_t));
+ }
+}
+
+#ifdef DEBUG
+/*
+ * Generate a string that represents the node specifier
+ * structure. Clamp the string length if the specifier
+ * structure contains too much information.
+ *
+ * General form:
+ *
+ * <nodename>:{<propname>=<propval>,...}
+ * e.g.
+ * vdevice:{name=vsw,reg=0x0}
+ */
+static void
+mdeg_spec_str(mdeg_node_spec_t *spec, char *buf, int len)
+{
+ mdeg_prop_spec_t *prop;
+ int offset;
+ boolean_t first = B_TRUE;
+ char *end = buf + len;
+
+ offset = snprintf(buf, len, "%s:{", spec->namep);
+
+ buf += offset;
+ len -= offset;
+ if (len <= 0)
+ goto trunc;
+
+ prop = spec->specp;
+
+ while (prop->type != MDET_LIST_END) {
+
+ switch (prop->type) {
+ case MDET_PROP_VAL:
+ offset = snprintf(buf, len, "%s%s=0x%lx",
+ (first) ? "" : ",", prop->namep, prop->ps_val);
+ buf += offset;
+ len -= offset;
+ if (len <= 0)
+ goto trunc;
+ break;
+
+ case MDET_PROP_STR:
+ offset = snprintf(buf, len, "%s%s=%s",
+ (first) ? "" : ",", prop->namep, prop->ps_str);
+ buf += offset;
+ len -= offset;
+ if (len <= 0)
+ goto trunc;
+ break;
+
+ default:
+ (void) snprintf(buf, len, "}");
+ return;
+ }
+
+ if (first)
+ first = B_FALSE;
+ prop++;
+ }
+
+ (void) snprintf(buf, len, "}");
+ return;
+
+trunc:
+ /* string too long, truncate it */
+ buf = end - (strlen(trunc_str) + 1);
+ (void) sprintf(buf, trunc_str);
+}
+
+/*
+ * Generate a string that represents the match structure.
+ * Clamp the string length if the match structure contains
+ * too much information.
+ *
+ * General form:
+ *
+ * <nodename>:{<propname>,...}
+ * e.g.
+ * nmatch=vport:{reg}
+ */
+static void
+mdeg_match_str(mdeg_node_match_t *match, char *buf, int len)
+{
+ md_prop_match_t *prop;
+ int offset;
+ boolean_t first = B_TRUE;
+ char *end = buf + len;
+
+ offset = snprintf(buf, len, "%s:{", match->namep);
+
+ buf += offset;
+ len -= offset;
+ if (len <= 0)
+ goto trunc;
+
+ prop = match->matchp;
+
+ while (prop->type != MDET_LIST_END) {
+ offset = snprintf(buf, len, "%s%s", (first) ? "" : ",",
+ prop->namep);
+ buf += offset;
+ len -= offset;
+ if (len <= 0)
+ goto trunc;
+
+ if (first)
+ first = B_FALSE;
+ prop++;
+ }
+
+ (void) snprintf(buf, len, "}");
+ return;
+
+trunc:
+ /* string too long, truncate it */
+ buf = end - (strlen(trunc_str) + 1);
+ (void) sprintf(buf, trunc_str);
+}
+
+#define MAX_FIELD_STR 80
+
+static void
+mdeg_dump_clnt(mdeg_clnt_t *clnt)
+{
+ char str[MAX_FIELD_STR];
+
+ if (!clnt->valid) {
+ MDEG_DBG(" valid=B_FALSE\n");
+ return;
+ }
+
+ mdeg_spec_str(clnt->pspec, str, MAX_FIELD_STR);
+ MDEG_DBG(" pspecp=%s\n", str);
+
+ mdeg_match_str(clnt->nmatch, str, MAX_FIELD_STR);
+ MDEG_DBG(" nmatch=%s\n", str);
+}
+
+static void
+mdeg_dump_table(void)
+{
+ int idx;
+ mdeg_clnt_t *clnt;
+
+ for (idx = 0; idx < mdeg.maxclnts; idx++) {
+ clnt = &(mdeg.tbl[idx]);
+
+ MDEG_DBG("client %d (0x%lx):\n", idx, clnt->hdl);
+ mdeg_dump_clnt(clnt);
+ }
+}
+#endif /* DEBUG */