xref: /titanic_51/usr/src/uts/common/io/hook.c (revision f4b3ec61df05330d25f55a36b975b4d7519fdeb1)
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 /*
22*f4b3ec61Sdh155122  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23381a2a9aSdr146992  * Use is subject to license terms.
24381a2a9aSdr146992  */
25381a2a9aSdr146992 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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>
37381a2a9aSdr146992 
38381a2a9aSdr146992 /*
39381a2a9aSdr146992  * This file provides kernel hook framework.
40381a2a9aSdr146992  */
41381a2a9aSdr146992 
42381a2a9aSdr146992 static struct modldrv modlmisc = {
43381a2a9aSdr146992 	&mod_miscops,				/* drv_modops */
44381a2a9aSdr146992 	"Hooks Interface v1.0",			/* drv_linkinfo */
45381a2a9aSdr146992 };
46381a2a9aSdr146992 
47381a2a9aSdr146992 static struct modlinkage modlinkage = {
48381a2a9aSdr146992 	MODREV_1,				/* ml_rev */
49381a2a9aSdr146992 	&modlmisc,				/* ml_linkage */
50381a2a9aSdr146992 	NULL
51381a2a9aSdr146992 };
52381a2a9aSdr146992 
53381a2a9aSdr146992 /*
54381a2a9aSdr146992  * Hook internal functions
55381a2a9aSdr146992  */
56381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src);
57*f4b3ec61Sdh155122 static hook_event_int_t *hook_event_checkdup(hook_event_t *he,
58*f4b3ec61Sdh155122     hook_stack_t *hks);
59381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
60381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
61381a2a9aSdr146992 static void hook_event_free(hook_event_int_t *hei);
62381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
63*f4b3ec61Sdh155122 static hook_family_int_t *hook_family_find(char *family, hook_stack_t *hks);
64381a2a9aSdr146992 static void hook_family_free(hook_family_int_t *hfi);
65381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
66381a2a9aSdr146992 static void hook_free(hook_int_t *hi);
67381a2a9aSdr146992 static void hook_init(void);
68*f4b3ec61Sdh155122 static void hook_fini(void);
69*f4b3ec61Sdh155122 static void *hook_stack_init(netstackid_t stackid, netstack_t *ns);
70*f4b3ec61Sdh155122 static void hook_stack_fini(netstackid_t stackid, void *arg);
71381a2a9aSdr146992 
72381a2a9aSdr146992 /*
73381a2a9aSdr146992  * Module entry points.
74381a2a9aSdr146992  */
75381a2a9aSdr146992 int
76381a2a9aSdr146992 _init(void)
77381a2a9aSdr146992 {
78*f4b3ec61Sdh155122 	int error;
79*f4b3ec61Sdh155122 
80381a2a9aSdr146992 	hook_init();
81*f4b3ec61Sdh155122 	error = mod_install(&modlinkage);
82*f4b3ec61Sdh155122 	if (error != 0)
83*f4b3ec61Sdh155122 		hook_fini();
84*f4b3ec61Sdh155122 
85*f4b3ec61Sdh155122 	return (error);
86381a2a9aSdr146992 }
87381a2a9aSdr146992 
88381a2a9aSdr146992 
89381a2a9aSdr146992 int
90381a2a9aSdr146992 _fini(void)
91381a2a9aSdr146992 {
92*f4b3ec61Sdh155122 	int error;
93*f4b3ec61Sdh155122 
94*f4b3ec61Sdh155122 	error = mod_remove(&modlinkage);
95*f4b3ec61Sdh155122 	if (error == 0)
96*f4b3ec61Sdh155122 		hook_fini();
97*f4b3ec61Sdh155122 
98*f4b3ec61Sdh155122 	return (error);
99381a2a9aSdr146992 }
100381a2a9aSdr146992 
101381a2a9aSdr146992 
102381a2a9aSdr146992 int
103381a2a9aSdr146992 _info(struct modinfo *modinfop)
104381a2a9aSdr146992 {
105381a2a9aSdr146992 	return (mod_info(&modlinkage, modinfop));
106381a2a9aSdr146992 }
107381a2a9aSdr146992 
108381a2a9aSdr146992 
109381a2a9aSdr146992 /*
110381a2a9aSdr146992  * Function:	hook_init
111381a2a9aSdr146992  * Returns:	None
112381a2a9aSdr146992  * Parameters:	None
113381a2a9aSdr146992  *
114381a2a9aSdr146992  * Initialize hooks
115381a2a9aSdr146992  */
116381a2a9aSdr146992 static void
117381a2a9aSdr146992 hook_init(void)
118381a2a9aSdr146992 {
119*f4b3ec61Sdh155122 	/*
120*f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
121*f4b3ec61Sdh155122 	 * destroyed in the kernel.
122*f4b3ec61Sdh155122 	 */
123*f4b3ec61Sdh155122 	netstack_register(NS_HOOK, hook_stack_init, NULL,
124*f4b3ec61Sdh155122 	    hook_stack_fini);
125381a2a9aSdr146992 }
126381a2a9aSdr146992 
127*f4b3ec61Sdh155122 /*
128*f4b3ec61Sdh155122  * Function:	hook_fini
129*f4b3ec61Sdh155122  * Returns:	None
130*f4b3ec61Sdh155122  * Parameters:	None
131*f4b3ec61Sdh155122  *
132*f4b3ec61Sdh155122  * Deinitialize hooks
133*f4b3ec61Sdh155122  */
134*f4b3ec61Sdh155122 static void
135*f4b3ec61Sdh155122 hook_fini(void)
136*f4b3ec61Sdh155122 {
137*f4b3ec61Sdh155122 	netstack_unregister(NS_HOOK);
138*f4b3ec61Sdh155122 }
139*f4b3ec61Sdh155122 
140*f4b3ec61Sdh155122 /*
141*f4b3ec61Sdh155122  * Initialize the hook stack instance.
142*f4b3ec61Sdh155122  */
143*f4b3ec61Sdh155122 /*ARGSUSED*/
144*f4b3ec61Sdh155122 static void *
145*f4b3ec61Sdh155122 hook_stack_init(netstackid_t stackid, netstack_t *ns)
146*f4b3ec61Sdh155122 {
147*f4b3ec61Sdh155122 	hook_stack_t	*hks;
148*f4b3ec61Sdh155122 
149*f4b3ec61Sdh155122 #ifdef NS_DEBUG
150*f4b3ec61Sdh155122 	printf("hook_stack_init(stack %d)\n", stackid);
151*f4b3ec61Sdh155122 #endif
152*f4b3ec61Sdh155122 
153*f4b3ec61Sdh155122 	hks = (hook_stack_t *)kmem_zalloc(sizeof (*hks), KM_SLEEP);
154*f4b3ec61Sdh155122 	hks->hk_netstack = ns;
155*f4b3ec61Sdh155122 
156*f4b3ec61Sdh155122 	CVW_INIT(&hks->hks_familylock);
157*f4b3ec61Sdh155122 	SLIST_INIT(&hks->hks_familylist);
158*f4b3ec61Sdh155122 
159*f4b3ec61Sdh155122 	return (hks);
160*f4b3ec61Sdh155122 }
161*f4b3ec61Sdh155122 
162*f4b3ec61Sdh155122 /*
163*f4b3ec61Sdh155122  * Free the hook stack instance.
164*f4b3ec61Sdh155122  */
165*f4b3ec61Sdh155122 /*ARGSUSED*/
166*f4b3ec61Sdh155122 static void
167*f4b3ec61Sdh155122 hook_stack_fini(netstackid_t stackid, void *arg)
168*f4b3ec61Sdh155122 {
169*f4b3ec61Sdh155122 	hook_stack_t	*hks = (hook_stack_t *)arg;
170*f4b3ec61Sdh155122 #ifdef NS_DEBUG
171*f4b3ec61Sdh155122 	printf("hook_stack_fini(%p, stack %d)\n", arg, stackid);
172*f4b3ec61Sdh155122 #endif
173*f4b3ec61Sdh155122 	CVW_DESTROY(&hks->hks_familylock);
174*f4b3ec61Sdh155122 	kmem_free(hks, sizeof (*hks));
175*f4b3ec61Sdh155122 }
176381a2a9aSdr146992 
177381a2a9aSdr146992 /*
178381a2a9aSdr146992  * Function:	hook_run
179381a2a9aSdr146992  * Returns:	int - return value according to callback func
180381a2a9aSdr146992  * Parameters:	token(I) - event pointer
181381a2a9aSdr146992  *		info(I) - message
182381a2a9aSdr146992  *
183381a2a9aSdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
184381a2a9aSdr146992  * until either the end of the list is reached or a hook function returns a
185381a2a9aSdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
186381a2a9aSdr146992  * return that value back to our caller.  By design, a hook function can be
187381a2a9aSdr146992  * called more than once, simultaneously.
188381a2a9aSdr146992  */
189381a2a9aSdr146992 int
190*f4b3ec61Sdh155122 hook_run(hook_event_token_t token, hook_data_t info, netstack_t *ns)
191381a2a9aSdr146992 {
192381a2a9aSdr146992 	hook_int_t *hi;
193381a2a9aSdr146992 	hook_event_int_t *hei;
194*f4b3ec61Sdh155122 	hook_stack_t *hks = ns->netstack_hook;
195381a2a9aSdr146992 	int rval = 0;
196381a2a9aSdr146992 
197381a2a9aSdr146992 	ASSERT(token != NULL);
198381a2a9aSdr146992 
199381a2a9aSdr146992 	hei = (hook_event_int_t *)token;
200381a2a9aSdr146992 	DTRACE_PROBE2(hook__run__start,
201381a2a9aSdr146992 	    hook_event_token_t, token,
202381a2a9aSdr146992 	    hook_data_t, info);
203381a2a9aSdr146992 
204381a2a9aSdr146992 	/* Hold global read lock to ensure event will not be deleted */
205*f4b3ec61Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
206381a2a9aSdr146992 
207381a2a9aSdr146992 	/* Hold event read lock to ensure hook will not be changed */
208381a2a9aSdr146992 	CVW_ENTER_READ(&hei->hei_lock);
209381a2a9aSdr146992 
210381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
211381a2a9aSdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
212381a2a9aSdr146992 		DTRACE_PROBE3(hook__func__start,
213381a2a9aSdr146992 		    hook_event_token_t, token,
214381a2a9aSdr146992 		    hook_data_t, info,
215381a2a9aSdr146992 		    hook_int_t *, hi);
216*f4b3ec61Sdh155122 		rval = (*hi->hi_hook.h_func)(token, info, ns);
217381a2a9aSdr146992 		DTRACE_PROBE4(hook__func__end,
218381a2a9aSdr146992 		    hook_event_token_t, token,
219381a2a9aSdr146992 		    hook_data_t, info,
220381a2a9aSdr146992 		    hook_int_t *, hi,
221381a2a9aSdr146992 		    int, rval);
222381a2a9aSdr146992 		if (rval != 0)
223381a2a9aSdr146992 			break;
224381a2a9aSdr146992 	}
225381a2a9aSdr146992 
226381a2a9aSdr146992 	CVW_EXIT_READ(&hei->hei_lock);
227*f4b3ec61Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
228381a2a9aSdr146992 
229381a2a9aSdr146992 	DTRACE_PROBE3(hook__run__end,
230381a2a9aSdr146992 	    hook_event_token_t, token,
231381a2a9aSdr146992 	    hook_data_t, info,
232381a2a9aSdr146992 	    hook_int_t *, hi);
233381a2a9aSdr146992 
234381a2a9aSdr146992 	return (rval);
235381a2a9aSdr146992 }
236381a2a9aSdr146992 
237381a2a9aSdr146992 
238381a2a9aSdr146992 /*
239381a2a9aSdr146992  * Function:	hook_family_add
240381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Fail
241381a2a9aSdr146992  * Parameters:	hf(I) - family pointer
242381a2a9aSdr146992  *
243381a2a9aSdr146992  * Add new family to family list
244381a2a9aSdr146992  */
245381a2a9aSdr146992 hook_family_int_t *
246*f4b3ec61Sdh155122 hook_family_add(hook_family_t *hf, hook_stack_t *hks)
247381a2a9aSdr146992 {
248381a2a9aSdr146992 	hook_family_int_t *hfi, *new;
249381a2a9aSdr146992 
250381a2a9aSdr146992 	ASSERT(hf != NULL);
251381a2a9aSdr146992 	ASSERT(hf->hf_name != NULL);
252381a2a9aSdr146992 
253381a2a9aSdr146992 	new = hook_family_copy(hf);
254381a2a9aSdr146992 	if (new == NULL)
255381a2a9aSdr146992 		return (NULL);
256381a2a9aSdr146992 
257*f4b3ec61Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
258381a2a9aSdr146992 
259381a2a9aSdr146992 	/* search family list */
260*f4b3ec61Sdh155122 	hfi = hook_family_find(hf->hf_name, hks);
261381a2a9aSdr146992 	if (hfi != NULL) {
262*f4b3ec61Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
263381a2a9aSdr146992 		hook_family_free(new);
264381a2a9aSdr146992 		return (NULL);
265381a2a9aSdr146992 	}
266381a2a9aSdr146992 
267*f4b3ec61Sdh155122 	new->hfi_ptr = (void *)hks;
268381a2a9aSdr146992 
269*f4b3ec61Sdh155122 	/* Add to family list head */
270*f4b3ec61Sdh155122 	SLIST_INSERT_HEAD(&hks->hks_familylist, new, hfi_entry);
271*f4b3ec61Sdh155122 
272*f4b3ec61Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
273381a2a9aSdr146992 	return (new);
274381a2a9aSdr146992 }
275381a2a9aSdr146992 
276381a2a9aSdr146992 
277381a2a9aSdr146992 /*
278381a2a9aSdr146992  * Function:	hook_family_remove
279381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
280381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
281381a2a9aSdr146992  *
282381a2a9aSdr146992  * Remove family from family list
283381a2a9aSdr146992  */
284381a2a9aSdr146992 int
285381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi)
286381a2a9aSdr146992 {
287*f4b3ec61Sdh155122 	hook_stack_t *hks;
288381a2a9aSdr146992 
289381a2a9aSdr146992 	ASSERT(hfi != NULL);
290*f4b3ec61Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
291381a2a9aSdr146992 
292*f4b3ec61Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
293381a2a9aSdr146992 
294381a2a9aSdr146992 	/* Check if there are events  */
295381a2a9aSdr146992 	if (!SLIST_EMPTY(&hfi->hfi_head)) {
296*f4b3ec61Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
297381a2a9aSdr146992 		return (EBUSY);
298381a2a9aSdr146992 	}
299381a2a9aSdr146992 
300381a2a9aSdr146992 	/* Remove from family list */
301*f4b3ec61Sdh155122 	SLIST_REMOVE(&hks->hks_familylist, hfi, hook_family_int, hfi_entry);
302381a2a9aSdr146992 
303*f4b3ec61Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
304381a2a9aSdr146992 	hook_family_free(hfi);
305381a2a9aSdr146992 
306381a2a9aSdr146992 	return (0);
307381a2a9aSdr146992 }
308381a2a9aSdr146992 
309381a2a9aSdr146992 
310381a2a9aSdr146992 /*
311381a2a9aSdr146992  * Function:	hook_family_copy
312381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Failed
313381a2a9aSdr146992  * Parameters:	src(I) - family pointer
314381a2a9aSdr146992  *
315381a2a9aSdr146992  * Allocate internal family block and duplicate incoming family
316381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
317381a2a9aSdr146992  */
318381a2a9aSdr146992 static hook_family_int_t *
319381a2a9aSdr146992 hook_family_copy(hook_family_t *src)
320381a2a9aSdr146992 {
321381a2a9aSdr146992 	hook_family_int_t *new;
322381a2a9aSdr146992 	hook_family_t *dst;
323381a2a9aSdr146992 
324381a2a9aSdr146992 	ASSERT(src != NULL);
325381a2a9aSdr146992 	ASSERT(src->hf_name != NULL);
326381a2a9aSdr146992 
327381a2a9aSdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
328381a2a9aSdr146992 
329381a2a9aSdr146992 	/* Copy body */
330381a2a9aSdr146992 	SLIST_INIT(&new->hfi_head);
331381a2a9aSdr146992 	dst = &new->hfi_family;
332381a2a9aSdr146992 	*dst = *src;
333381a2a9aSdr146992 
334381a2a9aSdr146992 	/* Copy name */
335381a2a9aSdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
336381a2a9aSdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
337381a2a9aSdr146992 
338381a2a9aSdr146992 	return (new);
339381a2a9aSdr146992 }
340381a2a9aSdr146992 
341381a2a9aSdr146992 
342381a2a9aSdr146992 /*
343381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Not match
344381a2a9aSdr146992  * Parameters:	family(I) - family name string
345381a2a9aSdr146992  *
346381a2a9aSdr146992  * Search family list with family name
347381a2a9aSdr146992  * 	A lock on familylock must be held when called.
348381a2a9aSdr146992  */
349381a2a9aSdr146992 static hook_family_int_t *
350*f4b3ec61Sdh155122 hook_family_find(char *family, hook_stack_t *hks)
351381a2a9aSdr146992 {
352381a2a9aSdr146992 	hook_family_int_t *hfi = NULL;
353381a2a9aSdr146992 
354381a2a9aSdr146992 	ASSERT(family != NULL);
355381a2a9aSdr146992 
356*f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
357381a2a9aSdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
358381a2a9aSdr146992 			break;
359381a2a9aSdr146992 	}
360381a2a9aSdr146992 	return (hfi);
361381a2a9aSdr146992 }
362381a2a9aSdr146992 
363381a2a9aSdr146992 
364381a2a9aSdr146992 /*
365381a2a9aSdr146992  * Function:	hook_family_free
366381a2a9aSdr146992  * Returns:	None
367381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
368381a2a9aSdr146992  *
369381a2a9aSdr146992  * Free alloc memory for family
370381a2a9aSdr146992  */
371381a2a9aSdr146992 static void
372381a2a9aSdr146992 hook_family_free(hook_family_int_t *hfi)
373381a2a9aSdr146992 {
374381a2a9aSdr146992 	ASSERT(hfi != NULL);
375381a2a9aSdr146992 
376381a2a9aSdr146992 	/* Free name space */
377381a2a9aSdr146992 	if (hfi->hfi_family.hf_name != NULL) {
378381a2a9aSdr146992 		kmem_free(hfi->hfi_family.hf_name,
379381a2a9aSdr146992 		    strlen(hfi->hfi_family.hf_name) + 1);
380381a2a9aSdr146992 	}
381381a2a9aSdr146992 
382381a2a9aSdr146992 	/* Free container */
383381a2a9aSdr146992 	kmem_free(hfi, sizeof (*hfi));
384381a2a9aSdr146992 }
385381a2a9aSdr146992 
386381a2a9aSdr146992 
387381a2a9aSdr146992 /*
388381a2a9aSdr146992  * Function:	hook_event_add
389381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Fail
390381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
391381a2a9aSdr146992  *		he(I) - event pointer
392381a2a9aSdr146992  *
393381a2a9aSdr146992  * Add new event to event list on specific family.
394381a2a9aSdr146992  * This function can fail to return successfully if (1) it cannot allocate
395381a2a9aSdr146992  * enough memory for its own internal data structures, (2) the event has
396381a2a9aSdr146992  * already been registered (for any hook family.)
397381a2a9aSdr146992  */
398381a2a9aSdr146992 hook_event_int_t *
399381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
400381a2a9aSdr146992 {
401*f4b3ec61Sdh155122 	hook_stack_t *hks;
402381a2a9aSdr146992 	hook_event_int_t *hei, *new;
403381a2a9aSdr146992 
404381a2a9aSdr146992 	ASSERT(hfi != NULL);
405381a2a9aSdr146992 	ASSERT(he != NULL);
406381a2a9aSdr146992 	ASSERT(he->he_name != NULL);
407*f4b3ec61Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
408381a2a9aSdr146992 
409381a2a9aSdr146992 	new = hook_event_copy(he);
410381a2a9aSdr146992 	if (new == NULL)
411381a2a9aSdr146992 		return (NULL);
412381a2a9aSdr146992 
413*f4b3ec61Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
414381a2a9aSdr146992 
415381a2a9aSdr146992 	/* Check whether this event pointer is already registered */
416*f4b3ec61Sdh155122 	hei = hook_event_checkdup(he, hks);
417381a2a9aSdr146992 	if (hei != NULL) {
418*f4b3ec61Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
419381a2a9aSdr146992 		hook_event_free(new);
420381a2a9aSdr146992 		return (NULL);
421381a2a9aSdr146992 	}
422381a2a9aSdr146992 
423381a2a9aSdr146992 	/* Add to event list head */
424381a2a9aSdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
425381a2a9aSdr146992 
426*f4b3ec61Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
427381a2a9aSdr146992 	return (new);
428381a2a9aSdr146992 }
429381a2a9aSdr146992 
430381a2a9aSdr146992 
431381a2a9aSdr146992 /*
432381a2a9aSdr146992  * Function:	hook_event_remove
433381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
434381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
435381a2a9aSdr146992  *		he(I) - event pointer
436381a2a9aSdr146992  *
437381a2a9aSdr146992  * Remove event from event list on specific family
438381a2a9aSdr146992  */
439381a2a9aSdr146992 int
440381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
441381a2a9aSdr146992 {
442*f4b3ec61Sdh155122 	hook_stack_t *hks;
443381a2a9aSdr146992 	hook_event_int_t *hei;
444381a2a9aSdr146992 
445381a2a9aSdr146992 	ASSERT(hfi != NULL);
446381a2a9aSdr146992 	ASSERT(he != NULL);
447*f4b3ec61Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
448381a2a9aSdr146992 
449*f4b3ec61Sdh155122 	CVW_ENTER_WRITE(&hks->hks_familylock);
450381a2a9aSdr146992 
451381a2a9aSdr146992 	hei = hook_event_find(hfi, he->he_name);
452381a2a9aSdr146992 	if (hei == NULL) {
453*f4b3ec61Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
454381a2a9aSdr146992 		return (ENXIO);
455381a2a9aSdr146992 	}
456381a2a9aSdr146992 
457381a2a9aSdr146992 	/* Check if there are registered hooks for this event */
458381a2a9aSdr146992 	if (!TAILQ_EMPTY(&hei->hei_head)) {
459*f4b3ec61Sdh155122 		CVW_EXIT_WRITE(&hks->hks_familylock);
460381a2a9aSdr146992 		return (EBUSY);
461381a2a9aSdr146992 	}
462381a2a9aSdr146992 
463381a2a9aSdr146992 	/* Remove from event list */
464381a2a9aSdr146992 	SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
465381a2a9aSdr146992 
466*f4b3ec61Sdh155122 	CVW_EXIT_WRITE(&hks->hks_familylock);
467381a2a9aSdr146992 	hook_event_free(hei);
468381a2a9aSdr146992 
469381a2a9aSdr146992 	return (0);
470381a2a9aSdr146992 }
471381a2a9aSdr146992 
472381a2a9aSdr146992 
473381a2a9aSdr146992 /*
474381a2a9aSdr146992  * Function:    hook_event_checkdup
475381a2a9aSdr146992  * Returns:     internal event pointer - NULL = Not match
476381a2a9aSdr146992  * Parameters:  he(I) - event pointer
477381a2a9aSdr146992  *
478381a2a9aSdr146992  * Search whole list with event pointer
479381a2a9aSdr146992  *      A lock on familylock must be held when called.
480381a2a9aSdr146992  */
481381a2a9aSdr146992 static hook_event_int_t *
482*f4b3ec61Sdh155122 hook_event_checkdup(hook_event_t *he, hook_stack_t *hks)
483381a2a9aSdr146992 {
484381a2a9aSdr146992 	hook_family_int_t *hfi;
485381a2a9aSdr146992 	hook_event_int_t *hei;
486381a2a9aSdr146992 
487381a2a9aSdr146992 	ASSERT(he != NULL);
488381a2a9aSdr146992 
489*f4b3ec61Sdh155122 	SLIST_FOREACH(hfi, &hks->hks_familylist, hfi_entry) {
490381a2a9aSdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
491381a2a9aSdr146992 			if (hei->hei_event == he)
492381a2a9aSdr146992 				return (hei);
493381a2a9aSdr146992 		}
494381a2a9aSdr146992 	}
495381a2a9aSdr146992 
496381a2a9aSdr146992 	return (NULL);
497381a2a9aSdr146992 }
498381a2a9aSdr146992 
499381a2a9aSdr146992 
500381a2a9aSdr146992 /*
501381a2a9aSdr146992  * Function:	hook_event_copy
502381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Failed
503381a2a9aSdr146992  * Parameters:	src(I) - event pointer
504381a2a9aSdr146992  *
505381a2a9aSdr146992  * Allocate internal event block and duplicate incoming event
506381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
507381a2a9aSdr146992  */
508381a2a9aSdr146992 static hook_event_int_t *
509381a2a9aSdr146992 hook_event_copy(hook_event_t *src)
510381a2a9aSdr146992 {
511381a2a9aSdr146992 	hook_event_int_t *new;
512381a2a9aSdr146992 
513381a2a9aSdr146992 	ASSERT(src != NULL);
514381a2a9aSdr146992 	ASSERT(src->he_name != NULL);
515381a2a9aSdr146992 
516381a2a9aSdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
517381a2a9aSdr146992 
518381a2a9aSdr146992 	/* Copy body */
519381a2a9aSdr146992 	TAILQ_INIT(&new->hei_head);
520381a2a9aSdr146992 	new->hei_event = src;
521381a2a9aSdr146992 
522381a2a9aSdr146992 	return (new);
523381a2a9aSdr146992 }
524381a2a9aSdr146992 
525381a2a9aSdr146992 
526381a2a9aSdr146992 /*
527381a2a9aSdr146992  * Function:	hook_event_find
528381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Not match
529381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
530381a2a9aSdr146992  *		event(I) - event name string
531381a2a9aSdr146992  *
532381a2a9aSdr146992  * Search event list with event name
533*f4b3ec61Sdh155122  * 	A lock on hks->hks_familylock must be held when called.
534381a2a9aSdr146992  */
535381a2a9aSdr146992 static hook_event_int_t *
536381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
537381a2a9aSdr146992 {
538381a2a9aSdr146992 	hook_event_int_t *hei = NULL;
539381a2a9aSdr146992 
540381a2a9aSdr146992 	ASSERT(hfi != NULL);
541381a2a9aSdr146992 	ASSERT(event != NULL);
542381a2a9aSdr146992 
543381a2a9aSdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
544381a2a9aSdr146992 		if (strcmp(hei->hei_event->he_name, event) == 0)
545381a2a9aSdr146992 			break;
546381a2a9aSdr146992 	}
547381a2a9aSdr146992 	return (hei);
548381a2a9aSdr146992 }
549381a2a9aSdr146992 
550381a2a9aSdr146992 
551381a2a9aSdr146992 /*
552381a2a9aSdr146992  * Function:	hook_event_free
553381a2a9aSdr146992  * Returns:	None
554381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
555381a2a9aSdr146992  *
556381a2a9aSdr146992  * Free alloc memory for event
557381a2a9aSdr146992  */
558381a2a9aSdr146992 static void
559381a2a9aSdr146992 hook_event_free(hook_event_int_t *hei)
560381a2a9aSdr146992 {
561381a2a9aSdr146992 	ASSERT(hei != NULL);
562381a2a9aSdr146992 
563381a2a9aSdr146992 	/* Free container */
564381a2a9aSdr146992 	kmem_free(hei, sizeof (*hei));
565381a2a9aSdr146992 }
566381a2a9aSdr146992 
567381a2a9aSdr146992 
568381a2a9aSdr146992 /*
569381a2a9aSdr146992  * Function:	hook_register
570381a2a9aSdr146992  * Returns:	int- 0 = Succ, Else = Fail
571381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
572381a2a9aSdr146992  *		event(I) - event name string
573381a2a9aSdr146992  *		h(I) - hook pointer
574381a2a9aSdr146992  *
575381a2a9aSdr146992  * Add new hook to hook list on spefic family, event
576381a2a9aSdr146992  */
577381a2a9aSdr146992 int
578381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
579381a2a9aSdr146992 {
580*f4b3ec61Sdh155122 	hook_stack_t *hks;
581381a2a9aSdr146992 	hook_event_int_t *hei;
582381a2a9aSdr146992 	hook_int_t *hi, *new;
583381a2a9aSdr146992 
584381a2a9aSdr146992 	ASSERT(hfi != NULL);
585381a2a9aSdr146992 	ASSERT(event != NULL);
586381a2a9aSdr146992 	ASSERT(h != NULL);
587*f4b3ec61Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
588381a2a9aSdr146992 
589381a2a9aSdr146992 	/* Alloc hook_int_t and copy hook */
590381a2a9aSdr146992 	new = hook_copy(h);
591381a2a9aSdr146992 	if (new == NULL)
592381a2a9aSdr146992 		return (ENOMEM);
593381a2a9aSdr146992 
594381a2a9aSdr146992 	/*
595381a2a9aSdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
596381a2a9aSdr146992 	 * to hold global family write lock. Just get read lock here to
597381a2a9aSdr146992 	 * ensure event will not be removed when doing hooks operation
598381a2a9aSdr146992 	 */
599*f4b3ec61Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
600381a2a9aSdr146992 
601381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
602381a2a9aSdr146992 	if (hei == NULL) {
603*f4b3ec61Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
604381a2a9aSdr146992 		hook_free(new);
605381a2a9aSdr146992 		return (ENXIO);
606381a2a9aSdr146992 	}
607381a2a9aSdr146992 
608381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
609381a2a9aSdr146992 
610381a2a9aSdr146992 	/* Multiple hooks are only allowed for read-only events. */
611381a2a9aSdr146992 	if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) &&
612381a2a9aSdr146992 	    (!TAILQ_EMPTY(&hei->hei_head))) {
613381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
614*f4b3ec61Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
615381a2a9aSdr146992 		hook_free(new);
616381a2a9aSdr146992 		return (EEXIST);
617381a2a9aSdr146992 	}
618381a2a9aSdr146992 
619381a2a9aSdr146992 	hi = hook_find(hei, h);
620381a2a9aSdr146992 	if (hi != NULL) {
621381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
622*f4b3ec61Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
623381a2a9aSdr146992 		hook_free(new);
624381a2a9aSdr146992 		return (EEXIST);
625381a2a9aSdr146992 	}
626381a2a9aSdr146992 
627381a2a9aSdr146992 	/* Add to hook list head */
628381a2a9aSdr146992 	TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry);
629381a2a9aSdr146992 	hei->hei_event->he_interested = B_TRUE;
630381a2a9aSdr146992 
631381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
632*f4b3ec61Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
633381a2a9aSdr146992 	return (0);
634381a2a9aSdr146992 }
635381a2a9aSdr146992 
636381a2a9aSdr146992 
637381a2a9aSdr146992 /*
638381a2a9aSdr146992  * Function:	hook_unregister
639381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
640381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
641381a2a9aSdr146992  *		event(I) - event name string
642381a2a9aSdr146992  *		h(I) - hook pointer
643381a2a9aSdr146992  *
644381a2a9aSdr146992  * Remove hook from hook list on specific family, event
645381a2a9aSdr146992  */
646381a2a9aSdr146992 int
647381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
648381a2a9aSdr146992 {
649*f4b3ec61Sdh155122 	hook_stack_t *hks;
650381a2a9aSdr146992 	hook_event_int_t *hei;
651381a2a9aSdr146992 	hook_int_t *hi;
652381a2a9aSdr146992 
653381a2a9aSdr146992 	ASSERT(hfi != NULL);
654381a2a9aSdr146992 	ASSERT(h != NULL);
655*f4b3ec61Sdh155122 	hks = (hook_stack_t *)hfi->hfi_ptr;
656381a2a9aSdr146992 
657*f4b3ec61Sdh155122 	CVW_ENTER_READ(&hks->hks_familylock);
658381a2a9aSdr146992 
659381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
660381a2a9aSdr146992 	if (hei == NULL) {
661*f4b3ec61Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
662381a2a9aSdr146992 		return (ENXIO);
663381a2a9aSdr146992 	}
664381a2a9aSdr146992 
665381a2a9aSdr146992 	/* Hold write lock for event */
666381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
667381a2a9aSdr146992 
668381a2a9aSdr146992 	hi = hook_find(hei, h);
669381a2a9aSdr146992 	if (hi == NULL) {
670381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
671*f4b3ec61Sdh155122 		CVW_EXIT_READ(&hks->hks_familylock);
672381a2a9aSdr146992 		return (ENXIO);
673381a2a9aSdr146992 	}
674381a2a9aSdr146992 
675381a2a9aSdr146992 	/* Remove from hook list */
676381a2a9aSdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
677381a2a9aSdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
678381a2a9aSdr146992 		hei->hei_event->he_interested = B_FALSE;
679381a2a9aSdr146992 	}
680381a2a9aSdr146992 
681381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
682*f4b3ec61Sdh155122 	CVW_EXIT_READ(&hks->hks_familylock);
683381a2a9aSdr146992 
684381a2a9aSdr146992 	hook_free(hi);
685381a2a9aSdr146992 	return (0);
686381a2a9aSdr146992 }
687381a2a9aSdr146992 
688381a2a9aSdr146992 
689381a2a9aSdr146992 /*
690381a2a9aSdr146992  * Function:	hook_find
691381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Not match
692381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
693381a2a9aSdr146992  *		h(I) - hook pointer
694381a2a9aSdr146992  *
695381a2a9aSdr146992  * Search hook list
696381a2a9aSdr146992  * 	A lock on familylock must be held when called.
697381a2a9aSdr146992  */
698381a2a9aSdr146992 static hook_int_t *
699381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
700381a2a9aSdr146992 {
701381a2a9aSdr146992 	hook_int_t *hi;
702381a2a9aSdr146992 
703381a2a9aSdr146992 	ASSERT(hei != NULL);
704381a2a9aSdr146992 	ASSERT(h != NULL);
705381a2a9aSdr146992 
706381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
707381a2a9aSdr146992 		if (strcmp(hi->hi_hook.h_name, h->h_name) == 0)
708381a2a9aSdr146992 			break;
709381a2a9aSdr146992 	}
710381a2a9aSdr146992 	return (hi);
711381a2a9aSdr146992 }
712381a2a9aSdr146992 
713381a2a9aSdr146992 
714381a2a9aSdr146992 /*
715381a2a9aSdr146992  * Function:	hook_copy
716381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Failed
717381a2a9aSdr146992  * Parameters:	src(I) - hook pointer
718381a2a9aSdr146992  *
719381a2a9aSdr146992  * Allocate internal hook block and duplicate incoming hook.
720381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
721381a2a9aSdr146992  */
722381a2a9aSdr146992 static hook_int_t *
723381a2a9aSdr146992 hook_copy(hook_t *src)
724381a2a9aSdr146992 {
725381a2a9aSdr146992 	hook_int_t *new;
726381a2a9aSdr146992 	hook_t *dst;
727381a2a9aSdr146992 
728381a2a9aSdr146992 	ASSERT(src != NULL);
729381a2a9aSdr146992 	ASSERT(src->h_name != NULL);
730381a2a9aSdr146992 
731381a2a9aSdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
732381a2a9aSdr146992 
733381a2a9aSdr146992 	/* Copy body */
734381a2a9aSdr146992 	dst = &new->hi_hook;
735381a2a9aSdr146992 	*dst = *src;
736381a2a9aSdr146992 
737381a2a9aSdr146992 	/* Copy name */
738381a2a9aSdr146992 	dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP);
739381a2a9aSdr146992 	(void) strcpy(dst->h_name, src->h_name);
740381a2a9aSdr146992 
741381a2a9aSdr146992 	return (new);
742381a2a9aSdr146992 }
743381a2a9aSdr146992 
744381a2a9aSdr146992 /*
745381a2a9aSdr146992  * Function:	hook_free
746381a2a9aSdr146992  * Returns:	None
747381a2a9aSdr146992  * Parameters:	hi(I) - internal hook pointer
748381a2a9aSdr146992  *
749381a2a9aSdr146992  * Free alloc memory for hook
750381a2a9aSdr146992  */
751381a2a9aSdr146992 static void
752381a2a9aSdr146992 hook_free(hook_int_t *hi)
753381a2a9aSdr146992 {
754381a2a9aSdr146992 	ASSERT(hi != NULL);
755381a2a9aSdr146992 
756381a2a9aSdr146992 	/* Free name space */
757381a2a9aSdr146992 	if (hi->hi_hook.h_name != NULL) {
758381a2a9aSdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
759381a2a9aSdr146992 	}
760381a2a9aSdr146992 
761381a2a9aSdr146992 	/* Free container */
762381a2a9aSdr146992 	kmem_free(hi, sizeof (*hi));
763381a2a9aSdr146992 }
764