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