11991Sheppo /*
21991Sheppo * CDDL HEADER START
31991Sheppo *
41991Sheppo * The contents of this file are subject to the terms of the
51991Sheppo * Common Development and Distribution License (the "License").
61991Sheppo * You may not use this file except in compliance with the License.
71991Sheppo *
81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo * or http://www.opensolaris.org/os/licensing.
101991Sheppo * See the License for the specific language governing permissions
111991Sheppo * and limitations under the License.
121991Sheppo *
131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo *
191991Sheppo * CDDL HEADER END
201991Sheppo */
211991Sheppo
221991Sheppo /*
2311543SWentao.Yang@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
241991Sheppo * Use is subject to license terms.
251991Sheppo */
261991Sheppo
271991Sheppo /*
281991Sheppo * MD Event Generator (MDEG) Module
291991Sheppo */
301991Sheppo
311991Sheppo #include <sys/machsystm.h>
321991Sheppo #include <sys/taskq.h>
331991Sheppo #include <sys/disp.h>
341991Sheppo #include <sys/cmn_err.h>
351991Sheppo #include <sys/note.h>
361991Sheppo
371991Sheppo #include <sys/mdeg.h>
381991Sheppo #include <sys/mach_descrip.h>
391991Sheppo #include <sys/mdesc.h>
401991Sheppo
411991Sheppo /*
421991Sheppo * A single client registration
431991Sheppo */
441991Sheppo typedef struct mdeg_clnt {
451991Sheppo boolean_t valid; /* structure is in active use */
461991Sheppo mdeg_node_match_t *nmatch; /* node match filter */
471991Sheppo mdeg_node_spec_t *pspec; /* parent match filter */
481991Sheppo mdeg_cb_t cb; /* the client callback */
491991Sheppo caddr_t cb_arg; /* argument to the callback */
501991Sheppo uint64_t magic; /* sanity checking magic */
511991Sheppo mdeg_handle_t hdl; /* handle assigned by MDEG */
521991Sheppo } mdeg_clnt_t;
531991Sheppo
541991Sheppo /*
551991Sheppo * Global MDEG data
561991Sheppo *
571991Sheppo * Locking Strategy:
581991Sheppo *
593759Srsmaeda * mdeg.lock - lock used to synchronize system-wide MD updates. An
601991Sheppo * MD update must be treated as an atomic event. The lock is
611991Sheppo * taken when notification that a new MD is available and held
621991Sheppo * until all clients have been notified.
631991Sheppo *
643759Srsmaeda * mdeg.rwlock - lock used to synchronize access to the table of
651991Sheppo * registered clients. The reader lock must be held when looking
661991Sheppo * up client information in the table. The writer lock must be
671991Sheppo * held when modifying any client information.
681991Sheppo */
691991Sheppo static struct mdeg {
701991Sheppo taskq_t *taskq; /* for internal processing */
711991Sheppo boolean_t enabled; /* enable/disable taskq processing */
721991Sheppo kmutex_t lock; /* synchronize MD updates */
731991Sheppo md_t *md_prev; /* previous MD */
741991Sheppo md_t *md_curr; /* current MD */
751991Sheppo mdeg_clnt_t *tbl; /* table of registered clients */
761991Sheppo krwlock_t rwlock; /* client table lock */
771991Sheppo uint_t maxclnts; /* client table size */
781991Sheppo uint_t nclnts; /* current number of clients */
791991Sheppo } mdeg;
801991Sheppo
811991Sheppo /*
821991Sheppo * Debugging routines
831991Sheppo */
841991Sheppo #ifdef DEBUG
851991Sheppo uint_t mdeg_debug = 0x0;
861991Sheppo
871991Sheppo static void mdeg_dump_clnt(mdeg_clnt_t *clnt);
881991Sheppo static void mdeg_dump_table(void);
891991Sheppo
901991Sheppo #define MDEG_DBG if (mdeg_debug) printf
911991Sheppo #define MDEG_DUMP_CLNT mdeg_dump_clnt
921991Sheppo #define MDEG_DUMP_TABLE mdeg_dump_table
931991Sheppo
941991Sheppo #else /* DEBUG */
951991Sheppo
961991Sheppo #define MDEG_DBG _NOTE(CONSTCOND) if (0) printf
971991Sheppo #define MDEG_DUMP_CLNT
981991Sheppo #define MDEG_DUMP_TABLE()
991991Sheppo
1001991Sheppo #endif /* DEBUG */
1011991Sheppo
1021991Sheppo /*
1031991Sheppo * Global constants
1041991Sheppo */
1051991Sheppo #define MDEG_MAX_TASKQ_THR 512 /* maximum number of taskq threads */
1061991Sheppo #define MDEG_MAX_CLNTS_INIT 64 /* initial client table size */
1071991Sheppo
1081991Sheppo #define MDEG_MAGIC 0x4D4445475F48444Cull /* 'MDEG_HDL' */
1091991Sheppo
1101991Sheppo /*
1111991Sheppo * A client handle is a 64 bit value with two pieces of
1121991Sheppo * information encoded in it. The upper 32 bits are the
1131991Sheppo * index into the table of a particular client structure.
1141991Sheppo * The lower 32 bits are a counter that is incremented
1151991Sheppo * each time a client structure is reused.
1161991Sheppo */
1171991Sheppo #define MDEG_IDX_SHIFT 32
1181991Sheppo #define MDEG_COUNT_MASK 0xfffffffful
1191991Sheppo
1201991Sheppo #define MDEG_ALLOC_HDL(_idx, _count) (((uint64_t)_idx << MDEG_IDX_SHIFT) | \
1211991Sheppo ((uint64_t)(_count + 1) & \
1221991Sheppo MDEG_COUNT_MASK))
1231991Sheppo #define MDEG_HDL2IDX(hdl) (hdl >> MDEG_IDX_SHIFT)
1241991Sheppo #define MDEG_HDL2COUNT(hdl) (hdl & MDEG_COUNT_MASK)
1251991Sheppo
1261991Sheppo static const char trunc_str[] = " ... }";
1271991Sheppo
1281991Sheppo /*
1291991Sheppo * Utility routines
1301991Sheppo */
1311991Sheppo static mdeg_clnt_t *mdeg_alloc_clnt(void);
1321991Sheppo static void mdeg_notify_client(void *);
1331991Sheppo static mde_cookie_t mdeg_find_start_node(md_t *, mdeg_node_spec_t *);
1341991Sheppo static boolean_t mdeg_node_spec_match(md_t *, mde_cookie_t, mdeg_node_spec_t *);
1351991Sheppo static void mdeg_get_diff_results(md_diff_cookie_t, mdeg_result_t *);
1361991Sheppo
1371991Sheppo int
mdeg_init(void)1381991Sheppo mdeg_init(void)
1391991Sheppo {
1401991Sheppo int tblsz;
1411991Sheppo
1421991Sheppo /*
1431991Sheppo * Grab the current MD
1441991Sheppo */
1451991Sheppo if ((mdeg.md_curr = md_get_handle()) == NULL) {
1461991Sheppo cmn_err(CE_WARN, "unable to cache snapshot of MD");
1471991Sheppo return (-1);
1481991Sheppo }
1491991Sheppo
1501991Sheppo /*
1511991Sheppo * Initialize table of registered clients
1521991Sheppo */
1531991Sheppo mdeg.maxclnts = MDEG_MAX_CLNTS_INIT;
1541991Sheppo
1551991Sheppo tblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
1561991Sheppo mdeg.tbl = kmem_zalloc(tblsz, KM_SLEEP);
1571991Sheppo
1581991Sheppo rw_init(&mdeg.rwlock, NULL, RW_DRIVER, NULL);
1591991Sheppo
1601991Sheppo mdeg.nclnts = 0;
1611991Sheppo
1621991Sheppo /*
1631991Sheppo * Initialize global lock
1641991Sheppo */
1651991Sheppo mutex_init(&mdeg.lock, NULL, MUTEX_DRIVER, NULL);
1661991Sheppo
1671991Sheppo /*
1681991Sheppo * Initialize the task queue
1691991Sheppo */
1701991Sheppo mdeg.taskq = taskq_create("mdeg_taskq", 1, minclsyspri, 1,
1711991Sheppo MDEG_MAX_TASKQ_THR, TASKQ_PREPOPULATE | TASKQ_DYNAMIC);
1721991Sheppo
1731991Sheppo /* ready to begin handling clients */
1741991Sheppo mdeg.enabled = B_TRUE;
1751991Sheppo
1761991Sheppo return (0);
1771991Sheppo }
1781991Sheppo
1791991Sheppo void
mdeg_fini(void)1801991Sheppo mdeg_fini(void)
1811991Sheppo {
1821991Sheppo /*
1831991Sheppo * Flip the enabled switch off to make sure that
1841991Sheppo * no events get dispatched while things are being
1851991Sheppo * torn down.
1861991Sheppo */
1871991Sheppo mdeg.enabled = B_FALSE;
1881991Sheppo
1891991Sheppo /* destroy the task queue */
1901991Sheppo taskq_destroy(mdeg.taskq);
1911991Sheppo
1921991Sheppo /*
1931991Sheppo * Deallocate the table of registered clients
1941991Sheppo */
1951991Sheppo kmem_free(mdeg.tbl, mdeg.maxclnts * sizeof (mdeg_clnt_t));
1961991Sheppo rw_destroy(&mdeg.rwlock);
1971991Sheppo
1981991Sheppo /*
1991991Sheppo * Free up the cached MDs.
2001991Sheppo */
2011991Sheppo if (mdeg.md_curr)
2021991Sheppo (void) md_fini_handle(mdeg.md_curr);
2031991Sheppo
2041991Sheppo if (mdeg.md_prev)
2051991Sheppo (void) md_fini_handle(mdeg.md_prev);
2061991Sheppo
2071991Sheppo mutex_destroy(&mdeg.lock);
2081991Sheppo }
2091991Sheppo
2101991Sheppo static mdeg_clnt_t *
mdeg_alloc_clnt(void)2111991Sheppo mdeg_alloc_clnt(void)
2121991Sheppo {
2131991Sheppo mdeg_clnt_t *clnt;
2141991Sheppo int idx;
2151991Sheppo mdeg_clnt_t *newtbl;
2161991Sheppo uint_t newmaxclnts;
2171991Sheppo uint_t newtblsz;
2181991Sheppo uint_t oldtblsz;
2191991Sheppo
2201991Sheppo ASSERT(RW_WRITE_HELD(&mdeg.rwlock));
2211991Sheppo
2221991Sheppo /* search for an unused slot in the table */
2231991Sheppo for (idx = 0; idx < mdeg.maxclnts; idx++) {
2241991Sheppo clnt = &mdeg.tbl[idx];
2251991Sheppo if (!clnt->valid) {
2261991Sheppo break;
2271991Sheppo }
2281991Sheppo }
2291991Sheppo
2301991Sheppo /* found any empty slot */
2311991Sheppo if (idx != mdeg.maxclnts) {
2321991Sheppo goto found;
2331991Sheppo }
2341991Sheppo
2351991Sheppo /*
2361991Sheppo * There was no free space in the table. Grow
2371991Sheppo * the table to double its current size.
2381991Sheppo */
2391991Sheppo
2401991Sheppo MDEG_DBG("client table full:\n");
2411991Sheppo MDEG_DUMP_TABLE();
2421991Sheppo
2431991Sheppo newmaxclnts = mdeg.maxclnts * 2;
2441991Sheppo newtblsz = newmaxclnts * sizeof (mdeg_clnt_t);
2451991Sheppo
2461991Sheppo newtbl = kmem_zalloc(newtblsz, KM_SLEEP);
2471991Sheppo
2481991Sheppo /* copy old table data to the new table */
2491991Sheppo oldtblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
2501991Sheppo bcopy(mdeg.tbl, newtbl, oldtblsz);
2511991Sheppo
2521991Sheppo /*
2531991Sheppo * Since the old table was full, the first free entry
2543759Srsmaeda * will be just past the end of the old table data in
2553759Srsmaeda * the new table.
2561991Sheppo */
2573759Srsmaeda clnt = &newtbl[mdeg.maxclnts];
2581991Sheppo
2591991Sheppo /* clean up the old table */
2601991Sheppo kmem_free(mdeg.tbl, oldtblsz);
2611991Sheppo mdeg.tbl = newtbl;
2621991Sheppo mdeg.maxclnts = newmaxclnts;
2631991Sheppo
2641991Sheppo found:
2651991Sheppo ASSERT(clnt->valid == 0);
2661991Sheppo
2671991Sheppo clnt->hdl = MDEG_ALLOC_HDL(idx, MDEG_HDL2COUNT(clnt->hdl));
2681991Sheppo
2691991Sheppo return (clnt);
2701991Sheppo }
2711991Sheppo
2721991Sheppo static mdeg_clnt_t *
mdeg_get_client(mdeg_handle_t hdl)2731991Sheppo mdeg_get_client(mdeg_handle_t hdl)
2741991Sheppo {
2751991Sheppo int idx;
2761991Sheppo mdeg_clnt_t *clnt;
2771991Sheppo
2781991Sheppo idx = MDEG_HDL2IDX(hdl);
2791991Sheppo
2801991Sheppo /* check if index is out of bounds */
2811991Sheppo if ((idx < 0) || (idx >= mdeg.maxclnts)) {
2821991Sheppo MDEG_DBG("mdeg_get_client: index out of bounds\n");
2831991Sheppo return (NULL);
2841991Sheppo }
2851991Sheppo
2861991Sheppo clnt = &mdeg.tbl[idx];
2871991Sheppo
2881991Sheppo /* check for a valid client */
2891991Sheppo if (!clnt->valid) {
2901991Sheppo MDEG_DBG("mdeg_get_client: client is not valid\n");
2911991Sheppo return (NULL);
2921991Sheppo }
2931991Sheppo
2941991Sheppo /* make sure the handle is an exact match */
2951991Sheppo if (clnt->hdl != hdl) {
2961991Sheppo MDEG_DBG("mdeg_get_client: bad handle\n");
2971991Sheppo return (NULL);
2981991Sheppo }
2991991Sheppo
3001991Sheppo if (clnt->magic != MDEG_MAGIC) {
3011991Sheppo MDEG_DBG("mdeg_get_client: bad magic\n");
3021991Sheppo return (NULL);
3031991Sheppo }
3041991Sheppo
3051991Sheppo return (clnt);
3061991Sheppo }
3071991Sheppo
3081991Sheppo /*
3091991Sheppo * Send a notification to a client immediately after it registers.
3101991Sheppo * The result_t is a list of all the nodes that match their specified
3111991Sheppo * nodes of interest, all returned on the added list. This serves
3121991Sheppo * as a base of reference to the client. All future MD updates are
3131991Sheppo * relative to this list.
3141991Sheppo */
3151991Sheppo static int
mdeg_notify_client_reg(mdeg_clnt_t * clnt)3161991Sheppo mdeg_notify_client_reg(mdeg_clnt_t *clnt)
3171991Sheppo {
3181991Sheppo md_t *mdp = NULL;
3191991Sheppo mde_str_cookie_t nname;
3201991Sheppo mde_str_cookie_t aname;
3211991Sheppo mde_cookie_t startnode;
3221991Sheppo int nnodes;
3231991Sheppo int nodechk;
3241991Sheppo mde_cookie_t *listp = NULL;
3251991Sheppo mdeg_result_t *mdeg_res = NULL;
3261991Sheppo int rv = MDEG_SUCCESS;
3271991Sheppo
3281991Sheppo mutex_enter(&mdeg.lock);
3291991Sheppo
3301991Sheppo /*
3311991Sheppo * Handle the special case where the node specification
3321991Sheppo * is NULL. In this case, call the client callback without
3331991Sheppo * any results. All processing is left to the client.
3341991Sheppo */
3351991Sheppo if (clnt->pspec == NULL) {
3361991Sheppo /* call the client callback */
3371991Sheppo (*clnt->cb)(clnt->cb_arg, NULL);
3381991Sheppo goto done;
3391991Sheppo }
3401991Sheppo
3411991Sheppo if ((mdp = md_get_handle()) == NULL) {
3421991Sheppo cmn_err(CE_WARN, "unable to retrieve current MD");
3431991Sheppo rv = MDEG_FAILURE;
3441991Sheppo goto done;
3451991Sheppo }
3461991Sheppo
3471991Sheppo startnode = mdeg_find_start_node(mdp, clnt->pspec);
3481991Sheppo if (startnode == MDE_INVAL_ELEM_COOKIE) {
3491991Sheppo /* not much we can do */
3501991Sheppo cmn_err(CE_WARN, "unable to match node specifier");
3511991Sheppo rv = MDEG_FAILURE;
3521991Sheppo goto done;
3531991Sheppo }
3541991Sheppo
3551991Sheppo /*
3561991Sheppo * Use zalloc to provide correct default values for the
3571991Sheppo * unused removed, match_prev, and match_curr lists.
3581991Sheppo */
3591991Sheppo mdeg_res = kmem_zalloc(sizeof (mdeg_result_t), KM_SLEEP);
3601991Sheppo
3611991Sheppo nname = md_find_name(mdp, clnt->nmatch->namep);
3621991Sheppo aname = md_find_name(mdp, "fwd");
3631991Sheppo
3641991Sheppo nnodes = md_scan_dag(mdp, startnode, nname, aname, NULL);
3651991Sheppo
3661991Sheppo if (nnodes == 0) {
3671991Sheppo MDEG_DBG("mdeg_notify_client_reg: no nodes of interest\n");
3681991Sheppo rv = MDEG_SUCCESS;
3691991Sheppo goto done;
3701991Sheppo } else if (nnodes == -1) {
3711991Sheppo MDEG_DBG("error scanning DAG\n");
3721991Sheppo rv = MDEG_FAILURE;
3731991Sheppo goto done;
3741991Sheppo }
3751991Sheppo
3761991Sheppo MDEG_DBG("mdeg_notify_client_reg: %d node%s of interest\n",
3771991Sheppo nnodes, (nnodes == 1) ? "" : "s");
3781991Sheppo
3791991Sheppo /* get the list of nodes of interest */
3801991Sheppo listp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
3811991Sheppo nodechk = md_scan_dag(mdp, startnode, nname, aname, listp);
3821991Sheppo
3831991Sheppo ASSERT(nodechk == nnodes);
3841991Sheppo
3851991Sheppo mdeg_res->added.mdp = mdp;
3861991Sheppo mdeg_res->added.mdep = listp;
3871991Sheppo mdeg_res->added.nelem = nnodes;
3881991Sheppo
3891991Sheppo /* call the client callback */
3901991Sheppo (*clnt->cb)(clnt->cb_arg, mdeg_res);
3911991Sheppo
3921991Sheppo done:
3931991Sheppo mutex_exit(&mdeg.lock);
3941991Sheppo
3951991Sheppo if (mdp)
3961991Sheppo (void) md_fini_handle(mdp);
3971991Sheppo
3981991Sheppo if (listp)
3991991Sheppo kmem_free(listp, sizeof (mde_cookie_t) * nnodes);
4001991Sheppo
4011991Sheppo if (mdeg_res)
4021991Sheppo kmem_free(mdeg_res, sizeof (mdeg_result_t));
4031991Sheppo
4041991Sheppo return (rv);
4051991Sheppo }
4061991Sheppo
4071991Sheppo /*
4081991Sheppo * Register to receive an event notification when the system
4091991Sheppo * machine description is updated.
4101991Sheppo *
4111991Sheppo * Passing NULL for the node specification parameter is valid
4121991Sheppo * as long as the match specification is also NULL. In this
4131991Sheppo * case, the client will receive a notification when the MD
4141991Sheppo * has been updated, but the callback will not include any
4151991Sheppo * information. The client is then responsible for obtaining
4161991Sheppo * its own copy of the system MD and performing any processing
4171991Sheppo * manually.
4181991Sheppo */
4191991Sheppo int
mdeg_register(mdeg_node_spec_t * pspecp,mdeg_node_match_t * nmatchp,mdeg_cb_t cb,void * cb_arg,mdeg_handle_t * hdlp)4201991Sheppo mdeg_register(mdeg_node_spec_t *pspecp, mdeg_node_match_t *nmatchp,
4211991Sheppo mdeg_cb_t cb, void *cb_arg, mdeg_handle_t *hdlp)
4221991Sheppo {
4231991Sheppo mdeg_clnt_t *clnt;
4241991Sheppo
42511543SWentao.Yang@Sun.COM /* should never be called from a callback */
42611543SWentao.Yang@Sun.COM ASSERT(!taskq_member(mdeg.taskq, curthread));
4271991Sheppo
4281991Sheppo /* node spec and node match must both be valid, or both NULL */
4291991Sheppo if (((pspecp != NULL) && (nmatchp == NULL)) ||
4301991Sheppo ((pspecp == NULL) && (nmatchp != NULL))) {
4311991Sheppo MDEG_DBG("mdeg_register: invalid parameters\n");
4321991Sheppo return (MDEG_FAILURE);
4331991Sheppo }
4341991Sheppo
4351991Sheppo rw_enter(&mdeg.rwlock, RW_WRITER);
4361991Sheppo
4371991Sheppo clnt = mdeg_alloc_clnt();
4381991Sheppo
4391991Sheppo ASSERT(clnt);
4401991Sheppo
4411991Sheppo /*
4421991Sheppo * Fill in the rest of the data
4431991Sheppo */
4441991Sheppo clnt->nmatch = nmatchp;
4451991Sheppo clnt->pspec = pspecp;
4461991Sheppo clnt->cb = cb;
4471991Sheppo clnt->cb_arg = cb_arg;
4481991Sheppo clnt->magic = MDEG_MAGIC;
4491991Sheppo
4501991Sheppo /* do this last */
4511991Sheppo clnt->valid = B_TRUE;
4521991Sheppo
4531991Sheppo MDEG_DBG("client registered (0x%lx):\n", clnt->hdl);
4541991Sheppo MDEG_DUMP_CLNT(clnt);
4551991Sheppo
4561991Sheppo mdeg.nclnts++;
4571991Sheppo
4581991Sheppo if (mdeg_notify_client_reg(clnt) != MDEG_SUCCESS) {
4591991Sheppo bzero(clnt, sizeof (mdeg_clnt_t));
4601991Sheppo rw_exit(&mdeg.rwlock);
4611991Sheppo return (MDEG_FAILURE);
4621991Sheppo }
4631991Sheppo
4641991Sheppo rw_exit(&mdeg.rwlock);
4651991Sheppo
4661991Sheppo *hdlp = clnt->hdl;
4671991Sheppo
4681991Sheppo return (MDEG_SUCCESS);
4691991Sheppo }
4701991Sheppo
4711991Sheppo int
mdeg_unregister(mdeg_handle_t hdl)4721991Sheppo mdeg_unregister(mdeg_handle_t hdl)
4731991Sheppo {
4741991Sheppo mdeg_clnt_t *clnt;
4751991Sheppo mdeg_handle_t mdh;
4761991Sheppo
47711543SWentao.Yang@Sun.COM /* should never be called from a callback */
47811543SWentao.Yang@Sun.COM ASSERT(!taskq_member(mdeg.taskq, curthread));
47911543SWentao.Yang@Sun.COM
48011543SWentao.Yang@Sun.COM rw_enter(&mdeg.rwlock, RW_WRITER);
4811991Sheppo
4821991Sheppo /* lookup the client */
4831991Sheppo if ((clnt = mdeg_get_client(hdl)) == NULL) {
48411543SWentao.Yang@Sun.COM rw_exit(&mdeg.rwlock);
4851991Sheppo return (MDEG_FAILURE);
4861991Sheppo }
4871991Sheppo
4881991Sheppo MDEG_DBG("client unregistered (0x%lx):\n", hdl);
4891991Sheppo MDEG_DUMP_CLNT(clnt);
4901991Sheppo
4911991Sheppo /* save the handle to prevent reuse */
4921991Sheppo mdh = clnt->hdl;
4931991Sheppo bzero(clnt, sizeof (mdeg_clnt_t));
4941991Sheppo
4951991Sheppo clnt->hdl = mdh;
4961991Sheppo
4971991Sheppo mdeg.nclnts--;
4981991Sheppo
4991991Sheppo rw_exit(&mdeg.rwlock);
5001991Sheppo
5011991Sheppo return (MDEG_SUCCESS);
5021991Sheppo }
5031991Sheppo
5041991Sheppo /*
5051991Sheppo * Simple algorithm for now, grab the global lock and let all
5061991Sheppo * the clients update themselves in parallel. There is a lot of
5071991Sheppo * room for improvement here. We could eliminate some scans of
5083759Srsmaeda * the DAG by incrementally scanning at lower levels of the DAG
5091991Sheppo * rather than having each client start its own scan from the root.
5101991Sheppo */
5111991Sheppo void
mdeg_notify_clients(void)5121991Sheppo mdeg_notify_clients(void)
5131991Sheppo {
5141991Sheppo md_t *md_new;
5151991Sheppo mdeg_clnt_t *clnt;
5161991Sheppo int idx;
5171991Sheppo int nclnt;
5181991Sheppo
5191991Sheppo rw_enter(&mdeg.rwlock, RW_READER);
5201991Sheppo mutex_enter(&mdeg.lock);
5211991Sheppo
5221991Sheppo /*
5231991Sheppo * Rotate the MDs
5241991Sheppo */
5251991Sheppo if ((md_new = md_get_handle()) == NULL) {
5261991Sheppo cmn_err(CE_WARN, "unable to retrieve new MD");
5271991Sheppo goto done;
5281991Sheppo }
5291991Sheppo
5301991Sheppo if (mdeg.md_prev) {
5311991Sheppo (void) md_fini_handle(mdeg.md_prev);
5321991Sheppo }
5331991Sheppo
5341991Sheppo mdeg.md_prev = mdeg.md_curr;
5351991Sheppo mdeg.md_curr = md_new;
5361991Sheppo
5371991Sheppo if (mdeg.nclnts == 0) {
5381991Sheppo MDEG_DBG("mdeg_notify_clients: no clients registered\n");
5391991Sheppo goto done;
5401991Sheppo }
5411991Sheppo
5421991Sheppo /* dispatch the update notification to all clients */
5431991Sheppo for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) {
5441991Sheppo clnt = &mdeg.tbl[idx];
5451991Sheppo
5461991Sheppo if (!clnt->valid)
5471991Sheppo continue;
5481991Sheppo
5491991Sheppo MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl,
5501991Sheppo ++nclnt, mdeg.nclnts);
5511991Sheppo
5521991Sheppo (void) taskq_dispatch(mdeg.taskq, mdeg_notify_client,
5531991Sheppo (void *)clnt, TQ_SLEEP);
5541991Sheppo }
5551991Sheppo
556*12014SHaik.Aftandilian@Sun.COM /*
557*12014SHaik.Aftandilian@Sun.COM * Wait for all mdeg_notify_client notifications to
558*12014SHaik.Aftandilian@Sun.COM * finish while we are still holding mdeg.rwlock.
559*12014SHaik.Aftandilian@Sun.COM */
5601991Sheppo taskq_wait(mdeg.taskq);
5611991Sheppo
5621991Sheppo done:
5631991Sheppo mutex_exit(&mdeg.lock);
5641991Sheppo rw_exit(&mdeg.rwlock);
5651991Sheppo }
5661991Sheppo
5671991Sheppo static void
mdeg_notify_client(void * arg)5681991Sheppo mdeg_notify_client(void *arg)
5691991Sheppo {
5701991Sheppo mdeg_clnt_t *clnt = (mdeg_clnt_t *)arg;
5711991Sheppo md_diff_cookie_t mdd = MD_INVAL_DIFF_COOKIE;
5721991Sheppo mdeg_result_t mdeg_res;
5731991Sheppo mde_cookie_t md_prev_start;
5741991Sheppo mde_cookie_t md_curr_start;
5751991Sheppo
576*12014SHaik.Aftandilian@Sun.COM /*
577*12014SHaik.Aftandilian@Sun.COM * mdeg.rwlock must be held as a reader while this function
578*12014SHaik.Aftandilian@Sun.COM * executes. However, we do not need to acquire the lock as a
579*12014SHaik.Aftandilian@Sun.COM * reader here because it is held as a reader by the thread
580*12014SHaik.Aftandilian@Sun.COM * executing mdeg_notify_clients which triggers the execution
581*12014SHaik.Aftandilian@Sun.COM * of this function from a taskq. Since mdeg_notify_clients
582*12014SHaik.Aftandilian@Sun.COM * holds the lock as a reader until the taskq callbacks have
583*12014SHaik.Aftandilian@Sun.COM * completed, it will be held for the life of this function call.
584*12014SHaik.Aftandilian@Sun.COM * Furthermore, we must not attempt to acquire the lock as a
585*12014SHaik.Aftandilian@Sun.COM * reader with rw_enter because if there is a pending writer,
586*12014SHaik.Aftandilian@Sun.COM * we will block, creating a circular deadlock with this function,
587*12014SHaik.Aftandilian@Sun.COM * the writer, and mdeg_notify_clients. Since we do not need
588*12014SHaik.Aftandilian@Sun.COM * to acquire the lock, just assert that it is held.
589*12014SHaik.Aftandilian@Sun.COM */
590*12014SHaik.Aftandilian@Sun.COM ASSERT(RW_READ_HELD(&mdeg.rwlock));
5911991Sheppo
5921991Sheppo if (!mdeg.enabled) {
5931991Sheppo /* trying to shutdown */
5941991Sheppo MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n");
5951991Sheppo goto cleanup;
5961991Sheppo }
5971991Sheppo
5981991Sheppo /*
5991991Sheppo * Handle the special case where the node specification
6001991Sheppo * is NULL. In this case, call the client callback without
6011991Sheppo * any results. All processing is left to the client.
6021991Sheppo */
6031991Sheppo if (clnt->pspec == NULL) {
6041991Sheppo /* call the client callback */
6051991Sheppo (*clnt->cb)(clnt->cb_arg, NULL);
6061991Sheppo
6071991Sheppo MDEG_DBG("MDEG client callback done\n");
6081991Sheppo goto cleanup;
6091991Sheppo }
6101991Sheppo
6111991Sheppo /* find our start nodes */
6121991Sheppo md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec);
6131991Sheppo if (md_prev_start == MDE_INVAL_ELEM_COOKIE) {
6141991Sheppo goto cleanup;
6151991Sheppo }
6161991Sheppo
6171991Sheppo md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec);
6181991Sheppo if (md_curr_start == MDE_INVAL_ELEM_COOKIE) {
6191991Sheppo goto cleanup;
6201991Sheppo }
6211991Sheppo
6221991Sheppo /* diff the MDs */
6231991Sheppo mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr,
6241991Sheppo md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp);
6251991Sheppo
6261991Sheppo if (mdd == MD_INVAL_DIFF_COOKIE) {
6271991Sheppo MDEG_DBG("unable to diff MDs\n");
6281991Sheppo goto cleanup;
6291991Sheppo }
6301991Sheppo
6311991Sheppo /*
6321991Sheppo * Cache the results of the diff
6331991Sheppo */
6341991Sheppo mdeg_get_diff_results(mdd, &mdeg_res);
6351991Sheppo
6361991Sheppo /* call the client callback */
6371991Sheppo (*clnt->cb)(clnt->cb_arg, &mdeg_res);
6381991Sheppo
6391991Sheppo MDEG_DBG("MDEG client callback done\n");
6401991Sheppo
6411991Sheppo cleanup:
6421991Sheppo if (mdd != MD_INVAL_DIFF_COOKIE)
6431991Sheppo (void) md_diff_fini(mdd);
6441991Sheppo }
6451991Sheppo
6461991Sheppo static mde_cookie_t
mdeg_find_start_node(md_t * md,mdeg_node_spec_t * nspec)6471991Sheppo mdeg_find_start_node(md_t *md, mdeg_node_spec_t *nspec)
6481991Sheppo {
6491991Sheppo mde_cookie_t *nodesp;
6501991Sheppo mde_str_cookie_t nname;
6511991Sheppo mde_str_cookie_t aname;
6521991Sheppo int nnodes;
6531991Sheppo int idx;
6541991Sheppo
6551991Sheppo if ((md == NULL) || (nspec == NULL))
6561991Sheppo return (MDE_INVAL_ELEM_COOKIE);
6571991Sheppo
6581991Sheppo nname = md_find_name(md, nspec->namep);
6591991Sheppo aname = md_find_name(md, "fwd");
6601991Sheppo
6611991Sheppo nnodes = md_scan_dag(md, NULL, nname, aname, NULL);
6621991Sheppo if (nnodes == 0)
6631991Sheppo return (MDE_INVAL_ELEM_COOKIE);
6641991Sheppo
6651991Sheppo nodesp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
6661991Sheppo
6671991Sheppo (void) md_scan_dag(md, NULL, nname, aname, nodesp);
6681991Sheppo
6691991Sheppo for (idx = 0; idx < nnodes; idx++) {
6701991Sheppo
6711991Sheppo if (mdeg_node_spec_match(md, nodesp[idx], nspec)) {
6721991Sheppo mde_cookie_t res = nodesp[idx];
6731991Sheppo
6741991Sheppo kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
6751991Sheppo return (res);
6761991Sheppo }
6771991Sheppo }
6781991Sheppo
6791991Sheppo kmem_free(nodesp, sizeof (mde_cookie_t) * nnodes);
6801991Sheppo return (MDE_INVAL_ELEM_COOKIE);
6811991Sheppo }
6821991Sheppo
6831991Sheppo static boolean_t
mdeg_node_spec_match(md_t * md,mde_cookie_t node,mdeg_node_spec_t * nspec)6841991Sheppo mdeg_node_spec_match(md_t *md, mde_cookie_t node, mdeg_node_spec_t *nspec)
6851991Sheppo {
6861991Sheppo mdeg_prop_spec_t *prop;
6871991Sheppo
6881991Sheppo ASSERT(md && nspec);
6891991Sheppo ASSERT(node != MDE_INVAL_ELEM_COOKIE);
6901991Sheppo
6911991Sheppo prop = nspec->specp;
6921991Sheppo
6931991Sheppo while (prop->type != MDET_LIST_END) {
6941991Sheppo
6951991Sheppo switch (prop->type) {
6961991Sheppo case MDET_PROP_VAL: {
6971991Sheppo uint64_t val;
6981991Sheppo
6991991Sheppo if (md_get_prop_val(md, node, prop->namep, &val) != 0)
7001991Sheppo return (B_FALSE);
7011991Sheppo
7021991Sheppo if (prop->ps_val != val)
7031991Sheppo return (B_FALSE);
7041991Sheppo
7051991Sheppo break;
7061991Sheppo }
7071991Sheppo case MDET_PROP_STR: {
7081991Sheppo char *str;
7091991Sheppo
7101991Sheppo if (md_get_prop_str(md, node, prop->namep, &str) != 0)
7111991Sheppo return (B_FALSE);
7121991Sheppo
7131991Sheppo if (strcmp(prop->ps_str, str) != 0)
7141991Sheppo return (B_FALSE);
7151991Sheppo
7161991Sheppo break;
7171991Sheppo }
7181991Sheppo
7191991Sheppo default:
7201991Sheppo return (B_FALSE);
7211991Sheppo }
7221991Sheppo
7231991Sheppo prop++;
7241991Sheppo }
7251991Sheppo
7261991Sheppo return (B_TRUE);
7271991Sheppo }
7281991Sheppo
7291991Sheppo static void
mdeg_get_diff_results(md_diff_cookie_t mdd,mdeg_result_t * res)7301991Sheppo mdeg_get_diff_results(md_diff_cookie_t mdd, mdeg_result_t *res)
7311991Sheppo {
7321991Sheppo /*
7331991Sheppo * Cache added nodes.
7341991Sheppo */
7351991Sheppo res->added.mdp = mdeg.md_curr;
7361991Sheppo res->added.nelem = md_diff_added(mdd, &(res->added.mdep));
7371991Sheppo
7381991Sheppo if (res->added.nelem == -1) {
7391991Sheppo bzero(&(res->added), sizeof (mdeg_diff_t));
7401991Sheppo }
7411991Sheppo
7421991Sheppo /*
7431991Sheppo * Cache removed nodes.
7441991Sheppo */
7451991Sheppo res->removed.mdp = mdeg.md_prev;
7461991Sheppo res->removed.nelem = md_diff_removed(mdd, &(res->removed.mdep));
7471991Sheppo
7481991Sheppo if (res->removed.nelem == -1) {
7491991Sheppo bzero(&(res->removed), sizeof (mdeg_diff_t));
7501991Sheppo }
7511991Sheppo
7521991Sheppo /*
7531991Sheppo * Cache matching node pairs.
7541991Sheppo */
7551991Sheppo res->match_curr.mdp = mdeg.md_curr;
7561991Sheppo res->match_prev.mdp = mdeg.md_prev;
7571991Sheppo res->match_curr.nelem = md_diff_matched(mdd, &(res->match_prev.mdep),
7581991Sheppo &(res->match_curr.mdep));
7591991Sheppo res->match_prev.nelem = res->match_curr.nelem;
7601991Sheppo
7611991Sheppo if (res->match_prev.nelem == -1) {
7621991Sheppo bzero(&(res->match_prev), sizeof (mdeg_diff_t));
7631991Sheppo bzero(&(res->match_curr), sizeof (mdeg_diff_t));
7641991Sheppo }
7651991Sheppo }
7661991Sheppo
7671991Sheppo #ifdef DEBUG
7681991Sheppo /*
7691991Sheppo * Generate a string that represents the node specifier
7701991Sheppo * structure. Clamp the string length if the specifier
7711991Sheppo * structure contains too much information.
7721991Sheppo *
7731991Sheppo * General form:
7741991Sheppo *
7751991Sheppo * <nodename>:{<propname>=<propval>,...}
7761991Sheppo * e.g.
7771991Sheppo * vdevice:{name=vsw,reg=0x0}
7781991Sheppo */
7791991Sheppo static void
mdeg_spec_str(mdeg_node_spec_t * spec,char * buf,int len)7801991Sheppo mdeg_spec_str(mdeg_node_spec_t *spec, char *buf, int len)
7811991Sheppo {
7821991Sheppo mdeg_prop_spec_t *prop;
7831991Sheppo int offset;
7841991Sheppo boolean_t first = B_TRUE;
7851991Sheppo char *end = buf + len;
7861991Sheppo
7871991Sheppo offset = snprintf(buf, len, "%s:{", spec->namep);
7881991Sheppo
7891991Sheppo buf += offset;
7901991Sheppo len -= offset;
7911991Sheppo if (len <= 0)
7921991Sheppo goto trunc;
7931991Sheppo
7941991Sheppo prop = spec->specp;
7951991Sheppo
7961991Sheppo while (prop->type != MDET_LIST_END) {
7971991Sheppo
7981991Sheppo switch (prop->type) {
7991991Sheppo case MDET_PROP_VAL:
8001991Sheppo offset = snprintf(buf, len, "%s%s=0x%lx",
8011991Sheppo (first) ? "" : ",", prop->namep, prop->ps_val);
8021991Sheppo buf += offset;
8031991Sheppo len -= offset;
8041991Sheppo if (len <= 0)
8051991Sheppo goto trunc;
8061991Sheppo break;
8071991Sheppo
8081991Sheppo case MDET_PROP_STR:
8091991Sheppo offset = snprintf(buf, len, "%s%s=%s",
8101991Sheppo (first) ? "" : ",", prop->namep, prop->ps_str);
8111991Sheppo buf += offset;
8121991Sheppo len -= offset;
8131991Sheppo if (len <= 0)
8141991Sheppo goto trunc;
8151991Sheppo break;
8161991Sheppo
8171991Sheppo default:
8181991Sheppo (void) snprintf(buf, len, "}");
8191991Sheppo return;
8201991Sheppo }
8211991Sheppo
8221991Sheppo if (first)
8231991Sheppo first = B_FALSE;
8241991Sheppo prop++;
8251991Sheppo }
8261991Sheppo
8271991Sheppo (void) snprintf(buf, len, "}");
8281991Sheppo return;
8291991Sheppo
8301991Sheppo trunc:
8311991Sheppo /* string too long, truncate it */
8321991Sheppo buf = end - (strlen(trunc_str) + 1);
8331991Sheppo (void) sprintf(buf, trunc_str);
8341991Sheppo }
8351991Sheppo
8361991Sheppo /*
8371991Sheppo * Generate a string that represents the match structure.
8381991Sheppo * Clamp the string length if the match structure contains
8391991Sheppo * too much information.
8401991Sheppo *
8411991Sheppo * General form:
8421991Sheppo *
8431991Sheppo * <nodename>:{<propname>,...}
8441991Sheppo * e.g.
8451991Sheppo * nmatch=vport:{reg}
8461991Sheppo */
8471991Sheppo static void
mdeg_match_str(mdeg_node_match_t * match,char * buf,int len)8481991Sheppo mdeg_match_str(mdeg_node_match_t *match, char *buf, int len)
8491991Sheppo {
8501991Sheppo md_prop_match_t *prop;
8511991Sheppo int offset;
8521991Sheppo boolean_t first = B_TRUE;
8531991Sheppo char *end = buf + len;
8541991Sheppo
8551991Sheppo offset = snprintf(buf, len, "%s:{", match->namep);
8561991Sheppo
8571991Sheppo buf += offset;
8581991Sheppo len -= offset;
8591991Sheppo if (len <= 0)
8601991Sheppo goto trunc;
8611991Sheppo
8621991Sheppo prop = match->matchp;
8631991Sheppo
8641991Sheppo while (prop->type != MDET_LIST_END) {
8651991Sheppo offset = snprintf(buf, len, "%s%s", (first) ? "" : ",",
8661991Sheppo prop->namep);
8671991Sheppo buf += offset;
8681991Sheppo len -= offset;
8691991Sheppo if (len <= 0)
8701991Sheppo goto trunc;
8711991Sheppo
8721991Sheppo if (first)
8731991Sheppo first = B_FALSE;
8741991Sheppo prop++;
8751991Sheppo }
8761991Sheppo
8771991Sheppo (void) snprintf(buf, len, "}");
8781991Sheppo return;
8791991Sheppo
8801991Sheppo trunc:
8811991Sheppo /* string too long, truncate it */
8821991Sheppo buf = end - (strlen(trunc_str) + 1);
8831991Sheppo (void) sprintf(buf, trunc_str);
8841991Sheppo }
8851991Sheppo
8861991Sheppo #define MAX_FIELD_STR 80
8871991Sheppo
8881991Sheppo static void
mdeg_dump_clnt(mdeg_clnt_t * clnt)8891991Sheppo mdeg_dump_clnt(mdeg_clnt_t *clnt)
8901991Sheppo {
89110106SJason.Beloro@Sun.COM char str[MAX_FIELD_STR] = "";
8921991Sheppo
8931991Sheppo if (!clnt->valid) {
8941991Sheppo MDEG_DBG(" valid=B_FALSE\n");
8951991Sheppo return;
8961991Sheppo }
8971991Sheppo
89810106SJason.Beloro@Sun.COM if (clnt->pspec) {
89910106SJason.Beloro@Sun.COM mdeg_spec_str(clnt->pspec, str, MAX_FIELD_STR);
90010106SJason.Beloro@Sun.COM MDEG_DBG(" pspecp=%s\n", str);
90110106SJason.Beloro@Sun.COM }
9021991Sheppo
90310106SJason.Beloro@Sun.COM if (clnt->nmatch) {
90410106SJason.Beloro@Sun.COM mdeg_match_str(clnt->nmatch, str, MAX_FIELD_STR);
90510106SJason.Beloro@Sun.COM MDEG_DBG(" nmatch=%s\n", str);
90610106SJason.Beloro@Sun.COM }
9071991Sheppo }
9081991Sheppo
9091991Sheppo static void
mdeg_dump_table(void)9101991Sheppo mdeg_dump_table(void)
9111991Sheppo {
9121991Sheppo int idx;
9131991Sheppo mdeg_clnt_t *clnt;
9141991Sheppo
9151991Sheppo for (idx = 0; idx < mdeg.maxclnts; idx++) {
9161991Sheppo clnt = &(mdeg.tbl[idx]);
9171991Sheppo
9181991Sheppo MDEG_DBG("client %d (0x%lx):\n", idx, clnt->hdl);
9191991Sheppo mdeg_dump_clnt(clnt);
9201991Sheppo }
9211991Sheppo }
9221991Sheppo #endif /* DEBUG */
923