xref: /titanic_50/usr/src/uts/common/io/hook.c (revision 8ad7418892268f9f0ba29518ab332f6a26f69fc5)
1381a2a9aSdr146992 /*
2381a2a9aSdr146992  * CDDL HEADER START
3381a2a9aSdr146992  *
4381a2a9aSdr146992  * The contents of this file are subject to the terms of the
5381a2a9aSdr146992  * Common Development and Distribution License (the "License").
6381a2a9aSdr146992  * You may not use this file except in compliance with the License.
7381a2a9aSdr146992  *
8381a2a9aSdr146992  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9381a2a9aSdr146992  * or http://www.opensolaris.org/os/licensing.
10381a2a9aSdr146992  * See the License for the specific language governing permissions
11381a2a9aSdr146992  * and limitations under the License.
12381a2a9aSdr146992  *
13381a2a9aSdr146992  * When distributing Covered Code, include this CDDL HEADER in each
14381a2a9aSdr146992  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15381a2a9aSdr146992  * If applicable, add the following below this CDDL HEADER, with the
16381a2a9aSdr146992  * fields enclosed by brackets "[]" replaced with your own identifying
17381a2a9aSdr146992  * information: Portions Copyright [yyyy] [name of copyright owner]
18381a2a9aSdr146992  *
19381a2a9aSdr146992  * CDDL HEADER END
20381a2a9aSdr146992  */
21381a2a9aSdr146992 /*
227ddc9b1aSDarren Reed  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23381a2a9aSdr146992  * Use is subject to license terms.
24381a2a9aSdr146992  */
25381a2a9aSdr146992 #include <sys/param.h>
26381a2a9aSdr146992 #include <sys/types.h>
27381a2a9aSdr146992 #include <sys/systm.h>
28381a2a9aSdr146992 #include <sys/errno.h>
29381a2a9aSdr146992 #include <sys/kmem.h>
30381a2a9aSdr146992 #include <sys/mutex.h>
31381a2a9aSdr146992 #include <sys/condvar.h>
32381a2a9aSdr146992 #include <sys/modctl.h>
33381a2a9aSdr146992 #include <sys/hook_impl.h>
34381a2a9aSdr146992 #include <sys/sdt.h>
357ddc9b1aSDarren Reed #include <sys/cmn_err.h>
36381a2a9aSdr146992 
37381a2a9aSdr146992 /*
38381a2a9aSdr146992  * This file provides kernel hook framework.
39381a2a9aSdr146992  */
40381a2a9aSdr146992 
41381a2a9aSdr146992 static struct modldrv modlmisc = {
42381a2a9aSdr146992 	&mod_miscops,				/* drv_modops */
43381a2a9aSdr146992 	"Hooks Interface v1.0",			/* drv_linkinfo */
44381a2a9aSdr146992 };
45381a2a9aSdr146992 
46381a2a9aSdr146992 static struct modlinkage modlinkage = {
47381a2a9aSdr146992 	MODREV_1,				/* ml_rev */
48381a2a9aSdr146992 	&modlmisc,				/* ml_linkage */
49381a2a9aSdr146992 	NULL
50381a2a9aSdr146992 };
51381a2a9aSdr146992 
52381a2a9aSdr146992 /*
537ddc9b1aSDarren Reed  * How it works.
547ddc9b1aSDarren Reed  * =============
557ddc9b1aSDarren Reed  * Use of the hook framework here is tied up with zones - when a new zone
567ddc9b1aSDarren Reed  * is created, we create a new hook_stack_t and are open to business for
577ddc9b1aSDarren Reed  * allowing new hook families and their events.
587ddc9b1aSDarren Reed  *
597ddc9b1aSDarren Reed  * A consumer of these hooks is expected to operate in this fashion:
607ddc9b1aSDarren Reed  * 1) call hook_family_add() to create a new family of hooks. It is a
617ddc9b1aSDarren Reed  *    current requirement that this call must be made with the value
627ddc9b1aSDarren Reed  *    returned from hook_stack_init, by way of infrastructure elsewhere.
637ddc9b1aSDarren Reed  * 2) add events to the registered family with calls to hook_event_add.
647ddc9b1aSDarren Reed  *
657ddc9b1aSDarren Reed  * At this point, the structures in place should be open to others to
667ddc9b1aSDarren Reed  * add hooks to the event or add notifiers for when the contents of the
677ddc9b1aSDarren Reed  * hook stack changes.
687ddc9b1aSDarren Reed  *
697ddc9b1aSDarren Reed  * The interesting stuff happens on teardown.
707ddc9b1aSDarren Reed  *
717ddc9b1aSDarren Reed  * It is a requirement that the provider of hook events work in the reverse
727ddc9b1aSDarren Reed  * order to the above, so that the first step is:
737ddc9b1aSDarren Reed  * 1) remove events from each hook family created earlier
747ddc9b1aSDarren Reed  * 2) remove hook families from the hook stack.
757ddc9b1aSDarren Reed  *
767ddc9b1aSDarren Reed  * When doing teardown of both events and families, a check is made to see
777ddc9b1aSDarren Reed  * if either structure is still "busy". If so then a boolean flag is set to
787ddc9b1aSDarren Reed  * say that the structure is condemned. The presence of this flag being set
797ddc9b1aSDarren Reed  * must be checked for in _add()/_register()/ functions and a failure returned
807ddc9b1aSDarren Reed  * if it is set. It is ignored by the _find() functions because they're
817ddc9b1aSDarren Reed  * used by _remove()/_unregister().  While setting the condemned flag when
827ddc9b1aSDarren Reed  * trying to delete a structure would normally be keyed from the presence
837ddc9b1aSDarren Reed  * of a reference count being greater than 1, in this implementation there
847ddc9b1aSDarren Reed  * are no reference counts required: instead the presence of objects on
857ddc9b1aSDarren Reed  * linked lists is taken to mean something is still "busy."
867ddc9b1aSDarren Reed  *
877ddc9b1aSDarren Reed  * ONLY the caller that adds the family and the events ever has a direct
887ddc9b1aSDarren Reed  * reference to the internal structures and thus ONLY it should be doing
897ddc9b1aSDarren Reed  * the removal of either the event or family.  In practise, what this means
907ddc9b1aSDarren Reed  * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
917ddc9b1aSDarren Reed  * by net_event_register() (these interface to hook_family_add() and
927ddc9b1aSDarren Reed  * hook_event_add(), respectively) that are made when we create an instance
937ddc9b1aSDarren Reed  * of IP and when the IP instance is shutdown/destroyed, it calls
947ddc9b1aSDarren Reed  * net_event_unregister() and net_protocol_unregister(), which in turn call
957ddc9b1aSDarren Reed  * hook_event_remove() and hook_family_remove() respectively. Nobody else
967ddc9b1aSDarren Reed  * is entitled to call the _unregister() functions.  It is imperative that
977ddc9b1aSDarren Reed  * there be only one _remove() call for every _add() call.
987ddc9b1aSDarren Reed  *
997ddc9b1aSDarren Reed  * It is possible that code which is interfacing with this hook framework
1007ddc9b1aSDarren Reed  * won't do all the cleaning up that it needs to at the right time. While
1017ddc9b1aSDarren Reed  * we can't prevent programmers from creating memory leaks, we can synchronise
1027ddc9b1aSDarren Reed  * when we clean up data structures to prevent code accessing free'd memory.
1037ddc9b1aSDarren Reed  *
1047ddc9b1aSDarren Reed  * A simple diagram showing the ownership is as follows:
1057ddc9b1aSDarren Reed  *
1067ddc9b1aSDarren Reed  *  Owned       +--------------+
1077ddc9b1aSDarren Reed  *   by         | hook_stack_t |
1087ddc9b1aSDarren Reed  *   the        +--------------+
1097ddc9b1aSDarren Reed  *  Instance      |
1107ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1117ddc9b1aSDarren Reed  *                V
1127ddc9b1aSDarren Reed  *  Owned       +-------------------+     +-------------------+
1137ddc9b1aSDarren Reed  *              | hook_family_int_t |---->| hook_family_int_t |
1147ddc9b1aSDarren Reed  *   by         +-------------------+     +-------------------+
1157ddc9b1aSDarren Reed  *                | \+---------------+        \+---------------+
1167ddc9b1aSDarren Reed  *  network       |  | hook_family_t |         | hook_family_t |
1177ddc9b1aSDarren Reed  *                V  +---------------+         +---------------+
1187ddc9b1aSDarren Reed  *  protocol   +------------------+     +------------------+
1197ddc9b1aSDarren Reed  *             | hook_event_int_t |---->| hook_event_int_t |
1207ddc9b1aSDarren Reed  * (ipv4,ipv6) +------------------+     +------------------+
1217ddc9b1aSDarren Reed  *                | \+--------------+        \+--------------+
1227ddc9b1aSDarren Reed  *                |  | hook_event_t |         | hook_event_t |
1237ddc9b1aSDarren Reed  *                |  +--------------+         +--------------+
1247ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1257ddc9b1aSDarren Reed  *                V
1267ddc9b1aSDarren Reed  *  Owned      +------------+
1277ddc9b1aSDarren Reed  *             | hook_int_t |
1287ddc9b1aSDarren Reed  *   by        +------------+
1297ddc9b1aSDarren Reed  *                  \+--------+
1307ddc9b1aSDarren Reed  * the consumer      | hook_t |
1317ddc9b1aSDarren Reed  *                   +--------+
1327ddc9b1aSDarren Reed  *
1337ddc9b1aSDarren Reed  * The consumers, such as IPFilter, do not have any pointers or hold any
1347ddc9b1aSDarren Reed  * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
1357ddc9b1aSDarren Reed  * a hook on an event through net_hook_register(), an implicit reference
1367ddc9b1aSDarren Reed  * to the hook_event_int_t is returned with a successful call.  Additionally,
1377ddc9b1aSDarren Reed  * IPFilter does not see the hook_family_int_t or hook_family_t directly.
1387ddc9b1aSDarren Reed  * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
1397ddc9b1aSDarren Reed  * contains a pointer to hook_family_int_t.  The structure behind the
1407ddc9b1aSDarren Reed  * net_handle_t (struct net_data) *is* reference counted and managed
1417ddc9b1aSDarren Reed  * appropriately.
1427ddc9b1aSDarren Reed  *
1437ddc9b1aSDarren Reed  * A more detailed picture that describes how the family/event structures
1447ddc9b1aSDarren Reed  * are linked together can be found in <sys/hook_impl.h>
1457ddc9b1aSDarren Reed  */
1467ddc9b1aSDarren Reed 
1477ddc9b1aSDarren Reed /*
1487ddc9b1aSDarren Reed  * Locking
1497ddc9b1aSDarren Reed  * =======
1507ddc9b1aSDarren Reed  * The use of CVW_* macros to do locking is driven by the need to allow
1517ddc9b1aSDarren Reed  * recursive locking with read locks when we're processing packets. This
1527ddc9b1aSDarren Reed  * is necessary because various netinfo functions need to hold read locks,
1537ddc9b1aSDarren Reed  * by design, as they can be called in or out of packet context.
1547ddc9b1aSDarren Reed  */
1557ddc9b1aSDarren Reed /*
156381a2a9aSdr146992  * Hook internal functions
157381a2a9aSdr146992  */
158381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src);
159f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
160f4b3ec61Sdh155122     hook_stack_t *hks);
161381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
162381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
1637ddc9b1aSDarren Reed static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
164381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
165f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
1667ddc9b1aSDarren Reed static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
167381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
1687ddc9b1aSDarren Reed static void hook_int_free(hook_int_t *hi, netstackid_t);
169381a2a9aSdr146992 static void hook_init(void);
170f4b3ec61Sdh155122 static void hook_fini(void);
171f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
172f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
1737ddc9b1aSDarren Reed static void hook_stack_shutdown(netstackid_t stackid, void *arg);
1747ddc9b1aSDarren Reed static int hook_insert(hook_int_head_t *head, hook_int_t *new);
1757ddc9b1aSDarren Reed static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
1767ddc9b1aSDarren Reed static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
1777ddc9b1aSDarren Reed static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
1787ddc9b1aSDarren Reed static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
1797ddc9b1aSDarren Reed static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
1807ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
1817ddc9b1aSDarren Reed static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
1827ddc9b1aSDarren Reed     hook_int_t *hi);
1837ddc9b1aSDarren Reed static int hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
1847ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg);
1857ddc9b1aSDarren Reed static int hook_notify_unregister(cvwaitlock_t *lock,
1867ddc9b1aSDarren Reed     hook_notify_head_t *head, hook_notify_fn_t callback);
1877ddc9b1aSDarren Reed static void hook_notify_run(hook_notify_head_t *head, char *family,
1887ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
1897ddc9b1aSDarren Reed static void hook_stack_notify_run(hook_stack_t *hks, char *name,
1907ddc9b1aSDarren Reed     hook_notify_cmd_t cmd);
1917ddc9b1aSDarren Reed static void hook_stack_remove(hook_stack_t *hks);
1927ddc9b1aSDarren Reed 
1937ddc9b1aSDarren Reed /*
1947ddc9b1aSDarren Reed  * A list of the hook stacks is kept here because we need to enable
1957ddc9b1aSDarren Reed  * net_instance_notify_register() to be called during the creation
1967ddc9b1aSDarren Reed  * of a new instance. Previously hook_stack_get() would just use
1977ddc9b1aSDarren Reed  * the netstack functions for this work but they will return NULL
1987ddc9b1aSDarren Reed  * until the zone has been fully initialised.
1997ddc9b1aSDarren Reed  */
2007ddc9b1aSDarren Reed static hook_stack_head_t hook_stacks;
2017ddc9b1aSDarren Reed static kmutex_t hook_stack_lock;
202381a2a9aSdr146992 
203381a2a9aSdr146992 /*
204381a2a9aSdr146992  * Module entry points.
205381a2a9aSdr146992  */
206381a2a9aSdr146992 int
207381a2a9aSdr146992 _init(void)
208381a2a9aSdr146992 {
209f4b3ec61Sdh155122 	int error;
210f4b3ec61Sdh155122 
211381a2a9aSdr146992 	hook_init();
212f4b3ec61Sdh155122 	error = mod_install(&modlinkage);
213f4b3ec61Sdh155122 	if (error != 0)
214f4b3ec61Sdh155122 		hook_fini();
215f4b3ec61Sdh155122 
216f4b3ec61Sdh155122 	return (error);
217381a2a9aSdr146992 }
218381a2a9aSdr146992 
219381a2a9aSdr146992 int
220381a2a9aSdr146992 _fini(void)
221381a2a9aSdr146992 {
222f4b3ec61Sdh155122 	int error;
223f4b3ec61Sdh155122 
224f4b3ec61Sdh155122 	error = mod_remove(&modlinkage);
225f4b3ec61Sdh155122 	if (error == 0)
226f4b3ec61Sdh155122 		hook_fini();
227f4b3ec61Sdh155122 
228f4b3ec61Sdh155122 	return (error);
229381a2a9aSdr146992 }
230381a2a9aSdr146992 
231381a2a9aSdr146992 int
232381a2a9aSdr146992 _info(struct modinfo *modinfop)
233381a2a9aSdr146992 {
234381a2a9aSdr146992 	return (mod_info(&modlinkage, modinfop));
235381a2a9aSdr146992 }
236381a2a9aSdr146992 
237381a2a9aSdr146992 /*
238381a2a9aSdr146992  * Function:	hook_init
239381a2a9aSdr146992  * Returns:	None
240381a2a9aSdr146992  * Parameters:	None
241381a2a9aSdr146992  *
242381a2a9aSdr146992  * Initialize hooks
243381a2a9aSdr146992  */
244381a2a9aSdr146992 static void
245381a2a9aSdr146992 hook_init(void)
246381a2a9aSdr146992 {
2477ddc9b1aSDarren Reed 	mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
2487ddc9b1aSDarren Reed 	SLIST_INIT(&hook_stacks);
2497ddc9b1aSDarren Reed 
250f4b3ec61Sdh155122 	/*
251f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
252f4b3ec61Sdh155122 	 * destroyed in the kernel.
253f4b3ec61Sdh155122 	 */
2547ddc9b1aSDarren Reed 	netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
255f4b3ec61Sdh155122 	    hook_stack_fini);
256381a2a9aSdr146992 }
257381a2a9aSdr146992 
258f4b3ec61Sdh155122 /*
259f4b3ec61Sdh155122  * Function:	hook_fini
260f4b3ec61Sdh155122  * Returns:	None
261f4b3ec61Sdh155122  * Parameters:	None
262f4b3ec61Sdh155122  *
263f4b3ec61Sdh155122  * Deinitialize hooks
264f4b3ec61Sdh155122  */
265f4b3ec61Sdh155122 static void
266f4b3ec61Sdh155122 hook_fini(void)
267f4b3ec61Sdh155122 {
268f4b3ec61Sdh155122 	netstack_unregister(NS_HOOK);
2697ddc9b1aSDarren Reed 
2707ddc9b1aSDarren Reed 	mutex_destroy(&hook_stack_lock);
2717ddc9b1aSDarren Reed 	ASSERT(SLIST_EMPTY(&hook_stacks));
2727ddc9b1aSDarren Reed }
2737ddc9b1aSDarren Reed 
2747ddc9b1aSDarren Reed /*
2757ddc9b1aSDarren Reed  * Function:	hook_wait_setflag
2767ddc9b1aSDarren Reed  * Returns:     -1 = setting flag is disallowed, 0 = flag set and did
2777ddc9b1aSDarren Reed  *              not have to wait (ie no lock droped), 1 = flag set but
2787ddc9b1aSDarren Reed  *              it was necessary to drop locks to set it.
2797ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
2807ddc9b1aSDarren Reed  *              busyset(I) - set of flags that we don't want set while
2817ddc9b1aSDarren Reed  *                           we are active.
2827ddc9b1aSDarren Reed  *              wanted(I)  - flag associated with newflag to indicate
2837ddc9b1aSDarren Reed  *                           what we want to do.
2847ddc9b1aSDarren Reed  *              newflag(I) - the new ACTIVE flag we want to set that
2857ddc9b1aSDarren Reed  *                           indicates what we are doing.
2867ddc9b1aSDarren Reed  *
2877ddc9b1aSDarren Reed  * The set of functions hook_wait_* implement an API that builds on top of
2887ddc9b1aSDarren Reed  * the kcondvar_t to provide controlled execution through a critical region.
2897ddc9b1aSDarren Reed  * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
2907ddc9b1aSDarren Reed  * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
2917ddc9b1aSDarren Reed  * The combination of flags is required as when this function exits to do
2927ddc9b1aSDarren Reed  * the task, the structure is then free for another caller to use and
2937ddc9b1aSDarren Reed  * to indicate that it wants to do work.  The trump flags here are those
2947ddc9b1aSDarren Reed  * that indicate someone wants to destroy the structure that owns this
2957ddc9b1aSDarren Reed  * flagwait_t.  In this case, we don't try to secure the ability to run
2967ddc9b1aSDarren Reed  * and return with an error.
2977ddc9b1aSDarren Reed  *
2987ddc9b1aSDarren Reed  * wanted - the FWF_*_WANTED flag that describes the action being requested
2997ddc9b1aSDarren Reed  * busyset- the set of FWF_* flags we don't want set when we run
3007ddc9b1aSDarren Reed  * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
3017ddc9b1aSDarren Reed  */
3027ddc9b1aSDarren Reed int
3037ddc9b1aSDarren Reed hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
3047ddc9b1aSDarren Reed     fwflag_t newflag)
3057ddc9b1aSDarren Reed {
3067ddc9b1aSDarren Reed 	int waited = 0;
3077ddc9b1aSDarren Reed 
3087ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
3097ddc9b1aSDarren Reed 	if (waiter->fw_flags & FWF_DESTROY) {
3107ddc9b1aSDarren Reed 		mutex_exit(&waiter->fw_lock);
3117ddc9b1aSDarren Reed 		return (-1);
3127ddc9b1aSDarren Reed 	}
3137ddc9b1aSDarren Reed 	while (waiter->fw_flags & busyset) {
3147ddc9b1aSDarren Reed 		waiter->fw_flags |= wanted;
3157ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
3167ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
3177ddc9b1aSDarren Reed 		waited = 1;
3187ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
3197ddc9b1aSDarren Reed 		if (waiter->fw_flags & FWF_DESTROY) {
3207ddc9b1aSDarren Reed 			waiter->fw_flags &= ~wanted;
3217ddc9b1aSDarren Reed 			mutex_exit(&waiter->fw_lock);
3227ddc9b1aSDarren Reed 			return (-1);
3237ddc9b1aSDarren Reed 		}
3247ddc9b1aSDarren Reed 		waiter->fw_flags |= wanted;
3257ddc9b1aSDarren Reed 	}
3267ddc9b1aSDarren Reed 	waiter->fw_flags &= ~wanted;
3277ddc9b1aSDarren Reed 	waiter->fw_flags |= newflag;
3287ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
3297ddc9b1aSDarren Reed 	return (waited);
3307ddc9b1aSDarren Reed }
3317ddc9b1aSDarren Reed 
3327ddc9b1aSDarren Reed /*
3337ddc9b1aSDarren Reed  * Function:	hook_wait_unsetflag
3347ddc9b1aSDarren Reed  * Returns:     None
3357ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
3367ddc9b1aSDarren Reed  *              oldflag(I) - flag to reset
3377ddc9b1aSDarren Reed  *
3387ddc9b1aSDarren Reed  * Turn off the bit that we had set to run and let others know that
3397ddc9b1aSDarren Reed  * they should now check to see if they can run.
3407ddc9b1aSDarren Reed  */
3417ddc9b1aSDarren Reed void
3427ddc9b1aSDarren Reed hook_wait_unsetflag(flagwait_t *waiter, uint32_t oldflag)
3437ddc9b1aSDarren Reed {
3447ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
3457ddc9b1aSDarren Reed 	waiter->fw_flags &= ~oldflag;
3467ddc9b1aSDarren Reed 	cv_signal(&waiter->fw_cv);
3477ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
3487ddc9b1aSDarren Reed }
3497ddc9b1aSDarren Reed 
3507ddc9b1aSDarren Reed /*
3517ddc9b1aSDarren Reed  * Function:	hook_wait_destroy
3527ddc9b1aSDarren Reed  * Returns:     None
3537ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
3547ddc9b1aSDarren Reed  *
3557ddc9b1aSDarren Reed  * Since outer locking (on fw_owner) should ensure that only one function
3567ddc9b1aSDarren Reed  * at a time gets to call hook_wait_destroy() on a given object, there is
3577ddc9b1aSDarren Reed  * no need to guard against setting FWF_DESTROY_WANTED already being set.
3587ddc9b1aSDarren Reed  * It is, however, necessary to wait for all activity on the owning
3597ddc9b1aSDarren Reed  * structure to cease.
3607ddc9b1aSDarren Reed  */
3617ddc9b1aSDarren Reed void
3627ddc9b1aSDarren Reed hook_wait_destroy(flagwait_t *waiter)
3637ddc9b1aSDarren Reed {
3647ddc9b1aSDarren Reed 	ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
3657ddc9b1aSDarren Reed 	waiter->fw_flags |= FWF_DESTROY_WANTED;
3667ddc9b1aSDarren Reed 	while (!FWF_DESTROY_OK(waiter)) {
3677ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
3687ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
3697ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
3707ddc9b1aSDarren Reed 	}
3717ddc9b1aSDarren Reed 	/*
3727ddc9b1aSDarren Reed 	 * There should now be nothing else using "waiter" or its
3737ddc9b1aSDarren Reed 	 * owner, so we can safely assign here without risk of wiiping
3747ddc9b1aSDarren Reed 	 * out someone's bit.
3757ddc9b1aSDarren Reed 	 */
3767ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_DESTROY_ACTIVE;
3777ddc9b1aSDarren Reed }
3787ddc9b1aSDarren Reed 
3797ddc9b1aSDarren Reed /*
3807ddc9b1aSDarren Reed  * Function:	hook_wait_init
3817ddc9b1aSDarren Reed  * Returns:     None
3827ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
3837ddc9b1aSDarren Reed  *              ownder(I)  - pointer to lock that the owner of this
3847ddc9b1aSDarren Reed  *                           waiter uses
3857ddc9b1aSDarren Reed  *
3867ddc9b1aSDarren Reed  * "owner" gets passed in here so that when we need to call cv_wait,
3877ddc9b1aSDarren Reed  * for example in hook_wait_setflag(), we can drop the lock for the
3887ddc9b1aSDarren Reed  * next layer out, which is likely to be held in an exclusive manner.
3897ddc9b1aSDarren Reed  */
3907ddc9b1aSDarren Reed void
3917ddc9b1aSDarren Reed hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
3927ddc9b1aSDarren Reed {
3937ddc9b1aSDarren Reed 	cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
3947ddc9b1aSDarren Reed 	mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
3957ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_NONE;
3967ddc9b1aSDarren Reed 	waiter->fw_owner = owner;
397f4b3ec61Sdh155122 }
398f4b3ec61Sdh155122 
399f4b3ec61Sdh155122 /*
400f4b3ec61Sdh155122  * Initialize the hook stack instance.
401f4b3ec61Sdh155122  */
402f4b3ec61Sdh155122 /*ARGSUSED*/
403f4b3ec61Sdh155122 static void *
404f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
405f4b3ec61Sdh155122 {
406f4b3ec61Sdh155122 	hook_stack_t	*hks;
407f4b3ec61Sdh155122 
408f4b3ec61Sdh155122 #ifdef NS_DEBUG
409f4b3ec61Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
410f4b3ec61Sdh155122 #endif
411f4b3ec61Sdh155122 
412f4b3ec61Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
4137ddc9b1aSDarren Reed 	hks->hks_netstack = ns;
4147ddc9b1aSDarren Reed 	hks->hks_netstackid = stackid;
415f4b3ec61Sdh155122 
4167ddc9b1aSDarren Reed 	CVW_INIT(&hks->hks_lock);
4177ddc9b1aSDarren Reed 	TAILQ_INIT(&hks->hks_nhead);
418f4b3ec61Sdh155122 	SLIST_INIT(&hks->hks_familylist);
419f4b3ec61Sdh155122 
4207ddc9b1aSDarren Reed 	hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
4217ddc9b1aSDarren Reed 
4227ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
4237ddc9b1aSDarren Reed 	SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
4247ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
4257ddc9b1aSDarren Reed 
426f4b3ec61Sdh155122 	return (hks);
427f4b3ec61Sdh155122 }
428f4b3ec61Sdh155122 
429*8ad74188SDarren Reed /*
430*8ad74188SDarren Reed  * Set the shutdown flag to indicate that we should stop accepting new
431*8ad74188SDarren Reed  * register calls as we're now in the cleanup process.
432*8ad74188SDarren Reed  */
4337ddc9b1aSDarren Reed /*ARGSUSED*/
4347ddc9b1aSDarren Reed static void
4357ddc9b1aSDarren Reed hook_stack_shutdown(netstackid_t stackid, void *arg)
4367ddc9b1aSDarren Reed {
4377ddc9b1aSDarren Reed 	hook_stack_t *hks = (hook_stack_t *)arg;
4387ddc9b1aSDarren Reed 
4397ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
4407ddc9b1aSDarren Reed 	/*
4417ddc9b1aSDarren Reed 	 * Once this flag gets set to one, no more additions are allowed
4427ddc9b1aSDarren Reed 	 * to any of the structures that make up this stack.
4437ddc9b1aSDarren Reed 	 */
4447ddc9b1aSDarren Reed 	hks->hks_shutdown = 1;
4457ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
4467ddc9b1aSDarren Reed }
4477ddc9b1aSDarren Reed 
448f4b3ec61Sdh155122 /*
449f4b3ec61Sdh155122  * Free the hook stack instance.
450f4b3ec61Sdh155122  */
451f4b3ec61Sdh155122 /*ARGSUSED*/
452f4b3ec61Sdh155122 static void
453f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
454f4b3ec61Sdh155122 {
455f4b3ec61Sdh155122 	hook_stack_t *hks = (hook_stack_t *)arg;
4567ddc9b1aSDarren Reed 
4577ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
4587ddc9b1aSDarren Reed 	hks->hks_shutdown = 2;
4597ddc9b1aSDarren Reed 	hook_stack_remove(hks);
4607ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
4617ddc9b1aSDarren Reed }
4627ddc9b1aSDarren Reed 
4637ddc9b1aSDarren Reed /*
4647ddc9b1aSDarren Reed  * This function assumes that it is called with hook_stack_lock held.
4657ddc9b1aSDarren Reed  * It functions differently to hook_family/event_remove in that it does
4667ddc9b1aSDarren Reed  * the checks to see if it can be removed. This difference exists
4677ddc9b1aSDarren Reed  * because this structure has nothing higher up that depends on it.
4687ddc9b1aSDarren Reed  */
4697ddc9b1aSDarren Reed static void
4707ddc9b1aSDarren Reed hook_stack_remove(hook_stack_t *hks)
4717ddc9b1aSDarren Reed {
4727ddc9b1aSDarren Reed 
4737ddc9b1aSDarren Reed 	ASSERT(mutex_owned(&hook_stack_lock));
4747ddc9b1aSDarren Reed 
4757ddc9b1aSDarren Reed 	/*
4767ddc9b1aSDarren Reed 	 * Is the structure still in use?
4777ddc9b1aSDarren Reed 	 */
4787ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hks->hks_familylist) ||
4797ddc9b1aSDarren Reed 	    !TAILQ_EMPTY(&hks->hks_nhead))
4807ddc9b1aSDarren Reed 		return;
4817ddc9b1aSDarren Reed 
4827ddc9b1aSDarren Reed 	SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
4837ddc9b1aSDarren Reed 
4847ddc9b1aSDarren Reed 	hook_wait_destroy(&hks->hks_waiter);
4857ddc9b1aSDarren Reed 	CVW_DESTROY(&hks->hks_lock);
486f4b3ec61Sdh155122 	kmem_free(hks, sizeof (*hks));
487f4b3ec61Sdh155122 }
488381a2a9aSdr146992 
4897ddc9b1aSDarren Reed static hook_stack_t *
4907ddc9b1aSDarren Reed hook_stack_get(netstackid_t stackid)
4917ddc9b1aSDarren Reed {
4927ddc9b1aSDarren Reed 	hook_stack_t *hks;
4937ddc9b1aSDarren Reed 
4947ddc9b1aSDarren Reed 	SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
4957ddc9b1aSDarren Reed 		if (hks->hks_netstackid == stackid)
4967ddc9b1aSDarren Reed 			break;
4977ddc9b1aSDarren Reed 	}
4987ddc9b1aSDarren Reed 
4997ddc9b1aSDarren Reed 	return (hks);
5007ddc9b1aSDarren Reed }
5017ddc9b1aSDarren Reed 
5027ddc9b1aSDarren Reed /*
5037ddc9b1aSDarren Reed  * Function:	hook_stack_notify_register
5047ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
5057ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
5067ddc9b1aSDarren Reed  *              callback(I)- function to be called
5077ddc9b1aSDarren Reed  *              arg(I)     - arg to provide callback when it is called
5087ddc9b1aSDarren Reed  *
5097ddc9b1aSDarren Reed  * If we're not shutting down this instance, append a new function to the
5107ddc9b1aSDarren Reed  * list of those to call when a new family of hooks is added to this stack.
5117ddc9b1aSDarren Reed  */
5127ddc9b1aSDarren Reed int
5137ddc9b1aSDarren Reed hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
5147ddc9b1aSDarren Reed     void *arg)
5157ddc9b1aSDarren Reed {
5167ddc9b1aSDarren Reed 	hook_stack_t *hks;
5177ddc9b1aSDarren Reed 	int error;
5187ddc9b1aSDarren Reed 
5197ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
5207ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
5217ddc9b1aSDarren Reed 	if (hks != NULL) {
5227ddc9b1aSDarren Reed 		if (hks->hks_shutdown != 0) {
5237ddc9b1aSDarren Reed 			error = ESHUTDOWN;
5247ddc9b1aSDarren Reed 		} else {
5257ddc9b1aSDarren Reed 			error = hook_notify_register(&hks->hks_lock,
5267ddc9b1aSDarren Reed 			    &hks->hks_nhead, callback, arg);
5277ddc9b1aSDarren Reed 		}
5287ddc9b1aSDarren Reed 	} else {
5297ddc9b1aSDarren Reed 		error = ESRCH;
5307ddc9b1aSDarren Reed 	}
5317ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
5327ddc9b1aSDarren Reed 
5337ddc9b1aSDarren Reed 	return (error);
5347ddc9b1aSDarren Reed }
5357ddc9b1aSDarren Reed 
5367ddc9b1aSDarren Reed /*
5377ddc9b1aSDarren Reed  * Function:	hook_stack_notify_unregister
5387ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
5397ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
5407ddc9b1aSDarren Reed  *              callback(I) - function to be called
5417ddc9b1aSDarren Reed  *
5427ddc9b1aSDarren Reed  * Attempt to remove a registered function from a hook stack's list of
5437ddc9b1aSDarren Reed  * callbacks to activiate when protocols are added/deleted.
5447ddc9b1aSDarren Reed  */
5457ddc9b1aSDarren Reed int
5467ddc9b1aSDarren Reed hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
5477ddc9b1aSDarren Reed {
5487ddc9b1aSDarren Reed 	hook_stack_t *hks;
5497ddc9b1aSDarren Reed 	int error;
5507ddc9b1aSDarren Reed 
5517ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
5527ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
5537ddc9b1aSDarren Reed 	if (hks != NULL) {
5547ddc9b1aSDarren Reed 		error = hook_notify_unregister(&hks->hks_lock,
5557ddc9b1aSDarren Reed 		    &hks->hks_nhead, callback);
5567ddc9b1aSDarren Reed 		if ((error == 0) && (hks->hks_shutdown == 2))
5577ddc9b1aSDarren Reed 			hook_stack_remove(hks);
5587ddc9b1aSDarren Reed 	} else {
5597ddc9b1aSDarren Reed 		error = ESRCH;
5607ddc9b1aSDarren Reed 	}
5617ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
5627ddc9b1aSDarren Reed 
5637ddc9b1aSDarren Reed 	return (error);
5647ddc9b1aSDarren Reed }
5657ddc9b1aSDarren Reed 
5667ddc9b1aSDarren Reed /*
5677ddc9b1aSDarren Reed  * Function:	hook_stack_notify_run
5687ddc9b1aSDarren Reed  * Returns:	None
5697ddc9b1aSDarren Reed  * Parameters:	hks(I)  - hook stack pointer to execute callbacks for
5707ddc9b1aSDarren Reed  *              name(I) - name of a hook family
5717ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
5727ddc9b1aSDarren Reed  *
5737ddc9b1aSDarren Reed  * Run through the list of callbacks on the hook stack to be called when
5747ddc9b1aSDarren Reed  * a new hook family is added
5757ddc9b1aSDarren Reed  *
5767ddc9b1aSDarren Reed  * As hook_notify_run() expects 3 names, one for the family, one for the
5777ddc9b1aSDarren Reed  * event and one for the object being introduced and we really only have
5787ddc9b1aSDarren Reed  * one name (that of the new hook family), fake the hook stack's name by
5797ddc9b1aSDarren Reed  * converting the integer to a string and for the event just pass NULL.
5807ddc9b1aSDarren Reed  */
5817ddc9b1aSDarren Reed static void
5827ddc9b1aSDarren Reed hook_stack_notify_run(hook_stack_t *hks, char *name,
5837ddc9b1aSDarren Reed     hook_notify_cmd_t cmd)
5847ddc9b1aSDarren Reed {
5857ddc9b1aSDarren Reed 	char buffer[16];
5867ddc9b1aSDarren Reed 
5877ddc9b1aSDarren Reed 	(void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
5887ddc9b1aSDarren Reed 
5897ddc9b1aSDarren Reed 	hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
5907ddc9b1aSDarren Reed }
5917ddc9b1aSDarren Reed 
592381a2a9aSdr146992 /*
593381a2a9aSdr146992  * Function:	hook_run
594381a2a9aSdr146992  * Returns:	int - return value according to callback func
595381a2a9aSdr146992  * Parameters:	token(I) - event pointer
596381a2a9aSdr146992  *		info(I) - message
597381a2a9aSdr146992  *
598381a2a9aSdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
599381a2a9aSdr146992  * until either the end of the list is reached or a hook function returns a
600381a2a9aSdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
601381a2a9aSdr146992  * return that value back to our caller.  By design, a hook function can be
602381a2a9aSdr146992  * called more than once, simultaneously.
603381a2a9aSdr146992  */
604381a2a9aSdr146992 int
6057ddc9b1aSDarren Reed hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
606381a2a9aSdr146992 {
607381a2a9aSdr146992 	hook_event_int_t *hei;
6087ddc9b1aSDarren Reed 	hook_int_t *hi;
609381a2a9aSdr146992 	int rval = 0;
610381a2a9aSdr146992 
611381a2a9aSdr146992 	ASSERT(token != NULL);
612381a2a9aSdr146992 
613381a2a9aSdr146992 	hei = (hook_event_int_t *)token;
614381a2a9aSdr146992 	DTRACE_PROBE2(hook__run__start,
615381a2a9aSdr146992 	    hook_event_token_t, token,
616381a2a9aSdr146992 	    hook_data_t, info);
617381a2a9aSdr146992 
6187ddc9b1aSDarren Reed 	/*
6197ddc9b1aSDarren Reed 	 * Hold global read lock to ensure event will not be deleted.
6207ddc9b1aSDarren Reed 	 * While it might be expected that we should also hold a read lock
6217ddc9b1aSDarren Reed 	 * on the event lock (hei_lock) to prevent the hook list from
6227ddc9b1aSDarren Reed 	 * changing while we're executing this function, both addition
6237ddc9b1aSDarren Reed 	 * to and removal from the hook list on the event is done with
6247ddc9b1aSDarren Reed 	 * a write lock held on hfi_lock. This is by design so that we
6257ddc9b1aSDarren Reed 	 * only need to get one of these locks to process a packet.
6267ddc9b1aSDarren Reed 	 * - locking is not a cheap thing to do for every packet.
6277ddc9b1aSDarren Reed 	 */
6287ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
629381a2a9aSdr146992 
630381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
631381a2a9aSdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
632381a2a9aSdr146992 		DTRACE_PROBE3(hook__func__start,
633381a2a9aSdr146992 		    hook_event_token_t, token,
634381a2a9aSdr146992 		    hook_data_t, info,
635381a2a9aSdr146992 		    hook_int_t *, hi);
6367ddc9b1aSDarren Reed 		rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
637381a2a9aSdr146992 		DTRACE_PROBE4(hook__func__end,
638381a2a9aSdr146992 		    hook_event_token_t, token,
639381a2a9aSdr146992 		    hook_data_t, info,
640381a2a9aSdr146992 		    hook_int_t *, hi,
641381a2a9aSdr146992 		    int, rval);
6427ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hits.value.ui64++;
643381a2a9aSdr146992 		if (rval != 0)
644381a2a9aSdr146992 			break;
645381a2a9aSdr146992 	}
646381a2a9aSdr146992 
6477ddc9b1aSDarren Reed 	hei->hei_kstats.events.value.ui64++;
6487ddc9b1aSDarren Reed 
6497ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
650381a2a9aSdr146992 
651381a2a9aSdr146992 	DTRACE_PROBE3(hook__run__end,
652381a2a9aSdr146992 	    hook_event_token_t, token,
653381a2a9aSdr146992 	    hook_data_t, info,
654381a2a9aSdr146992 	    hook_int_t *, hi);
655381a2a9aSdr146992 
656381a2a9aSdr146992 	return (rval);
657381a2a9aSdr146992 }
658381a2a9aSdr146992 
659381a2a9aSdr146992 /*
660381a2a9aSdr146992  * Function:	hook_family_add
661381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Fail
662381a2a9aSdr146992  * Parameters:	hf(I) - family pointer
663381a2a9aSdr146992  *
664381a2a9aSdr146992  * Add new family to family list
665381a2a9aSdr146992  */
666381a2a9aSdr146992 hook_family_int_t *
667f4b3ec61Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks)
668381a2a9aSdr146992 {
669381a2a9aSdr146992 	hook_family_int_t *hfi, *new;
670381a2a9aSdr146992 
671381a2a9aSdr146992 	ASSERT(hf != NULL);
672381a2a9aSdr146992 	ASSERT(hf->hf_name != NULL);
673381a2a9aSdr146992 
674381a2a9aSdr146992 	new = hook_family_copy(hf);
675381a2a9aSdr146992 	if (new == NULL)
676381a2a9aSdr146992 		return (NULL);
677381a2a9aSdr146992 
6787ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
6797ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
6807ddc9b1aSDarren Reed 
6817ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
6827ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
6837ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
6847ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
6857ddc9b1aSDarren Reed 		return (NULL);
6867ddc9b1aSDarren Reed 	}
687381a2a9aSdr146992 
688381a2a9aSdr146992 	/* search family list */
689f4b3ec61Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
690381a2a9aSdr146992 	if (hfi != NULL) {
6917ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
6927ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
6937ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
694381a2a9aSdr146992 		return (NULL);
695381a2a9aSdr146992 	}
696381a2a9aSdr146992 
6977ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
6987ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
6997ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
7007ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
7017ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
7027ddc9b1aSDarren Reed 		return (NULL);
7037ddc9b1aSDarren Reed 	}
7047ddc9b1aSDarren Reed 
7057ddc9b1aSDarren Reed 	CVW_INIT(&new->hfi_lock);
7067ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
7077ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
7087ddc9b1aSDarren Reed 
7097ddc9b1aSDarren Reed 	hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
7107ddc9b1aSDarren Reed 
7117ddc9b1aSDarren Reed 	new->hfi_stack = hks;
712381a2a9aSdr146992 
713f4b3ec61Sdh155122 	/* Add to family list head */
714f4b3ec61Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
715f4b3ec61Sdh155122 
7167ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
7177ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
7187ddc9b1aSDarren Reed 
7197ddc9b1aSDarren Reed 	hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
7207ddc9b1aSDarren Reed 
7217ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
7227ddc9b1aSDarren Reed 
723381a2a9aSdr146992 	return (new);
724381a2a9aSdr146992 }
725381a2a9aSdr146992 
726381a2a9aSdr146992 /*
727381a2a9aSdr146992  * Function:	hook_family_remove
728381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
729381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
730381a2a9aSdr146992  *
7317ddc9b1aSDarren Reed  * Remove family from family list. This function has been designed to be
7327ddc9b1aSDarren Reed  * called once and once only per hook_family_int_t. Thus when cleaning up
7337ddc9b1aSDarren Reed  * this structure as an orphan, callers should only call hook_family_free.
734381a2a9aSdr146992  */
735381a2a9aSdr146992 int
736381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi)
737381a2a9aSdr146992 {
738f4b3ec61Sdh155122 	hook_stack_t *hks;
739*8ad74188SDarren Reed 	boolean_t notifydone;
740381a2a9aSdr146992 
741381a2a9aSdr146992 	ASSERT(hfi != NULL);
7427ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
743381a2a9aSdr146992 
7447ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
745381a2a9aSdr146992 
7467ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
7477ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
7487ddc9b1aSDarren Reed 		/*
7497ddc9b1aSDarren Reed 		 * If we're trying to destroy the hook_stack_t...
7507ddc9b1aSDarren Reed 		 */
751*8ad74188SDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
7527ddc9b1aSDarren Reed 		return (ENXIO);
753381a2a9aSdr146992 	}
754381a2a9aSdr146992 
7557ddc9b1aSDarren Reed 	/*
7567ddc9b1aSDarren Reed 	 * Check if the family is in use by the presence of either events
7577ddc9b1aSDarren Reed 	 * or notify callbacks on the hook family.
7587ddc9b1aSDarren Reed 	 */
7597ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
7607ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_TRUE;
7617ddc9b1aSDarren Reed 	} else {
7627ddc9b1aSDarren Reed 		/*
7637ddc9b1aSDarren Reed 		 * Although hfi_condemned = B_FALSE is implied from creation,
7647ddc9b1aSDarren Reed 		 * putting a comment here inside the else upsets lint.
7657ddc9b1aSDarren Reed 		 */
7667ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_FALSE;
7677ddc9b1aSDarren Reed 	}
768381a2a9aSdr146992 
7697ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
7707ddc9b1aSDarren Reed 	hook_wait_destroy(&hfi->hfi_waiter);
771*8ad74188SDarren Reed 	notifydone = hfi->hfi_shutdown;
772*8ad74188SDarren Reed 	hfi->hfi_shutdown = B_TRUE;
7737ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
7747ddc9b1aSDarren Reed 
7757ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
7767ddc9b1aSDarren Reed 
777*8ad74188SDarren Reed 	if (!notifydone)
778*8ad74188SDarren Reed 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
779*8ad74188SDarren Reed 		    HN_UNREGISTER);
7807ddc9b1aSDarren Reed 
7817ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
7827ddc9b1aSDarren Reed 
7837ddc9b1aSDarren Reed 	/*
7847ddc9b1aSDarren Reed 	 * If we don't have to wait for anything else to disappear from this
7857ddc9b1aSDarren Reed 	 * structure then we can free it up.
7867ddc9b1aSDarren Reed 	 */
7877ddc9b1aSDarren Reed 	if (!hfi->hfi_condemned)
7887ddc9b1aSDarren Reed 		hook_family_free(hfi, hks);
789381a2a9aSdr146992 
790381a2a9aSdr146992 	return (0);
791381a2a9aSdr146992 }
792381a2a9aSdr146992 
793381a2a9aSdr146992 
794381a2a9aSdr146992 /*
7957ddc9b1aSDarren Reed  * Function:	hook_family_free
7967ddc9b1aSDarren Reed  * Returns:	None
7977ddc9b1aSDarren Reed  * Parameters:	hfi(I) - internal family pointer
7987ddc9b1aSDarren Reed  *
7997ddc9b1aSDarren Reed  * Free alloc memory for family
8007ddc9b1aSDarren Reed  */
8017ddc9b1aSDarren Reed static void
8027ddc9b1aSDarren Reed hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
8037ddc9b1aSDarren Reed {
8047ddc9b1aSDarren Reed 
8057ddc9b1aSDarren Reed 	/*
8067ddc9b1aSDarren Reed 	 * This lock gives us possession of the hks pointer after the
8077ddc9b1aSDarren Reed 	 * SLIST_REMOVE, for which it is not needed, when hks_shutdown
8087ddc9b1aSDarren Reed 	 * is checked and hook_stack_remove called.
8097ddc9b1aSDarren Reed 	 */
8107ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
8117ddc9b1aSDarren Reed 
8127ddc9b1aSDarren Reed 	ASSERT(hfi != NULL);
8137ddc9b1aSDarren Reed 
8147ddc9b1aSDarren Reed 	if (hks != NULL) {
8157ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hks->hks_lock);
8167ddc9b1aSDarren Reed 		/* Remove from family list */
8177ddc9b1aSDarren Reed 		SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
8187ddc9b1aSDarren Reed 		    hfi_entry);
8197ddc9b1aSDarren Reed 
8207ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
8217ddc9b1aSDarren Reed 	}
8227ddc9b1aSDarren Reed 
8237ddc9b1aSDarren Reed 	/* Free name space */
8247ddc9b1aSDarren Reed 	if (hfi->hfi_family.hf_name != NULL) {
8257ddc9b1aSDarren Reed 		kmem_free(hfi->hfi_family.hf_name,
8267ddc9b1aSDarren Reed 		    strlen(hfi->hfi_family.hf_name) + 1);
8277ddc9b1aSDarren Reed 	}
8287ddc9b1aSDarren Reed 
8297ddc9b1aSDarren Reed 	/* Free container */
8307ddc9b1aSDarren Reed 	kmem_free(hfi, sizeof (*hfi));
8317ddc9b1aSDarren Reed 
8327ddc9b1aSDarren Reed 	if (hks->hks_shutdown == 2)
8337ddc9b1aSDarren Reed 		hook_stack_remove(hks);
8347ddc9b1aSDarren Reed 
8357ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
8367ddc9b1aSDarren Reed }
8377ddc9b1aSDarren Reed 
838*8ad74188SDarren Reed /*
839*8ad74188SDarren Reed  * Function:	hook_family_shutdown
840*8ad74188SDarren Reed  * Returns:	int - 0 = Succ, Else = Fail
841*8ad74188SDarren Reed  * Parameters:	hfi(I) - internal family pointer
842*8ad74188SDarren Reed  *
843*8ad74188SDarren Reed  * As an alternative to removing a family, we may desire to just generate
844*8ad74188SDarren Reed  * a series of callbacks to indicate that we will be going away in the
845*8ad74188SDarren Reed  * future. The hfi_condemned flag isn't set because we aren't trying to
846*8ad74188SDarren Reed  * remove the structure.
847*8ad74188SDarren Reed  */
848*8ad74188SDarren Reed int
849*8ad74188SDarren Reed hook_family_shutdown(hook_family_int_t *hfi)
850*8ad74188SDarren Reed {
851*8ad74188SDarren Reed 	hook_stack_t *hks;
852*8ad74188SDarren Reed 	boolean_t notifydone;
853*8ad74188SDarren Reed 
854*8ad74188SDarren Reed 	ASSERT(hfi != NULL);
855*8ad74188SDarren Reed 	hks = hfi->hfi_stack;
856*8ad74188SDarren Reed 
857*8ad74188SDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
858*8ad74188SDarren Reed 
859*8ad74188SDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
860*8ad74188SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
861*8ad74188SDarren Reed 		/*
862*8ad74188SDarren Reed 		 * If we're trying to destroy the hook_stack_t...
863*8ad74188SDarren Reed 		 */
864*8ad74188SDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
865*8ad74188SDarren Reed 		return (ENXIO);
866*8ad74188SDarren Reed 	}
867*8ad74188SDarren Reed 
868*8ad74188SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
869*8ad74188SDarren Reed 	notifydone = hfi->hfi_shutdown;
870*8ad74188SDarren Reed 	hfi->hfi_shutdown = B_TRUE;
871*8ad74188SDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
872*8ad74188SDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
873*8ad74188SDarren Reed 
874*8ad74188SDarren Reed 	if (!notifydone)
875*8ad74188SDarren Reed 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
876*8ad74188SDarren Reed 		    HN_UNREGISTER);
877*8ad74188SDarren Reed 
878*8ad74188SDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
879*8ad74188SDarren Reed 
880*8ad74188SDarren Reed 	return (0);
881*8ad74188SDarren Reed }
8827ddc9b1aSDarren Reed 
8837ddc9b1aSDarren Reed /*
884381a2a9aSdr146992  * Function:	hook_family_copy
885381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Failed
886381a2a9aSdr146992  * Parameters:	src(I) - family pointer
887381a2a9aSdr146992  *
888381a2a9aSdr146992  * Allocate internal family block and duplicate incoming family
889381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
890381a2a9aSdr146992  */
891381a2a9aSdr146992 static hook_family_int_t *
892381a2a9aSdr146992 hook_family_copy(hook_family_t *src)
893381a2a9aSdr146992 {
894381a2a9aSdr146992 	hook_family_int_t *new;
895381a2a9aSdr146992 	hook_family_t *dst;
896381a2a9aSdr146992 
897381a2a9aSdr146992 	ASSERT(src != NULL);
898381a2a9aSdr146992 	ASSERT(src->hf_name != NULL);
899381a2a9aSdr146992 
900381a2a9aSdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
901381a2a9aSdr146992 
902381a2a9aSdr146992 	/* Copy body */
903381a2a9aSdr146992 	dst = &new->hfi_family;
904381a2a9aSdr146992 	*dst = *src;
905381a2a9aSdr146992 
9067ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
9077ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
9087ddc9b1aSDarren Reed 
909381a2a9aSdr146992 	/* Copy name */
910381a2a9aSdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
911381a2a9aSdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
912381a2a9aSdr146992 
913381a2a9aSdr146992 	return (new);
914381a2a9aSdr146992 }
915381a2a9aSdr146992 
916381a2a9aSdr146992 /*
9177ddc9b1aSDarren Reed  * Function:	hook_family_find
918381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Not match
919381a2a9aSdr146992  * Parameters:	family(I) - family name string
920381a2a9aSdr146992  *
921381a2a9aSdr146992  * Search family list with family name
9227ddc9b1aSDarren Reed  * 	A lock on hfi_lock must be held when called.
923381a2a9aSdr146992  */
924381a2a9aSdr146992 static hook_family_int_t *
925f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
926381a2a9aSdr146992 {
927381a2a9aSdr146992 	hook_family_int_t *hfi = NULL;
928381a2a9aSdr146992 
929381a2a9aSdr146992 	ASSERT(family != NULL);
930381a2a9aSdr146992 
931f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
932381a2a9aSdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
933381a2a9aSdr146992 			break;
934381a2a9aSdr146992 	}
935381a2a9aSdr146992 	return (hfi);
936381a2a9aSdr146992 }
937381a2a9aSdr146992 
9387ddc9b1aSDarren Reed /*
9397ddc9b1aSDarren Reed  * Function:	hook_family_notify_register
9407ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
9417ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
9427ddc9b1aSDarren Reed  *              callback(I) - function to be called
9437ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
9447ddc9b1aSDarren Reed  *
9457ddc9b1aSDarren Reed  * So long as this hook stack isn't being shut down, register a new
9467ddc9b1aSDarren Reed  * callback to be activated each time a new event is added to this
9477ddc9b1aSDarren Reed  * family.
9487ddc9b1aSDarren Reed  *
9497ddc9b1aSDarren Reed  * To call this function we must have an active handle in use on the family,
9507ddc9b1aSDarren Reed  * so if we take this into account, then neither the hook_family_int_t nor
9517ddc9b1aSDarren Reed  * the hook_stack_t that owns it can disappear. We have to put some trust
9527ddc9b1aSDarren Reed  * in the callers to be properly synchronised...
9537ddc9b1aSDarren Reed  *
9547ddc9b1aSDarren Reed  * Holding hks_lock is required to provide synchronisation for hks_shutdown.
9557ddc9b1aSDarren Reed  */
9567ddc9b1aSDarren Reed int
9577ddc9b1aSDarren Reed hook_family_notify_register(hook_family_int_t *hfi,
9587ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
9597ddc9b1aSDarren Reed {
9607ddc9b1aSDarren Reed 	hook_stack_t *hks;
9617ddc9b1aSDarren Reed 	int error;
9627ddc9b1aSDarren Reed 
9637ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
9647ddc9b1aSDarren Reed 
9657ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
9667ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
9677ddc9b1aSDarren Reed 
968*8ad74188SDarren Reed 	if ((hfi->hfi_stack->hks_shutdown != 0) ||
969*8ad74188SDarren Reed 	    hfi->hfi_condemned || hfi->hfi_shutdown) {
9707ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
9717ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
9727ddc9b1aSDarren Reed 		return (ESHUTDOWN);
9737ddc9b1aSDarren Reed 	}
9747ddc9b1aSDarren Reed 
9757ddc9b1aSDarren Reed 	error = hook_notify_register(&hfi->hfi_lock, &hfi->hfi_nhead,
9767ddc9b1aSDarren Reed 	    callback, arg);
9777ddc9b1aSDarren Reed 
9787ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
9797ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
9807ddc9b1aSDarren Reed 
9817ddc9b1aSDarren Reed 	return (error);
9827ddc9b1aSDarren Reed }
983381a2a9aSdr146992 
984381a2a9aSdr146992 /*
9857ddc9b1aSDarren Reed  * Function:	hook_family_notify_unregister
9867ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
9877ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
9887ddc9b1aSDarren Reed  *              callback(I) - function to be called
989381a2a9aSdr146992  *
9907ddc9b1aSDarren Reed  * Remove a callback from the list of those executed when a new event is
9917ddc9b1aSDarren Reed  * added to a hook family.
992381a2a9aSdr146992  */
9937ddc9b1aSDarren Reed int
9947ddc9b1aSDarren Reed hook_family_notify_unregister(hook_family_int_t *hfi,
9957ddc9b1aSDarren Reed     hook_notify_fn_t callback)
996381a2a9aSdr146992 {
9977ddc9b1aSDarren Reed 	boolean_t free_family;
9987ddc9b1aSDarren Reed 	int error;
999381a2a9aSdr146992 
10007ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
10017ddc9b1aSDarren Reed 
10027ddc9b1aSDarren Reed 	error = hook_notify_unregister(&hfi->hfi_lock, &hfi->hfi_nhead,
10037ddc9b1aSDarren Reed 	    callback);
10047ddc9b1aSDarren Reed 
10057ddc9b1aSDarren Reed 	/*
10067ddc9b1aSDarren Reed 	 * If hook_family_remove has been called but the structure was still
10077ddc9b1aSDarren Reed 	 * "busy" ... but we might have just made it "unbusy"...
10087ddc9b1aSDarren Reed 	 */
10097ddc9b1aSDarren Reed 	if ((error == 0) && hfi->hfi_condemned &&
10107ddc9b1aSDarren Reed 	    SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
10117ddc9b1aSDarren Reed 		free_family = B_TRUE;
10127ddc9b1aSDarren Reed 	} else {
10137ddc9b1aSDarren Reed 		free_family = B_FALSE;
1014381a2a9aSdr146992 	}
1015381a2a9aSdr146992 
10167ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1017381a2a9aSdr146992 
10187ddc9b1aSDarren Reed 	if (free_family)
10197ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
10207ddc9b1aSDarren Reed 
10217ddc9b1aSDarren Reed 	return (error);
10227ddc9b1aSDarren Reed }
1023381a2a9aSdr146992 
1024381a2a9aSdr146992 /*
1025381a2a9aSdr146992  * Function:	hook_event_add
1026381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Fail
1027381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1028381a2a9aSdr146992  *		he(I) - event pointer
1029381a2a9aSdr146992  *
1030381a2a9aSdr146992  * Add new event to event list on specific family.
1031381a2a9aSdr146992  * This function can fail to return successfully if (1) it cannot allocate
1032381a2a9aSdr146992  * enough memory for its own internal data structures, (2) the event has
1033381a2a9aSdr146992  * already been registered (for any hook family.)
1034381a2a9aSdr146992  */
1035381a2a9aSdr146992 hook_event_int_t *
1036381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
1037381a2a9aSdr146992 {
1038381a2a9aSdr146992 	hook_event_int_t *hei, *new;
10397ddc9b1aSDarren Reed 	hook_stack_t *hks;
1040381a2a9aSdr146992 
1041381a2a9aSdr146992 	ASSERT(hfi != NULL);
1042381a2a9aSdr146992 	ASSERT(he != NULL);
1043381a2a9aSdr146992 	ASSERT(he->he_name != NULL);
1044381a2a9aSdr146992 
1045381a2a9aSdr146992 	new = hook_event_copy(he);
1046381a2a9aSdr146992 	if (new == NULL)
1047381a2a9aSdr146992 		return (NULL);
1048381a2a9aSdr146992 
10497ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
10507ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
10517ddc9b1aSDarren Reed 
10527ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
10537ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
10547ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
10557ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
10567ddc9b1aSDarren Reed 		return (NULL);
10577ddc9b1aSDarren Reed 	}
1058381a2a9aSdr146992 
1059381a2a9aSdr146992 	/* Check whether this event pointer is already registered */
1060f4b3ec61Sdh155122 	hei = hook_event_checkdup(he, hks);
1061381a2a9aSdr146992 	if (hei != NULL) {
10627ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
10637ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1064381a2a9aSdr146992 		return (NULL);
1065381a2a9aSdr146992 	}
1066381a2a9aSdr146992 
10677ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
10687ddc9b1aSDarren Reed 
1069*8ad74188SDarren Reed 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
10707ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
10717ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
10727ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
10737ddc9b1aSDarren Reed 		return (NULL);
10747ddc9b1aSDarren Reed 	}
10757ddc9b1aSDarren Reed 
10767ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
10777ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
10787ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
10797ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
10807ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
10817ddc9b1aSDarren Reed 		return (NULL);
10827ddc9b1aSDarren Reed 	}
10837ddc9b1aSDarren Reed 
10847ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hei_nhead);
10857ddc9b1aSDarren Reed 
10867ddc9b1aSDarren Reed 	hook_event_init_kstats(hfi, new);
10877ddc9b1aSDarren Reed 	hook_wait_init(&new->hei_waiter, &new->hei_lock);
10887ddc9b1aSDarren Reed 
1089381a2a9aSdr146992 	/* Add to event list head */
1090381a2a9aSdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
1091381a2a9aSdr146992 
10927ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
10937ddc9b1aSDarren Reed 
10947ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
10957ddc9b1aSDarren Reed 
10967ddc9b1aSDarren Reed 	hook_notify_run(&hfi->hfi_nhead,
10977ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
10987ddc9b1aSDarren Reed 
10997ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
11007ddc9b1aSDarren Reed 
1101381a2a9aSdr146992 	return (new);
1102381a2a9aSdr146992 }
1103381a2a9aSdr146992 
11047ddc9b1aSDarren Reed /*
11057ddc9b1aSDarren Reed  * Function:	hook_event_init_kstats
11067ddc9b1aSDarren Reed  * Returns:	None
11077ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns this event.
11087ddc9b1aSDarren Reed  *              hei(I) - pointer to the hook event that needs some kstats.
11097ddc9b1aSDarren Reed  *
11107ddc9b1aSDarren Reed  * Create a set of kstats that relate to each event registered with
11117ddc9b1aSDarren Reed  * the hook framework.  A counter is kept for each time the event is
11127ddc9b1aSDarren Reed  * activated and for each time a hook is added or removed.  As the
11137ddc9b1aSDarren Reed  * kstats just count the events as they happen, the total number of
11147ddc9b1aSDarren Reed  * hooks registered must be obtained by subtractived removed from added.
11157ddc9b1aSDarren Reed  */
11167ddc9b1aSDarren Reed static void
11177ddc9b1aSDarren Reed hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
11187ddc9b1aSDarren Reed {
11197ddc9b1aSDarren Reed 	hook_event_kstat_t template = {
11207ddc9b1aSDarren Reed 		{ "hooksAdded",		KSTAT_DATA_UINT64 },
11217ddc9b1aSDarren Reed 		{ "hooksRemoved",	KSTAT_DATA_UINT64 },
11227ddc9b1aSDarren Reed 		{ "events",		KSTAT_DATA_UINT64 }
11237ddc9b1aSDarren Reed 	};
11247ddc9b1aSDarren Reed 	hook_stack_t *hks;
11257ddc9b1aSDarren Reed 
11267ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
11277ddc9b1aSDarren Reed 	hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
11287ddc9b1aSDarren Reed 	    hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
11297ddc9b1aSDarren Reed 	    sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
11307ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
11317ddc9b1aSDarren Reed 
11327ddc9b1aSDarren Reed 	bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
11337ddc9b1aSDarren Reed 
11347ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
11357ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
11367ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_private =
11377ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
11387ddc9b1aSDarren Reed 
11397ddc9b1aSDarren Reed 		kstat_install(hei->hei_kstatp);
11407ddc9b1aSDarren Reed 	}
11417ddc9b1aSDarren Reed }
1142381a2a9aSdr146992 
1143381a2a9aSdr146992 /*
1144381a2a9aSdr146992  * Function:	hook_event_remove
1145381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
1146381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1147381a2a9aSdr146992  *		he(I) - event pointer
1148381a2a9aSdr146992  *
1149381a2a9aSdr146992  * Remove event from event list on specific family
11507ddc9b1aSDarren Reed  *
11517ddc9b1aSDarren Reed  * This function assumes that the caller has received a pointer to a the
11527ddc9b1aSDarren Reed  * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
11537ddc9b1aSDarren Reed  * This the hook_family_int_t is guaranteed to be around for the life of this
11547ddc9b1aSDarren Reed  * call, unless the caller has decided to call net_protocol_release or
11557ddc9b1aSDarren Reed  * net_protocol_unregister before calling net_event_unregister - an error.
1156381a2a9aSdr146992  */
1157381a2a9aSdr146992 int
1158381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
1159381a2a9aSdr146992 {
11607ddc9b1aSDarren Reed 	boolean_t free_family;
1161381a2a9aSdr146992 	hook_event_int_t *hei;
1162*8ad74188SDarren Reed 	boolean_t notifydone;
1163381a2a9aSdr146992 
1164381a2a9aSdr146992 	ASSERT(hfi != NULL);
1165381a2a9aSdr146992 	ASSERT(he != NULL);
1166381a2a9aSdr146992 
11677ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1168381a2a9aSdr146992 
11697ddc9b1aSDarren Reed 	/*
11707ddc9b1aSDarren Reed 	 * Set the flag so that we can call hook_event_notify_run without
11717ddc9b1aSDarren Reed 	 * holding any locks but at the same time prevent other changes to
11727ddc9b1aSDarren Reed 	 * the event at the same time.
11737ddc9b1aSDarren Reed 	 */
11747ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
11757ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
11767ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1177381a2a9aSdr146992 		return (ENXIO);
1178381a2a9aSdr146992 	}
1179381a2a9aSdr146992 
11807ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, he->he_name);
11817ddc9b1aSDarren Reed 	if (hei == NULL) {
11827ddc9b1aSDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
11837ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
11847ddc9b1aSDarren Reed 		return (ESRCH);
1185381a2a9aSdr146992 	}
1186381a2a9aSdr146992 
11877ddc9b1aSDarren Reed 	free_family = B_FALSE;
1188381a2a9aSdr146992 
11897ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
11907ddc9b1aSDarren Reed 	/*
1191*8ad74188SDarren Reed 	 * The hei_shutdown flag is used to indicate whether or not we have
1192*8ad74188SDarren Reed 	 * done a shutdown and thus already walked through the notify list.
1193*8ad74188SDarren Reed 	 */
1194*8ad74188SDarren Reed 	notifydone = hei->hei_shutdown;
1195*8ad74188SDarren Reed 	hei->hei_shutdown = B_TRUE;
1196*8ad74188SDarren Reed 	/*
11977ddc9b1aSDarren Reed 	 * If there are any hooks still registered for this event or
11987ddc9b1aSDarren Reed 	 * there are any notifiers registered, return an error indicating
11997ddc9b1aSDarren Reed 	 * that the event is still busy.
12007ddc9b1aSDarren Reed 	 */
12017ddc9b1aSDarren Reed 	if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
12027ddc9b1aSDarren Reed 		hei->hei_condemned = B_TRUE;
12037ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
12047ddc9b1aSDarren Reed 	} else {
12057ddc9b1aSDarren Reed 		/* hei_condemned = B_FALSE is implied from creation */
12067ddc9b1aSDarren Reed 		/*
12077ddc9b1aSDarren Reed 		 * Even though we know the notify list is empty, we call
12087ddc9b1aSDarren Reed 		 * hook_wait_destroy here to synchronise wait removing a
12097ddc9b1aSDarren Reed 		 * hook from an event.
12107ddc9b1aSDarren Reed 		 */
12117ddc9b1aSDarren Reed 		hook_wait_destroy(&hei->hei_waiter);
12127ddc9b1aSDarren Reed 
12137ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
12147ddc9b1aSDarren Reed 
12157ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
12167ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead))
12177ddc9b1aSDarren Reed 			free_family = B_TRUE;
12187ddc9b1aSDarren Reed 	}
12197ddc9b1aSDarren Reed 
12207ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
12217ddc9b1aSDarren Reed 
1222*8ad74188SDarren Reed 	if (!notifydone)
12237ddc9b1aSDarren Reed 		hook_notify_run(&hfi->hfi_nhead,
12247ddc9b1aSDarren Reed 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
12257ddc9b1aSDarren Reed 
12267ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
12277ddc9b1aSDarren Reed 
12287ddc9b1aSDarren Reed 	if (!hei->hei_condemned) {
12297ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
12307ddc9b1aSDarren Reed 		if (free_family)
12317ddc9b1aSDarren Reed 			hook_family_free(hfi, hfi->hfi_stack);
12327ddc9b1aSDarren Reed 	}
1233381a2a9aSdr146992 
1234381a2a9aSdr146992 	return (0);
1235381a2a9aSdr146992 }
1236381a2a9aSdr146992 
12377ddc9b1aSDarren Reed /*
1238*8ad74188SDarren Reed  * Function:	hook_event_shutdown
1239*8ad74188SDarren Reed  * Returns:	int - 0 = Succ, Else = Fail
1240*8ad74188SDarren Reed  * Parameters:	hfi(I) - internal family pointer
1241*8ad74188SDarren Reed  *		he(I)  - event pointer
1242*8ad74188SDarren Reed  *
1243*8ad74188SDarren Reed  * As with hook_family_shutdown, we want to generate the notify callbacks
1244*8ad74188SDarren Reed  * as if the event was being removed but not actually do the remove.
1245*8ad74188SDarren Reed  */
1246*8ad74188SDarren Reed int
1247*8ad74188SDarren Reed hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he)
1248*8ad74188SDarren Reed {
1249*8ad74188SDarren Reed 	hook_event_int_t *hei;
1250*8ad74188SDarren Reed 	boolean_t notifydone;
1251*8ad74188SDarren Reed 
1252*8ad74188SDarren Reed 	ASSERT(hfi != NULL);
1253*8ad74188SDarren Reed 	ASSERT(he != NULL);
1254*8ad74188SDarren Reed 
1255*8ad74188SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1256*8ad74188SDarren Reed 
1257*8ad74188SDarren Reed 	/*
1258*8ad74188SDarren Reed 	 * Set the flag so that we can call hook_event_notify_run without
1259*8ad74188SDarren Reed 	 * holding any locks but at the same time prevent other changes to
1260*8ad74188SDarren Reed 	 * the event at the same time.
1261*8ad74188SDarren Reed 	 */
1262*8ad74188SDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
1263*8ad74188SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
1264*8ad74188SDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1265*8ad74188SDarren Reed 		return (ENXIO);
1266*8ad74188SDarren Reed 	}
1267*8ad74188SDarren Reed 
1268*8ad74188SDarren Reed 	hei = hook_event_find(hfi, he->he_name);
1269*8ad74188SDarren Reed 	if (hei == NULL) {
1270*8ad74188SDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1271*8ad74188SDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1272*8ad74188SDarren Reed 		return (ESRCH);
1273*8ad74188SDarren Reed 	}
1274*8ad74188SDarren Reed 
1275*8ad74188SDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1276*8ad74188SDarren Reed 	notifydone = hei->hei_shutdown;
1277*8ad74188SDarren Reed 	hei->hei_shutdown = B_TRUE;
1278*8ad74188SDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
1279*8ad74188SDarren Reed 
1280*8ad74188SDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1281*8ad74188SDarren Reed 
1282*8ad74188SDarren Reed 	if (!notifydone)
1283*8ad74188SDarren Reed 		hook_notify_run(&hfi->hfi_nhead,
1284*8ad74188SDarren Reed 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
1285*8ad74188SDarren Reed 
1286*8ad74188SDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1287*8ad74188SDarren Reed 
1288*8ad74188SDarren Reed 	return (0);
1289*8ad74188SDarren Reed }
1290*8ad74188SDarren Reed 
1291*8ad74188SDarren Reed /*
12927ddc9b1aSDarren Reed  * Function:	hook_event_free
12937ddc9b1aSDarren Reed  * Returns:	None
12947ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
12957ddc9b1aSDarren Reed  *
12967ddc9b1aSDarren Reed  * Free alloc memory for event
12977ddc9b1aSDarren Reed  */
12987ddc9b1aSDarren Reed static void
12997ddc9b1aSDarren Reed hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
13007ddc9b1aSDarren Reed {
13017ddc9b1aSDarren Reed 	boolean_t free_family;
13027ddc9b1aSDarren Reed 
13037ddc9b1aSDarren Reed 	ASSERT(hei != NULL);
13047ddc9b1aSDarren Reed 
13057ddc9b1aSDarren Reed 	if (hfi != NULL) {
13067ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hfi->hfi_lock);
13077ddc9b1aSDarren Reed 		/*
13087ddc9b1aSDarren Reed 		 * Remove the event from the hook family's list.
13097ddc9b1aSDarren Reed 		 */
13107ddc9b1aSDarren Reed 		SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
13117ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
13127ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead)) {
13137ddc9b1aSDarren Reed 			free_family = B_TRUE;
13147ddc9b1aSDarren Reed 		} else {
13157ddc9b1aSDarren Reed 			free_family = B_FALSE;
13167ddc9b1aSDarren Reed 		}
13177ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13187ddc9b1aSDarren Reed 	}
13197ddc9b1aSDarren Reed 
13207ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
13217ddc9b1aSDarren Reed 		ASSERT(hfi != NULL);
13227ddc9b1aSDarren Reed 
13237ddc9b1aSDarren Reed 		kstat_delete_netstack(hei->hei_kstatp,
13247ddc9b1aSDarren Reed 		    hfi->hfi_stack->hks_netstackid);
13257ddc9b1aSDarren Reed 		hei->hei_kstatp = NULL;
13267ddc9b1aSDarren Reed 	}
13277ddc9b1aSDarren Reed 
13287ddc9b1aSDarren Reed 	/* Free container */
13297ddc9b1aSDarren Reed 	kmem_free(hei, sizeof (*hei));
13307ddc9b1aSDarren Reed 
13317ddc9b1aSDarren Reed 	if (free_family)
13327ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
13337ddc9b1aSDarren Reed }
1334381a2a9aSdr146992 
1335381a2a9aSdr146992 /*
1336381a2a9aSdr146992  * Function:    hook_event_checkdup
1337381a2a9aSdr146992  * Returns:     internal event pointer - NULL = Not match
1338381a2a9aSdr146992  * Parameters:  he(I) - event pointer
1339381a2a9aSdr146992  *
13407ddc9b1aSDarren Reed  * Search all of the hook families to see if the event being passed in
13417ddc9b1aSDarren Reed  * has already been associated with one.
1342381a2a9aSdr146992  */
1343381a2a9aSdr146992 static hook_event_int_t *
1344f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
1345381a2a9aSdr146992 {
1346381a2a9aSdr146992 	hook_family_int_t *hfi;
1347381a2a9aSdr146992 	hook_event_int_t *hei;
1348381a2a9aSdr146992 
1349381a2a9aSdr146992 	ASSERT(he != NULL);
1350381a2a9aSdr146992 
13517ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
1352f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
1353381a2a9aSdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
13547ddc9b1aSDarren Reed 			if (hei->hei_event == he) {
13557ddc9b1aSDarren Reed 				CVW_EXIT_READ(&hks->hks_lock);
1356381a2a9aSdr146992 				return (hei);
1357381a2a9aSdr146992 			}
1358381a2a9aSdr146992 		}
13597ddc9b1aSDarren Reed 	}
13607ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
1361381a2a9aSdr146992 
1362381a2a9aSdr146992 	return (NULL);
1363381a2a9aSdr146992 }
1364381a2a9aSdr146992 
1365381a2a9aSdr146992 /*
1366381a2a9aSdr146992  * Function:	hook_event_copy
1367381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Failed
1368381a2a9aSdr146992  * Parameters:	src(I) - event pointer
1369381a2a9aSdr146992  *
1370381a2a9aSdr146992  * Allocate internal event block and duplicate incoming event
1371381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
1372381a2a9aSdr146992  */
1373381a2a9aSdr146992 static hook_event_int_t *
1374381a2a9aSdr146992 hook_event_copy(hook_event_t *src)
1375381a2a9aSdr146992 {
1376381a2a9aSdr146992 	hook_event_int_t *new;
1377381a2a9aSdr146992 
1378381a2a9aSdr146992 	ASSERT(src != NULL);
1379381a2a9aSdr146992 	ASSERT(src->he_name != NULL);
1380381a2a9aSdr146992 
1381381a2a9aSdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1382381a2a9aSdr146992 
1383381a2a9aSdr146992 	/* Copy body */
1384381a2a9aSdr146992 	TAILQ_INIT(&new->hei_head);
1385381a2a9aSdr146992 	new->hei_event = src;
1386381a2a9aSdr146992 
1387381a2a9aSdr146992 	return (new);
1388381a2a9aSdr146992 }
1389381a2a9aSdr146992 
1390381a2a9aSdr146992 /*
1391381a2a9aSdr146992  * Function:	hook_event_find
1392381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Not match
1393381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1394381a2a9aSdr146992  *		event(I) - event name string
1395381a2a9aSdr146992  *
1396381a2a9aSdr146992  * Search event list with event name
13977ddc9b1aSDarren Reed  * 	A lock on hfi->hfi_lock must be held when called.
1398381a2a9aSdr146992  */
1399381a2a9aSdr146992 static hook_event_int_t *
1400381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
1401381a2a9aSdr146992 {
1402381a2a9aSdr146992 	hook_event_int_t *hei = NULL;
1403381a2a9aSdr146992 
1404381a2a9aSdr146992 	ASSERT(hfi != NULL);
1405381a2a9aSdr146992 	ASSERT(event != NULL);
1406381a2a9aSdr146992 
1407381a2a9aSdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
14087ddc9b1aSDarren Reed 		if ((strcmp(hei->hei_event->he_name, event) == 0) &&
14097ddc9b1aSDarren Reed 		    ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
1410381a2a9aSdr146992 			break;
1411381a2a9aSdr146992 	}
1412381a2a9aSdr146992 	return (hei);
1413381a2a9aSdr146992 }
1414381a2a9aSdr146992 
1415381a2a9aSdr146992 /*
14167ddc9b1aSDarren Reed  * Function:	hook_event_notify_register
14177ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
14187ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
14197ddc9b1aSDarren Reed  *              event(I)    - name of the event
14207ddc9b1aSDarren Reed  *              callback(I) - function to be called
14217ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
1422381a2a9aSdr146992  *
14237ddc9b1aSDarren Reed  * Adds a new callback to the event named by "event" (we must find it)
14247ddc9b1aSDarren Reed  * that will be executed each time a new hook is added to the event.
14257ddc9b1aSDarren Reed  * Of course, if the stack is being shut down, this call should fail.
1426381a2a9aSdr146992  */
14277ddc9b1aSDarren Reed int
14287ddc9b1aSDarren Reed hook_event_notify_register(hook_family_int_t *hfi, char *event,
14297ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
1430381a2a9aSdr146992 {
14317ddc9b1aSDarren Reed 	hook_event_int_t *hei;
14327ddc9b1aSDarren Reed 	hook_stack_t *hks;
14337ddc9b1aSDarren Reed 	int error;
1434381a2a9aSdr146992 
14357ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
14367ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
14377ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
14387ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
14397ddc9b1aSDarren Reed 		return (ESHUTDOWN);
1440381a2a9aSdr146992 	}
1441381a2a9aSdr146992 
14427ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
14437ddc9b1aSDarren Reed 
1444*8ad74188SDarren Reed 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
14457ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
14467ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
14477ddc9b1aSDarren Reed 		return (ESHUTDOWN);
14487ddc9b1aSDarren Reed 	}
14497ddc9b1aSDarren Reed 
14507ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
14517ddc9b1aSDarren Reed 	if (hei == NULL) {
14527ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
14537ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
14547ddc9b1aSDarren Reed 		return (ESRCH);
14557ddc9b1aSDarren Reed 	}
14567ddc9b1aSDarren Reed 
14577ddc9b1aSDarren Reed 	/*
14587ddc9b1aSDarren Reed 	 * Grabbing the read lock on hei_lock is only so that we can
14597ddc9b1aSDarren Reed 	 * synchronise access to hei_condemned.
14607ddc9b1aSDarren Reed 	 */
14617ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1462*8ad74188SDarren Reed 	if (hei->hei_condemned || hei->hei_shutdown) {
14637ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
14647ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
14657ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
14667ddc9b1aSDarren Reed 		return (ESHUTDOWN);
14677ddc9b1aSDarren Reed 	}
14687ddc9b1aSDarren Reed 
14697ddc9b1aSDarren Reed 	error = hook_notify_register(&hei->hei_lock, &hei->hei_nhead,
14707ddc9b1aSDarren Reed 	    callback, arg);
14717ddc9b1aSDarren Reed 
14727ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
14737ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
14747ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
14757ddc9b1aSDarren Reed 
14767ddc9b1aSDarren Reed 	return (error);
14777ddc9b1aSDarren Reed }
14787ddc9b1aSDarren Reed 
14797ddc9b1aSDarren Reed /*
14807ddc9b1aSDarren Reed  * Function:	hook_event_notify_unregister
14817ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
14827ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
14837ddc9b1aSDarren Reed  *              event(I)    - name of the event
14847ddc9b1aSDarren Reed  *              callback(I) - function to be called
14857ddc9b1aSDarren Reed  *
14867ddc9b1aSDarren Reed  * Remove the given callback from the named event's list of functions
14877ddc9b1aSDarren Reed  * to call when a hook is added or removed.
14887ddc9b1aSDarren Reed  */
14897ddc9b1aSDarren Reed int
14907ddc9b1aSDarren Reed hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
14917ddc9b1aSDarren Reed     hook_notify_fn_t callback)
14927ddc9b1aSDarren Reed {
14937ddc9b1aSDarren Reed 	hook_event_int_t *hei;
14947ddc9b1aSDarren Reed 	boolean_t free_event;
14957ddc9b1aSDarren Reed 	int error;
14967ddc9b1aSDarren Reed 
14977ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
14987ddc9b1aSDarren Reed 
14997ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
15007ddc9b1aSDarren Reed 	if (hei == NULL) {
15017ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
15027ddc9b1aSDarren Reed 		return (ESRCH);
15037ddc9b1aSDarren Reed 	}
15047ddc9b1aSDarren Reed 
15057ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
15067ddc9b1aSDarren Reed 
15077ddc9b1aSDarren Reed 	error = hook_notify_unregister(&hei->hei_lock, &hei->hei_nhead,
15087ddc9b1aSDarren Reed 	    callback);
15097ddc9b1aSDarren Reed 
15107ddc9b1aSDarren Reed 	/*
15117ddc9b1aSDarren Reed 	 * hei_condemned has been set if someone tried to remove the
15127ddc9b1aSDarren Reed 	 * event but couldn't because there were still things attached to
15137ddc9b1aSDarren Reed 	 * it. Now that we've done a successful remove, if it is now empty
15147ddc9b1aSDarren Reed 	 * then by all rights we should be free'ing it too.  Note that the
15157ddc9b1aSDarren Reed 	 * expectation is that only the caller of hook_event_add will ever
15167ddc9b1aSDarren Reed 	 * call hook_event_remove.
15177ddc9b1aSDarren Reed 	 */
15187ddc9b1aSDarren Reed 	if ((error == 0) && hei->hei_condemned &&
15197ddc9b1aSDarren Reed 	    TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
15207ddc9b1aSDarren Reed 		free_event = B_TRUE;
15217ddc9b1aSDarren Reed 	} else {
15227ddc9b1aSDarren Reed 		free_event = B_FALSE;
15237ddc9b1aSDarren Reed 	}
15247ddc9b1aSDarren Reed 
15257ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
15267ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
15277ddc9b1aSDarren Reed 
15287ddc9b1aSDarren Reed 	if (free_event) {
15297ddc9b1aSDarren Reed 		/*
15307ddc9b1aSDarren Reed 		 * It is safe to pass in hfi here, without a lock, because
15317ddc9b1aSDarren Reed 		 * our structure (hei) is still on one of its lists and thus
15327ddc9b1aSDarren Reed 		 * it won't be able to disappear yet...
15337ddc9b1aSDarren Reed 		 */
15347ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
15357ddc9b1aSDarren Reed 	}
15367ddc9b1aSDarren Reed 
15377ddc9b1aSDarren Reed 	return (error);
15387ddc9b1aSDarren Reed }
15397ddc9b1aSDarren Reed 
15407ddc9b1aSDarren Reed /*
15417ddc9b1aSDarren Reed  * Function:	hook_event_notify_run
15427ddc9b1aSDarren Reed  * Returns:	None
15437ddc9b1aSDarren Reed  * Parameters:	nrun(I) - pointer to the list of callbacks to execute
15447ddc9b1aSDarren Reed  *              hfi(I)  - hook stack pointer to execute callbacks for
15457ddc9b1aSDarren Reed  *              name(I) - name of a hook family
15467ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
15477ddc9b1aSDarren Reed  *
15487ddc9b1aSDarren Reed  * Execute all of the callbacks registered for this event.
15497ddc9b1aSDarren Reed  */
15507ddc9b1aSDarren Reed static void
15517ddc9b1aSDarren Reed hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
15527ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd)
15537ddc9b1aSDarren Reed {
15547ddc9b1aSDarren Reed 
15557ddc9b1aSDarren Reed 	hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
15567ddc9b1aSDarren Reed 	    event, name, cmd);
15577ddc9b1aSDarren Reed }
1558381a2a9aSdr146992 
1559381a2a9aSdr146992 /*
1560381a2a9aSdr146992  * Function:	hook_register
1561381a2a9aSdr146992  * Returns:	int- 0 = Succ, Else = Fail
1562381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1563381a2a9aSdr146992  *		event(I) - event name string
1564381a2a9aSdr146992  *		h(I) - hook pointer
1565381a2a9aSdr146992  *
15667ddc9b1aSDarren Reed  * Add new hook to hook list on the specified family and event.
1567381a2a9aSdr146992  */
1568381a2a9aSdr146992 int
1569381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
1570381a2a9aSdr146992 {
1571381a2a9aSdr146992 	hook_event_int_t *hei;
1572381a2a9aSdr146992 	hook_int_t *hi, *new;
15737ddc9b1aSDarren Reed 	int error;
1574381a2a9aSdr146992 
1575381a2a9aSdr146992 	ASSERT(hfi != NULL);
1576381a2a9aSdr146992 	ASSERT(event != NULL);
1577381a2a9aSdr146992 	ASSERT(h != NULL);
15787ddc9b1aSDarren Reed 
15797ddc9b1aSDarren Reed 	if (hfi->hfi_stack->hks_shutdown)
15807ddc9b1aSDarren Reed 		return (NULL);
1581381a2a9aSdr146992 
1582381a2a9aSdr146992 	/* Alloc hook_int_t and copy hook */
1583381a2a9aSdr146992 	new = hook_copy(h);
1584381a2a9aSdr146992 	if (new == NULL)
1585381a2a9aSdr146992 		return (ENOMEM);
1586381a2a9aSdr146992 
1587381a2a9aSdr146992 	/*
1588381a2a9aSdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
1589381a2a9aSdr146992 	 * to hold global family write lock. Just get read lock here to
1590381a2a9aSdr146992 	 * ensure event will not be removed when doing hooks operation
1591381a2a9aSdr146992 	 */
15927ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1593381a2a9aSdr146992 
1594381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
1595381a2a9aSdr146992 	if (hei == NULL) {
15967ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15977ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
1598381a2a9aSdr146992 		return (ENXIO);
1599381a2a9aSdr146992 	}
1600381a2a9aSdr146992 
1601381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
1602381a2a9aSdr146992 
1603*8ad74188SDarren Reed 	/*
1604*8ad74188SDarren Reed 	 * If we've run either the remove() or shutdown(), do not allow any
1605*8ad74188SDarren Reed 	 * more hooks to be added to this event.
1606*8ad74188SDarren Reed 	 */
1607*8ad74188SDarren Reed 	if (hei->hei_shutdown) {
1608*8ad74188SDarren Reed 		error = ESHUTDOWN;
1609*8ad74188SDarren Reed 		goto bad_add;
1610*8ad74188SDarren Reed 	}
1611*8ad74188SDarren Reed 
1612381a2a9aSdr146992 	hi = hook_find(hei, h);
1613381a2a9aSdr146992 	if (hi != NULL) {
16147ddc9b1aSDarren Reed 		error = EEXIST;
16157ddc9b1aSDarren Reed 		goto bad_add;
16167ddc9b1aSDarren Reed 	}
16177ddc9b1aSDarren Reed 
16187ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
16197ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
16207ddc9b1aSDarren Reed 		error = ENOENT;
16217ddc9b1aSDarren Reed bad_add:
1622381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
16237ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
16247ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
16257ddc9b1aSDarren Reed 		return (error);
1626381a2a9aSdr146992 	}
1627381a2a9aSdr146992 
1628381a2a9aSdr146992 	/* Add to hook list head */
16297ddc9b1aSDarren Reed 	error = hook_insert(&hei->hei_head, new);
16307ddc9b1aSDarren Reed 	if (error == 0) {
1631381a2a9aSdr146992 		hei->hei_event->he_interested = B_TRUE;
16327ddc9b1aSDarren Reed 		hei->hei_kstats.hooks_added.value.ui64++;
16337ddc9b1aSDarren Reed 
16347ddc9b1aSDarren Reed 		hook_init_kstats(hfi, hei, new);
16357ddc9b1aSDarren Reed 	}
1636381a2a9aSdr146992 
1637381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
16387ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
16397ddc9b1aSDarren Reed 
16407ddc9b1aSDarren Reed 	/*
16417ddc9b1aSDarren Reed 	 * Note that the name string passed through to the notify callbacks
16427ddc9b1aSDarren Reed 	 * is from the original hook being registered, not the copy being
16437ddc9b1aSDarren Reed 	 * inserted.
16447ddc9b1aSDarren Reed 	 */
16457ddc9b1aSDarren Reed 	if (error == 0) {
16467ddc9b1aSDarren Reed 		hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
16477ddc9b1aSDarren Reed 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
16487ddc9b1aSDarren Reed 	}
16497ddc9b1aSDarren Reed 
16507ddc9b1aSDarren Reed 	return (error);
16517ddc9b1aSDarren Reed }
16527ddc9b1aSDarren Reed 
16537ddc9b1aSDarren Reed /*
16547ddc9b1aSDarren Reed  * Function:	hook_insert
16557ddc9b1aSDarren Reed  * Returns:	int- 0 = Succ, else = Fail
16567ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
16577ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
16587ddc9b1aSDarren Reed  *
16597ddc9b1aSDarren Reed  * Try to insert the hook onto the list of hooks according to the hints
16607ddc9b1aSDarren Reed  * given in the hook to be inserted and those that already exist on the
16617ddc9b1aSDarren Reed  * list.  For now, the implementation permits only a single hook to be
16627ddc9b1aSDarren Reed  * either first or last and names provided with before or after are only
16637ddc9b1aSDarren Reed  * loosely coupled with the action.
16647ddc9b1aSDarren Reed  */
16657ddc9b1aSDarren Reed static int
16667ddc9b1aSDarren Reed hook_insert(hook_int_head_t *head, hook_int_t *new)
16677ddc9b1aSDarren Reed {
16687ddc9b1aSDarren Reed 	hook_int_t *before;
16697ddc9b1aSDarren Reed 	hook_int_t *hi;
16707ddc9b1aSDarren Reed 	hook_t *hih;
16717ddc9b1aSDarren Reed 	hook_t *h = &new->hi_hook;
16727ddc9b1aSDarren Reed 
16737ddc9b1aSDarren Reed 	switch (new->hi_hook.h_hint) {
16747ddc9b1aSDarren Reed 	case HH_NONE :
16757ddc9b1aSDarren Reed 		before = NULL;
16767ddc9b1aSDarren Reed 		/*
16777ddc9b1aSDarren Reed 		 * If there is no hint present (or not one that can be
16787ddc9b1aSDarren Reed 		 * satisfied now) then try to at least respect the wishes
16797ddc9b1aSDarren Reed 		 * of those that want to be last.  If there are none wanting
16807ddc9b1aSDarren Reed 		 * to be last then add the new hook to the tail of the
16817ddc9b1aSDarren Reed 		 * list - this means we keep any wanting to be first
16827ddc9b1aSDarren Reed 		 * happy without having to search for HH_FIRST.
16837ddc9b1aSDarren Reed 		 */
16847ddc9b1aSDarren Reed 		TAILQ_FOREACH(hi, head, hi_entry) {
16857ddc9b1aSDarren Reed 			hih = &hi->hi_hook;
16867ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_AFTER) &&
16877ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
16887ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
16897ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
16907ddc9b1aSDarren Reed 				return (0);
16917ddc9b1aSDarren Reed 			}
16927ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
16937ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
16947ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
16957ddc9b1aSDarren Reed 				before = hi;
16967ddc9b1aSDarren Reed 			}
16977ddc9b1aSDarren Reed 		}
16987ddc9b1aSDarren Reed 		if (before != NULL) {
16997ddc9b1aSDarren Reed 			TAILQ_INSERT_AFTER(head, before, new, hi_entry);
17007ddc9b1aSDarren Reed 			return (0);
17017ddc9b1aSDarren Reed 		}
17027ddc9b1aSDarren Reed 		hook_insert_plain(head, new);
17037ddc9b1aSDarren Reed 		break;
17047ddc9b1aSDarren Reed 
17057ddc9b1aSDarren Reed 	case HH_FIRST :
17067ddc9b1aSDarren Reed 		hi = TAILQ_FIRST(head);
17077ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
17087ddc9b1aSDarren Reed 			return (EBUSY);
17097ddc9b1aSDarren Reed 		TAILQ_INSERT_HEAD(head, new, hi_entry);
17107ddc9b1aSDarren Reed 		break;
17117ddc9b1aSDarren Reed 
17127ddc9b1aSDarren Reed 	case HH_LAST :
17137ddc9b1aSDarren Reed 		hi = TAILQ_LAST(head, hook_int_head);
17147ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
17157ddc9b1aSDarren Reed 			return (EBUSY);
17167ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
17177ddc9b1aSDarren Reed 		break;
17187ddc9b1aSDarren Reed 
17197ddc9b1aSDarren Reed 	case HH_BEFORE :
17207ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
17217ddc9b1aSDarren Reed 		if (hi == NULL)
17227ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
17237ddc9b1aSDarren Reed 
17247ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_FIRST)
17257ddc9b1aSDarren Reed 			return (EBUSY);
17267ddc9b1aSDarren Reed 
17277ddc9b1aSDarren Reed 		TAILQ_INSERT_BEFORE(hi, new, hi_entry);
17287ddc9b1aSDarren Reed 		break;
17297ddc9b1aSDarren Reed 
17307ddc9b1aSDarren Reed 	case HH_AFTER :
17317ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
17327ddc9b1aSDarren Reed 		if (hi == NULL)
17337ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
17347ddc9b1aSDarren Reed 
17357ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST)
17367ddc9b1aSDarren Reed 			return (EBUSY);
17377ddc9b1aSDarren Reed 
17387ddc9b1aSDarren Reed 		TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
17397ddc9b1aSDarren Reed 		break;
17407ddc9b1aSDarren Reed 
17417ddc9b1aSDarren Reed 	default :
17427ddc9b1aSDarren Reed 		return (EINVAL);
17437ddc9b1aSDarren Reed 	}
17447ddc9b1aSDarren Reed 
1745381a2a9aSdr146992 	return (0);
1746381a2a9aSdr146992 }
1747381a2a9aSdr146992 
17487ddc9b1aSDarren Reed /*
17497ddc9b1aSDarren Reed  * Function:	hook_insert_plain
17507ddc9b1aSDarren Reed  * Returns:	int- 0 = success, else = failure
17517ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
17527ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
17537ddc9b1aSDarren Reed  *
17547ddc9b1aSDarren Reed  * Insert a hook such that it respects the wishes of those that want to
17557ddc9b1aSDarren Reed  * be last.  If there are none wanting to be last then add the new hook
17567ddc9b1aSDarren Reed  * to the tail of the list - this means we keep any wanting to be first
17577ddc9b1aSDarren Reed  * happy without having to search for HH_FIRST.
17587ddc9b1aSDarren Reed  */
17597ddc9b1aSDarren Reed static void
17607ddc9b1aSDarren Reed hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
17617ddc9b1aSDarren Reed {
17627ddc9b1aSDarren Reed 	hook_int_t *hi;
17637ddc9b1aSDarren Reed 
17647ddc9b1aSDarren Reed 	hi = TAILQ_FIRST(head);
17657ddc9b1aSDarren Reed 	if (hi != NULL) {
17667ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST) {
17677ddc9b1aSDarren Reed 			TAILQ_INSERT_BEFORE(hi, new, hi_entry);
17687ddc9b1aSDarren Reed 		} else {
17697ddc9b1aSDarren Reed 			TAILQ_INSERT_TAIL(head, new, hi_entry);
17707ddc9b1aSDarren Reed 		}
17717ddc9b1aSDarren Reed 	} else {
17727ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
17737ddc9b1aSDarren Reed 	}
17747ddc9b1aSDarren Reed }
17757ddc9b1aSDarren Reed 
17767ddc9b1aSDarren Reed /*
17777ddc9b1aSDarren Reed  * Function:	hook_insert_afterbefore
17787ddc9b1aSDarren Reed  * Returns:	int- 0 = success, else = failure
17797ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
17807ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
17817ddc9b1aSDarren Reed  *
17827ddc9b1aSDarren Reed  * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
17837ddc9b1aSDarren Reed  * possible, so now we need to be more careful.  The first pass is to go
17847ddc9b1aSDarren Reed  * through the list and look for any other hooks that also specify the
17857ddc9b1aSDarren Reed  * same hint name as the new one.  The object of this exercise is to make
17867ddc9b1aSDarren Reed  * sure that hooks with HH_BEFORE always appear on the list before those
17877ddc9b1aSDarren Reed  * with HH_AFTER so that when said hook arrives, it can be placed in the
17887ddc9b1aSDarren Reed  * middle of the BEFOREs and AFTERs.  If this condition does not arise,
17897ddc9b1aSDarren Reed  * just use hook_insert_plain() to try and insert the hook somewhere that
17907ddc9b1aSDarren Reed  * is innocuous to existing efforts.
17917ddc9b1aSDarren Reed  */
17927ddc9b1aSDarren Reed static int
17937ddc9b1aSDarren Reed hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
17947ddc9b1aSDarren Reed {
17957ddc9b1aSDarren Reed 	hook_int_t *hi;
17967ddc9b1aSDarren Reed 	hook_t *nh;
17977ddc9b1aSDarren Reed 	hook_t *h;
17987ddc9b1aSDarren Reed 
17997ddc9b1aSDarren Reed 	nh = &new->hi_hook;
18007ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_NONE);
18017ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_LAST);
18027ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_FIRST);
18037ddc9b1aSDarren Reed 
18047ddc9b1aSDarren Reed 	/*
18057ddc9b1aSDarren Reed 	 * First, look through the list to see if there are any other
18067ddc9b1aSDarren Reed 	 * before's or after's that have a matching hint name.
18077ddc9b1aSDarren Reed 	 */
18087ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
18097ddc9b1aSDarren Reed 		h = &hi->hi_hook;
18107ddc9b1aSDarren Reed 		switch (h->h_hint) {
18117ddc9b1aSDarren Reed 		case HH_FIRST :
18127ddc9b1aSDarren Reed 		case HH_LAST :
18137ddc9b1aSDarren Reed 		case HH_NONE :
18147ddc9b1aSDarren Reed 			break;
18157ddc9b1aSDarren Reed 		case HH_BEFORE :
18167ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
18177ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
18187ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
18197ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
18207ddc9b1aSDarren Reed 				return (0);
18217ddc9b1aSDarren Reed 			}
18227ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
18237ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
18247ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
18257ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
18267ddc9b1aSDarren Reed 				return (0);
18277ddc9b1aSDarren Reed 			}
18287ddc9b1aSDarren Reed 			break;
18297ddc9b1aSDarren Reed 		case HH_AFTER :
18307ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
18317ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
18327ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
18337ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
18347ddc9b1aSDarren Reed 				return (0);
18357ddc9b1aSDarren Reed 			}
18367ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
18377ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
18387ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
18397ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
18407ddc9b1aSDarren Reed 				return (0);
18417ddc9b1aSDarren Reed 			}
18427ddc9b1aSDarren Reed 			break;
18437ddc9b1aSDarren Reed 		}
18447ddc9b1aSDarren Reed 	}
18457ddc9b1aSDarren Reed 
18467ddc9b1aSDarren Reed 	hook_insert_plain(head, new);
18477ddc9b1aSDarren Reed 
18487ddc9b1aSDarren Reed 	return (0);
18497ddc9b1aSDarren Reed }
1850381a2a9aSdr146992 
1851381a2a9aSdr146992 /*
1852381a2a9aSdr146992  * Function:	hook_unregister
1853381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
1854381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1855381a2a9aSdr146992  *		event(I) - event name string
1856381a2a9aSdr146992  *		h(I) - hook pointer
1857381a2a9aSdr146992  *
1858381a2a9aSdr146992  * Remove hook from hook list on specific family, event
1859381a2a9aSdr146992  */
1860381a2a9aSdr146992 int
1861381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
1862381a2a9aSdr146992 {
1863381a2a9aSdr146992 	hook_event_int_t *hei;
1864381a2a9aSdr146992 	hook_int_t *hi;
18657ddc9b1aSDarren Reed 	boolean_t free_event;
1866381a2a9aSdr146992 
1867381a2a9aSdr146992 	ASSERT(hfi != NULL);
1868381a2a9aSdr146992 	ASSERT(h != NULL);
1869381a2a9aSdr146992 
18707ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1871381a2a9aSdr146992 
1872381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
1873381a2a9aSdr146992 	if (hei == NULL) {
18747ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1875381a2a9aSdr146992 		return (ENXIO);
1876381a2a9aSdr146992 	}
1877381a2a9aSdr146992 
1878381a2a9aSdr146992 	/* Hold write lock for event */
1879381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
1880381a2a9aSdr146992 
1881381a2a9aSdr146992 	hi = hook_find(hei, h);
1882381a2a9aSdr146992 	if (hi == NULL) {
1883381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
18847ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1885381a2a9aSdr146992 		return (ENXIO);
1886381a2a9aSdr146992 	}
1887381a2a9aSdr146992 
18887ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
18897ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
18907ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
18917ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18927ddc9b1aSDarren Reed 		return (ENOENT);
18937ddc9b1aSDarren Reed 	}
18947ddc9b1aSDarren Reed 
1895381a2a9aSdr146992 	/* Remove from hook list */
1896381a2a9aSdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
18977ddc9b1aSDarren Reed 
18987ddc9b1aSDarren Reed 	free_event = B_FALSE;
1899381a2a9aSdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
1900381a2a9aSdr146992 		hei->hei_event->he_interested = B_FALSE;
19017ddc9b1aSDarren Reed 		/*
19027ddc9b1aSDarren Reed 		 * If the delete pending flag has been set and there are
19037ddc9b1aSDarren Reed 		 * no notifiers on the event (and we've removed the last
19047ddc9b1aSDarren Reed 		 * hook) then we need to free this event after we're done.
19057ddc9b1aSDarren Reed 		 */
19067ddc9b1aSDarren Reed 		if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
19077ddc9b1aSDarren Reed 			free_event = B_TRUE;
1908381a2a9aSdr146992 	}
19097ddc9b1aSDarren Reed 	hei->hei_kstats.hooks_removed.value.ui64++;
1910381a2a9aSdr146992 
1911381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
19127ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
19137ddc9b1aSDarren Reed 	/*
19147ddc9b1aSDarren Reed 	 * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
19157ddc9b1aSDarren Reed 	 * will not be free'd and thus the hook_family_int_t wil not
19167ddc9b1aSDarren Reed 	 * be free'd either.
19177ddc9b1aSDarren Reed 	 */
19187ddc9b1aSDarren Reed 	hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
19197ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
1920381a2a9aSdr146992 
19217ddc9b1aSDarren Reed 	hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
19227ddc9b1aSDarren Reed 
19237ddc9b1aSDarren Reed 	if (free_event)
19247ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
19257ddc9b1aSDarren Reed 
1926381a2a9aSdr146992 	return (0);
1927381a2a9aSdr146992 }
1928381a2a9aSdr146992 
19297ddc9b1aSDarren Reed /*
19307ddc9b1aSDarren Reed  * Function:	hook_find_byname
19317ddc9b1aSDarren Reed  * Returns:	internal hook pointer - NULL = Not match
19327ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
19337ddc9b1aSDarren Reed  *		name(I)- hook name
19347ddc9b1aSDarren Reed  *
19357ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is a hook present that
19367ddc9b1aSDarren Reed  * has a matching name to the one being looked for.
19377ddc9b1aSDarren Reed  */
19387ddc9b1aSDarren Reed static hook_int_t *
19397ddc9b1aSDarren Reed hook_find_byname(hook_int_head_t *head, char *name)
19407ddc9b1aSDarren Reed {
19417ddc9b1aSDarren Reed 	hook_int_t *hi;
19427ddc9b1aSDarren Reed 
19437ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
19447ddc9b1aSDarren Reed 		if (strcmp(hi->hi_hook.h_name, name) == 0)
19457ddc9b1aSDarren Reed 			return (hi);
19467ddc9b1aSDarren Reed 	}
19477ddc9b1aSDarren Reed 
19487ddc9b1aSDarren Reed 	return (NULL);
19497ddc9b1aSDarren Reed }
1950381a2a9aSdr146992 
1951381a2a9aSdr146992 /*
1952381a2a9aSdr146992  * Function:	hook_find
1953381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Not match
1954381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
1955381a2a9aSdr146992  *		h(I) - hook pointer
1956381a2a9aSdr146992  *
19577ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is already one that
19587ddc9b1aSDarren Reed  * matches the hook being passed in.  Currently the only criteria for a
19597ddc9b1aSDarren Reed  * successful search here is for the names to be the same.
1960381a2a9aSdr146992  */
1961381a2a9aSdr146992 static hook_int_t *
1962381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
1963381a2a9aSdr146992 {
1964381a2a9aSdr146992 
1965381a2a9aSdr146992 	ASSERT(hei != NULL);
1966381a2a9aSdr146992 	ASSERT(h != NULL);
1967381a2a9aSdr146992 
19687ddc9b1aSDarren Reed 	return (hook_find_byname(&hei->hei_head, h->h_name));
1969381a2a9aSdr146992 }
1970381a2a9aSdr146992 
1971381a2a9aSdr146992 /*
1972381a2a9aSdr146992  * Function:	hook_copy
1973381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Failed
1974381a2a9aSdr146992  * Parameters:	src(I) - hook pointer
1975381a2a9aSdr146992  *
1976381a2a9aSdr146992  * Allocate internal hook block and duplicate incoming hook.
1977381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
19787ddc9b1aSDarren Reed  * Because hook_copy() is responsible for the creation of the internal
19797ddc9b1aSDarren Reed  * hook structure that is used here, it takes on population the structure
19807ddc9b1aSDarren Reed  * with the kstat information.  Note that while the kstat bits are
19817ddc9b1aSDarren Reed  * seeded here, their installation of the kstats is handled elsewhere.
1982381a2a9aSdr146992  */
1983381a2a9aSdr146992 static hook_int_t *
1984381a2a9aSdr146992 hook_copy(hook_t *src)
1985381a2a9aSdr146992 {
1986381a2a9aSdr146992 	hook_int_t *new;
1987381a2a9aSdr146992 	hook_t *dst;
19887ddc9b1aSDarren Reed 	int len;
1989381a2a9aSdr146992 
1990381a2a9aSdr146992 	ASSERT(src != NULL);
1991381a2a9aSdr146992 	ASSERT(src->h_name != NULL);
1992381a2a9aSdr146992 
1993381a2a9aSdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1994381a2a9aSdr146992 
1995381a2a9aSdr146992 	/* Copy body */
1996381a2a9aSdr146992 	dst = &new->hi_hook;
1997381a2a9aSdr146992 	*dst = *src;
1998381a2a9aSdr146992 
1999381a2a9aSdr146992 	/* Copy name */
20007ddc9b1aSDarren Reed 	len = strlen(src->h_name);
20017ddc9b1aSDarren Reed 	dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
2002381a2a9aSdr146992 	(void) strcpy(dst->h_name, src->h_name);
2003381a2a9aSdr146992 
20047ddc9b1aSDarren Reed 	/*
20057ddc9b1aSDarren Reed 	 * This is initialised in this manner to make it safer to use the
20067ddc9b1aSDarren Reed 	 * same pointer in the kstats field.
20077ddc9b1aSDarren Reed 	 */
20087ddc9b1aSDarren Reed 	dst->h_hintvalue = (uintptr_t)"";
20097ddc9b1aSDarren Reed 
20107ddc9b1aSDarren Reed 	if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
20117ddc9b1aSDarren Reed 		len = strlen((char *)src->h_hintvalue);
20127ddc9b1aSDarren Reed 		if (len > 0) {
20137ddc9b1aSDarren Reed 			dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
20147ddc9b1aSDarren Reed 			    KM_SLEEP);
20157ddc9b1aSDarren Reed 			(void) strcpy((char *)dst->h_hintvalue,
20167ddc9b1aSDarren Reed 			    (char *)src->h_hintvalue);
20177ddc9b1aSDarren Reed 		}
20187ddc9b1aSDarren Reed 	}
20197ddc9b1aSDarren Reed 
2020381a2a9aSdr146992 	return (new);
2021381a2a9aSdr146992 }
2022381a2a9aSdr146992 
2023381a2a9aSdr146992 /*
20247ddc9b1aSDarren Reed  * Function:	hook_init_kstats
20257ddc9b1aSDarren Reed  * Returns:	None
20267ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns the event.
20277ddc9b1aSDarren Reed  *              hei(I) - pointer to the event that owns this hook
20287ddc9b1aSDarren Reed  *              hi(I)  - pointer to the hook for which we create kstats for
20297ddc9b1aSDarren Reed  *
20307ddc9b1aSDarren Reed  * Each hook that is registered with this framework has its own kstats
20317ddc9b1aSDarren Reed  * set up so that we can provide an easy way in which to observe the
20327ddc9b1aSDarren Reed  * look of hooks (using the kstat command.) The position is set to 0
20337ddc9b1aSDarren Reed  * here but is recalculated after we know the insertion has been a
20347ddc9b1aSDarren Reed  * success.
20357ddc9b1aSDarren Reed  */
20367ddc9b1aSDarren Reed static void
20377ddc9b1aSDarren Reed hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
20387ddc9b1aSDarren Reed {
20397ddc9b1aSDarren Reed 	hook_hook_kstat_t template = {
20407ddc9b1aSDarren Reed 		{ "version",			KSTAT_DATA_INT32 },
20417ddc9b1aSDarren Reed 		{ "flags",			KSTAT_DATA_UINT32 },
20427ddc9b1aSDarren Reed 		{ "hint",			KSTAT_DATA_INT32 },
20437ddc9b1aSDarren Reed 		{ "hint_value",			KSTAT_DATA_UINT64 },
20447ddc9b1aSDarren Reed 		{ "position",			KSTAT_DATA_INT32 },
20457ddc9b1aSDarren Reed 		{ "hook_hits",			KSTAT_DATA_UINT64 }
20467ddc9b1aSDarren Reed 	};
20477ddc9b1aSDarren Reed 	hook_stack_t *hks;
20487ddc9b1aSDarren Reed 	size_t kslen;
20497ddc9b1aSDarren Reed 	int position;
20507ddc9b1aSDarren Reed 	hook_int_t *h;
20517ddc9b1aSDarren Reed 
20527ddc9b1aSDarren Reed 	kslen = strlen(hfi->hfi_family.hf_name) +
20537ddc9b1aSDarren Reed 	    strlen(hei->hei_event->he_name) + 2;
20547ddc9b1aSDarren Reed 
20557ddc9b1aSDarren Reed 	hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
20567ddc9b1aSDarren Reed 	(void) snprintf(hi->hi_ksname, kslen, "%s/%s",
20577ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, hei->hei_event->he_name);
20587ddc9b1aSDarren Reed 
20597ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
20607ddc9b1aSDarren Reed 	hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
20617ddc9b1aSDarren Reed 	    hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
20627ddc9b1aSDarren Reed 	    sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
20637ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
20647ddc9b1aSDarren Reed 
20657ddc9b1aSDarren Reed 	/* Initialise the kstats for the structure */
20667ddc9b1aSDarren Reed 	bcopy(&template, &hi->hi_kstats, sizeof (template));
20677ddc9b1aSDarren Reed 	hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
20687ddc9b1aSDarren Reed 	hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
20697ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
20707ddc9b1aSDarren Reed 	hi->hi_kstats.hook_position.value.i32 = 0;
20717ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hits.value.ui64 = 0;
20727ddc9b1aSDarren Reed 
20737ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
20747ddc9b1aSDarren Reed 	case HH_BEFORE :
20757ddc9b1aSDarren Reed 	case HH_AFTER :
20767ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
20777ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.value.ui64 =
20787ddc9b1aSDarren Reed 		    hi->hi_hook.h_hintvalue;
20797ddc9b1aSDarren Reed 		break;
20807ddc9b1aSDarren Reed 	default :
20817ddc9b1aSDarren Reed 		break;
20827ddc9b1aSDarren Reed 	}
20837ddc9b1aSDarren Reed 
20847ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL) {
20857ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
20867ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_private =
20877ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
20887ddc9b1aSDarren Reed 
20897ddc9b1aSDarren Reed 		kstat_install(hi->hi_kstatp);
20907ddc9b1aSDarren Reed 	}
20917ddc9b1aSDarren Reed 
20927ddc9b1aSDarren Reed 	position = 1;
20937ddc9b1aSDarren Reed 	TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
20947ddc9b1aSDarren Reed 		h->hi_kstats.hook_position.value.ui32 = position++;
20957ddc9b1aSDarren Reed 	}
20967ddc9b1aSDarren Reed }
20977ddc9b1aSDarren Reed 
20987ddc9b1aSDarren Reed /*
20997ddc9b1aSDarren Reed  * Function:	hook_int_free
2100381a2a9aSdr146992  * Returns:	None
2101381a2a9aSdr146992  * Parameters:	hi(I) - internal hook pointer
2102381a2a9aSdr146992  *
2103381a2a9aSdr146992  * Free alloc memory for hook
2104381a2a9aSdr146992  */
2105381a2a9aSdr146992 static void
21067ddc9b1aSDarren Reed hook_int_free(hook_int_t *hi, netstackid_t stackid)
2107381a2a9aSdr146992 {
21087ddc9b1aSDarren Reed 	int len;
21097ddc9b1aSDarren Reed 
2110381a2a9aSdr146992 	ASSERT(hi != NULL);
2111381a2a9aSdr146992 
2112381a2a9aSdr146992 	/* Free name space */
2113381a2a9aSdr146992 	if (hi->hi_hook.h_name != NULL) {
2114381a2a9aSdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
2115381a2a9aSdr146992 	}
21167ddc9b1aSDarren Reed 	if (hi->hi_ksname != NULL) {
21177ddc9b1aSDarren Reed 		kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
21187ddc9b1aSDarren Reed 	}
21197ddc9b1aSDarren Reed 
21207ddc9b1aSDarren Reed 	/* Free the name used with the before/after hints. */
21217ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
21227ddc9b1aSDarren Reed 	case HH_BEFORE :
21237ddc9b1aSDarren Reed 	case HH_AFTER :
21247ddc9b1aSDarren Reed 		len = strlen((char *)hi->hi_hook.h_hintvalue);
21257ddc9b1aSDarren Reed 		if (len > 0)
21267ddc9b1aSDarren Reed 			kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
21277ddc9b1aSDarren Reed 		break;
21287ddc9b1aSDarren Reed 	default :
21297ddc9b1aSDarren Reed 		break;
21307ddc9b1aSDarren Reed 	}
21317ddc9b1aSDarren Reed 
21327ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL)
21337ddc9b1aSDarren Reed 		kstat_delete_netstack(hi->hi_kstatp, stackid);
2134381a2a9aSdr146992 
2135381a2a9aSdr146992 	/* Free container */
2136381a2a9aSdr146992 	kmem_free(hi, sizeof (*hi));
2137381a2a9aSdr146992 }
21387ddc9b1aSDarren Reed 
21397ddc9b1aSDarren Reed /*
21407ddc9b1aSDarren Reed  * Function:	hook_alloc
21417ddc9b1aSDarren Reed  * Returns:	hook_t * - pointer to new hook structure
21427ddc9b1aSDarren Reed  * Parameters:	version(I) - version number of the API when compiled
21437ddc9b1aSDarren Reed  *
21447ddc9b1aSDarren Reed  * This function serves as the interface for consumers to obtain a hook_t
21457ddc9b1aSDarren Reed  * structure.  At this point in time, there is only a single "version" of
21467ddc9b1aSDarren Reed  * it, leading to a straight forward function.  In a perfect world the
21477ddc9b1aSDarren Reed  * h_vesion would be a protected data structure member, but C isn't that
21487ddc9b1aSDarren Reed  * advanced...
21497ddc9b1aSDarren Reed  */
21507ddc9b1aSDarren Reed hook_t *
21517ddc9b1aSDarren Reed hook_alloc(const int h_version)
21527ddc9b1aSDarren Reed {
21537ddc9b1aSDarren Reed 	hook_t *h;
21547ddc9b1aSDarren Reed 
21557ddc9b1aSDarren Reed 	h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
21567ddc9b1aSDarren Reed 	h->h_version = h_version;
21577ddc9b1aSDarren Reed 	return (h);
21587ddc9b1aSDarren Reed }
21597ddc9b1aSDarren Reed 
21607ddc9b1aSDarren Reed /*
21617ddc9b1aSDarren Reed  * Function:	hook_free
21627ddc9b1aSDarren Reed  * Returns:	None
21637ddc9b1aSDarren Reed  * Parameters:	h(I) - external hook pointer
21647ddc9b1aSDarren Reed  *
21657ddc9b1aSDarren Reed  * This function only free's memory allocated with hook_alloc(), so that if
21667ddc9b1aSDarren Reed  * (for example) kernel memory was allocated for h_name, this needs to be
21677ddc9b1aSDarren Reed  * free'd before calling hook_free().
21687ddc9b1aSDarren Reed  */
21697ddc9b1aSDarren Reed void
21707ddc9b1aSDarren Reed hook_free(hook_t *h)
21717ddc9b1aSDarren Reed {
21727ddc9b1aSDarren Reed 	kmem_free(h, sizeof (*h));
21737ddc9b1aSDarren Reed }
21747ddc9b1aSDarren Reed 
21757ddc9b1aSDarren Reed /*
21767ddc9b1aSDarren Reed  * Function:	hook_notify_register
21777ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
21787ddc9b1aSDarren Reed  * Parameters:	lock(I)     - netstack identifier
21797ddc9b1aSDarren Reed  *              head(I)     - top of the list of callbacks
21807ddc9b1aSDarren Reed  *              callback(I) - function to be called
21817ddc9b1aSDarren Reed  *              arg(I)      - arg to pass back to the function
21827ddc9b1aSDarren Reed  *
21837ddc9b1aSDarren Reed  * This function implements the modification of the list of callbacks
21847ddc9b1aSDarren Reed  * that are registered when someone wants to be advised of a change
21857ddc9b1aSDarren Reed  * that has happened.
21867ddc9b1aSDarren Reed  */
21877ddc9b1aSDarren Reed static int
21887ddc9b1aSDarren Reed hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
21897ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
21907ddc9b1aSDarren Reed {
21917ddc9b1aSDarren Reed 	hook_notify_t *hn;
21927ddc9b1aSDarren Reed 
21937ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(lock);
21947ddc9b1aSDarren Reed 
21957ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
21967ddc9b1aSDarren Reed 		if (hn->hn_func == callback) {
21977ddc9b1aSDarren Reed 			CVW_EXIT_WRITE(lock);
21987ddc9b1aSDarren Reed 			return (EEXIST);
21997ddc9b1aSDarren Reed 		}
22007ddc9b1aSDarren Reed 	}
22017ddc9b1aSDarren Reed 
22027ddc9b1aSDarren Reed 	hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
22037ddc9b1aSDarren Reed 	hn->hn_func = callback;
22047ddc9b1aSDarren Reed 	hn->hn_arg = arg;
22057ddc9b1aSDarren Reed 	TAILQ_INSERT_TAIL(head, hn, hn_entry);
22067ddc9b1aSDarren Reed 
22077ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(lock);
22087ddc9b1aSDarren Reed 
22097ddc9b1aSDarren Reed 	return (0);
22107ddc9b1aSDarren Reed }
22117ddc9b1aSDarren Reed 
22127ddc9b1aSDarren Reed /*
22137ddc9b1aSDarren Reed  * Function:	hook_stack_notify_register
22147ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
22157ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
22167ddc9b1aSDarren Reed  *              callback(I) - function to be called
22177ddc9b1aSDarren Reed  *
22187ddc9b1aSDarren Reed  */
22197ddc9b1aSDarren Reed static int
22207ddc9b1aSDarren Reed hook_notify_unregister(cvwaitlock_t *lock, hook_notify_head_t *head,
22217ddc9b1aSDarren Reed     hook_notify_fn_t callback)
22227ddc9b1aSDarren Reed {
22237ddc9b1aSDarren Reed 	hook_notify_t *hn;
22247ddc9b1aSDarren Reed 
22257ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(lock);
22267ddc9b1aSDarren Reed 
22277ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
22287ddc9b1aSDarren Reed 		if (hn->hn_func == callback)
22297ddc9b1aSDarren Reed 			break;
22307ddc9b1aSDarren Reed 	}
22317ddc9b1aSDarren Reed 	if (hn == NULL) {
22327ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(lock);
22337ddc9b1aSDarren Reed 		return (ESRCH);
22347ddc9b1aSDarren Reed 	}
22357ddc9b1aSDarren Reed 
22367ddc9b1aSDarren Reed 	TAILQ_REMOVE(head, hn, hn_entry);
22377ddc9b1aSDarren Reed 
22387ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(lock);
22397ddc9b1aSDarren Reed 
22407ddc9b1aSDarren Reed 	kmem_free(hn, sizeof (*hn));
22417ddc9b1aSDarren Reed 
22427ddc9b1aSDarren Reed 	return (0);
22437ddc9b1aSDarren Reed }
22447ddc9b1aSDarren Reed 
22457ddc9b1aSDarren Reed /*
22467ddc9b1aSDarren Reed  * Function:	hook_notify_run
22477ddc9b1aSDarren Reed  * Returns:	None
22487ddc9b1aSDarren Reed  * Parameters:	head(I)   - top of the list of callbacks
22497ddc9b1aSDarren Reed  *              family(I) - name of the hook family that owns the event
22507ddc9b1aSDarren Reed  *              event(I)  - name of the event being changed
22517ddc9b1aSDarren Reed  *              name(I)   - name of the object causing change
22527ddc9b1aSDarren Reed  *              cmd(I)    - either HN_UNREGISTER or HN_REGISTER
22537ddc9b1aSDarren Reed  *
22547ddc9b1aSDarren Reed  * This function walks through the list of registered callbacks and
22557ddc9b1aSDarren Reed  * executes each one, passing back the arg supplied when registered
22567ddc9b1aSDarren Reed  * and the name of the family (that owns the event), event (the thing
22577ddc9b1aSDarren Reed  * to which we're making a change) and finally a name that describes
22587ddc9b1aSDarren Reed  * what is being added or removed, as indicated by cmd.
22597ddc9b1aSDarren Reed  *
22607ddc9b1aSDarren Reed  * This function does not acquire or release any lock as it is required
22617ddc9b1aSDarren Reed  * that code calling it do so before hand.  The use of hook_notify_head_t
22627ddc9b1aSDarren Reed  * is protected by the use of flagwait_t in the structures that own this
22637ddc9b1aSDarren Reed  * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
22647ddc9b1aSDarren Reed  */
22657ddc9b1aSDarren Reed static void
22667ddc9b1aSDarren Reed hook_notify_run(hook_notify_head_t *head, char *family, char *event,
22677ddc9b1aSDarren Reed     char *name, hook_notify_cmd_t cmd)
22687ddc9b1aSDarren Reed {
22697ddc9b1aSDarren Reed 	hook_notify_t *hn;
22707ddc9b1aSDarren Reed 
22717ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
22727ddc9b1aSDarren Reed 		(*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
22737ddc9b1aSDarren Reed 	}
22747ddc9b1aSDarren Reed }
2275