Example #1
0
static mdeg_clnt_t *
mdeg_get_client(mdeg_handle_t hdl)
{
	int		idx;
	mdeg_clnt_t	*clnt;

	idx = MDEG_HDL2IDX(hdl);

	/* check if index is out of bounds */
	if ((idx < 0) || (idx >= mdeg.maxclnts)) {
		MDEG_DBG("mdeg_get_client: index out of bounds\n");
		return (NULL);
	}

	clnt = &mdeg.tbl[idx];

	/* check for a valid client */
	if (!clnt->valid) {
		MDEG_DBG("mdeg_get_client: client is not valid\n");
		return (NULL);
	}

	/* make sure the handle is an exact match */
	if (clnt->hdl != hdl) {
		MDEG_DBG("mdeg_get_client: bad handle\n");
		return (NULL);
	}

	if (clnt->magic != MDEG_MAGIC) {
		MDEG_DBG("mdeg_get_client: bad magic\n");
		return (NULL);
	}

	return (clnt);
}
Example #2
0
/*
 * Register to receive an event notification when the system
 * machine description is updated.
 *
 * Passing NULL for the node specification parameter is valid
 * as long as the match specification is also NULL. In this
 * case, the client will receive a notification when the MD
 * has been updated, but the callback will not include any
 * information. The client is then responsible for obtaining
 * its own copy of the system MD and performing any processing
 * manually.
 */
int
mdeg_register(mdeg_node_spec_t *pspecp, mdeg_node_match_t *nmatchp,
    mdeg_cb_t cb, void *cb_arg, mdeg_handle_t *hdlp)
{
	mdeg_clnt_t	*clnt;

	/*
	 * If the RW lock is held, a client is calling
	 * register from its own callback.
	 */
	if (RW_LOCK_HELD(&mdeg.rwlock)) {
		MDEG_DBG("mdeg_register: rwlock already held\n");
		return (MDEG_FAILURE);
	}

	/* node spec and node match must both be valid, or both NULL */
	if (((pspecp != NULL) && (nmatchp == NULL)) ||
	    ((pspecp == NULL) && (nmatchp != NULL))) {
		MDEG_DBG("mdeg_register: invalid parameters\n");
		return (MDEG_FAILURE);
	}

	rw_enter(&mdeg.rwlock, RW_WRITER);

	clnt = mdeg_alloc_clnt();

	ASSERT(clnt);

	/*
	 * Fill in the rest of the data
	 */
	clnt->nmatch = nmatchp;
	clnt->pspec = pspecp;
	clnt->cb = cb;
	clnt->cb_arg = cb_arg;
	clnt->magic = MDEG_MAGIC;

	/* do this last */
	clnt->valid = B_TRUE;

	MDEG_DBG("client registered (0x%lx):\n", clnt->hdl);
	MDEG_DUMP_CLNT(clnt);

	mdeg.nclnts++;

	if (mdeg_notify_client_reg(clnt) != MDEG_SUCCESS) {
		bzero(clnt, sizeof (mdeg_clnt_t));
		rw_exit(&mdeg.rwlock);
		return (MDEG_FAILURE);
	}

	rw_exit(&mdeg.rwlock);

	*hdlp = clnt->hdl;

	return (MDEG_SUCCESS);
}
Example #3
0
/*
 * Simple algorithm for now, grab the global lock and let all
 * the clients update themselves in parallel. There is a lot of
 * room for improvement here. We could eliminate some scans of
 * the DAG by incrementally scanning at lower levels of the DAG
 * rather than having each client start its own scan from the root.
 */
void
mdeg_notify_clients(void)
{
	md_t		*md_new;
	mdeg_clnt_t	*clnt;
	int		idx;
	int		nclnt;

	rw_enter(&mdeg.rwlock, RW_READER);
	mutex_enter(&mdeg.lock);

	/*
	 * Rotate the MDs
	 */
	if ((md_new = md_get_handle()) == NULL) {
		cmn_err(CE_WARN, "unable to retrieve new MD");
		goto done;
	}

	if (mdeg.md_prev) {
		(void) md_fini_handle(mdeg.md_prev);
	}

	mdeg.md_prev = mdeg.md_curr;
	mdeg.md_curr = md_new;

	if (mdeg.nclnts == 0) {
		MDEG_DBG("mdeg_notify_clients: no clients registered\n");
		goto done;
	}

	/* dispatch the update notification to all clients */
	for (idx = 0, nclnt = 0; idx < mdeg.maxclnts; idx++) {
		clnt = &mdeg.tbl[idx];

		if (!clnt->valid)
			continue;

		MDEG_DBG("notifying client 0x%lx (%d/%d)\n", clnt->hdl,
		    ++nclnt, mdeg.nclnts);

		(void) taskq_dispatch(mdeg.taskq, mdeg_notify_client,
		    (void *)clnt, TQ_SLEEP);
	}

	/*
	 * Wait for all mdeg_notify_client notifications to
	 * finish while we are still holding mdeg.rwlock.
	 */
	taskq_wait(mdeg.taskq);

done:
	mutex_exit(&mdeg.lock);
	rw_exit(&mdeg.rwlock);
}
Example #4
0
int
mdeg_unregister(mdeg_handle_t hdl)
{
	mdeg_clnt_t	*clnt;
	mdeg_handle_t	mdh;

	/* should never be called from a callback */
	ASSERT(!taskq_member(mdeg.taskq, curthread));

	rw_enter(&mdeg.rwlock, RW_WRITER);

	/* lookup the client */
	if ((clnt = mdeg_get_client(hdl)) == NULL) {
		rw_exit(&mdeg.rwlock);
		return (MDEG_FAILURE);
	}

	MDEG_DBG("client unregistered (0x%lx):\n", hdl);
	MDEG_DUMP_CLNT(clnt);

	/* save the handle to prevent reuse */
	mdh = clnt->hdl;
	bzero(clnt, sizeof (mdeg_clnt_t));

	clnt->hdl = mdh;

	mdeg.nclnts--;

	rw_exit(&mdeg.rwlock);

	return (MDEG_SUCCESS);
}
Example #5
0
static void
mdeg_dump_clnt(mdeg_clnt_t *clnt)
{
	char	str[MAX_FIELD_STR];

	if (!clnt->valid) {
		MDEG_DBG("  valid=B_FALSE\n");
		return;
	}

	mdeg_spec_str(clnt->pspec, str, MAX_FIELD_STR);
	MDEG_DBG("  pspecp=%s\n", str);

	mdeg_match_str(clnt->nmatch, str, MAX_FIELD_STR);
	MDEG_DBG("  nmatch=%s\n", str);
}
Example #6
0
static mdeg_clnt_t *
mdeg_alloc_clnt(void)
{
	mdeg_clnt_t	*clnt;
	int		idx;
	mdeg_clnt_t	*newtbl;
	uint_t		newmaxclnts;
	uint_t		newtblsz;
	uint_t		oldtblsz;

	ASSERT(RW_WRITE_HELD(&mdeg.rwlock));

	/* search for an unused slot in the table */
	for (idx = 0; idx < mdeg.maxclnts; idx++) {
		clnt = &mdeg.tbl[idx];
		if (!clnt->valid) {
			break;
		}
	}

	/* found any empty slot */
	if (idx != mdeg.maxclnts) {
		goto found;
	}

	/*
	 * There was no free space in the table. Grow
	 * the table to double its current size.
	 */

	MDEG_DBG("client table full:\n");
	MDEG_DUMP_TABLE();

	newmaxclnts = mdeg.maxclnts * 2;
	newtblsz = newmaxclnts * sizeof (mdeg_clnt_t);

	newtbl = kmem_zalloc(newtblsz, KM_SLEEP);

	/* copy old table data to the new table */
	oldtblsz = mdeg.maxclnts * sizeof (mdeg_clnt_t);
	bcopy(mdeg.tbl, newtbl, oldtblsz);

	/*
	 * Since the old table was full, the first free entry
	 * will be just past the end of the old table.
	 */
	clnt = &mdeg.tbl[mdeg.maxclnts];

	/* clean up the old table */
	kmem_free(mdeg.tbl, oldtblsz);
	mdeg.tbl = newtbl;
	mdeg.maxclnts = newmaxclnts;

found:
	ASSERT(clnt->valid == 0);

	clnt->hdl = MDEG_ALLOC_HDL(idx, MDEG_HDL2COUNT(clnt->hdl));

	return (clnt);
}
Example #7
0
static void
mdeg_dump_table(void)
{
	int		idx;
	mdeg_clnt_t	*clnt;

	for (idx = 0; idx < mdeg.maxclnts; idx++) {
		clnt = &(mdeg.tbl[idx]);

		MDEG_DBG("client %d (0x%lx):\n", idx, clnt->hdl);
		mdeg_dump_clnt(clnt);
	}
}
Example #8
0
int
mdeg_unregister(mdeg_handle_t hdl)
{
	mdeg_clnt_t	*clnt;
	mdeg_handle_t	mdh;

	/*
	 * If the RW lock is held, a client is calling
	 * unregister from its own callback.
	 */
	if (RW_LOCK_HELD(&mdeg.rwlock)) {
		MDEG_DBG("mdeg_unregister: rwlock already held\n");
		return (MDEG_FAILURE);
	}

	/* lookup the client */
	if ((clnt = mdeg_get_client(hdl)) == NULL) {
		return (MDEG_FAILURE);
	}

	rw_enter(&mdeg.rwlock, RW_WRITER);

	MDEG_DBG("client unregistered (0x%lx):\n", hdl);
	MDEG_DUMP_CLNT(clnt);

	/* save the handle to prevent reuse */
	mdh = clnt->hdl;
	bzero(clnt, sizeof (mdeg_clnt_t));

	clnt->hdl = mdh;

	mdeg.nclnts--;

	rw_exit(&mdeg.rwlock);

	return (MDEG_SUCCESS);
}
Example #9
0
static void
mdeg_notify_client(void *arg)
{
	mdeg_clnt_t		*clnt = (mdeg_clnt_t *)arg;
	md_diff_cookie_t	mdd = MD_INVAL_DIFF_COOKIE;
	mdeg_result_t		mdeg_res;
	mde_cookie_t		md_prev_start;
	mde_cookie_t		md_curr_start;

	/*
	 * mdeg.rwlock must be held as a reader while this function
	 * executes. However, we do not need to acquire the lock as a
	 * reader here because it is held as a reader by the thread
	 * executing mdeg_notify_clients which triggers the execution
	 * of this function from a taskq. Since mdeg_notify_clients
	 * holds the lock as a reader until the taskq callbacks have
	 * completed, it will be held for the life of this function call.
	 * Furthermore, we must not attempt to acquire the lock as a
	 * reader with rw_enter because if there is a pending writer,
	 * we will block, creating a circular deadlock with this function,
	 * the writer, and mdeg_notify_clients. Since we do not need
	 * to acquire the lock, just assert that it is held.
	 */
	ASSERT(RW_READ_HELD(&mdeg.rwlock));

	if (!mdeg.enabled) {
		/* trying to shutdown */
		MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n");
		goto cleanup;
	}

	/*
	 * Handle the special case where the node specification
	 * is NULL. In this case, call the client callback without
	 * any results. All processing is left to the client.
	 */
	if (clnt->pspec == NULL) {
		/* call the client callback */
		(*clnt->cb)(clnt->cb_arg, NULL);

		MDEG_DBG("MDEG client callback done\n");
		goto cleanup;
	}

	/* find our start nodes */
	md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec);
	if (md_prev_start == MDE_INVAL_ELEM_COOKIE) {
		goto cleanup;
	}

	md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec);
	if (md_curr_start == MDE_INVAL_ELEM_COOKIE) {
		goto cleanup;
	}

	/* diff the MDs */
	mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr,
	    md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp);

	if (mdd == MD_INVAL_DIFF_COOKIE) {
		MDEG_DBG("unable to diff MDs\n");
		goto cleanup;
	}

	/*
	 * Cache the results of the diff
	 */
	mdeg_get_diff_results(mdd, &mdeg_res);

	/* call the client callback */
	(*clnt->cb)(clnt->cb_arg, &mdeg_res);

	MDEG_DBG("MDEG client callback done\n");

cleanup:
	if (mdd != MD_INVAL_DIFF_COOKIE)
		(void) md_diff_fini(mdd);
}
Example #10
0
/*
 * Send a notification to a client immediately after it registers.
 * The result_t is a list of all the nodes that match their specified
 * nodes of interest, all returned on the added list. This serves
 * as a base of reference to the client. All future MD updates are
 * relative to this list.
 */
static int
mdeg_notify_client_reg(mdeg_clnt_t *clnt)
{
	md_t			*mdp = NULL;
	mde_str_cookie_t	nname;
	mde_str_cookie_t	aname;
	mde_cookie_t		startnode;
	int			nnodes;
	int			nodechk;
	mde_cookie_t		*listp = NULL;
	mdeg_result_t		*mdeg_res = NULL;
	int			rv = MDEG_SUCCESS;

	mutex_enter(&mdeg.lock);

	/*
	 * Handle the special case where the node specification
	 * is NULL. In this case, call the client callback without
	 * any results. All processing is left to the client.
	 */
	if (clnt->pspec == NULL) {
		/* call the client callback */
		(*clnt->cb)(clnt->cb_arg, NULL);
		goto done;
	}

	if ((mdp = md_get_handle()) == NULL) {
		cmn_err(CE_WARN, "unable to retrieve current MD");
		rv = MDEG_FAILURE;
		goto done;
	}

	startnode = mdeg_find_start_node(mdp, clnt->pspec);
	if (startnode == MDE_INVAL_ELEM_COOKIE) {
		/* not much we can do */
		cmn_err(CE_WARN, "unable to match node specifier");
		rv = MDEG_FAILURE;
		goto done;
	}

	/*
	 * Use zalloc to provide correct default values for the
	 * unused removed, match_prev, and match_curr lists.
	 */
	mdeg_res = kmem_zalloc(sizeof (mdeg_result_t), KM_SLEEP);

	nname = md_find_name(mdp, clnt->nmatch->namep);
	aname = md_find_name(mdp, "fwd");

	nnodes = md_scan_dag(mdp, startnode, nname, aname, NULL);

	if (nnodes == 0) {
		MDEG_DBG("mdeg_notify_client_reg: no nodes of interest\n");
		rv = MDEG_SUCCESS;
		goto done;
	} else if (nnodes == -1) {
		MDEG_DBG("error scanning DAG\n");
		rv = MDEG_FAILURE;
		goto done;
	}

	MDEG_DBG("mdeg_notify_client_reg: %d node%s of interest\n",
	    nnodes, (nnodes == 1) ? "" : "s");

	/* get the list of nodes of interest */
	listp = kmem_alloc(sizeof (mde_cookie_t) * nnodes, KM_SLEEP);
	nodechk = md_scan_dag(mdp, startnode, nname, aname, listp);

	ASSERT(nodechk == nnodes);

	mdeg_res->added.mdp = mdp;
	mdeg_res->added.mdep = listp;
	mdeg_res->added.nelem = nnodes;

	/* call the client callback */
	(*clnt->cb)(clnt->cb_arg, mdeg_res);

done:
	mutex_exit(&mdeg.lock);

	if (mdp)
		(void) md_fini_handle(mdp);

	if (listp)
		kmem_free(listp, sizeof (mde_cookie_t) * nnodes);

	if (mdeg_res)
		kmem_free(mdeg_res, sizeof (mdeg_result_t));

	return (rv);
}
Example #11
0
static void
mdeg_notify_client(void *arg)
{
	mdeg_clnt_t		*clnt = (mdeg_clnt_t *)arg;
	md_diff_cookie_t	mdd = MD_INVAL_DIFF_COOKIE;
	mdeg_result_t		mdeg_res;
	mde_cookie_t		md_prev_start;
	mde_cookie_t		md_curr_start;

	rw_enter(&mdeg.rwlock, RW_READER);

	if (!mdeg.enabled) {
		/* trying to shutdown */
		MDEG_DBG("mdeg_notify_client: mdeg disabled, aborting\n");
		goto cleanup;
	}

	/*
	 * Handle the special case where the node specification
	 * is NULL. In this case, call the client callback without
	 * any results. All processing is left to the client.
	 */
	if (clnt->pspec == NULL) {
		/* call the client callback */
		(*clnt->cb)(clnt->cb_arg, NULL);

		MDEG_DBG("MDEG client callback done\n");
		goto cleanup;
	}

	/* find our start nodes */
	md_prev_start = mdeg_find_start_node(mdeg.md_prev, clnt->pspec);
	if (md_prev_start == MDE_INVAL_ELEM_COOKIE) {
		goto cleanup;
	}

	md_curr_start = mdeg_find_start_node(mdeg.md_curr, clnt->pspec);
	if (md_curr_start == MDE_INVAL_ELEM_COOKIE) {
		goto cleanup;
	}

	/* diff the MDs */
	mdd = md_diff_init(mdeg.md_prev, md_prev_start, mdeg.md_curr,
	    md_curr_start, clnt->nmatch->namep, clnt->nmatch->matchp);

	if (mdd == MD_INVAL_DIFF_COOKIE) {
		MDEG_DBG("unable to diff MDs\n");
		goto cleanup;
	}

	/*
	 * Cache the results of the diff
	 */
	mdeg_get_diff_results(mdd, &mdeg_res);

	/* call the client callback */
	(*clnt->cb)(clnt->cb_arg, &mdeg_res);

	MDEG_DBG("MDEG client callback done\n");

cleanup:
	rw_exit(&mdeg.rwlock);

	if (mdd != MD_INVAL_DIFF_COOKIE)
		(void) md_diff_fini(mdd);
}