12958Sdr146992 /*
22958Sdr146992 * CDDL HEADER START
32958Sdr146992 *
42958Sdr146992 * The contents of this file are subject to the terms of the
52958Sdr146992 * Common Development and Distribution License (the "License").
62958Sdr146992 * You may not use this file except in compliance with the License.
72958Sdr146992 *
82958Sdr146992 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92958Sdr146992 * or http://www.opensolaris.org/os/licensing.
102958Sdr146992 * See the License for the specific language governing permissions
112958Sdr146992 * and limitations under the License.
122958Sdr146992 *
132958Sdr146992 * When distributing Covered Code, include this CDDL HEADER in each
142958Sdr146992 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152958Sdr146992 * If applicable, add the following below this CDDL HEADER, with the
162958Sdr146992 * fields enclosed by brackets "[]" replaced with your own identifying
172958Sdr146992 * information: Portions Copyright [yyyy] [name of copyright owner]
182958Sdr146992 *
192958Sdr146992 * CDDL HEADER END
202958Sdr146992 */
212958Sdr146992 /*
227513SDarren.Reed@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
232958Sdr146992 * Use is subject to license terms.
242958Sdr146992 */
252958Sdr146992 #include <sys/param.h>
262958Sdr146992 #include <sys/types.h>
272958Sdr146992 #include <sys/systm.h>
282958Sdr146992 #include <sys/errno.h>
292958Sdr146992 #include <sys/kmem.h>
302958Sdr146992 #include <sys/mutex.h>
312958Sdr146992 #include <sys/condvar.h>
322958Sdr146992 #include <sys/modctl.h>
332958Sdr146992 #include <sys/hook_impl.h>
342958Sdr146992 #include <sys/sdt.h>
357513SDarren.Reed@Sun.COM #include <sys/cmn_err.h>
362958Sdr146992
372958Sdr146992 /*
382958Sdr146992 * This file provides kernel hook framework.
392958Sdr146992 */
402958Sdr146992
412958Sdr146992 static struct modldrv modlmisc = {
422958Sdr146992 &mod_miscops, /* drv_modops */
432958Sdr146992 "Hooks Interface v1.0", /* drv_linkinfo */
442958Sdr146992 };
452958Sdr146992
462958Sdr146992 static struct modlinkage modlinkage = {
472958Sdr146992 MODREV_1, /* ml_rev */
482958Sdr146992 &modlmisc, /* ml_linkage */
492958Sdr146992 NULL
502958Sdr146992 };
512958Sdr146992
522958Sdr146992 /*
537513SDarren.Reed@Sun.COM * How it works.
547513SDarren.Reed@Sun.COM * =============
557513SDarren.Reed@Sun.COM * Use of the hook framework here is tied up with zones - when a new zone
567513SDarren.Reed@Sun.COM * is created, we create a new hook_stack_t and are open to business for
577513SDarren.Reed@Sun.COM * allowing new hook families and their events.
587513SDarren.Reed@Sun.COM *
597513SDarren.Reed@Sun.COM * A consumer of these hooks is expected to operate in this fashion:
607513SDarren.Reed@Sun.COM * 1) call hook_family_add() to create a new family of hooks. It is a
617513SDarren.Reed@Sun.COM * current requirement that this call must be made with the value
627513SDarren.Reed@Sun.COM * returned from hook_stack_init, by way of infrastructure elsewhere.
637513SDarren.Reed@Sun.COM * 2) add events to the registered family with calls to hook_event_add.
647513SDarren.Reed@Sun.COM *
657513SDarren.Reed@Sun.COM * At this point, the structures in place should be open to others to
667513SDarren.Reed@Sun.COM * add hooks to the event or add notifiers for when the contents of the
677513SDarren.Reed@Sun.COM * hook stack changes.
687513SDarren.Reed@Sun.COM *
697513SDarren.Reed@Sun.COM * The interesting stuff happens on teardown.
707513SDarren.Reed@Sun.COM *
717513SDarren.Reed@Sun.COM * It is a requirement that the provider of hook events work in the reverse
727513SDarren.Reed@Sun.COM * order to the above, so that the first step is:
737513SDarren.Reed@Sun.COM * 1) remove events from each hook family created earlier
747513SDarren.Reed@Sun.COM * 2) remove hook families from the hook stack.
757513SDarren.Reed@Sun.COM *
767513SDarren.Reed@Sun.COM * When doing teardown of both events and families, a check is made to see
77*12263SDarren.Reed@Sun.COM * if either structure is still "busy". If so then a boolean flag (FWF_DESTROY)
78*12263SDarren.Reed@Sun.COM * is set to say that the structure is condemned. The presence of this flag
79*12263SDarren.Reed@Sun.COM * being set must be checked for in _add()/_register()/ functions and a
80*12263SDarren.Reed@Sun.COM * failure returned if it is set. It is ignored by the _find() functions
81*12263SDarren.Reed@Sun.COM * because they're used by _remove()/_unregister().
82*12263SDarren.Reed@Sun.COM * While setting the condemned flag when trying to delete a structure would
83*12263SDarren.Reed@Sun.COM * normally be keyed from the presence of a reference count being greater
84*12263SDarren.Reed@Sun.COM * than 1, in this implementation there are no reference counts required:
85*12263SDarren.Reed@Sun.COM * instead the presence of objects on linked lists is taken to mean
86*12263SDarren.Reed@Sun.COM * something is still "busy."
877513SDarren.Reed@Sun.COM *
887513SDarren.Reed@Sun.COM * ONLY the caller that adds the family and the events ever has a direct
897513SDarren.Reed@Sun.COM * reference to the internal structures and thus ONLY it should be doing
907513SDarren.Reed@Sun.COM * the removal of either the event or family. In practise, what this means
917513SDarren.Reed@Sun.COM * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed
927513SDarren.Reed@Sun.COM * by net_event_register() (these interface to hook_family_add() and
937513SDarren.Reed@Sun.COM * hook_event_add(), respectively) that are made when we create an instance
947513SDarren.Reed@Sun.COM * of IP and when the IP instance is shutdown/destroyed, it calls
957513SDarren.Reed@Sun.COM * net_event_unregister() and net_protocol_unregister(), which in turn call
967513SDarren.Reed@Sun.COM * hook_event_remove() and hook_family_remove() respectively. Nobody else
977513SDarren.Reed@Sun.COM * is entitled to call the _unregister() functions. It is imperative that
987513SDarren.Reed@Sun.COM * there be only one _remove() call for every _add() call.
997513SDarren.Reed@Sun.COM *
1007513SDarren.Reed@Sun.COM * It is possible that code which is interfacing with this hook framework
1017513SDarren.Reed@Sun.COM * won't do all the cleaning up that it needs to at the right time. While
1027513SDarren.Reed@Sun.COM * we can't prevent programmers from creating memory leaks, we can synchronise
1037513SDarren.Reed@Sun.COM * when we clean up data structures to prevent code accessing free'd memory.
1047513SDarren.Reed@Sun.COM *
1057513SDarren.Reed@Sun.COM * A simple diagram showing the ownership is as follows:
1067513SDarren.Reed@Sun.COM *
1077513SDarren.Reed@Sun.COM * Owned +--------------+
1087513SDarren.Reed@Sun.COM * by | hook_stack_t |
1097513SDarren.Reed@Sun.COM * the +--------------+
1107513SDarren.Reed@Sun.COM * Instance |
1117513SDarren.Reed@Sun.COM * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1127513SDarren.Reed@Sun.COM * V
1137513SDarren.Reed@Sun.COM * Owned +-------------------+ +-------------------+
1147513SDarren.Reed@Sun.COM * | hook_family_int_t |---->| hook_family_int_t |
1157513SDarren.Reed@Sun.COM * by +-------------------+ +-------------------+
1167513SDarren.Reed@Sun.COM * | \+---------------+ \+---------------+
1177513SDarren.Reed@Sun.COM * network | | hook_family_t | | hook_family_t |
1187513SDarren.Reed@Sun.COM * V +---------------+ +---------------+
1197513SDarren.Reed@Sun.COM * protocol +------------------+ +------------------+
1207513SDarren.Reed@Sun.COM * | hook_event_int_t |---->| hook_event_int_t |
1217513SDarren.Reed@Sun.COM * (ipv4,ipv6) +------------------+ +------------------+
1227513SDarren.Reed@Sun.COM * | \+--------------+ \+--------------+
1237513SDarren.Reed@Sun.COM * | | hook_event_t | | hook_event_t |
1247513SDarren.Reed@Sun.COM * | +--------------+ +--------------+
1257513SDarren.Reed@Sun.COM * - - - - - - - -|- - - - - - - - - - - - - - - - - -
1267513SDarren.Reed@Sun.COM * V
1277513SDarren.Reed@Sun.COM * Owned +------------+
1287513SDarren.Reed@Sun.COM * | hook_int_t |
1297513SDarren.Reed@Sun.COM * by +------------+
1307513SDarren.Reed@Sun.COM * \+--------+
1317513SDarren.Reed@Sun.COM * the consumer | hook_t |
1327513SDarren.Reed@Sun.COM * +--------+
1337513SDarren.Reed@Sun.COM *
1347513SDarren.Reed@Sun.COM * The consumers, such as IPFilter, do not have any pointers or hold any
1357513SDarren.Reed@Sun.COM * references to hook_int_t, hook_event_t or hook_event_int_t. By placing
1367513SDarren.Reed@Sun.COM * a hook on an event through net_hook_register(), an implicit reference
1377513SDarren.Reed@Sun.COM * to the hook_event_int_t is returned with a successful call. Additionally,
1387513SDarren.Reed@Sun.COM * IPFilter does not see the hook_family_int_t or hook_family_t directly.
1397513SDarren.Reed@Sun.COM * Rather it is returned a net_handle_t (from net_protocol_lookup()) that
1407513SDarren.Reed@Sun.COM * contains a pointer to hook_family_int_t. The structure behind the
1417513SDarren.Reed@Sun.COM * net_handle_t (struct net_data) *is* reference counted and managed
1427513SDarren.Reed@Sun.COM * appropriately.
1437513SDarren.Reed@Sun.COM *
1447513SDarren.Reed@Sun.COM * A more detailed picture that describes how the family/event structures
1457513SDarren.Reed@Sun.COM * are linked together can be found in <sys/hook_impl.h>
146*12263SDarren.Reed@Sun.COM *
147*12263SDarren.Reed@Sun.COM * Notification callbacks.
148*12263SDarren.Reed@Sun.COM * =======================
149*12263SDarren.Reed@Sun.COM * For each of the hook stack, hook family and hook event, it is possible
150*12263SDarren.Reed@Sun.COM * to request notificatin of change to them. Why?
151*12263SDarren.Reed@Sun.COM * First, lets equate the hook stack to an IP instance, a hook family to
152*12263SDarren.Reed@Sun.COM * a network protocol and a hook event to IP packets on the input path.
153*12263SDarren.Reed@Sun.COM * If a kernel module wants to apply security from the very start of
154*12263SDarren.Reed@Sun.COM * things, it needs to know as soon as a new instance of networking
155*12263SDarren.Reed@Sun.COM * is initiated. Whilst for the global zone, it is taken for granted that
156*12263SDarren.Reed@Sun.COM * this instance will always exist before any interaction takes place,
157*12263SDarren.Reed@Sun.COM * that is not true for zones running with an exclusive networking instance.
158*12263SDarren.Reed@Sun.COM * Thus when a local zone is started and a new instance is created to support
159*12263SDarren.Reed@Sun.COM * that, parties that wish to monitor it and apply a security policy from
160*12263SDarren.Reed@Sun.COM * the onset need to be informed as early as possible - quite probably
161*12263SDarren.Reed@Sun.COM * before any networking is started by the zone's boot scripts.
162*12263SDarren.Reed@Sun.COM * Inside each instance, it is possible to have a number of network protocols
163*12263SDarren.Reed@Sun.COM * (hook families) in operation. Inside the context of the global zone,
164*12263SDarren.Reed@Sun.COM * it is possible to have code run before the kernel module providing the
165*12263SDarren.Reed@Sun.COM * IP networking is loaded. From here, to apply the appropriate security,
166*12263SDarren.Reed@Sun.COM * it is necessary to become informed of when IP is being configured into
167*12263SDarren.Reed@Sun.COM * the zone and this is done by registering a notification callback with
168*12263SDarren.Reed@Sun.COM * the hook stack for changes to it. The next step is to know when packets
169*12263SDarren.Reed@Sun.COM * can be received through the physical_in, etc, events. This is achieved
170*12263SDarren.Reed@Sun.COM * by registering a callback with the appropriate network protocol (or in
171*12263SDarren.Reed@Sun.COM * this file, the correct hook family.) Thus when IP finally attaches a
172*12263SDarren.Reed@Sun.COM * physical_in event to inet, the module looking to enforce a security
173*12263SDarren.Reed@Sun.COM * policy can become aware of it being present. Of course there's no
174*12263SDarren.Reed@Sun.COM * requirement for such a module to be present before all of the above
175*12263SDarren.Reed@Sun.COM * happens and in such a case, it is reasonable for the same module to
176*12263SDarren.Reed@Sun.COM * work after everything has been put in place. For this reason, when
177*12263SDarren.Reed@Sun.COM * a notification callback is added, a series of fake callback events
178*12263SDarren.Reed@Sun.COM * is generated to simulate the arrival of those entities. There is one
179*12263SDarren.Reed@Sun.COM * final series of callbacks that can be registered - those to monitor
180*12263SDarren.Reed@Sun.COM * actual hooks that are added or removed from an event. In practice,
181*12263SDarren.Reed@Sun.COM * this is useful when there are multiple kernel modules participating
182*12263SDarren.Reed@Sun.COM * in the processing of packets and there are behaviour dependencies
183*12263SDarren.Reed@Sun.COM * involved, such that one kernel module might only register its hook
184*12263SDarren.Reed@Sun.COM * if another is already present and also might want to remove its hook
185*12263SDarren.Reed@Sun.COM * when the other disappears.
186*12263SDarren.Reed@Sun.COM *
187*12263SDarren.Reed@Sun.COM * If you know a kernel module will not be loaded before the infrastructure
188*12263SDarren.Reed@Sun.COM * used in this file is present then it is not necessary to use this
189*12263SDarren.Reed@Sun.COM * notification callback mechanism.
1907513SDarren.Reed@Sun.COM */
1917513SDarren.Reed@Sun.COM
1927513SDarren.Reed@Sun.COM /*
1937513SDarren.Reed@Sun.COM * Locking
1947513SDarren.Reed@Sun.COM * =======
1957513SDarren.Reed@Sun.COM * The use of CVW_* macros to do locking is driven by the need to allow
1967513SDarren.Reed@Sun.COM * recursive locking with read locks when we're processing packets. This
1977513SDarren.Reed@Sun.COM * is necessary because various netinfo functions need to hold read locks,
1987513SDarren.Reed@Sun.COM * by design, as they can be called in or out of packet context.
1997513SDarren.Reed@Sun.COM */
2007513SDarren.Reed@Sun.COM /*
2012958Sdr146992 * Hook internal functions
2022958Sdr146992 */
2032958Sdr146992 static hook_int_t *hook_copy(hook_t *src);
2043448Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
2053448Sdh155122 hook_stack_t *hks);
2062958Sdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
2072958Sdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
2087513SDarren.Reed@Sun.COM static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi);
2092958Sdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
2103448Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
2117513SDarren.Reed@Sun.COM static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks);
2122958Sdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
2137513SDarren.Reed@Sun.COM static void hook_int_free(hook_int_t *hi, netstackid_t);
2142958Sdr146992 static void hook_init(void);
2153448Sdh155122 static void hook_fini(void);
2163448Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
2173448Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
2187513SDarren.Reed@Sun.COM static void hook_stack_shutdown(netstackid_t stackid, void *arg);
2197513SDarren.Reed@Sun.COM static int hook_insert(hook_int_head_t *head, hook_int_t *new);
2207513SDarren.Reed@Sun.COM static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new);
2217513SDarren.Reed@Sun.COM static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new);
2227513SDarren.Reed@Sun.COM static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name);
2237513SDarren.Reed@Sun.COM static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *);
2247513SDarren.Reed@Sun.COM static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *,
2257513SDarren.Reed@Sun.COM char *event, char *name, hook_notify_cmd_t cmd);
2267513SDarren.Reed@Sun.COM static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei,
2277513SDarren.Reed@Sun.COM hook_int_t *hi);
228*12263SDarren.Reed@Sun.COM static int hook_notify_register(hook_notify_head_t *head,
2297513SDarren.Reed@Sun.COM hook_notify_fn_t callback, void *arg);
230*12263SDarren.Reed@Sun.COM static int hook_notify_unregister(hook_notify_head_t *head,
231*12263SDarren.Reed@Sun.COM hook_notify_fn_t callback, void **);
2327513SDarren.Reed@Sun.COM static void hook_notify_run(hook_notify_head_t *head, char *family,
2337513SDarren.Reed@Sun.COM char *event, char *name, hook_notify_cmd_t cmd);
2347513SDarren.Reed@Sun.COM static void hook_stack_notify_run(hook_stack_t *hks, char *name,
2357513SDarren.Reed@Sun.COM hook_notify_cmd_t cmd);
2367513SDarren.Reed@Sun.COM static void hook_stack_remove(hook_stack_t *hks);
2377513SDarren.Reed@Sun.COM
2387513SDarren.Reed@Sun.COM /*
2397513SDarren.Reed@Sun.COM * A list of the hook stacks is kept here because we need to enable
2407513SDarren.Reed@Sun.COM * net_instance_notify_register() to be called during the creation
2417513SDarren.Reed@Sun.COM * of a new instance. Previously hook_stack_get() would just use
2427513SDarren.Reed@Sun.COM * the netstack functions for this work but they will return NULL
2437513SDarren.Reed@Sun.COM * until the zone has been fully initialised.
2447513SDarren.Reed@Sun.COM */
2457513SDarren.Reed@Sun.COM static hook_stack_head_t hook_stacks;
2467513SDarren.Reed@Sun.COM static kmutex_t hook_stack_lock;
2472958Sdr146992
2482958Sdr146992 /*
2492958Sdr146992 * Module entry points.
2502958Sdr146992 */
2512958Sdr146992 int
_init(void)2522958Sdr146992 _init(void)
2532958Sdr146992 {
2543448Sdh155122 int error;
2553448Sdh155122
2562958Sdr146992 hook_init();
2573448Sdh155122 error = mod_install(&modlinkage);
2583448Sdh155122 if (error != 0)
2593448Sdh155122 hook_fini();
2603448Sdh155122
2613448Sdh155122 return (error);
2622958Sdr146992 }
2632958Sdr146992
2642958Sdr146992 int
_fini(void)2652958Sdr146992 _fini(void)
2662958Sdr146992 {
2673448Sdh155122 int error;
2683448Sdh155122
2693448Sdh155122 error = mod_remove(&modlinkage);
2703448Sdh155122 if (error == 0)
2713448Sdh155122 hook_fini();
2723448Sdh155122
2733448Sdh155122 return (error);
2742958Sdr146992 }
2752958Sdr146992
2762958Sdr146992 int
_info(struct modinfo * modinfop)2772958Sdr146992 _info(struct modinfo *modinfop)
2782958Sdr146992 {
2792958Sdr146992 return (mod_info(&modlinkage, modinfop));
2802958Sdr146992 }
2812958Sdr146992
2822958Sdr146992 /*
2832958Sdr146992 * Function: hook_init
2842958Sdr146992 * Returns: None
2852958Sdr146992 * Parameters: None
2862958Sdr146992 *
2872958Sdr146992 * Initialize hooks
2882958Sdr146992 */
2892958Sdr146992 static void
hook_init(void)2902958Sdr146992 hook_init(void)
2912958Sdr146992 {
2927513SDarren.Reed@Sun.COM mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL);
2937513SDarren.Reed@Sun.COM SLIST_INIT(&hook_stacks);
2947513SDarren.Reed@Sun.COM
2953448Sdh155122 /*
2963448Sdh155122 * We want to be informed each time a stack is created or
2973448Sdh155122 * destroyed in the kernel.
2983448Sdh155122 */
2997513SDarren.Reed@Sun.COM netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown,
3003448Sdh155122 hook_stack_fini);
3013448Sdh155122 }
3023448Sdh155122
3033448Sdh155122 /*
3043448Sdh155122 * Function: hook_fini
3053448Sdh155122 * Returns: None
3063448Sdh155122 * Parameters: None
3073448Sdh155122 *
3083448Sdh155122 * Deinitialize hooks
3093448Sdh155122 */
3103448Sdh155122 static void
hook_fini(void)3113448Sdh155122 hook_fini(void)
3123448Sdh155122 {
3133448Sdh155122 netstack_unregister(NS_HOOK);
3147513SDarren.Reed@Sun.COM
3157513SDarren.Reed@Sun.COM mutex_destroy(&hook_stack_lock);
3167513SDarren.Reed@Sun.COM ASSERT(SLIST_EMPTY(&hook_stacks));
3177513SDarren.Reed@Sun.COM }
3187513SDarren.Reed@Sun.COM
3197513SDarren.Reed@Sun.COM /*
3207513SDarren.Reed@Sun.COM * Function: hook_wait_setflag
3217513SDarren.Reed@Sun.COM * Returns: -1 = setting flag is disallowed, 0 = flag set and did
3227513SDarren.Reed@Sun.COM * not have to wait (ie no lock droped), 1 = flag set but
3237513SDarren.Reed@Sun.COM * it was necessary to drop locks to set it.
3247513SDarren.Reed@Sun.COM * Parameters: waiter(I) - control data structure
3257513SDarren.Reed@Sun.COM * busyset(I) - set of flags that we don't want set while
3267513SDarren.Reed@Sun.COM * we are active.
3277513SDarren.Reed@Sun.COM * wanted(I) - flag associated with newflag to indicate
3287513SDarren.Reed@Sun.COM * what we want to do.
3297513SDarren.Reed@Sun.COM * newflag(I) - the new ACTIVE flag we want to set that
3307513SDarren.Reed@Sun.COM * indicates what we are doing.
3317513SDarren.Reed@Sun.COM *
3327513SDarren.Reed@Sun.COM * The set of functions hook_wait_* implement an API that builds on top of
3337513SDarren.Reed@Sun.COM * the kcondvar_t to provide controlled execution through a critical region.
3347513SDarren.Reed@Sun.COM * For each flag that indicates work is being done (FWF_*_ACTIVE) there is
3357513SDarren.Reed@Sun.COM * also a flag that we set to indicate that we want to do it (FWF_*_WANTED).
3367513SDarren.Reed@Sun.COM * The combination of flags is required as when this function exits to do
3377513SDarren.Reed@Sun.COM * the task, the structure is then free for another caller to use and
338*12263SDarren.Reed@Sun.COM * to indicate that it wants to do work. The flags used when a caller wants
339*12263SDarren.Reed@Sun.COM * to destroy an object take precedence over those that are used for making
340*12263SDarren.Reed@Sun.COM * changes to it (add/remove.) In this case, we don't try to secure the
341*12263SDarren.Reed@Sun.COM * ability to run and return with an error.
342*12263SDarren.Reed@Sun.COM *
343*12263SDarren.Reed@Sun.COM * "wantedset" is used here to determine who has the right to clear the
344*12263SDarren.Reed@Sun.COM * wanted but from the fw_flags set: only he that sets the flag has the
345*12263SDarren.Reed@Sun.COM * right to clear it at the bottom of the loop, even if someone else
346*12263SDarren.Reed@Sun.COM * wants to set it.
3477513SDarren.Reed@Sun.COM *
3487513SDarren.Reed@Sun.COM * wanted - the FWF_*_WANTED flag that describes the action being requested
3497513SDarren.Reed@Sun.COM * busyset- the set of FWF_* flags we don't want set when we run
3507513SDarren.Reed@Sun.COM * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy
3517513SDarren.Reed@Sun.COM */
3527513SDarren.Reed@Sun.COM int
hook_wait_setflag(flagwait_t * waiter,uint32_t busyset,fwflag_t wanted,fwflag_t newflag)3537513SDarren.Reed@Sun.COM hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted,
3547513SDarren.Reed@Sun.COM fwflag_t newflag)
3557513SDarren.Reed@Sun.COM {
356*12263SDarren.Reed@Sun.COM boolean_t wantedset;
3577513SDarren.Reed@Sun.COM int waited = 0;
3587513SDarren.Reed@Sun.COM
3597513SDarren.Reed@Sun.COM mutex_enter(&waiter->fw_lock);
3607513SDarren.Reed@Sun.COM if (waiter->fw_flags & FWF_DESTROY) {
361*12263SDarren.Reed@Sun.COM cv_signal(&waiter->fw_cv);
3627513SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
3637513SDarren.Reed@Sun.COM return (-1);
3647513SDarren.Reed@Sun.COM }
3657513SDarren.Reed@Sun.COM while (waiter->fw_flags & busyset) {
366*12263SDarren.Reed@Sun.COM wantedset = ((waiter->fw_flags & wanted) == wanted);
367*12263SDarren.Reed@Sun.COM if (!wantedset)
368*12263SDarren.Reed@Sun.COM waiter->fw_flags |= wanted;
3697513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(waiter->fw_owner);
3707513SDarren.Reed@Sun.COM cv_wait(&waiter->fw_cv, &waiter->fw_lock);
371*12263SDarren.Reed@Sun.COM /*
372*12263SDarren.Reed@Sun.COM * This lock needs to be dropped here to preserve the order
373*12263SDarren.Reed@Sun.COM * of acquisition that is fw_owner followed by fw_lock, else
374*12263SDarren.Reed@Sun.COM * we can deadlock.
375*12263SDarren.Reed@Sun.COM */
376*12263SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
3777513SDarren.Reed@Sun.COM waited = 1;
3787513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(waiter->fw_owner);
379*12263SDarren.Reed@Sun.COM mutex_enter(&waiter->fw_lock);
380*12263SDarren.Reed@Sun.COM if (!wantedset)
381*12263SDarren.Reed@Sun.COM waiter->fw_flags &= ~wanted;
3827513SDarren.Reed@Sun.COM if (waiter->fw_flags & FWF_DESTROY) {
383*12263SDarren.Reed@Sun.COM cv_signal(&waiter->fw_cv);
3847513SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
3857513SDarren.Reed@Sun.COM return (-1);
3867513SDarren.Reed@Sun.COM }
3877513SDarren.Reed@Sun.COM }
3887513SDarren.Reed@Sun.COM waiter->fw_flags &= ~wanted;
389*12263SDarren.Reed@Sun.COM ASSERT((waiter->fw_flags & wanted) == 0);
390*12263SDarren.Reed@Sun.COM ASSERT((waiter->fw_flags & newflag) == 0);
3917513SDarren.Reed@Sun.COM waiter->fw_flags |= newflag;
3927513SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
3937513SDarren.Reed@Sun.COM return (waited);
3947513SDarren.Reed@Sun.COM }
3957513SDarren.Reed@Sun.COM
3967513SDarren.Reed@Sun.COM /*
3977513SDarren.Reed@Sun.COM * Function: hook_wait_unsetflag
3987513SDarren.Reed@Sun.COM * Returns: None
3997513SDarren.Reed@Sun.COM * Parameters: waiter(I) - control data structure
4007513SDarren.Reed@Sun.COM * oldflag(I) - flag to reset
4017513SDarren.Reed@Sun.COM *
4027513SDarren.Reed@Sun.COM * Turn off the bit that we had set to run and let others know that
4037513SDarren.Reed@Sun.COM * they should now check to see if they can run.
4047513SDarren.Reed@Sun.COM */
4057513SDarren.Reed@Sun.COM void
hook_wait_unsetflag(flagwait_t * waiter,fwflag_t oldflag)406*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(flagwait_t *waiter, fwflag_t oldflag)
4077513SDarren.Reed@Sun.COM {
4087513SDarren.Reed@Sun.COM mutex_enter(&waiter->fw_lock);
4097513SDarren.Reed@Sun.COM waiter->fw_flags &= ~oldflag;
4107513SDarren.Reed@Sun.COM cv_signal(&waiter->fw_cv);
4117513SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
4127513SDarren.Reed@Sun.COM }
4137513SDarren.Reed@Sun.COM
4147513SDarren.Reed@Sun.COM /*
4157513SDarren.Reed@Sun.COM * Function: hook_wait_destroy
4167513SDarren.Reed@Sun.COM * Returns: None
4177513SDarren.Reed@Sun.COM * Parameters: waiter(I) - control data structure
4187513SDarren.Reed@Sun.COM *
4197513SDarren.Reed@Sun.COM * Since outer locking (on fw_owner) should ensure that only one function
4207513SDarren.Reed@Sun.COM * at a time gets to call hook_wait_destroy() on a given object, there is
4217513SDarren.Reed@Sun.COM * no need to guard against setting FWF_DESTROY_WANTED already being set.
4227513SDarren.Reed@Sun.COM * It is, however, necessary to wait for all activity on the owning
4237513SDarren.Reed@Sun.COM * structure to cease.
4247513SDarren.Reed@Sun.COM */
425*12263SDarren.Reed@Sun.COM int
hook_wait_destroy(flagwait_t * waiter)4267513SDarren.Reed@Sun.COM hook_wait_destroy(flagwait_t *waiter)
4277513SDarren.Reed@Sun.COM {
4287513SDarren.Reed@Sun.COM ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0);
429*12263SDarren.Reed@Sun.COM mutex_enter(&waiter->fw_lock);
430*12263SDarren.Reed@Sun.COM if (waiter->fw_flags & FWF_DESTROY_WANTED) {
431*12263SDarren.Reed@Sun.COM cv_signal(&waiter->fw_cv);
432*12263SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
433*12263SDarren.Reed@Sun.COM return (EINPROGRESS);
434*12263SDarren.Reed@Sun.COM }
4357513SDarren.Reed@Sun.COM waiter->fw_flags |= FWF_DESTROY_WANTED;
4367513SDarren.Reed@Sun.COM while (!FWF_DESTROY_OK(waiter)) {
4377513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(waiter->fw_owner);
4387513SDarren.Reed@Sun.COM cv_wait(&waiter->fw_cv, &waiter->fw_lock);
4397513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(waiter->fw_owner);
4407513SDarren.Reed@Sun.COM }
4417513SDarren.Reed@Sun.COM /*
4427513SDarren.Reed@Sun.COM * There should now be nothing else using "waiter" or its
4437513SDarren.Reed@Sun.COM * owner, so we can safely assign here without risk of wiiping
4447513SDarren.Reed@Sun.COM * out someone's bit.
4457513SDarren.Reed@Sun.COM */
4467513SDarren.Reed@Sun.COM waiter->fw_flags = FWF_DESTROY_ACTIVE;
447*12263SDarren.Reed@Sun.COM cv_signal(&waiter->fw_cv);
448*12263SDarren.Reed@Sun.COM mutex_exit(&waiter->fw_lock);
449*12263SDarren.Reed@Sun.COM
450*12263SDarren.Reed@Sun.COM return (0);
4517513SDarren.Reed@Sun.COM }
4527513SDarren.Reed@Sun.COM
4537513SDarren.Reed@Sun.COM /*
4547513SDarren.Reed@Sun.COM * Function: hook_wait_init
4557513SDarren.Reed@Sun.COM * Returns: None
4567513SDarren.Reed@Sun.COM * Parameters: waiter(I) - control data structure
4577513SDarren.Reed@Sun.COM * ownder(I) - pointer to lock that the owner of this
4587513SDarren.Reed@Sun.COM * waiter uses
4597513SDarren.Reed@Sun.COM *
4607513SDarren.Reed@Sun.COM * "owner" gets passed in here so that when we need to call cv_wait,
4617513SDarren.Reed@Sun.COM * for example in hook_wait_setflag(), we can drop the lock for the
4627513SDarren.Reed@Sun.COM * next layer out, which is likely to be held in an exclusive manner.
4637513SDarren.Reed@Sun.COM */
4647513SDarren.Reed@Sun.COM void
hook_wait_init(flagwait_t * waiter,cvwaitlock_t * owner)4657513SDarren.Reed@Sun.COM hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner)
4667513SDarren.Reed@Sun.COM {
4677513SDarren.Reed@Sun.COM cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL);
4687513SDarren.Reed@Sun.COM mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL);
4697513SDarren.Reed@Sun.COM waiter->fw_flags = FWF_NONE;
4707513SDarren.Reed@Sun.COM waiter->fw_owner = owner;
4712958Sdr146992 }
4722958Sdr146992
4733448Sdh155122 /*
474*12263SDarren.Reed@Sun.COM * Function: hook_stack_init
475*12263SDarren.Reed@Sun.COM * Returns: void * - pointer to new hook stack structure
476*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - identifier for the network instance that owns this
477*12263SDarren.Reed@Sun.COM * ns(I) - pointer to the network instance data structure
478*12263SDarren.Reed@Sun.COM *
479*12263SDarren.Reed@Sun.COM * Allocate and initialize the hook stack instance. This function is not
480*12263SDarren.Reed@Sun.COM * allowed to fail, so KM_SLEEP is used here when allocating memory. The
481*12263SDarren.Reed@Sun.COM * value returned is passed back into the shutdown and destroy hooks.
4823448Sdh155122 */
4833448Sdh155122 /*ARGSUSED*/
4843448Sdh155122 static void *
hook_stack_init(netstackid_t stackid,netstack_t * ns)4853448Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
4863448Sdh155122 {
4873448Sdh155122 hook_stack_t *hks;
4883448Sdh155122
4893448Sdh155122 #ifdef NS_DEBUG
4903448Sdh155122 printf("hook_stack_init(stack %d)\n", stackid);
4913448Sdh155122 #endif
4923448Sdh155122
4933448Sdh155122 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
4947513SDarren.Reed@Sun.COM hks->hks_netstack = ns;
4957513SDarren.Reed@Sun.COM hks->hks_netstackid = stackid;
4963448Sdh155122
4977513SDarren.Reed@Sun.COM CVW_INIT(&hks->hks_lock);
4987513SDarren.Reed@Sun.COM TAILQ_INIT(&hks->hks_nhead);
4993448Sdh155122 SLIST_INIT(&hks->hks_familylist);
5003448Sdh155122
5017513SDarren.Reed@Sun.COM hook_wait_init(&hks->hks_waiter, &hks->hks_lock);
5027513SDarren.Reed@Sun.COM
5037513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
5047513SDarren.Reed@Sun.COM SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry);
5057513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
5067513SDarren.Reed@Sun.COM
5073448Sdh155122 return (hks);
5083448Sdh155122 }
5093448Sdh155122
5107915SDarren.Reed@Sun.COM /*
511*12263SDarren.Reed@Sun.COM * Function: hook_stack_shutdown
512*12263SDarren.Reed@Sun.COM * Returns: void
513*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - identifier for the network instance that owns this
514*12263SDarren.Reed@Sun.COM * arg(I) - pointer returned by hook_stack_init
515*12263SDarren.Reed@Sun.COM *
5167915SDarren.Reed@Sun.COM * Set the shutdown flag to indicate that we should stop accepting new
517*12263SDarren.Reed@Sun.COM * register calls as we're now in the cleanup process. The cleanup is a
518*12263SDarren.Reed@Sun.COM * two stage process and we're not required to free any memory here.
519*12263SDarren.Reed@Sun.COM *
520*12263SDarren.Reed@Sun.COM * The curious would wonder why isn't there any code that walks through
521*12263SDarren.Reed@Sun.COM * all of the data structures and sets the flag(s) there? The answer is
522*12263SDarren.Reed@Sun.COM * that it is expected that this will happen when the zone shutdown calls
523*12263SDarren.Reed@Sun.COM * the shutdown callbacks for other modules that they will initiate the
524*12263SDarren.Reed@Sun.COM * free'ing and shutdown of the hooks themselves.
5257915SDarren.Reed@Sun.COM */
5267513SDarren.Reed@Sun.COM /*ARGSUSED*/
5277513SDarren.Reed@Sun.COM static void
hook_stack_shutdown(netstackid_t stackid,void * arg)5287513SDarren.Reed@Sun.COM hook_stack_shutdown(netstackid_t stackid, void *arg)
5297513SDarren.Reed@Sun.COM {
5307513SDarren.Reed@Sun.COM hook_stack_t *hks = (hook_stack_t *)arg;
5317513SDarren.Reed@Sun.COM
5327513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
5337513SDarren.Reed@Sun.COM /*
5347513SDarren.Reed@Sun.COM * Once this flag gets set to one, no more additions are allowed
5357513SDarren.Reed@Sun.COM * to any of the structures that make up this stack.
5367513SDarren.Reed@Sun.COM */
5377513SDarren.Reed@Sun.COM hks->hks_shutdown = 1;
5387513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
5397513SDarren.Reed@Sun.COM }
5407513SDarren.Reed@Sun.COM
5413448Sdh155122 /*
542*12263SDarren.Reed@Sun.COM * Function: hook_stack_destroy
543*12263SDarren.Reed@Sun.COM * Returns: void
544*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - identifier for the network instance that owns this
545*12263SDarren.Reed@Sun.COM * arg(I) - pointer returned by hook_stack_init
546*12263SDarren.Reed@Sun.COM *
5473448Sdh155122 * Free the hook stack instance.
548*12263SDarren.Reed@Sun.COM *
549*12263SDarren.Reed@Sun.COM * The rationale for the shutdown being lazy (see the comment above for
550*12263SDarren.Reed@Sun.COM * hook_stack_shutdown) also applies to the destroy being lazy. Only if
551*12263SDarren.Reed@Sun.COM * the hook_stack_t data structure is unused will it go away. Else it
552*12263SDarren.Reed@Sun.COM * is left up to the last user of a data structure to actually free it.
5533448Sdh155122 */
5543448Sdh155122 /*ARGSUSED*/
5553448Sdh155122 static void
hook_stack_fini(netstackid_t stackid,void * arg)5563448Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
5573448Sdh155122 {
5587513SDarren.Reed@Sun.COM hook_stack_t *hks = (hook_stack_t *)arg;
5597513SDarren.Reed@Sun.COM
5607513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
5617513SDarren.Reed@Sun.COM hks->hks_shutdown = 2;
5627513SDarren.Reed@Sun.COM hook_stack_remove(hks);
5637513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
5647513SDarren.Reed@Sun.COM }
5657513SDarren.Reed@Sun.COM
5667513SDarren.Reed@Sun.COM /*
567*12263SDarren.Reed@Sun.COM * Function: hook_stack_remove
568*12263SDarren.Reed@Sun.COM * Returns: void
569*12263SDarren.Reed@Sun.COM * Parameters: hks(I) - pointer to an instance of a hook_stack_t
570*12263SDarren.Reed@Sun.COM *
5717513SDarren.Reed@Sun.COM * This function assumes that it is called with hook_stack_lock held.
5727513SDarren.Reed@Sun.COM * It functions differently to hook_family/event_remove in that it does
5737513SDarren.Reed@Sun.COM * the checks to see if it can be removed. This difference exists
5747513SDarren.Reed@Sun.COM * because this structure has nothing higher up that depends on it.
5757513SDarren.Reed@Sun.COM */
5767513SDarren.Reed@Sun.COM static void
hook_stack_remove(hook_stack_t * hks)5777513SDarren.Reed@Sun.COM hook_stack_remove(hook_stack_t *hks)
5787513SDarren.Reed@Sun.COM {
5797513SDarren.Reed@Sun.COM
5807513SDarren.Reed@Sun.COM ASSERT(mutex_owned(&hook_stack_lock));
5817513SDarren.Reed@Sun.COM
5827513SDarren.Reed@Sun.COM /*
5837513SDarren.Reed@Sun.COM * Is the structure still in use?
5847513SDarren.Reed@Sun.COM */
5857513SDarren.Reed@Sun.COM if (!SLIST_EMPTY(&hks->hks_familylist) ||
5867513SDarren.Reed@Sun.COM !TAILQ_EMPTY(&hks->hks_nhead))
5877513SDarren.Reed@Sun.COM return;
5887513SDarren.Reed@Sun.COM
5897513SDarren.Reed@Sun.COM SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry);
5907513SDarren.Reed@Sun.COM
591*12263SDarren.Reed@Sun.COM VERIFY(hook_wait_destroy(&hks->hks_waiter) == 0);
5927513SDarren.Reed@Sun.COM CVW_DESTROY(&hks->hks_lock);
5933448Sdh155122 kmem_free(hks, sizeof (*hks));
5943448Sdh155122 }
5952958Sdr146992
596*12263SDarren.Reed@Sun.COM /*
597*12263SDarren.Reed@Sun.COM * Function: hook_stack_get
598*12263SDarren.Reed@Sun.COM * Returns: hook_stack_t * - NULL if not found, else matching instance
599*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - instance id to search for
600*12263SDarren.Reed@Sun.COM *
601*12263SDarren.Reed@Sun.COM * Search the list of currently active hook_stack_t structures for one that
602*12263SDarren.Reed@Sun.COM * has a matching netstackid_t to the value passed in. The linked list can
603*12263SDarren.Reed@Sun.COM * only ever have at most one match for this value.
604*12263SDarren.Reed@Sun.COM */
6057513SDarren.Reed@Sun.COM static hook_stack_t *
hook_stack_get(netstackid_t stackid)6067513SDarren.Reed@Sun.COM hook_stack_get(netstackid_t stackid)
6077513SDarren.Reed@Sun.COM {
6087513SDarren.Reed@Sun.COM hook_stack_t *hks;
6097513SDarren.Reed@Sun.COM
6107513SDarren.Reed@Sun.COM SLIST_FOREACH(hks, &hook_stacks, hks_entry) {
6117513SDarren.Reed@Sun.COM if (hks->hks_netstackid == stackid)
6127513SDarren.Reed@Sun.COM break;
6137513SDarren.Reed@Sun.COM }
6147513SDarren.Reed@Sun.COM
6157513SDarren.Reed@Sun.COM return (hks);
6167513SDarren.Reed@Sun.COM }
6177513SDarren.Reed@Sun.COM
6187513SDarren.Reed@Sun.COM /*
6197513SDarren.Reed@Sun.COM * Function: hook_stack_notify_register
620*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
6217513SDarren.Reed@Sun.COM * Parameters: stackid(I) - netstack identifier
6227513SDarren.Reed@Sun.COM * callback(I)- function to be called
6237513SDarren.Reed@Sun.COM * arg(I) - arg to provide callback when it is called
6247513SDarren.Reed@Sun.COM *
6257513SDarren.Reed@Sun.COM * If we're not shutting down this instance, append a new function to the
6267513SDarren.Reed@Sun.COM * list of those to call when a new family of hooks is added to this stack.
627*12263SDarren.Reed@Sun.COM * If the function can be successfully added to the list of callbacks
628*12263SDarren.Reed@Sun.COM * activated when there is a change to the stack (addition or removal of
629*12263SDarren.Reed@Sun.COM * a hook family) then generate a fake HN_REGISTER event by directly
630*12263SDarren.Reed@Sun.COM * calling the callback with the relevant information for each hook
631*12263SDarren.Reed@Sun.COM * family that currently exists (and isn't being shutdown.)
6327513SDarren.Reed@Sun.COM */
6337513SDarren.Reed@Sun.COM int
hook_stack_notify_register(netstackid_t stackid,hook_notify_fn_t callback,void * arg)6347513SDarren.Reed@Sun.COM hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback,
6357513SDarren.Reed@Sun.COM void *arg)
6367513SDarren.Reed@Sun.COM {
637*12263SDarren.Reed@Sun.COM hook_family_int_t *hfi;
6387513SDarren.Reed@Sun.COM hook_stack_t *hks;
639*12263SDarren.Reed@Sun.COM boolean_t canrun;
640*12263SDarren.Reed@Sun.COM char buffer[16];
6417513SDarren.Reed@Sun.COM int error;
6427513SDarren.Reed@Sun.COM
643*12263SDarren.Reed@Sun.COM ASSERT(callback != NULL);
644*12263SDarren.Reed@Sun.COM
645*12263SDarren.Reed@Sun.COM canrun = B_FALSE;
6467513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
6477513SDarren.Reed@Sun.COM hks = hook_stack_get(stackid);
6487513SDarren.Reed@Sun.COM if (hks != NULL) {
6497513SDarren.Reed@Sun.COM if (hks->hks_shutdown != 0) {
6507513SDarren.Reed@Sun.COM error = ESHUTDOWN;
6517513SDarren.Reed@Sun.COM } else {
652*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
653*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hks->hks_waiter,
654*12263SDarren.Reed@Sun.COM FWF_ADD_WAIT_MASK, FWF_ADD_WANTED,
655*12263SDarren.Reed@Sun.COM FWF_ADD_ACTIVE) != -1);
656*12263SDarren.Reed@Sun.COM error = hook_notify_register(&hks->hks_nhead,
657*12263SDarren.Reed@Sun.COM callback, arg);
658*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
6597513SDarren.Reed@Sun.COM }
6607513SDarren.Reed@Sun.COM } else {
6617513SDarren.Reed@Sun.COM error = ESRCH;
6627513SDarren.Reed@Sun.COM }
6637513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
6647513SDarren.Reed@Sun.COM
665*12263SDarren.Reed@Sun.COM if (error == 0 && canrun) {
666*12263SDarren.Reed@Sun.COM /*
667*12263SDarren.Reed@Sun.COM * Generate fake register event for callback that
668*12263SDarren.Reed@Sun.COM * is being added, letting it know everything that
669*12263SDarren.Reed@Sun.COM * already exists.
670*12263SDarren.Reed@Sun.COM */
671*12263SDarren.Reed@Sun.COM (void) snprintf(buffer, sizeof (buffer), "%u",
672*12263SDarren.Reed@Sun.COM hks->hks_netstackid);
673*12263SDarren.Reed@Sun.COM
674*12263SDarren.Reed@Sun.COM SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
675*12263SDarren.Reed@Sun.COM if (hfi->hfi_condemned || hfi->hfi_shutdown)
676*12263SDarren.Reed@Sun.COM continue;
677*12263SDarren.Reed@Sun.COM callback(HN_REGISTER, arg, buffer, NULL,
678*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name);
679*12263SDarren.Reed@Sun.COM }
680*12263SDarren.Reed@Sun.COM }
681*12263SDarren.Reed@Sun.COM
682*12263SDarren.Reed@Sun.COM if (canrun)
683*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
684*12263SDarren.Reed@Sun.COM
6857513SDarren.Reed@Sun.COM return (error);
6867513SDarren.Reed@Sun.COM }
6877513SDarren.Reed@Sun.COM
6887513SDarren.Reed@Sun.COM /*
6897513SDarren.Reed@Sun.COM * Function: hook_stack_notify_unregister
690*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
691*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - netstack identifier
6927513SDarren.Reed@Sun.COM * callback(I) - function to be called
6937513SDarren.Reed@Sun.COM *
6947513SDarren.Reed@Sun.COM * Attempt to remove a registered function from a hook stack's list of
6957513SDarren.Reed@Sun.COM * callbacks to activiate when protocols are added/deleted.
696*12263SDarren.Reed@Sun.COM * As with hook_stack_notify_register, if all things are going well then
697*12263SDarren.Reed@Sun.COM * a fake unregister event is delivered to the callback being removed
698*12263SDarren.Reed@Sun.COM * for each hook family that presently exists.
6997513SDarren.Reed@Sun.COM */
7007513SDarren.Reed@Sun.COM int
hook_stack_notify_unregister(netstackid_t stackid,hook_notify_fn_t callback)7017513SDarren.Reed@Sun.COM hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback)
7027513SDarren.Reed@Sun.COM {
703*12263SDarren.Reed@Sun.COM hook_family_int_t *hfi;
7047513SDarren.Reed@Sun.COM hook_stack_t *hks;
705*12263SDarren.Reed@Sun.COM boolean_t canrun;
706*12263SDarren.Reed@Sun.COM char buffer[16];
707*12263SDarren.Reed@Sun.COM void *arg;
7087513SDarren.Reed@Sun.COM int error;
7097513SDarren.Reed@Sun.COM
7107513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
7117513SDarren.Reed@Sun.COM hks = hook_stack_get(stackid);
7127513SDarren.Reed@Sun.COM if (hks != NULL) {
713*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
714*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
715*12263SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
716*12263SDarren.Reed@Sun.COM
717*12263SDarren.Reed@Sun.COM error = hook_notify_unregister(&hks->hks_nhead, callback, &arg);
718*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
7197513SDarren.Reed@Sun.COM } else {
7207513SDarren.Reed@Sun.COM error = ESRCH;
7217513SDarren.Reed@Sun.COM }
7227513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
7237513SDarren.Reed@Sun.COM
724*12263SDarren.Reed@Sun.COM if (error == 0) {
725*12263SDarren.Reed@Sun.COM if (canrun) {
726*12263SDarren.Reed@Sun.COM /*
727*12263SDarren.Reed@Sun.COM * Generate fake unregister event for callback that
728*12263SDarren.Reed@Sun.COM * is being removed, letting it know everything that
729*12263SDarren.Reed@Sun.COM * currently exists is now "disappearing."
730*12263SDarren.Reed@Sun.COM */
731*12263SDarren.Reed@Sun.COM (void) snprintf(buffer, sizeof (buffer), "%u",
732*12263SDarren.Reed@Sun.COM hks->hks_netstackid);
733*12263SDarren.Reed@Sun.COM
734*12263SDarren.Reed@Sun.COM SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
735*12263SDarren.Reed@Sun.COM callback(HN_UNREGISTER, arg, buffer, NULL,
736*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name);
737*12263SDarren.Reed@Sun.COM }
738*12263SDarren.Reed@Sun.COM
739*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
740*12263SDarren.Reed@Sun.COM }
741*12263SDarren.Reed@Sun.COM
742*12263SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
743*12263SDarren.Reed@Sun.COM hks = hook_stack_get(stackid);
744*12263SDarren.Reed@Sun.COM if ((error == 0) && (hks->hks_shutdown == 2))
745*12263SDarren.Reed@Sun.COM hook_stack_remove(hks);
746*12263SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
747*12263SDarren.Reed@Sun.COM }
748*12263SDarren.Reed@Sun.COM
7497513SDarren.Reed@Sun.COM return (error);
7507513SDarren.Reed@Sun.COM }
7517513SDarren.Reed@Sun.COM
7527513SDarren.Reed@Sun.COM /*
7537513SDarren.Reed@Sun.COM * Function: hook_stack_notify_run
7547513SDarren.Reed@Sun.COM * Returns: None
7557513SDarren.Reed@Sun.COM * Parameters: hks(I) - hook stack pointer to execute callbacks for
7567513SDarren.Reed@Sun.COM * name(I) - name of a hook family
7577513SDarren.Reed@Sun.COM * cmd(I) - either HN_UNREGISTER or HN_REGISTER
7587513SDarren.Reed@Sun.COM *
7597513SDarren.Reed@Sun.COM * Run through the list of callbacks on the hook stack to be called when
7607513SDarren.Reed@Sun.COM * a new hook family is added
7617513SDarren.Reed@Sun.COM *
762*12263SDarren.Reed@Sun.COM * As hook_notify_run() expects 3 names, one for the family that is associated
763*12263SDarren.Reed@Sun.COM * with the cmd (HN_REGISTER or HN_UNREGISTER), one for the event and one
764*12263SDarren.Reed@Sun.COM * for the object being introduced and we really only have one name (that
765*12263SDarren.Reed@Sun.COM * of the new hook family), fake the hook stack's name by converting the
766*12263SDarren.Reed@Sun.COM * integer to a string and for the event just pass NULL.
7677513SDarren.Reed@Sun.COM */
7687513SDarren.Reed@Sun.COM static void
hook_stack_notify_run(hook_stack_t * hks,char * name,hook_notify_cmd_t cmd)7697513SDarren.Reed@Sun.COM hook_stack_notify_run(hook_stack_t *hks, char *name,
7707513SDarren.Reed@Sun.COM hook_notify_cmd_t cmd)
7717513SDarren.Reed@Sun.COM {
7727513SDarren.Reed@Sun.COM char buffer[16];
7737513SDarren.Reed@Sun.COM
774*12263SDarren.Reed@Sun.COM ASSERT(hks != NULL);
775*12263SDarren.Reed@Sun.COM ASSERT(name != NULL);
776*12263SDarren.Reed@Sun.COM
7777513SDarren.Reed@Sun.COM (void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid);
7787513SDarren.Reed@Sun.COM
7797513SDarren.Reed@Sun.COM hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd);
7807513SDarren.Reed@Sun.COM }
7817513SDarren.Reed@Sun.COM
7822958Sdr146992 /*
7832958Sdr146992 * Function: hook_run
784*12263SDarren.Reed@Sun.COM * Returns: int - return value according to callback func
7852958Sdr146992 * Parameters: token(I) - event pointer
786*12263SDarren.Reed@Sun.COM * info(I) - message
7872958Sdr146992 *
7882958Sdr146992 * Run hooks for specific provider. The hooks registered are stepped through
7892958Sdr146992 * until either the end of the list is reached or a hook function returns a
7902958Sdr146992 * non-zero value. If a non-zero value is returned from a hook function, we
7912958Sdr146992 * return that value back to our caller. By design, a hook function can be
7922958Sdr146992 * called more than once, simultaneously.
7932958Sdr146992 */
7942958Sdr146992 int
hook_run(hook_family_int_t * hfi,hook_event_token_t token,hook_data_t info)7957513SDarren.Reed@Sun.COM hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info)
7962958Sdr146992 {
7977513SDarren.Reed@Sun.COM hook_event_int_t *hei;
7982958Sdr146992 hook_int_t *hi;
7992958Sdr146992 int rval = 0;
8002958Sdr146992
8012958Sdr146992 ASSERT(token != NULL);
8022958Sdr146992
8032958Sdr146992 hei = (hook_event_int_t *)token;
8042958Sdr146992 DTRACE_PROBE2(hook__run__start,
8052958Sdr146992 hook_event_token_t, token,
8062958Sdr146992 hook_data_t, info);
8072958Sdr146992
8087513SDarren.Reed@Sun.COM /*
809*12263SDarren.Reed@Sun.COM * If we consider that this function is only called from within the
810*12263SDarren.Reed@Sun.COM * stack while an instance is currently active,
8117513SDarren.Reed@Sun.COM */
8127513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hfi->hfi_lock);
8132958Sdr146992
8142958Sdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
8152958Sdr146992 ASSERT(hi->hi_hook.h_func != NULL);
8162958Sdr146992 DTRACE_PROBE3(hook__func__start,
8172958Sdr146992 hook_event_token_t, token,
8182958Sdr146992 hook_data_t, info,
8192958Sdr146992 hook_int_t *, hi);
8207513SDarren.Reed@Sun.COM rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg);
8212958Sdr146992 DTRACE_PROBE4(hook__func__end,
8222958Sdr146992 hook_event_token_t, token,
8232958Sdr146992 hook_data_t, info,
8242958Sdr146992 hook_int_t *, hi,
8252958Sdr146992 int, rval);
8267513SDarren.Reed@Sun.COM hi->hi_kstats.hook_hits.value.ui64++;
8272958Sdr146992 if (rval != 0)
8282958Sdr146992 break;
8292958Sdr146992 }
8302958Sdr146992
8317513SDarren.Reed@Sun.COM hei->hei_kstats.events.value.ui64++;
8327513SDarren.Reed@Sun.COM
8337513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
8342958Sdr146992
8352958Sdr146992 DTRACE_PROBE3(hook__run__end,
8362958Sdr146992 hook_event_token_t, token,
8372958Sdr146992 hook_data_t, info,
8382958Sdr146992 hook_int_t *, hi);
8392958Sdr146992
8402958Sdr146992 return (rval);
8412958Sdr146992 }
8422958Sdr146992
8432958Sdr146992 /*
8442958Sdr146992 * Function: hook_family_add
8452958Sdr146992 * Returns: internal family pointer - NULL = Fail
846*12263SDarren.Reed@Sun.COM * Parameters: hf(I) - family pointer
847*12263SDarren.Reed@Sun.COM * hks(I) - pointer to an instance of a hook_stack_t
848*12263SDarren.Reed@Sun.COM * store(O) - where returned pointer will be stored
8492958Sdr146992 *
850*12263SDarren.Reed@Sun.COM * Add new family to the family list. The requirements for the addition to
851*12263SDarren.Reed@Sun.COM * succeed are that the family name must not already be registered and that
852*12263SDarren.Reed@Sun.COM * the hook stack is not being shutdown.
853*12263SDarren.Reed@Sun.COM * If store is non-NULL, it is expected to be a pointer to the same variable
854*12263SDarren.Reed@Sun.COM * that is awaiting to be assigned the return value of this function.
855*12263SDarren.Reed@Sun.COM * In its current use, the returned value is assigned to netd_hooks in
856*12263SDarren.Reed@Sun.COM * net_family_register. The use of "store" allows the return value to be
857*12263SDarren.Reed@Sun.COM * used before this function returns. How can this happen? Through the
858*12263SDarren.Reed@Sun.COM * callbacks that can be activated at the bottom of this function, when
859*12263SDarren.Reed@Sun.COM * hook_stack_notify_run is called.
8602958Sdr146992 */
8612958Sdr146992 hook_family_int_t *
hook_family_add(hook_family_t * hf,hook_stack_t * hks,void ** store)862*12263SDarren.Reed@Sun.COM hook_family_add(hook_family_t *hf, hook_stack_t *hks, void **store)
8632958Sdr146992 {
8642958Sdr146992 hook_family_int_t *hfi, *new;
8652958Sdr146992
8662958Sdr146992 ASSERT(hf != NULL);
8672958Sdr146992 ASSERT(hf->hf_name != NULL);
8682958Sdr146992
8692958Sdr146992 new = hook_family_copy(hf);
8702958Sdr146992 if (new == NULL)
8712958Sdr146992 return (NULL);
8722958Sdr146992
8737513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
8747513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
8757513SDarren.Reed@Sun.COM
8767513SDarren.Reed@Sun.COM if (hks->hks_shutdown != 0) {
8777513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
8787513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
8797513SDarren.Reed@Sun.COM hook_family_free(new, NULL);
8807513SDarren.Reed@Sun.COM return (NULL);
8817513SDarren.Reed@Sun.COM }
8822958Sdr146992
8832958Sdr146992 /* search family list */
8843448Sdh155122 hfi = hook_family_find(hf->hf_name, hks);
8852958Sdr146992 if (hfi != NULL) {
8867513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
8877513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
8887513SDarren.Reed@Sun.COM hook_family_free(new, NULL);
8892958Sdr146992 return (NULL);
8902958Sdr146992 }
8912958Sdr146992
892*12263SDarren.Reed@Sun.COM /*
893*12263SDarren.Reed@Sun.COM * Try and set the FWF_ADD_ACTIVE flag so that we can drop all the
894*12263SDarren.Reed@Sun.COM * lock further down when calling all of the functions registered
895*12263SDarren.Reed@Sun.COM * for notification when a new hook family is added.
896*12263SDarren.Reed@Sun.COM */
897*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK,
8987513SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
8997513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
9007513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
9017513SDarren.Reed@Sun.COM hook_family_free(new, NULL);
9027513SDarren.Reed@Sun.COM return (NULL);
9037513SDarren.Reed@Sun.COM }
9047513SDarren.Reed@Sun.COM
9057513SDarren.Reed@Sun.COM CVW_INIT(&new->hfi_lock);
9067513SDarren.Reed@Sun.COM SLIST_INIT(&new->hfi_head);
9077513SDarren.Reed@Sun.COM TAILQ_INIT(&new->hfi_nhead);
9087513SDarren.Reed@Sun.COM
9097513SDarren.Reed@Sun.COM hook_wait_init(&new->hfi_waiter, &new->hfi_lock);
9107513SDarren.Reed@Sun.COM
9117513SDarren.Reed@Sun.COM new->hfi_stack = hks;
912*12263SDarren.Reed@Sun.COM if (store != NULL)
913*12263SDarren.Reed@Sun.COM *store = new;
9143448Sdh155122
9152958Sdr146992 /* Add to family list head */
9163448Sdh155122 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
9172958Sdr146992
9187513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
9197513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
9207513SDarren.Reed@Sun.COM
9217513SDarren.Reed@Sun.COM hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER);
9227513SDarren.Reed@Sun.COM
9237513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE);
9247513SDarren.Reed@Sun.COM
9252958Sdr146992 return (new);
9262958Sdr146992 }
9272958Sdr146992
9282958Sdr146992 /*
9292958Sdr146992 * Function: hook_family_remove
930*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
9312958Sdr146992 * Parameters: hfi(I) - internal family pointer
9322958Sdr146992 *
9337513SDarren.Reed@Sun.COM * Remove family from family list. This function has been designed to be
9347513SDarren.Reed@Sun.COM * called once and once only per hook_family_int_t. Thus when cleaning up
9357513SDarren.Reed@Sun.COM * this structure as an orphan, callers should only call hook_family_free.
9362958Sdr146992 */
9372958Sdr146992 int
hook_family_remove(hook_family_int_t * hfi)9382958Sdr146992 hook_family_remove(hook_family_int_t *hfi)
9392958Sdr146992 {
9403448Sdh155122 hook_stack_t *hks;
9417915SDarren.Reed@Sun.COM boolean_t notifydone;
9422958Sdr146992
9432958Sdr146992 ASSERT(hfi != NULL);
9447513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
9457513SDarren.Reed@Sun.COM
946*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
947*12263SDarren.Reed@Sun.COM notifydone = hfi->hfi_shutdown;
948*12263SDarren.Reed@Sun.COM hfi->hfi_shutdown = B_TRUE;
949*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
950*12263SDarren.Reed@Sun.COM
9517513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
9522958Sdr146992
953*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
9547513SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
9557513SDarren.Reed@Sun.COM /*
9567513SDarren.Reed@Sun.COM * If we're trying to destroy the hook_stack_t...
9577513SDarren.Reed@Sun.COM */
9587915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
9597513SDarren.Reed@Sun.COM return (ENXIO);
9607513SDarren.Reed@Sun.COM }
9612958Sdr146992
9627513SDarren.Reed@Sun.COM /*
9637513SDarren.Reed@Sun.COM * Check if the family is in use by the presence of either events
9647513SDarren.Reed@Sun.COM * or notify callbacks on the hook family.
9657513SDarren.Reed@Sun.COM */
9667513SDarren.Reed@Sun.COM if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) {
9677513SDarren.Reed@Sun.COM hfi->hfi_condemned = B_TRUE;
9687513SDarren.Reed@Sun.COM } else {
969*12263SDarren.Reed@Sun.COM VERIFY(hook_wait_destroy(&hfi->hfi_waiter) == 0);
9707513SDarren.Reed@Sun.COM /*
9717513SDarren.Reed@Sun.COM * Although hfi_condemned = B_FALSE is implied from creation,
9727513SDarren.Reed@Sun.COM * putting a comment here inside the else upsets lint.
9737513SDarren.Reed@Sun.COM */
9747513SDarren.Reed@Sun.COM hfi->hfi_condemned = B_FALSE;
9752958Sdr146992 }
9767513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
9777513SDarren.Reed@Sun.COM
9787915SDarren.Reed@Sun.COM if (!notifydone)
9797915SDarren.Reed@Sun.COM hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
9807915SDarren.Reed@Sun.COM HN_UNREGISTER);
9812958Sdr146992
9827513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
9837513SDarren.Reed@Sun.COM
9847513SDarren.Reed@Sun.COM /*
9857513SDarren.Reed@Sun.COM * If we don't have to wait for anything else to disappear from this
9867513SDarren.Reed@Sun.COM * structure then we can free it up.
9877513SDarren.Reed@Sun.COM */
9887513SDarren.Reed@Sun.COM if (!hfi->hfi_condemned)
9897513SDarren.Reed@Sun.COM hook_family_free(hfi, hks);
9902958Sdr146992
9912958Sdr146992 return (0);
9922958Sdr146992 }
9932958Sdr146992
9942958Sdr146992
9952958Sdr146992 /*
9967513SDarren.Reed@Sun.COM * Function: hook_family_free
9977513SDarren.Reed@Sun.COM * Returns: None
9987513SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
9997513SDarren.Reed@Sun.COM *
10007513SDarren.Reed@Sun.COM * Free alloc memory for family
10017513SDarren.Reed@Sun.COM */
10027513SDarren.Reed@Sun.COM static void
hook_family_free(hook_family_int_t * hfi,hook_stack_t * hks)10037513SDarren.Reed@Sun.COM hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks)
10047513SDarren.Reed@Sun.COM {
10057513SDarren.Reed@Sun.COM
10067513SDarren.Reed@Sun.COM /*
10077513SDarren.Reed@Sun.COM * This lock gives us possession of the hks pointer after the
10087513SDarren.Reed@Sun.COM * SLIST_REMOVE, for which it is not needed, when hks_shutdown
10097513SDarren.Reed@Sun.COM * is checked and hook_stack_remove called.
10107513SDarren.Reed@Sun.COM */
10117513SDarren.Reed@Sun.COM mutex_enter(&hook_stack_lock);
10127513SDarren.Reed@Sun.COM
10137513SDarren.Reed@Sun.COM ASSERT(hfi != NULL);
10147513SDarren.Reed@Sun.COM
10157513SDarren.Reed@Sun.COM if (hks != NULL) {
10167513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
10177513SDarren.Reed@Sun.COM /* Remove from family list */
10187513SDarren.Reed@Sun.COM SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int,
10197513SDarren.Reed@Sun.COM hfi_entry);
10207513SDarren.Reed@Sun.COM
10217513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
10227513SDarren.Reed@Sun.COM }
10237513SDarren.Reed@Sun.COM
10247513SDarren.Reed@Sun.COM /* Free name space */
10257513SDarren.Reed@Sun.COM if (hfi->hfi_family.hf_name != NULL) {
10267513SDarren.Reed@Sun.COM kmem_free(hfi->hfi_family.hf_name,
10277513SDarren.Reed@Sun.COM strlen(hfi->hfi_family.hf_name) + 1);
10287513SDarren.Reed@Sun.COM }
10297513SDarren.Reed@Sun.COM
10307513SDarren.Reed@Sun.COM /* Free container */
10317513SDarren.Reed@Sun.COM kmem_free(hfi, sizeof (*hfi));
10327513SDarren.Reed@Sun.COM
10337513SDarren.Reed@Sun.COM if (hks->hks_shutdown == 2)
10347513SDarren.Reed@Sun.COM hook_stack_remove(hks);
10357513SDarren.Reed@Sun.COM
10367513SDarren.Reed@Sun.COM mutex_exit(&hook_stack_lock);
10377513SDarren.Reed@Sun.COM }
10387513SDarren.Reed@Sun.COM
10397915SDarren.Reed@Sun.COM /*
10407915SDarren.Reed@Sun.COM * Function: hook_family_shutdown
1041*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
10427915SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
10437915SDarren.Reed@Sun.COM *
10447915SDarren.Reed@Sun.COM * As an alternative to removing a family, we may desire to just generate
10457915SDarren.Reed@Sun.COM * a series of callbacks to indicate that we will be going away in the
10467915SDarren.Reed@Sun.COM * future. The hfi_condemned flag isn't set because we aren't trying to
10477915SDarren.Reed@Sun.COM * remove the structure.
10487915SDarren.Reed@Sun.COM */
10497915SDarren.Reed@Sun.COM int
hook_family_shutdown(hook_family_int_t * hfi)10507915SDarren.Reed@Sun.COM hook_family_shutdown(hook_family_int_t *hfi)
10517915SDarren.Reed@Sun.COM {
10527915SDarren.Reed@Sun.COM hook_stack_t *hks;
10537915SDarren.Reed@Sun.COM boolean_t notifydone;
10547915SDarren.Reed@Sun.COM
10557915SDarren.Reed@Sun.COM ASSERT(hfi != NULL);
10567915SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
10577915SDarren.Reed@Sun.COM
1058*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
1059*12263SDarren.Reed@Sun.COM notifydone = hfi->hfi_shutdown;
1060*12263SDarren.Reed@Sun.COM hfi->hfi_shutdown = B_TRUE;
1061*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
1062*12263SDarren.Reed@Sun.COM
10637915SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hks->hks_lock);
10647915SDarren.Reed@Sun.COM
1065*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK,
10667915SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
10677915SDarren.Reed@Sun.COM /*
10687915SDarren.Reed@Sun.COM * If we're trying to destroy the hook_stack_t...
10697915SDarren.Reed@Sun.COM */
10707915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
10717915SDarren.Reed@Sun.COM return (ENXIO);
10727915SDarren.Reed@Sun.COM }
10737915SDarren.Reed@Sun.COM
10747915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hks->hks_lock);
10757915SDarren.Reed@Sun.COM
10767915SDarren.Reed@Sun.COM if (!notifydone)
10777915SDarren.Reed@Sun.COM hook_stack_notify_run(hks, hfi->hfi_family.hf_name,
10787915SDarren.Reed@Sun.COM HN_UNREGISTER);
10797915SDarren.Reed@Sun.COM
10807915SDarren.Reed@Sun.COM hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE);
10817915SDarren.Reed@Sun.COM
10827915SDarren.Reed@Sun.COM return (0);
10837915SDarren.Reed@Sun.COM }
10847513SDarren.Reed@Sun.COM
10857513SDarren.Reed@Sun.COM /*
10862958Sdr146992 * Function: hook_family_copy
10872958Sdr146992 * Returns: internal family pointer - NULL = Failed
10882958Sdr146992 * Parameters: src(I) - family pointer
10892958Sdr146992 *
10902958Sdr146992 * Allocate internal family block and duplicate incoming family
10912958Sdr146992 * No locks should be held across this function as it may sleep.
10922958Sdr146992 */
10932958Sdr146992 static hook_family_int_t *
hook_family_copy(hook_family_t * src)10942958Sdr146992 hook_family_copy(hook_family_t *src)
10952958Sdr146992 {
10962958Sdr146992 hook_family_int_t *new;
10972958Sdr146992 hook_family_t *dst;
10982958Sdr146992
10992958Sdr146992 ASSERT(src != NULL);
11002958Sdr146992 ASSERT(src->hf_name != NULL);
11012958Sdr146992
11022958Sdr146992 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
11032958Sdr146992
11042958Sdr146992 /* Copy body */
11052958Sdr146992 dst = &new->hfi_family;
11062958Sdr146992 *dst = *src;
11072958Sdr146992
11087513SDarren.Reed@Sun.COM SLIST_INIT(&new->hfi_head);
11097513SDarren.Reed@Sun.COM TAILQ_INIT(&new->hfi_nhead);
11107513SDarren.Reed@Sun.COM
11112958Sdr146992 /* Copy name */
11122958Sdr146992 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
11132958Sdr146992 (void) strcpy(dst->hf_name, src->hf_name);
11142958Sdr146992
11152958Sdr146992 return (new);
11162958Sdr146992 }
11172958Sdr146992
11182958Sdr146992 /*
11197513SDarren.Reed@Sun.COM * Function: hook_family_find
11202958Sdr146992 * Returns: internal family pointer - NULL = Not match
11212958Sdr146992 * Parameters: family(I) - family name string
11222958Sdr146992 *
11232958Sdr146992 * Search family list with family name
11247513SDarren.Reed@Sun.COM * A lock on hfi_lock must be held when called.
11252958Sdr146992 */
11262958Sdr146992 static hook_family_int_t *
hook_family_find(char * family,hook_stack_t * hks)11273448Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
11282958Sdr146992 {
11292958Sdr146992 hook_family_int_t *hfi = NULL;
11302958Sdr146992
11312958Sdr146992 ASSERT(family != NULL);
11322958Sdr146992
11333448Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
11342958Sdr146992 if (strcmp(hfi->hfi_family.hf_name, family) == 0)
11352958Sdr146992 break;
11362958Sdr146992 }
11372958Sdr146992 return (hfi);
11382958Sdr146992 }
11392958Sdr146992
11407513SDarren.Reed@Sun.COM /*
11417513SDarren.Reed@Sun.COM * Function: hook_family_notify_register
1142*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
11437513SDarren.Reed@Sun.COM * Parameters: hfi(I) - hook family
11447513SDarren.Reed@Sun.COM * callback(I) - function to be called
11457513SDarren.Reed@Sun.COM * arg(I) - arg to provide callback when it is called
11467513SDarren.Reed@Sun.COM *
11477513SDarren.Reed@Sun.COM * So long as this hook stack isn't being shut down, register a new
11487513SDarren.Reed@Sun.COM * callback to be activated each time a new event is added to this
11497513SDarren.Reed@Sun.COM * family.
11507513SDarren.Reed@Sun.COM *
11517513SDarren.Reed@Sun.COM * To call this function we must have an active handle in use on the family,
11527513SDarren.Reed@Sun.COM * so if we take this into account, then neither the hook_family_int_t nor
11537513SDarren.Reed@Sun.COM * the hook_stack_t that owns it can disappear. We have to put some trust
11547513SDarren.Reed@Sun.COM * in the callers to be properly synchronised...
11557513SDarren.Reed@Sun.COM *
11567513SDarren.Reed@Sun.COM * Holding hks_lock is required to provide synchronisation for hks_shutdown.
11577513SDarren.Reed@Sun.COM */
11587513SDarren.Reed@Sun.COM int
hook_family_notify_register(hook_family_int_t * hfi,hook_notify_fn_t callback,void * arg)11597513SDarren.Reed@Sun.COM hook_family_notify_register(hook_family_int_t *hfi,
11607513SDarren.Reed@Sun.COM hook_notify_fn_t callback, void *arg)
11617513SDarren.Reed@Sun.COM {
1162*12263SDarren.Reed@Sun.COM hook_event_int_t *hei;
11637513SDarren.Reed@Sun.COM hook_stack_t *hks;
1164*12263SDarren.Reed@Sun.COM boolean_t canrun;
11657513SDarren.Reed@Sun.COM int error;
11667513SDarren.Reed@Sun.COM
1167*12263SDarren.Reed@Sun.COM ASSERT(hfi != NULL);
1168*12263SDarren.Reed@Sun.COM canrun = B_FALSE;
11697513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
11707513SDarren.Reed@Sun.COM
11717513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hks->hks_lock);
11727513SDarren.Reed@Sun.COM
11737915SDarren.Reed@Sun.COM if ((hfi->hfi_stack->hks_shutdown != 0) ||
11747915SDarren.Reed@Sun.COM hfi->hfi_condemned || hfi->hfi_shutdown) {
11757513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
11767513SDarren.Reed@Sun.COM return (ESHUTDOWN);
11777513SDarren.Reed@Sun.COM }
11787513SDarren.Reed@Sun.COM
1179*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
1180*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1181*12263SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1182*12263SDarren.Reed@Sun.COM error = hook_notify_register(&hfi->hfi_nhead, callback, arg);
1183*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
1184*12263SDarren.Reed@Sun.COM
1185*12263SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
11867513SDarren.Reed@Sun.COM
1187*12263SDarren.Reed@Sun.COM if (error == 0 && canrun) {
1188*12263SDarren.Reed@Sun.COM SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1189*12263SDarren.Reed@Sun.COM callback(HN_REGISTER, arg,
1190*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, NULL,
1191*12263SDarren.Reed@Sun.COM hei->hei_event->he_name);
1192*12263SDarren.Reed@Sun.COM }
1193*12263SDarren.Reed@Sun.COM }
1194*12263SDarren.Reed@Sun.COM
1195*12263SDarren.Reed@Sun.COM if (canrun)
1196*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
11977513SDarren.Reed@Sun.COM
11987513SDarren.Reed@Sun.COM return (error);
11997513SDarren.Reed@Sun.COM }
12002958Sdr146992
12012958Sdr146992 /*
12027513SDarren.Reed@Sun.COM * Function: hook_family_notify_unregister
1203*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
12047513SDarren.Reed@Sun.COM * Parameters: hfi(I) - hook family
12057513SDarren.Reed@Sun.COM * callback(I) - function to be called
12062958Sdr146992 *
12077513SDarren.Reed@Sun.COM * Remove a callback from the list of those executed when a new event is
1208*12263SDarren.Reed@Sun.COM * added to a hook family. If the family is not in the process of being
1209*12263SDarren.Reed@Sun.COM * destroyed then simulate an unregister callback for each event that is
1210*12263SDarren.Reed@Sun.COM * on the family. This pairs up with the hook_family_notify_register
1211*12263SDarren.Reed@Sun.COM * action that simulates register events.
1212*12263SDarren.Reed@Sun.COM * The order of what happens here is important and goes like this.
1213*12263SDarren.Reed@Sun.COM * 1) Remove the callback from the list of functions to be called as part
1214*12263SDarren.Reed@Sun.COM * of the notify operation when an event is added or removed from the
1215*12263SDarren.Reed@Sun.COM * hook family.
1216*12263SDarren.Reed@Sun.COM * 2) If the hook_family_int_t structure is on death row (free_family will
1217*12263SDarren.Reed@Sun.COM * be set to true) then there's nothing else to do than let it be free'd.
1218*12263SDarren.Reed@Sun.COM * 3) If the structure isn't about to die, mark it up as being busy using
1219*12263SDarren.Reed@Sun.COM * hook_wait_setflag and then drop the lock so the loop can be run.
1220*12263SDarren.Reed@Sun.COM * 4) if hook_wait_setflag was successful, tell all of the notify callback
1221*12263SDarren.Reed@Sun.COM * functions that this family has been unregistered.
1222*12263SDarren.Reed@Sun.COM * 5) Cleanup
12232958Sdr146992 */
12247513SDarren.Reed@Sun.COM int
hook_family_notify_unregister(hook_family_int_t * hfi,hook_notify_fn_t callback)12257513SDarren.Reed@Sun.COM hook_family_notify_unregister(hook_family_int_t *hfi,
12267513SDarren.Reed@Sun.COM hook_notify_fn_t callback)
12272958Sdr146992 {
1228*12263SDarren.Reed@Sun.COM hook_event_int_t *hei;
12297513SDarren.Reed@Sun.COM boolean_t free_family;
1230*12263SDarren.Reed@Sun.COM boolean_t canrun;
12317513SDarren.Reed@Sun.COM int error;
1232*12263SDarren.Reed@Sun.COM void *arg;
1233*12263SDarren.Reed@Sun.COM
1234*12263SDarren.Reed@Sun.COM canrun = B_FALSE;
12357513SDarren.Reed@Sun.COM
12367513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
12372958Sdr146992
1238*12263SDarren.Reed@Sun.COM (void) hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
1239*12263SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1240*12263SDarren.Reed@Sun.COM
1241*12263SDarren.Reed@Sun.COM error = hook_notify_unregister(&hfi->hfi_nhead, callback, &arg);
1242*12263SDarren.Reed@Sun.COM
1243*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
12447513SDarren.Reed@Sun.COM
12457513SDarren.Reed@Sun.COM /*
12467513SDarren.Reed@Sun.COM * If hook_family_remove has been called but the structure was still
12477513SDarren.Reed@Sun.COM * "busy" ... but we might have just made it "unbusy"...
12487513SDarren.Reed@Sun.COM */
12497513SDarren.Reed@Sun.COM if ((error == 0) && hfi->hfi_condemned &&
12507513SDarren.Reed@Sun.COM SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) {
12517513SDarren.Reed@Sun.COM free_family = B_TRUE;
12527513SDarren.Reed@Sun.COM } else {
12537513SDarren.Reed@Sun.COM free_family = B_FALSE;
12542958Sdr146992 }
12552958Sdr146992
1256*12263SDarren.Reed@Sun.COM if (error == 0 && !free_family) {
1257*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
1258*12263SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1259*12263SDarren.Reed@Sun.COM }
1260*12263SDarren.Reed@Sun.COM
12617513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
12627513SDarren.Reed@Sun.COM
1263*12263SDarren.Reed@Sun.COM if (canrun) {
1264*12263SDarren.Reed@Sun.COM SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
1265*12263SDarren.Reed@Sun.COM callback(HN_UNREGISTER, arg,
1266*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, NULL,
1267*12263SDarren.Reed@Sun.COM hei->hei_event->he_name);
1268*12263SDarren.Reed@Sun.COM }
1269*12263SDarren.Reed@Sun.COM
1270*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
1271*12263SDarren.Reed@Sun.COM } else if (free_family) {
12727513SDarren.Reed@Sun.COM hook_family_free(hfi, hfi->hfi_stack);
1273*12263SDarren.Reed@Sun.COM }
12747513SDarren.Reed@Sun.COM
12757513SDarren.Reed@Sun.COM return (error);
12762958Sdr146992 }
12772958Sdr146992
12782958Sdr146992 /*
12792958Sdr146992 * Function: hook_event_add
12802958Sdr146992 * Returns: internal event pointer - NULL = Fail
12812958Sdr146992 * Parameters: hfi(I) - internal family pointer
1282*12263SDarren.Reed@Sun.COM * he(I) - event pointer
12832958Sdr146992 *
12842958Sdr146992 * Add new event to event list on specific family.
12852958Sdr146992 * This function can fail to return successfully if (1) it cannot allocate
12862958Sdr146992 * enough memory for its own internal data structures, (2) the event has
12872958Sdr146992 * already been registered (for any hook family.)
12882958Sdr146992 */
12892958Sdr146992 hook_event_int_t *
hook_event_add(hook_family_int_t * hfi,hook_event_t * he)12902958Sdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
12912958Sdr146992 {
12927513SDarren.Reed@Sun.COM hook_event_int_t *hei, *new;
12933448Sdh155122 hook_stack_t *hks;
12942958Sdr146992
12952958Sdr146992 ASSERT(hfi != NULL);
12962958Sdr146992 ASSERT(he != NULL);
12972958Sdr146992 ASSERT(he->he_name != NULL);
12982958Sdr146992
12992958Sdr146992 new = hook_event_copy(he);
13002958Sdr146992 if (new == NULL)
13012958Sdr146992 return (NULL);
13022958Sdr146992
13037513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
13047513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hks->hks_lock);
13057513SDarren.Reed@Sun.COM
13067513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
13077513SDarren.Reed@Sun.COM if (hks->hks_shutdown != 0) {
13087513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
13097513SDarren.Reed@Sun.COM hook_event_free(new, NULL);
13107513SDarren.Reed@Sun.COM return (NULL);
13117513SDarren.Reed@Sun.COM }
13122958Sdr146992
13132958Sdr146992 /* Check whether this event pointer is already registered */
13143448Sdh155122 hei = hook_event_checkdup(he, hks);
13152958Sdr146992 if (hei != NULL) {
13167513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
13177513SDarren.Reed@Sun.COM hook_event_free(new, NULL);
13187513SDarren.Reed@Sun.COM return (NULL);
13197513SDarren.Reed@Sun.COM }
13207513SDarren.Reed@Sun.COM
13217513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
13227513SDarren.Reed@Sun.COM
13237915SDarren.Reed@Sun.COM if (hfi->hfi_condemned || hfi->hfi_shutdown) {
13247513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
13257513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
13267513SDarren.Reed@Sun.COM hook_event_free(new, NULL);
13272958Sdr146992 return (NULL);
13282958Sdr146992 }
1329*12263SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
13302958Sdr146992
1331*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK,
13327513SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
13337513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
13347513SDarren.Reed@Sun.COM hook_event_free(new, NULL);
13357513SDarren.Reed@Sun.COM return (NULL);
13367513SDarren.Reed@Sun.COM }
13377513SDarren.Reed@Sun.COM
13387513SDarren.Reed@Sun.COM TAILQ_INIT(&new->hei_nhead);
13397513SDarren.Reed@Sun.COM
13407513SDarren.Reed@Sun.COM hook_event_init_kstats(hfi, new);
13417513SDarren.Reed@Sun.COM hook_wait_init(&new->hei_waiter, &new->hei_lock);
13427513SDarren.Reed@Sun.COM
13432958Sdr146992 /* Add to event list head */
13442958Sdr146992 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
13452958Sdr146992
13467513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
13477513SDarren.Reed@Sun.COM
13487513SDarren.Reed@Sun.COM hook_notify_run(&hfi->hfi_nhead,
13497513SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER);
13507513SDarren.Reed@Sun.COM
13517513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE);
13527513SDarren.Reed@Sun.COM
13532958Sdr146992 return (new);
13542958Sdr146992 }
13552958Sdr146992
13567513SDarren.Reed@Sun.COM /*
13577513SDarren.Reed@Sun.COM * Function: hook_event_init_kstats
13587513SDarren.Reed@Sun.COM * Returns: None
13597513SDarren.Reed@Sun.COM * Parameters: hfi(I) - pointer to the family that owns this event.
13607513SDarren.Reed@Sun.COM * hei(I) - pointer to the hook event that needs some kstats.
13617513SDarren.Reed@Sun.COM *
13627513SDarren.Reed@Sun.COM * Create a set of kstats that relate to each event registered with
13637513SDarren.Reed@Sun.COM * the hook framework. A counter is kept for each time the event is
13647513SDarren.Reed@Sun.COM * activated and for each time a hook is added or removed. As the
13657513SDarren.Reed@Sun.COM * kstats just count the events as they happen, the total number of
13667513SDarren.Reed@Sun.COM * hooks registered must be obtained by subtractived removed from added.
13677513SDarren.Reed@Sun.COM */
13687513SDarren.Reed@Sun.COM static void
hook_event_init_kstats(hook_family_int_t * hfi,hook_event_int_t * hei)13697513SDarren.Reed@Sun.COM hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei)
13707513SDarren.Reed@Sun.COM {
13717513SDarren.Reed@Sun.COM hook_event_kstat_t template = {
13727513SDarren.Reed@Sun.COM { "hooksAdded", KSTAT_DATA_UINT64 },
13737513SDarren.Reed@Sun.COM { "hooksRemoved", KSTAT_DATA_UINT64 },
13747513SDarren.Reed@Sun.COM { "events", KSTAT_DATA_UINT64 }
13757513SDarren.Reed@Sun.COM };
13767513SDarren.Reed@Sun.COM hook_stack_t *hks;
13777513SDarren.Reed@Sun.COM
13787513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
13797513SDarren.Reed@Sun.COM hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0,
13807513SDarren.Reed@Sun.COM hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED,
13817513SDarren.Reed@Sun.COM sizeof (hei->hei_kstats) / sizeof (kstat_named_t),
13827513SDarren.Reed@Sun.COM KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
13837513SDarren.Reed@Sun.COM
13847513SDarren.Reed@Sun.COM bcopy((char *)&template, &hei->hei_kstats, sizeof (template));
13857513SDarren.Reed@Sun.COM
13867513SDarren.Reed@Sun.COM if (hei->hei_kstatp != NULL) {
13877513SDarren.Reed@Sun.COM hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats;
13887513SDarren.Reed@Sun.COM hei->hei_kstatp->ks_private =
13897513SDarren.Reed@Sun.COM (void *)(uintptr_t)hks->hks_netstackid;
13907513SDarren.Reed@Sun.COM
13917513SDarren.Reed@Sun.COM kstat_install(hei->hei_kstatp);
13927513SDarren.Reed@Sun.COM }
13937513SDarren.Reed@Sun.COM }
13942958Sdr146992
13952958Sdr146992 /*
13962958Sdr146992 * Function: hook_event_remove
1397*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
13982958Sdr146992 * Parameters: hfi(I) - internal family pointer
1399*12263SDarren.Reed@Sun.COM * he(I) - event pointer
14002958Sdr146992 *
14012958Sdr146992 * Remove event from event list on specific family
14027513SDarren.Reed@Sun.COM *
14037513SDarren.Reed@Sun.COM * This function assumes that the caller has received a pointer to a the
14047513SDarren.Reed@Sun.COM * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'.
14057513SDarren.Reed@Sun.COM * This the hook_family_int_t is guaranteed to be around for the life of this
14067513SDarren.Reed@Sun.COM * call, unless the caller has decided to call net_protocol_release or
14077513SDarren.Reed@Sun.COM * net_protocol_unregister before calling net_event_unregister - an error.
14082958Sdr146992 */
14092958Sdr146992 int
hook_event_remove(hook_family_int_t * hfi,hook_event_t * he)14102958Sdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
14112958Sdr146992 {
14127513SDarren.Reed@Sun.COM boolean_t free_family;
14132958Sdr146992 hook_event_int_t *hei;
14147915SDarren.Reed@Sun.COM boolean_t notifydone;
14152958Sdr146992
14162958Sdr146992 ASSERT(hfi != NULL);
14172958Sdr146992 ASSERT(he != NULL);
14182958Sdr146992
14197513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
14202958Sdr146992
14217513SDarren.Reed@Sun.COM /*
14227513SDarren.Reed@Sun.COM * Set the flag so that we can call hook_event_notify_run without
14237513SDarren.Reed@Sun.COM * holding any locks but at the same time prevent other changes to
14247513SDarren.Reed@Sun.COM * the event at the same time.
14257513SDarren.Reed@Sun.COM */
1426*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
14277513SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
14287513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
14292958Sdr146992 return (ENXIO);
14302958Sdr146992 }
14312958Sdr146992
14327513SDarren.Reed@Sun.COM hei = hook_event_find(hfi, he->he_name);
14337513SDarren.Reed@Sun.COM if (hei == NULL) {
14347513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14357513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
14367513SDarren.Reed@Sun.COM return (ESRCH);
14372958Sdr146992 }
14382958Sdr146992
14397513SDarren.Reed@Sun.COM free_family = B_FALSE;
14407513SDarren.Reed@Sun.COM
14417513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hei->hei_lock);
14427513SDarren.Reed@Sun.COM /*
14437915SDarren.Reed@Sun.COM * The hei_shutdown flag is used to indicate whether or not we have
14447915SDarren.Reed@Sun.COM * done a shutdown and thus already walked through the notify list.
14457915SDarren.Reed@Sun.COM */
14467915SDarren.Reed@Sun.COM notifydone = hei->hei_shutdown;
14477915SDarren.Reed@Sun.COM hei->hei_shutdown = B_TRUE;
14487915SDarren.Reed@Sun.COM /*
14497513SDarren.Reed@Sun.COM * If there are any hooks still registered for this event or
14507513SDarren.Reed@Sun.COM * there are any notifiers registered, return an error indicating
14517513SDarren.Reed@Sun.COM * that the event is still busy.
14527513SDarren.Reed@Sun.COM */
14537513SDarren.Reed@Sun.COM if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) {
14547513SDarren.Reed@Sun.COM hei->hei_condemned = B_TRUE;
14557513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
14567513SDarren.Reed@Sun.COM } else {
14577513SDarren.Reed@Sun.COM /* hei_condemned = B_FALSE is implied from creation */
14587513SDarren.Reed@Sun.COM /*
14597513SDarren.Reed@Sun.COM * Even though we know the notify list is empty, we call
14607513SDarren.Reed@Sun.COM * hook_wait_destroy here to synchronise wait removing a
14617513SDarren.Reed@Sun.COM * hook from an event.
14627513SDarren.Reed@Sun.COM */
1463*12263SDarren.Reed@Sun.COM VERIFY(hook_wait_destroy(&hei->hei_waiter) == 0);
14642958Sdr146992
14657513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
14667513SDarren.Reed@Sun.COM
14677513SDarren.Reed@Sun.COM if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
14687513SDarren.Reed@Sun.COM TAILQ_EMPTY(&hfi->hfi_nhead))
14697513SDarren.Reed@Sun.COM free_family = B_TRUE;
14707513SDarren.Reed@Sun.COM }
14717513SDarren.Reed@Sun.COM
14727513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
14737513SDarren.Reed@Sun.COM
14747915SDarren.Reed@Sun.COM if (!notifydone)
14757915SDarren.Reed@Sun.COM hook_notify_run(&hfi->hfi_nhead,
14767915SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
14777513SDarren.Reed@Sun.COM
14787513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
14797513SDarren.Reed@Sun.COM
14807513SDarren.Reed@Sun.COM if (!hei->hei_condemned) {
14817513SDarren.Reed@Sun.COM hook_event_free(hei, hfi);
14827513SDarren.Reed@Sun.COM if (free_family)
14837513SDarren.Reed@Sun.COM hook_family_free(hfi, hfi->hfi_stack);
14847513SDarren.Reed@Sun.COM }
14852958Sdr146992
14862958Sdr146992 return (0);
14872958Sdr146992 }
14882958Sdr146992
14897513SDarren.Reed@Sun.COM /*
14907915SDarren.Reed@Sun.COM * Function: hook_event_shutdown
1491*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
14927915SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
14937915SDarren.Reed@Sun.COM * he(I) - event pointer
14947915SDarren.Reed@Sun.COM *
14957915SDarren.Reed@Sun.COM * As with hook_family_shutdown, we want to generate the notify callbacks
14967915SDarren.Reed@Sun.COM * as if the event was being removed but not actually do the remove.
14977915SDarren.Reed@Sun.COM */
14987915SDarren.Reed@Sun.COM int
hook_event_shutdown(hook_family_int_t * hfi,hook_event_t * he)14997915SDarren.Reed@Sun.COM hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he)
15007915SDarren.Reed@Sun.COM {
15017915SDarren.Reed@Sun.COM hook_event_int_t *hei;
15027915SDarren.Reed@Sun.COM boolean_t notifydone;
15037915SDarren.Reed@Sun.COM
15047915SDarren.Reed@Sun.COM ASSERT(hfi != NULL);
15057915SDarren.Reed@Sun.COM ASSERT(he != NULL);
15067915SDarren.Reed@Sun.COM
15077915SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
15087915SDarren.Reed@Sun.COM
15097915SDarren.Reed@Sun.COM /*
15107915SDarren.Reed@Sun.COM * Set the flag so that we can call hook_event_notify_run without
15117915SDarren.Reed@Sun.COM * holding any locks but at the same time prevent other changes to
15127915SDarren.Reed@Sun.COM * the event at the same time.
15137915SDarren.Reed@Sun.COM */
1514*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK,
15157915SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
15167915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
15177915SDarren.Reed@Sun.COM return (ENXIO);
15187915SDarren.Reed@Sun.COM }
15197915SDarren.Reed@Sun.COM
15207915SDarren.Reed@Sun.COM hei = hook_event_find(hfi, he->he_name);
15217915SDarren.Reed@Sun.COM if (hei == NULL) {
15227915SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15237915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
15247915SDarren.Reed@Sun.COM return (ESRCH);
15257915SDarren.Reed@Sun.COM }
15267915SDarren.Reed@Sun.COM
15277915SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hei->hei_lock);
15287915SDarren.Reed@Sun.COM notifydone = hei->hei_shutdown;
15297915SDarren.Reed@Sun.COM hei->hei_shutdown = B_TRUE;
15307915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
15317915SDarren.Reed@Sun.COM
15327915SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
15337915SDarren.Reed@Sun.COM
15347915SDarren.Reed@Sun.COM if (!notifydone)
15357915SDarren.Reed@Sun.COM hook_notify_run(&hfi->hfi_nhead,
15367915SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER);
15377915SDarren.Reed@Sun.COM
15387915SDarren.Reed@Sun.COM hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE);
15397915SDarren.Reed@Sun.COM
15407915SDarren.Reed@Sun.COM return (0);
15417915SDarren.Reed@Sun.COM }
15427915SDarren.Reed@Sun.COM
15437915SDarren.Reed@Sun.COM /*
15447513SDarren.Reed@Sun.COM * Function: hook_event_free
15457513SDarren.Reed@Sun.COM * Returns: None
15467513SDarren.Reed@Sun.COM * Parameters: hei(I) - internal event pointer
15477513SDarren.Reed@Sun.COM *
15487513SDarren.Reed@Sun.COM * Free alloc memory for event
15497513SDarren.Reed@Sun.COM */
15507513SDarren.Reed@Sun.COM static void
hook_event_free(hook_event_int_t * hei,hook_family_int_t * hfi)15517513SDarren.Reed@Sun.COM hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi)
15527513SDarren.Reed@Sun.COM {
15537513SDarren.Reed@Sun.COM boolean_t free_family;
15547513SDarren.Reed@Sun.COM
15557513SDarren.Reed@Sun.COM ASSERT(hei != NULL);
15567513SDarren.Reed@Sun.COM
15577513SDarren.Reed@Sun.COM if (hfi != NULL) {
15587513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
15597513SDarren.Reed@Sun.COM /*
15607513SDarren.Reed@Sun.COM * Remove the event from the hook family's list.
15617513SDarren.Reed@Sun.COM */
15627513SDarren.Reed@Sun.COM SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
15637513SDarren.Reed@Sun.COM if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) &&
15647513SDarren.Reed@Sun.COM TAILQ_EMPTY(&hfi->hfi_nhead)) {
15657513SDarren.Reed@Sun.COM free_family = B_TRUE;
15667513SDarren.Reed@Sun.COM } else {
15677513SDarren.Reed@Sun.COM free_family = B_FALSE;
15687513SDarren.Reed@Sun.COM }
15697513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
15707513SDarren.Reed@Sun.COM }
15717513SDarren.Reed@Sun.COM
15727513SDarren.Reed@Sun.COM if (hei->hei_kstatp != NULL) {
15737513SDarren.Reed@Sun.COM ASSERT(hfi != NULL);
15747513SDarren.Reed@Sun.COM
15757513SDarren.Reed@Sun.COM kstat_delete_netstack(hei->hei_kstatp,
15767513SDarren.Reed@Sun.COM hfi->hfi_stack->hks_netstackid);
15777513SDarren.Reed@Sun.COM hei->hei_kstatp = NULL;
15787513SDarren.Reed@Sun.COM }
15797513SDarren.Reed@Sun.COM
15807513SDarren.Reed@Sun.COM /* Free container */
15817513SDarren.Reed@Sun.COM kmem_free(hei, sizeof (*hei));
15827513SDarren.Reed@Sun.COM
15837513SDarren.Reed@Sun.COM if (free_family)
15847513SDarren.Reed@Sun.COM hook_family_free(hfi, hfi->hfi_stack);
15857513SDarren.Reed@Sun.COM }
15862958Sdr146992
15872958Sdr146992 /*
15882958Sdr146992 * Function: hook_event_checkdup
15892958Sdr146992 * Returns: internal event pointer - NULL = Not match
15902958Sdr146992 * Parameters: he(I) - event pointer
15912958Sdr146992 *
15927513SDarren.Reed@Sun.COM * Search all of the hook families to see if the event being passed in
15937513SDarren.Reed@Sun.COM * has already been associated with one.
15942958Sdr146992 */
15952958Sdr146992 static hook_event_int_t *
hook_event_checkdup(hook_event_t * he,hook_stack_t * hks)15963448Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
15972958Sdr146992 {
15982958Sdr146992 hook_family_int_t *hfi;
15992958Sdr146992 hook_event_int_t *hei;
16002958Sdr146992
16012958Sdr146992 ASSERT(he != NULL);
16022958Sdr146992
16037513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hks->hks_lock);
16043448Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
16052958Sdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16067513SDarren.Reed@Sun.COM if (hei->hei_event == he) {
16077513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
16082958Sdr146992 return (hei);
16097513SDarren.Reed@Sun.COM }
16102958Sdr146992 }
16112958Sdr146992 }
16127513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
16132958Sdr146992
16142958Sdr146992 return (NULL);
16152958Sdr146992 }
16162958Sdr146992
16172958Sdr146992 /*
16182958Sdr146992 * Function: hook_event_copy
16192958Sdr146992 * Returns: internal event pointer - NULL = Failed
16202958Sdr146992 * Parameters: src(I) - event pointer
16212958Sdr146992 *
16222958Sdr146992 * Allocate internal event block and duplicate incoming event
16232958Sdr146992 * No locks should be held across this function as it may sleep.
16242958Sdr146992 */
16252958Sdr146992 static hook_event_int_t *
hook_event_copy(hook_event_t * src)16262958Sdr146992 hook_event_copy(hook_event_t *src)
16272958Sdr146992 {
16282958Sdr146992 hook_event_int_t *new;
16292958Sdr146992
16302958Sdr146992 ASSERT(src != NULL);
16312958Sdr146992 ASSERT(src->he_name != NULL);
16322958Sdr146992
16332958Sdr146992 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
16342958Sdr146992
16352958Sdr146992 /* Copy body */
16362958Sdr146992 TAILQ_INIT(&new->hei_head);
16372958Sdr146992 new->hei_event = src;
16382958Sdr146992
16392958Sdr146992 return (new);
16402958Sdr146992 }
16412958Sdr146992
16422958Sdr146992 /*
16432958Sdr146992 * Function: hook_event_find
16442958Sdr146992 * Returns: internal event pointer - NULL = Not match
1645*12263SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
16462958Sdr146992 * event(I) - event name string
16472958Sdr146992 *
16482958Sdr146992 * Search event list with event name
16497513SDarren.Reed@Sun.COM * A lock on hfi->hfi_lock must be held when called.
16502958Sdr146992 */
16512958Sdr146992 static hook_event_int_t *
hook_event_find(hook_family_int_t * hfi,char * event)16522958Sdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
16532958Sdr146992 {
16542958Sdr146992 hook_event_int_t *hei = NULL;
16552958Sdr146992
16562958Sdr146992 ASSERT(hfi != NULL);
16572958Sdr146992 ASSERT(event != NULL);
16582958Sdr146992
16592958Sdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
16607513SDarren.Reed@Sun.COM if ((strcmp(hei->hei_event->he_name, event) == 0) &&
16617513SDarren.Reed@Sun.COM ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0))
16622958Sdr146992 break;
16632958Sdr146992 }
16642958Sdr146992 return (hei);
16652958Sdr146992 }
16662958Sdr146992
16677513SDarren.Reed@Sun.COM /*
16687513SDarren.Reed@Sun.COM * Function: hook_event_notify_register
1669*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
16707513SDarren.Reed@Sun.COM * Parameters: hfi(I) - hook family
16717513SDarren.Reed@Sun.COM * event(I) - name of the event
16727513SDarren.Reed@Sun.COM * callback(I) - function to be called
16737513SDarren.Reed@Sun.COM * arg(I) - arg to provide callback when it is called
16747513SDarren.Reed@Sun.COM *
16757513SDarren.Reed@Sun.COM * Adds a new callback to the event named by "event" (we must find it)
16767513SDarren.Reed@Sun.COM * that will be executed each time a new hook is added to the event.
16777513SDarren.Reed@Sun.COM * Of course, if the stack is being shut down, this call should fail.
16787513SDarren.Reed@Sun.COM */
16797513SDarren.Reed@Sun.COM int
hook_event_notify_register(hook_family_int_t * hfi,char * event,hook_notify_fn_t callback,void * arg)16807513SDarren.Reed@Sun.COM hook_event_notify_register(hook_family_int_t *hfi, char *event,
16817513SDarren.Reed@Sun.COM hook_notify_fn_t callback, void *arg)
16827513SDarren.Reed@Sun.COM {
16837513SDarren.Reed@Sun.COM hook_event_int_t *hei;
16847513SDarren.Reed@Sun.COM hook_stack_t *hks;
1685*12263SDarren.Reed@Sun.COM boolean_t canrun;
1686*12263SDarren.Reed@Sun.COM hook_int_t *h;
16877513SDarren.Reed@Sun.COM int error;
16887513SDarren.Reed@Sun.COM
1689*12263SDarren.Reed@Sun.COM canrun = B_FALSE;
16907513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
16917513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hks->hks_lock);
16927513SDarren.Reed@Sun.COM if (hks->hks_shutdown != 0) {
16937513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
16947513SDarren.Reed@Sun.COM return (ESHUTDOWN);
16957513SDarren.Reed@Sun.COM }
16967513SDarren.Reed@Sun.COM
16977513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hfi->hfi_lock);
16987513SDarren.Reed@Sun.COM
16997915SDarren.Reed@Sun.COM if (hfi->hfi_condemned || hfi->hfi_shutdown) {
17007513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
17017513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
17027513SDarren.Reed@Sun.COM return (ESHUTDOWN);
17037513SDarren.Reed@Sun.COM }
17047513SDarren.Reed@Sun.COM
17057513SDarren.Reed@Sun.COM hei = hook_event_find(hfi, event);
17067513SDarren.Reed@Sun.COM if (hei == NULL) {
17077513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
17087513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
17097513SDarren.Reed@Sun.COM return (ESRCH);
17107513SDarren.Reed@Sun.COM }
17117513SDarren.Reed@Sun.COM
17127915SDarren.Reed@Sun.COM if (hei->hei_condemned || hei->hei_shutdown) {
17137513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
17147513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
17157513SDarren.Reed@Sun.COM return (ESHUTDOWN);
17167513SDarren.Reed@Sun.COM }
17177513SDarren.Reed@Sun.COM
1718*12263SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hei->hei_lock);
1719*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1720*12263SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1721*12263SDarren.Reed@Sun.COM error = hook_notify_register(&hei->hei_nhead, callback, arg);
1722*12263SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
17237513SDarren.Reed@Sun.COM
17247513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
17257513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hks->hks_lock);
17267513SDarren.Reed@Sun.COM
1727*12263SDarren.Reed@Sun.COM if (error == 0 && canrun) {
1728*12263SDarren.Reed@Sun.COM TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1729*12263SDarren.Reed@Sun.COM callback(HN_REGISTER, arg,
1730*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, hei->hei_event->he_name,
1731*12263SDarren.Reed@Sun.COM h->hi_hook.h_name);
1732*12263SDarren.Reed@Sun.COM }
1733*12263SDarren.Reed@Sun.COM }
1734*12263SDarren.Reed@Sun.COM
1735*12263SDarren.Reed@Sun.COM if (canrun)
1736*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1737*12263SDarren.Reed@Sun.COM
17387513SDarren.Reed@Sun.COM return (error);
17397513SDarren.Reed@Sun.COM }
17402958Sdr146992
17412958Sdr146992 /*
17427513SDarren.Reed@Sun.COM * Function: hook_event_notify_unregister
1743*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
17447513SDarren.Reed@Sun.COM * Parameters: hfi(I) - hook family
17457513SDarren.Reed@Sun.COM * event(I) - name of the event
17467513SDarren.Reed@Sun.COM * callback(I) - function to be called
17477513SDarren.Reed@Sun.COM *
17487513SDarren.Reed@Sun.COM * Remove the given callback from the named event's list of functions
17497513SDarren.Reed@Sun.COM * to call when a hook is added or removed.
17507513SDarren.Reed@Sun.COM */
17517513SDarren.Reed@Sun.COM int
hook_event_notify_unregister(hook_family_int_t * hfi,char * event,hook_notify_fn_t callback)17527513SDarren.Reed@Sun.COM hook_event_notify_unregister(hook_family_int_t *hfi, char *event,
17537513SDarren.Reed@Sun.COM hook_notify_fn_t callback)
17547513SDarren.Reed@Sun.COM {
17557513SDarren.Reed@Sun.COM hook_event_int_t *hei;
17567513SDarren.Reed@Sun.COM boolean_t free_event;
1757*12263SDarren.Reed@Sun.COM boolean_t canrun;
1758*12263SDarren.Reed@Sun.COM hook_int_t *h;
1759*12263SDarren.Reed@Sun.COM void *arg;
17607513SDarren.Reed@Sun.COM int error;
17617513SDarren.Reed@Sun.COM
1762*12263SDarren.Reed@Sun.COM canrun = B_FALSE;
1763*12263SDarren.Reed@Sun.COM
17647513SDarren.Reed@Sun.COM CVW_ENTER_READ(&hfi->hfi_lock);
17657513SDarren.Reed@Sun.COM
17667513SDarren.Reed@Sun.COM hei = hook_event_find(hfi, event);
17677513SDarren.Reed@Sun.COM if (hei == NULL) {
17687513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
17697513SDarren.Reed@Sun.COM return (ESRCH);
17707513SDarren.Reed@Sun.COM }
17717513SDarren.Reed@Sun.COM
17727513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hei->hei_lock);
17737513SDarren.Reed@Sun.COM
1774*12263SDarren.Reed@Sun.COM (void) hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
1775*12263SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE);
1776*12263SDarren.Reed@Sun.COM
1777*12263SDarren.Reed@Sun.COM error = hook_notify_unregister(&hei->hei_nhead, callback, &arg);
1778*12263SDarren.Reed@Sun.COM
1779*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
17807513SDarren.Reed@Sun.COM
17817513SDarren.Reed@Sun.COM /*
17827513SDarren.Reed@Sun.COM * hei_condemned has been set if someone tried to remove the
17837513SDarren.Reed@Sun.COM * event but couldn't because there were still things attached to
17847513SDarren.Reed@Sun.COM * it. Now that we've done a successful remove, if it is now empty
17857513SDarren.Reed@Sun.COM * then by all rights we should be free'ing it too. Note that the
17867513SDarren.Reed@Sun.COM * expectation is that only the caller of hook_event_add will ever
17877513SDarren.Reed@Sun.COM * call hook_event_remove.
17887513SDarren.Reed@Sun.COM */
17897513SDarren.Reed@Sun.COM if ((error == 0) && hei->hei_condemned &&
17907513SDarren.Reed@Sun.COM TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) {
17917513SDarren.Reed@Sun.COM free_event = B_TRUE;
17927513SDarren.Reed@Sun.COM } else {
17937513SDarren.Reed@Sun.COM free_event = B_FALSE;
17947513SDarren.Reed@Sun.COM }
17957513SDarren.Reed@Sun.COM
1796*12263SDarren.Reed@Sun.COM if (error == 0 && !free_event) {
1797*12263SDarren.Reed@Sun.COM canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
1798*12263SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1);
1799*12263SDarren.Reed@Sun.COM }
1800*12263SDarren.Reed@Sun.COM
18017513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
18027513SDarren.Reed@Sun.COM CVW_EXIT_READ(&hfi->hfi_lock);
18037513SDarren.Reed@Sun.COM
1804*12263SDarren.Reed@Sun.COM if (canrun) {
1805*12263SDarren.Reed@Sun.COM TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
1806*12263SDarren.Reed@Sun.COM callback(HN_UNREGISTER, arg,
1807*12263SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, hei->hei_event->he_name,
1808*12263SDarren.Reed@Sun.COM h->hi_hook.h_name);
1809*12263SDarren.Reed@Sun.COM }
1810*12263SDarren.Reed@Sun.COM
1811*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
1812*12263SDarren.Reed@Sun.COM }
1813*12263SDarren.Reed@Sun.COM
18147513SDarren.Reed@Sun.COM if (free_event) {
18157513SDarren.Reed@Sun.COM /*
18167513SDarren.Reed@Sun.COM * It is safe to pass in hfi here, without a lock, because
18177513SDarren.Reed@Sun.COM * our structure (hei) is still on one of its lists and thus
18187513SDarren.Reed@Sun.COM * it won't be able to disappear yet...
18197513SDarren.Reed@Sun.COM */
18207513SDarren.Reed@Sun.COM hook_event_free(hei, hfi);
18217513SDarren.Reed@Sun.COM }
18227513SDarren.Reed@Sun.COM
18237513SDarren.Reed@Sun.COM return (error);
18247513SDarren.Reed@Sun.COM }
18257513SDarren.Reed@Sun.COM
18267513SDarren.Reed@Sun.COM /*
18277513SDarren.Reed@Sun.COM * Function: hook_event_notify_run
18282958Sdr146992 * Returns: None
18297513SDarren.Reed@Sun.COM * Parameters: nrun(I) - pointer to the list of callbacks to execute
18307513SDarren.Reed@Sun.COM * hfi(I) - hook stack pointer to execute callbacks for
18317513SDarren.Reed@Sun.COM * name(I) - name of a hook family
18327513SDarren.Reed@Sun.COM * cmd(I) - either HN_UNREGISTER or HN_REGISTER
18332958Sdr146992 *
18347513SDarren.Reed@Sun.COM * Execute all of the callbacks registered for this event.
18352958Sdr146992 */
18362958Sdr146992 static void
hook_event_notify_run(hook_event_int_t * hei,hook_family_int_t * hfi,char * event,char * name,hook_notify_cmd_t cmd)18377513SDarren.Reed@Sun.COM hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi,
18387513SDarren.Reed@Sun.COM char *event, char *name, hook_notify_cmd_t cmd)
18392958Sdr146992 {
18402958Sdr146992
18417513SDarren.Reed@Sun.COM hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name,
18427513SDarren.Reed@Sun.COM event, name, cmd);
18432958Sdr146992 }
18442958Sdr146992
18452958Sdr146992 /*
18462958Sdr146992 * Function: hook_register
1847*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
1848*12263SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
18492958Sdr146992 * event(I) - event name string
1850*12263SDarren.Reed@Sun.COM * h(I) - hook pointer
18512958Sdr146992 *
18527513SDarren.Reed@Sun.COM * Add new hook to hook list on the specified family and event.
18532958Sdr146992 */
18542958Sdr146992 int
hook_register(hook_family_int_t * hfi,char * event,hook_t * h)18552958Sdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
18562958Sdr146992 {
18572958Sdr146992 hook_event_int_t *hei;
18582958Sdr146992 hook_int_t *hi, *new;
18597513SDarren.Reed@Sun.COM int error;
18602958Sdr146992
18612958Sdr146992 ASSERT(hfi != NULL);
18622958Sdr146992 ASSERT(event != NULL);
18632958Sdr146992 ASSERT(h != NULL);
18647513SDarren.Reed@Sun.COM
18657513SDarren.Reed@Sun.COM if (hfi->hfi_stack->hks_shutdown)
18667513SDarren.Reed@Sun.COM return (NULL);
18672958Sdr146992
18682958Sdr146992 /* Alloc hook_int_t and copy hook */
18692958Sdr146992 new = hook_copy(h);
18702958Sdr146992 if (new == NULL)
18712958Sdr146992 return (ENOMEM);
18722958Sdr146992
18732958Sdr146992 /*
18742958Sdr146992 * Since hook add/remove only impact event, so it is unnecessary
18752958Sdr146992 * to hold global family write lock. Just get read lock here to
18762958Sdr146992 * ensure event will not be removed when doing hooks operation
18772958Sdr146992 */
18787513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
18792958Sdr146992
18802958Sdr146992 hei = hook_event_find(hfi, event);
18812958Sdr146992 if (hei == NULL) {
18827513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
18837513SDarren.Reed@Sun.COM hook_int_free(new, hfi->hfi_stack->hks_netstackid);
18842958Sdr146992 return (ENXIO);
18852958Sdr146992 }
18862958Sdr146992
18872958Sdr146992 CVW_ENTER_WRITE(&hei->hei_lock);
18882958Sdr146992
18897915SDarren.Reed@Sun.COM /*
18907915SDarren.Reed@Sun.COM * If we've run either the remove() or shutdown(), do not allow any
18917915SDarren.Reed@Sun.COM * more hooks to be added to this event.
18927915SDarren.Reed@Sun.COM */
18937915SDarren.Reed@Sun.COM if (hei->hei_shutdown) {
18947915SDarren.Reed@Sun.COM error = ESHUTDOWN;
18957915SDarren.Reed@Sun.COM goto bad_add;
18967915SDarren.Reed@Sun.COM }
18977915SDarren.Reed@Sun.COM
18987513SDarren.Reed@Sun.COM hi = hook_find(hei, h);
18997513SDarren.Reed@Sun.COM if (hi != NULL) {
19007513SDarren.Reed@Sun.COM error = EEXIST;
19017513SDarren.Reed@Sun.COM goto bad_add;
19022958Sdr146992 }
19032958Sdr146992
1904*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK,
19057513SDarren.Reed@Sun.COM FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) {
19067513SDarren.Reed@Sun.COM error = ENOENT;
19077513SDarren.Reed@Sun.COM bad_add:
19082958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock);
19097513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
19107513SDarren.Reed@Sun.COM hook_int_free(new, hfi->hfi_stack->hks_netstackid);
19117513SDarren.Reed@Sun.COM return (error);
19122958Sdr146992 }
19132958Sdr146992
19142958Sdr146992 /* Add to hook list head */
19157513SDarren.Reed@Sun.COM error = hook_insert(&hei->hei_head, new);
19167513SDarren.Reed@Sun.COM if (error == 0) {
19177513SDarren.Reed@Sun.COM hei->hei_event->he_interested = B_TRUE;
19187513SDarren.Reed@Sun.COM hei->hei_kstats.hooks_added.value.ui64++;
19197513SDarren.Reed@Sun.COM
19207513SDarren.Reed@Sun.COM hook_init_kstats(hfi, hei, new);
19217513SDarren.Reed@Sun.COM }
19222958Sdr146992
19232958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock);
19247513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
19257513SDarren.Reed@Sun.COM
19267513SDarren.Reed@Sun.COM /*
19277513SDarren.Reed@Sun.COM * Note that the name string passed through to the notify callbacks
19287513SDarren.Reed@Sun.COM * is from the original hook being registered, not the copy being
19297513SDarren.Reed@Sun.COM * inserted.
19307513SDarren.Reed@Sun.COM */
1931*12263SDarren.Reed@Sun.COM if (error == 0)
19327513SDarren.Reed@Sun.COM hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER);
1933*12263SDarren.Reed@Sun.COM
1934*12263SDarren.Reed@Sun.COM hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE);
19357513SDarren.Reed@Sun.COM
19367513SDarren.Reed@Sun.COM return (error);
19377513SDarren.Reed@Sun.COM }
19387513SDarren.Reed@Sun.COM
19397513SDarren.Reed@Sun.COM /*
19407513SDarren.Reed@Sun.COM * Function: hook_insert
1941*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
19427513SDarren.Reed@Sun.COM * Parameters: head(I) - pointer to hook list to insert hook onto
19437513SDarren.Reed@Sun.COM * new(I) - pointer to hook to be inserted
19447513SDarren.Reed@Sun.COM *
19457513SDarren.Reed@Sun.COM * Try to insert the hook onto the list of hooks according to the hints
19467513SDarren.Reed@Sun.COM * given in the hook to be inserted and those that already exist on the
19477513SDarren.Reed@Sun.COM * list. For now, the implementation permits only a single hook to be
19487513SDarren.Reed@Sun.COM * either first or last and names provided with before or after are only
19497513SDarren.Reed@Sun.COM * loosely coupled with the action.
19507513SDarren.Reed@Sun.COM */
19517513SDarren.Reed@Sun.COM static int
hook_insert(hook_int_head_t * head,hook_int_t * new)19527513SDarren.Reed@Sun.COM hook_insert(hook_int_head_t *head, hook_int_t *new)
19537513SDarren.Reed@Sun.COM {
19547513SDarren.Reed@Sun.COM hook_int_t *before;
19557513SDarren.Reed@Sun.COM hook_int_t *hi;
19567513SDarren.Reed@Sun.COM hook_t *hih;
19577513SDarren.Reed@Sun.COM hook_t *h = &new->hi_hook;
19587513SDarren.Reed@Sun.COM
19597513SDarren.Reed@Sun.COM switch (new->hi_hook.h_hint) {
19607513SDarren.Reed@Sun.COM case HH_NONE :
19617513SDarren.Reed@Sun.COM before = NULL;
19627513SDarren.Reed@Sun.COM /*
19637513SDarren.Reed@Sun.COM * If there is no hint present (or not one that can be
19647513SDarren.Reed@Sun.COM * satisfied now) then try to at least respect the wishes
19657513SDarren.Reed@Sun.COM * of those that want to be last. If there are none wanting
19667513SDarren.Reed@Sun.COM * to be last then add the new hook to the tail of the
19677513SDarren.Reed@Sun.COM * list - this means we keep any wanting to be first
19687513SDarren.Reed@Sun.COM * happy without having to search for HH_FIRST.
19697513SDarren.Reed@Sun.COM */
19707513SDarren.Reed@Sun.COM TAILQ_FOREACH(hi, head, hi_entry) {
19717513SDarren.Reed@Sun.COM hih = &hi->hi_hook;
19727513SDarren.Reed@Sun.COM if ((hih->h_hint == HH_AFTER) &&
19737513SDarren.Reed@Sun.COM (strcmp(h->h_name,
19747513SDarren.Reed@Sun.COM (char *)hih->h_hintvalue) == 0)) {
19757513SDarren.Reed@Sun.COM TAILQ_INSERT_BEFORE(hi, new, hi_entry);
19767513SDarren.Reed@Sun.COM return (0);
19777513SDarren.Reed@Sun.COM }
19787513SDarren.Reed@Sun.COM if ((hih->h_hint == HH_BEFORE) && (before == NULL) &&
19797513SDarren.Reed@Sun.COM (strcmp(h->h_name,
19807513SDarren.Reed@Sun.COM (char *)hih->h_hintvalue) == 0)) {
19817513SDarren.Reed@Sun.COM before = hi;
19827513SDarren.Reed@Sun.COM }
19837513SDarren.Reed@Sun.COM }
19847513SDarren.Reed@Sun.COM if (before != NULL) {
19857513SDarren.Reed@Sun.COM TAILQ_INSERT_AFTER(head, before, new, hi_entry);
19867513SDarren.Reed@Sun.COM return (0);
19877513SDarren.Reed@Sun.COM }
19887513SDarren.Reed@Sun.COM hook_insert_plain(head, new);
19897513SDarren.Reed@Sun.COM break;
19907513SDarren.Reed@Sun.COM
19917513SDarren.Reed@Sun.COM case HH_FIRST :
19927513SDarren.Reed@Sun.COM hi = TAILQ_FIRST(head);
19937513SDarren.Reed@Sun.COM if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST))
19947513SDarren.Reed@Sun.COM return (EBUSY);
19957513SDarren.Reed@Sun.COM TAILQ_INSERT_HEAD(head, new, hi_entry);
19967513SDarren.Reed@Sun.COM break;
19977513SDarren.Reed@Sun.COM
19987513SDarren.Reed@Sun.COM case HH_LAST :
19997513SDarren.Reed@Sun.COM hi = TAILQ_LAST(head, hook_int_head);
20007513SDarren.Reed@Sun.COM if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST))
20017513SDarren.Reed@Sun.COM return (EBUSY);
20027513SDarren.Reed@Sun.COM TAILQ_INSERT_TAIL(head, new, hi_entry);
20037513SDarren.Reed@Sun.COM break;
20047513SDarren.Reed@Sun.COM
20057513SDarren.Reed@Sun.COM case HH_BEFORE :
20067513SDarren.Reed@Sun.COM hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20077513SDarren.Reed@Sun.COM if (hi == NULL)
20087513SDarren.Reed@Sun.COM return (hook_insert_afterbefore(head, new));
20097513SDarren.Reed@Sun.COM
20107513SDarren.Reed@Sun.COM if (hi->hi_hook.h_hint == HH_FIRST)
20117513SDarren.Reed@Sun.COM return (EBUSY);
20127513SDarren.Reed@Sun.COM
20137513SDarren.Reed@Sun.COM TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20147513SDarren.Reed@Sun.COM break;
20157513SDarren.Reed@Sun.COM
20167513SDarren.Reed@Sun.COM case HH_AFTER :
20177513SDarren.Reed@Sun.COM hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue);
20187513SDarren.Reed@Sun.COM if (hi == NULL)
20197513SDarren.Reed@Sun.COM return (hook_insert_afterbefore(head, new));
20207513SDarren.Reed@Sun.COM
20217513SDarren.Reed@Sun.COM if (hi->hi_hook.h_hint == HH_LAST)
20227513SDarren.Reed@Sun.COM return (EBUSY);
20237513SDarren.Reed@Sun.COM
20247513SDarren.Reed@Sun.COM TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
20257513SDarren.Reed@Sun.COM break;
20267513SDarren.Reed@Sun.COM
20277513SDarren.Reed@Sun.COM default :
20287513SDarren.Reed@Sun.COM return (EINVAL);
20297513SDarren.Reed@Sun.COM }
20307513SDarren.Reed@Sun.COM
20312958Sdr146992 return (0);
20322958Sdr146992 }
20332958Sdr146992
20347513SDarren.Reed@Sun.COM /*
20357513SDarren.Reed@Sun.COM * Function: hook_insert_plain
2036*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
20377513SDarren.Reed@Sun.COM * Parameters: head(I) - pointer to hook list to insert hook onto
20387513SDarren.Reed@Sun.COM * new(I) - pointer to hook to be inserted
20397513SDarren.Reed@Sun.COM *
20407513SDarren.Reed@Sun.COM * Insert a hook such that it respects the wishes of those that want to
20417513SDarren.Reed@Sun.COM * be last. If there are none wanting to be last then add the new hook
20427513SDarren.Reed@Sun.COM * to the tail of the list - this means we keep any wanting to be first
20437513SDarren.Reed@Sun.COM * happy without having to search for HH_FIRST.
20447513SDarren.Reed@Sun.COM */
20457513SDarren.Reed@Sun.COM static void
hook_insert_plain(hook_int_head_t * head,hook_int_t * new)20467513SDarren.Reed@Sun.COM hook_insert_plain(hook_int_head_t *head, hook_int_t *new)
20477513SDarren.Reed@Sun.COM {
20487513SDarren.Reed@Sun.COM hook_int_t *hi;
20497513SDarren.Reed@Sun.COM
20507513SDarren.Reed@Sun.COM hi = TAILQ_FIRST(head);
20517513SDarren.Reed@Sun.COM if (hi != NULL) {
20527513SDarren.Reed@Sun.COM if (hi->hi_hook.h_hint == HH_LAST) {
20537513SDarren.Reed@Sun.COM TAILQ_INSERT_BEFORE(hi, new, hi_entry);
20547513SDarren.Reed@Sun.COM } else {
20557513SDarren.Reed@Sun.COM TAILQ_INSERT_TAIL(head, new, hi_entry);
20567513SDarren.Reed@Sun.COM }
20577513SDarren.Reed@Sun.COM } else {
20587513SDarren.Reed@Sun.COM TAILQ_INSERT_TAIL(head, new, hi_entry);
20597513SDarren.Reed@Sun.COM }
20607513SDarren.Reed@Sun.COM }
20617513SDarren.Reed@Sun.COM
20627513SDarren.Reed@Sun.COM /*
20637513SDarren.Reed@Sun.COM * Function: hook_insert_afterbefore
2064*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
20657513SDarren.Reed@Sun.COM * Parameters: head(I) - pointer to hook list to insert hook onto
20667513SDarren.Reed@Sun.COM * new(I) - pointer to hook to be inserted
20677513SDarren.Reed@Sun.COM *
20687513SDarren.Reed@Sun.COM * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not
20697513SDarren.Reed@Sun.COM * possible, so now we need to be more careful. The first pass is to go
20707513SDarren.Reed@Sun.COM * through the list and look for any other hooks that also specify the
20717513SDarren.Reed@Sun.COM * same hint name as the new one. The object of this exercise is to make
20727513SDarren.Reed@Sun.COM * sure that hooks with HH_BEFORE always appear on the list before those
20737513SDarren.Reed@Sun.COM * with HH_AFTER so that when said hook arrives, it can be placed in the
20747513SDarren.Reed@Sun.COM * middle of the BEFOREs and AFTERs. If this condition does not arise,
20757513SDarren.Reed@Sun.COM * just use hook_insert_plain() to try and insert the hook somewhere that
20767513SDarren.Reed@Sun.COM * is innocuous to existing efforts.
20777513SDarren.Reed@Sun.COM */
20787513SDarren.Reed@Sun.COM static int
hook_insert_afterbefore(hook_int_head_t * head,hook_int_t * new)20797513SDarren.Reed@Sun.COM hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new)
20807513SDarren.Reed@Sun.COM {
20817513SDarren.Reed@Sun.COM hook_int_t *hi;
20827513SDarren.Reed@Sun.COM hook_t *nh;
20837513SDarren.Reed@Sun.COM hook_t *h;
20847513SDarren.Reed@Sun.COM
20857513SDarren.Reed@Sun.COM nh = &new->hi_hook;
20867513SDarren.Reed@Sun.COM ASSERT(new->hi_hook.h_hint != HH_NONE);
20877513SDarren.Reed@Sun.COM ASSERT(new->hi_hook.h_hint != HH_LAST);
20887513SDarren.Reed@Sun.COM ASSERT(new->hi_hook.h_hint != HH_FIRST);
20897513SDarren.Reed@Sun.COM
20907513SDarren.Reed@Sun.COM /*
20917513SDarren.Reed@Sun.COM * First, look through the list to see if there are any other
20927513SDarren.Reed@Sun.COM * before's or after's that have a matching hint name.
20937513SDarren.Reed@Sun.COM */
20947513SDarren.Reed@Sun.COM TAILQ_FOREACH(hi, head, hi_entry) {
20957513SDarren.Reed@Sun.COM h = &hi->hi_hook;
20967513SDarren.Reed@Sun.COM switch (h->h_hint) {
20977513SDarren.Reed@Sun.COM case HH_FIRST :
20987513SDarren.Reed@Sun.COM case HH_LAST :
20997513SDarren.Reed@Sun.COM case HH_NONE :
21007513SDarren.Reed@Sun.COM break;
21017513SDarren.Reed@Sun.COM case HH_BEFORE :
21027513SDarren.Reed@Sun.COM if ((nh->h_hint == HH_BEFORE) &&
21037513SDarren.Reed@Sun.COM (strcmp((char *)h->h_hintvalue,
21047513SDarren.Reed@Sun.COM (char *)nh->h_hintvalue) == 0)) {
21057513SDarren.Reed@Sun.COM TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21067513SDarren.Reed@Sun.COM return (0);
21077513SDarren.Reed@Sun.COM }
21087513SDarren.Reed@Sun.COM if ((nh->h_hint == HH_AFTER) &&
21097513SDarren.Reed@Sun.COM (strcmp((char *)h->h_hintvalue,
21107513SDarren.Reed@Sun.COM (char *)nh->h_hintvalue) == 0)) {
21117513SDarren.Reed@Sun.COM TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21127513SDarren.Reed@Sun.COM return (0);
21137513SDarren.Reed@Sun.COM }
21147513SDarren.Reed@Sun.COM break;
21157513SDarren.Reed@Sun.COM case HH_AFTER :
21167513SDarren.Reed@Sun.COM if ((nh->h_hint == HH_AFTER) &&
21177513SDarren.Reed@Sun.COM (strcmp((char *)h->h_hintvalue,
21187513SDarren.Reed@Sun.COM (char *)nh->h_hintvalue) == 0)) {
21197513SDarren.Reed@Sun.COM TAILQ_INSERT_AFTER(head, hi, new, hi_entry);
21207513SDarren.Reed@Sun.COM return (0);
21217513SDarren.Reed@Sun.COM }
21227513SDarren.Reed@Sun.COM if ((nh->h_hint == HH_BEFORE) &&
21237513SDarren.Reed@Sun.COM (strcmp((char *)h->h_hintvalue,
21247513SDarren.Reed@Sun.COM (char *)nh->h_hintvalue) == 0)) {
21257513SDarren.Reed@Sun.COM TAILQ_INSERT_BEFORE(hi, new, hi_entry);
21267513SDarren.Reed@Sun.COM return (0);
21277513SDarren.Reed@Sun.COM }
21287513SDarren.Reed@Sun.COM break;
21297513SDarren.Reed@Sun.COM }
21307513SDarren.Reed@Sun.COM }
21317513SDarren.Reed@Sun.COM
21327513SDarren.Reed@Sun.COM hook_insert_plain(head, new);
21337513SDarren.Reed@Sun.COM
21347513SDarren.Reed@Sun.COM return (0);
21357513SDarren.Reed@Sun.COM }
21362958Sdr146992
21372958Sdr146992 /*
21382958Sdr146992 * Function: hook_unregister
2139*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else = failure
2140*12263SDarren.Reed@Sun.COM * Parameters: hfi(I) - internal family pointer
21412958Sdr146992 * event(I) - event name string
2142*12263SDarren.Reed@Sun.COM * h(I) - hook pointer
21432958Sdr146992 *
21442958Sdr146992 * Remove hook from hook list on specific family, event
21452958Sdr146992 */
21462958Sdr146992 int
hook_unregister(hook_family_int_t * hfi,char * event,hook_t * h)21472958Sdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
21482958Sdr146992 {
21492958Sdr146992 hook_event_int_t *hei;
21502958Sdr146992 hook_int_t *hi;
21517513SDarren.Reed@Sun.COM boolean_t free_event;
21522958Sdr146992
21532958Sdr146992 ASSERT(hfi != NULL);
21542958Sdr146992 ASSERT(h != NULL);
21552958Sdr146992
21567513SDarren.Reed@Sun.COM CVW_ENTER_WRITE(&hfi->hfi_lock);
21572958Sdr146992
21582958Sdr146992 hei = hook_event_find(hfi, event);
21592958Sdr146992 if (hei == NULL) {
21607513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
21612958Sdr146992 return (ENXIO);
21622958Sdr146992 }
21632958Sdr146992
21642958Sdr146992 /* Hold write lock for event */
21652958Sdr146992 CVW_ENTER_WRITE(&hei->hei_lock);
21662958Sdr146992
21672958Sdr146992 hi = hook_find(hei, h);
21682958Sdr146992 if (hi == NULL) {
21692958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock);
21707513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
21712958Sdr146992 return (ENXIO);
21722958Sdr146992 }
21732958Sdr146992
2174*12263SDarren.Reed@Sun.COM if (hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK,
21757513SDarren.Reed@Sun.COM FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) {
21767513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hei->hei_lock);
21777513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
21787513SDarren.Reed@Sun.COM return (ENOENT);
21797513SDarren.Reed@Sun.COM }
21807513SDarren.Reed@Sun.COM
21812958Sdr146992 /* Remove from hook list */
21822958Sdr146992 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
21837513SDarren.Reed@Sun.COM
21847513SDarren.Reed@Sun.COM free_event = B_FALSE;
21852958Sdr146992 if (TAILQ_EMPTY(&hei->hei_head)) {
21862958Sdr146992 hei->hei_event->he_interested = B_FALSE;
21877513SDarren.Reed@Sun.COM /*
21887513SDarren.Reed@Sun.COM * If the delete pending flag has been set and there are
21897513SDarren.Reed@Sun.COM * no notifiers on the event (and we've removed the last
21907513SDarren.Reed@Sun.COM * hook) then we need to free this event after we're done.
21917513SDarren.Reed@Sun.COM */
21927513SDarren.Reed@Sun.COM if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead))
21937513SDarren.Reed@Sun.COM free_event = B_TRUE;
21942958Sdr146992 }
21957513SDarren.Reed@Sun.COM hei->hei_kstats.hooks_removed.value.ui64++;
21962958Sdr146992
21972958Sdr146992 CVW_EXIT_WRITE(&hei->hei_lock);
21987513SDarren.Reed@Sun.COM CVW_EXIT_WRITE(&hfi->hfi_lock);
21997513SDarren.Reed@Sun.COM /*
22007513SDarren.Reed@Sun.COM * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t
22017513SDarren.Reed@Sun.COM * will not be free'd and thus the hook_family_int_t wil not
22027513SDarren.Reed@Sun.COM * be free'd either.
22037513SDarren.Reed@Sun.COM */
22047513SDarren.Reed@Sun.COM hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER);
22057513SDarren.Reed@Sun.COM hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE);
22062958Sdr146992
22077513SDarren.Reed@Sun.COM hook_int_free(hi, hfi->hfi_stack->hks_netstackid);
22087513SDarren.Reed@Sun.COM
22097513SDarren.Reed@Sun.COM if (free_event)
22107513SDarren.Reed@Sun.COM hook_event_free(hei, hfi);
22117513SDarren.Reed@Sun.COM
22122958Sdr146992 return (0);
22132958Sdr146992 }
22142958Sdr146992
22157513SDarren.Reed@Sun.COM /*
22167513SDarren.Reed@Sun.COM * Function: hook_find_byname
22177513SDarren.Reed@Sun.COM * Returns: internal hook pointer - NULL = Not match
22187513SDarren.Reed@Sun.COM * Parameters: hei(I) - internal event pointer
22197513SDarren.Reed@Sun.COM * name(I)- hook name
22207513SDarren.Reed@Sun.COM *
22217513SDarren.Reed@Sun.COM * Search an event's list of hooks to see if there is a hook present that
22227513SDarren.Reed@Sun.COM * has a matching name to the one being looked for.
22237513SDarren.Reed@Sun.COM */
22247513SDarren.Reed@Sun.COM static hook_int_t *
hook_find_byname(hook_int_head_t * head,char * name)22257513SDarren.Reed@Sun.COM hook_find_byname(hook_int_head_t *head, char *name)
22267513SDarren.Reed@Sun.COM {
22277513SDarren.Reed@Sun.COM hook_int_t *hi;
22287513SDarren.Reed@Sun.COM
22297513SDarren.Reed@Sun.COM TAILQ_FOREACH(hi, head, hi_entry) {
22307513SDarren.Reed@Sun.COM if (strcmp(hi->hi_hook.h_name, name) == 0)
22317513SDarren.Reed@Sun.COM return (hi);
22327513SDarren.Reed@Sun.COM }
22337513SDarren.Reed@Sun.COM
22347513SDarren.Reed@Sun.COM return (NULL);
22357513SDarren.Reed@Sun.COM }
22362958Sdr146992
22372958Sdr146992 /*
22382958Sdr146992 * Function: hook_find
22392958Sdr146992 * Returns: internal hook pointer - NULL = Not match
22402958Sdr146992 * Parameters: hei(I) - internal event pointer
2241*12263SDarren.Reed@Sun.COM * h(I) - hook pointer
22422958Sdr146992 *
22437513SDarren.Reed@Sun.COM * Search an event's list of hooks to see if there is already one that
22447513SDarren.Reed@Sun.COM * matches the hook being passed in. Currently the only criteria for a
22457513SDarren.Reed@Sun.COM * successful search here is for the names to be the same.
22462958Sdr146992 */
22472958Sdr146992 static hook_int_t *
hook_find(hook_event_int_t * hei,hook_t * h)22482958Sdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
22492958Sdr146992 {
22502958Sdr146992
22512958Sdr146992 ASSERT(hei != NULL);
22522958Sdr146992 ASSERT(h != NULL);
22532958Sdr146992
22547513SDarren.Reed@Sun.COM return (hook_find_byname(&hei->hei_head, h->h_name));
22552958Sdr146992 }
22562958Sdr146992
22572958Sdr146992 /*
22582958Sdr146992 * Function: hook_copy
22592958Sdr146992 * Returns: internal hook pointer - NULL = Failed
22602958Sdr146992 * Parameters: src(I) - hook pointer
22612958Sdr146992 *
22622958Sdr146992 * Allocate internal hook block and duplicate incoming hook.
22632958Sdr146992 * No locks should be held across this function as it may sleep.
22647513SDarren.Reed@Sun.COM * Because hook_copy() is responsible for the creation of the internal
22657513SDarren.Reed@Sun.COM * hook structure that is used here, it takes on population the structure
22667513SDarren.Reed@Sun.COM * with the kstat information. Note that while the kstat bits are
22677513SDarren.Reed@Sun.COM * seeded here, their installation of the kstats is handled elsewhere.
22682958Sdr146992 */
22692958Sdr146992 static hook_int_t *
hook_copy(hook_t * src)22702958Sdr146992 hook_copy(hook_t *src)
22712958Sdr146992 {
22722958Sdr146992 hook_int_t *new;
22732958Sdr146992 hook_t *dst;
22747513SDarren.Reed@Sun.COM int len;
22752958Sdr146992
22762958Sdr146992 ASSERT(src != NULL);
22772958Sdr146992 ASSERT(src->h_name != NULL);
22782958Sdr146992
22792958Sdr146992 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
22802958Sdr146992
22812958Sdr146992 /* Copy body */
22822958Sdr146992 dst = &new->hi_hook;
22832958Sdr146992 *dst = *src;
22842958Sdr146992
22852958Sdr146992 /* Copy name */
22867513SDarren.Reed@Sun.COM len = strlen(src->h_name);
22877513SDarren.Reed@Sun.COM dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP);
22882958Sdr146992 (void) strcpy(dst->h_name, src->h_name);
22892958Sdr146992
22907513SDarren.Reed@Sun.COM /*
22917513SDarren.Reed@Sun.COM * This is initialised in this manner to make it safer to use the
22927513SDarren.Reed@Sun.COM * same pointer in the kstats field.
22937513SDarren.Reed@Sun.COM */
22947513SDarren.Reed@Sun.COM dst->h_hintvalue = (uintptr_t)"";
22957513SDarren.Reed@Sun.COM
22967513SDarren.Reed@Sun.COM if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) {
22977513SDarren.Reed@Sun.COM len = strlen((char *)src->h_hintvalue);
22987513SDarren.Reed@Sun.COM if (len > 0) {
22997513SDarren.Reed@Sun.COM dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1,
23007513SDarren.Reed@Sun.COM KM_SLEEP);
23017513SDarren.Reed@Sun.COM (void) strcpy((char *)dst->h_hintvalue,
23027513SDarren.Reed@Sun.COM (char *)src->h_hintvalue);
23037513SDarren.Reed@Sun.COM }
23047513SDarren.Reed@Sun.COM }
23057513SDarren.Reed@Sun.COM
23062958Sdr146992 return (new);
23072958Sdr146992 }
23082958Sdr146992
23092958Sdr146992 /*
23107513SDarren.Reed@Sun.COM * Function: hook_init_kstats
23117513SDarren.Reed@Sun.COM * Returns: None
23127513SDarren.Reed@Sun.COM * Parameters: hfi(I) - pointer to the family that owns the event.
23137513SDarren.Reed@Sun.COM * hei(I) - pointer to the event that owns this hook
23147513SDarren.Reed@Sun.COM * hi(I) - pointer to the hook for which we create kstats for
23157513SDarren.Reed@Sun.COM *
23167513SDarren.Reed@Sun.COM * Each hook that is registered with this framework has its own kstats
23177513SDarren.Reed@Sun.COM * set up so that we can provide an easy way in which to observe the
23187513SDarren.Reed@Sun.COM * look of hooks (using the kstat command.) The position is set to 0
23197513SDarren.Reed@Sun.COM * here but is recalculated after we know the insertion has been a
23207513SDarren.Reed@Sun.COM * success.
23217513SDarren.Reed@Sun.COM */
23227513SDarren.Reed@Sun.COM static void
hook_init_kstats(hook_family_int_t * hfi,hook_event_int_t * hei,hook_int_t * hi)23237513SDarren.Reed@Sun.COM hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi)
23247513SDarren.Reed@Sun.COM {
23257513SDarren.Reed@Sun.COM hook_hook_kstat_t template = {
23267513SDarren.Reed@Sun.COM { "version", KSTAT_DATA_INT32 },
23277513SDarren.Reed@Sun.COM { "flags", KSTAT_DATA_UINT32 },
23287513SDarren.Reed@Sun.COM { "hint", KSTAT_DATA_INT32 },
23297513SDarren.Reed@Sun.COM { "hint_value", KSTAT_DATA_UINT64 },
23307513SDarren.Reed@Sun.COM { "position", KSTAT_DATA_INT32 },
23317513SDarren.Reed@Sun.COM { "hook_hits", KSTAT_DATA_UINT64 }
23327513SDarren.Reed@Sun.COM };
23337513SDarren.Reed@Sun.COM hook_stack_t *hks;
23347513SDarren.Reed@Sun.COM size_t kslen;
23357513SDarren.Reed@Sun.COM int position;
23367513SDarren.Reed@Sun.COM hook_int_t *h;
23377513SDarren.Reed@Sun.COM
23387513SDarren.Reed@Sun.COM kslen = strlen(hfi->hfi_family.hf_name) +
23397513SDarren.Reed@Sun.COM strlen(hei->hei_event->he_name) + 2;
23407513SDarren.Reed@Sun.COM
23417513SDarren.Reed@Sun.COM hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP);
23427513SDarren.Reed@Sun.COM (void) snprintf(hi->hi_ksname, kslen, "%s/%s",
23437513SDarren.Reed@Sun.COM hfi->hfi_family.hf_name, hei->hei_event->he_name);
23447513SDarren.Reed@Sun.COM
23457513SDarren.Reed@Sun.COM hks = hfi->hfi_stack;
23467513SDarren.Reed@Sun.COM hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0,
23477513SDarren.Reed@Sun.COM hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED,
23487513SDarren.Reed@Sun.COM sizeof (hi->hi_kstats) / sizeof (kstat_named_t),
23497513SDarren.Reed@Sun.COM KSTAT_FLAG_VIRTUAL, hks->hks_netstackid);
23507513SDarren.Reed@Sun.COM
23517513SDarren.Reed@Sun.COM /* Initialise the kstats for the structure */
23527513SDarren.Reed@Sun.COM bcopy(&template, &hi->hi_kstats, sizeof (template));
23537513SDarren.Reed@Sun.COM hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version;
23547513SDarren.Reed@Sun.COM hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags;
23557513SDarren.Reed@Sun.COM hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint;
23567513SDarren.Reed@Sun.COM hi->hi_kstats.hook_position.value.i32 = 0;
23577513SDarren.Reed@Sun.COM hi->hi_kstats.hook_hits.value.ui64 = 0;
23587513SDarren.Reed@Sun.COM
23597513SDarren.Reed@Sun.COM switch (hi->hi_hook.h_hint) {
23607513SDarren.Reed@Sun.COM case HH_BEFORE :
23617513SDarren.Reed@Sun.COM case HH_AFTER :
23627513SDarren.Reed@Sun.COM hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING;
23637513SDarren.Reed@Sun.COM hi->hi_kstats.hook_hintvalue.value.ui64 =
23647513SDarren.Reed@Sun.COM hi->hi_hook.h_hintvalue;
23657513SDarren.Reed@Sun.COM break;
23667513SDarren.Reed@Sun.COM default :
23677513SDarren.Reed@Sun.COM break;
23687513SDarren.Reed@Sun.COM }
23697513SDarren.Reed@Sun.COM
23707513SDarren.Reed@Sun.COM if (hi->hi_kstatp != NULL) {
23717513SDarren.Reed@Sun.COM hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats;
23727513SDarren.Reed@Sun.COM hi->hi_kstatp->ks_private =
23737513SDarren.Reed@Sun.COM (void *)(uintptr_t)hks->hks_netstackid;
23747513SDarren.Reed@Sun.COM
23757513SDarren.Reed@Sun.COM kstat_install(hi->hi_kstatp);
23767513SDarren.Reed@Sun.COM }
23777513SDarren.Reed@Sun.COM
23787513SDarren.Reed@Sun.COM position = 1;
23797513SDarren.Reed@Sun.COM TAILQ_FOREACH(h, &hei->hei_head, hi_entry) {
23807513SDarren.Reed@Sun.COM h->hi_kstats.hook_position.value.ui32 = position++;
23817513SDarren.Reed@Sun.COM }
23827513SDarren.Reed@Sun.COM }
23837513SDarren.Reed@Sun.COM
23847513SDarren.Reed@Sun.COM /*
23857513SDarren.Reed@Sun.COM * Function: hook_int_free
23862958Sdr146992 * Returns: None
23872958Sdr146992 * Parameters: hi(I) - internal hook pointer
23882958Sdr146992 *
2389*12263SDarren.Reed@Sun.COM * Free memory allocated to support a hook.
23902958Sdr146992 */
23912958Sdr146992 static void
hook_int_free(hook_int_t * hi,netstackid_t stackid)23927513SDarren.Reed@Sun.COM hook_int_free(hook_int_t *hi, netstackid_t stackid)
23932958Sdr146992 {
23947513SDarren.Reed@Sun.COM int len;
23957513SDarren.Reed@Sun.COM
23962958Sdr146992 ASSERT(hi != NULL);
23972958Sdr146992
23982958Sdr146992 /* Free name space */
23992958Sdr146992 if (hi->hi_hook.h_name != NULL) {
24002958Sdr146992 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
24012958Sdr146992 }
24027513SDarren.Reed@Sun.COM if (hi->hi_ksname != NULL) {
24037513SDarren.Reed@Sun.COM kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1);
24047513SDarren.Reed@Sun.COM }
24057513SDarren.Reed@Sun.COM
24067513SDarren.Reed@Sun.COM /* Free the name used with the before/after hints. */
24077513SDarren.Reed@Sun.COM switch (hi->hi_hook.h_hint) {
24087513SDarren.Reed@Sun.COM case HH_BEFORE :
24097513SDarren.Reed@Sun.COM case HH_AFTER :
24107513SDarren.Reed@Sun.COM len = strlen((char *)hi->hi_hook.h_hintvalue);
24117513SDarren.Reed@Sun.COM if (len > 0)
24127513SDarren.Reed@Sun.COM kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1);
24137513SDarren.Reed@Sun.COM break;
24147513SDarren.Reed@Sun.COM default :
24157513SDarren.Reed@Sun.COM break;
24167513SDarren.Reed@Sun.COM }
24177513SDarren.Reed@Sun.COM
24187513SDarren.Reed@Sun.COM if (hi->hi_kstatp != NULL)
24197513SDarren.Reed@Sun.COM kstat_delete_netstack(hi->hi_kstatp, stackid);
24202958Sdr146992
24212958Sdr146992 /* Free container */
24222958Sdr146992 kmem_free(hi, sizeof (*hi));
24232958Sdr146992 }
24247513SDarren.Reed@Sun.COM
24257513SDarren.Reed@Sun.COM /*
24267513SDarren.Reed@Sun.COM * Function: hook_alloc
2427*12263SDarren.Reed@Sun.COM * Returns: hook_t * - pointer to new hook structure
24287513SDarren.Reed@Sun.COM * Parameters: version(I) - version number of the API when compiled
24297513SDarren.Reed@Sun.COM *
24307513SDarren.Reed@Sun.COM * This function serves as the interface for consumers to obtain a hook_t
24317513SDarren.Reed@Sun.COM * structure. At this point in time, there is only a single "version" of
24327513SDarren.Reed@Sun.COM * it, leading to a straight forward function. In a perfect world the
24337513SDarren.Reed@Sun.COM * h_vesion would be a protected data structure member, but C isn't that
24347513SDarren.Reed@Sun.COM * advanced...
24357513SDarren.Reed@Sun.COM */
24367513SDarren.Reed@Sun.COM hook_t *
hook_alloc(const int h_version)24377513SDarren.Reed@Sun.COM hook_alloc(const int h_version)
24387513SDarren.Reed@Sun.COM {
24397513SDarren.Reed@Sun.COM hook_t *h;
24407513SDarren.Reed@Sun.COM
24417513SDarren.Reed@Sun.COM h = kmem_zalloc(sizeof (hook_t), KM_SLEEP);
24427513SDarren.Reed@Sun.COM h->h_version = h_version;
24437513SDarren.Reed@Sun.COM return (h);
24447513SDarren.Reed@Sun.COM }
24457513SDarren.Reed@Sun.COM
24467513SDarren.Reed@Sun.COM /*
24477513SDarren.Reed@Sun.COM * Function: hook_free
24487513SDarren.Reed@Sun.COM * Returns: None
24497513SDarren.Reed@Sun.COM * Parameters: h(I) - external hook pointer
24507513SDarren.Reed@Sun.COM *
24517513SDarren.Reed@Sun.COM * This function only free's memory allocated with hook_alloc(), so that if
24527513SDarren.Reed@Sun.COM * (for example) kernel memory was allocated for h_name, this needs to be
24537513SDarren.Reed@Sun.COM * free'd before calling hook_free().
24547513SDarren.Reed@Sun.COM */
24557513SDarren.Reed@Sun.COM void
hook_free(hook_t * h)24567513SDarren.Reed@Sun.COM hook_free(hook_t *h)
24577513SDarren.Reed@Sun.COM {
24587513SDarren.Reed@Sun.COM kmem_free(h, sizeof (*h));
24597513SDarren.Reed@Sun.COM }
24607513SDarren.Reed@Sun.COM
24617513SDarren.Reed@Sun.COM /*
24627513SDarren.Reed@Sun.COM * Function: hook_notify_register
2463*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
2464*12263SDarren.Reed@Sun.COM * Parameters: head(I) - top of the list of callbacks
24657513SDarren.Reed@Sun.COM * callback(I) - function to be called
24667513SDarren.Reed@Sun.COM * arg(I) - arg to pass back to the function
24677513SDarren.Reed@Sun.COM *
24687513SDarren.Reed@Sun.COM * This function implements the modification of the list of callbacks
24697513SDarren.Reed@Sun.COM * that are registered when someone wants to be advised of a change
24707513SDarren.Reed@Sun.COM * that has happened.
24717513SDarren.Reed@Sun.COM */
24727513SDarren.Reed@Sun.COM static int
hook_notify_register(hook_notify_head_t * head,hook_notify_fn_t callback,void * arg)2473*12263SDarren.Reed@Sun.COM hook_notify_register(hook_notify_head_t *head, hook_notify_fn_t callback,
2474*12263SDarren.Reed@Sun.COM void *arg)
24757513SDarren.Reed@Sun.COM {
24767513SDarren.Reed@Sun.COM hook_notify_t *hn;
24777513SDarren.Reed@Sun.COM
24787513SDarren.Reed@Sun.COM TAILQ_FOREACH(hn, head, hn_entry) {
24797513SDarren.Reed@Sun.COM if (hn->hn_func == callback) {
24807513SDarren.Reed@Sun.COM return (EEXIST);
24817513SDarren.Reed@Sun.COM }
24827513SDarren.Reed@Sun.COM }
24837513SDarren.Reed@Sun.COM
24847513SDarren.Reed@Sun.COM hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP);
24857513SDarren.Reed@Sun.COM hn->hn_func = callback;
24867513SDarren.Reed@Sun.COM hn->hn_arg = arg;
24877513SDarren.Reed@Sun.COM TAILQ_INSERT_TAIL(head, hn, hn_entry);
24887513SDarren.Reed@Sun.COM
24897513SDarren.Reed@Sun.COM return (0);
24907513SDarren.Reed@Sun.COM }
24917513SDarren.Reed@Sun.COM
24927513SDarren.Reed@Sun.COM /*
2493*12263SDarren.Reed@Sun.COM * Function: hook_notify_unregister
2494*12263SDarren.Reed@Sun.COM * Returns: int - 0 = success, else failure
2495*12263SDarren.Reed@Sun.COM * Parameters: stackid(I) - netstack identifier
24967513SDarren.Reed@Sun.COM * callback(I) - function to be called
2497*12263SDarren.Reed@Sun.COM * parg(O) - pointer to storage for pointer
24987513SDarren.Reed@Sun.COM *
2499*12263SDarren.Reed@Sun.COM * When calling this function, the provision of a valid pointer in parg
2500*12263SDarren.Reed@Sun.COM * allows the caller to be made aware of what argument the hook function
2501*12263SDarren.Reed@Sun.COM * was expecting. This then allows the simulation of HN_UNREGISTER events
2502*12263SDarren.Reed@Sun.COM * when a notify-unregister is performed.
25037513SDarren.Reed@Sun.COM */
25047513SDarren.Reed@Sun.COM static int
hook_notify_unregister(hook_notify_head_t * head,hook_notify_fn_t callback,void ** parg)2505*12263SDarren.Reed@Sun.COM hook_notify_unregister(hook_notify_head_t *head,
2506*12263SDarren.Reed@Sun.COM hook_notify_fn_t callback, void **parg)
25077513SDarren.Reed@Sun.COM {
25087513SDarren.Reed@Sun.COM hook_notify_t *hn;
25097513SDarren.Reed@Sun.COM
2510*12263SDarren.Reed@Sun.COM ASSERT(parg != NULL);
25117513SDarren.Reed@Sun.COM
25127513SDarren.Reed@Sun.COM TAILQ_FOREACH(hn, head, hn_entry) {
25137513SDarren.Reed@Sun.COM if (hn->hn_func == callback)
25147513SDarren.Reed@Sun.COM break;
25157513SDarren.Reed@Sun.COM }
2516*12263SDarren.Reed@Sun.COM
2517*12263SDarren.Reed@Sun.COM if (hn == NULL)
25187513SDarren.Reed@Sun.COM return (ESRCH);
2519*12263SDarren.Reed@Sun.COM
2520*12263SDarren.Reed@Sun.COM *parg = hn->hn_arg;
25217513SDarren.Reed@Sun.COM
25227513SDarren.Reed@Sun.COM TAILQ_REMOVE(head, hn, hn_entry);
25237513SDarren.Reed@Sun.COM
25247513SDarren.Reed@Sun.COM kmem_free(hn, sizeof (*hn));
25257513SDarren.Reed@Sun.COM
25267513SDarren.Reed@Sun.COM return (0);
25277513SDarren.Reed@Sun.COM }
25287513SDarren.Reed@Sun.COM
25297513SDarren.Reed@Sun.COM /*
25307513SDarren.Reed@Sun.COM * Function: hook_notify_run
25317513SDarren.Reed@Sun.COM * Returns: None
25327513SDarren.Reed@Sun.COM * Parameters: head(I) - top of the list of callbacks
25337513SDarren.Reed@Sun.COM * family(I) - name of the hook family that owns the event
25347513SDarren.Reed@Sun.COM * event(I) - name of the event being changed
25357513SDarren.Reed@Sun.COM * name(I) - name of the object causing change
25367513SDarren.Reed@Sun.COM * cmd(I) - either HN_UNREGISTER or HN_REGISTER
25377513SDarren.Reed@Sun.COM *
25387513SDarren.Reed@Sun.COM * This function walks through the list of registered callbacks and
25397513SDarren.Reed@Sun.COM * executes each one, passing back the arg supplied when registered
25407513SDarren.Reed@Sun.COM * and the name of the family (that owns the event), event (the thing
25417513SDarren.Reed@Sun.COM * to which we're making a change) and finally a name that describes
25427513SDarren.Reed@Sun.COM * what is being added or removed, as indicated by cmd.
25437513SDarren.Reed@Sun.COM *
25447513SDarren.Reed@Sun.COM * This function does not acquire or release any lock as it is required
25457513SDarren.Reed@Sun.COM * that code calling it do so before hand. The use of hook_notify_head_t
25467513SDarren.Reed@Sun.COM * is protected by the use of flagwait_t in the structures that own this
25477513SDarren.Reed@Sun.COM * list and with the use of the FWF_ADD/DEL_ACTIVE flags.
25487513SDarren.Reed@Sun.COM */
25497513SDarren.Reed@Sun.COM static void
hook_notify_run(hook_notify_head_t * head,char * family,char * event,char * name,hook_notify_cmd_t cmd)25507513SDarren.Reed@Sun.COM hook_notify_run(hook_notify_head_t *head, char *family, char *event,
25517513SDarren.Reed@Sun.COM char *name, hook_notify_cmd_t cmd)
25527513SDarren.Reed@Sun.COM {
25537513SDarren.Reed@Sun.COM hook_notify_t *hn;
25547513SDarren.Reed@Sun.COM
25557513SDarren.Reed@Sun.COM TAILQ_FOREACH(hn, head, hn_entry) {
25567513SDarren.Reed@Sun.COM (*hn->hn_func)(cmd, hn->hn_arg, family, event, name);
25577513SDarren.Reed@Sun.COM }
25587513SDarren.Reed@Sun.COM }
2559