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/param.h>
30#include <sys/kernel.h>
31#include <sys/ktr.h>
32#include <sys/lock.h>
33#include <sys/malloc.h>
34#include <sys/mutex.h>
35#include <sys/proc.h>
36#include <sys/systm.h>
37#include <sys/eventhandler.h>
38
39static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
40
41/* List of all eventhandler lists */
42static TAILQ_HEAD(, eventhandler_list)	eventhandler_lists;
43static int				eventhandler_lists_initted = 0;
44static struct mtx			eventhandler_mutex;
45
46struct eventhandler_entry_generic
47{
48    struct eventhandler_entry	ee;
49    void			(* func)(void);
50};
51
52static struct eventhandler_list *_eventhandler_find_list(const char *name);
53
54/*
55 * Initialize the eventhandler mutex and list.
56 */
57static void
58eventhandler_init(void *dummy __unused)
59{
60    TAILQ_INIT(&eventhandler_lists);
61    mtx_init(&eventhandler_mutex, "eventhandler", NULL, MTX_DEF);
62    atomic_store_rel_int(&eventhandler_lists_initted, 1);
63}
64SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init,
65    NULL);
66
67static struct eventhandler_list *
68eventhandler_find_or_create_list(const char *name)
69{
70	struct eventhandler_list *list, *new_list;
71
72	/* look for a matching, existing list */
73	list = _eventhandler_find_list(name);
74
75	/* Do we need to create the list? */
76	if (list == NULL) {
77	    mtx_unlock(&eventhandler_mutex);
78
79	    new_list = malloc(sizeof(*new_list) + strlen(name) + 1,
80		M_EVENTHANDLER, M_WAITOK | M_ZERO);
81
82	    /* If someone else created it already, then use that one. */
83	    mtx_lock(&eventhandler_mutex);
84	    list = _eventhandler_find_list(name);
85	    if (list != NULL) {
86		free(new_list, M_EVENTHANDLER);
87	    } else {
88		CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name);
89		list = new_list;
90		TAILQ_INIT(&list->el_entries);
91		list->el_name = (char *)(list + 1);
92		strcpy(list->el_name, name);
93		mtx_init(&list->el_lock, list->el_name, "eventhandler list",
94		    MTX_DEF);
95		TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link);
96	    }
97	}
98	return (list);
99}
100
101/*
102 * Insertion is O(n) due to the priority scan, but optimises to O(1)
103 * if all priorities are identical.
104 */
105static eventhandler_tag
106eventhandler_register_internal(struct eventhandler_list *list,
107    const char *name, eventhandler_tag epn)
108{
109    struct eventhandler_entry		*ep;
110
111    KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
112    KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
113
114    /* Do we need to find/create the list? */
115    if (list == NULL) {
116	    mtx_lock(&eventhandler_mutex);
117	    list = eventhandler_find_or_create_list(name);
118	    mtx_unlock(&eventhandler_mutex);
119    }
120
121    KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
122	("%s: handler for %s registered with dead priority", __func__, name));
123
124    /* sort it into the list */
125    CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn,
126	((struct eventhandler_entry_generic *)epn)->func, name);
127    EHL_LOCK(list);
128    TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
129	if (ep->ee_priority != EHE_DEAD_PRIORITY &&
130	    epn->ee_priority < ep->ee_priority) {
131	    TAILQ_INSERT_BEFORE(ep, epn, ee_link);
132	    break;
133	}
134    }
135    if (ep == NULL)
136	TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
137    EHL_UNLOCK(list);
138    return(epn);
139}
140
141eventhandler_tag
142eventhandler_register(struct eventhandler_list *list, const char *name,
143		      void *func, void *arg, int priority)
144{
145    struct eventhandler_entry_generic	*eg;
146
147    /* allocate an entry for this handler, populate it */
148    eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER,
149	M_WAITOK | M_ZERO);
150    eg->func = func;
151    eg->ee.ee_arg = arg;
152    eg->ee.ee_priority = priority;
153
154    return (eventhandler_register_internal(list, name, &eg->ee));
155}
156
157#ifdef VIMAGE
158struct eventhandler_entry_generic_vimage
159{
160    struct eventhandler_entry		ee;
161    vimage_iterator_func_t		func;		/* Vimage iterator function. */
162    struct eventhandler_entry_vimage	v_ee;		/* Original func, arg. */
163};
164
165eventhandler_tag
166vimage_eventhandler_register(struct eventhandler_list *list, const char *name,
167    void *func, void *arg, int priority, vimage_iterator_func_t iterfunc)
168{
169    struct eventhandler_entry_generic_vimage	*eg;
170
171    /* allocate an entry for this handler, populate it */
172    eg = malloc(sizeof(struct eventhandler_entry_generic_vimage),
173	M_EVENTHANDLER, M_WAITOK | M_ZERO);
174    eg->func = iterfunc;
175    eg->v_ee.func = func;
176    eg->v_ee.ee_arg = arg;
177    eg->ee.ee_arg = &eg->v_ee;
178    eg->ee.ee_priority = priority;
179
180    return (eventhandler_register_internal(list, name, &eg->ee));
181}
182#endif
183
184static void
185_eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag,
186    bool wait)
187{
188    struct eventhandler_entry	*ep = tag;
189
190    EHL_LOCK_ASSERT(list, MA_OWNED);
191    if (ep != NULL) {
192	/* remove just this entry */
193	if (list->el_runcount == 0) {
194	    CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep,
195		list->el_name);
196	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
197	    free(ep, M_EVENTHANDLER);
198	} else {
199	    CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__,
200		ep, list->el_name);
201	    ep->ee_priority = EHE_DEAD_PRIORITY;
202	}
203    } else {
204	/* remove entire list */
205	if (list->el_runcount == 0) {
206	    CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__,
207		list->el_name);
208	    while (!TAILQ_EMPTY(&list->el_entries)) {
209		ep = TAILQ_FIRST(&list->el_entries);
210		TAILQ_REMOVE(&list->el_entries, ep, ee_link);
211		free(ep, M_EVENTHANDLER);
212	    }
213	} else {
214	    CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead",
215		__func__, list->el_name);
216	    TAILQ_FOREACH(ep, &list->el_entries, ee_link)
217		ep->ee_priority = EHE_DEAD_PRIORITY;
218	}
219    }
220    while (wait && list->el_runcount > 0)
221	    mtx_sleep(list, &list->el_lock, 0, "evhrm", 0);
222    EHL_UNLOCK(list);
223}
224
225void
226eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
227{
228
229	_eventhandler_deregister(list, tag, true);
230}
231
232void
233eventhandler_deregister_nowait(struct eventhandler_list *list,
234    eventhandler_tag tag)
235{
236
237	_eventhandler_deregister(list, tag, false);
238}
239
240/*
241 * Internal version for use when eventhandler list is already locked.
242 */
243static struct eventhandler_list *
244_eventhandler_find_list(const char *name)
245{
246    struct eventhandler_list	*list;
247
248    mtx_assert(&eventhandler_mutex, MA_OWNED);
249    TAILQ_FOREACH(list, &eventhandler_lists, el_link) {
250	if (!strcmp(name, list->el_name))
251	    break;
252    }
253    return (list);
254}
255
256/*
257 * Lookup a "slow" list by name.  Returns with the list locked.
258 */
259struct eventhandler_list *
260eventhandler_find_list(const char *name)
261{
262    struct eventhandler_list	*list;
263
264    if (!eventhandler_lists_initted)
265	return(NULL);
266
267    /* scan looking for the requested list */
268    mtx_lock(&eventhandler_mutex);
269    list = _eventhandler_find_list(name);
270    if (list != NULL)
271	EHL_LOCK(list);
272    mtx_unlock(&eventhandler_mutex);
273
274    return(list);
275}
276
277/*
278 * Prune "dead" entries from an eventhandler list.
279 */
280void
281eventhandler_prune_list(struct eventhandler_list *list)
282{
283    struct eventhandler_entry *ep, *en;
284    int pruned = 0;
285
286    CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name);
287    EHL_LOCK_ASSERT(list, MA_OWNED);
288    TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
289	if (ep->ee_priority == EHE_DEAD_PRIORITY) {
290	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
291	    free(ep, M_EVENTHANDLER);
292	    pruned++;
293	}
294    }
295    if (pruned > 0)
296	    wakeup(list);
297}
298
299/*
300 * Create (or get the existing) list so the pointer can be stored by
301 * EVENTHANDLER_LIST_DEFINE.
302 */
303struct eventhandler_list *
304eventhandler_create_list(const char *name)
305{
306	struct eventhandler_list *list;
307
308	KASSERT(eventhandler_lists_initted,
309	    ("eventhandler list created too early"));
310
311	mtx_lock(&eventhandler_mutex);
312	list = eventhandler_find_or_create_list(name);
313	mtx_unlock(&eventhandler_mutex);
314
315	return (list);
316}
317