xref: /titanic_50/usr/src/uts/common/io/hook.c (revision 7ddc9b1afd18f260b9fb78ec7732facd91769131)
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 /*
22*7ddc9b1aSDarren 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>
35*7ddc9b1aSDarren 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 /*
53*7ddc9b1aSDarren Reed  * How it works.
54*7ddc9b1aSDarren Reed  * =============
55*7ddc9b1aSDarren Reed  * Use of the hook framework here is tied up with zones - when a new zone
56*7ddc9b1aSDarren Reed  * is created, we create a new hook_stack_t and are open to business for
57*7ddc9b1aSDarren Reed  * allowing new hook families and their events.
58*7ddc9b1aSDarren Reed  *
59*7ddc9b1aSDarren Reed  * A consumer of these hooks is expected to operate in this fashion:
60*7ddc9b1aSDarren Reed  * 1) call hook_family_add() to create a new family of hooks. It is a
61*7ddc9b1aSDarren Reed  *    current requirement that this call must be made with the value
62*7ddc9b1aSDarren Reed  *    returned from hook_stack_init, by way of infrastructure elsewhere.
63*7ddc9b1aSDarren Reed  * 2) add events to the registered family with calls to hook_event_add.
64*7ddc9b1aSDarren Reed  *
65*7ddc9b1aSDarren Reed  * At this point, the structures in place should be open to others to
66*7ddc9b1aSDarren Reed  * add hooks to the event or add notifiers for when the contents of the
67*7ddc9b1aSDarren Reed  * hook stack changes.
68*7ddc9b1aSDarren Reed  *
69*7ddc9b1aSDarren Reed  * The interesting stuff happens on teardown.
70*7ddc9b1aSDarren Reed  *
71*7ddc9b1aSDarren Reed  * It is a requirement that the provider of hook events work in the reverse
72*7ddc9b1aSDarren Reed  * order to the above, so that the first step is:
73*7ddc9b1aSDarren Reed  * 1) remove events from each hook family created earlier
74*7ddc9b1aSDarren Reed  * 2) remove hook families from the hook stack.
75*7ddc9b1aSDarren Reed  *
76*7ddc9b1aSDarren Reed  * When doing teardown of both events and families, a check is made to see
77*7ddc9b1aSDarren Reed  * if either structure is still "busy". If so then a boolean flag is set to
78*7ddc9b1aSDarren Reed  * say that the structure is condemned. The presence of this flag being set
79*7ddc9b1aSDarren Reed  * must be checked for in _add()/_register()/ functions and a failure returned
80*7ddc9b1aSDarren Reed  * if it is set. It is ignored by the _find() functions because they're
81*7ddc9b1aSDarren Reed  * used by _remove()/_unregister().  While setting the condemned flag when
82*7ddc9b1aSDarren Reed  * trying to delete a structure would normally be keyed from the presence
83*7ddc9b1aSDarren Reed  * of a reference count being greater than 1, in this implementation there
84*7ddc9b1aSDarren Reed  * are no reference counts required: instead the presence of objects on
85*7ddc9b1aSDarren Reed  * linked lists is taken to mean something is still "busy."
86*7ddc9b1aSDarren Reed  *
87*7ddc9b1aSDarren Reed  * ONLY the caller that adds the family and the events ever has a direct
88*7ddc9b1aSDarren Reed  * reference to the internal structures and thus ONLY it should be doing
89*7ddc9b1aSDarren Reed  * the removal of either the event or family.  In practise, what this means
90*7ddc9b1aSDarren Reed  * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
91*7ddc9b1aSDarren Reed  * by net_event_register() (these interface to hook_family_add() and
92*7ddc9b1aSDarren Reed  * hook_event_add(), respectively) that are made when we create an instance
93*7ddc9b1aSDarren Reed  * of IP and when the IP instance is shutdown/destroyed, it calls
94*7ddc9b1aSDarren Reed  * net_event_unregister() and net_protocol_unregister(), which in turn call
95*7ddc9b1aSDarren Reed  * hook_event_remove() and hook_family_remove() respectively. Nobody else
96*7ddc9b1aSDarren Reed  * is entitled to call the _unregister() functions.  It is imperative that
97*7ddc9b1aSDarren Reed  * there be only one _remove() call for every _add() call.
98*7ddc9b1aSDarren Reed  *
99*7ddc9b1aSDarren Reed  * It is possible that code which is interfacing with this hook framework
100*7ddc9b1aSDarren Reed  * won't do all the cleaning up that it needs to at the right time. While
101*7ddc9b1aSDarren Reed  * we can't prevent programmers from creating memory leaks, we can synchronise
102*7ddc9b1aSDarren Reed  * when we clean up data structures to prevent code accessing free'd memory.
103*7ddc9b1aSDarren Reed  *
104*7ddc9b1aSDarren Reed  * A simple diagram showing the ownership is as follows:
105*7ddc9b1aSDarren Reed  *
106*7ddc9b1aSDarren Reed  *  Owned       +--------------+
107*7ddc9b1aSDarren Reed  *   by         | hook_stack_t |
108*7ddc9b1aSDarren Reed  *   the        +--------------+
109*7ddc9b1aSDarren Reed  *  Instance      |
110*7ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
111*7ddc9b1aSDarren Reed  *                V
112*7ddc9b1aSDarren Reed  *  Owned       +-------------------+     +-------------------+
113*7ddc9b1aSDarren Reed  *              | hook_family_int_t |---->| hook_family_int_t |
114*7ddc9b1aSDarren Reed  *   by         +-------------------+     +-------------------+
115*7ddc9b1aSDarren Reed  *                | \+---------------+        \+---------------+
116*7ddc9b1aSDarren Reed  *  network       |  | hook_family_t |         | hook_family_t |
117*7ddc9b1aSDarren Reed  *                V  +---------------+         +---------------+
118*7ddc9b1aSDarren Reed  *  protocol   +------------------+     +------------------+
119*7ddc9b1aSDarren Reed  *             | hook_event_int_t |---->| hook_event_int_t |
120*7ddc9b1aSDarren Reed  * (ipv4,ipv6) +------------------+     +------------------+
121*7ddc9b1aSDarren Reed  *                | \+--------------+        \+--------------+
122*7ddc9b1aSDarren Reed  *                |  | hook_event_t |         | hook_event_t |
123*7ddc9b1aSDarren Reed  *                |  +--------------+         +--------------+
124*7ddc9b1aSDarren Reed  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
125*7ddc9b1aSDarren Reed  *                V
126*7ddc9b1aSDarren Reed  *  Owned      +------------+
127*7ddc9b1aSDarren Reed  *             | hook_int_t |
128*7ddc9b1aSDarren Reed  *   by        +------------+
129*7ddc9b1aSDarren Reed  *                  \+--------+
130*7ddc9b1aSDarren Reed  * the consumer      | hook_t |
131*7ddc9b1aSDarren Reed  *                   +--------+
132*7ddc9b1aSDarren Reed  *
133*7ddc9b1aSDarren Reed  * The consumers, such as IPFilter, do not have any pointers or hold any
134*7ddc9b1aSDarren Reed  * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
135*7ddc9b1aSDarren Reed  * a hook on an event through net_hook_register(), an implicit reference
136*7ddc9b1aSDarren Reed  * to the hook_event_int_t is returned with a successful call.  Additionally,
137*7ddc9b1aSDarren Reed  * IPFilter does not see the hook_family_int_t or hook_family_t directly.
138*7ddc9b1aSDarren Reed  * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
139*7ddc9b1aSDarren Reed  * contains a pointer to hook_family_int_t.  The structure behind the
140*7ddc9b1aSDarren Reed  * net_handle_t (struct net_data) *is* reference counted and managed
141*7ddc9b1aSDarren Reed  * appropriately.
142*7ddc9b1aSDarren Reed  *
143*7ddc9b1aSDarren Reed  * A more detailed picture that describes how the family/event structures
144*7ddc9b1aSDarren Reed  * are linked together can be found in <sys/hook_impl.h>
145*7ddc9b1aSDarren Reed  */
146*7ddc9b1aSDarren Reed 
147*7ddc9b1aSDarren Reed /*
148*7ddc9b1aSDarren Reed  * Locking
149*7ddc9b1aSDarren Reed  * =======
150*7ddc9b1aSDarren Reed  * The use of CVW_* macros to do locking is driven by the need to allow
151*7ddc9b1aSDarren Reed  * recursive locking with read locks when we're processing packets. This
152*7ddc9b1aSDarren Reed  * is necessary because various netinfo functions need to hold read locks,
153*7ddc9b1aSDarren Reed  * by design, as they can be called in or out of packet context.
154*7ddc9b1aSDarren Reed  */
155*7ddc9b1aSDarren Reed /*
156381a2a9aSdr146992  * Hook internal functions
157381a2a9aSdr146992  */
158381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src);
159f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
160f4b3ec61Sdh155122     hook_stack_t *hks);
161381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
162381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
163*7ddc9b1aSDarren Reed static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
164381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
165f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
166*7ddc9b1aSDarren Reed static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
167381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
168*7ddc9b1aSDarren Reed static void hook_int_free(hook_int_t *hi, netstackid_t);
169381a2a9aSdr146992 static void hook_init(void);
170f4b3ec61Sdh155122 static void hook_fini(void);
171f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
172f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
173*7ddc9b1aSDarren Reed static void hook_stack_shutdown(netstackid_t stackid, void *arg);
174*7ddc9b1aSDarren Reed static int hook_insert(hook_int_head_t *head, hook_int_t *new);
175*7ddc9b1aSDarren Reed static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
176*7ddc9b1aSDarren Reed static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
177*7ddc9b1aSDarren Reed static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
178*7ddc9b1aSDarren Reed static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
179*7ddc9b1aSDarren Reed static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
180*7ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
181*7ddc9b1aSDarren Reed static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
182*7ddc9b1aSDarren Reed     hook_int_t *hi);
183*7ddc9b1aSDarren Reed static int hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
184*7ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg);
185*7ddc9b1aSDarren Reed static int hook_notify_unregister(cvwaitlock_t *lock,
186*7ddc9b1aSDarren Reed     hook_notify_head_t *head, hook_notify_fn_t callback);
187*7ddc9b1aSDarren Reed static void hook_notify_run(hook_notify_head_t *head, char *family,
188*7ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd);
189*7ddc9b1aSDarren Reed static void hook_stack_notify_run(hook_stack_t *hks, char *name,
190*7ddc9b1aSDarren Reed     hook_notify_cmd_t cmd);
191*7ddc9b1aSDarren Reed static void hook_stack_remove(hook_stack_t *hks);
192*7ddc9b1aSDarren Reed 
193*7ddc9b1aSDarren Reed /*
194*7ddc9b1aSDarren Reed  * A list of the hook stacks is kept here because we need to enable
195*7ddc9b1aSDarren Reed  * net_instance_notify_register() to be called during the creation
196*7ddc9b1aSDarren Reed  * of a new instance. Previously hook_stack_get() would just use
197*7ddc9b1aSDarren Reed  * the netstack functions for this work but they will return NULL
198*7ddc9b1aSDarren Reed  * until the zone has been fully initialised.
199*7ddc9b1aSDarren Reed  */
200*7ddc9b1aSDarren Reed static hook_stack_head_t hook_stacks;
201*7ddc9b1aSDarren Reed static kmutex_t hook_stack_lock;
202381a2a9aSdr146992 
203381a2a9aSdr146992 /*
204381a2a9aSdr146992  * Module entry points.
205381a2a9aSdr146992  */
206381a2a9aSdr146992 int
207381a2a9aSdr146992 _init(void)
208381a2a9aSdr146992 {
209f4b3ec61Sdh155122 	int error;
210f4b3ec61Sdh155122 
211381a2a9aSdr146992 	hook_init();
212f4b3ec61Sdh155122 	error = mod_install(&modlinkage);
213f4b3ec61Sdh155122 	if (error != 0)
214f4b3ec61Sdh155122 		hook_fini();
215f4b3ec61Sdh155122 
216f4b3ec61Sdh155122 	return (error);
217381a2a9aSdr146992 }
218381a2a9aSdr146992 
219381a2a9aSdr146992 int
220381a2a9aSdr146992 _fini(void)
221381a2a9aSdr146992 {
222f4b3ec61Sdh155122 	int error;
223f4b3ec61Sdh155122 
224f4b3ec61Sdh155122 	error = mod_remove(&modlinkage);
225f4b3ec61Sdh155122 	if (error == 0)
226f4b3ec61Sdh155122 		hook_fini();
227f4b3ec61Sdh155122 
228f4b3ec61Sdh155122 	return (error);
229381a2a9aSdr146992 }
230381a2a9aSdr146992 
231381a2a9aSdr146992 int
232381a2a9aSdr146992 _info(struct modinfo *modinfop)
233381a2a9aSdr146992 {
234381a2a9aSdr146992 	return (mod_info(&modlinkage, modinfop));
235381a2a9aSdr146992 }
236381a2a9aSdr146992 
237381a2a9aSdr146992 /*
238381a2a9aSdr146992  * Function:	hook_init
239381a2a9aSdr146992  * Returns:	None
240381a2a9aSdr146992  * Parameters:	None
241381a2a9aSdr146992  *
242381a2a9aSdr146992  * Initialize hooks
243381a2a9aSdr146992  */
244381a2a9aSdr146992 static void
245381a2a9aSdr146992 hook_init(void)
246381a2a9aSdr146992 {
247*7ddc9b1aSDarren Reed 	mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
248*7ddc9b1aSDarren Reed 	SLIST_INIT(&hook_stacks);
249*7ddc9b1aSDarren Reed 
250f4b3ec61Sdh155122 	/*
251f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
252f4b3ec61Sdh155122 	 * destroyed in the kernel.
253f4b3ec61Sdh155122 	 */
254*7ddc9b1aSDarren Reed 	netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
255f4b3ec61Sdh155122 	    hook_stack_fini);
256381a2a9aSdr146992 }
257381a2a9aSdr146992 
258f4b3ec61Sdh155122 /*
259f4b3ec61Sdh155122  * Function:	hook_fini
260f4b3ec61Sdh155122  * Returns:	None
261f4b3ec61Sdh155122  * Parameters:	None
262f4b3ec61Sdh155122  *
263f4b3ec61Sdh155122  * Deinitialize hooks
264f4b3ec61Sdh155122  */
265f4b3ec61Sdh155122 static void
266f4b3ec61Sdh155122 hook_fini(void)
267f4b3ec61Sdh155122 {
268f4b3ec61Sdh155122 	netstack_unregister(NS_HOOK);
269*7ddc9b1aSDarren Reed 
270*7ddc9b1aSDarren Reed 	mutex_destroy(&hook_stack_lock);
271*7ddc9b1aSDarren Reed 	ASSERT(SLIST_EMPTY(&hook_stacks));
272*7ddc9b1aSDarren Reed }
273*7ddc9b1aSDarren Reed 
274*7ddc9b1aSDarren Reed /*
275*7ddc9b1aSDarren Reed  * Function:	hook_wait_setflag
276*7ddc9b1aSDarren Reed  * Returns:     -1 = setting flag is disallowed, 0 = flag set and did
277*7ddc9b1aSDarren Reed  *              not have to wait (ie no lock droped), 1 = flag set but
278*7ddc9b1aSDarren Reed  *              it was necessary to drop locks to set it.
279*7ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
280*7ddc9b1aSDarren Reed  *              busyset(I) - set of flags that we don't want set while
281*7ddc9b1aSDarren Reed  *                           we are active.
282*7ddc9b1aSDarren Reed  *              wanted(I)  - flag associated with newflag to indicate
283*7ddc9b1aSDarren Reed  *                           what we want to do.
284*7ddc9b1aSDarren Reed  *              newflag(I) - the new ACTIVE flag we want to set that
285*7ddc9b1aSDarren Reed  *                           indicates what we are doing.
286*7ddc9b1aSDarren Reed  *
287*7ddc9b1aSDarren Reed  * The set of functions hook_wait_* implement an API that builds on top of
288*7ddc9b1aSDarren Reed  * the kcondvar_t to provide controlled execution through a critical region.
289*7ddc9b1aSDarren Reed  * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
290*7ddc9b1aSDarren Reed  * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
291*7ddc9b1aSDarren Reed  * The combination of flags is required as when this function exits to do
292*7ddc9b1aSDarren Reed  * the task, the structure is then free for another caller to use and
293*7ddc9b1aSDarren Reed  * to indicate that it wants to do work.  The trump flags here are those
294*7ddc9b1aSDarren Reed  * that indicate someone wants to destroy the structure that owns this
295*7ddc9b1aSDarren Reed  * flagwait_t.  In this case, we don't try to secure the ability to run
296*7ddc9b1aSDarren Reed  * and return with an error.
297*7ddc9b1aSDarren Reed  *
298*7ddc9b1aSDarren Reed  * wanted - the FWF_*_WANTED flag that describes the action being requested
299*7ddc9b1aSDarren Reed  * busyset- the set of FWF_* flags we don't want set when we run
300*7ddc9b1aSDarren Reed  * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
301*7ddc9b1aSDarren Reed  */
302*7ddc9b1aSDarren Reed int
303*7ddc9b1aSDarren Reed hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
304*7ddc9b1aSDarren Reed     fwflag_t newflag)
305*7ddc9b1aSDarren Reed {
306*7ddc9b1aSDarren Reed 	int waited = 0;
307*7ddc9b1aSDarren Reed 
308*7ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
309*7ddc9b1aSDarren Reed 	if (waiter->fw_flags & FWF_DESTROY) {
310*7ddc9b1aSDarren Reed 		mutex_exit(&waiter->fw_lock);
311*7ddc9b1aSDarren Reed 		return (-1);
312*7ddc9b1aSDarren Reed 	}
313*7ddc9b1aSDarren Reed 	while (waiter->fw_flags & busyset) {
314*7ddc9b1aSDarren Reed 		waiter->fw_flags |= wanted;
315*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
316*7ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
317*7ddc9b1aSDarren Reed 		waited = 1;
318*7ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
319*7ddc9b1aSDarren Reed 		if (waiter->fw_flags & FWF_DESTROY) {
320*7ddc9b1aSDarren Reed 			waiter->fw_flags &= ~wanted;
321*7ddc9b1aSDarren Reed 			mutex_exit(&waiter->fw_lock);
322*7ddc9b1aSDarren Reed 			return (-1);
323*7ddc9b1aSDarren Reed 		}
324*7ddc9b1aSDarren Reed 		waiter->fw_flags |= wanted;
325*7ddc9b1aSDarren Reed 	}
326*7ddc9b1aSDarren Reed 	waiter->fw_flags &= ~wanted;
327*7ddc9b1aSDarren Reed 	waiter->fw_flags |= newflag;
328*7ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
329*7ddc9b1aSDarren Reed 	return (waited);
330*7ddc9b1aSDarren Reed }
331*7ddc9b1aSDarren Reed 
332*7ddc9b1aSDarren Reed /*
333*7ddc9b1aSDarren Reed  * Function:	hook_wait_unsetflag
334*7ddc9b1aSDarren Reed  * Returns:     None
335*7ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
336*7ddc9b1aSDarren Reed  *              oldflag(I) - flag to reset
337*7ddc9b1aSDarren Reed  *
338*7ddc9b1aSDarren Reed  * Turn off the bit that we had set to run and let others know that
339*7ddc9b1aSDarren Reed  * they should now check to see if they can run.
340*7ddc9b1aSDarren Reed  */
341*7ddc9b1aSDarren Reed void
342*7ddc9b1aSDarren Reed hook_wait_unsetflag(flagwait_t *waiter, uint32_t oldflag)
343*7ddc9b1aSDarren Reed {
344*7ddc9b1aSDarren Reed 	mutex_enter(&waiter->fw_lock);
345*7ddc9b1aSDarren Reed 	waiter->fw_flags &= ~oldflag;
346*7ddc9b1aSDarren Reed 	cv_signal(&waiter->fw_cv);
347*7ddc9b1aSDarren Reed 	mutex_exit(&waiter->fw_lock);
348*7ddc9b1aSDarren Reed }
349*7ddc9b1aSDarren Reed 
350*7ddc9b1aSDarren Reed /*
351*7ddc9b1aSDarren Reed  * Function:	hook_wait_destroy
352*7ddc9b1aSDarren Reed  * Returns:     None
353*7ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
354*7ddc9b1aSDarren Reed  *
355*7ddc9b1aSDarren Reed  * Since outer locking (on fw_owner) should ensure that only one function
356*7ddc9b1aSDarren Reed  * at a time gets to call hook_wait_destroy() on a given object, there is
357*7ddc9b1aSDarren Reed  * no need to guard against setting FWF_DESTROY_WANTED already being set.
358*7ddc9b1aSDarren Reed  * It is, however, necessary to wait for all activity on the owning
359*7ddc9b1aSDarren Reed  * structure to cease.
360*7ddc9b1aSDarren Reed  */
361*7ddc9b1aSDarren Reed void
362*7ddc9b1aSDarren Reed hook_wait_destroy(flagwait_t *waiter)
363*7ddc9b1aSDarren Reed {
364*7ddc9b1aSDarren Reed 	ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
365*7ddc9b1aSDarren Reed 	waiter->fw_flags |= FWF_DESTROY_WANTED;
366*7ddc9b1aSDarren Reed 	while (!FWF_DESTROY_OK(waiter)) {
367*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(waiter->fw_owner);
368*7ddc9b1aSDarren Reed 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
369*7ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(waiter->fw_owner);
370*7ddc9b1aSDarren Reed 	}
371*7ddc9b1aSDarren Reed 	/*
372*7ddc9b1aSDarren Reed 	 * There should now be nothing else using "waiter" or its
373*7ddc9b1aSDarren Reed 	 * owner, so we can safely assign here without risk of wiiping
374*7ddc9b1aSDarren Reed 	 * out someone's bit.
375*7ddc9b1aSDarren Reed 	 */
376*7ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_DESTROY_ACTIVE;
377*7ddc9b1aSDarren Reed }
378*7ddc9b1aSDarren Reed 
379*7ddc9b1aSDarren Reed /*
380*7ddc9b1aSDarren Reed  * Function:	hook_wait_init
381*7ddc9b1aSDarren Reed  * Returns:     None
382*7ddc9b1aSDarren Reed  * Parameters:  waiter(I)  - control data structure
383*7ddc9b1aSDarren Reed  *              ownder(I)  - pointer to lock that the owner of this
384*7ddc9b1aSDarren Reed  *                           waiter uses
385*7ddc9b1aSDarren Reed  *
386*7ddc9b1aSDarren Reed  * "owner" gets passed in here so that when we need to call cv_wait,
387*7ddc9b1aSDarren Reed  * for example in hook_wait_setflag(), we can drop the lock for the
388*7ddc9b1aSDarren Reed  * next layer out, which is likely to be held in an exclusive manner.
389*7ddc9b1aSDarren Reed  */
390*7ddc9b1aSDarren Reed void
391*7ddc9b1aSDarren Reed hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
392*7ddc9b1aSDarren Reed {
393*7ddc9b1aSDarren Reed 	cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
394*7ddc9b1aSDarren Reed 	mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
395*7ddc9b1aSDarren Reed 	waiter->fw_flags = FWF_NONE;
396*7ddc9b1aSDarren Reed 	waiter->fw_owner = owner;
397f4b3ec61Sdh155122 }
398f4b3ec61Sdh155122 
399f4b3ec61Sdh155122 /*
400f4b3ec61Sdh155122  * Initialize the hook stack instance.
401f4b3ec61Sdh155122  */
402f4b3ec61Sdh155122 /*ARGSUSED*/
403f4b3ec61Sdh155122 static void *
404f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
405f4b3ec61Sdh155122 {
406f4b3ec61Sdh155122 	hook_stack_t	*hks;
407f4b3ec61Sdh155122 
408f4b3ec61Sdh155122 #ifdef NS_DEBUG
409f4b3ec61Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
410f4b3ec61Sdh155122 #endif
411f4b3ec61Sdh155122 
412f4b3ec61Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
413*7ddc9b1aSDarren Reed 	hks->hks_netstack = ns;
414*7ddc9b1aSDarren Reed 	hks->hks_netstackid = stackid;
415f4b3ec61Sdh155122 
416*7ddc9b1aSDarren Reed 	CVW_INIT(&hks->hks_lock);
417*7ddc9b1aSDarren Reed 	TAILQ_INIT(&hks->hks_nhead);
418f4b3ec61Sdh155122 	SLIST_INIT(&hks->hks_familylist);
419f4b3ec61Sdh155122 
420*7ddc9b1aSDarren Reed 	hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
421*7ddc9b1aSDarren Reed 
422*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
423*7ddc9b1aSDarren Reed 	SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
424*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
425*7ddc9b1aSDarren Reed 
426f4b3ec61Sdh155122 	return (hks);
427f4b3ec61Sdh155122 }
428f4b3ec61Sdh155122 
429*7ddc9b1aSDarren Reed /*ARGSUSED*/
430*7ddc9b1aSDarren Reed static void
431*7ddc9b1aSDarren Reed hook_stack_shutdown(netstackid_t stackid, void *arg)
432*7ddc9b1aSDarren Reed {
433*7ddc9b1aSDarren Reed 	hook_stack_t *hks = (hook_stack_t *)arg;
434*7ddc9b1aSDarren Reed 
435*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
436*7ddc9b1aSDarren Reed 	/*
437*7ddc9b1aSDarren Reed 	 * Once this flag gets set to one, no more additions are allowed
438*7ddc9b1aSDarren Reed 	 * to any of the structures that make up this stack.
439*7ddc9b1aSDarren Reed 	 */
440*7ddc9b1aSDarren Reed 	hks->hks_shutdown = 1;
441*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
442*7ddc9b1aSDarren Reed }
443*7ddc9b1aSDarren Reed 
444f4b3ec61Sdh155122 /*
445f4b3ec61Sdh155122  * Free the hook stack instance.
446f4b3ec61Sdh155122  */
447f4b3ec61Sdh155122 /*ARGSUSED*/
448f4b3ec61Sdh155122 static void
449f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
450f4b3ec61Sdh155122 {
451f4b3ec61Sdh155122 	hook_stack_t *hks = (hook_stack_t *)arg;
452*7ddc9b1aSDarren Reed 
453*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
454*7ddc9b1aSDarren Reed 	hks->hks_shutdown = 2;
455*7ddc9b1aSDarren Reed 	hook_stack_remove(hks);
456*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
457*7ddc9b1aSDarren Reed }
458*7ddc9b1aSDarren Reed 
459*7ddc9b1aSDarren Reed /*
460*7ddc9b1aSDarren Reed  * This function assumes that it is called with hook_stack_lock held.
461*7ddc9b1aSDarren Reed  * It functions differently to hook_family/event_remove in that it does
462*7ddc9b1aSDarren Reed  * the checks to see if it can be removed. This difference exists
463*7ddc9b1aSDarren Reed  * because this structure has nothing higher up that depends on it.
464*7ddc9b1aSDarren Reed  */
465*7ddc9b1aSDarren Reed static void
466*7ddc9b1aSDarren Reed hook_stack_remove(hook_stack_t *hks)
467*7ddc9b1aSDarren Reed {
468*7ddc9b1aSDarren Reed 
469*7ddc9b1aSDarren Reed 	ASSERT(mutex_owned(&hook_stack_lock));
470*7ddc9b1aSDarren Reed 
471*7ddc9b1aSDarren Reed 	/*
472*7ddc9b1aSDarren Reed 	 * Is the structure still in use?
473*7ddc9b1aSDarren Reed 	 */
474*7ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hks->hks_familylist) ||
475*7ddc9b1aSDarren Reed 	    !TAILQ_EMPTY(&hks->hks_nhead))
476*7ddc9b1aSDarren Reed 		return;
477*7ddc9b1aSDarren Reed 
478*7ddc9b1aSDarren Reed 	SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
479*7ddc9b1aSDarren Reed 
480*7ddc9b1aSDarren Reed 	hook_wait_destroy(&hks->hks_waiter);
481*7ddc9b1aSDarren Reed 	CVW_DESTROY(&hks->hks_lock);
482f4b3ec61Sdh155122 	kmem_free(hks, sizeof (*hks));
483f4b3ec61Sdh155122 }
484381a2a9aSdr146992 
485*7ddc9b1aSDarren Reed static hook_stack_t *
486*7ddc9b1aSDarren Reed hook_stack_get(netstackid_t stackid)
487*7ddc9b1aSDarren Reed {
488*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
489*7ddc9b1aSDarren Reed 
490*7ddc9b1aSDarren Reed 	SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
491*7ddc9b1aSDarren Reed 		if (hks->hks_netstackid == stackid)
492*7ddc9b1aSDarren Reed 			break;
493*7ddc9b1aSDarren Reed 	}
494*7ddc9b1aSDarren Reed 
495*7ddc9b1aSDarren Reed 	return (hks);
496*7ddc9b1aSDarren Reed }
497*7ddc9b1aSDarren Reed 
498*7ddc9b1aSDarren Reed /*
499*7ddc9b1aSDarren Reed  * Function:	hook_stack_notify_register
500*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
501*7ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
502*7ddc9b1aSDarren Reed  *              callback(I)- function to be called
503*7ddc9b1aSDarren Reed  *              arg(I)     - arg to provide callback when it is called
504*7ddc9b1aSDarren Reed  *
505*7ddc9b1aSDarren Reed  * If we're not shutting down this instance, append a new function to the
506*7ddc9b1aSDarren Reed  * list of those to call when a new family of hooks is added to this stack.
507*7ddc9b1aSDarren Reed  */
508*7ddc9b1aSDarren Reed int
509*7ddc9b1aSDarren Reed hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
510*7ddc9b1aSDarren Reed     void *arg)
511*7ddc9b1aSDarren Reed {
512*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
513*7ddc9b1aSDarren Reed 	int error;
514*7ddc9b1aSDarren Reed 
515*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
516*7ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
517*7ddc9b1aSDarren Reed 	if (hks != NULL) {
518*7ddc9b1aSDarren Reed 		if (hks->hks_shutdown != 0) {
519*7ddc9b1aSDarren Reed 			error = ESHUTDOWN;
520*7ddc9b1aSDarren Reed 		} else {
521*7ddc9b1aSDarren Reed 			error = hook_notify_register(&hks->hks_lock,
522*7ddc9b1aSDarren Reed 			    &hks->hks_nhead, callback, arg);
523*7ddc9b1aSDarren Reed 		}
524*7ddc9b1aSDarren Reed 	} else {
525*7ddc9b1aSDarren Reed 		error = ESRCH;
526*7ddc9b1aSDarren Reed 	}
527*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
528*7ddc9b1aSDarren Reed 
529*7ddc9b1aSDarren Reed 	return (error);
530*7ddc9b1aSDarren Reed }
531*7ddc9b1aSDarren Reed 
532*7ddc9b1aSDarren Reed /*
533*7ddc9b1aSDarren Reed  * Function:	hook_stack_notify_unregister
534*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
535*7ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
536*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
537*7ddc9b1aSDarren Reed  *
538*7ddc9b1aSDarren Reed  * Attempt to remove a registered function from a hook stack's list of
539*7ddc9b1aSDarren Reed  * callbacks to activiate when protocols are added/deleted.
540*7ddc9b1aSDarren Reed  */
541*7ddc9b1aSDarren Reed int
542*7ddc9b1aSDarren Reed hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
543*7ddc9b1aSDarren Reed {
544*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
545*7ddc9b1aSDarren Reed 	int error;
546*7ddc9b1aSDarren Reed 
547*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
548*7ddc9b1aSDarren Reed 	hks = hook_stack_get(stackid);
549*7ddc9b1aSDarren Reed 	if (hks != NULL) {
550*7ddc9b1aSDarren Reed 		error = hook_notify_unregister(&hks->hks_lock,
551*7ddc9b1aSDarren Reed 		    &hks->hks_nhead, callback);
552*7ddc9b1aSDarren Reed 		if ((error == 0) && (hks->hks_shutdown == 2))
553*7ddc9b1aSDarren Reed 			hook_stack_remove(hks);
554*7ddc9b1aSDarren Reed 	} else {
555*7ddc9b1aSDarren Reed 		error = ESRCH;
556*7ddc9b1aSDarren Reed 	}
557*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
558*7ddc9b1aSDarren Reed 
559*7ddc9b1aSDarren Reed 	return (error);
560*7ddc9b1aSDarren Reed }
561*7ddc9b1aSDarren Reed 
562*7ddc9b1aSDarren Reed /*
563*7ddc9b1aSDarren Reed  * Function:	hook_stack_notify_run
564*7ddc9b1aSDarren Reed  * Returns:	None
565*7ddc9b1aSDarren Reed  * Parameters:	hks(I)  - hook stack pointer to execute callbacks for
566*7ddc9b1aSDarren Reed  *              name(I) - name of a hook family
567*7ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
568*7ddc9b1aSDarren Reed  *
569*7ddc9b1aSDarren Reed  * Run through the list of callbacks on the hook stack to be called when
570*7ddc9b1aSDarren Reed  * a new hook family is added
571*7ddc9b1aSDarren Reed  *
572*7ddc9b1aSDarren Reed  * As hook_notify_run() expects 3 names, one for the family, one for the
573*7ddc9b1aSDarren Reed  * event and one for the object being introduced and we really only have
574*7ddc9b1aSDarren Reed  * one name (that of the new hook family), fake the hook stack's name by
575*7ddc9b1aSDarren Reed  * converting the integer to a string and for the event just pass NULL.
576*7ddc9b1aSDarren Reed  */
577*7ddc9b1aSDarren Reed static void
578*7ddc9b1aSDarren Reed hook_stack_notify_run(hook_stack_t *hks, char *name,
579*7ddc9b1aSDarren Reed     hook_notify_cmd_t cmd)
580*7ddc9b1aSDarren Reed {
581*7ddc9b1aSDarren Reed 	char buffer[16];
582*7ddc9b1aSDarren Reed 
583*7ddc9b1aSDarren Reed 	(void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
584*7ddc9b1aSDarren Reed 
585*7ddc9b1aSDarren Reed 	hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
586*7ddc9b1aSDarren Reed }
587*7ddc9b1aSDarren Reed 
588381a2a9aSdr146992 /*
589381a2a9aSdr146992  * Function:	hook_run
590381a2a9aSdr146992  * Returns:	int - return value according to callback func
591381a2a9aSdr146992  * Parameters:	token(I) - event pointer
592381a2a9aSdr146992  *		info(I) - message
593381a2a9aSdr146992  *
594381a2a9aSdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
595381a2a9aSdr146992  * until either the end of the list is reached or a hook function returns a
596381a2a9aSdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
597381a2a9aSdr146992  * return that value back to our caller.  By design, a hook function can be
598381a2a9aSdr146992  * called more than once, simultaneously.
599381a2a9aSdr146992  */
600381a2a9aSdr146992 int
601*7ddc9b1aSDarren Reed hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
602381a2a9aSdr146992 {
603381a2a9aSdr146992 	hook_event_int_t *hei;
604*7ddc9b1aSDarren Reed 	hook_int_t *hi;
605381a2a9aSdr146992 	int rval = 0;
606381a2a9aSdr146992 
607381a2a9aSdr146992 	ASSERT(token != NULL);
608381a2a9aSdr146992 
609381a2a9aSdr146992 	hei = (hook_event_int_t *)token;
610381a2a9aSdr146992 	DTRACE_PROBE2(hook__run__start,
611381a2a9aSdr146992 	    hook_event_token_t, token,
612381a2a9aSdr146992 	    hook_data_t, info);
613381a2a9aSdr146992 
614*7ddc9b1aSDarren Reed 	/*
615*7ddc9b1aSDarren Reed 	 * Hold global read lock to ensure event will not be deleted.
616*7ddc9b1aSDarren Reed 	 * While it might be expected that we should also hold a read lock
617*7ddc9b1aSDarren Reed 	 * on the event lock (hei_lock) to prevent the hook list from
618*7ddc9b1aSDarren Reed 	 * changing while we're executing this function, both addition
619*7ddc9b1aSDarren Reed 	 * to and removal from the hook list on the event is done with
620*7ddc9b1aSDarren Reed 	 * a write lock held on hfi_lock. This is by design so that we
621*7ddc9b1aSDarren Reed 	 * only need to get one of these locks to process a packet.
622*7ddc9b1aSDarren Reed 	 * - locking is not a cheap thing to do for every packet.
623*7ddc9b1aSDarren Reed 	 */
624*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
625381a2a9aSdr146992 
626381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
627381a2a9aSdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
628381a2a9aSdr146992 		DTRACE_PROBE3(hook__func__start,
629381a2a9aSdr146992 		    hook_event_token_t, token,
630381a2a9aSdr146992 		    hook_data_t, info,
631381a2a9aSdr146992 		    hook_int_t *, hi);
632*7ddc9b1aSDarren Reed 		rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
633381a2a9aSdr146992 		DTRACE_PROBE4(hook__func__end,
634381a2a9aSdr146992 		    hook_event_token_t, token,
635381a2a9aSdr146992 		    hook_data_t, info,
636381a2a9aSdr146992 		    hook_int_t *, hi,
637381a2a9aSdr146992 		    int, rval);
638*7ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hits.value.ui64++;
639381a2a9aSdr146992 		if (rval != 0)
640381a2a9aSdr146992 			break;
641381a2a9aSdr146992 	}
642381a2a9aSdr146992 
643*7ddc9b1aSDarren Reed 	hei->hei_kstats.events.value.ui64++;
644*7ddc9b1aSDarren Reed 
645*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
646381a2a9aSdr146992 
647381a2a9aSdr146992 	DTRACE_PROBE3(hook__run__end,
648381a2a9aSdr146992 	    hook_event_token_t, token,
649381a2a9aSdr146992 	    hook_data_t, info,
650381a2a9aSdr146992 	    hook_int_t *, hi);
651381a2a9aSdr146992 
652381a2a9aSdr146992 	return (rval);
653381a2a9aSdr146992 }
654381a2a9aSdr146992 
655381a2a9aSdr146992 /*
656381a2a9aSdr146992  * Function:	hook_family_add
657381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Fail
658381a2a9aSdr146992  * Parameters:	hf(I) - family pointer
659381a2a9aSdr146992  *
660381a2a9aSdr146992  * Add new family to family list
661381a2a9aSdr146992  */
662381a2a9aSdr146992 hook_family_int_t *
663f4b3ec61Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks)
664381a2a9aSdr146992 {
665381a2a9aSdr146992 	hook_family_int_t *hfi, *new;
666381a2a9aSdr146992 
667381a2a9aSdr146992 	ASSERT(hf != NULL);
668381a2a9aSdr146992 	ASSERT(hf->hf_name != NULL);
669381a2a9aSdr146992 
670381a2a9aSdr146992 	new = hook_family_copy(hf);
671381a2a9aSdr146992 	if (new == NULL)
672381a2a9aSdr146992 		return (NULL);
673381a2a9aSdr146992 
674*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
675*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
676*7ddc9b1aSDarren Reed 
677*7ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
678*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
679*7ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
680*7ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
681*7ddc9b1aSDarren Reed 		return (NULL);
682*7ddc9b1aSDarren Reed 	}
683381a2a9aSdr146992 
684381a2a9aSdr146992 	/* search family list */
685f4b3ec61Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
686381a2a9aSdr146992 	if (hfi != NULL) {
687*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
688*7ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
689*7ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
690381a2a9aSdr146992 		return (NULL);
691381a2a9aSdr146992 	}
692381a2a9aSdr146992 
693*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
694*7ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
695*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
696*7ddc9b1aSDarren Reed 		mutex_exit(&hook_stack_lock);
697*7ddc9b1aSDarren Reed 		hook_family_free(new, NULL);
698*7ddc9b1aSDarren Reed 		return (NULL);
699*7ddc9b1aSDarren Reed 	}
700*7ddc9b1aSDarren Reed 
701*7ddc9b1aSDarren Reed 	CVW_INIT(&new->hfi_lock);
702*7ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
703*7ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
704*7ddc9b1aSDarren Reed 
705*7ddc9b1aSDarren Reed 	hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
706*7ddc9b1aSDarren Reed 
707*7ddc9b1aSDarren Reed 	new->hfi_stack = hks;
708381a2a9aSdr146992 
709f4b3ec61Sdh155122 	/* Add to family list head */
710f4b3ec61Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
711f4b3ec61Sdh155122 
712*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
713*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
714*7ddc9b1aSDarren Reed 
715*7ddc9b1aSDarren Reed 	hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
716*7ddc9b1aSDarren Reed 
717*7ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
718*7ddc9b1aSDarren Reed 
719381a2a9aSdr146992 	return (new);
720381a2a9aSdr146992 }
721381a2a9aSdr146992 
722381a2a9aSdr146992 /*
723381a2a9aSdr146992  * Function:	hook_family_remove
724381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
725381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
726381a2a9aSdr146992  *
727*7ddc9b1aSDarren Reed  * Remove family from family list. This function has been designed to be
728*7ddc9b1aSDarren Reed  * called once and once only per hook_family_int_t. Thus when cleaning up
729*7ddc9b1aSDarren Reed  * this structure as an orphan, callers should only call hook_family_free.
730381a2a9aSdr146992  */
731381a2a9aSdr146992 int
732381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi)
733381a2a9aSdr146992 {
734f4b3ec61Sdh155122 	hook_stack_t *hks;
735381a2a9aSdr146992 
736381a2a9aSdr146992 	ASSERT(hfi != NULL);
737*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
738381a2a9aSdr146992 
739*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hks->hks_lock);
740381a2a9aSdr146992 
741*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
742*7ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
743*7ddc9b1aSDarren Reed 		/*
744*7ddc9b1aSDarren Reed 		 * If we're trying to destroy the hook_stack_t...
745*7ddc9b1aSDarren Reed 		 */
746*7ddc9b1aSDarren Reed 		return (ENXIO);
747381a2a9aSdr146992 	}
748381a2a9aSdr146992 
749*7ddc9b1aSDarren Reed 	/*
750*7ddc9b1aSDarren Reed 	 * Check if the family is in use by the presence of either events
751*7ddc9b1aSDarren Reed 	 * or notify callbacks on the hook family.
752*7ddc9b1aSDarren Reed 	 */
753*7ddc9b1aSDarren Reed 	if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
754*7ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_TRUE;
755*7ddc9b1aSDarren Reed 	} else {
756*7ddc9b1aSDarren Reed 		/*
757*7ddc9b1aSDarren Reed 		 * Although hfi_condemned = B_FALSE is implied from creation,
758*7ddc9b1aSDarren Reed 		 * putting a comment here inside the else upsets lint.
759*7ddc9b1aSDarren Reed 		 */
760*7ddc9b1aSDarren Reed 		hfi->hfi_condemned = B_FALSE;
761*7ddc9b1aSDarren Reed 	}
762381a2a9aSdr146992 
763*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
764*7ddc9b1aSDarren Reed 	hook_wait_destroy(&hfi->hfi_waiter);
765*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
766*7ddc9b1aSDarren Reed 
767*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hks->hks_lock);
768*7ddc9b1aSDarren Reed 
769*7ddc9b1aSDarren Reed 	hook_stack_notify_run(hks, hfi->hfi_family.hf_name, HN_UNREGISTER);
770*7ddc9b1aSDarren Reed 
771*7ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
772*7ddc9b1aSDarren Reed 
773*7ddc9b1aSDarren Reed 	/*
774*7ddc9b1aSDarren Reed 	 * If we don't have to wait for anything else to disappear from this
775*7ddc9b1aSDarren Reed 	 * structure then we can free it up.
776*7ddc9b1aSDarren Reed 	 */
777*7ddc9b1aSDarren Reed 	if (!hfi->hfi_condemned)
778*7ddc9b1aSDarren Reed 		hook_family_free(hfi, hks);
779381a2a9aSdr146992 
780381a2a9aSdr146992 	return (0);
781381a2a9aSdr146992 }
782381a2a9aSdr146992 
783381a2a9aSdr146992 
784381a2a9aSdr146992 /*
785*7ddc9b1aSDarren Reed  * Function:	hook_family_free
786*7ddc9b1aSDarren Reed  * Returns:	None
787*7ddc9b1aSDarren Reed  * Parameters:	hfi(I) - internal family pointer
788*7ddc9b1aSDarren Reed  *
789*7ddc9b1aSDarren Reed  * Free alloc memory for family
790*7ddc9b1aSDarren Reed  */
791*7ddc9b1aSDarren Reed static void
792*7ddc9b1aSDarren Reed hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
793*7ddc9b1aSDarren Reed {
794*7ddc9b1aSDarren Reed 
795*7ddc9b1aSDarren Reed 	/*
796*7ddc9b1aSDarren Reed 	 * This lock gives us possession of the hks pointer after the
797*7ddc9b1aSDarren Reed 	 * SLIST_REMOVE, for which it is not needed, when hks_shutdown
798*7ddc9b1aSDarren Reed 	 * is checked and hook_stack_remove called.
799*7ddc9b1aSDarren Reed 	 */
800*7ddc9b1aSDarren Reed 	mutex_enter(&hook_stack_lock);
801*7ddc9b1aSDarren Reed 
802*7ddc9b1aSDarren Reed 	ASSERT(hfi != NULL);
803*7ddc9b1aSDarren Reed 
804*7ddc9b1aSDarren Reed 	if (hks != NULL) {
805*7ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hks->hks_lock);
806*7ddc9b1aSDarren Reed 		/* Remove from family list */
807*7ddc9b1aSDarren Reed 		SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
808*7ddc9b1aSDarren Reed 		    hfi_entry);
809*7ddc9b1aSDarren Reed 
810*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hks->hks_lock);
811*7ddc9b1aSDarren Reed 	}
812*7ddc9b1aSDarren Reed 
813*7ddc9b1aSDarren Reed 	/* Free name space */
814*7ddc9b1aSDarren Reed 	if (hfi->hfi_family.hf_name != NULL) {
815*7ddc9b1aSDarren Reed 		kmem_free(hfi->hfi_family.hf_name,
816*7ddc9b1aSDarren Reed 		    strlen(hfi->hfi_family.hf_name) + 1);
817*7ddc9b1aSDarren Reed 	}
818*7ddc9b1aSDarren Reed 
819*7ddc9b1aSDarren Reed 	/* Free container */
820*7ddc9b1aSDarren Reed 	kmem_free(hfi, sizeof (*hfi));
821*7ddc9b1aSDarren Reed 
822*7ddc9b1aSDarren Reed 	if (hks->hks_shutdown == 2)
823*7ddc9b1aSDarren Reed 		hook_stack_remove(hks);
824*7ddc9b1aSDarren Reed 
825*7ddc9b1aSDarren Reed 	mutex_exit(&hook_stack_lock);
826*7ddc9b1aSDarren Reed }
827*7ddc9b1aSDarren Reed 
828*7ddc9b1aSDarren Reed 
829*7ddc9b1aSDarren Reed /*
830381a2a9aSdr146992  * Function:	hook_family_copy
831381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Failed
832381a2a9aSdr146992  * Parameters:	src(I) - family pointer
833381a2a9aSdr146992  *
834381a2a9aSdr146992  * Allocate internal family block and duplicate incoming family
835381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
836381a2a9aSdr146992  */
837381a2a9aSdr146992 static hook_family_int_t *
838381a2a9aSdr146992 hook_family_copy(hook_family_t *src)
839381a2a9aSdr146992 {
840381a2a9aSdr146992 	hook_family_int_t *new;
841381a2a9aSdr146992 	hook_family_t *dst;
842381a2a9aSdr146992 
843381a2a9aSdr146992 	ASSERT(src != NULL);
844381a2a9aSdr146992 	ASSERT(src->hf_name != NULL);
845381a2a9aSdr146992 
846381a2a9aSdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
847381a2a9aSdr146992 
848381a2a9aSdr146992 	/* Copy body */
849381a2a9aSdr146992 	dst = &new->hfi_family;
850381a2a9aSdr146992 	*dst = *src;
851381a2a9aSdr146992 
852*7ddc9b1aSDarren Reed 	SLIST_INIT(&new->hfi_head);
853*7ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hfi_nhead);
854*7ddc9b1aSDarren Reed 
855381a2a9aSdr146992 	/* Copy name */
856381a2a9aSdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
857381a2a9aSdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
858381a2a9aSdr146992 
859381a2a9aSdr146992 	return (new);
860381a2a9aSdr146992 }
861381a2a9aSdr146992 
862381a2a9aSdr146992 /*
863*7ddc9b1aSDarren Reed  * Function:	hook_family_find
864381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Not match
865381a2a9aSdr146992  * Parameters:	family(I) - family name string
866381a2a9aSdr146992  *
867381a2a9aSdr146992  * Search family list with family name
868*7ddc9b1aSDarren Reed  * 	A lock on hfi_lock must be held when called.
869381a2a9aSdr146992  */
870381a2a9aSdr146992 static hook_family_int_t *
871f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
872381a2a9aSdr146992 {
873381a2a9aSdr146992 	hook_family_int_t *hfi = NULL;
874381a2a9aSdr146992 
875381a2a9aSdr146992 	ASSERT(family != NULL);
876381a2a9aSdr146992 
877f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
878381a2a9aSdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
879381a2a9aSdr146992 			break;
880381a2a9aSdr146992 	}
881381a2a9aSdr146992 	return (hfi);
882381a2a9aSdr146992 }
883381a2a9aSdr146992 
884*7ddc9b1aSDarren Reed /*
885*7ddc9b1aSDarren Reed  * Function:	hook_family_notify_register
886*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
887*7ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
888*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
889*7ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
890*7ddc9b1aSDarren Reed  *
891*7ddc9b1aSDarren Reed  * So long as this hook stack isn't being shut down, register a new
892*7ddc9b1aSDarren Reed  * callback to be activated each time a new event is added to this
893*7ddc9b1aSDarren Reed  * family.
894*7ddc9b1aSDarren Reed  *
895*7ddc9b1aSDarren Reed  * To call this function we must have an active handle in use on the family,
896*7ddc9b1aSDarren Reed  * so if we take this into account, then neither the hook_family_int_t nor
897*7ddc9b1aSDarren Reed  * the hook_stack_t that owns it can disappear. We have to put some trust
898*7ddc9b1aSDarren Reed  * in the callers to be properly synchronised...
899*7ddc9b1aSDarren Reed  *
900*7ddc9b1aSDarren Reed  * Holding hks_lock is required to provide synchronisation for hks_shutdown.
901*7ddc9b1aSDarren Reed  */
902*7ddc9b1aSDarren Reed int
903*7ddc9b1aSDarren Reed hook_family_notify_register(hook_family_int_t *hfi,
904*7ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
905*7ddc9b1aSDarren Reed {
906*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
907*7ddc9b1aSDarren Reed 	int error;
908*7ddc9b1aSDarren Reed 
909*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
910*7ddc9b1aSDarren Reed 
911*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
912*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
913*7ddc9b1aSDarren Reed 
914*7ddc9b1aSDarren Reed 	if (hfi->hfi_stack->hks_shutdown != 0) {
915*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
916*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
917*7ddc9b1aSDarren Reed 		return (ESHUTDOWN);
918*7ddc9b1aSDarren Reed 	}
919*7ddc9b1aSDarren Reed 
920*7ddc9b1aSDarren Reed 	error = hook_notify_register(&hfi->hfi_lock, &hfi->hfi_nhead,
921*7ddc9b1aSDarren Reed 	    callback, arg);
922*7ddc9b1aSDarren Reed 
923*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
924*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
925*7ddc9b1aSDarren Reed 
926*7ddc9b1aSDarren Reed 	return (error);
927*7ddc9b1aSDarren Reed }
928381a2a9aSdr146992 
929381a2a9aSdr146992 /*
930*7ddc9b1aSDarren Reed  * Function:	hook_family_notify_unregister
931*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
932*7ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
933*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
934381a2a9aSdr146992  *
935*7ddc9b1aSDarren Reed  * Remove a callback from the list of those executed when a new event is
936*7ddc9b1aSDarren Reed  * added to a hook family.
937381a2a9aSdr146992  */
938*7ddc9b1aSDarren Reed int
939*7ddc9b1aSDarren Reed hook_family_notify_unregister(hook_family_int_t *hfi,
940*7ddc9b1aSDarren Reed     hook_notify_fn_t callback)
941381a2a9aSdr146992 {
942*7ddc9b1aSDarren Reed 	boolean_t free_family;
943*7ddc9b1aSDarren Reed 	int error;
944381a2a9aSdr146992 
945*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
946*7ddc9b1aSDarren Reed 
947*7ddc9b1aSDarren Reed 	error = hook_notify_unregister(&hfi->hfi_lock, &hfi->hfi_nhead,
948*7ddc9b1aSDarren Reed 	    callback);
949*7ddc9b1aSDarren Reed 
950*7ddc9b1aSDarren Reed 	/*
951*7ddc9b1aSDarren Reed 	 * If hook_family_remove has been called but the structure was still
952*7ddc9b1aSDarren Reed 	 * "busy" ... but we might have just made it "unbusy"...
953*7ddc9b1aSDarren Reed 	 */
954*7ddc9b1aSDarren Reed 	if ((error == 0) && hfi->hfi_condemned &&
955*7ddc9b1aSDarren Reed 	    SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
956*7ddc9b1aSDarren Reed 		free_family = B_TRUE;
957*7ddc9b1aSDarren Reed 	} else {
958*7ddc9b1aSDarren Reed 		free_family = B_FALSE;
959381a2a9aSdr146992 	}
960381a2a9aSdr146992 
961*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
962381a2a9aSdr146992 
963*7ddc9b1aSDarren Reed 	if (free_family)
964*7ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
965*7ddc9b1aSDarren Reed 
966*7ddc9b1aSDarren Reed 	return (error);
967*7ddc9b1aSDarren Reed }
968381a2a9aSdr146992 
969381a2a9aSdr146992 /*
970381a2a9aSdr146992  * Function:	hook_event_add
971381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Fail
972381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
973381a2a9aSdr146992  *		he(I) - event pointer
974381a2a9aSdr146992  *
975381a2a9aSdr146992  * Add new event to event list on specific family.
976381a2a9aSdr146992  * This function can fail to return successfully if (1) it cannot allocate
977381a2a9aSdr146992  * enough memory for its own internal data structures, (2) the event has
978381a2a9aSdr146992  * already been registered (for any hook family.)
979381a2a9aSdr146992  */
980381a2a9aSdr146992 hook_event_int_t *
981381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
982381a2a9aSdr146992 {
983381a2a9aSdr146992 	hook_event_int_t *hei, *new;
984*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
985381a2a9aSdr146992 
986381a2a9aSdr146992 	ASSERT(hfi != NULL);
987381a2a9aSdr146992 	ASSERT(he != NULL);
988381a2a9aSdr146992 	ASSERT(he->he_name != NULL);
989381a2a9aSdr146992 
990381a2a9aSdr146992 	new = hook_event_copy(he);
991381a2a9aSdr146992 	if (new == NULL)
992381a2a9aSdr146992 		return (NULL);
993381a2a9aSdr146992 
994*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
995*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
996*7ddc9b1aSDarren Reed 
997*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
998*7ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
999*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1000*7ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1001*7ddc9b1aSDarren Reed 		return (NULL);
1002*7ddc9b1aSDarren Reed 	}
1003381a2a9aSdr146992 
1004381a2a9aSdr146992 	/* Check whether this event pointer is already registered */
1005f4b3ec61Sdh155122 	hei = hook_event_checkdup(he, hks);
1006381a2a9aSdr146992 	if (hei != NULL) {
1007*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1008*7ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1009381a2a9aSdr146992 		return (NULL);
1010381a2a9aSdr146992 	}
1011381a2a9aSdr146992 
1012*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1013*7ddc9b1aSDarren Reed 
1014*7ddc9b1aSDarren Reed 	if (hfi->hfi_condemned) {
1015*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1016*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1017*7ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1018*7ddc9b1aSDarren Reed 		return (NULL);
1019*7ddc9b1aSDarren Reed 	}
1020*7ddc9b1aSDarren Reed 
1021*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
1022*7ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
1023*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1024*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1025*7ddc9b1aSDarren Reed 		hook_event_free(new, NULL);
1026*7ddc9b1aSDarren Reed 		return (NULL);
1027*7ddc9b1aSDarren Reed 	}
1028*7ddc9b1aSDarren Reed 
1029*7ddc9b1aSDarren Reed 	TAILQ_INIT(&new->hei_nhead);
1030*7ddc9b1aSDarren Reed 
1031*7ddc9b1aSDarren Reed 	hook_event_init_kstats(hfi, new);
1032*7ddc9b1aSDarren Reed 	hook_wait_init(&new->hei_waiter, &new->hei_lock);
1033*7ddc9b1aSDarren Reed 
1034381a2a9aSdr146992 	/* Add to event list head */
1035381a2a9aSdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
1036381a2a9aSdr146992 
1037*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1038*7ddc9b1aSDarren Reed 
1039*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
1040*7ddc9b1aSDarren Reed 
1041*7ddc9b1aSDarren Reed 	hook_notify_run(&hfi->hfi_nhead,
1042*7ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
1043*7ddc9b1aSDarren Reed 
1044*7ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
1045*7ddc9b1aSDarren Reed 
1046381a2a9aSdr146992 	return (new);
1047381a2a9aSdr146992 }
1048381a2a9aSdr146992 
1049*7ddc9b1aSDarren Reed /*
1050*7ddc9b1aSDarren Reed  * Function:	hook_event_init_kstats
1051*7ddc9b1aSDarren Reed  * Returns:	None
1052*7ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns this event.
1053*7ddc9b1aSDarren Reed  *              hei(I) - pointer to the hook event that needs some kstats.
1054*7ddc9b1aSDarren Reed  *
1055*7ddc9b1aSDarren Reed  * Create a set of kstats that relate to each event registered with
1056*7ddc9b1aSDarren Reed  * the hook framework.  A counter is kept for each time the event is
1057*7ddc9b1aSDarren Reed  * activated and for each time a hook is added or removed.  As the
1058*7ddc9b1aSDarren Reed  * kstats just count the events as they happen, the total number of
1059*7ddc9b1aSDarren Reed  * hooks registered must be obtained by subtractived removed from added.
1060*7ddc9b1aSDarren Reed  */
1061*7ddc9b1aSDarren Reed static void
1062*7ddc9b1aSDarren Reed hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
1063*7ddc9b1aSDarren Reed {
1064*7ddc9b1aSDarren Reed 	hook_event_kstat_t template = {
1065*7ddc9b1aSDarren Reed 		{ "hooksAdded",		KSTAT_DATA_UINT64 },
1066*7ddc9b1aSDarren Reed 		{ "hooksRemoved",	KSTAT_DATA_UINT64 },
1067*7ddc9b1aSDarren Reed 		{ "events",		KSTAT_DATA_UINT64 }
1068*7ddc9b1aSDarren Reed 	};
1069*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
1070*7ddc9b1aSDarren Reed 
1071*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
1072*7ddc9b1aSDarren Reed 	hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
1073*7ddc9b1aSDarren Reed 	    hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
1074*7ddc9b1aSDarren Reed 	    sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
1075*7ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
1076*7ddc9b1aSDarren Reed 
1077*7ddc9b1aSDarren Reed 	bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
1078*7ddc9b1aSDarren Reed 
1079*7ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
1080*7ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
1081*7ddc9b1aSDarren Reed 		hei->hei_kstatp->ks_private =
1082*7ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
1083*7ddc9b1aSDarren Reed 
1084*7ddc9b1aSDarren Reed 		kstat_install(hei->hei_kstatp);
1085*7ddc9b1aSDarren Reed 	}
1086*7ddc9b1aSDarren Reed }
1087381a2a9aSdr146992 
1088381a2a9aSdr146992 /*
1089381a2a9aSdr146992  * Function:	hook_event_remove
1090381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
1091381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1092381a2a9aSdr146992  *		he(I) - event pointer
1093381a2a9aSdr146992  *
1094381a2a9aSdr146992  * Remove event from event list on specific family
1095*7ddc9b1aSDarren Reed  *
1096*7ddc9b1aSDarren Reed  * This function assumes that the caller has received a pointer to a the
1097*7ddc9b1aSDarren Reed  * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
1098*7ddc9b1aSDarren Reed  * This the hook_family_int_t is guaranteed to be around for the life of this
1099*7ddc9b1aSDarren Reed  * call, unless the caller has decided to call net_protocol_release or
1100*7ddc9b1aSDarren Reed  * net_protocol_unregister before calling net_event_unregister - an error.
1101381a2a9aSdr146992  */
1102381a2a9aSdr146992 int
1103381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
1104381a2a9aSdr146992 {
1105*7ddc9b1aSDarren Reed 	boolean_t free_family;
1106381a2a9aSdr146992 	hook_event_int_t *hei;
1107381a2a9aSdr146992 
1108381a2a9aSdr146992 	ASSERT(hfi != NULL);
1109381a2a9aSdr146992 	ASSERT(he != NULL);
1110381a2a9aSdr146992 
1111*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1112381a2a9aSdr146992 
1113*7ddc9b1aSDarren Reed 	/*
1114*7ddc9b1aSDarren Reed 	 * Set the flag so that we can call hook_event_notify_run without
1115*7ddc9b1aSDarren Reed 	 * holding any locks but at the same time prevent other changes to
1116*7ddc9b1aSDarren Reed 	 * the event at the same time.
1117*7ddc9b1aSDarren Reed 	 */
1118*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
1119*7ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
1120*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1121381a2a9aSdr146992 		return (ENXIO);
1122381a2a9aSdr146992 	}
1123381a2a9aSdr146992 
1124*7ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, he->he_name);
1125*7ddc9b1aSDarren Reed 	if (hei == NULL) {
1126*7ddc9b1aSDarren Reed 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1127*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1128*7ddc9b1aSDarren Reed 		return (ESRCH);
1129381a2a9aSdr146992 	}
1130381a2a9aSdr146992 
1131*7ddc9b1aSDarren Reed 	free_family = B_FALSE;
1132381a2a9aSdr146992 
1133*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1134*7ddc9b1aSDarren Reed 	/*
1135*7ddc9b1aSDarren Reed 	 * If there are any hooks still registered for this event or
1136*7ddc9b1aSDarren Reed 	 * there are any notifiers registered, return an error indicating
1137*7ddc9b1aSDarren Reed 	 * that the event is still busy.
1138*7ddc9b1aSDarren Reed 	 */
1139*7ddc9b1aSDarren Reed 	if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
1140*7ddc9b1aSDarren Reed 		hei->hei_condemned = B_TRUE;
1141*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
1142*7ddc9b1aSDarren Reed 	} else {
1143*7ddc9b1aSDarren Reed 		/* hei_condemned = B_FALSE is implied from creation */
1144*7ddc9b1aSDarren Reed 		/*
1145*7ddc9b1aSDarren Reed 		 * Even though we know the notify list is empty, we call
1146*7ddc9b1aSDarren Reed 		 * hook_wait_destroy here to synchronise wait removing a
1147*7ddc9b1aSDarren Reed 		 * hook from an event.
1148*7ddc9b1aSDarren Reed 		 */
1149*7ddc9b1aSDarren Reed 		hook_wait_destroy(&hei->hei_waiter);
1150*7ddc9b1aSDarren Reed 
1151*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
1152*7ddc9b1aSDarren Reed 
1153*7ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
1154*7ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead))
1155*7ddc9b1aSDarren Reed 			free_family = B_TRUE;
1156*7ddc9b1aSDarren Reed 	}
1157*7ddc9b1aSDarren Reed 
1158*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1159*7ddc9b1aSDarren Reed 
1160*7ddc9b1aSDarren Reed 	hook_notify_run(&hfi->hfi_nhead,
1161*7ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
1162*7ddc9b1aSDarren Reed 
1163*7ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1164*7ddc9b1aSDarren Reed 
1165*7ddc9b1aSDarren Reed 	if (!hei->hei_condemned) {
1166*7ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
1167*7ddc9b1aSDarren Reed 		if (free_family)
1168*7ddc9b1aSDarren Reed 			hook_family_free(hfi, hfi->hfi_stack);
1169*7ddc9b1aSDarren Reed 	}
1170381a2a9aSdr146992 
1171381a2a9aSdr146992 	return (0);
1172381a2a9aSdr146992 }
1173381a2a9aSdr146992 
1174*7ddc9b1aSDarren Reed /*
1175*7ddc9b1aSDarren Reed  * Function:	hook_event_free
1176*7ddc9b1aSDarren Reed  * Returns:	None
1177*7ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
1178*7ddc9b1aSDarren Reed  *
1179*7ddc9b1aSDarren Reed  * Free alloc memory for event
1180*7ddc9b1aSDarren Reed  */
1181*7ddc9b1aSDarren Reed static void
1182*7ddc9b1aSDarren Reed hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
1183*7ddc9b1aSDarren Reed {
1184*7ddc9b1aSDarren Reed 	boolean_t free_family;
1185*7ddc9b1aSDarren Reed 
1186*7ddc9b1aSDarren Reed 	ASSERT(hei != NULL);
1187*7ddc9b1aSDarren Reed 
1188*7ddc9b1aSDarren Reed 	if (hfi != NULL) {
1189*7ddc9b1aSDarren Reed 		CVW_ENTER_WRITE(&hfi->hfi_lock);
1190*7ddc9b1aSDarren Reed 		/*
1191*7ddc9b1aSDarren Reed 		 * Remove the event from the hook family's list.
1192*7ddc9b1aSDarren Reed 		 */
1193*7ddc9b1aSDarren Reed 		SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
1194*7ddc9b1aSDarren Reed 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
1195*7ddc9b1aSDarren Reed 		    TAILQ_EMPTY(&hfi->hfi_nhead)) {
1196*7ddc9b1aSDarren Reed 			free_family = B_TRUE;
1197*7ddc9b1aSDarren Reed 		} else {
1198*7ddc9b1aSDarren Reed 			free_family = B_FALSE;
1199*7ddc9b1aSDarren Reed 		}
1200*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1201*7ddc9b1aSDarren Reed 	}
1202*7ddc9b1aSDarren Reed 
1203*7ddc9b1aSDarren Reed 	if (hei->hei_kstatp != NULL) {
1204*7ddc9b1aSDarren Reed 		ASSERT(hfi != NULL);
1205*7ddc9b1aSDarren Reed 
1206*7ddc9b1aSDarren Reed 		kstat_delete_netstack(hei->hei_kstatp,
1207*7ddc9b1aSDarren Reed 		    hfi->hfi_stack->hks_netstackid);
1208*7ddc9b1aSDarren Reed 		hei->hei_kstatp = NULL;
1209*7ddc9b1aSDarren Reed 	}
1210*7ddc9b1aSDarren Reed 
1211*7ddc9b1aSDarren Reed 	/* Free container */
1212*7ddc9b1aSDarren Reed 	kmem_free(hei, sizeof (*hei));
1213*7ddc9b1aSDarren Reed 
1214*7ddc9b1aSDarren Reed 	if (free_family)
1215*7ddc9b1aSDarren Reed 		hook_family_free(hfi, hfi->hfi_stack);
1216*7ddc9b1aSDarren Reed }
1217381a2a9aSdr146992 
1218381a2a9aSdr146992 /*
1219381a2a9aSdr146992  * Function:    hook_event_checkdup
1220381a2a9aSdr146992  * Returns:     internal event pointer - NULL = Not match
1221381a2a9aSdr146992  * Parameters:  he(I) - event pointer
1222381a2a9aSdr146992  *
1223*7ddc9b1aSDarren Reed  * Search all of the hook families to see if the event being passed in
1224*7ddc9b1aSDarren Reed  * has already been associated with one.
1225381a2a9aSdr146992  */
1226381a2a9aSdr146992 static hook_event_int_t *
1227f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
1228381a2a9aSdr146992 {
1229381a2a9aSdr146992 	hook_family_int_t *hfi;
1230381a2a9aSdr146992 	hook_event_int_t *hei;
1231381a2a9aSdr146992 
1232381a2a9aSdr146992 	ASSERT(he != NULL);
1233381a2a9aSdr146992 
1234*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
1235f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
1236381a2a9aSdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1237*7ddc9b1aSDarren Reed 			if (hei->hei_event == he) {
1238*7ddc9b1aSDarren Reed 				CVW_EXIT_READ(&hks->hks_lock);
1239381a2a9aSdr146992 				return (hei);
1240381a2a9aSdr146992 			}
1241381a2a9aSdr146992 		}
1242*7ddc9b1aSDarren Reed 	}
1243*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
1244381a2a9aSdr146992 
1245381a2a9aSdr146992 	return (NULL);
1246381a2a9aSdr146992 }
1247381a2a9aSdr146992 
1248381a2a9aSdr146992 /*
1249381a2a9aSdr146992  * Function:	hook_event_copy
1250381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Failed
1251381a2a9aSdr146992  * Parameters:	src(I) - event pointer
1252381a2a9aSdr146992  *
1253381a2a9aSdr146992  * Allocate internal event block and duplicate incoming event
1254381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
1255381a2a9aSdr146992  */
1256381a2a9aSdr146992 static hook_event_int_t *
1257381a2a9aSdr146992 hook_event_copy(hook_event_t *src)
1258381a2a9aSdr146992 {
1259381a2a9aSdr146992 	hook_event_int_t *new;
1260381a2a9aSdr146992 
1261381a2a9aSdr146992 	ASSERT(src != NULL);
1262381a2a9aSdr146992 	ASSERT(src->he_name != NULL);
1263381a2a9aSdr146992 
1264381a2a9aSdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1265381a2a9aSdr146992 
1266381a2a9aSdr146992 	/* Copy body */
1267381a2a9aSdr146992 	TAILQ_INIT(&new->hei_head);
1268381a2a9aSdr146992 	new->hei_event = src;
1269381a2a9aSdr146992 
1270381a2a9aSdr146992 	return (new);
1271381a2a9aSdr146992 }
1272381a2a9aSdr146992 
1273381a2a9aSdr146992 /*
1274381a2a9aSdr146992  * Function:	hook_event_find
1275381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Not match
1276381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1277381a2a9aSdr146992  *		event(I) - event name string
1278381a2a9aSdr146992  *
1279381a2a9aSdr146992  * Search event list with event name
1280*7ddc9b1aSDarren Reed  * 	A lock on hfi->hfi_lock must be held when called.
1281381a2a9aSdr146992  */
1282381a2a9aSdr146992 static hook_event_int_t *
1283381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
1284381a2a9aSdr146992 {
1285381a2a9aSdr146992 	hook_event_int_t *hei = NULL;
1286381a2a9aSdr146992 
1287381a2a9aSdr146992 	ASSERT(hfi != NULL);
1288381a2a9aSdr146992 	ASSERT(event != NULL);
1289381a2a9aSdr146992 
1290381a2a9aSdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1291*7ddc9b1aSDarren Reed 		if ((strcmp(hei->hei_event->he_name, event) == 0) &&
1292*7ddc9b1aSDarren Reed 		    ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
1293381a2a9aSdr146992 			break;
1294381a2a9aSdr146992 	}
1295381a2a9aSdr146992 	return (hei);
1296381a2a9aSdr146992 }
1297381a2a9aSdr146992 
1298381a2a9aSdr146992 /*
1299*7ddc9b1aSDarren Reed  * Function:	hook_event_notify_register
1300*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
1301*7ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
1302*7ddc9b1aSDarren Reed  *              event(I)    - name of the event
1303*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
1304*7ddc9b1aSDarren Reed  *              arg(I)      - arg to provide callback when it is called
1305381a2a9aSdr146992  *
1306*7ddc9b1aSDarren Reed  * Adds a new callback to the event named by "event" (we must find it)
1307*7ddc9b1aSDarren Reed  * that will be executed each time a new hook is added to the event.
1308*7ddc9b1aSDarren Reed  * Of course, if the stack is being shut down, this call should fail.
1309381a2a9aSdr146992  */
1310*7ddc9b1aSDarren Reed int
1311*7ddc9b1aSDarren Reed hook_event_notify_register(hook_family_int_t *hfi, char *event,
1312*7ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
1313381a2a9aSdr146992 {
1314*7ddc9b1aSDarren Reed 	hook_event_int_t *hei;
1315*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
1316*7ddc9b1aSDarren Reed 	int error;
1317381a2a9aSdr146992 
1318*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
1319*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hks->hks_lock);
1320*7ddc9b1aSDarren Reed 	if (hks->hks_shutdown != 0) {
1321*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1322*7ddc9b1aSDarren Reed 		return (ESHUTDOWN);
1323381a2a9aSdr146992 	}
1324381a2a9aSdr146992 
1325*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
1326*7ddc9b1aSDarren Reed 
1327*7ddc9b1aSDarren Reed 	if (hfi->hfi_condemned) {
1328*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
1329*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1330*7ddc9b1aSDarren Reed 		return (ESHUTDOWN);
1331*7ddc9b1aSDarren Reed 	}
1332*7ddc9b1aSDarren Reed 
1333*7ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
1334*7ddc9b1aSDarren Reed 	if (hei == NULL) {
1335*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
1336*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1337*7ddc9b1aSDarren Reed 		return (ESRCH);
1338*7ddc9b1aSDarren Reed 	}
1339*7ddc9b1aSDarren Reed 
1340*7ddc9b1aSDarren Reed 	/*
1341*7ddc9b1aSDarren Reed 	 * Grabbing the read lock on hei_lock is only so that we can
1342*7ddc9b1aSDarren Reed 	 * synchronise access to hei_condemned.
1343*7ddc9b1aSDarren Reed 	 */
1344*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1345*7ddc9b1aSDarren Reed 	if (hei->hei_condemned) {
1346*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
1347*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
1348*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hks->hks_lock);
1349*7ddc9b1aSDarren Reed 		return (ESHUTDOWN);
1350*7ddc9b1aSDarren Reed 	}
1351*7ddc9b1aSDarren Reed 
1352*7ddc9b1aSDarren Reed 	error = hook_notify_register(&hei->hei_lock, &hei->hei_nhead,
1353*7ddc9b1aSDarren Reed 	    callback, arg);
1354*7ddc9b1aSDarren Reed 
1355*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
1356*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
1357*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hks->hks_lock);
1358*7ddc9b1aSDarren Reed 
1359*7ddc9b1aSDarren Reed 	return (error);
1360*7ddc9b1aSDarren Reed }
1361*7ddc9b1aSDarren Reed 
1362*7ddc9b1aSDarren Reed /*
1363*7ddc9b1aSDarren Reed  * Function:	hook_event_notify_unregister
1364*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
1365*7ddc9b1aSDarren Reed  * Parameters:	hfi(I)      - hook family
1366*7ddc9b1aSDarren Reed  *              event(I)    - name of the event
1367*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
1368*7ddc9b1aSDarren Reed  *
1369*7ddc9b1aSDarren Reed  * Remove the given callback from the named event's list of functions
1370*7ddc9b1aSDarren Reed  * to call when a hook is added or removed.
1371*7ddc9b1aSDarren Reed  */
1372*7ddc9b1aSDarren Reed int
1373*7ddc9b1aSDarren Reed hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
1374*7ddc9b1aSDarren Reed     hook_notify_fn_t callback)
1375*7ddc9b1aSDarren Reed {
1376*7ddc9b1aSDarren Reed 	hook_event_int_t *hei;
1377*7ddc9b1aSDarren Reed 	boolean_t free_event;
1378*7ddc9b1aSDarren Reed 	int error;
1379*7ddc9b1aSDarren Reed 
1380*7ddc9b1aSDarren Reed 	CVW_ENTER_READ(&hfi->hfi_lock);
1381*7ddc9b1aSDarren Reed 
1382*7ddc9b1aSDarren Reed 	hei = hook_event_find(hfi, event);
1383*7ddc9b1aSDarren Reed 	if (hei == NULL) {
1384*7ddc9b1aSDarren Reed 		CVW_EXIT_READ(&hfi->hfi_lock);
1385*7ddc9b1aSDarren Reed 		return (ESRCH);
1386*7ddc9b1aSDarren Reed 	}
1387*7ddc9b1aSDarren Reed 
1388*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hei->hei_lock);
1389*7ddc9b1aSDarren Reed 
1390*7ddc9b1aSDarren Reed 	error = hook_notify_unregister(&hei->hei_lock, &hei->hei_nhead,
1391*7ddc9b1aSDarren Reed 	    callback);
1392*7ddc9b1aSDarren Reed 
1393*7ddc9b1aSDarren Reed 	/*
1394*7ddc9b1aSDarren Reed 	 * hei_condemned has been set if someone tried to remove the
1395*7ddc9b1aSDarren Reed 	 * event but couldn't because there were still things attached to
1396*7ddc9b1aSDarren Reed 	 * it. Now that we've done a successful remove, if it is now empty
1397*7ddc9b1aSDarren Reed 	 * then by all rights we should be free'ing it too.  Note that the
1398*7ddc9b1aSDarren Reed 	 * expectation is that only the caller of hook_event_add will ever
1399*7ddc9b1aSDarren Reed 	 * call hook_event_remove.
1400*7ddc9b1aSDarren Reed 	 */
1401*7ddc9b1aSDarren Reed 	if ((error == 0) && hei->hei_condemned &&
1402*7ddc9b1aSDarren Reed 	    TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
1403*7ddc9b1aSDarren Reed 		free_event = B_TRUE;
1404*7ddc9b1aSDarren Reed 	} else {
1405*7ddc9b1aSDarren Reed 		free_event = B_FALSE;
1406*7ddc9b1aSDarren Reed 	}
1407*7ddc9b1aSDarren Reed 
1408*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hei->hei_lock);
1409*7ddc9b1aSDarren Reed 	CVW_EXIT_READ(&hfi->hfi_lock);
1410*7ddc9b1aSDarren Reed 
1411*7ddc9b1aSDarren Reed 	if (free_event) {
1412*7ddc9b1aSDarren Reed 		/*
1413*7ddc9b1aSDarren Reed 		 * It is safe to pass in hfi here, without a lock, because
1414*7ddc9b1aSDarren Reed 		 * our structure (hei) is still on one of its lists and thus
1415*7ddc9b1aSDarren Reed 		 * it won't be able to disappear yet...
1416*7ddc9b1aSDarren Reed 		 */
1417*7ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
1418*7ddc9b1aSDarren Reed 	}
1419*7ddc9b1aSDarren Reed 
1420*7ddc9b1aSDarren Reed 	return (error);
1421*7ddc9b1aSDarren Reed }
1422*7ddc9b1aSDarren Reed 
1423*7ddc9b1aSDarren Reed /*
1424*7ddc9b1aSDarren Reed  * Function:	hook_event_notify_run
1425*7ddc9b1aSDarren Reed  * Returns:	None
1426*7ddc9b1aSDarren Reed  * Parameters:	nrun(I) - pointer to the list of callbacks to execute
1427*7ddc9b1aSDarren Reed  *              hfi(I)  - hook stack pointer to execute callbacks for
1428*7ddc9b1aSDarren Reed  *              name(I) - name of a hook family
1429*7ddc9b1aSDarren Reed  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
1430*7ddc9b1aSDarren Reed  *
1431*7ddc9b1aSDarren Reed  * Execute all of the callbacks registered for this event.
1432*7ddc9b1aSDarren Reed  */
1433*7ddc9b1aSDarren Reed static void
1434*7ddc9b1aSDarren Reed hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
1435*7ddc9b1aSDarren Reed     char *event, char *name, hook_notify_cmd_t cmd)
1436*7ddc9b1aSDarren Reed {
1437*7ddc9b1aSDarren Reed 
1438*7ddc9b1aSDarren Reed 	hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
1439*7ddc9b1aSDarren Reed 	    event, name, cmd);
1440*7ddc9b1aSDarren Reed }
1441381a2a9aSdr146992 
1442381a2a9aSdr146992 /*
1443381a2a9aSdr146992  * Function:	hook_register
1444381a2a9aSdr146992  * Returns:	int- 0 = Succ, Else = Fail
1445381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1446381a2a9aSdr146992  *		event(I) - event name string
1447381a2a9aSdr146992  *		h(I) - hook pointer
1448381a2a9aSdr146992  *
1449*7ddc9b1aSDarren Reed  * Add new hook to hook list on the specified family and event.
1450381a2a9aSdr146992  */
1451381a2a9aSdr146992 int
1452381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
1453381a2a9aSdr146992 {
1454381a2a9aSdr146992 	hook_event_int_t *hei;
1455381a2a9aSdr146992 	hook_int_t *hi, *new;
1456*7ddc9b1aSDarren Reed 	int error;
1457381a2a9aSdr146992 
1458381a2a9aSdr146992 	ASSERT(hfi != NULL);
1459381a2a9aSdr146992 	ASSERT(event != NULL);
1460381a2a9aSdr146992 	ASSERT(h != NULL);
1461*7ddc9b1aSDarren Reed 
1462*7ddc9b1aSDarren Reed 	if (hfi->hfi_stack->hks_shutdown)
1463*7ddc9b1aSDarren Reed 		return (NULL);
1464381a2a9aSdr146992 
1465381a2a9aSdr146992 	/* Alloc hook_int_t and copy hook */
1466381a2a9aSdr146992 	new = hook_copy(h);
1467381a2a9aSdr146992 	if (new == NULL)
1468381a2a9aSdr146992 		return (ENOMEM);
1469381a2a9aSdr146992 
1470381a2a9aSdr146992 	/*
1471381a2a9aSdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
1472381a2a9aSdr146992 	 * to hold global family write lock. Just get read lock here to
1473381a2a9aSdr146992 	 * ensure event will not be removed when doing hooks operation
1474381a2a9aSdr146992 	 */
1475*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1476381a2a9aSdr146992 
1477381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
1478381a2a9aSdr146992 	if (hei == NULL) {
1479*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1480*7ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
1481381a2a9aSdr146992 		return (ENXIO);
1482381a2a9aSdr146992 	}
1483381a2a9aSdr146992 
1484381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
1485381a2a9aSdr146992 
1486381a2a9aSdr146992 	hi = hook_find(hei, h);
1487381a2a9aSdr146992 	if (hi != NULL) {
1488*7ddc9b1aSDarren Reed 		error = EEXIST;
1489*7ddc9b1aSDarren Reed 		goto bad_add;
1490*7ddc9b1aSDarren Reed 	}
1491*7ddc9b1aSDarren Reed 
1492*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
1493*7ddc9b1aSDarren Reed 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
1494*7ddc9b1aSDarren Reed 		error = ENOENT;
1495*7ddc9b1aSDarren Reed bad_add:
1496381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
1497*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1498*7ddc9b1aSDarren Reed 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
1499*7ddc9b1aSDarren Reed 		return (error);
1500381a2a9aSdr146992 	}
1501381a2a9aSdr146992 
1502381a2a9aSdr146992 	/* Add to hook list head */
1503*7ddc9b1aSDarren Reed 	error = hook_insert(&hei->hei_head, new);
1504*7ddc9b1aSDarren Reed 	if (error == 0) {
1505381a2a9aSdr146992 		hei->hei_event->he_interested = B_TRUE;
1506*7ddc9b1aSDarren Reed 		hei->hei_kstats.hooks_added.value.ui64++;
1507*7ddc9b1aSDarren Reed 
1508*7ddc9b1aSDarren Reed 		hook_init_kstats(hfi, hei, new);
1509*7ddc9b1aSDarren Reed 	}
1510381a2a9aSdr146992 
1511381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
1512*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1513*7ddc9b1aSDarren Reed 
1514*7ddc9b1aSDarren Reed 	/*
1515*7ddc9b1aSDarren Reed 	 * Note that the name string passed through to the notify callbacks
1516*7ddc9b1aSDarren Reed 	 * is from the original hook being registered, not the copy being
1517*7ddc9b1aSDarren Reed 	 * inserted.
1518*7ddc9b1aSDarren Reed 	 */
1519*7ddc9b1aSDarren Reed 	if (error == 0) {
1520*7ddc9b1aSDarren Reed 		hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
1521*7ddc9b1aSDarren Reed 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1522*7ddc9b1aSDarren Reed 	}
1523*7ddc9b1aSDarren Reed 
1524*7ddc9b1aSDarren Reed 	return (error);
1525*7ddc9b1aSDarren Reed }
1526*7ddc9b1aSDarren Reed 
1527*7ddc9b1aSDarren Reed /*
1528*7ddc9b1aSDarren Reed  * Function:	hook_insert
1529*7ddc9b1aSDarren Reed  * Returns:	int- 0 = Succ, else = Fail
1530*7ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
1531*7ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
1532*7ddc9b1aSDarren Reed  *
1533*7ddc9b1aSDarren Reed  * Try to insert the hook onto the list of hooks according to the hints
1534*7ddc9b1aSDarren Reed  * given in the hook to be inserted and those that already exist on the
1535*7ddc9b1aSDarren Reed  * list.  For now, the implementation permits only a single hook to be
1536*7ddc9b1aSDarren Reed  * either first or last and names provided with before or after are only
1537*7ddc9b1aSDarren Reed  * loosely coupled with the action.
1538*7ddc9b1aSDarren Reed  */
1539*7ddc9b1aSDarren Reed static int
1540*7ddc9b1aSDarren Reed hook_insert(hook_int_head_t *head, hook_int_t *new)
1541*7ddc9b1aSDarren Reed {
1542*7ddc9b1aSDarren Reed 	hook_int_t *before;
1543*7ddc9b1aSDarren Reed 	hook_int_t *hi;
1544*7ddc9b1aSDarren Reed 	hook_t *hih;
1545*7ddc9b1aSDarren Reed 	hook_t *h = &new->hi_hook;
1546*7ddc9b1aSDarren Reed 
1547*7ddc9b1aSDarren Reed 	switch (new->hi_hook.h_hint) {
1548*7ddc9b1aSDarren Reed 	case HH_NONE :
1549*7ddc9b1aSDarren Reed 		before = NULL;
1550*7ddc9b1aSDarren Reed 		/*
1551*7ddc9b1aSDarren Reed 		 * If there is no hint present (or not one that can be
1552*7ddc9b1aSDarren Reed 		 * satisfied now) then try to at least respect the wishes
1553*7ddc9b1aSDarren Reed 		 * of those that want to be last.  If there are none wanting
1554*7ddc9b1aSDarren Reed 		 * to be last then add the new hook to the tail of the
1555*7ddc9b1aSDarren Reed 		 * list - this means we keep any wanting to be first
1556*7ddc9b1aSDarren Reed 		 * happy without having to search for HH_FIRST.
1557*7ddc9b1aSDarren Reed 		 */
1558*7ddc9b1aSDarren Reed 		TAILQ_FOREACH(hi, head, hi_entry) {
1559*7ddc9b1aSDarren Reed 			hih = &hi->hi_hook;
1560*7ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_AFTER) &&
1561*7ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
1562*7ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
1563*7ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
1564*7ddc9b1aSDarren Reed 				return (0);
1565*7ddc9b1aSDarren Reed 			}
1566*7ddc9b1aSDarren Reed 			if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
1567*7ddc9b1aSDarren Reed 			    (strcmp(h->h_name,
1568*7ddc9b1aSDarren Reed 			    (char *)hih->h_hintvalue) == 0)) {
1569*7ddc9b1aSDarren Reed 				before = hi;
1570*7ddc9b1aSDarren Reed 			}
1571*7ddc9b1aSDarren Reed 		}
1572*7ddc9b1aSDarren Reed 		if (before != NULL) {
1573*7ddc9b1aSDarren Reed 			TAILQ_INSERT_AFTER(head, before, new, hi_entry);
1574*7ddc9b1aSDarren Reed 			return (0);
1575*7ddc9b1aSDarren Reed 		}
1576*7ddc9b1aSDarren Reed 		hook_insert_plain(head, new);
1577*7ddc9b1aSDarren Reed 		break;
1578*7ddc9b1aSDarren Reed 
1579*7ddc9b1aSDarren Reed 	case HH_FIRST :
1580*7ddc9b1aSDarren Reed 		hi = TAILQ_FIRST(head);
1581*7ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
1582*7ddc9b1aSDarren Reed 			return (EBUSY);
1583*7ddc9b1aSDarren Reed 		TAILQ_INSERT_HEAD(head, new, hi_entry);
1584*7ddc9b1aSDarren Reed 		break;
1585*7ddc9b1aSDarren Reed 
1586*7ddc9b1aSDarren Reed 	case HH_LAST :
1587*7ddc9b1aSDarren Reed 		hi = TAILQ_LAST(head, hook_int_head);
1588*7ddc9b1aSDarren Reed 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
1589*7ddc9b1aSDarren Reed 			return (EBUSY);
1590*7ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
1591*7ddc9b1aSDarren Reed 		break;
1592*7ddc9b1aSDarren Reed 
1593*7ddc9b1aSDarren Reed 	case HH_BEFORE :
1594*7ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
1595*7ddc9b1aSDarren Reed 		if (hi == NULL)
1596*7ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
1597*7ddc9b1aSDarren Reed 
1598*7ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_FIRST)
1599*7ddc9b1aSDarren Reed 			return (EBUSY);
1600*7ddc9b1aSDarren Reed 
1601*7ddc9b1aSDarren Reed 		TAILQ_INSERT_BEFORE(hi, new, hi_entry);
1602*7ddc9b1aSDarren Reed 		break;
1603*7ddc9b1aSDarren Reed 
1604*7ddc9b1aSDarren Reed 	case HH_AFTER :
1605*7ddc9b1aSDarren Reed 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
1606*7ddc9b1aSDarren Reed 		if (hi == NULL)
1607*7ddc9b1aSDarren Reed 			return (hook_insert_afterbefore(head, new));
1608*7ddc9b1aSDarren Reed 
1609*7ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST)
1610*7ddc9b1aSDarren Reed 			return (EBUSY);
1611*7ddc9b1aSDarren Reed 
1612*7ddc9b1aSDarren Reed 		TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
1613*7ddc9b1aSDarren Reed 		break;
1614*7ddc9b1aSDarren Reed 
1615*7ddc9b1aSDarren Reed 	default :
1616*7ddc9b1aSDarren Reed 		return (EINVAL);
1617*7ddc9b1aSDarren Reed 	}
1618*7ddc9b1aSDarren Reed 
1619381a2a9aSdr146992 	return (0);
1620381a2a9aSdr146992 }
1621381a2a9aSdr146992 
1622*7ddc9b1aSDarren Reed /*
1623*7ddc9b1aSDarren Reed  * Function:	hook_insert_plain
1624*7ddc9b1aSDarren Reed  * Returns:	int- 0 = success, else = failure
1625*7ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
1626*7ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
1627*7ddc9b1aSDarren Reed  *
1628*7ddc9b1aSDarren Reed  * Insert a hook such that it respects the wishes of those that want to
1629*7ddc9b1aSDarren Reed  * be last.  If there are none wanting to be last then add the new hook
1630*7ddc9b1aSDarren Reed  * to the tail of the list - this means we keep any wanting to be first
1631*7ddc9b1aSDarren Reed  * happy without having to search for HH_FIRST.
1632*7ddc9b1aSDarren Reed  */
1633*7ddc9b1aSDarren Reed static void
1634*7ddc9b1aSDarren Reed hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
1635*7ddc9b1aSDarren Reed {
1636*7ddc9b1aSDarren Reed 	hook_int_t *hi;
1637*7ddc9b1aSDarren Reed 
1638*7ddc9b1aSDarren Reed 	hi = TAILQ_FIRST(head);
1639*7ddc9b1aSDarren Reed 	if (hi != NULL) {
1640*7ddc9b1aSDarren Reed 		if (hi->hi_hook.h_hint == HH_LAST) {
1641*7ddc9b1aSDarren Reed 			TAILQ_INSERT_BEFORE(hi, new, hi_entry);
1642*7ddc9b1aSDarren Reed 		} else {
1643*7ddc9b1aSDarren Reed 			TAILQ_INSERT_TAIL(head, new, hi_entry);
1644*7ddc9b1aSDarren Reed 		}
1645*7ddc9b1aSDarren Reed 	} else {
1646*7ddc9b1aSDarren Reed 		TAILQ_INSERT_TAIL(head, new, hi_entry);
1647*7ddc9b1aSDarren Reed 	}
1648*7ddc9b1aSDarren Reed }
1649*7ddc9b1aSDarren Reed 
1650*7ddc9b1aSDarren Reed /*
1651*7ddc9b1aSDarren Reed  * Function:	hook_insert_afterbefore
1652*7ddc9b1aSDarren Reed  * Returns:	int- 0 = success, else = failure
1653*7ddc9b1aSDarren Reed  * Parameters:	head(I) - pointer to hook list to insert hook onto
1654*7ddc9b1aSDarren Reed  *		new(I)  - pointer to hook to be inserted
1655*7ddc9b1aSDarren Reed  *
1656*7ddc9b1aSDarren Reed  * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
1657*7ddc9b1aSDarren Reed  * possible, so now we need to be more careful.  The first pass is to go
1658*7ddc9b1aSDarren Reed  * through the list and look for any other hooks that also specify the
1659*7ddc9b1aSDarren Reed  * same hint name as the new one.  The object of this exercise is to make
1660*7ddc9b1aSDarren Reed  * sure that hooks with HH_BEFORE always appear on the list before those
1661*7ddc9b1aSDarren Reed  * with HH_AFTER so that when said hook arrives, it can be placed in the
1662*7ddc9b1aSDarren Reed  * middle of the BEFOREs and AFTERs.  If this condition does not arise,
1663*7ddc9b1aSDarren Reed  * just use hook_insert_plain() to try and insert the hook somewhere that
1664*7ddc9b1aSDarren Reed  * is innocuous to existing efforts.
1665*7ddc9b1aSDarren Reed  */
1666*7ddc9b1aSDarren Reed static int
1667*7ddc9b1aSDarren Reed hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
1668*7ddc9b1aSDarren Reed {
1669*7ddc9b1aSDarren Reed 	hook_int_t *hi;
1670*7ddc9b1aSDarren Reed 	hook_t *nh;
1671*7ddc9b1aSDarren Reed 	hook_t *h;
1672*7ddc9b1aSDarren Reed 
1673*7ddc9b1aSDarren Reed 	nh = &new->hi_hook;
1674*7ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_NONE);
1675*7ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_LAST);
1676*7ddc9b1aSDarren Reed 	ASSERT(new->hi_hook.h_hint != HH_FIRST);
1677*7ddc9b1aSDarren Reed 
1678*7ddc9b1aSDarren Reed 	/*
1679*7ddc9b1aSDarren Reed 	 * First, look through the list to see if there are any other
1680*7ddc9b1aSDarren Reed 	 * before's or after's that have a matching hint name.
1681*7ddc9b1aSDarren Reed 	 */
1682*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
1683*7ddc9b1aSDarren Reed 		h = &hi->hi_hook;
1684*7ddc9b1aSDarren Reed 		switch (h->h_hint) {
1685*7ddc9b1aSDarren Reed 		case HH_FIRST :
1686*7ddc9b1aSDarren Reed 		case HH_LAST :
1687*7ddc9b1aSDarren Reed 		case HH_NONE :
1688*7ddc9b1aSDarren Reed 			break;
1689*7ddc9b1aSDarren Reed 		case HH_BEFORE :
1690*7ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
1691*7ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
1692*7ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
1693*7ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
1694*7ddc9b1aSDarren Reed 				return (0);
1695*7ddc9b1aSDarren Reed 			}
1696*7ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
1697*7ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
1698*7ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
1699*7ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
1700*7ddc9b1aSDarren Reed 				return (0);
1701*7ddc9b1aSDarren Reed 			}
1702*7ddc9b1aSDarren Reed 			break;
1703*7ddc9b1aSDarren Reed 		case HH_AFTER :
1704*7ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_AFTER) &&
1705*7ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
1706*7ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
1707*7ddc9b1aSDarren Reed 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
1708*7ddc9b1aSDarren Reed 				return (0);
1709*7ddc9b1aSDarren Reed 			}
1710*7ddc9b1aSDarren Reed 			if ((nh->h_hint == HH_BEFORE) &&
1711*7ddc9b1aSDarren Reed 			    (strcmp((char *)h->h_hintvalue,
1712*7ddc9b1aSDarren Reed 			    (char *)nh->h_hintvalue) == 0)) {
1713*7ddc9b1aSDarren Reed 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
1714*7ddc9b1aSDarren Reed 				return (0);
1715*7ddc9b1aSDarren Reed 			}
1716*7ddc9b1aSDarren Reed 			break;
1717*7ddc9b1aSDarren Reed 		}
1718*7ddc9b1aSDarren Reed 	}
1719*7ddc9b1aSDarren Reed 
1720*7ddc9b1aSDarren Reed 	hook_insert_plain(head, new);
1721*7ddc9b1aSDarren Reed 
1722*7ddc9b1aSDarren Reed 	return (0);
1723*7ddc9b1aSDarren Reed }
1724381a2a9aSdr146992 
1725381a2a9aSdr146992 /*
1726381a2a9aSdr146992  * Function:	hook_unregister
1727381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
1728381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
1729381a2a9aSdr146992  *		event(I) - event name string
1730381a2a9aSdr146992  *		h(I) - hook pointer
1731381a2a9aSdr146992  *
1732381a2a9aSdr146992  * Remove hook from hook list on specific family, event
1733381a2a9aSdr146992  */
1734381a2a9aSdr146992 int
1735381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
1736381a2a9aSdr146992 {
1737381a2a9aSdr146992 	hook_event_int_t *hei;
1738381a2a9aSdr146992 	hook_int_t *hi;
1739*7ddc9b1aSDarren Reed 	boolean_t free_event;
1740381a2a9aSdr146992 
1741381a2a9aSdr146992 	ASSERT(hfi != NULL);
1742381a2a9aSdr146992 	ASSERT(h != NULL);
1743381a2a9aSdr146992 
1744*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1745381a2a9aSdr146992 
1746381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
1747381a2a9aSdr146992 	if (hei == NULL) {
1748*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1749381a2a9aSdr146992 		return (ENXIO);
1750381a2a9aSdr146992 	}
1751381a2a9aSdr146992 
1752381a2a9aSdr146992 	/* Hold write lock for event */
1753381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
1754381a2a9aSdr146992 
1755381a2a9aSdr146992 	hi = hook_find(hei, h);
1756381a2a9aSdr146992 	if (hi == NULL) {
1757381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
1758*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1759381a2a9aSdr146992 		return (ENXIO);
1760381a2a9aSdr146992 	}
1761381a2a9aSdr146992 
1762*7ddc9b1aSDarren Reed 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
1763*7ddc9b1aSDarren Reed 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
1764*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hei->hei_lock);
1765*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1766*7ddc9b1aSDarren Reed 		return (ENOENT);
1767*7ddc9b1aSDarren Reed 	}
1768*7ddc9b1aSDarren Reed 
1769381a2a9aSdr146992 	/* Remove from hook list */
1770381a2a9aSdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
1771*7ddc9b1aSDarren Reed 
1772*7ddc9b1aSDarren Reed 	free_event = B_FALSE;
1773381a2a9aSdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
1774381a2a9aSdr146992 		hei->hei_event->he_interested = B_FALSE;
1775*7ddc9b1aSDarren Reed 		/*
1776*7ddc9b1aSDarren Reed 		 * If the delete pending flag has been set and there are
1777*7ddc9b1aSDarren Reed 		 * no notifiers on the event (and we've removed the last
1778*7ddc9b1aSDarren Reed 		 * hook) then we need to free this event after we're done.
1779*7ddc9b1aSDarren Reed 		 */
1780*7ddc9b1aSDarren Reed 		if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
1781*7ddc9b1aSDarren Reed 			free_event = B_TRUE;
1782381a2a9aSdr146992 	}
1783*7ddc9b1aSDarren Reed 	hei->hei_kstats.hooks_removed.value.ui64++;
1784381a2a9aSdr146992 
1785381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
1786*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1787*7ddc9b1aSDarren Reed 	/*
1788*7ddc9b1aSDarren Reed 	 * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
1789*7ddc9b1aSDarren Reed 	 * will not be free'd and thus the hook_family_int_t wil not
1790*7ddc9b1aSDarren Reed 	 * be free'd either.
1791*7ddc9b1aSDarren Reed 	 */
1792*7ddc9b1aSDarren Reed 	hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
1793*7ddc9b1aSDarren Reed 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
1794381a2a9aSdr146992 
1795*7ddc9b1aSDarren Reed 	hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
1796*7ddc9b1aSDarren Reed 
1797*7ddc9b1aSDarren Reed 	if (free_event)
1798*7ddc9b1aSDarren Reed 		hook_event_free(hei, hfi);
1799*7ddc9b1aSDarren Reed 
1800381a2a9aSdr146992 	return (0);
1801381a2a9aSdr146992 }
1802381a2a9aSdr146992 
1803*7ddc9b1aSDarren Reed /*
1804*7ddc9b1aSDarren Reed  * Function:	hook_find_byname
1805*7ddc9b1aSDarren Reed  * Returns:	internal hook pointer - NULL = Not match
1806*7ddc9b1aSDarren Reed  * Parameters:	hei(I) - internal event pointer
1807*7ddc9b1aSDarren Reed  *		name(I)- hook name
1808*7ddc9b1aSDarren Reed  *
1809*7ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is a hook present that
1810*7ddc9b1aSDarren Reed  * has a matching name to the one being looked for.
1811*7ddc9b1aSDarren Reed  */
1812*7ddc9b1aSDarren Reed static hook_int_t *
1813*7ddc9b1aSDarren Reed hook_find_byname(hook_int_head_t *head, char *name)
1814*7ddc9b1aSDarren Reed {
1815*7ddc9b1aSDarren Reed 	hook_int_t *hi;
1816*7ddc9b1aSDarren Reed 
1817*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(hi, head, hi_entry) {
1818*7ddc9b1aSDarren Reed 		if (strcmp(hi->hi_hook.h_name, name) == 0)
1819*7ddc9b1aSDarren Reed 			return (hi);
1820*7ddc9b1aSDarren Reed 	}
1821*7ddc9b1aSDarren Reed 
1822*7ddc9b1aSDarren Reed 	return (NULL);
1823*7ddc9b1aSDarren Reed }
1824381a2a9aSdr146992 
1825381a2a9aSdr146992 /*
1826381a2a9aSdr146992  * Function:	hook_find
1827381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Not match
1828381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
1829381a2a9aSdr146992  *		h(I) - hook pointer
1830381a2a9aSdr146992  *
1831*7ddc9b1aSDarren Reed  * Search an event's list of hooks to see if there is already one that
1832*7ddc9b1aSDarren Reed  * matches the hook being passed in.  Currently the only criteria for a
1833*7ddc9b1aSDarren Reed  * successful search here is for the names to be the same.
1834381a2a9aSdr146992  */
1835381a2a9aSdr146992 static hook_int_t *
1836381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
1837381a2a9aSdr146992 {
1838381a2a9aSdr146992 
1839381a2a9aSdr146992 	ASSERT(hei != NULL);
1840381a2a9aSdr146992 	ASSERT(h != NULL);
1841381a2a9aSdr146992 
1842*7ddc9b1aSDarren Reed 	return (hook_find_byname(&hei->hei_head, h->h_name));
1843381a2a9aSdr146992 }
1844381a2a9aSdr146992 
1845381a2a9aSdr146992 /*
1846381a2a9aSdr146992  * Function:	hook_copy
1847381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Failed
1848381a2a9aSdr146992  * Parameters:	src(I) - hook pointer
1849381a2a9aSdr146992  *
1850381a2a9aSdr146992  * Allocate internal hook block and duplicate incoming hook.
1851381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
1852*7ddc9b1aSDarren Reed  * Because hook_copy() is responsible for the creation of the internal
1853*7ddc9b1aSDarren Reed  * hook structure that is used here, it takes on population the structure
1854*7ddc9b1aSDarren Reed  * with the kstat information.  Note that while the kstat bits are
1855*7ddc9b1aSDarren Reed  * seeded here, their installation of the kstats is handled elsewhere.
1856381a2a9aSdr146992  */
1857381a2a9aSdr146992 static hook_int_t *
1858381a2a9aSdr146992 hook_copy(hook_t *src)
1859381a2a9aSdr146992 {
1860381a2a9aSdr146992 	hook_int_t *new;
1861381a2a9aSdr146992 	hook_t *dst;
1862*7ddc9b1aSDarren Reed 	int len;
1863381a2a9aSdr146992 
1864381a2a9aSdr146992 	ASSERT(src != NULL);
1865381a2a9aSdr146992 	ASSERT(src->h_name != NULL);
1866381a2a9aSdr146992 
1867381a2a9aSdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
1868381a2a9aSdr146992 
1869381a2a9aSdr146992 	/* Copy body */
1870381a2a9aSdr146992 	dst = &new->hi_hook;
1871381a2a9aSdr146992 	*dst = *src;
1872381a2a9aSdr146992 
1873381a2a9aSdr146992 	/* Copy name */
1874*7ddc9b1aSDarren Reed 	len = strlen(src->h_name);
1875*7ddc9b1aSDarren Reed 	dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
1876381a2a9aSdr146992 	(void) strcpy(dst->h_name, src->h_name);
1877381a2a9aSdr146992 
1878*7ddc9b1aSDarren Reed 	/*
1879*7ddc9b1aSDarren Reed 	 * This is initialised in this manner to make it safer to use the
1880*7ddc9b1aSDarren Reed 	 * same pointer in the kstats field.
1881*7ddc9b1aSDarren Reed 	 */
1882*7ddc9b1aSDarren Reed 	dst->h_hintvalue = (uintptr_t)"";
1883*7ddc9b1aSDarren Reed 
1884*7ddc9b1aSDarren Reed 	if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
1885*7ddc9b1aSDarren Reed 		len = strlen((char *)src->h_hintvalue);
1886*7ddc9b1aSDarren Reed 		if (len > 0) {
1887*7ddc9b1aSDarren Reed 			dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
1888*7ddc9b1aSDarren Reed 			    KM_SLEEP);
1889*7ddc9b1aSDarren Reed 			(void) strcpy((char *)dst->h_hintvalue,
1890*7ddc9b1aSDarren Reed 			    (char *)src->h_hintvalue);
1891*7ddc9b1aSDarren Reed 		}
1892*7ddc9b1aSDarren Reed 	}
1893*7ddc9b1aSDarren Reed 
1894381a2a9aSdr146992 	return (new);
1895381a2a9aSdr146992 }
1896381a2a9aSdr146992 
1897381a2a9aSdr146992 /*
1898*7ddc9b1aSDarren Reed  * Function:	hook_init_kstats
1899*7ddc9b1aSDarren Reed  * Returns:	None
1900*7ddc9b1aSDarren Reed  * Parameters:  hfi(I) - pointer to the family that owns the event.
1901*7ddc9b1aSDarren Reed  *              hei(I) - pointer to the event that owns this hook
1902*7ddc9b1aSDarren Reed  *              hi(I)  - pointer to the hook for which we create kstats for
1903*7ddc9b1aSDarren Reed  *
1904*7ddc9b1aSDarren Reed  * Each hook that is registered with this framework has its own kstats
1905*7ddc9b1aSDarren Reed  * set up so that we can provide an easy way in which to observe the
1906*7ddc9b1aSDarren Reed  * look of hooks (using the kstat command.) The position is set to 0
1907*7ddc9b1aSDarren Reed  * here but is recalculated after we know the insertion has been a
1908*7ddc9b1aSDarren Reed  * success.
1909*7ddc9b1aSDarren Reed  */
1910*7ddc9b1aSDarren Reed static void
1911*7ddc9b1aSDarren Reed hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
1912*7ddc9b1aSDarren Reed {
1913*7ddc9b1aSDarren Reed 	hook_hook_kstat_t template = {
1914*7ddc9b1aSDarren Reed 		{ "version",			KSTAT_DATA_INT32 },
1915*7ddc9b1aSDarren Reed 		{ "flags",			KSTAT_DATA_UINT32 },
1916*7ddc9b1aSDarren Reed 		{ "hint",			KSTAT_DATA_INT32 },
1917*7ddc9b1aSDarren Reed 		{ "hint_value",			KSTAT_DATA_UINT64 },
1918*7ddc9b1aSDarren Reed 		{ "position",			KSTAT_DATA_INT32 },
1919*7ddc9b1aSDarren Reed 		{ "hook_hits",			KSTAT_DATA_UINT64 }
1920*7ddc9b1aSDarren Reed 	};
1921*7ddc9b1aSDarren Reed 	hook_stack_t *hks;
1922*7ddc9b1aSDarren Reed 	size_t kslen;
1923*7ddc9b1aSDarren Reed 	int position;
1924*7ddc9b1aSDarren Reed 	hook_int_t *h;
1925*7ddc9b1aSDarren Reed 
1926*7ddc9b1aSDarren Reed 	kslen = strlen(hfi->hfi_family.hf_name) +
1927*7ddc9b1aSDarren Reed 	    strlen(hei->hei_event->he_name) + 2;
1928*7ddc9b1aSDarren Reed 
1929*7ddc9b1aSDarren Reed 	hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
1930*7ddc9b1aSDarren Reed 	(void) snprintf(hi->hi_ksname, kslen, "%s/%s",
1931*7ddc9b1aSDarren Reed 	    hfi->hfi_family.hf_name, hei->hei_event->he_name);
1932*7ddc9b1aSDarren Reed 
1933*7ddc9b1aSDarren Reed 	hks = hfi->hfi_stack;
1934*7ddc9b1aSDarren Reed 	hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
1935*7ddc9b1aSDarren Reed 	    hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
1936*7ddc9b1aSDarren Reed 	    sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
1937*7ddc9b1aSDarren Reed 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
1938*7ddc9b1aSDarren Reed 
1939*7ddc9b1aSDarren Reed 	/* Initialise the kstats for the structure */
1940*7ddc9b1aSDarren Reed 	bcopy(&template, &hi->hi_kstats, sizeof (template));
1941*7ddc9b1aSDarren Reed 	hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
1942*7ddc9b1aSDarren Reed 	hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
1943*7ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
1944*7ddc9b1aSDarren Reed 	hi->hi_kstats.hook_position.value.i32 = 0;
1945*7ddc9b1aSDarren Reed 	hi->hi_kstats.hook_hits.value.ui64 = 0;
1946*7ddc9b1aSDarren Reed 
1947*7ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
1948*7ddc9b1aSDarren Reed 	case HH_BEFORE :
1949*7ddc9b1aSDarren Reed 	case HH_AFTER :
1950*7ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
1951*7ddc9b1aSDarren Reed 		hi->hi_kstats.hook_hintvalue.value.ui64 =
1952*7ddc9b1aSDarren Reed 		    hi->hi_hook.h_hintvalue;
1953*7ddc9b1aSDarren Reed 		break;
1954*7ddc9b1aSDarren Reed 	default :
1955*7ddc9b1aSDarren Reed 		break;
1956*7ddc9b1aSDarren Reed 	}
1957*7ddc9b1aSDarren Reed 
1958*7ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL) {
1959*7ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
1960*7ddc9b1aSDarren Reed 		hi->hi_kstatp->ks_private =
1961*7ddc9b1aSDarren Reed 		    (void *)(uintptr_t)hks->hks_netstackid;
1962*7ddc9b1aSDarren Reed 
1963*7ddc9b1aSDarren Reed 		kstat_install(hi->hi_kstatp);
1964*7ddc9b1aSDarren Reed 	}
1965*7ddc9b1aSDarren Reed 
1966*7ddc9b1aSDarren Reed 	position = 1;
1967*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1968*7ddc9b1aSDarren Reed 		h->hi_kstats.hook_position.value.ui32 = position++;
1969*7ddc9b1aSDarren Reed 	}
1970*7ddc9b1aSDarren Reed }
1971*7ddc9b1aSDarren Reed 
1972*7ddc9b1aSDarren Reed /*
1973*7ddc9b1aSDarren Reed  * Function:	hook_int_free
1974381a2a9aSdr146992  * Returns:	None
1975381a2a9aSdr146992  * Parameters:	hi(I) - internal hook pointer
1976381a2a9aSdr146992  *
1977381a2a9aSdr146992  * Free alloc memory for hook
1978381a2a9aSdr146992  */
1979381a2a9aSdr146992 static void
1980*7ddc9b1aSDarren Reed hook_int_free(hook_int_t *hi, netstackid_t stackid)
1981381a2a9aSdr146992 {
1982*7ddc9b1aSDarren Reed 	int len;
1983*7ddc9b1aSDarren Reed 
1984381a2a9aSdr146992 	ASSERT(hi != NULL);
1985381a2a9aSdr146992 
1986381a2a9aSdr146992 	/* Free name space */
1987381a2a9aSdr146992 	if (hi->hi_hook.h_name != NULL) {
1988381a2a9aSdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
1989381a2a9aSdr146992 	}
1990*7ddc9b1aSDarren Reed 	if (hi->hi_ksname != NULL) {
1991*7ddc9b1aSDarren Reed 		kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
1992*7ddc9b1aSDarren Reed 	}
1993*7ddc9b1aSDarren Reed 
1994*7ddc9b1aSDarren Reed 	/* Free the name used with the before/after hints. */
1995*7ddc9b1aSDarren Reed 	switch (hi->hi_hook.h_hint) {
1996*7ddc9b1aSDarren Reed 	case HH_BEFORE :
1997*7ddc9b1aSDarren Reed 	case HH_AFTER :
1998*7ddc9b1aSDarren Reed 		len = strlen((char *)hi->hi_hook.h_hintvalue);
1999*7ddc9b1aSDarren Reed 		if (len > 0)
2000*7ddc9b1aSDarren Reed 			kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
2001*7ddc9b1aSDarren Reed 		break;
2002*7ddc9b1aSDarren Reed 	default :
2003*7ddc9b1aSDarren Reed 		break;
2004*7ddc9b1aSDarren Reed 	}
2005*7ddc9b1aSDarren Reed 
2006*7ddc9b1aSDarren Reed 	if (hi->hi_kstatp != NULL)
2007*7ddc9b1aSDarren Reed 		kstat_delete_netstack(hi->hi_kstatp, stackid);
2008381a2a9aSdr146992 
2009381a2a9aSdr146992 	/* Free container */
2010381a2a9aSdr146992 	kmem_free(hi, sizeof (*hi));
2011381a2a9aSdr146992 }
2012*7ddc9b1aSDarren Reed 
2013*7ddc9b1aSDarren Reed /*
2014*7ddc9b1aSDarren Reed  * Function:	hook_alloc
2015*7ddc9b1aSDarren Reed  * Returns:	hook_t * - pointer to new hook structure
2016*7ddc9b1aSDarren Reed  * Parameters:	version(I) - version number of the API when compiled
2017*7ddc9b1aSDarren Reed  *
2018*7ddc9b1aSDarren Reed  * This function serves as the interface for consumers to obtain a hook_t
2019*7ddc9b1aSDarren Reed  * structure.  At this point in time, there is only a single "version" of
2020*7ddc9b1aSDarren Reed  * it, leading to a straight forward function.  In a perfect world the
2021*7ddc9b1aSDarren Reed  * h_vesion would be a protected data structure member, but C isn't that
2022*7ddc9b1aSDarren Reed  * advanced...
2023*7ddc9b1aSDarren Reed  */
2024*7ddc9b1aSDarren Reed hook_t *
2025*7ddc9b1aSDarren Reed hook_alloc(const int h_version)
2026*7ddc9b1aSDarren Reed {
2027*7ddc9b1aSDarren Reed 	hook_t *h;
2028*7ddc9b1aSDarren Reed 
2029*7ddc9b1aSDarren Reed 	h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
2030*7ddc9b1aSDarren Reed 	h->h_version = h_version;
2031*7ddc9b1aSDarren Reed 	return (h);
2032*7ddc9b1aSDarren Reed }
2033*7ddc9b1aSDarren Reed 
2034*7ddc9b1aSDarren Reed /*
2035*7ddc9b1aSDarren Reed  * Function:	hook_free
2036*7ddc9b1aSDarren Reed  * Returns:	None
2037*7ddc9b1aSDarren Reed  * Parameters:	h(I) - external hook pointer
2038*7ddc9b1aSDarren Reed  *
2039*7ddc9b1aSDarren Reed  * This function only free's memory allocated with hook_alloc(), so that if
2040*7ddc9b1aSDarren Reed  * (for example) kernel memory was allocated for h_name, this needs to be
2041*7ddc9b1aSDarren Reed  * free'd before calling hook_free().
2042*7ddc9b1aSDarren Reed  */
2043*7ddc9b1aSDarren Reed void
2044*7ddc9b1aSDarren Reed hook_free(hook_t *h)
2045*7ddc9b1aSDarren Reed {
2046*7ddc9b1aSDarren Reed 	kmem_free(h, sizeof (*h));
2047*7ddc9b1aSDarren Reed }
2048*7ddc9b1aSDarren Reed 
2049*7ddc9b1aSDarren Reed /*
2050*7ddc9b1aSDarren Reed  * Function:	hook_notify_register
2051*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
2052*7ddc9b1aSDarren Reed  * Parameters:	lock(I)     - netstack identifier
2053*7ddc9b1aSDarren Reed  *              head(I)     - top of the list of callbacks
2054*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
2055*7ddc9b1aSDarren Reed  *              arg(I)      - arg to pass back to the function
2056*7ddc9b1aSDarren Reed  *
2057*7ddc9b1aSDarren Reed  * This function implements the modification of the list of callbacks
2058*7ddc9b1aSDarren Reed  * that are registered when someone wants to be advised of a change
2059*7ddc9b1aSDarren Reed  * that has happened.
2060*7ddc9b1aSDarren Reed  */
2061*7ddc9b1aSDarren Reed static int
2062*7ddc9b1aSDarren Reed hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
2063*7ddc9b1aSDarren Reed     hook_notify_fn_t callback, void *arg)
2064*7ddc9b1aSDarren Reed {
2065*7ddc9b1aSDarren Reed 	hook_notify_t *hn;
2066*7ddc9b1aSDarren Reed 
2067*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(lock);
2068*7ddc9b1aSDarren Reed 
2069*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
2070*7ddc9b1aSDarren Reed 		if (hn->hn_func == callback) {
2071*7ddc9b1aSDarren Reed 			CVW_EXIT_WRITE(lock);
2072*7ddc9b1aSDarren Reed 			return (EEXIST);
2073*7ddc9b1aSDarren Reed 		}
2074*7ddc9b1aSDarren Reed 	}
2075*7ddc9b1aSDarren Reed 
2076*7ddc9b1aSDarren Reed 	hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
2077*7ddc9b1aSDarren Reed 	hn->hn_func = callback;
2078*7ddc9b1aSDarren Reed 	hn->hn_arg = arg;
2079*7ddc9b1aSDarren Reed 	TAILQ_INSERT_TAIL(head, hn, hn_entry);
2080*7ddc9b1aSDarren Reed 
2081*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(lock);
2082*7ddc9b1aSDarren Reed 
2083*7ddc9b1aSDarren Reed 	return (0);
2084*7ddc9b1aSDarren Reed }
2085*7ddc9b1aSDarren Reed 
2086*7ddc9b1aSDarren Reed /*
2087*7ddc9b1aSDarren Reed  * Function:	hook_stack_notify_register
2088*7ddc9b1aSDarren Reed  * Returns:	0 = success, else failure
2089*7ddc9b1aSDarren Reed  * Parameters:	stackid(I) - netstack identifier
2090*7ddc9b1aSDarren Reed  *              callback(I) - function to be called
2091*7ddc9b1aSDarren Reed  *
2092*7ddc9b1aSDarren Reed  */
2093*7ddc9b1aSDarren Reed static int
2094*7ddc9b1aSDarren Reed hook_notify_unregister(cvwaitlock_t *lock, hook_notify_head_t *head,
2095*7ddc9b1aSDarren Reed     hook_notify_fn_t callback)
2096*7ddc9b1aSDarren Reed {
2097*7ddc9b1aSDarren Reed 	hook_notify_t *hn;
2098*7ddc9b1aSDarren Reed 
2099*7ddc9b1aSDarren Reed 	CVW_ENTER_WRITE(lock);
2100*7ddc9b1aSDarren Reed 
2101*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
2102*7ddc9b1aSDarren Reed 		if (hn->hn_func == callback)
2103*7ddc9b1aSDarren Reed 			break;
2104*7ddc9b1aSDarren Reed 	}
2105*7ddc9b1aSDarren Reed 	if (hn == NULL) {
2106*7ddc9b1aSDarren Reed 		CVW_EXIT_WRITE(lock);
2107*7ddc9b1aSDarren Reed 		return (ESRCH);
2108*7ddc9b1aSDarren Reed 	}
2109*7ddc9b1aSDarren Reed 
2110*7ddc9b1aSDarren Reed 	TAILQ_REMOVE(head, hn, hn_entry);
2111*7ddc9b1aSDarren Reed 
2112*7ddc9b1aSDarren Reed 	CVW_EXIT_WRITE(lock);
2113*7ddc9b1aSDarren Reed 
2114*7ddc9b1aSDarren Reed 	kmem_free(hn, sizeof (*hn));
2115*7ddc9b1aSDarren Reed 
2116*7ddc9b1aSDarren Reed 	return (0);
2117*7ddc9b1aSDarren Reed }
2118*7ddc9b1aSDarren Reed 
2119*7ddc9b1aSDarren Reed /*
2120*7ddc9b1aSDarren Reed  * Function:	hook_notify_run
2121*7ddc9b1aSDarren Reed  * Returns:	None
2122*7ddc9b1aSDarren Reed  * Parameters:	head(I)   - top of the list of callbacks
2123*7ddc9b1aSDarren Reed  *              family(I) - name of the hook family that owns the event
2124*7ddc9b1aSDarren Reed  *              event(I)  - name of the event being changed
2125*7ddc9b1aSDarren Reed  *              name(I)   - name of the object causing change
2126*7ddc9b1aSDarren Reed  *              cmd(I)    - either HN_UNREGISTER or HN_REGISTER
2127*7ddc9b1aSDarren Reed  *
2128*7ddc9b1aSDarren Reed  * This function walks through the list of registered callbacks and
2129*7ddc9b1aSDarren Reed  * executes each one, passing back the arg supplied when registered
2130*7ddc9b1aSDarren Reed  * and the name of the family (that owns the event), event (the thing
2131*7ddc9b1aSDarren Reed  * to which we're making a change) and finally a name that describes
2132*7ddc9b1aSDarren Reed  * what is being added or removed, as indicated by cmd.
2133*7ddc9b1aSDarren Reed  *
2134*7ddc9b1aSDarren Reed  * This function does not acquire or release any lock as it is required
2135*7ddc9b1aSDarren Reed  * that code calling it do so before hand.  The use of hook_notify_head_t
2136*7ddc9b1aSDarren Reed  * is protected by the use of flagwait_t in the structures that own this
2137*7ddc9b1aSDarren Reed  * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
2138*7ddc9b1aSDarren Reed  */
2139*7ddc9b1aSDarren Reed static void
2140*7ddc9b1aSDarren Reed hook_notify_run(hook_notify_head_t *head, char *family, char *event,
2141*7ddc9b1aSDarren Reed     char *name, hook_notify_cmd_t cmd)
2142*7ddc9b1aSDarren Reed {
2143*7ddc9b1aSDarren Reed 	hook_notify_t *hn;
2144*7ddc9b1aSDarren Reed 
2145*7ddc9b1aSDarren Reed 	TAILQ_FOREACH(hn, head, hn_entry) {
2146*7ddc9b1aSDarren Reed 		(*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
2147*7ddc9b1aSDarren Reed 	}
2148*7ddc9b1aSDarren Reed }
2149