xref: /onnv-gate/usr/src/uts/common/io/hook.c (revision 7915:4883817dda2c)
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
777513SDarren.Reed@Sun.COM  * if either structure is still "busy". If so then a boolean flag is set to
787513SDarren.Reed@Sun.COM  * say that the structure is condemned. The presence of this flag being set
797513SDarren.Reed@Sun.COM  * must be checked for in _add()/_register()/ functions and a failure returned
807513SDarren.Reed@Sun.COM  * if it is set. It is ignored by the _find() functions because they're
817513SDarren.Reed@Sun.COM  * used by _remove()/_unregister().  While setting the condemned flag when
827513SDarren.Reed@Sun.COM  * trying to delete a structure would normally be keyed from the presence
837513SDarren.Reed@Sun.COM  * of a reference count being greater than 1, in this implementation there
847513SDarren.Reed@Sun.COM  * are no reference counts required: instead the presence of objects on
857513SDarren.Reed@Sun.COM  * linked lists is taken to mean something is still "busy."
867513SDarren.Reed@Sun.COM  *
877513SDarren.Reed@Sun.COM  * ONLY the caller that adds the family and the events ever has a direct
887513SDarren.Reed@Sun.COM  * reference to the internal structures and thus ONLY it should be doing
897513SDarren.Reed@Sun.COM  * the removal of either the event or family.  In practise, what this means
907513SDarren.Reed@Sun.COM  * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
917513SDarren.Reed@Sun.COM  * by net_event_register() (these interface to hook_family_add() and
927513SDarren.Reed@Sun.COM  * hook_event_add(), respectively) that are made when we create an instance
937513SDarren.Reed@Sun.COM  * of IP and when the IP instance is shutdown/destroyed, it calls
947513SDarren.Reed@Sun.COM  * net_event_unregister() and net_protocol_unregister(), which in turn call
957513SDarren.Reed@Sun.COM  * hook_event_remove() and hook_family_remove() respectively. Nobody else
967513SDarren.Reed@Sun.COM  * is entitled to call the _unregister() functions.  It is imperative that
977513SDarren.Reed@Sun.COM  * there be only one _remove() call for every _add() call.
987513SDarren.Reed@Sun.COM  *
997513SDarren.Reed@Sun.COM  * It is possible that code which is interfacing with this hook framework
1007513SDarren.Reed@Sun.COM  * won't do all the cleaning up that it needs to at the right time. While
1017513SDarren.Reed@Sun.COM  * we can't prevent programmers from creating memory leaks, we can synchronise
1027513SDarren.Reed@Sun.COM  * when we clean up data structures to prevent code accessing free'd memory.
1037513SDarren.Reed@Sun.COM  *
1047513SDarren.Reed@Sun.COM  * A simple diagram showing the ownership is as follows:
1057513SDarren.Reed@Sun.COM  *
1067513SDarren.Reed@Sun.COM  *  Owned       +--------------+
1077513SDarren.Reed@Sun.COM  *   by         | hook_stack_t |
1087513SDarren.Reed@Sun.COM  *   the        +--------------+
1097513SDarren.Reed@Sun.COM  *  Instance      |
1107513SDarren.Reed@Sun.COM  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1117513SDarren.Reed@Sun.COM  *                V
1127513SDarren.Reed@Sun.COM  *  Owned       +-------------------+     +-------------------+
1137513SDarren.Reed@Sun.COM  *              | hook_family_int_t |---->| hook_family_int_t |
1147513SDarren.Reed@Sun.COM  *   by         +-------------------+     +-------------------+
1157513SDarren.Reed@Sun.COM  *                | \+---------------+        \+---------------+
1167513SDarren.Reed@Sun.COM  *  network       |  | hook_family_t |         | hook_family_t |
1177513SDarren.Reed@Sun.COM  *                V  +---------------+         +---------------+
1187513SDarren.Reed@Sun.COM  *  protocol   +------------------+     +------------------+
1197513SDarren.Reed@Sun.COM  *             | hook_event_int_t |---->| hook_event_int_t |
1207513SDarren.Reed@Sun.COM  * (ipv4,ipv6) +------------------+     +------------------+
1217513SDarren.Reed@Sun.COM  *                | \+--------------+        \+--------------+
1227513SDarren.Reed@Sun.COM  *                |  | hook_event_t |         | hook_event_t |
1237513SDarren.Reed@Sun.COM  *                |  +--------------+         +--------------+
1247513SDarren.Reed@Sun.COM  * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1257513SDarren.Reed@Sun.COM  *                V
1267513SDarren.Reed@Sun.COM  *  Owned      +------------+
1277513SDarren.Reed@Sun.COM  *             | hook_int_t |
1287513SDarren.Reed@Sun.COM  *   by        +------------+
1297513SDarren.Reed@Sun.COM  *                  \+--------+
1307513SDarren.Reed@Sun.COM  * the consumer      | hook_t |
1317513SDarren.Reed@Sun.COM  *                   +--------+
1327513SDarren.Reed@Sun.COM  *
1337513SDarren.Reed@Sun.COM  * The consumers, such as IPFilter, do not have any pointers or hold any
1347513SDarren.Reed@Sun.COM  * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
1357513SDarren.Reed@Sun.COM  * a hook on an event through net_hook_register(), an implicit reference
1367513SDarren.Reed@Sun.COM  * to the hook_event_int_t is returned with a successful call.  Additionally,
1377513SDarren.Reed@Sun.COM  * IPFilter does not see the hook_family_int_t or hook_family_t directly.
1387513SDarren.Reed@Sun.COM  * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
1397513SDarren.Reed@Sun.COM  * contains a pointer to hook_family_int_t.  The structure behind the
1407513SDarren.Reed@Sun.COM  * net_handle_t (struct net_data) *is* reference counted and managed
1417513SDarren.Reed@Sun.COM  * appropriately.
1427513SDarren.Reed@Sun.COM  *
1437513SDarren.Reed@Sun.COM  * A more detailed picture that describes how the family/event structures
1447513SDarren.Reed@Sun.COM  * are linked together can be found in <sys/hook_impl.h>
1457513SDarren.Reed@Sun.COM  */
1467513SDarren.Reed@Sun.COM 
1477513SDarren.Reed@Sun.COM /*
1487513SDarren.Reed@Sun.COM  * Locking
1497513SDarren.Reed@Sun.COM  * =======
1507513SDarren.Reed@Sun.COM  * The use of CVW_* macros to do locking is driven by the need to allow
1517513SDarren.Reed@Sun.COM  * recursive locking with read locks when we're processing packets. This
1527513SDarren.Reed@Sun.COM  * is necessary because various netinfo functions need to hold read locks,
1537513SDarren.Reed@Sun.COM  * by design, as they can be called in or out of packet context.
1547513SDarren.Reed@Sun.COM  */
1557513SDarren.Reed@Sun.COM /*
1562958Sdr146992  * Hook internal functions
1572958Sdr146992  */
1582958Sdr146992 static hook_int_t *hook_copy(hook_t *src);
1593448Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
1603448Sdh155122     hook_stack_t *hks);
1612958Sdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
1622958Sdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
1637513SDarren.Reed@Sun.COM static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
1642958Sdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
1653448Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
1667513SDarren.Reed@Sun.COM static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
1672958Sdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
1687513SDarren.Reed@Sun.COM static void hook_int_free(hook_int_t *hi, netstackid_t);
1692958Sdr146992 static void hook_init(void);
1703448Sdh155122 static void hook_fini(void);
1713448Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
1723448Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
1737513SDarren.Reed@Sun.COM static void hook_stack_shutdown(netstackid_t stackid, void *arg);
1747513SDarren.Reed@Sun.COM static int hook_insert(hook_int_head_t *head, hook_int_t *new);
1757513SDarren.Reed@Sun.COM static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
1767513SDarren.Reed@Sun.COM static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
1777513SDarren.Reed@Sun.COM static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
1787513SDarren.Reed@Sun.COM static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
1797513SDarren.Reed@Sun.COM static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
1807513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd);
1817513SDarren.Reed@Sun.COM static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
1827513SDarren.Reed@Sun.COM     hook_int_t *hi);
1837513SDarren.Reed@Sun.COM static int hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
1847513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg);
1857513SDarren.Reed@Sun.COM static int hook_notify_unregister(cvwaitlock_t *lock,
1867513SDarren.Reed@Sun.COM     hook_notify_head_t *head, hook_notify_fn_t callback);
1877513SDarren.Reed@Sun.COM static void hook_notify_run(hook_notify_head_t *head, char *family,
1887513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd);
1897513SDarren.Reed@Sun.COM static void hook_stack_notify_run(hook_stack_t *hks, char *name,
1907513SDarren.Reed@Sun.COM     hook_notify_cmd_t cmd);
1917513SDarren.Reed@Sun.COM static void hook_stack_remove(hook_stack_t *hks);
1927513SDarren.Reed@Sun.COM 
1937513SDarren.Reed@Sun.COM /*
1947513SDarren.Reed@Sun.COM  * A list of the hook stacks is kept here because we need to enable
1957513SDarren.Reed@Sun.COM  * net_instance_notify_register() to be called during the creation
1967513SDarren.Reed@Sun.COM  * of a new instance. Previously hook_stack_get() would just use
1977513SDarren.Reed@Sun.COM  * the netstack functions for this work but they will return NULL
1987513SDarren.Reed@Sun.COM  * until the zone has been fully initialised.
1997513SDarren.Reed@Sun.COM  */
2007513SDarren.Reed@Sun.COM static hook_stack_head_t hook_stacks;
2017513SDarren.Reed@Sun.COM static kmutex_t hook_stack_lock;
2022958Sdr146992 
2032958Sdr146992 /*
2042958Sdr146992  * Module entry points.
2052958Sdr146992  */
2062958Sdr146992 int
2072958Sdr146992 _init(void)
2082958Sdr146992 {
2093448Sdh155122 	int error;
2103448Sdh155122 
2112958Sdr146992 	hook_init();
2123448Sdh155122 	error = mod_install(&modlinkage);
2133448Sdh155122 	if (error != 0)
2143448Sdh155122 		hook_fini();
2153448Sdh155122 
2163448Sdh155122 	return (error);
2172958Sdr146992 }
2182958Sdr146992 
2192958Sdr146992 int
2202958Sdr146992 _fini(void)
2212958Sdr146992 {
2223448Sdh155122 	int error;
2233448Sdh155122 
2243448Sdh155122 	error = mod_remove(&modlinkage);
2253448Sdh155122 	if (error == 0)
2263448Sdh155122 		hook_fini();
2273448Sdh155122 
2283448Sdh155122 	return (error);
2292958Sdr146992 }
2302958Sdr146992 
2312958Sdr146992 int
2322958Sdr146992 _info(struct modinfo *modinfop)
2332958Sdr146992 {
2342958Sdr146992 	return (mod_info(&modlinkage, modinfop));
2352958Sdr146992 }
2362958Sdr146992 
2372958Sdr146992 /*
2382958Sdr146992  * Function:	hook_init
2392958Sdr146992  * Returns:	None
2402958Sdr146992  * Parameters:	None
2412958Sdr146992  *
2422958Sdr146992  * Initialize hooks
2432958Sdr146992  */
2442958Sdr146992 static void
2452958Sdr146992 hook_init(void)
2462958Sdr146992 {
2477513SDarren.Reed@Sun.COM 	mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
2487513SDarren.Reed@Sun.COM 	SLIST_INIT(&hook_stacks);
2497513SDarren.Reed@Sun.COM 
2503448Sdh155122 	/*
2513448Sdh155122 	 * We want to be informed each time a stack is created or
2523448Sdh155122 	 * destroyed in the kernel.
2533448Sdh155122 	 */
2547513SDarren.Reed@Sun.COM 	netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
2553448Sdh155122 	    hook_stack_fini);
2563448Sdh155122 }
2573448Sdh155122 
2583448Sdh155122 /*
2593448Sdh155122  * Function:	hook_fini
2603448Sdh155122  * Returns:	None
2613448Sdh155122  * Parameters:	None
2623448Sdh155122  *
2633448Sdh155122  * Deinitialize hooks
2643448Sdh155122  */
2653448Sdh155122 static void
2663448Sdh155122 hook_fini(void)
2673448Sdh155122 {
2683448Sdh155122 	netstack_unregister(NS_HOOK);
2697513SDarren.Reed@Sun.COM 
2707513SDarren.Reed@Sun.COM 	mutex_destroy(&hook_stack_lock);
2717513SDarren.Reed@Sun.COM 	ASSERT(SLIST_EMPTY(&hook_stacks));
2727513SDarren.Reed@Sun.COM }
2737513SDarren.Reed@Sun.COM 
2747513SDarren.Reed@Sun.COM /*
2757513SDarren.Reed@Sun.COM  * Function:	hook_wait_setflag
2767513SDarren.Reed@Sun.COM  * Returns:     -1 = setting flag is disallowed, 0 = flag set and did
2777513SDarren.Reed@Sun.COM  *              not have to wait (ie no lock droped), 1 = flag set but
2787513SDarren.Reed@Sun.COM  *              it was necessary to drop locks to set it.
2797513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
2807513SDarren.Reed@Sun.COM  *              busyset(I) - set of flags that we don't want set while
2817513SDarren.Reed@Sun.COM  *                           we are active.
2827513SDarren.Reed@Sun.COM  *              wanted(I)  - flag associated with newflag to indicate
2837513SDarren.Reed@Sun.COM  *                           what we want to do.
2847513SDarren.Reed@Sun.COM  *              newflag(I) - the new ACTIVE flag we want to set that
2857513SDarren.Reed@Sun.COM  *                           indicates what we are doing.
2867513SDarren.Reed@Sun.COM  *
2877513SDarren.Reed@Sun.COM  * The set of functions hook_wait_* implement an API that builds on top of
2887513SDarren.Reed@Sun.COM  * the kcondvar_t to provide controlled execution through a critical region.
2897513SDarren.Reed@Sun.COM  * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
2907513SDarren.Reed@Sun.COM  * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
2917513SDarren.Reed@Sun.COM  * The combination of flags is required as when this function exits to do
2927513SDarren.Reed@Sun.COM  * the task, the structure is then free for another caller to use and
2937513SDarren.Reed@Sun.COM  * to indicate that it wants to do work.  The trump flags here are those
2947513SDarren.Reed@Sun.COM  * that indicate someone wants to destroy the structure that owns this
2957513SDarren.Reed@Sun.COM  * flagwait_t.  In this case, we don't try to secure the ability to run
2967513SDarren.Reed@Sun.COM  * and return with an error.
2977513SDarren.Reed@Sun.COM  *
2987513SDarren.Reed@Sun.COM  * wanted - the FWF_*_WANTED flag that describes the action being requested
2997513SDarren.Reed@Sun.COM  * busyset- the set of FWF_* flags we don't want set when we run
3007513SDarren.Reed@Sun.COM  * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
3017513SDarren.Reed@Sun.COM  */
3027513SDarren.Reed@Sun.COM int
3037513SDarren.Reed@Sun.COM hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
3047513SDarren.Reed@Sun.COM     fwflag_t newflag)
3057513SDarren.Reed@Sun.COM {
3067513SDarren.Reed@Sun.COM 	int waited = 0;
3077513SDarren.Reed@Sun.COM 
3087513SDarren.Reed@Sun.COM 	mutex_enter(&waiter->fw_lock);
3097513SDarren.Reed@Sun.COM 	if (waiter->fw_flags & FWF_DESTROY) {
3107513SDarren.Reed@Sun.COM 		mutex_exit(&waiter->fw_lock);
3117513SDarren.Reed@Sun.COM 		return (-1);
3127513SDarren.Reed@Sun.COM 	}
3137513SDarren.Reed@Sun.COM 	while (waiter->fw_flags & busyset) {
3147513SDarren.Reed@Sun.COM 		waiter->fw_flags |= wanted;
3157513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(waiter->fw_owner);
3167513SDarren.Reed@Sun.COM 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
3177513SDarren.Reed@Sun.COM 		waited = 1;
3187513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(waiter->fw_owner);
3197513SDarren.Reed@Sun.COM 		if (waiter->fw_flags & FWF_DESTROY) {
3207513SDarren.Reed@Sun.COM 			waiter->fw_flags &= ~wanted;
3217513SDarren.Reed@Sun.COM 			mutex_exit(&waiter->fw_lock);
3227513SDarren.Reed@Sun.COM 			return (-1);
3237513SDarren.Reed@Sun.COM 		}
3247513SDarren.Reed@Sun.COM 		waiter->fw_flags |= wanted;
3257513SDarren.Reed@Sun.COM 	}
3267513SDarren.Reed@Sun.COM 	waiter->fw_flags &= ~wanted;
3277513SDarren.Reed@Sun.COM 	waiter->fw_flags |= newflag;
3287513SDarren.Reed@Sun.COM 	mutex_exit(&waiter->fw_lock);
3297513SDarren.Reed@Sun.COM 	return (waited);
3307513SDarren.Reed@Sun.COM }
3317513SDarren.Reed@Sun.COM 
3327513SDarren.Reed@Sun.COM /*
3337513SDarren.Reed@Sun.COM  * Function:	hook_wait_unsetflag
3347513SDarren.Reed@Sun.COM  * Returns:     None
3357513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
3367513SDarren.Reed@Sun.COM  *              oldflag(I) - flag to reset
3377513SDarren.Reed@Sun.COM  *
3387513SDarren.Reed@Sun.COM  * Turn off the bit that we had set to run and let others know that
3397513SDarren.Reed@Sun.COM  * they should now check to see if they can run.
3407513SDarren.Reed@Sun.COM  */
3417513SDarren.Reed@Sun.COM void
3427513SDarren.Reed@Sun.COM hook_wait_unsetflag(flagwait_t *waiter, uint32_t oldflag)
3437513SDarren.Reed@Sun.COM {
3447513SDarren.Reed@Sun.COM 	mutex_enter(&waiter->fw_lock);
3457513SDarren.Reed@Sun.COM 	waiter->fw_flags &= ~oldflag;
3467513SDarren.Reed@Sun.COM 	cv_signal(&waiter->fw_cv);
3477513SDarren.Reed@Sun.COM 	mutex_exit(&waiter->fw_lock);
3487513SDarren.Reed@Sun.COM }
3497513SDarren.Reed@Sun.COM 
3507513SDarren.Reed@Sun.COM /*
3517513SDarren.Reed@Sun.COM  * Function:	hook_wait_destroy
3527513SDarren.Reed@Sun.COM  * Returns:     None
3537513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
3547513SDarren.Reed@Sun.COM  *
3557513SDarren.Reed@Sun.COM  * Since outer locking (on fw_owner) should ensure that only one function
3567513SDarren.Reed@Sun.COM  * at a time gets to call hook_wait_destroy() on a given object, there is
3577513SDarren.Reed@Sun.COM  * no need to guard against setting FWF_DESTROY_WANTED already being set.
3587513SDarren.Reed@Sun.COM  * It is, however, necessary to wait for all activity on the owning
3597513SDarren.Reed@Sun.COM  * structure to cease.
3607513SDarren.Reed@Sun.COM  */
3617513SDarren.Reed@Sun.COM void
3627513SDarren.Reed@Sun.COM hook_wait_destroy(flagwait_t *waiter)
3637513SDarren.Reed@Sun.COM {
3647513SDarren.Reed@Sun.COM 	ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
3657513SDarren.Reed@Sun.COM 	waiter->fw_flags |= FWF_DESTROY_WANTED;
3667513SDarren.Reed@Sun.COM 	while (!FWF_DESTROY_OK(waiter)) {
3677513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(waiter->fw_owner);
3687513SDarren.Reed@Sun.COM 		cv_wait(&waiter->fw_cv, &waiter->fw_lock);
3697513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(waiter->fw_owner);
3707513SDarren.Reed@Sun.COM 	}
3717513SDarren.Reed@Sun.COM 	/*
3727513SDarren.Reed@Sun.COM 	 * There should now be nothing else using "waiter" or its
3737513SDarren.Reed@Sun.COM 	 * owner, so we can safely assign here without risk of wiiping
3747513SDarren.Reed@Sun.COM 	 * out someone's bit.
3757513SDarren.Reed@Sun.COM 	 */
3767513SDarren.Reed@Sun.COM 	waiter->fw_flags = FWF_DESTROY_ACTIVE;
3777513SDarren.Reed@Sun.COM }
3787513SDarren.Reed@Sun.COM 
3797513SDarren.Reed@Sun.COM /*
3807513SDarren.Reed@Sun.COM  * Function:	hook_wait_init
3817513SDarren.Reed@Sun.COM  * Returns:     None
3827513SDarren.Reed@Sun.COM  * Parameters:  waiter(I)  - control data structure
3837513SDarren.Reed@Sun.COM  *              ownder(I)  - pointer to lock that the owner of this
3847513SDarren.Reed@Sun.COM  *                           waiter uses
3857513SDarren.Reed@Sun.COM  *
3867513SDarren.Reed@Sun.COM  * "owner" gets passed in here so that when we need to call cv_wait,
3877513SDarren.Reed@Sun.COM  * for example in hook_wait_setflag(), we can drop the lock for the
3887513SDarren.Reed@Sun.COM  * next layer out, which is likely to be held in an exclusive manner.
3897513SDarren.Reed@Sun.COM  */
3907513SDarren.Reed@Sun.COM void
3917513SDarren.Reed@Sun.COM hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
3927513SDarren.Reed@Sun.COM {
3937513SDarren.Reed@Sun.COM 	cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
3947513SDarren.Reed@Sun.COM 	mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
3957513SDarren.Reed@Sun.COM 	waiter->fw_flags = FWF_NONE;
3967513SDarren.Reed@Sun.COM 	waiter->fw_owner = owner;
3972958Sdr146992 }
3982958Sdr146992 
3993448Sdh155122 /*
4003448Sdh155122  * Initialize the hook stack instance.
4013448Sdh155122  */
4023448Sdh155122 /*ARGSUSED*/
4033448Sdh155122 static void *
4043448Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
4053448Sdh155122 {
4063448Sdh155122 	hook_stack_t	*hks;
4073448Sdh155122 
4083448Sdh155122 #ifdef NS_DEBUG
4093448Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
4103448Sdh155122 #endif
4113448Sdh155122 
4123448Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
4137513SDarren.Reed@Sun.COM 	hks->hks_netstack = ns;
4147513SDarren.Reed@Sun.COM 	hks->hks_netstackid = stackid;
4153448Sdh155122 
4167513SDarren.Reed@Sun.COM 	CVW_INIT(&hks->hks_lock);
4177513SDarren.Reed@Sun.COM 	TAILQ_INIT(&hks->hks_nhead);
4183448Sdh155122 	SLIST_INIT(&hks->hks_familylist);
4193448Sdh155122 
4207513SDarren.Reed@Sun.COM 	hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
4217513SDarren.Reed@Sun.COM 
4227513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
4237513SDarren.Reed@Sun.COM 	SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
4247513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
4257513SDarren.Reed@Sun.COM 
4263448Sdh155122 	return (hks);
4273448Sdh155122 }
4283448Sdh155122 
429*7915SDarren.Reed@Sun.COM /*
430*7915SDarren.Reed@Sun.COM  * Set the shutdown flag to indicate that we should stop accepting new
431*7915SDarren.Reed@Sun.COM  * register calls as we're now in the cleanup process.
432*7915SDarren.Reed@Sun.COM  */
4337513SDarren.Reed@Sun.COM /*ARGSUSED*/
4347513SDarren.Reed@Sun.COM static void
4357513SDarren.Reed@Sun.COM hook_stack_shutdown(netstackid_t stackid, void *arg)
4367513SDarren.Reed@Sun.COM {
4377513SDarren.Reed@Sun.COM 	hook_stack_t *hks = (hook_stack_t *)arg;
4387513SDarren.Reed@Sun.COM 
4397513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
4407513SDarren.Reed@Sun.COM 	/*
4417513SDarren.Reed@Sun.COM 	 * Once this flag gets set to one, no more additions are allowed
4427513SDarren.Reed@Sun.COM 	 * to any of the structures that make up this stack.
4437513SDarren.Reed@Sun.COM 	 */
4447513SDarren.Reed@Sun.COM 	hks->hks_shutdown = 1;
4457513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
4467513SDarren.Reed@Sun.COM }
4477513SDarren.Reed@Sun.COM 
4483448Sdh155122 /*
4493448Sdh155122  * Free the hook stack instance.
4503448Sdh155122  */
4513448Sdh155122 /*ARGSUSED*/
4523448Sdh155122 static void
4533448Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
4543448Sdh155122 {
4557513SDarren.Reed@Sun.COM 	hook_stack_t *hks = (hook_stack_t *)arg;
4567513SDarren.Reed@Sun.COM 
4577513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
4587513SDarren.Reed@Sun.COM 	hks->hks_shutdown = 2;
4597513SDarren.Reed@Sun.COM 	hook_stack_remove(hks);
4607513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
4617513SDarren.Reed@Sun.COM }
4627513SDarren.Reed@Sun.COM 
4637513SDarren.Reed@Sun.COM /*
4647513SDarren.Reed@Sun.COM  * This function assumes that it is called with hook_stack_lock held.
4657513SDarren.Reed@Sun.COM  * It functions differently to hook_family/event_remove in that it does
4667513SDarren.Reed@Sun.COM  * the checks to see if it can be removed. This difference exists
4677513SDarren.Reed@Sun.COM  * because this structure has nothing higher up that depends on it.
4687513SDarren.Reed@Sun.COM  */
4697513SDarren.Reed@Sun.COM static void
4707513SDarren.Reed@Sun.COM hook_stack_remove(hook_stack_t *hks)
4717513SDarren.Reed@Sun.COM {
4727513SDarren.Reed@Sun.COM 
4737513SDarren.Reed@Sun.COM 	ASSERT(mutex_owned(&hook_stack_lock));
4747513SDarren.Reed@Sun.COM 
4757513SDarren.Reed@Sun.COM 	/*
4767513SDarren.Reed@Sun.COM 	 * Is the structure still in use?
4777513SDarren.Reed@Sun.COM 	 */
4787513SDarren.Reed@Sun.COM 	if (!SLIST_EMPTY(&hks->hks_familylist) ||
4797513SDarren.Reed@Sun.COM 	    !TAILQ_EMPTY(&hks->hks_nhead))
4807513SDarren.Reed@Sun.COM 		return;
4817513SDarren.Reed@Sun.COM 
4827513SDarren.Reed@Sun.COM 	SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
4837513SDarren.Reed@Sun.COM 
4847513SDarren.Reed@Sun.COM 	hook_wait_destroy(&hks->hks_waiter);
4857513SDarren.Reed@Sun.COM 	CVW_DESTROY(&hks->hks_lock);
4863448Sdh155122 	kmem_free(hks, sizeof (*hks));
4873448Sdh155122 }
4882958Sdr146992 
4897513SDarren.Reed@Sun.COM static hook_stack_t *
4907513SDarren.Reed@Sun.COM hook_stack_get(netstackid_t stackid)
4917513SDarren.Reed@Sun.COM {
4927513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
4937513SDarren.Reed@Sun.COM 
4947513SDarren.Reed@Sun.COM 	SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
4957513SDarren.Reed@Sun.COM 		if (hks->hks_netstackid == stackid)
4967513SDarren.Reed@Sun.COM 			break;
4977513SDarren.Reed@Sun.COM 	}
4987513SDarren.Reed@Sun.COM 
4997513SDarren.Reed@Sun.COM 	return (hks);
5007513SDarren.Reed@Sun.COM }
5017513SDarren.Reed@Sun.COM 
5027513SDarren.Reed@Sun.COM /*
5037513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_register
5047513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
5057513SDarren.Reed@Sun.COM  * Parameters:	stackid(I) - netstack identifier
5067513SDarren.Reed@Sun.COM  *              callback(I)- function to be called
5077513SDarren.Reed@Sun.COM  *              arg(I)     - arg to provide callback when it is called
5087513SDarren.Reed@Sun.COM  *
5097513SDarren.Reed@Sun.COM  * If we're not shutting down this instance, append a new function to the
5107513SDarren.Reed@Sun.COM  * list of those to call when a new family of hooks is added to this stack.
5117513SDarren.Reed@Sun.COM  */
5127513SDarren.Reed@Sun.COM int
5137513SDarren.Reed@Sun.COM hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
5147513SDarren.Reed@Sun.COM     void *arg)
5157513SDarren.Reed@Sun.COM {
5167513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
5177513SDarren.Reed@Sun.COM 	int error;
5187513SDarren.Reed@Sun.COM 
5197513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
5207513SDarren.Reed@Sun.COM 	hks = hook_stack_get(stackid);
5217513SDarren.Reed@Sun.COM 	if (hks != NULL) {
5227513SDarren.Reed@Sun.COM 		if (hks->hks_shutdown != 0) {
5237513SDarren.Reed@Sun.COM 			error = ESHUTDOWN;
5247513SDarren.Reed@Sun.COM 		} else {
5257513SDarren.Reed@Sun.COM 			error = hook_notify_register(&hks->hks_lock,
5267513SDarren.Reed@Sun.COM 			    &hks->hks_nhead, callback, arg);
5277513SDarren.Reed@Sun.COM 		}
5287513SDarren.Reed@Sun.COM 	} else {
5297513SDarren.Reed@Sun.COM 		error = ESRCH;
5307513SDarren.Reed@Sun.COM 	}
5317513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
5327513SDarren.Reed@Sun.COM 
5337513SDarren.Reed@Sun.COM 	return (error);
5347513SDarren.Reed@Sun.COM }
5357513SDarren.Reed@Sun.COM 
5367513SDarren.Reed@Sun.COM /*
5377513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_unregister
5387513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
5397513SDarren.Reed@Sun.COM  * Parameters:	stackid(I) - netstack identifier
5407513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
5417513SDarren.Reed@Sun.COM  *
5427513SDarren.Reed@Sun.COM  * Attempt to remove a registered function from a hook stack's list of
5437513SDarren.Reed@Sun.COM  * callbacks to activiate when protocols are added/deleted.
5447513SDarren.Reed@Sun.COM  */
5457513SDarren.Reed@Sun.COM int
5467513SDarren.Reed@Sun.COM hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
5477513SDarren.Reed@Sun.COM {
5487513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
5497513SDarren.Reed@Sun.COM 	int error;
5507513SDarren.Reed@Sun.COM 
5517513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
5527513SDarren.Reed@Sun.COM 	hks = hook_stack_get(stackid);
5537513SDarren.Reed@Sun.COM 	if (hks != NULL) {
5547513SDarren.Reed@Sun.COM 		error = hook_notify_unregister(&hks->hks_lock,
5557513SDarren.Reed@Sun.COM 		    &hks->hks_nhead, callback);
5567513SDarren.Reed@Sun.COM 		if ((error == 0) && (hks->hks_shutdown == 2))
5577513SDarren.Reed@Sun.COM 			hook_stack_remove(hks);
5587513SDarren.Reed@Sun.COM 	} else {
5597513SDarren.Reed@Sun.COM 		error = ESRCH;
5607513SDarren.Reed@Sun.COM 	}
5617513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
5627513SDarren.Reed@Sun.COM 
5637513SDarren.Reed@Sun.COM 	return (error);
5647513SDarren.Reed@Sun.COM }
5657513SDarren.Reed@Sun.COM 
5667513SDarren.Reed@Sun.COM /*
5677513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_run
5687513SDarren.Reed@Sun.COM  * Returns:	None
5697513SDarren.Reed@Sun.COM  * Parameters:	hks(I)  - hook stack pointer to execute callbacks for
5707513SDarren.Reed@Sun.COM  *              name(I) - name of a hook family
5717513SDarren.Reed@Sun.COM  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
5727513SDarren.Reed@Sun.COM  *
5737513SDarren.Reed@Sun.COM  * Run through the list of callbacks on the hook stack to be called when
5747513SDarren.Reed@Sun.COM  * a new hook family is added
5757513SDarren.Reed@Sun.COM  *
5767513SDarren.Reed@Sun.COM  * As hook_notify_run() expects 3 names, one for the family, one for the
5777513SDarren.Reed@Sun.COM  * event and one for the object being introduced and we really only have
5787513SDarren.Reed@Sun.COM  * one name (that of the new hook family), fake the hook stack's name by
5797513SDarren.Reed@Sun.COM  * converting the integer to a string and for the event just pass NULL.
5807513SDarren.Reed@Sun.COM  */
5817513SDarren.Reed@Sun.COM static void
5827513SDarren.Reed@Sun.COM hook_stack_notify_run(hook_stack_t *hks, char *name,
5837513SDarren.Reed@Sun.COM     hook_notify_cmd_t cmd)
5847513SDarren.Reed@Sun.COM {
5857513SDarren.Reed@Sun.COM 	char buffer[16];
5867513SDarren.Reed@Sun.COM 
5877513SDarren.Reed@Sun.COM 	(void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
5887513SDarren.Reed@Sun.COM 
5897513SDarren.Reed@Sun.COM 	hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
5907513SDarren.Reed@Sun.COM }
5917513SDarren.Reed@Sun.COM 
5922958Sdr146992 /*
5932958Sdr146992  * Function:	hook_run
5942958Sdr146992  * Returns:	int - return value according to callback func
5952958Sdr146992  * Parameters:	token(I) - event pointer
5962958Sdr146992  *		info(I) - message
5972958Sdr146992  *
5982958Sdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
5992958Sdr146992  * until either the end of the list is reached or a hook function returns a
6002958Sdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
6012958Sdr146992  * return that value back to our caller.  By design, a hook function can be
6022958Sdr146992  * called more than once, simultaneously.
6032958Sdr146992  */
6042958Sdr146992 int
6057513SDarren.Reed@Sun.COM hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
6062958Sdr146992 {
6077513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
6082958Sdr146992 	hook_int_t *hi;
6092958Sdr146992 	int rval = 0;
6102958Sdr146992 
6112958Sdr146992 	ASSERT(token != NULL);
6122958Sdr146992 
6132958Sdr146992 	hei = (hook_event_int_t *)token;
6142958Sdr146992 	DTRACE_PROBE2(hook__run__start,
6152958Sdr146992 	    hook_event_token_t, token,
6162958Sdr146992 	    hook_data_t, info);
6172958Sdr146992 
6187513SDarren.Reed@Sun.COM 	/*
6197513SDarren.Reed@Sun.COM 	 * Hold global read lock to ensure event will not be deleted.
6207513SDarren.Reed@Sun.COM 	 * While it might be expected that we should also hold a read lock
6217513SDarren.Reed@Sun.COM 	 * on the event lock (hei_lock) to prevent the hook list from
6227513SDarren.Reed@Sun.COM 	 * changing while we're executing this function, both addition
6237513SDarren.Reed@Sun.COM 	 * to and removal from the hook list on the event is done with
6247513SDarren.Reed@Sun.COM 	 * a write lock held on hfi_lock. This is by design so that we
6257513SDarren.Reed@Sun.COM 	 * only need to get one of these locks to process a packet.
6267513SDarren.Reed@Sun.COM 	 * - locking is not a cheap thing to do for every packet.
6277513SDarren.Reed@Sun.COM 	 */
6287513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
6292958Sdr146992 
6302958Sdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
6312958Sdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
6322958Sdr146992 		DTRACE_PROBE3(hook__func__start,
6332958Sdr146992 		    hook_event_token_t, token,
6342958Sdr146992 		    hook_data_t, info,
6352958Sdr146992 		    hook_int_t *, hi);
6367513SDarren.Reed@Sun.COM 		rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
6372958Sdr146992 		DTRACE_PROBE4(hook__func__end,
6382958Sdr146992 		    hook_event_token_t, token,
6392958Sdr146992 		    hook_data_t, info,
6402958Sdr146992 		    hook_int_t *, hi,
6412958Sdr146992 		    int, rval);
6427513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hits.value.ui64++;
6432958Sdr146992 		if (rval != 0)
6442958Sdr146992 			break;
6452958Sdr146992 	}
6462958Sdr146992 
6477513SDarren.Reed@Sun.COM 	hei->hei_kstats.events.value.ui64++;
6487513SDarren.Reed@Sun.COM 
6497513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
6502958Sdr146992 
6512958Sdr146992 	DTRACE_PROBE3(hook__run__end,
6522958Sdr146992 	    hook_event_token_t, token,
6532958Sdr146992 	    hook_data_t, info,
6542958Sdr146992 	    hook_int_t *, hi);
6552958Sdr146992 
6562958Sdr146992 	return (rval);
6572958Sdr146992 }
6582958Sdr146992 
6592958Sdr146992 /*
6602958Sdr146992  * Function:	hook_family_add
6612958Sdr146992  * Returns:	internal family pointer - NULL = Fail
6622958Sdr146992  * Parameters:	hf(I) - family pointer
6632958Sdr146992  *
6642958Sdr146992  * Add new family to family list
6652958Sdr146992  */
6662958Sdr146992 hook_family_int_t *
6673448Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks)
6682958Sdr146992 {
6692958Sdr146992 	hook_family_int_t *hfi, *new;
6702958Sdr146992 
6712958Sdr146992 	ASSERT(hf != NULL);
6722958Sdr146992 	ASSERT(hf->hf_name != NULL);
6732958Sdr146992 
6742958Sdr146992 	new = hook_family_copy(hf);
6752958Sdr146992 	if (new == NULL)
6762958Sdr146992 		return (NULL);
6772958Sdr146992 
6787513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
6797513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
6807513SDarren.Reed@Sun.COM 
6817513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
6827513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
6837513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
6847513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
6857513SDarren.Reed@Sun.COM 		return (NULL);
6867513SDarren.Reed@Sun.COM 	}
6872958Sdr146992 
6882958Sdr146992 	/* search family list */
6893448Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
6902958Sdr146992 	if (hfi != NULL) {
6917513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
6927513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
6937513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
6942958Sdr146992 		return (NULL);
6952958Sdr146992 	}
6962958Sdr146992 
6977513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
6987513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
6997513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
7007513SDarren.Reed@Sun.COM 		mutex_exit(&hook_stack_lock);
7017513SDarren.Reed@Sun.COM 		hook_family_free(new, NULL);
7027513SDarren.Reed@Sun.COM 		return (NULL);
7037513SDarren.Reed@Sun.COM 	}
7047513SDarren.Reed@Sun.COM 
7057513SDarren.Reed@Sun.COM 	CVW_INIT(&new->hfi_lock);
7067513SDarren.Reed@Sun.COM 	SLIST_INIT(&new->hfi_head);
7077513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hfi_nhead);
7087513SDarren.Reed@Sun.COM 
7097513SDarren.Reed@Sun.COM 	hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
7107513SDarren.Reed@Sun.COM 
7117513SDarren.Reed@Sun.COM 	new->hfi_stack = hks;
7123448Sdh155122 
7132958Sdr146992 	/* Add to family list head */
7143448Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
7152958Sdr146992 
7167513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
7177513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
7187513SDarren.Reed@Sun.COM 
7197513SDarren.Reed@Sun.COM 	hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
7207513SDarren.Reed@Sun.COM 
7217513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
7227513SDarren.Reed@Sun.COM 
7232958Sdr146992 	return (new);
7242958Sdr146992 }
7252958Sdr146992 
7262958Sdr146992 /*
7272958Sdr146992  * Function:	hook_family_remove
7282958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
7292958Sdr146992  * Parameters:	hfi(I) - internal family pointer
7302958Sdr146992  *
7317513SDarren.Reed@Sun.COM  * Remove family from family list. This function has been designed to be
7327513SDarren.Reed@Sun.COM  * called once and once only per hook_family_int_t. Thus when cleaning up
7337513SDarren.Reed@Sun.COM  * this structure as an orphan, callers should only call hook_family_free.
7342958Sdr146992  */
7352958Sdr146992 int
7362958Sdr146992 hook_family_remove(hook_family_int_t *hfi)
7372958Sdr146992 {
7383448Sdh155122 	hook_stack_t *hks;
739*7915SDarren.Reed@Sun.COM 	boolean_t notifydone;
7402958Sdr146992 
7412958Sdr146992 	ASSERT(hfi != NULL);
7427513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
7437513SDarren.Reed@Sun.COM 
7447513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
7452958Sdr146992 
7467513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
7477513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
7487513SDarren.Reed@Sun.COM 		/*
7497513SDarren.Reed@Sun.COM 		 * If we're trying to destroy the hook_stack_t...
7507513SDarren.Reed@Sun.COM 		 */
751*7915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
7527513SDarren.Reed@Sun.COM 		return (ENXIO);
7537513SDarren.Reed@Sun.COM 	}
7542958Sdr146992 
7557513SDarren.Reed@Sun.COM 	/*
7567513SDarren.Reed@Sun.COM 	 * Check if the family is in use by the presence of either events
7577513SDarren.Reed@Sun.COM 	 * or notify callbacks on the hook family.
7587513SDarren.Reed@Sun.COM 	 */
7597513SDarren.Reed@Sun.COM 	if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
7607513SDarren.Reed@Sun.COM 		hfi->hfi_condemned = B_TRUE;
7617513SDarren.Reed@Sun.COM 	} else {
7627513SDarren.Reed@Sun.COM 		/*
7637513SDarren.Reed@Sun.COM 		 * Although hfi_condemned = B_FALSE is implied from creation,
7647513SDarren.Reed@Sun.COM 		 * putting a comment here inside the else upsets lint.
7657513SDarren.Reed@Sun.COM 		 */
7667513SDarren.Reed@Sun.COM 		hfi->hfi_condemned = B_FALSE;
7672958Sdr146992 	}
7682958Sdr146992 
7697513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
7707513SDarren.Reed@Sun.COM 	hook_wait_destroy(&hfi->hfi_waiter);
771*7915SDarren.Reed@Sun.COM 	notifydone = hfi->hfi_shutdown;
772*7915SDarren.Reed@Sun.COM 	hfi->hfi_shutdown = B_TRUE;
7737513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
7747513SDarren.Reed@Sun.COM 
7757513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
7767513SDarren.Reed@Sun.COM 
777*7915SDarren.Reed@Sun.COM 	if (!notifydone)
778*7915SDarren.Reed@Sun.COM 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
779*7915SDarren.Reed@Sun.COM 		    HN_UNREGISTER);
7802958Sdr146992 
7817513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
7827513SDarren.Reed@Sun.COM 
7837513SDarren.Reed@Sun.COM 	/*
7847513SDarren.Reed@Sun.COM 	 * If we don't have to wait for anything else to disappear from this
7857513SDarren.Reed@Sun.COM 	 * structure then we can free it up.
7867513SDarren.Reed@Sun.COM 	 */
7877513SDarren.Reed@Sun.COM 	if (!hfi->hfi_condemned)
7887513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hks);
7892958Sdr146992 
7902958Sdr146992 	return (0);
7912958Sdr146992 }
7922958Sdr146992 
7932958Sdr146992 
7942958Sdr146992 /*
7957513SDarren.Reed@Sun.COM  * Function:	hook_family_free
7967513SDarren.Reed@Sun.COM  * Returns:	None
7977513SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
7987513SDarren.Reed@Sun.COM  *
7997513SDarren.Reed@Sun.COM  * Free alloc memory for family
8007513SDarren.Reed@Sun.COM  */
8017513SDarren.Reed@Sun.COM static void
8027513SDarren.Reed@Sun.COM hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
8037513SDarren.Reed@Sun.COM {
8047513SDarren.Reed@Sun.COM 
8057513SDarren.Reed@Sun.COM 	/*
8067513SDarren.Reed@Sun.COM 	 * This lock gives us possession of the hks pointer after the
8077513SDarren.Reed@Sun.COM 	 * SLIST_REMOVE, for which it is not needed, when hks_shutdown
8087513SDarren.Reed@Sun.COM 	 * is checked and hook_stack_remove called.
8097513SDarren.Reed@Sun.COM 	 */
8107513SDarren.Reed@Sun.COM 	mutex_enter(&hook_stack_lock);
8117513SDarren.Reed@Sun.COM 
8127513SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
8137513SDarren.Reed@Sun.COM 
8147513SDarren.Reed@Sun.COM 	if (hks != NULL) {
8157513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(&hks->hks_lock);
8167513SDarren.Reed@Sun.COM 		/* Remove from family list */
8177513SDarren.Reed@Sun.COM 		SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
8187513SDarren.Reed@Sun.COM 		    hfi_entry);
8197513SDarren.Reed@Sun.COM 
8207513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
8217513SDarren.Reed@Sun.COM 	}
8227513SDarren.Reed@Sun.COM 
8237513SDarren.Reed@Sun.COM 	/* Free name space */
8247513SDarren.Reed@Sun.COM 	if (hfi->hfi_family.hf_name != NULL) {
8257513SDarren.Reed@Sun.COM 		kmem_free(hfi->hfi_family.hf_name,
8267513SDarren.Reed@Sun.COM 		    strlen(hfi->hfi_family.hf_name) + 1);
8277513SDarren.Reed@Sun.COM 	}
8287513SDarren.Reed@Sun.COM 
8297513SDarren.Reed@Sun.COM 	/* Free container */
8307513SDarren.Reed@Sun.COM 	kmem_free(hfi, sizeof (*hfi));
8317513SDarren.Reed@Sun.COM 
8327513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown == 2)
8337513SDarren.Reed@Sun.COM 		hook_stack_remove(hks);
8347513SDarren.Reed@Sun.COM 
8357513SDarren.Reed@Sun.COM 	mutex_exit(&hook_stack_lock);
8367513SDarren.Reed@Sun.COM }
8377513SDarren.Reed@Sun.COM 
838*7915SDarren.Reed@Sun.COM /*
839*7915SDarren.Reed@Sun.COM  * Function:	hook_family_shutdown
840*7915SDarren.Reed@Sun.COM  * Returns:	int - 0 = Succ, Else = Fail
841*7915SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
842*7915SDarren.Reed@Sun.COM  *
843*7915SDarren.Reed@Sun.COM  * As an alternative to removing a family, we may desire to just generate
844*7915SDarren.Reed@Sun.COM  * a series of callbacks to indicate that we will be going away in the
845*7915SDarren.Reed@Sun.COM  * future. The hfi_condemned flag isn't set because we aren't trying to
846*7915SDarren.Reed@Sun.COM  * remove the structure.
847*7915SDarren.Reed@Sun.COM  */
848*7915SDarren.Reed@Sun.COM int
849*7915SDarren.Reed@Sun.COM hook_family_shutdown(hook_family_int_t *hfi)
850*7915SDarren.Reed@Sun.COM {
851*7915SDarren.Reed@Sun.COM 	hook_stack_t *hks;
852*7915SDarren.Reed@Sun.COM 	boolean_t notifydone;
853*7915SDarren.Reed@Sun.COM 
854*7915SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
855*7915SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
856*7915SDarren.Reed@Sun.COM 
857*7915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hks->hks_lock);
858*7915SDarren.Reed@Sun.COM 
859*7915SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK,
860*7915SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
861*7915SDarren.Reed@Sun.COM 		/*
862*7915SDarren.Reed@Sun.COM 		 * If we're trying to destroy the hook_stack_t...
863*7915SDarren.Reed@Sun.COM 		 */
864*7915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hks->hks_lock);
865*7915SDarren.Reed@Sun.COM 		return (ENXIO);
866*7915SDarren.Reed@Sun.COM 	}
867*7915SDarren.Reed@Sun.COM 
868*7915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
869*7915SDarren.Reed@Sun.COM 	notifydone = hfi->hfi_shutdown;
870*7915SDarren.Reed@Sun.COM 	hfi->hfi_shutdown = B_TRUE;
871*7915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
872*7915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hks->hks_lock);
873*7915SDarren.Reed@Sun.COM 
874*7915SDarren.Reed@Sun.COM 	if (!notifydone)
875*7915SDarren.Reed@Sun.COM 		hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
876*7915SDarren.Reed@Sun.COM 		    HN_UNREGISTER);
877*7915SDarren.Reed@Sun.COM 
878*7915SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
879*7915SDarren.Reed@Sun.COM 
880*7915SDarren.Reed@Sun.COM 	return (0);
881*7915SDarren.Reed@Sun.COM }
8827513SDarren.Reed@Sun.COM 
8837513SDarren.Reed@Sun.COM /*
8842958Sdr146992  * Function:	hook_family_copy
8852958Sdr146992  * Returns:	internal family pointer - NULL = Failed
8862958Sdr146992  * Parameters:	src(I) - family pointer
8872958Sdr146992  *
8882958Sdr146992  * Allocate internal family block and duplicate incoming family
8892958Sdr146992  * No locks should be held across this function as it may sleep.
8902958Sdr146992  */
8912958Sdr146992 static hook_family_int_t *
8922958Sdr146992 hook_family_copy(hook_family_t *src)
8932958Sdr146992 {
8942958Sdr146992 	hook_family_int_t *new;
8952958Sdr146992 	hook_family_t *dst;
8962958Sdr146992 
8972958Sdr146992 	ASSERT(src != NULL);
8982958Sdr146992 	ASSERT(src->hf_name != NULL);
8992958Sdr146992 
9002958Sdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
9012958Sdr146992 
9022958Sdr146992 	/* Copy body */
9032958Sdr146992 	dst = &new->hfi_family;
9042958Sdr146992 	*dst = *src;
9052958Sdr146992 
9067513SDarren.Reed@Sun.COM 	SLIST_INIT(&new->hfi_head);
9077513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hfi_nhead);
9087513SDarren.Reed@Sun.COM 
9092958Sdr146992 	/* Copy name */
9102958Sdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
9112958Sdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
9122958Sdr146992 
9132958Sdr146992 	return (new);
9142958Sdr146992 }
9152958Sdr146992 
9162958Sdr146992 /*
9177513SDarren.Reed@Sun.COM  * Function:	hook_family_find
9182958Sdr146992  * Returns:	internal family pointer - NULL = Not match
9192958Sdr146992  * Parameters:	family(I) - family name string
9202958Sdr146992  *
9212958Sdr146992  * Search family list with family name
9227513SDarren.Reed@Sun.COM  * 	A lock on hfi_lock must be held when called.
9232958Sdr146992  */
9242958Sdr146992 static hook_family_int_t *
9253448Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
9262958Sdr146992 {
9272958Sdr146992 	hook_family_int_t *hfi = NULL;
9282958Sdr146992 
9292958Sdr146992 	ASSERT(family != NULL);
9302958Sdr146992 
9313448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
9322958Sdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
9332958Sdr146992 			break;
9342958Sdr146992 	}
9352958Sdr146992 	return (hfi);
9362958Sdr146992 }
9372958Sdr146992 
9387513SDarren.Reed@Sun.COM /*
9397513SDarren.Reed@Sun.COM  * Function:	hook_family_notify_register
9407513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
9417513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
9427513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
9437513SDarren.Reed@Sun.COM  *              arg(I)      - arg to provide callback when it is called
9447513SDarren.Reed@Sun.COM  *
9457513SDarren.Reed@Sun.COM  * So long as this hook stack isn't being shut down, register a new
9467513SDarren.Reed@Sun.COM  * callback to be activated each time a new event is added to this
9477513SDarren.Reed@Sun.COM  * family.
9487513SDarren.Reed@Sun.COM  *
9497513SDarren.Reed@Sun.COM  * To call this function we must have an active handle in use on the family,
9507513SDarren.Reed@Sun.COM  * so if we take this into account, then neither the hook_family_int_t nor
9517513SDarren.Reed@Sun.COM  * the hook_stack_t that owns it can disappear. We have to put some trust
9527513SDarren.Reed@Sun.COM  * in the callers to be properly synchronised...
9537513SDarren.Reed@Sun.COM  *
9547513SDarren.Reed@Sun.COM  * Holding hks_lock is required to provide synchronisation for hks_shutdown.
9557513SDarren.Reed@Sun.COM  */
9567513SDarren.Reed@Sun.COM int
9577513SDarren.Reed@Sun.COM hook_family_notify_register(hook_family_int_t *hfi,
9587513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg)
9597513SDarren.Reed@Sun.COM {
9607513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
9617513SDarren.Reed@Sun.COM 	int error;
9627513SDarren.Reed@Sun.COM 
9637513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
9647513SDarren.Reed@Sun.COM 
9657513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
9667513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
9677513SDarren.Reed@Sun.COM 
968*7915SDarren.Reed@Sun.COM 	if ((hfi->hfi_stack->hks_shutdown != 0) ||
969*7915SDarren.Reed@Sun.COM 	    hfi->hfi_condemned || hfi->hfi_shutdown) {
9707513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
9717513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
9727513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
9737513SDarren.Reed@Sun.COM 	}
9747513SDarren.Reed@Sun.COM 
9757513SDarren.Reed@Sun.COM 	error = hook_notify_register(&hfi->hfi_lock, &hfi->hfi_nhead,
9767513SDarren.Reed@Sun.COM 	    callback, arg);
9777513SDarren.Reed@Sun.COM 
9787513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
9797513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
9807513SDarren.Reed@Sun.COM 
9817513SDarren.Reed@Sun.COM 	return (error);
9827513SDarren.Reed@Sun.COM }
9832958Sdr146992 
9842958Sdr146992 /*
9857513SDarren.Reed@Sun.COM  * Function:	hook_family_notify_unregister
9867513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
9877513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
9887513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
9892958Sdr146992  *
9907513SDarren.Reed@Sun.COM  * Remove a callback from the list of those executed when a new event is
9917513SDarren.Reed@Sun.COM  * added to a hook family.
9922958Sdr146992  */
9937513SDarren.Reed@Sun.COM int
9947513SDarren.Reed@Sun.COM hook_family_notify_unregister(hook_family_int_t *hfi,
9957513SDarren.Reed@Sun.COM     hook_notify_fn_t callback)
9962958Sdr146992 {
9977513SDarren.Reed@Sun.COM 	boolean_t free_family;
9987513SDarren.Reed@Sun.COM 	int error;
9997513SDarren.Reed@Sun.COM 
10007513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
10012958Sdr146992 
10027513SDarren.Reed@Sun.COM 	error = hook_notify_unregister(&hfi->hfi_lock, &hfi->hfi_nhead,
10037513SDarren.Reed@Sun.COM 	    callback);
10047513SDarren.Reed@Sun.COM 
10057513SDarren.Reed@Sun.COM 	/*
10067513SDarren.Reed@Sun.COM 	 * If hook_family_remove has been called but the structure was still
10077513SDarren.Reed@Sun.COM 	 * "busy" ... but we might have just made it "unbusy"...
10087513SDarren.Reed@Sun.COM 	 */
10097513SDarren.Reed@Sun.COM 	if ((error == 0) && hfi->hfi_condemned &&
10107513SDarren.Reed@Sun.COM 	    SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
10117513SDarren.Reed@Sun.COM 		free_family = B_TRUE;
10127513SDarren.Reed@Sun.COM 	} else {
10137513SDarren.Reed@Sun.COM 		free_family = B_FALSE;
10142958Sdr146992 	}
10152958Sdr146992 
10167513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
10177513SDarren.Reed@Sun.COM 
10187513SDarren.Reed@Sun.COM 	if (free_family)
10197513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hfi->hfi_stack);
10207513SDarren.Reed@Sun.COM 
10217513SDarren.Reed@Sun.COM 	return (error);
10222958Sdr146992 }
10232958Sdr146992 
10242958Sdr146992 /*
10252958Sdr146992  * Function:	hook_event_add
10262958Sdr146992  * Returns:	internal event pointer - NULL = Fail
10272958Sdr146992  * Parameters:	hfi(I) - internal family pointer
10282958Sdr146992  *		he(I) - event pointer
10292958Sdr146992  *
10302958Sdr146992  * Add new event to event list on specific family.
10312958Sdr146992  * This function can fail to return successfully if (1) it cannot allocate
10322958Sdr146992  * enough memory for its own internal data structures, (2) the event has
10332958Sdr146992  * already been registered (for any hook family.)
10342958Sdr146992  */
10352958Sdr146992 hook_event_int_t *
10362958Sdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
10372958Sdr146992 {
10387513SDarren.Reed@Sun.COM 	hook_event_int_t *hei, *new;
10393448Sdh155122 	hook_stack_t *hks;
10402958Sdr146992 
10412958Sdr146992 	ASSERT(hfi != NULL);
10422958Sdr146992 	ASSERT(he != NULL);
10432958Sdr146992 	ASSERT(he->he_name != NULL);
10442958Sdr146992 
10452958Sdr146992 	new = hook_event_copy(he);
10462958Sdr146992 	if (new == NULL)
10472958Sdr146992 		return (NULL);
10482958Sdr146992 
10497513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
10507513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
10517513SDarren.Reed@Sun.COM 
10527513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
10537513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
10547513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
10557513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
10567513SDarren.Reed@Sun.COM 		return (NULL);
10577513SDarren.Reed@Sun.COM 	}
10582958Sdr146992 
10592958Sdr146992 	/* Check whether this event pointer is already registered */
10603448Sdh155122 	hei = hook_event_checkdup(he, hks);
10612958Sdr146992 	if (hei != NULL) {
10627513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
10637513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
10647513SDarren.Reed@Sun.COM 		return (NULL);
10657513SDarren.Reed@Sun.COM 	}
10667513SDarren.Reed@Sun.COM 
10677513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
10687513SDarren.Reed@Sun.COM 
1069*7915SDarren.Reed@Sun.COM 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
10707513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
10717513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
10727513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
10732958Sdr146992 		return (NULL);
10742958Sdr146992 	}
10752958Sdr146992 
10767513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
10777513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
10787513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
10797513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
10807513SDarren.Reed@Sun.COM 		hook_event_free(new, NULL);
10817513SDarren.Reed@Sun.COM 		return (NULL);
10827513SDarren.Reed@Sun.COM 	}
10837513SDarren.Reed@Sun.COM 
10847513SDarren.Reed@Sun.COM 	TAILQ_INIT(&new->hei_nhead);
10857513SDarren.Reed@Sun.COM 
10867513SDarren.Reed@Sun.COM 	hook_event_init_kstats(hfi, new);
10877513SDarren.Reed@Sun.COM 	hook_wait_init(&new->hei_waiter, &new->hei_lock);
10887513SDarren.Reed@Sun.COM 
10892958Sdr146992 	/* Add to event list head */
10902958Sdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
10912958Sdr146992 
10927513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
10937513SDarren.Reed@Sun.COM 
10947513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
10957513SDarren.Reed@Sun.COM 
10967513SDarren.Reed@Sun.COM 	hook_notify_run(&hfi->hfi_nhead,
10977513SDarren.Reed@Sun.COM 	    hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
10987513SDarren.Reed@Sun.COM 
10997513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
11007513SDarren.Reed@Sun.COM 
11012958Sdr146992 	return (new);
11022958Sdr146992 }
11032958Sdr146992 
11047513SDarren.Reed@Sun.COM /*
11057513SDarren.Reed@Sun.COM  * Function:	hook_event_init_kstats
11067513SDarren.Reed@Sun.COM  * Returns:	None
11077513SDarren.Reed@Sun.COM  * Parameters:  hfi(I) - pointer to the family that owns this event.
11087513SDarren.Reed@Sun.COM  *              hei(I) - pointer to the hook event that needs some kstats.
11097513SDarren.Reed@Sun.COM  *
11107513SDarren.Reed@Sun.COM  * Create a set of kstats that relate to each event registered with
11117513SDarren.Reed@Sun.COM  * the hook framework.  A counter is kept for each time the event is
11127513SDarren.Reed@Sun.COM  * activated and for each time a hook is added or removed.  As the
11137513SDarren.Reed@Sun.COM  * kstats just count the events as they happen, the total number of
11147513SDarren.Reed@Sun.COM  * hooks registered must be obtained by subtractived removed from added.
11157513SDarren.Reed@Sun.COM  */
11167513SDarren.Reed@Sun.COM static void
11177513SDarren.Reed@Sun.COM hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
11187513SDarren.Reed@Sun.COM {
11197513SDarren.Reed@Sun.COM 	hook_event_kstat_t template = {
11207513SDarren.Reed@Sun.COM 		{ "hooksAdded",		KSTAT_DATA_UINT64 },
11217513SDarren.Reed@Sun.COM 		{ "hooksRemoved",	KSTAT_DATA_UINT64 },
11227513SDarren.Reed@Sun.COM 		{ "events",		KSTAT_DATA_UINT64 }
11237513SDarren.Reed@Sun.COM 	};
11247513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
11257513SDarren.Reed@Sun.COM 
11267513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
11277513SDarren.Reed@Sun.COM 	hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
11287513SDarren.Reed@Sun.COM 	    hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
11297513SDarren.Reed@Sun.COM 	    sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
11307513SDarren.Reed@Sun.COM 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
11317513SDarren.Reed@Sun.COM 
11327513SDarren.Reed@Sun.COM 	bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
11337513SDarren.Reed@Sun.COM 
11347513SDarren.Reed@Sun.COM 	if (hei->hei_kstatp != NULL) {
11357513SDarren.Reed@Sun.COM 		hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
11367513SDarren.Reed@Sun.COM 		hei->hei_kstatp->ks_private =
11377513SDarren.Reed@Sun.COM 		    (void *)(uintptr_t)hks->hks_netstackid;
11387513SDarren.Reed@Sun.COM 
11397513SDarren.Reed@Sun.COM 		kstat_install(hei->hei_kstatp);
11407513SDarren.Reed@Sun.COM 	}
11417513SDarren.Reed@Sun.COM }
11422958Sdr146992 
11432958Sdr146992 /*
11442958Sdr146992  * Function:	hook_event_remove
11452958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
11462958Sdr146992  * Parameters:	hfi(I) - internal family pointer
11472958Sdr146992  *		he(I) - event pointer
11482958Sdr146992  *
11492958Sdr146992  * Remove event from event list on specific family
11507513SDarren.Reed@Sun.COM  *
11517513SDarren.Reed@Sun.COM  * This function assumes that the caller has received a pointer to a the
11527513SDarren.Reed@Sun.COM  * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
11537513SDarren.Reed@Sun.COM  * This the hook_family_int_t is guaranteed to be around for the life of this
11547513SDarren.Reed@Sun.COM  * call, unless the caller has decided to call net_protocol_release or
11557513SDarren.Reed@Sun.COM  * net_protocol_unregister before calling net_event_unregister - an error.
11562958Sdr146992  */
11572958Sdr146992 int
11582958Sdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
11592958Sdr146992 {
11607513SDarren.Reed@Sun.COM 	boolean_t free_family;
11612958Sdr146992 	hook_event_int_t *hei;
1162*7915SDarren.Reed@Sun.COM 	boolean_t notifydone;
11632958Sdr146992 
11642958Sdr146992 	ASSERT(hfi != NULL);
11652958Sdr146992 	ASSERT(he != NULL);
11662958Sdr146992 
11677513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
11682958Sdr146992 
11697513SDarren.Reed@Sun.COM 	/*
11707513SDarren.Reed@Sun.COM 	 * Set the flag so that we can call hook_event_notify_run without
11717513SDarren.Reed@Sun.COM 	 * holding any locks but at the same time prevent other changes to
11727513SDarren.Reed@Sun.COM 	 * the event at the same time.
11737513SDarren.Reed@Sun.COM 	 */
11747513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
11757513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
11767513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
11772958Sdr146992 		return (ENXIO);
11782958Sdr146992 	}
11792958Sdr146992 
11807513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, he->he_name);
11817513SDarren.Reed@Sun.COM 	if (hei == NULL) {
11827513SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
11837513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
11847513SDarren.Reed@Sun.COM 		return (ESRCH);
11852958Sdr146992 	}
11862958Sdr146992 
11877513SDarren.Reed@Sun.COM 	free_family = B_FALSE;
11887513SDarren.Reed@Sun.COM 
11897513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
11907513SDarren.Reed@Sun.COM 	/*
1191*7915SDarren.Reed@Sun.COM 	 * The hei_shutdown flag is used to indicate whether or not we have
1192*7915SDarren.Reed@Sun.COM 	 * done a shutdown and thus already walked through the notify list.
1193*7915SDarren.Reed@Sun.COM 	 */
1194*7915SDarren.Reed@Sun.COM 	notifydone = hei->hei_shutdown;
1195*7915SDarren.Reed@Sun.COM 	hei->hei_shutdown = B_TRUE;
1196*7915SDarren.Reed@Sun.COM 	/*
11977513SDarren.Reed@Sun.COM 	 * If there are any hooks still registered for this event or
11987513SDarren.Reed@Sun.COM 	 * there are any notifiers registered, return an error indicating
11997513SDarren.Reed@Sun.COM 	 * that the event is still busy.
12007513SDarren.Reed@Sun.COM 	 */
12017513SDarren.Reed@Sun.COM 	if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
12027513SDarren.Reed@Sun.COM 		hei->hei_condemned = B_TRUE;
12037513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
12047513SDarren.Reed@Sun.COM 	} else {
12057513SDarren.Reed@Sun.COM 		/* hei_condemned = B_FALSE is implied from creation */
12067513SDarren.Reed@Sun.COM 		/*
12077513SDarren.Reed@Sun.COM 		 * Even though we know the notify list is empty, we call
12087513SDarren.Reed@Sun.COM 		 * hook_wait_destroy here to synchronise wait removing a
12097513SDarren.Reed@Sun.COM 		 * hook from an event.
12107513SDarren.Reed@Sun.COM 		 */
12117513SDarren.Reed@Sun.COM 		hook_wait_destroy(&hei->hei_waiter);
12122958Sdr146992 
12137513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
12147513SDarren.Reed@Sun.COM 
12157513SDarren.Reed@Sun.COM 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
12167513SDarren.Reed@Sun.COM 		    TAILQ_EMPTY(&hfi->hfi_nhead))
12177513SDarren.Reed@Sun.COM 			free_family = B_TRUE;
12187513SDarren.Reed@Sun.COM 	}
12197513SDarren.Reed@Sun.COM 
12207513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
12217513SDarren.Reed@Sun.COM 
1222*7915SDarren.Reed@Sun.COM 	if (!notifydone)
1223*7915SDarren.Reed@Sun.COM 		hook_notify_run(&hfi->hfi_nhead,
1224*7915SDarren.Reed@Sun.COM 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
12257513SDarren.Reed@Sun.COM 
12267513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
12277513SDarren.Reed@Sun.COM 
12287513SDarren.Reed@Sun.COM 	if (!hei->hei_condemned) {
12297513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
12307513SDarren.Reed@Sun.COM 		if (free_family)
12317513SDarren.Reed@Sun.COM 			hook_family_free(hfi, hfi->hfi_stack);
12327513SDarren.Reed@Sun.COM 	}
12332958Sdr146992 
12342958Sdr146992 	return (0);
12352958Sdr146992 }
12362958Sdr146992 
12377513SDarren.Reed@Sun.COM /*
1238*7915SDarren.Reed@Sun.COM  * Function:	hook_event_shutdown
1239*7915SDarren.Reed@Sun.COM  * Returns:	int - 0 = Succ, Else = Fail
1240*7915SDarren.Reed@Sun.COM  * Parameters:	hfi(I) - internal family pointer
1241*7915SDarren.Reed@Sun.COM  *		he(I)  - event pointer
1242*7915SDarren.Reed@Sun.COM  *
1243*7915SDarren.Reed@Sun.COM  * As with hook_family_shutdown, we want to generate the notify callbacks
1244*7915SDarren.Reed@Sun.COM  * as if the event was being removed but not actually do the remove.
1245*7915SDarren.Reed@Sun.COM  */
1246*7915SDarren.Reed@Sun.COM int
1247*7915SDarren.Reed@Sun.COM hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he)
1248*7915SDarren.Reed@Sun.COM {
1249*7915SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
1250*7915SDarren.Reed@Sun.COM 	boolean_t notifydone;
1251*7915SDarren.Reed@Sun.COM 
1252*7915SDarren.Reed@Sun.COM 	ASSERT(hfi != NULL);
1253*7915SDarren.Reed@Sun.COM 	ASSERT(he != NULL);
1254*7915SDarren.Reed@Sun.COM 
1255*7915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
1256*7915SDarren.Reed@Sun.COM 
1257*7915SDarren.Reed@Sun.COM 	/*
1258*7915SDarren.Reed@Sun.COM 	 * Set the flag so that we can call hook_event_notify_run without
1259*7915SDarren.Reed@Sun.COM 	 * holding any locks but at the same time prevent other changes to
1260*7915SDarren.Reed@Sun.COM 	 * the event at the same time.
1261*7915SDarren.Reed@Sun.COM 	 */
1262*7915SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK,
1263*7915SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
1264*7915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1265*7915SDarren.Reed@Sun.COM 		return (ENXIO);
1266*7915SDarren.Reed@Sun.COM 	}
1267*7915SDarren.Reed@Sun.COM 
1268*7915SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, he->he_name);
1269*7915SDarren.Reed@Sun.COM 	if (hei == NULL) {
1270*7915SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1271*7915SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
1272*7915SDarren.Reed@Sun.COM 		return (ESRCH);
1273*7915SDarren.Reed@Sun.COM 	}
1274*7915SDarren.Reed@Sun.COM 
1275*7915SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
1276*7915SDarren.Reed@Sun.COM 	notifydone = hei->hei_shutdown;
1277*7915SDarren.Reed@Sun.COM 	hei->hei_shutdown = B_TRUE;
1278*7915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
1279*7915SDarren.Reed@Sun.COM 
1280*7915SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
1281*7915SDarren.Reed@Sun.COM 
1282*7915SDarren.Reed@Sun.COM 	if (!notifydone)
1283*7915SDarren.Reed@Sun.COM 		hook_notify_run(&hfi->hfi_nhead,
1284*7915SDarren.Reed@Sun.COM 		    hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
1285*7915SDarren.Reed@Sun.COM 
1286*7915SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
1287*7915SDarren.Reed@Sun.COM 
1288*7915SDarren.Reed@Sun.COM 	return (0);
1289*7915SDarren.Reed@Sun.COM }
1290*7915SDarren.Reed@Sun.COM 
1291*7915SDarren.Reed@Sun.COM /*
12927513SDarren.Reed@Sun.COM  * Function:	hook_event_free
12937513SDarren.Reed@Sun.COM  * Returns:	None
12947513SDarren.Reed@Sun.COM  * Parameters:	hei(I) - internal event pointer
12957513SDarren.Reed@Sun.COM  *
12967513SDarren.Reed@Sun.COM  * Free alloc memory for event
12977513SDarren.Reed@Sun.COM  */
12987513SDarren.Reed@Sun.COM static void
12997513SDarren.Reed@Sun.COM hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
13007513SDarren.Reed@Sun.COM {
13017513SDarren.Reed@Sun.COM 	boolean_t free_family;
13027513SDarren.Reed@Sun.COM 
13037513SDarren.Reed@Sun.COM 	ASSERT(hei != NULL);
13047513SDarren.Reed@Sun.COM 
13057513SDarren.Reed@Sun.COM 	if (hfi != NULL) {
13067513SDarren.Reed@Sun.COM 		CVW_ENTER_WRITE(&hfi->hfi_lock);
13077513SDarren.Reed@Sun.COM 		/*
13087513SDarren.Reed@Sun.COM 		 * Remove the event from the hook family's list.
13097513SDarren.Reed@Sun.COM 		 */
13107513SDarren.Reed@Sun.COM 		SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
13117513SDarren.Reed@Sun.COM 		if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
13127513SDarren.Reed@Sun.COM 		    TAILQ_EMPTY(&hfi->hfi_nhead)) {
13137513SDarren.Reed@Sun.COM 			free_family = B_TRUE;
13147513SDarren.Reed@Sun.COM 		} else {
13157513SDarren.Reed@Sun.COM 			free_family = B_FALSE;
13167513SDarren.Reed@Sun.COM 		}
13177513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
13187513SDarren.Reed@Sun.COM 	}
13197513SDarren.Reed@Sun.COM 
13207513SDarren.Reed@Sun.COM 	if (hei->hei_kstatp != NULL) {
13217513SDarren.Reed@Sun.COM 		ASSERT(hfi != NULL);
13227513SDarren.Reed@Sun.COM 
13237513SDarren.Reed@Sun.COM 		kstat_delete_netstack(hei->hei_kstatp,
13247513SDarren.Reed@Sun.COM 		    hfi->hfi_stack->hks_netstackid);
13257513SDarren.Reed@Sun.COM 		hei->hei_kstatp = NULL;
13267513SDarren.Reed@Sun.COM 	}
13277513SDarren.Reed@Sun.COM 
13287513SDarren.Reed@Sun.COM 	/* Free container */
13297513SDarren.Reed@Sun.COM 	kmem_free(hei, sizeof (*hei));
13307513SDarren.Reed@Sun.COM 
13317513SDarren.Reed@Sun.COM 	if (free_family)
13327513SDarren.Reed@Sun.COM 		hook_family_free(hfi, hfi->hfi_stack);
13337513SDarren.Reed@Sun.COM }
13342958Sdr146992 
13352958Sdr146992 /*
13362958Sdr146992  * Function:    hook_event_checkdup
13372958Sdr146992  * Returns:     internal event pointer - NULL = Not match
13382958Sdr146992  * Parameters:  he(I) - event pointer
13392958Sdr146992  *
13407513SDarren.Reed@Sun.COM  * Search all of the hook families to see if the event being passed in
13417513SDarren.Reed@Sun.COM  * has already been associated with one.
13422958Sdr146992  */
13432958Sdr146992 static hook_event_int_t *
13443448Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
13452958Sdr146992 {
13462958Sdr146992 	hook_family_int_t *hfi;
13472958Sdr146992 	hook_event_int_t *hei;
13482958Sdr146992 
13492958Sdr146992 	ASSERT(he != NULL);
13502958Sdr146992 
13517513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
13523448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
13532958Sdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
13547513SDarren.Reed@Sun.COM 			if (hei->hei_event == he) {
13557513SDarren.Reed@Sun.COM 				CVW_EXIT_READ(&hks->hks_lock);
13562958Sdr146992 				return (hei);
13577513SDarren.Reed@Sun.COM 			}
13582958Sdr146992 		}
13592958Sdr146992 	}
13607513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
13612958Sdr146992 
13622958Sdr146992 	return (NULL);
13632958Sdr146992 }
13642958Sdr146992 
13652958Sdr146992 /*
13662958Sdr146992  * Function:	hook_event_copy
13672958Sdr146992  * Returns:	internal event pointer - NULL = Failed
13682958Sdr146992  * Parameters:	src(I) - event pointer
13692958Sdr146992  *
13702958Sdr146992  * Allocate internal event block and duplicate incoming event
13712958Sdr146992  * No locks should be held across this function as it may sleep.
13722958Sdr146992  */
13732958Sdr146992 static hook_event_int_t *
13742958Sdr146992 hook_event_copy(hook_event_t *src)
13752958Sdr146992 {
13762958Sdr146992 	hook_event_int_t *new;
13772958Sdr146992 
13782958Sdr146992 	ASSERT(src != NULL);
13792958Sdr146992 	ASSERT(src->he_name != NULL);
13802958Sdr146992 
13812958Sdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
13822958Sdr146992 
13832958Sdr146992 	/* Copy body */
13842958Sdr146992 	TAILQ_INIT(&new->hei_head);
13852958Sdr146992 	new->hei_event = src;
13862958Sdr146992 
13872958Sdr146992 	return (new);
13882958Sdr146992 }
13892958Sdr146992 
13902958Sdr146992 /*
13912958Sdr146992  * Function:	hook_event_find
13922958Sdr146992  * Returns:	internal event pointer - NULL = Not match
13932958Sdr146992  * Parameters:	hfi(I) - internal family pointer
13942958Sdr146992  *		event(I) - event name string
13952958Sdr146992  *
13962958Sdr146992  * Search event list with event name
13977513SDarren.Reed@Sun.COM  * 	A lock on hfi->hfi_lock must be held when called.
13982958Sdr146992  */
13992958Sdr146992 static hook_event_int_t *
14002958Sdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
14012958Sdr146992 {
14022958Sdr146992 	hook_event_int_t *hei = NULL;
14032958Sdr146992 
14042958Sdr146992 	ASSERT(hfi != NULL);
14052958Sdr146992 	ASSERT(event != NULL);
14062958Sdr146992 
14072958Sdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
14087513SDarren.Reed@Sun.COM 		if ((strcmp(hei->hei_event->he_name, event) == 0) &&
14097513SDarren.Reed@Sun.COM 		    ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
14102958Sdr146992 			break;
14112958Sdr146992 	}
14122958Sdr146992 	return (hei);
14132958Sdr146992 }
14142958Sdr146992 
14157513SDarren.Reed@Sun.COM /*
14167513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_register
14177513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
14187513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
14197513SDarren.Reed@Sun.COM  *              event(I)    - name of the event
14207513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
14217513SDarren.Reed@Sun.COM  *              arg(I)      - arg to provide callback when it is called
14227513SDarren.Reed@Sun.COM  *
14237513SDarren.Reed@Sun.COM  * Adds a new callback to the event named by "event" (we must find it)
14247513SDarren.Reed@Sun.COM  * that will be executed each time a new hook is added to the event.
14257513SDarren.Reed@Sun.COM  * Of course, if the stack is being shut down, this call should fail.
14267513SDarren.Reed@Sun.COM  */
14277513SDarren.Reed@Sun.COM int
14287513SDarren.Reed@Sun.COM hook_event_notify_register(hook_family_int_t *hfi, char *event,
14297513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg)
14307513SDarren.Reed@Sun.COM {
14317513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
14327513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
14337513SDarren.Reed@Sun.COM 	int error;
14347513SDarren.Reed@Sun.COM 
14357513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
14367513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hks->hks_lock);
14377513SDarren.Reed@Sun.COM 	if (hks->hks_shutdown != 0) {
14387513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
14397513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
14407513SDarren.Reed@Sun.COM 	}
14417513SDarren.Reed@Sun.COM 
14427513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
14437513SDarren.Reed@Sun.COM 
1444*7915SDarren.Reed@Sun.COM 	if (hfi->hfi_condemned || hfi->hfi_shutdown) {
14457513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
14467513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
14477513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
14487513SDarren.Reed@Sun.COM 	}
14497513SDarren.Reed@Sun.COM 
14507513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, event);
14517513SDarren.Reed@Sun.COM 	if (hei == NULL) {
14527513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
14537513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
14547513SDarren.Reed@Sun.COM 		return (ESRCH);
14557513SDarren.Reed@Sun.COM 	}
14567513SDarren.Reed@Sun.COM 
14577513SDarren.Reed@Sun.COM 	/*
14587513SDarren.Reed@Sun.COM 	 * Grabbing the read lock on hei_lock is only so that we can
14597513SDarren.Reed@Sun.COM 	 * synchronise access to hei_condemned.
14607513SDarren.Reed@Sun.COM 	 */
14617513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
1462*7915SDarren.Reed@Sun.COM 	if (hei->hei_condemned || hei->hei_shutdown) {
14637513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
14647513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
14657513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hks->hks_lock);
14667513SDarren.Reed@Sun.COM 		return (ESHUTDOWN);
14677513SDarren.Reed@Sun.COM 	}
14687513SDarren.Reed@Sun.COM 
14697513SDarren.Reed@Sun.COM 	error = hook_notify_register(&hei->hei_lock, &hei->hei_nhead,
14707513SDarren.Reed@Sun.COM 	    callback, arg);
14717513SDarren.Reed@Sun.COM 
14727513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
14737513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
14747513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hks->hks_lock);
14757513SDarren.Reed@Sun.COM 
14767513SDarren.Reed@Sun.COM 	return (error);
14777513SDarren.Reed@Sun.COM }
14782958Sdr146992 
14792958Sdr146992 /*
14807513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_unregister
14817513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
14827513SDarren.Reed@Sun.COM  * Parameters:	hfi(I)      - hook family
14837513SDarren.Reed@Sun.COM  *              event(I)    - name of the event
14847513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
14857513SDarren.Reed@Sun.COM  *
14867513SDarren.Reed@Sun.COM  * Remove the given callback from the named event's list of functions
14877513SDarren.Reed@Sun.COM  * to call when a hook is added or removed.
14887513SDarren.Reed@Sun.COM  */
14897513SDarren.Reed@Sun.COM int
14907513SDarren.Reed@Sun.COM hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
14917513SDarren.Reed@Sun.COM     hook_notify_fn_t callback)
14927513SDarren.Reed@Sun.COM {
14937513SDarren.Reed@Sun.COM 	hook_event_int_t *hei;
14947513SDarren.Reed@Sun.COM 	boolean_t free_event;
14957513SDarren.Reed@Sun.COM 	int error;
14967513SDarren.Reed@Sun.COM 
14977513SDarren.Reed@Sun.COM 	CVW_ENTER_READ(&hfi->hfi_lock);
14987513SDarren.Reed@Sun.COM 
14997513SDarren.Reed@Sun.COM 	hei = hook_event_find(hfi, event);
15007513SDarren.Reed@Sun.COM 	if (hei == NULL) {
15017513SDarren.Reed@Sun.COM 		CVW_EXIT_READ(&hfi->hfi_lock);
15027513SDarren.Reed@Sun.COM 		return (ESRCH);
15037513SDarren.Reed@Sun.COM 	}
15047513SDarren.Reed@Sun.COM 
15057513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hei->hei_lock);
15067513SDarren.Reed@Sun.COM 
15077513SDarren.Reed@Sun.COM 	error = hook_notify_unregister(&hei->hei_lock, &hei->hei_nhead,
15087513SDarren.Reed@Sun.COM 	    callback);
15097513SDarren.Reed@Sun.COM 
15107513SDarren.Reed@Sun.COM 	/*
15117513SDarren.Reed@Sun.COM 	 * hei_condemned has been set if someone tried to remove the
15127513SDarren.Reed@Sun.COM 	 * event but couldn't because there were still things attached to
15137513SDarren.Reed@Sun.COM 	 * it. Now that we've done a successful remove, if it is now empty
15147513SDarren.Reed@Sun.COM 	 * then by all rights we should be free'ing it too.  Note that the
15157513SDarren.Reed@Sun.COM 	 * expectation is that only the caller of hook_event_add will ever
15167513SDarren.Reed@Sun.COM 	 * call hook_event_remove.
15177513SDarren.Reed@Sun.COM 	 */
15187513SDarren.Reed@Sun.COM 	if ((error == 0) && hei->hei_condemned &&
15197513SDarren.Reed@Sun.COM 	    TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
15207513SDarren.Reed@Sun.COM 		free_event = B_TRUE;
15217513SDarren.Reed@Sun.COM 	} else {
15227513SDarren.Reed@Sun.COM 		free_event = B_FALSE;
15237513SDarren.Reed@Sun.COM 	}
15247513SDarren.Reed@Sun.COM 
15257513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hei->hei_lock);
15267513SDarren.Reed@Sun.COM 	CVW_EXIT_READ(&hfi->hfi_lock);
15277513SDarren.Reed@Sun.COM 
15287513SDarren.Reed@Sun.COM 	if (free_event) {
15297513SDarren.Reed@Sun.COM 		/*
15307513SDarren.Reed@Sun.COM 		 * It is safe to pass in hfi here, without a lock, because
15317513SDarren.Reed@Sun.COM 		 * our structure (hei) is still on one of its lists and thus
15327513SDarren.Reed@Sun.COM 		 * it won't be able to disappear yet...
15337513SDarren.Reed@Sun.COM 		 */
15347513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
15357513SDarren.Reed@Sun.COM 	}
15367513SDarren.Reed@Sun.COM 
15377513SDarren.Reed@Sun.COM 	return (error);
15387513SDarren.Reed@Sun.COM }
15397513SDarren.Reed@Sun.COM 
15407513SDarren.Reed@Sun.COM /*
15417513SDarren.Reed@Sun.COM  * Function:	hook_event_notify_run
15422958Sdr146992  * Returns:	None
15437513SDarren.Reed@Sun.COM  * Parameters:	nrun(I) - pointer to the list of callbacks to execute
15447513SDarren.Reed@Sun.COM  *              hfi(I)  - hook stack pointer to execute callbacks for
15457513SDarren.Reed@Sun.COM  *              name(I) - name of a hook family
15467513SDarren.Reed@Sun.COM  *              cmd(I)  - either HN_UNREGISTER or HN_REGISTER
15472958Sdr146992  *
15487513SDarren.Reed@Sun.COM  * Execute all of the callbacks registered for this event.
15492958Sdr146992  */
15502958Sdr146992 static void
15517513SDarren.Reed@Sun.COM hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
15527513SDarren.Reed@Sun.COM     char *event, char *name, hook_notify_cmd_t cmd)
15532958Sdr146992 {
15542958Sdr146992 
15557513SDarren.Reed@Sun.COM 	hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
15567513SDarren.Reed@Sun.COM 	    event, name, cmd);
15572958Sdr146992 }
15582958Sdr146992 
15592958Sdr146992 /*
15602958Sdr146992  * Function:	hook_register
15612958Sdr146992  * Returns:	int- 0 = Succ, Else = Fail
15622958Sdr146992  * Parameters:	hfi(I) - internal family pointer
15632958Sdr146992  *		event(I) - event name string
15642958Sdr146992  *		h(I) - hook pointer
15652958Sdr146992  *
15667513SDarren.Reed@Sun.COM  * Add new hook to hook list on the specified family and event.
15672958Sdr146992  */
15682958Sdr146992 int
15692958Sdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
15702958Sdr146992 {
15712958Sdr146992 	hook_event_int_t *hei;
15722958Sdr146992 	hook_int_t *hi, *new;
15737513SDarren.Reed@Sun.COM 	int error;
15742958Sdr146992 
15752958Sdr146992 	ASSERT(hfi != NULL);
15762958Sdr146992 	ASSERT(event != NULL);
15772958Sdr146992 	ASSERT(h != NULL);
15787513SDarren.Reed@Sun.COM 
15797513SDarren.Reed@Sun.COM 	if (hfi->hfi_stack->hks_shutdown)
15807513SDarren.Reed@Sun.COM 		return (NULL);
15812958Sdr146992 
15822958Sdr146992 	/* Alloc hook_int_t and copy hook */
15832958Sdr146992 	new = hook_copy(h);
15842958Sdr146992 	if (new == NULL)
15852958Sdr146992 		return (ENOMEM);
15862958Sdr146992 
15872958Sdr146992 	/*
15882958Sdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
15892958Sdr146992 	 * to hold global family write lock. Just get read lock here to
15902958Sdr146992 	 * ensure event will not be removed when doing hooks operation
15912958Sdr146992 	 */
15927513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
15932958Sdr146992 
15942958Sdr146992 	hei = hook_event_find(hfi, event);
15952958Sdr146992 	if (hei == NULL) {
15967513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
15977513SDarren.Reed@Sun.COM 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
15982958Sdr146992 		return (ENXIO);
15992958Sdr146992 	}
16002958Sdr146992 
16012958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
16022958Sdr146992 
1603*7915SDarren.Reed@Sun.COM 	/*
1604*7915SDarren.Reed@Sun.COM 	 * If we've run either the remove() or shutdown(), do not allow any
1605*7915SDarren.Reed@Sun.COM 	 * more hooks to be added to this event.
1606*7915SDarren.Reed@Sun.COM 	 */
1607*7915SDarren.Reed@Sun.COM 	if (hei->hei_shutdown) {
1608*7915SDarren.Reed@Sun.COM 		error = ESHUTDOWN;
1609*7915SDarren.Reed@Sun.COM 		goto bad_add;
1610*7915SDarren.Reed@Sun.COM 	}
1611*7915SDarren.Reed@Sun.COM 
16127513SDarren.Reed@Sun.COM 	hi = hook_find(hei, h);
16137513SDarren.Reed@Sun.COM 	if (hi != NULL) {
16147513SDarren.Reed@Sun.COM 		error = EEXIST;
16157513SDarren.Reed@Sun.COM 		goto bad_add;
16162958Sdr146992 	}
16172958Sdr146992 
16187513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
16197513SDarren.Reed@Sun.COM 	    FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
16207513SDarren.Reed@Sun.COM 		error = ENOENT;
16217513SDarren.Reed@Sun.COM bad_add:
16222958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
16237513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
16247513SDarren.Reed@Sun.COM 		hook_int_free(new, hfi->hfi_stack->hks_netstackid);
16257513SDarren.Reed@Sun.COM 		return (error);
16262958Sdr146992 	}
16272958Sdr146992 
16282958Sdr146992 	/* Add to hook list head */
16297513SDarren.Reed@Sun.COM 	error = hook_insert(&hei->hei_head, new);
16307513SDarren.Reed@Sun.COM 	if (error == 0) {
16317513SDarren.Reed@Sun.COM 		hei->hei_event->he_interested = B_TRUE;
16327513SDarren.Reed@Sun.COM 		hei->hei_kstats.hooks_added.value.ui64++;
16337513SDarren.Reed@Sun.COM 
16347513SDarren.Reed@Sun.COM 		hook_init_kstats(hfi, hei, new);
16357513SDarren.Reed@Sun.COM 	}
16362958Sdr146992 
16372958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
16387513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
16397513SDarren.Reed@Sun.COM 
16407513SDarren.Reed@Sun.COM 	/*
16417513SDarren.Reed@Sun.COM 	 * Note that the name string passed through to the notify callbacks
16427513SDarren.Reed@Sun.COM 	 * is from the original hook being registered, not the copy being
16437513SDarren.Reed@Sun.COM 	 * inserted.
16447513SDarren.Reed@Sun.COM 	 */
16457513SDarren.Reed@Sun.COM 	if (error == 0) {
16467513SDarren.Reed@Sun.COM 		hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
16477513SDarren.Reed@Sun.COM 		hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
16487513SDarren.Reed@Sun.COM 	}
16497513SDarren.Reed@Sun.COM 
16507513SDarren.Reed@Sun.COM 	return (error);
16517513SDarren.Reed@Sun.COM }
16527513SDarren.Reed@Sun.COM 
16537513SDarren.Reed@Sun.COM /*
16547513SDarren.Reed@Sun.COM  * Function:	hook_insert
16557513SDarren.Reed@Sun.COM  * Returns:	int- 0 = Succ, else = Fail
16567513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
16577513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
16587513SDarren.Reed@Sun.COM  *
16597513SDarren.Reed@Sun.COM  * Try to insert the hook onto the list of hooks according to the hints
16607513SDarren.Reed@Sun.COM  * given in the hook to be inserted and those that already exist on the
16617513SDarren.Reed@Sun.COM  * list.  For now, the implementation permits only a single hook to be
16627513SDarren.Reed@Sun.COM  * either first or last and names provided with before or after are only
16637513SDarren.Reed@Sun.COM  * loosely coupled with the action.
16647513SDarren.Reed@Sun.COM  */
16657513SDarren.Reed@Sun.COM static int
16667513SDarren.Reed@Sun.COM hook_insert(hook_int_head_t *head, hook_int_t *new)
16677513SDarren.Reed@Sun.COM {
16687513SDarren.Reed@Sun.COM 	hook_int_t *before;
16697513SDarren.Reed@Sun.COM 	hook_int_t *hi;
16707513SDarren.Reed@Sun.COM 	hook_t *hih;
16717513SDarren.Reed@Sun.COM 	hook_t *h = &new->hi_hook;
16727513SDarren.Reed@Sun.COM 
16737513SDarren.Reed@Sun.COM 	switch (new->hi_hook.h_hint) {
16747513SDarren.Reed@Sun.COM 	case HH_NONE :
16757513SDarren.Reed@Sun.COM 		before = NULL;
16767513SDarren.Reed@Sun.COM 		/*
16777513SDarren.Reed@Sun.COM 		 * If there is no hint present (or not one that can be
16787513SDarren.Reed@Sun.COM 		 * satisfied now) then try to at least respect the wishes
16797513SDarren.Reed@Sun.COM 		 * of those that want to be last.  If there are none wanting
16807513SDarren.Reed@Sun.COM 		 * to be last then add the new hook to the tail of the
16817513SDarren.Reed@Sun.COM 		 * list - this means we keep any wanting to be first
16827513SDarren.Reed@Sun.COM 		 * happy without having to search for HH_FIRST.
16837513SDarren.Reed@Sun.COM 		 */
16847513SDarren.Reed@Sun.COM 		TAILQ_FOREACH(hi, head, hi_entry) {
16857513SDarren.Reed@Sun.COM 			hih = &hi->hi_hook;
16867513SDarren.Reed@Sun.COM 			if ((hih->h_hint == HH_AFTER) &&
16877513SDarren.Reed@Sun.COM 			    (strcmp(h->h_name,
16887513SDarren.Reed@Sun.COM 			    (char *)hih->h_hintvalue) == 0)) {
16897513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
16907513SDarren.Reed@Sun.COM 				return (0);
16917513SDarren.Reed@Sun.COM 			}
16927513SDarren.Reed@Sun.COM 			if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
16937513SDarren.Reed@Sun.COM 			    (strcmp(h->h_name,
16947513SDarren.Reed@Sun.COM 			    (char *)hih->h_hintvalue) == 0)) {
16957513SDarren.Reed@Sun.COM 				before = hi;
16967513SDarren.Reed@Sun.COM 			}
16977513SDarren.Reed@Sun.COM 		}
16987513SDarren.Reed@Sun.COM 		if (before != NULL) {
16997513SDarren.Reed@Sun.COM 			TAILQ_INSERT_AFTER(head, before, new, hi_entry);
17007513SDarren.Reed@Sun.COM 			return (0);
17017513SDarren.Reed@Sun.COM 		}
17027513SDarren.Reed@Sun.COM 		hook_insert_plain(head, new);
17037513SDarren.Reed@Sun.COM 		break;
17047513SDarren.Reed@Sun.COM 
17057513SDarren.Reed@Sun.COM 	case HH_FIRST :
17067513SDarren.Reed@Sun.COM 		hi = TAILQ_FIRST(head);
17077513SDarren.Reed@Sun.COM 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
17087513SDarren.Reed@Sun.COM 			return (EBUSY);
17097513SDarren.Reed@Sun.COM 		TAILQ_INSERT_HEAD(head, new, hi_entry);
17107513SDarren.Reed@Sun.COM 		break;
17117513SDarren.Reed@Sun.COM 
17127513SDarren.Reed@Sun.COM 	case HH_LAST :
17137513SDarren.Reed@Sun.COM 		hi = TAILQ_LAST(head, hook_int_head);
17147513SDarren.Reed@Sun.COM 		if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
17157513SDarren.Reed@Sun.COM 			return (EBUSY);
17167513SDarren.Reed@Sun.COM 		TAILQ_INSERT_TAIL(head, new, hi_entry);
17177513SDarren.Reed@Sun.COM 		break;
17187513SDarren.Reed@Sun.COM 
17197513SDarren.Reed@Sun.COM 	case HH_BEFORE :
17207513SDarren.Reed@Sun.COM 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
17217513SDarren.Reed@Sun.COM 		if (hi == NULL)
17227513SDarren.Reed@Sun.COM 			return (hook_insert_afterbefore(head, new));
17237513SDarren.Reed@Sun.COM 
17247513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_FIRST)
17257513SDarren.Reed@Sun.COM 			return (EBUSY);
17267513SDarren.Reed@Sun.COM 
17277513SDarren.Reed@Sun.COM 		TAILQ_INSERT_BEFORE(hi, new, hi_entry);
17287513SDarren.Reed@Sun.COM 		break;
17297513SDarren.Reed@Sun.COM 
17307513SDarren.Reed@Sun.COM 	case HH_AFTER :
17317513SDarren.Reed@Sun.COM 		hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
17327513SDarren.Reed@Sun.COM 		if (hi == NULL)
17337513SDarren.Reed@Sun.COM 			return (hook_insert_afterbefore(head, new));
17347513SDarren.Reed@Sun.COM 
17357513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_LAST)
17367513SDarren.Reed@Sun.COM 			return (EBUSY);
17377513SDarren.Reed@Sun.COM 
17387513SDarren.Reed@Sun.COM 		TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
17397513SDarren.Reed@Sun.COM 		break;
17407513SDarren.Reed@Sun.COM 
17417513SDarren.Reed@Sun.COM 	default :
17427513SDarren.Reed@Sun.COM 		return (EINVAL);
17437513SDarren.Reed@Sun.COM 	}
17447513SDarren.Reed@Sun.COM 
17452958Sdr146992 	return (0);
17462958Sdr146992 }
17472958Sdr146992 
17487513SDarren.Reed@Sun.COM /*
17497513SDarren.Reed@Sun.COM  * Function:	hook_insert_plain
17507513SDarren.Reed@Sun.COM  * Returns:	int- 0 = success, else = failure
17517513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
17527513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
17537513SDarren.Reed@Sun.COM  *
17547513SDarren.Reed@Sun.COM  * Insert a hook such that it respects the wishes of those that want to
17557513SDarren.Reed@Sun.COM  * be last.  If there are none wanting to be last then add the new hook
17567513SDarren.Reed@Sun.COM  * to the tail of the list - this means we keep any wanting to be first
17577513SDarren.Reed@Sun.COM  * happy without having to search for HH_FIRST.
17587513SDarren.Reed@Sun.COM  */
17597513SDarren.Reed@Sun.COM static void
17607513SDarren.Reed@Sun.COM hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
17617513SDarren.Reed@Sun.COM {
17627513SDarren.Reed@Sun.COM 	hook_int_t *hi;
17637513SDarren.Reed@Sun.COM 
17647513SDarren.Reed@Sun.COM 	hi = TAILQ_FIRST(head);
17657513SDarren.Reed@Sun.COM 	if (hi != NULL) {
17667513SDarren.Reed@Sun.COM 		if (hi->hi_hook.h_hint == HH_LAST) {
17677513SDarren.Reed@Sun.COM 			TAILQ_INSERT_BEFORE(hi, new, hi_entry);
17687513SDarren.Reed@Sun.COM 		} else {
17697513SDarren.Reed@Sun.COM 			TAILQ_INSERT_TAIL(head, new, hi_entry);
17707513SDarren.Reed@Sun.COM 		}
17717513SDarren.Reed@Sun.COM 	} else {
17727513SDarren.Reed@Sun.COM 		TAILQ_INSERT_TAIL(head, new, hi_entry);
17737513SDarren.Reed@Sun.COM 	}
17747513SDarren.Reed@Sun.COM }
17757513SDarren.Reed@Sun.COM 
17767513SDarren.Reed@Sun.COM /*
17777513SDarren.Reed@Sun.COM  * Function:	hook_insert_afterbefore
17787513SDarren.Reed@Sun.COM  * Returns:	int- 0 = success, else = failure
17797513SDarren.Reed@Sun.COM  * Parameters:	head(I) - pointer to hook list to insert hook onto
17807513SDarren.Reed@Sun.COM  *		new(I)  - pointer to hook to be inserted
17817513SDarren.Reed@Sun.COM  *
17827513SDarren.Reed@Sun.COM  * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
17837513SDarren.Reed@Sun.COM  * possible, so now we need to be more careful.  The first pass is to go
17847513SDarren.Reed@Sun.COM  * through the list and look for any other hooks that also specify the
17857513SDarren.Reed@Sun.COM  * same hint name as the new one.  The object of this exercise is to make
17867513SDarren.Reed@Sun.COM  * sure that hooks with HH_BEFORE always appear on the list before those
17877513SDarren.Reed@Sun.COM  * with HH_AFTER so that when said hook arrives, it can be placed in the
17887513SDarren.Reed@Sun.COM  * middle of the BEFOREs and AFTERs.  If this condition does not arise,
17897513SDarren.Reed@Sun.COM  * just use hook_insert_plain() to try and insert the hook somewhere that
17907513SDarren.Reed@Sun.COM  * is innocuous to existing efforts.
17917513SDarren.Reed@Sun.COM  */
17927513SDarren.Reed@Sun.COM static int
17937513SDarren.Reed@Sun.COM hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
17947513SDarren.Reed@Sun.COM {
17957513SDarren.Reed@Sun.COM 	hook_int_t *hi;
17967513SDarren.Reed@Sun.COM 	hook_t *nh;
17977513SDarren.Reed@Sun.COM 	hook_t *h;
17987513SDarren.Reed@Sun.COM 
17997513SDarren.Reed@Sun.COM 	nh = &new->hi_hook;
18007513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_NONE);
18017513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_LAST);
18027513SDarren.Reed@Sun.COM 	ASSERT(new->hi_hook.h_hint != HH_FIRST);
18037513SDarren.Reed@Sun.COM 
18047513SDarren.Reed@Sun.COM 	/*
18057513SDarren.Reed@Sun.COM 	 * First, look through the list to see if there are any other
18067513SDarren.Reed@Sun.COM 	 * before's or after's that have a matching hint name.
18077513SDarren.Reed@Sun.COM 	 */
18087513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hi, head, hi_entry) {
18097513SDarren.Reed@Sun.COM 		h = &hi->hi_hook;
18107513SDarren.Reed@Sun.COM 		switch (h->h_hint) {
18117513SDarren.Reed@Sun.COM 		case HH_FIRST :
18127513SDarren.Reed@Sun.COM 		case HH_LAST :
18137513SDarren.Reed@Sun.COM 		case HH_NONE :
18147513SDarren.Reed@Sun.COM 			break;
18157513SDarren.Reed@Sun.COM 		case HH_BEFORE :
18167513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_BEFORE) &&
18177513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
18187513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
18197513SDarren.Reed@Sun.COM 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
18207513SDarren.Reed@Sun.COM 				return (0);
18217513SDarren.Reed@Sun.COM 			}
18227513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_AFTER) &&
18237513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
18247513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
18257513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
18267513SDarren.Reed@Sun.COM 				return (0);
18277513SDarren.Reed@Sun.COM 			}
18287513SDarren.Reed@Sun.COM 			break;
18297513SDarren.Reed@Sun.COM 		case HH_AFTER :
18307513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_AFTER) &&
18317513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
18327513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
18337513SDarren.Reed@Sun.COM 				TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
18347513SDarren.Reed@Sun.COM 				return (0);
18357513SDarren.Reed@Sun.COM 			}
18367513SDarren.Reed@Sun.COM 			if ((nh->h_hint == HH_BEFORE) &&
18377513SDarren.Reed@Sun.COM 			    (strcmp((char *)h->h_hintvalue,
18387513SDarren.Reed@Sun.COM 			    (char *)nh->h_hintvalue) == 0)) {
18397513SDarren.Reed@Sun.COM 				TAILQ_INSERT_BEFORE(hi, new, hi_entry);
18407513SDarren.Reed@Sun.COM 				return (0);
18417513SDarren.Reed@Sun.COM 			}
18427513SDarren.Reed@Sun.COM 			break;
18437513SDarren.Reed@Sun.COM 		}
18447513SDarren.Reed@Sun.COM 	}
18457513SDarren.Reed@Sun.COM 
18467513SDarren.Reed@Sun.COM 	hook_insert_plain(head, new);
18477513SDarren.Reed@Sun.COM 
18487513SDarren.Reed@Sun.COM 	return (0);
18497513SDarren.Reed@Sun.COM }
18502958Sdr146992 
18512958Sdr146992 /*
18522958Sdr146992  * Function:	hook_unregister
18532958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
18542958Sdr146992  * Parameters:	hfi(I) - internal family pointer
18552958Sdr146992  *		event(I) - event name string
18562958Sdr146992  *		h(I) - hook pointer
18572958Sdr146992  *
18582958Sdr146992  * Remove hook from hook list on specific family, event
18592958Sdr146992  */
18602958Sdr146992 int
18612958Sdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
18622958Sdr146992 {
18632958Sdr146992 	hook_event_int_t *hei;
18642958Sdr146992 	hook_int_t *hi;
18657513SDarren.Reed@Sun.COM 	boolean_t free_event;
18662958Sdr146992 
18672958Sdr146992 	ASSERT(hfi != NULL);
18682958Sdr146992 	ASSERT(h != NULL);
18692958Sdr146992 
18707513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(&hfi->hfi_lock);
18712958Sdr146992 
18722958Sdr146992 	hei = hook_event_find(hfi, event);
18732958Sdr146992 	if (hei == NULL) {
18747513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18752958Sdr146992 		return (ENXIO);
18762958Sdr146992 	}
18772958Sdr146992 
18782958Sdr146992 	/* Hold write lock for event */
18792958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
18802958Sdr146992 
18812958Sdr146992 	hi = hook_find(hei, h);
18822958Sdr146992 	if (hi == NULL) {
18832958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
18847513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18852958Sdr146992 		return (ENXIO);
18862958Sdr146992 	}
18872958Sdr146992 
18887513SDarren.Reed@Sun.COM 	if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK,
18897513SDarren.Reed@Sun.COM 	    FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
18907513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hei->hei_lock);
18917513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(&hfi->hfi_lock);
18927513SDarren.Reed@Sun.COM 		return (ENOENT);
18937513SDarren.Reed@Sun.COM 	}
18947513SDarren.Reed@Sun.COM 
18952958Sdr146992 	/* Remove from hook list */
18962958Sdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
18977513SDarren.Reed@Sun.COM 
18987513SDarren.Reed@Sun.COM 	free_event = B_FALSE;
18992958Sdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
19002958Sdr146992 		hei->hei_event->he_interested = B_FALSE;
19017513SDarren.Reed@Sun.COM 		/*
19027513SDarren.Reed@Sun.COM 		 * If the delete pending flag has been set and there are
19037513SDarren.Reed@Sun.COM 		 * no notifiers on the event (and we've removed the last
19047513SDarren.Reed@Sun.COM 		 * hook) then we need to free this event after we're done.
19057513SDarren.Reed@Sun.COM 		 */
19067513SDarren.Reed@Sun.COM 		if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
19077513SDarren.Reed@Sun.COM 			free_event = B_TRUE;
19082958Sdr146992 	}
19097513SDarren.Reed@Sun.COM 	hei->hei_kstats.hooks_removed.value.ui64++;
19102958Sdr146992 
19112958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
19127513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(&hfi->hfi_lock);
19137513SDarren.Reed@Sun.COM 	/*
19147513SDarren.Reed@Sun.COM 	 * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
19157513SDarren.Reed@Sun.COM 	 * will not be free'd and thus the hook_family_int_t wil not
19167513SDarren.Reed@Sun.COM 	 * be free'd either.
19177513SDarren.Reed@Sun.COM 	 */
19187513SDarren.Reed@Sun.COM 	hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
19197513SDarren.Reed@Sun.COM 	hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
19202958Sdr146992 
19217513SDarren.Reed@Sun.COM 	hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
19227513SDarren.Reed@Sun.COM 
19237513SDarren.Reed@Sun.COM 	if (free_event)
19247513SDarren.Reed@Sun.COM 		hook_event_free(hei, hfi);
19257513SDarren.Reed@Sun.COM 
19262958Sdr146992 	return (0);
19272958Sdr146992 }
19282958Sdr146992 
19297513SDarren.Reed@Sun.COM /*
19307513SDarren.Reed@Sun.COM  * Function:	hook_find_byname
19317513SDarren.Reed@Sun.COM  * Returns:	internal hook pointer - NULL = Not match
19327513SDarren.Reed@Sun.COM  * Parameters:	hei(I) - internal event pointer
19337513SDarren.Reed@Sun.COM  *		name(I)- hook name
19347513SDarren.Reed@Sun.COM  *
19357513SDarren.Reed@Sun.COM  * Search an event's list of hooks to see if there is a hook present that
19367513SDarren.Reed@Sun.COM  * has a matching name to the one being looked for.
19377513SDarren.Reed@Sun.COM  */
19387513SDarren.Reed@Sun.COM static hook_int_t *
19397513SDarren.Reed@Sun.COM hook_find_byname(hook_int_head_t *head, char *name)
19407513SDarren.Reed@Sun.COM {
19417513SDarren.Reed@Sun.COM 	hook_int_t *hi;
19427513SDarren.Reed@Sun.COM 
19437513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hi, head, hi_entry) {
19447513SDarren.Reed@Sun.COM 		if (strcmp(hi->hi_hook.h_name, name) == 0)
19457513SDarren.Reed@Sun.COM 			return (hi);
19467513SDarren.Reed@Sun.COM 	}
19477513SDarren.Reed@Sun.COM 
19487513SDarren.Reed@Sun.COM 	return (NULL);
19497513SDarren.Reed@Sun.COM }
19502958Sdr146992 
19512958Sdr146992 /*
19522958Sdr146992  * Function:	hook_find
19532958Sdr146992  * Returns:	internal hook pointer - NULL = Not match
19542958Sdr146992  * Parameters:	hei(I) - internal event pointer
19552958Sdr146992  *		h(I) - hook pointer
19562958Sdr146992  *
19577513SDarren.Reed@Sun.COM  * Search an event's list of hooks to see if there is already one that
19587513SDarren.Reed@Sun.COM  * matches the hook being passed in.  Currently the only criteria for a
19597513SDarren.Reed@Sun.COM  * successful search here is for the names to be the same.
19602958Sdr146992  */
19612958Sdr146992 static hook_int_t *
19622958Sdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
19632958Sdr146992 {
19642958Sdr146992 
19652958Sdr146992 	ASSERT(hei != NULL);
19662958Sdr146992 	ASSERT(h != NULL);
19672958Sdr146992 
19687513SDarren.Reed@Sun.COM 	return (hook_find_byname(&hei->hei_head, h->h_name));
19692958Sdr146992 }
19702958Sdr146992 
19712958Sdr146992 /*
19722958Sdr146992  * Function:	hook_copy
19732958Sdr146992  * Returns:	internal hook pointer - NULL = Failed
19742958Sdr146992  * Parameters:	src(I) - hook pointer
19752958Sdr146992  *
19762958Sdr146992  * Allocate internal hook block and duplicate incoming hook.
19772958Sdr146992  * No locks should be held across this function as it may sleep.
19787513SDarren.Reed@Sun.COM  * Because hook_copy() is responsible for the creation of the internal
19797513SDarren.Reed@Sun.COM  * hook structure that is used here, it takes on population the structure
19807513SDarren.Reed@Sun.COM  * with the kstat information.  Note that while the kstat bits are
19817513SDarren.Reed@Sun.COM  * seeded here, their installation of the kstats is handled elsewhere.
19822958Sdr146992  */
19832958Sdr146992 static hook_int_t *
19842958Sdr146992 hook_copy(hook_t *src)
19852958Sdr146992 {
19862958Sdr146992 	hook_int_t *new;
19872958Sdr146992 	hook_t *dst;
19887513SDarren.Reed@Sun.COM 	int len;
19892958Sdr146992 
19902958Sdr146992 	ASSERT(src != NULL);
19912958Sdr146992 	ASSERT(src->h_name != NULL);
19922958Sdr146992 
19932958Sdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
19942958Sdr146992 
19952958Sdr146992 	/* Copy body */
19962958Sdr146992 	dst = &new->hi_hook;
19972958Sdr146992 	*dst = *src;
19982958Sdr146992 
19992958Sdr146992 	/* Copy name */
20007513SDarren.Reed@Sun.COM 	len = strlen(src->h_name);
20017513SDarren.Reed@Sun.COM 	dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
20022958Sdr146992 	(void) strcpy(dst->h_name, src->h_name);
20032958Sdr146992 
20047513SDarren.Reed@Sun.COM 	/*
20057513SDarren.Reed@Sun.COM 	 * This is initialised in this manner to make it safer to use the
20067513SDarren.Reed@Sun.COM 	 * same pointer in the kstats field.
20077513SDarren.Reed@Sun.COM 	 */
20087513SDarren.Reed@Sun.COM 	dst->h_hintvalue = (uintptr_t)"";
20097513SDarren.Reed@Sun.COM 
20107513SDarren.Reed@Sun.COM 	if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
20117513SDarren.Reed@Sun.COM 		len = strlen((char *)src->h_hintvalue);
20127513SDarren.Reed@Sun.COM 		if (len > 0) {
20137513SDarren.Reed@Sun.COM 			dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
20147513SDarren.Reed@Sun.COM 			    KM_SLEEP);
20157513SDarren.Reed@Sun.COM 			(void) strcpy((char *)dst->h_hintvalue,
20167513SDarren.Reed@Sun.COM 			    (char *)src->h_hintvalue);
20177513SDarren.Reed@Sun.COM 		}
20187513SDarren.Reed@Sun.COM 	}
20197513SDarren.Reed@Sun.COM 
20202958Sdr146992 	return (new);
20212958Sdr146992 }
20222958Sdr146992 
20232958Sdr146992 /*
20247513SDarren.Reed@Sun.COM  * Function:	hook_init_kstats
20257513SDarren.Reed@Sun.COM  * Returns:	None
20267513SDarren.Reed@Sun.COM  * Parameters:  hfi(I) - pointer to the family that owns the event.
20277513SDarren.Reed@Sun.COM  *              hei(I) - pointer to the event that owns this hook
20287513SDarren.Reed@Sun.COM  *              hi(I)  - pointer to the hook for which we create kstats for
20297513SDarren.Reed@Sun.COM  *
20307513SDarren.Reed@Sun.COM  * Each hook that is registered with this framework has its own kstats
20317513SDarren.Reed@Sun.COM  * set up so that we can provide an easy way in which to observe the
20327513SDarren.Reed@Sun.COM  * look of hooks (using the kstat command.) The position is set to 0
20337513SDarren.Reed@Sun.COM  * here but is recalculated after we know the insertion has been a
20347513SDarren.Reed@Sun.COM  * success.
20357513SDarren.Reed@Sun.COM  */
20367513SDarren.Reed@Sun.COM static void
20377513SDarren.Reed@Sun.COM hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
20387513SDarren.Reed@Sun.COM {
20397513SDarren.Reed@Sun.COM 	hook_hook_kstat_t template = {
20407513SDarren.Reed@Sun.COM 		{ "version",			KSTAT_DATA_INT32 },
20417513SDarren.Reed@Sun.COM 		{ "flags",			KSTAT_DATA_UINT32 },
20427513SDarren.Reed@Sun.COM 		{ "hint",			KSTAT_DATA_INT32 },
20437513SDarren.Reed@Sun.COM 		{ "hint_value",			KSTAT_DATA_UINT64 },
20447513SDarren.Reed@Sun.COM 		{ "position",			KSTAT_DATA_INT32 },
20457513SDarren.Reed@Sun.COM 		{ "hook_hits",			KSTAT_DATA_UINT64 }
20467513SDarren.Reed@Sun.COM 	};
20477513SDarren.Reed@Sun.COM 	hook_stack_t *hks;
20487513SDarren.Reed@Sun.COM 	size_t kslen;
20497513SDarren.Reed@Sun.COM 	int position;
20507513SDarren.Reed@Sun.COM 	hook_int_t *h;
20517513SDarren.Reed@Sun.COM 
20527513SDarren.Reed@Sun.COM 	kslen = strlen(hfi->hfi_family.hf_name) +
20537513SDarren.Reed@Sun.COM 	    strlen(hei->hei_event->he_name) + 2;
20547513SDarren.Reed@Sun.COM 
20557513SDarren.Reed@Sun.COM 	hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
20567513SDarren.Reed@Sun.COM 	(void) snprintf(hi->hi_ksname, kslen, "%s/%s",
20577513SDarren.Reed@Sun.COM 	    hfi->hfi_family.hf_name, hei->hei_event->he_name);
20587513SDarren.Reed@Sun.COM 
20597513SDarren.Reed@Sun.COM 	hks = hfi->hfi_stack;
20607513SDarren.Reed@Sun.COM 	hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
20617513SDarren.Reed@Sun.COM 	    hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
20627513SDarren.Reed@Sun.COM 	    sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
20637513SDarren.Reed@Sun.COM 	    KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
20647513SDarren.Reed@Sun.COM 
20657513SDarren.Reed@Sun.COM 	/* Initialise the kstats for the structure */
20667513SDarren.Reed@Sun.COM 	bcopy(&template, &hi->hi_kstats, sizeof (template));
20677513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
20687513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
20697513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
20707513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_position.value.i32 = 0;
20717513SDarren.Reed@Sun.COM 	hi->hi_kstats.hook_hits.value.ui64 = 0;
20727513SDarren.Reed@Sun.COM 
20737513SDarren.Reed@Sun.COM 	switch (hi->hi_hook.h_hint) {
20747513SDarren.Reed@Sun.COM 	case HH_BEFORE :
20757513SDarren.Reed@Sun.COM 	case HH_AFTER :
20767513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
20777513SDarren.Reed@Sun.COM 		hi->hi_kstats.hook_hintvalue.value.ui64 =
20787513SDarren.Reed@Sun.COM 		    hi->hi_hook.h_hintvalue;
20797513SDarren.Reed@Sun.COM 		break;
20807513SDarren.Reed@Sun.COM 	default :
20817513SDarren.Reed@Sun.COM 		break;
20827513SDarren.Reed@Sun.COM 	}
20837513SDarren.Reed@Sun.COM 
20847513SDarren.Reed@Sun.COM 	if (hi->hi_kstatp != NULL) {
20857513SDarren.Reed@Sun.COM 		hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
20867513SDarren.Reed@Sun.COM 		hi->hi_kstatp->ks_private =
20877513SDarren.Reed@Sun.COM 		    (void *)(uintptr_t)hks->hks_netstackid;
20887513SDarren.Reed@Sun.COM 
20897513SDarren.Reed@Sun.COM 		kstat_install(hi->hi_kstatp);
20907513SDarren.Reed@Sun.COM 	}
20917513SDarren.Reed@Sun.COM 
20927513SDarren.Reed@Sun.COM 	position = 1;
20937513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
20947513SDarren.Reed@Sun.COM 		h->hi_kstats.hook_position.value.ui32 = position++;
20957513SDarren.Reed@Sun.COM 	}
20967513SDarren.Reed@Sun.COM }
20977513SDarren.Reed@Sun.COM 
20987513SDarren.Reed@Sun.COM /*
20997513SDarren.Reed@Sun.COM  * Function:	hook_int_free
21002958Sdr146992  * Returns:	None
21012958Sdr146992  * Parameters:	hi(I) - internal hook pointer
21022958Sdr146992  *
21032958Sdr146992  * Free alloc memory for hook
21042958Sdr146992  */
21052958Sdr146992 static void
21067513SDarren.Reed@Sun.COM hook_int_free(hook_int_t *hi, netstackid_t stackid)
21072958Sdr146992 {
21087513SDarren.Reed@Sun.COM 	int len;
21097513SDarren.Reed@Sun.COM 
21102958Sdr146992 	ASSERT(hi != NULL);
21112958Sdr146992 
21122958Sdr146992 	/* Free name space */
21132958Sdr146992 	if (hi->hi_hook.h_name != NULL) {
21142958Sdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
21152958Sdr146992 	}
21167513SDarren.Reed@Sun.COM 	if (hi->hi_ksname != NULL) {
21177513SDarren.Reed@Sun.COM 		kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
21187513SDarren.Reed@Sun.COM 	}
21197513SDarren.Reed@Sun.COM 
21207513SDarren.Reed@Sun.COM 	/* Free the name used with the before/after hints. */
21217513SDarren.Reed@Sun.COM 	switch (hi->hi_hook.h_hint) {
21227513SDarren.Reed@Sun.COM 	case HH_BEFORE :
21237513SDarren.Reed@Sun.COM 	case HH_AFTER :
21247513SDarren.Reed@Sun.COM 		len = strlen((char *)hi->hi_hook.h_hintvalue);
21257513SDarren.Reed@Sun.COM 		if (len > 0)
21267513SDarren.Reed@Sun.COM 			kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
21277513SDarren.Reed@Sun.COM 		break;
21287513SDarren.Reed@Sun.COM 	default :
21297513SDarren.Reed@Sun.COM 		break;
21307513SDarren.Reed@Sun.COM 	}
21317513SDarren.Reed@Sun.COM 
21327513SDarren.Reed@Sun.COM 	if (hi->hi_kstatp != NULL)
21337513SDarren.Reed@Sun.COM 		kstat_delete_netstack(hi->hi_kstatp, stackid);
21342958Sdr146992 
21352958Sdr146992 	/* Free container */
21362958Sdr146992 	kmem_free(hi, sizeof (*hi));
21372958Sdr146992 }
21387513SDarren.Reed@Sun.COM 
21397513SDarren.Reed@Sun.COM /*
21407513SDarren.Reed@Sun.COM  * Function:	hook_alloc
21417513SDarren.Reed@Sun.COM  * Returns:	hook_t * - pointer to new hook structure
21427513SDarren.Reed@Sun.COM  * Parameters:	version(I) - version number of the API when compiled
21437513SDarren.Reed@Sun.COM  *
21447513SDarren.Reed@Sun.COM  * This function serves as the interface for consumers to obtain a hook_t
21457513SDarren.Reed@Sun.COM  * structure.  At this point in time, there is only a single "version" of
21467513SDarren.Reed@Sun.COM  * it, leading to a straight forward function.  In a perfect world the
21477513SDarren.Reed@Sun.COM  * h_vesion would be a protected data structure member, but C isn't that
21487513SDarren.Reed@Sun.COM  * advanced...
21497513SDarren.Reed@Sun.COM  */
21507513SDarren.Reed@Sun.COM hook_t *
21517513SDarren.Reed@Sun.COM hook_alloc(const int h_version)
21527513SDarren.Reed@Sun.COM {
21537513SDarren.Reed@Sun.COM 	hook_t *h;
21547513SDarren.Reed@Sun.COM 
21557513SDarren.Reed@Sun.COM 	h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
21567513SDarren.Reed@Sun.COM 	h->h_version = h_version;
21577513SDarren.Reed@Sun.COM 	return (h);
21587513SDarren.Reed@Sun.COM }
21597513SDarren.Reed@Sun.COM 
21607513SDarren.Reed@Sun.COM /*
21617513SDarren.Reed@Sun.COM  * Function:	hook_free
21627513SDarren.Reed@Sun.COM  * Returns:	None
21637513SDarren.Reed@Sun.COM  * Parameters:	h(I) - external hook pointer
21647513SDarren.Reed@Sun.COM  *
21657513SDarren.Reed@Sun.COM  * This function only free's memory allocated with hook_alloc(), so that if
21667513SDarren.Reed@Sun.COM  * (for example) kernel memory was allocated for h_name, this needs to be
21677513SDarren.Reed@Sun.COM  * free'd before calling hook_free().
21687513SDarren.Reed@Sun.COM  */
21697513SDarren.Reed@Sun.COM void
21707513SDarren.Reed@Sun.COM hook_free(hook_t *h)
21717513SDarren.Reed@Sun.COM {
21727513SDarren.Reed@Sun.COM 	kmem_free(h, sizeof (*h));
21737513SDarren.Reed@Sun.COM }
21747513SDarren.Reed@Sun.COM 
21757513SDarren.Reed@Sun.COM /*
21767513SDarren.Reed@Sun.COM  * Function:	hook_notify_register
21777513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
21787513SDarren.Reed@Sun.COM  * Parameters:	lock(I)     - netstack identifier
21797513SDarren.Reed@Sun.COM  *              head(I)     - top of the list of callbacks
21807513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
21817513SDarren.Reed@Sun.COM  *              arg(I)      - arg to pass back to the function
21827513SDarren.Reed@Sun.COM  *
21837513SDarren.Reed@Sun.COM  * This function implements the modification of the list of callbacks
21847513SDarren.Reed@Sun.COM  * that are registered when someone wants to be advised of a change
21857513SDarren.Reed@Sun.COM  * that has happened.
21867513SDarren.Reed@Sun.COM  */
21877513SDarren.Reed@Sun.COM static int
21887513SDarren.Reed@Sun.COM hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head,
21897513SDarren.Reed@Sun.COM     hook_notify_fn_t callback, void *arg)
21907513SDarren.Reed@Sun.COM {
21917513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
21927513SDarren.Reed@Sun.COM 
21937513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(lock);
21947513SDarren.Reed@Sun.COM 
21957513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
21967513SDarren.Reed@Sun.COM 		if (hn->hn_func == callback) {
21977513SDarren.Reed@Sun.COM 			CVW_EXIT_WRITE(lock);
21987513SDarren.Reed@Sun.COM 			return (EEXIST);
21997513SDarren.Reed@Sun.COM 		}
22007513SDarren.Reed@Sun.COM 	}
22017513SDarren.Reed@Sun.COM 
22027513SDarren.Reed@Sun.COM 	hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
22037513SDarren.Reed@Sun.COM 	hn->hn_func = callback;
22047513SDarren.Reed@Sun.COM 	hn->hn_arg = arg;
22057513SDarren.Reed@Sun.COM 	TAILQ_INSERT_TAIL(head, hn, hn_entry);
22067513SDarren.Reed@Sun.COM 
22077513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(lock);
22087513SDarren.Reed@Sun.COM 
22097513SDarren.Reed@Sun.COM 	return (0);
22107513SDarren.Reed@Sun.COM }
22117513SDarren.Reed@Sun.COM 
22127513SDarren.Reed@Sun.COM /*
22137513SDarren.Reed@Sun.COM  * Function:	hook_stack_notify_register
22147513SDarren.Reed@Sun.COM  * Returns:	0 = success, else failure
22157513SDarren.Reed@Sun.COM  * Parameters:	stackid(I) - netstack identifier
22167513SDarren.Reed@Sun.COM  *              callback(I) - function to be called
22177513SDarren.Reed@Sun.COM  *
22187513SDarren.Reed@Sun.COM  */
22197513SDarren.Reed@Sun.COM static int
22207513SDarren.Reed@Sun.COM hook_notify_unregister(cvwaitlock_t *lock, hook_notify_head_t *head,
22217513SDarren.Reed@Sun.COM     hook_notify_fn_t callback)
22227513SDarren.Reed@Sun.COM {
22237513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
22247513SDarren.Reed@Sun.COM 
22257513SDarren.Reed@Sun.COM 	CVW_ENTER_WRITE(lock);
22267513SDarren.Reed@Sun.COM 
22277513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
22287513SDarren.Reed@Sun.COM 		if (hn->hn_func == callback)
22297513SDarren.Reed@Sun.COM 			break;
22307513SDarren.Reed@Sun.COM 	}
22317513SDarren.Reed@Sun.COM 	if (hn == NULL) {
22327513SDarren.Reed@Sun.COM 		CVW_EXIT_WRITE(lock);
22337513SDarren.Reed@Sun.COM 		return (ESRCH);
22347513SDarren.Reed@Sun.COM 	}
22357513SDarren.Reed@Sun.COM 
22367513SDarren.Reed@Sun.COM 	TAILQ_REMOVE(head, hn, hn_entry);
22377513SDarren.Reed@Sun.COM 
22387513SDarren.Reed@Sun.COM 	CVW_EXIT_WRITE(lock);
22397513SDarren.Reed@Sun.COM 
22407513SDarren.Reed@Sun.COM 	kmem_free(hn, sizeof (*hn));
22417513SDarren.Reed@Sun.COM 
22427513SDarren.Reed@Sun.COM 	return (0);
22437513SDarren.Reed@Sun.COM }
22447513SDarren.Reed@Sun.COM 
22457513SDarren.Reed@Sun.COM /*
22467513SDarren.Reed@Sun.COM  * Function:	hook_notify_run
22477513SDarren.Reed@Sun.COM  * Returns:	None
22487513SDarren.Reed@Sun.COM  * Parameters:	head(I)   - top of the list of callbacks
22497513SDarren.Reed@Sun.COM  *              family(I) - name of the hook family that owns the event
22507513SDarren.Reed@Sun.COM  *              event(I)  - name of the event being changed
22517513SDarren.Reed@Sun.COM  *              name(I)   - name of the object causing change
22527513SDarren.Reed@Sun.COM  *              cmd(I)    - either HN_UNREGISTER or HN_REGISTER
22537513SDarren.Reed@Sun.COM  *
22547513SDarren.Reed@Sun.COM  * This function walks through the list of registered callbacks and
22557513SDarren.Reed@Sun.COM  * executes each one, passing back the arg supplied when registered
22567513SDarren.Reed@Sun.COM  * and the name of the family (that owns the event), event (the thing
22577513SDarren.Reed@Sun.COM  * to which we're making a change) and finally a name that describes
22587513SDarren.Reed@Sun.COM  * what is being added or removed, as indicated by cmd.
22597513SDarren.Reed@Sun.COM  *
22607513SDarren.Reed@Sun.COM  * This function does not acquire or release any lock as it is required
22617513SDarren.Reed@Sun.COM  * that code calling it do so before hand.  The use of hook_notify_head_t
22627513SDarren.Reed@Sun.COM  * is protected by the use of flagwait_t in the structures that own this
22637513SDarren.Reed@Sun.COM  * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
22647513SDarren.Reed@Sun.COM  */
22657513SDarren.Reed@Sun.COM static void
22667513SDarren.Reed@Sun.COM hook_notify_run(hook_notify_head_t *head, char *family, char *event,
22677513SDarren.Reed@Sun.COM     char *name, hook_notify_cmd_t cmd)
22687513SDarren.Reed@Sun.COM {
22697513SDarren.Reed@Sun.COM 	hook_notify_t *hn;
22707513SDarren.Reed@Sun.COM 
22717513SDarren.Reed@Sun.COM 	TAILQ_FOREACH(hn, head, hn_entry) {
22727513SDarren.Reed@Sun.COM 		(*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
22737513SDarren.Reed@Sun.COM 	}
22747513SDarren.Reed@Sun.COM }
2275