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. 24*652fb50dSRob Gulewich * 25*652fb50dSRob Gulewich * Copyright 2013 Joyent, Inc. All rights reserved. 26381a2a9aSdr146992 */ 27381a2a9aSdr146992 #include <sys/param.h> 28381a2a9aSdr146992 #include <sys/types.h> 29381a2a9aSdr146992 #include <sys/systm.h> 30381a2a9aSdr146992 #include <sys/errno.h> 31381a2a9aSdr146992 #include <sys/kmem.h> 32381a2a9aSdr146992 #include <sys/mutex.h> 33381a2a9aSdr146992 #include <sys/condvar.h> 34381a2a9aSdr146992 #include <sys/modctl.h> 35381a2a9aSdr146992 #include <sys/hook_impl.h> 36381a2a9aSdr146992 #include <sys/sdt.h> 377ddc9b1aSDarren Reed #include <sys/cmn_err.h> 38381a2a9aSdr146992 39381a2a9aSdr146992 /* 40381a2a9aSdr146992 * This file provides kernel hook framework. 41381a2a9aSdr146992 */ 42381a2a9aSdr146992 43381a2a9aSdr146992 static struct modldrv modlmisc = { 44381a2a9aSdr146992 &mod_miscops, /* drv_modops */ 45381a2a9aSdr146992 "Hooks Interface v1.0", /* drv_linkinfo */ 46381a2a9aSdr146992 }; 47381a2a9aSdr146992 48381a2a9aSdr146992 static struct modlinkage modlinkage = { 49381a2a9aSdr146992 MODREV_1, /* ml_rev */ 50381a2a9aSdr146992 &modlmisc, /* ml_linkage */ 51381a2a9aSdr146992 NULL 52381a2a9aSdr146992 }; 53381a2a9aSdr146992 54*652fb50dSRob Gulewich static const char *hook_hintvalue_none = "<none>"; 55*652fb50dSRob Gulewich 56381a2a9aSdr146992 /* 577ddc9b1aSDarren Reed * How it works. 587ddc9b1aSDarren Reed * ============= 597ddc9b1aSDarren Reed * Use of the hook framework here is tied up with zones - when a new zone 607ddc9b1aSDarren Reed * is created, we create a new hook_stack_t and are open to business for 617ddc9b1aSDarren Reed * allowing new hook families and their events. 627ddc9b1aSDarren Reed * 637ddc9b1aSDarren Reed * A consumer of these hooks is expected to operate in this fashion: 647ddc9b1aSDarren Reed * 1) call hook_family_add() to create a new family of hooks. It is a 657ddc9b1aSDarren Reed * current requirement that this call must be made with the value 667ddc9b1aSDarren Reed * returned from hook_stack_init, by way of infrastructure elsewhere. 677ddc9b1aSDarren Reed * 2) add events to the registered family with calls to hook_event_add. 687ddc9b1aSDarren Reed * 697ddc9b1aSDarren Reed * At this point, the structures in place should be open to others to 707ddc9b1aSDarren Reed * add hooks to the event or add notifiers for when the contents of the 717ddc9b1aSDarren Reed * hook stack changes. 727ddc9b1aSDarren Reed * 737ddc9b1aSDarren Reed * The interesting stuff happens on teardown. 747ddc9b1aSDarren Reed * 757ddc9b1aSDarren Reed * It is a requirement that the provider of hook events work in the reverse 767ddc9b1aSDarren Reed * order to the above, so that the first step is: 777ddc9b1aSDarren Reed * 1) remove events from each hook family created earlier 787ddc9b1aSDarren Reed * 2) remove hook families from the hook stack. 797ddc9b1aSDarren Reed * 807ddc9b1aSDarren Reed * When doing teardown of both events and families, a check is made to see 814a9b8375SDarren Reed * if either structure is still "busy". If so then a boolean flag (FWF_DESTROY) 824a9b8375SDarren Reed * is set to say that the structure is condemned. The presence of this flag 834a9b8375SDarren Reed * being set must be checked for in _add()/_register()/ functions and a 844a9b8375SDarren Reed * failure returned if it is set. It is ignored by the _find() functions 854a9b8375SDarren Reed * because they're used by _remove()/_unregister(). 864a9b8375SDarren Reed * While setting the condemned flag when trying to delete a structure would 874a9b8375SDarren Reed * normally be keyed from the presence of a reference count being greater 884a9b8375SDarren Reed * than 1, in this implementation there are no reference counts required: 894a9b8375SDarren Reed * instead the presence of objects on linked lists is taken to mean 904a9b8375SDarren Reed * something is still "busy." 917ddc9b1aSDarren Reed * 927ddc9b1aSDarren Reed * ONLY the caller that adds the family and the events ever has a direct 937ddc9b1aSDarren Reed * reference to the internal structures and thus ONLY it should be doing 947ddc9b1aSDarren Reed * the removal of either the event or family. In practise, what this means 957ddc9b1aSDarren Reed * is that in ip_netinfo.c, we have calls to net_protocol_register(), followed 967ddc9b1aSDarren Reed * by net_event_register() (these interface to hook_family_add() and 977ddc9b1aSDarren Reed * hook_event_add(), respectively) that are made when we create an instance 987ddc9b1aSDarren Reed * of IP and when the IP instance is shutdown/destroyed, it calls 997ddc9b1aSDarren Reed * net_event_unregister() and net_protocol_unregister(), which in turn call 1007ddc9b1aSDarren Reed * hook_event_remove() and hook_family_remove() respectively. Nobody else 1017ddc9b1aSDarren Reed * is entitled to call the _unregister() functions. It is imperative that 1027ddc9b1aSDarren Reed * there be only one _remove() call for every _add() call. 1037ddc9b1aSDarren Reed * 1047ddc9b1aSDarren Reed * It is possible that code which is interfacing with this hook framework 1057ddc9b1aSDarren Reed * won't do all the cleaning up that it needs to at the right time. While 1067ddc9b1aSDarren Reed * we can't prevent programmers from creating memory leaks, we can synchronise 1077ddc9b1aSDarren Reed * when we clean up data structures to prevent code accessing free'd memory. 1087ddc9b1aSDarren Reed * 1097ddc9b1aSDarren Reed * A simple diagram showing the ownership is as follows: 1107ddc9b1aSDarren Reed * 1117ddc9b1aSDarren Reed * Owned +--------------+ 1127ddc9b1aSDarren Reed * by | hook_stack_t | 1137ddc9b1aSDarren Reed * the +--------------+ 1147ddc9b1aSDarren Reed * Instance | 1157ddc9b1aSDarren Reed * - - - - - - - -|- - - - - - - - - - - - - - - - - - 1167ddc9b1aSDarren Reed * V 1177ddc9b1aSDarren Reed * Owned +-------------------+ +-------------------+ 1187ddc9b1aSDarren Reed * | hook_family_int_t |---->| hook_family_int_t | 1197ddc9b1aSDarren Reed * by +-------------------+ +-------------------+ 1207ddc9b1aSDarren Reed * | \+---------------+ \+---------------+ 1217ddc9b1aSDarren Reed * network | | hook_family_t | | hook_family_t | 1227ddc9b1aSDarren Reed * V +---------------+ +---------------+ 1237ddc9b1aSDarren Reed * protocol +------------------+ +------------------+ 1247ddc9b1aSDarren Reed * | hook_event_int_t |---->| hook_event_int_t | 1257ddc9b1aSDarren Reed * (ipv4,ipv6) +------------------+ +------------------+ 1267ddc9b1aSDarren Reed * | \+--------------+ \+--------------+ 1277ddc9b1aSDarren Reed * | | hook_event_t | | hook_event_t | 1287ddc9b1aSDarren Reed * | +--------------+ +--------------+ 1297ddc9b1aSDarren Reed * - - - - - - - -|- - - - - - - - - - - - - - - - - - 1307ddc9b1aSDarren Reed * V 1317ddc9b1aSDarren Reed * Owned +------------+ 1327ddc9b1aSDarren Reed * | hook_int_t | 1337ddc9b1aSDarren Reed * by +------------+ 1347ddc9b1aSDarren Reed * \+--------+ 1357ddc9b1aSDarren Reed * the consumer | hook_t | 1367ddc9b1aSDarren Reed * +--------+ 1377ddc9b1aSDarren Reed * 1387ddc9b1aSDarren Reed * The consumers, such as IPFilter, do not have any pointers or hold any 1397ddc9b1aSDarren Reed * references to hook_int_t, hook_event_t or hook_event_int_t. By placing 1407ddc9b1aSDarren Reed * a hook on an event through net_hook_register(), an implicit reference 1417ddc9b1aSDarren Reed * to the hook_event_int_t is returned with a successful call. Additionally, 1427ddc9b1aSDarren Reed * IPFilter does not see the hook_family_int_t or hook_family_t directly. 1437ddc9b1aSDarren Reed * Rather it is returned a net_handle_t (from net_protocol_lookup()) that 1447ddc9b1aSDarren Reed * contains a pointer to hook_family_int_t. The structure behind the 1457ddc9b1aSDarren Reed * net_handle_t (struct net_data) *is* reference counted and managed 1467ddc9b1aSDarren Reed * appropriately. 1477ddc9b1aSDarren Reed * 1487ddc9b1aSDarren Reed * A more detailed picture that describes how the family/event structures 1497ddc9b1aSDarren Reed * are linked together can be found in <sys/hook_impl.h> 1504a9b8375SDarren Reed * 1514a9b8375SDarren Reed * Notification callbacks. 1524a9b8375SDarren Reed * ======================= 1534a9b8375SDarren Reed * For each of the hook stack, hook family and hook event, it is possible 1544a9b8375SDarren Reed * to request notificatin of change to them. Why? 1554a9b8375SDarren Reed * First, lets equate the hook stack to an IP instance, a hook family to 1564a9b8375SDarren Reed * a network protocol and a hook event to IP packets on the input path. 1574a9b8375SDarren Reed * If a kernel module wants to apply security from the very start of 1584a9b8375SDarren Reed * things, it needs to know as soon as a new instance of networking 1594a9b8375SDarren Reed * is initiated. Whilst for the global zone, it is taken for granted that 1604a9b8375SDarren Reed * this instance will always exist before any interaction takes place, 1614a9b8375SDarren Reed * that is not true for zones running with an exclusive networking instance. 1624a9b8375SDarren Reed * Thus when a local zone is started and a new instance is created to support 1634a9b8375SDarren Reed * that, parties that wish to monitor it and apply a security policy from 1644a9b8375SDarren Reed * the onset need to be informed as early as possible - quite probably 1654a9b8375SDarren Reed * before any networking is started by the zone's boot scripts. 1664a9b8375SDarren Reed * Inside each instance, it is possible to have a number of network protocols 1674a9b8375SDarren Reed * (hook families) in operation. Inside the context of the global zone, 1684a9b8375SDarren Reed * it is possible to have code run before the kernel module providing the 1694a9b8375SDarren Reed * IP networking is loaded. From here, to apply the appropriate security, 1704a9b8375SDarren Reed * it is necessary to become informed of when IP is being configured into 1714a9b8375SDarren Reed * the zone and this is done by registering a notification callback with 1724a9b8375SDarren Reed * the hook stack for changes to it. The next step is to know when packets 1734a9b8375SDarren Reed * can be received through the physical_in, etc, events. This is achieved 1744a9b8375SDarren Reed * by registering a callback with the appropriate network protocol (or in 1754a9b8375SDarren Reed * this file, the correct hook family.) Thus when IP finally attaches a 1764a9b8375SDarren Reed * physical_in event to inet, the module looking to enforce a security 1774a9b8375SDarren Reed * policy can become aware of it being present. Of course there's no 1784a9b8375SDarren Reed * requirement for such a module to be present before all of the above 1794a9b8375SDarren Reed * happens and in such a case, it is reasonable for the same module to 1804a9b8375SDarren Reed * work after everything has been put in place. For this reason, when 1814a9b8375SDarren Reed * a notification callback is added, a series of fake callback events 1824a9b8375SDarren Reed * is generated to simulate the arrival of those entities. There is one 1834a9b8375SDarren Reed * final series of callbacks that can be registered - those to monitor 1844a9b8375SDarren Reed * actual hooks that are added or removed from an event. In practice, 1854a9b8375SDarren Reed * this is useful when there are multiple kernel modules participating 1864a9b8375SDarren Reed * in the processing of packets and there are behaviour dependencies 1874a9b8375SDarren Reed * involved, such that one kernel module might only register its hook 1884a9b8375SDarren Reed * if another is already present and also might want to remove its hook 1894a9b8375SDarren Reed * when the other disappears. 1904a9b8375SDarren Reed * 1914a9b8375SDarren Reed * If you know a kernel module will not be loaded before the infrastructure 1924a9b8375SDarren Reed * used in this file is present then it is not necessary to use this 1934a9b8375SDarren Reed * notification callback mechanism. 1947ddc9b1aSDarren Reed */ 1957ddc9b1aSDarren Reed 1967ddc9b1aSDarren Reed /* 1977ddc9b1aSDarren Reed * Locking 1987ddc9b1aSDarren Reed * ======= 1997ddc9b1aSDarren Reed * The use of CVW_* macros to do locking is driven by the need to allow 2007ddc9b1aSDarren Reed * recursive locking with read locks when we're processing packets. This 2017ddc9b1aSDarren Reed * is necessary because various netinfo functions need to hold read locks, 2027ddc9b1aSDarren Reed * by design, as they can be called in or out of packet context. 2037ddc9b1aSDarren Reed */ 2047ddc9b1aSDarren Reed /* 205381a2a9aSdr146992 * Hook internal functions 206381a2a9aSdr146992 */ 207381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src); 208f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he, 209f4b3ec61Sdh155122 hook_stack_t *hks); 210381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src); 211381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event); 2127ddc9b1aSDarren Reed static void hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi); 213381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src); 214f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks); 2157ddc9b1aSDarren Reed static void hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks); 216381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h); 2177ddc9b1aSDarren Reed static void hook_int_free(hook_int_t *hi, netstackid_t); 218381a2a9aSdr146992 static void hook_init(void); 219f4b3ec61Sdh155122 static void hook_fini(void); 220f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns); 221f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg); 2227ddc9b1aSDarren Reed static void hook_stack_shutdown(netstackid_t stackid, void *arg); 2237ddc9b1aSDarren Reed static int hook_insert(hook_int_head_t *head, hook_int_t *new); 2247ddc9b1aSDarren Reed static void hook_insert_plain(hook_int_head_t *head, hook_int_t *new); 2257ddc9b1aSDarren Reed static int hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new); 2267ddc9b1aSDarren Reed static hook_int_t *hook_find_byname(hook_int_head_t *head, char *name); 2277ddc9b1aSDarren Reed static void hook_event_init_kstats(hook_family_int_t *, hook_event_int_t *); 2287ddc9b1aSDarren Reed static void hook_event_notify_run(hook_event_int_t *, hook_family_int_t *, 2297ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd); 2307ddc9b1aSDarren Reed static void hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, 2317ddc9b1aSDarren Reed hook_int_t *hi); 2324a9b8375SDarren Reed static int hook_notify_register(hook_notify_head_t *head, 2337ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg); 2344a9b8375SDarren Reed static int hook_notify_unregister(hook_notify_head_t *head, 2354a9b8375SDarren Reed hook_notify_fn_t callback, void **); 2367ddc9b1aSDarren Reed static void hook_notify_run(hook_notify_head_t *head, char *family, 2377ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd); 2387ddc9b1aSDarren Reed static void hook_stack_notify_run(hook_stack_t *hks, char *name, 2397ddc9b1aSDarren Reed hook_notify_cmd_t cmd); 2407ddc9b1aSDarren Reed static void hook_stack_remove(hook_stack_t *hks); 2417ddc9b1aSDarren Reed 2427ddc9b1aSDarren Reed /* 2437ddc9b1aSDarren Reed * A list of the hook stacks is kept here because we need to enable 2447ddc9b1aSDarren Reed * net_instance_notify_register() to be called during the creation 2457ddc9b1aSDarren Reed * of a new instance. Previously hook_stack_get() would just use 2467ddc9b1aSDarren Reed * the netstack functions for this work but they will return NULL 2477ddc9b1aSDarren Reed * until the zone has been fully initialised. 2487ddc9b1aSDarren Reed */ 2497ddc9b1aSDarren Reed static hook_stack_head_t hook_stacks; 2507ddc9b1aSDarren Reed static kmutex_t hook_stack_lock; 251381a2a9aSdr146992 252381a2a9aSdr146992 /* 253381a2a9aSdr146992 * Module entry points. 254381a2a9aSdr146992 */ 255381a2a9aSdr146992 int 256381a2a9aSdr146992 _init(void) 257381a2a9aSdr146992 { 258f4b3ec61Sdh155122 int error; 259f4b3ec61Sdh155122 260381a2a9aSdr146992 hook_init(); 261f4b3ec61Sdh155122 error = mod_install(&modlinkage); 262f4b3ec61Sdh155122 if (error != 0) 263f4b3ec61Sdh155122 hook_fini(); 264f4b3ec61Sdh155122 265f4b3ec61Sdh155122 return (error); 266381a2a9aSdr146992 } 267381a2a9aSdr146992 268381a2a9aSdr146992 int 269381a2a9aSdr146992 _fini(void) 270381a2a9aSdr146992 { 271f4b3ec61Sdh155122 int error; 272f4b3ec61Sdh155122 273f4b3ec61Sdh155122 error = mod_remove(&modlinkage); 274f4b3ec61Sdh155122 if (error == 0) 275f4b3ec61Sdh155122 hook_fini(); 276f4b3ec61Sdh155122 277f4b3ec61Sdh155122 return (error); 278381a2a9aSdr146992 } 279381a2a9aSdr146992 280381a2a9aSdr146992 int 281381a2a9aSdr146992 _info(struct modinfo *modinfop) 282381a2a9aSdr146992 { 283381a2a9aSdr146992 return (mod_info(&modlinkage, modinfop)); 284381a2a9aSdr146992 } 285381a2a9aSdr146992 286381a2a9aSdr146992 /* 287381a2a9aSdr146992 * Function: hook_init 288381a2a9aSdr146992 * Returns: None 289381a2a9aSdr146992 * Parameters: None 290381a2a9aSdr146992 * 291381a2a9aSdr146992 * Initialize hooks 292381a2a9aSdr146992 */ 293381a2a9aSdr146992 static void 294381a2a9aSdr146992 hook_init(void) 295381a2a9aSdr146992 { 2967ddc9b1aSDarren Reed mutex_init(&hook_stack_lock, NULL, MUTEX_DRIVER, NULL); 2977ddc9b1aSDarren Reed SLIST_INIT(&hook_stacks); 2987ddc9b1aSDarren Reed 299f4b3ec61Sdh155122 /* 300f4b3ec61Sdh155122 * We want to be informed each time a stack is created or 301f4b3ec61Sdh155122 * destroyed in the kernel. 302f4b3ec61Sdh155122 */ 3037ddc9b1aSDarren Reed netstack_register(NS_HOOK, hook_stack_init, hook_stack_shutdown, 304f4b3ec61Sdh155122 hook_stack_fini); 305381a2a9aSdr146992 } 306381a2a9aSdr146992 307f4b3ec61Sdh155122 /* 308f4b3ec61Sdh155122 * Function: hook_fini 309f4b3ec61Sdh155122 * Returns: None 310f4b3ec61Sdh155122 * Parameters: None 311f4b3ec61Sdh155122 * 312f4b3ec61Sdh155122 * Deinitialize hooks 313f4b3ec61Sdh155122 */ 314f4b3ec61Sdh155122 static void 315f4b3ec61Sdh155122 hook_fini(void) 316f4b3ec61Sdh155122 { 317f4b3ec61Sdh155122 netstack_unregister(NS_HOOK); 3187ddc9b1aSDarren Reed 3197ddc9b1aSDarren Reed mutex_destroy(&hook_stack_lock); 3207ddc9b1aSDarren Reed ASSERT(SLIST_EMPTY(&hook_stacks)); 3217ddc9b1aSDarren Reed } 3227ddc9b1aSDarren Reed 3237ddc9b1aSDarren Reed /* 3247ddc9b1aSDarren Reed * Function: hook_wait_setflag 3257ddc9b1aSDarren Reed * Returns: -1 = setting flag is disallowed, 0 = flag set and did 3267ddc9b1aSDarren Reed * not have to wait (ie no lock droped), 1 = flag set but 3277ddc9b1aSDarren Reed * it was necessary to drop locks to set it. 3287ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 3297ddc9b1aSDarren Reed * busyset(I) - set of flags that we don't want set while 3307ddc9b1aSDarren Reed * we are active. 3317ddc9b1aSDarren Reed * wanted(I) - flag associated with newflag to indicate 3327ddc9b1aSDarren Reed * what we want to do. 3337ddc9b1aSDarren Reed * newflag(I) - the new ACTIVE flag we want to set that 3347ddc9b1aSDarren Reed * indicates what we are doing. 3357ddc9b1aSDarren Reed * 3367ddc9b1aSDarren Reed * The set of functions hook_wait_* implement an API that builds on top of 3377ddc9b1aSDarren Reed * the kcondvar_t to provide controlled execution through a critical region. 3387ddc9b1aSDarren Reed * For each flag that indicates work is being done (FWF_*_ACTIVE) there is 3397ddc9b1aSDarren Reed * also a flag that we set to indicate that we want to do it (FWF_*_WANTED). 3407ddc9b1aSDarren Reed * The combination of flags is required as when this function exits to do 3417ddc9b1aSDarren Reed * the task, the structure is then free for another caller to use and 3424a9b8375SDarren Reed * to indicate that it wants to do work. The flags used when a caller wants 3434a9b8375SDarren Reed * to destroy an object take precedence over those that are used for making 3444a9b8375SDarren Reed * changes to it (add/remove.) In this case, we don't try to secure the 3454a9b8375SDarren Reed * ability to run and return with an error. 3464a9b8375SDarren Reed * 3474a9b8375SDarren Reed * "wantedset" is used here to determine who has the right to clear the 3484a9b8375SDarren Reed * wanted but from the fw_flags set: only he that sets the flag has the 3494a9b8375SDarren Reed * right to clear it at the bottom of the loop, even if someone else 3504a9b8375SDarren Reed * wants to set it. 3517ddc9b1aSDarren Reed * 3527ddc9b1aSDarren Reed * wanted - the FWF_*_WANTED flag that describes the action being requested 3537ddc9b1aSDarren Reed * busyset- the set of FWF_* flags we don't want set when we run 3547ddc9b1aSDarren Reed * newflag- the FWF_*_ACTIVE flag we will set to indicate we are busy 3557ddc9b1aSDarren Reed */ 3567ddc9b1aSDarren Reed int 3577ddc9b1aSDarren Reed hook_wait_setflag(flagwait_t *waiter, uint32_t busyset, fwflag_t wanted, 3587ddc9b1aSDarren Reed fwflag_t newflag) 3597ddc9b1aSDarren Reed { 3604a9b8375SDarren Reed boolean_t wantedset; 3617ddc9b1aSDarren Reed int waited = 0; 3627ddc9b1aSDarren Reed 3637ddc9b1aSDarren Reed mutex_enter(&waiter->fw_lock); 3647ddc9b1aSDarren Reed if (waiter->fw_flags & FWF_DESTROY) { 3654a9b8375SDarren Reed cv_signal(&waiter->fw_cv); 3667ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3677ddc9b1aSDarren Reed return (-1); 3687ddc9b1aSDarren Reed } 3697ddc9b1aSDarren Reed while (waiter->fw_flags & busyset) { 3704a9b8375SDarren Reed wantedset = ((waiter->fw_flags & wanted) == wanted); 3714a9b8375SDarren Reed if (!wantedset) 3727ddc9b1aSDarren Reed waiter->fw_flags |= wanted; 3737ddc9b1aSDarren Reed CVW_EXIT_WRITE(waiter->fw_owner); 3747ddc9b1aSDarren Reed cv_wait(&waiter->fw_cv, &waiter->fw_lock); 3754a9b8375SDarren Reed /* 3764a9b8375SDarren Reed * This lock needs to be dropped here to preserve the order 3774a9b8375SDarren Reed * of acquisition that is fw_owner followed by fw_lock, else 3784a9b8375SDarren Reed * we can deadlock. 3794a9b8375SDarren Reed */ 3804a9b8375SDarren Reed mutex_exit(&waiter->fw_lock); 3817ddc9b1aSDarren Reed waited = 1; 3827ddc9b1aSDarren Reed CVW_ENTER_WRITE(waiter->fw_owner); 3834a9b8375SDarren Reed mutex_enter(&waiter->fw_lock); 3844a9b8375SDarren Reed if (!wantedset) 3857ddc9b1aSDarren Reed waiter->fw_flags &= ~wanted; 3864a9b8375SDarren Reed if (waiter->fw_flags & FWF_DESTROY) { 3874a9b8375SDarren Reed cv_signal(&waiter->fw_cv); 3887ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3897ddc9b1aSDarren Reed return (-1); 3907ddc9b1aSDarren Reed } 3917ddc9b1aSDarren Reed } 3927ddc9b1aSDarren Reed waiter->fw_flags &= ~wanted; 3934a9b8375SDarren Reed ASSERT((waiter->fw_flags & wanted) == 0); 3944a9b8375SDarren Reed ASSERT((waiter->fw_flags & newflag) == 0); 3957ddc9b1aSDarren Reed waiter->fw_flags |= newflag; 3967ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 3977ddc9b1aSDarren Reed return (waited); 3987ddc9b1aSDarren Reed } 3997ddc9b1aSDarren Reed 4007ddc9b1aSDarren Reed /* 4017ddc9b1aSDarren Reed * Function: hook_wait_unsetflag 4027ddc9b1aSDarren Reed * Returns: None 4037ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 4047ddc9b1aSDarren Reed * oldflag(I) - flag to reset 4057ddc9b1aSDarren Reed * 4067ddc9b1aSDarren Reed * Turn off the bit that we had set to run and let others know that 4077ddc9b1aSDarren Reed * they should now check to see if they can run. 4087ddc9b1aSDarren Reed */ 4097ddc9b1aSDarren Reed void 4104a9b8375SDarren Reed hook_wait_unsetflag(flagwait_t *waiter, fwflag_t oldflag) 4117ddc9b1aSDarren Reed { 4127ddc9b1aSDarren Reed mutex_enter(&waiter->fw_lock); 4137ddc9b1aSDarren Reed waiter->fw_flags &= ~oldflag; 4147ddc9b1aSDarren Reed cv_signal(&waiter->fw_cv); 4157ddc9b1aSDarren Reed mutex_exit(&waiter->fw_lock); 4167ddc9b1aSDarren Reed } 4177ddc9b1aSDarren Reed 4187ddc9b1aSDarren Reed /* 4197ddc9b1aSDarren Reed * Function: hook_wait_destroy 4207ddc9b1aSDarren Reed * Returns: None 4217ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 4227ddc9b1aSDarren Reed * 4237ddc9b1aSDarren Reed * Since outer locking (on fw_owner) should ensure that only one function 4247ddc9b1aSDarren Reed * at a time gets to call hook_wait_destroy() on a given object, there is 4257ddc9b1aSDarren Reed * no need to guard against setting FWF_DESTROY_WANTED already being set. 4267ddc9b1aSDarren Reed * It is, however, necessary to wait for all activity on the owning 4277ddc9b1aSDarren Reed * structure to cease. 4287ddc9b1aSDarren Reed */ 4294a9b8375SDarren Reed int 4307ddc9b1aSDarren Reed hook_wait_destroy(flagwait_t *waiter) 4317ddc9b1aSDarren Reed { 4327ddc9b1aSDarren Reed ASSERT((waiter->fw_flags & FWF_DESTROY_WANTED) == 0); 4334a9b8375SDarren Reed mutex_enter(&waiter->fw_lock); 4344a9b8375SDarren Reed if (waiter->fw_flags & FWF_DESTROY_WANTED) { 4354a9b8375SDarren Reed cv_signal(&waiter->fw_cv); 4364a9b8375SDarren Reed mutex_exit(&waiter->fw_lock); 4374a9b8375SDarren Reed return (EINPROGRESS); 4384a9b8375SDarren Reed } 4397ddc9b1aSDarren Reed waiter->fw_flags |= FWF_DESTROY_WANTED; 4407ddc9b1aSDarren Reed while (!FWF_DESTROY_OK(waiter)) { 4417ddc9b1aSDarren Reed CVW_EXIT_WRITE(waiter->fw_owner); 4427ddc9b1aSDarren Reed cv_wait(&waiter->fw_cv, &waiter->fw_lock); 4437ddc9b1aSDarren Reed CVW_ENTER_WRITE(waiter->fw_owner); 4447ddc9b1aSDarren Reed } 4457ddc9b1aSDarren Reed /* 4467ddc9b1aSDarren Reed * There should now be nothing else using "waiter" or its 4477ddc9b1aSDarren Reed * owner, so we can safely assign here without risk of wiiping 4487ddc9b1aSDarren Reed * out someone's bit. 4497ddc9b1aSDarren Reed */ 4507ddc9b1aSDarren Reed waiter->fw_flags = FWF_DESTROY_ACTIVE; 4514a9b8375SDarren Reed cv_signal(&waiter->fw_cv); 4524a9b8375SDarren Reed mutex_exit(&waiter->fw_lock); 4534a9b8375SDarren Reed 4544a9b8375SDarren Reed return (0); 4557ddc9b1aSDarren Reed } 4567ddc9b1aSDarren Reed 4577ddc9b1aSDarren Reed /* 4587ddc9b1aSDarren Reed * Function: hook_wait_init 4597ddc9b1aSDarren Reed * Returns: None 4607ddc9b1aSDarren Reed * Parameters: waiter(I) - control data structure 4617ddc9b1aSDarren Reed * ownder(I) - pointer to lock that the owner of this 4627ddc9b1aSDarren Reed * waiter uses 4637ddc9b1aSDarren Reed * 4647ddc9b1aSDarren Reed * "owner" gets passed in here so that when we need to call cv_wait, 4657ddc9b1aSDarren Reed * for example in hook_wait_setflag(), we can drop the lock for the 4667ddc9b1aSDarren Reed * next layer out, which is likely to be held in an exclusive manner. 4677ddc9b1aSDarren Reed */ 4687ddc9b1aSDarren Reed void 4697ddc9b1aSDarren Reed hook_wait_init(flagwait_t *waiter, cvwaitlock_t *owner) 4707ddc9b1aSDarren Reed { 4717ddc9b1aSDarren Reed cv_init(&waiter->fw_cv, NULL, CV_DRIVER, NULL); 4727ddc9b1aSDarren Reed mutex_init(&waiter->fw_lock, NULL, MUTEX_DRIVER, NULL); 4737ddc9b1aSDarren Reed waiter->fw_flags = FWF_NONE; 4747ddc9b1aSDarren Reed waiter->fw_owner = owner; 475f4b3ec61Sdh155122 } 476f4b3ec61Sdh155122 477f4b3ec61Sdh155122 /* 4784a9b8375SDarren Reed * Function: hook_stack_init 4794a9b8375SDarren Reed * Returns: void * - pointer to new hook stack structure 4804a9b8375SDarren Reed * Parameters: stackid(I) - identifier for the network instance that owns this 4814a9b8375SDarren Reed * ns(I) - pointer to the network instance data structure 4824a9b8375SDarren Reed * 4834a9b8375SDarren Reed * Allocate and initialize the hook stack instance. This function is not 4844a9b8375SDarren Reed * allowed to fail, so KM_SLEEP is used here when allocating memory. The 4854a9b8375SDarren Reed * value returned is passed back into the shutdown and destroy hooks. 486f4b3ec61Sdh155122 */ 487f4b3ec61Sdh155122 /*ARGSUSED*/ 488f4b3ec61Sdh155122 static void * 489f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns) 490f4b3ec61Sdh155122 { 491f4b3ec61Sdh155122 hook_stack_t *hks; 492f4b3ec61Sdh155122 493f4b3ec61Sdh155122 #ifdef NS_DEBUG 494f4b3ec61Sdh155122 printf("hook_stack_init(stack %d)\n", stackid); 495f4b3ec61Sdh155122 #endif 496f4b3ec61Sdh155122 497f4b3ec61Sdh155122 hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP); 4987ddc9b1aSDarren Reed hks->hks_netstack = ns; 4997ddc9b1aSDarren Reed hks->hks_netstackid = stackid; 500f4b3ec61Sdh155122 5017ddc9b1aSDarren Reed CVW_INIT(&hks->hks_lock); 5027ddc9b1aSDarren Reed TAILQ_INIT(&hks->hks_nhead); 503f4b3ec61Sdh155122 SLIST_INIT(&hks->hks_familylist); 504f4b3ec61Sdh155122 5057ddc9b1aSDarren Reed hook_wait_init(&hks->hks_waiter, &hks->hks_lock); 5067ddc9b1aSDarren Reed 5077ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 5087ddc9b1aSDarren Reed SLIST_INSERT_HEAD(&hook_stacks, hks, hks_entry); 5097ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 5107ddc9b1aSDarren Reed 511f4b3ec61Sdh155122 return (hks); 512f4b3ec61Sdh155122 } 513f4b3ec61Sdh155122 5148ad74188SDarren Reed /* 5154a9b8375SDarren Reed * Function: hook_stack_shutdown 5164a9b8375SDarren Reed * Returns: void 5174a9b8375SDarren Reed * Parameters: stackid(I) - identifier for the network instance that owns this 5184a9b8375SDarren Reed * arg(I) - pointer returned by hook_stack_init 5194a9b8375SDarren Reed * 5208ad74188SDarren Reed * Set the shutdown flag to indicate that we should stop accepting new 5214a9b8375SDarren Reed * register calls as we're now in the cleanup process. The cleanup is a 5224a9b8375SDarren Reed * two stage process and we're not required to free any memory here. 5234a9b8375SDarren Reed * 5244a9b8375SDarren Reed * The curious would wonder why isn't there any code that walks through 5254a9b8375SDarren Reed * all of the data structures and sets the flag(s) there? The answer is 5264a9b8375SDarren Reed * that it is expected that this will happen when the zone shutdown calls 5274a9b8375SDarren Reed * the shutdown callbacks for other modules that they will initiate the 5284a9b8375SDarren Reed * free'ing and shutdown of the hooks themselves. 5298ad74188SDarren Reed */ 5307ddc9b1aSDarren Reed /*ARGSUSED*/ 5317ddc9b1aSDarren Reed static void 5327ddc9b1aSDarren Reed hook_stack_shutdown(netstackid_t stackid, void *arg) 5337ddc9b1aSDarren Reed { 5347ddc9b1aSDarren Reed hook_stack_t *hks = (hook_stack_t *)arg; 5357ddc9b1aSDarren Reed 5367ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 5377ddc9b1aSDarren Reed /* 5387ddc9b1aSDarren Reed * Once this flag gets set to one, no more additions are allowed 5397ddc9b1aSDarren Reed * to any of the structures that make up this stack. 5407ddc9b1aSDarren Reed */ 5417ddc9b1aSDarren Reed hks->hks_shutdown = 1; 5427ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 5437ddc9b1aSDarren Reed } 5447ddc9b1aSDarren Reed 545f4b3ec61Sdh155122 /* 5464a9b8375SDarren Reed * Function: hook_stack_destroy 5474a9b8375SDarren Reed * Returns: void 5484a9b8375SDarren Reed * Parameters: stackid(I) - identifier for the network instance that owns this 5494a9b8375SDarren Reed * arg(I) - pointer returned by hook_stack_init 5504a9b8375SDarren Reed * 551f4b3ec61Sdh155122 * Free the hook stack instance. 5524a9b8375SDarren Reed * 5534a9b8375SDarren Reed * The rationale for the shutdown being lazy (see the comment above for 5544a9b8375SDarren Reed * hook_stack_shutdown) also applies to the destroy being lazy. Only if 5554a9b8375SDarren Reed * the hook_stack_t data structure is unused will it go away. Else it 5564a9b8375SDarren Reed * is left up to the last user of a data structure to actually free it. 557f4b3ec61Sdh155122 */ 558f4b3ec61Sdh155122 /*ARGSUSED*/ 559f4b3ec61Sdh155122 static void 560f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg) 561f4b3ec61Sdh155122 { 562f4b3ec61Sdh155122 hook_stack_t *hks = (hook_stack_t *)arg; 5637ddc9b1aSDarren Reed 5647ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 5657ddc9b1aSDarren Reed hks->hks_shutdown = 2; 5667ddc9b1aSDarren Reed hook_stack_remove(hks); 5677ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 5687ddc9b1aSDarren Reed } 5697ddc9b1aSDarren Reed 5707ddc9b1aSDarren Reed /* 5714a9b8375SDarren Reed * Function: hook_stack_remove 5724a9b8375SDarren Reed * Returns: void 5734a9b8375SDarren Reed * Parameters: hks(I) - pointer to an instance of a hook_stack_t 5744a9b8375SDarren Reed * 5757ddc9b1aSDarren Reed * This function assumes that it is called with hook_stack_lock held. 5767ddc9b1aSDarren Reed * It functions differently to hook_family/event_remove in that it does 5777ddc9b1aSDarren Reed * the checks to see if it can be removed. This difference exists 5787ddc9b1aSDarren Reed * because this structure has nothing higher up that depends on it. 5797ddc9b1aSDarren Reed */ 5807ddc9b1aSDarren Reed static void 5817ddc9b1aSDarren Reed hook_stack_remove(hook_stack_t *hks) 5827ddc9b1aSDarren Reed { 5837ddc9b1aSDarren Reed 5847ddc9b1aSDarren Reed ASSERT(mutex_owned(&hook_stack_lock)); 5857ddc9b1aSDarren Reed 5867ddc9b1aSDarren Reed /* 5877ddc9b1aSDarren Reed * Is the structure still in use? 5887ddc9b1aSDarren Reed */ 5897ddc9b1aSDarren Reed if (!SLIST_EMPTY(&hks->hks_familylist) || 5907ddc9b1aSDarren Reed !TAILQ_EMPTY(&hks->hks_nhead)) 5917ddc9b1aSDarren Reed return; 5927ddc9b1aSDarren Reed 5937ddc9b1aSDarren Reed SLIST_REMOVE(&hook_stacks, hks, hook_stack, hks_entry); 5947ddc9b1aSDarren Reed 5954a9b8375SDarren Reed VERIFY(hook_wait_destroy(&hks->hks_waiter) == 0); 5967ddc9b1aSDarren Reed CVW_DESTROY(&hks->hks_lock); 597f4b3ec61Sdh155122 kmem_free(hks, sizeof (*hks)); 598f4b3ec61Sdh155122 } 599381a2a9aSdr146992 6004a9b8375SDarren Reed /* 6014a9b8375SDarren Reed * Function: hook_stack_get 6024a9b8375SDarren Reed * Returns: hook_stack_t * - NULL if not found, else matching instance 6034a9b8375SDarren Reed * Parameters: stackid(I) - instance id to search for 6044a9b8375SDarren Reed * 6054a9b8375SDarren Reed * Search the list of currently active hook_stack_t structures for one that 6064a9b8375SDarren Reed * has a matching netstackid_t to the value passed in. The linked list can 6074a9b8375SDarren Reed * only ever have at most one match for this value. 6084a9b8375SDarren Reed */ 6097ddc9b1aSDarren Reed static hook_stack_t * 6107ddc9b1aSDarren Reed hook_stack_get(netstackid_t stackid) 6117ddc9b1aSDarren Reed { 6127ddc9b1aSDarren Reed hook_stack_t *hks; 6137ddc9b1aSDarren Reed 6147ddc9b1aSDarren Reed SLIST_FOREACH(hks, &hook_stacks, hks_entry) { 6157ddc9b1aSDarren Reed if (hks->hks_netstackid == stackid) 6167ddc9b1aSDarren Reed break; 6177ddc9b1aSDarren Reed } 6187ddc9b1aSDarren Reed 6197ddc9b1aSDarren Reed return (hks); 6207ddc9b1aSDarren Reed } 6217ddc9b1aSDarren Reed 6227ddc9b1aSDarren Reed /* 6237ddc9b1aSDarren Reed * Function: hook_stack_notify_register 6244a9b8375SDarren Reed * Returns: int - 0 = success, else failure 6257ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 6267ddc9b1aSDarren Reed * callback(I)- function to be called 6277ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 6287ddc9b1aSDarren Reed * 6297ddc9b1aSDarren Reed * If we're not shutting down this instance, append a new function to the 6307ddc9b1aSDarren Reed * list of those to call when a new family of hooks is added to this stack. 6314a9b8375SDarren Reed * If the function can be successfully added to the list of callbacks 6324a9b8375SDarren Reed * activated when there is a change to the stack (addition or removal of 6334a9b8375SDarren Reed * a hook family) then generate a fake HN_REGISTER event by directly 6344a9b8375SDarren Reed * calling the callback with the relevant information for each hook 6354a9b8375SDarren Reed * family that currently exists (and isn't being shutdown.) 6367ddc9b1aSDarren Reed */ 6377ddc9b1aSDarren Reed int 6387ddc9b1aSDarren Reed hook_stack_notify_register(netstackid_t stackid, hook_notify_fn_t callback, 6397ddc9b1aSDarren Reed void *arg) 6407ddc9b1aSDarren Reed { 6414a9b8375SDarren Reed hook_family_int_t *hfi; 6427ddc9b1aSDarren Reed hook_stack_t *hks; 6434a9b8375SDarren Reed boolean_t canrun; 6444a9b8375SDarren Reed char buffer[16]; 6457ddc9b1aSDarren Reed int error; 6467ddc9b1aSDarren Reed 6474a9b8375SDarren Reed ASSERT(callback != NULL); 6484a9b8375SDarren Reed 6494a9b8375SDarren Reed canrun = B_FALSE; 6507ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 6517ddc9b1aSDarren Reed hks = hook_stack_get(stackid); 6527ddc9b1aSDarren Reed if (hks != NULL) { 6537ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 6547ddc9b1aSDarren Reed error = ESHUTDOWN; 6557ddc9b1aSDarren Reed } else { 6564a9b8375SDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 6574a9b8375SDarren Reed canrun = (hook_wait_setflag(&hks->hks_waiter, 6584a9b8375SDarren Reed FWF_ADD_WAIT_MASK, FWF_ADD_WANTED, 6594a9b8375SDarren Reed FWF_ADD_ACTIVE) != -1); 6604a9b8375SDarren Reed error = hook_notify_register(&hks->hks_nhead, 6614a9b8375SDarren Reed callback, arg); 6624a9b8375SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 6637ddc9b1aSDarren Reed } 6647ddc9b1aSDarren Reed } else { 6657ddc9b1aSDarren Reed error = ESRCH; 6667ddc9b1aSDarren Reed } 6677ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 6687ddc9b1aSDarren Reed 6694a9b8375SDarren Reed if (error == 0 && canrun) { 6704a9b8375SDarren Reed /* 6714a9b8375SDarren Reed * Generate fake register event for callback that 6724a9b8375SDarren Reed * is being added, letting it know everything that 6734a9b8375SDarren Reed * already exists. 6744a9b8375SDarren Reed */ 6754a9b8375SDarren Reed (void) snprintf(buffer, sizeof (buffer), "%u", 6764a9b8375SDarren Reed hks->hks_netstackid); 6774a9b8375SDarren Reed 6784a9b8375SDarren Reed SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 6794a9b8375SDarren Reed if (hfi->hfi_condemned || hfi->hfi_shutdown) 6804a9b8375SDarren Reed continue; 6814a9b8375SDarren Reed callback(HN_REGISTER, arg, buffer, NULL, 6824a9b8375SDarren Reed hfi->hfi_family.hf_name); 6834a9b8375SDarren Reed } 6844a9b8375SDarren Reed } 6854a9b8375SDarren Reed 6864a9b8375SDarren Reed if (canrun) 6874a9b8375SDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE); 6884a9b8375SDarren Reed 6897ddc9b1aSDarren Reed return (error); 6907ddc9b1aSDarren Reed } 6917ddc9b1aSDarren Reed 6927ddc9b1aSDarren Reed /* 6937ddc9b1aSDarren Reed * Function: hook_stack_notify_unregister 6944a9b8375SDarren Reed * Returns: int - 0 = success, else failure 6957ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 6967ddc9b1aSDarren Reed * callback(I) - function to be called 6977ddc9b1aSDarren Reed * 6987ddc9b1aSDarren Reed * Attempt to remove a registered function from a hook stack's list of 6997ddc9b1aSDarren Reed * callbacks to activiate when protocols are added/deleted. 7004a9b8375SDarren Reed * As with hook_stack_notify_register, if all things are going well then 7014a9b8375SDarren Reed * a fake unregister event is delivered to the callback being removed 7024a9b8375SDarren Reed * for each hook family that presently exists. 7037ddc9b1aSDarren Reed */ 7047ddc9b1aSDarren Reed int 7057ddc9b1aSDarren Reed hook_stack_notify_unregister(netstackid_t stackid, hook_notify_fn_t callback) 7067ddc9b1aSDarren Reed { 7074a9b8375SDarren Reed hook_family_int_t *hfi; 7087ddc9b1aSDarren Reed hook_stack_t *hks; 7094a9b8375SDarren Reed boolean_t canrun; 7104a9b8375SDarren Reed char buffer[16]; 7114a9b8375SDarren Reed void *arg; 7127ddc9b1aSDarren Reed int error; 7137ddc9b1aSDarren Reed 7147ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 7157ddc9b1aSDarren Reed hks = hook_stack_get(stackid); 7167ddc9b1aSDarren Reed if (hks != NULL) { 7174a9b8375SDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 7184a9b8375SDarren Reed canrun = (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK, 7194a9b8375SDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1); 7204a9b8375SDarren Reed 7214a9b8375SDarren Reed error = hook_notify_unregister(&hks->hks_nhead, callback, &arg); 7224a9b8375SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 7237ddc9b1aSDarren Reed } else { 7247ddc9b1aSDarren Reed error = ESRCH; 7257ddc9b1aSDarren Reed } 7267ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 7277ddc9b1aSDarren Reed 7284a9b8375SDarren Reed if (error == 0) { 7294a9b8375SDarren Reed if (canrun) { 7304a9b8375SDarren Reed /* 7314a9b8375SDarren Reed * Generate fake unregister event for callback that 7324a9b8375SDarren Reed * is being removed, letting it know everything that 7334a9b8375SDarren Reed * currently exists is now "disappearing." 7344a9b8375SDarren Reed */ 7354a9b8375SDarren Reed (void) snprintf(buffer, sizeof (buffer), "%u", 7364a9b8375SDarren Reed hks->hks_netstackid); 7374a9b8375SDarren Reed 7384a9b8375SDarren Reed SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 7394a9b8375SDarren Reed callback(HN_UNREGISTER, arg, buffer, NULL, 7404a9b8375SDarren Reed hfi->hfi_family.hf_name); 7414a9b8375SDarren Reed } 7424a9b8375SDarren Reed 7434a9b8375SDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE); 7444a9b8375SDarren Reed } 7454a9b8375SDarren Reed 7464a9b8375SDarren Reed mutex_enter(&hook_stack_lock); 7474a9b8375SDarren Reed hks = hook_stack_get(stackid); 7484a9b8375SDarren Reed if ((error == 0) && (hks->hks_shutdown == 2)) 7494a9b8375SDarren Reed hook_stack_remove(hks); 7504a9b8375SDarren Reed mutex_exit(&hook_stack_lock); 7514a9b8375SDarren Reed } 7524a9b8375SDarren Reed 7537ddc9b1aSDarren Reed return (error); 7547ddc9b1aSDarren Reed } 7557ddc9b1aSDarren Reed 7567ddc9b1aSDarren Reed /* 7577ddc9b1aSDarren Reed * Function: hook_stack_notify_run 7587ddc9b1aSDarren Reed * Returns: None 7597ddc9b1aSDarren Reed * Parameters: hks(I) - hook stack pointer to execute callbacks for 7607ddc9b1aSDarren Reed * name(I) - name of a hook family 7617ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 7627ddc9b1aSDarren Reed * 7637ddc9b1aSDarren Reed * Run through the list of callbacks on the hook stack to be called when 7647ddc9b1aSDarren Reed * a new hook family is added 7657ddc9b1aSDarren Reed * 7664a9b8375SDarren Reed * As hook_notify_run() expects 3 names, one for the family that is associated 7674a9b8375SDarren Reed * with the cmd (HN_REGISTER or HN_UNREGISTER), one for the event and one 7684a9b8375SDarren Reed * for the object being introduced and we really only have one name (that 7694a9b8375SDarren Reed * of the new hook family), fake the hook stack's name by converting the 7704a9b8375SDarren Reed * integer to a string and for the event just pass NULL. 7717ddc9b1aSDarren Reed */ 7727ddc9b1aSDarren Reed static void 7737ddc9b1aSDarren Reed hook_stack_notify_run(hook_stack_t *hks, char *name, 7747ddc9b1aSDarren Reed hook_notify_cmd_t cmd) 7757ddc9b1aSDarren Reed { 7767ddc9b1aSDarren Reed char buffer[16]; 7777ddc9b1aSDarren Reed 7784a9b8375SDarren Reed ASSERT(hks != NULL); 7794a9b8375SDarren Reed ASSERT(name != NULL); 7804a9b8375SDarren Reed 7817ddc9b1aSDarren Reed (void) snprintf(buffer, sizeof (buffer), "%u", hks->hks_netstackid); 7827ddc9b1aSDarren Reed 7837ddc9b1aSDarren Reed hook_notify_run(&hks->hks_nhead, buffer, NULL, name, cmd); 7847ddc9b1aSDarren Reed } 7857ddc9b1aSDarren Reed 786381a2a9aSdr146992 /* 787381a2a9aSdr146992 * Function: hook_run 788381a2a9aSdr146992 * Returns: int - return value according to callback func 789381a2a9aSdr146992 * Parameters: token(I) - event pointer 790381a2a9aSdr146992 * info(I) - message 791381a2a9aSdr146992 * 792381a2a9aSdr146992 * Run hooks for specific provider. The hooks registered are stepped through 793381a2a9aSdr146992 * until either the end of the list is reached or a hook function returns a 794381a2a9aSdr146992 * non-zero value. If a non-zero value is returned from a hook function, we 795381a2a9aSdr146992 * return that value back to our caller. By design, a hook function can be 796381a2a9aSdr146992 * called more than once, simultaneously. 797381a2a9aSdr146992 */ 798381a2a9aSdr146992 int 7997ddc9b1aSDarren Reed hook_run(hook_family_int_t *hfi, hook_event_token_t token, hook_data_t info) 800381a2a9aSdr146992 { 801381a2a9aSdr146992 hook_event_int_t *hei; 8027ddc9b1aSDarren Reed hook_int_t *hi; 803381a2a9aSdr146992 int rval = 0; 804381a2a9aSdr146992 805381a2a9aSdr146992 ASSERT(token != NULL); 806381a2a9aSdr146992 807381a2a9aSdr146992 hei = (hook_event_int_t *)token; 808381a2a9aSdr146992 DTRACE_PROBE2(hook__run__start, 809381a2a9aSdr146992 hook_event_token_t, token, 810381a2a9aSdr146992 hook_data_t, info); 811381a2a9aSdr146992 8127ddc9b1aSDarren Reed /* 8134a9b8375SDarren Reed * If we consider that this function is only called from within the 8144a9b8375SDarren Reed * stack while an instance is currently active, 8157ddc9b1aSDarren Reed */ 8167ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 817381a2a9aSdr146992 818381a2a9aSdr146992 TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) { 819381a2a9aSdr146992 ASSERT(hi->hi_hook.h_func != NULL); 820381a2a9aSdr146992 DTRACE_PROBE3(hook__func__start, 821381a2a9aSdr146992 hook_event_token_t, token, 822381a2a9aSdr146992 hook_data_t, info, 823381a2a9aSdr146992 hook_int_t *, hi); 8247ddc9b1aSDarren Reed rval = (*hi->hi_hook.h_func)(token, info, hi->hi_hook.h_arg); 825381a2a9aSdr146992 DTRACE_PROBE4(hook__func__end, 826381a2a9aSdr146992 hook_event_token_t, token, 827381a2a9aSdr146992 hook_data_t, info, 828381a2a9aSdr146992 hook_int_t *, hi, 829381a2a9aSdr146992 int, rval); 8307ddc9b1aSDarren Reed hi->hi_kstats.hook_hits.value.ui64++; 831381a2a9aSdr146992 if (rval != 0) 832381a2a9aSdr146992 break; 833381a2a9aSdr146992 } 834381a2a9aSdr146992 8357ddc9b1aSDarren Reed hei->hei_kstats.events.value.ui64++; 8367ddc9b1aSDarren Reed 8377ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 838381a2a9aSdr146992 839381a2a9aSdr146992 DTRACE_PROBE3(hook__run__end, 840381a2a9aSdr146992 hook_event_token_t, token, 841381a2a9aSdr146992 hook_data_t, info, 842381a2a9aSdr146992 hook_int_t *, hi); 843381a2a9aSdr146992 844381a2a9aSdr146992 return (rval); 845381a2a9aSdr146992 } 846381a2a9aSdr146992 847381a2a9aSdr146992 /* 848381a2a9aSdr146992 * Function: hook_family_add 849381a2a9aSdr146992 * Returns: internal family pointer - NULL = Fail 850381a2a9aSdr146992 * Parameters: hf(I) - family pointer 8514a9b8375SDarren Reed * hks(I) - pointer to an instance of a hook_stack_t 8524a9b8375SDarren Reed * store(O) - where returned pointer will be stored 853381a2a9aSdr146992 * 8544a9b8375SDarren Reed * Add new family to the family list. The requirements for the addition to 8554a9b8375SDarren Reed * succeed are that the family name must not already be registered and that 8564a9b8375SDarren Reed * the hook stack is not being shutdown. 8574a9b8375SDarren Reed * If store is non-NULL, it is expected to be a pointer to the same variable 8584a9b8375SDarren Reed * that is awaiting to be assigned the return value of this function. 8594a9b8375SDarren Reed * In its current use, the returned value is assigned to netd_hooks in 8604a9b8375SDarren Reed * net_family_register. The use of "store" allows the return value to be 8614a9b8375SDarren Reed * used before this function returns. How can this happen? Through the 8624a9b8375SDarren Reed * callbacks that can be activated at the bottom of this function, when 8634a9b8375SDarren Reed * hook_stack_notify_run is called. 864381a2a9aSdr146992 */ 865381a2a9aSdr146992 hook_family_int_t * 8664a9b8375SDarren Reed hook_family_add(hook_family_t *hf, hook_stack_t *hks, void **store) 867381a2a9aSdr146992 { 868381a2a9aSdr146992 hook_family_int_t *hfi, *new; 869381a2a9aSdr146992 870381a2a9aSdr146992 ASSERT(hf != NULL); 871381a2a9aSdr146992 ASSERT(hf->hf_name != NULL); 872381a2a9aSdr146992 873381a2a9aSdr146992 new = hook_family_copy(hf); 874381a2a9aSdr146992 if (new == NULL) 875381a2a9aSdr146992 return (NULL); 876381a2a9aSdr146992 8777ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 8787ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 8797ddc9b1aSDarren Reed 8807ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 8817ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 8827ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 8837ddc9b1aSDarren Reed hook_family_free(new, NULL); 8847ddc9b1aSDarren Reed return (NULL); 8857ddc9b1aSDarren Reed } 886381a2a9aSdr146992 887381a2a9aSdr146992 /* search family list */ 888f4b3ec61Sdh155122 hfi = hook_family_find(hf->hf_name, hks); 889381a2a9aSdr146992 if (hfi != NULL) { 8907ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 8917ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 8927ddc9b1aSDarren Reed hook_family_free(new, NULL); 893381a2a9aSdr146992 return (NULL); 894381a2a9aSdr146992 } 895381a2a9aSdr146992 8964a9b8375SDarren Reed /* 8974a9b8375SDarren Reed * Try and set the FWF_ADD_ACTIVE flag so that we can drop all the 8984a9b8375SDarren Reed * lock further down when calling all of the functions registered 8994a9b8375SDarren Reed * for notification when a new hook family is added. 9004a9b8375SDarren Reed */ 9014a9b8375SDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_ADD_WAIT_MASK, 9027ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 9037ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 9047ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 9057ddc9b1aSDarren Reed hook_family_free(new, NULL); 9067ddc9b1aSDarren Reed return (NULL); 9077ddc9b1aSDarren Reed } 9087ddc9b1aSDarren Reed 9097ddc9b1aSDarren Reed CVW_INIT(&new->hfi_lock); 9107ddc9b1aSDarren Reed SLIST_INIT(&new->hfi_head); 9117ddc9b1aSDarren Reed TAILQ_INIT(&new->hfi_nhead); 9127ddc9b1aSDarren Reed 9137ddc9b1aSDarren Reed hook_wait_init(&new->hfi_waiter, &new->hfi_lock); 9147ddc9b1aSDarren Reed 9157ddc9b1aSDarren Reed new->hfi_stack = hks; 9164a9b8375SDarren Reed if (store != NULL) 9174a9b8375SDarren Reed *store = new; 918381a2a9aSdr146992 919f4b3ec61Sdh155122 /* Add to family list head */ 920f4b3ec61Sdh155122 SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry); 921f4b3ec61Sdh155122 9227ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 9237ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 9247ddc9b1aSDarren Reed 9257ddc9b1aSDarren Reed hook_stack_notify_run(hks, hf->hf_name, HN_REGISTER); 9267ddc9b1aSDarren Reed 9277ddc9b1aSDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_ADD_ACTIVE); 9287ddc9b1aSDarren Reed 929381a2a9aSdr146992 return (new); 930381a2a9aSdr146992 } 931381a2a9aSdr146992 932381a2a9aSdr146992 /* 933381a2a9aSdr146992 * Function: hook_family_remove 9344a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 935381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 936381a2a9aSdr146992 * 9377ddc9b1aSDarren Reed * Remove family from family list. This function has been designed to be 9387ddc9b1aSDarren Reed * called once and once only per hook_family_int_t. Thus when cleaning up 9397ddc9b1aSDarren Reed * this structure as an orphan, callers should only call hook_family_free. 940381a2a9aSdr146992 */ 941381a2a9aSdr146992 int 942381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi) 943381a2a9aSdr146992 { 944f4b3ec61Sdh155122 hook_stack_t *hks; 9458ad74188SDarren Reed boolean_t notifydone; 946381a2a9aSdr146992 947381a2a9aSdr146992 ASSERT(hfi != NULL); 9487ddc9b1aSDarren Reed hks = hfi->hfi_stack; 949381a2a9aSdr146992 9504a9b8375SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 9514a9b8375SDarren Reed notifydone = hfi->hfi_shutdown; 9524a9b8375SDarren Reed hfi->hfi_shutdown = B_TRUE; 9534a9b8375SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 9544a9b8375SDarren Reed 9557ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 956381a2a9aSdr146992 9574a9b8375SDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK, 9587ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 9597ddc9b1aSDarren Reed /* 9607ddc9b1aSDarren Reed * If we're trying to destroy the hook_stack_t... 9617ddc9b1aSDarren Reed */ 9628ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 9637ddc9b1aSDarren Reed return (ENXIO); 964381a2a9aSdr146992 } 965381a2a9aSdr146992 9667ddc9b1aSDarren Reed /* 9677ddc9b1aSDarren Reed * Check if the family is in use by the presence of either events 9687ddc9b1aSDarren Reed * or notify callbacks on the hook family. 9697ddc9b1aSDarren Reed */ 9707ddc9b1aSDarren Reed if (!SLIST_EMPTY(&hfi->hfi_head) || !TAILQ_EMPTY(&hfi->hfi_nhead)) { 9717ddc9b1aSDarren Reed hfi->hfi_condemned = B_TRUE; 9727ddc9b1aSDarren Reed } else { 9734a9b8375SDarren Reed VERIFY(hook_wait_destroy(&hfi->hfi_waiter) == 0); 9747ddc9b1aSDarren Reed /* 9757ddc9b1aSDarren Reed * Although hfi_condemned = B_FALSE is implied from creation, 9767ddc9b1aSDarren Reed * putting a comment here inside the else upsets lint. 9777ddc9b1aSDarren Reed */ 9787ddc9b1aSDarren Reed hfi->hfi_condemned = B_FALSE; 9797ddc9b1aSDarren Reed } 9807ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 9817ddc9b1aSDarren Reed 9828ad74188SDarren Reed if (!notifydone) 9838ad74188SDarren Reed hook_stack_notify_run(hks, hfi->hfi_family.hf_name, 9848ad74188SDarren Reed HN_UNREGISTER); 9857ddc9b1aSDarren Reed 9867ddc9b1aSDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE); 9877ddc9b1aSDarren Reed 9887ddc9b1aSDarren Reed /* 9897ddc9b1aSDarren Reed * If we don't have to wait for anything else to disappear from this 9907ddc9b1aSDarren Reed * structure then we can free it up. 9917ddc9b1aSDarren Reed */ 9927ddc9b1aSDarren Reed if (!hfi->hfi_condemned) 9937ddc9b1aSDarren Reed hook_family_free(hfi, hks); 994381a2a9aSdr146992 995381a2a9aSdr146992 return (0); 996381a2a9aSdr146992 } 997381a2a9aSdr146992 998381a2a9aSdr146992 999381a2a9aSdr146992 /* 10007ddc9b1aSDarren Reed * Function: hook_family_free 10017ddc9b1aSDarren Reed * Returns: None 10027ddc9b1aSDarren Reed * Parameters: hfi(I) - internal family pointer 10037ddc9b1aSDarren Reed * 10047ddc9b1aSDarren Reed * Free alloc memory for family 10057ddc9b1aSDarren Reed */ 10067ddc9b1aSDarren Reed static void 10077ddc9b1aSDarren Reed hook_family_free(hook_family_int_t *hfi, hook_stack_t *hks) 10087ddc9b1aSDarren Reed { 10097ddc9b1aSDarren Reed 10107ddc9b1aSDarren Reed /* 10117ddc9b1aSDarren Reed * This lock gives us possession of the hks pointer after the 10127ddc9b1aSDarren Reed * SLIST_REMOVE, for which it is not needed, when hks_shutdown 10137ddc9b1aSDarren Reed * is checked and hook_stack_remove called. 10147ddc9b1aSDarren Reed */ 10157ddc9b1aSDarren Reed mutex_enter(&hook_stack_lock); 10167ddc9b1aSDarren Reed 10177ddc9b1aSDarren Reed ASSERT(hfi != NULL); 10187ddc9b1aSDarren Reed 10197ddc9b1aSDarren Reed if (hks != NULL) { 10207ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 10217ddc9b1aSDarren Reed /* Remove from family list */ 10227ddc9b1aSDarren Reed SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, 10237ddc9b1aSDarren Reed hfi_entry); 10247ddc9b1aSDarren Reed 10257ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 10267ddc9b1aSDarren Reed } 10277ddc9b1aSDarren Reed 10287ddc9b1aSDarren Reed /* Free name space */ 10297ddc9b1aSDarren Reed if (hfi->hfi_family.hf_name != NULL) { 10307ddc9b1aSDarren Reed kmem_free(hfi->hfi_family.hf_name, 10317ddc9b1aSDarren Reed strlen(hfi->hfi_family.hf_name) + 1); 10327ddc9b1aSDarren Reed } 10337ddc9b1aSDarren Reed 10347ddc9b1aSDarren Reed /* Free container */ 10357ddc9b1aSDarren Reed kmem_free(hfi, sizeof (*hfi)); 10367ddc9b1aSDarren Reed 10377ddc9b1aSDarren Reed if (hks->hks_shutdown == 2) 10387ddc9b1aSDarren Reed hook_stack_remove(hks); 10397ddc9b1aSDarren Reed 10407ddc9b1aSDarren Reed mutex_exit(&hook_stack_lock); 10417ddc9b1aSDarren Reed } 10427ddc9b1aSDarren Reed 10438ad74188SDarren Reed /* 10448ad74188SDarren Reed * Function: hook_family_shutdown 10454a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 10468ad74188SDarren Reed * Parameters: hfi(I) - internal family pointer 10478ad74188SDarren Reed * 10488ad74188SDarren Reed * As an alternative to removing a family, we may desire to just generate 10498ad74188SDarren Reed * a series of callbacks to indicate that we will be going away in the 10508ad74188SDarren Reed * future. The hfi_condemned flag isn't set because we aren't trying to 10518ad74188SDarren Reed * remove the structure. 10528ad74188SDarren Reed */ 10538ad74188SDarren Reed int 10548ad74188SDarren Reed hook_family_shutdown(hook_family_int_t *hfi) 10558ad74188SDarren Reed { 10568ad74188SDarren Reed hook_stack_t *hks; 10578ad74188SDarren Reed boolean_t notifydone; 10588ad74188SDarren Reed 10598ad74188SDarren Reed ASSERT(hfi != NULL); 10608ad74188SDarren Reed hks = hfi->hfi_stack; 10618ad74188SDarren Reed 10624a9b8375SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 10634a9b8375SDarren Reed notifydone = hfi->hfi_shutdown; 10644a9b8375SDarren Reed hfi->hfi_shutdown = B_TRUE; 10654a9b8375SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 10664a9b8375SDarren Reed 10678ad74188SDarren Reed CVW_ENTER_WRITE(&hks->hks_lock); 10688ad74188SDarren Reed 10694a9b8375SDarren Reed if (hook_wait_setflag(&hks->hks_waiter, FWF_DEL_WAIT_MASK, 10708ad74188SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 10718ad74188SDarren Reed /* 10728ad74188SDarren Reed * If we're trying to destroy the hook_stack_t... 10738ad74188SDarren Reed */ 10748ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 10758ad74188SDarren Reed return (ENXIO); 10768ad74188SDarren Reed } 10778ad74188SDarren Reed 10788ad74188SDarren Reed CVW_EXIT_WRITE(&hks->hks_lock); 10798ad74188SDarren Reed 10808ad74188SDarren Reed if (!notifydone) 10818ad74188SDarren Reed hook_stack_notify_run(hks, hfi->hfi_family.hf_name, 10828ad74188SDarren Reed HN_UNREGISTER); 10838ad74188SDarren Reed 10848ad74188SDarren Reed hook_wait_unsetflag(&hks->hks_waiter, FWF_DEL_ACTIVE); 10858ad74188SDarren Reed 10868ad74188SDarren Reed return (0); 10878ad74188SDarren Reed } 10887ddc9b1aSDarren Reed 10897ddc9b1aSDarren Reed /* 1090381a2a9aSdr146992 * Function: hook_family_copy 1091381a2a9aSdr146992 * Returns: internal family pointer - NULL = Failed 1092381a2a9aSdr146992 * Parameters: src(I) - family pointer 1093381a2a9aSdr146992 * 1094381a2a9aSdr146992 * Allocate internal family block and duplicate incoming family 1095381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 1096381a2a9aSdr146992 */ 1097381a2a9aSdr146992 static hook_family_int_t * 1098381a2a9aSdr146992 hook_family_copy(hook_family_t *src) 1099381a2a9aSdr146992 { 1100381a2a9aSdr146992 hook_family_int_t *new; 1101381a2a9aSdr146992 hook_family_t *dst; 1102381a2a9aSdr146992 1103381a2a9aSdr146992 ASSERT(src != NULL); 1104381a2a9aSdr146992 ASSERT(src->hf_name != NULL); 1105381a2a9aSdr146992 1106381a2a9aSdr146992 new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 1107381a2a9aSdr146992 1108381a2a9aSdr146992 /* Copy body */ 1109381a2a9aSdr146992 dst = &new->hfi_family; 1110381a2a9aSdr146992 *dst = *src; 1111381a2a9aSdr146992 11127ddc9b1aSDarren Reed SLIST_INIT(&new->hfi_head); 11137ddc9b1aSDarren Reed TAILQ_INIT(&new->hfi_nhead); 11147ddc9b1aSDarren Reed 1115381a2a9aSdr146992 /* Copy name */ 1116381a2a9aSdr146992 dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP); 1117381a2a9aSdr146992 (void) strcpy(dst->hf_name, src->hf_name); 1118381a2a9aSdr146992 1119381a2a9aSdr146992 return (new); 1120381a2a9aSdr146992 } 1121381a2a9aSdr146992 1122381a2a9aSdr146992 /* 11237ddc9b1aSDarren Reed * Function: hook_family_find 1124381a2a9aSdr146992 * Returns: internal family pointer - NULL = Not match 1125381a2a9aSdr146992 * Parameters: family(I) - family name string 1126381a2a9aSdr146992 * 1127381a2a9aSdr146992 * Search family list with family name 11287ddc9b1aSDarren Reed * A lock on hfi_lock must be held when called. 1129381a2a9aSdr146992 */ 1130381a2a9aSdr146992 static hook_family_int_t * 1131f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks) 1132381a2a9aSdr146992 { 1133381a2a9aSdr146992 hook_family_int_t *hfi = NULL; 1134381a2a9aSdr146992 1135381a2a9aSdr146992 ASSERT(family != NULL); 1136381a2a9aSdr146992 1137f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 1138381a2a9aSdr146992 if (strcmp(hfi->hfi_family.hf_name, family) == 0) 1139381a2a9aSdr146992 break; 1140381a2a9aSdr146992 } 1141381a2a9aSdr146992 return (hfi); 1142381a2a9aSdr146992 } 1143381a2a9aSdr146992 11447ddc9b1aSDarren Reed /* 11457ddc9b1aSDarren Reed * Function: hook_family_notify_register 11464a9b8375SDarren Reed * Returns: int - 0 = success, else failure 11477ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 11487ddc9b1aSDarren Reed * callback(I) - function to be called 11497ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 11507ddc9b1aSDarren Reed * 11517ddc9b1aSDarren Reed * So long as this hook stack isn't being shut down, register a new 11527ddc9b1aSDarren Reed * callback to be activated each time a new event is added to this 11537ddc9b1aSDarren Reed * family. 11547ddc9b1aSDarren Reed * 11557ddc9b1aSDarren Reed * To call this function we must have an active handle in use on the family, 11567ddc9b1aSDarren Reed * so if we take this into account, then neither the hook_family_int_t nor 11577ddc9b1aSDarren Reed * the hook_stack_t that owns it can disappear. We have to put some trust 11587ddc9b1aSDarren Reed * in the callers to be properly synchronised... 11597ddc9b1aSDarren Reed * 11607ddc9b1aSDarren Reed * Holding hks_lock is required to provide synchronisation for hks_shutdown. 11617ddc9b1aSDarren Reed */ 11627ddc9b1aSDarren Reed int 11637ddc9b1aSDarren Reed hook_family_notify_register(hook_family_int_t *hfi, 11647ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg) 11657ddc9b1aSDarren Reed { 11664a9b8375SDarren Reed hook_event_int_t *hei; 11677ddc9b1aSDarren Reed hook_stack_t *hks; 11684a9b8375SDarren Reed boolean_t canrun; 11697ddc9b1aSDarren Reed int error; 11707ddc9b1aSDarren Reed 11714a9b8375SDarren Reed ASSERT(hfi != NULL); 11724a9b8375SDarren Reed canrun = B_FALSE; 11737ddc9b1aSDarren Reed hks = hfi->hfi_stack; 11747ddc9b1aSDarren Reed 11757ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 11767ddc9b1aSDarren Reed 11778ad74188SDarren Reed if ((hfi->hfi_stack->hks_shutdown != 0) || 11788ad74188SDarren Reed hfi->hfi_condemned || hfi->hfi_shutdown) { 11797ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 11807ddc9b1aSDarren Reed return (ESHUTDOWN); 11817ddc9b1aSDarren Reed } 11827ddc9b1aSDarren Reed 11834a9b8375SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 11844a9b8375SDarren Reed canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK, 11854a9b8375SDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1); 11864a9b8375SDarren Reed error = hook_notify_register(&hfi->hfi_nhead, callback, arg); 11877ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 11884a9b8375SDarren Reed 11897ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 11907ddc9b1aSDarren Reed 11914a9b8375SDarren Reed if (error == 0 && canrun) { 11924a9b8375SDarren Reed SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 11934a9b8375SDarren Reed callback(HN_REGISTER, arg, 11944a9b8375SDarren Reed hfi->hfi_family.hf_name, NULL, 11954a9b8375SDarren Reed hei->hei_event->he_name); 11964a9b8375SDarren Reed } 11974a9b8375SDarren Reed } 11984a9b8375SDarren Reed 11994a9b8375SDarren Reed if (canrun) 12004a9b8375SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE); 12014a9b8375SDarren Reed 12027ddc9b1aSDarren Reed return (error); 12037ddc9b1aSDarren Reed } 1204381a2a9aSdr146992 1205381a2a9aSdr146992 /* 12067ddc9b1aSDarren Reed * Function: hook_family_notify_unregister 12074a9b8375SDarren Reed * Returns: int - 0 = success, else failure 12087ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 12097ddc9b1aSDarren Reed * callback(I) - function to be called 1210381a2a9aSdr146992 * 12117ddc9b1aSDarren Reed * Remove a callback from the list of those executed when a new event is 12124a9b8375SDarren Reed * added to a hook family. If the family is not in the process of being 12134a9b8375SDarren Reed * destroyed then simulate an unregister callback for each event that is 12144a9b8375SDarren Reed * on the family. This pairs up with the hook_family_notify_register 12154a9b8375SDarren Reed * action that simulates register events. 12164a9b8375SDarren Reed * The order of what happens here is important and goes like this. 12174a9b8375SDarren Reed * 1) Remove the callback from the list of functions to be called as part 12184a9b8375SDarren Reed * of the notify operation when an event is added or removed from the 12194a9b8375SDarren Reed * hook family. 12204a9b8375SDarren Reed * 2) If the hook_family_int_t structure is on death row (free_family will 12214a9b8375SDarren Reed * be set to true) then there's nothing else to do than let it be free'd. 12224a9b8375SDarren Reed * 3) If the structure isn't about to die, mark it up as being busy using 12234a9b8375SDarren Reed * hook_wait_setflag and then drop the lock so the loop can be run. 12244a9b8375SDarren Reed * 4) if hook_wait_setflag was successful, tell all of the notify callback 12254a9b8375SDarren Reed * functions that this family has been unregistered. 12264a9b8375SDarren Reed * 5) Cleanup 1227381a2a9aSdr146992 */ 12287ddc9b1aSDarren Reed int 12297ddc9b1aSDarren Reed hook_family_notify_unregister(hook_family_int_t *hfi, 12307ddc9b1aSDarren Reed hook_notify_fn_t callback) 1231381a2a9aSdr146992 { 12324a9b8375SDarren Reed hook_event_int_t *hei; 12337ddc9b1aSDarren Reed boolean_t free_family; 12344a9b8375SDarren Reed boolean_t canrun; 12357ddc9b1aSDarren Reed int error; 12364a9b8375SDarren Reed void *arg; 12374a9b8375SDarren Reed 12384a9b8375SDarren Reed canrun = B_FALSE; 1239381a2a9aSdr146992 12407ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 12417ddc9b1aSDarren Reed 12424a9b8375SDarren Reed (void) hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK, 12434a9b8375SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE); 12444a9b8375SDarren Reed 12454a9b8375SDarren Reed error = hook_notify_unregister(&hfi->hfi_nhead, callback, &arg); 12464a9b8375SDarren Reed 12474a9b8375SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 12487ddc9b1aSDarren Reed 12497ddc9b1aSDarren Reed /* 12507ddc9b1aSDarren Reed * If hook_family_remove has been called but the structure was still 12517ddc9b1aSDarren Reed * "busy" ... but we might have just made it "unbusy"... 12527ddc9b1aSDarren Reed */ 12537ddc9b1aSDarren Reed if ((error == 0) && hfi->hfi_condemned && 12547ddc9b1aSDarren Reed SLIST_EMPTY(&hfi->hfi_head) && TAILQ_EMPTY(&hfi->hfi_nhead)) { 12557ddc9b1aSDarren Reed free_family = B_TRUE; 12567ddc9b1aSDarren Reed } else { 12577ddc9b1aSDarren Reed free_family = B_FALSE; 1258381a2a9aSdr146992 } 1259381a2a9aSdr146992 12604a9b8375SDarren Reed if (error == 0 && !free_family) { 12614a9b8375SDarren Reed canrun = (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK, 12624a9b8375SDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1); 12634a9b8375SDarren Reed } 12644a9b8375SDarren Reed 12657ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1266381a2a9aSdr146992 12674a9b8375SDarren Reed if (canrun) { 12684a9b8375SDarren Reed SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 12694a9b8375SDarren Reed callback(HN_UNREGISTER, arg, 12704a9b8375SDarren Reed hfi->hfi_family.hf_name, NULL, 12714a9b8375SDarren Reed hei->hei_event->he_name); 12724a9b8375SDarren Reed } 12734a9b8375SDarren Reed 12744a9b8375SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE); 12754a9b8375SDarren Reed } else if (free_family) { 12767ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 12774a9b8375SDarren Reed } 12787ddc9b1aSDarren Reed 12797ddc9b1aSDarren Reed return (error); 12807ddc9b1aSDarren Reed } 1281381a2a9aSdr146992 1282381a2a9aSdr146992 /* 1283381a2a9aSdr146992 * Function: hook_event_add 1284381a2a9aSdr146992 * Returns: internal event pointer - NULL = Fail 1285381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1286381a2a9aSdr146992 * he(I) - event pointer 1287381a2a9aSdr146992 * 1288381a2a9aSdr146992 * Add new event to event list on specific family. 1289381a2a9aSdr146992 * This function can fail to return successfully if (1) it cannot allocate 1290381a2a9aSdr146992 * enough memory for its own internal data structures, (2) the event has 1291381a2a9aSdr146992 * already been registered (for any hook family.) 1292381a2a9aSdr146992 */ 1293381a2a9aSdr146992 hook_event_int_t * 1294381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he) 1295381a2a9aSdr146992 { 1296381a2a9aSdr146992 hook_event_int_t *hei, *new; 12977ddc9b1aSDarren Reed hook_stack_t *hks; 1298381a2a9aSdr146992 1299381a2a9aSdr146992 ASSERT(hfi != NULL); 1300381a2a9aSdr146992 ASSERT(he != NULL); 1301381a2a9aSdr146992 ASSERT(he->he_name != NULL); 1302381a2a9aSdr146992 1303381a2a9aSdr146992 new = hook_event_copy(he); 1304381a2a9aSdr146992 if (new == NULL) 1305381a2a9aSdr146992 return (NULL); 1306381a2a9aSdr146992 13077ddc9b1aSDarren Reed hks = hfi->hfi_stack; 13087ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 13097ddc9b1aSDarren Reed 13107ddc9b1aSDarren Reed hks = hfi->hfi_stack; 13117ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 13127ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 13137ddc9b1aSDarren Reed hook_event_free(new, NULL); 13147ddc9b1aSDarren Reed return (NULL); 13157ddc9b1aSDarren Reed } 1316381a2a9aSdr146992 1317381a2a9aSdr146992 /* Check whether this event pointer is already registered */ 1318f4b3ec61Sdh155122 hei = hook_event_checkdup(he, hks); 1319381a2a9aSdr146992 if (hei != NULL) { 13207ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 13217ddc9b1aSDarren Reed hook_event_free(new, NULL); 1322381a2a9aSdr146992 return (NULL); 1323381a2a9aSdr146992 } 1324381a2a9aSdr146992 13257ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 13267ddc9b1aSDarren Reed 13278ad74188SDarren Reed if (hfi->hfi_condemned || hfi->hfi_shutdown) { 13287ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 13297ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 13307ddc9b1aSDarren Reed hook_event_free(new, NULL); 13317ddc9b1aSDarren Reed return (NULL); 13327ddc9b1aSDarren Reed } 13334a9b8375SDarren Reed CVW_EXIT_READ(&hks->hks_lock); 13347ddc9b1aSDarren Reed 13354a9b8375SDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_ADD_WAIT_MASK, 13367ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 13377ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 13387ddc9b1aSDarren Reed hook_event_free(new, NULL); 13397ddc9b1aSDarren Reed return (NULL); 13407ddc9b1aSDarren Reed } 13417ddc9b1aSDarren Reed 13427ddc9b1aSDarren Reed TAILQ_INIT(&new->hei_nhead); 13437ddc9b1aSDarren Reed 13447ddc9b1aSDarren Reed hook_event_init_kstats(hfi, new); 13457ddc9b1aSDarren Reed hook_wait_init(&new->hei_waiter, &new->hei_lock); 13467ddc9b1aSDarren Reed 1347381a2a9aSdr146992 /* Add to event list head */ 1348381a2a9aSdr146992 SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry); 1349381a2a9aSdr146992 13507ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 13517ddc9b1aSDarren Reed 13527ddc9b1aSDarren Reed hook_notify_run(&hfi->hfi_nhead, 13537ddc9b1aSDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_REGISTER); 13547ddc9b1aSDarren Reed 13557ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_ADD_ACTIVE); 13567ddc9b1aSDarren Reed 1357381a2a9aSdr146992 return (new); 1358381a2a9aSdr146992 } 1359381a2a9aSdr146992 13607ddc9b1aSDarren Reed /* 13617ddc9b1aSDarren Reed * Function: hook_event_init_kstats 13627ddc9b1aSDarren Reed * Returns: None 13637ddc9b1aSDarren Reed * Parameters: hfi(I) - pointer to the family that owns this event. 13647ddc9b1aSDarren Reed * hei(I) - pointer to the hook event that needs some kstats. 13657ddc9b1aSDarren Reed * 13667ddc9b1aSDarren Reed * Create a set of kstats that relate to each event registered with 13677ddc9b1aSDarren Reed * the hook framework. A counter is kept for each time the event is 13687ddc9b1aSDarren Reed * activated and for each time a hook is added or removed. As the 13697ddc9b1aSDarren Reed * kstats just count the events as they happen, the total number of 13707ddc9b1aSDarren Reed * hooks registered must be obtained by subtractived removed from added. 13717ddc9b1aSDarren Reed */ 13727ddc9b1aSDarren Reed static void 13737ddc9b1aSDarren Reed hook_event_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei) 13747ddc9b1aSDarren Reed { 13757ddc9b1aSDarren Reed hook_event_kstat_t template = { 13767ddc9b1aSDarren Reed { "hooksAdded", KSTAT_DATA_UINT64 }, 13777ddc9b1aSDarren Reed { "hooksRemoved", KSTAT_DATA_UINT64 }, 13787ddc9b1aSDarren Reed { "events", KSTAT_DATA_UINT64 } 13797ddc9b1aSDarren Reed }; 13807ddc9b1aSDarren Reed hook_stack_t *hks; 13817ddc9b1aSDarren Reed 13827ddc9b1aSDarren Reed hks = hfi->hfi_stack; 13837ddc9b1aSDarren Reed hei->hei_kstatp = kstat_create_netstack(hfi->hfi_family.hf_name, 0, 13847ddc9b1aSDarren Reed hei->hei_event->he_name, "hook_event", KSTAT_TYPE_NAMED, 13857ddc9b1aSDarren Reed sizeof (hei->hei_kstats) / sizeof (kstat_named_t), 13867ddc9b1aSDarren Reed KSTAT_FLAG_VIRTUAL, hks->hks_netstackid); 13877ddc9b1aSDarren Reed 13887ddc9b1aSDarren Reed bcopy((char *)&template, &hei->hei_kstats, sizeof (template)); 13897ddc9b1aSDarren Reed 13907ddc9b1aSDarren Reed if (hei->hei_kstatp != NULL) { 13917ddc9b1aSDarren Reed hei->hei_kstatp->ks_data = (void *)&hei->hei_kstats; 13927ddc9b1aSDarren Reed hei->hei_kstatp->ks_private = 13937ddc9b1aSDarren Reed (void *)(uintptr_t)hks->hks_netstackid; 13947ddc9b1aSDarren Reed 13957ddc9b1aSDarren Reed kstat_install(hei->hei_kstatp); 13967ddc9b1aSDarren Reed } 13977ddc9b1aSDarren Reed } 1398381a2a9aSdr146992 1399381a2a9aSdr146992 /* 1400381a2a9aSdr146992 * Function: hook_event_remove 14014a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 1402381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1403381a2a9aSdr146992 * he(I) - event pointer 1404381a2a9aSdr146992 * 1405381a2a9aSdr146992 * Remove event from event list on specific family 14067ddc9b1aSDarren Reed * 14077ddc9b1aSDarren Reed * This function assumes that the caller has received a pointer to a the 14087ddc9b1aSDarren Reed * hook_family_int_t via a call to net_protocol_lookup or net_protocol_unreg'. 14097ddc9b1aSDarren Reed * This the hook_family_int_t is guaranteed to be around for the life of this 14107ddc9b1aSDarren Reed * call, unless the caller has decided to call net_protocol_release or 14117ddc9b1aSDarren Reed * net_protocol_unregister before calling net_event_unregister - an error. 1412381a2a9aSdr146992 */ 1413381a2a9aSdr146992 int 1414381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he) 1415381a2a9aSdr146992 { 14167ddc9b1aSDarren Reed boolean_t free_family; 1417381a2a9aSdr146992 hook_event_int_t *hei; 14188ad74188SDarren Reed boolean_t notifydone; 1419381a2a9aSdr146992 1420381a2a9aSdr146992 ASSERT(hfi != NULL); 1421381a2a9aSdr146992 ASSERT(he != NULL); 1422381a2a9aSdr146992 14237ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1424381a2a9aSdr146992 14257ddc9b1aSDarren Reed /* 14267ddc9b1aSDarren Reed * Set the flag so that we can call hook_event_notify_run without 14277ddc9b1aSDarren Reed * holding any locks but at the same time prevent other changes to 14287ddc9b1aSDarren Reed * the event at the same time. 14297ddc9b1aSDarren Reed */ 14304a9b8375SDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK, 14317ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 14327ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 1433381a2a9aSdr146992 return (ENXIO); 1434381a2a9aSdr146992 } 1435381a2a9aSdr146992 14367ddc9b1aSDarren Reed hei = hook_event_find(hfi, he->he_name); 14377ddc9b1aSDarren Reed if (hei == NULL) { 14387ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 14397ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 14407ddc9b1aSDarren Reed return (ESRCH); 1441381a2a9aSdr146992 } 1442381a2a9aSdr146992 14437ddc9b1aSDarren Reed free_family = B_FALSE; 1444381a2a9aSdr146992 14457ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 14467ddc9b1aSDarren Reed /* 14478ad74188SDarren Reed * The hei_shutdown flag is used to indicate whether or not we have 14488ad74188SDarren Reed * done a shutdown and thus already walked through the notify list. 14498ad74188SDarren Reed */ 14508ad74188SDarren Reed notifydone = hei->hei_shutdown; 14518ad74188SDarren Reed hei->hei_shutdown = B_TRUE; 14528ad74188SDarren Reed /* 14537ddc9b1aSDarren Reed * If there are any hooks still registered for this event or 14547ddc9b1aSDarren Reed * there are any notifiers registered, return an error indicating 14557ddc9b1aSDarren Reed * that the event is still busy. 14567ddc9b1aSDarren Reed */ 14577ddc9b1aSDarren Reed if (!TAILQ_EMPTY(&hei->hei_head) || !TAILQ_EMPTY(&hei->hei_nhead)) { 14587ddc9b1aSDarren Reed hei->hei_condemned = B_TRUE; 14597ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 14607ddc9b1aSDarren Reed } else { 14617ddc9b1aSDarren Reed /* hei_condemned = B_FALSE is implied from creation */ 14627ddc9b1aSDarren Reed /* 14637ddc9b1aSDarren Reed * Even though we know the notify list is empty, we call 14647ddc9b1aSDarren Reed * hook_wait_destroy here to synchronise wait removing a 14657ddc9b1aSDarren Reed * hook from an event. 14667ddc9b1aSDarren Reed */ 14674a9b8375SDarren Reed VERIFY(hook_wait_destroy(&hei->hei_waiter) == 0); 14687ddc9b1aSDarren Reed 14697ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 14707ddc9b1aSDarren Reed 14717ddc9b1aSDarren Reed if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) && 14727ddc9b1aSDarren Reed TAILQ_EMPTY(&hfi->hfi_nhead)) 14737ddc9b1aSDarren Reed free_family = B_TRUE; 14747ddc9b1aSDarren Reed } 14757ddc9b1aSDarren Reed 14767ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 14777ddc9b1aSDarren Reed 14788ad74188SDarren Reed if (!notifydone) 14797ddc9b1aSDarren Reed hook_notify_run(&hfi->hfi_nhead, 14807ddc9b1aSDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER); 14817ddc9b1aSDarren Reed 14827ddc9b1aSDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 14837ddc9b1aSDarren Reed 14847ddc9b1aSDarren Reed if (!hei->hei_condemned) { 14857ddc9b1aSDarren Reed hook_event_free(hei, hfi); 14867ddc9b1aSDarren Reed if (free_family) 14877ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 14887ddc9b1aSDarren Reed } 1489381a2a9aSdr146992 1490381a2a9aSdr146992 return (0); 1491381a2a9aSdr146992 } 1492381a2a9aSdr146992 14937ddc9b1aSDarren Reed /* 14948ad74188SDarren Reed * Function: hook_event_shutdown 14954a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 14968ad74188SDarren Reed * Parameters: hfi(I) - internal family pointer 14978ad74188SDarren Reed * he(I) - event pointer 14988ad74188SDarren Reed * 14998ad74188SDarren Reed * As with hook_family_shutdown, we want to generate the notify callbacks 15008ad74188SDarren Reed * as if the event was being removed but not actually do the remove. 15018ad74188SDarren Reed */ 15028ad74188SDarren Reed int 15038ad74188SDarren Reed hook_event_shutdown(hook_family_int_t *hfi, hook_event_t *he) 15048ad74188SDarren Reed { 15058ad74188SDarren Reed hook_event_int_t *hei; 15068ad74188SDarren Reed boolean_t notifydone; 15078ad74188SDarren Reed 15088ad74188SDarren Reed ASSERT(hfi != NULL); 15098ad74188SDarren Reed ASSERT(he != NULL); 15108ad74188SDarren Reed 15118ad74188SDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 15128ad74188SDarren Reed 15138ad74188SDarren Reed /* 15148ad74188SDarren Reed * Set the flag so that we can call hook_event_notify_run without 15158ad74188SDarren Reed * holding any locks but at the same time prevent other changes to 15168ad74188SDarren Reed * the event at the same time. 15178ad74188SDarren Reed */ 15184a9b8375SDarren Reed if (hook_wait_setflag(&hfi->hfi_waiter, FWF_DEL_WAIT_MASK, 15198ad74188SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 15208ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 15218ad74188SDarren Reed return (ENXIO); 15228ad74188SDarren Reed } 15238ad74188SDarren Reed 15248ad74188SDarren Reed hei = hook_event_find(hfi, he->he_name); 15258ad74188SDarren Reed if (hei == NULL) { 15268ad74188SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 15278ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 15288ad74188SDarren Reed return (ESRCH); 15298ad74188SDarren Reed } 15308ad74188SDarren Reed 15318ad74188SDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 15328ad74188SDarren Reed notifydone = hei->hei_shutdown; 15338ad74188SDarren Reed hei->hei_shutdown = B_TRUE; 15348ad74188SDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 15358ad74188SDarren Reed 15368ad74188SDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 15378ad74188SDarren Reed 15388ad74188SDarren Reed if (!notifydone) 15398ad74188SDarren Reed hook_notify_run(&hfi->hfi_nhead, 15408ad74188SDarren Reed hfi->hfi_family.hf_name, NULL, he->he_name, HN_UNREGISTER); 15418ad74188SDarren Reed 15428ad74188SDarren Reed hook_wait_unsetflag(&hfi->hfi_waiter, FWF_DEL_ACTIVE); 15438ad74188SDarren Reed 15448ad74188SDarren Reed return (0); 15458ad74188SDarren Reed } 15468ad74188SDarren Reed 15478ad74188SDarren Reed /* 15487ddc9b1aSDarren Reed * Function: hook_event_free 15497ddc9b1aSDarren Reed * Returns: None 15507ddc9b1aSDarren Reed * Parameters: hei(I) - internal event pointer 15517ddc9b1aSDarren Reed * 15527ddc9b1aSDarren Reed * Free alloc memory for event 15537ddc9b1aSDarren Reed */ 15547ddc9b1aSDarren Reed static void 15557ddc9b1aSDarren Reed hook_event_free(hook_event_int_t *hei, hook_family_int_t *hfi) 15567ddc9b1aSDarren Reed { 15577ddc9b1aSDarren Reed boolean_t free_family; 15587ddc9b1aSDarren Reed 15597ddc9b1aSDarren Reed ASSERT(hei != NULL); 15607ddc9b1aSDarren Reed 15617ddc9b1aSDarren Reed if (hfi != NULL) { 15627ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 15637ddc9b1aSDarren Reed /* 15647ddc9b1aSDarren Reed * Remove the event from the hook family's list. 15657ddc9b1aSDarren Reed */ 15667ddc9b1aSDarren Reed SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry); 15677ddc9b1aSDarren Reed if (hfi->hfi_condemned && SLIST_EMPTY(&hfi->hfi_head) && 15687ddc9b1aSDarren Reed TAILQ_EMPTY(&hfi->hfi_nhead)) { 15697ddc9b1aSDarren Reed free_family = B_TRUE; 15707ddc9b1aSDarren Reed } else { 15717ddc9b1aSDarren Reed free_family = B_FALSE; 15727ddc9b1aSDarren Reed } 15737ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 15747ddc9b1aSDarren Reed } 15757ddc9b1aSDarren Reed 15767ddc9b1aSDarren Reed if (hei->hei_kstatp != NULL) { 15777ddc9b1aSDarren Reed ASSERT(hfi != NULL); 15787ddc9b1aSDarren Reed 15797ddc9b1aSDarren Reed kstat_delete_netstack(hei->hei_kstatp, 15807ddc9b1aSDarren Reed hfi->hfi_stack->hks_netstackid); 15817ddc9b1aSDarren Reed hei->hei_kstatp = NULL; 15827ddc9b1aSDarren Reed } 15837ddc9b1aSDarren Reed 15847ddc9b1aSDarren Reed /* Free container */ 15857ddc9b1aSDarren Reed kmem_free(hei, sizeof (*hei)); 15867ddc9b1aSDarren Reed 15877ddc9b1aSDarren Reed if (free_family) 15887ddc9b1aSDarren Reed hook_family_free(hfi, hfi->hfi_stack); 15897ddc9b1aSDarren Reed } 1590381a2a9aSdr146992 1591381a2a9aSdr146992 /* 1592381a2a9aSdr146992 * Function: hook_event_checkdup 1593381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 1594381a2a9aSdr146992 * Parameters: he(I) - event pointer 1595381a2a9aSdr146992 * 15967ddc9b1aSDarren Reed * Search all of the hook families to see if the event being passed in 15977ddc9b1aSDarren Reed * has already been associated with one. 1598381a2a9aSdr146992 */ 1599381a2a9aSdr146992 static hook_event_int_t * 1600f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks) 1601381a2a9aSdr146992 { 1602381a2a9aSdr146992 hook_family_int_t *hfi; 1603381a2a9aSdr146992 hook_event_int_t *hei; 1604381a2a9aSdr146992 1605381a2a9aSdr146992 ASSERT(he != NULL); 1606381a2a9aSdr146992 16077ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 1608f4b3ec61Sdh155122 SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) { 1609381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 16107ddc9b1aSDarren Reed if (hei->hei_event == he) { 16117ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 1612381a2a9aSdr146992 return (hei); 1613381a2a9aSdr146992 } 1614381a2a9aSdr146992 } 16157ddc9b1aSDarren Reed } 16167ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 1617381a2a9aSdr146992 1618381a2a9aSdr146992 return (NULL); 1619381a2a9aSdr146992 } 1620381a2a9aSdr146992 1621381a2a9aSdr146992 /* 1622381a2a9aSdr146992 * Function: hook_event_copy 1623381a2a9aSdr146992 * Returns: internal event pointer - NULL = Failed 1624381a2a9aSdr146992 * Parameters: src(I) - event pointer 1625381a2a9aSdr146992 * 1626381a2a9aSdr146992 * Allocate internal event block and duplicate incoming event 1627381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 1628381a2a9aSdr146992 */ 1629381a2a9aSdr146992 static hook_event_int_t * 1630381a2a9aSdr146992 hook_event_copy(hook_event_t *src) 1631381a2a9aSdr146992 { 1632381a2a9aSdr146992 hook_event_int_t *new; 1633381a2a9aSdr146992 1634381a2a9aSdr146992 ASSERT(src != NULL); 1635381a2a9aSdr146992 ASSERT(src->he_name != NULL); 1636381a2a9aSdr146992 1637381a2a9aSdr146992 new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 1638381a2a9aSdr146992 1639381a2a9aSdr146992 /* Copy body */ 1640381a2a9aSdr146992 TAILQ_INIT(&new->hei_head); 1641381a2a9aSdr146992 new->hei_event = src; 1642381a2a9aSdr146992 1643381a2a9aSdr146992 return (new); 1644381a2a9aSdr146992 } 1645381a2a9aSdr146992 1646381a2a9aSdr146992 /* 1647381a2a9aSdr146992 * Function: hook_event_find 1648381a2a9aSdr146992 * Returns: internal event pointer - NULL = Not match 1649381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1650381a2a9aSdr146992 * event(I) - event name string 1651381a2a9aSdr146992 * 1652381a2a9aSdr146992 * Search event list with event name 16537ddc9b1aSDarren Reed * A lock on hfi->hfi_lock must be held when called. 1654381a2a9aSdr146992 */ 1655381a2a9aSdr146992 static hook_event_int_t * 1656381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event) 1657381a2a9aSdr146992 { 1658381a2a9aSdr146992 hook_event_int_t *hei = NULL; 1659381a2a9aSdr146992 1660381a2a9aSdr146992 ASSERT(hfi != NULL); 1661381a2a9aSdr146992 ASSERT(event != NULL); 1662381a2a9aSdr146992 1663381a2a9aSdr146992 SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) { 16647ddc9b1aSDarren Reed if ((strcmp(hei->hei_event->he_name, event) == 0) && 16657ddc9b1aSDarren Reed ((hei->hei_waiter.fw_flags & FWF_UNSAFE) == 0)) 1666381a2a9aSdr146992 break; 1667381a2a9aSdr146992 } 1668381a2a9aSdr146992 return (hei); 1669381a2a9aSdr146992 } 1670381a2a9aSdr146992 1671381a2a9aSdr146992 /* 16727ddc9b1aSDarren Reed * Function: hook_event_notify_register 16734a9b8375SDarren Reed * Returns: int - 0 = success, else failure 16747ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 16757ddc9b1aSDarren Reed * event(I) - name of the event 16767ddc9b1aSDarren Reed * callback(I) - function to be called 16777ddc9b1aSDarren Reed * arg(I) - arg to provide callback when it is called 1678381a2a9aSdr146992 * 16797ddc9b1aSDarren Reed * Adds a new callback to the event named by "event" (we must find it) 16807ddc9b1aSDarren Reed * that will be executed each time a new hook is added to the event. 16817ddc9b1aSDarren Reed * Of course, if the stack is being shut down, this call should fail. 1682381a2a9aSdr146992 */ 16837ddc9b1aSDarren Reed int 16847ddc9b1aSDarren Reed hook_event_notify_register(hook_family_int_t *hfi, char *event, 16857ddc9b1aSDarren Reed hook_notify_fn_t callback, void *arg) 1686381a2a9aSdr146992 { 16877ddc9b1aSDarren Reed hook_event_int_t *hei; 16887ddc9b1aSDarren Reed hook_stack_t *hks; 16894a9b8375SDarren Reed boolean_t canrun; 16904a9b8375SDarren Reed hook_int_t *h; 16917ddc9b1aSDarren Reed int error; 1692381a2a9aSdr146992 16934a9b8375SDarren Reed canrun = B_FALSE; 16947ddc9b1aSDarren Reed hks = hfi->hfi_stack; 16957ddc9b1aSDarren Reed CVW_ENTER_READ(&hks->hks_lock); 16967ddc9b1aSDarren Reed if (hks->hks_shutdown != 0) { 16977ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 16987ddc9b1aSDarren Reed return (ESHUTDOWN); 1699381a2a9aSdr146992 } 1700381a2a9aSdr146992 17017ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 17027ddc9b1aSDarren Reed 17038ad74188SDarren Reed if (hfi->hfi_condemned || hfi->hfi_shutdown) { 17047ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 17057ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 17067ddc9b1aSDarren Reed return (ESHUTDOWN); 17077ddc9b1aSDarren Reed } 17087ddc9b1aSDarren Reed 17097ddc9b1aSDarren Reed hei = hook_event_find(hfi, event); 17107ddc9b1aSDarren Reed if (hei == NULL) { 17117ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 17127ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 17137ddc9b1aSDarren Reed return (ESRCH); 17147ddc9b1aSDarren Reed } 17157ddc9b1aSDarren Reed 17168ad74188SDarren Reed if (hei->hei_condemned || hei->hei_shutdown) { 17177ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 17187ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 17197ddc9b1aSDarren Reed return (ESHUTDOWN); 17207ddc9b1aSDarren Reed } 17217ddc9b1aSDarren Reed 17224a9b8375SDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 17234a9b8375SDarren Reed canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK, 17244a9b8375SDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1); 17254a9b8375SDarren Reed error = hook_notify_register(&hei->hei_nhead, callback, arg); 17267ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 17274a9b8375SDarren Reed 17287ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 17297ddc9b1aSDarren Reed CVW_EXIT_READ(&hks->hks_lock); 17307ddc9b1aSDarren Reed 17314a9b8375SDarren Reed if (error == 0 && canrun) { 17324a9b8375SDarren Reed TAILQ_FOREACH(h, &hei->hei_head, hi_entry) { 17334a9b8375SDarren Reed callback(HN_REGISTER, arg, 17344a9b8375SDarren Reed hfi->hfi_family.hf_name, hei->hei_event->he_name, 17354a9b8375SDarren Reed h->hi_hook.h_name); 17364a9b8375SDarren Reed } 17374a9b8375SDarren Reed } 17384a9b8375SDarren Reed 17394a9b8375SDarren Reed if (canrun) 17404a9b8375SDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE); 17414a9b8375SDarren Reed 17427ddc9b1aSDarren Reed return (error); 17437ddc9b1aSDarren Reed } 17447ddc9b1aSDarren Reed 17457ddc9b1aSDarren Reed /* 17467ddc9b1aSDarren Reed * Function: hook_event_notify_unregister 17474a9b8375SDarren Reed * Returns: int - 0 = success, else failure 17487ddc9b1aSDarren Reed * Parameters: hfi(I) - hook family 17497ddc9b1aSDarren Reed * event(I) - name of the event 17507ddc9b1aSDarren Reed * callback(I) - function to be called 17517ddc9b1aSDarren Reed * 17527ddc9b1aSDarren Reed * Remove the given callback from the named event's list of functions 17537ddc9b1aSDarren Reed * to call when a hook is added or removed. 17547ddc9b1aSDarren Reed */ 17557ddc9b1aSDarren Reed int 17567ddc9b1aSDarren Reed hook_event_notify_unregister(hook_family_int_t *hfi, char *event, 17577ddc9b1aSDarren Reed hook_notify_fn_t callback) 17587ddc9b1aSDarren Reed { 17597ddc9b1aSDarren Reed hook_event_int_t *hei; 17607ddc9b1aSDarren Reed boolean_t free_event; 17614a9b8375SDarren Reed boolean_t canrun; 17624a9b8375SDarren Reed hook_int_t *h; 17634a9b8375SDarren Reed void *arg; 17647ddc9b1aSDarren Reed int error; 17657ddc9b1aSDarren Reed 17664a9b8375SDarren Reed canrun = B_FALSE; 17674a9b8375SDarren Reed 17687ddc9b1aSDarren Reed CVW_ENTER_READ(&hfi->hfi_lock); 17697ddc9b1aSDarren Reed 17707ddc9b1aSDarren Reed hei = hook_event_find(hfi, event); 17717ddc9b1aSDarren Reed if (hei == NULL) { 17727ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 17737ddc9b1aSDarren Reed return (ESRCH); 17747ddc9b1aSDarren Reed } 17757ddc9b1aSDarren Reed 17767ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hei->hei_lock); 17777ddc9b1aSDarren Reed 17784a9b8375SDarren Reed (void) hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK, 17794a9b8375SDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE); 17804a9b8375SDarren Reed 17814a9b8375SDarren Reed error = hook_notify_unregister(&hei->hei_nhead, callback, &arg); 17824a9b8375SDarren Reed 17834a9b8375SDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE); 17847ddc9b1aSDarren Reed 17857ddc9b1aSDarren Reed /* 17867ddc9b1aSDarren Reed * hei_condemned has been set if someone tried to remove the 17877ddc9b1aSDarren Reed * event but couldn't because there were still things attached to 17887ddc9b1aSDarren Reed * it. Now that we've done a successful remove, if it is now empty 17897ddc9b1aSDarren Reed * then by all rights we should be free'ing it too. Note that the 17907ddc9b1aSDarren Reed * expectation is that only the caller of hook_event_add will ever 17917ddc9b1aSDarren Reed * call hook_event_remove. 17927ddc9b1aSDarren Reed */ 17937ddc9b1aSDarren Reed if ((error == 0) && hei->hei_condemned && 17947ddc9b1aSDarren Reed TAILQ_EMPTY(&hei->hei_head) && TAILQ_EMPTY(&hei->hei_nhead)) { 17957ddc9b1aSDarren Reed free_event = B_TRUE; 17967ddc9b1aSDarren Reed } else { 17977ddc9b1aSDarren Reed free_event = B_FALSE; 17987ddc9b1aSDarren Reed } 17997ddc9b1aSDarren Reed 18004a9b8375SDarren Reed if (error == 0 && !free_event) { 18014a9b8375SDarren Reed canrun = (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK, 18024a9b8375SDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) != -1); 18034a9b8375SDarren Reed } 18044a9b8375SDarren Reed 18057ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 18067ddc9b1aSDarren Reed CVW_EXIT_READ(&hfi->hfi_lock); 18077ddc9b1aSDarren Reed 18084a9b8375SDarren Reed if (canrun) { 18094a9b8375SDarren Reed TAILQ_FOREACH(h, &hei->hei_head, hi_entry) { 18104a9b8375SDarren Reed callback(HN_UNREGISTER, arg, 18114a9b8375SDarren Reed hfi->hfi_family.hf_name, hei->hei_event->he_name, 18124a9b8375SDarren Reed h->hi_hook.h_name); 18134a9b8375SDarren Reed } 18144a9b8375SDarren Reed 18154a9b8375SDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE); 18164a9b8375SDarren Reed } 18174a9b8375SDarren Reed 18187ddc9b1aSDarren Reed if (free_event) { 18197ddc9b1aSDarren Reed /* 18207ddc9b1aSDarren Reed * It is safe to pass in hfi here, without a lock, because 18217ddc9b1aSDarren Reed * our structure (hei) is still on one of its lists and thus 18227ddc9b1aSDarren Reed * it won't be able to disappear yet... 18237ddc9b1aSDarren Reed */ 18247ddc9b1aSDarren Reed hook_event_free(hei, hfi); 18257ddc9b1aSDarren Reed } 18267ddc9b1aSDarren Reed 18277ddc9b1aSDarren Reed return (error); 18287ddc9b1aSDarren Reed } 18297ddc9b1aSDarren Reed 18307ddc9b1aSDarren Reed /* 18317ddc9b1aSDarren Reed * Function: hook_event_notify_run 18327ddc9b1aSDarren Reed * Returns: None 18337ddc9b1aSDarren Reed * Parameters: nrun(I) - pointer to the list of callbacks to execute 18347ddc9b1aSDarren Reed * hfi(I) - hook stack pointer to execute callbacks for 18357ddc9b1aSDarren Reed * name(I) - name of a hook family 18367ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 18377ddc9b1aSDarren Reed * 18387ddc9b1aSDarren Reed * Execute all of the callbacks registered for this event. 18397ddc9b1aSDarren Reed */ 18407ddc9b1aSDarren Reed static void 18417ddc9b1aSDarren Reed hook_event_notify_run(hook_event_int_t *hei, hook_family_int_t *hfi, 18427ddc9b1aSDarren Reed char *event, char *name, hook_notify_cmd_t cmd) 18437ddc9b1aSDarren Reed { 18447ddc9b1aSDarren Reed 18457ddc9b1aSDarren Reed hook_notify_run(&hei->hei_nhead, hfi->hfi_family.hf_name, 18467ddc9b1aSDarren Reed event, name, cmd); 18477ddc9b1aSDarren Reed } 1848381a2a9aSdr146992 1849381a2a9aSdr146992 /* 1850381a2a9aSdr146992 * Function: hook_register 18514a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 1852381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 1853381a2a9aSdr146992 * event(I) - event name string 1854381a2a9aSdr146992 * h(I) - hook pointer 1855381a2a9aSdr146992 * 18567ddc9b1aSDarren Reed * Add new hook to hook list on the specified family and event. 1857381a2a9aSdr146992 */ 1858381a2a9aSdr146992 int 1859381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h) 1860381a2a9aSdr146992 { 1861381a2a9aSdr146992 hook_event_int_t *hei; 1862381a2a9aSdr146992 hook_int_t *hi, *new; 18637ddc9b1aSDarren Reed int error; 1864381a2a9aSdr146992 1865381a2a9aSdr146992 ASSERT(hfi != NULL); 1866381a2a9aSdr146992 ASSERT(event != NULL); 1867381a2a9aSdr146992 ASSERT(h != NULL); 18687ddc9b1aSDarren Reed 18697ddc9b1aSDarren Reed if (hfi->hfi_stack->hks_shutdown) 18707ddc9b1aSDarren Reed return (NULL); 1871381a2a9aSdr146992 1872381a2a9aSdr146992 /* Alloc hook_int_t and copy hook */ 1873381a2a9aSdr146992 new = hook_copy(h); 1874381a2a9aSdr146992 if (new == NULL) 1875381a2a9aSdr146992 return (ENOMEM); 1876381a2a9aSdr146992 1877381a2a9aSdr146992 /* 1878381a2a9aSdr146992 * Since hook add/remove only impact event, so it is unnecessary 1879381a2a9aSdr146992 * to hold global family write lock. Just get read lock here to 1880381a2a9aSdr146992 * ensure event will not be removed when doing hooks operation 1881381a2a9aSdr146992 */ 18827ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 1883381a2a9aSdr146992 1884381a2a9aSdr146992 hei = hook_event_find(hfi, event); 1885381a2a9aSdr146992 if (hei == NULL) { 18867ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 18877ddc9b1aSDarren Reed hook_int_free(new, hfi->hfi_stack->hks_netstackid); 1888381a2a9aSdr146992 return (ENXIO); 1889381a2a9aSdr146992 } 1890381a2a9aSdr146992 1891381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 1892381a2a9aSdr146992 18938ad74188SDarren Reed /* 18948ad74188SDarren Reed * If we've run either the remove() or shutdown(), do not allow any 18958ad74188SDarren Reed * more hooks to be added to this event. 18968ad74188SDarren Reed */ 18978ad74188SDarren Reed if (hei->hei_shutdown) { 18988ad74188SDarren Reed error = ESHUTDOWN; 18998ad74188SDarren Reed goto bad_add; 19008ad74188SDarren Reed } 19018ad74188SDarren Reed 1902381a2a9aSdr146992 hi = hook_find(hei, h); 1903381a2a9aSdr146992 if (hi != NULL) { 19047ddc9b1aSDarren Reed error = EEXIST; 19057ddc9b1aSDarren Reed goto bad_add; 19067ddc9b1aSDarren Reed } 19077ddc9b1aSDarren Reed 19084a9b8375SDarren Reed if (hook_wait_setflag(&hei->hei_waiter, FWF_ADD_WAIT_MASK, 19097ddc9b1aSDarren Reed FWF_ADD_WANTED, FWF_ADD_ACTIVE) == -1) { 19107ddc9b1aSDarren Reed error = ENOENT; 19117ddc9b1aSDarren Reed bad_add: 1912381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 19137ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 19147ddc9b1aSDarren Reed hook_int_free(new, hfi->hfi_stack->hks_netstackid); 19157ddc9b1aSDarren Reed return (error); 1916381a2a9aSdr146992 } 1917381a2a9aSdr146992 1918381a2a9aSdr146992 /* Add to hook list head */ 19197ddc9b1aSDarren Reed error = hook_insert(&hei->hei_head, new); 19207ddc9b1aSDarren Reed if (error == 0) { 1921381a2a9aSdr146992 hei->hei_event->he_interested = B_TRUE; 19227ddc9b1aSDarren Reed hei->hei_kstats.hooks_added.value.ui64++; 19237ddc9b1aSDarren Reed 19247ddc9b1aSDarren Reed hook_init_kstats(hfi, hei, new); 19257ddc9b1aSDarren Reed } 1926381a2a9aSdr146992 1927381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 19287ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 19297ddc9b1aSDarren Reed 19307ddc9b1aSDarren Reed /* 19317ddc9b1aSDarren Reed * Note that the name string passed through to the notify callbacks 19327ddc9b1aSDarren Reed * is from the original hook being registered, not the copy being 19337ddc9b1aSDarren Reed * inserted. 19347ddc9b1aSDarren Reed */ 19354a9b8375SDarren Reed if (error == 0) 19367ddc9b1aSDarren Reed hook_event_notify_run(hei, hfi, event, h->h_name, HN_REGISTER); 19374a9b8375SDarren Reed 19387ddc9b1aSDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_ADD_ACTIVE); 19397ddc9b1aSDarren Reed 19407ddc9b1aSDarren Reed return (error); 19417ddc9b1aSDarren Reed } 19427ddc9b1aSDarren Reed 19437ddc9b1aSDarren Reed /* 19447ddc9b1aSDarren Reed * Function: hook_insert 19454a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 19467ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 19477ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 19487ddc9b1aSDarren Reed * 19497ddc9b1aSDarren Reed * Try to insert the hook onto the list of hooks according to the hints 19507ddc9b1aSDarren Reed * given in the hook to be inserted and those that already exist on the 19517ddc9b1aSDarren Reed * list. For now, the implementation permits only a single hook to be 19527ddc9b1aSDarren Reed * either first or last and names provided with before or after are only 19537ddc9b1aSDarren Reed * loosely coupled with the action. 19547ddc9b1aSDarren Reed */ 19557ddc9b1aSDarren Reed static int 19567ddc9b1aSDarren Reed hook_insert(hook_int_head_t *head, hook_int_t *new) 19577ddc9b1aSDarren Reed { 19587ddc9b1aSDarren Reed hook_int_t *before; 19597ddc9b1aSDarren Reed hook_int_t *hi; 19607ddc9b1aSDarren Reed hook_t *hih; 19617ddc9b1aSDarren Reed hook_t *h = &new->hi_hook; 19627ddc9b1aSDarren Reed 19637ddc9b1aSDarren Reed switch (new->hi_hook.h_hint) { 19647ddc9b1aSDarren Reed case HH_NONE : 19657ddc9b1aSDarren Reed before = NULL; 19667ddc9b1aSDarren Reed /* 19677ddc9b1aSDarren Reed * If there is no hint present (or not one that can be 19687ddc9b1aSDarren Reed * satisfied now) then try to at least respect the wishes 19697ddc9b1aSDarren Reed * of those that want to be last. If there are none wanting 19707ddc9b1aSDarren Reed * to be last then add the new hook to the tail of the 19717ddc9b1aSDarren Reed * list - this means we keep any wanting to be first 19727ddc9b1aSDarren Reed * happy without having to search for HH_FIRST. 19737ddc9b1aSDarren Reed */ 19747ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 19757ddc9b1aSDarren Reed hih = &hi->hi_hook; 19767ddc9b1aSDarren Reed if ((hih->h_hint == HH_AFTER) && 19777ddc9b1aSDarren Reed (strcmp(h->h_name, 19787ddc9b1aSDarren Reed (char *)hih->h_hintvalue) == 0)) { 19797ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 19807ddc9b1aSDarren Reed return (0); 19817ddc9b1aSDarren Reed } 19827ddc9b1aSDarren Reed if ((hih->h_hint == HH_BEFORE) && (before == NULL) && 19837ddc9b1aSDarren Reed (strcmp(h->h_name, 19847ddc9b1aSDarren Reed (char *)hih->h_hintvalue) == 0)) { 19857ddc9b1aSDarren Reed before = hi; 19867ddc9b1aSDarren Reed } 19877ddc9b1aSDarren Reed } 19887ddc9b1aSDarren Reed if (before != NULL) { 19897ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, before, new, hi_entry); 19907ddc9b1aSDarren Reed return (0); 19917ddc9b1aSDarren Reed } 19927ddc9b1aSDarren Reed hook_insert_plain(head, new); 19937ddc9b1aSDarren Reed break; 19947ddc9b1aSDarren Reed 19957ddc9b1aSDarren Reed case HH_FIRST : 19967ddc9b1aSDarren Reed hi = TAILQ_FIRST(head); 19977ddc9b1aSDarren Reed if ((hi != NULL) && (hi->hi_hook.h_hint == HH_FIRST)) 19987ddc9b1aSDarren Reed return (EBUSY); 19997ddc9b1aSDarren Reed TAILQ_INSERT_HEAD(head, new, hi_entry); 20007ddc9b1aSDarren Reed break; 20017ddc9b1aSDarren Reed 20027ddc9b1aSDarren Reed case HH_LAST : 20037ddc9b1aSDarren Reed hi = TAILQ_LAST(head, hook_int_head); 20047ddc9b1aSDarren Reed if ((hi != NULL) && (hi->hi_hook.h_hint == HH_LAST)) 20057ddc9b1aSDarren Reed return (EBUSY); 20067ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 20077ddc9b1aSDarren Reed break; 20087ddc9b1aSDarren Reed 20097ddc9b1aSDarren Reed case HH_BEFORE : 20107ddc9b1aSDarren Reed hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue); 20117ddc9b1aSDarren Reed if (hi == NULL) 20127ddc9b1aSDarren Reed return (hook_insert_afterbefore(head, new)); 20137ddc9b1aSDarren Reed 20147ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_FIRST) 20157ddc9b1aSDarren Reed return (EBUSY); 20167ddc9b1aSDarren Reed 20177ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 20187ddc9b1aSDarren Reed break; 20197ddc9b1aSDarren Reed 20207ddc9b1aSDarren Reed case HH_AFTER : 20217ddc9b1aSDarren Reed hi = hook_find_byname(head, (char *)new->hi_hook.h_hintvalue); 20227ddc9b1aSDarren Reed if (hi == NULL) 20237ddc9b1aSDarren Reed return (hook_insert_afterbefore(head, new)); 20247ddc9b1aSDarren Reed 20257ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_LAST) 20267ddc9b1aSDarren Reed return (EBUSY); 20277ddc9b1aSDarren Reed 20287ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 20297ddc9b1aSDarren Reed break; 20307ddc9b1aSDarren Reed 20317ddc9b1aSDarren Reed default : 20327ddc9b1aSDarren Reed return (EINVAL); 20337ddc9b1aSDarren Reed } 20347ddc9b1aSDarren Reed 2035381a2a9aSdr146992 return (0); 2036381a2a9aSdr146992 } 2037381a2a9aSdr146992 20387ddc9b1aSDarren Reed /* 20397ddc9b1aSDarren Reed * Function: hook_insert_plain 20407ddc9b1aSDarren Reed * Returns: int - 0 = success, else = failure 20417ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 20427ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 20437ddc9b1aSDarren Reed * 20447ddc9b1aSDarren Reed * Insert a hook such that it respects the wishes of those that want to 20457ddc9b1aSDarren Reed * be last. If there are none wanting to be last then add the new hook 20467ddc9b1aSDarren Reed * to the tail of the list - this means we keep any wanting to be first 20477ddc9b1aSDarren Reed * happy without having to search for HH_FIRST. 20487ddc9b1aSDarren Reed */ 20497ddc9b1aSDarren Reed static void 20507ddc9b1aSDarren Reed hook_insert_plain(hook_int_head_t *head, hook_int_t *new) 20517ddc9b1aSDarren Reed { 20527ddc9b1aSDarren Reed hook_int_t *hi; 20537ddc9b1aSDarren Reed 20547ddc9b1aSDarren Reed hi = TAILQ_FIRST(head); 20557ddc9b1aSDarren Reed if (hi != NULL) { 20567ddc9b1aSDarren Reed if (hi->hi_hook.h_hint == HH_LAST) { 20577ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 20587ddc9b1aSDarren Reed } else { 20597ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 20607ddc9b1aSDarren Reed } 20617ddc9b1aSDarren Reed } else { 20627ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, new, hi_entry); 20637ddc9b1aSDarren Reed } 20647ddc9b1aSDarren Reed } 20657ddc9b1aSDarren Reed 20667ddc9b1aSDarren Reed /* 20677ddc9b1aSDarren Reed * Function: hook_insert_afterbefore 20687ddc9b1aSDarren Reed * Returns: int - 0 = success, else = failure 20697ddc9b1aSDarren Reed * Parameters: head(I) - pointer to hook list to insert hook onto 20707ddc9b1aSDarren Reed * new(I) - pointer to hook to be inserted 20717ddc9b1aSDarren Reed * 20727ddc9b1aSDarren Reed * Simple insertion of a hook specifying a HH_BEFORE or HH_AFTER was not 20737ddc9b1aSDarren Reed * possible, so now we need to be more careful. The first pass is to go 20747ddc9b1aSDarren Reed * through the list and look for any other hooks that also specify the 20757ddc9b1aSDarren Reed * same hint name as the new one. The object of this exercise is to make 20767ddc9b1aSDarren Reed * sure that hooks with HH_BEFORE always appear on the list before those 20777ddc9b1aSDarren Reed * with HH_AFTER so that when said hook arrives, it can be placed in the 20787ddc9b1aSDarren Reed * middle of the BEFOREs and AFTERs. If this condition does not arise, 20797ddc9b1aSDarren Reed * just use hook_insert_plain() to try and insert the hook somewhere that 20807ddc9b1aSDarren Reed * is innocuous to existing efforts. 20817ddc9b1aSDarren Reed */ 20827ddc9b1aSDarren Reed static int 20837ddc9b1aSDarren Reed hook_insert_afterbefore(hook_int_head_t *head, hook_int_t *new) 20847ddc9b1aSDarren Reed { 20857ddc9b1aSDarren Reed hook_int_t *hi; 20867ddc9b1aSDarren Reed hook_t *nh; 20877ddc9b1aSDarren Reed hook_t *h; 20887ddc9b1aSDarren Reed 20897ddc9b1aSDarren Reed nh = &new->hi_hook; 20907ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_NONE); 20917ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_LAST); 20927ddc9b1aSDarren Reed ASSERT(new->hi_hook.h_hint != HH_FIRST); 20937ddc9b1aSDarren Reed 20947ddc9b1aSDarren Reed /* 20957ddc9b1aSDarren Reed * First, look through the list to see if there are any other 20967ddc9b1aSDarren Reed * before's or after's that have a matching hint name. 20977ddc9b1aSDarren Reed */ 20987ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 20997ddc9b1aSDarren Reed h = &hi->hi_hook; 21007ddc9b1aSDarren Reed switch (h->h_hint) { 21017ddc9b1aSDarren Reed case HH_FIRST : 21027ddc9b1aSDarren Reed case HH_LAST : 21037ddc9b1aSDarren Reed case HH_NONE : 21047ddc9b1aSDarren Reed break; 21057ddc9b1aSDarren Reed case HH_BEFORE : 21067ddc9b1aSDarren Reed if ((nh->h_hint == HH_BEFORE) && 21077ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 21087ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 21097ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 21107ddc9b1aSDarren Reed return (0); 21117ddc9b1aSDarren Reed } 21127ddc9b1aSDarren Reed if ((nh->h_hint == HH_AFTER) && 21137ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 21147ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 21157ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 21167ddc9b1aSDarren Reed return (0); 21177ddc9b1aSDarren Reed } 21187ddc9b1aSDarren Reed break; 21197ddc9b1aSDarren Reed case HH_AFTER : 21207ddc9b1aSDarren Reed if ((nh->h_hint == HH_AFTER) && 21217ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 21227ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 21237ddc9b1aSDarren Reed TAILQ_INSERT_AFTER(head, hi, new, hi_entry); 21247ddc9b1aSDarren Reed return (0); 21257ddc9b1aSDarren Reed } 21267ddc9b1aSDarren Reed if ((nh->h_hint == HH_BEFORE) && 21277ddc9b1aSDarren Reed (strcmp((char *)h->h_hintvalue, 21287ddc9b1aSDarren Reed (char *)nh->h_hintvalue) == 0)) { 21297ddc9b1aSDarren Reed TAILQ_INSERT_BEFORE(hi, new, hi_entry); 21307ddc9b1aSDarren Reed return (0); 21317ddc9b1aSDarren Reed } 21327ddc9b1aSDarren Reed break; 21337ddc9b1aSDarren Reed } 21347ddc9b1aSDarren Reed } 21357ddc9b1aSDarren Reed 21367ddc9b1aSDarren Reed hook_insert_plain(head, new); 21377ddc9b1aSDarren Reed 21387ddc9b1aSDarren Reed return (0); 21397ddc9b1aSDarren Reed } 2140381a2a9aSdr146992 2141381a2a9aSdr146992 /* 2142381a2a9aSdr146992 * Function: hook_unregister 21434a9b8375SDarren Reed * Returns: int - 0 = success, else = failure 2144381a2a9aSdr146992 * Parameters: hfi(I) - internal family pointer 2145381a2a9aSdr146992 * event(I) - event name string 2146381a2a9aSdr146992 * h(I) - hook pointer 2147381a2a9aSdr146992 * 2148381a2a9aSdr146992 * Remove hook from hook list on specific family, event 2149381a2a9aSdr146992 */ 2150381a2a9aSdr146992 int 2151381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h) 2152381a2a9aSdr146992 { 2153381a2a9aSdr146992 hook_event_int_t *hei; 2154381a2a9aSdr146992 hook_int_t *hi; 21557ddc9b1aSDarren Reed boolean_t free_event; 2156381a2a9aSdr146992 2157381a2a9aSdr146992 ASSERT(hfi != NULL); 2158381a2a9aSdr146992 ASSERT(h != NULL); 2159381a2a9aSdr146992 21607ddc9b1aSDarren Reed CVW_ENTER_WRITE(&hfi->hfi_lock); 2161381a2a9aSdr146992 2162381a2a9aSdr146992 hei = hook_event_find(hfi, event); 2163381a2a9aSdr146992 if (hei == NULL) { 21647ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 2165381a2a9aSdr146992 return (ENXIO); 2166381a2a9aSdr146992 } 2167381a2a9aSdr146992 2168381a2a9aSdr146992 /* Hold write lock for event */ 2169381a2a9aSdr146992 CVW_ENTER_WRITE(&hei->hei_lock); 2170381a2a9aSdr146992 2171381a2a9aSdr146992 hi = hook_find(hei, h); 2172381a2a9aSdr146992 if (hi == NULL) { 2173381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 21747ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 2175381a2a9aSdr146992 return (ENXIO); 2176381a2a9aSdr146992 } 2177381a2a9aSdr146992 21784a9b8375SDarren Reed if (hook_wait_setflag(&hei->hei_waiter, FWF_DEL_WAIT_MASK, 21797ddc9b1aSDarren Reed FWF_DEL_WANTED, FWF_DEL_ACTIVE) == -1) { 21807ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hei->hei_lock); 21817ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 21827ddc9b1aSDarren Reed return (ENOENT); 21837ddc9b1aSDarren Reed } 21847ddc9b1aSDarren Reed 2185381a2a9aSdr146992 /* Remove from hook list */ 2186381a2a9aSdr146992 TAILQ_REMOVE(&hei->hei_head, hi, hi_entry); 21877ddc9b1aSDarren Reed 21887ddc9b1aSDarren Reed free_event = B_FALSE; 2189381a2a9aSdr146992 if (TAILQ_EMPTY(&hei->hei_head)) { 2190381a2a9aSdr146992 hei->hei_event->he_interested = B_FALSE; 21917ddc9b1aSDarren Reed /* 21927ddc9b1aSDarren Reed * If the delete pending flag has been set and there are 21937ddc9b1aSDarren Reed * no notifiers on the event (and we've removed the last 21947ddc9b1aSDarren Reed * hook) then we need to free this event after we're done. 21957ddc9b1aSDarren Reed */ 21967ddc9b1aSDarren Reed if (hei->hei_condemned && TAILQ_EMPTY(&hei->hei_nhead)) 21977ddc9b1aSDarren Reed free_event = B_TRUE; 2198381a2a9aSdr146992 } 21997ddc9b1aSDarren Reed hei->hei_kstats.hooks_removed.value.ui64++; 2200381a2a9aSdr146992 2201381a2a9aSdr146992 CVW_EXIT_WRITE(&hei->hei_lock); 22027ddc9b1aSDarren Reed CVW_EXIT_WRITE(&hfi->hfi_lock); 22037ddc9b1aSDarren Reed /* 22047ddc9b1aSDarren Reed * While the FWF_DEL_ACTIVE flag is set, the hook_event_int_t 22057ddc9b1aSDarren Reed * will not be free'd and thus the hook_family_int_t wil not 22067ddc9b1aSDarren Reed * be free'd either. 22077ddc9b1aSDarren Reed */ 22087ddc9b1aSDarren Reed hook_event_notify_run(hei, hfi, event, h->h_name, HN_UNREGISTER); 22097ddc9b1aSDarren Reed hook_wait_unsetflag(&hei->hei_waiter, FWF_DEL_ACTIVE); 2210381a2a9aSdr146992 22117ddc9b1aSDarren Reed hook_int_free(hi, hfi->hfi_stack->hks_netstackid); 22127ddc9b1aSDarren Reed 22137ddc9b1aSDarren Reed if (free_event) 22147ddc9b1aSDarren Reed hook_event_free(hei, hfi); 22157ddc9b1aSDarren Reed 2216381a2a9aSdr146992 return (0); 2217381a2a9aSdr146992 } 2218381a2a9aSdr146992 22197ddc9b1aSDarren Reed /* 22207ddc9b1aSDarren Reed * Function: hook_find_byname 22217ddc9b1aSDarren Reed * Returns: internal hook pointer - NULL = Not match 22227ddc9b1aSDarren Reed * Parameters: hei(I) - internal event pointer 22237ddc9b1aSDarren Reed * name(I)- hook name 22247ddc9b1aSDarren Reed * 22257ddc9b1aSDarren Reed * Search an event's list of hooks to see if there is a hook present that 22267ddc9b1aSDarren Reed * has a matching name to the one being looked for. 22277ddc9b1aSDarren Reed */ 22287ddc9b1aSDarren Reed static hook_int_t * 22297ddc9b1aSDarren Reed hook_find_byname(hook_int_head_t *head, char *name) 22307ddc9b1aSDarren Reed { 22317ddc9b1aSDarren Reed hook_int_t *hi; 22327ddc9b1aSDarren Reed 22337ddc9b1aSDarren Reed TAILQ_FOREACH(hi, head, hi_entry) { 22347ddc9b1aSDarren Reed if (strcmp(hi->hi_hook.h_name, name) == 0) 22357ddc9b1aSDarren Reed return (hi); 22367ddc9b1aSDarren Reed } 22377ddc9b1aSDarren Reed 22387ddc9b1aSDarren Reed return (NULL); 22397ddc9b1aSDarren Reed } 2240381a2a9aSdr146992 2241381a2a9aSdr146992 /* 2242381a2a9aSdr146992 * Function: hook_find 2243381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Not match 2244381a2a9aSdr146992 * Parameters: hei(I) - internal event pointer 2245381a2a9aSdr146992 * h(I) - hook pointer 2246381a2a9aSdr146992 * 22477ddc9b1aSDarren Reed * Search an event's list of hooks to see if there is already one that 22487ddc9b1aSDarren Reed * matches the hook being passed in. Currently the only criteria for a 22497ddc9b1aSDarren Reed * successful search here is for the names to be the same. 2250381a2a9aSdr146992 */ 2251381a2a9aSdr146992 static hook_int_t * 2252381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h) 2253381a2a9aSdr146992 { 2254381a2a9aSdr146992 2255381a2a9aSdr146992 ASSERT(hei != NULL); 2256381a2a9aSdr146992 ASSERT(h != NULL); 2257381a2a9aSdr146992 22587ddc9b1aSDarren Reed return (hook_find_byname(&hei->hei_head, h->h_name)); 2259381a2a9aSdr146992 } 2260381a2a9aSdr146992 2261381a2a9aSdr146992 /* 2262381a2a9aSdr146992 * Function: hook_copy 2263381a2a9aSdr146992 * Returns: internal hook pointer - NULL = Failed 2264381a2a9aSdr146992 * Parameters: src(I) - hook pointer 2265381a2a9aSdr146992 * 2266381a2a9aSdr146992 * Allocate internal hook block and duplicate incoming hook. 2267381a2a9aSdr146992 * No locks should be held across this function as it may sleep. 22687ddc9b1aSDarren Reed * Because hook_copy() is responsible for the creation of the internal 22697ddc9b1aSDarren Reed * hook structure that is used here, it takes on population the structure 22707ddc9b1aSDarren Reed * with the kstat information. Note that while the kstat bits are 22717ddc9b1aSDarren Reed * seeded here, their installation of the kstats is handled elsewhere. 2272381a2a9aSdr146992 */ 2273381a2a9aSdr146992 static hook_int_t * 2274381a2a9aSdr146992 hook_copy(hook_t *src) 2275381a2a9aSdr146992 { 2276381a2a9aSdr146992 hook_int_t *new; 2277381a2a9aSdr146992 hook_t *dst; 22787ddc9b1aSDarren Reed int len; 2279381a2a9aSdr146992 2280381a2a9aSdr146992 ASSERT(src != NULL); 2281381a2a9aSdr146992 ASSERT(src->h_name != NULL); 2282381a2a9aSdr146992 2283381a2a9aSdr146992 new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP); 2284381a2a9aSdr146992 2285381a2a9aSdr146992 /* Copy body */ 2286381a2a9aSdr146992 dst = &new->hi_hook; 2287381a2a9aSdr146992 *dst = *src; 2288381a2a9aSdr146992 2289381a2a9aSdr146992 /* Copy name */ 22907ddc9b1aSDarren Reed len = strlen(src->h_name); 22917ddc9b1aSDarren Reed dst->h_name = (char *)kmem_alloc(len + 1, KM_SLEEP); 2292381a2a9aSdr146992 (void) strcpy(dst->h_name, src->h_name); 2293381a2a9aSdr146992 22947ddc9b1aSDarren Reed /* 22957ddc9b1aSDarren Reed * This is initialised in this manner to make it safer to use the 22967ddc9b1aSDarren Reed * same pointer in the kstats field. 22977ddc9b1aSDarren Reed */ 22987ddc9b1aSDarren Reed dst->h_hintvalue = (uintptr_t)""; 22997ddc9b1aSDarren Reed 23007ddc9b1aSDarren Reed if (dst->h_hint == HH_BEFORE || dst->h_hint == HH_AFTER) { 23017ddc9b1aSDarren Reed len = strlen((char *)src->h_hintvalue); 23027ddc9b1aSDarren Reed if (len > 0) { 23037ddc9b1aSDarren Reed dst->h_hintvalue = (uintptr_t)kmem_alloc(len + 1, 23047ddc9b1aSDarren Reed KM_SLEEP); 23057ddc9b1aSDarren Reed (void) strcpy((char *)dst->h_hintvalue, 23067ddc9b1aSDarren Reed (char *)src->h_hintvalue); 23077ddc9b1aSDarren Reed } 23087ddc9b1aSDarren Reed } 23097ddc9b1aSDarren Reed 2310381a2a9aSdr146992 return (new); 2311381a2a9aSdr146992 } 2312381a2a9aSdr146992 2313381a2a9aSdr146992 /* 23147ddc9b1aSDarren Reed * Function: hook_init_kstats 23157ddc9b1aSDarren Reed * Returns: None 23167ddc9b1aSDarren Reed * Parameters: hfi(I) - pointer to the family that owns the event. 23177ddc9b1aSDarren Reed * hei(I) - pointer to the event that owns this hook 23187ddc9b1aSDarren Reed * hi(I) - pointer to the hook for which we create kstats for 23197ddc9b1aSDarren Reed * 23207ddc9b1aSDarren Reed * Each hook that is registered with this framework has its own kstats 23217ddc9b1aSDarren Reed * set up so that we can provide an easy way in which to observe the 23227ddc9b1aSDarren Reed * look of hooks (using the kstat command.) The position is set to 0 23237ddc9b1aSDarren Reed * here but is recalculated after we know the insertion has been a 23247ddc9b1aSDarren Reed * success. 23257ddc9b1aSDarren Reed */ 23267ddc9b1aSDarren Reed static void 23277ddc9b1aSDarren Reed hook_init_kstats(hook_family_int_t *hfi, hook_event_int_t *hei, hook_int_t *hi) 23287ddc9b1aSDarren Reed { 23297ddc9b1aSDarren Reed hook_hook_kstat_t template = { 23307ddc9b1aSDarren Reed { "version", KSTAT_DATA_INT32 }, 23317ddc9b1aSDarren Reed { "flags", KSTAT_DATA_UINT32 }, 23327ddc9b1aSDarren Reed { "hint", KSTAT_DATA_INT32 }, 2333*652fb50dSRob Gulewich { "hint_value", KSTAT_DATA_STRING }, 23347ddc9b1aSDarren Reed { "position", KSTAT_DATA_INT32 }, 23357ddc9b1aSDarren Reed { "hook_hits", KSTAT_DATA_UINT64 } 23367ddc9b1aSDarren Reed }; 23377ddc9b1aSDarren Reed hook_stack_t *hks; 23387ddc9b1aSDarren Reed size_t kslen; 23397ddc9b1aSDarren Reed int position; 23407ddc9b1aSDarren Reed hook_int_t *h; 23417ddc9b1aSDarren Reed 23427ddc9b1aSDarren Reed kslen = strlen(hfi->hfi_family.hf_name) + 23437ddc9b1aSDarren Reed strlen(hei->hei_event->he_name) + 2; 23447ddc9b1aSDarren Reed 23457ddc9b1aSDarren Reed hi->hi_ksname = (char *)kmem_zalloc(kslen, KM_SLEEP); 23467ddc9b1aSDarren Reed (void) snprintf(hi->hi_ksname, kslen, "%s/%s", 23477ddc9b1aSDarren Reed hfi->hfi_family.hf_name, hei->hei_event->he_name); 23487ddc9b1aSDarren Reed 23497ddc9b1aSDarren Reed hks = hfi->hfi_stack; 23507ddc9b1aSDarren Reed hi->hi_kstatp = kstat_create_netstack(hi->hi_ksname, 0, 23517ddc9b1aSDarren Reed hi->hi_hook.h_name, "hook", KSTAT_TYPE_NAMED, 23527ddc9b1aSDarren Reed sizeof (hi->hi_kstats) / sizeof (kstat_named_t), 23537ddc9b1aSDarren Reed KSTAT_FLAG_VIRTUAL, hks->hks_netstackid); 23547ddc9b1aSDarren Reed 23557ddc9b1aSDarren Reed /* Initialise the kstats for the structure */ 23567ddc9b1aSDarren Reed bcopy(&template, &hi->hi_kstats, sizeof (template)); 23577ddc9b1aSDarren Reed hi->hi_kstats.hook_version.value.i32 = hi->hi_hook.h_version; 23587ddc9b1aSDarren Reed hi->hi_kstats.hook_flags.value.ui32 = hi->hi_hook.h_flags; 23597ddc9b1aSDarren Reed hi->hi_kstats.hook_hint.value.i32 = hi->hi_hook.h_hint; 23607ddc9b1aSDarren Reed hi->hi_kstats.hook_position.value.i32 = 0; 23617ddc9b1aSDarren Reed hi->hi_kstats.hook_hits.value.ui64 = 0; 23627ddc9b1aSDarren Reed 23637ddc9b1aSDarren Reed switch (hi->hi_hook.h_hint) { 23647ddc9b1aSDarren Reed case HH_BEFORE : 23657ddc9b1aSDarren Reed case HH_AFTER : 2366*652fb50dSRob Gulewich kstat_named_setstr(&(hi->hi_kstats.hook_hintvalue), 2367*652fb50dSRob Gulewich (const char *)hi->hi_hook.h_hintvalue); 23687ddc9b1aSDarren Reed break; 23697ddc9b1aSDarren Reed default : 2370*652fb50dSRob Gulewich kstat_named_setstr(&(hi->hi_kstats.hook_hintvalue), 2371*652fb50dSRob Gulewich hook_hintvalue_none); 23727ddc9b1aSDarren Reed break; 23737ddc9b1aSDarren Reed } 23747ddc9b1aSDarren Reed 23757ddc9b1aSDarren Reed if (hi->hi_kstatp != NULL) { 23767ddc9b1aSDarren Reed hi->hi_kstatp->ks_data = (void *)&hi->hi_kstats; 23777ddc9b1aSDarren Reed hi->hi_kstatp->ks_private = 23787ddc9b1aSDarren Reed (void *)(uintptr_t)hks->hks_netstackid; 2379*652fb50dSRob Gulewich hi->hi_kstatp->ks_data_size += 2380*652fb50dSRob Gulewich KSTAT_NAMED_STR_BUFLEN(&(hi->hi_kstats.hook_hintvalue)) + 1; 23817ddc9b1aSDarren Reed 23827ddc9b1aSDarren Reed kstat_install(hi->hi_kstatp); 23837ddc9b1aSDarren Reed } 23847ddc9b1aSDarren Reed 23857ddc9b1aSDarren Reed position = 1; 23867ddc9b1aSDarren Reed TAILQ_FOREACH(h, &hei->hei_head, hi_entry) { 23877ddc9b1aSDarren Reed h->hi_kstats.hook_position.value.ui32 = position++; 23887ddc9b1aSDarren Reed } 23897ddc9b1aSDarren Reed } 23907ddc9b1aSDarren Reed 23917ddc9b1aSDarren Reed /* 23927ddc9b1aSDarren Reed * Function: hook_int_free 2393381a2a9aSdr146992 * Returns: None 2394381a2a9aSdr146992 * Parameters: hi(I) - internal hook pointer 2395381a2a9aSdr146992 * 23964a9b8375SDarren Reed * Free memory allocated to support a hook. 2397381a2a9aSdr146992 */ 2398381a2a9aSdr146992 static void 23997ddc9b1aSDarren Reed hook_int_free(hook_int_t *hi, netstackid_t stackid) 2400381a2a9aSdr146992 { 24017ddc9b1aSDarren Reed int len; 24027ddc9b1aSDarren Reed 2403381a2a9aSdr146992 ASSERT(hi != NULL); 2404381a2a9aSdr146992 2405381a2a9aSdr146992 /* Free name space */ 2406381a2a9aSdr146992 if (hi->hi_hook.h_name != NULL) { 2407381a2a9aSdr146992 kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1); 2408381a2a9aSdr146992 } 24097ddc9b1aSDarren Reed if (hi->hi_ksname != NULL) { 24107ddc9b1aSDarren Reed kmem_free(hi->hi_ksname, strlen(hi->hi_ksname) + 1); 24117ddc9b1aSDarren Reed } 24127ddc9b1aSDarren Reed 24137ddc9b1aSDarren Reed /* Free the name used with the before/after hints. */ 24147ddc9b1aSDarren Reed switch (hi->hi_hook.h_hint) { 24157ddc9b1aSDarren Reed case HH_BEFORE : 24167ddc9b1aSDarren Reed case HH_AFTER : 24177ddc9b1aSDarren Reed len = strlen((char *)hi->hi_hook.h_hintvalue); 24187ddc9b1aSDarren Reed if (len > 0) 24197ddc9b1aSDarren Reed kmem_free((void *)hi->hi_hook.h_hintvalue, len + 1); 24207ddc9b1aSDarren Reed break; 24217ddc9b1aSDarren Reed default : 24227ddc9b1aSDarren Reed break; 24237ddc9b1aSDarren Reed } 24247ddc9b1aSDarren Reed 24257ddc9b1aSDarren Reed if (hi->hi_kstatp != NULL) 24267ddc9b1aSDarren Reed kstat_delete_netstack(hi->hi_kstatp, stackid); 2427381a2a9aSdr146992 2428381a2a9aSdr146992 /* Free container */ 2429381a2a9aSdr146992 kmem_free(hi, sizeof (*hi)); 2430381a2a9aSdr146992 } 24317ddc9b1aSDarren Reed 24327ddc9b1aSDarren Reed /* 24337ddc9b1aSDarren Reed * Function: hook_alloc 24347ddc9b1aSDarren Reed * Returns: hook_t * - pointer to new hook structure 24357ddc9b1aSDarren Reed * Parameters: version(I) - version number of the API when compiled 24367ddc9b1aSDarren Reed * 24377ddc9b1aSDarren Reed * This function serves as the interface for consumers to obtain a hook_t 24387ddc9b1aSDarren Reed * structure. At this point in time, there is only a single "version" of 24397ddc9b1aSDarren Reed * it, leading to a straight forward function. In a perfect world the 24407ddc9b1aSDarren Reed * h_vesion would be a protected data structure member, but C isn't that 24417ddc9b1aSDarren Reed * advanced... 24427ddc9b1aSDarren Reed */ 24437ddc9b1aSDarren Reed hook_t * 24447ddc9b1aSDarren Reed hook_alloc(const int h_version) 24457ddc9b1aSDarren Reed { 24467ddc9b1aSDarren Reed hook_t *h; 24477ddc9b1aSDarren Reed 24487ddc9b1aSDarren Reed h = kmem_zalloc(sizeof (hook_t), KM_SLEEP); 24497ddc9b1aSDarren Reed h->h_version = h_version; 24507ddc9b1aSDarren Reed return (h); 24517ddc9b1aSDarren Reed } 24527ddc9b1aSDarren Reed 24537ddc9b1aSDarren Reed /* 24547ddc9b1aSDarren Reed * Function: hook_free 24557ddc9b1aSDarren Reed * Returns: None 24567ddc9b1aSDarren Reed * Parameters: h(I) - external hook pointer 24577ddc9b1aSDarren Reed * 24587ddc9b1aSDarren Reed * This function only free's memory allocated with hook_alloc(), so that if 24597ddc9b1aSDarren Reed * (for example) kernel memory was allocated for h_name, this needs to be 24607ddc9b1aSDarren Reed * free'd before calling hook_free(). 24617ddc9b1aSDarren Reed */ 24627ddc9b1aSDarren Reed void 24637ddc9b1aSDarren Reed hook_free(hook_t *h) 24647ddc9b1aSDarren Reed { 24657ddc9b1aSDarren Reed kmem_free(h, sizeof (*h)); 24667ddc9b1aSDarren Reed } 24677ddc9b1aSDarren Reed 24687ddc9b1aSDarren Reed /* 24697ddc9b1aSDarren Reed * Function: hook_notify_register 24704a9b8375SDarren Reed * Returns: int - 0 = success, else failure 24714a9b8375SDarren Reed * Parameters: head(I) - top of the list of callbacks 24727ddc9b1aSDarren Reed * callback(I) - function to be called 24737ddc9b1aSDarren Reed * arg(I) - arg to pass back to the function 24747ddc9b1aSDarren Reed * 24757ddc9b1aSDarren Reed * This function implements the modification of the list of callbacks 24767ddc9b1aSDarren Reed * that are registered when someone wants to be advised of a change 24777ddc9b1aSDarren Reed * that has happened. 24787ddc9b1aSDarren Reed */ 24797ddc9b1aSDarren Reed static int 24804a9b8375SDarren Reed hook_notify_register(hook_notify_head_t *head, hook_notify_fn_t callback, 24814a9b8375SDarren Reed void *arg) 24827ddc9b1aSDarren Reed { 24837ddc9b1aSDarren Reed hook_notify_t *hn; 24847ddc9b1aSDarren Reed 24857ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 24867ddc9b1aSDarren Reed if (hn->hn_func == callback) { 24877ddc9b1aSDarren Reed return (EEXIST); 24887ddc9b1aSDarren Reed } 24897ddc9b1aSDarren Reed } 24907ddc9b1aSDarren Reed 24917ddc9b1aSDarren Reed hn = (hook_notify_t *)kmem_alloc(sizeof (*hn), KM_SLEEP); 24927ddc9b1aSDarren Reed hn->hn_func = callback; 24937ddc9b1aSDarren Reed hn->hn_arg = arg; 24947ddc9b1aSDarren Reed TAILQ_INSERT_TAIL(head, hn, hn_entry); 24957ddc9b1aSDarren Reed 24967ddc9b1aSDarren Reed return (0); 24977ddc9b1aSDarren Reed } 24987ddc9b1aSDarren Reed 24997ddc9b1aSDarren Reed /* 25004a9b8375SDarren Reed * Function: hook_notify_unregister 25014a9b8375SDarren Reed * Returns: int - 0 = success, else failure 25027ddc9b1aSDarren Reed * Parameters: stackid(I) - netstack identifier 25037ddc9b1aSDarren Reed * callback(I) - function to be called 25044a9b8375SDarren Reed * parg(O) - pointer to storage for pointer 25057ddc9b1aSDarren Reed * 25064a9b8375SDarren Reed * When calling this function, the provision of a valid pointer in parg 25074a9b8375SDarren Reed * allows the caller to be made aware of what argument the hook function 25084a9b8375SDarren Reed * was expecting. This then allows the simulation of HN_UNREGISTER events 25094a9b8375SDarren Reed * when a notify-unregister is performed. 25107ddc9b1aSDarren Reed */ 25117ddc9b1aSDarren Reed static int 25124a9b8375SDarren Reed hook_notify_unregister(hook_notify_head_t *head, 25134a9b8375SDarren Reed hook_notify_fn_t callback, void **parg) 25147ddc9b1aSDarren Reed { 25157ddc9b1aSDarren Reed hook_notify_t *hn; 25167ddc9b1aSDarren Reed 25174a9b8375SDarren Reed ASSERT(parg != NULL); 25187ddc9b1aSDarren Reed 25197ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 25207ddc9b1aSDarren Reed if (hn->hn_func == callback) 25217ddc9b1aSDarren Reed break; 25227ddc9b1aSDarren Reed } 25234a9b8375SDarren Reed 25244a9b8375SDarren Reed if (hn == NULL) 25257ddc9b1aSDarren Reed return (ESRCH); 25264a9b8375SDarren Reed 25274a9b8375SDarren Reed *parg = hn->hn_arg; 25287ddc9b1aSDarren Reed 25297ddc9b1aSDarren Reed TAILQ_REMOVE(head, hn, hn_entry); 25307ddc9b1aSDarren Reed 25317ddc9b1aSDarren Reed kmem_free(hn, sizeof (*hn)); 25327ddc9b1aSDarren Reed 25337ddc9b1aSDarren Reed return (0); 25347ddc9b1aSDarren Reed } 25357ddc9b1aSDarren Reed 25367ddc9b1aSDarren Reed /* 25377ddc9b1aSDarren Reed * Function: hook_notify_run 25387ddc9b1aSDarren Reed * Returns: None 25397ddc9b1aSDarren Reed * Parameters: head(I) - top of the list of callbacks 25407ddc9b1aSDarren Reed * family(I) - name of the hook family that owns the event 25417ddc9b1aSDarren Reed * event(I) - name of the event being changed 25427ddc9b1aSDarren Reed * name(I) - name of the object causing change 25437ddc9b1aSDarren Reed * cmd(I) - either HN_UNREGISTER or HN_REGISTER 25447ddc9b1aSDarren Reed * 25457ddc9b1aSDarren Reed * This function walks through the list of registered callbacks and 25467ddc9b1aSDarren Reed * executes each one, passing back the arg supplied when registered 25477ddc9b1aSDarren Reed * and the name of the family (that owns the event), event (the thing 25487ddc9b1aSDarren Reed * to which we're making a change) and finally a name that describes 25497ddc9b1aSDarren Reed * what is being added or removed, as indicated by cmd. 25507ddc9b1aSDarren Reed * 25517ddc9b1aSDarren Reed * This function does not acquire or release any lock as it is required 25527ddc9b1aSDarren Reed * that code calling it do so before hand. The use of hook_notify_head_t 25537ddc9b1aSDarren Reed * is protected by the use of flagwait_t in the structures that own this 25547ddc9b1aSDarren Reed * list and with the use of the FWF_ADD/DEL_ACTIVE flags. 25557ddc9b1aSDarren Reed */ 25567ddc9b1aSDarren Reed static void 25577ddc9b1aSDarren Reed hook_notify_run(hook_notify_head_t *head, char *family, char *event, 25587ddc9b1aSDarren Reed char *name, hook_notify_cmd_t cmd) 25597ddc9b1aSDarren Reed { 25607ddc9b1aSDarren Reed hook_notify_t *hn; 25617ddc9b1aSDarren Reed 25627ddc9b1aSDarren Reed TAILQ_FOREACH(hn, head, hn_entry) { 25637ddc9b1aSDarren Reed (*hn->hn_func)(cmd, hn->hn_arg, family, event, name); 25647ddc9b1aSDarren Reed } 25657ddc9b1aSDarren Reed } 2566