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