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