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