FreeBSD kernel kern code
subr_eventhandler.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
42static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
43
44/* List of all eventhandler lists */
45static TAILQ_HEAD(, eventhandler_list) eventhandler_lists;
46static int eventhandler_lists_initted = 0;
47static struct mtx eventhandler_mutex;
48
49struct eventhandler_entry_generic
50{
51 struct eventhandler_entry ee;
52 void (* func)(void);
53};
54
55static struct eventhandler_list *_eventhandler_find_list(const char *name);
56
57/*
58 * Initialize the eventhandler mutex and list.
59 */
60static void
61eventhandler_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}
67SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init,
68 NULL);
69
70static struct eventhandler_list *
72{
73 struct eventhandler_list *list, *new_list;
74
75 /* look for a matching, existing list */
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);
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 */
108static eventhandler_tag
109eventhandler_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);
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
144eventhandler_tag
145eventhandler_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
161struct 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
168eventhandler_tag
169vimage_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
187static 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
228void
229eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
230{
231
232 _eventhandler_deregister(list, tag, true);
233}
234
235void
236eventhandler_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 */
246static struct eventhandler_list *
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 */
262struct eventhandler_list *
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);
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 */
283void
284eventhandler_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 */
306struct eventhandler_list *
308{
309 struct eventhandler_list *list;
310
311 KASSERT(eventhandler_lists_initted,
312 ("eventhandler list created too early"));
313
314 mtx_lock(&eventhandler_mutex);
316 mtx_unlock(&eventhandler_mutex);
317
318 return (list);
319}
int priority
Definition: cpufreq_if.m:46
const char * name
Definition: kern_fail.c:145
void *() malloc(size_t size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:632
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:907
void wakeup(const void *ident)
Definition: kern_synch.c:349
static struct eventhandler_list * _eventhandler_find_list(const char *name)
void eventhandler_prune_list(struct eventhandler_list *list)
static void eventhandler_init(void *dummy __unused)
void eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
static struct eventhandler_list * eventhandler_find_or_create_list(const char *name)
SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init, NULL)
static TAILQ_HEAD(eventhandler_list)
static void _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag, bool wait)
__FBSDID("$FreeBSD$")
void eventhandler_deregister_nowait(struct eventhandler_list *list, eventhandler_tag tag)
static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records")
struct eventhandler_list * eventhandler_create_list(const char *name)
eventhandler_tag eventhandler_register(struct eventhandler_list *list, const char *name, void *func, void *arg, int priority)
static eventhandler_tag eventhandler_register_internal(struct eventhandler_list *list, const char *name, eventhandler_tag epn)
struct eventhandler_list * eventhandler_find_list(const char *name)
struct mtx mtx
Definition: uipc_ktls.c:0
static int dummy