1381a2a9aSdr146992 /* 2381a2a9aSdr146992 * CDDL HEADER START 3381a2a9aSdr146992 * 4381a2a9aSdr146992 * The contents of this file are subject to the terms of the 5381a2a9aSdr146992 * Common Development and Distribution License (the "License"). 6381a2a9aSdr146992 * You may not use this file except in compliance with the License. 7381a2a9aSdr146992 * 8381a2a9aSdr146992 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9381a2a9aSdr146992 * or http://www.opensolaris.org/os/licensing. 10381a2a9aSdr146992 * See the License for the specific language governing permissions 11381a2a9aSdr146992 * and limitations under the License. 12381a2a9aSdr146992 * 13381a2a9aSdr146992 * When distributing Covered Code, include this CDDL HEADER in each 14381a2a9aSdr146992 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15381a2a9aSdr146992 * If applicable, add the following below this CDDL HEADER, with the 16381a2a9aSdr146992 * fields enclosed by brackets "[]" replaced with your own identifying 17381a2a9aSdr146992 * information: Portions Copyright [yyyy] [name of copyright owner] 18381a2a9aSdr146992 * 19381a2a9aSdr146992 * CDDL HEADER END 20381a2a9aSdr146992 */ 21381a2a9aSdr146992 /* 227ddc9b1aSDarren Reed * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23381a2a9aSdr146992 * Use is subject to license terms. 24381a2a9aSdr146992 */ 25381a2a9aSdr146992 #include <sys/param.h> 26381a2a9aSdr146992 #include <sys/types.h> 27381a2a9aSdr146992 #include <sys/systm.h> 28381a2a9aSdr146992 #include <sys/errno.h> 29381a2a9aSdr146992 #include <sys/kmem.h> 30381a2a9aSdr146992 #include <sys/mutex.h> 31381a2a9aSdr146992 #include <sys/condvar.h> 32381a2a9aSdr146992 #include <sys/modctl.h> 33381a2a9aSdr146992 #include <sys/hook_impl.h> 34381a2a9aSdr146992 #include <sys/sdt.h> 357ddc9b1aSDarren Reed #include <sys/cmn_err.h> 36381a2a9aSdr146992 37381a2a9aSdr146992 /* 38381a2a9aSdr146992 * This file provides kernel hook framework. 39381a2a9aSdr146992 */ 40381a2a9aSdr146992 41381a2a9aSdr146992 static struct modldrv modlmisc = { 42381a2a9aSdr146992 &mod_miscops, /* drv_modops */ 43381a2a9aSdr146992 "Hooks Interface v1.0", /* drv_linkinfo */ 44381a2a9aSdr146992 }; 45381a2a9aSdr146992 46381a2a9aSdr146992 static struct modlinkage modlinkage = { 47381a2a9aSdr146992 MODREV_1, /* ml_rev */ 48381a2a9aSdr146992 &modlmisc, /* ml_linkage */ 49381a2a9aSdr146992 NULL 50381a2a9aSdr146992 }; 51381a2a9aSdr146992 52381a2a9aSdr146992 /* 537ddc9b1aSDarren Reed * How it works. 547ddc9b1aSDarren Reed * ============= 557ddc9b1aSDarren Reed * Use of the hook framework here is tied up with zones - when a new zone 567ddc9b1aSDarren Reed * is created, we create a new hook_stack_t and are open to business for 577ddc9b1aSDarren Reed * allowing new hook families and their events. 587ddc9b1aSDarren Reed * 597ddc9b1aSDarren Reed * A consumer of these hooks is expected to operate in this fashion: 607ddc9b1aSDarren Reed * 1) call hook_family_add() to create a new family of hooks. It is a 617ddc9b1aSDarren Reed * current requirement that this call must be made with the value 627ddc9b1aSDarren Reed * returned from hook_stack_init, by way of infrastructure elsewhere. 637ddc9b1aSDarren Reed * 2) add events to the registered family with calls to hook_event_add. 647ddc9b1aSDarren Reed * 657ddc9b1aSDarren Reed * At this point, the structures in place should be open to others to 667ddc9b1aSDarren Reed * add hooks to the event or add notifiers for when the contents of the 677ddc9b1aSDarren Reed * hook stack changes. 687ddc9b1aSDarren Reed * 697ddc9b1aSDarren Reed * The interesting stuff happens on teardown. 707ddc9b1aSDarren Reed * 717ddc9b1aSDarren Reed * It is a requirement that the provider of hook events work in the reverse 727ddc9b1aSDarren Reed * order to the above, so that the first step is: 737ddc9b1aSDarren Reed * 1) remove events from each hook family created earlier 747ddc9b1aSDarren Reed * 2) remove hook families from the hook stack. 757ddc9b1aSDarren Reed * 767ddc9b1aSDarren Reed * When doing teardown of both events and families, a check is made to see 777ddc9b1aSDarren Reed * if either structure is still "busy". If so then a boolean flag is set to 787ddc9b1aSDarren Reed * say that the structure is condemned. The presence of this flag being set 797ddc9b1aSDarren Reed * must be checked for in _add()/_register()/ functions and a failure returned 807ddc9b1aSDarren Reed * if it is set. It is ignored by the _find() functions because they're 817ddc9b1aSDarren Reed * used by _remove()/_unregister(). While setting the condemned flag when 827ddc9b1aSDarren Reed * trying to delete a structure would normally be keyed from the presence 837ddc9b1aSDarren Reed * of a reference count being greater than 1, in this implementation there 847ddc9b1aSDarren Reed * are no reference counts required: instead the presence of objects on 857ddc9b1aSDarren Reed * linked lists is taken to mean something is still "busy." 867ddc9b1aSDarren Reed * 877ddc9b1aSDarren Reed * ONLY the caller that adds the family and the events ever has a direct 887ddc9b1aSDarren Reed * reference to the internal structures and thus ONLY it should be doing 897ddc9b1aSDarren Reed * the removal of either the event or family. In practise, what this means 907ddc9b1aSDarren Reed * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed 917ddc9b1aSDarren Reed * by net_event_register() (these interface to hook_family_add() and 927ddc9b1aSDarren Reed * hook_event_add(), respectively) that are made when we create an instance 937ddc9b1aSDarren Reed * of IP and when the IP instance is shutdown/destroyed, it calls 947ddc9b1aSDarren Reed * net_event_unregister() and net_protocol_unregister(), which in turn call 957ddc9b1aSDarren Reed * hook_event_remove() and hook_family_remove() respectively. Nobody else 967ddc9b1aSDarren Reed * is entitled to call the _unregister() functions. It is imperative that 977ddc9b1aSDarren Reed * there be only one _remove() call for every _add() call. 987ddc9b1aSDarren Reed * 997ddc9b1aSDarren Reed * It is possible that code which is interfacing with this hook framework 1007ddc9b1aSDarren Reed * won't do all the cleaning up that it needs to at the right time. While 1017ddc9b1aSDarren Reed * we can't prevent programmers from creating memory leaks, we can synchronise 1027ddc9b1aSDarren Reed * when we clean up data structures to prevent code accessing free'd memory. 1037ddc9b1aSDarren Reed * 1047ddc9b1aSDarren Reed * A simple diagram showing the ownership is as follows: 1057ddc9b1aSDarren Reed * 1067ddc9b1aSDarren Reed * Owned +--------------+ 1077ddc9b1aSDarren Reed * by | hook_stack_t | 1087ddc9b1aSDarren Reed * the +--------------+ 1097ddc9b1aSDarren Reed * Instance | 1107ddc9b1aSDarren Reed * - - - - - - - -|- - - - - - - - - - - - - - - - - - 1117ddc9b1aSDarren Reed * V 1127ddc9b1aSDarren Reed * Owned +-------------------+ +-------------------+ 1137ddc9b1aSDarren Reed * | hook_family_int_t |---->| hook_family_int_t | 1147ddc9b1aSDarren Reed * by +-------------------+ +-------------------+ 1157ddc9b1aSDarren Reed * | \+---------------+ \+---------------+ 1167ddc9b1aSDarren Reed * network | | hook_family_t | | hook_family_t | 1177ddc9b1aSDarren Reed * V +---------------+ +---------------+ 1187ddc9b1aSDarren Reed * protocol +------------------+ +------------------+ 1197ddc9b1aSDarren Reed * | hook_event_int_t |---->| hook_event_int_t | 1207ddc9b1aSDarren Reed * (ipv4,ipv6) +------------------+ +------------------+ 1217ddc9b1aSDarren Reed * | \+--------------+ \+--------------+ 1227ddc9b1aSDarren Reed * | | hook_event_t | | hook_event_t | 1237ddc9b1aSDarren Reed * | +--------------+ +--------------+ 1247ddc9b1aSDarren Reed * - - - - - - - -|- - - - - - - - - - - - - - - - - - 1257ddc9b1aSDarren Reed * V 1267ddc9b1aSDarren Reed * Owned +------------+ 1277ddc9b1aSDarren Reed * | hook_int_t | 1287ddc9b1aSDarren Reed * by +------------+ 1297ddc9b1aSDarren Reed * \+--------+ 1307ddc9b1aSDarren Reed * the consumer | hook_t | 1317ddc9b1aSDarren Reed * +--------+ 1327ddc9b1aSDarren Reed * 1337ddc9b1aSDarren Reed * The consumers, such as IPFilter, do not have any pointers or hold any 1347ddc9b1aSDarren Reed * references to hook_int_t, hook_event_t or hook_event_int_t. By placing 1357ddc9b1aSDarren Reed * a hook on an event through net_hook_register(), an implicit reference 1367ddc9b1aSDarren Reed * to the hook_event_int_t is returned with a successful call. Additionally, 1377ddc9b1aSDarren Reed * IPFilter does not see the hook_family_int_t or hook_family_t directly. 1387ddc9b1aSDarren Reed * Rather it is returned a net_handle_t (from net_protocol_lookup()) that 1397ddc9b1aSDarren Reed * contains a pointer to hook_family_int_t. The structure behind the 1407ddc9b1aSDarren Reed * net_handle_t (struct net_data) *is* reference counted and managed 1417ddc9b1aSDarren Reed * appropriately. 1427ddc9b1aSDarren Reed * 1437ddc9b1aSDarren Reed * A more detailed picture that describes how the family/event structures 1447ddc9b1aSDarren Reed * are linked together can be found in <sys/hook_impl.h> 1457ddc9b1aSDarren Reed */ 1467ddc9b1aSDarren Reed 1477ddc9b1aSDarren Reed /* 1487ddc9b1aSDarren Reed * Locking 1497ddc9b1aSDarren Reed * ======= 1507ddc9b1aSDarren Reed * The use of CVW_* macros to do locking is driven by the need to allow 1517ddc9b1aSDarren Reed * recursive locking with read locks when we're processing packets. This 1527ddc9b1aSDarren Reed * is necessary because various netinfo functions need to hold read locks, 1537ddc9b1aSDarren Reed * by design, as they can be called in or out of packet context. 1547ddc9b1aSDarren Reed */ 1557ddc9b1aSDarren Reed /* 156381a2a9aSdr146992 * Hook internal functions 157381a2a9aSdr146992 */ 158381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src); 159f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he, 160f4b3ec61Sdh155122 hook_stack_t *hks); 161381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src); 162381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 1637ddc9b1aSDarren Reed static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi); 164381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src); 165f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks); 1667ddc9b1aSDarren Reed static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks); 167381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 1687ddc9b1aSDarren Reed static void hook_int_free(hook_int_t *hi, netstackid_t); 169381a2a9aSdr146992 static void hook_init(void); 170f4b3ec61Sdh155122 static void hook_fini(void); 171f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns); 172f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg); 1737ddc9b1aSDarren Reed static void hook_stack_shutdown(netstackid_t stackid, void *arg); 1747ddc9b1aSDarren Reed static int hook_insert(hook_int_head_t *head, hook_int_t *new); 1757ddc9b1aSDarren Reed static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new); 1767ddc9b1aSDarren Reed static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new); 1777ddc9b1aSDarren Reed static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name); 1787ddc9b1aSDarren Reed static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *); 1797ddc9b1aSDarren Reed static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *, 1807ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd); 1817ddc9b1aSDarren Reed static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, 1827ddc9b1aSDarren Reed hook_int_t *hi); 1837ddc9b1aSDarren Reed static int hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head, 1847ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg); 1857ddc9b1aSDarren Reed static int hook_notify_unregister(cvwaitlock_t *lock, 1867ddc9b1aSDarren Reed hook_notify_head_t *head, hook_notify_fn_t callback); 1877ddc9b1aSDarren Reed static void hook_notify_run(hook_notify_head_t *head, char *family, 1887ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd); 1897ddc9b1aSDarren Reed static void hook_stack_notify_run(hook_stack_t *hks, char *name, 1907ddc9b1aSDarren Reed hook_notify_cmd_t cmd); 1917ddc9b1aSDarren Reed static void hook_stack_remove(hook_stack_t *hks); 1927ddc9b1aSDarren Reed 1937ddc9b1aSDarren Reed /* 1947ddc9b1aSDarren Reed * A list of the hook stacks is kept here because we need to enable 1957ddc9b1aSDarren Reed * net_instance_notify_register() to be called during the creation 1967ddc9b1aSDarren Reed * of a new instance. Previously hook_stack_get() would just use 1977ddc9b1aSDarren Reed * the netstack functions for this work but they will return NULL 1987ddc9b1aSDarren Reed * until the zone has been fully initialised. 1997ddc9b1aSDarren Reed */ 2007ddc9b1aSDarren Reed static hook_stack_head_t hook_stacks; 2017ddc9b1aSDarren Reed static kmutex_t hook_stack_lock; 202381a2a9aSdr146992 203381a2a9aSdr146992 /* 204381a2a9aSdr146992 * Module entry points. 205381a2a9aSdr146992 */ 206381a2a9aSdr146992 int 207381a2a9aSdr146992 _init(void) 208381a2a9aSdr146992 { 209f4b3ec61Sdh155122 int error; 210f4b3ec61Sdh155122 211381a2a9aSdr146992 hook_init(); 212f4b3ec61Sdh155122 error = mod_install(&modlinkage); 213f4b3ec61Sdh155122 if (error != 0) 214f4b3ec61Sdh155122 hook_fini(); 215f4b3ec61Sdh155122 216f4b3ec61Sdh155122 return (error); 217381a2a9aSdr146992 } 218381a2a9aSdr146992 219381a2a9aSdr146992 int 220381a2a9aSdr146992 _fini(void) 221381a2a9aSdr146992 { 222f4b3ec61Sdh155122 int error; 223f4b3ec61Sdh155122 224f4b3ec61Sdh155122 error = mod_remove(&modlinkage); 225f4b3ec61Sdh155122 if (error == 0) 226f4b3ec61Sdh155122 hook_fini(); 227f4b3ec61Sdh155122 228f4b3ec61Sdh155122 return (error); 229381a2a9aSdr146992 } 230381a2a9aSdr146992 231381a2a9aSdr146992 int 232381a2a9aSdr146992 _info(struct modinfo *modinfop) 233381a2a9aSdr146992 { 234381a2a9aSdr146992 return (mod_info(&modlinkage, modinfop)); 235381a2a9aSdr146992 } 236381a2a9aSdr146992 237381a2a9aSdr146992 /* 238381a2a9aSdr146992 * Function: hook_init 239381a2a9aSdr146992 * Returns: None 240381a2a9aSdr146992 * Parameters: None 241381a2a9aSdr146992 * 242381a2a9aSdr146992 * Initialize hooks 243381a2a9aSdr146992 */ 244381a2a9aSdr146992 static void 245381a2a9aSdr146992 hook_init(void) 246381a2a9aSdr146992 { 2477ddc9b1aSDarren Reed mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL); 2487ddc9b1aSDarren Reed SLIST_INIT(&hook_stacks); 2497ddc9b1aSDarren Reed 250f4b3ec61Sdh155122 /* 251f4b3ec61Sdh155122 * We want to be informed each time a stack is created or 252f4b3ec61Sdh155122 * destroyed in the kernel. 253f4b3ec61Sdh155122 */ 2547ddc9b1aSDarren Reed netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown, 255f4b3ec61Sdh155122 hook_stack_fini); 256381a2a9aSdr146992 } 257381a2a9aSdr146992 258f4b3ec61Sdh155122 /* 259f4b3ec61Sdh155122 * Function: hook_fini 260f4b3ec61Sdh155122 * Returns: None 261f4b3ec61Sdh155122 * Parameters: None 262f4b3ec61Sdh155122 * 263f4b3ec61Sdh155122 * Deinitialize hooks 264f4b3ec61Sdh155122 */ 265f4b3ec61Sdh155122 static void 266f4b3ec61Sdh155122 hook_fini(void) 267f4b3ec61Sdh155122 { 268f4b3ec61Sdh155122 netstack_unregister(NS_HOOK); 2697ddc9b1aSDarren Reed 2707ddc9b1aSDarren Reed mutex_destroy(&hook_stack_lock); 2717ddc9b1aSDarren Reed ASSERT(SLIST_EMPTY(&hook_stacks)); 2727ddc9b1aSDarren Reed } 2737ddc9b1aSDarren Reed 2747ddc9b1aSDarren Reed /* 2757ddc9b1aSDarren Reed * Function: hook_wait_setflag 2767ddc9b1aSDarren Reed * Returns: -1 = setting flag is disallowed, 0 = flag set and did 2777ddc9b1aSDarren Reed * not have to wait (ie no lock droped), 1 = flag set but 2787ddc9b1aSDarren Reed * it was necessary to drop locks to set it. 2797ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 2807ddc9b1aSDarren Reed * busyset(I) - set of flags that we don't want set while 2817ddc9b1aSDarren Reed * we are active. 2827ddc9b1aSDarren Reed * wanted(I) - flag associated with newflag to indicate 2837ddc9b1aSDarren Reed * what we want to do. 2847ddc9b1aSDarren Reed * newflag(I) - the new ACTIVE flag we want to set that 2857ddc9b1aSDarren Reed * indicates what we are doing. 2867ddc9b1aSDarren Reed * 2877ddc9b1aSDarren Reed * The set of functions hook_wait_* implement an API that builds on top of 2887ddc9b1aSDarren Reed * the kcondvar_t to provide controlled execution through a critical region. 2897ddc9b1aSDarren Reed * For each flag that indicates work is being done (FWF_*_ACTIVE) there is 2907ddc9b1aSDarren Reed * also a flag that we set to indicate that we want to do it (FWF_*_WANTED). 2917ddc9b1aSDarren Reed * The combination of flags is required as when this function exits to do 2927ddc9b1aSDarren Reed * the task, the structure is then free for another caller to use and 2937ddc9b1aSDarren Reed * to indicate that it wants to do work. The trump flags here are those 2947ddc9b1aSDarren Reed * that indicate someone wants to destroy the structure that owns this 2957ddc9b1aSDarren Reed * flagwait_t. In this case, we don't try to secure the ability to run 2967ddc9b1aSDarren Reed * and return with an error. 2977ddc9b1aSDarren Reed * 2987ddc9b1aSDarren Reed * wanted - the FWF_*_WANTED flag that describes the action being requested 2997ddc9b1aSDarren Reed * busyset- the set of FWF_* flags we don't want set when we run 3007ddc9b1aSDarren Reed * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy 3017ddc9b1aSDarren Reed */ 3027ddc9b1aSDarren Reed int 3037ddc9b1aSDarren Reed hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted, 3047ddc9b1aSDarren Reed fwflag_t newflag) 3057ddc9b1aSDarren Reed { 3067ddc9b1aSDarren Reed int waited = 0; 3077ddc9b1aSDarren Reed 3087ddc9b1aSDarren Reed mutex_enter(&waiter->fw_lock); 3097ddc9b1aSDarren Reed if (waiter->fw_flags & FWF_DESTROY) { 3107ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3117ddc9b1aSDarren Reed return (-1); 3127ddc9b1aSDarren Reed } 3137ddc9b1aSDarren Reed while (waiter->fw_flags & busyset) { 3147ddc9b1aSDarren Reed waiter->fw_flags |= wanted; 3157ddc9b1aSDarren Reed CVW_EXIT_WRITE(waiter->fw_owner); 3167ddc9b1aSDarren Reed cv_wait(&waiter->fw_cv, &waiter->fw_lock); 3177ddc9b1aSDarren Reed waited = 1; 3187ddc9b1aSDarren Reed CVW_ENTER_WRITE(waiter->fw_owner); 3197ddc9b1aSDarren Reed if (waiter->fw_flags & FWF_DESTROY) { 3207ddc9b1aSDarren Reed waiter->fw_flags &= ~wanted; 3217ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3227ddc9b1aSDarren Reed return (-1); 3237ddc9b1aSDarren Reed } 3247ddc9b1aSDarren Reed waiter->fw_flags |= wanted; 3257ddc9b1aSDarren Reed } 3267ddc9b1aSDarren Reed waiter->fw_flags &= ~wanted; 3277ddc9b1aSDarren Reed waiter->fw_flags |= newflag; 3287ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3297ddc9b1aSDarren Reed return (waited); 3307ddc9b1aSDarren Reed } 3317ddc9b1aSDarren Reed 3327ddc9b1aSDarren Reed /* 3337ddc9b1aSDarren Reed * Function: hook_wait_unsetflag 3347ddc9b1aSDarren Reed * Returns: None 3357ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 3367ddc9b1aSDarren Reed * oldflag(I) - flag to reset 3377ddc9b1aSDarren Reed * 3387ddc9b1aSDarren Reed * Turn off the bit that we had set to run and let others know that 3397ddc9b1aSDarren Reed * they should now check to see if they can run. 3407ddc9b1aSDarren Reed */ 3417ddc9b1aSDarren Reed void 3427ddc9b1aSDarren Reed hook_wait_unsetflag(flagwait_t *waiter, uint32_t oldflag) 3437ddc9b1aSDarren Reed { 3447ddc9b1aSDarren Reed mutex_enter(&waiter->fw_lock); 3457ddc9b1aSDarren Reed waiter->fw_flags &= ~oldflag; 3467ddc9b1aSDarren Reed cv_signal(&waiter->fw_cv); 3477ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3487ddc9b1aSDarren Reed } 3497ddc9b1aSDarren Reed 3507ddc9b1aSDarren Reed /* 3517ddc9b1aSDarren Reed * Function: hook_wait_destroy 3527ddc9b1aSDarren Reed * Returns: None 3537ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 3547ddc9b1aSDarren Reed * 3557ddc9b1aSDarren Reed * Since outer locking (on fw_owner) should ensure that only one function 3567ddc9b1aSDarren Reed * at a time gets to call hook_wait_destroy() on a given object, there is 3577ddc9b1aSDarren Reed * no need to guard against setting FWF_DESTROY_WANTED already being set. 3587ddc9b1aSDarren Reed * It is, however, necessary to wait for all activity on the owning 3597ddc9b1aSDarren Reed * structure to cease. 3607ddc9b1aSDarren Reed */ 3617ddc9b1aSDarren Reed void 3627ddc9b1aSDarren Reed hook_wait_destroy(flagwait_t *waiter) 3637ddc9b1aSDarren Reed { 3647ddc9b1aSDarren Reed ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0); 3657ddc9b1aSDarren Reed waiter->fw_flags |= FWF_DESTROY_WANTED; 3667ddc9b1aSDarren Reed while (!FWF_DESTROY_OK(waiter)) { 3677ddc9b1aSDarren Reed CVW_EXIT_WRITE(waiter->fw_owner); 3687ddc9b1aSDarren Reed cv_wait(&waiter->fw_cv, &waiter->fw_lock); 3697ddc9b1aSDarren Reed CVW_ENTER_WRITE(waiter->fw_owner); 3707ddc9b1aSDarren Reed } 3717ddc9b1aSDarren Reed /* 3727ddc9b1aSDarren Reed * There should now be nothing else using "waiter" or its 3737ddc9b1aSDarren Reed * owner, so we can safely assign here without risk of wiiping 3747ddc9b1aSDarren Reed * out someone's bit. 3757ddc9b1aSDarren Reed */ 3767ddc9b1aSDarren Reed waiter->fw_flags = FWF_DESTROY_ACTIVE; 3777ddc9b1aSDarren Reed } 3787ddc9b1aSDarren Reed 3797ddc9b1aSDarren Reed /* 3807ddc9b1aSDarren Reed * Function: hook_wait_init 3817ddc9b1aSDarren Reed * Returns: None 3827ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 3837ddc9b1aSDarren Reed * ownder(I) - pointer to lock that the owner of this 3847ddc9b1aSDarren Reed * waiter uses 3857ddc9b1aSDarren Reed * 3867ddc9b1aSDarren Reed * "owner" gets passed in here so that when we need to call cv_wait, 3877ddc9b1aSDarren Reed * for example in hook_wait_setflag(), we can drop the lock for the 3887ddc9b1aSDarren Reed * next layer out, which is likely to be held in an exclusive manner. 3897ddc9b1aSDarren Reed */ 3907ddc9b1aSDarren Reed void 3917ddc9b1aSDarren Reed hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner) 3927ddc9b1aSDarren Reed { 3937ddc9b1aSDarren Reed cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL); 3947ddc9b1aSDarren Reed mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL); 3957ddc9b1aSDarren Reed waiter->fw_flags = FWF_NONE; 3967ddc9b1aSDarren Reed waiter->fw_owner = owner; 397f4b3ec61Sdh155122 } 398f4b3ec61Sdh155122 399f4b3ec61Sdh155122 /* 400f4b3ec61Sdh155122 * Initialize the hook stack instance. 401f4b3ec61Sdh155122 */ 402f4b3ec61Sdh155122 /*ARGSUSED*/ 403f4b3ec61Sdh155122 static void * 404f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns) 405f4b3ec61Sdh155122 { 406f4b3ec61Sdh155122 hook_stack_t *hks; 407f4b3ec61Sdh155122 408f4b3ec61Sdh155122 #ifdef NS_DEBUG 409f4b3ec61Sdh155122 printf("hook_stack_init(stack %d)\n", stackid); 410f4b3ec61Sdh155122 #endif 411f4b3ec61Sdh155122 412f4b3ec61Sdh155122 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP); 4137ddc9b1aSDarren Reed hks->hks_netstack = ns; 4147ddc9b1aSDarren Reed hks->hks_netstackid = stackid; 415f4b3ec61Sdh155122 4167ddc9b1aSDarren Reed CVW_INIT(&hks->hks_lock); 4177ddc9b1aSDarren Reed TAILQ_INIT(&hks->hks_nhead); 418f4b3ec61Sdh155122 SLIST_INIT(&hks->hks_familylist); 419f4b3ec61Sdh155122 4207ddc9b1aSDarren Reed hook_wait_init(&hks->hks_waiter, &hks->hks_lock); 4217ddc9b1aSDarren Reed 4227ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 4237ddc9b1aSDarren Reed SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry); 4247ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 4257ddc9b1aSDarren Reed 426f4b3ec61Sdh155122 return (hks); 427f4b3ec61Sdh155122 } 428f4b3ec61Sdh155122 429*8ad74188SDarren Reed /* 430*8ad74188SDarren Reed * Set the shutdown flag to indicate that we should stop accepting new 431*8ad74188SDarren Reed * register calls as we're now in the cleanup process. 432*8ad74188SDarren Reed */ 4337ddc9b1aSDarren Reed /*ARGSUSED*/ 4347ddc9b1aSDarren Reed static void 4357ddc9b1aSDarren Reed hook_stack_shutdown(netstackid_t stackid, void *arg) 4367ddc9b1aSDarren Reed { 4377ddc9b1aSDarren Reed hook_stack_t *hks = (hook_stack_t *)arg; 4387ddc9b1aSDarren Reed 4397ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 4407ddc9b1aSDarren Reed /* 4417ddc9b1aSDarren Reed * Once this flag gets set to one, no more additions are allowed 4427ddc9b1aSDarren Reed * to any of the structures that make up this stack. 4437ddc9b1aSDarren Reed */ 4447ddc9b1aSDarren Reed hks->hks_shutdown = 1; 4457ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 4467ddc9b1aSDarren Reed } 4477ddc9b1aSDarren Reed 448f4b3ec61Sdh155122 /* 449f4b3ec61Sdh155122 * Free the hook stack instance. 450f4b3ec61Sdh155122 */ 451f4b3ec61Sdh155122 /*ARGSUSED*/ 452f4b3ec61Sdh155122 static void 453f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg) 454f4b3ec61Sdh155122 { 455f4b3ec61Sdh155122 hook_stack_t *hks = (hook_stack_t *)arg; 4567ddc9b1aSDarren Reed 4577ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 4587ddc9b1aSDarren Reed hks->hks_shutdown = 2; 4597ddc9b1aSDarren Reed hook_stack_remove(hks); 4607ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 4617ddc9b1aSDarren Reed } 4627ddc9b1aSDarren Reed 4637ddc9b1aSDarren Reed /* 4647ddc9b1aSDarren Reed * This function assumes that it is called with hook_stack_lock held. 4657ddc9b1aSDarren Reed * It functions differently to hook_family/event_remove in that it does 4667ddc9b1aSDarren Reed * the checks to see if it can be removed. This difference exists 4677ddc9b1aSDarren Reed * because this structure has nothing higher up that depends on it. 4687ddc9b1aSDarren Reed */ 4697ddc9b1aSDarren Reed static void 4707ddc9b1aSDarren Reed hook_stack_remove(hook_stack_t *hks) 4717ddc9b1aSDarren Reed { 4727ddc9b1aSDarren Reed 4737ddc9b1aSDarren Reed ASSERT(mutex_owned(&hook_stack_lock)); 4747ddc9b1aSDarren Reed 4757ddc9b1aSDarren Reed /* 4767ddc9b1aSDarren Reed * Is the structure still in use? 4777ddc9b1aSDarren Reed */ 4787ddc9b1aSDarren Reed if (!SLIST_EMPTY(&hks->hks_familylist) || 4797ddc9b1aSDarren Reed !TAILQ_EMPTY(&hks->hks_nhead)) 4807ddc9b1aSDarren Reed return; 4817ddc9b1aSDarren Reed 4827ddc9b1aSDarren Reed SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry); 4837ddc9b1aSDarren Reed 4847ddc9b1aSDarren Reed hook_wait_destroy(&hks->hks_waiter); 4857ddc9b1aSDarren Reed CVW_DESTROY(&hks->hks_lock); 486f4b3ec61Sdh155122 kmem_free(hks, sizeof (*hks)); 487f4b3ec61Sdh155122 } 488381a2a9aSdr146992 4897ddc9b1aSDarren Reed static hook_stack_t * 4907ddc9b1aSDarren Reed hook_stack_get(netstackid_t stackid) 4917ddc9b1aSDarren Reed { 4927ddc9b1aSDarren Reed hook_stack_t *hks; 4937ddc9b1aSDarren Reed 4947ddc9b1aSDarren Reed SLIST_FOREACH(hks, &hook_stacks, hks_entry) { 4957ddc9b1aSDarren Reed if (hks->hks_netstackid == stackid) 4967ddc9b1aSDarren Reed break; 4977ddc9b1aSDarren Reed } 4987ddc9b1aSDarren Reed 4997ddc9b1aSDarren Reed return (hks); 5007ddc9b1aSDarren Reed } 5017ddc9b1aSDarren Reed 5027ddc9b1aSDarren Reed /* 5037ddc9b1aSDarren Reed * Function: hook_stack_notify_register 5047ddc9b1aSDarren Reed * Returns: 0 = success, else failure 5057ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 5067ddc9b1aSDarren Reed * callback(I)- function to be called 5077ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 5087ddc9b1aSDarren Reed * 5097ddc9b1aSDarren Reed * If we're not shutting down this instance, append a new function to the 5107ddc9b1aSDarren Reed * list of those to call when a new family of hooks is added to this stack. 5117ddc9b1aSDarren Reed */ 5127ddc9b1aSDarren Reed int 5137ddc9b1aSDarren Reed hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback, 5147ddc9b1aSDarren Reed void *arg) 5157ddc9b1aSDarren Reed { 5167ddc9b1aSDarren Reed hook_stack_t *hks; 5177ddc9b1aSDarren Reed int error; 5187ddc9b1aSDarren Reed 5197ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 5207ddc9b1aSDarren Reed hks = hook_stack_get(stackid); 5217ddc9b1aSDarren Reed if (hks != NULL) { 5227ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 5237ddc9b1aSDarren Reed error = ESHUTDOWN; 5247ddc9b1aSDarren Reed } else { 5257ddc9b1aSDarren Reed error = hook_notify_register(&hks->hks_lock, 5267ddc9b1aSDarren Reed &hks->hks_nhead, callback, arg); 5277ddc9b1aSDarren Reed } 5287ddc9b1aSDarren Reed } else { 5297ddc9b1aSDarren Reed error = ESRCH; 5307ddc9b1aSDarren Reed } 5317ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 5327ddc9b1aSDarren Reed 5337ddc9b1aSDarren Reed return (error); 5347ddc9b1aSDarren Reed } 5357ddc9b1aSDarren Reed 5367ddc9b1aSDarren Reed /* 5377ddc9b1aSDarren Reed * Function: hook_stack_notify_unregister 5387ddc9b1aSDarren Reed * Returns: 0 = success, else failure 5397ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 5407ddc9b1aSDarren Reed * callback(I) - function to be called 5417ddc9b1aSDarren Reed * 5427ddc9b1aSDarren Reed * Attempt to remove a registered function from a hook stack's list of 5437ddc9b1aSDarren Reed * callbacks to activiate when protocols are added/deleted. 5447ddc9b1aSDarren Reed */ 5457ddc9b1aSDarren Reed int 5467ddc9b1aSDarren Reed hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback) 5477ddc9b1aSDarren Reed { 5487ddc9b1aSDarren Reed hook_stack_t *hks; 5497ddc9b1aSDarren Reed int error; 5507ddc9b1aSDarren Reed 5517ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 5527ddc9b1aSDarren Reed hks = hook_stack_get(stackid); 5537ddc9b1aSDarren Reed if (hks != NULL) { 5547ddc9b1aSDarren Reed error = hook_notify_unregister(&hks->hks_lock, 5557ddc9b1aSDarren Reed &hks->hks_nhead, callback); 5567ddc9b1aSDarren Reed if ((error == 0) && (hks->hks_shutdown == 2)) 5577ddc9b1aSDarren Reed hook_stack_remove(hks); 5587ddc9b1aSDarren Reed } else { 5597ddc9b1aSDarren Reed error = ESRCH; 5607ddc9b1aSDarren Reed } 5617ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 5627ddc9b1aSDarren Reed 5637ddc9b1aSDarren Reed return (error); 5647ddc9b1aSDarren Reed } 5657ddc9b1aSDarren Reed 5667ddc9b1aSDarren Reed /* 5677ddc9b1aSDarren Reed * Function: hook_stack_notify_run 5687ddc9b1aSDarren Reed * Returns: None 5697ddc9b1aSDarren Reed * Parameters: hks(I) - hook stack pointer to execute callbacks for 5707ddc9b1aSDarren Reed * name(I) - name of a hook family 5717ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 5727ddc9b1aSDarren Reed * 5737ddc9b1aSDarren Reed * Run through the list of callbacks on the hook stack to be called when 5747ddc9b1aSDarren Reed * a new hook family is added 5757ddc9b1aSDarren Reed * 5767ddc9b1aSDarren Reed * As hook_notify_run() expects 3 names, one for the family, one for the 5777ddc9b1aSDarren Reed * event and one for the object being introduced and we really only have 5787ddc9b1aSDarren Reed * one name (that of the new hook family), fake the hook stack's name by 5797ddc9b1aSDarren Reed * converting the integer to a string and for the event just pass NULL. 5807ddc9b1aSDarren Reed */ 5817ddc9b1aSDarren Reed static void 5827ddc9b1aSDarren Reed hook_stack_notify_run(hook_stack_t *hks, char *name, 5837ddc9b1aSDarren Reed hook_notify_cmd_t cmd) 5847ddc9b1aSDarren Reed { 5857ddc9b1aSDarren Reed char buffer[16]; 5867ddc9b1aSDarren Reed 5877ddc9b1aSDarren Reed (void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid); 5887ddc9b1aSDarren Reed 5897ddc9b1aSDarren Reed hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd); 5907ddc9b1aSDarren Reed } 5917ddc9b1aSDarren Reed 592381a2a9aSdr146992 /* 593381a2a9aSdr146992 * Function: hook_run 594381a2a9aSdr146992 * Returns: int - return value according to callback func 595381a2a9aSdr146992 * Parameters: token(I) - event pointer 596381a2a9aSdr146992 * info(I) - message 597381a2a9aSdr146992 * 598381a2a9aSdr146992 * Run hooks for specific provider. The hooks registered are stepped through 599381a2a9aSdr146992 * until either the end of the list is reached or a hook function returns a 600381a2a9aSdr146992 * non-zero value. If a non-zero value is returned from a hook function, we 601381a2a9aSdr146992 * return that value back to our caller. By design, a hook function can be 602381a2a9aSdr146992 * called more than once, simultaneously. 603381a2a9aSdr146992 */ 604381a2a9aSdr146992 int 6057ddc9b1aSDarren Reed hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info) 606381a2a9aSdr146992 { 607381a2a9aSdr146992 hook_event_int_t *hei; 6087ddc9b1aSDarren Reed hook_int_t *hi; 609381a2a9aSdr146992 int rval = 0; 610381a2a9aSdr146992 611381a2a9aSdr146992 ASSERT(token != NULL); 612381a2a9aSdr146992 613381a2a9aSdr146992 hei = (hook_event_int_t *)token; 614381a2a9aSdr146992 DTRACE_PROBE2(hook__run__start, 615381a2a9aSdr146992 hook_event_token_t, token, 616381a2a9aSdr146992 hook_data_t, info); 617381a2a9aSdr146992 6187ddc9b1aSDarren Reed /* 6197ddc9b1aSDarren Reed * Hold global read lock to ensure event will not be deleted. 6207ddc9b1aSDarren Reed * While it might be expected that we should also hold a read lock 6217ddc9b1aSDarren Reed * on the event lock (hei_lock) to prevent the hook list from 6227ddc9b1aSDarren Reed * changing while we're executing this function, both addition 6237ddc9b1aSDarren Reed * to and removal from the hook list on the event is done with 6247ddc9b1aSDarren Reed * a write lock held on hfi_lock. This is by design so that we 6257ddc9b1aSDarren Reed * only need to get one of these locks to process a packet. 6267ddc9b1aSDarren Reed * - locking is not a cheap thing to do for every packet. 6277ddc9b1aSDarren Reed */ 6287ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 629381a2a9aSdr146992 630381a2a9aSdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 631381a2a9aSdr146992 ASSERT(hi->hi_hook.h_func != NULL); 632381a2a9aSdr146992 DTRACE_PROBE3(hook__func__start, 633381a2a9aSdr146992 hook_event_token_t, token, 634381a2a9aSdr146992 hook_data_t, info, 635381a2a9aSdr146992 hook_int_t *, hi); 6367ddc9b1aSDarren Reed rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg); 637381a2a9aSdr146992 DTRACE_PROBE4(hook__func__end, 638381a2a9aSdr146992 hook_event_token_t, token, 639381a2a9aSdr146992 hook_data_t, info, 640381a2a9aSdr146992 hook_int_t *, hi, 641381a2a9aSdr146992 int, rval); 6427ddc9b1aSDarren Reed hi->hi_kstats.hook_hits.value.ui64++; 643381a2a9aSdr146992 if (rval != 0) 644381a2a9aSdr146992 break; 645381a2a9aSdr146992 } 646381a2a9aSdr146992 6477ddc9b1aSDarren Reed hei->hei_kstats.events.value.ui64++; 6487ddc9b1aSDarren Reed 6497ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 650381a2a9aSdr146992 651381a2a9aSdr146992 DTRACE_PROBE3(hook__run__end, 652381a2a9aSdr146992 hook_event_token_t, token, 653381a2a9aSdr146992 hook_data_t, info, 654381a2a9aSdr146992 hook_int_t *, hi); 655381a2a9aSdr146992 656381a2a9aSdr146992 return (rval); 657381a2a9aSdr146992 } 658381a2a9aSdr146992 659381a2a9aSdr146992 /* 660381a2a9aSdr146992 * Function: hook_family_add 661381a2a9aSdr146992 * Returns: internal family pointer - NULL = Fail 662381a2a9aSdr146992 * Parameters: hf(I) - family pointer 663381a2a9aSdr146992 * 664381a2a9aSdr146992 * Add new family to family list 665381a2a9aSdr146992 */ 666381a2a9aSdr146992 hook_family_int_t * 667f4b3ec61Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks) 668381a2a9aSdr146992 { 669381a2a9aSdr146992 hook_family_int_t *hfi, *new; 670381a2a9aSdr146992 671381a2a9aSdr146992 ASSERT(hf != NULL); 672381a2a9aSdr146992 ASSERT(hf->hf_name != NULL); 673381a2a9aSdr146992 674381a2a9aSdr146992 new = hook_family_copy(hf); 675381a2a9aSdr146992 if (new == NULL) 676381a2a9aSdr146992 return (NULL); 677381a2a9aSdr146992 6787ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 6797ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 6807ddc9b1aSDarren Reed 6817ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 6827ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 6837ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 6847ddc9b1aSDarren Reed hook_family_free(new, NULL); 6857ddc9b1aSDarren Reed return (NULL); 6867ddc9b1aSDarren Reed } 687381a2a9aSdr146992 688381a2a9aSdr146992 /* search family list */ 689f4b3ec61Sdh155122 hfi = hook_family_find(hf->hf_name, hks); 690381a2a9aSdr146992 if (hfi != NULL) { 6917ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 6927ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 6937ddc9b1aSDarren Reed hook_family_free(new, NULL); 694381a2a9aSdr146992 return (NULL); 695381a2a9aSdr146992 } 696381a2a9aSdr146992 6977ddc9b1aSDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK, 6987ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 6997ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 7007ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 7017ddc9b1aSDarren Reed hook_family_free(new, NULL); 7027ddc9b1aSDarren Reed return (NULL); 7037ddc9b1aSDarren Reed } 7047ddc9b1aSDarren Reed 7057ddc9b1aSDarren Reed CVW_INIT(&new->hfi_lock); 7067ddc9b1aSDarren Reed SLIST_INIT(&new->hfi_head); 7077ddc9b1aSDarren Reed TAILQ_INIT(&new->hfi_nhead); 7087ddc9b1aSDarren Reed 7097ddc9b1aSDarren Reed hook_wait_init(&new->hfi_waiter, &new->hfi_lock); 7107ddc9b1aSDarren Reed 7117ddc9b1aSDarren Reed new->hfi_stack = hks; 712381a2a9aSdr146992 713f4b3ec61Sdh155122 /* Add to family list head */ 714f4b3ec61Sdh155122 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry); 715f4b3ec61Sdh155122 7167ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 7177ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 7187ddc9b1aSDarren Reed 7197ddc9b1aSDarren Reed hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER); 7207ddc9b1aSDarren Reed 7217ddc9b1aSDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE); 7227ddc9b1aSDarren Reed 723381a2a9aSdr146992 return (new); 724381a2a9aSdr146992 } 725381a2a9aSdr146992 726381a2a9aSdr146992 /* 727381a2a9aSdr146992 * Function: hook_family_remove 728381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 729381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 730381a2a9aSdr146992 * 7317ddc9b1aSDarren Reed * Remove family from family list. This function has been designed to be 7327ddc9b1aSDarren Reed * called once and once only per hook_family_int_t. Thus when cleaning up 7337ddc9b1aSDarren Reed * this structure as an orphan, callers should only call hook_family_free. 734381a2a9aSdr146992 */ 735381a2a9aSdr146992 int 736381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi) 737381a2a9aSdr146992 { 738f4b3ec61Sdh155122 hook_stack_t *hks; 739*8ad74188SDarren Reed boolean_t notifydone; 740381a2a9aSdr146992 741381a2a9aSdr146992 ASSERT(hfi != NULL); 7427ddc9b1aSDarren Reed hks = hfi->hfi_stack; 743381a2a9aSdr146992 7447ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 745381a2a9aSdr146992 7467ddc9b1aSDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK, 7477ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 7487ddc9b1aSDarren Reed /* 7497ddc9b1aSDarren Reed * If we're trying to destroy the hook_stack_t... 7507ddc9b1aSDarren Reed */ 751*8ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 7527ddc9b1aSDarren Reed return (ENXIO); 753381a2a9aSdr146992 } 754381a2a9aSdr146992 7557ddc9b1aSDarren Reed /* 7567ddc9b1aSDarren Reed * Check if the family is in use by the presence of either events 7577ddc9b1aSDarren Reed * or notify callbacks on the hook family. 7587ddc9b1aSDarren Reed */ 7597ddc9b1aSDarren Reed if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) { 7607ddc9b1aSDarren Reed hfi->hfi_condemned = B_TRUE; 7617ddc9b1aSDarren Reed } else { 7627ddc9b1aSDarren Reed /* 7637ddc9b1aSDarren Reed * Although hfi_condemned = B_FALSE is implied from creation, 7647ddc9b1aSDarren Reed * putting a comment here inside the else upsets lint. 7657ddc9b1aSDarren Reed */ 7667ddc9b1aSDarren Reed hfi->hfi_condemned = B_FALSE; 7677ddc9b1aSDarren Reed } 768381a2a9aSdr146992 7697ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 7707ddc9b1aSDarren Reed hook_wait_destroy(&hfi->hfi_waiter); 771*8ad74188SDarren Reed notifydone = hfi->hfi_shutdown; 772*8ad74188SDarren Reed hfi->hfi_shutdown = B_TRUE; 7737ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 7747ddc9b1aSDarren Reed 7757ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 7767ddc9b1aSDarren Reed 777*8ad74188SDarren Reed if (!notifydone) 778*8ad74188SDarren Reed hook_stack_notify_run(hks, hfi->hfi_family.hf_name, 779*8ad74188SDarren Reed HN_UNREGISTER); 7807ddc9b1aSDarren Reed 7817ddc9b1aSDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE); 7827ddc9b1aSDarren Reed 7837ddc9b1aSDarren Reed /* 7847ddc9b1aSDarren Reed * If we don't have to wait for anything else to disappear from this 7857ddc9b1aSDarren Reed * structure then we can free it up. 7867ddc9b1aSDarren Reed */ 7877ddc9b1aSDarren Reed if (!hfi->hfi_condemned) 7887ddc9b1aSDarren Reed hook_family_free(hfi, hks); 789381a2a9aSdr146992 790381a2a9aSdr146992 return (0); 791381a2a9aSdr146992 } 792381a2a9aSdr146992 793381a2a9aSdr146992 794381a2a9aSdr146992 /* 7957ddc9b1aSDarren Reed * Function: hook_family_free 7967ddc9b1aSDarren Reed * Returns: None 7977ddc9b1aSDarren Reed * Parameters: hfi(I) - internal family pointer 7987ddc9b1aSDarren Reed * 7997ddc9b1aSDarren Reed * Free alloc memory for family 8007ddc9b1aSDarren Reed */ 8017ddc9b1aSDarren Reed static void 8027ddc9b1aSDarren Reed hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks) 8037ddc9b1aSDarren Reed { 8047ddc9b1aSDarren Reed 8057ddc9b1aSDarren Reed /* 8067ddc9b1aSDarren Reed * This lock gives us possession of the hks pointer after the 8077ddc9b1aSDarren Reed * SLIST_REMOVE, for which it is not needed, when hks_shutdown 8087ddc9b1aSDarren Reed * is checked and hook_stack_remove called. 8097ddc9b1aSDarren Reed */ 8107ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 8117ddc9b1aSDarren Reed 8127ddc9b1aSDarren Reed ASSERT(hfi != NULL); 8137ddc9b1aSDarren Reed 8147ddc9b1aSDarren Reed if (hks != NULL) { 8157ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 8167ddc9b1aSDarren Reed /* Remove from family list */ 8177ddc9b1aSDarren Reed SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, 8187ddc9b1aSDarren Reed hfi_entry); 8197ddc9b1aSDarren Reed 8207ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 8217ddc9b1aSDarren Reed } 8227ddc9b1aSDarren Reed 8237ddc9b1aSDarren Reed /* Free name space */ 8247ddc9b1aSDarren Reed if (hfi->hfi_family.hf_name != NULL) { 8257ddc9b1aSDarren Reed kmem_free(hfi->hfi_family.hf_name, 8267ddc9b1aSDarren Reed strlen(hfi->hfi_family.hf_name) + 1); 8277ddc9b1aSDarren Reed } 8287ddc9b1aSDarren Reed 8297ddc9b1aSDarren Reed /* Free container */ 8307ddc9b1aSDarren Reed kmem_free(hfi, sizeof (*hfi)); 8317ddc9b1aSDarren Reed 8327ddc9b1aSDarren Reed if (hks->hks_shutdown == 2) 8337ddc9b1aSDarren Reed hook_stack_remove(hks); 8347ddc9b1aSDarren Reed 8357ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 8367ddc9b1aSDarren Reed } 8377ddc9b1aSDarren Reed 838*8ad74188SDarren Reed /* 839*8ad74188SDarren Reed * Function: hook_family_shutdown 840*8ad74188SDarren Reed * Returns: int - 0 = Succ, Else = Fail 841*8ad74188SDarren Reed * Parameters: hfi(I) - internal family pointer 842*8ad74188SDarren Reed * 843*8ad74188SDarren Reed * As an alternative to removing a family, we may desire to just generate 844*8ad74188SDarren Reed * a series of callbacks to indicate that we will be going away in the 845*8ad74188SDarren Reed * future. The hfi_condemned flag isn't set because we aren't trying to 846*8ad74188SDarren Reed * remove the structure. 847*8ad74188SDarren Reed */ 848*8ad74188SDarren Reed int 849*8ad74188SDarren Reed hook_family_shutdown(hook_family_int_t *hfi) 850*8ad74188SDarren Reed { 851*8ad74188SDarren Reed hook_stack_t *hks; 852*8ad74188SDarren Reed boolean_t notifydone; 853*8ad74188SDarren Reed 854*8ad74188SDarren Reed ASSERT(hfi != NULL); 855*8ad74188SDarren Reed hks = hfi->hfi_stack; 856*8ad74188SDarren Reed 857*8ad74188SDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 858*8ad74188SDarren Reed 859*8ad74188SDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_WAIT_MASK, 860*8ad74188SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 861*8ad74188SDarren Reed /* 862*8ad74188SDarren Reed * If we're trying to destroy the hook_stack_t... 863*8ad74188SDarren Reed */ 864*8ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 865*8ad74188SDarren Reed return (ENXIO); 866*8ad74188SDarren Reed } 867*8ad74188SDarren Reed 868*8ad74188SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 869*8ad74188SDarren Reed notifydone = hfi->hfi_shutdown; 870*8ad74188SDarren Reed hfi->hfi_shutdown = B_TRUE; 871*8ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 872*8ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 873*8ad74188SDarren Reed 874*8ad74188SDarren Reed if (!notifydone) 875*8ad74188SDarren Reed hook_stack_notify_run(hks, hfi->hfi_family.hf_name, 876*8ad74188SDarren Reed HN_UNREGISTER); 877*8ad74188SDarren Reed 878*8ad74188SDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE); 879*8ad74188SDarren Reed 880*8ad74188SDarren Reed return (0); 881*8ad74188SDarren Reed } 8827ddc9b1aSDarren Reed 8837ddc9b1aSDarren Reed /* 884381a2a9aSdr146992 * Function: hook_family_copy 885381a2a9aSdr146992 * Returns: internal family pointer - NULL = Failed 886381a2a9aSdr146992 * Parameters: src(I) - family pointer 887381a2a9aSdr146992 * 888381a2a9aSdr146992 * Allocate internal family block and duplicate incoming family 889381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 890381a2a9aSdr146992 */ 891381a2a9aSdr146992 static hook_family_int_t * 892381a2a9aSdr146992 hook_family_copy(hook_family_t *src) 893381a2a9aSdr146992 { 894381a2a9aSdr146992 hook_family_int_t *new; 895381a2a9aSdr146992 hook_family_t *dst; 896381a2a9aSdr146992 897381a2a9aSdr146992 ASSERT(src != NULL); 898381a2a9aSdr146992 ASSERT(src->hf_name != NULL); 899381a2a9aSdr146992 900381a2a9aSdr146992 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 901381a2a9aSdr146992 902381a2a9aSdr146992 /* Copy body */ 903381a2a9aSdr146992 dst = &new->hfi_family; 904381a2a9aSdr146992 *dst = *src; 905381a2a9aSdr146992 9067ddc9b1aSDarren Reed SLIST_INIT(&new->hfi_head); 9077ddc9b1aSDarren Reed TAILQ_INIT(&new->hfi_nhead); 9087ddc9b1aSDarren Reed 909381a2a9aSdr146992 /* Copy name */ 910381a2a9aSdr146992 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 911381a2a9aSdr146992 (void) strcpy(dst->hf_name, src->hf_name); 912381a2a9aSdr146992 913381a2a9aSdr146992 return (new); 914381a2a9aSdr146992 } 915381a2a9aSdr146992 916381a2a9aSdr146992 /* 9177ddc9b1aSDarren Reed * Function: hook_family_find 918381a2a9aSdr146992 * Returns: internal family pointer - NULL = Not match 919381a2a9aSdr146992 * Parameters: family(I) - family name string 920381a2a9aSdr146992 * 921381a2a9aSdr146992 * Search family list with family name 9227ddc9b1aSDarren Reed * A lock on hfi_lock must be held when called. 923381a2a9aSdr146992 */ 924381a2a9aSdr146992 static hook_family_int_t * 925f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks) 926381a2a9aSdr146992 { 927381a2a9aSdr146992 hook_family_int_t *hfi = NULL; 928381a2a9aSdr146992 929381a2a9aSdr146992 ASSERT(family != NULL); 930381a2a9aSdr146992 931f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 932381a2a9aSdr146992 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 933381a2a9aSdr146992 break; 934381a2a9aSdr146992 } 935381a2a9aSdr146992 return (hfi); 936381a2a9aSdr146992 } 937381a2a9aSdr146992 9387ddc9b1aSDarren Reed /* 9397ddc9b1aSDarren Reed * Function: hook_family_notify_register 9407ddc9b1aSDarren Reed * Returns: 0 = success, else failure 9417ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 9427ddc9b1aSDarren Reed * callback(I) - function to be called 9437ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 9447ddc9b1aSDarren Reed * 9457ddc9b1aSDarren Reed * So long as this hook stack isn't being shut down, register a new 9467ddc9b1aSDarren Reed * callback to be activated each time a new event is added to this 9477ddc9b1aSDarren Reed * family. 9487ddc9b1aSDarren Reed * 9497ddc9b1aSDarren Reed * To call this function we must have an active handle in use on the family, 9507ddc9b1aSDarren Reed * so if we take this into account, then neither the hook_family_int_t nor 9517ddc9b1aSDarren Reed * the hook_stack_t that owns it can disappear. We have to put some trust 9527ddc9b1aSDarren Reed * in the callers to be properly synchronised... 9537ddc9b1aSDarren Reed * 9547ddc9b1aSDarren Reed * Holding hks_lock is required to provide synchronisation for hks_shutdown. 9557ddc9b1aSDarren Reed */ 9567ddc9b1aSDarren Reed int 9577ddc9b1aSDarren Reed hook_family_notify_register(hook_family_int_t *hfi, 9587ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg) 9597ddc9b1aSDarren Reed { 9607ddc9b1aSDarren Reed hook_stack_t *hks; 9617ddc9b1aSDarren Reed int error; 9627ddc9b1aSDarren Reed 9637ddc9b1aSDarren Reed hks = hfi->hfi_stack; 9647ddc9b1aSDarren Reed 9657ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 9667ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 9677ddc9b1aSDarren Reed 968*8ad74188SDarren Reed if ((hfi->hfi_stack->hks_shutdown != 0) || 969*8ad74188SDarren Reed hfi->hfi_condemned || hfi->hfi_shutdown) { 9707ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 9717ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 9727ddc9b1aSDarren Reed return (ESHUTDOWN); 9737ddc9b1aSDarren Reed } 9747ddc9b1aSDarren Reed 9757ddc9b1aSDarren Reed error = hook_notify_register(&hfi->hfi_lock, &hfi->hfi_nhead, 9767ddc9b1aSDarren Reed callback, arg); 9777ddc9b1aSDarren Reed 9787ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 9797ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 9807ddc9b1aSDarren Reed 9817ddc9b1aSDarren Reed return (error); 9827ddc9b1aSDarren Reed } 983381a2a9aSdr146992 984381a2a9aSdr146992 /* 9857ddc9b1aSDarren Reed * Function: hook_family_notify_unregister 9867ddc9b1aSDarren Reed * Returns: 0 = success, else failure 9877ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 9887ddc9b1aSDarren Reed * callback(I) - function to be called 989381a2a9aSdr146992 * 9907ddc9b1aSDarren Reed * Remove a callback from the list of those executed when a new event is 9917ddc9b1aSDarren Reed * added to a hook family. 992381a2a9aSdr146992 */ 9937ddc9b1aSDarren Reed int 9947ddc9b1aSDarren Reed hook_family_notify_unregister(hook_family_int_t *hfi, 9957ddc9b1aSDarren Reed hook_notify_fn_t callback) 996381a2a9aSdr146992 { 9977ddc9b1aSDarren Reed boolean_t free_family; 9987ddc9b1aSDarren Reed int error; 999381a2a9aSdr146992 10007ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 10017ddc9b1aSDarren Reed 10027ddc9b1aSDarren Reed error = hook_notify_unregister(&hfi->hfi_lock, &hfi->hfi_nhead, 10037ddc9b1aSDarren Reed callback); 10047ddc9b1aSDarren Reed 10057ddc9b1aSDarren Reed /* 10067ddc9b1aSDarren Reed * If hook_family_remove has been called but the structure was still 10077ddc9b1aSDarren Reed * "busy" ... but we might have just made it "unbusy"... 10087ddc9b1aSDarren Reed */ 10097ddc9b1aSDarren Reed if ((error == 0) && hfi->hfi_condemned && 10107ddc9b1aSDarren Reed SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) { 10117ddc9b1aSDarren Reed free_family = B_TRUE; 10127ddc9b1aSDarren Reed } else { 10137ddc9b1aSDarren Reed free_family = B_FALSE; 1014381a2a9aSdr146992 } 1015381a2a9aSdr146992 10167ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1017381a2a9aSdr146992 10187ddc9b1aSDarren Reed if (free_family) 10197ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 10207ddc9b1aSDarren Reed 10217ddc9b1aSDarren Reed return (error); 10227ddc9b1aSDarren Reed } 1023381a2a9aSdr146992 1024381a2a9aSdr146992 /* 1025381a2a9aSdr146992 * Function: hook_event_add 1026381a2a9aSdr146992 * Returns: internal event pointer - NULL = Fail 1027381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1028381a2a9aSdr146992 * he(I) - event pointer 1029381a2a9aSdr146992 * 1030381a2a9aSdr146992 * Add new event to event list on specific family. 1031381a2a9aSdr146992 * This function can fail to return successfully if (1) it cannot allocate 1032381a2a9aSdr146992 * enough memory for its own internal data structures, (2) the event has 1033381a2a9aSdr146992 * already been registered (for any hook family.) 1034381a2a9aSdr146992 */ 1035381a2a9aSdr146992 hook_event_int_t * 1036381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 1037381a2a9aSdr146992 { 1038381a2a9aSdr146992 hook_event_int_t *hei, *new; 10397ddc9b1aSDarren Reed hook_stack_t *hks; 1040381a2a9aSdr146992 1041381a2a9aSdr146992 ASSERT(hfi != NULL); 1042381a2a9aSdr146992 ASSERT(he != NULL); 1043381a2a9aSdr146992 ASSERT(he->he_name != NULL); 1044381a2a9aSdr146992 1045381a2a9aSdr146992 new = hook_event_copy(he); 1046381a2a9aSdr146992 if (new == NULL) 1047381a2a9aSdr146992 return (NULL); 1048381a2a9aSdr146992 10497ddc9b1aSDarren Reed hks = hfi->hfi_stack; 10507ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 10517ddc9b1aSDarren Reed 10527ddc9b1aSDarren Reed hks = hfi->hfi_stack; 10537ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 10547ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 10557ddc9b1aSDarren Reed hook_event_free(new, NULL); 10567ddc9b1aSDarren Reed return (NULL); 10577ddc9b1aSDarren Reed } 1058381a2a9aSdr146992 1059381a2a9aSdr146992 /* Check whether this event pointer is already registered */ 1060f4b3ec61Sdh155122 hei = hook_event_checkdup(he, hks); 1061381a2a9aSdr146992 if (hei != NULL) { 10627ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 10637ddc9b1aSDarren Reed hook_event_free(new, NULL); 1064381a2a9aSdr146992 return (NULL); 1065381a2a9aSdr146992 } 1066381a2a9aSdr146992 10677ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 10687ddc9b1aSDarren Reed 1069*8ad74188SDarren Reed if (hfi->hfi_condemned || hfi->hfi_shutdown) { 10707ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 10717ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 10727ddc9b1aSDarren Reed hook_event_free(new, NULL); 10737ddc9b1aSDarren Reed return (NULL); 10747ddc9b1aSDarren Reed } 10757ddc9b1aSDarren Reed 10767ddc9b1aSDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK, 10777ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 10787ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 10797ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 10807ddc9b1aSDarren Reed hook_event_free(new, NULL); 10817ddc9b1aSDarren Reed return (NULL); 10827ddc9b1aSDarren Reed } 10837ddc9b1aSDarren Reed 10847ddc9b1aSDarren Reed TAILQ_INIT(&new->hei_nhead); 10857ddc9b1aSDarren Reed 10867ddc9b1aSDarren Reed hook_event_init_kstats(hfi, new); 10877ddc9b1aSDarren Reed hook_wait_init(&new->hei_waiter, &new->hei_lock); 10887ddc9b1aSDarren Reed 1089381a2a9aSdr146992 /* Add to event list head */ 1090381a2a9aSdr146992 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 1091381a2a9aSdr146992 10927ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 10937ddc9b1aSDarren Reed 10947ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 10957ddc9b1aSDarren Reed 10967ddc9b1aSDarren Reed hook_notify_run(&hfi->hfi_nhead, 10977ddc9b1aSDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER); 10987ddc9b1aSDarren Reed 10997ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE); 11007ddc9b1aSDarren Reed 1101381a2a9aSdr146992 return (new); 1102381a2a9aSdr146992 } 1103381a2a9aSdr146992 11047ddc9b1aSDarren Reed /* 11057ddc9b1aSDarren Reed * Function: hook_event_init_kstats 11067ddc9b1aSDarren Reed * Returns: None 11077ddc9b1aSDarren Reed * Parameters: hfi(I) - pointer to the family that owns this event. 11087ddc9b1aSDarren Reed * hei(I) - pointer to the hook event that needs some kstats. 11097ddc9b1aSDarren Reed * 11107ddc9b1aSDarren Reed * Create a set of kstats that relate to each event registered with 11117ddc9b1aSDarren Reed * the hook framework. A counter is kept for each time the event is 11127ddc9b1aSDarren Reed * activated and for each time a hook is added or removed. As the 11137ddc9b1aSDarren Reed * kstats just count the events as they happen, the total number of 11147ddc9b1aSDarren Reed * hooks registered must be obtained by subtractived removed from added. 11157ddc9b1aSDarren Reed */ 11167ddc9b1aSDarren Reed static void 11177ddc9b1aSDarren Reed hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei) 11187ddc9b1aSDarren Reed { 11197ddc9b1aSDarren Reed hook_event_kstat_t template = { 11207ddc9b1aSDarren Reed { "hooksAdded", KSTAT_DATA_UINT64 }, 11217ddc9b1aSDarren Reed { "hooksRemoved", KSTAT_DATA_UINT64 }, 11227ddc9b1aSDarren Reed { "events", KSTAT_DATA_UINT64 } 11237ddc9b1aSDarren Reed }; 11247ddc9b1aSDarren Reed hook_stack_t *hks; 11257ddc9b1aSDarren Reed 11267ddc9b1aSDarren Reed hks = hfi->hfi_stack; 11277ddc9b1aSDarren Reed hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0, 11287ddc9b1aSDarren Reed hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED, 11297ddc9b1aSDarren Reed sizeof (hei->hei_kstats) / sizeof (kstat_named_t), 11307ddc9b1aSDarren Reed KSTAT_FLAG_VIRTUAL, hks->hks_netstackid); 11317ddc9b1aSDarren Reed 11327ddc9b1aSDarren Reed bcopy((char *)&template, &hei->hei_kstats, sizeof (template)); 11337ddc9b1aSDarren Reed 11347ddc9b1aSDarren Reed if (hei->hei_kstatp != NULL) { 11357ddc9b1aSDarren Reed hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats; 11367ddc9b1aSDarren Reed hei->hei_kstatp->ks_private = 11377ddc9b1aSDarren Reed (void *)(uintptr_t)hks->hks_netstackid; 11387ddc9b1aSDarren Reed 11397ddc9b1aSDarren Reed kstat_install(hei->hei_kstatp); 11407ddc9b1aSDarren Reed } 11417ddc9b1aSDarren Reed } 1142381a2a9aSdr146992 1143381a2a9aSdr146992 /* 1144381a2a9aSdr146992 * Function: hook_event_remove 1145381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 1146381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1147381a2a9aSdr146992 * he(I) - event pointer 1148381a2a9aSdr146992 * 1149381a2a9aSdr146992 * Remove event from event list on specific family 11507ddc9b1aSDarren Reed * 11517ddc9b1aSDarren Reed * This function assumes that the caller has received a pointer to a the 11527ddc9b1aSDarren Reed * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'. 11537ddc9b1aSDarren Reed * This the hook_family_int_t is guaranteed to be around for the life of this 11547ddc9b1aSDarren Reed * call, unless the caller has decided to call net_protocol_release or 11557ddc9b1aSDarren Reed * net_protocol_unregister before calling net_event_unregister - an error. 1156381a2a9aSdr146992 */ 1157381a2a9aSdr146992 int 1158381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 1159381a2a9aSdr146992 { 11607ddc9b1aSDarren Reed boolean_t free_family; 1161381a2a9aSdr146992 hook_event_int_t *hei; 1162*8ad74188SDarren Reed boolean_t notifydone; 1163381a2a9aSdr146992 1164381a2a9aSdr146992 ASSERT(hfi != NULL); 1165381a2a9aSdr146992 ASSERT(he != NULL); 1166381a2a9aSdr146992 11677ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1168381a2a9aSdr146992 11697ddc9b1aSDarren Reed /* 11707ddc9b1aSDarren Reed * Set the flag so that we can call hook_event_notify_run without 11717ddc9b1aSDarren Reed * holding any locks but at the same time prevent other changes to 11727ddc9b1aSDarren Reed * the event at the same time. 11737ddc9b1aSDarren Reed */ 11747ddc9b1aSDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK, 11757ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 11767ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1177381a2a9aSdr146992 return (ENXIO); 1178381a2a9aSdr146992 } 1179381a2a9aSdr146992 11807ddc9b1aSDarren Reed hei = hook_event_find(hfi, he->he_name); 11817ddc9b1aSDarren Reed if (hei == NULL) { 11827ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 11837ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 11847ddc9b1aSDarren Reed return (ESRCH); 1185381a2a9aSdr146992 } 1186381a2a9aSdr146992 11877ddc9b1aSDarren Reed free_family = B_FALSE; 1188381a2a9aSdr146992 11897ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 11907ddc9b1aSDarren Reed /* 1191*8ad74188SDarren Reed * The hei_shutdown flag is used to indicate whether or not we have 1192*8ad74188SDarren Reed * done a shutdown and thus already walked through the notify list. 1193*8ad74188SDarren Reed */ 1194*8ad74188SDarren Reed notifydone = hei->hei_shutdown; 1195*8ad74188SDarren Reed hei->hei_shutdown = B_TRUE; 1196*8ad74188SDarren Reed /* 11977ddc9b1aSDarren Reed * If there are any hooks still registered for this event or 11987ddc9b1aSDarren Reed * there are any notifiers registered, return an error indicating 11997ddc9b1aSDarren Reed * that the event is still busy. 12007ddc9b1aSDarren Reed */ 12017ddc9b1aSDarren Reed if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) { 12027ddc9b1aSDarren Reed hei->hei_condemned = B_TRUE; 12037ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 12047ddc9b1aSDarren Reed } else { 12057ddc9b1aSDarren Reed /* hei_condemned = B_FALSE is implied from creation */ 12067ddc9b1aSDarren Reed /* 12077ddc9b1aSDarren Reed * Even though we know the notify list is empty, we call 12087ddc9b1aSDarren Reed * hook_wait_destroy here to synchronise wait removing a 12097ddc9b1aSDarren Reed * hook from an event. 12107ddc9b1aSDarren Reed */ 12117ddc9b1aSDarren Reed hook_wait_destroy(&hei->hei_waiter); 12127ddc9b1aSDarren Reed 12137ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 12147ddc9b1aSDarren Reed 12157ddc9b1aSDarren Reed if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) && 12167ddc9b1aSDarren Reed TAILQ_EMPTY(&hfi->hfi_nhead)) 12177ddc9b1aSDarren Reed free_family = B_TRUE; 12187ddc9b1aSDarren Reed } 12197ddc9b1aSDarren Reed 12207ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 12217ddc9b1aSDarren Reed 1222*8ad74188SDarren Reed if (!notifydone) 12237ddc9b1aSDarren Reed hook_notify_run(&hfi->hfi_nhead, 12247ddc9b1aSDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER); 12257ddc9b1aSDarren Reed 12267ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 12277ddc9b1aSDarren Reed 12287ddc9b1aSDarren Reed if (!hei->hei_condemned) { 12297ddc9b1aSDarren Reed hook_event_free(hei, hfi); 12307ddc9b1aSDarren Reed if (free_family) 12317ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 12327ddc9b1aSDarren Reed } 1233381a2a9aSdr146992 1234381a2a9aSdr146992 return (0); 1235381a2a9aSdr146992 } 1236381a2a9aSdr146992 12377ddc9b1aSDarren Reed /* 1238*8ad74188SDarren Reed * Function: hook_event_shutdown 1239*8ad74188SDarren Reed * Returns: int - 0 = Succ, Else = Fail 1240*8ad74188SDarren Reed * Parameters: hfi(I) - internal family pointer 1241*8ad74188SDarren Reed * he(I) - event pointer 1242*8ad74188SDarren Reed * 1243*8ad74188SDarren Reed * As with hook_family_shutdown, we want to generate the notify callbacks 1244*8ad74188SDarren Reed * as if the event was being removed but not actually do the remove. 1245*8ad74188SDarren Reed */ 1246*8ad74188SDarren Reed int 1247*8ad74188SDarren Reed hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he) 1248*8ad74188SDarren Reed { 1249*8ad74188SDarren Reed hook_event_int_t *hei; 1250*8ad74188SDarren Reed boolean_t notifydone; 1251*8ad74188SDarren Reed 1252*8ad74188SDarren Reed ASSERT(hfi != NULL); 1253*8ad74188SDarren Reed ASSERT(he != NULL); 1254*8ad74188SDarren Reed 1255*8ad74188SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1256*8ad74188SDarren Reed 1257*8ad74188SDarren Reed /* 1258*8ad74188SDarren Reed * Set the flag so that we can call hook_event_notify_run without 1259*8ad74188SDarren Reed * holding any locks but at the same time prevent other changes to 1260*8ad74188SDarren Reed * the event at the same time. 1261*8ad74188SDarren Reed */ 1262*8ad74188SDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_WAIT_MASK, 1263*8ad74188SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 1264*8ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1265*8ad74188SDarren Reed return (ENXIO); 1266*8ad74188SDarren Reed } 1267*8ad74188SDarren Reed 1268*8ad74188SDarren Reed hei = hook_event_find(hfi, he->he_name); 1269*8ad74188SDarren Reed if (hei == NULL) { 1270*8ad74188SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 1271*8ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1272*8ad74188SDarren Reed return (ESRCH); 1273*8ad74188SDarren Reed } 1274*8ad74188SDarren Reed 1275*8ad74188SDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 1276*8ad74188SDarren Reed notifydone = hei->hei_shutdown; 1277*8ad74188SDarren Reed hei->hei_shutdown = B_TRUE; 1278*8ad74188SDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 1279*8ad74188SDarren Reed 1280*8ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1281*8ad74188SDarren Reed 1282*8ad74188SDarren Reed if (!notifydone) 1283*8ad74188SDarren Reed hook_notify_run(&hfi->hfi_nhead, 1284*8ad74188SDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER); 1285*8ad74188SDarren Reed 1286*8ad74188SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 1287*8ad74188SDarren Reed 1288*8ad74188SDarren Reed return (0); 1289*8ad74188SDarren Reed } 1290*8ad74188SDarren Reed 1291*8ad74188SDarren Reed /* 12927ddc9b1aSDarren Reed * Function: hook_event_free 12937ddc9b1aSDarren Reed * Returns: None 12947ddc9b1aSDarren Reed * Parameters: hei(I) - internal event pointer 12957ddc9b1aSDarren Reed * 12967ddc9b1aSDarren Reed * Free alloc memory for event 12977ddc9b1aSDarren Reed */ 12987ddc9b1aSDarren Reed static void 12997ddc9b1aSDarren Reed hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi) 13007ddc9b1aSDarren Reed { 13017ddc9b1aSDarren Reed boolean_t free_family; 13027ddc9b1aSDarren Reed 13037ddc9b1aSDarren Reed ASSERT(hei != NULL); 13047ddc9b1aSDarren Reed 13057ddc9b1aSDarren Reed if (hfi != NULL) { 13067ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 13077ddc9b1aSDarren Reed /* 13087ddc9b1aSDarren Reed * Remove the event from the hook family's list. 13097ddc9b1aSDarren Reed */ 13107ddc9b1aSDarren Reed SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 13117ddc9b1aSDarren Reed if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) && 13127ddc9b1aSDarren Reed TAILQ_EMPTY(&hfi->hfi_nhead)) { 13137ddc9b1aSDarren Reed free_family = B_TRUE; 13147ddc9b1aSDarren Reed } else { 13157ddc9b1aSDarren Reed free_family = B_FALSE; 13167ddc9b1aSDarren Reed } 13177ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 13187ddc9b1aSDarren Reed } 13197ddc9b1aSDarren Reed 13207ddc9b1aSDarren Reed if (hei->hei_kstatp != NULL) { 13217ddc9b1aSDarren Reed ASSERT(hfi != NULL); 13227ddc9b1aSDarren Reed 13237ddc9b1aSDarren Reed kstat_delete_netstack(hei->hei_kstatp, 13247ddc9b1aSDarren Reed hfi->hfi_stack->hks_netstackid); 13257ddc9b1aSDarren Reed hei->hei_kstatp = NULL; 13267ddc9b1aSDarren Reed } 13277ddc9b1aSDarren Reed 13287ddc9b1aSDarren Reed /* Free container */ 13297ddc9b1aSDarren Reed kmem_free(hei, sizeof (*hei)); 13307ddc9b1aSDarren Reed 13317ddc9b1aSDarren Reed if (free_family) 13327ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 13337ddc9b1aSDarren Reed } 1334381a2a9aSdr146992 1335381a2a9aSdr146992 /* 1336381a2a9aSdr146992 * Function: hook_event_checkdup 1337381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 1338381a2a9aSdr146992 * Parameters: he(I) - event pointer 1339381a2a9aSdr146992 * 13407ddc9b1aSDarren Reed * Search all of the hook families to see if the event being passed in 13417ddc9b1aSDarren Reed * has already been associated with one. 1342381a2a9aSdr146992 */ 1343381a2a9aSdr146992 static hook_event_int_t * 1344f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks) 1345381a2a9aSdr146992 { 1346381a2a9aSdr146992 hook_family_int_t *hfi; 1347381a2a9aSdr146992 hook_event_int_t *hei; 1348381a2a9aSdr146992 1349381a2a9aSdr146992 ASSERT(he != NULL); 1350381a2a9aSdr146992 13517ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 1352f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 1353381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 13547ddc9b1aSDarren Reed if (hei->hei_event == he) { 13557ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 1356381a2a9aSdr146992 return (hei); 1357381a2a9aSdr146992 } 1358381a2a9aSdr146992 } 13597ddc9b1aSDarren Reed } 13607ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 1361381a2a9aSdr146992 1362381a2a9aSdr146992 return (NULL); 1363381a2a9aSdr146992 } 1364381a2a9aSdr146992 1365381a2a9aSdr146992 /* 1366381a2a9aSdr146992 * Function: hook_event_copy 1367381a2a9aSdr146992 * Returns: internal event pointer - NULL = Failed 1368381a2a9aSdr146992 * Parameters: src(I) - event pointer 1369381a2a9aSdr146992 * 1370381a2a9aSdr146992 * Allocate internal event block and duplicate incoming event 1371381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 1372381a2a9aSdr146992 */ 1373381a2a9aSdr146992 static hook_event_int_t * 1374381a2a9aSdr146992 hook_event_copy(hook_event_t *src) 1375381a2a9aSdr146992 { 1376381a2a9aSdr146992 hook_event_int_t *new; 1377381a2a9aSdr146992 1378381a2a9aSdr146992 ASSERT(src != NULL); 1379381a2a9aSdr146992 ASSERT(src->he_name != NULL); 1380381a2a9aSdr146992 1381381a2a9aSdr146992 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 1382381a2a9aSdr146992 1383381a2a9aSdr146992 /* Copy body */ 1384381a2a9aSdr146992 TAILQ_INIT(&new->hei_head); 1385381a2a9aSdr146992 new->hei_event = src; 1386381a2a9aSdr146992 1387381a2a9aSdr146992 return (new); 1388381a2a9aSdr146992 } 1389381a2a9aSdr146992 1390381a2a9aSdr146992 /* 1391381a2a9aSdr146992 * Function: hook_event_find 1392381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 1393381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1394381a2a9aSdr146992 * event(I) - event name string 1395381a2a9aSdr146992 * 1396381a2a9aSdr146992 * Search event list with event name 13977ddc9b1aSDarren Reed * A lock on hfi->hfi_lock must be held when called. 1398381a2a9aSdr146992 */ 1399381a2a9aSdr146992 static hook_event_int_t * 1400381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event) 1401381a2a9aSdr146992 { 1402381a2a9aSdr146992 hook_event_int_t *hei = NULL; 1403381a2a9aSdr146992 1404381a2a9aSdr146992 ASSERT(hfi != NULL); 1405381a2a9aSdr146992 ASSERT(event != NULL); 1406381a2a9aSdr146992 1407381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 14087ddc9b1aSDarren Reed if ((strcmp(hei->hei_event->he_name, event) == 0) && 14097ddc9b1aSDarren Reed ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0)) 1410381a2a9aSdr146992 break; 1411381a2a9aSdr146992 } 1412381a2a9aSdr146992 return (hei); 1413381a2a9aSdr146992 } 1414381a2a9aSdr146992 1415381a2a9aSdr146992 /* 14167ddc9b1aSDarren Reed * Function: hook_event_notify_register 14177ddc9b1aSDarren Reed * Returns: 0 = success, else failure 14187ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 14197ddc9b1aSDarren Reed * event(I) - name of the event 14207ddc9b1aSDarren Reed * callback(I) - function to be called 14217ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 1422381a2a9aSdr146992 * 14237ddc9b1aSDarren Reed * Adds a new callback to the event named by "event" (we must find it) 14247ddc9b1aSDarren Reed * that will be executed each time a new hook is added to the event. 14257ddc9b1aSDarren Reed * Of course, if the stack is being shut down, this call should fail. 1426381a2a9aSdr146992 */ 14277ddc9b1aSDarren Reed int 14287ddc9b1aSDarren Reed hook_event_notify_register(hook_family_int_t *hfi, char *event, 14297ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg) 1430381a2a9aSdr146992 { 14317ddc9b1aSDarren Reed hook_event_int_t *hei; 14327ddc9b1aSDarren Reed hook_stack_t *hks; 14337ddc9b1aSDarren Reed int error; 1434381a2a9aSdr146992 14357ddc9b1aSDarren Reed hks = hfi->hfi_stack; 14367ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 14377ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 14387ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 14397ddc9b1aSDarren Reed return (ESHUTDOWN); 1440381a2a9aSdr146992 } 1441381a2a9aSdr146992 14427ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 14437ddc9b1aSDarren Reed 1444*8ad74188SDarren Reed if (hfi->hfi_condemned || hfi->hfi_shutdown) { 14457ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 14467ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 14477ddc9b1aSDarren Reed return (ESHUTDOWN); 14487ddc9b1aSDarren Reed } 14497ddc9b1aSDarren Reed 14507ddc9b1aSDarren Reed hei = hook_event_find(hfi, event); 14517ddc9b1aSDarren Reed if (hei == NULL) { 14527ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 14537ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 14547ddc9b1aSDarren Reed return (ESRCH); 14557ddc9b1aSDarren Reed } 14567ddc9b1aSDarren Reed 14577ddc9b1aSDarren Reed /* 14587ddc9b1aSDarren Reed * Grabbing the read lock on hei_lock is only so that we can 14597ddc9b1aSDarren Reed * synchronise access to hei_condemned. 14607ddc9b1aSDarren Reed */ 14617ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 1462*8ad74188SDarren Reed if (hei->hei_condemned || hei->hei_shutdown) { 14637ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 14647ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 14657ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 14667ddc9b1aSDarren Reed return (ESHUTDOWN); 14677ddc9b1aSDarren Reed } 14687ddc9b1aSDarren Reed 14697ddc9b1aSDarren Reed error = hook_notify_register(&hei->hei_lock, &hei->hei_nhead, 14707ddc9b1aSDarren Reed callback, arg); 14717ddc9b1aSDarren Reed 14727ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 14737ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 14747ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 14757ddc9b1aSDarren Reed 14767ddc9b1aSDarren Reed return (error); 14777ddc9b1aSDarren Reed } 14787ddc9b1aSDarren Reed 14797ddc9b1aSDarren Reed /* 14807ddc9b1aSDarren Reed * Function: hook_event_notify_unregister 14817ddc9b1aSDarren Reed * Returns: 0 = success, else failure 14827ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 14837ddc9b1aSDarren Reed * event(I) - name of the event 14847ddc9b1aSDarren Reed * callback(I) - function to be called 14857ddc9b1aSDarren Reed * 14867ddc9b1aSDarren Reed * Remove the given callback from the named event's list of functions 14877ddc9b1aSDarren Reed * to call when a hook is added or removed. 14887ddc9b1aSDarren Reed */ 14897ddc9b1aSDarren Reed int 14907ddc9b1aSDarren Reed hook_event_notify_unregister(hook_family_int_t *hfi, char *event, 14917ddc9b1aSDarren Reed hook_notify_fn_t callback) 14927ddc9b1aSDarren Reed { 14937ddc9b1aSDarren Reed hook_event_int_t *hei; 14947ddc9b1aSDarren Reed boolean_t free_event; 14957ddc9b1aSDarren Reed int error; 14967ddc9b1aSDarren Reed 14977ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 14987ddc9b1aSDarren Reed 14997ddc9b1aSDarren Reed hei = hook_event_find(hfi, event); 15007ddc9b1aSDarren Reed if (hei == NULL) { 15017ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 15027ddc9b1aSDarren Reed return (ESRCH); 15037ddc9b1aSDarren Reed } 15047ddc9b1aSDarren Reed 15057ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 15067ddc9b1aSDarren Reed 15077ddc9b1aSDarren Reed error = hook_notify_unregister(&hei->hei_lock, &hei->hei_nhead, 15087ddc9b1aSDarren Reed callback); 15097ddc9b1aSDarren Reed 15107ddc9b1aSDarren Reed /* 15117ddc9b1aSDarren Reed * hei_condemned has been set if someone tried to remove the 15127ddc9b1aSDarren Reed * event but couldn't because there were still things attached to 15137ddc9b1aSDarren Reed * it. Now that we've done a successful remove, if it is now empty 15147ddc9b1aSDarren Reed * then by all rights we should be free'ing it too. Note that the 15157ddc9b1aSDarren Reed * expectation is that only the caller of hook_event_add will ever 15167ddc9b1aSDarren Reed * call hook_event_remove. 15177ddc9b1aSDarren Reed */ 15187ddc9b1aSDarren Reed if ((error == 0) && hei->hei_condemned && 15197ddc9b1aSDarren Reed TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) { 15207ddc9b1aSDarren Reed free_event = B_TRUE; 15217ddc9b1aSDarren Reed } else { 15227ddc9b1aSDarren Reed free_event = B_FALSE; 15237ddc9b1aSDarren Reed } 15247ddc9b1aSDarren Reed 15257ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 15267ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 15277ddc9b1aSDarren Reed 15287ddc9b1aSDarren Reed if (free_event) { 15297ddc9b1aSDarren Reed /* 15307ddc9b1aSDarren Reed * It is safe to pass in hfi here, without a lock, because 15317ddc9b1aSDarren Reed * our structure (hei) is still on one of its lists and thus 15327ddc9b1aSDarren Reed * it won't be able to disappear yet... 15337ddc9b1aSDarren Reed */ 15347ddc9b1aSDarren Reed hook_event_free(hei, hfi); 15357ddc9b1aSDarren Reed } 15367ddc9b1aSDarren Reed 15377ddc9b1aSDarren Reed return (error); 15387ddc9b1aSDarren Reed } 15397ddc9b1aSDarren Reed 15407ddc9b1aSDarren Reed /* 15417ddc9b1aSDarren Reed * Function: hook_event_notify_run 15427ddc9b1aSDarren Reed * Returns: None 15437ddc9b1aSDarren Reed * Parameters: nrun(I) - pointer to the list of callbacks to execute 15447ddc9b1aSDarren Reed * hfi(I) - hook stack pointer to execute callbacks for 15457ddc9b1aSDarren Reed * name(I) - name of a hook family 15467ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 15477ddc9b1aSDarren Reed * 15487ddc9b1aSDarren Reed * Execute all of the callbacks registered for this event. 15497ddc9b1aSDarren Reed */ 15507ddc9b1aSDarren Reed static void 15517ddc9b1aSDarren Reed hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi, 15527ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd) 15537ddc9b1aSDarren Reed { 15547ddc9b1aSDarren Reed 15557ddc9b1aSDarren Reed hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name, 15567ddc9b1aSDarren Reed event, name, cmd); 15577ddc9b1aSDarren Reed } 1558381a2a9aSdr146992 1559381a2a9aSdr146992 /* 1560381a2a9aSdr146992 * Function: hook_register 1561381a2a9aSdr146992 * Returns: int- 0 = Succ, Else = Fail 1562381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1563381a2a9aSdr146992 * event(I) - event name string 1564381a2a9aSdr146992 * h(I) - hook pointer 1565381a2a9aSdr146992 * 15667ddc9b1aSDarren Reed * Add new hook to hook list on the specified family and event. 1567381a2a9aSdr146992 */ 1568381a2a9aSdr146992 int 1569381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 1570381a2a9aSdr146992 { 1571381a2a9aSdr146992 hook_event_int_t *hei; 1572381a2a9aSdr146992 hook_int_t *hi, *new; 15737ddc9b1aSDarren Reed int error; 1574381a2a9aSdr146992 1575381a2a9aSdr146992 ASSERT(hfi != NULL); 1576381a2a9aSdr146992 ASSERT(event != NULL); 1577381a2a9aSdr146992 ASSERT(h != NULL); 15787ddc9b1aSDarren Reed 15797ddc9b1aSDarren Reed if (hfi->hfi_stack->hks_shutdown) 15807ddc9b1aSDarren Reed return (NULL); 1581381a2a9aSdr146992 1582381a2a9aSdr146992 /* Alloc hook_int_t and copy hook */ 1583381a2a9aSdr146992 new = hook_copy(h); 1584381a2a9aSdr146992 if (new == NULL) 1585381a2a9aSdr146992 return (ENOMEM); 1586381a2a9aSdr146992 1587381a2a9aSdr146992 /* 1588381a2a9aSdr146992 * Since hook add/remove only impact event, so it is unnecessary 1589381a2a9aSdr146992 * to hold global family write lock. Just get read lock here to 1590381a2a9aSdr146992 * ensure event will not be removed when doing hooks operation 1591381a2a9aSdr146992 */ 15927ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1593381a2a9aSdr146992 1594381a2a9aSdr146992 hei = hook_event_find(hfi, event); 1595381a2a9aSdr146992 if (hei == NULL) { 15967ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 15977ddc9b1aSDarren Reed hook_int_free(new, hfi->hfi_stack->hks_netstackid); 1598381a2a9aSdr146992 return (ENXIO); 1599381a2a9aSdr146992 } 1600381a2a9aSdr146992 1601381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 1602381a2a9aSdr146992 1603*8ad74188SDarren Reed /* 1604*8ad74188SDarren Reed * If we've run either the remove() or shutdown(), do not allow any 1605*8ad74188SDarren Reed * more hooks to be added to this event. 1606*8ad74188SDarren Reed */ 1607*8ad74188SDarren Reed if (hei->hei_shutdown) { 1608*8ad74188SDarren Reed error = ESHUTDOWN; 1609*8ad74188SDarren Reed goto bad_add; 1610*8ad74188SDarren Reed } 1611*8ad74188SDarren Reed 1612381a2a9aSdr146992 hi = hook_find(hei, h); 1613381a2a9aSdr146992 if (hi != NULL) { 16147ddc9b1aSDarren Reed error = EEXIST; 16157ddc9b1aSDarren Reed goto bad_add; 16167ddc9b1aSDarren Reed } 16177ddc9b1aSDarren Reed 16187ddc9b1aSDarren Reed if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK, 16197ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 16207ddc9b1aSDarren Reed error = ENOENT; 16217ddc9b1aSDarren Reed bad_add: 1622381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 16237ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 16247ddc9b1aSDarren Reed hook_int_free(new, hfi->hfi_stack->hks_netstackid); 16257ddc9b1aSDarren Reed return (error); 1626381a2a9aSdr146992 } 1627381a2a9aSdr146992 1628381a2a9aSdr146992 /* Add to hook list head */ 16297ddc9b1aSDarren Reed error = hook_insert(&hei->hei_head, new); 16307ddc9b1aSDarren Reed if (error == 0) { 1631381a2a9aSdr146992 hei->hei_event->he_interested = B_TRUE; 16327ddc9b1aSDarren Reed hei->hei_kstats.hooks_added.value.ui64++; 16337ddc9b1aSDarren Reed 16347ddc9b1aSDarren Reed hook_init_kstats(hfi, hei, new); 16357ddc9b1aSDarren Reed } 1636381a2a9aSdr146992 1637381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 16387ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 16397ddc9b1aSDarren Reed 16407ddc9b1aSDarren Reed /* 16417ddc9b1aSDarren Reed * Note that the name string passed through to the notify callbacks 16427ddc9b1aSDarren Reed * is from the original hook being registered, not the copy being 16437ddc9b1aSDarren Reed * inserted. 16447ddc9b1aSDarren Reed */ 16457ddc9b1aSDarren Reed if (error == 0) { 16467ddc9b1aSDarren Reed hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER); 16477ddc9b1aSDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE); 16487ddc9b1aSDarren Reed } 16497ddc9b1aSDarren Reed 16507ddc9b1aSDarren Reed return (error); 16517ddc9b1aSDarren Reed } 16527ddc9b1aSDarren Reed 16537ddc9b1aSDarren Reed /* 16547ddc9b1aSDarren Reed * Function: hook_insert 16557ddc9b1aSDarren Reed * Returns: int- 0 = Succ, else = Fail 16567ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 16577ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 16587ddc9b1aSDarren Reed * 16597ddc9b1aSDarren Reed * Try to insert the hook onto the list of hooks according to the hints 16607ddc9b1aSDarren Reed * given in the hook to be inserted and those that already exist on the 16617ddc9b1aSDarren Reed * list. For now, the implementation permits only a single hook to be 16627ddc9b1aSDarren Reed * either first or last and names provided with before or after are only 16637ddc9b1aSDarren Reed * loosely coupled with the action. 16647ddc9b1aSDarren Reed */ 16657ddc9b1aSDarren Reed static int 16667ddc9b1aSDarren Reed hook_insert(hook_int_head_t *head, hook_int_t *new) 16677ddc9b1aSDarren Reed { 16687ddc9b1aSDarren Reed hook_int_t *before; 16697ddc9b1aSDarren Reed hook_int_t *hi; 16707ddc9b1aSDarren Reed hook_t *hih; 16717ddc9b1aSDarren Reed hook_t *h = &new->hi_hook; 16727ddc9b1aSDarren Reed 16737ddc9b1aSDarren Reed switch (new->hi_hook.h_hint) { 16747ddc9b1aSDarren Reed case HH_NONE : 16757ddc9b1aSDarren Reed before = NULL; 16767ddc9b1aSDarren Reed /* 16777ddc9b1aSDarren Reed * If there is no hint present (or not one that can be 16787ddc9b1aSDarren Reed * satisfied now) then try to at least respect the wishes 16797ddc9b1aSDarren Reed * of those that want to be last. If there are none wanting 16807ddc9b1aSDarren Reed * to be last then add the new hook to the tail of the 16817ddc9b1aSDarren Reed * list - this means we keep any wanting to be first 16827ddc9b1aSDarren Reed * happy without having to search for HH_FIRST. 16837ddc9b1aSDarren Reed */ 16847ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 16857ddc9b1aSDarren Reed hih = &hi->hi_hook; 16867ddc9b1aSDarren Reed if ((hih->h_hint == HH_AFTER) && 16877ddc9b1aSDarren Reed (strcmp(h->h_name, 16887ddc9b1aSDarren Reed (char *)hih->h_hintvalue) == 0)) { 16897ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 16907ddc9b1aSDarren Reed return (0); 16917ddc9b1aSDarren Reed } 16927ddc9b1aSDarren Reed if ((hih->h_hint == HH_BEFORE) && (before == NULL) && 16937ddc9b1aSDarren Reed (strcmp(h->h_name, 16947ddc9b1aSDarren Reed (char *)hih->h_hintvalue) == 0)) { 16957ddc9b1aSDarren Reed before = hi; 16967ddc9b1aSDarren Reed } 16977ddc9b1aSDarren Reed } 16987ddc9b1aSDarren Reed if (before != NULL) { 16997ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, before, new, hi_entry); 17007ddc9b1aSDarren Reed return (0); 17017ddc9b1aSDarren Reed } 17027ddc9b1aSDarren Reed hook_insert_plain(head, new); 17037ddc9b1aSDarren Reed break; 17047ddc9b1aSDarren Reed 17057ddc9b1aSDarren Reed case HH_FIRST : 17067ddc9b1aSDarren Reed hi = TAILQ_FIRST(head); 17077ddc9b1aSDarren Reed if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST)) 17087ddc9b1aSDarren Reed return (EBUSY); 17097ddc9b1aSDarren Reed TAILQ_INSERT_HEAD(head, new, hi_entry); 17107ddc9b1aSDarren Reed break; 17117ddc9b1aSDarren Reed 17127ddc9b1aSDarren Reed case HH_LAST : 17137ddc9b1aSDarren Reed hi = TAILQ_LAST(head, hook_int_head); 17147ddc9b1aSDarren Reed if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST)) 17157ddc9b1aSDarren Reed return (EBUSY); 17167ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 17177ddc9b1aSDarren Reed break; 17187ddc9b1aSDarren Reed 17197ddc9b1aSDarren Reed case HH_BEFORE : 17207ddc9b1aSDarren Reed hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue); 17217ddc9b1aSDarren Reed if (hi == NULL) 17227ddc9b1aSDarren Reed return (hook_insert_afterbefore(head, new)); 17237ddc9b1aSDarren Reed 17247ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_FIRST) 17257ddc9b1aSDarren Reed return (EBUSY); 17267ddc9b1aSDarren Reed 17277ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 17287ddc9b1aSDarren Reed break; 17297ddc9b1aSDarren Reed 17307ddc9b1aSDarren Reed case HH_AFTER : 17317ddc9b1aSDarren Reed hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue); 17327ddc9b1aSDarren Reed if (hi == NULL) 17337ddc9b1aSDarren Reed return (hook_insert_afterbefore(head, new)); 17347ddc9b1aSDarren Reed 17357ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_LAST) 17367ddc9b1aSDarren Reed return (EBUSY); 17377ddc9b1aSDarren Reed 17387ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 17397ddc9b1aSDarren Reed break; 17407ddc9b1aSDarren Reed 17417ddc9b1aSDarren Reed default : 17427ddc9b1aSDarren Reed return (EINVAL); 17437ddc9b1aSDarren Reed } 17447ddc9b1aSDarren Reed 1745381a2a9aSdr146992 return (0); 1746381a2a9aSdr146992 } 1747381a2a9aSdr146992 17487ddc9b1aSDarren Reed /* 17497ddc9b1aSDarren Reed * Function: hook_insert_plain 17507ddc9b1aSDarren Reed * Returns: int- 0 = success, else = failure 17517ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 17527ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 17537ddc9b1aSDarren Reed * 17547ddc9b1aSDarren Reed * Insert a hook such that it respects the wishes of those that want to 17557ddc9b1aSDarren Reed * be last. If there are none wanting to be last then add the new hook 17567ddc9b1aSDarren Reed * to the tail of the list - this means we keep any wanting to be first 17577ddc9b1aSDarren Reed * happy without having to search for HH_FIRST. 17587ddc9b1aSDarren Reed */ 17597ddc9b1aSDarren Reed static void 17607ddc9b1aSDarren Reed hook_insert_plain(hook_int_head_t *head, hook_int_t *new) 17617ddc9b1aSDarren Reed { 17627ddc9b1aSDarren Reed hook_int_t *hi; 17637ddc9b1aSDarren Reed 17647ddc9b1aSDarren Reed hi = TAILQ_FIRST(head); 17657ddc9b1aSDarren Reed if (hi != NULL) { 17667ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_LAST) { 17677ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 17687ddc9b1aSDarren Reed } else { 17697ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 17707ddc9b1aSDarren Reed } 17717ddc9b1aSDarren Reed } else { 17727ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 17737ddc9b1aSDarren Reed } 17747ddc9b1aSDarren Reed } 17757ddc9b1aSDarren Reed 17767ddc9b1aSDarren Reed /* 17777ddc9b1aSDarren Reed * Function: hook_insert_afterbefore 17787ddc9b1aSDarren Reed * Returns: int- 0 = success, else = failure 17797ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 17807ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 17817ddc9b1aSDarren Reed * 17827ddc9b1aSDarren Reed * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not 17837ddc9b1aSDarren Reed * possible, so now we need to be more careful. The first pass is to go 17847ddc9b1aSDarren Reed * through the list and look for any other hooks that also specify the 17857ddc9b1aSDarren Reed * same hint name as the new one. The object of this exercise is to make 17867ddc9b1aSDarren Reed * sure that hooks with HH_BEFORE always appear on the list before those 17877ddc9b1aSDarren Reed * with HH_AFTER so that when said hook arrives, it can be placed in the 17887ddc9b1aSDarren Reed * middle of the BEFOREs and AFTERs. If this condition does not arise, 17897ddc9b1aSDarren Reed * just use hook_insert_plain() to try and insert the hook somewhere that 17907ddc9b1aSDarren Reed * is innocuous to existing efforts. 17917ddc9b1aSDarren Reed */ 17927ddc9b1aSDarren Reed static int 17937ddc9b1aSDarren Reed hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new) 17947ddc9b1aSDarren Reed { 17957ddc9b1aSDarren Reed hook_int_t *hi; 17967ddc9b1aSDarren Reed hook_t *nh; 17977ddc9b1aSDarren Reed hook_t *h; 17987ddc9b1aSDarren Reed 17997ddc9b1aSDarren Reed nh = &new->hi_hook; 18007ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_NONE); 18017ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_LAST); 18027ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_FIRST); 18037ddc9b1aSDarren Reed 18047ddc9b1aSDarren Reed /* 18057ddc9b1aSDarren Reed * First, look through the list to see if there are any other 18067ddc9b1aSDarren Reed * before's or after's that have a matching hint name. 18077ddc9b1aSDarren Reed */ 18087ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 18097ddc9b1aSDarren Reed h = &hi->hi_hook; 18107ddc9b1aSDarren Reed switch (h->h_hint) { 18117ddc9b1aSDarren Reed case HH_FIRST : 18127ddc9b1aSDarren Reed case HH_LAST : 18137ddc9b1aSDarren Reed case HH_NONE : 18147ddc9b1aSDarren Reed break; 18157ddc9b1aSDarren Reed case HH_BEFORE : 18167ddc9b1aSDarren Reed if ((nh->h_hint == HH_BEFORE) && 18177ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 18187ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 18197ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 18207ddc9b1aSDarren Reed return (0); 18217ddc9b1aSDarren Reed } 18227ddc9b1aSDarren Reed if ((nh->h_hint == HH_AFTER) && 18237ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 18247ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 18257ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 18267ddc9b1aSDarren Reed return (0); 18277ddc9b1aSDarren Reed } 18287ddc9b1aSDarren Reed break; 18297ddc9b1aSDarren Reed case HH_AFTER : 18307ddc9b1aSDarren Reed if ((nh->h_hint == HH_AFTER) && 18317ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 18327ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 18337ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 18347ddc9b1aSDarren Reed return (0); 18357ddc9b1aSDarren Reed } 18367ddc9b1aSDarren Reed if ((nh->h_hint == HH_BEFORE) && 18377ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 18387ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 18397ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 18407ddc9b1aSDarren Reed return (0); 18417ddc9b1aSDarren Reed } 18427ddc9b1aSDarren Reed break; 18437ddc9b1aSDarren Reed } 18447ddc9b1aSDarren Reed } 18457ddc9b1aSDarren Reed 18467ddc9b1aSDarren Reed hook_insert_plain(head, new); 18477ddc9b1aSDarren Reed 18487ddc9b1aSDarren Reed return (0); 18497ddc9b1aSDarren Reed } 1850381a2a9aSdr146992 1851381a2a9aSdr146992 /* 1852381a2a9aSdr146992 * Function: hook_unregister 1853381a2a9aSdr146992 * Returns: int - 0 = Succ, Else = Fail 1854381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1855381a2a9aSdr146992 * event(I) - event name string 1856381a2a9aSdr146992 * h(I) - hook pointer 1857381a2a9aSdr146992 * 1858381a2a9aSdr146992 * Remove hook from hook list on specific family, event 1859381a2a9aSdr146992 */ 1860381a2a9aSdr146992 int 1861381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 1862381a2a9aSdr146992 { 1863381a2a9aSdr146992 hook_event_int_t *hei; 1864381a2a9aSdr146992 hook_int_t *hi; 18657ddc9b1aSDarren Reed boolean_t free_event; 1866381a2a9aSdr146992 1867381a2a9aSdr146992 ASSERT(hfi != NULL); 1868381a2a9aSdr146992 ASSERT(h != NULL); 1869381a2a9aSdr146992 18707ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1871381a2a9aSdr146992 1872381a2a9aSdr146992 hei = hook_event_find(hfi, event); 1873381a2a9aSdr146992 if (hei == NULL) { 18747ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1875381a2a9aSdr146992 return (ENXIO); 1876381a2a9aSdr146992 } 1877381a2a9aSdr146992 1878381a2a9aSdr146992 /* Hold write lock for event */ 1879381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 1880381a2a9aSdr146992 1881381a2a9aSdr146992 hi = hook_find(hei, h); 1882381a2a9aSdr146992 if (hi == NULL) { 1883381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 18847ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1885381a2a9aSdr146992 return (ENXIO); 1886381a2a9aSdr146992 } 1887381a2a9aSdr146992 18887ddc9b1aSDarren Reed if (hook_wait_setflag(&hei->hei_waiter, FWF_WAIT_MASK, 18897ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 18907ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 18917ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 18927ddc9b1aSDarren Reed return (ENOENT); 18937ddc9b1aSDarren Reed } 18947ddc9b1aSDarren Reed 1895381a2a9aSdr146992 /* Remove from hook list */ 1896381a2a9aSdr146992 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 18977ddc9b1aSDarren Reed 18987ddc9b1aSDarren Reed free_event = B_FALSE; 1899381a2a9aSdr146992 if (TAILQ_EMPTY(&hei->hei_head)) { 1900381a2a9aSdr146992 hei->hei_event->he_interested = B_FALSE; 19017ddc9b1aSDarren Reed /* 19027ddc9b1aSDarren Reed * If the delete pending flag has been set and there are 19037ddc9b1aSDarren Reed * no notifiers on the event (and we've removed the last 19047ddc9b1aSDarren Reed * hook) then we need to free this event after we're done. 19057ddc9b1aSDarren Reed */ 19067ddc9b1aSDarren Reed if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead)) 19077ddc9b1aSDarren Reed free_event = B_TRUE; 1908381a2a9aSdr146992 } 19097ddc9b1aSDarren Reed hei->hei_kstats.hooks_removed.value.ui64++; 1910381a2a9aSdr146992 1911381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 19127ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 19137ddc9b1aSDarren Reed /* 19147ddc9b1aSDarren Reed * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t 19157ddc9b1aSDarren Reed * will not be free'd and thus the hook_family_int_t wil not 19167ddc9b1aSDarren Reed * be free'd either. 19177ddc9b1aSDarren Reed */ 19187ddc9b1aSDarren Reed hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER); 19197ddc9b1aSDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE); 1920381a2a9aSdr146992 19217ddc9b1aSDarren Reed hook_int_free(hi, hfi->hfi_stack->hks_netstackid); 19227ddc9b1aSDarren Reed 19237ddc9b1aSDarren Reed if (free_event) 19247ddc9b1aSDarren Reed hook_event_free(hei, hfi); 19257ddc9b1aSDarren Reed 1926381a2a9aSdr146992 return (0); 1927381a2a9aSdr146992 } 1928381a2a9aSdr146992 19297ddc9b1aSDarren Reed /* 19307ddc9b1aSDarren Reed * Function: hook_find_byname 19317ddc9b1aSDarren Reed * Returns: internal hook pointer - NULL = Not match 19327ddc9b1aSDarren Reed * Parameters: hei(I) - internal event pointer 19337ddc9b1aSDarren Reed * name(I)- hook name 19347ddc9b1aSDarren Reed * 19357ddc9b1aSDarren Reed * Search an event's list of hooks to see if there is a hook present that 19367ddc9b1aSDarren Reed * has a matching name to the one being looked for. 19377ddc9b1aSDarren Reed */ 19387ddc9b1aSDarren Reed static hook_int_t * 19397ddc9b1aSDarren Reed hook_find_byname(hook_int_head_t *head, char *name) 19407ddc9b1aSDarren Reed { 19417ddc9b1aSDarren Reed hook_int_t *hi; 19427ddc9b1aSDarren Reed 19437ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 19447ddc9b1aSDarren Reed if (strcmp(hi->hi_hook.h_name, name) == 0) 19457ddc9b1aSDarren Reed return (hi); 19467ddc9b1aSDarren Reed } 19477ddc9b1aSDarren Reed 19487ddc9b1aSDarren Reed return (NULL); 19497ddc9b1aSDarren Reed } 1950381a2a9aSdr146992 1951381a2a9aSdr146992 /* 1952381a2a9aSdr146992 * Function: hook_find 1953381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Not match 1954381a2a9aSdr146992 * Parameters: hei(I) - internal event pointer 1955381a2a9aSdr146992 * h(I) - hook pointer 1956381a2a9aSdr146992 * 19577ddc9b1aSDarren Reed * Search an event's list of hooks to see if there is already one that 19587ddc9b1aSDarren Reed * matches the hook being passed in. Currently the only criteria for a 19597ddc9b1aSDarren Reed * successful search here is for the names to be the same. 1960381a2a9aSdr146992 */ 1961381a2a9aSdr146992 static hook_int_t * 1962381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h) 1963381a2a9aSdr146992 { 1964381a2a9aSdr146992 1965381a2a9aSdr146992 ASSERT(hei != NULL); 1966381a2a9aSdr146992 ASSERT(h != NULL); 1967381a2a9aSdr146992 19687ddc9b1aSDarren Reed return (hook_find_byname(&hei->hei_head, h->h_name)); 1969381a2a9aSdr146992 } 1970381a2a9aSdr146992 1971381a2a9aSdr146992 /* 1972381a2a9aSdr146992 * Function: hook_copy 1973381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Failed 1974381a2a9aSdr146992 * Parameters: src(I) - hook pointer 1975381a2a9aSdr146992 * 1976381a2a9aSdr146992 * Allocate internal hook block and duplicate incoming hook. 1977381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 19787ddc9b1aSDarren Reed * Because hook_copy() is responsible for the creation of the internal 19797ddc9b1aSDarren Reed * hook structure that is used here, it takes on population the structure 19807ddc9b1aSDarren Reed * with the kstat information. Note that while the kstat bits are 19817ddc9b1aSDarren Reed * seeded here, their installation of the kstats is handled elsewhere. 1982381a2a9aSdr146992 */ 1983381a2a9aSdr146992 static hook_int_t * 1984381a2a9aSdr146992 hook_copy(hook_t *src) 1985381a2a9aSdr146992 { 1986381a2a9aSdr146992 hook_int_t *new; 1987381a2a9aSdr146992 hook_t *dst; 19887ddc9b1aSDarren Reed int len; 1989381a2a9aSdr146992 1990381a2a9aSdr146992 ASSERT(src != NULL); 1991381a2a9aSdr146992 ASSERT(src->h_name != NULL); 1992381a2a9aSdr146992 1993381a2a9aSdr146992 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 1994381a2a9aSdr146992 1995381a2a9aSdr146992 /* Copy body */ 1996381a2a9aSdr146992 dst = &new->hi_hook; 1997381a2a9aSdr146992 *dst = *src; 1998381a2a9aSdr146992 1999381a2a9aSdr146992 /* Copy name */ 20007ddc9b1aSDarren Reed len = strlen(src->h_name); 20017ddc9b1aSDarren Reed dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP); 2002381a2a9aSdr146992 (void) strcpy(dst->h_name, src->h_name); 2003381a2a9aSdr146992 20047ddc9b1aSDarren Reed /* 20057ddc9b1aSDarren Reed * This is initialised in this manner to make it safer to use the 20067ddc9b1aSDarren Reed * same pointer in the kstats field. 20077ddc9b1aSDarren Reed */ 20087ddc9b1aSDarren Reed dst->h_hintvalue = (uintptr_t)""; 20097ddc9b1aSDarren Reed 20107ddc9b1aSDarren Reed if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) { 20117ddc9b1aSDarren Reed len = strlen((char *)src->h_hintvalue); 20127ddc9b1aSDarren Reed if (len > 0) { 20137ddc9b1aSDarren Reed dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1, 20147ddc9b1aSDarren Reed KM_SLEEP); 20157ddc9b1aSDarren Reed (void) strcpy((char *)dst->h_hintvalue, 20167ddc9b1aSDarren Reed (char *)src->h_hintvalue); 20177ddc9b1aSDarren Reed } 20187ddc9b1aSDarren Reed } 20197ddc9b1aSDarren Reed 2020381a2a9aSdr146992 return (new); 2021381a2a9aSdr146992 } 2022381a2a9aSdr146992 2023381a2a9aSdr146992 /* 20247ddc9b1aSDarren Reed * Function: hook_init_kstats 20257ddc9b1aSDarren Reed * Returns: None 20267ddc9b1aSDarren Reed * Parameters: hfi(I) - pointer to the family that owns the event. 20277ddc9b1aSDarren Reed * hei(I) - pointer to the event that owns this hook 20287ddc9b1aSDarren Reed * hi(I) - pointer to the hook for which we create kstats for 20297ddc9b1aSDarren Reed * 20307ddc9b1aSDarren Reed * Each hook that is registered with this framework has its own kstats 20317ddc9b1aSDarren Reed * set up so that we can provide an easy way in which to observe the 20327ddc9b1aSDarren Reed * look of hooks (using the kstat command.) The position is set to 0 20337ddc9b1aSDarren Reed * here but is recalculated after we know the insertion has been a 20347ddc9b1aSDarren Reed * success. 20357ddc9b1aSDarren Reed */ 20367ddc9b1aSDarren Reed static void 20377ddc9b1aSDarren Reed hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi) 20387ddc9b1aSDarren Reed { 20397ddc9b1aSDarren Reed hook_hook_kstat_t template = { 20407ddc9b1aSDarren Reed { "version", KSTAT_DATA_INT32 }, 20417ddc9b1aSDarren Reed { "flags", KSTAT_DATA_UINT32 }, 20427ddc9b1aSDarren Reed { "hint", KSTAT_DATA_INT32 }, 20437ddc9b1aSDarren Reed { "hint_value", KSTAT_DATA_UINT64 }, 20447ddc9b1aSDarren Reed { "position", KSTAT_DATA_INT32 }, 20457ddc9b1aSDarren Reed { "hook_hits", KSTAT_DATA_UINT64 } 20467ddc9b1aSDarren Reed }; 20477ddc9b1aSDarren Reed hook_stack_t *hks; 20487ddc9b1aSDarren Reed size_t kslen; 20497ddc9b1aSDarren Reed int position; 20507ddc9b1aSDarren Reed hook_int_t *h; 20517ddc9b1aSDarren Reed 20527ddc9b1aSDarren Reed kslen = strlen(hfi->hfi_family.hf_name) + 20537ddc9b1aSDarren Reed strlen(hei->hei_event->he_name) + 2; 20547ddc9b1aSDarren Reed 20557ddc9b1aSDarren Reed hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP); 20567ddc9b1aSDarren Reed (void) snprintf(hi->hi_ksname, kslen, "%s/%s", 20577ddc9b1aSDarren Reed hfi->hfi_family.hf_name, hei->hei_event->he_name); 20587ddc9b1aSDarren Reed 20597ddc9b1aSDarren Reed hks = hfi->hfi_stack; 20607ddc9b1aSDarren Reed hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0, 20617ddc9b1aSDarren Reed hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED, 20627ddc9b1aSDarren Reed sizeof (hi->hi_kstats) / sizeof (kstat_named_t), 20637ddc9b1aSDarren Reed KSTAT_FLAG_VIRTUAL, hks->hks_netstackid); 20647ddc9b1aSDarren Reed 20657ddc9b1aSDarren Reed /* Initialise the kstats for the structure */ 20667ddc9b1aSDarren Reed bcopy(&template, &hi->hi_kstats, sizeof (template)); 20677ddc9b1aSDarren Reed hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version; 20687ddc9b1aSDarren Reed hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags; 20697ddc9b1aSDarren Reed hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint; 20707ddc9b1aSDarren Reed hi->hi_kstats.hook_position.value.i32 = 0; 20717ddc9b1aSDarren Reed hi->hi_kstats.hook_hits.value.ui64 = 0; 20727ddc9b1aSDarren Reed 20737ddc9b1aSDarren Reed switch (hi->hi_hook.h_hint) { 20747ddc9b1aSDarren Reed case HH_BEFORE : 20757ddc9b1aSDarren Reed case HH_AFTER : 20767ddc9b1aSDarren Reed hi->hi_kstats.hook_hintvalue.data_type = KSTAT_DATA_STRING; 20777ddc9b1aSDarren Reed hi->hi_kstats.hook_hintvalue.value.ui64 = 20787ddc9b1aSDarren Reed hi->hi_hook.h_hintvalue; 20797ddc9b1aSDarren Reed break; 20807ddc9b1aSDarren Reed default : 20817ddc9b1aSDarren Reed break; 20827ddc9b1aSDarren Reed } 20837ddc9b1aSDarren Reed 20847ddc9b1aSDarren Reed if (hi->hi_kstatp != NULL) { 20857ddc9b1aSDarren Reed hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats; 20867ddc9b1aSDarren Reed hi->hi_kstatp->ks_private = 20877ddc9b1aSDarren Reed (void *)(uintptr_t)hks->hks_netstackid; 20887ddc9b1aSDarren Reed 20897ddc9b1aSDarren Reed kstat_install(hi->hi_kstatp); 20907ddc9b1aSDarren Reed } 20917ddc9b1aSDarren Reed 20927ddc9b1aSDarren Reed position = 1; 20937ddc9b1aSDarren Reed TAILQ_FOREACH(h, &hei->hei_head, hi_entry) { 20947ddc9b1aSDarren Reed h->hi_kstats.hook_position.value.ui32 = position++; 20957ddc9b1aSDarren Reed } 20967ddc9b1aSDarren Reed } 20977ddc9b1aSDarren Reed 20987ddc9b1aSDarren Reed /* 20997ddc9b1aSDarren Reed * Function: hook_int_free 2100381a2a9aSdr146992 * Returns: None 2101381a2a9aSdr146992 * Parameters: hi(I) - internal hook pointer 2102381a2a9aSdr146992 * 2103381a2a9aSdr146992 * Free alloc memory for hook 2104381a2a9aSdr146992 */ 2105381a2a9aSdr146992 static void 21067ddc9b1aSDarren Reed hook_int_free(hook_int_t *hi, netstackid_t stackid) 2107381a2a9aSdr146992 { 21087ddc9b1aSDarren Reed int len; 21097ddc9b1aSDarren Reed 2110381a2a9aSdr146992 ASSERT(hi != NULL); 2111381a2a9aSdr146992 2112381a2a9aSdr146992 /* Free name space */ 2113381a2a9aSdr146992 if (hi->hi_hook.h_name != NULL) { 2114381a2a9aSdr146992 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 2115381a2a9aSdr146992 } 21167ddc9b1aSDarren Reed if (hi->hi_ksname != NULL) { 21177ddc9b1aSDarren Reed kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1); 21187ddc9b1aSDarren Reed } 21197ddc9b1aSDarren Reed 21207ddc9b1aSDarren Reed /* Free the name used with the before/after hints. */ 21217ddc9b1aSDarren Reed switch (hi->hi_hook.h_hint) { 21227ddc9b1aSDarren Reed case HH_BEFORE : 21237ddc9b1aSDarren Reed case HH_AFTER : 21247ddc9b1aSDarren Reed len = strlen((char *)hi->hi_hook.h_hintvalue); 21257ddc9b1aSDarren Reed if (len > 0) 21267ddc9b1aSDarren Reed kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1); 21277ddc9b1aSDarren Reed break; 21287ddc9b1aSDarren Reed default : 21297ddc9b1aSDarren Reed break; 21307ddc9b1aSDarren Reed } 21317ddc9b1aSDarren Reed 21327ddc9b1aSDarren Reed if (hi->hi_kstatp != NULL) 21337ddc9b1aSDarren Reed kstat_delete_netstack(hi->hi_kstatp, stackid); 2134381a2a9aSdr146992 2135381a2a9aSdr146992 /* Free container */ 2136381a2a9aSdr146992 kmem_free(hi, sizeof (*hi)); 2137381a2a9aSdr146992 } 21387ddc9b1aSDarren Reed 21397ddc9b1aSDarren Reed /* 21407ddc9b1aSDarren Reed * Function: hook_alloc 21417ddc9b1aSDarren Reed * Returns: hook_t * - pointer to new hook structure 21427ddc9b1aSDarren Reed * Parameters: version(I) - version number of the API when compiled 21437ddc9b1aSDarren Reed * 21447ddc9b1aSDarren Reed * This function serves as the interface for consumers to obtain a hook_t 21457ddc9b1aSDarren Reed * structure. At this point in time, there is only a single "version" of 21467ddc9b1aSDarren Reed * it, leading to a straight forward function. In a perfect world the 21477ddc9b1aSDarren Reed * h_vesion would be a protected data structure member, but C isn't that 21487ddc9b1aSDarren Reed * advanced... 21497ddc9b1aSDarren Reed */ 21507ddc9b1aSDarren Reed hook_t * 21517ddc9b1aSDarren Reed hook_alloc(const int h_version) 21527ddc9b1aSDarren Reed { 21537ddc9b1aSDarren Reed hook_t *h; 21547ddc9b1aSDarren Reed 21557ddc9b1aSDarren Reed h = kmem_zalloc(sizeof (hook_t), KM_SLEEP); 21567ddc9b1aSDarren Reed h->h_version = h_version; 21577ddc9b1aSDarren Reed return (h); 21587ddc9b1aSDarren Reed } 21597ddc9b1aSDarren Reed 21607ddc9b1aSDarren Reed /* 21617ddc9b1aSDarren Reed * Function: hook_free 21627ddc9b1aSDarren Reed * Returns: None 21637ddc9b1aSDarren Reed * Parameters: h(I) - external hook pointer 21647ddc9b1aSDarren Reed * 21657ddc9b1aSDarren Reed * This function only free's memory allocated with hook_alloc(), so that if 21667ddc9b1aSDarren Reed * (for example) kernel memory was allocated for h_name, this needs to be 21677ddc9b1aSDarren Reed * free'd before calling hook_free(). 21687ddc9b1aSDarren Reed */ 21697ddc9b1aSDarren Reed void 21707ddc9b1aSDarren Reed hook_free(hook_t *h) 21717ddc9b1aSDarren Reed { 21727ddc9b1aSDarren Reed kmem_free(h, sizeof (*h)); 21737ddc9b1aSDarren Reed } 21747ddc9b1aSDarren Reed 21757ddc9b1aSDarren Reed /* 21767ddc9b1aSDarren Reed * Function: hook_notify_register 21777ddc9b1aSDarren Reed * Returns: 0 = success, else failure 21787ddc9b1aSDarren Reed * Parameters: lock(I) - netstack identifier 21797ddc9b1aSDarren Reed * head(I) - top of the list of callbacks 21807ddc9b1aSDarren Reed * callback(I) - function to be called 21817ddc9b1aSDarren Reed * arg(I) - arg to pass back to the function 21827ddc9b1aSDarren Reed * 21837ddc9b1aSDarren Reed * This function implements the modification of the list of callbacks 21847ddc9b1aSDarren Reed * that are registered when someone wants to be advised of a change 21857ddc9b1aSDarren Reed * that has happened. 21867ddc9b1aSDarren Reed */ 21877ddc9b1aSDarren Reed static int 21887ddc9b1aSDarren Reed hook_notify_register(cvwaitlock_t *lock, hook_notify_head_t *head, 21897ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg) 21907ddc9b1aSDarren Reed { 21917ddc9b1aSDarren Reed hook_notify_t *hn; 21927ddc9b1aSDarren Reed 21937ddc9b1aSDarren Reed CVW_ENTER_WRITE(lock); 21947ddc9b1aSDarren Reed 21957ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 21967ddc9b1aSDarren Reed if (hn->hn_func == callback) { 21977ddc9b1aSDarren Reed CVW_EXIT_WRITE(lock); 21987ddc9b1aSDarren Reed return (EEXIST); 21997ddc9b1aSDarren Reed } 22007ddc9b1aSDarren Reed } 22017ddc9b1aSDarren Reed 22027ddc9b1aSDarren Reed hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP); 22037ddc9b1aSDarren Reed hn->hn_func = callback; 22047ddc9b1aSDarren Reed hn->hn_arg = arg; 22057ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, hn, hn_entry); 22067ddc9b1aSDarren Reed 22077ddc9b1aSDarren Reed CVW_EXIT_WRITE(lock); 22087ddc9b1aSDarren Reed 22097ddc9b1aSDarren Reed return (0); 22107ddc9b1aSDarren Reed } 22117ddc9b1aSDarren Reed 22127ddc9b1aSDarren Reed /* 22137ddc9b1aSDarren Reed * Function: hook_stack_notify_register 22147ddc9b1aSDarren Reed * Returns: 0 = success, else failure 22157ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 22167ddc9b1aSDarren Reed * callback(I) - function to be called 22177ddc9b1aSDarren Reed * 22187ddc9b1aSDarren Reed */ 22197ddc9b1aSDarren Reed static int 22207ddc9b1aSDarren Reed hook_notify_unregister(cvwaitlock_t *lock, hook_notify_head_t *head, 22217ddc9b1aSDarren Reed hook_notify_fn_t callback) 22227ddc9b1aSDarren Reed { 22237ddc9b1aSDarren Reed hook_notify_t *hn; 22247ddc9b1aSDarren Reed 22257ddc9b1aSDarren Reed CVW_ENTER_WRITE(lock); 22267ddc9b1aSDarren Reed 22277ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 22287ddc9b1aSDarren Reed if (hn->hn_func == callback) 22297ddc9b1aSDarren Reed break; 22307ddc9b1aSDarren Reed } 22317ddc9b1aSDarren Reed if (hn == NULL) { 22327ddc9b1aSDarren Reed CVW_EXIT_WRITE(lock); 22337ddc9b1aSDarren Reed return (ESRCH); 22347ddc9b1aSDarren Reed } 22357ddc9b1aSDarren Reed 22367ddc9b1aSDarren Reed TAILQ_REMOVE(head, hn, hn_entry); 22377ddc9b1aSDarren Reed 22387ddc9b1aSDarren Reed CVW_EXIT_WRITE(lock); 22397ddc9b1aSDarren Reed 22407ddc9b1aSDarren Reed kmem_free(hn, sizeof (*hn)); 22417ddc9b1aSDarren Reed 22427ddc9b1aSDarren Reed return (0); 22437ddc9b1aSDarren Reed } 22447ddc9b1aSDarren Reed 22457ddc9b1aSDarren Reed /* 22467ddc9b1aSDarren Reed * Function: hook_notify_run 22477ddc9b1aSDarren Reed * Returns: None 22487ddc9b1aSDarren Reed * Parameters: head(I) - top of the list of callbacks 22497ddc9b1aSDarren Reed * family(I) - name of the hook family that owns the event 22507ddc9b1aSDarren Reed * event(I) - name of the event being changed 22517ddc9b1aSDarren Reed * name(I) - name of the object causing change 22527ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 22537ddc9b1aSDarren Reed * 22547ddc9b1aSDarren Reed * This function walks through the list of registered callbacks and 22557ddc9b1aSDarren Reed * executes each one, passing back the arg supplied when registered 22567ddc9b1aSDarren Reed * and the name of the family (that owns the event), event (the thing 22577ddc9b1aSDarren Reed * to which we're making a change) and finally a name that describes 22587ddc9b1aSDarren Reed * what is being added or removed, as indicated by cmd. 22597ddc9b1aSDarren Reed * 22607ddc9b1aSDarren Reed * This function does not acquire or release any lock as it is required 22617ddc9b1aSDarren Reed * that code calling it do so before hand. The use of hook_notify_head_t 22627ddc9b1aSDarren Reed * is protected by the use of flagwait_t in the structures that own this 22637ddc9b1aSDarren Reed * list and with the use of the FWF_ADD/DEL_ACTIVE flags. 22647ddc9b1aSDarren Reed */ 22657ddc9b1aSDarren Reed static void 22667ddc9b1aSDarren Reed hook_notify_run(hook_notify_head_t *head, char *family, char *event, 22677ddc9b1aSDarren Reed char *name, hook_notify_cmd_t cmd) 22687ddc9b1aSDarren Reed { 22697ddc9b1aSDarren Reed hook_notify_t *hn; 22707ddc9b1aSDarren Reed 22717ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 22727ddc9b1aSDarren Reed (*hn->hn_func)(cmd, hn->hn_arg, family, event, name); 22737ddc9b1aSDarren Reed } 22747ddc9b1aSDarren Reed } 2275