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