xref: /onnv-gate/usr/src/uts/common/io/hook.c (revision 3448:aaf16568054b)
12958Sdr146992 /*
22958Sdr146992  * CDDL HEADER START
32958Sdr146992  *
42958Sdr146992  * The contents of this file are subject to the terms of the
52958Sdr146992  * Common Development and Distribution License (the "License").
62958Sdr146992  * You may not use this file except in compliance with the License.
72958Sdr146992  *
82958Sdr146992  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92958Sdr146992  * or http://www.opensolaris.org/os/licensing.
102958Sdr146992  * See the License for the specific language governing permissions
112958Sdr146992  * and limitations under the License.
122958Sdr146992  *
132958Sdr146992  * When distributing Covered Code, include this CDDL HEADER in each
142958Sdr146992  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152958Sdr146992  * If applicable, add the following below this CDDL HEADER, with the
162958Sdr146992  * fields enclosed by brackets "[]" replaced with your own identifying
172958Sdr146992  * information: Portions Copyright [yyyy] [name of copyright owner]
182958Sdr146992  *
192958Sdr146992  * CDDL HEADER END
202958Sdr146992  */
212958Sdr146992 /*
22*3448Sdh155122  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
232958Sdr146992  * Use is subject to license terms.
242958Sdr146992  */
252958Sdr146992 #pragma ident	"%Z%%M%	%I%	%E% SMI"
262958Sdr146992 
272958Sdr146992 #include <sys/param.h>
282958Sdr146992 #include <sys/types.h>
292958Sdr146992 #include <sys/systm.h>
302958Sdr146992 #include <sys/errno.h>
312958Sdr146992 #include <sys/kmem.h>
322958Sdr146992 #include <sys/mutex.h>
332958Sdr146992 #include <sys/condvar.h>
342958Sdr146992 #include <sys/modctl.h>
352958Sdr146992 #include <sys/hook_impl.h>
362958Sdr146992 #include <sys/sdt.h>
372958Sdr146992 
382958Sdr146992 /*
392958Sdr146992  * This file provides kernel hook framework.
402958Sdr146992  */
412958Sdr146992 
422958Sdr146992 static struct modldrv modlmisc = {
432958Sdr146992 	&mod_miscops,				/* drv_modops */
442958Sdr146992 	"Hooks Interface v1.0",			/* drv_linkinfo */
452958Sdr146992 };
462958Sdr146992 
472958Sdr146992 static struct modlinkage modlinkage = {
482958Sdr146992 	MODREV_1,				/* ml_rev */
492958Sdr146992 	&modlmisc,				/* ml_linkage */
502958Sdr146992 	NULL
512958Sdr146992 };
522958Sdr146992 
532958Sdr146992 /*
542958Sdr146992  * Hook internal functions
552958Sdr146992  */
562958Sdr146992 static hook_int_t *hook_copy(hook_t *src);
57*3448Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
58*3448Sdh155122     hook_stack_t *hks);
592958Sdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
602958Sdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
612958Sdr146992 static void hook_event_free(hook_event_int_t *hei);
622958Sdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
63*3448Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
642958Sdr146992 static void hook_family_free(hook_family_int_t *hfi);
652958Sdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
662958Sdr146992 static void hook_free(hook_int_t *hi);
672958Sdr146992 static void hook_init(void);
68*3448Sdh155122 static void hook_fini(void);
69*3448Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
70*3448Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
712958Sdr146992 
722958Sdr146992 /*
732958Sdr146992  * Module entry points.
742958Sdr146992  */
752958Sdr146992 int
762958Sdr146992 _init(void)
772958Sdr146992 {
78*3448Sdh155122 	int error;
79*3448Sdh155122 
802958Sdr146992 	hook_init();
81*3448Sdh155122 	error = mod_install(&modlinkage);
82*3448Sdh155122 	if (error != 0)
83*3448Sdh155122 		hook_fini();
84*3448Sdh155122 
85*3448Sdh155122 	return (error);
862958Sdr146992 }
872958Sdr146992 
882958Sdr146992 
892958Sdr146992 int
902958Sdr146992 _fini(void)
912958Sdr146992 {
92*3448Sdh155122 	int error;
93*3448Sdh155122 
94*3448Sdh155122 	error = mod_remove(&modlinkage);
95*3448Sdh155122 	if (error == 0)
96*3448Sdh155122 		hook_fini();
97*3448Sdh155122 
98*3448Sdh155122 	return (error);
992958Sdr146992 }
1002958Sdr146992 
1012958Sdr146992 
1022958Sdr146992 int
1032958Sdr146992 _info(struct modinfo *modinfop)
1042958Sdr146992 {
1052958Sdr146992 	return (mod_info(&modlinkage, modinfop));
1062958Sdr146992 }
1072958Sdr146992 
1082958Sdr146992 
1092958Sdr146992 /*
1102958Sdr146992  * Function:	hook_init
1112958Sdr146992  * Returns:	None
1122958Sdr146992  * Parameters:	None
1132958Sdr146992  *
1142958Sdr146992  * Initialize hooks
1152958Sdr146992  */
1162958Sdr146992 static void
1172958Sdr146992 hook_init(void)
1182958Sdr146992 {
119*3448Sdh155122 	/*
120*3448Sdh155122 	 * We want to be informed each time a stack is created or
121*3448Sdh155122 	 * destroyed in the kernel.
122*3448Sdh155122 	 */
123*3448Sdh155122 	netstack_register(NS_HOOK, hook_stack_init, NULL,
124*3448Sdh155122 	    hook_stack_fini);
125*3448Sdh155122 }
126*3448Sdh155122 
127*3448Sdh155122 /*
128*3448Sdh155122  * Function:	hook_fini
129*3448Sdh155122  * Returns:	None
130*3448Sdh155122  * Parameters:	None
131*3448Sdh155122  *
132*3448Sdh155122  * Deinitialize hooks
133*3448Sdh155122  */
134*3448Sdh155122 static void
135*3448Sdh155122 hook_fini(void)
136*3448Sdh155122 {
137*3448Sdh155122 	netstack_unregister(NS_HOOK);
1382958Sdr146992 }
1392958Sdr146992 
140*3448Sdh155122 /*
141*3448Sdh155122  * Initialize the hook stack instance.
142*3448Sdh155122  */
143*3448Sdh155122 /*ARGSUSED*/
144*3448Sdh155122 static void *
145*3448Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
146*3448Sdh155122 {
147*3448Sdh155122 	hook_stack_t	*hks;
148*3448Sdh155122 
149*3448Sdh155122 #ifdef NS_DEBUG
150*3448Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
151*3448Sdh155122 #endif
152*3448Sdh155122 
153*3448Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
154*3448Sdh155122 	hks->hk_netstack = ns;
155*3448Sdh155122 
156*3448Sdh155122 	CVW_INIT(&hks->hks_familylock);
157*3448Sdh155122 	SLIST_INIT(&hks->hks_familylist);
158*3448Sdh155122 
159*3448Sdh155122 	return (hks);
160*3448Sdh155122 }
161*3448Sdh155122 
162*3448Sdh155122 /*
163*3448Sdh155122  * Free the hook stack instance.
164*3448Sdh155122  */
165*3448Sdh155122 /*ARGSUSED*/
166*3448Sdh155122 static void
167*3448Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
168*3448Sdh155122 {
169*3448Sdh155122 	hook_stack_t	*hks = (hook_stack_t *)arg;
170*3448Sdh155122 #ifdef NS_DEBUG
171*3448Sdh155122 	printf("hook_stack_fini(%p, stack %d)\n", arg, stackid);
172*3448Sdh155122 #endif
173*3448Sdh155122 	CVW_DESTROY(&hks->hks_familylock);
174*3448Sdh155122 	kmem_free(hks, sizeof (*hks));
175*3448Sdh155122 }
1762958Sdr146992 
1772958Sdr146992 /*
1782958Sdr146992  * Function:	hook_run
1792958Sdr146992  * Returns:	int - return value according to callback func
1802958Sdr146992  * Parameters:	token(I) - event pointer
1812958Sdr146992  *		info(I) - message
1822958Sdr146992  *
1832958Sdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
1842958Sdr146992  * until either the end of the list is reached or a hook function returns a
1852958Sdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
1862958Sdr146992  * return that value back to our caller.  By design, a hook function can be
1872958Sdr146992  * called more than once, simultaneously.
1882958Sdr146992  */
1892958Sdr146992 int
190*3448Sdh155122 hook_run(hook_event_token_t token, hook_data_t info, netstack_t *ns)
1912958Sdr146992 {
1922958Sdr146992 	hook_int_t *hi;
1932958Sdr146992 	hook_event_int_t *hei;
194*3448Sdh155122 	hook_stack_t *hks = ns->netstack_hook;
1952958Sdr146992 	int rval = 0;
1962958Sdr146992 
1972958Sdr146992 	ASSERT(token != NULL);
1982958Sdr146992 
1992958Sdr146992 	hei = (hook_event_int_t *)token;
2002958Sdr146992 	DTRACE_PROBE2(hook__run__start,
2012958Sdr146992 	    hook_event_token_t, token,
2022958Sdr146992 	    hook_data_t, info);
2032958Sdr146992 
2042958Sdr146992 	/* Hold global read lock to ensure event will not be deleted */
205*3448Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
2062958Sdr146992 
2072958Sdr146992 	/* Hold event read lock to ensure hook will not be changed */
2082958Sdr146992 	CVW_ENTER_READ(&hei->hei_lock);
2092958Sdr146992 
2102958Sdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
2112958Sdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
2122958Sdr146992 		DTRACE_PROBE3(hook__func__start,
2132958Sdr146992 		    hook_event_token_t, token,
2142958Sdr146992 		    hook_data_t, info,
2152958Sdr146992 		    hook_int_t *, hi);
216*3448Sdh155122 		rval = (*hi->hi_hook.h_func)(token, info, ns);
2172958Sdr146992 		DTRACE_PROBE4(hook__func__end,
2182958Sdr146992 		    hook_event_token_t, token,
2192958Sdr146992 		    hook_data_t, info,
2202958Sdr146992 		    hook_int_t *, hi,
2212958Sdr146992 		    int, rval);
2222958Sdr146992 		if (rval != 0)
2232958Sdr146992 			break;
2242958Sdr146992 	}
2252958Sdr146992 
2262958Sdr146992 	CVW_EXIT_READ(&hei->hei_lock);
227*3448Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
2282958Sdr146992 
2292958Sdr146992 	DTRACE_PROBE3(hook__run__end,
2302958Sdr146992 	    hook_event_token_t, token,
2312958Sdr146992 	    hook_data_t, info,
2322958Sdr146992 	    hook_int_t *, hi);
2332958Sdr146992 
2342958Sdr146992 	return (rval);
2352958Sdr146992 }
2362958Sdr146992 
2372958Sdr146992 
2382958Sdr146992 /*
2392958Sdr146992  * Function:	hook_family_add
2402958Sdr146992  * Returns:	internal family pointer - NULL = Fail
2412958Sdr146992  * Parameters:	hf(I) - family pointer
2422958Sdr146992  *
2432958Sdr146992  * Add new family to family list
2442958Sdr146992  */
2452958Sdr146992 hook_family_int_t *
246*3448Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks)
2472958Sdr146992 {
2482958Sdr146992 	hook_family_int_t *hfi, *new;
2492958Sdr146992 
2502958Sdr146992 	ASSERT(hf != NULL);
2512958Sdr146992 	ASSERT(hf->hf_name != NULL);
2522958Sdr146992 
2532958Sdr146992 	new = hook_family_copy(hf);
2542958Sdr146992 	if (new == NULL)
2552958Sdr146992 		return (NULL);
2562958Sdr146992 
257*3448Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
2582958Sdr146992 
2592958Sdr146992 	/* search family list */
260*3448Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
2612958Sdr146992 	if (hfi != NULL) {
262*3448Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
2632958Sdr146992 		hook_family_free(new);
2642958Sdr146992 		return (NULL);
2652958Sdr146992 	}
2662958Sdr146992 
267*3448Sdh155122 	new->hfi_ptr = (void *)hks;
268*3448Sdh155122 
2692958Sdr146992 	/* Add to family list head */
270*3448Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
2712958Sdr146992 
272*3448Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
2732958Sdr146992 	return (new);
2742958Sdr146992 }
2752958Sdr146992 
2762958Sdr146992 
2772958Sdr146992 /*
2782958Sdr146992  * Function:	hook_family_remove
2792958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
2802958Sdr146992  * Parameters:	hfi(I) - internal family pointer
2812958Sdr146992  *
2822958Sdr146992  * Remove family from family list
2832958Sdr146992  */
2842958Sdr146992 int
2852958Sdr146992 hook_family_remove(hook_family_int_t *hfi)
2862958Sdr146992 {
287*3448Sdh155122 	hook_stack_t *hks;
2882958Sdr146992 
2892958Sdr146992 	ASSERT(hfi != NULL);
290*3448Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
2912958Sdr146992 
292*3448Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
2932958Sdr146992 
2942958Sdr146992 	/* Check if there are events  */
2952958Sdr146992 	if (!SLIST_EMPTY(&hfi->hfi_head)) {
296*3448Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
2972958Sdr146992 		return (EBUSY);
2982958Sdr146992 	}
2992958Sdr146992 
3002958Sdr146992 	/* Remove from family list */
301*3448Sdh155122 	SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, hfi_entry);
3022958Sdr146992 
303*3448Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
3042958Sdr146992 	hook_family_free(hfi);
3052958Sdr146992 
3062958Sdr146992 	return (0);
3072958Sdr146992 }
3082958Sdr146992 
3092958Sdr146992 
3102958Sdr146992 /*
3112958Sdr146992  * Function:	hook_family_copy
3122958Sdr146992  * Returns:	internal family pointer - NULL = Failed
3132958Sdr146992  * Parameters:	src(I) - family pointer
3142958Sdr146992  *
3152958Sdr146992  * Allocate internal family block and duplicate incoming family
3162958Sdr146992  * No locks should be held across this function as it may sleep.
3172958Sdr146992  */
3182958Sdr146992 static hook_family_int_t *
3192958Sdr146992 hook_family_copy(hook_family_t *src)
3202958Sdr146992 {
3212958Sdr146992 	hook_family_int_t *new;
3222958Sdr146992 	hook_family_t *dst;
3232958Sdr146992 
3242958Sdr146992 	ASSERT(src != NULL);
3252958Sdr146992 	ASSERT(src->hf_name != NULL);
3262958Sdr146992 
3272958Sdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
3282958Sdr146992 
3292958Sdr146992 	/* Copy body */
3302958Sdr146992 	SLIST_INIT(&new->hfi_head);
3312958Sdr146992 	dst = &new->hfi_family;
3322958Sdr146992 	*dst = *src;
3332958Sdr146992 
3342958Sdr146992 	/* Copy name */
3352958Sdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
3362958Sdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
3372958Sdr146992 
3382958Sdr146992 	return (new);
3392958Sdr146992 }
3402958Sdr146992 
3412958Sdr146992 
3422958Sdr146992 /*
3432958Sdr146992  * Returns:	internal family pointer - NULL = Not match
3442958Sdr146992  * Parameters:	family(I) - family name string
3452958Sdr146992  *
3462958Sdr146992  * Search family list with family name
3472958Sdr146992  * 	A lock on familylock must be held when called.
3482958Sdr146992  */
3492958Sdr146992 static hook_family_int_t *
350*3448Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
3512958Sdr146992 {
3522958Sdr146992 	hook_family_int_t *hfi = NULL;
3532958Sdr146992 
3542958Sdr146992 	ASSERT(family != NULL);
3552958Sdr146992 
356*3448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
3572958Sdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
3582958Sdr146992 			break;
3592958Sdr146992 	}
3602958Sdr146992 	return (hfi);
3612958Sdr146992 }
3622958Sdr146992 
3632958Sdr146992 
3642958Sdr146992 /*
3652958Sdr146992  * Function:	hook_family_free
3662958Sdr146992  * Returns:	None
3672958Sdr146992  * Parameters:	hfi(I) - internal family pointer
3682958Sdr146992  *
3692958Sdr146992  * Free alloc memory for family
3702958Sdr146992  */
3712958Sdr146992 static void
3722958Sdr146992 hook_family_free(hook_family_int_t *hfi)
3732958Sdr146992 {
3742958Sdr146992 	ASSERT(hfi != NULL);
3752958Sdr146992 
3762958Sdr146992 	/* Free name space */
3772958Sdr146992 	if (hfi->hfi_family.hf_name != NULL) {
3782958Sdr146992 		kmem_free(hfi->hfi_family.hf_name,
3792958Sdr146992 		    strlen(hfi->hfi_family.hf_name) + 1);
3802958Sdr146992 	}
3812958Sdr146992 
3822958Sdr146992 	/* Free container */
3832958Sdr146992 	kmem_free(hfi, sizeof (*hfi));
3842958Sdr146992 }
3852958Sdr146992 
3862958Sdr146992 
3872958Sdr146992 /*
3882958Sdr146992  * Function:	hook_event_add
3892958Sdr146992  * Returns:	internal event pointer - NULL = Fail
3902958Sdr146992  * Parameters:	hfi(I) - internal family pointer
3912958Sdr146992  *		he(I) - event pointer
3922958Sdr146992  *
3932958Sdr146992  * Add new event to event list on specific family.
3942958Sdr146992  * This function can fail to return successfully if (1) it cannot allocate
3952958Sdr146992  * enough memory for its own internal data structures, (2) the event has
3962958Sdr146992  * already been registered (for any hook family.)
3972958Sdr146992  */
3982958Sdr146992 hook_event_int_t *
3992958Sdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
4002958Sdr146992 {
401*3448Sdh155122 	hook_stack_t *hks;
4022958Sdr146992 	hook_event_int_t *hei, *new;
4032958Sdr146992 
4042958Sdr146992 	ASSERT(hfi != NULL);
4052958Sdr146992 	ASSERT(he != NULL);
4062958Sdr146992 	ASSERT(he->he_name != NULL);
407*3448Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
4082958Sdr146992 
4092958Sdr146992 	new = hook_event_copy(he);
4102958Sdr146992 	if (new == NULL)
4112958Sdr146992 		return (NULL);
4122958Sdr146992 
413*3448Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
4142958Sdr146992 
4152958Sdr146992 	/* Check whether this event pointer is already registered */
416*3448Sdh155122 	hei = hook_event_checkdup(he, hks);
4172958Sdr146992 	if (hei != NULL) {
418*3448Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
4192958Sdr146992 		hook_event_free(new);
4202958Sdr146992 		return (NULL);
4212958Sdr146992 	}
4222958Sdr146992 
4232958Sdr146992 	/* Add to event list head */
4242958Sdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
4252958Sdr146992 
426*3448Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
4272958Sdr146992 	return (new);
4282958Sdr146992 }
4292958Sdr146992 
4302958Sdr146992 
4312958Sdr146992 /*
4322958Sdr146992  * Function:	hook_event_remove
4332958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
4342958Sdr146992  * Parameters:	hfi(I) - internal family pointer
4352958Sdr146992  *		he(I) - event pointer
4362958Sdr146992  *
4372958Sdr146992  * Remove event from event list on specific family
4382958Sdr146992  */
4392958Sdr146992 int
4402958Sdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
4412958Sdr146992 {
442*3448Sdh155122 	hook_stack_t *hks;
4432958Sdr146992 	hook_event_int_t *hei;
4442958Sdr146992 
4452958Sdr146992 	ASSERT(hfi != NULL);
4462958Sdr146992 	ASSERT(he != NULL);
447*3448Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
4482958Sdr146992 
449*3448Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
4502958Sdr146992 
4512958Sdr146992 	hei = hook_event_find(hfi, he->he_name);
4522958Sdr146992 	if (hei == NULL) {
453*3448Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
4542958Sdr146992 		return (ENXIO);
4552958Sdr146992 	}
4562958Sdr146992 
4572958Sdr146992 	/* Check if there are registered hooks for this event */
4582958Sdr146992 	if (!TAILQ_EMPTY(&hei->hei_head)) {
459*3448Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
4602958Sdr146992 		return (EBUSY);
4612958Sdr146992 	}
4622958Sdr146992 
4632958Sdr146992 	/* Remove from event list */
4642958Sdr146992 	SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
4652958Sdr146992 
466*3448Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
4672958Sdr146992 	hook_event_free(hei);
4682958Sdr146992 
4692958Sdr146992 	return (0);
4702958Sdr146992 }
4712958Sdr146992 
4722958Sdr146992 
4732958Sdr146992 /*
4742958Sdr146992  * Function:    hook_event_checkdup
4752958Sdr146992  * Returns:     internal event pointer - NULL = Not match
4762958Sdr146992  * Parameters:  he(I) - event pointer
4772958Sdr146992  *
4782958Sdr146992  * Search whole list with event pointer
4792958Sdr146992  *      A lock on familylock must be held when called.
4802958Sdr146992  */
4812958Sdr146992 static hook_event_int_t *
482*3448Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
4832958Sdr146992 {
4842958Sdr146992 	hook_family_int_t *hfi;
4852958Sdr146992 	hook_event_int_t *hei;
4862958Sdr146992 
4872958Sdr146992 	ASSERT(he != NULL);
4882958Sdr146992 
489*3448Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
4902958Sdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
4912958Sdr146992 			if (hei->hei_event == he)
4922958Sdr146992 				return (hei);
4932958Sdr146992 		}
4942958Sdr146992 	}
4952958Sdr146992 
4962958Sdr146992 	return (NULL);
4972958Sdr146992 }
4982958Sdr146992 
4992958Sdr146992 
5002958Sdr146992 /*
5012958Sdr146992  * Function:	hook_event_copy
5022958Sdr146992  * Returns:	internal event pointer - NULL = Failed
5032958Sdr146992  * Parameters:	src(I) - event pointer
5042958Sdr146992  *
5052958Sdr146992  * Allocate internal event block and duplicate incoming event
5062958Sdr146992  * No locks should be held across this function as it may sleep.
5072958Sdr146992  */
5082958Sdr146992 static hook_event_int_t *
5092958Sdr146992 hook_event_copy(hook_event_t *src)
5102958Sdr146992 {
5112958Sdr146992 	hook_event_int_t *new;
5122958Sdr146992 
5132958Sdr146992 	ASSERT(src != NULL);
5142958Sdr146992 	ASSERT(src->he_name != NULL);
5152958Sdr146992 
5162958Sdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
5172958Sdr146992 
5182958Sdr146992 	/* Copy body */
5192958Sdr146992 	TAILQ_INIT(&new->hei_head);
5202958Sdr146992 	new->hei_event = src;
5212958Sdr146992 
5222958Sdr146992 	return (new);
5232958Sdr146992 }
5242958Sdr146992 
5252958Sdr146992 
5262958Sdr146992 /*
5272958Sdr146992  * Function:	hook_event_find
5282958Sdr146992  * Returns:	internal event pointer - NULL = Not match
5292958Sdr146992  * Parameters:	hfi(I) - internal family pointer
5302958Sdr146992  *		event(I) - event name string
5312958Sdr146992  *
5322958Sdr146992  * Search event list with event name
533*3448Sdh155122  * 	A lock on hks->hks_familylock must be held when called.
5342958Sdr146992  */
5352958Sdr146992 static hook_event_int_t *
5362958Sdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
5372958Sdr146992 {
5382958Sdr146992 	hook_event_int_t *hei = NULL;
5392958Sdr146992 
5402958Sdr146992 	ASSERT(hfi != NULL);
5412958Sdr146992 	ASSERT(event != NULL);
5422958Sdr146992 
5432958Sdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
5442958Sdr146992 		if (strcmp(hei->hei_event->he_name, event) == 0)
5452958Sdr146992 			break;
5462958Sdr146992 	}
5472958Sdr146992 	return (hei);
5482958Sdr146992 }
5492958Sdr146992 
5502958Sdr146992 
5512958Sdr146992 /*
5522958Sdr146992  * Function:	hook_event_free
5532958Sdr146992  * Returns:	None
5542958Sdr146992  * Parameters:	hei(I) - internal event pointer
5552958Sdr146992  *
5562958Sdr146992  * Free alloc memory for event
5572958Sdr146992  */
5582958Sdr146992 static void
5592958Sdr146992 hook_event_free(hook_event_int_t *hei)
5602958Sdr146992 {
5612958Sdr146992 	ASSERT(hei != NULL);
5622958Sdr146992 
5632958Sdr146992 	/* Free container */
5642958Sdr146992 	kmem_free(hei, sizeof (*hei));
5652958Sdr146992 }
5662958Sdr146992 
5672958Sdr146992 
5682958Sdr146992 /*
5692958Sdr146992  * Function:	hook_register
5702958Sdr146992  * Returns:	int- 0 = Succ, Else = Fail
5712958Sdr146992  * Parameters:	hfi(I) - internal family pointer
5722958Sdr146992  *		event(I) - event name string
5732958Sdr146992  *		h(I) - hook pointer
5742958Sdr146992  *
5752958Sdr146992  * Add new hook to hook list on spefic family, event
5762958Sdr146992  */
5772958Sdr146992 int
5782958Sdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
5792958Sdr146992 {
580*3448Sdh155122 	hook_stack_t *hks;
5812958Sdr146992 	hook_event_int_t *hei;
5822958Sdr146992 	hook_int_t *hi, *new;
5832958Sdr146992 
5842958Sdr146992 	ASSERT(hfi != NULL);
5852958Sdr146992 	ASSERT(event != NULL);
5862958Sdr146992 	ASSERT(h != NULL);
587*3448Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
5882958Sdr146992 
5892958Sdr146992 	/* Alloc hook_int_t and copy hook */
5902958Sdr146992 	new = hook_copy(h);
5912958Sdr146992 	if (new == NULL)
5922958Sdr146992 		return (ENOMEM);
5932958Sdr146992 
5942958Sdr146992 	/*
5952958Sdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
5962958Sdr146992 	 * to hold global family write lock. Just get read lock here to
5972958Sdr146992 	 * ensure event will not be removed when doing hooks operation
5982958Sdr146992 	 */
599*3448Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
6002958Sdr146992 
6012958Sdr146992 	hei = hook_event_find(hfi, event);
6022958Sdr146992 	if (hei == NULL) {
603*3448Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
6042958Sdr146992 		hook_free(new);
6052958Sdr146992 		return (ENXIO);
6062958Sdr146992 	}
6072958Sdr146992 
6082958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
6092958Sdr146992 
6102958Sdr146992 	/* Multiple hooks are only allowed for read-only events. */
6112958Sdr146992 	if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) &&
6122958Sdr146992 	    (!TAILQ_EMPTY(&hei->hei_head))) {
6132958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
614*3448Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
6152958Sdr146992 		hook_free(new);
6162958Sdr146992 		return (EEXIST);
6172958Sdr146992 	}
6182958Sdr146992 
6192958Sdr146992 	hi = hook_find(hei, h);
6202958Sdr146992 	if (hi != NULL) {
6212958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
622*3448Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
6232958Sdr146992 		hook_free(new);
6242958Sdr146992 		return (EEXIST);
6252958Sdr146992 	}
6262958Sdr146992 
6272958Sdr146992 	/* Add to hook list head */
6282958Sdr146992 	TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry);
6292958Sdr146992 	hei->hei_event->he_interested = B_TRUE;
6302958Sdr146992 
6312958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
632*3448Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
6332958Sdr146992 	return (0);
6342958Sdr146992 }
6352958Sdr146992 
6362958Sdr146992 
6372958Sdr146992 /*
6382958Sdr146992  * Function:	hook_unregister
6392958Sdr146992  * Returns:	int - 0 = Succ, Else = Fail
6402958Sdr146992  * Parameters:	hfi(I) - internal family pointer
6412958Sdr146992  *		event(I) - event name string
6422958Sdr146992  *		h(I) - hook pointer
6432958Sdr146992  *
6442958Sdr146992  * Remove hook from hook list on specific family, event
6452958Sdr146992  */
6462958Sdr146992 int
6472958Sdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
6482958Sdr146992 {
649*3448Sdh155122 	hook_stack_t *hks;
6502958Sdr146992 	hook_event_int_t *hei;
6512958Sdr146992 	hook_int_t *hi;
6522958Sdr146992 
6532958Sdr146992 	ASSERT(hfi != NULL);
6542958Sdr146992 	ASSERT(h != NULL);
655*3448Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
6562958Sdr146992 
657*3448Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
6582958Sdr146992 
6592958Sdr146992 	hei = hook_event_find(hfi, event);
6602958Sdr146992 	if (hei == NULL) {
661*3448Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
6622958Sdr146992 		return (ENXIO);
6632958Sdr146992 	}
6642958Sdr146992 
6652958Sdr146992 	/* Hold write lock for event */
6662958Sdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
6672958Sdr146992 
6682958Sdr146992 	hi = hook_find(hei, h);
6692958Sdr146992 	if (hi == NULL) {
6702958Sdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
671*3448Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
6722958Sdr146992 		return (ENXIO);
6732958Sdr146992 	}
6742958Sdr146992 
6752958Sdr146992 	/* Remove from hook list */
6762958Sdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
6772958Sdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
6782958Sdr146992 		hei->hei_event->he_interested = B_FALSE;
6792958Sdr146992 	}
6802958Sdr146992 
6812958Sdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
682*3448Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
6832958Sdr146992 
6842958Sdr146992 	hook_free(hi);
6852958Sdr146992 	return (0);
6862958Sdr146992 }
6872958Sdr146992 
6882958Sdr146992 
6892958Sdr146992 /*
6902958Sdr146992  * Function:	hook_find
6912958Sdr146992  * Returns:	internal hook pointer - NULL = Not match
6922958Sdr146992  * Parameters:	hei(I) - internal event pointer
6932958Sdr146992  *		h(I) - hook pointer
6942958Sdr146992  *
6952958Sdr146992  * Search hook list
6962958Sdr146992  * 	A lock on familylock must be held when called.
6972958Sdr146992  */
6982958Sdr146992 static hook_int_t *
6992958Sdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
7002958Sdr146992 {
7012958Sdr146992 	hook_int_t *hi;
7022958Sdr146992 
7032958Sdr146992 	ASSERT(hei != NULL);
7042958Sdr146992 	ASSERT(h != NULL);
7052958Sdr146992 
7062958Sdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
7072958Sdr146992 		if (strcmp(hi->hi_hook.h_name, h->h_name) == 0)
7082958Sdr146992 			break;
7092958Sdr146992 	}
7102958Sdr146992 	return (hi);
7112958Sdr146992 }
7122958Sdr146992 
7132958Sdr146992 
7142958Sdr146992 /*
7152958Sdr146992  * Function:	hook_copy
7162958Sdr146992  * Returns:	internal hook pointer - NULL = Failed
7172958Sdr146992  * Parameters:	src(I) - hook pointer
7182958Sdr146992  *
7192958Sdr146992  * Allocate internal hook block and duplicate incoming hook.
7202958Sdr146992  * No locks should be held across this function as it may sleep.
7212958Sdr146992  */
7222958Sdr146992 static hook_int_t *
7232958Sdr146992 hook_copy(hook_t *src)
7242958Sdr146992 {
7252958Sdr146992 	hook_int_t *new;
7262958Sdr146992 	hook_t *dst;
7272958Sdr146992 
7282958Sdr146992 	ASSERT(src != NULL);
7292958Sdr146992 	ASSERT(src->h_name != NULL);
7302958Sdr146992 
7312958Sdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
7322958Sdr146992 
7332958Sdr146992 	/* Copy body */
7342958Sdr146992 	dst = &new->hi_hook;
7352958Sdr146992 	*dst = *src;
7362958Sdr146992 
7372958Sdr146992 	/* Copy name */
7382958Sdr146992 	dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP);
7392958Sdr146992 	(void) strcpy(dst->h_name, src->h_name);
7402958Sdr146992 
7412958Sdr146992 	return (new);
7422958Sdr146992 }
7432958Sdr146992 
7442958Sdr146992 /*
7452958Sdr146992  * Function:	hook_free
7462958Sdr146992  * Returns:	None
7472958Sdr146992  * Parameters:	hi(I) - internal hook pointer
7482958Sdr146992  *
7492958Sdr146992  * Free alloc memory for hook
7502958Sdr146992  */
7512958Sdr146992 static void
7522958Sdr146992 hook_free(hook_int_t *hi)
7532958Sdr146992 {
7542958Sdr146992 	ASSERT(hi != NULL);
7552958Sdr146992 
7562958Sdr146992 	/* Free name space */
7572958Sdr146992 	if (hi->hi_hook.h_name != NULL) {
7582958Sdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
7592958Sdr146992 	}
7602958Sdr146992 
7612958Sdr146992 	/* Free container */
7622958Sdr146992 	kmem_free(hi, sizeof (*hi));
7632958Sdr146992 }
764