diff options
Diffstat (limited to 'usr/src/uts/sun4v/io/mdeg.c')
| -rw-r--r-- | usr/src/uts/sun4v/io/mdeg.c | 914 |
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 */ |
