xref: /titanic_50/usr/src/uts/common/io/hook.c (revision 4a9b8375b74044a5b0fc0191eb42b835e72b65c0)
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
77*4a9b8375SDarren Reed  * if either structure is still "busy". If so then a boolean flag (FWF_DESTROY)
78*4a9b8375SDarren Reed  * is set to say that the structure is condemned. The presence of this flag
79*4a9b8375SDarren Reed  * being set must be checked for in _add()/_register()/ functions and a
80*4a9b8375SDarren Reed  * failure returned if it is set. It is ignored by the _find() functions
81*4a9b8375SDarren Reed  * because they're used by _remove()/_unregister().
82*4a9b8375SDarren Reed  * While setting the condemned flag when trying to delete a structure would
83*4a9b8375SDarren Reed  * normally be keyed from the presence of a reference count being greater
84*4a9b8375SDarren Reed  * than 1, in this implementation there are no reference counts required:
85*4a9b8375SDarren Reed  * instead the presence of objects on linked lists is taken to mean
86*4a9b8375SDarren Reed  * something is still "busy."
877ddc9b1aSDarren Reed  *
887ddc9b1aSDarren Reed  * ONLY the caller that adds the family and the events ever has a direct
897ddc9b1aSDarren Reed  * reference to the internal structures and thus ONLY it should be doing
907ddc9b1aSDarren Reed  * the removal of either the event or family.  In practise, what this means
917ddc9b1aSDarren Reed  * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
927ddc9b1aSDarren Reed  * by net_event_register() (these interface to hook_family_add() and
937ddc9b1aSDarren Reed  * hook_event_add(), respectively) that are made when we create an instance
947ddc9b1aSDarren Reed  * of IP and when the IP instance is shutdown/destroyed, it calls
957ddc9b1aSDarren Reed  * net_event_unregister() and net_protocol_unregister(), which in turn call
967ddc9b1aSDarren Reed  * hook_event_remove() and hook_family_remove() respectively. Nobody else
977ddc9b1aSDarren Reed  * is entitled to call the _unregister() functions.  It is imperative that
987ddc9b1aSDarren Reed  * there be only one _remove() call for every _add() call.
997ddc9b1aSDarren Reed  *
1007ddc9b1aSDarren Reed  * It is possible that code which is interfacing with this hook framework
1017ddc9b1aSDarren Reed  * won't do all the cleaning up that it needs to at the right time. While
1027ddc9b1aSDarren Reed  * we can't prevent programmers from creating memory leaks, we can synchronise
1037ddc9b1aSDarren Reed  * when we clean up data structures to prevent code accessing free'd memory.
1047ddc9b1aSDarren Reed  *
1057ddc9b1aSDarren Reed  * A simple diagram showing the ownership is as follows:
1067ddc9b1aSDarren Reed  *
1077ddc9b1aSDarren Reed  *  Owned       +--------------+
1087ddc9b1aSDarren Reed  *   by         | hook_stack_t |
1097ddc9b1aSDarren Reed  *   the        +--------------+
1107ddc9b1aSDarren Reed  *  Instance      |
1117ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1127ddc9b1aSDarren Reed  *                V
1137ddc9b1aSDarren Reed  *  Owned       +-------------------+     +-------------------+
1147ddc9b1aSDarren Reed  *              | hook_family_int_t |---->| hook_family_int_t |
1157ddc9b1aSDarren Reed  *   by         +-------------------+     +-------------------+
1167ddc9b1aSDarren Reed  *                | \+---------------+        \+---------------+
1177ddc9b1aSDarren Reed  *  network       |  | hook_family_t |         | hook_family_t |
1187ddc9b1aSDarren Reed  *                V  +---------------+         +---------------+
1197ddc9b1aSDarren Reed  *  protocol   +------------------+     +------------------+
1207ddc9b1aSDarren Reed  *             | hook_event_int_t |---->| hook_event_int_t |
1217ddc9b1aSDarren Reed  * (ipv4,ipv6) +------------------+     +------------------+
1227ddc9b1aSDarren Reed  *                | \+--------------+        \+--------------+
1237ddc9b1aSDarren Reed  *                |  | hook_event_t |         | hook_event_t |
1247ddc9b1aSDarren Reed  *                |  +--------------+         +--------------+
1257ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1267ddc9b1aSDarren Reed  *                V
1277ddc9b1aSDarren Reed  *  Owned      +------------+
1287ddc9b1aSDarren Reed  *             | hook_int_t |
1297ddc9b1aSDarren Reed  *   by        +------------+
1307ddc9b1aSDarren Reed  *                  \+--------+
1317ddc9b1aSDarren Reed  * the consumer      | hook_t |
1327ddc9b1aSDarren Reed  *                   +--------+
1337ddc9b1aSDarren Reed  *
1347ddc9b1aSDarren Reed  * The consumers, such as IPFilter, do not have any pointers or hold any
1357ddc9b1aSDarren Reed  * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
1367ddc9b1aSDarren Reed  * a hook on an event through net_hook_register(), an implicit reference
1377ddc9b1aSDarren Reed  * to the hook_event_int_t is returned with a successful call.  Additionally,
1387ddc9b1aSDarren Reed  * IPFilter does not see the hook_family_int_t or hook_family_t directly.
1397ddc9b1aSDarren Reed  * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
1407ddc9b1aSDarren Reed  * contains a pointer to hook_family_int_t.  The structure behind the
1417ddc9b1aSDarren Reed  * net_handle_t (struct net_data) *is* reference counted and managed
1427ddc9b1aSDarren Reed  * appropriately.
1437ddc9b1aSDarren Reed  *
1447ddc9b1aSDarren Reed  * A more detailed picture that describes how the family/event structures
1457ddc9b1aSDarren Reed  * are linked together can be found in <sys/hook_impl.h>
146*4a9b8375SDarren Reed  *
147*4a9b8375SDarren Reed  * Notification callbacks.
148*4a9b8375SDarren Reed  * =======================
149*4a9b8375SDarren Reed  * For each of the hook stack, hook family and hook event, it is possible
150*4a9b8375SDarren Reed  * to request notificatin of change to them. Why?
151*4a9b8375SDarren Reed  * First, lets equate the hook stack to an IP instance, a hook family to
152*4a9b8375SDarren Reed  * a network protocol and a hook event to IP packets on the input path.
153*4a9b8375SDarren Reed  * If a kernel module wants to apply security from the very start of
154*4a9b8375SDarren Reed  * things, it needs to know as soon as a new instance of networking
155*4a9b8375SDarren Reed  * is initiated. Whilst for the global zone, it is taken for granted that
156*4a9b8375SDarren Reed  * this instance will always exist before any interaction takes place,
157*4a9b8375SDarren Reed  * that is not true for zones running with an exclusive networking instance.
158*4a9b8375SDarren Reed  * Thus when a local zone is started and a new instance is created to support
159*4a9b8375SDarren Reed  * that, parties that wish to monitor it and apply a security policy from
160*4a9b8375SDarren Reed  * the onset need to be informed as early as possible - quite probably
161*4a9b8375SDarren Reed  * before any networking is started by the zone's boot scripts.
162*4a9b8375SDarren Reed  * Inside each instance, it is possible to have a number of network protocols
163*4a9b8375SDarren Reed  * (hook families) in operation. Inside the context of the global zone,
164*4a9b8375SDarren Reed  * it is possible to have code run before the kernel module providing the
165*4a9b8375SDarren Reed  * IP networking is loaded. From here, to apply the appropriate security,
166*4a9b8375SDarren Reed  * it is necessary to become informed of when IP is being configured into
167*4a9b8375SDarren Reed  * the zone and this is done by registering a notification callback with
168*4a9b8375SDarren Reed  * the hook stack for changes to it. The next step is to know when packets
169*4a9b8375SDarren Reed  * can be received through the physical_in, etc, events. This is achieved
170*4a9b8375SDarren Reed  * by registering a callback with the appropriate network protocol (or in
171*4a9b8375SDarren Reed  * this file, the correct hook family.) Thus when IP finally attaches a
172*4a9b8375SDarren Reed  * physical_in event to inet, the module looking to enforce a security
173*4a9b8375SDarren Reed  * policy can become aware of it being present. Of course there's no
174*4a9b8375SDarren Reed  * requirement for such a module to be present before all of the above
175*4a9b8375SDarren Reed  * happens and in such a case, it is reasonable for the same module to
176*4a9b8375SDarren Reed  * work after everything has been put in place. For this reason, when
177*4a9b8375SDarren Reed  * a notification callback is added, a series of fake callback events
178*4a9b8375SDarren Reed  * is generated to simulate the arrival of those entities. There is one
179*4a9b8375SDarren Reed  * final series of callbacks that can be registered - those to monitor
180*4a9b8375SDarren Reed  * actual hooks that are added or removed from an event. In practice,
181*4a9b8375SDarren Reed  * this is useful when there are multiple kernel modules participating
182*4a9b8375SDarren Reed  * in the processing of packets and there are behaviour dependencies
183*4a9b8375SDarren Reed  * involved, such that one kernel module might only register its hook
184*4a9b8375SDarren Reed  * if another is already present and also might want to remove its hook
185*4a9b8375SDarren Reed  * when the other disappears.
186*4a9b8375SDarren Reed  *
187*4a9b8375SDarren Reed  * If you know a kernel module will not be loaded before the infrastructure
188*4a9b8375SDarren Reed  * used in this file is present then it is not necessary to use this
189*4a9b8375SDarren Reed  * notification callback mechanism.
1907ddc9b1aSDarren Reed  */
1917ddc9b1aSDarren Reed 
1927ddc9b1aSDarren Reed /*
1937ddc9b1aSDarren Reed  * Locking
1947ddc9b1aSDarren Reed  * =======
1957ddc9b1aSDarren Reed  * The use of CVW_* macros to do locking is driven by the need to allow
1967ddc9b1aSDarren Reed  * recursive locking with read locks when we're processing packets. This
1977ddc9b1aSDarren Reed  * is necessary because various netinfo functions need to hold read locks,
1987ddc9b1aSDarren Reed  * by design, as they can be called in or out of packet context.
1997ddc9b1aSDarren Reed  */
2007ddc9b1aSDarren Reed /*
201381a2a9aSdr146992  * Hook internal functions
202381a2a9aSdr146992  */
203381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src);
204f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
205f4b3ec61Sdh155122     hook_stack_t *hks);
206381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
207381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
2087ddc9b1aSDarren Reed static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
209381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
210f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
2117ddc9b1aSDarren Reed static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
212381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
2137ddc9b1aSDarren Reed static void hook_int_free(hook_int_t *hi, netstackid_t);
214381a2a9aSdr146992 static void hook_init(void);
215f4b3ec61Sdh155122 static void hook_fini(void);
216f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
217f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
2187ddc9b1aSDarren Reed static void hook_stack_shutdown(netstackid_t stackid, void *arg);
2197ddc9b1aSDarren Reed static int hook_insert(hook_int_head_t *head, hook_int_t *new);
2207ddc9b1aSDarren Reed static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
2217ddc9b1aSDarren Reed static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
2227ddc9b1aSDarren Reed static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
2237ddc9b1aSDarren Reed static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
2247ddc9b1aSDarren Reed static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
2257ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
2267ddc9b1aSDarren Reed static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
2277ddc9b1aSDarren Reed     hook_int_t *hi);
228*4a9b8375SDarren Reed static int hook_notify_register(hook_notify_head_t *head,
2297ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg);
230*4a9b8375SDarren Reed static int hook_notify_unregister(hook_notify_head_t *head,
231*4a9b8375SDarren Reed     hook_notify_fn_t callback, void **);
2327ddc9b1aSDarren Reed static void hook_notify_run(hook_notify_head_t *head, char *family,
2337ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
2347ddc9b1aSDarren Reed static void hook_stack_notify_run(hook_stack_t *hks, char *name,
2357ddc9b1aSDarren Reed     hook_notify_cmd_t cmd);
2367ddc9b1aSDarren Reed static void hook_stack_remove(hook_stack_t *hks);
2377ddc9b1aSDarren Reed 
2387ddc9b1aSDarren Reed /*
2397ddc9b1aSDarren Reed  * A list of the hook stacks is kept here because we need to enable
2407ddc9b1aSDarren Reed  * net_instance_notify_register() to be called during the creation
2417ddc9b1aSDarren Reed  * of a new instance. Previously hook_stack_get() would just use
2427ddc9b1aSDarren Reed  * the netstack functions for this work but they will return NULL
2437ddc9b1aSDarren Reed  * until the zone has been fully initialised.
2447ddc9b1aSDarren Reed  */
2457ddc9b1aSDarren Reed static hook_stack_head_t hook_stacks;
2467ddc9b1aSDarren Reed static kmutex_t hook_stack_lock;
247381a2a9aSdr146992 
248381a2a9aSdr146992 /*
249381a2a9aSdr146992  * Module entry points.
250381a2a9aSdr146992  */
251381a2a9aSdr146992 int
252381a2a9aSdr146992 _init(void)
253381a2a9aSdr146992 {
254f4b3ec61Sdh155122 	int error;
255f4b3ec61Sdh155122 
256381a2a9aSdr146992 	hook_init();
257f4b3ec61Sdh155122 	error = mod_install(&modlinkage);
258f4b3ec61Sdh155122 	if (error != 0)
259f4b3ec61Sdh155122 		hook_fini();
260f4b3ec61Sdh155122 
261f4b3ec61Sdh155122 	return (error);
262381a2a9aSdr146992 }
263381a2a9aSdr146992 
264381a2a9aSdr146992 int
265381a2a9aSdr146992 _fini(void)
266381a2a9aSdr146992 {
267f4b3ec61Sdh155122 	int error;
268f4b3ec61Sdh155122 
269f4b3ec61Sdh155122 	error = mod_remove(&modlinkage);
270f4b3ec61Sdh155122 	if (error == 0)
271f4b3ec61Sdh155122 		hook_fini();
272f4b3ec61Sdh155122 
273f4b3ec61Sdh155122 	return (error);
274381a2a9aSdr146992 }
275381a2a9aSdr146992 
276381a2a9aSdr146992 int
277381a2a9aSdr146992 _info(struct modinfo *modinfop)
278381a2a9aSdr146992 {
279381a2a9aSdr146992 	return (mod_info(&modlinkage, modinfop));
280381a2a9aSdr146992 }
281381a2a9aSdr146992 
282381a2a9aSdr146992 /*
283381a2a9aSdr146992  * Function:	hook_init
284381a2a9aSdr146992  * Returns:	None
285381a2a9aSdr146992  * Parameters:	None
286381a2a9aSdr146992  *
287381a2a9aSdr146992  * Initialize hooks
288381a2a9aSdr146992  */
289381a2a9aSdr146992 static void
290381a2a9aSdr146992 hook_init(void)
291381a2a9aSdr146992 {
2927ddc9b1aSDarren Reed 	mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
2937ddc9b1aSDarren Reed 	SLIST_INIT(&hook_stacks);
2947ddc9b1aSDarren Reed 
295f4b3ec61Sdh155122 	/*
296f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
297f4b3ec61Sdh155122 	 * destroyed in the kernel.
298f4b3ec61Sdh155122 	 */
2997ddc9b1aSDarren Reed 	netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
300f4b3ec61Sdh155122 	    hook_stack_fini);
301381a2a9aSdr146992 }
302381a2a9aSdr146992 
303f4b3ec61Sdh155122 /*
304f4b3ec61Sdh155122  * Function:	hook_fini
305f4b3ec61Sdh155122  * Returns:	None
306f4b3ec61Sdh155122  * Parameters:	None
307f4b3ec61Sdh155122  *
308f4b3ec61Sdh155122  * Deinitialize hooks
309f4b3ec61Sdh155122  */
310f4b3ec61Sdh155122 static void
311f4b3ec61Sdh155122 hook_fini(void)
312f4b3ec61Sdh155122 {
313f4b3ec61Sdh155122 	netstack_unregister(NS_HOOK);
3147ddc9b1aSDarren Reed 
3157ddc9b1aSDarren Reed 	mutex_destroy(&hook_stack_lock);
3167ddc9b1aSDarren Reed 	ASSERT(SLIST_EMPTY(&hook_stacks));
3177ddc9b1aSDarren Reed }
3187ddc9b1aSDarren Reed 
3197ddc9b1aSDarren Reed /*
3207ddc9b1aSDarren Reed  * Function:	hook_wait_setflag
3217ddc9b1aSDarren Reed  * Returns:     -1 = setting flag is disallowed, 0 = flag set and did
3227ddc9b1aSDarren Reed  *              not have to wait (ie no lock droped), 1 = flag set but
3237ddc9b1aSDarren Reed  *              it was necessary to drop locks to set it.
3247ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
3257ddc9b1aSDarren Reed  *              busyset(I) - set of flags that we don't want set while
3267ddc9b1aSDarren Reed  *                           we are active.
3277ddc9b1aSDarren Reed  *              wanted(I)  - flag associated with newflag to indicate
3287ddc9b1aSDarren Reed  *                           what we want to do.
3297ddc9b1aSDarren Reed  *              newflag(I) - the new ACTIVE flag we want to set that
3307ddc9b1aSDarren Reed  *                           indicates what we are doing.
3317ddc9b1aSDarren Reed  *
3327ddc9b1aSDarren Reed  * The set of functions hook_wait_* implement an API that builds on top of
3337ddc9b1aSDarren Reed  * the kcondvar_t to provide controlled execution through a critical region.
3347ddc9b1aSDarren Reed  * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
3357ddc9b1aSDarren Reed  * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
3367ddc9b1aSDarren Reed  * The combination of flags is required as when this function exits to do
3377ddc9b1aSDarren Reed  * the task, the structure is then free for another caller to use and
338*4a9b8375SDarren Reed  * to indicate that it wants to do work.  The flags used when a caller wants
339*4a9b8375SDarren Reed  * to destroy an object take precedence over those that are used for making
340*4a9b8375SDarren Reed  * changes to it (add/remove.) In this case, we don't try to secure the
341*4a9b8375SDarren Reed  * ability to run and return with an error.
342*4a9b8375SDarren Reed  *
343*4a9b8375SDarren Reed  * "wantedset" is used here to determine who has the right to clear the
344*4a9b8375SDarren Reed  * wanted but from the fw_flags set: only he that sets the flag has the
345*4a9b8375SDarren Reed  * right to clear it at the bottom of the loop, even if someone else
346*4a9b8375SDarren Reed  * wants to set it.
3477ddc9b1aSDarren Reed  *
3487ddc9b1aSDarren Reed  * wanted - the FWF_*_WANTED flag that describes the action being requested
3497ddc9b1aSDarren Reed  * busyset- the set of FWF_* flags we don't want set when we run
3507ddc9b1aSDarren Reed  * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
3517ddc9b1aSDarren Reed  */
3527ddc9b1aSDarren Reed int
3537ddc9b1aSDarren Reed hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
3547ddc9b1aSDarren Reed     fwflag_t newflag)
3557ddc9b1aSDarren Reed {
356*4a9b8375SDarren Reed 	boolean_t wantedset;
3577ddc9b1aSDarren Reed 	int waited = 0;
3587ddc9b1aSDarren Reed 
3597ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
3607ddc9b1aSDarren Reed 	if (waiter->fw_flags & FWF_DESTROY) {
361*4a9b8375SDarren Reed 		cv_signal(&waiter->fw_cv);
3627ddc9b1aSDarren Reed 		mutex_exit(&waiter->fw_lock);
3637ddc9b1aSDarren Reed 		return (-1);
3647ddc9b1aSDarren Reed 	}
3657ddc9b1aSDarren Reed 	while (waiter->fw_flags & busyset) {
366*4a9b8375SDarren Reed 		wantedset = ((waiter->fw_flags & wanted) == wanted);
367*4a9b8375SDarren Reed 		if (!wantedset)
3687ddc9b1aSDarren Reed 			waiter->fw_flags |= wanted;
3697ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
3707ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
371*4a9b8375SDarren Reed 		/*
372*4a9b8375SDarren Reed 		 * This lock needs to be dropped here to preserve the order
373*4a9b8375SDarren Reed 		 * of acquisition that is fw_owner followed by fw_lock, else
374*4a9b8375SDarren Reed 		 * we can deadlock.
375*4a9b8375SDarren Reed 		 */
376*4a9b8375SDarren Reed 		mutex_exit(&waiter->fw_lock);
3777ddc9b1aSDarren Reed 		waited = 1;
3787ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
379*4a9b8375SDarren Reed 		mutex_enter(&waiter->fw_lock);
380*4a9b8375SDarren Reed 		if (!wantedset)
3817ddc9b1aSDarren Reed 			waiter->fw_flags &= ~wanted;
382*4a9b8375SDarren Reed 		if (waiter->fw_flags & FWF_DESTROY) {
383*4a9b8375SDarren Reed 			cv_signal(&waiter->fw_cv);
3847ddc9b1aSDarren Reed 			mutex_exit(&waiter->fw_lock);
3857ddc9b1aSDarren Reed 			return (-1);
3867ddc9b1aSDarren Reed 		}
3877ddc9b1aSDarren Reed 	}
3887ddc9b1aSDarren Reed 	waiter->fw_flags &= ~wanted;
389*4a9b8375SDarren Reed 	ASSERT((waiter->fw_flags & wanted) == 0);
390*4a9b8375SDarren Reed 	ASSERT((waiter->fw_flags & newflag) == 0);
3917ddc9b1aSDarren Reed 	waiter->fw_flags |= newflag;
3927ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
3937ddc9b1aSDarren Reed 	return (waited);
3947ddc9b1aSDarren Reed }
3957ddc9b1aSDarren Reed 
3967ddc9b1aSDarren Reed /*
3977ddc9b1aSDarren Reed  * Function:	hook_wait_unsetflag
3987ddc9b1aSDarren Reed  * Returns:     None
3997ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
4007ddc9b1aSDarren Reed  *              oldflag(I) - flag to reset
4017ddc9b1aSDarren Reed  *
4027ddc9b1aSDarren Reed  * Turn off the bit that we had set to run and let others know that
4037ddc9b1aSDarren Reed  * they should now check to see if they can run.
4047ddc9b1aSDarren Reed  */
4057ddc9b1aSDarren Reed void
406*4a9b8375SDarren Reed hook_wait_unsetflag(flagwait_t *waiter, fwflag_t oldflag)
4077ddc9b1aSDarren Reed {
4087ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
4097ddc9b1aSDarren Reed 	waiter->fw_flags &= ~oldflag;
4107ddc9b1aSDarren Reed 	cv_signal(&waiter->fw_cv);
4117ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
4127ddc9b1aSDarren Reed }
4137ddc9b1aSDarren Reed 
4147ddc9b1aSDarren Reed /*
4157ddc9b1aSDarren Reed  * Function:	hook_wait_destroy
4167ddc9b1aSDarren Reed  * Returns:     None
4177ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
4187ddc9b1aSDarren Reed  *
4197ddc9b1aSDarren Reed  * Since outer locking (on fw_owner) should ensure that only one function
4207ddc9b1aSDarren Reed  * at a time gets to call hook_wait_destroy() on a given object, there is
4217ddc9b1aSDarren Reed  * no need to guard against setting FWF_DESTROY_WANTED already being set.
4227ddc9b1aSDarren Reed  * It is, however, necessary to wait for all activity on the owning
4237ddc9b1aSDarren Reed  * structure to cease.
4247ddc9b1aSDarren Reed  */
425*4a9b8375SDarren Reed int
4267ddc9b1aSDarren Reed hook_wait_destroy(flagwait_t *waiter)
4277ddc9b1aSDarren Reed {
428*4a9b8375SDarren Reed 	boolean_t wanted;
429*4a9b8375SDarren Reed 
4307ddc9b1aSDarren Reed 	ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
431*4a9b8375SDarren Reed 	mutex_enter(&waiter->fw_lock);
432*4a9b8375SDarren Reed 	if (waiter->fw_flags & FWF_DESTROY_WANTED) {
433*4a9b8375SDarren Reed 		cv_signal(&waiter->fw_cv);
434*4a9b8375SDarren Reed 		mutex_exit(&waiter->fw_lock);
435*4a9b8375SDarren Reed 		return (EINPROGRESS);
436*4a9b8375SDarren Reed 	}
4377ddc9b1aSDarren Reed 	waiter->fw_flags |= FWF_DESTROY_WANTED;
4387ddc9b1aSDarren Reed 	while (!FWF_DESTROY_OK(waiter)) {
4397ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
4407ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
4417ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
4427ddc9b1aSDarren Reed 	}
4437ddc9b1aSDarren Reed 	/*
4447ddc9b1aSDarren Reed 	 * There should now be nothing else using "waiter" or its
4457ddc9b1aSDarren Reed 	 * owner, so we can safely assign here without risk of wiiping
4467ddc9b1aSDarren Reed 	 * out someone's bit.
4477ddc9b1aSDarren Reed 	 */
4487ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_DESTROY_ACTIVE;
449*4a9b8375SDarren Reed 	cv_signal(&waiter->fw_cv);
450*4a9b8375SDarren Reed 	mutex_exit(&waiter->fw_lock);
451*4a9b8375SDarren Reed 
452*4a9b8375SDarren Reed 	return (0);
4537ddc9b1aSDarren Reed }
4547ddc9b1aSDarren Reed 
4557ddc9b1aSDarren Reed /*
4567ddc9b1aSDarren Reed  * Function:	hook_wait_init
4577ddc9b1aSDarren Reed  * Returns:     None
4587ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
4597ddc9b1aSDarren Reed  *              ownder(I)  - pointer to lock that the owner of this
4607ddc9b1aSDarren Reed  *                           waiter uses
4617ddc9b1aSDarren Reed  *
4627ddc9b1aSDarren Reed  * "owner" gets passed in here so that when we need to call cv_wait,
4637ddc9b1aSDarren Reed  * for example in hook_wait_setflag(), we can drop the lock for the
4647ddc9b1aSDarren Reed  * next layer out, which is likely to be held in an exclusive manner.
4657ddc9b1aSDarren Reed  */
4667ddc9b1aSDarren Reed void
4677ddc9b1aSDarren Reed hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
4687ddc9b1aSDarren Reed {
4697ddc9b1aSDarren Reed 	cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
4707ddc9b1aSDarren Reed 	mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
4717ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_NONE;
4727ddc9b1aSDarren Reed 	waiter->fw_owner = owner;
473f4b3ec61Sdh155122 }
474f4b3ec61Sdh155122 
475f4b3ec61Sdh155122 /*
476*4a9b8375SDarren Reed  * Function:	hook_stack_init
477*4a9b8375SDarren Reed  * Returns:     void *     - pointer to new hook stack structure
478*4a9b8375SDarren Reed  * Parameters:  stackid(I) - identifier for the network instance that owns this
479*4a9b8375SDarren Reed  *              ns(I)      - pointer to the network instance data structure
480*4a9b8375SDarren Reed  *
481*4a9b8375SDarren Reed  * Allocate and initialize the hook stack instance. This function is not
482*4a9b8375SDarren Reed  * allowed to fail, so KM_SLEEP is used here when allocating memory. The
483*4a9b8375SDarren Reed  * value returned is passed back into the shutdown and destroy hooks.
484f4b3ec61Sdh155122  */
485f4b3ec61Sdh155122 /*ARGSUSED*/
486f4b3ec61Sdh155122 static void *
487f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
488f4b3ec61Sdh155122 {
489f4b3ec61Sdh155122 	hook_stack_t	*hks;
490f4b3ec61Sdh155122 
491f4b3ec61Sdh155122 #ifdef NS_DEBUG
492f4b3ec61Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
493f4b3ec61Sdh155122 #endif
494f4b3ec61Sdh155122 
495f4b3ec61Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
4967ddc9b1aSDarren Reed 	hks->hks_netstack = ns;
4977ddc9b1aSDarren Reed 	hks->hks_netstackid = stackid;
498f4b3ec61Sdh155122 
4997ddc9b1aSDarren Reed 	CVW_INIT(&hks->hks_lock);
5007ddc9b1aSDarren Reed 	TAILQ_INIT(&hks->hks_nhead);
501f4b3ec61Sdh155122 	SLIST_INIT(&hks->hks_familylist);
502f4b3ec61Sdh155122 
5037ddc9b1aSDarren Reed 	hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
5047ddc9b1aSDarren Reed 
5057ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
5067ddc9b1aSDarren Reed 	SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
5077ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
5087ddc9b1aSDarren Reed 
509f4b3ec61Sdh155122 	return (hks);
510f4b3ec61Sdh155122 }
511f4b3ec61Sdh155122 
5128ad74188SDarren Reed /*
513*4a9b8375SDarren Reed  * Function:	hook_stack_shutdown
514*4a9b8375SDarren Reed  * Returns:     void
515*4a9b8375SDarren Reed  * Parameters:  stackid(I) - identifier for the network instance that owns this
516*4a9b8375SDarren Reed  *              arg(I)     - pointer returned by hook_stack_init
517*4a9b8375SDarren Reed  *
5188ad74188SDarren Reed  * Set the shutdown flag to indicate that we should stop accepting new
519*4a9b8375SDarren Reed  * register calls as we're now in the cleanup process. The cleanup is a
520*4a9b8375SDarren Reed  * two stage process and we're not required to free any memory here.
521*4a9b8375SDarren Reed  *
522*4a9b8375SDarren Reed  * The curious would wonder why isn't there any code that walks through
523*4a9b8375SDarren Reed  * all of the data structures and sets the flag(s) there? The answer is
524*4a9b8375SDarren Reed  * that it is expected that this will happen when the zone shutdown calls
525*4a9b8375SDarren Reed  * the shutdown callbacks for other modules that they will initiate the
526*4a9b8375SDarren Reed  * free'ing and shutdown of the hooks themselves.
5278ad74188SDarren Reed  */
5287ddc9b1aSDarren Reed /*ARGSUSED*/
5297ddc9b1aSDarren Reed static void
5307ddc9b1aSDarren Reed hook_stack_shutdown(netstackid_t stackid, void *arg)
5317ddc9b1aSDarren Reed {
5327ddc9b1aSDarren Reed 	hook_stack_t *hks = (hook_stack_t *)arg;
5337ddc9b1aSDarren Reed 
5347ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
5357ddc9b1aSDarren Reed 	/*
5367ddc9b1aSDarren Reed 	 * Once this flag gets set to one, no more additions are allowed
5377ddc9b1aSDarren Reed 	 * to any of the structures that make up this stack.
5387ddc9b1aSDarren Reed 	 */
5397ddc9b1aSDarren Reed 	hks->hks_shutdown = 1;
5407ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
5417ddc9b1aSDarren Reed }
5427ddc9b1aSDarren Reed 
543f4b3ec61Sdh155122 /*
544*4a9b8375SDarren Reed  * Function:	hook_stack_destroy
545*4a9b8375SDarren Reed  * Returns:     void
546*4a9b8375SDarren Reed  * Parameters:  stackid(I) - identifier for the network instance that owns this
547*4a9b8375SDarren Reed  *              arg(I)     - pointer returned by hook_stack_init
548*4a9b8375SDarren Reed  *
549f4b3ec61Sdh155122  * Free the hook stack instance.
550*4a9b8375SDarren Reed  *
551*4a9b8375SDarren Reed  * The rationale for the shutdown being lazy (see the comment above for
552*4a9b8375SDarren Reed  * hook_stack_shutdown) also applies to the destroy being lazy. Only if
553*4a9b8375SDarren Reed  * the hook_stack_t data structure is unused will it go away. Else it
554*4a9b8375SDarren Reed  * is left up to the last user of a data structure to actually free it.
555f4b3ec61Sdh155122  */
556f4b3ec61Sdh155122 /*ARGSUSED*/
557f4b3ec61Sdh155122 static void
558f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
559f4b3ec61Sdh155122 {
560f4b3ec61Sdh155122 	hook_stack_t *hks = (hook_stack_t *)arg;
5617ddc9b1aSDarren Reed 
5627ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
5637ddc9b1aSDarren Reed 	hks->hks_shutdown = 2;
5647ddc9b1aSDarren Reed 	hook_stack_remove(hks);
5657ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
5667ddc9b1aSDarren Reed }
5677ddc9b1aSDarren Reed 
5687ddc9b1aSDarren Reed /*
569*4a9b8375SDarren Reed  * Function:	hook_stack_remove
570*4a9b8375SDarren Reed  * Returns:     void
571*4a9b8375SDarren Reed  * Parameters:  hks(I) - pointer to an instance of a hook_stack_t
572*4a9b8375SDarren Reed  *
5737ddc9b1aSDarren Reed  * This function assumes that it is called with hook_stack_lock held.
5747ddc9b1aSDarren Reed  * It functions differently to hook_family/event_remove in that it does
5757ddc9b1aSDarren Reed  * the checks to see if it can be removed. This difference exists
5767ddc9b1aSDarren Reed  * because this structure has nothing higher up that depends on it.
5777ddc9b1aSDarren Reed  */
5787ddc9b1aSDarren Reed static void
5797ddc9b1aSDarren Reed hook_stack_remove(hook_stack_t *hks)
5807ddc9b1aSDarren Reed {
5817ddc9b1aSDarren Reed 
5827ddc9b1aSDarren Reed 	ASSERT(mutex_owned(&hook_stack_lock));
5837ddc9b1aSDarren Reed 
5847ddc9b1aSDarren Reed 	/*
5857ddc9b1aSDarren Reed 	 * Is the structure still in use?
5867ddc9b1aSDarren Reed 	 */
5877ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hks->hks_familylist) ||
5887ddc9b1aSDarren Reed 	    !TAILQ_EMPTY(&hks->hks_nhead))
5897ddc9b1aSDarren Reed 		return;
5907ddc9b1aSDarren Reed 
5917ddc9b1aSDarren Reed 	SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
5927ddc9b1aSDarren Reed 
593*4a9b8375SDarren Reed 	VERIFY(hook_wait_destroy(&hks->hks_waiter) == 0);
5947ddc9b1aSDarren Reed 	CVW_DESTROY(&hks->hks_lock);
595f4b3ec61Sdh155122 	kmem_free(hks, sizeof (*hks));
596f4b3ec61Sdh155122 }
597381a2a9aSdr146992 
598*4a9b8375SDarren Reed /*
599*4a9b8375SDarren Reed  * Function:	hook_stack_get
600*4a9b8375SDarren Reed  * Returns:     hook_stack_t * - NULL if not found, else matching instance
601*4a9b8375SDarren Reed  * Parameters:  stackid(I)     - instance id to search for
602*4a9b8375SDarren Reed  *
603*4a9b8375SDarren Reed  * Search the list of currently active hook_stack_t structures for one that
604*4a9b8375SDarren Reed  * has a matching netstackid_t to the value passed in. The linked list can
605*4a9b8375SDarren Reed  * only ever have at most one match for this value.
606*4a9b8375SDarren Reed  */
6077ddc9b1aSDarren Reed static hook_stack_t *
6087ddc9b1aSDarren Reed hook_stack_get(netstackid_t stackid)
6097ddc9b1aSDarren Reed {
6107ddc9b1aSDarren Reed 	hook_stack_t *hks;
6117ddc9b1aSDarren Reed 
6127ddc9b1aSDarren Reed 	SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
6137ddc9b1aSDarren Reed 		if (hks->hks_netstackid == stackid)
6147ddc9b1aSDarren Reed 			break;
6157ddc9b1aSDarren Reed 	}
6167ddc9b1aSDarren Reed 
6177ddc9b1aSDarren Reed 	return (hks);
6187ddc9b1aSDarren Reed }
6197ddc9b1aSDarren Reed 
6207ddc9b1aSDarren Reed /*
6217ddc9b1aSDarren Reed  * Function:	hook_stack_notify_register
622*4a9b8375SDarren Reed  * Returns:	int        - 0 = success, else failure
6237ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
6247ddc9b1aSDarren Reed  *              callback(I)- function to be called
6257ddc9b1aSDarren Reed  *              arg(I)     - arg to provide callback when it is called
6267ddc9b1aSDarren Reed  *
6277ddc9b1aSDarren Reed  * If we're not shutting down this instance, append a new function to the
6287ddc9b1aSDarren Reed  * list of those to call when a new family of hooks is added to this stack.
629*4a9b8375SDarren Reed  * If the function can be successfully added to the list of callbacks
630*4a9b8375SDarren Reed  * activated when there is a change to the stack (addition or removal of
631*4a9b8375SDarren Reed  * a hook family) then generate a fake HN_REGISTER event by directly
632*4a9b8375SDarren Reed  * calling the callback with the relevant information for each hook
633*4a9b8375SDarren Reed  * family that currently exists (and isn't being shutdown.)
6347ddc9b1aSDarren Reed  */
6357ddc9b1aSDarren Reed int
6367ddc9b1aSDarren Reed hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
6377ddc9b1aSDarren Reed     void *arg)
6387ddc9b1aSDarren Reed {
639*4a9b8375SDarren Reed 	hook_family_int_t *hfi;
6407ddc9b1aSDarren Reed 	hook_stack_t *hks;
641*4a9b8375SDarren Reed 	boolean_t canrun;
642*4a9b8375SDarren Reed 	char buffer[16];
6437ddc9b1aSDarren Reed 	int error;
6447ddc9b1aSDarren Reed 
645*4a9b8375SDarren Reed 	ASSERT(callback != NULL);
646*4a9b8375SDarren Reed 
647*4a9b8375SDarren Reed 	canrun = B_FALSE;
6487ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
6497ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
6507ddc9b1aSDarren Reed 	if (hks != NULL) {
6517ddc9b1aSDarren Reed 		if (hks->hks_shutdown != 0) {
6527ddc9b1aSDarren Reed 			error = ESHUTDOWN;
6537ddc9b1aSDarren Reed 		} else {
654*4a9b8375SDarren Reed 			CVW_ENTER_WRITE(&hks->hks_lock);
655*4a9b8375SDarren Reed 			canrun = (hook_wait_setflag(&hks->hks_waiter,
656*4a9b8375SDarren Reed 			    FWF_ADD_WAIT_MASK, FWF_ADD_WANTED,
657*4a9b8375SDarren Reed 			    FWF_ADD_ACTIVE) != -1);
658*4a9b8375SDarren Reed 			error = hook_notify_register(&hks->hks_nhead,
659*4a9b8375SDarren Reed 			    callback, arg);
660*4a9b8375SDarren Reed 			CVW_EXIT_WRITE(&hks->hks_lock);
6617ddc9b1aSDarren Reed 		}
6627ddc9b1aSDarren Reed 	} else {
6637ddc9b1aSDarren Reed 		error = ESRCH;
6647ddc9b1aSDarren Reed 	}
6657ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
6667ddc9b1aSDarren Reed 
667*4a9b8375SDarren Reed 	if (error == 0 && canrun) {
668*4a9b8375SDarren Reed 		/*
669*4a9b8375SDarren Reed 		 * Generate fake register event for callback that
670*4a9b8375SDarren Reed 		 * is being added, letting it know everything that
671*4a9b8375SDarren Reed 		 * already exists.
672*4a9b8375SDarren Reed 		 */
673*4a9b8375SDarren Reed 		(void) snprintf(buffer, sizeof (buffer), "%u",
674*4a9b8375SDarren Reed 		    hks->hks_netstackid);
675*4a9b8375SDarren Reed 
676*4a9b8375SDarren Reed 		SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
677*4a9b8375SDarren Reed 			if (hfi->hfi_condemned || hfi->hfi_shutdown)
678*4a9b8375SDarren Reed 				continue;
679*4a9b8375SDarren Reed 			callback(HN_REGISTER, arg, buffer, NULL,
680*4a9b8375SDarren Reed 			    hfi->hfi_family.hf_name);
681*4a9b8375SDarren Reed 		}
682*4a9b8375SDarren Reed 	}
683*4a9b8375SDarren Reed 
684*4a9b8375SDarren Reed 	if (canrun)
685*4a9b8375SDarren Reed 		hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
686*4a9b8375SDarren Reed 
6877ddc9b1aSDarren Reed 	return (error);
6887ddc9b1aSDarren Reed }
6897ddc9b1aSDarren Reed 
6907ddc9b1aSDarren Reed /*
6917ddc9b1aSDarren Reed  * Function:	hook_stack_notify_unregister
692*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
6937ddc9b1aSDarren Reed  * Parameters:	stackid(I)  - netstack identifier
6947ddc9b1aSDarren Reed  *              callback(I) - function to be called
6957ddc9b1aSDarren Reed  *
6967ddc9b1aSDarren Reed  * Attempt to remove a registered function from a hook stack's list of
6977ddc9b1aSDarren Reed  * callbacks to activiate when protocols are added/deleted.
698*4a9b8375SDarren Reed  * As with hook_stack_notify_register, if all things are going well then
699*4a9b8375SDarren Reed  * a fake unregister event is delivered to the callback being removed
700*4a9b8375SDarren Reed  * for each hook family that presently exists.
7017ddc9b1aSDarren Reed  */
7027ddc9b1aSDarren Reed int
7037ddc9b1aSDarren Reed hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
7047ddc9b1aSDarren Reed {
705*4a9b8375SDarren Reed 	hook_family_int_t *hfi;
7067ddc9b1aSDarren Reed 	hook_stack_t *hks;
707*4a9b8375SDarren Reed 	boolean_t canrun;
708*4a9b8375SDarren Reed 	char buffer[16];
709*4a9b8375SDarren Reed 	void *arg;
7107ddc9b1aSDarren Reed 	int error;
7117ddc9b1aSDarren Reed 
7127ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
7137ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
7147ddc9b1aSDarren Reed 	if (hks != NULL) {
715*4a9b8375SDarren Reed 		CVW_ENTER_WRITE(&hks->hks_lock);
716*4a9b8375SDarren Reed 		canrun = (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
717*4a9b8375SDarren Reed 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
718*4a9b8375SDarren Reed 
719*4a9b8375SDarren Reed 		error = hook_notify_unregister(&hks->hks_nhead, callback, &arg);
720*4a9b8375SDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
7217ddc9b1aSDarren Reed 	} else {
7227ddc9b1aSDarren Reed 		error = ESRCH;
7237ddc9b1aSDarren Reed 	}
7247ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
7257ddc9b1aSDarren Reed 
726*4a9b8375SDarren Reed 	if (error == 0) {
727*4a9b8375SDarren Reed 		if (canrun) {
728*4a9b8375SDarren Reed 			/*
729*4a9b8375SDarren Reed 			 * Generate fake unregister event for callback that
730*4a9b8375SDarren Reed 			 * is being removed, letting it know everything that
731*4a9b8375SDarren Reed 			 * currently exists is now "disappearing."
732*4a9b8375SDarren Reed 			 */
733*4a9b8375SDarren Reed 			(void) snprintf(buffer, sizeof (buffer), "%u",
734*4a9b8375SDarren Reed 			    hks->hks_netstackid);
735*4a9b8375SDarren Reed 
736*4a9b8375SDarren Reed 			SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
737*4a9b8375SDarren Reed 				callback(HN_UNREGISTER, arg, buffer, NULL,
738*4a9b8375SDarren Reed 				    hfi->hfi_family.hf_name);
739*4a9b8375SDarren Reed 			}
740*4a9b8375SDarren Reed 
741*4a9b8375SDarren Reed 			hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
742*4a9b8375SDarren Reed 		}
743*4a9b8375SDarren Reed 
744*4a9b8375SDarren Reed 		mutex_enter(&hook_stack_lock);
745*4a9b8375SDarren Reed 		hks = hook_stack_get(stackid);
746*4a9b8375SDarren Reed 		if ((error == 0) && (hks->hks_shutdown == 2))
747*4a9b8375SDarren Reed 			hook_stack_remove(hks);
748*4a9b8375SDarren Reed 		mutex_exit(&hook_stack_lock);
749*4a9b8375SDarren Reed 	}
750*4a9b8375SDarren Reed 
7517ddc9b1aSDarren Reed 	return (error);
7527ddc9b1aSDarren Reed }
7537ddc9b1aSDarren Reed 
7547ddc9b1aSDarren Reed /*
7557ddc9b1aSDarren Reed  * Function:	hook_stack_notify_run
7567ddc9b1aSDarren Reed  * Returns:	None
7577ddc9b1aSDarren Reed  * Parameters:	hks(I)  - hook stack pointer to execute callbacks for
7587ddc9b1aSDarren Reed  *              name(I) - name of a hook family
7597ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
7607ddc9b1aSDarren Reed  *
7617ddc9b1aSDarren Reed  * Run through the list of callbacks on the hook stack to be called when
7627ddc9b1aSDarren Reed  * a new hook family is added
7637ddc9b1aSDarren Reed  *
764*4a9b8375SDarren Reed  * As hook_notify_run() expects 3 names, one for the family that is associated
765*4a9b8375SDarren Reed  * with the cmd (HN_REGISTER or HN_UNREGISTER), one for the event and one
766*4a9b8375SDarren Reed  * for the object being introduced and we really only have one name (that
767*4a9b8375SDarren Reed  * of the new hook family), fake the hook stack's name by converting the
768*4a9b8375SDarren Reed  * integer to a string and for the event just pass NULL.
7697ddc9b1aSDarren Reed  */
7707ddc9b1aSDarren Reed static void
7717ddc9b1aSDarren Reed hook_stack_notify_run(hook_stack_t *hks, char *name,
7727ddc9b1aSDarren Reed     hook_notify_cmd_t cmd)
7737ddc9b1aSDarren Reed {
7747ddc9b1aSDarren Reed 	char buffer[16];
7757ddc9b1aSDarren Reed 
776*4a9b8375SDarren Reed 	ASSERT(hks != NULL);
777*4a9b8375SDarren Reed 	ASSERT(name != NULL);
778*4a9b8375SDarren Reed 
7797ddc9b1aSDarren Reed 	(void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
7807ddc9b1aSDarren Reed 
7817ddc9b1aSDarren Reed 	hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
7827ddc9b1aSDarren Reed }
7837ddc9b1aSDarren Reed 
784381a2a9aSdr146992 /*
785381a2a9aSdr146992  * Function:	hook_run
786381a2a9aSdr146992  * Returns:	int      - return value according to callback func
787381a2a9aSdr146992  * Parameters:	token(I) - event pointer
788381a2a9aSdr146992  *		info(I)  - message
789381a2a9aSdr146992  *
790381a2a9aSdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
791381a2a9aSdr146992  * until either the end of the list is reached or a hook function returns a
792381a2a9aSdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
793381a2a9aSdr146992  * return that value back to our caller.  By design, a hook function can be
794381a2a9aSdr146992  * called more than once, simultaneously.
795381a2a9aSdr146992  */
796381a2a9aSdr146992 int
7977ddc9b1aSDarren Reed hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
798381a2a9aSdr146992 {
799381a2a9aSdr146992 	hook_event_int_t *hei;
8007ddc9b1aSDarren Reed 	hook_int_t *hi;
801381a2a9aSdr146992 	int rval = 0;
802381a2a9aSdr146992 
803381a2a9aSdr146992 	ASSERT(token != NULL);
804381a2a9aSdr146992 
805381a2a9aSdr146992 	hei = (hook_event_int_t *)token;
806381a2a9aSdr146992 	DTRACE_PROBE2(hook__run__start,
807381a2a9aSdr146992 	    hook_event_token_t, token,
808381a2a9aSdr146992 	    hook_data_t, info);
809381a2a9aSdr146992 
8107ddc9b1aSDarren Reed 	/*
811*4a9b8375SDarren Reed 	 * If we consider that this function is only called from within the
812*4a9b8375SDarren Reed 	 * stack while an instance is currently active,
8137ddc9b1aSDarren Reed 	 */
8147ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
815381a2a9aSdr146992 
816381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
817381a2a9aSdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
818381a2a9aSdr146992 		DTRACE_PROBE3(hook__func__start,
819381a2a9aSdr146992 		    hook_event_token_t, token,
820381a2a9aSdr146992 		    hook_data_t, info,
821381a2a9aSdr146992 		    hook_int_t *, hi);
8227ddc9b1aSDarren Reed 		rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
823381a2a9aSdr146992 		DTRACE_PROBE4(hook__func__end,
824381a2a9aSdr146992 		    hook_event_token_t, token,
825381a2a9aSdr146992 		    hook_data_t, info,
826381a2a9aSdr146992 		    hook_int_t *, hi,
827381a2a9aSdr146992 		    int, rval);
8287ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hits.value.ui64++;
829381a2a9aSdr146992 		if (rval != 0)
830381a2a9aSdr146992 			break;
831381a2a9aSdr146992 	}
832381a2a9aSdr146992 
8337ddc9b1aSDarren Reed 	hei->hei_kstats.events.value.ui64++;
8347ddc9b1aSDarren Reed 
8357ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
836381a2a9aSdr146992 
837381a2a9aSdr146992 	DTRACE_PROBE3(hook__run__end,
838381a2a9aSdr146992 	    hook_event_token_t, token,
839381a2a9aSdr146992 	    hook_data_t, info,
840381a2a9aSdr146992 	    hook_int_t *, hi);
841381a2a9aSdr146992 
842381a2a9aSdr146992 	return (rval);
843381a2a9aSdr146992 }
844381a2a9aSdr146992 
845381a2a9aSdr146992 /*
846381a2a9aSdr146992  * Function:	hook_family_add
847381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Fail
848381a2a9aSdr146992  * Parameters:	hf(I)    - family pointer
849*4a9b8375SDarren Reed  *              hks(I)   - pointer to an instance of a hook_stack_t
850*4a9b8375SDarren Reed  *              store(O) - where returned pointer will be stored
851381a2a9aSdr146992  *
852*4a9b8375SDarren Reed  * Add new family to the family list. The requirements for the addition to
853*4a9b8375SDarren Reed  * succeed are that the family name must not already be registered and that
854*4a9b8375SDarren Reed  * the hook stack is not being shutdown.
855*4a9b8375SDarren Reed  * If store is non-NULL, it is expected to be a pointer to the same variable
856*4a9b8375SDarren Reed  * that is awaiting to be assigned the return value of this function.
857*4a9b8375SDarren Reed  * In its current use, the returned value is assigned to netd_hooks in
858*4a9b8375SDarren Reed  * net_family_register. The use of "store" allows the return value to be
859*4a9b8375SDarren Reed  * used before this function returns. How can this happen? Through the
860*4a9b8375SDarren Reed  * callbacks that can be activated at the bottom of this function, when
861*4a9b8375SDarren Reed  * hook_stack_notify_run is called.
862381a2a9aSdr146992  */
863381a2a9aSdr146992 hook_family_int_t *
864*4a9b8375SDarren Reed hook_family_add(hook_family_t *hf, hook_stack_t *hks, void **store)
865381a2a9aSdr146992 {
866381a2a9aSdr146992 	hook_family_int_t *hfi, *new;
867381a2a9aSdr146992 
868381a2a9aSdr146992 	ASSERT(hf != NULL);
869381a2a9aSdr146992 	ASSERT(hf->hf_name != NULL);
870381a2a9aSdr146992 
871381a2a9aSdr146992 	new = hook_family_copy(hf);
872381a2a9aSdr146992 	if (new == NULL)
873381a2a9aSdr146992 		return (NULL);
874381a2a9aSdr146992 
8757ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
8767ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
8777ddc9b1aSDarren Reed 
8787ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
8797ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
8807ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
8817ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
8827ddc9b1aSDarren Reed 		return (NULL);
8837ddc9b1aSDarren Reed 	}
884381a2a9aSdr146992 
885381a2a9aSdr146992 	/* search family list */
886f4b3ec61Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
887381a2a9aSdr146992 	if (hfi != NULL) {
8887ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
8897ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
8907ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
891381a2a9aSdr146992 		return (NULL);
892381a2a9aSdr146992 	}
893381a2a9aSdr146992 
894*4a9b8375SDarren Reed 	/*
895*4a9b8375SDarren Reed 	 * Try and set the FWF_ADD_ACTIVE flag so that we can drop all the
896*4a9b8375SDarren Reed 	 * lock further down when calling all of the functions registered
897*4a9b8375SDarren Reed 	 * for notification when a new hook family is added.
898*4a9b8375SDarren Reed 	 */
899*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
9007ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
9017ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
9027ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
9037ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
9047ddc9b1aSDarren Reed 		return (NULL);
9057ddc9b1aSDarren Reed 	}
9067ddc9b1aSDarren Reed 
9077ddc9b1aSDarren Reed 	CVW_INIT(&new->hfi_lock);
9087ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
9097ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
9107ddc9b1aSDarren Reed 
9117ddc9b1aSDarren Reed 	hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
9127ddc9b1aSDarren Reed 
9137ddc9b1aSDarren Reed 	new->hfi_stack = hks;
914*4a9b8375SDarren Reed 	if (store != NULL)
915*4a9b8375SDarren Reed 		*store = new;
916381a2a9aSdr146992 
917f4b3ec61Sdh155122 	/* Add to family list head */
918f4b3ec61Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
919f4b3ec61Sdh155122 
9207ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
9217ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
9227ddc9b1aSDarren Reed 
9237ddc9b1aSDarren Reed 	hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
9247ddc9b1aSDarren Reed 
9257ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
9267ddc9b1aSDarren Reed 
927381a2a9aSdr146992 	return (new);
928381a2a9aSdr146992 }
929381a2a9aSdr146992 
930381a2a9aSdr146992 /*
931381a2a9aSdr146992  * Function:	hook_family_remove
932*4a9b8375SDarren Reed  * Returns:	int    - 0 = success, else = failure
933381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
934381a2a9aSdr146992  *
9357ddc9b1aSDarren Reed  * Remove family from family list. This function has been designed to be
9367ddc9b1aSDarren Reed  * called once and once only per hook_family_int_t. Thus when cleaning up
9377ddc9b1aSDarren Reed  * this structure as an orphan, callers should only call hook_family_free.
938381a2a9aSdr146992  */
939381a2a9aSdr146992 int
940381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi)
941381a2a9aSdr146992 {
942f4b3ec61Sdh155122 	hook_stack_t *hks;
9438ad74188SDarren Reed 	boolean_t notifydone;
944381a2a9aSdr146992 
945381a2a9aSdr146992 	ASSERT(hfi != NULL);
9467ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
947381a2a9aSdr146992 
948*4a9b8375SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
949*4a9b8375SDarren Reed 	notifydone = hfi->hfi_shutdown;
950*4a9b8375SDarren Reed 	hfi->hfi_shutdown = B_TRUE;
951*4a9b8375SDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
952*4a9b8375SDarren Reed 
9537ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
954381a2a9aSdr146992 
955*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
9567ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
9577ddc9b1aSDarren Reed 		/*
9587ddc9b1aSDarren Reed 		 * If we're trying to destroy the hook_stack_t...
9597ddc9b1aSDarren Reed 		 */
9608ad74188SDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
9617ddc9b1aSDarren Reed 		return (ENXIO);
962381a2a9aSdr146992 	}
963381a2a9aSdr146992 
9647ddc9b1aSDarren Reed 	/*
9657ddc9b1aSDarren Reed 	 * Check if the family is in use by the presence of either events
9667ddc9b1aSDarren Reed 	 * or notify callbacks on the hook family.
9677ddc9b1aSDarren Reed 	 */
9687ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
9697ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_TRUE;
9707ddc9b1aSDarren Reed 	} else {
971*4a9b8375SDarren Reed 		VERIFY(hook_wait_destroy(&hfi->hfi_waiter) == 0);
9727ddc9b1aSDarren Reed 		/*
9737ddc9b1aSDarren Reed 		 * Although hfi_condemned = B_FALSE is implied from creation,
9747ddc9b1aSDarren Reed 		 * putting a comment here inside the else upsets lint.
9757ddc9b1aSDarren Reed 		 */
9767ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_FALSE;
9777ddc9b1aSDarren Reed 	}
9787ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
9797ddc9b1aSDarren Reed 
9808ad74188SDarren Reed 	if (!notifydone)
9818ad74188SDarren Reed 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
9828ad74188SDarren Reed 		    HN_UNREGISTER);
9837ddc9b1aSDarren Reed 
9847ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
9857ddc9b1aSDarren Reed 
9867ddc9b1aSDarren Reed 	/*
9877ddc9b1aSDarren Reed 	 * If we don't have to wait for anything else to disappear from this
9887ddc9b1aSDarren Reed 	 * structure then we can free it up.
9897ddc9b1aSDarren Reed 	 */
9907ddc9b1aSDarren Reed 	if (!hfi->hfi_condemned)
9917ddc9b1aSDarren Reed 		hook_family_free(hfi, hks);
992381a2a9aSdr146992 
993381a2a9aSdr146992 	return (0);
994381a2a9aSdr146992 }
995381a2a9aSdr146992 
996381a2a9aSdr146992 
997381a2a9aSdr146992 /*
9987ddc9b1aSDarren Reed  * Function:	hook_family_free
9997ddc9b1aSDarren Reed  * Returns:	None
10007ddc9b1aSDarren Reed  * Parameters:	hfi(I) - internal family pointer
10017ddc9b1aSDarren Reed  *
10027ddc9b1aSDarren Reed  * Free alloc memory for family
10037ddc9b1aSDarren Reed  */
10047ddc9b1aSDarren Reed static void
10057ddc9b1aSDarren Reed hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
10067ddc9b1aSDarren Reed {
10077ddc9b1aSDarren Reed 
10087ddc9b1aSDarren Reed 	/*
10097ddc9b1aSDarren Reed 	 * This lock gives us possession of the hks pointer after the
10107ddc9b1aSDarren Reed 	 * SLIST_REMOVE, for which it is not needed, when hks_shutdown
10117ddc9b1aSDarren Reed 	 * is checked and hook_stack_remove called.
10127ddc9b1aSDarren Reed 	 */
10137ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
10147ddc9b1aSDarren Reed 
10157ddc9b1aSDarren Reed 	ASSERT(hfi != NULL);
10167ddc9b1aSDarren Reed 
10177ddc9b1aSDarren Reed 	if (hks != NULL) {
10187ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hks->hks_lock);
10197ddc9b1aSDarren Reed 		/* Remove from family list */
10207ddc9b1aSDarren Reed 		SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
10217ddc9b1aSDarren Reed 		    hfi_entry);
10227ddc9b1aSDarren Reed 
10237ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
10247ddc9b1aSDarren Reed 	}
10257ddc9b1aSDarren Reed 
10267ddc9b1aSDarren Reed 	/* Free name space */
10277ddc9b1aSDarren Reed 	if (hfi->hfi_family.hf_name != NULL) {
10287ddc9b1aSDarren Reed 		kmem_free(hfi->hfi_family.hf_name,
10297ddc9b1aSDarren Reed 		    strlen(hfi->hfi_family.hf_name) + 1);
10307ddc9b1aSDarren Reed 	}
10317ddc9b1aSDarren Reed 
10327ddc9b1aSDarren Reed 	/* Free container */
10337ddc9b1aSDarren Reed 	kmem_free(hfi, sizeof (*hfi));
10347ddc9b1aSDarren Reed 
10357ddc9b1aSDarren Reed 	if (hks->hks_shutdown == 2)
10367ddc9b1aSDarren Reed 		hook_stack_remove(hks);
10377ddc9b1aSDarren Reed 
10387ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
10397ddc9b1aSDarren Reed }
10407ddc9b1aSDarren Reed 
10418ad74188SDarren Reed /*
10428ad74188SDarren Reed  * Function:	hook_family_shutdown
1043*4a9b8375SDarren Reed  * Returns:	int    - 0 = success, else = failure
10448ad74188SDarren Reed  * Parameters:	hfi(I) - internal family pointer
10458ad74188SDarren Reed  *
10468ad74188SDarren Reed  * As an alternative to removing a family, we may desire to just generate
10478ad74188SDarren Reed  * a series of callbacks to indicate that we will be going away in the
10488ad74188SDarren Reed  * future. The hfi_condemned flag isn't set because we aren't trying to
10498ad74188SDarren Reed  * remove the structure.
10508ad74188SDarren Reed  */
10518ad74188SDarren Reed int
10528ad74188SDarren Reed hook_family_shutdown(hook_family_int_t *hfi)
10538ad74188SDarren Reed {
10548ad74188SDarren Reed 	hook_stack_t *hks;
10558ad74188SDarren Reed 	boolean_t notifydone;
10568ad74188SDarren Reed 
10578ad74188SDarren Reed 	ASSERT(hfi != NULL);
10588ad74188SDarren Reed 	hks = hfi->hfi_stack;
10598ad74188SDarren Reed 
1060*4a9b8375SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1061*4a9b8375SDarren Reed 	notifydone = hfi->hfi_shutdown;
1062*4a9b8375SDarren Reed 	hfi->hfi_shutdown = B_TRUE;
1063*4a9b8375SDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1064*4a9b8375SDarren Reed 
10658ad74188SDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
10668ad74188SDarren Reed 
1067*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
10688ad74188SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
10698ad74188SDarren Reed 		/*
10708ad74188SDarren Reed 		 * If we're trying to destroy the hook_stack_t...
10718ad74188SDarren Reed 		 */
10728ad74188SDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
10738ad74188SDarren Reed 		return (ENXIO);
10748ad74188SDarren Reed 	}
10758ad74188SDarren Reed 
10768ad74188SDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
10778ad74188SDarren Reed 
10788ad74188SDarren Reed 	if (!notifydone)
10798ad74188SDarren Reed 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
10808ad74188SDarren Reed 		    HN_UNREGISTER);
10818ad74188SDarren Reed 
10828ad74188SDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
10838ad74188SDarren Reed 
10848ad74188SDarren Reed 	return (0);
10858ad74188SDarren Reed }
10867ddc9b1aSDarren Reed 
10877ddc9b1aSDarren Reed /*
1088381a2a9aSdr146992  * Function:	hook_family_copy
1089381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Failed
1090381a2a9aSdr146992  * Parameters:	src(I) - family pointer
1091381a2a9aSdr146992  *
1092381a2a9aSdr146992  * Allocate internal family block and duplicate incoming family
1093381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
1094381a2a9aSdr146992  */
1095381a2a9aSdr146992 static hook_family_int_t *
1096381a2a9aSdr146992 hook_family_copy(hook_family_t *src)
1097381a2a9aSdr146992 {
1098381a2a9aSdr146992 	hook_family_int_t *new;
1099381a2a9aSdr146992 	hook_family_t *dst;
1100381a2a9aSdr146992 
1101381a2a9aSdr146992 	ASSERT(src != NULL);
1102381a2a9aSdr146992 	ASSERT(src->hf_name != NULL);
1103381a2a9aSdr146992 
1104381a2a9aSdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1105381a2a9aSdr146992 
1106381a2a9aSdr146992 	/* Copy body */
1107381a2a9aSdr146992 	dst = &new->hfi_family;
1108381a2a9aSdr146992 	*dst = *src;
1109381a2a9aSdr146992 
11107ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
11117ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
11127ddc9b1aSDarren Reed 
1113381a2a9aSdr146992 	/* Copy name */
1114381a2a9aSdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
1115381a2a9aSdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
1116381a2a9aSdr146992 
1117381a2a9aSdr146992 	return (new);
1118381a2a9aSdr146992 }
1119381a2a9aSdr146992 
1120381a2a9aSdr146992 /*
11217ddc9b1aSDarren Reed  * Function:	hook_family_find
1122381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Not match
1123381a2a9aSdr146992  * Parameters:	family(I) - family name string
1124381a2a9aSdr146992  *
1125381a2a9aSdr146992  * Search family list with family name
11267ddc9b1aSDarren Reed  * 	A lock on hfi_lock must be held when called.
1127381a2a9aSdr146992  */
1128381a2a9aSdr146992 static hook_family_int_t *
1129f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
1130381a2a9aSdr146992 {
1131381a2a9aSdr146992 	hook_family_int_t *hfi = NULL;
1132381a2a9aSdr146992 
1133381a2a9aSdr146992 	ASSERT(family != NULL);
1134381a2a9aSdr146992 
1135f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
1136381a2a9aSdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
1137381a2a9aSdr146992 			break;
1138381a2a9aSdr146992 	}
1139381a2a9aSdr146992 	return (hfi);
1140381a2a9aSdr146992 }
1141381a2a9aSdr146992 
11427ddc9b1aSDarren Reed /*
11437ddc9b1aSDarren Reed  * Function:	hook_family_notify_register
1144*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
11457ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
11467ddc9b1aSDarren Reed  *              callback(I) - function to be called
11477ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
11487ddc9b1aSDarren Reed  *
11497ddc9b1aSDarren Reed  * So long as this hook stack isn't being shut down, register a new
11507ddc9b1aSDarren Reed  * callback to be activated each time a new event is added to this
11517ddc9b1aSDarren Reed  * family.
11527ddc9b1aSDarren Reed  *
11537ddc9b1aSDarren Reed  * To call this function we must have an active handle in use on the family,
11547ddc9b1aSDarren Reed  * so if we take this into account, then neither the hook_family_int_t nor
11557ddc9b1aSDarren Reed  * the hook_stack_t that owns it can disappear. We have to put some trust
11567ddc9b1aSDarren Reed  * in the callers to be properly synchronised...
11577ddc9b1aSDarren Reed  *
11587ddc9b1aSDarren Reed  * Holding hks_lock is required to provide synchronisation for hks_shutdown.
11597ddc9b1aSDarren Reed  */
11607ddc9b1aSDarren Reed int
11617ddc9b1aSDarren Reed hook_family_notify_register(hook_family_int_t *hfi,
11627ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
11637ddc9b1aSDarren Reed {
1164*4a9b8375SDarren Reed 	hook_event_int_t *hei;
11657ddc9b1aSDarren Reed 	hook_stack_t *hks;
1166*4a9b8375SDarren Reed 	boolean_t canrun;
11677ddc9b1aSDarren Reed 	int error;
11687ddc9b1aSDarren Reed 
1169*4a9b8375SDarren Reed 	ASSERT(hfi != NULL);
1170*4a9b8375SDarren Reed 	canrun = B_FALSE;
11717ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
11727ddc9b1aSDarren Reed 
11737ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
11747ddc9b1aSDarren Reed 
11758ad74188SDarren Reed 	if ((hfi->hfi_stack->hks_shutdown != 0) ||
11768ad74188SDarren Reed 	    hfi->hfi_condemned || hfi->hfi_shutdown) {
11777ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
11787ddc9b1aSDarren Reed 		return (ESHUTDOWN);
11797ddc9b1aSDarren Reed 	}
11807ddc9b1aSDarren Reed 
1181*4a9b8375SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1182*4a9b8375SDarren Reed 	canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1183*4a9b8375SDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1184*4a9b8375SDarren Reed 	error = hook_notify_register(&hfi->hfi_nhead, callback, arg);
11857ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1186*4a9b8375SDarren Reed 
11877ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
11887ddc9b1aSDarren Reed 
1189*4a9b8375SDarren Reed 	if (error == 0 && canrun) {
1190*4a9b8375SDarren Reed 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1191*4a9b8375SDarren Reed 			callback(HN_REGISTER, arg,
1192*4a9b8375SDarren Reed 			    hfi->hfi_family.hf_name, NULL,
1193*4a9b8375SDarren Reed 			    hei->hei_event->he_name);
1194*4a9b8375SDarren Reed 		}
1195*4a9b8375SDarren Reed 	}
1196*4a9b8375SDarren Reed 
1197*4a9b8375SDarren Reed 	if (canrun)
1198*4a9b8375SDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
1199*4a9b8375SDarren Reed 
12007ddc9b1aSDarren Reed 	return (error);
12017ddc9b1aSDarren Reed }
1202381a2a9aSdr146992 
1203381a2a9aSdr146992 /*
12047ddc9b1aSDarren Reed  * Function:	hook_family_notify_unregister
1205*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
12067ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
12077ddc9b1aSDarren Reed  *              callback(I) - function to be called
1208381a2a9aSdr146992  *
12097ddc9b1aSDarren Reed  * Remove a callback from the list of those executed when a new event is
1210*4a9b8375SDarren Reed  * added to a hook family. If the family is not in the process of being
1211*4a9b8375SDarren Reed  * destroyed then simulate an unregister callback for each event that is
1212*4a9b8375SDarren Reed  * on the family. This pairs up with the hook_family_notify_register
1213*4a9b8375SDarren Reed  * action that simulates register events.
1214*4a9b8375SDarren Reed  * The order of what happens here is important and goes like this.
1215*4a9b8375SDarren Reed  * 1) Remove the callback from the list of functions to be called as part
1216*4a9b8375SDarren Reed  *    of the notify operation when an event is added or removed from the
1217*4a9b8375SDarren Reed  *    hook family.
1218*4a9b8375SDarren Reed  * 2) If the hook_family_int_t structure is on death row (free_family will
1219*4a9b8375SDarren Reed  *    be set to true) then there's nothing else to do than let it be free'd.
1220*4a9b8375SDarren Reed  * 3) If the structure isn't about to die, mark it up as being busy using
1221*4a9b8375SDarren Reed  *    hook_wait_setflag and then drop the lock so the loop can be run.
1222*4a9b8375SDarren Reed  * 4) if hook_wait_setflag was successful, tell all of the notify callback
1223*4a9b8375SDarren Reed  *    functions that this family has been unregistered.
1224*4a9b8375SDarren Reed  * 5) Cleanup
1225381a2a9aSdr146992  */
12267ddc9b1aSDarren Reed int
12277ddc9b1aSDarren Reed hook_family_notify_unregister(hook_family_int_t *hfi,
12287ddc9b1aSDarren Reed     hook_notify_fn_t callback)
1229381a2a9aSdr146992 {
1230*4a9b8375SDarren Reed 	hook_event_int_t *hei;
12317ddc9b1aSDarren Reed 	boolean_t free_family;
1232*4a9b8375SDarren Reed 	boolean_t canrun;
12337ddc9b1aSDarren Reed 	int error;
1234*4a9b8375SDarren Reed 	void *arg;
1235*4a9b8375SDarren Reed 
1236*4a9b8375SDarren Reed 	canrun = B_FALSE;
1237381a2a9aSdr146992 
12387ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
12397ddc9b1aSDarren Reed 
1240*4a9b8375SDarren Reed 	(void) hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
1241*4a9b8375SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1242*4a9b8375SDarren Reed 
1243*4a9b8375SDarren Reed 	error = hook_notify_unregister(&hfi->hfi_nhead, callback, &arg);
1244*4a9b8375SDarren Reed 
1245*4a9b8375SDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
12467ddc9b1aSDarren Reed 
12477ddc9b1aSDarren Reed 	/*
12487ddc9b1aSDarren Reed 	 * If hook_family_remove has been called but the structure was still
12497ddc9b1aSDarren Reed 	 * "busy" ... but we might have just made it "unbusy"...
12507ddc9b1aSDarren Reed 	 */
12517ddc9b1aSDarren Reed 	if ((error == 0) && hfi->hfi_condemned &&
12527ddc9b1aSDarren Reed 	    SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
12537ddc9b1aSDarren Reed 		free_family = B_TRUE;
12547ddc9b1aSDarren Reed 	} else {
12557ddc9b1aSDarren Reed 		free_family = B_FALSE;
1256381a2a9aSdr146992 	}
1257381a2a9aSdr146992 
1258*4a9b8375SDarren Reed 	if (error == 0 && !free_family) {
1259*4a9b8375SDarren Reed 		canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1260*4a9b8375SDarren Reed 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1261*4a9b8375SDarren Reed 	}
1262*4a9b8375SDarren Reed 
12637ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1264381a2a9aSdr146992 
1265*4a9b8375SDarren Reed 	if (canrun) {
1266*4a9b8375SDarren Reed 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1267*4a9b8375SDarren Reed 			callback(HN_UNREGISTER, arg,
1268*4a9b8375SDarren Reed 			    hfi->hfi_family.hf_name, NULL,
1269*4a9b8375SDarren Reed 			    hei->hei_event->he_name);
1270*4a9b8375SDarren Reed 		}
1271*4a9b8375SDarren Reed 
1272*4a9b8375SDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
1273*4a9b8375SDarren Reed 	} else if (free_family) {
12747ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
1275*4a9b8375SDarren Reed 	}
12767ddc9b1aSDarren Reed 
12777ddc9b1aSDarren Reed 	return (error);
12787ddc9b1aSDarren Reed }
1279381a2a9aSdr146992 
1280381a2a9aSdr146992 /*
1281381a2a9aSdr146992  * Function:	hook_event_add
1282381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Fail
1283381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1284381a2a9aSdr146992  *		he(I)  - event pointer
1285381a2a9aSdr146992  *
1286381a2a9aSdr146992  * Add new event to event list on specific family.
1287381a2a9aSdr146992  * This function can fail to return successfully if (1) it cannot allocate
1288381a2a9aSdr146992  * enough memory for its own internal data structures, (2) the event has
1289381a2a9aSdr146992  * already been registered (for any hook family.)
1290381a2a9aSdr146992  */
1291381a2a9aSdr146992 hook_event_int_t *
1292381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
1293381a2a9aSdr146992 {
1294381a2a9aSdr146992 	hook_event_int_t *hei, *new;
12957ddc9b1aSDarren Reed 	hook_stack_t *hks;
1296381a2a9aSdr146992 
1297381a2a9aSdr146992 	ASSERT(hfi != NULL);
1298381a2a9aSdr146992 	ASSERT(he != NULL);
1299381a2a9aSdr146992 	ASSERT(he->he_name != NULL);
1300381a2a9aSdr146992 
1301381a2a9aSdr146992 	new = hook_event_copy(he);
1302381a2a9aSdr146992 	if (new == NULL)
1303381a2a9aSdr146992 		return (NULL);
1304381a2a9aSdr146992 
13057ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
13067ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
13077ddc9b1aSDarren Reed 
13087ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
13097ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
13107ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
13117ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
13127ddc9b1aSDarren Reed 		return (NULL);
13137ddc9b1aSDarren Reed 	}
1314381a2a9aSdr146992 
1315381a2a9aSdr146992 	/* Check whether this event pointer is already registered */
1316f4b3ec61Sdh155122 	hei = hook_event_checkdup(he, hks);
1317381a2a9aSdr146992 	if (hei != NULL) {
13187ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
13197ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1320381a2a9aSdr146992 		return (NULL);
1321381a2a9aSdr146992 	}
1322381a2a9aSdr146992 
13237ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
13247ddc9b1aSDarren Reed 
13258ad74188SDarren Reed 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
13267ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13277ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
13287ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
13297ddc9b1aSDarren Reed 		return (NULL);
13307ddc9b1aSDarren Reed 	}
1331*4a9b8375SDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
13327ddc9b1aSDarren Reed 
1333*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
13347ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
13357ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13367ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
13377ddc9b1aSDarren Reed 		return (NULL);
13387ddc9b1aSDarren Reed 	}
13397ddc9b1aSDarren Reed 
13407ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hei_nhead);
13417ddc9b1aSDarren Reed 
13427ddc9b1aSDarren Reed 	hook_event_init_kstats(hfi, new);
13437ddc9b1aSDarren Reed 	hook_wait_init(&new->hei_waiter, &new->hei_lock);
13447ddc9b1aSDarren Reed 
1345381a2a9aSdr146992 	/* Add to event list head */
1346381a2a9aSdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
1347381a2a9aSdr146992 
13487ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
13497ddc9b1aSDarren Reed 
13507ddc9b1aSDarren Reed 	hook_notify_run(&hfi->hfi_nhead,
13517ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
13527ddc9b1aSDarren Reed 
13537ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
13547ddc9b1aSDarren Reed 
1355381a2a9aSdr146992 	return (new);
1356381a2a9aSdr146992 }
1357381a2a9aSdr146992 
13587ddc9b1aSDarren Reed /*
13597ddc9b1aSDarren Reed  * Function:	hook_event_init_kstats
13607ddc9b1aSDarren Reed  * Returns:	None
13617ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns this event.
13627ddc9b1aSDarren Reed  *              hei(I) - pointer to the hook event that needs some kstats.
13637ddc9b1aSDarren Reed  *
13647ddc9b1aSDarren Reed  * Create a set of kstats that relate to each event registered with
13657ddc9b1aSDarren Reed  * the hook framework.  A counter is kept for each time the event is
13667ddc9b1aSDarren Reed  * activated and for each time a hook is added or removed.  As the
13677ddc9b1aSDarren Reed  * kstats just count the events as they happen, the total number of
13687ddc9b1aSDarren Reed  * hooks registered must be obtained by subtractived removed from added.
13697ddc9b1aSDarren Reed  */
13707ddc9b1aSDarren Reed static void
13717ddc9b1aSDarren Reed hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
13727ddc9b1aSDarren Reed {
13737ddc9b1aSDarren Reed 	hook_event_kstat_t template = {
13747ddc9b1aSDarren Reed 		{ "hooksAdded",		KSTAT_DATA_UINT64 },
13757ddc9b1aSDarren Reed 		{ "hooksRemoved",	KSTAT_DATA_UINT64 },
13767ddc9b1aSDarren Reed 		{ "events",		KSTAT_DATA_UINT64 }
13777ddc9b1aSDarren Reed 	};
13787ddc9b1aSDarren Reed 	hook_stack_t *hks;
13797ddc9b1aSDarren Reed 
13807ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
13817ddc9b1aSDarren Reed 	hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
13827ddc9b1aSDarren Reed 	    hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
13837ddc9b1aSDarren Reed 	    sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
13847ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
13857ddc9b1aSDarren Reed 
13867ddc9b1aSDarren Reed 	bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
13877ddc9b1aSDarren Reed 
13887ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
13897ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
13907ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_private =
13917ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
13927ddc9b1aSDarren Reed 
13937ddc9b1aSDarren Reed 		kstat_install(hei->hei_kstatp);
13947ddc9b1aSDarren Reed 	}
13957ddc9b1aSDarren Reed }
1396381a2a9aSdr146992 
1397381a2a9aSdr146992 /*
1398381a2a9aSdr146992  * Function:	hook_event_remove
1399*4a9b8375SDarren Reed  * Returns:	int    - 0 = success, else = failure
1400381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1401381a2a9aSdr146992  *		he(I)  - event pointer
1402381a2a9aSdr146992  *
1403381a2a9aSdr146992  * Remove event from event list on specific family
14047ddc9b1aSDarren Reed  *
14057ddc9b1aSDarren Reed  * This function assumes that the caller has received a pointer to a the
14067ddc9b1aSDarren Reed  * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
14077ddc9b1aSDarren Reed  * This the hook_family_int_t is guaranteed to be around for the life of this
14087ddc9b1aSDarren Reed  * call, unless the caller has decided to call net_protocol_release or
14097ddc9b1aSDarren Reed  * net_protocol_unregister before calling net_event_unregister - an error.
1410381a2a9aSdr146992  */
1411381a2a9aSdr146992 int
1412381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
1413381a2a9aSdr146992 {
14147ddc9b1aSDarren Reed 	boolean_t free_family;
1415381a2a9aSdr146992 	hook_event_int_t *hei;
14168ad74188SDarren Reed 	boolean_t notifydone;
1417381a2a9aSdr146992 
1418381a2a9aSdr146992 	ASSERT(hfi != NULL);
1419381a2a9aSdr146992 	ASSERT(he != NULL);
1420381a2a9aSdr146992 
14217ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1422381a2a9aSdr146992 
14237ddc9b1aSDarren Reed 	/*
14247ddc9b1aSDarren Reed 	 * Set the flag so that we can call hook_event_notify_run without
14257ddc9b1aSDarren Reed 	 * holding any locks but at the same time prevent other changes to
14267ddc9b1aSDarren Reed 	 * the event at the same time.
14277ddc9b1aSDarren Reed 	 */
1428*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
14297ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
14307ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1431381a2a9aSdr146992 		return (ENXIO);
1432381a2a9aSdr146992 	}
1433381a2a9aSdr146992 
14347ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, he->he_name);
14357ddc9b1aSDarren Reed 	if (hei == NULL) {
14367ddc9b1aSDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14377ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
14387ddc9b1aSDarren Reed 		return (ESRCH);
1439381a2a9aSdr146992 	}
1440381a2a9aSdr146992 
14417ddc9b1aSDarren Reed 	free_family = B_FALSE;
1442381a2a9aSdr146992 
14437ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
14447ddc9b1aSDarren Reed 	/*
14458ad74188SDarren Reed 	 * The hei_shutdown flag is used to indicate whether or not we have
14468ad74188SDarren Reed 	 * done a shutdown and thus already walked through the notify list.
14478ad74188SDarren Reed 	 */
14488ad74188SDarren Reed 	notifydone = hei->hei_shutdown;
14498ad74188SDarren Reed 	hei->hei_shutdown = B_TRUE;
14508ad74188SDarren Reed 	/*
14517ddc9b1aSDarren Reed 	 * If there are any hooks still registered for this event or
14527ddc9b1aSDarren Reed 	 * there are any notifiers registered, return an error indicating
14537ddc9b1aSDarren Reed 	 * that the event is still busy.
14547ddc9b1aSDarren Reed 	 */
14557ddc9b1aSDarren Reed 	if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
14567ddc9b1aSDarren Reed 		hei->hei_condemned = B_TRUE;
14577ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
14587ddc9b1aSDarren Reed 	} else {
14597ddc9b1aSDarren Reed 		/* hei_condemned = B_FALSE is implied from creation */
14607ddc9b1aSDarren Reed 		/*
14617ddc9b1aSDarren Reed 		 * Even though we know the notify list is empty, we call
14627ddc9b1aSDarren Reed 		 * hook_wait_destroy here to synchronise wait removing a
14637ddc9b1aSDarren Reed 		 * hook from an event.
14647ddc9b1aSDarren Reed 		 */
1465*4a9b8375SDarren Reed 		VERIFY(hook_wait_destroy(&hei->hei_waiter) == 0);
14667ddc9b1aSDarren Reed 
14677ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
14687ddc9b1aSDarren Reed 
14697ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
14707ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead))
14717ddc9b1aSDarren Reed 			free_family = B_TRUE;
14727ddc9b1aSDarren Reed 	}
14737ddc9b1aSDarren Reed 
14747ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
14757ddc9b1aSDarren Reed 
14768ad74188SDarren Reed 	if (!notifydone)
14777ddc9b1aSDarren Reed 		hook_notify_run(&hfi->hfi_nhead,
14787ddc9b1aSDarren Reed 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
14797ddc9b1aSDarren Reed 
14807ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14817ddc9b1aSDarren Reed 
14827ddc9b1aSDarren Reed 	if (!hei->hei_condemned) {
14837ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
14847ddc9b1aSDarren Reed 		if (free_family)
14857ddc9b1aSDarren Reed 			hook_family_free(hfi, hfi->hfi_stack);
14867ddc9b1aSDarren Reed 	}
1487381a2a9aSdr146992 
1488381a2a9aSdr146992 	return (0);
1489381a2a9aSdr146992 }
1490381a2a9aSdr146992 
14917ddc9b1aSDarren Reed /*
14928ad74188SDarren Reed  * Function:	hook_event_shutdown
1493*4a9b8375SDarren Reed  * Returns:	int    - 0 = success, else = failure
14948ad74188SDarren Reed  * Parameters:	hfi(I) - internal family pointer
14958ad74188SDarren Reed  *		he(I)  - event pointer
14968ad74188SDarren Reed  *
14978ad74188SDarren Reed  * As with hook_family_shutdown, we want to generate the notify callbacks
14988ad74188SDarren Reed  * as if the event was being removed but not actually do the remove.
14998ad74188SDarren Reed  */
15008ad74188SDarren Reed int
15018ad74188SDarren Reed hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he)
15028ad74188SDarren Reed {
15038ad74188SDarren Reed 	hook_event_int_t *hei;
15048ad74188SDarren Reed 	boolean_t notifydone;
15058ad74188SDarren Reed 
15068ad74188SDarren Reed 	ASSERT(hfi != NULL);
15078ad74188SDarren Reed 	ASSERT(he != NULL);
15088ad74188SDarren Reed 
15098ad74188SDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
15108ad74188SDarren Reed 
15118ad74188SDarren Reed 	/*
15128ad74188SDarren Reed 	 * Set the flag so that we can call hook_event_notify_run without
15138ad74188SDarren Reed 	 * holding any locks but at the same time prevent other changes to
15148ad74188SDarren Reed 	 * the event at the same time.
15158ad74188SDarren Reed 	 */
1516*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
15178ad74188SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
15188ad74188SDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15198ad74188SDarren Reed 		return (ENXIO);
15208ad74188SDarren Reed 	}
15218ad74188SDarren Reed 
15228ad74188SDarren Reed 	hei = hook_event_find(hfi, he->he_name);
15238ad74188SDarren Reed 	if (hei == NULL) {
15248ad74188SDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15258ad74188SDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15268ad74188SDarren Reed 		return (ESRCH);
15278ad74188SDarren Reed 	}
15288ad74188SDarren Reed 
15298ad74188SDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
15308ad74188SDarren Reed 	notifydone = hei->hei_shutdown;
15318ad74188SDarren Reed 	hei->hei_shutdown = B_TRUE;
15328ad74188SDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
15338ad74188SDarren Reed 
15348ad74188SDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
15358ad74188SDarren Reed 
15368ad74188SDarren Reed 	if (!notifydone)
15378ad74188SDarren Reed 		hook_notify_run(&hfi->hfi_nhead,
15388ad74188SDarren Reed 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
15398ad74188SDarren Reed 
15408ad74188SDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15418ad74188SDarren Reed 
15428ad74188SDarren Reed 	return (0);
15438ad74188SDarren Reed }
15448ad74188SDarren Reed 
15458ad74188SDarren Reed /*
15467ddc9b1aSDarren Reed  * Function:	hook_event_free
15477ddc9b1aSDarren Reed  * Returns:	None
15487ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
15497ddc9b1aSDarren Reed  *
15507ddc9b1aSDarren Reed  * Free alloc memory for event
15517ddc9b1aSDarren Reed  */
15527ddc9b1aSDarren Reed static void
15537ddc9b1aSDarren Reed hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
15547ddc9b1aSDarren Reed {
15557ddc9b1aSDarren Reed 	boolean_t free_family;
15567ddc9b1aSDarren Reed 
15577ddc9b1aSDarren Reed 	ASSERT(hei != NULL);
15587ddc9b1aSDarren Reed 
15597ddc9b1aSDarren Reed 	if (hfi != NULL) {
15607ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hfi->hfi_lock);
15617ddc9b1aSDarren Reed 		/*
15627ddc9b1aSDarren Reed 		 * Remove the event from the hook family's list.
15637ddc9b1aSDarren Reed 		 */
15647ddc9b1aSDarren Reed 		SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
15657ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
15667ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead)) {
15677ddc9b1aSDarren Reed 			free_family = B_TRUE;
15687ddc9b1aSDarren Reed 		} else {
15697ddc9b1aSDarren Reed 			free_family = B_FALSE;
15707ddc9b1aSDarren Reed 		}
15717ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15727ddc9b1aSDarren Reed 	}
15737ddc9b1aSDarren Reed 
15747ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
15757ddc9b1aSDarren Reed 		ASSERT(hfi != NULL);
15767ddc9b1aSDarren Reed 
15777ddc9b1aSDarren Reed 		kstat_delete_netstack(hei->hei_kstatp,
15787ddc9b1aSDarren Reed 		    hfi->hfi_stack->hks_netstackid);
15797ddc9b1aSDarren Reed 		hei->hei_kstatp = NULL;
15807ddc9b1aSDarren Reed 	}
15817ddc9b1aSDarren Reed 
15827ddc9b1aSDarren Reed 	/* Free container */
15837ddc9b1aSDarren Reed 	kmem_free(hei, sizeof (*hei));
15847ddc9b1aSDarren Reed 
15857ddc9b1aSDarren Reed 	if (free_family)
15867ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
15877ddc9b1aSDarren Reed }
1588381a2a9aSdr146992 
1589381a2a9aSdr146992 /*
1590381a2a9aSdr146992  * Function:    hook_event_checkdup
1591381a2a9aSdr146992  * Returns:     internal event pointer - NULL = Not match
1592381a2a9aSdr146992  * Parameters:  he(I) - event pointer
1593381a2a9aSdr146992  *
15947ddc9b1aSDarren Reed  * Search all of the hook families to see if the event being passed in
15957ddc9b1aSDarren Reed  * has already been associated with one.
1596381a2a9aSdr146992  */
1597381a2a9aSdr146992 static hook_event_int_t *
1598f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
1599381a2a9aSdr146992 {
1600381a2a9aSdr146992 	hook_family_int_t *hfi;
1601381a2a9aSdr146992 	hook_event_int_t *hei;
1602381a2a9aSdr146992 
1603381a2a9aSdr146992 	ASSERT(he != NULL);
1604381a2a9aSdr146992 
16057ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
1606f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
1607381a2a9aSdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16087ddc9b1aSDarren Reed 			if (hei->hei_event == he) {
16097ddc9b1aSDarren Reed 				CVW_EXIT_READ(&hks->hks_lock);
1610381a2a9aSdr146992 				return (hei);
1611381a2a9aSdr146992 			}
1612381a2a9aSdr146992 		}
16137ddc9b1aSDarren Reed 	}
16147ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
1615381a2a9aSdr146992 
1616381a2a9aSdr146992 	return (NULL);
1617381a2a9aSdr146992 }
1618381a2a9aSdr146992 
1619381a2a9aSdr146992 /*
1620381a2a9aSdr146992  * Function:	hook_event_copy
1621381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Failed
1622381a2a9aSdr146992  * Parameters:	src(I) - event pointer
1623381a2a9aSdr146992  *
1624381a2a9aSdr146992  * Allocate internal event block and duplicate incoming event
1625381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
1626381a2a9aSdr146992  */
1627381a2a9aSdr146992 static hook_event_int_t *
1628381a2a9aSdr146992 hook_event_copy(hook_event_t *src)
1629381a2a9aSdr146992 {
1630381a2a9aSdr146992 	hook_event_int_t *new;
1631381a2a9aSdr146992 
1632381a2a9aSdr146992 	ASSERT(src != NULL);
1633381a2a9aSdr146992 	ASSERT(src->he_name != NULL);
1634381a2a9aSdr146992 
1635381a2a9aSdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1636381a2a9aSdr146992 
1637381a2a9aSdr146992 	/* Copy body */
1638381a2a9aSdr146992 	TAILQ_INIT(&new->hei_head);
1639381a2a9aSdr146992 	new->hei_event = src;
1640381a2a9aSdr146992 
1641381a2a9aSdr146992 	return (new);
1642381a2a9aSdr146992 }
1643381a2a9aSdr146992 
1644381a2a9aSdr146992 /*
1645381a2a9aSdr146992  * Function:	hook_event_find
1646381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Not match
1647381a2a9aSdr146992  * Parameters:	hfi(I)   - internal family pointer
1648381a2a9aSdr146992  *		event(I) - event name string
1649381a2a9aSdr146992  *
1650381a2a9aSdr146992  * Search event list with event name
16517ddc9b1aSDarren Reed  * 	A lock on hfi->hfi_lock must be held when called.
1652381a2a9aSdr146992  */
1653381a2a9aSdr146992 static hook_event_int_t *
1654381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
1655381a2a9aSdr146992 {
1656381a2a9aSdr146992 	hook_event_int_t *hei = NULL;
1657381a2a9aSdr146992 
1658381a2a9aSdr146992 	ASSERT(hfi != NULL);
1659381a2a9aSdr146992 	ASSERT(event != NULL);
1660381a2a9aSdr146992 
1661381a2a9aSdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16627ddc9b1aSDarren Reed 		if ((strcmp(hei->hei_event->he_name, event) == 0) &&
16637ddc9b1aSDarren Reed 		    ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
1664381a2a9aSdr146992 			break;
1665381a2a9aSdr146992 	}
1666381a2a9aSdr146992 	return (hei);
1667381a2a9aSdr146992 }
1668381a2a9aSdr146992 
1669381a2a9aSdr146992 /*
16707ddc9b1aSDarren Reed  * Function:	hook_event_notify_register
1671*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
16727ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
16737ddc9b1aSDarren Reed  *              event(I)    - name of the event
16747ddc9b1aSDarren Reed  *              callback(I) - function to be called
16757ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
1676381a2a9aSdr146992  *
16777ddc9b1aSDarren Reed  * Adds a new callback to the event named by "event" (we must find it)
16787ddc9b1aSDarren Reed  * that will be executed each time a new hook is added to the event.
16797ddc9b1aSDarren Reed  * Of course, if the stack is being shut down, this call should fail.
1680381a2a9aSdr146992  */
16817ddc9b1aSDarren Reed int
16827ddc9b1aSDarren Reed hook_event_notify_register(hook_family_int_t *hfi, char *event,
16837ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
1684381a2a9aSdr146992 {
16857ddc9b1aSDarren Reed 	hook_event_int_t *hei;
16867ddc9b1aSDarren Reed 	hook_stack_t *hks;
1687*4a9b8375SDarren Reed 	boolean_t canrun;
1688*4a9b8375SDarren Reed 	hook_int_t *h;
16897ddc9b1aSDarren Reed 	int error;
1690381a2a9aSdr146992 
1691*4a9b8375SDarren Reed 	canrun = B_FALSE;
16927ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
16937ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
16947ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
16957ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
16967ddc9b1aSDarren Reed 		return (ESHUTDOWN);
1697381a2a9aSdr146992 	}
1698381a2a9aSdr146992 
16997ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
17007ddc9b1aSDarren Reed 
17018ad74188SDarren Reed 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
17027ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
17037ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
17047ddc9b1aSDarren Reed 		return (ESHUTDOWN);
17057ddc9b1aSDarren Reed 	}
17067ddc9b1aSDarren Reed 
17077ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
17087ddc9b1aSDarren Reed 	if (hei == NULL) {
17097ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
17107ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
17117ddc9b1aSDarren Reed 		return (ESRCH);
17127ddc9b1aSDarren Reed 	}
17137ddc9b1aSDarren Reed 
17148ad74188SDarren Reed 	if (hei->hei_condemned || hei->hei_shutdown) {
17157ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
17167ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
17177ddc9b1aSDarren Reed 		return (ESHUTDOWN);
17187ddc9b1aSDarren Reed 	}
17197ddc9b1aSDarren Reed 
1720*4a9b8375SDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1721*4a9b8375SDarren Reed 	canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1722*4a9b8375SDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1723*4a9b8375SDarren Reed 	error = hook_notify_register(&hei->hei_nhead, callback, arg);
17247ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
1725*4a9b8375SDarren Reed 
17267ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
17277ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
17287ddc9b1aSDarren Reed 
1729*4a9b8375SDarren Reed 	if (error == 0 && canrun) {
1730*4a9b8375SDarren Reed 		TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1731*4a9b8375SDarren Reed 			callback(HN_REGISTER, arg,
1732*4a9b8375SDarren Reed 			    hfi->hfi_family.hf_name, hei->hei_event->he_name,
1733*4a9b8375SDarren Reed 			    h->hi_hook.h_name);
1734*4a9b8375SDarren Reed 		}
1735*4a9b8375SDarren Reed 	}
1736*4a9b8375SDarren Reed 
1737*4a9b8375SDarren Reed 	if (canrun)
1738*4a9b8375SDarren Reed 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1739*4a9b8375SDarren Reed 
17407ddc9b1aSDarren Reed 	return (error);
17417ddc9b1aSDarren Reed }
17427ddc9b1aSDarren Reed 
17437ddc9b1aSDarren Reed /*
17447ddc9b1aSDarren Reed  * Function:	hook_event_notify_unregister
1745*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
17467ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
17477ddc9b1aSDarren Reed  *              event(I)    - name of the event
17487ddc9b1aSDarren Reed  *              callback(I) - function to be called
17497ddc9b1aSDarren Reed  *
17507ddc9b1aSDarren Reed  * Remove the given callback from the named event's list of functions
17517ddc9b1aSDarren Reed  * to call when a hook is added or removed.
17527ddc9b1aSDarren Reed  */
17537ddc9b1aSDarren Reed int
17547ddc9b1aSDarren Reed hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
17557ddc9b1aSDarren Reed     hook_notify_fn_t callback)
17567ddc9b1aSDarren Reed {
17577ddc9b1aSDarren Reed 	hook_event_int_t *hei;
17587ddc9b1aSDarren Reed 	boolean_t free_event;
1759*4a9b8375SDarren Reed 	boolean_t canrun;
1760*4a9b8375SDarren Reed 	hook_int_t *h;
1761*4a9b8375SDarren Reed 	void *arg;
17627ddc9b1aSDarren Reed 	int error;
17637ddc9b1aSDarren Reed 
1764*4a9b8375SDarren Reed 	canrun = B_FALSE;
1765*4a9b8375SDarren Reed 
17667ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
17677ddc9b1aSDarren Reed 
17687ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
17697ddc9b1aSDarren Reed 	if (hei == NULL) {
17707ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
17717ddc9b1aSDarren Reed 		return (ESRCH);
17727ddc9b1aSDarren Reed 	}
17737ddc9b1aSDarren Reed 
17747ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
17757ddc9b1aSDarren Reed 
1776*4a9b8375SDarren Reed 	(void) hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
1777*4a9b8375SDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1778*4a9b8375SDarren Reed 
1779*4a9b8375SDarren Reed 	error = hook_notify_unregister(&hei->hei_nhead, callback, &arg);
1780*4a9b8375SDarren Reed 
1781*4a9b8375SDarren Reed 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
17827ddc9b1aSDarren Reed 
17837ddc9b1aSDarren Reed 	/*
17847ddc9b1aSDarren Reed 	 * hei_condemned has been set if someone tried to remove the
17857ddc9b1aSDarren Reed 	 * event but couldn't because there were still things attached to
17867ddc9b1aSDarren Reed 	 * it. Now that we've done a successful remove, if it is now empty
17877ddc9b1aSDarren Reed 	 * then by all rights we should be free'ing it too.  Note that the
17887ddc9b1aSDarren Reed 	 * expectation is that only the caller of hook_event_add will ever
17897ddc9b1aSDarren Reed 	 * call hook_event_remove.
17907ddc9b1aSDarren Reed 	 */
17917ddc9b1aSDarren Reed 	if ((error == 0) && hei->hei_condemned &&
17927ddc9b1aSDarren Reed 	    TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
17937ddc9b1aSDarren Reed 		free_event = B_TRUE;
17947ddc9b1aSDarren Reed 	} else {
17957ddc9b1aSDarren Reed 		free_event = B_FALSE;
17967ddc9b1aSDarren Reed 	}
17977ddc9b1aSDarren Reed 
1798*4a9b8375SDarren Reed 	if (error == 0 && !free_event) {
1799*4a9b8375SDarren Reed 		canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1800*4a9b8375SDarren Reed 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1801*4a9b8375SDarren Reed 	}
1802*4a9b8375SDarren Reed 
18037ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
18047ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
18057ddc9b1aSDarren Reed 
1806*4a9b8375SDarren Reed 	if (canrun) {
1807*4a9b8375SDarren Reed 		TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1808*4a9b8375SDarren Reed 			callback(HN_UNREGISTER, arg,
1809*4a9b8375SDarren Reed 			    hfi->hfi_family.hf_name, hei->hei_event->he_name,
1810*4a9b8375SDarren Reed 			    h->hi_hook.h_name);
1811*4a9b8375SDarren Reed 		}
1812*4a9b8375SDarren Reed 
1813*4a9b8375SDarren Reed 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1814*4a9b8375SDarren Reed 	}
1815*4a9b8375SDarren Reed 
18167ddc9b1aSDarren Reed 	if (free_event) {
18177ddc9b1aSDarren Reed 		/*
18187ddc9b1aSDarren Reed 		 * It is safe to pass in hfi here, without a lock, because
18197ddc9b1aSDarren Reed 		 * our structure (hei) is still on one of its lists and thus
18207ddc9b1aSDarren Reed 		 * it won't be able to disappear yet...
18217ddc9b1aSDarren Reed 		 */
18227ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
18237ddc9b1aSDarren Reed 	}
18247ddc9b1aSDarren Reed 
18257ddc9b1aSDarren Reed 	return (error);
18267ddc9b1aSDarren Reed }
18277ddc9b1aSDarren Reed 
18287ddc9b1aSDarren Reed /*
18297ddc9b1aSDarren Reed  * Function:	hook_event_notify_run
18307ddc9b1aSDarren Reed  * Returns:	None
18317ddc9b1aSDarren Reed  * Parameters:	nrun(I) - pointer to the list of callbacks to execute
18327ddc9b1aSDarren Reed  *              hfi(I)  - hook stack pointer to execute callbacks for
18337ddc9b1aSDarren Reed  *              name(I) - name of a hook family
18347ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
18357ddc9b1aSDarren Reed  *
18367ddc9b1aSDarren Reed  * Execute all of the callbacks registered for this event.
18377ddc9b1aSDarren Reed  */
18387ddc9b1aSDarren Reed static void
18397ddc9b1aSDarren Reed hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
18407ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd)
18417ddc9b1aSDarren Reed {
18427ddc9b1aSDarren Reed 
18437ddc9b1aSDarren Reed 	hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
18447ddc9b1aSDarren Reed 	    event, name, cmd);
18457ddc9b1aSDarren Reed }
1846381a2a9aSdr146992 
1847381a2a9aSdr146992 /*
1848381a2a9aSdr146992  * Function:	hook_register
1849*4a9b8375SDarren Reed  * Returns:	int      - 0 = success, else = failure
1850381a2a9aSdr146992  * Parameters:	hfi(I)   - internal family pointer
1851381a2a9aSdr146992  *		event(I) - event name string
1852381a2a9aSdr146992  *		h(I)     - hook pointer
1853381a2a9aSdr146992  *
18547ddc9b1aSDarren Reed  * Add new hook to hook list on the specified family and event.
1855381a2a9aSdr146992  */
1856381a2a9aSdr146992 int
1857381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
1858381a2a9aSdr146992 {
1859381a2a9aSdr146992 	hook_event_int_t *hei;
1860381a2a9aSdr146992 	hook_int_t *hi, *new;
18617ddc9b1aSDarren Reed 	int error;
1862381a2a9aSdr146992 
1863381a2a9aSdr146992 	ASSERT(hfi != NULL);
1864381a2a9aSdr146992 	ASSERT(event != NULL);
1865381a2a9aSdr146992 	ASSERT(h != NULL);
18667ddc9b1aSDarren Reed 
18677ddc9b1aSDarren Reed 	if (hfi->hfi_stack->hks_shutdown)
18687ddc9b1aSDarren Reed 		return (NULL);
1869381a2a9aSdr146992 
1870381a2a9aSdr146992 	/* Alloc hook_int_t and copy hook */
1871381a2a9aSdr146992 	new = hook_copy(h);
1872381a2a9aSdr146992 	if (new == NULL)
1873381a2a9aSdr146992 		return (ENOMEM);
1874381a2a9aSdr146992 
1875381a2a9aSdr146992 	/*
1876381a2a9aSdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
1877381a2a9aSdr146992 	 * to hold global family write lock. Just get read lock here to
1878381a2a9aSdr146992 	 * ensure event will not be removed when doing hooks operation
1879381a2a9aSdr146992 	 */
18807ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1881381a2a9aSdr146992 
1882381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
1883381a2a9aSdr146992 	if (hei == NULL) {
18847ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18857ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
1886381a2a9aSdr146992 		return (ENXIO);
1887381a2a9aSdr146992 	}
1888381a2a9aSdr146992 
1889381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
1890381a2a9aSdr146992 
18918ad74188SDarren Reed 	/*
18928ad74188SDarren Reed 	 * If we've run either the remove() or shutdown(), do not allow any
18938ad74188SDarren Reed 	 * more hooks to be added to this event.
18948ad74188SDarren Reed 	 */
18958ad74188SDarren Reed 	if (hei->hei_shutdown) {
18968ad74188SDarren Reed 		error = ESHUTDOWN;
18978ad74188SDarren Reed 		goto bad_add;
18988ad74188SDarren Reed 	}
18998ad74188SDarren Reed 
1900381a2a9aSdr146992 	hi = hook_find(hei, h);
1901381a2a9aSdr146992 	if (hi != NULL) {
19027ddc9b1aSDarren Reed 		error = EEXIST;
19037ddc9b1aSDarren Reed 		goto bad_add;
19047ddc9b1aSDarren Reed 	}
19057ddc9b1aSDarren Reed 
1906*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
19077ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
19087ddc9b1aSDarren Reed 		error = ENOENT;
19097ddc9b1aSDarren Reed bad_add:
1910381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
19117ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
19127ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
19137ddc9b1aSDarren Reed 		return (error);
1914381a2a9aSdr146992 	}
1915381a2a9aSdr146992 
1916381a2a9aSdr146992 	/* Add to hook list head */
19177ddc9b1aSDarren Reed 	error = hook_insert(&hei->hei_head, new);
19187ddc9b1aSDarren Reed 	if (error == 0) {
1919381a2a9aSdr146992 		hei->hei_event->he_interested = B_TRUE;
19207ddc9b1aSDarren Reed 		hei->hei_kstats.hooks_added.value.ui64++;
19217ddc9b1aSDarren Reed 
19227ddc9b1aSDarren Reed 		hook_init_kstats(hfi, hei, new);
19237ddc9b1aSDarren Reed 	}
1924381a2a9aSdr146992 
1925381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
19267ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
19277ddc9b1aSDarren Reed 
19287ddc9b1aSDarren Reed 	/*
19297ddc9b1aSDarren Reed 	 * Note that the name string passed through to the notify callbacks
19307ddc9b1aSDarren Reed 	 * is from the original hook being registered, not the copy being
19317ddc9b1aSDarren Reed 	 * inserted.
19327ddc9b1aSDarren Reed 	 */
1933*4a9b8375SDarren Reed 	if (error == 0)
19347ddc9b1aSDarren Reed 		hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
1935*4a9b8375SDarren Reed 
19367ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
19377ddc9b1aSDarren Reed 
19387ddc9b1aSDarren Reed 	return (error);
19397ddc9b1aSDarren Reed }
19407ddc9b1aSDarren Reed 
19417ddc9b1aSDarren Reed /*
19427ddc9b1aSDarren Reed  * Function:	hook_insert
1943*4a9b8375SDarren Reed  * Returns:	int     - 0 = success, else = failure
19447ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
19457ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
19467ddc9b1aSDarren Reed  *
19477ddc9b1aSDarren Reed  * Try to insert the hook onto the list of hooks according to the hints
19487ddc9b1aSDarren Reed  * given in the hook to be inserted and those that already exist on the
19497ddc9b1aSDarren Reed  * list.  For now, the implementation permits only a single hook to be
19507ddc9b1aSDarren Reed  * either first or last and names provided with before or after are only
19517ddc9b1aSDarren Reed  * loosely coupled with the action.
19527ddc9b1aSDarren Reed  */
19537ddc9b1aSDarren Reed static int
19547ddc9b1aSDarren Reed hook_insert(hook_int_head_t *head, hook_int_t *new)
19557ddc9b1aSDarren Reed {
19567ddc9b1aSDarren Reed 	hook_int_t *before;
19577ddc9b1aSDarren Reed 	hook_int_t *hi;
19587ddc9b1aSDarren Reed 	hook_t *hih;
19597ddc9b1aSDarren Reed 	hook_t *h = &new->hi_hook;
19607ddc9b1aSDarren Reed 
19617ddc9b1aSDarren Reed 	switch (new->hi_hook.h_hint) {
19627ddc9b1aSDarren Reed 	case HH_NONE :
19637ddc9b1aSDarren Reed 		before = NULL;
19647ddc9b1aSDarren Reed 		/*
19657ddc9b1aSDarren Reed 		 * If there is no hint present (or not one that can be
19667ddc9b1aSDarren Reed 		 * satisfied now) then try to at least respect the wishes
19677ddc9b1aSDarren Reed 		 * of those that want to be last.  If there are none wanting
19687ddc9b1aSDarren Reed 		 * to be last then add the new hook to the tail of the
19697ddc9b1aSDarren Reed 		 * list - this means we keep any wanting to be first
19707ddc9b1aSDarren Reed 		 * happy without having to search for HH_FIRST.
19717ddc9b1aSDarren Reed 		 */
19727ddc9b1aSDarren Reed 		TAILQ_FOREACH(hi, head, hi_entry) {
19737ddc9b1aSDarren Reed 			hih = &hi->hi_hook;
19747ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_AFTER) &&
19757ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
19767ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
19777ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
19787ddc9b1aSDarren Reed 				return (0);
19797ddc9b1aSDarren Reed 			}
19807ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
19817ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
19827ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
19837ddc9b1aSDarren Reed 				before = hi;
19847ddc9b1aSDarren Reed 			}
19857ddc9b1aSDarren Reed 		}
19867ddc9b1aSDarren Reed 		if (before != NULL) {
19877ddc9b1aSDarren Reed 			TAILQ_INSERT_AFTER(head, before, new, hi_entry);
19887ddc9b1aSDarren Reed 			return (0);
19897ddc9b1aSDarren Reed 		}
19907ddc9b1aSDarren Reed 		hook_insert_plain(head, new);
19917ddc9b1aSDarren Reed 		break;
19927ddc9b1aSDarren Reed 
19937ddc9b1aSDarren Reed 	case HH_FIRST :
19947ddc9b1aSDarren Reed 		hi = TAILQ_FIRST(head);
19957ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
19967ddc9b1aSDarren Reed 			return (EBUSY);
19977ddc9b1aSDarren Reed 		TAILQ_INSERT_HEAD(head, new, hi_entry);
19987ddc9b1aSDarren Reed 		break;
19997ddc9b1aSDarren Reed 
20007ddc9b1aSDarren Reed 	case HH_LAST :
20017ddc9b1aSDarren Reed 		hi = TAILQ_LAST(head, hook_int_head);
20027ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
20037ddc9b1aSDarren Reed 			return (EBUSY);
20047ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
20057ddc9b1aSDarren Reed 		break;
20067ddc9b1aSDarren Reed 
20077ddc9b1aSDarren Reed 	case HH_BEFORE :
20087ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20097ddc9b1aSDarren Reed 		if (hi == NULL)
20107ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
20117ddc9b1aSDarren Reed 
20127ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_FIRST)
20137ddc9b1aSDarren Reed 			return (EBUSY);
20147ddc9b1aSDarren Reed 
20157ddc9b1aSDarren Reed 		TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20167ddc9b1aSDarren Reed 		break;
20177ddc9b1aSDarren Reed 
20187ddc9b1aSDarren Reed 	case HH_AFTER :
20197ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20207ddc9b1aSDarren Reed 		if (hi == NULL)
20217ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
20227ddc9b1aSDarren Reed 
20237ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST)
20247ddc9b1aSDarren Reed 			return (EBUSY);
20257ddc9b1aSDarren Reed 
20267ddc9b1aSDarren Reed 		TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
20277ddc9b1aSDarren Reed 		break;
20287ddc9b1aSDarren Reed 
20297ddc9b1aSDarren Reed 	default :
20307ddc9b1aSDarren Reed 		return (EINVAL);
20317ddc9b1aSDarren Reed 	}
20327ddc9b1aSDarren Reed 
2033381a2a9aSdr146992 	return (0);
2034381a2a9aSdr146992 }
2035381a2a9aSdr146992 
20367ddc9b1aSDarren Reed /*
20377ddc9b1aSDarren Reed  * Function:	hook_insert_plain
20387ddc9b1aSDarren Reed  * Returns:	int     - 0 = success, else = failure
20397ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
20407ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
20417ddc9b1aSDarren Reed  *
20427ddc9b1aSDarren Reed  * Insert a hook such that it respects the wishes of those that want to
20437ddc9b1aSDarren Reed  * be last.  If there are none wanting to be last then add the new hook
20447ddc9b1aSDarren Reed  * to the tail of the list - this means we keep any wanting to be first
20457ddc9b1aSDarren Reed  * happy without having to search for HH_FIRST.
20467ddc9b1aSDarren Reed  */
20477ddc9b1aSDarren Reed static void
20487ddc9b1aSDarren Reed hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
20497ddc9b1aSDarren Reed {
20507ddc9b1aSDarren Reed 	hook_int_t *hi;
20517ddc9b1aSDarren Reed 
20527ddc9b1aSDarren Reed 	hi = TAILQ_FIRST(head);
20537ddc9b1aSDarren Reed 	if (hi != NULL) {
20547ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST) {
20557ddc9b1aSDarren Reed 			TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20567ddc9b1aSDarren Reed 		} else {
20577ddc9b1aSDarren Reed 			TAILQ_INSERT_TAIL(head, new, hi_entry);
20587ddc9b1aSDarren Reed 		}
20597ddc9b1aSDarren Reed 	} else {
20607ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
20617ddc9b1aSDarren Reed 	}
20627ddc9b1aSDarren Reed }
20637ddc9b1aSDarren Reed 
20647ddc9b1aSDarren Reed /*
20657ddc9b1aSDarren Reed  * Function:	hook_insert_afterbefore
20667ddc9b1aSDarren Reed  * Returns:	int     - 0 = success, else = failure
20677ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
20687ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
20697ddc9b1aSDarren Reed  *
20707ddc9b1aSDarren Reed  * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
20717ddc9b1aSDarren Reed  * possible, so now we need to be more careful.  The first pass is to go
20727ddc9b1aSDarren Reed  * through the list and look for any other hooks that also specify the
20737ddc9b1aSDarren Reed  * same hint name as the new one.  The object of this exercise is to make
20747ddc9b1aSDarren Reed  * sure that hooks with HH_BEFORE always appear on the list before those
20757ddc9b1aSDarren Reed  * with HH_AFTER so that when said hook arrives, it can be placed in the
20767ddc9b1aSDarren Reed  * middle of the BEFOREs and AFTERs.  If this condition does not arise,
20777ddc9b1aSDarren Reed  * just use hook_insert_plain() to try and insert the hook somewhere that
20787ddc9b1aSDarren Reed  * is innocuous to existing efforts.
20797ddc9b1aSDarren Reed  */
20807ddc9b1aSDarren Reed static int
20817ddc9b1aSDarren Reed hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
20827ddc9b1aSDarren Reed {
20837ddc9b1aSDarren Reed 	hook_int_t *hi;
20847ddc9b1aSDarren Reed 	hook_t *nh;
20857ddc9b1aSDarren Reed 	hook_t *h;
20867ddc9b1aSDarren Reed 
20877ddc9b1aSDarren Reed 	nh = &new->hi_hook;
20887ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_NONE);
20897ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_LAST);
20907ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_FIRST);
20917ddc9b1aSDarren Reed 
20927ddc9b1aSDarren Reed 	/*
20937ddc9b1aSDarren Reed 	 * First, look through the list to see if there are any other
20947ddc9b1aSDarren Reed 	 * before's or after's that have a matching hint name.
20957ddc9b1aSDarren Reed 	 */
20967ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
20977ddc9b1aSDarren Reed 		h = &hi->hi_hook;
20987ddc9b1aSDarren Reed 		switch (h->h_hint) {
20997ddc9b1aSDarren Reed 		case HH_FIRST :
21007ddc9b1aSDarren Reed 		case HH_LAST :
21017ddc9b1aSDarren Reed 		case HH_NONE :
21027ddc9b1aSDarren Reed 			break;
21037ddc9b1aSDarren Reed 		case HH_BEFORE :
21047ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
21057ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
21067ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
21077ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21087ddc9b1aSDarren Reed 				return (0);
21097ddc9b1aSDarren Reed 			}
21107ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
21117ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
21127ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
21137ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21147ddc9b1aSDarren Reed 				return (0);
21157ddc9b1aSDarren Reed 			}
21167ddc9b1aSDarren Reed 			break;
21177ddc9b1aSDarren Reed 		case HH_AFTER :
21187ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
21197ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
21207ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
21217ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21227ddc9b1aSDarren Reed 				return (0);
21237ddc9b1aSDarren Reed 			}
21247ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
21257ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
21267ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
21277ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21287ddc9b1aSDarren Reed 				return (0);
21297ddc9b1aSDarren Reed 			}
21307ddc9b1aSDarren Reed 			break;
21317ddc9b1aSDarren Reed 		}
21327ddc9b1aSDarren Reed 	}
21337ddc9b1aSDarren Reed 
21347ddc9b1aSDarren Reed 	hook_insert_plain(head, new);
21357ddc9b1aSDarren Reed 
21367ddc9b1aSDarren Reed 	return (0);
21377ddc9b1aSDarren Reed }
2138381a2a9aSdr146992 
2139381a2a9aSdr146992 /*
2140381a2a9aSdr146992  * Function:	hook_unregister
2141*4a9b8375SDarren Reed  * Returns:	int      - 0 = success, else = failure
2142381a2a9aSdr146992  * Parameters:	hfi(I)   - internal family pointer
2143381a2a9aSdr146992  *		event(I) - event name string
2144381a2a9aSdr146992  *		h(I)     - hook pointer
2145381a2a9aSdr146992  *
2146381a2a9aSdr146992  * Remove hook from hook list on specific family, event
2147381a2a9aSdr146992  */
2148381a2a9aSdr146992 int
2149381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
2150381a2a9aSdr146992 {
2151381a2a9aSdr146992 	hook_event_int_t *hei;
2152381a2a9aSdr146992 	hook_int_t *hi;
21537ddc9b1aSDarren Reed 	boolean_t free_event;
2154381a2a9aSdr146992 
2155381a2a9aSdr146992 	ASSERT(hfi != NULL);
2156381a2a9aSdr146992 	ASSERT(h != NULL);
2157381a2a9aSdr146992 
21587ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
2159381a2a9aSdr146992 
2160381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
2161381a2a9aSdr146992 	if (hei == NULL) {
21627ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
2163381a2a9aSdr146992 		return (ENXIO);
2164381a2a9aSdr146992 	}
2165381a2a9aSdr146992 
2166381a2a9aSdr146992 	/* Hold write lock for event */
2167381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
2168381a2a9aSdr146992 
2169381a2a9aSdr146992 	hi = hook_find(hei, h);
2170381a2a9aSdr146992 	if (hi == NULL) {
2171381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
21727ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
2173381a2a9aSdr146992 		return (ENXIO);
2174381a2a9aSdr146992 	}
2175381a2a9aSdr146992 
2176*4a9b8375SDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
21777ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
21787ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
21797ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
21807ddc9b1aSDarren Reed 		return (ENOENT);
21817ddc9b1aSDarren Reed 	}
21827ddc9b1aSDarren Reed 
2183381a2a9aSdr146992 	/* Remove from hook list */
2184381a2a9aSdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
21857ddc9b1aSDarren Reed 
21867ddc9b1aSDarren Reed 	free_event = B_FALSE;
2187381a2a9aSdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
2188381a2a9aSdr146992 		hei->hei_event->he_interested = B_FALSE;
21897ddc9b1aSDarren Reed 		/*
21907ddc9b1aSDarren Reed 		 * If the delete pending flag has been set and there are
21917ddc9b1aSDarren Reed 		 * no notifiers on the event (and we've removed the last
21927ddc9b1aSDarren Reed 		 * hook) then we need to free this event after we're done.
21937ddc9b1aSDarren Reed 		 */
21947ddc9b1aSDarren Reed 		if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
21957ddc9b1aSDarren Reed 			free_event = B_TRUE;
2196381a2a9aSdr146992 	}
21977ddc9b1aSDarren Reed 	hei->hei_kstats.hooks_removed.value.ui64++;
2198381a2a9aSdr146992 
2199381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
22007ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
22017ddc9b1aSDarren Reed 	/*
22027ddc9b1aSDarren Reed 	 * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
22037ddc9b1aSDarren Reed 	 * will not be free'd and thus the hook_family_int_t wil not
22047ddc9b1aSDarren Reed 	 * be free'd either.
22057ddc9b1aSDarren Reed 	 */
22067ddc9b1aSDarren Reed 	hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
22077ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
2208381a2a9aSdr146992 
22097ddc9b1aSDarren Reed 	hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
22107ddc9b1aSDarren Reed 
22117ddc9b1aSDarren Reed 	if (free_event)
22127ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
22137ddc9b1aSDarren Reed 
2214381a2a9aSdr146992 	return (0);
2215381a2a9aSdr146992 }
2216381a2a9aSdr146992 
22177ddc9b1aSDarren Reed /*
22187ddc9b1aSDarren Reed  * Function:	hook_find_byname
22197ddc9b1aSDarren Reed  * Returns:	internal hook pointer - NULL = Not match
22207ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
22217ddc9b1aSDarren Reed  *		name(I)- hook name
22227ddc9b1aSDarren Reed  *
22237ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is a hook present that
22247ddc9b1aSDarren Reed  * has a matching name to the one being looked for.
22257ddc9b1aSDarren Reed  */
22267ddc9b1aSDarren Reed static hook_int_t *
22277ddc9b1aSDarren Reed hook_find_byname(hook_int_head_t *head, char *name)
22287ddc9b1aSDarren Reed {
22297ddc9b1aSDarren Reed 	hook_int_t *hi;
22307ddc9b1aSDarren Reed 
22317ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
22327ddc9b1aSDarren Reed 		if (strcmp(hi->hi_hook.h_name, name) == 0)
22337ddc9b1aSDarren Reed 			return (hi);
22347ddc9b1aSDarren Reed 	}
22357ddc9b1aSDarren Reed 
22367ddc9b1aSDarren Reed 	return (NULL);
22377ddc9b1aSDarren Reed }
2238381a2a9aSdr146992 
2239381a2a9aSdr146992 /*
2240381a2a9aSdr146992  * Function:	hook_find
2241381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Not match
2242381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
2243381a2a9aSdr146992  *		h(I)   - hook pointer
2244381a2a9aSdr146992  *
22457ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is already one that
22467ddc9b1aSDarren Reed  * matches the hook being passed in.  Currently the only criteria for a
22477ddc9b1aSDarren Reed  * successful search here is for the names to be the same.
2248381a2a9aSdr146992  */
2249381a2a9aSdr146992 static hook_int_t *
2250381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
2251381a2a9aSdr146992 {
2252381a2a9aSdr146992 
2253381a2a9aSdr146992 	ASSERT(hei != NULL);
2254381a2a9aSdr146992 	ASSERT(h != NULL);
2255381a2a9aSdr146992 
22567ddc9b1aSDarren Reed 	return (hook_find_byname(&hei->hei_head, h->h_name));
2257381a2a9aSdr146992 }
2258381a2a9aSdr146992 
2259381a2a9aSdr146992 /*
2260381a2a9aSdr146992  * Function:	hook_copy
2261381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Failed
2262381a2a9aSdr146992  * Parameters:	src(I) - hook pointer
2263381a2a9aSdr146992  *
2264381a2a9aSdr146992  * Allocate internal hook block and duplicate incoming hook.
2265381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
22667ddc9b1aSDarren Reed  * Because hook_copy() is responsible for the creation of the internal
22677ddc9b1aSDarren Reed  * hook structure that is used here, it takes on population the structure
22687ddc9b1aSDarren Reed  * with the kstat information.  Note that while the kstat bits are
22697ddc9b1aSDarren Reed  * seeded here, their installation of the kstats is handled elsewhere.
2270381a2a9aSdr146992  */
2271381a2a9aSdr146992 static hook_int_t *
2272381a2a9aSdr146992 hook_copy(hook_t *src)
2273381a2a9aSdr146992 {
2274381a2a9aSdr146992 	hook_int_t *new;
2275381a2a9aSdr146992 	hook_t *dst;
22767ddc9b1aSDarren Reed 	int len;
2277381a2a9aSdr146992 
2278381a2a9aSdr146992 	ASSERT(src != NULL);
2279381a2a9aSdr146992 	ASSERT(src->h_name != NULL);
2280381a2a9aSdr146992 
2281381a2a9aSdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
2282381a2a9aSdr146992 
2283381a2a9aSdr146992 	/* Copy body */
2284381a2a9aSdr146992 	dst = &new->hi_hook;
2285381a2a9aSdr146992 	*dst = *src;
2286381a2a9aSdr146992 
2287381a2a9aSdr146992 	/* Copy name */
22887ddc9b1aSDarren Reed 	len = strlen(src->h_name);
22897ddc9b1aSDarren Reed 	dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
2290381a2a9aSdr146992 	(void) strcpy(dst->h_name, src->h_name);
2291381a2a9aSdr146992 
22927ddc9b1aSDarren Reed 	/*
22937ddc9b1aSDarren Reed 	 * This is initialised in this manner to make it safer to use the
22947ddc9b1aSDarren Reed 	 * same pointer in the kstats field.
22957ddc9b1aSDarren Reed 	 */
22967ddc9b1aSDarren Reed 	dst->h_hintvalue = (uintptr_t)"";
22977ddc9b1aSDarren Reed 
22987ddc9b1aSDarren Reed 	if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
22997ddc9b1aSDarren Reed 		len = strlen((char *)src->h_hintvalue);
23007ddc9b1aSDarren Reed 		if (len > 0) {
23017ddc9b1aSDarren Reed 			dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
23027ddc9b1aSDarren Reed 			    KM_SLEEP);
23037ddc9b1aSDarren Reed 			(void) strcpy((char *)dst->h_hintvalue,
23047ddc9b1aSDarren Reed 			    (char *)src->h_hintvalue);
23057ddc9b1aSDarren Reed 		}
23067ddc9b1aSDarren Reed 	}
23077ddc9b1aSDarren Reed 
2308381a2a9aSdr146992 	return (new);
2309381a2a9aSdr146992 }
2310381a2a9aSdr146992 
2311381a2a9aSdr146992 /*
23127ddc9b1aSDarren Reed  * Function:	hook_init_kstats
23137ddc9b1aSDarren Reed  * Returns:	None
23147ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns the event.
23157ddc9b1aSDarren Reed  *              hei(I) - pointer to the event that owns this hook
23167ddc9b1aSDarren Reed  *              hi(I)  - pointer to the hook for which we create kstats for
23177ddc9b1aSDarren Reed  *
23187ddc9b1aSDarren Reed  * Each hook that is registered with this framework has its own kstats
23197ddc9b1aSDarren Reed  * set up so that we can provide an easy way in which to observe the
23207ddc9b1aSDarren Reed  * look of hooks (using the kstat command.) The position is set to 0
23217ddc9b1aSDarren Reed  * here but is recalculated after we know the insertion has been a
23227ddc9b1aSDarren Reed  * success.
23237ddc9b1aSDarren Reed  */
23247ddc9b1aSDarren Reed static void
23257ddc9b1aSDarren Reed hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
23267ddc9b1aSDarren Reed {
23277ddc9b1aSDarren Reed 	hook_hook_kstat_t template = {
23287ddc9b1aSDarren Reed 		{ "version",			KSTAT_DATA_INT32 },
23297ddc9b1aSDarren Reed 		{ "flags",			KSTAT_DATA_UINT32 },
23307ddc9b1aSDarren Reed 		{ "hint",			KSTAT_DATA_INT32 },
23317ddc9b1aSDarren Reed 		{ "hint_value",			KSTAT_DATA_UINT64 },
23327ddc9b1aSDarren Reed 		{ "position",			KSTAT_DATA_INT32 },
23337ddc9b1aSDarren Reed 		{ "hook_hits",			KSTAT_DATA_UINT64 }
23347ddc9b1aSDarren Reed 	};
23357ddc9b1aSDarren Reed 	hook_stack_t *hks;
23367ddc9b1aSDarren Reed 	size_t kslen;
23377ddc9b1aSDarren Reed 	int position;
23387ddc9b1aSDarren Reed 	hook_int_t *h;
23397ddc9b1aSDarren Reed 
23407ddc9b1aSDarren Reed 	kslen = strlen(hfi->hfi_family.hf_name) +
23417ddc9b1aSDarren Reed 	    strlen(hei->hei_event->he_name) + 2;
23427ddc9b1aSDarren Reed 
23437ddc9b1aSDarren Reed 	hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
23447ddc9b1aSDarren Reed 	(void) snprintf(hi->hi_ksname, kslen, "%s/%s",
23457ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, hei->hei_event->he_name);
23467ddc9b1aSDarren Reed 
23477ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
23487ddc9b1aSDarren Reed 	hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
23497ddc9b1aSDarren Reed 	    hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
23507ddc9b1aSDarren Reed 	    sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
23517ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
23527ddc9b1aSDarren Reed 
23537ddc9b1aSDarren Reed 	/* Initialise the kstats for the structure */
23547ddc9b1aSDarren Reed 	bcopy(&template, &hi->hi_kstats, sizeof (template));
23557ddc9b1aSDarren Reed 	hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
23567ddc9b1aSDarren Reed 	hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
23577ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
23587ddc9b1aSDarren Reed 	hi->hi_kstats.hook_position.value.i32 = 0;
23597ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hits.value.ui64 = 0;
23607ddc9b1aSDarren Reed 
23617ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
23627ddc9b1aSDarren Reed 	case HH_BEFORE :
23637ddc9b1aSDarren Reed 	case HH_AFTER :
23647ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
23657ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.value.ui64 =
23667ddc9b1aSDarren Reed 		    hi->hi_hook.h_hintvalue;
23677ddc9b1aSDarren Reed 		break;
23687ddc9b1aSDarren Reed 	default :
23697ddc9b1aSDarren Reed 		break;
23707ddc9b1aSDarren Reed 	}
23717ddc9b1aSDarren Reed 
23727ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL) {
23737ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
23747ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_private =
23757ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
23767ddc9b1aSDarren Reed 
23777ddc9b1aSDarren Reed 		kstat_install(hi->hi_kstatp);
23787ddc9b1aSDarren Reed 	}
23797ddc9b1aSDarren Reed 
23807ddc9b1aSDarren Reed 	position = 1;
23817ddc9b1aSDarren Reed 	TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
23827ddc9b1aSDarren Reed 		h->hi_kstats.hook_position.value.ui32 = position++;
23837ddc9b1aSDarren Reed 	}
23847ddc9b1aSDarren Reed }
23857ddc9b1aSDarren Reed 
23867ddc9b1aSDarren Reed /*
23877ddc9b1aSDarren Reed  * Function:	hook_int_free
2388381a2a9aSdr146992  * Returns:	None
2389381a2a9aSdr146992  * Parameters:	hi(I) - internal hook pointer
2390381a2a9aSdr146992  *
2391*4a9b8375SDarren Reed  * Free memory allocated to support a hook.
2392381a2a9aSdr146992  */
2393381a2a9aSdr146992 static void
23947ddc9b1aSDarren Reed hook_int_free(hook_int_t *hi, netstackid_t stackid)
2395381a2a9aSdr146992 {
23967ddc9b1aSDarren Reed 	int len;
23977ddc9b1aSDarren Reed 
2398381a2a9aSdr146992 	ASSERT(hi != NULL);
2399381a2a9aSdr146992 
2400381a2a9aSdr146992 	/* Free name space */
2401381a2a9aSdr146992 	if (hi->hi_hook.h_name != NULL) {
2402381a2a9aSdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
2403381a2a9aSdr146992 	}
24047ddc9b1aSDarren Reed 	if (hi->hi_ksname != NULL) {
24057ddc9b1aSDarren Reed 		kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
24067ddc9b1aSDarren Reed 	}
24077ddc9b1aSDarren Reed 
24087ddc9b1aSDarren Reed 	/* Free the name used with the before/after hints. */
24097ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
24107ddc9b1aSDarren Reed 	case HH_BEFORE :
24117ddc9b1aSDarren Reed 	case HH_AFTER :
24127ddc9b1aSDarren Reed 		len = strlen((char *)hi->hi_hook.h_hintvalue);
24137ddc9b1aSDarren Reed 		if (len > 0)
24147ddc9b1aSDarren Reed 			kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
24157ddc9b1aSDarren Reed 		break;
24167ddc9b1aSDarren Reed 	default :
24177ddc9b1aSDarren Reed 		break;
24187ddc9b1aSDarren Reed 	}
24197ddc9b1aSDarren Reed 
24207ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL)
24217ddc9b1aSDarren Reed 		kstat_delete_netstack(hi->hi_kstatp, stackid);
2422381a2a9aSdr146992 
2423381a2a9aSdr146992 	/* Free container */
2424381a2a9aSdr146992 	kmem_free(hi, sizeof (*hi));
2425381a2a9aSdr146992 }
24267ddc9b1aSDarren Reed 
24277ddc9b1aSDarren Reed /*
24287ddc9b1aSDarren Reed  * Function:	hook_alloc
24297ddc9b1aSDarren Reed  * Returns:	hook_t *   - pointer to new hook structure
24307ddc9b1aSDarren Reed  * Parameters:	version(I) - version number of the API when compiled
24317ddc9b1aSDarren Reed  *
24327ddc9b1aSDarren Reed  * This function serves as the interface for consumers to obtain a hook_t
24337ddc9b1aSDarren Reed  * structure.  At this point in time, there is only a single "version" of
24347ddc9b1aSDarren Reed  * it, leading to a straight forward function.  In a perfect world the
24357ddc9b1aSDarren Reed  * h_vesion would be a protected data structure member, but C isn't that
24367ddc9b1aSDarren Reed  * advanced...
24377ddc9b1aSDarren Reed  */
24387ddc9b1aSDarren Reed hook_t *
24397ddc9b1aSDarren Reed hook_alloc(const int h_version)
24407ddc9b1aSDarren Reed {
24417ddc9b1aSDarren Reed 	hook_t *h;
24427ddc9b1aSDarren Reed 
24437ddc9b1aSDarren Reed 	h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
24447ddc9b1aSDarren Reed 	h->h_version = h_version;
24457ddc9b1aSDarren Reed 	return (h);
24467ddc9b1aSDarren Reed }
24477ddc9b1aSDarren Reed 
24487ddc9b1aSDarren Reed /*
24497ddc9b1aSDarren Reed  * Function:	hook_free
24507ddc9b1aSDarren Reed  * Returns:	None
24517ddc9b1aSDarren Reed  * Parameters:	h(I) - external hook pointer
24527ddc9b1aSDarren Reed  *
24537ddc9b1aSDarren Reed  * This function only free's memory allocated with hook_alloc(), so that if
24547ddc9b1aSDarren Reed  * (for example) kernel memory was allocated for h_name, this needs to be
24557ddc9b1aSDarren Reed  * free'd before calling hook_free().
24567ddc9b1aSDarren Reed  */
24577ddc9b1aSDarren Reed void
24587ddc9b1aSDarren Reed hook_free(hook_t *h)
24597ddc9b1aSDarren Reed {
24607ddc9b1aSDarren Reed 	kmem_free(h, sizeof (*h));
24617ddc9b1aSDarren Reed }
24627ddc9b1aSDarren Reed 
24637ddc9b1aSDarren Reed /*
24647ddc9b1aSDarren Reed  * Function:	hook_notify_register
2465*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
2466*4a9b8375SDarren Reed  * Parameters:	head(I)     - top of the list of callbacks
24677ddc9b1aSDarren Reed  *              callback(I) - function to be called
24687ddc9b1aSDarren Reed  *              arg(I)      - arg to pass back to the function
24697ddc9b1aSDarren Reed  *
24707ddc9b1aSDarren Reed  * This function implements the modification of the list of callbacks
24717ddc9b1aSDarren Reed  * that are registered when someone wants to be advised of a change
24727ddc9b1aSDarren Reed  * that has happened.
24737ddc9b1aSDarren Reed  */
24747ddc9b1aSDarren Reed static int
2475*4a9b8375SDarren Reed hook_notify_register(hook_notify_head_t *head, hook_notify_fn_t callback,
2476*4a9b8375SDarren Reed     void *arg)
24777ddc9b1aSDarren Reed {
24787ddc9b1aSDarren Reed 	hook_notify_t *hn;
24797ddc9b1aSDarren Reed 
24807ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
24817ddc9b1aSDarren Reed 		if (hn->hn_func == callback) {
24827ddc9b1aSDarren Reed 			return (EEXIST);
24837ddc9b1aSDarren Reed 		}
24847ddc9b1aSDarren Reed 	}
24857ddc9b1aSDarren Reed 
24867ddc9b1aSDarren Reed 	hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
24877ddc9b1aSDarren Reed 	hn->hn_func = callback;
24887ddc9b1aSDarren Reed 	hn->hn_arg = arg;
24897ddc9b1aSDarren Reed 	TAILQ_INSERT_TAIL(head, hn, hn_entry);
24907ddc9b1aSDarren Reed 
24917ddc9b1aSDarren Reed 	return (0);
24927ddc9b1aSDarren Reed }
24937ddc9b1aSDarren Reed 
24947ddc9b1aSDarren Reed /*
2495*4a9b8375SDarren Reed  * Function:	hook_notify_unregister
2496*4a9b8375SDarren Reed  * Returns:	int         - 0 = success, else failure
24977ddc9b1aSDarren Reed  * Parameters:	stackid(I)  - netstack identifier
24987ddc9b1aSDarren Reed  *              callback(I) - function to be called
2499*4a9b8375SDarren Reed  *              parg(O)     - pointer to storage for pointer
25007ddc9b1aSDarren Reed  *
2501*4a9b8375SDarren Reed  * When calling this function, the provision of a valid pointer in parg
2502*4a9b8375SDarren Reed  * allows the caller to be made aware of what argument the hook function
2503*4a9b8375SDarren Reed  * was expecting. This then allows the simulation of HN_UNREGISTER events
2504*4a9b8375SDarren Reed  * when a notify-unregister is performed.
25057ddc9b1aSDarren Reed  */
25067ddc9b1aSDarren Reed static int
2507*4a9b8375SDarren Reed hook_notify_unregister(hook_notify_head_t *head,
2508*4a9b8375SDarren Reed     hook_notify_fn_t callback, void **parg)
25097ddc9b1aSDarren Reed {
25107ddc9b1aSDarren Reed 	hook_notify_t *hn;
25117ddc9b1aSDarren Reed 
2512*4a9b8375SDarren Reed 	ASSERT(parg != NULL);
25137ddc9b1aSDarren Reed 
25147ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
25157ddc9b1aSDarren Reed 		if (hn->hn_func == callback)
25167ddc9b1aSDarren Reed 			break;
25177ddc9b1aSDarren Reed 	}
2518*4a9b8375SDarren Reed 
2519*4a9b8375SDarren Reed 	if (hn == NULL)
25207ddc9b1aSDarren Reed 		return (ESRCH);
2521*4a9b8375SDarren Reed 
2522*4a9b8375SDarren Reed 	*parg = hn->hn_arg;
25237ddc9b1aSDarren Reed 
25247ddc9b1aSDarren Reed 	TAILQ_REMOVE(head, hn, hn_entry);
25257ddc9b1aSDarren Reed 
25267ddc9b1aSDarren Reed 	kmem_free(hn, sizeof (*hn));
25277ddc9b1aSDarren Reed 
25287ddc9b1aSDarren Reed 	return (0);
25297ddc9b1aSDarren Reed }
25307ddc9b1aSDarren Reed 
25317ddc9b1aSDarren Reed /*
25327ddc9b1aSDarren Reed  * Function:	hook_notify_run
25337ddc9b1aSDarren Reed  * Returns:	None
25347ddc9b1aSDarren Reed  * Parameters:	head(I)   - top of the list of callbacks
25357ddc9b1aSDarren Reed  *              family(I) - name of the hook family that owns the event
25367ddc9b1aSDarren Reed  *              event(I)  - name of the event being changed
25377ddc9b1aSDarren Reed  *              name(I)   - name of the object causing change
25387ddc9b1aSDarren Reed  *              cmd(I)    - either HN_UNREGISTER or HN_REGISTER
25397ddc9b1aSDarren Reed  *
25407ddc9b1aSDarren Reed  * This function walks through the list of registered callbacks and
25417ddc9b1aSDarren Reed  * executes each one, passing back the arg supplied when registered
25427ddc9b1aSDarren Reed  * and the name of the family (that owns the event), event (the thing
25437ddc9b1aSDarren Reed  * to which we're making a change) and finally a name that describes
25447ddc9b1aSDarren Reed  * what is being added or removed, as indicated by cmd.
25457ddc9b1aSDarren Reed  *
25467ddc9b1aSDarren Reed  * This function does not acquire or release any lock as it is required
25477ddc9b1aSDarren Reed  * that code calling it do so before hand.  The use of hook_notify_head_t
25487ddc9b1aSDarren Reed  * is protected by the use of flagwait_t in the structures that own this
25497ddc9b1aSDarren Reed  * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
25507ddc9b1aSDarren Reed  */
25517ddc9b1aSDarren Reed static void
25527ddc9b1aSDarren Reed hook_notify_run(hook_notify_head_t *head, char *family, char *event,
25537ddc9b1aSDarren Reed     char *name, hook_notify_cmd_t cmd)
25547ddc9b1aSDarren Reed {
25557ddc9b1aSDarren Reed 	hook_notify_t *hn;
25567ddc9b1aSDarren Reed 
25577ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
25587ddc9b1aSDarren Reed 		(*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
25597ddc9b1aSDarren Reed 	}
25607ddc9b1aSDarren Reed }
2561