xref: /titanic_44/usr/src/uts/sun4v/io/mdeg.c (revision 1ae0874509b6811fdde1dfd46f0d93fd09867a3f)
1*1ae08745Sheppo /*
2*1ae08745Sheppo  * CDDL HEADER START
3*1ae08745Sheppo  *
4*1ae08745Sheppo  * The contents of this file are subject to the terms of the
5*1ae08745Sheppo  * Common Development and Distribution License (the "License").
6*1ae08745Sheppo  * You may not use this file except in compliance with the License.
7*1ae08745Sheppo  *
8*1ae08745Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1ae08745Sheppo  * or http://www.opensolaris.org/os/licensing.
10*1ae08745Sheppo  * See the License for the specific language governing permissions
11*1ae08745Sheppo  * and limitations under the License.
12*1ae08745Sheppo  *
13*1ae08745Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
14*1ae08745Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1ae08745Sheppo  * If applicable, add the following below this CDDL HEADER, with the
16*1ae08745Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
17*1ae08745Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1ae08745Sheppo  *
19*1ae08745Sheppo  * CDDL HEADER END
20*1ae08745Sheppo  */
21*1ae08745Sheppo 
22*1ae08745Sheppo /*
23*1ae08745Sheppo  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*1ae08745Sheppo  * Use is subject to license terms.
25*1ae08745Sheppo  */
26*1ae08745Sheppo 
27*1ae08745Sheppo #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*1ae08745Sheppo 
29*1ae08745Sheppo /*
30*1ae08745Sheppo  * MD Event Generator (MDEG) Module
31*1ae08745Sheppo  */
32*1ae08745Sheppo 
33*1ae08745Sheppo #include <sys/machsystm.h>
34*1ae08745Sheppo #include <sys/taskq.h>
35*1ae08745Sheppo #include <sys/disp.h>
36*1ae08745Sheppo #include <sys/cmn_err.h>
37*1ae08745Sheppo #include <sys/note.h>
38*1ae08745Sheppo 
39*1ae08745Sheppo #include <sys/mdeg.h>
40*1ae08745Sheppo #include <sys/mach_descrip.h>
41*1ae08745Sheppo #include <sys/mdesc.h>
42*1ae08745Sheppo 
43*1ae08745Sheppo /*
44*1ae08745Sheppo  * A single client registration
45*1ae08745Sheppo  */
46*1ae08745Sheppo typedef struct mdeg_clnt {
47*1ae08745Sheppo 	boolean_t		valid;		/* structure is in active use */
48*1ae08745Sheppo 	mdeg_node_match_t	*nmatch;	/* node match filter */
49*1ae08745Sheppo 	mdeg_node_spec_t	*pspec;		/* parent match filter */
50*1ae08745Sheppo 	mdeg_cb_t		cb;		/* the client callback */
51*1ae08745Sheppo 	caddr_t			cb_arg;		/* argument to the callback */
52*1ae08745Sheppo 	uint64_t		magic;		/* sanity checking magic */
53*1ae08745Sheppo 	mdeg_handle_t		hdl;		/* handle assigned by MDEG */
54*1ae08745Sheppo } mdeg_clnt_t;
55*1ae08745Sheppo 
56*1ae08745Sheppo /*
57*1ae08745Sheppo  * Global MDEG data
58*1ae08745Sheppo  *
59*1ae08745Sheppo  * Locking Strategy:
60*1ae08745Sheppo  *
61*1ae08745Sheppo  *   mdeg.lock - lock used to sychronize system wide MD updates. An
62*1ae08745Sheppo  *	MD update must be treated as an atomic event. The lock is
63*1ae08745Sheppo  *	taken when notification that a new MD is available and held
64*1ae08745Sheppo  *	until all clients have been notified.
65*1ae08745Sheppo  *
66*1ae08745Sheppo  *   mdeg.rwlock - lock used to sychronize access to the table of
67*1ae08745Sheppo  *	registered clients. The reader lock must be held when looking
68*1ae08745Sheppo  *	up client information in the table. The writer lock must be
69*1ae08745Sheppo  *	held when modifying any client information.
70*1ae08745Sheppo  */
71*1ae08745Sheppo static struct mdeg {
72*1ae08745Sheppo 	taskq_t 	*taskq;		/* for internal processing */
73*1ae08745Sheppo 	boolean_t	enabled;	/* enable/disable taskq processing */
74*1ae08745Sheppo 	kmutex_t	lock;		/* synchronize MD updates */
75*1ae08745Sheppo 	md_t		*md_prev;	/* previous MD */
76*1ae08745Sheppo 	md_t		*md_curr;	/* current MD */
77*1ae08745Sheppo 	mdeg_clnt_t	*tbl;		/* table of registered clients */
78*1ae08745Sheppo 	krwlock_t	rwlock;		/* client table lock */
79*1ae08745Sheppo 	uint_t		maxclnts;	/* client table size */
80*1ae08745Sheppo 	uint_t		nclnts;		/* current number of clients */
81*1ae08745Sheppo } mdeg;
82*1ae08745Sheppo 
83*1ae08745Sheppo /*
84*1ae08745Sheppo  * Debugging routines
85*1ae08745Sheppo  */
86*1ae08745Sheppo #ifdef DEBUG
87*1ae08745Sheppo uint_t mdeg_debug = 0x0;
88*1ae08745Sheppo 
89*1ae08745Sheppo static void mdeg_dump_clnt(mdeg_clnt_t *clnt);
90*1ae08745Sheppo static void mdeg_dump_table(void);
91*1ae08745Sheppo 
92*1ae08745Sheppo #define	MDEG_DBG		if (mdeg_debug) printf
93*1ae08745Sheppo #define	MDEG_DUMP_CLNT		mdeg_dump_clnt
94*1ae08745Sheppo #define	MDEG_DUMP_TABLE		mdeg_dump_table
95*1ae08745Sheppo 
96*1ae08745Sheppo #else /* DEBUG */
97*1ae08745Sheppo 
98*1ae08745Sheppo #define	MDEG_DBG		_NOTE(CONSTCOND) if (0) printf
99*1ae08745Sheppo #define	MDEG_DUMP_CLNT
100*1ae08745Sheppo #define	MDEG_DUMP_TABLE()
101*1ae08745Sheppo 
102*1ae08745Sheppo #endif /* DEBUG */
103*1ae08745Sheppo 
104*1ae08745Sheppo /*
105*1ae08745Sheppo  * Global constants
106*1ae08745Sheppo  */
107*1ae08745Sheppo #define	MDEG_MAX_TASKQ_THR	512	/* maximum number of taskq threads */
108*1ae08745Sheppo #define	MDEG_MAX_CLNTS_INIT	64	/* initial client table size */
109*1ae08745Sheppo 
110*1ae08745Sheppo #define	MDEG_MAGIC		0x4D4445475F48444Cull	/* 'MDEG_HDL' */
111*1ae08745Sheppo 
112*1ae08745Sheppo /*
113*1ae08745Sheppo  * A client handle is a 64 bit value with two pieces of
114*1ae08745Sheppo  * information encoded in it. The upper 32 bits are the
115*1ae08745Sheppo  * index into the table of a particular client structure.
116*1ae08745Sheppo  * The lower 32 bits are a counter that is incremented
117*1ae08745Sheppo  * each time a client structure is reused.
118*1ae08745Sheppo  */
119*1ae08745Sheppo #define	MDEG_IDX_SHIFT			32
120*1ae08745Sheppo #define	MDEG_COUNT_MASK			0xfffffffful
121*1ae08745Sheppo 
122*1ae08745Sheppo #define	MDEG_ALLOC_HDL(_idx, _count)	(((uint64_t)_idx << MDEG_IDX_SHIFT) | \
123*1ae08745Sheppo 					((uint64_t)(_count + 1) &	      \
124*1ae08745Sheppo 					MDEG_COUNT_MASK))
125*1ae08745Sheppo #define	MDEG_HDL2IDX(hdl)		(hdl >> MDEG_IDX_SHIFT)
126*1ae08745Sheppo #define	MDEG_HDL2COUNT(hdl)		(hdl & MDEG_COUNT_MASK)
127*1ae08745Sheppo 
128*1ae08745Sheppo static const char trunc_str[] = " ... }";
129*1ae08745Sheppo 
130*1ae08745Sheppo /*
131*1ae08745Sheppo  * Utility routines
132*1ae08745Sheppo  */
133*1ae08745Sheppo static mdeg_clnt_t *mdeg_alloc_clnt(void);
134*1ae08745Sheppo static void mdeg_notify_client(void *);
135*1ae08745Sheppo static mde_cookie_t mdeg_find_start_node(md_t *, mdeg_node_spec_t *);
136*1ae08745Sheppo static boolean_t mdeg_node_spec_match(md_t *, mde_cookie_t, mdeg_node_spec_t *);
137*1ae08745Sheppo static void mdeg_get_diff_results(md_diff_cookie_t, mdeg_result_t *);
138*1ae08745Sheppo 
139*1ae08745Sheppo int
140*1ae08745Sheppo mdeg_init(void)
141*1ae08745Sheppo {
142*1ae08745Sheppo 	int	tblsz;
143*1ae08745Sheppo 
144*1ae08745Sheppo 	/*
145*1ae08745Sheppo 	 * Grab the current MD
146*1ae08745Sheppo 	 */
147*1ae08745Sheppo 	if ((mdeg.md_curr = md_get_handle()) == NULL) {
148*1ae08745Sheppo 		cmn_err(CE_WARN, "unable to cache snapshot of MD");
149*1ae08745Sheppo 		return (-1);
150*1ae08745Sheppo 	}
151*1ae08745Sheppo 
152*1ae08745Sheppo 	/*
153*1ae08745Sheppo 	 * Initialize table of registered clients
154*1ae08745Sheppo 	 */
155*1ae08745Sheppo 	mdeg.maxclnts = MDEG_MAX_CLNTS_INIT;
156*1ae08745Sheppo 
157*1ae08745Sheppo 	tblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
158*1ae08745Sheppo 	mdeg.tbl = kmem_zalloc(tblsz, KM_SLEEP);
159*1ae08745Sheppo 
160*1ae08745Sheppo 	rw_init(&mdeg.rwlock, NULL, RW_DRIVER, NULL);
161*1ae08745Sheppo 
162*1ae08745Sheppo 	mdeg.nclnts = 0;
163*1ae08745Sheppo 
164*1ae08745Sheppo 	/*
165*1ae08745Sheppo 	 * Initialize global lock
166*1ae08745Sheppo 	 */
167*1ae08745Sheppo 	mutex_init(&mdeg.lock, NULL, MUTEX_DRIVER, NULL);
168*1ae08745Sheppo 
169*1ae08745Sheppo 	/*
170*1ae08745Sheppo 	 * Initialize the task queue
171*1ae08745Sheppo 	 */
172*1ae08745Sheppo 	mdeg.taskq = taskq_create("mdeg_taskq", 1, minclsyspri, 1,
173*1ae08745Sheppo 	    MDEG_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
174*1ae08745Sheppo 
175*1ae08745Sheppo 	/* ready to begin handling clients */
176*1ae08745Sheppo 	mdeg.enabled = B_TRUE;
177*1ae08745Sheppo 
178*1ae08745Sheppo 	return (0);
179*1ae08745Sheppo }
180*1ae08745Sheppo 
181*1ae08745Sheppo void
182*1ae08745Sheppo mdeg_fini(void)
183*1ae08745Sheppo {
184*1ae08745Sheppo 	/*
185*1ae08745Sheppo 	 * Flip the enabled switch off to make sure that
186*1ae08745Sheppo 	 * no events get dispatched while things are being
187*1ae08745Sheppo 	 * torn down.
188*1ae08745Sheppo 	 */
189*1ae08745Sheppo 	mdeg.enabled = B_FALSE;
190*1ae08745Sheppo 
191*1ae08745Sheppo 	/* destroy the task queue */
192*1ae08745Sheppo 	taskq_destroy(mdeg.taskq);
193*1ae08745Sheppo 
194*1ae08745Sheppo 	/*
195*1ae08745Sheppo 	 * Deallocate the table of registered clients
196*1ae08745Sheppo 	 */
197*1ae08745Sheppo 	kmem_free(mdeg.tbl, mdeg.maxclnts * sizeof (mdeg_clnt_t));
198*1ae08745Sheppo 	rw_destroy(&mdeg.rwlock);
199*1ae08745Sheppo 
200*1ae08745Sheppo 	/*
201*1ae08745Sheppo 	 * Free up the cached MDs.
202*1ae08745Sheppo 	 */
203*1ae08745Sheppo 	if (mdeg.md_curr)
204*1ae08745Sheppo 		(void) md_fini_handle(mdeg.md_curr);
205*1ae08745Sheppo 
206*1ae08745Sheppo 	if (mdeg.md_prev)
207*1ae08745Sheppo 		(void) md_fini_handle(mdeg.md_prev);
208*1ae08745Sheppo 
209*1ae08745Sheppo 	mutex_destroy(&mdeg.lock);
210*1ae08745Sheppo }
211*1ae08745Sheppo 
212*1ae08745Sheppo static mdeg_clnt_t *
213*1ae08745Sheppo mdeg_alloc_clnt(void)
214*1ae08745Sheppo {
215*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
216*1ae08745Sheppo 	int		idx;
217*1ae08745Sheppo 	mdeg_clnt_t	*newtbl;
218*1ae08745Sheppo 	uint_t		newmaxclnts;
219*1ae08745Sheppo 	uint_t		newtblsz;
220*1ae08745Sheppo 	uint_t		oldtblsz;
221*1ae08745Sheppo 
222*1ae08745Sheppo 	ASSERT(RW_WRITE_HELD(&mdeg.rwlock));
223*1ae08745Sheppo 
224*1ae08745Sheppo 	/* search for an unused slot in the table */
225*1ae08745Sheppo 	for (idx = 0; idx < mdeg.maxclnts; idx++) {
226*1ae08745Sheppo 		clnt = &mdeg.tbl[idx];
227*1ae08745Sheppo 		if (!clnt->valid) {
228*1ae08745Sheppo 			break;
229*1ae08745Sheppo 		}
230*1ae08745Sheppo 	}
231*1ae08745Sheppo 
232*1ae08745Sheppo 	/* found any empty slot */
233*1ae08745Sheppo 	if (idx != mdeg.maxclnts) {
234*1ae08745Sheppo 		goto found;
235*1ae08745Sheppo 	}
236*1ae08745Sheppo 
237*1ae08745Sheppo 	/*
238*1ae08745Sheppo 	 * There was no free space in the table. Grow
239*1ae08745Sheppo 	 * the table to double its current size.
240*1ae08745Sheppo 	 */
241*1ae08745Sheppo 
242*1ae08745Sheppo 	MDEG_DBG("client table full:\n");
243*1ae08745Sheppo 	MDEG_DUMP_TABLE();
244*1ae08745Sheppo 
245*1ae08745Sheppo 	newmaxclnts = mdeg.maxclnts * 2;
246*1ae08745Sheppo 	newtblsz = newmaxclnts * sizeof (mdeg_clnt_t);
247*1ae08745Sheppo 
248*1ae08745Sheppo 	newtbl = kmem_zalloc(newtblsz, KM_SLEEP);
249*1ae08745Sheppo 
250*1ae08745Sheppo 	/* copy old table data to the new table */
251*1ae08745Sheppo 	oldtblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
252*1ae08745Sheppo 	bcopy(mdeg.tbl, newtbl, oldtblsz);
253*1ae08745Sheppo 
254*1ae08745Sheppo 	/*
255*1ae08745Sheppo 	 * Since the old table was full, the first free entry
256*1ae08745Sheppo 	 * will be just past the end of the old table.
257*1ae08745Sheppo 	 */
258*1ae08745Sheppo 	clnt = &mdeg.tbl[mdeg.maxclnts];
259*1ae08745Sheppo 
260*1ae08745Sheppo 	/* clean up the old table */
261*1ae08745Sheppo 	kmem_free(mdeg.tbl, oldtblsz);
262*1ae08745Sheppo 	mdeg.tbl = newtbl;
263*1ae08745Sheppo 	mdeg.maxclnts = newmaxclnts;
264*1ae08745Sheppo 
265*1ae08745Sheppo found:
266*1ae08745Sheppo 	ASSERT(clnt->valid == 0);
267*1ae08745Sheppo 
268*1ae08745Sheppo 	clnt->hdl = MDEG_ALLOC_HDL(idx, MDEG_HDL2COUNT(clnt->hdl));
269*1ae08745Sheppo 
270*1ae08745Sheppo 	return (clnt);
271*1ae08745Sheppo }
272*1ae08745Sheppo 
273*1ae08745Sheppo static mdeg_clnt_t *
274*1ae08745Sheppo mdeg_get_client(mdeg_handle_t hdl)
275*1ae08745Sheppo {
276*1ae08745Sheppo 	int		idx;
277*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
278*1ae08745Sheppo 
279*1ae08745Sheppo 	idx = MDEG_HDL2IDX(hdl);
280*1ae08745Sheppo 
281*1ae08745Sheppo 	/* check if index is out of bounds */
282*1ae08745Sheppo 	if ((idx < 0) || (idx >= mdeg.maxclnts)) {
283*1ae08745Sheppo 		MDEG_DBG("mdeg_get_client: index out of bounds\n");
284*1ae08745Sheppo 		return (NULL);
285*1ae08745Sheppo 	}
286*1ae08745Sheppo 
287*1ae08745Sheppo 	clnt = &mdeg.tbl[idx];
288*1ae08745Sheppo 
289*1ae08745Sheppo 	/* check for a valid client */
290*1ae08745Sheppo 	if (!clnt->valid) {
291*1ae08745Sheppo 		MDEG_DBG("mdeg_get_client: client is not valid\n");
292*1ae08745Sheppo 		return (NULL);
293*1ae08745Sheppo 	}
294*1ae08745Sheppo 
295*1ae08745Sheppo 	/* make sure the handle is an exact match */
296*1ae08745Sheppo 	if (clnt->hdl != hdl) {
297*1ae08745Sheppo 		MDEG_DBG("mdeg_get_client: bad handle\n");
298*1ae08745Sheppo 		return (NULL);
299*1ae08745Sheppo 	}
300*1ae08745Sheppo 
301*1ae08745Sheppo 	if (clnt->magic != MDEG_MAGIC) {
302*1ae08745Sheppo 		MDEG_DBG("mdeg_get_client: bad magic\n");
303*1ae08745Sheppo 		return (NULL);
304*1ae08745Sheppo 	}
305*1ae08745Sheppo 
306*1ae08745Sheppo 	return (clnt);
307*1ae08745Sheppo }
308*1ae08745Sheppo 
309*1ae08745Sheppo /*
310*1ae08745Sheppo  * Send a notification to a client immediately after it registers.
311*1ae08745Sheppo  * The result_t is a list of all the nodes that match their specified
312*1ae08745Sheppo  * nodes of interest, all returned on the added list. This serves
313*1ae08745Sheppo  * as a base of reference to the client. All future MD updates are
314*1ae08745Sheppo  * relative to this list.
315*1ae08745Sheppo  */
316*1ae08745Sheppo static int
317*1ae08745Sheppo mdeg_notify_client_reg(mdeg_clnt_t *clnt)
318*1ae08745Sheppo {
319*1ae08745Sheppo 	md_t			*mdp = NULL;
320*1ae08745Sheppo 	mde_str_cookie_t	nname;
321*1ae08745Sheppo 	mde_str_cookie_t	aname;
322*1ae08745Sheppo 	mde_cookie_t		startnode;
323*1ae08745Sheppo 	int			nnodes;
324*1ae08745Sheppo 	int			nodechk;
325*1ae08745Sheppo 	mde_cookie_t		*listp = NULL;
326*1ae08745Sheppo 	mdeg_result_t		*mdeg_res = NULL;
327*1ae08745Sheppo 	int			rv = MDEG_SUCCESS;
328*1ae08745Sheppo 
329*1ae08745Sheppo 	mutex_enter(&mdeg.lock);
330*1ae08745Sheppo 
331*1ae08745Sheppo 	/*
332*1ae08745Sheppo 	 * Handle the special case where the node specification
333*1ae08745Sheppo 	 * is NULL. In this case, call the client callback without
334*1ae08745Sheppo 	 * any results. All processing is left to the client.
335*1ae08745Sheppo 	 */
336*1ae08745Sheppo 	if (clnt->pspec == NULL) {
337*1ae08745Sheppo 		/* call the client callback */
338*1ae08745Sheppo 		(*clnt->cb)(clnt->cb_arg, NULL);
339*1ae08745Sheppo 		goto done;
340*1ae08745Sheppo 	}
341*1ae08745Sheppo 
342*1ae08745Sheppo 	if ((mdp = md_get_handle()) == NULL) {
343*1ae08745Sheppo 		cmn_err(CE_WARN, "unable to retrieve current MD");
344*1ae08745Sheppo 		rv = MDEG_FAILURE;
345*1ae08745Sheppo 		goto done;
346*1ae08745Sheppo 	}
347*1ae08745Sheppo 
348*1ae08745Sheppo 	startnode = mdeg_find_start_node(mdp, clnt->pspec);
349*1ae08745Sheppo 	if (startnode == MDE_INVAL_ELEM_COOKIE) {
350*1ae08745Sheppo 		/* not much we can do */
351*1ae08745Sheppo 		cmn_err(CE_WARN, "unable to match node specifier");
352*1ae08745Sheppo 		rv = MDEG_FAILURE;
353*1ae08745Sheppo 		goto done;
354*1ae08745Sheppo 	}
355*1ae08745Sheppo 
356*1ae08745Sheppo 	/*
357*1ae08745Sheppo 	 * Use zalloc to provide correct default values for the
358*1ae08745Sheppo 	 * unused removed, match_prev, and match_curr lists.
359*1ae08745Sheppo 	 */
360*1ae08745Sheppo 	mdeg_res = kmem_zalloc(sizeof (mdeg_result_t), KM_SLEEP);
361*1ae08745Sheppo 
362*1ae08745Sheppo 	nname = md_find_name(mdp, clnt->nmatch->namep);
363*1ae08745Sheppo 	aname = md_find_name(mdp, "fwd");
364*1ae08745Sheppo 
365*1ae08745Sheppo 	nnodes = md_scan_dag(mdp, startnode, nname, aname, NULL);
366*1ae08745Sheppo 
367*1ae08745Sheppo 	if (nnodes == 0) {
368*1ae08745Sheppo 		MDEG_DBG("mdeg_notify_client_reg: no nodes of interest\n");
369*1ae08745Sheppo 		rv = MDEG_SUCCESS;
370*1ae08745Sheppo 		goto done;
371*1ae08745Sheppo 	} else if (nnodes == -1) {
372*1ae08745Sheppo 		MDEG_DBG("error scanning DAG\n");
373*1ae08745Sheppo 		rv = MDEG_FAILURE;
374*1ae08745Sheppo 		goto done;
375*1ae08745Sheppo 	}
376*1ae08745Sheppo 
377*1ae08745Sheppo 	MDEG_DBG("mdeg_notify_client_reg: %d node%s of interest\n",
378*1ae08745Sheppo 	    nnodes, (nnodes == 1) ? "" : "s");
379*1ae08745Sheppo 
380*1ae08745Sheppo 	/* get the list of nodes of interest */
381*1ae08745Sheppo 	listp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
382*1ae08745Sheppo 	nodechk = md_scan_dag(mdp, startnode, nname, aname, listp);
383*1ae08745Sheppo 
384*1ae08745Sheppo 	ASSERT(nodechk == nnodes);
385*1ae08745Sheppo 
386*1ae08745Sheppo 	mdeg_res->added.mdp = mdp;
387*1ae08745Sheppo 	mdeg_res->added.mdep = listp;
388*1ae08745Sheppo 	mdeg_res->added.nelem = nnodes;
389*1ae08745Sheppo 
390*1ae08745Sheppo 	/* call the client callback */
391*1ae08745Sheppo 	(*clnt->cb)(clnt->cb_arg, mdeg_res);
392*1ae08745Sheppo 
393*1ae08745Sheppo done:
394*1ae08745Sheppo 	mutex_exit(&mdeg.lock);
395*1ae08745Sheppo 
396*1ae08745Sheppo 	if (mdp)
397*1ae08745Sheppo 		(void) md_fini_handle(mdp);
398*1ae08745Sheppo 
399*1ae08745Sheppo 	if (listp)
400*1ae08745Sheppo 		kmem_free(listp, sizeof (mde_cookie_t) * nnodes);
401*1ae08745Sheppo 
402*1ae08745Sheppo 	if (mdeg_res)
403*1ae08745Sheppo 		kmem_free(mdeg_res, sizeof (mdeg_result_t));
404*1ae08745Sheppo 
405*1ae08745Sheppo 	return (rv);
406*1ae08745Sheppo }
407*1ae08745Sheppo 
408*1ae08745Sheppo /*
409*1ae08745Sheppo  * Register to receive an event notification when the system
410*1ae08745Sheppo  * machine description is updated.
411*1ae08745Sheppo  *
412*1ae08745Sheppo  * Passing NULL for the node specification parameter is valid
413*1ae08745Sheppo  * as long as the match specification is also NULL. In this
414*1ae08745Sheppo  * case, the client will receive a notification when the MD
415*1ae08745Sheppo  * has been updated, but the callback will not include any
416*1ae08745Sheppo  * information. The client is then responsible for obtaining
417*1ae08745Sheppo  * its own copy of the system MD and performing any processing
418*1ae08745Sheppo  * manually.
419*1ae08745Sheppo  */
420*1ae08745Sheppo int
421*1ae08745Sheppo mdeg_register(mdeg_node_spec_t *pspecp, mdeg_node_match_t *nmatchp,
422*1ae08745Sheppo     mdeg_cb_t cb, void *cb_arg, mdeg_handle_t *hdlp)
423*1ae08745Sheppo {
424*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
425*1ae08745Sheppo 
426*1ae08745Sheppo 	/*
427*1ae08745Sheppo 	 * If the RW lock is held, a client is calling
428*1ae08745Sheppo 	 * register from its own callback.
429*1ae08745Sheppo 	 */
430*1ae08745Sheppo 	if (RW_LOCK_HELD(&mdeg.rwlock)) {
431*1ae08745Sheppo 		MDEG_DBG("mdeg_register: rwlock already held\n");
432*1ae08745Sheppo 		return (MDEG_FAILURE);
433*1ae08745Sheppo 	}
434*1ae08745Sheppo 
435*1ae08745Sheppo 	/* node spec and node match must both be valid, or both NULL */
436*1ae08745Sheppo 	if (((pspecp != NULL) && (nmatchp == NULL)) ||
437*1ae08745Sheppo 	    ((pspecp == NULL) && (nmatchp != NULL))) {
438*1ae08745Sheppo 		MDEG_DBG("mdeg_register: invalid parameters\n");
439*1ae08745Sheppo 		return (MDEG_FAILURE);
440*1ae08745Sheppo 	}
441*1ae08745Sheppo 
442*1ae08745Sheppo 	rw_enter(&mdeg.rwlock, RW_WRITER);
443*1ae08745Sheppo 
444*1ae08745Sheppo 	clnt = mdeg_alloc_clnt();
445*1ae08745Sheppo 
446*1ae08745Sheppo 	ASSERT(clnt);
447*1ae08745Sheppo 
448*1ae08745Sheppo 	/*
449*1ae08745Sheppo 	 * Fill in the rest of the data
450*1ae08745Sheppo 	 */
451*1ae08745Sheppo 	clnt->nmatch = nmatchp;
452*1ae08745Sheppo 	clnt->pspec = pspecp;
453*1ae08745Sheppo 	clnt->cb = cb;
454*1ae08745Sheppo 	clnt->cb_arg = cb_arg;
455*1ae08745Sheppo 	clnt->magic = MDEG_MAGIC;
456*1ae08745Sheppo 
457*1ae08745Sheppo 	/* do this last */
458*1ae08745Sheppo 	clnt->valid = B_TRUE;
459*1ae08745Sheppo 
460*1ae08745Sheppo 	MDEG_DBG("client registered (0x%lx):\n", clnt->hdl);
461*1ae08745Sheppo 	MDEG_DUMP_CLNT(clnt);
462*1ae08745Sheppo 
463*1ae08745Sheppo 	mdeg.nclnts++;
464*1ae08745Sheppo 
465*1ae08745Sheppo 	if (mdeg_notify_client_reg(clnt) != MDEG_SUCCESS) {
466*1ae08745Sheppo 		bzero(clnt, sizeof (mdeg_clnt_t));
467*1ae08745Sheppo 		rw_exit(&mdeg.rwlock);
468*1ae08745Sheppo 		return (MDEG_FAILURE);
469*1ae08745Sheppo 	}
470*1ae08745Sheppo 
471*1ae08745Sheppo 	rw_exit(&mdeg.rwlock);
472*1ae08745Sheppo 
473*1ae08745Sheppo 	*hdlp = clnt->hdl;
474*1ae08745Sheppo 
475*1ae08745Sheppo 	return (MDEG_SUCCESS);
476*1ae08745Sheppo }
477*1ae08745Sheppo 
478*1ae08745Sheppo int
479*1ae08745Sheppo mdeg_unregister(mdeg_handle_t hdl)
480*1ae08745Sheppo {
481*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
482*1ae08745Sheppo 	mdeg_handle_t	mdh;
483*1ae08745Sheppo 
484*1ae08745Sheppo 	/*
485*1ae08745Sheppo 	 * If the RW lock is held, a client is calling
486*1ae08745Sheppo 	 * unregister from its own callback.
487*1ae08745Sheppo 	 */
488*1ae08745Sheppo 	if (RW_LOCK_HELD(&mdeg.rwlock)) {
489*1ae08745Sheppo 		MDEG_DBG("mdeg_unregister: rwlock already held\n");
490*1ae08745Sheppo 		return (MDEG_FAILURE);
491*1ae08745Sheppo 	}
492*1ae08745Sheppo 
493*1ae08745Sheppo 	/* lookup the client */
494*1ae08745Sheppo 	if ((clnt = mdeg_get_client(hdl)) == NULL) {
495*1ae08745Sheppo 		return (MDEG_FAILURE);
496*1ae08745Sheppo 	}
497*1ae08745Sheppo 
498*1ae08745Sheppo 	rw_enter(&mdeg.rwlock, RW_WRITER);
499*1ae08745Sheppo 
500*1ae08745Sheppo 	MDEG_DBG("client unregistered (0x%lx):\n", hdl);
501*1ae08745Sheppo 	MDEG_DUMP_CLNT(clnt);
502*1ae08745Sheppo 
503*1ae08745Sheppo 	/* save the handle to prevent reuse */
504*1ae08745Sheppo 	mdh = clnt->hdl;
505*1ae08745Sheppo 	bzero(clnt, sizeof (mdeg_clnt_t));
506*1ae08745Sheppo 
507*1ae08745Sheppo 	clnt->hdl = mdh;
508*1ae08745Sheppo 
509*1ae08745Sheppo 	mdeg.nclnts--;
510*1ae08745Sheppo 
511*1ae08745Sheppo 	rw_exit(&mdeg.rwlock);
512*1ae08745Sheppo 
513*1ae08745Sheppo 	return (MDEG_SUCCESS);
514*1ae08745Sheppo }
515*1ae08745Sheppo 
516*1ae08745Sheppo /*
517*1ae08745Sheppo  * Simple algorithm for now, grab the global lock and let all
518*1ae08745Sheppo  * the clients update themselves in parallel. There is a lot of
519*1ae08745Sheppo  * room for improvement here. We could eliminate some scans of
520*1ae08745Sheppo  * the DAG by imcrementally scanning at lower levels of the DAG
521*1ae08745Sheppo  * rather than having each client start its own scan from the root.
522*1ae08745Sheppo  */
523*1ae08745Sheppo void
524*1ae08745Sheppo mdeg_notify_clients(void)
525*1ae08745Sheppo {
526*1ae08745Sheppo 	md_t		*md_new;
527*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
528*1ae08745Sheppo 	int		idx;
529*1ae08745Sheppo 	int		nclnt;
530*1ae08745Sheppo 
531*1ae08745Sheppo 	rw_enter(&mdeg.rwlock, RW_READER);
532*1ae08745Sheppo 	mutex_enter(&mdeg.lock);
533*1ae08745Sheppo 
534*1ae08745Sheppo 	/*
535*1ae08745Sheppo 	 * Rotate the MDs
536*1ae08745Sheppo 	 */
537*1ae08745Sheppo 	if ((md_new = md_get_handle()) == NULL) {
538*1ae08745Sheppo 		cmn_err(CE_WARN, "unable to retrieve new MD");
539*1ae08745Sheppo 		goto done;
540*1ae08745Sheppo 	}
541*1ae08745Sheppo 
542*1ae08745Sheppo 	if (mdeg.md_prev) {
543*1ae08745Sheppo 		(void) md_fini_handle(mdeg.md_prev);
544*1ae08745Sheppo 	}
545*1ae08745Sheppo 
546*1ae08745Sheppo 	mdeg.md_prev = mdeg.md_curr;
547*1ae08745Sheppo 	mdeg.md_curr = md_new;
548*1ae08745Sheppo 
549*1ae08745Sheppo 	if (mdeg.nclnts == 0) {
550*1ae08745Sheppo 		MDEG_DBG("mdeg_notify_clients: no clients registered\n");
551*1ae08745Sheppo 		goto done;
552*1ae08745Sheppo 	}
553*1ae08745Sheppo 
554*1ae08745Sheppo 	/* dispatch the update notification to all clients */
555*1ae08745Sheppo 	for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) {
556*1ae08745Sheppo 		clnt = &mdeg.tbl[idx];
557*1ae08745Sheppo 
558*1ae08745Sheppo 		if (!clnt->valid)
559*1ae08745Sheppo 			continue;
560*1ae08745Sheppo 
561*1ae08745Sheppo 		MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl,
562*1ae08745Sheppo 		    ++nclnt, mdeg.nclnts);
563*1ae08745Sheppo 
564*1ae08745Sheppo 		(void) taskq_dispatch(mdeg.taskq, mdeg_notify_client,
565*1ae08745Sheppo 		    (void *)clnt, TQ_SLEEP);
566*1ae08745Sheppo 	}
567*1ae08745Sheppo 
568*1ae08745Sheppo 	taskq_wait(mdeg.taskq);
569*1ae08745Sheppo 
570*1ae08745Sheppo done:
571*1ae08745Sheppo 	mutex_exit(&mdeg.lock);
572*1ae08745Sheppo 	rw_exit(&mdeg.rwlock);
573*1ae08745Sheppo }
574*1ae08745Sheppo 
575*1ae08745Sheppo static void
576*1ae08745Sheppo mdeg_notify_client(void *arg)
577*1ae08745Sheppo {
578*1ae08745Sheppo 	mdeg_clnt_t		*clnt = (mdeg_clnt_t *)arg;
579*1ae08745Sheppo 	md_diff_cookie_t	mdd = MD_INVAL_DIFF_COOKIE;
580*1ae08745Sheppo 	mdeg_result_t		mdeg_res;
581*1ae08745Sheppo 	mde_cookie_t		md_prev_start;
582*1ae08745Sheppo 	mde_cookie_t		md_curr_start;
583*1ae08745Sheppo 
584*1ae08745Sheppo 	rw_enter(&mdeg.rwlock, RW_READER);
585*1ae08745Sheppo 
586*1ae08745Sheppo 	if (!mdeg.enabled) {
587*1ae08745Sheppo 		/* trying to shutdown */
588*1ae08745Sheppo 		MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n");
589*1ae08745Sheppo 		goto cleanup;
590*1ae08745Sheppo 	}
591*1ae08745Sheppo 
592*1ae08745Sheppo 	/*
593*1ae08745Sheppo 	 * Handle the special case where the node specification
594*1ae08745Sheppo 	 * is NULL. In this case, call the client callback without
595*1ae08745Sheppo 	 * any results. All processing is left to the client.
596*1ae08745Sheppo 	 */
597*1ae08745Sheppo 	if (clnt->pspec == NULL) {
598*1ae08745Sheppo 		/* call the client callback */
599*1ae08745Sheppo 		(*clnt->cb)(clnt->cb_arg, NULL);
600*1ae08745Sheppo 
601*1ae08745Sheppo 		MDEG_DBG("MDEG client callback done\n");
602*1ae08745Sheppo 		goto cleanup;
603*1ae08745Sheppo 	}
604*1ae08745Sheppo 
605*1ae08745Sheppo 	/* find our start nodes */
606*1ae08745Sheppo 	md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec);
607*1ae08745Sheppo 	if (md_prev_start == MDE_INVAL_ELEM_COOKIE) {
608*1ae08745Sheppo 		goto cleanup;
609*1ae08745Sheppo 	}
610*1ae08745Sheppo 
611*1ae08745Sheppo 	md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec);
612*1ae08745Sheppo 	if (md_curr_start == MDE_INVAL_ELEM_COOKIE) {
613*1ae08745Sheppo 		goto cleanup;
614*1ae08745Sheppo 	}
615*1ae08745Sheppo 
616*1ae08745Sheppo 	/* diff the MDs */
617*1ae08745Sheppo 	mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr,
618*1ae08745Sheppo 	    md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp);
619*1ae08745Sheppo 
620*1ae08745Sheppo 	if (mdd == MD_INVAL_DIFF_COOKIE) {
621*1ae08745Sheppo 		MDEG_DBG("unable to diff MDs\n");
622*1ae08745Sheppo 		goto cleanup;
623*1ae08745Sheppo 	}
624*1ae08745Sheppo 
625*1ae08745Sheppo 	/*
626*1ae08745Sheppo 	 * Cache the results of the diff
627*1ae08745Sheppo 	 */
628*1ae08745Sheppo 	mdeg_get_diff_results(mdd, &mdeg_res);
629*1ae08745Sheppo 
630*1ae08745Sheppo 	/* call the client callback */
631*1ae08745Sheppo 	(*clnt->cb)(clnt->cb_arg, &mdeg_res);
632*1ae08745Sheppo 
633*1ae08745Sheppo 	MDEG_DBG("MDEG client callback done\n");
634*1ae08745Sheppo 
635*1ae08745Sheppo cleanup:
636*1ae08745Sheppo 	rw_exit(&mdeg.rwlock);
637*1ae08745Sheppo 
638*1ae08745Sheppo 	if (mdd != MD_INVAL_DIFF_COOKIE)
639*1ae08745Sheppo 		(void) md_diff_fini(mdd);
640*1ae08745Sheppo }
641*1ae08745Sheppo 
642*1ae08745Sheppo static mde_cookie_t
643*1ae08745Sheppo mdeg_find_start_node(md_t *md, mdeg_node_spec_t *nspec)
644*1ae08745Sheppo {
645*1ae08745Sheppo 	mde_cookie_t		*nodesp;
646*1ae08745Sheppo 	mde_str_cookie_t	nname;
647*1ae08745Sheppo 	mde_str_cookie_t	aname;
648*1ae08745Sheppo 	int			nnodes;
649*1ae08745Sheppo 	int			idx;
650*1ae08745Sheppo 
651*1ae08745Sheppo 	if ((md == NULL) || (nspec == NULL))
652*1ae08745Sheppo 		return (MDE_INVAL_ELEM_COOKIE);
653*1ae08745Sheppo 
654*1ae08745Sheppo 	nname = md_find_name(md, nspec->namep);
655*1ae08745Sheppo 	aname = md_find_name(md, "fwd");
656*1ae08745Sheppo 
657*1ae08745Sheppo 	nnodes = md_scan_dag(md, NULL, nname, aname, NULL);
658*1ae08745Sheppo 	if (nnodes == 0)
659*1ae08745Sheppo 		return (MDE_INVAL_ELEM_COOKIE);
660*1ae08745Sheppo 
661*1ae08745Sheppo 	nodesp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
662*1ae08745Sheppo 
663*1ae08745Sheppo 	(void) md_scan_dag(md, NULL, nname, aname, nodesp);
664*1ae08745Sheppo 
665*1ae08745Sheppo 	for (idx = 0; idx < nnodes; idx++) {
666*1ae08745Sheppo 
667*1ae08745Sheppo 		if (mdeg_node_spec_match(md, nodesp[idx], nspec)) {
668*1ae08745Sheppo 			mde_cookie_t res = nodesp[idx];
669*1ae08745Sheppo 
670*1ae08745Sheppo 			kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
671*1ae08745Sheppo 			return (res);
672*1ae08745Sheppo 		}
673*1ae08745Sheppo 	}
674*1ae08745Sheppo 
675*1ae08745Sheppo 	kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
676*1ae08745Sheppo 	return (MDE_INVAL_ELEM_COOKIE);
677*1ae08745Sheppo }
678*1ae08745Sheppo 
679*1ae08745Sheppo static boolean_t
680*1ae08745Sheppo mdeg_node_spec_match(md_t *md, mde_cookie_t node, mdeg_node_spec_t *nspec)
681*1ae08745Sheppo {
682*1ae08745Sheppo 	mdeg_prop_spec_t	*prop;
683*1ae08745Sheppo 
684*1ae08745Sheppo 	ASSERT(md && nspec);
685*1ae08745Sheppo 	ASSERT(node != MDE_INVAL_ELEM_COOKIE);
686*1ae08745Sheppo 
687*1ae08745Sheppo 	prop = nspec->specp;
688*1ae08745Sheppo 
689*1ae08745Sheppo 	while (prop->type != MDET_LIST_END) {
690*1ae08745Sheppo 
691*1ae08745Sheppo 		switch (prop->type) {
692*1ae08745Sheppo 		case MDET_PROP_VAL: {
693*1ae08745Sheppo 			uint64_t val;
694*1ae08745Sheppo 
695*1ae08745Sheppo 			if (md_get_prop_val(md, node, prop->namep, &val) != 0)
696*1ae08745Sheppo 				return (B_FALSE);
697*1ae08745Sheppo 
698*1ae08745Sheppo 			if (prop->ps_val != val)
699*1ae08745Sheppo 				return (B_FALSE);
700*1ae08745Sheppo 
701*1ae08745Sheppo 			break;
702*1ae08745Sheppo 		}
703*1ae08745Sheppo 		case MDET_PROP_STR: {
704*1ae08745Sheppo 			char	*str;
705*1ae08745Sheppo 
706*1ae08745Sheppo 			if (md_get_prop_str(md, node, prop->namep, &str) != 0)
707*1ae08745Sheppo 				return (B_FALSE);
708*1ae08745Sheppo 
709*1ae08745Sheppo 			if (strcmp(prop->ps_str, str) != 0)
710*1ae08745Sheppo 				return (B_FALSE);
711*1ae08745Sheppo 
712*1ae08745Sheppo 			break;
713*1ae08745Sheppo 		}
714*1ae08745Sheppo 
715*1ae08745Sheppo 		default:
716*1ae08745Sheppo 			return (B_FALSE);
717*1ae08745Sheppo 		}
718*1ae08745Sheppo 
719*1ae08745Sheppo 		prop++;
720*1ae08745Sheppo 	}
721*1ae08745Sheppo 
722*1ae08745Sheppo 	return (B_TRUE);
723*1ae08745Sheppo }
724*1ae08745Sheppo 
725*1ae08745Sheppo static void
726*1ae08745Sheppo mdeg_get_diff_results(md_diff_cookie_t mdd, mdeg_result_t *res)
727*1ae08745Sheppo {
728*1ae08745Sheppo 	/*
729*1ae08745Sheppo 	 * Cache added nodes.
730*1ae08745Sheppo 	 */
731*1ae08745Sheppo 	res->added.mdp = mdeg.md_curr;
732*1ae08745Sheppo 	res->added.nelem = md_diff_added(mdd, &(res->added.mdep));
733*1ae08745Sheppo 
734*1ae08745Sheppo 	if (res->added.nelem == -1) {
735*1ae08745Sheppo 		bzero(&(res->added), sizeof (mdeg_diff_t));
736*1ae08745Sheppo 	}
737*1ae08745Sheppo 
738*1ae08745Sheppo 	/*
739*1ae08745Sheppo 	 * Cache removed nodes.
740*1ae08745Sheppo 	 */
741*1ae08745Sheppo 	res->removed.mdp = mdeg.md_prev;
742*1ae08745Sheppo 	res->removed.nelem = md_diff_removed(mdd, &(res->removed.mdep));
743*1ae08745Sheppo 
744*1ae08745Sheppo 	if (res->removed.nelem == -1) {
745*1ae08745Sheppo 		bzero(&(res->removed), sizeof (mdeg_diff_t));
746*1ae08745Sheppo 	}
747*1ae08745Sheppo 
748*1ae08745Sheppo 	/*
749*1ae08745Sheppo 	 * Cache matching node pairs.
750*1ae08745Sheppo 	 */
751*1ae08745Sheppo 	res->match_curr.mdp = mdeg.md_curr;
752*1ae08745Sheppo 	res->match_prev.mdp = mdeg.md_prev;
753*1ae08745Sheppo 	res->match_curr.nelem = md_diff_matched(mdd, &(res->match_prev.mdep),
754*1ae08745Sheppo 	    &(res->match_curr.mdep));
755*1ae08745Sheppo 	res->match_prev.nelem = res->match_curr.nelem;
756*1ae08745Sheppo 
757*1ae08745Sheppo 	if (res->match_prev.nelem == -1) {
758*1ae08745Sheppo 		bzero(&(res->match_prev), sizeof (mdeg_diff_t));
759*1ae08745Sheppo 		bzero(&(res->match_curr), sizeof (mdeg_diff_t));
760*1ae08745Sheppo 	}
761*1ae08745Sheppo }
762*1ae08745Sheppo 
763*1ae08745Sheppo #ifdef DEBUG
764*1ae08745Sheppo /*
765*1ae08745Sheppo  * Generate a string that represents the node specifier
766*1ae08745Sheppo  * structure. Clamp the string length if the specifier
767*1ae08745Sheppo  * structure contains too much information.
768*1ae08745Sheppo  *
769*1ae08745Sheppo  *	General form:
770*1ae08745Sheppo  *
771*1ae08745Sheppo  *		<nodename>:{<propname>=<propval>,...}
772*1ae08745Sheppo  *	e.g.
773*1ae08745Sheppo  *		vdevice:{name=vsw,reg=0x0}
774*1ae08745Sheppo  */
775*1ae08745Sheppo static void
776*1ae08745Sheppo mdeg_spec_str(mdeg_node_spec_t *spec, char *buf, int len)
777*1ae08745Sheppo {
778*1ae08745Sheppo 	mdeg_prop_spec_t	*prop;
779*1ae08745Sheppo 	int			offset;
780*1ae08745Sheppo 	boolean_t		first = B_TRUE;
781*1ae08745Sheppo 	char			*end = buf + len;
782*1ae08745Sheppo 
783*1ae08745Sheppo 	offset = snprintf(buf, len, "%s:{", spec->namep);
784*1ae08745Sheppo 
785*1ae08745Sheppo 	buf += offset;
786*1ae08745Sheppo 	len -= offset;
787*1ae08745Sheppo 	if (len <= 0)
788*1ae08745Sheppo 		goto trunc;
789*1ae08745Sheppo 
790*1ae08745Sheppo 	prop = spec->specp;
791*1ae08745Sheppo 
792*1ae08745Sheppo 	while (prop->type != MDET_LIST_END) {
793*1ae08745Sheppo 
794*1ae08745Sheppo 		switch (prop->type) {
795*1ae08745Sheppo 		case MDET_PROP_VAL:
796*1ae08745Sheppo 			offset = snprintf(buf, len, "%s%s=0x%lx",
797*1ae08745Sheppo 			    (first) ? "" : ",", prop->namep, prop->ps_val);
798*1ae08745Sheppo 			buf += offset;
799*1ae08745Sheppo 			len -= offset;
800*1ae08745Sheppo 			if (len <= 0)
801*1ae08745Sheppo 				goto trunc;
802*1ae08745Sheppo 			break;
803*1ae08745Sheppo 
804*1ae08745Sheppo 		case MDET_PROP_STR:
805*1ae08745Sheppo 			offset = snprintf(buf, len, "%s%s=%s",
806*1ae08745Sheppo 			    (first) ? "" : ",", prop->namep, prop->ps_str);
807*1ae08745Sheppo 			buf += offset;
808*1ae08745Sheppo 			len -= offset;
809*1ae08745Sheppo 			if (len <= 0)
810*1ae08745Sheppo 				goto trunc;
811*1ae08745Sheppo 			break;
812*1ae08745Sheppo 
813*1ae08745Sheppo 		default:
814*1ae08745Sheppo 			(void) snprintf(buf, len, "}");
815*1ae08745Sheppo 			return;
816*1ae08745Sheppo 		}
817*1ae08745Sheppo 
818*1ae08745Sheppo 		if (first)
819*1ae08745Sheppo 			first = B_FALSE;
820*1ae08745Sheppo 		prop++;
821*1ae08745Sheppo 	}
822*1ae08745Sheppo 
823*1ae08745Sheppo 	(void) snprintf(buf, len, "}");
824*1ae08745Sheppo 	return;
825*1ae08745Sheppo 
826*1ae08745Sheppo trunc:
827*1ae08745Sheppo 	/* string too long, truncate it */
828*1ae08745Sheppo 	buf = end - (strlen(trunc_str) + 1);
829*1ae08745Sheppo 	(void) sprintf(buf, trunc_str);
830*1ae08745Sheppo }
831*1ae08745Sheppo 
832*1ae08745Sheppo /*
833*1ae08745Sheppo  * Generate a string that represents the match structure.
834*1ae08745Sheppo  * Clamp the string length if the match structure contains
835*1ae08745Sheppo  * too much information.
836*1ae08745Sheppo  *
837*1ae08745Sheppo  *	General form:
838*1ae08745Sheppo  *
839*1ae08745Sheppo  *		<nodename>:{<propname>,...}
840*1ae08745Sheppo  *	e.g.
841*1ae08745Sheppo  *		nmatch=vport:{reg}
842*1ae08745Sheppo  */
843*1ae08745Sheppo static void
844*1ae08745Sheppo mdeg_match_str(mdeg_node_match_t *match, char *buf, int len)
845*1ae08745Sheppo {
846*1ae08745Sheppo 	md_prop_match_t	*prop;
847*1ae08745Sheppo 	int		offset;
848*1ae08745Sheppo 	boolean_t	first = B_TRUE;
849*1ae08745Sheppo 	char		*end = buf + len;
850*1ae08745Sheppo 
851*1ae08745Sheppo 	offset = snprintf(buf, len, "%s:{", match->namep);
852*1ae08745Sheppo 
853*1ae08745Sheppo 	buf += offset;
854*1ae08745Sheppo 	len -= offset;
855*1ae08745Sheppo 	if (len <= 0)
856*1ae08745Sheppo 		goto trunc;
857*1ae08745Sheppo 
858*1ae08745Sheppo 	prop = match->matchp;
859*1ae08745Sheppo 
860*1ae08745Sheppo 	while (prop->type != MDET_LIST_END) {
861*1ae08745Sheppo 		offset = snprintf(buf, len, "%s%s", (first) ? "" : ",",
862*1ae08745Sheppo 		    prop->namep);
863*1ae08745Sheppo 		buf += offset;
864*1ae08745Sheppo 		len -= offset;
865*1ae08745Sheppo 		if (len <= 0)
866*1ae08745Sheppo 			goto trunc;
867*1ae08745Sheppo 
868*1ae08745Sheppo 		if (first)
869*1ae08745Sheppo 			first = B_FALSE;
870*1ae08745Sheppo 		prop++;
871*1ae08745Sheppo 	}
872*1ae08745Sheppo 
873*1ae08745Sheppo 	(void) snprintf(buf, len, "}");
874*1ae08745Sheppo 	return;
875*1ae08745Sheppo 
876*1ae08745Sheppo trunc:
877*1ae08745Sheppo 	/* string too long, truncate it */
878*1ae08745Sheppo 	buf = end - (strlen(trunc_str) + 1);
879*1ae08745Sheppo 	(void) sprintf(buf, trunc_str);
880*1ae08745Sheppo }
881*1ae08745Sheppo 
882*1ae08745Sheppo #define	MAX_FIELD_STR	80
883*1ae08745Sheppo 
884*1ae08745Sheppo static void
885*1ae08745Sheppo mdeg_dump_clnt(mdeg_clnt_t *clnt)
886*1ae08745Sheppo {
887*1ae08745Sheppo 	char	str[MAX_FIELD_STR];
888*1ae08745Sheppo 
889*1ae08745Sheppo 	if (!clnt->valid) {
890*1ae08745Sheppo 		MDEG_DBG("  valid=B_FALSE\n");
891*1ae08745Sheppo 		return;
892*1ae08745Sheppo 	}
893*1ae08745Sheppo 
894*1ae08745Sheppo 	mdeg_spec_str(clnt->pspec, str, MAX_FIELD_STR);
895*1ae08745Sheppo 	MDEG_DBG("  pspecp=%s\n", str);
896*1ae08745Sheppo 
897*1ae08745Sheppo 	mdeg_match_str(clnt->nmatch, str, MAX_FIELD_STR);
898*1ae08745Sheppo 	MDEG_DBG("  nmatch=%s\n", str);
899*1ae08745Sheppo }
900*1ae08745Sheppo 
901*1ae08745Sheppo static void
902*1ae08745Sheppo mdeg_dump_table(void)
903*1ae08745Sheppo {
904*1ae08745Sheppo 	int		idx;
905*1ae08745Sheppo 	mdeg_clnt_t	*clnt;
906*1ae08745Sheppo 
907*1ae08745Sheppo 	for (idx = 0; idx < mdeg.maxclnts; idx++) {
908*1ae08745Sheppo 		clnt = &(mdeg.tbl[idx]);
909*1ae08745Sheppo 
910*1ae08745Sheppo 		MDEG_DBG("client %d (0x%lx):\n", idx, clnt->hdl);
911*1ae08745Sheppo 		mdeg_dump_clnt(clnt);
912*1ae08745Sheppo 	}
913*1ae08745Sheppo }
914*1ae08745Sheppo #endif /* DEBUG */
915