xref: /freebsd/sys/kern/subr_eventhandler.c (revision ccfd87fe2ac0e2e6aeb1911a7d7cce6712a8564f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/ktr.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/mutex.h>
38 #include <sys/proc.h>
39 #include <sys/systm.h>
40 #include <sys/eventhandler.h>
41 
42 static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
43 
44 /* List of all eventhandler lists */
45 static TAILQ_HEAD(, eventhandler_list)	eventhandler_lists;
46 static int				eventhandler_lists_initted = 0;
47 static struct mtx			eventhandler_mutex;
48 
49 struct eventhandler_entry_generic
50 {
51     struct eventhandler_entry	ee;
52     void			(* func)(void);
53 };
54 
55 static struct eventhandler_list *_eventhandler_find_list(const char *name);
56 
57 /*
58  * Initialize the eventhandler mutex and list.
59  */
60 static void
61 eventhandler_init(void *dummy __unused)
62 {
63     TAILQ_INIT(&eventhandler_lists);
64     mtx_init(&eventhandler_mutex, "eventhandler", NULL, MTX_DEF);
65     atomic_store_rel_int(&eventhandler_lists_initted, 1);
66 }
67 SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init,
68     NULL);
69 
70 static struct eventhandler_list *
71 eventhandler_find_or_create_list(const char *name)
72 {
73 	struct eventhandler_list *list, *new_list;
74 
75 	/* look for a matching, existing list */
76 	list = _eventhandler_find_list(name);
77 
78 	/* Do we need to create the list? */
79 	if (list == NULL) {
80 	    mtx_unlock(&eventhandler_mutex);
81 
82 	    new_list = malloc(sizeof(*new_list) + strlen(name) + 1,
83 		M_EVENTHANDLER, M_WAITOK | M_ZERO);
84 
85 	    /* If someone else created it already, then use that one. */
86 	    mtx_lock(&eventhandler_mutex);
87 	    list = _eventhandler_find_list(name);
88 	    if (list != NULL) {
89 		free(new_list, M_EVENTHANDLER);
90 	    } else {
91 		CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name);
92 		list = new_list;
93 		TAILQ_INIT(&list->el_entries);
94 		list->el_name = (char *)(list + 1);
95 		strcpy(list->el_name, name);
96 		mtx_init(&list->el_lock, list->el_name, "eventhandler list",
97 		    MTX_DEF);
98 		TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link);
99 	    }
100 	}
101 	return (list);
102 }
103 
104 /*
105  * Insertion is O(n) due to the priority scan, but optimises to O(1)
106  * if all priorities are identical.
107  */
108 static eventhandler_tag
109 eventhandler_register_internal(struct eventhandler_list *list,
110     const char *name, eventhandler_tag epn)
111 {
112     struct eventhandler_entry		*ep;
113 
114     KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
115     KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
116 
117     /* Do we need to find/create the list? */
118     if (list == NULL) {
119 	    mtx_lock(&eventhandler_mutex);
120 	    list = eventhandler_find_or_create_list(name);
121 	    mtx_unlock(&eventhandler_mutex);
122     }
123 
124     KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
125 	("%s: handler for %s registered with dead priority", __func__, name));
126 
127     /* sort it into the list */
128     CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn,
129 	((struct eventhandler_entry_generic *)epn)->func, name);
130     EHL_LOCK(list);
131     TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
132 	if (ep->ee_priority != EHE_DEAD_PRIORITY &&
133 	    epn->ee_priority < ep->ee_priority) {
134 	    TAILQ_INSERT_BEFORE(ep, epn, ee_link);
135 	    break;
136 	}
137     }
138     if (ep == NULL)
139 	TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
140     EHL_UNLOCK(list);
141     return(epn);
142 }
143 
144 eventhandler_tag
145 eventhandler_register(struct eventhandler_list *list, const char *name,
146 		      void *func, void *arg, int priority)
147 {
148     struct eventhandler_entry_generic	*eg;
149 
150     /* allocate an entry for this handler, populate it */
151     eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER,
152 	M_WAITOK | M_ZERO);
153     eg->func = func;
154     eg->ee.ee_arg = arg;
155     eg->ee.ee_priority = priority;
156 
157     return (eventhandler_register_internal(list, name, &eg->ee));
158 }
159 
160 #ifdef VIMAGE
161 struct eventhandler_entry_generic_vimage
162 {
163     struct eventhandler_entry		ee;
164     vimage_iterator_func_t		func;		/* Vimage iterator function. */
165     struct eventhandler_entry_vimage	v_ee;		/* Original func, arg. */
166 };
167 
168 eventhandler_tag
169 vimage_eventhandler_register(struct eventhandler_list *list, const char *name,
170     void *func, void *arg, int priority, vimage_iterator_func_t iterfunc)
171 {
172     struct eventhandler_entry_generic_vimage	*eg;
173 
174     /* allocate an entry for this handler, populate it */
175     eg = malloc(sizeof(struct eventhandler_entry_generic_vimage),
176 	M_EVENTHANDLER, M_WAITOK | M_ZERO);
177     eg->func = iterfunc;
178     eg->v_ee.func = func;
179     eg->v_ee.ee_arg = arg;
180     eg->ee.ee_arg = &eg->v_ee;
181     eg->ee.ee_priority = priority;
182 
183     return (eventhandler_register_internal(list, name, &eg->ee));
184 }
185 #endif
186 
187 static void
188 _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag,
189     bool wait)
190 {
191     struct eventhandler_entry	*ep = tag;
192 
193     EHL_LOCK_ASSERT(list, MA_OWNED);
194     if (ep != NULL) {
195 	/* remove just this entry */
196 	if (list->el_runcount == 0) {
197 	    CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep,
198 		list->el_name);
199 	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
200 	    free(ep, M_EVENTHANDLER);
201 	} else {
202 	    CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__,
203 		ep, list->el_name);
204 	    ep->ee_priority = EHE_DEAD_PRIORITY;
205 	}
206     } else {
207 	/* remove entire list */
208 	if (list->el_runcount == 0) {
209 	    CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__,
210 		list->el_name);
211 	    while (!TAILQ_EMPTY(&list->el_entries)) {
212 		ep = TAILQ_FIRST(&list->el_entries);
213 		TAILQ_REMOVE(&list->el_entries, ep, ee_link);
214 		free(ep, M_EVENTHANDLER);
215 	    }
216 	} else {
217 	    CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead",
218 		__func__, list->el_name);
219 	    TAILQ_FOREACH(ep, &list->el_entries, ee_link)
220 		ep->ee_priority = EHE_DEAD_PRIORITY;
221 	}
222     }
223     while (wait && list->el_runcount > 0)
224 	    mtx_sleep(list, &list->el_lock, 0, "evhrm", 0);
225     EHL_UNLOCK(list);
226 }
227 
228 void
229 eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
230 {
231 
232 	_eventhandler_deregister(list, tag, true);
233 }
234 
235 void
236 eventhandler_deregister_nowait(struct eventhandler_list *list,
237     eventhandler_tag tag)
238 {
239 
240 	_eventhandler_deregister(list, tag, false);
241 }
242 
243 /*
244  * Internal version for use when eventhandler list is already locked.
245  */
246 static struct eventhandler_list *
247 _eventhandler_find_list(const char *name)
248 {
249     struct eventhandler_list	*list;
250 
251     mtx_assert(&eventhandler_mutex, MA_OWNED);
252     TAILQ_FOREACH(list, &eventhandler_lists, el_link) {
253 	if (!strcmp(name, list->el_name))
254 	    break;
255     }
256     return (list);
257 }
258 
259 /*
260  * Lookup a "slow" list by name.  Returns with the list locked.
261  */
262 struct eventhandler_list *
263 eventhandler_find_list(const char *name)
264 {
265     struct eventhandler_list	*list;
266 
267     if (!eventhandler_lists_initted)
268 	return(NULL);
269 
270     /* scan looking for the requested list */
271     mtx_lock(&eventhandler_mutex);
272     list = _eventhandler_find_list(name);
273     if (list != NULL)
274 	EHL_LOCK(list);
275     mtx_unlock(&eventhandler_mutex);
276 
277     return(list);
278 }
279 
280 /*
281  * Prune "dead" entries from an eventhandler list.
282  */
283 void
284 eventhandler_prune_list(struct eventhandler_list *list)
285 {
286     struct eventhandler_entry *ep, *en;
287     int pruned = 0;
288 
289     CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name);
290     EHL_LOCK_ASSERT(list, MA_OWNED);
291     TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
292 	if (ep->ee_priority == EHE_DEAD_PRIORITY) {
293 	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
294 	    free(ep, M_EVENTHANDLER);
295 	    pruned++;
296 	}
297     }
298     if (pruned > 0)
299 	    wakeup(list);
300 }
301 
302 /*
303  * Create (or get the existing) list so the pointer can be stored by
304  * EVENTHANDLER_LIST_DEFINE.
305  */
306 struct eventhandler_list *
307 eventhandler_create_list(const char *name)
308 {
309 	struct eventhandler_list *list;
310 
311 	KASSERT(eventhandler_lists_initted,
312 	    ("eventhandler list created too early"));
313 
314 	mtx_lock(&eventhandler_mutex);
315 	list = eventhandler_find_or_create_list(name);
316 	mtx_unlock(&eventhandler_mutex);
317 
318 	return (list);
319 }
320