xref: /titanic_50/usr/src/uts/common/io/hook.c (revision 381a2a9a387f449fab7d0c7e97c4184c26963abf)
1*381a2a9aSdr146992 /*
2*381a2a9aSdr146992  * CDDL HEADER START
3*381a2a9aSdr146992  *
4*381a2a9aSdr146992  * The contents of this file are subject to the terms of the
5*381a2a9aSdr146992  * Common Development and Distribution License (the "License").
6*381a2a9aSdr146992  * You may not use this file except in compliance with the License.
7*381a2a9aSdr146992  *
8*381a2a9aSdr146992  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*381a2a9aSdr146992  * or http://www.opensolaris.org/os/licensing.
10*381a2a9aSdr146992  * See the License for the specific language governing permissions
11*381a2a9aSdr146992  * and limitations under the License.
12*381a2a9aSdr146992  *
13*381a2a9aSdr146992  * When distributing Covered Code, include this CDDL HEADER in each
14*381a2a9aSdr146992  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*381a2a9aSdr146992  * If applicable, add the following below this CDDL HEADER, with the
16*381a2a9aSdr146992  * fields enclosed by brackets "[]" replaced with your own identifying
17*381a2a9aSdr146992  * information: Portions Copyright [yyyy] [name of copyright owner]
18*381a2a9aSdr146992  *
19*381a2a9aSdr146992  * CDDL HEADER END
20*381a2a9aSdr146992  */
21*381a2a9aSdr146992 /*
22*381a2a9aSdr146992  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*381a2a9aSdr146992  * Use is subject to license terms.
24*381a2a9aSdr146992  */
25*381a2a9aSdr146992 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26*381a2a9aSdr146992 
27*381a2a9aSdr146992 #include <sys/param.h>
28*381a2a9aSdr146992 #include <sys/types.h>
29*381a2a9aSdr146992 #include <sys/systm.h>
30*381a2a9aSdr146992 #include <sys/errno.h>
31*381a2a9aSdr146992 #include <sys/kmem.h>
32*381a2a9aSdr146992 #include <sys/mutex.h>
33*381a2a9aSdr146992 #include <sys/condvar.h>
34*381a2a9aSdr146992 #include <sys/modctl.h>
35*381a2a9aSdr146992 #include <sys/hook_impl.h>
36*381a2a9aSdr146992 #include <sys/sdt.h>
37*381a2a9aSdr146992 
38*381a2a9aSdr146992 /*
39*381a2a9aSdr146992  * This file provides kernel hook framework.
40*381a2a9aSdr146992  */
41*381a2a9aSdr146992 
42*381a2a9aSdr146992 static struct modldrv modlmisc = {
43*381a2a9aSdr146992 	&mod_miscops,				/* drv_modops */
44*381a2a9aSdr146992 	"Hooks Interface v1.0",			/* drv_linkinfo */
45*381a2a9aSdr146992 };
46*381a2a9aSdr146992 
47*381a2a9aSdr146992 static struct modlinkage modlinkage = {
48*381a2a9aSdr146992 	MODREV_1,				/* ml_rev */
49*381a2a9aSdr146992 	&modlmisc,				/* ml_linkage */
50*381a2a9aSdr146992 	NULL
51*381a2a9aSdr146992 };
52*381a2a9aSdr146992 
53*381a2a9aSdr146992 /*
54*381a2a9aSdr146992  * Hook internal functions
55*381a2a9aSdr146992  */
56*381a2a9aSdr146992 static hook_int_t *hook_copy(hook_t *src);
57*381a2a9aSdr146992 static hook_event_int_t *hook_event_checkdup(hook_event_t *he);
58*381a2a9aSdr146992 static hook_event_int_t *hook_event_copy(hook_event_t *src);
59*381a2a9aSdr146992 static hook_event_int_t *hook_event_find(hook_family_int_t *hfi, char *event);
60*381a2a9aSdr146992 static void hook_event_free(hook_event_int_t *hei);
61*381a2a9aSdr146992 static hook_family_int_t *hook_family_copy(hook_family_t *src);
62*381a2a9aSdr146992 static hook_family_int_t *hook_family_find(char *family);
63*381a2a9aSdr146992 static void hook_family_free(hook_family_int_t *hfi);
64*381a2a9aSdr146992 static hook_int_t *hook_find(hook_event_int_t *hei, hook_t *h);
65*381a2a9aSdr146992 static void hook_free(hook_int_t *hi);
66*381a2a9aSdr146992 static void hook_init(void);
67*381a2a9aSdr146992 
68*381a2a9aSdr146992 static cvwaitlock_t familylock;			/* global lock */
69*381a2a9aSdr146992 static hook_family_int_head_t familylist;	/* family list head */
70*381a2a9aSdr146992 
71*381a2a9aSdr146992 /*
72*381a2a9aSdr146992  * Module entry points.
73*381a2a9aSdr146992  */
74*381a2a9aSdr146992 int
75*381a2a9aSdr146992 _init(void)
76*381a2a9aSdr146992 {
77*381a2a9aSdr146992 	hook_init();
78*381a2a9aSdr146992 	return (mod_install(&modlinkage));
79*381a2a9aSdr146992 }
80*381a2a9aSdr146992 
81*381a2a9aSdr146992 
82*381a2a9aSdr146992 int
83*381a2a9aSdr146992 _fini(void)
84*381a2a9aSdr146992 {
85*381a2a9aSdr146992 	return (mod_remove(&modlinkage));
86*381a2a9aSdr146992 }
87*381a2a9aSdr146992 
88*381a2a9aSdr146992 
89*381a2a9aSdr146992 int
90*381a2a9aSdr146992 _info(struct modinfo *modinfop)
91*381a2a9aSdr146992 {
92*381a2a9aSdr146992 	return (mod_info(&modlinkage, modinfop));
93*381a2a9aSdr146992 }
94*381a2a9aSdr146992 
95*381a2a9aSdr146992 
96*381a2a9aSdr146992 /*
97*381a2a9aSdr146992  * Function:	hook_init
98*381a2a9aSdr146992  * Returns:	None
99*381a2a9aSdr146992  * Parameters:	None
100*381a2a9aSdr146992  *
101*381a2a9aSdr146992  * Initialize hooks
102*381a2a9aSdr146992  */
103*381a2a9aSdr146992 static void
104*381a2a9aSdr146992 hook_init(void)
105*381a2a9aSdr146992 {
106*381a2a9aSdr146992 	CVW_INIT(&familylock);
107*381a2a9aSdr146992 	SLIST_INIT(&familylist);
108*381a2a9aSdr146992 }
109*381a2a9aSdr146992 
110*381a2a9aSdr146992 
111*381a2a9aSdr146992 /*
112*381a2a9aSdr146992  * Function:	hook_run
113*381a2a9aSdr146992  * Returns:	int - return value according to callback func
114*381a2a9aSdr146992  * Parameters:	token(I) - event pointer
115*381a2a9aSdr146992  *		info(I) - message
116*381a2a9aSdr146992  *
117*381a2a9aSdr146992  * Run hooks for specific provider.  The hooks registered are stepped through
118*381a2a9aSdr146992  * until either the end of the list is reached or a hook function returns a
119*381a2a9aSdr146992  * non-zero value.  If a non-zero value is returned from a hook function, we
120*381a2a9aSdr146992  * return that value back to our caller.  By design, a hook function can be
121*381a2a9aSdr146992  * called more than once, simultaneously.
122*381a2a9aSdr146992  */
123*381a2a9aSdr146992 int
124*381a2a9aSdr146992 hook_run(hook_event_token_t token, hook_data_t info)
125*381a2a9aSdr146992 {
126*381a2a9aSdr146992 	hook_int_t *hi;
127*381a2a9aSdr146992 	hook_event_int_t *hei;
128*381a2a9aSdr146992 	int rval = 0;
129*381a2a9aSdr146992 
130*381a2a9aSdr146992 	ASSERT(token != NULL);
131*381a2a9aSdr146992 
132*381a2a9aSdr146992 	hei = (hook_event_int_t *)token;
133*381a2a9aSdr146992 	DTRACE_PROBE2(hook__run__start,
134*381a2a9aSdr146992 	    hook_event_token_t, token,
135*381a2a9aSdr146992 	    hook_data_t, info);
136*381a2a9aSdr146992 
137*381a2a9aSdr146992 	/* Hold global read lock to ensure event will not be deleted */
138*381a2a9aSdr146992 	CVW_ENTER_READ(&familylock);
139*381a2a9aSdr146992 
140*381a2a9aSdr146992 	/* Hold event read lock to ensure hook will not be changed */
141*381a2a9aSdr146992 	CVW_ENTER_READ(&hei->hei_lock);
142*381a2a9aSdr146992 
143*381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
144*381a2a9aSdr146992 		ASSERT(hi->hi_hook.h_func != NULL);
145*381a2a9aSdr146992 		DTRACE_PROBE3(hook__func__start,
146*381a2a9aSdr146992 		    hook_event_token_t, token,
147*381a2a9aSdr146992 		    hook_data_t, info,
148*381a2a9aSdr146992 		    hook_int_t *, hi);
149*381a2a9aSdr146992 		rval = (*hi->hi_hook.h_func)(token, info);
150*381a2a9aSdr146992 		DTRACE_PROBE4(hook__func__end,
151*381a2a9aSdr146992 		    hook_event_token_t, token,
152*381a2a9aSdr146992 		    hook_data_t, info,
153*381a2a9aSdr146992 		    hook_int_t *, hi,
154*381a2a9aSdr146992 		    int, rval);
155*381a2a9aSdr146992 		if (rval != 0)
156*381a2a9aSdr146992 			break;
157*381a2a9aSdr146992 	}
158*381a2a9aSdr146992 
159*381a2a9aSdr146992 	CVW_EXIT_READ(&hei->hei_lock);
160*381a2a9aSdr146992 	CVW_EXIT_READ(&familylock);
161*381a2a9aSdr146992 
162*381a2a9aSdr146992 	DTRACE_PROBE3(hook__run__end,
163*381a2a9aSdr146992 	    hook_event_token_t, token,
164*381a2a9aSdr146992 	    hook_data_t, info,
165*381a2a9aSdr146992 	    hook_int_t *, hi);
166*381a2a9aSdr146992 
167*381a2a9aSdr146992 	return (rval);
168*381a2a9aSdr146992 }
169*381a2a9aSdr146992 
170*381a2a9aSdr146992 
171*381a2a9aSdr146992 /*
172*381a2a9aSdr146992  * Function:	hook_family_add
173*381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Fail
174*381a2a9aSdr146992  * Parameters:	hf(I) - family pointer
175*381a2a9aSdr146992  *
176*381a2a9aSdr146992  * Add new family to family list
177*381a2a9aSdr146992  */
178*381a2a9aSdr146992 hook_family_int_t *
179*381a2a9aSdr146992 hook_family_add(hook_family_t *hf)
180*381a2a9aSdr146992 {
181*381a2a9aSdr146992 	hook_family_int_t *hfi, *new;
182*381a2a9aSdr146992 
183*381a2a9aSdr146992 	ASSERT(hf != NULL);
184*381a2a9aSdr146992 	ASSERT(hf->hf_name != NULL);
185*381a2a9aSdr146992 
186*381a2a9aSdr146992 	new = hook_family_copy(hf);
187*381a2a9aSdr146992 	if (new == NULL)
188*381a2a9aSdr146992 		return (NULL);
189*381a2a9aSdr146992 
190*381a2a9aSdr146992 	CVW_ENTER_WRITE(&familylock);
191*381a2a9aSdr146992 
192*381a2a9aSdr146992 	/* search family list */
193*381a2a9aSdr146992 	hfi = hook_family_find(hf->hf_name);
194*381a2a9aSdr146992 	if (hfi != NULL) {
195*381a2a9aSdr146992 		CVW_EXIT_WRITE(&familylock);
196*381a2a9aSdr146992 		hook_family_free(new);
197*381a2a9aSdr146992 		return (NULL);
198*381a2a9aSdr146992 	}
199*381a2a9aSdr146992 
200*381a2a9aSdr146992 	/* Add to family list head */
201*381a2a9aSdr146992 	SLIST_INSERT_HEAD(&familylist, new, hfi_entry);
202*381a2a9aSdr146992 
203*381a2a9aSdr146992 	CVW_EXIT_WRITE(&familylock);
204*381a2a9aSdr146992 	return (new);
205*381a2a9aSdr146992 }
206*381a2a9aSdr146992 
207*381a2a9aSdr146992 
208*381a2a9aSdr146992 /*
209*381a2a9aSdr146992  * Function:	hook_family_remove
210*381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
211*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
212*381a2a9aSdr146992  *
213*381a2a9aSdr146992  * Remove family from family list
214*381a2a9aSdr146992  */
215*381a2a9aSdr146992 int
216*381a2a9aSdr146992 hook_family_remove(hook_family_int_t *hfi)
217*381a2a9aSdr146992 {
218*381a2a9aSdr146992 
219*381a2a9aSdr146992 	ASSERT(hfi != NULL);
220*381a2a9aSdr146992 
221*381a2a9aSdr146992 	CVW_ENTER_WRITE(&familylock);
222*381a2a9aSdr146992 
223*381a2a9aSdr146992 	/* Check if there are events  */
224*381a2a9aSdr146992 	if (!SLIST_EMPTY(&hfi->hfi_head)) {
225*381a2a9aSdr146992 		CVW_EXIT_WRITE(&familylock);
226*381a2a9aSdr146992 		return (EBUSY);
227*381a2a9aSdr146992 	}
228*381a2a9aSdr146992 
229*381a2a9aSdr146992 	/* Remove from family list */
230*381a2a9aSdr146992 	SLIST_REMOVE(&familylist, hfi, hook_family_int, hfi_entry);
231*381a2a9aSdr146992 
232*381a2a9aSdr146992 	CVW_EXIT_WRITE(&familylock);
233*381a2a9aSdr146992 	hook_family_free(hfi);
234*381a2a9aSdr146992 
235*381a2a9aSdr146992 	return (0);
236*381a2a9aSdr146992 }
237*381a2a9aSdr146992 
238*381a2a9aSdr146992 
239*381a2a9aSdr146992 /*
240*381a2a9aSdr146992  * Function:	hook_family_copy
241*381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Failed
242*381a2a9aSdr146992  * Parameters:	src(I) - family pointer
243*381a2a9aSdr146992  *
244*381a2a9aSdr146992  * Allocate internal family block and duplicate incoming family
245*381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
246*381a2a9aSdr146992  */
247*381a2a9aSdr146992 static hook_family_int_t *
248*381a2a9aSdr146992 hook_family_copy(hook_family_t *src)
249*381a2a9aSdr146992 {
250*381a2a9aSdr146992 	hook_family_int_t *new;
251*381a2a9aSdr146992 	hook_family_t *dst;
252*381a2a9aSdr146992 
253*381a2a9aSdr146992 	ASSERT(src != NULL);
254*381a2a9aSdr146992 	ASSERT(src->hf_name != NULL);
255*381a2a9aSdr146992 
256*381a2a9aSdr146992 	new = (hook_family_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
257*381a2a9aSdr146992 
258*381a2a9aSdr146992 	/* Copy body */
259*381a2a9aSdr146992 	SLIST_INIT(&new->hfi_head);
260*381a2a9aSdr146992 	dst = &new->hfi_family;
261*381a2a9aSdr146992 	*dst = *src;
262*381a2a9aSdr146992 
263*381a2a9aSdr146992 	/* Copy name */
264*381a2a9aSdr146992 	dst->hf_name = (char *)kmem_alloc(strlen(src->hf_name) + 1, KM_SLEEP);
265*381a2a9aSdr146992 	(void) strcpy(dst->hf_name, src->hf_name);
266*381a2a9aSdr146992 
267*381a2a9aSdr146992 	return (new);
268*381a2a9aSdr146992 }
269*381a2a9aSdr146992 
270*381a2a9aSdr146992 
271*381a2a9aSdr146992 /*
272*381a2a9aSdr146992  * Function:	hook_family_find
273*381a2a9aSdr146992  * Returns:	internal family pointer - NULL = Not match
274*381a2a9aSdr146992  * Parameters:	family(I) - family name string
275*381a2a9aSdr146992  *
276*381a2a9aSdr146992  * Search family list with family name
277*381a2a9aSdr146992  * 	A lock on familylock must be held when called.
278*381a2a9aSdr146992  */
279*381a2a9aSdr146992 static hook_family_int_t *
280*381a2a9aSdr146992 hook_family_find(char *family)
281*381a2a9aSdr146992 {
282*381a2a9aSdr146992 	hook_family_int_t *hfi = NULL;
283*381a2a9aSdr146992 
284*381a2a9aSdr146992 	ASSERT(family != NULL);
285*381a2a9aSdr146992 
286*381a2a9aSdr146992 	SLIST_FOREACH(hfi, &familylist, hfi_entry) {
287*381a2a9aSdr146992 		if (strcmp(hfi->hfi_family.hf_name, family) == 0)
288*381a2a9aSdr146992 			break;
289*381a2a9aSdr146992 	}
290*381a2a9aSdr146992 	return (hfi);
291*381a2a9aSdr146992 }
292*381a2a9aSdr146992 
293*381a2a9aSdr146992 
294*381a2a9aSdr146992 /*
295*381a2a9aSdr146992  * Function:	hook_family_free
296*381a2a9aSdr146992  * Returns:	None
297*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
298*381a2a9aSdr146992  *
299*381a2a9aSdr146992  * Free alloc memory for family
300*381a2a9aSdr146992  */
301*381a2a9aSdr146992 static void
302*381a2a9aSdr146992 hook_family_free(hook_family_int_t *hfi)
303*381a2a9aSdr146992 {
304*381a2a9aSdr146992 	ASSERT(hfi != NULL);
305*381a2a9aSdr146992 
306*381a2a9aSdr146992 	/* Free name space */
307*381a2a9aSdr146992 	if (hfi->hfi_family.hf_name != NULL) {
308*381a2a9aSdr146992 		kmem_free(hfi->hfi_family.hf_name,
309*381a2a9aSdr146992 		    strlen(hfi->hfi_family.hf_name) + 1);
310*381a2a9aSdr146992 	}
311*381a2a9aSdr146992 
312*381a2a9aSdr146992 	/* Free container */
313*381a2a9aSdr146992 	kmem_free(hfi, sizeof (*hfi));
314*381a2a9aSdr146992 }
315*381a2a9aSdr146992 
316*381a2a9aSdr146992 
317*381a2a9aSdr146992 /*
318*381a2a9aSdr146992  * Function:	hook_event_add
319*381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Fail
320*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
321*381a2a9aSdr146992  *		he(I) - event pointer
322*381a2a9aSdr146992  *
323*381a2a9aSdr146992  * Add new event to event list on specific family.
324*381a2a9aSdr146992  * This function can fail to return successfully if (1) it cannot allocate
325*381a2a9aSdr146992  * enough memory for its own internal data structures, (2) the event has
326*381a2a9aSdr146992  * already been registered (for any hook family.)
327*381a2a9aSdr146992  */
328*381a2a9aSdr146992 hook_event_int_t *
329*381a2a9aSdr146992 hook_event_add(hook_family_int_t *hfi, hook_event_t *he)
330*381a2a9aSdr146992 {
331*381a2a9aSdr146992 	hook_event_int_t *hei, *new;
332*381a2a9aSdr146992 
333*381a2a9aSdr146992 	ASSERT(hfi != NULL);
334*381a2a9aSdr146992 	ASSERT(he != NULL);
335*381a2a9aSdr146992 	ASSERT(he->he_name != NULL);
336*381a2a9aSdr146992 
337*381a2a9aSdr146992 	new = hook_event_copy(he);
338*381a2a9aSdr146992 	if (new == NULL)
339*381a2a9aSdr146992 		return (NULL);
340*381a2a9aSdr146992 
341*381a2a9aSdr146992 	CVW_ENTER_WRITE(&familylock);
342*381a2a9aSdr146992 
343*381a2a9aSdr146992 	/* Check whether this event pointer is already registered */
344*381a2a9aSdr146992 	hei = hook_event_checkdup(he);
345*381a2a9aSdr146992 	if (hei != NULL) {
346*381a2a9aSdr146992 		CVW_EXIT_WRITE(&familylock);
347*381a2a9aSdr146992 		hook_event_free(new);
348*381a2a9aSdr146992 		return (NULL);
349*381a2a9aSdr146992 	}
350*381a2a9aSdr146992 
351*381a2a9aSdr146992 	/* Add to event list head */
352*381a2a9aSdr146992 	SLIST_INSERT_HEAD(&hfi->hfi_head, new, hei_entry);
353*381a2a9aSdr146992 
354*381a2a9aSdr146992 	CVW_EXIT_WRITE(&familylock);
355*381a2a9aSdr146992 	return (new);
356*381a2a9aSdr146992 }
357*381a2a9aSdr146992 
358*381a2a9aSdr146992 
359*381a2a9aSdr146992 /*
360*381a2a9aSdr146992  * Function:	hook_event_remove
361*381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
362*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
363*381a2a9aSdr146992  *		he(I) - event pointer
364*381a2a9aSdr146992  *
365*381a2a9aSdr146992  * Remove event from event list on specific family
366*381a2a9aSdr146992  */
367*381a2a9aSdr146992 int
368*381a2a9aSdr146992 hook_event_remove(hook_family_int_t *hfi, hook_event_t *he)
369*381a2a9aSdr146992 {
370*381a2a9aSdr146992 	hook_event_int_t *hei;
371*381a2a9aSdr146992 
372*381a2a9aSdr146992 	ASSERT(hfi != NULL);
373*381a2a9aSdr146992 	ASSERT(he != NULL);
374*381a2a9aSdr146992 
375*381a2a9aSdr146992 	CVW_ENTER_WRITE(&familylock);
376*381a2a9aSdr146992 
377*381a2a9aSdr146992 	hei = hook_event_find(hfi, he->he_name);
378*381a2a9aSdr146992 	if (hei == NULL) {
379*381a2a9aSdr146992 		CVW_EXIT_WRITE(&familylock);
380*381a2a9aSdr146992 		return (ENXIO);
381*381a2a9aSdr146992 	}
382*381a2a9aSdr146992 
383*381a2a9aSdr146992 	/* Check if there are registered hooks for this event */
384*381a2a9aSdr146992 	if (!TAILQ_EMPTY(&hei->hei_head)) {
385*381a2a9aSdr146992 		CVW_EXIT_WRITE(&familylock);
386*381a2a9aSdr146992 		return (EBUSY);
387*381a2a9aSdr146992 	}
388*381a2a9aSdr146992 
389*381a2a9aSdr146992 	/* Remove from event list */
390*381a2a9aSdr146992 	SLIST_REMOVE(&hfi->hfi_head, hei, hook_event_int, hei_entry);
391*381a2a9aSdr146992 
392*381a2a9aSdr146992 	CVW_EXIT_WRITE(&familylock);
393*381a2a9aSdr146992 	hook_event_free(hei);
394*381a2a9aSdr146992 
395*381a2a9aSdr146992 	return (0);
396*381a2a9aSdr146992 }
397*381a2a9aSdr146992 
398*381a2a9aSdr146992 
399*381a2a9aSdr146992 /*
400*381a2a9aSdr146992  * Function:    hook_event_checkdup
401*381a2a9aSdr146992  * Returns:     internal event pointer - NULL = Not match
402*381a2a9aSdr146992  * Parameters:  he(I) - event pointer
403*381a2a9aSdr146992  *
404*381a2a9aSdr146992  * Search whole list with event pointer
405*381a2a9aSdr146992  *      A lock on familylock must be held when called.
406*381a2a9aSdr146992  */
407*381a2a9aSdr146992 static hook_event_int_t *
408*381a2a9aSdr146992 hook_event_checkdup(hook_event_t *he)
409*381a2a9aSdr146992 {
410*381a2a9aSdr146992 	hook_family_int_t *hfi;
411*381a2a9aSdr146992 	hook_event_int_t *hei;
412*381a2a9aSdr146992 
413*381a2a9aSdr146992 	ASSERT(he != NULL);
414*381a2a9aSdr146992 
415*381a2a9aSdr146992 	SLIST_FOREACH(hfi, &familylist, hfi_entry) {
416*381a2a9aSdr146992 		SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
417*381a2a9aSdr146992 			if (hei->hei_event == he)
418*381a2a9aSdr146992 				return (hei);
419*381a2a9aSdr146992 		}
420*381a2a9aSdr146992 	}
421*381a2a9aSdr146992 
422*381a2a9aSdr146992 	return (NULL);
423*381a2a9aSdr146992 }
424*381a2a9aSdr146992 
425*381a2a9aSdr146992 
426*381a2a9aSdr146992 /*
427*381a2a9aSdr146992  * Function:	hook_event_copy
428*381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Failed
429*381a2a9aSdr146992  * Parameters:	src(I) - event pointer
430*381a2a9aSdr146992  *
431*381a2a9aSdr146992  * Allocate internal event block and duplicate incoming event
432*381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
433*381a2a9aSdr146992  */
434*381a2a9aSdr146992 static hook_event_int_t *
435*381a2a9aSdr146992 hook_event_copy(hook_event_t *src)
436*381a2a9aSdr146992 {
437*381a2a9aSdr146992 	hook_event_int_t *new;
438*381a2a9aSdr146992 
439*381a2a9aSdr146992 	ASSERT(src != NULL);
440*381a2a9aSdr146992 	ASSERT(src->he_name != NULL);
441*381a2a9aSdr146992 
442*381a2a9aSdr146992 	new = (hook_event_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
443*381a2a9aSdr146992 
444*381a2a9aSdr146992 	/* Copy body */
445*381a2a9aSdr146992 	TAILQ_INIT(&new->hei_head);
446*381a2a9aSdr146992 	new->hei_event = src;
447*381a2a9aSdr146992 
448*381a2a9aSdr146992 	return (new);
449*381a2a9aSdr146992 }
450*381a2a9aSdr146992 
451*381a2a9aSdr146992 
452*381a2a9aSdr146992 /*
453*381a2a9aSdr146992  * Function:	hook_event_find
454*381a2a9aSdr146992  * Returns:	internal event pointer - NULL = Not match
455*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
456*381a2a9aSdr146992  *		event(I) - event name string
457*381a2a9aSdr146992  *
458*381a2a9aSdr146992  * Search event list with event name
459*381a2a9aSdr146992  * 	A lock on familylock must be held when called.
460*381a2a9aSdr146992  */
461*381a2a9aSdr146992 static hook_event_int_t *
462*381a2a9aSdr146992 hook_event_find(hook_family_int_t *hfi, char *event)
463*381a2a9aSdr146992 {
464*381a2a9aSdr146992 	hook_event_int_t *hei = NULL;
465*381a2a9aSdr146992 
466*381a2a9aSdr146992 	ASSERT(hfi != NULL);
467*381a2a9aSdr146992 	ASSERT(event != NULL);
468*381a2a9aSdr146992 
469*381a2a9aSdr146992 	SLIST_FOREACH(hei, &hfi->hfi_head, hei_entry) {
470*381a2a9aSdr146992 		if (strcmp(hei->hei_event->he_name, event) == 0)
471*381a2a9aSdr146992 			break;
472*381a2a9aSdr146992 	}
473*381a2a9aSdr146992 	return (hei);
474*381a2a9aSdr146992 }
475*381a2a9aSdr146992 
476*381a2a9aSdr146992 
477*381a2a9aSdr146992 /*
478*381a2a9aSdr146992  * Function:	hook_event_free
479*381a2a9aSdr146992  * Returns:	None
480*381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
481*381a2a9aSdr146992  *
482*381a2a9aSdr146992  * Free alloc memory for event
483*381a2a9aSdr146992  */
484*381a2a9aSdr146992 static void
485*381a2a9aSdr146992 hook_event_free(hook_event_int_t *hei)
486*381a2a9aSdr146992 {
487*381a2a9aSdr146992 	ASSERT(hei != NULL);
488*381a2a9aSdr146992 
489*381a2a9aSdr146992 	/* Free container */
490*381a2a9aSdr146992 	kmem_free(hei, sizeof (*hei));
491*381a2a9aSdr146992 }
492*381a2a9aSdr146992 
493*381a2a9aSdr146992 
494*381a2a9aSdr146992 /*
495*381a2a9aSdr146992  * Function:	hook_register
496*381a2a9aSdr146992  * Returns:	int- 0 = Succ, Else = Fail
497*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
498*381a2a9aSdr146992  *		event(I) - event name string
499*381a2a9aSdr146992  *		h(I) - hook pointer
500*381a2a9aSdr146992  *
501*381a2a9aSdr146992  * Add new hook to hook list on spefic family, event
502*381a2a9aSdr146992  */
503*381a2a9aSdr146992 int
504*381a2a9aSdr146992 hook_register(hook_family_int_t *hfi, char *event, hook_t *h)
505*381a2a9aSdr146992 {
506*381a2a9aSdr146992 	hook_event_int_t *hei;
507*381a2a9aSdr146992 	hook_int_t *hi, *new;
508*381a2a9aSdr146992 
509*381a2a9aSdr146992 	ASSERT(hfi != NULL);
510*381a2a9aSdr146992 	ASSERT(event != NULL);
511*381a2a9aSdr146992 	ASSERT(h != NULL);
512*381a2a9aSdr146992 
513*381a2a9aSdr146992 	/* Alloc hook_int_t and copy hook */
514*381a2a9aSdr146992 	new = hook_copy(h);
515*381a2a9aSdr146992 	if (new == NULL)
516*381a2a9aSdr146992 		return (ENOMEM);
517*381a2a9aSdr146992 
518*381a2a9aSdr146992 	/*
519*381a2a9aSdr146992 	 * Since hook add/remove only impact event, so it is unnecessary
520*381a2a9aSdr146992 	 * to hold global family write lock. Just get read lock here to
521*381a2a9aSdr146992 	 * ensure event will not be removed when doing hooks operation
522*381a2a9aSdr146992 	 */
523*381a2a9aSdr146992 	CVW_ENTER_READ(&familylock);
524*381a2a9aSdr146992 
525*381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
526*381a2a9aSdr146992 	if (hei == NULL) {
527*381a2a9aSdr146992 		CVW_EXIT_READ(&familylock);
528*381a2a9aSdr146992 		hook_free(new);
529*381a2a9aSdr146992 		return (ENXIO);
530*381a2a9aSdr146992 	}
531*381a2a9aSdr146992 
532*381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
533*381a2a9aSdr146992 
534*381a2a9aSdr146992 	/* Multiple hooks are only allowed for read-only events. */
535*381a2a9aSdr146992 	if (((hei->hei_event->he_flags & HOOK_RDONLY) == 0) &&
536*381a2a9aSdr146992 	    (!TAILQ_EMPTY(&hei->hei_head))) {
537*381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
538*381a2a9aSdr146992 		CVW_EXIT_READ(&familylock);
539*381a2a9aSdr146992 		hook_free(new);
540*381a2a9aSdr146992 		return (EEXIST);
541*381a2a9aSdr146992 	}
542*381a2a9aSdr146992 
543*381a2a9aSdr146992 	hi = hook_find(hei, h);
544*381a2a9aSdr146992 	if (hi != NULL) {
545*381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
546*381a2a9aSdr146992 		CVW_EXIT_READ(&familylock);
547*381a2a9aSdr146992 		hook_free(new);
548*381a2a9aSdr146992 		return (EEXIST);
549*381a2a9aSdr146992 	}
550*381a2a9aSdr146992 
551*381a2a9aSdr146992 	/* Add to hook list head */
552*381a2a9aSdr146992 	TAILQ_INSERT_HEAD(&hei->hei_head, new, hi_entry);
553*381a2a9aSdr146992 	hei->hei_event->he_interested = B_TRUE;
554*381a2a9aSdr146992 
555*381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
556*381a2a9aSdr146992 	CVW_EXIT_READ(&familylock);
557*381a2a9aSdr146992 	return (0);
558*381a2a9aSdr146992 }
559*381a2a9aSdr146992 
560*381a2a9aSdr146992 
561*381a2a9aSdr146992 /*
562*381a2a9aSdr146992  * Function:	hook_unregister
563*381a2a9aSdr146992  * Returns:	int - 0 = Succ, Else = Fail
564*381a2a9aSdr146992  * Parameters:	hfi(I) - internal family pointer
565*381a2a9aSdr146992  *		event(I) - event name string
566*381a2a9aSdr146992  *		h(I) - hook pointer
567*381a2a9aSdr146992  *
568*381a2a9aSdr146992  * Remove hook from hook list on specific family, event
569*381a2a9aSdr146992  */
570*381a2a9aSdr146992 int
571*381a2a9aSdr146992 hook_unregister(hook_family_int_t *hfi, char *event, hook_t *h)
572*381a2a9aSdr146992 {
573*381a2a9aSdr146992 	hook_event_int_t *hei;
574*381a2a9aSdr146992 	hook_int_t *hi;
575*381a2a9aSdr146992 
576*381a2a9aSdr146992 	ASSERT(hfi != NULL);
577*381a2a9aSdr146992 	ASSERT(h != NULL);
578*381a2a9aSdr146992 
579*381a2a9aSdr146992 	CVW_ENTER_READ(&familylock);
580*381a2a9aSdr146992 
581*381a2a9aSdr146992 	hei = hook_event_find(hfi, event);
582*381a2a9aSdr146992 	if (hei == NULL) {
583*381a2a9aSdr146992 		CVW_EXIT_READ(&familylock);
584*381a2a9aSdr146992 		return (ENXIO);
585*381a2a9aSdr146992 	}
586*381a2a9aSdr146992 
587*381a2a9aSdr146992 	/* Hold write lock for event */
588*381a2a9aSdr146992 	CVW_ENTER_WRITE(&hei->hei_lock);
589*381a2a9aSdr146992 
590*381a2a9aSdr146992 	hi = hook_find(hei, h);
591*381a2a9aSdr146992 	if (hi == NULL) {
592*381a2a9aSdr146992 		CVW_EXIT_WRITE(&hei->hei_lock);
593*381a2a9aSdr146992 		CVW_EXIT_READ(&familylock);
594*381a2a9aSdr146992 		return (ENXIO);
595*381a2a9aSdr146992 	}
596*381a2a9aSdr146992 
597*381a2a9aSdr146992 	/* Remove from hook list */
598*381a2a9aSdr146992 	TAILQ_REMOVE(&hei->hei_head, hi, hi_entry);
599*381a2a9aSdr146992 	if (TAILQ_EMPTY(&hei->hei_head)) {
600*381a2a9aSdr146992 		hei->hei_event->he_interested = B_FALSE;
601*381a2a9aSdr146992 	}
602*381a2a9aSdr146992 
603*381a2a9aSdr146992 	CVW_EXIT_WRITE(&hei->hei_lock);
604*381a2a9aSdr146992 	CVW_EXIT_READ(&familylock);
605*381a2a9aSdr146992 
606*381a2a9aSdr146992 	hook_free(hi);
607*381a2a9aSdr146992 	return (0);
608*381a2a9aSdr146992 }
609*381a2a9aSdr146992 
610*381a2a9aSdr146992 
611*381a2a9aSdr146992 /*
612*381a2a9aSdr146992  * Function:	hook_find
613*381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Not match
614*381a2a9aSdr146992  * Parameters:	hei(I) - internal event pointer
615*381a2a9aSdr146992  *		h(I) - hook pointer
616*381a2a9aSdr146992  *
617*381a2a9aSdr146992  * Search hook list
618*381a2a9aSdr146992  * 	A lock on familylock must be held when called.
619*381a2a9aSdr146992  */
620*381a2a9aSdr146992 static hook_int_t *
621*381a2a9aSdr146992 hook_find(hook_event_int_t *hei, hook_t *h)
622*381a2a9aSdr146992 {
623*381a2a9aSdr146992 	hook_int_t *hi;
624*381a2a9aSdr146992 
625*381a2a9aSdr146992 	ASSERT(hei != NULL);
626*381a2a9aSdr146992 	ASSERT(h != NULL);
627*381a2a9aSdr146992 
628*381a2a9aSdr146992 	TAILQ_FOREACH(hi, &hei->hei_head, hi_entry) {
629*381a2a9aSdr146992 		if (strcmp(hi->hi_hook.h_name, h->h_name) == 0)
630*381a2a9aSdr146992 			break;
631*381a2a9aSdr146992 	}
632*381a2a9aSdr146992 	return (hi);
633*381a2a9aSdr146992 }
634*381a2a9aSdr146992 
635*381a2a9aSdr146992 
636*381a2a9aSdr146992 /*
637*381a2a9aSdr146992  * Function:	hook_copy
638*381a2a9aSdr146992  * Returns:	internal hook pointer - NULL = Failed
639*381a2a9aSdr146992  * Parameters:	src(I) - hook pointer
640*381a2a9aSdr146992  *
641*381a2a9aSdr146992  * Allocate internal hook block and duplicate incoming hook.
642*381a2a9aSdr146992  * No locks should be held across this function as it may sleep.
643*381a2a9aSdr146992  */
644*381a2a9aSdr146992 static hook_int_t *
645*381a2a9aSdr146992 hook_copy(hook_t *src)
646*381a2a9aSdr146992 {
647*381a2a9aSdr146992 	hook_int_t *new;
648*381a2a9aSdr146992 	hook_t *dst;
649*381a2a9aSdr146992 
650*381a2a9aSdr146992 	ASSERT(src != NULL);
651*381a2a9aSdr146992 	ASSERT(src->h_name != NULL);
652*381a2a9aSdr146992 
653*381a2a9aSdr146992 	new = (hook_int_t *)kmem_zalloc(sizeof (*new), KM_SLEEP);
654*381a2a9aSdr146992 
655*381a2a9aSdr146992 	/* Copy body */
656*381a2a9aSdr146992 	dst = &new->hi_hook;
657*381a2a9aSdr146992 	*dst = *src;
658*381a2a9aSdr146992 
659*381a2a9aSdr146992 	/* Copy name */
660*381a2a9aSdr146992 	dst->h_name = (char *)kmem_alloc(strlen(src->h_name) + 1, KM_SLEEP);
661*381a2a9aSdr146992 	(void) strcpy(dst->h_name, src->h_name);
662*381a2a9aSdr146992 
663*381a2a9aSdr146992 	return (new);
664*381a2a9aSdr146992 }
665*381a2a9aSdr146992 
666*381a2a9aSdr146992 /*
667*381a2a9aSdr146992  * Function:	hook_free
668*381a2a9aSdr146992  * Returns:	None
669*381a2a9aSdr146992  * Parameters:	hi(I) - internal hook pointer
670*381a2a9aSdr146992  *
671*381a2a9aSdr146992  * Free alloc memory for hook
672*381a2a9aSdr146992  */
673*381a2a9aSdr146992 static void
674*381a2a9aSdr146992 hook_free(hook_int_t *hi)
675*381a2a9aSdr146992 {
676*381a2a9aSdr146992 	ASSERT(hi != NULL);
677*381a2a9aSdr146992 
678*381a2a9aSdr146992 	/* Free name space */
679*381a2a9aSdr146992 	if (hi->hi_hook.h_name != NULL) {
680*381a2a9aSdr146992 		kmem_free(hi->hi_hook.h_name, strlen(hi->hi_hook.h_name) + 1);
681*381a2a9aSdr146992 	}
682*381a2a9aSdr146992 
683*381a2a9aSdr146992 	/* Free container */
684*381a2a9aSdr146992 	kmem_free(hi, sizeof (*hi));
685*381a2a9aSdr146992 }
686