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