11ae08745Sheppo /* 21ae08745Sheppo * CDDL HEADER START 31ae08745Sheppo * 41ae08745Sheppo * The contents of this file are subject to the terms of the 51ae08745Sheppo * Common Development and Distribution License (the "License"). 61ae08745Sheppo * You may not use this file except in compliance with the License. 71ae08745Sheppo * 81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91ae08745Sheppo * or http://www.opensolaris.org/os/licensing. 101ae08745Sheppo * See the License for the specific language governing permissions 111ae08745Sheppo * and limitations under the License. 121ae08745Sheppo * 131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the 161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181ae08745Sheppo * 191ae08745Sheppo * CDDL HEADER END 201ae08745Sheppo */ 211ae08745Sheppo 221ae08745Sheppo /* 23*9853d9e8SJason Beloro * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 241ae08745Sheppo * Use is subject to license terms. 251ae08745Sheppo */ 261ae08745Sheppo 271ae08745Sheppo /* 281ae08745Sheppo * MD Event Generator (MDEG) Module 291ae08745Sheppo */ 301ae08745Sheppo 311ae08745Sheppo #include <sys/machsystm.h> 321ae08745Sheppo #include <sys/taskq.h> 331ae08745Sheppo #include <sys/disp.h> 341ae08745Sheppo #include <sys/cmn_err.h> 351ae08745Sheppo #include <sys/note.h> 361ae08745Sheppo 371ae08745Sheppo #include <sys/mdeg.h> 381ae08745Sheppo #include <sys/mach_descrip.h> 391ae08745Sheppo #include <sys/mdesc.h> 401ae08745Sheppo 411ae08745Sheppo /* 421ae08745Sheppo * A single client registration 431ae08745Sheppo */ 441ae08745Sheppo typedef struct mdeg_clnt { 451ae08745Sheppo boolean_t valid; /* structure is in active use */ 461ae08745Sheppo mdeg_node_match_t *nmatch; /* node match filter */ 471ae08745Sheppo mdeg_node_spec_t *pspec; /* parent match filter */ 481ae08745Sheppo mdeg_cb_t cb; /* the client callback */ 491ae08745Sheppo caddr_t cb_arg; /* argument to the callback */ 501ae08745Sheppo uint64_t magic; /* sanity checking magic */ 511ae08745Sheppo mdeg_handle_t hdl; /* handle assigned by MDEG */ 521ae08745Sheppo } mdeg_clnt_t; 531ae08745Sheppo 541ae08745Sheppo /* 551ae08745Sheppo * Global MDEG data 561ae08745Sheppo * 571ae08745Sheppo * Locking Strategy: 581ae08745Sheppo * 594e476149Srsmaeda * mdeg.lock - lock used to synchronize system-wide MD updates. An 601ae08745Sheppo * MD update must be treated as an atomic event. The lock is 611ae08745Sheppo * taken when notification that a new MD is available and held 621ae08745Sheppo * until all clients have been notified. 631ae08745Sheppo * 644e476149Srsmaeda * mdeg.rwlock - lock used to synchronize access to the table of 651ae08745Sheppo * registered clients. The reader lock must be held when looking 661ae08745Sheppo * up client information in the table. The writer lock must be 671ae08745Sheppo * held when modifying any client information. 681ae08745Sheppo */ 691ae08745Sheppo static struct mdeg { 701ae08745Sheppo taskq_t *taskq; /* for internal processing */ 711ae08745Sheppo boolean_t enabled; /* enable/disable taskq processing */ 721ae08745Sheppo kmutex_t lock; /* synchronize MD updates */ 731ae08745Sheppo md_t *md_prev; /* previous MD */ 741ae08745Sheppo md_t *md_curr; /* current MD */ 751ae08745Sheppo mdeg_clnt_t *tbl; /* table of registered clients */ 761ae08745Sheppo krwlock_t rwlock; /* client table lock */ 771ae08745Sheppo uint_t maxclnts; /* client table size */ 781ae08745Sheppo uint_t nclnts; /* current number of clients */ 791ae08745Sheppo } mdeg; 801ae08745Sheppo 811ae08745Sheppo /* 821ae08745Sheppo * Debugging routines 831ae08745Sheppo */ 841ae08745Sheppo #ifdef DEBUG 851ae08745Sheppo uint_t mdeg_debug = 0x0; 861ae08745Sheppo 871ae08745Sheppo static void mdeg_dump_clnt(mdeg_clnt_t *clnt); 881ae08745Sheppo static void mdeg_dump_table(void); 891ae08745Sheppo 901ae08745Sheppo #define MDEG_DBG if (mdeg_debug) printf 911ae08745Sheppo #define MDEG_DUMP_CLNT mdeg_dump_clnt 921ae08745Sheppo #define MDEG_DUMP_TABLE mdeg_dump_table 931ae08745Sheppo 941ae08745Sheppo #else /* DEBUG */ 951ae08745Sheppo 961ae08745Sheppo #define MDEG_DBG _NOTE(CONSTCOND) if (0) printf 971ae08745Sheppo #define MDEG_DUMP_CLNT 981ae08745Sheppo #define MDEG_DUMP_TABLE() 991ae08745Sheppo 1001ae08745Sheppo #endif /* DEBUG */ 1011ae08745Sheppo 1021ae08745Sheppo /* 1031ae08745Sheppo * Global constants 1041ae08745Sheppo */ 1051ae08745Sheppo #define MDEG_MAX_TASKQ_THR 512 /* maximum number of taskq threads */ 1061ae08745Sheppo #define MDEG_MAX_CLNTS_INIT 64 /* initial client table size */ 1071ae08745Sheppo 1081ae08745Sheppo #define MDEG_MAGIC 0x4D4445475F48444Cull /* 'MDEG_HDL' */ 1091ae08745Sheppo 1101ae08745Sheppo /* 1111ae08745Sheppo * A client handle is a 64 bit value with two pieces of 1121ae08745Sheppo * information encoded in it. The upper 32 bits are the 1131ae08745Sheppo * index into the table of a particular client structure. 1141ae08745Sheppo * The lower 32 bits are a counter that is incremented 1151ae08745Sheppo * each time a client structure is reused. 1161ae08745Sheppo */ 1171ae08745Sheppo #define MDEG_IDX_SHIFT 32 1181ae08745Sheppo #define MDEG_COUNT_MASK 0xfffffffful 1191ae08745Sheppo 1201ae08745Sheppo #define MDEG_ALLOC_HDL(_idx, _count) (((uint64_t)_idx << MDEG_IDX_SHIFT) | \ 1211ae08745Sheppo ((uint64_t)(_count + 1) & \ 1221ae08745Sheppo MDEG_COUNT_MASK)) 1231ae08745Sheppo #define MDEG_HDL2IDX(hdl) (hdl >> MDEG_IDX_SHIFT) 1241ae08745Sheppo #define MDEG_HDL2COUNT(hdl) (hdl & MDEG_COUNT_MASK) 1251ae08745Sheppo 1261ae08745Sheppo static const char trunc_str[] = " ... }"; 1271ae08745Sheppo 1281ae08745Sheppo /* 1291ae08745Sheppo * Utility routines 1301ae08745Sheppo */ 1311ae08745Sheppo static mdeg_clnt_t *mdeg_alloc_clnt(void); 1321ae08745Sheppo static void mdeg_notify_client(void *); 1331ae08745Sheppo static mde_cookie_t mdeg_find_start_node(md_t *, mdeg_node_spec_t *); 1341ae08745Sheppo static boolean_t mdeg_node_spec_match(md_t *, mde_cookie_t, mdeg_node_spec_t *); 1351ae08745Sheppo static void mdeg_get_diff_results(md_diff_cookie_t, mdeg_result_t *); 1361ae08745Sheppo 1371ae08745Sheppo int 1381ae08745Sheppo mdeg_init(void) 1391ae08745Sheppo { 1401ae08745Sheppo int tblsz; 1411ae08745Sheppo 1421ae08745Sheppo /* 1431ae08745Sheppo * Grab the current MD 1441ae08745Sheppo */ 1451ae08745Sheppo if ((mdeg.md_curr = md_get_handle()) == NULL) { 1461ae08745Sheppo cmn_err(CE_WARN, "unable to cache snapshot of MD"); 1471ae08745Sheppo return (-1); 1481ae08745Sheppo } 1491ae08745Sheppo 1501ae08745Sheppo /* 1511ae08745Sheppo * Initialize table of registered clients 1521ae08745Sheppo */ 1531ae08745Sheppo mdeg.maxclnts = MDEG_MAX_CLNTS_INIT; 1541ae08745Sheppo 1551ae08745Sheppo tblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t); 1561ae08745Sheppo mdeg.tbl = kmem_zalloc(tblsz, KM_SLEEP); 1571ae08745Sheppo 1581ae08745Sheppo rw_init(&mdeg.rwlock, NULL, RW_DRIVER, NULL); 1591ae08745Sheppo 1601ae08745Sheppo mdeg.nclnts = 0; 1611ae08745Sheppo 1621ae08745Sheppo /* 1631ae08745Sheppo * Initialize global lock 1641ae08745Sheppo */ 1651ae08745Sheppo mutex_init(&mdeg.lock, NULL, MUTEX_DRIVER, NULL); 1661ae08745Sheppo 1671ae08745Sheppo /* 1681ae08745Sheppo * Initialize the task queue 1691ae08745Sheppo */ 1701ae08745Sheppo mdeg.taskq = taskq_create("mdeg_taskq", 1, minclsyspri, 1, 1711ae08745Sheppo MDEG_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC); 1721ae08745Sheppo 1731ae08745Sheppo /* ready to begin handling clients */ 1741ae08745Sheppo mdeg.enabled = B_TRUE; 1751ae08745Sheppo 1761ae08745Sheppo return (0); 1771ae08745Sheppo } 1781ae08745Sheppo 1791ae08745Sheppo void 1801ae08745Sheppo mdeg_fini(void) 1811ae08745Sheppo { 1821ae08745Sheppo /* 1831ae08745Sheppo * Flip the enabled switch off to make sure that 1841ae08745Sheppo * no events get dispatched while things are being 1851ae08745Sheppo * torn down. 1861ae08745Sheppo */ 1871ae08745Sheppo mdeg.enabled = B_FALSE; 1881ae08745Sheppo 1891ae08745Sheppo /* destroy the task queue */ 1901ae08745Sheppo taskq_destroy(mdeg.taskq); 1911ae08745Sheppo 1921ae08745Sheppo /* 1931ae08745Sheppo * Deallocate the table of registered clients 1941ae08745Sheppo */ 1951ae08745Sheppo kmem_free(mdeg.tbl, mdeg.maxclnts * sizeof (mdeg_clnt_t)); 1961ae08745Sheppo rw_destroy(&mdeg.rwlock); 1971ae08745Sheppo 1981ae08745Sheppo /* 1991ae08745Sheppo * Free up the cached MDs. 2001ae08745Sheppo */ 2011ae08745Sheppo if (mdeg.md_curr) 2021ae08745Sheppo (void) md_fini_handle(mdeg.md_curr); 2031ae08745Sheppo 2041ae08745Sheppo if (mdeg.md_prev) 2051ae08745Sheppo (void) md_fini_handle(mdeg.md_prev); 2061ae08745Sheppo 2071ae08745Sheppo mutex_destroy(&mdeg.lock); 2081ae08745Sheppo } 2091ae08745Sheppo 2101ae08745Sheppo static mdeg_clnt_t * 2111ae08745Sheppo mdeg_alloc_clnt(void) 2121ae08745Sheppo { 2131ae08745Sheppo mdeg_clnt_t *clnt; 2141ae08745Sheppo int idx; 2151ae08745Sheppo mdeg_clnt_t *newtbl; 2161ae08745Sheppo uint_t newmaxclnts; 2171ae08745Sheppo uint_t newtblsz; 2181ae08745Sheppo uint_t oldtblsz; 2191ae08745Sheppo 2201ae08745Sheppo ASSERT(RW_WRITE_HELD(&mdeg.rwlock)); 2211ae08745Sheppo 2221ae08745Sheppo /* search for an unused slot in the table */ 2231ae08745Sheppo for (idx = 0; idx < mdeg.maxclnts; idx++) { 2241ae08745Sheppo clnt = &mdeg.tbl[idx]; 2251ae08745Sheppo if (!clnt->valid) { 2261ae08745Sheppo break; 2271ae08745Sheppo } 2281ae08745Sheppo } 2291ae08745Sheppo 2301ae08745Sheppo /* found any empty slot */ 2311ae08745Sheppo if (idx != mdeg.maxclnts) { 2321ae08745Sheppo goto found; 2331ae08745Sheppo } 2341ae08745Sheppo 2351ae08745Sheppo /* 2361ae08745Sheppo * There was no free space in the table. Grow 2371ae08745Sheppo * the table to double its current size. 2381ae08745Sheppo */ 2391ae08745Sheppo 2401ae08745Sheppo MDEG_DBG("client table full:\n"); 2411ae08745Sheppo MDEG_DUMP_TABLE(); 2421ae08745Sheppo 2431ae08745Sheppo newmaxclnts = mdeg.maxclnts * 2; 2441ae08745Sheppo newtblsz = newmaxclnts * sizeof (mdeg_clnt_t); 2451ae08745Sheppo 2461ae08745Sheppo newtbl = kmem_zalloc(newtblsz, KM_SLEEP); 2471ae08745Sheppo 2481ae08745Sheppo /* copy old table data to the new table */ 2491ae08745Sheppo oldtblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t); 2501ae08745Sheppo bcopy(mdeg.tbl, newtbl, oldtblsz); 2511ae08745Sheppo 2521ae08745Sheppo /* 2531ae08745Sheppo * Since the old table was full, the first free entry 2544e476149Srsmaeda * will be just past the end of the old table data in 2554e476149Srsmaeda * the new table. 2561ae08745Sheppo */ 2574e476149Srsmaeda clnt = &newtbl[mdeg.maxclnts]; 2581ae08745Sheppo 2591ae08745Sheppo /* clean up the old table */ 2601ae08745Sheppo kmem_free(mdeg.tbl, oldtblsz); 2611ae08745Sheppo mdeg.tbl = newtbl; 2621ae08745Sheppo mdeg.maxclnts = newmaxclnts; 2631ae08745Sheppo 2641ae08745Sheppo found: 2651ae08745Sheppo ASSERT(clnt->valid == 0); 2661ae08745Sheppo 2671ae08745Sheppo clnt->hdl = MDEG_ALLOC_HDL(idx, MDEG_HDL2COUNT(clnt->hdl)); 2681ae08745Sheppo 2691ae08745Sheppo return (clnt); 2701ae08745Sheppo } 2711ae08745Sheppo 2721ae08745Sheppo static mdeg_clnt_t * 2731ae08745Sheppo mdeg_get_client(mdeg_handle_t hdl) 2741ae08745Sheppo { 2751ae08745Sheppo int idx; 2761ae08745Sheppo mdeg_clnt_t *clnt; 2771ae08745Sheppo 2781ae08745Sheppo idx = MDEG_HDL2IDX(hdl); 2791ae08745Sheppo 2801ae08745Sheppo /* check if index is out of bounds */ 2811ae08745Sheppo if ((idx < 0) || (idx >= mdeg.maxclnts)) { 2821ae08745Sheppo MDEG_DBG("mdeg_get_client: index out of bounds\n"); 2831ae08745Sheppo return (NULL); 2841ae08745Sheppo } 2851ae08745Sheppo 2861ae08745Sheppo clnt = &mdeg.tbl[idx]; 2871ae08745Sheppo 2881ae08745Sheppo /* check for a valid client */ 2891ae08745Sheppo if (!clnt->valid) { 2901ae08745Sheppo MDEG_DBG("mdeg_get_client: client is not valid\n"); 2911ae08745Sheppo return (NULL); 2921ae08745Sheppo } 2931ae08745Sheppo 2941ae08745Sheppo /* make sure the handle is an exact match */ 2951ae08745Sheppo if (clnt->hdl != hdl) { 2961ae08745Sheppo MDEG_DBG("mdeg_get_client: bad handle\n"); 2971ae08745Sheppo return (NULL); 2981ae08745Sheppo } 2991ae08745Sheppo 3001ae08745Sheppo if (clnt->magic != MDEG_MAGIC) { 3011ae08745Sheppo MDEG_DBG("mdeg_get_client: bad magic\n"); 3021ae08745Sheppo return (NULL); 3031ae08745Sheppo } 3041ae08745Sheppo 3051ae08745Sheppo return (clnt); 3061ae08745Sheppo } 3071ae08745Sheppo 3081ae08745Sheppo /* 3091ae08745Sheppo * Send a notification to a client immediately after it registers. 3101ae08745Sheppo * The result_t is a list of all the nodes that match their specified 3111ae08745Sheppo * nodes of interest, all returned on the added list. This serves 3121ae08745Sheppo * as a base of reference to the client. All future MD updates are 3131ae08745Sheppo * relative to this list. 3141ae08745Sheppo */ 3151ae08745Sheppo static int 3161ae08745Sheppo mdeg_notify_client_reg(mdeg_clnt_t *clnt) 3171ae08745Sheppo { 3181ae08745Sheppo md_t *mdp = NULL; 3191ae08745Sheppo mde_str_cookie_t nname; 3201ae08745Sheppo mde_str_cookie_t aname; 3211ae08745Sheppo mde_cookie_t startnode; 3221ae08745Sheppo int nnodes; 3231ae08745Sheppo int nodechk; 3241ae08745Sheppo mde_cookie_t *listp = NULL; 3251ae08745Sheppo mdeg_result_t *mdeg_res = NULL; 3261ae08745Sheppo int rv = MDEG_SUCCESS; 3271ae08745Sheppo 3281ae08745Sheppo mutex_enter(&mdeg.lock); 3291ae08745Sheppo 3301ae08745Sheppo /* 3311ae08745Sheppo * Handle the special case where the node specification 3321ae08745Sheppo * is NULL. In this case, call the client callback without 3331ae08745Sheppo * any results. All processing is left to the client. 3341ae08745Sheppo */ 3351ae08745Sheppo if (clnt->pspec == NULL) { 3361ae08745Sheppo /* call the client callback */ 3371ae08745Sheppo (*clnt->cb)(clnt->cb_arg, NULL); 3381ae08745Sheppo goto done; 3391ae08745Sheppo } 3401ae08745Sheppo 3411ae08745Sheppo if ((mdp = md_get_handle()) == NULL) { 3421ae08745Sheppo cmn_err(CE_WARN, "unable to retrieve current MD"); 3431ae08745Sheppo rv = MDEG_FAILURE; 3441ae08745Sheppo goto done; 3451ae08745Sheppo } 3461ae08745Sheppo 3471ae08745Sheppo startnode = mdeg_find_start_node(mdp, clnt->pspec); 3481ae08745Sheppo if (startnode == MDE_INVAL_ELEM_COOKIE) { 3491ae08745Sheppo /* not much we can do */ 3501ae08745Sheppo cmn_err(CE_WARN, "unable to match node specifier"); 3511ae08745Sheppo rv = MDEG_FAILURE; 3521ae08745Sheppo goto done; 3531ae08745Sheppo } 3541ae08745Sheppo 3551ae08745Sheppo /* 3561ae08745Sheppo * Use zalloc to provide correct default values for the 3571ae08745Sheppo * unused removed, match_prev, and match_curr lists. 3581ae08745Sheppo */ 3591ae08745Sheppo mdeg_res = kmem_zalloc(sizeof (mdeg_result_t), KM_SLEEP); 3601ae08745Sheppo 3611ae08745Sheppo nname = md_find_name(mdp, clnt->nmatch->namep); 3621ae08745Sheppo aname = md_find_name(mdp, "fwd"); 3631ae08745Sheppo 3641ae08745Sheppo nnodes = md_scan_dag(mdp, startnode, nname, aname, NULL); 3651ae08745Sheppo 3661ae08745Sheppo if (nnodes == 0) { 3671ae08745Sheppo MDEG_DBG("mdeg_notify_client_reg: no nodes of interest\n"); 3681ae08745Sheppo rv = MDEG_SUCCESS; 3691ae08745Sheppo goto done; 3701ae08745Sheppo } else if (nnodes == -1) { 3711ae08745Sheppo MDEG_DBG("error scanning DAG\n"); 3721ae08745Sheppo rv = MDEG_FAILURE; 3731ae08745Sheppo goto done; 3741ae08745Sheppo } 3751ae08745Sheppo 3761ae08745Sheppo MDEG_DBG("mdeg_notify_client_reg: %d node%s of interest\n", 3771ae08745Sheppo nnodes, (nnodes == 1) ? "" : "s"); 3781ae08745Sheppo 3791ae08745Sheppo /* get the list of nodes of interest */ 3801ae08745Sheppo listp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP); 3811ae08745Sheppo nodechk = md_scan_dag(mdp, startnode, nname, aname, listp); 3821ae08745Sheppo 3831ae08745Sheppo ASSERT(nodechk == nnodes); 3841ae08745Sheppo 3851ae08745Sheppo mdeg_res->added.mdp = mdp; 3861ae08745Sheppo mdeg_res->added.mdep = listp; 3871ae08745Sheppo mdeg_res->added.nelem = nnodes; 3881ae08745Sheppo 3891ae08745Sheppo /* call the client callback */ 3901ae08745Sheppo (*clnt->cb)(clnt->cb_arg, mdeg_res); 3911ae08745Sheppo 3921ae08745Sheppo done: 3931ae08745Sheppo mutex_exit(&mdeg.lock); 3941ae08745Sheppo 3951ae08745Sheppo if (mdp) 3961ae08745Sheppo (void) md_fini_handle(mdp); 3971ae08745Sheppo 3981ae08745Sheppo if (listp) 3991ae08745Sheppo kmem_free(listp, sizeof (mde_cookie_t) * nnodes); 4001ae08745Sheppo 4011ae08745Sheppo if (mdeg_res) 4021ae08745Sheppo kmem_free(mdeg_res, sizeof (mdeg_result_t)); 4031ae08745Sheppo 4041ae08745Sheppo return (rv); 4051ae08745Sheppo } 4061ae08745Sheppo 4071ae08745Sheppo /* 4081ae08745Sheppo * Register to receive an event notification when the system 4091ae08745Sheppo * machine description is updated. 4101ae08745Sheppo * 4111ae08745Sheppo * Passing NULL for the node specification parameter is valid 4121ae08745Sheppo * as long as the match specification is also NULL. In this 4131ae08745Sheppo * case, the client will receive a notification when the MD 4141ae08745Sheppo * has been updated, but the callback will not include any 4151ae08745Sheppo * information. The client is then responsible for obtaining 4161ae08745Sheppo * its own copy of the system MD and performing any processing 4171ae08745Sheppo * manually. 4181ae08745Sheppo */ 4191ae08745Sheppo int 4201ae08745Sheppo mdeg_register(mdeg_node_spec_t *pspecp, mdeg_node_match_t *nmatchp, 4211ae08745Sheppo mdeg_cb_t cb, void *cb_arg, mdeg_handle_t *hdlp) 4221ae08745Sheppo { 4231ae08745Sheppo mdeg_clnt_t *clnt; 4241ae08745Sheppo 4251ae08745Sheppo /* 4261ae08745Sheppo * If the RW lock is held, a client is calling 4271ae08745Sheppo * register from its own callback. 4281ae08745Sheppo */ 4291ae08745Sheppo if (RW_LOCK_HELD(&mdeg.rwlock)) { 4301ae08745Sheppo MDEG_DBG("mdeg_register: rwlock already held\n"); 4311ae08745Sheppo return (MDEG_FAILURE); 4321ae08745Sheppo } 4331ae08745Sheppo 4341ae08745Sheppo /* node spec and node match must both be valid, or both NULL */ 4351ae08745Sheppo if (((pspecp != NULL) && (nmatchp == NULL)) || 4361ae08745Sheppo ((pspecp == NULL) && (nmatchp != NULL))) { 4371ae08745Sheppo MDEG_DBG("mdeg_register: invalid parameters\n"); 4381ae08745Sheppo return (MDEG_FAILURE); 4391ae08745Sheppo } 4401ae08745Sheppo 4411ae08745Sheppo rw_enter(&mdeg.rwlock, RW_WRITER); 4421ae08745Sheppo 4431ae08745Sheppo clnt = mdeg_alloc_clnt(); 4441ae08745Sheppo 4451ae08745Sheppo ASSERT(clnt); 4461ae08745Sheppo 4471ae08745Sheppo /* 4481ae08745Sheppo * Fill in the rest of the data 4491ae08745Sheppo */ 4501ae08745Sheppo clnt->nmatch = nmatchp; 4511ae08745Sheppo clnt->pspec = pspecp; 4521ae08745Sheppo clnt->cb = cb; 4531ae08745Sheppo clnt->cb_arg = cb_arg; 4541ae08745Sheppo clnt->magic = MDEG_MAGIC; 4551ae08745Sheppo 4561ae08745Sheppo /* do this last */ 4571ae08745Sheppo clnt->valid = B_TRUE; 4581ae08745Sheppo 4591ae08745Sheppo MDEG_DBG("client registered (0x%lx):\n", clnt->hdl); 4601ae08745Sheppo MDEG_DUMP_CLNT(clnt); 4611ae08745Sheppo 4621ae08745Sheppo mdeg.nclnts++; 4631ae08745Sheppo 4641ae08745Sheppo if (mdeg_notify_client_reg(clnt) != MDEG_SUCCESS) { 4651ae08745Sheppo bzero(clnt, sizeof (mdeg_clnt_t)); 4661ae08745Sheppo rw_exit(&mdeg.rwlock); 4671ae08745Sheppo return (MDEG_FAILURE); 4681ae08745Sheppo } 4691ae08745Sheppo 4701ae08745Sheppo rw_exit(&mdeg.rwlock); 4711ae08745Sheppo 4721ae08745Sheppo *hdlp = clnt->hdl; 4731ae08745Sheppo 4741ae08745Sheppo return (MDEG_SUCCESS); 4751ae08745Sheppo } 4761ae08745Sheppo 4771ae08745Sheppo int 4781ae08745Sheppo mdeg_unregister(mdeg_handle_t hdl) 4791ae08745Sheppo { 4801ae08745Sheppo mdeg_clnt_t *clnt; 4811ae08745Sheppo mdeg_handle_t mdh; 4821ae08745Sheppo 4831ae08745Sheppo /* 4841ae08745Sheppo * If the RW lock is held, a client is calling 4851ae08745Sheppo * unregister from its own callback. 4861ae08745Sheppo */ 4871ae08745Sheppo if (RW_LOCK_HELD(&mdeg.rwlock)) { 4881ae08745Sheppo MDEG_DBG("mdeg_unregister: rwlock already held\n"); 4891ae08745Sheppo return (MDEG_FAILURE); 4901ae08745Sheppo } 4911ae08745Sheppo 4921ae08745Sheppo /* lookup the client */ 4931ae08745Sheppo if ((clnt = mdeg_get_client(hdl)) == NULL) { 4941ae08745Sheppo return (MDEG_FAILURE); 4951ae08745Sheppo } 4961ae08745Sheppo 4971ae08745Sheppo rw_enter(&mdeg.rwlock, RW_WRITER); 4981ae08745Sheppo 4991ae08745Sheppo MDEG_DBG("client unregistered (0x%lx):\n", hdl); 5001ae08745Sheppo MDEG_DUMP_CLNT(clnt); 5011ae08745Sheppo 5021ae08745Sheppo /* save the handle to prevent reuse */ 5031ae08745Sheppo mdh = clnt->hdl; 5041ae08745Sheppo bzero(clnt, sizeof (mdeg_clnt_t)); 5051ae08745Sheppo 5061ae08745Sheppo clnt->hdl = mdh; 5071ae08745Sheppo 5081ae08745Sheppo mdeg.nclnts--; 5091ae08745Sheppo 5101ae08745Sheppo rw_exit(&mdeg.rwlock); 5111ae08745Sheppo 5121ae08745Sheppo return (MDEG_SUCCESS); 5131ae08745Sheppo } 5141ae08745Sheppo 5151ae08745Sheppo /* 5161ae08745Sheppo * Simple algorithm for now, grab the global lock and let all 5171ae08745Sheppo * the clients update themselves in parallel. There is a lot of 5181ae08745Sheppo * room for improvement here. We could eliminate some scans of 5194e476149Srsmaeda * the DAG by incrementally scanning at lower levels of the DAG 5201ae08745Sheppo * rather than having each client start its own scan from the root. 5211ae08745Sheppo */ 5221ae08745Sheppo void 5231ae08745Sheppo mdeg_notify_clients(void) 5241ae08745Sheppo { 5251ae08745Sheppo md_t *md_new; 5261ae08745Sheppo mdeg_clnt_t *clnt; 5271ae08745Sheppo int idx; 5281ae08745Sheppo int nclnt; 5291ae08745Sheppo 5301ae08745Sheppo rw_enter(&mdeg.rwlock, RW_READER); 5311ae08745Sheppo mutex_enter(&mdeg.lock); 5321ae08745Sheppo 5331ae08745Sheppo /* 5341ae08745Sheppo * Rotate the MDs 5351ae08745Sheppo */ 5361ae08745Sheppo if ((md_new = md_get_handle()) == NULL) { 5371ae08745Sheppo cmn_err(CE_WARN, "unable to retrieve new MD"); 5381ae08745Sheppo goto done; 5391ae08745Sheppo } 5401ae08745Sheppo 5411ae08745Sheppo if (mdeg.md_prev) { 5421ae08745Sheppo (void) md_fini_handle(mdeg.md_prev); 5431ae08745Sheppo } 5441ae08745Sheppo 5451ae08745Sheppo mdeg.md_prev = mdeg.md_curr; 5461ae08745Sheppo mdeg.md_curr = md_new; 5471ae08745Sheppo 5481ae08745Sheppo if (mdeg.nclnts == 0) { 5491ae08745Sheppo MDEG_DBG("mdeg_notify_clients: no clients registered\n"); 5501ae08745Sheppo goto done; 5511ae08745Sheppo } 5521ae08745Sheppo 5531ae08745Sheppo /* dispatch the update notification to all clients */ 5541ae08745Sheppo for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) { 5551ae08745Sheppo clnt = &mdeg.tbl[idx]; 5561ae08745Sheppo 5571ae08745Sheppo if (!clnt->valid) 5581ae08745Sheppo continue; 5591ae08745Sheppo 5601ae08745Sheppo MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl, 5611ae08745Sheppo ++nclnt, mdeg.nclnts); 5621ae08745Sheppo 5631ae08745Sheppo (void) taskq_dispatch(mdeg.taskq, mdeg_notify_client, 5641ae08745Sheppo (void *)clnt, TQ_SLEEP); 5651ae08745Sheppo } 5661ae08745Sheppo 5671ae08745Sheppo taskq_wait(mdeg.taskq); 5681ae08745Sheppo 5691ae08745Sheppo done: 5701ae08745Sheppo mutex_exit(&mdeg.lock); 5711ae08745Sheppo rw_exit(&mdeg.rwlock); 5721ae08745Sheppo } 5731ae08745Sheppo 5741ae08745Sheppo static void 5751ae08745Sheppo mdeg_notify_client(void *arg) 5761ae08745Sheppo { 5771ae08745Sheppo mdeg_clnt_t *clnt = (mdeg_clnt_t *)arg; 5781ae08745Sheppo md_diff_cookie_t mdd = MD_INVAL_DIFF_COOKIE; 5791ae08745Sheppo mdeg_result_t mdeg_res; 5801ae08745Sheppo mde_cookie_t md_prev_start; 5811ae08745Sheppo mde_cookie_t md_curr_start; 5821ae08745Sheppo 5831ae08745Sheppo rw_enter(&mdeg.rwlock, RW_READER); 5841ae08745Sheppo 5851ae08745Sheppo if (!mdeg.enabled) { 5861ae08745Sheppo /* trying to shutdown */ 5871ae08745Sheppo MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n"); 5881ae08745Sheppo goto cleanup; 5891ae08745Sheppo } 5901ae08745Sheppo 5911ae08745Sheppo /* 5921ae08745Sheppo * Handle the special case where the node specification 5931ae08745Sheppo * is NULL. In this case, call the client callback without 5941ae08745Sheppo * any results. All processing is left to the client. 5951ae08745Sheppo */ 5961ae08745Sheppo if (clnt->pspec == NULL) { 5971ae08745Sheppo /* call the client callback */ 5981ae08745Sheppo (*clnt->cb)(clnt->cb_arg, NULL); 5991ae08745Sheppo 6001ae08745Sheppo MDEG_DBG("MDEG client callback done\n"); 6011ae08745Sheppo goto cleanup; 6021ae08745Sheppo } 6031ae08745Sheppo 6041ae08745Sheppo /* find our start nodes */ 6051ae08745Sheppo md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec); 6061ae08745Sheppo if (md_prev_start == MDE_INVAL_ELEM_COOKIE) { 6071ae08745Sheppo goto cleanup; 6081ae08745Sheppo } 6091ae08745Sheppo 6101ae08745Sheppo md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec); 6111ae08745Sheppo if (md_curr_start == MDE_INVAL_ELEM_COOKIE) { 6121ae08745Sheppo goto cleanup; 6131ae08745Sheppo } 6141ae08745Sheppo 6151ae08745Sheppo /* diff the MDs */ 6161ae08745Sheppo mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr, 6171ae08745Sheppo md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp); 6181ae08745Sheppo 6191ae08745Sheppo if (mdd == MD_INVAL_DIFF_COOKIE) { 6201ae08745Sheppo MDEG_DBG("unable to diff MDs\n"); 6211ae08745Sheppo goto cleanup; 6221ae08745Sheppo } 6231ae08745Sheppo 6241ae08745Sheppo /* 6251ae08745Sheppo * Cache the results of the diff 6261ae08745Sheppo */ 6271ae08745Sheppo mdeg_get_diff_results(mdd, &mdeg_res); 6281ae08745Sheppo 6291ae08745Sheppo /* call the client callback */ 6301ae08745Sheppo (*clnt->cb)(clnt->cb_arg, &mdeg_res); 6311ae08745Sheppo 6321ae08745Sheppo MDEG_DBG("MDEG client callback done\n"); 6331ae08745Sheppo 6341ae08745Sheppo cleanup: 6351ae08745Sheppo rw_exit(&mdeg.rwlock); 6361ae08745Sheppo 6371ae08745Sheppo if (mdd != MD_INVAL_DIFF_COOKIE) 6381ae08745Sheppo (void) md_diff_fini(mdd); 6391ae08745Sheppo } 6401ae08745Sheppo 6411ae08745Sheppo static mde_cookie_t 6421ae08745Sheppo mdeg_find_start_node(md_t *md, mdeg_node_spec_t *nspec) 6431ae08745Sheppo { 6441ae08745Sheppo mde_cookie_t *nodesp; 6451ae08745Sheppo mde_str_cookie_t nname; 6461ae08745Sheppo mde_str_cookie_t aname; 6471ae08745Sheppo int nnodes; 6481ae08745Sheppo int idx; 6491ae08745Sheppo 6501ae08745Sheppo if ((md == NULL) || (nspec == NULL)) 6511ae08745Sheppo return (MDE_INVAL_ELEM_COOKIE); 6521ae08745Sheppo 6531ae08745Sheppo nname = md_find_name(md, nspec->namep); 6541ae08745Sheppo aname = md_find_name(md, "fwd"); 6551ae08745Sheppo 6561ae08745Sheppo nnodes = md_scan_dag(md, NULL, nname, aname, NULL); 6571ae08745Sheppo if (nnodes == 0) 6581ae08745Sheppo return (MDE_INVAL_ELEM_COOKIE); 6591ae08745Sheppo 6601ae08745Sheppo nodesp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP); 6611ae08745Sheppo 6621ae08745Sheppo (void) md_scan_dag(md, NULL, nname, aname, nodesp); 6631ae08745Sheppo 6641ae08745Sheppo for (idx = 0; idx < nnodes; idx++) { 6651ae08745Sheppo 6661ae08745Sheppo if (mdeg_node_spec_match(md, nodesp[idx], nspec)) { 6671ae08745Sheppo mde_cookie_t res = nodesp[idx]; 6681ae08745Sheppo 6691ae08745Sheppo kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes); 6701ae08745Sheppo return (res); 6711ae08745Sheppo } 6721ae08745Sheppo } 6731ae08745Sheppo 6741ae08745Sheppo kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes); 6751ae08745Sheppo return (MDE_INVAL_ELEM_COOKIE); 6761ae08745Sheppo } 6771ae08745Sheppo 6781ae08745Sheppo static boolean_t 6791ae08745Sheppo mdeg_node_spec_match(md_t *md, mde_cookie_t node, mdeg_node_spec_t *nspec) 6801ae08745Sheppo { 6811ae08745Sheppo mdeg_prop_spec_t *prop; 6821ae08745Sheppo 6831ae08745Sheppo ASSERT(md && nspec); 6841ae08745Sheppo ASSERT(node != MDE_INVAL_ELEM_COOKIE); 6851ae08745Sheppo 6861ae08745Sheppo prop = nspec->specp; 6871ae08745Sheppo 6881ae08745Sheppo while (prop->type != MDET_LIST_END) { 6891ae08745Sheppo 6901ae08745Sheppo switch (prop->type) { 6911ae08745Sheppo case MDET_PROP_VAL: { 6921ae08745Sheppo uint64_t val; 6931ae08745Sheppo 6941ae08745Sheppo if (md_get_prop_val(md, node, prop->namep, &val) != 0) 6951ae08745Sheppo return (B_FALSE); 6961ae08745Sheppo 6971ae08745Sheppo if (prop->ps_val != val) 6981ae08745Sheppo return (B_FALSE); 6991ae08745Sheppo 7001ae08745Sheppo break; 7011ae08745Sheppo } 7021ae08745Sheppo case MDET_PROP_STR: { 7031ae08745Sheppo char *str; 7041ae08745Sheppo 7051ae08745Sheppo if (md_get_prop_str(md, node, prop->namep, &str) != 0) 7061ae08745Sheppo return (B_FALSE); 7071ae08745Sheppo 7081ae08745Sheppo if (strcmp(prop->ps_str, str) != 0) 7091ae08745Sheppo return (B_FALSE); 7101ae08745Sheppo 7111ae08745Sheppo break; 7121ae08745Sheppo } 7131ae08745Sheppo 7141ae08745Sheppo default: 7151ae08745Sheppo return (B_FALSE); 7161ae08745Sheppo } 7171ae08745Sheppo 7181ae08745Sheppo prop++; 7191ae08745Sheppo } 7201ae08745Sheppo 7211ae08745Sheppo return (B_TRUE); 7221ae08745Sheppo } 7231ae08745Sheppo 7241ae08745Sheppo static void 7251ae08745Sheppo mdeg_get_diff_results(md_diff_cookie_t mdd, mdeg_result_t *res) 7261ae08745Sheppo { 7271ae08745Sheppo /* 7281ae08745Sheppo * Cache added nodes. 7291ae08745Sheppo */ 7301ae08745Sheppo res->added.mdp = mdeg.md_curr; 7311ae08745Sheppo res->added.nelem = md_diff_added(mdd, &(res->added.mdep)); 7321ae08745Sheppo 7331ae08745Sheppo if (res->added.nelem == -1) { 7341ae08745Sheppo bzero(&(res->added), sizeof (mdeg_diff_t)); 7351ae08745Sheppo } 7361ae08745Sheppo 7371ae08745Sheppo /* 7381ae08745Sheppo * Cache removed nodes. 7391ae08745Sheppo */ 7401ae08745Sheppo res->removed.mdp = mdeg.md_prev; 7411ae08745Sheppo res->removed.nelem = md_diff_removed(mdd, &(res->removed.mdep)); 7421ae08745Sheppo 7431ae08745Sheppo if (res->removed.nelem == -1) { 7441ae08745Sheppo bzero(&(res->removed), sizeof (mdeg_diff_t)); 7451ae08745Sheppo } 7461ae08745Sheppo 7471ae08745Sheppo /* 7481ae08745Sheppo * Cache matching node pairs. 7491ae08745Sheppo */ 7501ae08745Sheppo res->match_curr.mdp = mdeg.md_curr; 7511ae08745Sheppo res->match_prev.mdp = mdeg.md_prev; 7521ae08745Sheppo res->match_curr.nelem = md_diff_matched(mdd, &(res->match_prev.mdep), 7531ae08745Sheppo &(res->match_curr.mdep)); 7541ae08745Sheppo res->match_prev.nelem = res->match_curr.nelem; 7551ae08745Sheppo 7561ae08745Sheppo if (res->match_prev.nelem == -1) { 7571ae08745Sheppo bzero(&(res->match_prev), sizeof (mdeg_diff_t)); 7581ae08745Sheppo bzero(&(res->match_curr), sizeof (mdeg_diff_t)); 7591ae08745Sheppo } 7601ae08745Sheppo } 7611ae08745Sheppo 7621ae08745Sheppo #ifdef DEBUG 7631ae08745Sheppo /* 7641ae08745Sheppo * Generate a string that represents the node specifier 7651ae08745Sheppo * structure. Clamp the string length if the specifier 7661ae08745Sheppo * structure contains too much information. 7671ae08745Sheppo * 7681ae08745Sheppo * General form: 7691ae08745Sheppo * 7701ae08745Sheppo * <nodename>:{<propname>=<propval>,...} 7711ae08745Sheppo * e.g. 7721ae08745Sheppo * vdevice:{name=vsw,reg=0x0} 7731ae08745Sheppo */ 7741ae08745Sheppo static void 7751ae08745Sheppo mdeg_spec_str(mdeg_node_spec_t *spec, char *buf, int len) 7761ae08745Sheppo { 7771ae08745Sheppo mdeg_prop_spec_t *prop; 7781ae08745Sheppo int offset; 7791ae08745Sheppo boolean_t first = B_TRUE; 7801ae08745Sheppo char *end = buf + len; 7811ae08745Sheppo 7821ae08745Sheppo offset = snprintf(buf, len, "%s:{", spec->namep); 7831ae08745Sheppo 7841ae08745Sheppo buf += offset; 7851ae08745Sheppo len -= offset; 7861ae08745Sheppo if (len <= 0) 7871ae08745Sheppo goto trunc; 7881ae08745Sheppo 7891ae08745Sheppo prop = spec->specp; 7901ae08745Sheppo 7911ae08745Sheppo while (prop->type != MDET_LIST_END) { 7921ae08745Sheppo 7931ae08745Sheppo switch (prop->type) { 7941ae08745Sheppo case MDET_PROP_VAL: 7951ae08745Sheppo offset = snprintf(buf, len, "%s%s=0x%lx", 7961ae08745Sheppo (first) ? "" : ",", prop->namep, prop->ps_val); 7971ae08745Sheppo buf += offset; 7981ae08745Sheppo len -= offset; 7991ae08745Sheppo if (len <= 0) 8001ae08745Sheppo goto trunc; 8011ae08745Sheppo break; 8021ae08745Sheppo 8031ae08745Sheppo case MDET_PROP_STR: 8041ae08745Sheppo offset = snprintf(buf, len, "%s%s=%s", 8051ae08745Sheppo (first) ? "" : ",", prop->namep, prop->ps_str); 8061ae08745Sheppo buf += offset; 8071ae08745Sheppo len -= offset; 8081ae08745Sheppo if (len <= 0) 8091ae08745Sheppo goto trunc; 8101ae08745Sheppo break; 8111ae08745Sheppo 8121ae08745Sheppo default: 8131ae08745Sheppo (void) snprintf(buf, len, "}"); 8141ae08745Sheppo return; 8151ae08745Sheppo } 8161ae08745Sheppo 8171ae08745Sheppo if (first) 8181ae08745Sheppo first = B_FALSE; 8191ae08745Sheppo prop++; 8201ae08745Sheppo } 8211ae08745Sheppo 8221ae08745Sheppo (void) snprintf(buf, len, "}"); 8231ae08745Sheppo return; 8241ae08745Sheppo 8251ae08745Sheppo trunc: 8261ae08745Sheppo /* string too long, truncate it */ 8271ae08745Sheppo buf = end - (strlen(trunc_str) + 1); 8281ae08745Sheppo (void) sprintf(buf, trunc_str); 8291ae08745Sheppo } 8301ae08745Sheppo 8311ae08745Sheppo /* 8321ae08745Sheppo * Generate a string that represents the match structure. 8331ae08745Sheppo * Clamp the string length if the match structure contains 8341ae08745Sheppo * too much information. 8351ae08745Sheppo * 8361ae08745Sheppo * General form: 8371ae08745Sheppo * 8381ae08745Sheppo * <nodename>:{<propname>,...} 8391ae08745Sheppo * e.g. 8401ae08745Sheppo * nmatch=vport:{reg} 8411ae08745Sheppo */ 8421ae08745Sheppo static void 8431ae08745Sheppo mdeg_match_str(mdeg_node_match_t *match, char *buf, int len) 8441ae08745Sheppo { 8451ae08745Sheppo md_prop_match_t *prop; 8461ae08745Sheppo int offset; 8471ae08745Sheppo boolean_t first = B_TRUE; 8481ae08745Sheppo char *end = buf + len; 8491ae08745Sheppo 8501ae08745Sheppo offset = snprintf(buf, len, "%s:{", match->namep); 8511ae08745Sheppo 8521ae08745Sheppo buf += offset; 8531ae08745Sheppo len -= offset; 8541ae08745Sheppo if (len <= 0) 8551ae08745Sheppo goto trunc; 8561ae08745Sheppo 8571ae08745Sheppo prop = match->matchp; 8581ae08745Sheppo 8591ae08745Sheppo while (prop->type != MDET_LIST_END) { 8601ae08745Sheppo offset = snprintf(buf, len, "%s%s", (first) ? "" : ",", 8611ae08745Sheppo prop->namep); 8621ae08745Sheppo buf += offset; 8631ae08745Sheppo len -= offset; 8641ae08745Sheppo if (len <= 0) 8651ae08745Sheppo goto trunc; 8661ae08745Sheppo 8671ae08745Sheppo if (first) 8681ae08745Sheppo first = B_FALSE; 8691ae08745Sheppo prop++; 8701ae08745Sheppo } 8711ae08745Sheppo 8721ae08745Sheppo (void) snprintf(buf, len, "}"); 8731ae08745Sheppo return; 8741ae08745Sheppo 8751ae08745Sheppo trunc: 8761ae08745Sheppo /* string too long, truncate it */ 8771ae08745Sheppo buf = end - (strlen(trunc_str) + 1); 8781ae08745Sheppo (void) sprintf(buf, trunc_str); 8791ae08745Sheppo } 8801ae08745Sheppo 8811ae08745Sheppo #define MAX_FIELD_STR 80 8821ae08745Sheppo 8831ae08745Sheppo static void 8841ae08745Sheppo mdeg_dump_clnt(mdeg_clnt_t *clnt) 8851ae08745Sheppo { 886*9853d9e8SJason Beloro char str[MAX_FIELD_STR] = ""; 8871ae08745Sheppo 8881ae08745Sheppo if (!clnt->valid) { 8891ae08745Sheppo MDEG_DBG(" valid=B_FALSE\n"); 8901ae08745Sheppo return; 8911ae08745Sheppo } 8921ae08745Sheppo 893*9853d9e8SJason Beloro if (clnt->pspec) { 8941ae08745Sheppo mdeg_spec_str(clnt->pspec, str, MAX_FIELD_STR); 8951ae08745Sheppo MDEG_DBG(" pspecp=%s\n", str); 896*9853d9e8SJason Beloro } 8971ae08745Sheppo 898*9853d9e8SJason Beloro if (clnt->nmatch) { 8991ae08745Sheppo mdeg_match_str(clnt->nmatch, str, MAX_FIELD_STR); 9001ae08745Sheppo MDEG_DBG(" nmatch=%s\n", str); 9011ae08745Sheppo } 902*9853d9e8SJason Beloro } 9031ae08745Sheppo 9041ae08745Sheppo static void 9051ae08745Sheppo mdeg_dump_table(void) 9061ae08745Sheppo { 9071ae08745Sheppo int idx; 9081ae08745Sheppo mdeg_clnt_t *clnt; 9091ae08745Sheppo 9101ae08745Sheppo for (idx = 0; idx < mdeg.maxclnts; idx++) { 9111ae08745Sheppo clnt = &(mdeg.tbl[idx]); 9121ae08745Sheppo 9131ae08745Sheppo MDEG_DBG("client %d (0x%lx):\n", idx, clnt->hdl); 9141ae08745Sheppo mdeg_dump_clnt(clnt); 9151ae08745Sheppo } 9161ae08745Sheppo } 9171ae08745Sheppo #endif /* DEBUG */ 918