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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
hook_init(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
hook_fini(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
hook_wait_setflag(flagwait_t * waiter,uint32_t busyset,fwflag_t wanted,fwflag_t newflag)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
hook_wait_unsetflag(flagwait_t * waiter,fwflag_t oldflag)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
hook_wait_destroy(flagwait_t * waiter)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
hook_wait_init(flagwait_t * waiter,cvwaitlock_t * owner)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 *
hook_stack_init(netstackid_t stackid,netstack_t * ns)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
hook_stack_shutdown(netstackid_t stackid,void * arg)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
hook_stack_fini(netstackid_t stackid,void * arg)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
hook_stack_remove(hook_stack_t * hks)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 *
hook_stack_get(netstackid_t stackid)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
hook_stack_notify_register(netstackid_t stackid,hook_notify_fn_t callback,void * arg)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
hook_stack_notify_unregister(netstackid_t stackid,hook_notify_fn_t callback)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
hook_stack_notify_run(hook_stack_t * hks,char * name,hook_notify_cmd_t cmd)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
hook_run(hook_family_int_t * hfi,hook_event_token_t token,hook_data_t info)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 *
hook_family_add(hook_family_t * hf,hook_stack_t * hks,void ** store)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
hook_family_remove(hook_family_int_t * hfi)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
hook_family_free(hook_family_int_t * hfi,hook_stack_t * hks)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
hook_family_shutdown(hook_family_int_t * hfi)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 *
hook_family_copy(hook_family_t * src)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 *
hook_family_find(char * family,hook_stack_t * hks)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
hook_family_notify_register(hook_family_int_t * hfi,hook_notify_fn_t callback,void * arg)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
hook_family_notify_unregister(hook_family_int_t * hfi,hook_notify_fn_t callback)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 *
hook_event_add(hook_family_int_t * hfi,hook_event_t * he)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
hook_event_init_kstats(hook_family_int_t * hfi,hook_event_int_t * hei)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
hook_event_remove(hook_family_int_t * hfi,hook_event_t * he)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
hook_event_shutdown(hook_family_int_t * hfi,hook_event_t * he)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
hook_event_free(hook_event_int_t * hei,hook_family_int_t * hfi)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 *
hook_event_checkdup(hook_event_t * he,hook_stack_t * hks)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 *
hook_event_copy(hook_event_t * src)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 *
hook_event_find(hook_family_int_t * hfi,char * event)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
hook_event_notify_register(hook_family_int_t * hfi,char * event,hook_notify_fn_t callback,void * arg)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
hook_event_notify_unregister(hook_family_int_t * hfi,char * event,hook_notify_fn_t callback)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
hook_event_notify_run(hook_event_int_t * hei,hook_family_int_t * hfi,char * event,char * name,hook_notify_cmd_t cmd)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
hook_register(hook_family_int_t * hfi,char * event,hook_t * h)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
hook_insert(hook_int_head_t * head,hook_int_t * new)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
hook_insert_plain(hook_int_head_t * head,hook_int_t * new)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
hook_insert_afterbefore(hook_int_head_t * head,hook_int_t * new)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
hook_unregister(hook_family_int_t * hfi,char * event,hook_t * h)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 *
hook_find_byname(hook_int_head_t * head,char * name)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 *
hook_find(hook_event_int_t * hei,hook_t * h)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 *
hook_copy(hook_t * src)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
hook_init_kstats(hook_family_int_t * hfi,hook_event_int_t * hei,hook_int_t * hi)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
hook_int_free(hook_int_t * hi,netstackid_t stackid)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 *
hook_alloc(const int h_version)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
hook_free(hook_t * h)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
hook_notify_register(hook_notify_head_t * head,hook_notify_fn_t callback,void * arg)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
hook_notify_unregister(hook_notify_head_t * head,hook_notify_fn_t callback,void ** parg)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
hook_notify_run(hook_notify_head_t * head,char * family,char * event,char * name,hook_notify_cmd_t cmd)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