xref: /onnv-gate/usr/src/uts/common/io/hook.c (revision 12263:18746354f914)
12958Sdr146992 /*
22958Sdr146992  * CDDL HEADER START
32958Sdr146992  *
42958Sdr146992  * The contents of this file are subject to the terms of the
52958Sdr146992  * Common Development and Distribution License (the "License").
62958Sdr146992  * You may not use this file except in compliance with the License.
72958Sdr146992  *
82958Sdr146992  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92958Sdr146992  * or http://www.opensolaris.org/os/licensing.
102958Sdr146992  * See the License for the specific language governing permissions
112958Sdr146992  * and limitations under the License.
122958Sdr146992  *
132958Sdr146992  * When distributing Covered Code, include this CDDL HEADER in each
142958Sdr146992  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152958Sdr146992  * If applicable, add the following below this CDDL HEADER, with the
162958Sdr146992  * fields enclosed by brackets "[]" replaced with your own identifying
172958Sdr146992  * information: Portions Copyright [yyyy] [name of copyright owner]
182958Sdr146992  *
192958Sdr146992  * CDDL HEADER END
202958Sdr146992  */
212958Sdr146992 /*
227513SDarren.Reed@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232958Sdr146992  * Use is subject to license terms.
242958Sdr146992  */
252958Sdr146992 #include <sys/param.h>
262958Sdr146992 #include <sys/types.h>
272958Sdr146992 #include <sys/systm.h>
282958Sdr146992 #include <sys/errno.h>
292958Sdr146992 #include <sys/kmem.h>
302958Sdr146992 #include <sys/mutex.h>
312958Sdr146992 #include <sys/condvar.h>
322958Sdr146992 #include <sys/modctl.h>
332958Sdr146992 #include <sys/hook_impl.h>
342958Sdr146992 #include <sys/sdt.h>
357513SDarren.Reed@Sun.COM #include <sys/cmn_err.h>
362958Sdr146992 
372958Sdr146992 /*
382958Sdr146992  * This file provides kernel hook framework.
392958Sdr146992  */
402958Sdr146992 
412958Sdr146992 static struct modldrv modlmisc = {
422958Sdr146992 	&mod_miscops,				/* drv_modops */
432958Sdr146992 	"Hooks Interface v1.0",			/* drv_linkinfo */
442958Sdr146992 };
452958Sdr146992 
462958Sdr146992 static struct modlinkage modlinkage = {
472958Sdr146992 	MODREV_1,				/* ml_rev */
482958Sdr146992 	&modlmisc,				/* ml_linkage */
492958Sdr146992 	NULL
502958Sdr146992 };
512958Sdr146992 
522958Sdr146992 /*
537513SDarren.Reed@Sun.COM  * How it works.
547513SDarren.Reed@Sun.COM  * =============
557513SDarren.Reed@Sun.COM  * Use of the hook framework here is tied up with zones - when a new zone
567513SDarren.Reed@Sun.COM  * is created, we create a new hook_stack_t and are open to business for
577513SDarren.Reed@Sun.COM  * allowing new hook families and their events.
587513SDarren.Reed@Sun.COM  *
597513SDarren.Reed@Sun.COM  * A consumer of these hooks is expected to operate in this fashion:
607513SDarren.Reed@Sun.COM  * 1) call hook_family_add() to create a new family of hooks. It is a
617513SDarren.Reed@Sun.COM  *    current requirement that this call must be made with the value
627513SDarren.Reed@Sun.COM  *    returned from hook_stack_init, by way of infrastructure elsewhere.
637513SDarren.Reed@Sun.COM  * 2) add events to the registered family with calls to hook_event_add.
647513SDarren.Reed@Sun.COM  *
657513SDarren.Reed@Sun.COM  * At this point, the structures in place should be open to others to
667513SDarren.Reed@Sun.COM  * add hooks to the event or add notifiers for when the contents of the
677513SDarren.Reed@Sun.COM  * hook stack changes.
687513SDarren.Reed@Sun.COM  *
697513SDarren.Reed@Sun.COM  * The interesting stuff happens on teardown.
707513SDarren.Reed@Sun.COM  *
717513SDarren.Reed@Sun.COM  * It is a requirement that the provider of hook events work in the reverse
727513SDarren.Reed@Sun.COM  * order to the above, so that the first step is:
737513SDarren.Reed@Sun.COM  * 1) remove events from each hook family created earlier
747513SDarren.Reed@Sun.COM  * 2) remove hook families from the hook stack.
757513SDarren.Reed@Sun.COM  *
767513SDarren.Reed@Sun.COM  * When doing teardown of both events and families, a check is made to see
77*12263SDarren.Reed@Sun.COM  * if either structure is still "busy". If so then a boolean flag (FWF_DESTROY)
78*12263SDarren.Reed@Sun.COM  * is set to say that the structure is condemned. The presence of this flag
79*12263SDarren.Reed@Sun.COM  * being set must be checked for in _add()/_register()/ functions and a
80*12263SDarren.Reed@Sun.COM  * failure returned if it is set. It is ignored by the _find() functions
81*12263SDarren.Reed@Sun.COM  * because they're used by _remove()/_unregister().
82*12263SDarren.Reed@Sun.COM  * While setting the condemned flag when trying to delete a structure would
83*12263SDarren.Reed@Sun.COM  * normally be keyed from the presence of a reference count being greater
84*12263SDarren.Reed@Sun.COM  * than 1, in this implementation there are no reference counts required:
85*12263SDarren.Reed@Sun.COM  * instead the presence of objects on linked lists is taken to mean
86*12263SDarren.Reed@Sun.COM  * something is still "busy."
877513SDarren.Reed@Sun.COM  *
887513SDarren.Reed@Sun.COM  * ONLY the caller that adds the family and the events ever has a direct
897513SDarren.Reed@Sun.COM  * reference to the internal structures and thus ONLY it should be doing
907513SDarren.Reed@Sun.COM  * the removal of either the event or family.  In practise, what this means
917513SDarren.Reed@Sun.COM  * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
927513SDarren.Reed@Sun.COM  * by net_event_register() (these interface to hook_family_add() and
937513SDarren.Reed@Sun.COM  * hook_event_add(), respectively) that are made when we create an instance
947513SDarren.Reed@Sun.COM  * of IP and when the IP instance is shutdown/destroyed, it calls
957513SDarren.Reed@Sun.COM  * net_event_unregister() and net_protocol_unregister(), which in turn call
967513SDarren.Reed@Sun.COM  * hook_event_remove() and hook_family_remove() respectively. Nobody else
977513SDarren.Reed@Sun.COM  * is entitled to call the _unregister() functions.  It is imperative that
987513SDarren.Reed@Sun.COM  * there be only one _remove() call for every _add() call.
997513SDarren.Reed@Sun.COM  *
1007513SDarren.Reed@Sun.COM  * It is possible that code which is interfacing with this hook framework
1017513SDarren.Reed@Sun.COM  * won't do all the cleaning up that it needs to at the right time. While
1027513SDarren.Reed@Sun.COM  * we can't prevent programmers from creating memory leaks, we can synchronise
1037513SDarren.Reed@Sun.COM  * when we clean up data structures to prevent code accessing free'd memory.
1047513SDarren.Reed@Sun.COM  *
1057513SDarren.Reed@Sun.COM  * A simple diagram showing the ownership is as follows:
1067513SDarren.Reed@Sun.COM  *
1077513SDarren.Reed@Sun.COM  *  Owned       +--------------+
1087513SDarren.Reed@Sun.COM  *   by         | hook_stack_t |
1097513SDarren.Reed@Sun.COM  *   the        +--------------+
1107513SDarren.Reed@Sun.COM  *  Instance      |
1117513SDarren.Reed@Sun.COM  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1127513SDarren.Reed@Sun.COM  *                V
1137513SDarren.Reed@Sun.COM  *  Owned       +-------------------+     +-------------------+
1147513SDarren.Reed@Sun.COM  *              | hook_family_int_t |---->| hook_family_int_t |
1157513SDarren.Reed@Sun.COM  *   by         +-------------------+     +-------------------+
1167513SDarren.Reed@Sun.COM  *                | \+---------------+        \+---------------+
1177513SDarren.Reed@Sun.COM  *  network       |  | hook_family_t |         | hook_family_t |
1187513SDarren.Reed@Sun.COM  *                V  +---------------+         +---------------+
1197513SDarren.Reed@Sun.COM  *  protocol   +------------------+     +------------------+
1207513SDarren.Reed@Sun.COM  *             | hook_event_int_t |---->| hook_event_int_t |
1217513SDarren.Reed@Sun.COM  * (ipv4,ipv6) +------------------+     +------------------+
1227513SDarren.Reed@Sun.COM  *                | \+--------------+        \+--------------+
1237513SDarren.Reed@Sun.COM  *                |  | hook_event_t |         | hook_event_t |
1247513SDarren.Reed@Sun.COM  *                |  +--------------+         +--------------+
1257513SDarren.Reed@Sun.COM  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1267513SDarren.Reed@Sun.COM  *                V
1277513SDarren.Reed@Sun.COM  *  Owned      +------------+
1287513SDarren.Reed@Sun.COM  *             | hook_int_t |
1297513SDarren.Reed@Sun.COM  *   by        +------------+
1307513SDarren.Reed@Sun.COM  *                  \+--------+
1317513SDarren.Reed@Sun.COM  * the consumer      | hook_t |
1327513SDarren.Reed@Sun.COM  *                   +--------+
1337513SDarren.Reed@Sun.COM  *
1347513SDarren.Reed@Sun.COM  * The consumers, such as IPFilter, do not have any pointers or hold any
1357513SDarren.Reed@Sun.COM  * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
1367513SDarren.Reed@Sun.COM  * a hook on an event through net_hook_register(), an implicit reference
1377513SDarren.Reed@Sun.COM  * to the hook_event_int_t is returned with a successful call.  Additionally,
1387513SDarren.Reed@Sun.COM  * IPFilter does not see the hook_family_int_t or hook_family_t directly.
1397513SDarren.Reed@Sun.COM  * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
1407513SDarren.Reed@Sun.COM  * contains a pointer to hook_family_int_t.  The structure behind the
1417513SDarren.Reed@Sun.COM  * net_handle_t (struct net_data) *is* reference counted and managed
1427513SDarren.Reed@Sun.COM  * appropriately.
1437513SDarren.Reed@Sun.COM  *
1447513SDarren.Reed@Sun.COM  * A more detailed picture that describes how the family/event structures
1457513SDarren.Reed@Sun.COM  * are linked together can be found in <sys/hook_impl.h>
146*12263SDarren.Reed@Sun.COM  *
147*12263SDarren.Reed@Sun.COM  * Notification callbacks.
148*12263SDarren.Reed@Sun.COM  * =======================
149*12263SDarren.Reed@Sun.COM  * For each of the hook stack, hook family and hook event, it is possible
150*12263SDarren.Reed@Sun.COM  * to request notificatin of change to them. Why?
151*12263SDarren.Reed@Sun.COM  * First, lets equate the hook stack to an IP instance, a hook family to
152*12263SDarren.Reed@Sun.COM  * a network protocol and a hook event to IP packets on the input path.
153*12263SDarren.Reed@Sun.COM  * If a kernel module wants to apply security from the very start of
154*12263SDarren.Reed@Sun.COM  * things, it needs to know as soon as a new instance of networking
155*12263SDarren.Reed@Sun.COM  * is initiated. Whilst for the global zone, it is taken for granted that
156*12263SDarren.Reed@Sun.COM  * this instance will always exist before any interaction takes place,
157*12263SDarren.Reed@Sun.COM  * that is not true for zones running with an exclusive networking instance.
158*12263SDarren.Reed@Sun.COM  * Thus when a local zone is started and a new instance is created to support
159*12263SDarren.Reed@Sun.COM  * that, parties that wish to monitor it and apply a security policy from
160*12263SDarren.Reed@Sun.COM  * the onset need to be informed as early as possible - quite probably
161*12263SDarren.Reed@Sun.COM  * before any networking is started by the zone's boot scripts.
162*12263SDarren.Reed@Sun.COM  * Inside each instance, it is possible to have a number of network protocols
163*12263SDarren.Reed@Sun.COM  * (hook families) in operation. Inside the context of the global zone,
164*12263SDarren.Reed@Sun.COM  * it is possible to have code run before the kernel module providing the
165*12263SDarren.Reed@Sun.COM  * IP networking is loaded. From here, to apply the appropriate security,
166*12263SDarren.Reed@Sun.COM  * it is necessary to become informed of when IP is being configured into
167*12263SDarren.Reed@Sun.COM  * the zone and this is done by registering a notification callback with
168*12263SDarren.Reed@Sun.COM  * the hook stack for changes to it. The next step is to know when packets
169*12263SDarren.Reed@Sun.COM  * can be received through the physical_in, etc, events. This is achieved
170*12263SDarren.Reed@Sun.COM  * by registering a callback with the appropriate network protocol (or in
171*12263SDarren.Reed@Sun.COM  * this file, the correct hook family.) Thus when IP finally attaches a
172*12263SDarren.Reed@Sun.COM  * physical_in event to inet, the module looking to enforce a security
173*12263SDarren.Reed@Sun.COM  * policy can become aware of it being present. Of course there's no
174*12263SDarren.Reed@Sun.COM  * requirement for such a module to be present before all of the above
175*12263SDarren.Reed@Sun.COM  * happens and in such a case, it is reasonable for the same module to
176*12263SDarren.Reed@Sun.COM  * work after everything has been put in place. For this reason, when
177*12263SDarren.Reed@Sun.COM  * a notification callback is added, a series of fake callback events
178*12263SDarren.Reed@Sun.COM  * is generated to simulate the arrival of those entities. There is one
179*12263SDarren.Reed@Sun.COM  * final series of callbacks that can be registered - those to monitor
180*12263SDarren.Reed@Sun.COM  * actual hooks that are added or removed from an event. In practice,
181*12263SDarren.Reed@Sun.COM  * this is useful when there are multiple kernel modules participating
182*12263SDarren.Reed@Sun.COM  * in the processing of packets and there are behaviour dependencies
183*12263SDarren.Reed@Sun.COM  * involved, such that one kernel module might only register its hook
184*12263SDarren.Reed@Sun.COM  * if another is already present and also might want to remove its hook
185*12263SDarren.Reed@Sun.COM  * when the other disappears.
186*12263SDarren.Reed@Sun.COM  *
187*12263SDarren.Reed@Sun.COM  * If you know a kernel module will not be loaded before the infrastructure
188*12263SDarren.Reed@Sun.COM  * used in this file is present then it is not necessary to use this
189*12263SDarren.Reed@Sun.COM  * notification callback mechanism.
1907513SDarren.Reed@Sun.COM  */
1917513SDarren.Reed@Sun.COM 
1927513SDarren.Reed@Sun.COM /*
1937513SDarren.Reed@Sun.COM  * Locking
1947513SDarren.Reed@Sun.COM  * =======
1957513SDarren.Reed@Sun.COM  * The use of CVW_* macros to do locking is driven by the need to allow
1967513SDarren.Reed@Sun.COM  * recursive locking with read locks when we're processing packets. This
1977513SDarren.Reed@Sun.COM  * is necessary because various netinfo functions need to hold read locks,
1987513SDarren.Reed@Sun.COM  * by design, as they can be called in or out of packet context.
1997513SDarren.Reed@Sun.COM  */
2007513SDarren.Reed@Sun.COM /*
2012958Sdr146992  * Hook internal functions
2022958Sdr146992  */
2032958Sdr146992 static hook_int_t *hook_copy(hook_t *src);
2043448Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
2053448Sdh155122     hook_stack_t *hks);
2062958Sdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
2072958Sdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
2087513SDarren.Reed@Sun.COM static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
2092958Sdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
2103448Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
2117513SDarren.Reed@Sun.COM static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
2122958Sdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
2137513SDarren.Reed@Sun.COM static void hook_int_free(hook_int_t *hi, netstackid_t);
2142958Sdr146992 static void hook_init(void);
2153448Sdh155122 static void hook_fini(void);
2163448Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
2173448Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
2187513SDarren.Reed@Sun.COM static void hook_stack_shutdown(netstackid_t stackid, void *arg);
2197513SDarren.Reed@Sun.COM static int hook_insert(hook_int_head_t *head, hook_int_t *new);
2207513SDarren.Reed@Sun.COM static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
2217513SDarren.Reed@Sun.COM static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
2227513SDarren.Reed@Sun.COM static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
2237513SDarren.Reed@Sun.COM static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
2247513SDarren.Reed@Sun.COM static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
2257513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd);
2267513SDarren.Reed@Sun.COM static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
2277513SDarren.Reed@Sun.COM     hook_int_t *hi);
228*12263SDarren.Reed@Sun.COM static int hook_notify_register(hook_notify_head_t *head,
2297513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg);
230*12263SDarren.Reed@Sun.COM static int hook_notify_unregister(hook_notify_head_t *head,
231*12263SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void **);
2327513SDarren.Reed@Sun.COM static void hook_notify_run(hook_notify_head_t *head, char *family,
2337513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd);
2347513SDarren.Reed@Sun.COM static void hook_stack_notify_run(hook_stack_t *hks, char *name,
2357513SDarren.Reed@Sun.COM     hook_notify_cmd_t cmd);
2367513SDarren.Reed@Sun.COM static void hook_stack_remove(hook_stack_t *hks);
2377513SDarren.Reed@Sun.COM 
2387513SDarren.Reed@Sun.COM /*
2397513SDarren.Reed@Sun.COM  * A list of the hook stacks is kept here because we need to enable
2407513SDarren.Reed@Sun.COM  * net_instance_notify_register() to be called during the creation
2417513SDarren.Reed@Sun.COM  * of a new instance. Previously hook_stack_get() would just use
2427513SDarren.Reed@Sun.COM  * the netstack functions for this work but they will return NULL
2437513SDarren.Reed@Sun.COM  * until the zone has been fully initialised.
2447513SDarren.Reed@Sun.COM  */
2457513SDarren.Reed@Sun.COM static hook_stack_head_t hook_stacks;
2467513SDarren.Reed@Sun.COM static kmutex_t hook_stack_lock;
2472958Sdr146992 
2482958Sdr146992 /*
2492958Sdr146992  * Module entry points.
2502958Sdr146992  */
2512958Sdr146992 int
2522958Sdr146992 _init(void)
2532958Sdr146992 {
2543448Sdh155122 	int error;
2553448Sdh155122 
2562958Sdr146992 	hook_init();
2573448Sdh155122 	error = mod_install(&modlinkage);
2583448Sdh155122 	if (error != 0)
2593448Sdh155122 		hook_fini();
2603448Sdh155122 
2613448Sdh155122 	return (error);
2622958Sdr146992 }
2632958Sdr146992 
2642958Sdr146992 int
2652958Sdr146992 _fini(void)
2662958Sdr146992 {
2673448Sdh155122 	int error;
2683448Sdh155122 
2693448Sdh155122 	error = mod_remove(&modlinkage);
2703448Sdh155122 	if (error == 0)
2713448Sdh155122 		hook_fini();
2723448Sdh155122 
2733448Sdh155122 	return (error);
2742958Sdr146992 }
2752958Sdr146992 
2762958Sdr146992 int
2772958Sdr146992 _info(struct modinfo *modinfop)
2782958Sdr146992 {
2792958Sdr146992 	return (mod_info(&modlinkage, modinfop));
2802958Sdr146992 }
2812958Sdr146992 
2822958Sdr146992 /*
2832958Sdr146992  * Function:	hook_init
2842958Sdr146992  * Returns:	None
2852958Sdr146992  * Parameters:	None
2862958Sdr146992  *
2872958Sdr146992  * Initialize hooks
2882958Sdr146992  */
2892958Sdr146992 static void
2902958Sdr146992 hook_init(void)
2912958Sdr146992 {
2927513SDarren.Reed@Sun.COM 	mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
2937513SDarren.Reed@Sun.COM 	SLIST_INIT(&hook_stacks);
2947513SDarren.Reed@Sun.COM 
2953448Sdh155122 	/*
2963448Sdh155122 	 * We want to be informed each time a stack is created or
2973448Sdh155122 	 * destroyed in the kernel.
2983448Sdh155122 	 */
2997513SDarren.Reed@Sun.COM 	netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
3003448Sdh155122 	    hook_stack_fini);
3013448Sdh155122 }
3023448Sdh155122 
3033448Sdh155122 /*
3043448Sdh155122  * Function:	hook_fini
3053448Sdh155122  * Returns:	None
3063448Sdh155122  * Parameters:	None
3073448Sdh155122  *
3083448Sdh155122  * Deinitialize hooks
3093448Sdh155122  */
3103448Sdh155122 static void
3113448Sdh155122 hook_fini(void)
3123448Sdh155122 {
3133448Sdh155122 	netstack_unregister(NS_HOOK);
3147513SDarren.Reed@Sun.COM 
3157513SDarren.Reed@Sun.COM 	mutex_destroy(&hook_stack_lock);
3167513SDarren.Reed@Sun.COM 	ASSERT(SLIST_EMPTY(&hook_stacks));
3177513SDarren.Reed@Sun.COM }
3187513SDarren.Reed@Sun.COM 
3197513SDarren.Reed@Sun.COM /*
3207513SDarren.Reed@Sun.COM  * Function:	hook_wait_setflag
3217513SDarren.Reed@Sun.COM  * Returns:     -1 = setting flag is disallowed, 0 = flag set and did
3227513SDarren.Reed@Sun.COM  *              not have to wait (ie no lock droped), 1 = flag set but
3237513SDarren.Reed@Sun.COM  *              it was necessary to drop locks to set it.
3247513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
3257513SDarren.Reed@Sun.COM  *              busyset(I) - set of flags that we don't want set while
3267513SDarren.Reed@Sun.COM  *                           we are active.
3277513SDarren.Reed@Sun.COM  *              wanted(I)  - flag associated with newflag to indicate
3287513SDarren.Reed@Sun.COM  *                           what we want to do.
3297513SDarren.Reed@Sun.COM  *              newflag(I) - the new ACTIVE flag we want to set that
3307513SDarren.Reed@Sun.COM  *                           indicates what we are doing.
3317513SDarren.Reed@Sun.COM  *
3327513SDarren.Reed@Sun.COM  * The set of functions hook_wait_* implement an API that builds on top of
3337513SDarren.Reed@Sun.COM  * the kcondvar_t to provide controlled execution through a critical region.
3347513SDarren.Reed@Sun.COM  * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
3357513SDarren.Reed@Sun.COM  * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
3367513SDarren.Reed@Sun.COM  * The combination of flags is required as when this function exits to do
3377513SDarren.Reed@Sun.COM  * the task, the structure is then free for another caller to use and
338*12263SDarren.Reed@Sun.COM  * to indicate that it wants to do work.  The flags used when a caller wants
339*12263SDarren.Reed@Sun.COM  * to destroy an object take precedence over those that are used for making
340*12263SDarren.Reed@Sun.COM  * changes to it (add/remove.) In this case, we don't try to secure the
341*12263SDarren.Reed@Sun.COM  * ability to run and return with an error.
342*12263SDarren.Reed@Sun.COM  *
343*12263SDarren.Reed@Sun.COM  * "wantedset" is used here to determine who has the right to clear the
344*12263SDarren.Reed@Sun.COM  * wanted but from the fw_flags set: only he that sets the flag has the
345*12263SDarren.Reed@Sun.COM  * right to clear it at the bottom of the loop, even if someone else
346*12263SDarren.Reed@Sun.COM  * wants to set it.
3477513SDarren.Reed@Sun.COM  *
3487513SDarren.Reed@Sun.COM  * wanted - the FWF_*_WANTED flag that describes the action being requested
3497513SDarren.Reed@Sun.COM  * busyset- the set of FWF_* flags we don't want set when we run
3507513SDarren.Reed@Sun.COM  * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
3517513SDarren.Reed@Sun.COM  */
3527513SDarren.Reed@Sun.COM int
3537513SDarren.Reed@Sun.COM hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
3547513SDarren.Reed@Sun.COM     fwflag_t newflag)
3557513SDarren.Reed@Sun.COM {
356*12263SDarren.Reed@Sun.COM 	boolean_t wantedset;
3577513SDarren.Reed@Sun.COM 	int waited = 0;
3587513SDarren.Reed@Sun.COM 
3597513SDarren.Reed@Sun.COM 	mutex_enter(&waiter->fw_lock);
3607513SDarren.Reed@Sun.COM 	if (waiter->fw_flags & FWF_DESTROY) {
361*12263SDarren.Reed@Sun.COM 		cv_signal(&waiter->fw_cv);
3627513SDarren.Reed@Sun.COM 		mutex_exit(&waiter->fw_lock);
3637513SDarren.Reed@Sun.COM 		return (-1);
3647513SDarren.Reed@Sun.COM 	}
3657513SDarren.Reed@Sun.COM 	while (waiter->fw_flags & busyset) {
366*12263SDarren.Reed@Sun.COM 		wantedset = ((waiter->fw_flags & wanted) == wanted);
367*12263SDarren.Reed@Sun.COM 		if (!wantedset)
368*12263SDarren.Reed@Sun.COM 			waiter->fw_flags |= wanted;
3697513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(waiter->fw_owner);
3707513SDarren.Reed@Sun.COM 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
371*12263SDarren.Reed@Sun.COM 		/*
372*12263SDarren.Reed@Sun.COM 		 * This lock needs to be dropped here to preserve the order
373*12263SDarren.Reed@Sun.COM 		 * of acquisition that is fw_owner followed by fw_lock, else
374*12263SDarren.Reed@Sun.COM 		 * we can deadlock.
375*12263SDarren.Reed@Sun.COM 		 */
376*12263SDarren.Reed@Sun.COM 		mutex_exit(&waiter->fw_lock);
3777513SDarren.Reed@Sun.COM 		waited = 1;
3787513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(waiter->fw_owner);
379*12263SDarren.Reed@Sun.COM 		mutex_enter(&waiter->fw_lock);
380*12263SDarren.Reed@Sun.COM 		if (!wantedset)
381*12263SDarren.Reed@Sun.COM 			waiter->fw_flags &= ~wanted;
3827513SDarren.Reed@Sun.COM 		if (waiter->fw_flags & FWF_DESTROY) {
383*12263SDarren.Reed@Sun.COM 			cv_signal(&waiter->fw_cv);
3847513SDarren.Reed@Sun.COM 			mutex_exit(&waiter->fw_lock);
3857513SDarren.Reed@Sun.COM 			return (-1);
3867513SDarren.Reed@Sun.COM 		}
3877513SDarren.Reed@Sun.COM 	}
3887513SDarren.Reed@Sun.COM 	waiter->fw_flags &= ~wanted;
389*12263SDarren.Reed@Sun.COM 	ASSERT((waiter->fw_flags & wanted) == 0);
390*12263SDarren.Reed@Sun.COM 	ASSERT((waiter->fw_flags & newflag) == 0);
3917513SDarren.Reed@Sun.COM 	waiter->fw_flags |= newflag;
3927513SDarren.Reed@Sun.COM 	mutex_exit(&waiter->fw_lock);
3937513SDarren.Reed@Sun.COM 	return (waited);
3947513SDarren.Reed@Sun.COM }
3957513SDarren.Reed@Sun.COM 
3967513SDarren.Reed@Sun.COM /*
3977513SDarren.Reed@Sun.COM  * Function:	hook_wait_unsetflag
3987513SDarren.Reed@Sun.COM  * Returns:     None
3997513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
4007513SDarren.Reed@Sun.COM  *              oldflag(I) - flag to reset
4017513SDarren.Reed@Sun.COM  *
4027513SDarren.Reed@Sun.COM  * Turn off the bit that we had set to run and let others know that
4037513SDarren.Reed@Sun.COM  * they should now check to see if they can run.
4047513SDarren.Reed@Sun.COM  */
4057513SDarren.Reed@Sun.COM void
406*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(flagwait_t *waiter, fwflag_t oldflag)
4077513SDarren.Reed@Sun.COM {
4087513SDarren.Reed@Sun.COM 	mutex_enter(&waiter->fw_lock);
4097513SDarren.Reed@Sun.COM 	waiter->fw_flags &= ~oldflag;
4107513SDarren.Reed@Sun.COM 	cv_signal(&waiter->fw_cv);
4117513SDarren.Reed@Sun.COM 	mutex_exit(&waiter->fw_lock);
4127513SDarren.Reed@Sun.COM }
4137513SDarren.Reed@Sun.COM 
4147513SDarren.Reed@Sun.COM /*
4157513SDarren.Reed@Sun.COM  * Function:	hook_wait_destroy
4167513SDarren.Reed@Sun.COM  * Returns:     None
4177513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
4187513SDarren.Reed@Sun.COM  *
4197513SDarren.Reed@Sun.COM  * Since outer locking (on fw_owner) should ensure that only one function
4207513SDarren.Reed@Sun.COM  * at a time gets to call hook_wait_destroy() on a given object, there is
4217513SDarren.Reed@Sun.COM  * no need to guard against setting FWF_DESTROY_WANTED already being set.
4227513SDarren.Reed@Sun.COM  * It is, however, necessary to wait for all activity on the owning
4237513SDarren.Reed@Sun.COM  * structure to cease.
4247513SDarren.Reed@Sun.COM  */
425*12263SDarren.Reed@Sun.COM int
4267513SDarren.Reed@Sun.COM hook_wait_destroy(flagwait_t *waiter)
4277513SDarren.Reed@Sun.COM {
428*12263SDarren.Reed@Sun.COM 	boolean_t wanted;
429*12263SDarren.Reed@Sun.COM 
4307513SDarren.Reed@Sun.COM 	ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
431*12263SDarren.Reed@Sun.COM 	mutex_enter(&waiter->fw_lock);
432*12263SDarren.Reed@Sun.COM 	if (waiter->fw_flags & FWF_DESTROY_WANTED) {
433*12263SDarren.Reed@Sun.COM 		cv_signal(&waiter->fw_cv);
434*12263SDarren.Reed@Sun.COM 		mutex_exit(&waiter->fw_lock);
435*12263SDarren.Reed@Sun.COM 		return (EINPROGRESS);
436*12263SDarren.Reed@Sun.COM 	}
4377513SDarren.Reed@Sun.COM 	waiter->fw_flags |= FWF_DESTROY_WANTED;
4387513SDarren.Reed@Sun.COM 	while (!FWF_DESTROY_OK(waiter)) {
4397513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(waiter->fw_owner);
4407513SDarren.Reed@Sun.COM 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
4417513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(waiter->fw_owner);
4427513SDarren.Reed@Sun.COM 	}
4437513SDarren.Reed@Sun.COM 	/*
4447513SDarren.Reed@Sun.COM 	 * There should now be nothing else using "waiter" or its
4457513SDarren.Reed@Sun.COM 	 * owner, so we can safely assign here without risk of wiiping
4467513SDarren.Reed@Sun.COM 	 * out someone's bit.
4477513SDarren.Reed@Sun.COM 	 */
4487513SDarren.Reed@Sun.COM 	waiter->fw_flags = FWF_DESTROY_ACTIVE;
449*12263SDarren.Reed@Sun.COM 	cv_signal(&waiter->fw_cv);
450*12263SDarren.Reed@Sun.COM 	mutex_exit(&waiter->fw_lock);
451*12263SDarren.Reed@Sun.COM 
452*12263SDarren.Reed@Sun.COM 	return (0);
4537513SDarren.Reed@Sun.COM }
4547513SDarren.Reed@Sun.COM 
4557513SDarren.Reed@Sun.COM /*
4567513SDarren.Reed@Sun.COM  * Function:	hook_wait_init
4577513SDarren.Reed@Sun.COM  * Returns:     None
4587513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
4597513SDarren.Reed@Sun.COM  *              ownder(I)  - pointer to lock that the owner of this
4607513SDarren.Reed@Sun.COM  *                           waiter uses
4617513SDarren.Reed@Sun.COM  *
4627513SDarren.Reed@Sun.COM  * "owner" gets passed in here so that when we need to call cv_wait,
4637513SDarren.Reed@Sun.COM  * for example in hook_wait_setflag(), we can drop the lock for the
4647513SDarren.Reed@Sun.COM  * next layer out, which is likely to be held in an exclusive manner.
4657513SDarren.Reed@Sun.COM  */
4667513SDarren.Reed@Sun.COM void
4677513SDarren.Reed@Sun.COM hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
4687513SDarren.Reed@Sun.COM {
4697513SDarren.Reed@Sun.COM 	cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
4707513SDarren.Reed@Sun.COM 	mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
4717513SDarren.Reed@Sun.COM 	waiter->fw_flags = FWF_NONE;
4727513SDarren.Reed@Sun.COM 	waiter->fw_owner = owner;
4732958Sdr146992 }
4742958Sdr146992 
4753448Sdh155122 /*
476*12263SDarren.Reed@Sun.COM  * Function:	hook_stack_init
477*12263SDarren.Reed@Sun.COM  * Returns:     void *     - pointer to new hook stack structure
478*12263SDarren.Reed@Sun.COM  * Parameters:  stackid(I) - identifier for the network instance that owns this
479*12263SDarren.Reed@Sun.COM  *              ns(I)      - pointer to the network instance data structure
480*12263SDarren.Reed@Sun.COM  *
481*12263SDarren.Reed@Sun.COM  * Allocate and initialize the hook stack instance. This function is not
482*12263SDarren.Reed@Sun.COM  * allowed to fail, so KM_SLEEP is used here when allocating memory. The
483*12263SDarren.Reed@Sun.COM  * value returned is passed back into the shutdown and destroy hooks.
4843448Sdh155122  */
4853448Sdh155122 /*ARGSUSED*/
4863448Sdh155122 static void *
4873448Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
4883448Sdh155122 {
4893448Sdh155122 	hook_stack_t	*hks;
4903448Sdh155122 
4913448Sdh155122 #ifdef NS_DEBUG
4923448Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
4933448Sdh155122 #endif
4943448Sdh155122 
4953448Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
4967513SDarren.Reed@Sun.COM 	hks->hks_netstack = ns;
4977513SDarren.Reed@Sun.COM 	hks->hks_netstackid = stackid;
4983448Sdh155122 
4997513SDarren.Reed@Sun.COM 	CVW_INIT(&hks->hks_lock);
5007513SDarren.Reed@Sun.COM 	TAILQ_INIT(&hks->hks_nhead);
5013448Sdh155122 	SLIST_INIT(&hks->hks_familylist);
5023448Sdh155122 
5037513SDarren.Reed@Sun.COM 	hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
5047513SDarren.Reed@Sun.COM 
5057513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
5067513SDarren.Reed@Sun.COM 	SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
5077513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
5087513SDarren.Reed@Sun.COM 
5093448Sdh155122 	return (hks);
5103448Sdh155122 }
5113448Sdh155122 
5127915SDarren.Reed@Sun.COM /*
513*12263SDarren.Reed@Sun.COM  * Function:	hook_stack_shutdown
514*12263SDarren.Reed@Sun.COM  * Returns:     void
515*12263SDarren.Reed@Sun.COM  * Parameters:  stackid(I) - identifier for the network instance that owns this
516*12263SDarren.Reed@Sun.COM  *              arg(I)     - pointer returned by hook_stack_init
517*12263SDarren.Reed@Sun.COM  *
5187915SDarren.Reed@Sun.COM  * Set the shutdown flag to indicate that we should stop accepting new
519*12263SDarren.Reed@Sun.COM  * register calls as we're now in the cleanup process. The cleanup is a
520*12263SDarren.Reed@Sun.COM  * two stage process and we're not required to free any memory here.
521*12263SDarren.Reed@Sun.COM  *
522*12263SDarren.Reed@Sun.COM  * The curious would wonder why isn't there any code that walks through
523*12263SDarren.Reed@Sun.COM  * all of the data structures and sets the flag(s) there? The answer is
524*12263SDarren.Reed@Sun.COM  * that it is expected that this will happen when the zone shutdown calls
525*12263SDarren.Reed@Sun.COM  * the shutdown callbacks for other modules that they will initiate the
526*12263SDarren.Reed@Sun.COM  * free'ing and shutdown of the hooks themselves.
5277915SDarren.Reed@Sun.COM  */
5287513SDarren.Reed@Sun.COM /*ARGSUSED*/
5297513SDarren.Reed@Sun.COM static void
5307513SDarren.Reed@Sun.COM hook_stack_shutdown(netstackid_t stackid, void *arg)
5317513SDarren.Reed@Sun.COM {
5327513SDarren.Reed@Sun.COM 	hook_stack_t *hks = (hook_stack_t *)arg;
5337513SDarren.Reed@Sun.COM 
5347513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
5357513SDarren.Reed@Sun.COM 	/*
5367513SDarren.Reed@Sun.COM 	 * Once this flag gets set to one, no more additions are allowed
5377513SDarren.Reed@Sun.COM 	 * to any of the structures that make up this stack.
5387513SDarren.Reed@Sun.COM 	 */
5397513SDarren.Reed@Sun.COM 	hks->hks_shutdown = 1;
5407513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
5417513SDarren.Reed@Sun.COM }
5427513SDarren.Reed@Sun.COM 
5433448Sdh155122 /*
544*12263SDarren.Reed@Sun.COM  * Function:	hook_stack_destroy
545*12263SDarren.Reed@Sun.COM  * Returns:     void
546*12263SDarren.Reed@Sun.COM  * Parameters:  stackid(I) - identifier for the network instance that owns this
547*12263SDarren.Reed@Sun.COM  *              arg(I)     - pointer returned by hook_stack_init
548*12263SDarren.Reed@Sun.COM  *
5493448Sdh155122  * Free the hook stack instance.
550*12263SDarren.Reed@Sun.COM  *
551*12263SDarren.Reed@Sun.COM  * The rationale for the shutdown being lazy (see the comment above for
552*12263SDarren.Reed@Sun.COM  * hook_stack_shutdown) also applies to the destroy being lazy. Only if
553*12263SDarren.Reed@Sun.COM  * the hook_stack_t data structure is unused will it go away. Else it
554*12263SDarren.Reed@Sun.COM  * is left up to the last user of a data structure to actually free it.
5553448Sdh155122  */
5563448Sdh155122 /*ARGSUSED*/
5573448Sdh155122 static void
5583448Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
5593448Sdh155122 {
5607513SDarren.Reed@Sun.COM 	hook_stack_t *hks = (hook_stack_t *)arg;
5617513SDarren.Reed@Sun.COM 
5627513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
5637513SDarren.Reed@Sun.COM 	hks->hks_shutdown = 2;
5647513SDarren.Reed@Sun.COM 	hook_stack_remove(hks);
5657513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
5667513SDarren.Reed@Sun.COM }
5677513SDarren.Reed@Sun.COM 
5687513SDarren.Reed@Sun.COM /*
569*12263SDarren.Reed@Sun.COM  * Function:	hook_stack_remove
570*12263SDarren.Reed@Sun.COM  * Returns:     void
571*12263SDarren.Reed@Sun.COM  * Parameters:  hks(I) - pointer to an instance of a hook_stack_t
572*12263SDarren.Reed@Sun.COM  *
5737513SDarren.Reed@Sun.COM  * This function assumes that it is called with hook_stack_lock held.
5747513SDarren.Reed@Sun.COM  * It functions differently to hook_family/event_remove in that it does
5757513SDarren.Reed@Sun.COM  * the checks to see if it can be removed. This difference exists
5767513SDarren.Reed@Sun.COM  * because this structure has nothing higher up that depends on it.
5777513SDarren.Reed@Sun.COM  */
5787513SDarren.Reed@Sun.COM static void
5797513SDarren.Reed@Sun.COM hook_stack_remove(hook_stack_t *hks)
5807513SDarren.Reed@Sun.COM {
5817513SDarren.Reed@Sun.COM 
5827513SDarren.Reed@Sun.COM 	ASSERT(mutex_owned(&hook_stack_lock));
5837513SDarren.Reed@Sun.COM 
5847513SDarren.Reed@Sun.COM 	/*
5857513SDarren.Reed@Sun.COM 	 * Is the structure still in use?
5867513SDarren.Reed@Sun.COM 	 */
5877513SDarren.Reed@Sun.COM 	if (!SLIST_EMPTY(&hks->hks_familylist) ||
5887513SDarren.Reed@Sun.COM 	    !TAILQ_EMPTY(&hks->hks_nhead))
5897513SDarren.Reed@Sun.COM 		return;
5907513SDarren.Reed@Sun.COM 
5917513SDarren.Reed@Sun.COM 	SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
5927513SDarren.Reed@Sun.COM 
593*12263SDarren.Reed@Sun.COM 	VERIFY(hook_wait_destroy(&hks->hks_waiter) == 0);
5947513SDarren.Reed@Sun.COM 	CVW_DESTROY(&hks->hks_lock);
5953448Sdh155122 	kmem_free(hks, sizeof (*hks));
5963448Sdh155122 }
5972958Sdr146992 
598*12263SDarren.Reed@Sun.COM /*
599*12263SDarren.Reed@Sun.COM  * Function:	hook_stack_get
600*12263SDarren.Reed@Sun.COM  * Returns:     hook_stack_t * - NULL if not found, else matching instance
601*12263SDarren.Reed@Sun.COM  * Parameters:  stackid(I)     - instance id to search for
602*12263SDarren.Reed@Sun.COM  *
603*12263SDarren.Reed@Sun.COM  * Search the list of currently active hook_stack_t structures for one that
604*12263SDarren.Reed@Sun.COM  * has a matching netstackid_t to the value passed in. The linked list can
605*12263SDarren.Reed@Sun.COM  * only ever have at most one match for this value.
606*12263SDarren.Reed@Sun.COM  */
6077513SDarren.Reed@Sun.COM static hook_stack_t *
6087513SDarren.Reed@Sun.COM hook_stack_get(netstackid_t stackid)
6097513SDarren.Reed@Sun.COM {
6107513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
6117513SDarren.Reed@Sun.COM 
6127513SDarren.Reed@Sun.COM 	SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
6137513SDarren.Reed@Sun.COM 		if (hks->hks_netstackid == stackid)
6147513SDarren.Reed@Sun.COM 			break;
6157513SDarren.Reed@Sun.COM 	}
6167513SDarren.Reed@Sun.COM 
6177513SDarren.Reed@Sun.COM 	return (hks);
6187513SDarren.Reed@Sun.COM }
6197513SDarren.Reed@Sun.COM 
6207513SDarren.Reed@Sun.COM /*
6217513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_register
622*12263SDarren.Reed@Sun.COM  * Returns:	int        - 0 = success, else failure
6237513SDarren.Reed@Sun.COM  * Parameters:	stackid(I) - netstack identifier
6247513SDarren.Reed@Sun.COM  *              callback(I)- function to be called
6257513SDarren.Reed@Sun.COM  *              arg(I)     - arg to provide callback when it is called
6267513SDarren.Reed@Sun.COM  *
6277513SDarren.Reed@Sun.COM  * If we're not shutting down this instance, append a new function to the
6287513SDarren.Reed@Sun.COM  * list of those to call when a new family of hooks is added to this stack.
629*12263SDarren.Reed@Sun.COM  * If the function can be successfully added to the list of callbacks
630*12263SDarren.Reed@Sun.COM  * activated when there is a change to the stack (addition or removal of
631*12263SDarren.Reed@Sun.COM  * a hook family) then generate a fake HN_REGISTER event by directly
632*12263SDarren.Reed@Sun.COM  * calling the callback with the relevant information for each hook
633*12263SDarren.Reed@Sun.COM  * family that currently exists (and isn't being shutdown.)
6347513SDarren.Reed@Sun.COM  */
6357513SDarren.Reed@Sun.COM int
6367513SDarren.Reed@Sun.COM hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
6377513SDarren.Reed@Sun.COM     void *arg)
6387513SDarren.Reed@Sun.COM {
639*12263SDarren.Reed@Sun.COM 	hook_family_int_t *hfi;
6407513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
641*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
642*12263SDarren.Reed@Sun.COM 	char buffer[16];
6437513SDarren.Reed@Sun.COM 	int error;
6447513SDarren.Reed@Sun.COM 
645*12263SDarren.Reed@Sun.COM 	ASSERT(callback != NULL);
646*12263SDarren.Reed@Sun.COM 
647*12263SDarren.Reed@Sun.COM 	canrun = B_FALSE;
6487513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
6497513SDarren.Reed@Sun.COM 	hks = hook_stack_get(stackid);
6507513SDarren.Reed@Sun.COM 	if (hks != NULL) {
6517513SDarren.Reed@Sun.COM 		if (hks->hks_shutdown != 0) {
6527513SDarren.Reed@Sun.COM 			error = ESHUTDOWN;
6537513SDarren.Reed@Sun.COM 		} else {
654*12263SDarren.Reed@Sun.COM 			CVW_ENTER_WRITE(&hks->hks_lock);
655*12263SDarren.Reed@Sun.COM 			canrun = (hook_wait_setflag(&hks->hks_waiter,
656*12263SDarren.Reed@Sun.COM 			    FWF_ADD_WAIT_MASK, FWF_ADD_WANTED,
657*12263SDarren.Reed@Sun.COM 			    FWF_ADD_ACTIVE) != -1);
658*12263SDarren.Reed@Sun.COM 			error = hook_notify_register(&hks->hks_nhead,
659*12263SDarren.Reed@Sun.COM 			    callback, arg);
660*12263SDarren.Reed@Sun.COM 			CVW_EXIT_WRITE(&hks->hks_lock);
6617513SDarren.Reed@Sun.COM 		}
6627513SDarren.Reed@Sun.COM 	} else {
6637513SDarren.Reed@Sun.COM 		error = ESRCH;
6647513SDarren.Reed@Sun.COM 	}
6657513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
6667513SDarren.Reed@Sun.COM 
667*12263SDarren.Reed@Sun.COM 	if (error == 0 && canrun) {
668*12263SDarren.Reed@Sun.COM 		/*
669*12263SDarren.Reed@Sun.COM 		 * Generate fake register event for callback that
670*12263SDarren.Reed@Sun.COM 		 * is being added, letting it know everything that
671*12263SDarren.Reed@Sun.COM 		 * already exists.
672*12263SDarren.Reed@Sun.COM 		 */
673*12263SDarren.Reed@Sun.COM 		(void) snprintf(buffer, sizeof (buffer), "%u",
674*12263SDarren.Reed@Sun.COM 		    hks->hks_netstackid);
675*12263SDarren.Reed@Sun.COM 
676*12263SDarren.Reed@Sun.COM 		SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
677*12263SDarren.Reed@Sun.COM 			if (hfi->hfi_condemned || hfi->hfi_shutdown)
678*12263SDarren.Reed@Sun.COM 				continue;
679*12263SDarren.Reed@Sun.COM 			callback(HN_REGISTER, arg, buffer, NULL,
680*12263SDarren.Reed@Sun.COM 			    hfi->hfi_family.hf_name);
681*12263SDarren.Reed@Sun.COM 		}
682*12263SDarren.Reed@Sun.COM 	}
683*12263SDarren.Reed@Sun.COM 
684*12263SDarren.Reed@Sun.COM 	if (canrun)
685*12263SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
686*12263SDarren.Reed@Sun.COM 
6877513SDarren.Reed@Sun.COM 	return (error);
6887513SDarren.Reed@Sun.COM }
6897513SDarren.Reed@Sun.COM 
6907513SDarren.Reed@Sun.COM /*
6917513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_unregister
692*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
693*12263SDarren.Reed@Sun.COM  * Parameters:	stackid(I)  - netstack identifier
6947513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
6957513SDarren.Reed@Sun.COM  *
6967513SDarren.Reed@Sun.COM  * Attempt to remove a registered function from a hook stack's list of
6977513SDarren.Reed@Sun.COM  * callbacks to activiate when protocols are added/deleted.
698*12263SDarren.Reed@Sun.COM  * As with hook_stack_notify_register, if all things are going well then
699*12263SDarren.Reed@Sun.COM  * a fake unregister event is delivered to the callback being removed
700*12263SDarren.Reed@Sun.COM  * for each hook family that presently exists.
7017513SDarren.Reed@Sun.COM  */
7027513SDarren.Reed@Sun.COM int
7037513SDarren.Reed@Sun.COM hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
7047513SDarren.Reed@Sun.COM {
705*12263SDarren.Reed@Sun.COM 	hook_family_int_t *hfi;
7067513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
707*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
708*12263SDarren.Reed@Sun.COM 	char buffer[16];
709*12263SDarren.Reed@Sun.COM 	void *arg;
7107513SDarren.Reed@Sun.COM 	int error;
7117513SDarren.Reed@Sun.COM 
7127513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
7137513SDarren.Reed@Sun.COM 	hks = hook_stack_get(stackid);
7147513SDarren.Reed@Sun.COM 	if (hks != NULL) {
715*12263SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(&hks->hks_lock);
716*12263SDarren.Reed@Sun.COM 		canrun = (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
717*12263SDarren.Reed@Sun.COM 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
718*12263SDarren.Reed@Sun.COM 
719*12263SDarren.Reed@Sun.COM 		error = hook_notify_unregister(&hks->hks_nhead, callback, &arg);
720*12263SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
7217513SDarren.Reed@Sun.COM 	} else {
7227513SDarren.Reed@Sun.COM 		error = ESRCH;
7237513SDarren.Reed@Sun.COM 	}
7247513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
7257513SDarren.Reed@Sun.COM 
726*12263SDarren.Reed@Sun.COM 	if (error == 0) {
727*12263SDarren.Reed@Sun.COM 		if (canrun) {
728*12263SDarren.Reed@Sun.COM 			/*
729*12263SDarren.Reed@Sun.COM 			 * Generate fake unregister event for callback that
730*12263SDarren.Reed@Sun.COM 			 * is being removed, letting it know everything that
731*12263SDarren.Reed@Sun.COM 			 * currently exists is now "disappearing."
732*12263SDarren.Reed@Sun.COM 			 */
733*12263SDarren.Reed@Sun.COM 			(void) snprintf(buffer, sizeof (buffer), "%u",
734*12263SDarren.Reed@Sun.COM 			    hks->hks_netstackid);
735*12263SDarren.Reed@Sun.COM 
736*12263SDarren.Reed@Sun.COM 			SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
737*12263SDarren.Reed@Sun.COM 				callback(HN_UNREGISTER, arg, buffer, NULL,
738*12263SDarren.Reed@Sun.COM 				    hfi->hfi_family.hf_name);
739*12263SDarren.Reed@Sun.COM 			}
740*12263SDarren.Reed@Sun.COM 
741*12263SDarren.Reed@Sun.COM 			hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
742*12263SDarren.Reed@Sun.COM 		}
743*12263SDarren.Reed@Sun.COM 
744*12263SDarren.Reed@Sun.COM 		mutex_enter(&hook_stack_lock);
745*12263SDarren.Reed@Sun.COM 		hks = hook_stack_get(stackid);
746*12263SDarren.Reed@Sun.COM 		if ((error == 0) && (hks->hks_shutdown == 2))
747*12263SDarren.Reed@Sun.COM 			hook_stack_remove(hks);
748*12263SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
749*12263SDarren.Reed@Sun.COM 	}
750*12263SDarren.Reed@Sun.COM 
7517513SDarren.Reed@Sun.COM 	return (error);
7527513SDarren.Reed@Sun.COM }
7537513SDarren.Reed@Sun.COM 
7547513SDarren.Reed@Sun.COM /*
7557513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_run
7567513SDarren.Reed@Sun.COM  * Returns:	None
7577513SDarren.Reed@Sun.COM  * Parameters:	hks(I)  - hook stack pointer to execute callbacks for
7587513SDarren.Reed@Sun.COM  *              name(I) - name of a hook family
7597513SDarren.Reed@Sun.COM  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
7607513SDarren.Reed@Sun.COM  *
7617513SDarren.Reed@Sun.COM  * Run through the list of callbacks on the hook stack to be called when
7627513SDarren.Reed@Sun.COM  * a new hook family is added
7637513SDarren.Reed@Sun.COM  *
764*12263SDarren.Reed@Sun.COM  * As hook_notify_run() expects 3 names, one for the family that is associated
765*12263SDarren.Reed@Sun.COM  * with the cmd (HN_REGISTER or HN_UNREGISTER), one for the event and one
766*12263SDarren.Reed@Sun.COM  * for the object being introduced and we really only have one name (that
767*12263SDarren.Reed@Sun.COM  * of the new hook family), fake the hook stack's name by converting the
768*12263SDarren.Reed@Sun.COM  * integer to a string and for the event just pass NULL.
7697513SDarren.Reed@Sun.COM  */
7707513SDarren.Reed@Sun.COM static void
7717513SDarren.Reed@Sun.COM hook_stack_notify_run(hook_stack_t *hks, char *name,
7727513SDarren.Reed@Sun.COM     hook_notify_cmd_t cmd)
7737513SDarren.Reed@Sun.COM {
7747513SDarren.Reed@Sun.COM 	char buffer[16];
7757513SDarren.Reed@Sun.COM 
776*12263SDarren.Reed@Sun.COM 	ASSERT(hks != NULL);
777*12263SDarren.Reed@Sun.COM 	ASSERT(name != NULL);
778*12263SDarren.Reed@Sun.COM 
7797513SDarren.Reed@Sun.COM 	(void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
7807513SDarren.Reed@Sun.COM 
7817513SDarren.Reed@Sun.COM 	hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
7827513SDarren.Reed@Sun.COM }
7837513SDarren.Reed@Sun.COM 
7842958Sdr146992 /*
7852958Sdr146992  * Function:	hook_run
786*12263SDarren.Reed@Sun.COM  * Returns:	int      - return value according to callback func
7872958Sdr146992  * Parameters:	token(I) - event pointer
788*12263SDarren.Reed@Sun.COM  *		info(I)  - message
7892958Sdr146992  *
7902958Sdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
7912958Sdr146992  * until either the end of the list is reached or a hook function returns a
7922958Sdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
7932958Sdr146992  * return that value back to our caller.  By design, a hook function can be
7942958Sdr146992  * called more than once, simultaneously.
7952958Sdr146992  */
7962958Sdr146992 int
7977513SDarren.Reed@Sun.COM hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
7982958Sdr146992 {
7997513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
8002958Sdr146992 	hook_int_t *hi;
8012958Sdr146992 	int rval = 0;
8022958Sdr146992 
8032958Sdr146992 	ASSERT(token != NULL);
8042958Sdr146992 
8052958Sdr146992 	hei = (hook_event_int_t *)token;
8062958Sdr146992 	DTRACE_PROBE2(hook__run__start,
8072958Sdr146992 	    hook_event_token_t, token,
8082958Sdr146992 	    hook_data_t, info);
8092958Sdr146992 
8107513SDarren.Reed@Sun.COM 	/*
811*12263SDarren.Reed@Sun.COM 	 * If we consider that this function is only called from within the
812*12263SDarren.Reed@Sun.COM 	 * stack while an instance is currently active,
8137513SDarren.Reed@Sun.COM 	 */
8147513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
8152958Sdr146992 
8162958Sdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
8172958Sdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
8182958Sdr146992 		DTRACE_PROBE3(hook__func__start,
8192958Sdr146992 		    hook_event_token_t, token,
8202958Sdr146992 		    hook_data_t, info,
8212958Sdr146992 		    hook_int_t *, hi);
8227513SDarren.Reed@Sun.COM 		rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
8232958Sdr146992 		DTRACE_PROBE4(hook__func__end,
8242958Sdr146992 		    hook_event_token_t, token,
8252958Sdr146992 		    hook_data_t, info,
8262958Sdr146992 		    hook_int_t *, hi,
8272958Sdr146992 		    int, rval);
8287513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hits.value.ui64++;
8292958Sdr146992 		if (rval != 0)
8302958Sdr146992 			break;
8312958Sdr146992 	}
8322958Sdr146992 
8337513SDarren.Reed@Sun.COM 	hei->hei_kstats.events.value.ui64++;
8347513SDarren.Reed@Sun.COM 
8357513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
8362958Sdr146992 
8372958Sdr146992 	DTRACE_PROBE3(hook__run__end,
8382958Sdr146992 	    hook_event_token_t, token,
8392958Sdr146992 	    hook_data_t, info,
8402958Sdr146992 	    hook_int_t *, hi);
8412958Sdr146992 
8422958Sdr146992 	return (rval);
8432958Sdr146992 }
8442958Sdr146992 
8452958Sdr146992 /*
8462958Sdr146992  * Function:	hook_family_add
8472958Sdr146992  * Returns:	internal family pointer - NULL = Fail
848*12263SDarren.Reed@Sun.COM  * Parameters:	hf(I)    - family pointer
849*12263SDarren.Reed@Sun.COM  *              hks(I)   - pointer to an instance of a hook_stack_t
850*12263SDarren.Reed@Sun.COM  *              store(O) - where returned pointer will be stored
8512958Sdr146992  *
852*12263SDarren.Reed@Sun.COM  * Add new family to the family list. The requirements for the addition to
853*12263SDarren.Reed@Sun.COM  * succeed are that the family name must not already be registered and that
854*12263SDarren.Reed@Sun.COM  * the hook stack is not being shutdown.
855*12263SDarren.Reed@Sun.COM  * If store is non-NULL, it is expected to be a pointer to the same variable
856*12263SDarren.Reed@Sun.COM  * that is awaiting to be assigned the return value of this function.
857*12263SDarren.Reed@Sun.COM  * In its current use, the returned value is assigned to netd_hooks in
858*12263SDarren.Reed@Sun.COM  * net_family_register. The use of "store" allows the return value to be
859*12263SDarren.Reed@Sun.COM  * used before this function returns. How can this happen? Through the
860*12263SDarren.Reed@Sun.COM  * callbacks that can be activated at the bottom of this function, when
861*12263SDarren.Reed@Sun.COM  * hook_stack_notify_run is called.
8622958Sdr146992  */
8632958Sdr146992 hook_family_int_t *
864*12263SDarren.Reed@Sun.COM hook_family_add(hook_family_t *hf, hook_stack_t *hks, void **store)
8652958Sdr146992 {
8662958Sdr146992 	hook_family_int_t *hfi, *new;
8672958Sdr146992 
8682958Sdr146992 	ASSERT(hf != NULL);
8692958Sdr146992 	ASSERT(hf->hf_name != NULL);
8702958Sdr146992 
8712958Sdr146992 	new = hook_family_copy(hf);
8722958Sdr146992 	if (new == NULL)
8732958Sdr146992 		return (NULL);
8742958Sdr146992 
8757513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
8767513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
8777513SDarren.Reed@Sun.COM 
8787513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
8797513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
8807513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
8817513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
8827513SDarren.Reed@Sun.COM 		return (NULL);
8837513SDarren.Reed@Sun.COM 	}
8842958Sdr146992 
8852958Sdr146992 	/* search family list */
8863448Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
8872958Sdr146992 	if (hfi != NULL) {
8887513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
8897513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
8907513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
8912958Sdr146992 		return (NULL);
8922958Sdr146992 	}
8932958Sdr146992 
894*12263SDarren.Reed@Sun.COM 	/*
895*12263SDarren.Reed@Sun.COM 	 * Try and set the FWF_ADD_ACTIVE flag so that we can drop all the
896*12263SDarren.Reed@Sun.COM 	 * lock further down when calling all of the functions registered
897*12263SDarren.Reed@Sun.COM 	 * for notification when a new hook family is added.
898*12263SDarren.Reed@Sun.COM 	 */
899*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
9007513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
9017513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
9027513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
9037513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
9047513SDarren.Reed@Sun.COM 		return (NULL);
9057513SDarren.Reed@Sun.COM 	}
9067513SDarren.Reed@Sun.COM 
9077513SDarren.Reed@Sun.COM 	CVW_INIT(&new->hfi_lock);
9087513SDarren.Reed@Sun.COM 	SLIST_INIT(&new->hfi_head);
9097513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hfi_nhead);
9107513SDarren.Reed@Sun.COM 
9117513SDarren.Reed@Sun.COM 	hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
9127513SDarren.Reed@Sun.COM 
9137513SDarren.Reed@Sun.COM 	new->hfi_stack = hks;
914*12263SDarren.Reed@Sun.COM 	if (store != NULL)
915*12263SDarren.Reed@Sun.COM 		*store = new;
9163448Sdh155122 
9172958Sdr146992 	/* Add to family list head */
9183448Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
9192958Sdr146992 
9207513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
9217513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
9227513SDarren.Reed@Sun.COM 
9237513SDarren.Reed@Sun.COM 	hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
9247513SDarren.Reed@Sun.COM 
9257513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
9267513SDarren.Reed@Sun.COM 
9272958Sdr146992 	return (new);
9282958Sdr146992 }
9292958Sdr146992 
9302958Sdr146992 /*
9312958Sdr146992  * Function:	hook_family_remove
932*12263SDarren.Reed@Sun.COM  * Returns:	int    - 0 = success, else = failure
9332958Sdr146992  * Parameters:	hfi(I) - internal family pointer
9342958Sdr146992  *
9357513SDarren.Reed@Sun.COM  * Remove family from family list. This function has been designed to be
9367513SDarren.Reed@Sun.COM  * called once and once only per hook_family_int_t. Thus when cleaning up
9377513SDarren.Reed@Sun.COM  * this structure as an orphan, callers should only call hook_family_free.
9382958Sdr146992  */
9392958Sdr146992 int
9402958Sdr146992 hook_family_remove(hook_family_int_t *hfi)
9412958Sdr146992 {
9423448Sdh155122 	hook_stack_t *hks;
9437915SDarren.Reed@Sun.COM 	boolean_t notifydone;
9442958Sdr146992 
9452958Sdr146992 	ASSERT(hfi != NULL);
9467513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
9477513SDarren.Reed@Sun.COM 
948*12263SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
949*12263SDarren.Reed@Sun.COM 	notifydone = hfi->hfi_shutdown;
950*12263SDarren.Reed@Sun.COM 	hfi->hfi_shutdown = B_TRUE;
951*12263SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
952*12263SDarren.Reed@Sun.COM 
9537513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
9542958Sdr146992 
955*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
9567513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
9577513SDarren.Reed@Sun.COM 		/*
9587513SDarren.Reed@Sun.COM 		 * If we're trying to destroy the hook_stack_t...
9597513SDarren.Reed@Sun.COM 		 */
9607915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
9617513SDarren.Reed@Sun.COM 		return (ENXIO);
9627513SDarren.Reed@Sun.COM 	}
9632958Sdr146992 
9647513SDarren.Reed@Sun.COM 	/*
9657513SDarren.Reed@Sun.COM 	 * Check if the family is in use by the presence of either events
9667513SDarren.Reed@Sun.COM 	 * or notify callbacks on the hook family.
9677513SDarren.Reed@Sun.COM 	 */
9687513SDarren.Reed@Sun.COM 	if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
9697513SDarren.Reed@Sun.COM 		hfi->hfi_condemned = B_TRUE;
9707513SDarren.Reed@Sun.COM 	} else {
971*12263SDarren.Reed@Sun.COM 		VERIFY(hook_wait_destroy(&hfi->hfi_waiter) == 0);
9727513SDarren.Reed@Sun.COM 		/*
9737513SDarren.Reed@Sun.COM 		 * Although hfi_condemned = B_FALSE is implied from creation,
9747513SDarren.Reed@Sun.COM 		 * putting a comment here inside the else upsets lint.
9757513SDarren.Reed@Sun.COM 		 */
9767513SDarren.Reed@Sun.COM 		hfi->hfi_condemned = B_FALSE;
9772958Sdr146992 	}
9787513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
9797513SDarren.Reed@Sun.COM 
9807915SDarren.Reed@Sun.COM 	if (!notifydone)
9817915SDarren.Reed@Sun.COM 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
9827915SDarren.Reed@Sun.COM 		    HN_UNREGISTER);
9832958Sdr146992 
9847513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
9857513SDarren.Reed@Sun.COM 
9867513SDarren.Reed@Sun.COM 	/*
9877513SDarren.Reed@Sun.COM 	 * If we don't have to wait for anything else to disappear from this
9887513SDarren.Reed@Sun.COM 	 * structure then we can free it up.
9897513SDarren.Reed@Sun.COM 	 */
9907513SDarren.Reed@Sun.COM 	if (!hfi->hfi_condemned)
9917513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hks);
9922958Sdr146992 
9932958Sdr146992 	return (0);
9942958Sdr146992 }
9952958Sdr146992 
9962958Sdr146992 
9972958Sdr146992 /*
9987513SDarren.Reed@Sun.COM  * Function:	hook_family_free
9997513SDarren.Reed@Sun.COM  * Returns:	None
10007513SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
10017513SDarren.Reed@Sun.COM  *
10027513SDarren.Reed@Sun.COM  * Free alloc memory for family
10037513SDarren.Reed@Sun.COM  */
10047513SDarren.Reed@Sun.COM static void
10057513SDarren.Reed@Sun.COM hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
10067513SDarren.Reed@Sun.COM {
10077513SDarren.Reed@Sun.COM 
10087513SDarren.Reed@Sun.COM 	/*
10097513SDarren.Reed@Sun.COM 	 * This lock gives us possession of the hks pointer after the
10107513SDarren.Reed@Sun.COM 	 * SLIST_REMOVE, for which it is not needed, when hks_shutdown
10117513SDarren.Reed@Sun.COM 	 * is checked and hook_stack_remove called.
10127513SDarren.Reed@Sun.COM 	 */
10137513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
10147513SDarren.Reed@Sun.COM 
10157513SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
10167513SDarren.Reed@Sun.COM 
10177513SDarren.Reed@Sun.COM 	if (hks != NULL) {
10187513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(&hks->hks_lock);
10197513SDarren.Reed@Sun.COM 		/* Remove from family list */
10207513SDarren.Reed@Sun.COM 		SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
10217513SDarren.Reed@Sun.COM 		    hfi_entry);
10227513SDarren.Reed@Sun.COM 
10237513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
10247513SDarren.Reed@Sun.COM 	}
10257513SDarren.Reed@Sun.COM 
10267513SDarren.Reed@Sun.COM 	/* Free name space */
10277513SDarren.Reed@Sun.COM 	if (hfi->hfi_family.hf_name != NULL) {
10287513SDarren.Reed@Sun.COM 		kmem_free(hfi->hfi_family.hf_name,
10297513SDarren.Reed@Sun.COM 		    strlen(hfi->hfi_family.hf_name) + 1);
10307513SDarren.Reed@Sun.COM 	}
10317513SDarren.Reed@Sun.COM 
10327513SDarren.Reed@Sun.COM 	/* Free container */
10337513SDarren.Reed@Sun.COM 	kmem_free(hfi, sizeof (*hfi));
10347513SDarren.Reed@Sun.COM 
10357513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown == 2)
10367513SDarren.Reed@Sun.COM 		hook_stack_remove(hks);
10377513SDarren.Reed@Sun.COM 
10387513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
10397513SDarren.Reed@Sun.COM }
10407513SDarren.Reed@Sun.COM 
10417915SDarren.Reed@Sun.COM /*
10427915SDarren.Reed@Sun.COM  * Function:	hook_family_shutdown
1043*12263SDarren.Reed@Sun.COM  * Returns:	int    - 0 = success, else = failure
10447915SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
10457915SDarren.Reed@Sun.COM  *
10467915SDarren.Reed@Sun.COM  * As an alternative to removing a family, we may desire to just generate
10477915SDarren.Reed@Sun.COM  * a series of callbacks to indicate that we will be going away in the
10487915SDarren.Reed@Sun.COM  * future. The hfi_condemned flag isn't set because we aren't trying to
10497915SDarren.Reed@Sun.COM  * remove the structure.
10507915SDarren.Reed@Sun.COM  */
10517915SDarren.Reed@Sun.COM int
10527915SDarren.Reed@Sun.COM hook_family_shutdown(hook_family_int_t *hfi)
10537915SDarren.Reed@Sun.COM {
10547915SDarren.Reed@Sun.COM 	hook_stack_t *hks;
10557915SDarren.Reed@Sun.COM 	boolean_t notifydone;
10567915SDarren.Reed@Sun.COM 
10577915SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
10587915SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
10597915SDarren.Reed@Sun.COM 
1060*12263SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1061*12263SDarren.Reed@Sun.COM 	notifydone = hfi->hfi_shutdown;
1062*12263SDarren.Reed@Sun.COM 	hfi->hfi_shutdown = B_TRUE;
1063*12263SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1064*12263SDarren.Reed@Sun.COM 
10657915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
10667915SDarren.Reed@Sun.COM 
1067*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
10687915SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
10697915SDarren.Reed@Sun.COM 		/*
10707915SDarren.Reed@Sun.COM 		 * If we're trying to destroy the hook_stack_t...
10717915SDarren.Reed@Sun.COM 		 */
10727915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
10737915SDarren.Reed@Sun.COM 		return (ENXIO);
10747915SDarren.Reed@Sun.COM 	}
10757915SDarren.Reed@Sun.COM 
10767915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
10777915SDarren.Reed@Sun.COM 
10787915SDarren.Reed@Sun.COM 	if (!notifydone)
10797915SDarren.Reed@Sun.COM 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
10807915SDarren.Reed@Sun.COM 		    HN_UNREGISTER);
10817915SDarren.Reed@Sun.COM 
10827915SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
10837915SDarren.Reed@Sun.COM 
10847915SDarren.Reed@Sun.COM 	return (0);
10857915SDarren.Reed@Sun.COM }
10867513SDarren.Reed@Sun.COM 
10877513SDarren.Reed@Sun.COM /*
10882958Sdr146992  * Function:	hook_family_copy
10892958Sdr146992  * Returns:	internal family pointer - NULL = Failed
10902958Sdr146992  * Parameters:	src(I) - family pointer
10912958Sdr146992  *
10922958Sdr146992  * Allocate internal family block and duplicate incoming family
10932958Sdr146992  * No locks should be held across this function as it may sleep.
10942958Sdr146992  */
10952958Sdr146992 static hook_family_int_t *
10962958Sdr146992 hook_family_copy(hook_family_t *src)
10972958Sdr146992 {
10982958Sdr146992 	hook_family_int_t *new;
10992958Sdr146992 	hook_family_t *dst;
11002958Sdr146992 
11012958Sdr146992 	ASSERT(src != NULL);
11022958Sdr146992 	ASSERT(src->hf_name != NULL);
11032958Sdr146992 
11042958Sdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
11052958Sdr146992 
11062958Sdr146992 	/* Copy body */
11072958Sdr146992 	dst = &new->hfi_family;
11082958Sdr146992 	*dst = *src;
11092958Sdr146992 
11107513SDarren.Reed@Sun.COM 	SLIST_INIT(&new->hfi_head);
11117513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hfi_nhead);
11127513SDarren.Reed@Sun.COM 
11132958Sdr146992 	/* Copy name */
11142958Sdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
11152958Sdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
11162958Sdr146992 
11172958Sdr146992 	return (new);
11182958Sdr146992 }
11192958Sdr146992 
11202958Sdr146992 /*
11217513SDarren.Reed@Sun.COM  * Function:	hook_family_find
11222958Sdr146992  * Returns:	internal family pointer - NULL = Not match
11232958Sdr146992  * Parameters:	family(I) - family name string
11242958Sdr146992  *
11252958Sdr146992  * Search family list with family name
11267513SDarren.Reed@Sun.COM  * 	A lock on hfi_lock must be held when called.
11272958Sdr146992  */
11282958Sdr146992 static hook_family_int_t *
11293448Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
11302958Sdr146992 {
11312958Sdr146992 	hook_family_int_t *hfi = NULL;
11322958Sdr146992 
11332958Sdr146992 	ASSERT(family != NULL);
11342958Sdr146992 
11353448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
11362958Sdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
11372958Sdr146992 			break;
11382958Sdr146992 	}
11392958Sdr146992 	return (hfi);
11402958Sdr146992 }
11412958Sdr146992 
11427513SDarren.Reed@Sun.COM /*
11437513SDarren.Reed@Sun.COM  * Function:	hook_family_notify_register
1144*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
11457513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
11467513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
11477513SDarren.Reed@Sun.COM  *              arg(I)      - arg to provide callback when it is called
11487513SDarren.Reed@Sun.COM  *
11497513SDarren.Reed@Sun.COM  * So long as this hook stack isn't being shut down, register a new
11507513SDarren.Reed@Sun.COM  * callback to be activated each time a new event is added to this
11517513SDarren.Reed@Sun.COM  * family.
11527513SDarren.Reed@Sun.COM  *
11537513SDarren.Reed@Sun.COM  * To call this function we must have an active handle in use on the family,
11547513SDarren.Reed@Sun.COM  * so if we take this into account, then neither the hook_family_int_t nor
11557513SDarren.Reed@Sun.COM  * the hook_stack_t that owns it can disappear. We have to put some trust
11567513SDarren.Reed@Sun.COM  * in the callers to be properly synchronised...
11577513SDarren.Reed@Sun.COM  *
11587513SDarren.Reed@Sun.COM  * Holding hks_lock is required to provide synchronisation for hks_shutdown.
11597513SDarren.Reed@Sun.COM  */
11607513SDarren.Reed@Sun.COM int
11617513SDarren.Reed@Sun.COM hook_family_notify_register(hook_family_int_t *hfi,
11627513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg)
11637513SDarren.Reed@Sun.COM {
1164*12263SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
11657513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
1166*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
11677513SDarren.Reed@Sun.COM 	int error;
11687513SDarren.Reed@Sun.COM 
1169*12263SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
1170*12263SDarren.Reed@Sun.COM 	canrun = B_FALSE;
11717513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
11727513SDarren.Reed@Sun.COM 
11737513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
11747513SDarren.Reed@Sun.COM 
11757915SDarren.Reed@Sun.COM 	if ((hfi->hfi_stack->hks_shutdown != 0) ||
11767915SDarren.Reed@Sun.COM 	    hfi->hfi_condemned || hfi->hfi_shutdown) {
11777513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
11787513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
11797513SDarren.Reed@Sun.COM 	}
11807513SDarren.Reed@Sun.COM 
1181*12263SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1182*12263SDarren.Reed@Sun.COM 	canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1183*12263SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1184*12263SDarren.Reed@Sun.COM 	error = hook_notify_register(&hfi->hfi_nhead, callback, arg);
1185*12263SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1186*12263SDarren.Reed@Sun.COM 
1187*12263SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
11887513SDarren.Reed@Sun.COM 
1189*12263SDarren.Reed@Sun.COM 	if (error == 0 && canrun) {
1190*12263SDarren.Reed@Sun.COM 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1191*12263SDarren.Reed@Sun.COM 			callback(HN_REGISTER, arg,
1192*12263SDarren.Reed@Sun.COM 			    hfi->hfi_family.hf_name, NULL,
1193*12263SDarren.Reed@Sun.COM 			    hei->hei_event->he_name);
1194*12263SDarren.Reed@Sun.COM 		}
1195*12263SDarren.Reed@Sun.COM 	}
1196*12263SDarren.Reed@Sun.COM 
1197*12263SDarren.Reed@Sun.COM 	if (canrun)
1198*12263SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
11997513SDarren.Reed@Sun.COM 
12007513SDarren.Reed@Sun.COM 	return (error);
12017513SDarren.Reed@Sun.COM }
12022958Sdr146992 
12032958Sdr146992 /*
12047513SDarren.Reed@Sun.COM  * Function:	hook_family_notify_unregister
1205*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
12067513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
12077513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
12082958Sdr146992  *
12097513SDarren.Reed@Sun.COM  * Remove a callback from the list of those executed when a new event is
1210*12263SDarren.Reed@Sun.COM  * added to a hook family. If the family is not in the process of being
1211*12263SDarren.Reed@Sun.COM  * destroyed then simulate an unregister callback for each event that is
1212*12263SDarren.Reed@Sun.COM  * on the family. This pairs up with the hook_family_notify_register
1213*12263SDarren.Reed@Sun.COM  * action that simulates register events.
1214*12263SDarren.Reed@Sun.COM  * The order of what happens here is important and goes like this.
1215*12263SDarren.Reed@Sun.COM  * 1) Remove the callback from the list of functions to be called as part
1216*12263SDarren.Reed@Sun.COM  *    of the notify operation when an event is added or removed from the
1217*12263SDarren.Reed@Sun.COM  *    hook family.
1218*12263SDarren.Reed@Sun.COM  * 2) If the hook_family_int_t structure is on death row (free_family will
1219*12263SDarren.Reed@Sun.COM  *    be set to true) then there's nothing else to do than let it be free'd.
1220*12263SDarren.Reed@Sun.COM  * 3) If the structure isn't about to die, mark it up as being busy using
1221*12263SDarren.Reed@Sun.COM  *    hook_wait_setflag and then drop the lock so the loop can be run.
1222*12263SDarren.Reed@Sun.COM  * 4) if hook_wait_setflag was successful, tell all of the notify callback
1223*12263SDarren.Reed@Sun.COM  *    functions that this family has been unregistered.
1224*12263SDarren.Reed@Sun.COM  * 5) Cleanup
12252958Sdr146992  */
12267513SDarren.Reed@Sun.COM int
12277513SDarren.Reed@Sun.COM hook_family_notify_unregister(hook_family_int_t *hfi,
12287513SDarren.Reed@Sun.COM     hook_notify_fn_t callback)
12292958Sdr146992 {
1230*12263SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
12317513SDarren.Reed@Sun.COM 	boolean_t free_family;
1232*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
12337513SDarren.Reed@Sun.COM 	int error;
1234*12263SDarren.Reed@Sun.COM 	void *arg;
1235*12263SDarren.Reed@Sun.COM 
1236*12263SDarren.Reed@Sun.COM 	canrun = B_FALSE;
12377513SDarren.Reed@Sun.COM 
12387513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
12392958Sdr146992 
1240*12263SDarren.Reed@Sun.COM 	(void) hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
1241*12263SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1242*12263SDarren.Reed@Sun.COM 
1243*12263SDarren.Reed@Sun.COM 	error = hook_notify_unregister(&hfi->hfi_nhead, callback, &arg);
1244*12263SDarren.Reed@Sun.COM 
1245*12263SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
12467513SDarren.Reed@Sun.COM 
12477513SDarren.Reed@Sun.COM 	/*
12487513SDarren.Reed@Sun.COM 	 * If hook_family_remove has been called but the structure was still
12497513SDarren.Reed@Sun.COM 	 * "busy" ... but we might have just made it "unbusy"...
12507513SDarren.Reed@Sun.COM 	 */
12517513SDarren.Reed@Sun.COM 	if ((error == 0) && hfi->hfi_condemned &&
12527513SDarren.Reed@Sun.COM 	    SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
12537513SDarren.Reed@Sun.COM 		free_family = B_TRUE;
12547513SDarren.Reed@Sun.COM 	} else {
12557513SDarren.Reed@Sun.COM 		free_family = B_FALSE;
12562958Sdr146992 	}
12572958Sdr146992 
1258*12263SDarren.Reed@Sun.COM 	if (error == 0 && !free_family) {
1259*12263SDarren.Reed@Sun.COM 		canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1260*12263SDarren.Reed@Sun.COM 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1261*12263SDarren.Reed@Sun.COM 	}
1262*12263SDarren.Reed@Sun.COM 
12637513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
12647513SDarren.Reed@Sun.COM 
1265*12263SDarren.Reed@Sun.COM 	if (canrun) {
1266*12263SDarren.Reed@Sun.COM 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1267*12263SDarren.Reed@Sun.COM 			callback(HN_UNREGISTER, arg,
1268*12263SDarren.Reed@Sun.COM 			    hfi->hfi_family.hf_name, NULL,
1269*12263SDarren.Reed@Sun.COM 			    hei->hei_event->he_name);
1270*12263SDarren.Reed@Sun.COM 		}
1271*12263SDarren.Reed@Sun.COM 
1272*12263SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
1273*12263SDarren.Reed@Sun.COM 	} else if (free_family) {
12747513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hfi->hfi_stack);
1275*12263SDarren.Reed@Sun.COM 	}
12767513SDarren.Reed@Sun.COM 
12777513SDarren.Reed@Sun.COM 	return (error);
12782958Sdr146992 }
12792958Sdr146992 
12802958Sdr146992 /*
12812958Sdr146992  * Function:	hook_event_add
12822958Sdr146992  * Returns:	internal event pointer - NULL = Fail
12832958Sdr146992  * Parameters:	hfi(I) - internal family pointer
1284*12263SDarren.Reed@Sun.COM  *		he(I)  - event pointer
12852958Sdr146992  *
12862958Sdr146992  * Add new event to event list on specific family.
12872958Sdr146992  * This function can fail to return successfully if (1) it cannot allocate
12882958Sdr146992  * enough memory for its own internal data structures, (2) the event has
12892958Sdr146992  * already been registered (for any hook family.)
12902958Sdr146992  */
12912958Sdr146992 hook_event_int_t *
12922958Sdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
12932958Sdr146992 {
12947513SDarren.Reed@Sun.COM 	hook_event_int_t *hei, *new;
12953448Sdh155122 	hook_stack_t *hks;
12962958Sdr146992 
12972958Sdr146992 	ASSERT(hfi != NULL);
12982958Sdr146992 	ASSERT(he != NULL);
12992958Sdr146992 	ASSERT(he->he_name != NULL);
13002958Sdr146992 
13012958Sdr146992 	new = hook_event_copy(he);
13022958Sdr146992 	if (new == NULL)
13032958Sdr146992 		return (NULL);
13042958Sdr146992 
13057513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
13067513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
13077513SDarren.Reed@Sun.COM 
13087513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
13097513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
13107513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
13117513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
13127513SDarren.Reed@Sun.COM 		return (NULL);
13137513SDarren.Reed@Sun.COM 	}
13142958Sdr146992 
13152958Sdr146992 	/* Check whether this event pointer is already registered */
13163448Sdh155122 	hei = hook_event_checkdup(he, hks);
13172958Sdr146992 	if (hei != NULL) {
13187513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
13197513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
13207513SDarren.Reed@Sun.COM 		return (NULL);
13217513SDarren.Reed@Sun.COM 	}
13227513SDarren.Reed@Sun.COM 
13237513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
13247513SDarren.Reed@Sun.COM 
13257915SDarren.Reed@Sun.COM 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
13267513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13277513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
13287513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
13292958Sdr146992 		return (NULL);
13302958Sdr146992 	}
1331*12263SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
13322958Sdr146992 
1333*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
13347513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
13357513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13367513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
13377513SDarren.Reed@Sun.COM 		return (NULL);
13387513SDarren.Reed@Sun.COM 	}
13397513SDarren.Reed@Sun.COM 
13407513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hei_nhead);
13417513SDarren.Reed@Sun.COM 
13427513SDarren.Reed@Sun.COM 	hook_event_init_kstats(hfi, new);
13437513SDarren.Reed@Sun.COM 	hook_wait_init(&new->hei_waiter, &new->hei_lock);
13447513SDarren.Reed@Sun.COM 
13452958Sdr146992 	/* Add to event list head */
13462958Sdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
13472958Sdr146992 
13487513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
13497513SDarren.Reed@Sun.COM 
13507513SDarren.Reed@Sun.COM 	hook_notify_run(&hfi->hfi_nhead,
13517513SDarren.Reed@Sun.COM 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
13527513SDarren.Reed@Sun.COM 
13537513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
13547513SDarren.Reed@Sun.COM 
13552958Sdr146992 	return (new);
13562958Sdr146992 }
13572958Sdr146992 
13587513SDarren.Reed@Sun.COM /*
13597513SDarren.Reed@Sun.COM  * Function:	hook_event_init_kstats
13607513SDarren.Reed@Sun.COM  * Returns:	None
13617513SDarren.Reed@Sun.COM  * Parameters:  hfi(I) - pointer to the family that owns this event.
13627513SDarren.Reed@Sun.COM  *              hei(I) - pointer to the hook event that needs some kstats.
13637513SDarren.Reed@Sun.COM  *
13647513SDarren.Reed@Sun.COM  * Create a set of kstats that relate to each event registered with
13657513SDarren.Reed@Sun.COM  * the hook framework.  A counter is kept for each time the event is
13667513SDarren.Reed@Sun.COM  * activated and for each time a hook is added or removed.  As the
13677513SDarren.Reed@Sun.COM  * kstats just count the events as they happen, the total number of
13687513SDarren.Reed@Sun.COM  * hooks registered must be obtained by subtractived removed from added.
13697513SDarren.Reed@Sun.COM  */
13707513SDarren.Reed@Sun.COM static void
13717513SDarren.Reed@Sun.COM hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
13727513SDarren.Reed@Sun.COM {
13737513SDarren.Reed@Sun.COM 	hook_event_kstat_t template = {
13747513SDarren.Reed@Sun.COM 		{ "hooksAdded",		KSTAT_DATA_UINT64 },
13757513SDarren.Reed@Sun.COM 		{ "hooksRemoved",	KSTAT_DATA_UINT64 },
13767513SDarren.Reed@Sun.COM 		{ "events",		KSTAT_DATA_UINT64 }
13777513SDarren.Reed@Sun.COM 	};
13787513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
13797513SDarren.Reed@Sun.COM 
13807513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
13817513SDarren.Reed@Sun.COM 	hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
13827513SDarren.Reed@Sun.COM 	    hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
13837513SDarren.Reed@Sun.COM 	    sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
13847513SDarren.Reed@Sun.COM 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
13857513SDarren.Reed@Sun.COM 
13867513SDarren.Reed@Sun.COM 	bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
13877513SDarren.Reed@Sun.COM 
13887513SDarren.Reed@Sun.COM 	if (hei->hei_kstatp != NULL) {
13897513SDarren.Reed@Sun.COM 		hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
13907513SDarren.Reed@Sun.COM 		hei->hei_kstatp->ks_private =
13917513SDarren.Reed@Sun.COM 		    (void *)(uintptr_t)hks->hks_netstackid;
13927513SDarren.Reed@Sun.COM 
13937513SDarren.Reed@Sun.COM 		kstat_install(hei->hei_kstatp);
13947513SDarren.Reed@Sun.COM 	}
13957513SDarren.Reed@Sun.COM }
13962958Sdr146992 
13972958Sdr146992 /*
13982958Sdr146992  * Function:	hook_event_remove
1399*12263SDarren.Reed@Sun.COM  * Returns:	int    - 0 = success, else = failure
14002958Sdr146992  * Parameters:	hfi(I) - internal family pointer
1401*12263SDarren.Reed@Sun.COM  *		he(I)  - event pointer
14022958Sdr146992  *
14032958Sdr146992  * Remove event from event list on specific family
14047513SDarren.Reed@Sun.COM  *
14057513SDarren.Reed@Sun.COM  * This function assumes that the caller has received a pointer to a the
14067513SDarren.Reed@Sun.COM  * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
14077513SDarren.Reed@Sun.COM  * This the hook_family_int_t is guaranteed to be around for the life of this
14087513SDarren.Reed@Sun.COM  * call, unless the caller has decided to call net_protocol_release or
14097513SDarren.Reed@Sun.COM  * net_protocol_unregister before calling net_event_unregister - an error.
14102958Sdr146992  */
14112958Sdr146992 int
14122958Sdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
14132958Sdr146992 {
14147513SDarren.Reed@Sun.COM 	boolean_t free_family;
14152958Sdr146992 	hook_event_int_t *hei;
14167915SDarren.Reed@Sun.COM 	boolean_t notifydone;
14172958Sdr146992 
14182958Sdr146992 	ASSERT(hfi != NULL);
14192958Sdr146992 	ASSERT(he != NULL);
14202958Sdr146992 
14217513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
14222958Sdr146992 
14237513SDarren.Reed@Sun.COM 	/*
14247513SDarren.Reed@Sun.COM 	 * Set the flag so that we can call hook_event_notify_run without
14257513SDarren.Reed@Sun.COM 	 * holding any locks but at the same time prevent other changes to
14267513SDarren.Reed@Sun.COM 	 * the event at the same time.
14277513SDarren.Reed@Sun.COM 	 */
1428*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
14297513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
14307513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
14312958Sdr146992 		return (ENXIO);
14322958Sdr146992 	}
14332958Sdr146992 
14347513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, he->he_name);
14357513SDarren.Reed@Sun.COM 	if (hei == NULL) {
14367513SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14377513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
14387513SDarren.Reed@Sun.COM 		return (ESRCH);
14392958Sdr146992 	}
14402958Sdr146992 
14417513SDarren.Reed@Sun.COM 	free_family = B_FALSE;
14427513SDarren.Reed@Sun.COM 
14437513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
14447513SDarren.Reed@Sun.COM 	/*
14457915SDarren.Reed@Sun.COM 	 * The hei_shutdown flag is used to indicate whether or not we have
14467915SDarren.Reed@Sun.COM 	 * done a shutdown and thus already walked through the notify list.
14477915SDarren.Reed@Sun.COM 	 */
14487915SDarren.Reed@Sun.COM 	notifydone = hei->hei_shutdown;
14497915SDarren.Reed@Sun.COM 	hei->hei_shutdown = B_TRUE;
14507915SDarren.Reed@Sun.COM 	/*
14517513SDarren.Reed@Sun.COM 	 * If there are any hooks still registered for this event or
14527513SDarren.Reed@Sun.COM 	 * there are any notifiers registered, return an error indicating
14537513SDarren.Reed@Sun.COM 	 * that the event is still busy.
14547513SDarren.Reed@Sun.COM 	 */
14557513SDarren.Reed@Sun.COM 	if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
14567513SDarren.Reed@Sun.COM 		hei->hei_condemned = B_TRUE;
14577513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
14587513SDarren.Reed@Sun.COM 	} else {
14597513SDarren.Reed@Sun.COM 		/* hei_condemned = B_FALSE is implied from creation */
14607513SDarren.Reed@Sun.COM 		/*
14617513SDarren.Reed@Sun.COM 		 * Even though we know the notify list is empty, we call
14627513SDarren.Reed@Sun.COM 		 * hook_wait_destroy here to synchronise wait removing a
14637513SDarren.Reed@Sun.COM 		 * hook from an event.
14647513SDarren.Reed@Sun.COM 		 */
1465*12263SDarren.Reed@Sun.COM 		VERIFY(hook_wait_destroy(&hei->hei_waiter) == 0);
14662958Sdr146992 
14677513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
14687513SDarren.Reed@Sun.COM 
14697513SDarren.Reed@Sun.COM 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
14707513SDarren.Reed@Sun.COM 		    TAILQ_EMPTY(&hfi->hfi_nhead))
14717513SDarren.Reed@Sun.COM 			free_family = B_TRUE;
14727513SDarren.Reed@Sun.COM 	}
14737513SDarren.Reed@Sun.COM 
14747513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
14757513SDarren.Reed@Sun.COM 
14767915SDarren.Reed@Sun.COM 	if (!notifydone)
14777915SDarren.Reed@Sun.COM 		hook_notify_run(&hfi->hfi_nhead,
14787915SDarren.Reed@Sun.COM 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
14797513SDarren.Reed@Sun.COM 
14807513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14817513SDarren.Reed@Sun.COM 
14827513SDarren.Reed@Sun.COM 	if (!hei->hei_condemned) {
14837513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
14847513SDarren.Reed@Sun.COM 		if (free_family)
14857513SDarren.Reed@Sun.COM 			hook_family_free(hfi, hfi->hfi_stack);
14867513SDarren.Reed@Sun.COM 	}
14872958Sdr146992 
14882958Sdr146992 	return (0);
14892958Sdr146992 }
14902958Sdr146992 
14917513SDarren.Reed@Sun.COM /*
14927915SDarren.Reed@Sun.COM  * Function:	hook_event_shutdown
1493*12263SDarren.Reed@Sun.COM  * Returns:	int    - 0 = success, else = failure
14947915SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
14957915SDarren.Reed@Sun.COM  *		he(I)  - event pointer
14967915SDarren.Reed@Sun.COM  *
14977915SDarren.Reed@Sun.COM  * As with hook_family_shutdown, we want to generate the notify callbacks
14987915SDarren.Reed@Sun.COM  * as if the event was being removed but not actually do the remove.
14997915SDarren.Reed@Sun.COM  */
15007915SDarren.Reed@Sun.COM int
15017915SDarren.Reed@Sun.COM hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he)
15027915SDarren.Reed@Sun.COM {
15037915SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
15047915SDarren.Reed@Sun.COM 	boolean_t notifydone;
15057915SDarren.Reed@Sun.COM 
15067915SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
15077915SDarren.Reed@Sun.COM 	ASSERT(he != NULL);
15087915SDarren.Reed@Sun.COM 
15097915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
15107915SDarren.Reed@Sun.COM 
15117915SDarren.Reed@Sun.COM 	/*
15127915SDarren.Reed@Sun.COM 	 * Set the flag so that we can call hook_event_notify_run without
15137915SDarren.Reed@Sun.COM 	 * holding any locks but at the same time prevent other changes to
15147915SDarren.Reed@Sun.COM 	 * the event at the same time.
15157915SDarren.Reed@Sun.COM 	 */
1516*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
15177915SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
15187915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15197915SDarren.Reed@Sun.COM 		return (ENXIO);
15207915SDarren.Reed@Sun.COM 	}
15217915SDarren.Reed@Sun.COM 
15227915SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, he->he_name);
15237915SDarren.Reed@Sun.COM 	if (hei == NULL) {
15247915SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15257915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15267915SDarren.Reed@Sun.COM 		return (ESRCH);
15277915SDarren.Reed@Sun.COM 	}
15287915SDarren.Reed@Sun.COM 
15297915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
15307915SDarren.Reed@Sun.COM 	notifydone = hei->hei_shutdown;
15317915SDarren.Reed@Sun.COM 	hei->hei_shutdown = B_TRUE;
15327915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
15337915SDarren.Reed@Sun.COM 
15347915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
15357915SDarren.Reed@Sun.COM 
15367915SDarren.Reed@Sun.COM 	if (!notifydone)
15377915SDarren.Reed@Sun.COM 		hook_notify_run(&hfi->hfi_nhead,
15387915SDarren.Reed@Sun.COM 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
15397915SDarren.Reed@Sun.COM 
15407915SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15417915SDarren.Reed@Sun.COM 
15427915SDarren.Reed@Sun.COM 	return (0);
15437915SDarren.Reed@Sun.COM }
15447915SDarren.Reed@Sun.COM 
15457915SDarren.Reed@Sun.COM /*
15467513SDarren.Reed@Sun.COM  * Function:	hook_event_free
15477513SDarren.Reed@Sun.COM  * Returns:	None
15487513SDarren.Reed@Sun.COM  * Parameters:	hei(I) - internal event pointer
15497513SDarren.Reed@Sun.COM  *
15507513SDarren.Reed@Sun.COM  * Free alloc memory for event
15517513SDarren.Reed@Sun.COM  */
15527513SDarren.Reed@Sun.COM static void
15537513SDarren.Reed@Sun.COM hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
15547513SDarren.Reed@Sun.COM {
15557513SDarren.Reed@Sun.COM 	boolean_t free_family;
15567513SDarren.Reed@Sun.COM 
15577513SDarren.Reed@Sun.COM 	ASSERT(hei != NULL);
15587513SDarren.Reed@Sun.COM 
15597513SDarren.Reed@Sun.COM 	if (hfi != NULL) {
15607513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(&hfi->hfi_lock);
15617513SDarren.Reed@Sun.COM 		/*
15627513SDarren.Reed@Sun.COM 		 * Remove the event from the hook family's list.
15637513SDarren.Reed@Sun.COM 		 */
15647513SDarren.Reed@Sun.COM 		SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
15657513SDarren.Reed@Sun.COM 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
15667513SDarren.Reed@Sun.COM 		    TAILQ_EMPTY(&hfi->hfi_nhead)) {
15677513SDarren.Reed@Sun.COM 			free_family = B_TRUE;
15687513SDarren.Reed@Sun.COM 		} else {
15697513SDarren.Reed@Sun.COM 			free_family = B_FALSE;
15707513SDarren.Reed@Sun.COM 		}
15717513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15727513SDarren.Reed@Sun.COM 	}
15737513SDarren.Reed@Sun.COM 
15747513SDarren.Reed@Sun.COM 	if (hei->hei_kstatp != NULL) {
15757513SDarren.Reed@Sun.COM 		ASSERT(hfi != NULL);
15767513SDarren.Reed@Sun.COM 
15777513SDarren.Reed@Sun.COM 		kstat_delete_netstack(hei->hei_kstatp,
15787513SDarren.Reed@Sun.COM 		    hfi->hfi_stack->hks_netstackid);
15797513SDarren.Reed@Sun.COM 		hei->hei_kstatp = NULL;
15807513SDarren.Reed@Sun.COM 	}
15817513SDarren.Reed@Sun.COM 
15827513SDarren.Reed@Sun.COM 	/* Free container */
15837513SDarren.Reed@Sun.COM 	kmem_free(hei, sizeof (*hei));
15847513SDarren.Reed@Sun.COM 
15857513SDarren.Reed@Sun.COM 	if (free_family)
15867513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hfi->hfi_stack);
15877513SDarren.Reed@Sun.COM }
15882958Sdr146992 
15892958Sdr146992 /*
15902958Sdr146992  * Function:    hook_event_checkdup
15912958Sdr146992  * Returns:     internal event pointer - NULL = Not match
15922958Sdr146992  * Parameters:  he(I) - event pointer
15932958Sdr146992  *
15947513SDarren.Reed@Sun.COM  * Search all of the hook families to see if the event being passed in
15957513SDarren.Reed@Sun.COM  * has already been associated with one.
15962958Sdr146992  */
15972958Sdr146992 static hook_event_int_t *
15983448Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
15992958Sdr146992 {
16002958Sdr146992 	hook_family_int_t *hfi;
16012958Sdr146992 	hook_event_int_t *hei;
16022958Sdr146992 
16032958Sdr146992 	ASSERT(he != NULL);
16042958Sdr146992 
16057513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
16063448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
16072958Sdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16087513SDarren.Reed@Sun.COM 			if (hei->hei_event == he) {
16097513SDarren.Reed@Sun.COM 				CVW_EXIT_READ(&hks->hks_lock);
16102958Sdr146992 				return (hei);
16117513SDarren.Reed@Sun.COM 			}
16122958Sdr146992 		}
16132958Sdr146992 	}
16147513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
16152958Sdr146992 
16162958Sdr146992 	return (NULL);
16172958Sdr146992 }
16182958Sdr146992 
16192958Sdr146992 /*
16202958Sdr146992  * Function:	hook_event_copy
16212958Sdr146992  * Returns:	internal event pointer - NULL = Failed
16222958Sdr146992  * Parameters:	src(I) - event pointer
16232958Sdr146992  *
16242958Sdr146992  * Allocate internal event block and duplicate incoming event
16252958Sdr146992  * No locks should be held across this function as it may sleep.
16262958Sdr146992  */
16272958Sdr146992 static hook_event_int_t *
16282958Sdr146992 hook_event_copy(hook_event_t *src)
16292958Sdr146992 {
16302958Sdr146992 	hook_event_int_t *new;
16312958Sdr146992 
16322958Sdr146992 	ASSERT(src != NULL);
16332958Sdr146992 	ASSERT(src->he_name != NULL);
16342958Sdr146992 
16352958Sdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
16362958Sdr146992 
16372958Sdr146992 	/* Copy body */
16382958Sdr146992 	TAILQ_INIT(&new->hei_head);
16392958Sdr146992 	new->hei_event = src;
16402958Sdr146992 
16412958Sdr146992 	return (new);
16422958Sdr146992 }
16432958Sdr146992 
16442958Sdr146992 /*
16452958Sdr146992  * Function:	hook_event_find
16462958Sdr146992  * Returns:	internal event pointer - NULL = Not match
1647*12263SDarren.Reed@Sun.COM  * Parameters:	hfi(I)   - internal family pointer
16482958Sdr146992  *		event(I) - event name string
16492958Sdr146992  *
16502958Sdr146992  * Search event list with event name
16517513SDarren.Reed@Sun.COM  * 	A lock on hfi->hfi_lock must be held when called.
16522958Sdr146992  */
16532958Sdr146992 static hook_event_int_t *
16542958Sdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
16552958Sdr146992 {
16562958Sdr146992 	hook_event_int_t *hei = NULL;
16572958Sdr146992 
16582958Sdr146992 	ASSERT(hfi != NULL);
16592958Sdr146992 	ASSERT(event != NULL);
16602958Sdr146992 
16612958Sdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16627513SDarren.Reed@Sun.COM 		if ((strcmp(hei->hei_event->he_name, event) == 0) &&
16637513SDarren.Reed@Sun.COM 		    ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
16642958Sdr146992 			break;
16652958Sdr146992 	}
16662958Sdr146992 	return (hei);
16672958Sdr146992 }
16682958Sdr146992 
16697513SDarren.Reed@Sun.COM /*
16707513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_register
1671*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
16727513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
16737513SDarren.Reed@Sun.COM  *              event(I)    - name of the event
16747513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
16757513SDarren.Reed@Sun.COM  *              arg(I)      - arg to provide callback when it is called
16767513SDarren.Reed@Sun.COM  *
16777513SDarren.Reed@Sun.COM  * Adds a new callback to the event named by "event" (we must find it)
16787513SDarren.Reed@Sun.COM  * that will be executed each time a new hook is added to the event.
16797513SDarren.Reed@Sun.COM  * Of course, if the stack is being shut down, this call should fail.
16807513SDarren.Reed@Sun.COM  */
16817513SDarren.Reed@Sun.COM int
16827513SDarren.Reed@Sun.COM hook_event_notify_register(hook_family_int_t *hfi, char *event,
16837513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg)
16847513SDarren.Reed@Sun.COM {
16857513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
16867513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
1687*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
1688*12263SDarren.Reed@Sun.COM 	hook_int_t *h;
16897513SDarren.Reed@Sun.COM 	int error;
16907513SDarren.Reed@Sun.COM 
1691*12263SDarren.Reed@Sun.COM 	canrun = B_FALSE;
16927513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
16937513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
16947513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
16957513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
16967513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
16977513SDarren.Reed@Sun.COM 	}
16987513SDarren.Reed@Sun.COM 
16997513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
17007513SDarren.Reed@Sun.COM 
17017915SDarren.Reed@Sun.COM 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
17027513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
17037513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
17047513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
17057513SDarren.Reed@Sun.COM 	}
17067513SDarren.Reed@Sun.COM 
17077513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, event);
17087513SDarren.Reed@Sun.COM 	if (hei == NULL) {
17097513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
17107513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
17117513SDarren.Reed@Sun.COM 		return (ESRCH);
17127513SDarren.Reed@Sun.COM 	}
17137513SDarren.Reed@Sun.COM 
17147915SDarren.Reed@Sun.COM 	if (hei->hei_condemned || hei->hei_shutdown) {
17157513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
17167513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
17177513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
17187513SDarren.Reed@Sun.COM 	}
17197513SDarren.Reed@Sun.COM 
1720*12263SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
1721*12263SDarren.Reed@Sun.COM 	canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1722*12263SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1723*12263SDarren.Reed@Sun.COM 	error = hook_notify_register(&hei->hei_nhead, callback, arg);
1724*12263SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
17257513SDarren.Reed@Sun.COM 
17267513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
17277513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
17287513SDarren.Reed@Sun.COM 
1729*12263SDarren.Reed@Sun.COM 	if (error == 0 && canrun) {
1730*12263SDarren.Reed@Sun.COM 		TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1731*12263SDarren.Reed@Sun.COM 			callback(HN_REGISTER, arg,
1732*12263SDarren.Reed@Sun.COM 			    hfi->hfi_family.hf_name, hei->hei_event->he_name,
1733*12263SDarren.Reed@Sun.COM 			    h->hi_hook.h_name);
1734*12263SDarren.Reed@Sun.COM 		}
1735*12263SDarren.Reed@Sun.COM 	}
1736*12263SDarren.Reed@Sun.COM 
1737*12263SDarren.Reed@Sun.COM 	if (canrun)
1738*12263SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1739*12263SDarren.Reed@Sun.COM 
17407513SDarren.Reed@Sun.COM 	return (error);
17417513SDarren.Reed@Sun.COM }
17422958Sdr146992 
17432958Sdr146992 /*
17447513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_unregister
1745*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
17467513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
17477513SDarren.Reed@Sun.COM  *              event(I)    - name of the event
17487513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
17497513SDarren.Reed@Sun.COM  *
17507513SDarren.Reed@Sun.COM  * Remove the given callback from the named event's list of functions
17517513SDarren.Reed@Sun.COM  * to call when a hook is added or removed.
17527513SDarren.Reed@Sun.COM  */
17537513SDarren.Reed@Sun.COM int
17547513SDarren.Reed@Sun.COM hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
17557513SDarren.Reed@Sun.COM     hook_notify_fn_t callback)
17567513SDarren.Reed@Sun.COM {
17577513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
17587513SDarren.Reed@Sun.COM 	boolean_t free_event;
1759*12263SDarren.Reed@Sun.COM 	boolean_t canrun;
1760*12263SDarren.Reed@Sun.COM 	hook_int_t *h;
1761*12263SDarren.Reed@Sun.COM 	void *arg;
17627513SDarren.Reed@Sun.COM 	int error;
17637513SDarren.Reed@Sun.COM 
1764*12263SDarren.Reed@Sun.COM 	canrun = B_FALSE;
1765*12263SDarren.Reed@Sun.COM 
17667513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
17677513SDarren.Reed@Sun.COM 
17687513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, event);
17697513SDarren.Reed@Sun.COM 	if (hei == NULL) {
17707513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
17717513SDarren.Reed@Sun.COM 		return (ESRCH);
17727513SDarren.Reed@Sun.COM 	}
17737513SDarren.Reed@Sun.COM 
17747513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
17757513SDarren.Reed@Sun.COM 
1776*12263SDarren.Reed@Sun.COM 	(void) hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
1777*12263SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1778*12263SDarren.Reed@Sun.COM 
1779*12263SDarren.Reed@Sun.COM 	error = hook_notify_unregister(&hei->hei_nhead, callback, &arg);
1780*12263SDarren.Reed@Sun.COM 
1781*12263SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
17827513SDarren.Reed@Sun.COM 
17837513SDarren.Reed@Sun.COM 	/*
17847513SDarren.Reed@Sun.COM 	 * hei_condemned has been set if someone tried to remove the
17857513SDarren.Reed@Sun.COM 	 * event but couldn't because there were still things attached to
17867513SDarren.Reed@Sun.COM 	 * it. Now that we've done a successful remove, if it is now empty
17877513SDarren.Reed@Sun.COM 	 * then by all rights we should be free'ing it too.  Note that the
17887513SDarren.Reed@Sun.COM 	 * expectation is that only the caller of hook_event_add will ever
17897513SDarren.Reed@Sun.COM 	 * call hook_event_remove.
17907513SDarren.Reed@Sun.COM 	 */
17917513SDarren.Reed@Sun.COM 	if ((error == 0) && hei->hei_condemned &&
17927513SDarren.Reed@Sun.COM 	    TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
17937513SDarren.Reed@Sun.COM 		free_event = B_TRUE;
17947513SDarren.Reed@Sun.COM 	} else {
17957513SDarren.Reed@Sun.COM 		free_event = B_FALSE;
17967513SDarren.Reed@Sun.COM 	}
17977513SDarren.Reed@Sun.COM 
1798*12263SDarren.Reed@Sun.COM 	if (error == 0 && !free_event) {
1799*12263SDarren.Reed@Sun.COM 		canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1800*12263SDarren.Reed@Sun.COM 		    FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1801*12263SDarren.Reed@Sun.COM 	}
1802*12263SDarren.Reed@Sun.COM 
18037513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
18047513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
18057513SDarren.Reed@Sun.COM 
1806*12263SDarren.Reed@Sun.COM 	if (canrun) {
1807*12263SDarren.Reed@Sun.COM 		TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1808*12263SDarren.Reed@Sun.COM 			callback(HN_UNREGISTER, arg,
1809*12263SDarren.Reed@Sun.COM 			    hfi->hfi_family.hf_name, hei->hei_event->he_name,
1810*12263SDarren.Reed@Sun.COM 			    h->hi_hook.h_name);
1811*12263SDarren.Reed@Sun.COM 		}
1812*12263SDarren.Reed@Sun.COM 
1813*12263SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1814*12263SDarren.Reed@Sun.COM 	}
1815*12263SDarren.Reed@Sun.COM 
18167513SDarren.Reed@Sun.COM 	if (free_event) {
18177513SDarren.Reed@Sun.COM 		/*
18187513SDarren.Reed@Sun.COM 		 * It is safe to pass in hfi here, without a lock, because
18197513SDarren.Reed@Sun.COM 		 * our structure (hei) is still on one of its lists and thus
18207513SDarren.Reed@Sun.COM 		 * it won't be able to disappear yet...
18217513SDarren.Reed@Sun.COM 		 */
18227513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
18237513SDarren.Reed@Sun.COM 	}
18247513SDarren.Reed@Sun.COM 
18257513SDarren.Reed@Sun.COM 	return (error);
18267513SDarren.Reed@Sun.COM }
18277513SDarren.Reed@Sun.COM 
18287513SDarren.Reed@Sun.COM /*
18297513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_run
18302958Sdr146992  * Returns:	None
18317513SDarren.Reed@Sun.COM  * Parameters:	nrun(I) - pointer to the list of callbacks to execute
18327513SDarren.Reed@Sun.COM  *              hfi(I)  - hook stack pointer to execute callbacks for
18337513SDarren.Reed@Sun.COM  *              name(I) - name of a hook family
18347513SDarren.Reed@Sun.COM  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
18352958Sdr146992  *
18367513SDarren.Reed@Sun.COM  * Execute all of the callbacks registered for this event.
18372958Sdr146992  */
18382958Sdr146992 static void
18397513SDarren.Reed@Sun.COM hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
18407513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd)
18412958Sdr146992 {
18422958Sdr146992 
18437513SDarren.Reed@Sun.COM 	hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
18447513SDarren.Reed@Sun.COM 	    event, name, cmd);
18452958Sdr146992 }
18462958Sdr146992 
18472958Sdr146992 /*
18482958Sdr146992  * Function:	hook_register
1849*12263SDarren.Reed@Sun.COM  * Returns:	int      - 0 = success, else = failure
1850*12263SDarren.Reed@Sun.COM  * Parameters:	hfi(I)   - internal family pointer
18512958Sdr146992  *		event(I) - event name string
1852*12263SDarren.Reed@Sun.COM  *		h(I)     - hook pointer
18532958Sdr146992  *
18547513SDarren.Reed@Sun.COM  * Add new hook to hook list on the specified family and event.
18552958Sdr146992  */
18562958Sdr146992 int
18572958Sdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
18582958Sdr146992 {
18592958Sdr146992 	hook_event_int_t *hei;
18602958Sdr146992 	hook_int_t *hi, *new;
18617513SDarren.Reed@Sun.COM 	int error;
18622958Sdr146992 
18632958Sdr146992 	ASSERT(hfi != NULL);
18642958Sdr146992 	ASSERT(event != NULL);
18652958Sdr146992 	ASSERT(h != NULL);
18667513SDarren.Reed@Sun.COM 
18677513SDarren.Reed@Sun.COM 	if (hfi->hfi_stack->hks_shutdown)
18687513SDarren.Reed@Sun.COM 		return (NULL);
18692958Sdr146992 
18702958Sdr146992 	/* Alloc hook_int_t and copy hook */
18712958Sdr146992 	new = hook_copy(h);
18722958Sdr146992 	if (new == NULL)
18732958Sdr146992 		return (ENOMEM);
18742958Sdr146992 
18752958Sdr146992 	/*
18762958Sdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
18772958Sdr146992 	 * to hold global family write lock. Just get read lock here to
18782958Sdr146992 	 * ensure event will not be removed when doing hooks operation
18792958Sdr146992 	 */
18807513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
18812958Sdr146992 
18822958Sdr146992 	hei = hook_event_find(hfi, event);
18832958Sdr146992 	if (hei == NULL) {
18847513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18857513SDarren.Reed@Sun.COM 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
18862958Sdr146992 		return (ENXIO);
18872958Sdr146992 	}
18882958Sdr146992 
18892958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
18902958Sdr146992 
18917915SDarren.Reed@Sun.COM 	/*
18927915SDarren.Reed@Sun.COM 	 * If we've run either the remove() or shutdown(), do not allow any
18937915SDarren.Reed@Sun.COM 	 * more hooks to be added to this event.
18947915SDarren.Reed@Sun.COM 	 */
18957915SDarren.Reed@Sun.COM 	if (hei->hei_shutdown) {
18967915SDarren.Reed@Sun.COM 		error = ESHUTDOWN;
18977915SDarren.Reed@Sun.COM 		goto bad_add;
18987915SDarren.Reed@Sun.COM 	}
18997915SDarren.Reed@Sun.COM 
19007513SDarren.Reed@Sun.COM 	hi = hook_find(hei, h);
19017513SDarren.Reed@Sun.COM 	if (hi != NULL) {
19027513SDarren.Reed@Sun.COM 		error = EEXIST;
19037513SDarren.Reed@Sun.COM 		goto bad_add;
19042958Sdr146992 	}
19052958Sdr146992 
1906*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
19077513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
19087513SDarren.Reed@Sun.COM 		error = ENOENT;
19097513SDarren.Reed@Sun.COM bad_add:
19102958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
19117513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
19127513SDarren.Reed@Sun.COM 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
19137513SDarren.Reed@Sun.COM 		return (error);
19142958Sdr146992 	}
19152958Sdr146992 
19162958Sdr146992 	/* Add to hook list head */
19177513SDarren.Reed@Sun.COM 	error = hook_insert(&hei->hei_head, new);
19187513SDarren.Reed@Sun.COM 	if (error == 0) {
19197513SDarren.Reed@Sun.COM 		hei->hei_event->he_interested = B_TRUE;
19207513SDarren.Reed@Sun.COM 		hei->hei_kstats.hooks_added.value.ui64++;
19217513SDarren.Reed@Sun.COM 
19227513SDarren.Reed@Sun.COM 		hook_init_kstats(hfi, hei, new);
19237513SDarren.Reed@Sun.COM 	}
19242958Sdr146992 
19252958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
19267513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
19277513SDarren.Reed@Sun.COM 
19287513SDarren.Reed@Sun.COM 	/*
19297513SDarren.Reed@Sun.COM 	 * Note that the name string passed through to the notify callbacks
19307513SDarren.Reed@Sun.COM 	 * is from the original hook being registered, not the copy being
19317513SDarren.Reed@Sun.COM 	 * inserted.
19327513SDarren.Reed@Sun.COM 	 */
1933*12263SDarren.Reed@Sun.COM 	if (error == 0)
19347513SDarren.Reed@Sun.COM 		hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
1935*12263SDarren.Reed@Sun.COM 
1936*12263SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
19377513SDarren.Reed@Sun.COM 
19387513SDarren.Reed@Sun.COM 	return (error);
19397513SDarren.Reed@Sun.COM }
19407513SDarren.Reed@Sun.COM 
19417513SDarren.Reed@Sun.COM /*
19427513SDarren.Reed@Sun.COM  * Function:	hook_insert
1943*12263SDarren.Reed@Sun.COM  * Returns:	int     - 0 = success, else = failure
19447513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
19457513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
19467513SDarren.Reed@Sun.COM  *
19477513SDarren.Reed@Sun.COM  * Try to insert the hook onto the list of hooks according to the hints
19487513SDarren.Reed@Sun.COM  * given in the hook to be inserted and those that already exist on the
19497513SDarren.Reed@Sun.COM  * list.  For now, the implementation permits only a single hook to be
19507513SDarren.Reed@Sun.COM  * either first or last and names provided with before or after are only
19517513SDarren.Reed@Sun.COM  * loosely coupled with the action.
19527513SDarren.Reed@Sun.COM  */
19537513SDarren.Reed@Sun.COM static int
19547513SDarren.Reed@Sun.COM hook_insert(hook_int_head_t *head, hook_int_t *new)
19557513SDarren.Reed@Sun.COM {
19567513SDarren.Reed@Sun.COM 	hook_int_t *before;
19577513SDarren.Reed@Sun.COM 	hook_int_t *hi;
19587513SDarren.Reed@Sun.COM 	hook_t *hih;
19597513SDarren.Reed@Sun.COM 	hook_t *h = &new->hi_hook;
19607513SDarren.Reed@Sun.COM 
19617513SDarren.Reed@Sun.COM 	switch (new->hi_hook.h_hint) {
19627513SDarren.Reed@Sun.COM 	case HH_NONE :
19637513SDarren.Reed@Sun.COM 		before = NULL;
19647513SDarren.Reed@Sun.COM 		/*
19657513SDarren.Reed@Sun.COM 		 * If there is no hint present (or not one that can be
19667513SDarren.Reed@Sun.COM 		 * satisfied now) then try to at least respect the wishes
19677513SDarren.Reed@Sun.COM 		 * of those that want to be last.  If there are none wanting
19687513SDarren.Reed@Sun.COM 		 * to be last then add the new hook to the tail of the
19697513SDarren.Reed@Sun.COM 		 * list - this means we keep any wanting to be first
19707513SDarren.Reed@Sun.COM 		 * happy without having to search for HH_FIRST.
19717513SDarren.Reed@Sun.COM 		 */
19727513SDarren.Reed@Sun.COM 		TAILQ_FOREACH(hi, head, hi_entry) {
19737513SDarren.Reed@Sun.COM 			hih = &hi->hi_hook;
19747513SDarren.Reed@Sun.COM 			if ((hih->h_hint == HH_AFTER) &&
19757513SDarren.Reed@Sun.COM 			    (strcmp(h->h_name,
19767513SDarren.Reed@Sun.COM 			    (char *)hih->h_hintvalue) == 0)) {
19777513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
19787513SDarren.Reed@Sun.COM 				return (0);
19797513SDarren.Reed@Sun.COM 			}
19807513SDarren.Reed@Sun.COM 			if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
19817513SDarren.Reed@Sun.COM 			    (strcmp(h->h_name,
19827513SDarren.Reed@Sun.COM 			    (char *)hih->h_hintvalue) == 0)) {
19837513SDarren.Reed@Sun.COM 				before = hi;
19847513SDarren.Reed@Sun.COM 			}
19857513SDarren.Reed@Sun.COM 		}
19867513SDarren.Reed@Sun.COM 		if (before != NULL) {
19877513SDarren.Reed@Sun.COM 			TAILQ_INSERT_AFTER(head, before, new, hi_entry);
19887513SDarren.Reed@Sun.COM 			return (0);
19897513SDarren.Reed@Sun.COM 		}
19907513SDarren.Reed@Sun.COM 		hook_insert_plain(head, new);
19917513SDarren.Reed@Sun.COM 		break;
19927513SDarren.Reed@Sun.COM 
19937513SDarren.Reed@Sun.COM 	case HH_FIRST :
19947513SDarren.Reed@Sun.COM 		hi = TAILQ_FIRST(head);
19957513SDarren.Reed@Sun.COM 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
19967513SDarren.Reed@Sun.COM 			return (EBUSY);
19977513SDarren.Reed@Sun.COM 		TAILQ_INSERT_HEAD(head, new, hi_entry);
19987513SDarren.Reed@Sun.COM 		break;
19997513SDarren.Reed@Sun.COM 
20007513SDarren.Reed@Sun.COM 	case HH_LAST :
20017513SDarren.Reed@Sun.COM 		hi = TAILQ_LAST(head, hook_int_head);
20027513SDarren.Reed@Sun.COM 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
20037513SDarren.Reed@Sun.COM 			return (EBUSY);
20047513SDarren.Reed@Sun.COM 		TAILQ_INSERT_TAIL(head, new, hi_entry);
20057513SDarren.Reed@Sun.COM 		break;
20067513SDarren.Reed@Sun.COM 
20077513SDarren.Reed@Sun.COM 	case HH_BEFORE :
20087513SDarren.Reed@Sun.COM 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20097513SDarren.Reed@Sun.COM 		if (hi == NULL)
20107513SDarren.Reed@Sun.COM 			return (hook_insert_afterbefore(head, new));
20117513SDarren.Reed@Sun.COM 
20127513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_FIRST)
20137513SDarren.Reed@Sun.COM 			return (EBUSY);
20147513SDarren.Reed@Sun.COM 
20157513SDarren.Reed@Sun.COM 		TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20167513SDarren.Reed@Sun.COM 		break;
20177513SDarren.Reed@Sun.COM 
20187513SDarren.Reed@Sun.COM 	case HH_AFTER :
20197513SDarren.Reed@Sun.COM 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20207513SDarren.Reed@Sun.COM 		if (hi == NULL)
20217513SDarren.Reed@Sun.COM 			return (hook_insert_afterbefore(head, new));
20227513SDarren.Reed@Sun.COM 
20237513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_LAST)
20247513SDarren.Reed@Sun.COM 			return (EBUSY);
20257513SDarren.Reed@Sun.COM 
20267513SDarren.Reed@Sun.COM 		TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
20277513SDarren.Reed@Sun.COM 		break;
20287513SDarren.Reed@Sun.COM 
20297513SDarren.Reed@Sun.COM 	default :
20307513SDarren.Reed@Sun.COM 		return (EINVAL);
20317513SDarren.Reed@Sun.COM 	}
20327513SDarren.Reed@Sun.COM 
20332958Sdr146992 	return (0);
20342958Sdr146992 }
20352958Sdr146992 
20367513SDarren.Reed@Sun.COM /*
20377513SDarren.Reed@Sun.COM  * Function:	hook_insert_plain
2038*12263SDarren.Reed@Sun.COM  * Returns:	int     - 0 = success, else = failure
20397513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
20407513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
20417513SDarren.Reed@Sun.COM  *
20427513SDarren.Reed@Sun.COM  * Insert a hook such that it respects the wishes of those that want to
20437513SDarren.Reed@Sun.COM  * be last.  If there are none wanting to be last then add the new hook
20447513SDarren.Reed@Sun.COM  * to the tail of the list - this means we keep any wanting to be first
20457513SDarren.Reed@Sun.COM  * happy without having to search for HH_FIRST.
20467513SDarren.Reed@Sun.COM  */
20477513SDarren.Reed@Sun.COM static void
20487513SDarren.Reed@Sun.COM hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
20497513SDarren.Reed@Sun.COM {
20507513SDarren.Reed@Sun.COM 	hook_int_t *hi;
20517513SDarren.Reed@Sun.COM 
20527513SDarren.Reed@Sun.COM 	hi = TAILQ_FIRST(head);
20537513SDarren.Reed@Sun.COM 	if (hi != NULL) {
20547513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_LAST) {
20557513SDarren.Reed@Sun.COM 			TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20567513SDarren.Reed@Sun.COM 		} else {
20577513SDarren.Reed@Sun.COM 			TAILQ_INSERT_TAIL(head, new, hi_entry);
20587513SDarren.Reed@Sun.COM 		}
20597513SDarren.Reed@Sun.COM 	} else {
20607513SDarren.Reed@Sun.COM 		TAILQ_INSERT_TAIL(head, new, hi_entry);
20617513SDarren.Reed@Sun.COM 	}
20627513SDarren.Reed@Sun.COM }
20637513SDarren.Reed@Sun.COM 
20647513SDarren.Reed@Sun.COM /*
20657513SDarren.Reed@Sun.COM  * Function:	hook_insert_afterbefore
2066*12263SDarren.Reed@Sun.COM  * Returns:	int     - 0 = success, else = failure
20677513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
20687513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
20697513SDarren.Reed@Sun.COM  *
20707513SDarren.Reed@Sun.COM  * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
20717513SDarren.Reed@Sun.COM  * possible, so now we need to be more careful.  The first pass is to go
20727513SDarren.Reed@Sun.COM  * through the list and look for any other hooks that also specify the
20737513SDarren.Reed@Sun.COM  * same hint name as the new one.  The object of this exercise is to make
20747513SDarren.Reed@Sun.COM  * sure that hooks with HH_BEFORE always appear on the list before those
20757513SDarren.Reed@Sun.COM  * with HH_AFTER so that when said hook arrives, it can be placed in the
20767513SDarren.Reed@Sun.COM  * middle of the BEFOREs and AFTERs.  If this condition does not arise,
20777513SDarren.Reed@Sun.COM  * just use hook_insert_plain() to try and insert the hook somewhere that
20787513SDarren.Reed@Sun.COM  * is innocuous to existing efforts.
20797513SDarren.Reed@Sun.COM  */
20807513SDarren.Reed@Sun.COM static int
20817513SDarren.Reed@Sun.COM hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
20827513SDarren.Reed@Sun.COM {
20837513SDarren.Reed@Sun.COM 	hook_int_t *hi;
20847513SDarren.Reed@Sun.COM 	hook_t *nh;
20857513SDarren.Reed@Sun.COM 	hook_t *h;
20867513SDarren.Reed@Sun.COM 
20877513SDarren.Reed@Sun.COM 	nh = &new->hi_hook;
20887513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_NONE);
20897513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_LAST);
20907513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_FIRST);
20917513SDarren.Reed@Sun.COM 
20927513SDarren.Reed@Sun.COM 	/*
20937513SDarren.Reed@Sun.COM 	 * First, look through the list to see if there are any other
20947513SDarren.Reed@Sun.COM 	 * before's or after's that have a matching hint name.
20957513SDarren.Reed@Sun.COM 	 */
20967513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hi, head, hi_entry) {
20977513SDarren.Reed@Sun.COM 		h = &hi->hi_hook;
20987513SDarren.Reed@Sun.COM 		switch (h->h_hint) {
20997513SDarren.Reed@Sun.COM 		case HH_FIRST :
21007513SDarren.Reed@Sun.COM 		case HH_LAST :
21017513SDarren.Reed@Sun.COM 		case HH_NONE :
21027513SDarren.Reed@Sun.COM 			break;
21037513SDarren.Reed@Sun.COM 		case HH_BEFORE :
21047513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_BEFORE) &&
21057513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
21067513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
21077513SDarren.Reed@Sun.COM 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21087513SDarren.Reed@Sun.COM 				return (0);
21097513SDarren.Reed@Sun.COM 			}
21107513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_AFTER) &&
21117513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
21127513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
21137513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21147513SDarren.Reed@Sun.COM 				return (0);
21157513SDarren.Reed@Sun.COM 			}
21167513SDarren.Reed@Sun.COM 			break;
21177513SDarren.Reed@Sun.COM 		case HH_AFTER :
21187513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_AFTER) &&
21197513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
21207513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
21217513SDarren.Reed@Sun.COM 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21227513SDarren.Reed@Sun.COM 				return (0);
21237513SDarren.Reed@Sun.COM 			}
21247513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_BEFORE) &&
21257513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
21267513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
21277513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21287513SDarren.Reed@Sun.COM 				return (0);
21297513SDarren.Reed@Sun.COM 			}
21307513SDarren.Reed@Sun.COM 			break;
21317513SDarren.Reed@Sun.COM 		}
21327513SDarren.Reed@Sun.COM 	}
21337513SDarren.Reed@Sun.COM 
21347513SDarren.Reed@Sun.COM 	hook_insert_plain(head, new);
21357513SDarren.Reed@Sun.COM 
21367513SDarren.Reed@Sun.COM 	return (0);
21377513SDarren.Reed@Sun.COM }
21382958Sdr146992 
21392958Sdr146992 /*
21402958Sdr146992  * Function:	hook_unregister
2141*12263SDarren.Reed@Sun.COM  * Returns:	int      - 0 = success, else = failure
2142*12263SDarren.Reed@Sun.COM  * Parameters:	hfi(I)   - internal family pointer
21432958Sdr146992  *		event(I) - event name string
2144*12263SDarren.Reed@Sun.COM  *		h(I)     - hook pointer
21452958Sdr146992  *
21462958Sdr146992  * Remove hook from hook list on specific family, event
21472958Sdr146992  */
21482958Sdr146992 int
21492958Sdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
21502958Sdr146992 {
21512958Sdr146992 	hook_event_int_t *hei;
21522958Sdr146992 	hook_int_t *hi;
21537513SDarren.Reed@Sun.COM 	boolean_t free_event;
21542958Sdr146992 
21552958Sdr146992 	ASSERT(hfi != NULL);
21562958Sdr146992 	ASSERT(h != NULL);
21572958Sdr146992 
21587513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
21592958Sdr146992 
21602958Sdr146992 	hei = hook_event_find(hfi, event);
21612958Sdr146992 	if (hei == NULL) {
21627513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
21632958Sdr146992 		return (ENXIO);
21642958Sdr146992 	}
21652958Sdr146992 
21662958Sdr146992 	/* Hold write lock for event */
21672958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
21682958Sdr146992 
21692958Sdr146992 	hi = hook_find(hei, h);
21702958Sdr146992 	if (hi == NULL) {
21712958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
21727513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
21732958Sdr146992 		return (ENXIO);
21742958Sdr146992 	}
21752958Sdr146992 
2176*12263SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
21777513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
21787513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
21797513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
21807513SDarren.Reed@Sun.COM 		return (ENOENT);
21817513SDarren.Reed@Sun.COM 	}
21827513SDarren.Reed@Sun.COM 
21832958Sdr146992 	/* Remove from hook list */
21842958Sdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
21857513SDarren.Reed@Sun.COM 
21867513SDarren.Reed@Sun.COM 	free_event = B_FALSE;
21872958Sdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
21882958Sdr146992 		hei->hei_event->he_interested = B_FALSE;
21897513SDarren.Reed@Sun.COM 		/*
21907513SDarren.Reed@Sun.COM 		 * If the delete pending flag has been set and there are
21917513SDarren.Reed@Sun.COM 		 * no notifiers on the event (and we've removed the last
21927513SDarren.Reed@Sun.COM 		 * hook) then we need to free this event after we're done.
21937513SDarren.Reed@Sun.COM 		 */
21947513SDarren.Reed@Sun.COM 		if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
21957513SDarren.Reed@Sun.COM 			free_event = B_TRUE;
21962958Sdr146992 	}
21977513SDarren.Reed@Sun.COM 	hei->hei_kstats.hooks_removed.value.ui64++;
21982958Sdr146992 
21992958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
22007513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
22017513SDarren.Reed@Sun.COM 	/*
22027513SDarren.Reed@Sun.COM 	 * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
22037513SDarren.Reed@Sun.COM 	 * will not be free'd and thus the hook_family_int_t wil not
22047513SDarren.Reed@Sun.COM 	 * be free'd either.
22057513SDarren.Reed@Sun.COM 	 */
22067513SDarren.Reed@Sun.COM 	hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
22077513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
22082958Sdr146992 
22097513SDarren.Reed@Sun.COM 	hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
22107513SDarren.Reed@Sun.COM 
22117513SDarren.Reed@Sun.COM 	if (free_event)
22127513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
22137513SDarren.Reed@Sun.COM 
22142958Sdr146992 	return (0);
22152958Sdr146992 }
22162958Sdr146992 
22177513SDarren.Reed@Sun.COM /*
22187513SDarren.Reed@Sun.COM  * Function:	hook_find_byname
22197513SDarren.Reed@Sun.COM  * Returns:	internal hook pointer - NULL = Not match
22207513SDarren.Reed@Sun.COM  * Parameters:	hei(I) - internal event pointer
22217513SDarren.Reed@Sun.COM  *		name(I)- hook name
22227513SDarren.Reed@Sun.COM  *
22237513SDarren.Reed@Sun.COM  * Search an event's list of hooks to see if there is a hook present that
22247513SDarren.Reed@Sun.COM  * has a matching name to the one being looked for.
22257513SDarren.Reed@Sun.COM  */
22267513SDarren.Reed@Sun.COM static hook_int_t *
22277513SDarren.Reed@Sun.COM hook_find_byname(hook_int_head_t *head, char *name)
22287513SDarren.Reed@Sun.COM {
22297513SDarren.Reed@Sun.COM 	hook_int_t *hi;
22307513SDarren.Reed@Sun.COM 
22317513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hi, head, hi_entry) {
22327513SDarren.Reed@Sun.COM 		if (strcmp(hi->hi_hook.h_name, name) == 0)
22337513SDarren.Reed@Sun.COM 			return (hi);
22347513SDarren.Reed@Sun.COM 	}
22357513SDarren.Reed@Sun.COM 
22367513SDarren.Reed@Sun.COM 	return (NULL);
22377513SDarren.Reed@Sun.COM }
22382958Sdr146992 
22392958Sdr146992 /*
22402958Sdr146992  * Function:	hook_find
22412958Sdr146992  * Returns:	internal hook pointer - NULL = Not match
22422958Sdr146992  * Parameters:	hei(I) - internal event pointer
2243*12263SDarren.Reed@Sun.COM  *		h(I)   - hook pointer
22442958Sdr146992  *
22457513SDarren.Reed@Sun.COM  * Search an event's list of hooks to see if there is already one that
22467513SDarren.Reed@Sun.COM  * matches the hook being passed in.  Currently the only criteria for a
22477513SDarren.Reed@Sun.COM  * successful search here is for the names to be the same.
22482958Sdr146992  */
22492958Sdr146992 static hook_int_t *
22502958Sdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
22512958Sdr146992 {
22522958Sdr146992 
22532958Sdr146992 	ASSERT(hei != NULL);
22542958Sdr146992 	ASSERT(h != NULL);
22552958Sdr146992 
22567513SDarren.Reed@Sun.COM 	return (hook_find_byname(&hei->hei_head, h->h_name));
22572958Sdr146992 }
22582958Sdr146992 
22592958Sdr146992 /*
22602958Sdr146992  * Function:	hook_copy
22612958Sdr146992  * Returns:	internal hook pointer - NULL = Failed
22622958Sdr146992  * Parameters:	src(I) - hook pointer
22632958Sdr146992  *
22642958Sdr146992  * Allocate internal hook block and duplicate incoming hook.
22652958Sdr146992  * No locks should be held across this function as it may sleep.
22667513SDarren.Reed@Sun.COM  * Because hook_copy() is responsible for the creation of the internal
22677513SDarren.Reed@Sun.COM  * hook structure that is used here, it takes on population the structure
22687513SDarren.Reed@Sun.COM  * with the kstat information.  Note that while the kstat bits are
22697513SDarren.Reed@Sun.COM  * seeded here, their installation of the kstats is handled elsewhere.
22702958Sdr146992  */
22712958Sdr146992 static hook_int_t *
22722958Sdr146992 hook_copy(hook_t *src)
22732958Sdr146992 {
22742958Sdr146992 	hook_int_t *new;
22752958Sdr146992 	hook_t *dst;
22767513SDarren.Reed@Sun.COM 	int len;
22772958Sdr146992 
22782958Sdr146992 	ASSERT(src != NULL);
22792958Sdr146992 	ASSERT(src->h_name != NULL);
22802958Sdr146992 
22812958Sdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
22822958Sdr146992 
22832958Sdr146992 	/* Copy body */
22842958Sdr146992 	dst = &new->hi_hook;
22852958Sdr146992 	*dst = *src;
22862958Sdr146992 
22872958Sdr146992 	/* Copy name */
22887513SDarren.Reed@Sun.COM 	len = strlen(src->h_name);
22897513SDarren.Reed@Sun.COM 	dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
22902958Sdr146992 	(void) strcpy(dst->h_name, src->h_name);
22912958Sdr146992 
22927513SDarren.Reed@Sun.COM 	/*
22937513SDarren.Reed@Sun.COM 	 * This is initialised in this manner to make it safer to use the
22947513SDarren.Reed@Sun.COM 	 * same pointer in the kstats field.
22957513SDarren.Reed@Sun.COM 	 */
22967513SDarren.Reed@Sun.COM 	dst->h_hintvalue = (uintptr_t)"";
22977513SDarren.Reed@Sun.COM 
22987513SDarren.Reed@Sun.COM 	if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
22997513SDarren.Reed@Sun.COM 		len = strlen((char *)src->h_hintvalue);
23007513SDarren.Reed@Sun.COM 		if (len > 0) {
23017513SDarren.Reed@Sun.COM 			dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
23027513SDarren.Reed@Sun.COM 			    KM_SLEEP);
23037513SDarren.Reed@Sun.COM 			(void) strcpy((char *)dst->h_hintvalue,
23047513SDarren.Reed@Sun.COM 			    (char *)src->h_hintvalue);
23057513SDarren.Reed@Sun.COM 		}
23067513SDarren.Reed@Sun.COM 	}
23077513SDarren.Reed@Sun.COM 
23082958Sdr146992 	return (new);
23092958Sdr146992 }
23102958Sdr146992 
23112958Sdr146992 /*
23127513SDarren.Reed@Sun.COM  * Function:	hook_init_kstats
23137513SDarren.Reed@Sun.COM  * Returns:	None
23147513SDarren.Reed@Sun.COM  * Parameters:  hfi(I) - pointer to the family that owns the event.
23157513SDarren.Reed@Sun.COM  *              hei(I) - pointer to the event that owns this hook
23167513SDarren.Reed@Sun.COM  *              hi(I)  - pointer to the hook for which we create kstats for
23177513SDarren.Reed@Sun.COM  *
23187513SDarren.Reed@Sun.COM  * Each hook that is registered with this framework has its own kstats
23197513SDarren.Reed@Sun.COM  * set up so that we can provide an easy way in which to observe the
23207513SDarren.Reed@Sun.COM  * look of hooks (using the kstat command.) The position is set to 0
23217513SDarren.Reed@Sun.COM  * here but is recalculated after we know the insertion has been a
23227513SDarren.Reed@Sun.COM  * success.
23237513SDarren.Reed@Sun.COM  */
23247513SDarren.Reed@Sun.COM static void
23257513SDarren.Reed@Sun.COM hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
23267513SDarren.Reed@Sun.COM {
23277513SDarren.Reed@Sun.COM 	hook_hook_kstat_t template = {
23287513SDarren.Reed@Sun.COM 		{ "version",			KSTAT_DATA_INT32 },
23297513SDarren.Reed@Sun.COM 		{ "flags",			KSTAT_DATA_UINT32 },
23307513SDarren.Reed@Sun.COM 		{ "hint",			KSTAT_DATA_INT32 },
23317513SDarren.Reed@Sun.COM 		{ "hint_value",			KSTAT_DATA_UINT64 },
23327513SDarren.Reed@Sun.COM 		{ "position",			KSTAT_DATA_INT32 },
23337513SDarren.Reed@Sun.COM 		{ "hook_hits",			KSTAT_DATA_UINT64 }
23347513SDarren.Reed@Sun.COM 	};
23357513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
23367513SDarren.Reed@Sun.COM 	size_t kslen;
23377513SDarren.Reed@Sun.COM 	int position;
23387513SDarren.Reed@Sun.COM 	hook_int_t *h;
23397513SDarren.Reed@Sun.COM 
23407513SDarren.Reed@Sun.COM 	kslen = strlen(hfi->hfi_family.hf_name) +
23417513SDarren.Reed@Sun.COM 	    strlen(hei->hei_event->he_name) + 2;
23427513SDarren.Reed@Sun.COM 
23437513SDarren.Reed@Sun.COM 	hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
23447513SDarren.Reed@Sun.COM 	(void) snprintf(hi->hi_ksname, kslen, "%s/%s",
23457513SDarren.Reed@Sun.COM 	    hfi->hfi_family.hf_name, hei->hei_event->he_name);
23467513SDarren.Reed@Sun.COM 
23477513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
23487513SDarren.Reed@Sun.COM 	hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
23497513SDarren.Reed@Sun.COM 	    hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
23507513SDarren.Reed@Sun.COM 	    sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
23517513SDarren.Reed@Sun.COM 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
23527513SDarren.Reed@Sun.COM 
23537513SDarren.Reed@Sun.COM 	/* Initialise the kstats for the structure */
23547513SDarren.Reed@Sun.COM 	bcopy(&template, &hi->hi_kstats, sizeof (template));
23557513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
23567513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
23577513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
23587513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_position.value.i32 = 0;
23597513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_hits.value.ui64 = 0;
23607513SDarren.Reed@Sun.COM 
23617513SDarren.Reed@Sun.COM 	switch (hi->hi_hook.h_hint) {
23627513SDarren.Reed@Sun.COM 	case HH_BEFORE :
23637513SDarren.Reed@Sun.COM 	case HH_AFTER :
23647513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
23657513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hintvalue.value.ui64 =
23667513SDarren.Reed@Sun.COM 		    hi->hi_hook.h_hintvalue;
23677513SDarren.Reed@Sun.COM 		break;
23687513SDarren.Reed@Sun.COM 	default :
23697513SDarren.Reed@Sun.COM 		break;
23707513SDarren.Reed@Sun.COM 	}
23717513SDarren.Reed@Sun.COM 
23727513SDarren.Reed@Sun.COM 	if (hi->hi_kstatp != NULL) {
23737513SDarren.Reed@Sun.COM 		hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
23747513SDarren.Reed@Sun.COM 		hi->hi_kstatp->ks_private =
23757513SDarren.Reed@Sun.COM 		    (void *)(uintptr_t)hks->hks_netstackid;
23767513SDarren.Reed@Sun.COM 
23777513SDarren.Reed@Sun.COM 		kstat_install(hi->hi_kstatp);
23787513SDarren.Reed@Sun.COM 	}
23797513SDarren.Reed@Sun.COM 
23807513SDarren.Reed@Sun.COM 	position = 1;
23817513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
23827513SDarren.Reed@Sun.COM 		h->hi_kstats.hook_position.value.ui32 = position++;
23837513SDarren.Reed@Sun.COM 	}
23847513SDarren.Reed@Sun.COM }
23857513SDarren.Reed@Sun.COM 
23867513SDarren.Reed@Sun.COM /*
23877513SDarren.Reed@Sun.COM  * Function:	hook_int_free
23882958Sdr146992  * Returns:	None
23892958Sdr146992  * Parameters:	hi(I) - internal hook pointer
23902958Sdr146992  *
2391*12263SDarren.Reed@Sun.COM  * Free memory allocated to support a hook.
23922958Sdr146992  */
23932958Sdr146992 static void
23947513SDarren.Reed@Sun.COM hook_int_free(hook_int_t *hi, netstackid_t stackid)
23952958Sdr146992 {
23967513SDarren.Reed@Sun.COM 	int len;
23977513SDarren.Reed@Sun.COM 
23982958Sdr146992 	ASSERT(hi != NULL);
23992958Sdr146992 
24002958Sdr146992 	/* Free name space */
24012958Sdr146992 	if (hi->hi_hook.h_name != NULL) {
24022958Sdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
24032958Sdr146992 	}
24047513SDarren.Reed@Sun.COM 	if (hi->hi_ksname != NULL) {
24057513SDarren.Reed@Sun.COM 		kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
24067513SDarren.Reed@Sun.COM 	}
24077513SDarren.Reed@Sun.COM 
24087513SDarren.Reed@Sun.COM 	/* Free the name used with the before/after hints. */
24097513SDarren.Reed@Sun.COM 	switch (hi->hi_hook.h_hint) {
24107513SDarren.Reed@Sun.COM 	case HH_BEFORE :
24117513SDarren.Reed@Sun.COM 	case HH_AFTER :
24127513SDarren.Reed@Sun.COM 		len = strlen((char *)hi->hi_hook.h_hintvalue);
24137513SDarren.Reed@Sun.COM 		if (len > 0)
24147513SDarren.Reed@Sun.COM 			kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
24157513SDarren.Reed@Sun.COM 		break;
24167513SDarren.Reed@Sun.COM 	default :
24177513SDarren.Reed@Sun.COM 		break;
24187513SDarren.Reed@Sun.COM 	}
24197513SDarren.Reed@Sun.COM 
24207513SDarren.Reed@Sun.COM 	if (hi->hi_kstatp != NULL)
24217513SDarren.Reed@Sun.COM 		kstat_delete_netstack(hi->hi_kstatp, stackid);
24222958Sdr146992 
24232958Sdr146992 	/* Free container */
24242958Sdr146992 	kmem_free(hi, sizeof (*hi));
24252958Sdr146992 }
24267513SDarren.Reed@Sun.COM 
24277513SDarren.Reed@Sun.COM /*
24287513SDarren.Reed@Sun.COM  * Function:	hook_alloc
2429*12263SDarren.Reed@Sun.COM  * Returns:	hook_t *   - pointer to new hook structure
24307513SDarren.Reed@Sun.COM  * Parameters:	version(I) - version number of the API when compiled
24317513SDarren.Reed@Sun.COM  *
24327513SDarren.Reed@Sun.COM  * This function serves as the interface for consumers to obtain a hook_t
24337513SDarren.Reed@Sun.COM  * structure.  At this point in time, there is only a single "version" of
24347513SDarren.Reed@Sun.COM  * it, leading to a straight forward function.  In a perfect world the
24357513SDarren.Reed@Sun.COM  * h_vesion would be a protected data structure member, but C isn't that
24367513SDarren.Reed@Sun.COM  * advanced...
24377513SDarren.Reed@Sun.COM  */
24387513SDarren.Reed@Sun.COM hook_t *
24397513SDarren.Reed@Sun.COM hook_alloc(const int h_version)
24407513SDarren.Reed@Sun.COM {
24417513SDarren.Reed@Sun.COM 	hook_t *h;
24427513SDarren.Reed@Sun.COM 
24437513SDarren.Reed@Sun.COM 	h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
24447513SDarren.Reed@Sun.COM 	h->h_version = h_version;
24457513SDarren.Reed@Sun.COM 	return (h);
24467513SDarren.Reed@Sun.COM }
24477513SDarren.Reed@Sun.COM 
24487513SDarren.Reed@Sun.COM /*
24497513SDarren.Reed@Sun.COM  * Function:	hook_free
24507513SDarren.Reed@Sun.COM  * Returns:	None
24517513SDarren.Reed@Sun.COM  * Parameters:	h(I) - external hook pointer
24527513SDarren.Reed@Sun.COM  *
24537513SDarren.Reed@Sun.COM  * This function only free's memory allocated with hook_alloc(), so that if
24547513SDarren.Reed@Sun.COM  * (for example) kernel memory was allocated for h_name, this needs to be
24557513SDarren.Reed@Sun.COM  * free'd before calling hook_free().
24567513SDarren.Reed@Sun.COM  */
24577513SDarren.Reed@Sun.COM void
24587513SDarren.Reed@Sun.COM hook_free(hook_t *h)
24597513SDarren.Reed@Sun.COM {
24607513SDarren.Reed@Sun.COM 	kmem_free(h, sizeof (*h));
24617513SDarren.Reed@Sun.COM }
24627513SDarren.Reed@Sun.COM 
24637513SDarren.Reed@Sun.COM /*
24647513SDarren.Reed@Sun.COM  * Function:	hook_notify_register
2465*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
2466*12263SDarren.Reed@Sun.COM  * Parameters:	head(I)     - top of the list of callbacks
24677513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
24687513SDarren.Reed@Sun.COM  *              arg(I)      - arg to pass back to the function
24697513SDarren.Reed@Sun.COM  *
24707513SDarren.Reed@Sun.COM  * This function implements the modification of the list of callbacks
24717513SDarren.Reed@Sun.COM  * that are registered when someone wants to be advised of a change
24727513SDarren.Reed@Sun.COM  * that has happened.
24737513SDarren.Reed@Sun.COM  */
24747513SDarren.Reed@Sun.COM static int
2475*12263SDarren.Reed@Sun.COM hook_notify_register(hook_notify_head_t *head, hook_notify_fn_t callback,
2476*12263SDarren.Reed@Sun.COM     void *arg)
24777513SDarren.Reed@Sun.COM {
24787513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
24797513SDarren.Reed@Sun.COM 
24807513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
24817513SDarren.Reed@Sun.COM 		if (hn->hn_func == callback) {
24827513SDarren.Reed@Sun.COM 			return (EEXIST);
24837513SDarren.Reed@Sun.COM 		}
24847513SDarren.Reed@Sun.COM 	}
24857513SDarren.Reed@Sun.COM 
24867513SDarren.Reed@Sun.COM 	hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
24877513SDarren.Reed@Sun.COM 	hn->hn_func = callback;
24887513SDarren.Reed@Sun.COM 	hn->hn_arg = arg;
24897513SDarren.Reed@Sun.COM 	TAILQ_INSERT_TAIL(head, hn, hn_entry);
24907513SDarren.Reed@Sun.COM 
24917513SDarren.Reed@Sun.COM 	return (0);
24927513SDarren.Reed@Sun.COM }
24937513SDarren.Reed@Sun.COM 
24947513SDarren.Reed@Sun.COM /*
2495*12263SDarren.Reed@Sun.COM  * Function:	hook_notify_unregister
2496*12263SDarren.Reed@Sun.COM  * Returns:	int         - 0 = success, else failure
2497*12263SDarren.Reed@Sun.COM  * Parameters:	stackid(I)  - netstack identifier
24987513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
2499*12263SDarren.Reed@Sun.COM  *              parg(O)     - pointer to storage for pointer
25007513SDarren.Reed@Sun.COM  *
2501*12263SDarren.Reed@Sun.COM  * When calling this function, the provision of a valid pointer in parg
2502*12263SDarren.Reed@Sun.COM  * allows the caller to be made aware of what argument the hook function
2503*12263SDarren.Reed@Sun.COM  * was expecting. This then allows the simulation of HN_UNREGISTER events
2504*12263SDarren.Reed@Sun.COM  * when a notify-unregister is performed.
25057513SDarren.Reed@Sun.COM  */
25067513SDarren.Reed@Sun.COM static int
2507*12263SDarren.Reed@Sun.COM hook_notify_unregister(hook_notify_head_t *head,
2508*12263SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void **parg)
25097513SDarren.Reed@Sun.COM {
25107513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
25117513SDarren.Reed@Sun.COM 
2512*12263SDarren.Reed@Sun.COM 	ASSERT(parg != NULL);
25137513SDarren.Reed@Sun.COM 
25147513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
25157513SDarren.Reed@Sun.COM 		if (hn->hn_func == callback)
25167513SDarren.Reed@Sun.COM 			break;
25177513SDarren.Reed@Sun.COM 	}
2518*12263SDarren.Reed@Sun.COM 
2519*12263SDarren.Reed@Sun.COM 	if (hn == NULL)
25207513SDarren.Reed@Sun.COM 		return (ESRCH);
2521*12263SDarren.Reed@Sun.COM 
2522*12263SDarren.Reed@Sun.COM 	*parg = hn->hn_arg;
25237513SDarren.Reed@Sun.COM 
25247513SDarren.Reed@Sun.COM 	TAILQ_REMOVE(head, hn, hn_entry);
25257513SDarren.Reed@Sun.COM 
25267513SDarren.Reed@Sun.COM 	kmem_free(hn, sizeof (*hn));
25277513SDarren.Reed@Sun.COM 
25287513SDarren.Reed@Sun.COM 	return (0);
25297513SDarren.Reed@Sun.COM }
25307513SDarren.Reed@Sun.COM 
25317513SDarren.Reed@Sun.COM /*
25327513SDarren.Reed@Sun.COM  * Function:	hook_notify_run
25337513SDarren.Reed@Sun.COM  * Returns:	None
25347513SDarren.Reed@Sun.COM  * Parameters:	head(I)   - top of the list of callbacks
25357513SDarren.Reed@Sun.COM  *              family(I) - name of the hook family that owns the event
25367513SDarren.Reed@Sun.COM  *              event(I)  - name of the event being changed
25377513SDarren.Reed@Sun.COM  *              name(I)   - name of the object causing change
25387513SDarren.Reed@Sun.COM  *              cmd(I)    - either HN_UNREGISTER or HN_REGISTER
25397513SDarren.Reed@Sun.COM  *
25407513SDarren.Reed@Sun.COM  * This function walks through the list of registered callbacks and
25417513SDarren.Reed@Sun.COM  * executes each one, passing back the arg supplied when registered
25427513SDarren.Reed@Sun.COM  * and the name of the family (that owns the event), event (the thing
25437513SDarren.Reed@Sun.COM  * to which we're making a change) and finally a name that describes
25447513SDarren.Reed@Sun.COM  * what is being added or removed, as indicated by cmd.
25457513SDarren.Reed@Sun.COM  *
25467513SDarren.Reed@Sun.COM  * This function does not acquire or release any lock as it is required
25477513SDarren.Reed@Sun.COM  * that code calling it do so before hand.  The use of hook_notify_head_t
25487513SDarren.Reed@Sun.COM  * is protected by the use of flagwait_t in the structures that own this
25497513SDarren.Reed@Sun.COM  * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
25507513SDarren.Reed@Sun.COM  */
25517513SDarren.Reed@Sun.COM static void
25527513SDarren.Reed@Sun.COM hook_notify_run(hook_notify_head_t *head, char *family, char *event,
25537513SDarren.Reed@Sun.COM     char *name, hook_notify_cmd_t cmd)
25547513SDarren.Reed@Sun.COM {
25557513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
25567513SDarren.Reed@Sun.COM 
25577513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
25587513SDarren.Reed@Sun.COM 		(*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
25597513SDarren.Reed@Sun.COM 	}
25607513SDarren.Reed@Sun.COM }
2561