150107Smsmith/*-
250107Smsmith * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
350107Smsmith * All rights reserved.
450107Smsmith *
550107Smsmith * Redistribution and use in source and binary forms, with or without
650107Smsmith * modification, are permitted provided that the following conditions
750107Smsmith * are met:
850107Smsmith * 1. Redistributions of source code must retain the above copyright
950107Smsmith *    notice, this list of conditions and the following disclaimer.
1050107Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1150107Smsmith *    notice, this list of conditions and the following disclaimer in the
1250107Smsmith *    documentation and/or other materials provided with the distribution.
1350107Smsmith *
1450107Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550107Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650107Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750107Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850107Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950107Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050107Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150107Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250107Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350107Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450107Smsmith * SUCH DAMAGE.
2550107Smsmith */
2650107Smsmith
27116182Sobrien#include <sys/cdefs.h>
28116182Sobrien__FBSDID("$FreeBSD: stable/11/sys/kern/subr_eventhandler.c 331727 2018-03-29 04:41:45Z mjoras $");
29116182Sobrien
3050107Smsmith#include <sys/param.h>
3150107Smsmith#include <sys/kernel.h>
3274914Sjhb#include <sys/lock.h>
3350107Smsmith#include <sys/malloc.h>
3469893Sjhb#include <sys/mutex.h>
3576166Smarkm#include <sys/proc.h>
3650107Smsmith#include <sys/systm.h>
3750107Smsmith#include <sys/eventhandler.h>
3850107Smsmith
3969774Sphkstatic MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
4050107Smsmith
41331727Smjoras/* List of all eventhandler lists */
4260938Sjakestatic TAILQ_HEAD(, eventhandler_list)	eventhandler_lists;
4350107Smsmithstatic int				eventhandler_lists_initted = 0;
4467535Sjhbstatic struct mtx			eventhandler_mutex;
4550107Smsmith
4650107Smsmithstruct eventhandler_entry_generic
4750107Smsmith{
4850107Smsmith    struct eventhandler_entry	ee;
4950107Smsmith    void			(* func)(void);
5050107Smsmith};
5150107Smsmith
52138439Sjkoshystatic struct eventhandler_list *_eventhandler_find_list(const char *name);
53112111Sjhb
5469893Sjhb/*
5569893Sjhb * Initialize the eventhandler mutex and list.
5669893Sjhb */
5769893Sjhbstatic void
5869893Sjhbeventhandler_init(void *dummy __unused)
5969893Sjhb{
6069893Sjhb    TAILQ_INIT(&eventhandler_lists);
61112111Sjhb    mtx_init(&eventhandler_mutex, "eventhandler", NULL, MTX_DEF);
62112111Sjhb    atomic_store_rel_int(&eventhandler_lists_initted, 1);
6369893Sjhb}
6469893SjhbSYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init,
65177253Srwatson    NULL);
6669893Sjhb
67331727Smjorasstatic struct eventhandler_list *
68331727Smjoraseventhandler_find_or_create_list(const char *name)
6950107Smsmith{
70331727Smjoras	struct eventhandler_list *list, *new_list;
7150107Smsmith
7250107Smsmith	/* look for a matching, existing list */
73112111Sjhb	list = _eventhandler_find_list(name);
7450107Smsmith
7550107Smsmith	/* Do we need to create the list? */
7650107Smsmith	if (list == NULL) {
77112111Sjhb	    mtx_unlock(&eventhandler_mutex);
78112111Sjhb
79331727Smjoras	    new_list = malloc(sizeof(*new_list) + strlen(name) + 1,
80331727Smjoras		M_EVENTHANDLER, M_WAITOK | M_ZERO);
81112111Sjhb
82112111Sjhb	    /* If someone else created it already, then use that one. */
83112111Sjhb	    mtx_lock(&eventhandler_mutex);
84112111Sjhb	    list = _eventhandler_find_list(name);
85112111Sjhb	    if (list != NULL) {
86112111Sjhb		free(new_list, M_EVENTHANDLER);
87112111Sjhb	    } else {
88112111Sjhb		CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name);
89112111Sjhb		list = new_list;
90331727Smjoras		TAILQ_INIT(&list->el_entries);
91331727Smjoras		list->el_name = (char *)(list + 1);
92112111Sjhb		strcpy(list->el_name, name);
93331727Smjoras		mtx_init(&list->el_lock, list->el_name, "eventhandler list",
94331727Smjoras		    MTX_DEF);
95112111Sjhb		TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link);
9666205Smsmith	    }
9750107Smsmith	}
98331727Smjoras	return (list);
99331727Smjoras}
100331727Smjoras
101331727Smjoras/*
102331727Smjoras * Insertion is O(n) due to the priority scan, but optimises to O(1)
103331727Smjoras * if all priorities are identical.
104331727Smjoras */
105331727Smjorasstatic eventhandler_tag
106331727Smjoraseventhandler_register_internal(struct eventhandler_list *list,
107331727Smjoras    const char *name, eventhandler_tag epn)
108331727Smjoras{
109331727Smjoras    struct eventhandler_entry		*ep;
110331727Smjoras
111331727Smjoras    KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
112331727Smjoras    KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
113331727Smjoras
114331727Smjoras    /* Do we need to find/create the list? */
115331727Smjoras    if (list == NULL) {
116331727Smjoras	    mtx_lock(&eventhandler_mutex);
117331727Smjoras	    list = eventhandler_find_or_create_list(name);
118331727Smjoras	    mtx_unlock(&eventhandler_mutex);
11950107Smsmith    }
120112111Sjhb
121205345Sbz    KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
122112111Sjhb	("%s: handler for %s registered with dead priority", __func__, name));
123112111Sjhb
12450107Smsmith    /* sort it into the list */
125205345Sbz    CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn,
126205345Sbz	((struct eventhandler_entry_generic *)epn)->func, name);
127112111Sjhb    EHL_LOCK(list);
128112111Sjhb    TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
129112111Sjhb	if (ep->ee_priority != EHE_DEAD_PRIORITY &&
130205345Sbz	    epn->ee_priority < ep->ee_priority) {
131205345Sbz	    TAILQ_INSERT_BEFORE(ep, epn, ee_link);
13250107Smsmith	    break;
13350107Smsmith	}
13450107Smsmith    }
13550107Smsmith    if (ep == NULL)
136205345Sbz	TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
137112111Sjhb    EHL_UNLOCK(list);
138205345Sbz    return(epn);
13950107Smsmith}
14050107Smsmith
141205345Sbzeventhandler_tag
142205345Sbzeventhandler_register(struct eventhandler_list *list, const char *name,
143205345Sbz		      void *func, void *arg, int priority)
144205345Sbz{
145205345Sbz    struct eventhandler_entry_generic	*eg;
146205345Sbz
147205345Sbz    /* allocate an entry for this handler, populate it */
148205345Sbz    eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER,
149205345Sbz	M_WAITOK | M_ZERO);
150205345Sbz    eg->func = func;
151205345Sbz    eg->ee.ee_arg = arg;
152205345Sbz    eg->ee.ee_priority = priority;
153205345Sbz
154205345Sbz    return (eventhandler_register_internal(list, name, &eg->ee));
155205345Sbz}
156205345Sbz
157205345Sbz#ifdef VIMAGE
158205345Sbzstruct eventhandler_entry_generic_vimage
159205345Sbz{
160205345Sbz    struct eventhandler_entry		ee;
161205345Sbz    vimage_iterator_func_t		func;		/* Vimage iterator function. */
162205345Sbz    struct eventhandler_entry_vimage	v_ee;		/* Original func, arg. */
163205345Sbz};
164205345Sbz
165205345Sbzeventhandler_tag
166205345Sbzvimage_eventhandler_register(struct eventhandler_list *list, const char *name,
167205345Sbz    void *func, void *arg, int priority, vimage_iterator_func_t iterfunc)
168205345Sbz{
169205345Sbz    struct eventhandler_entry_generic_vimage	*eg;
170205345Sbz
171205345Sbz    /* allocate an entry for this handler, populate it */
172205345Sbz    eg = malloc(sizeof(struct eventhandler_entry_generic_vimage),
173205345Sbz	M_EVENTHANDLER, M_WAITOK | M_ZERO);
174205345Sbz    eg->func = iterfunc;
175205345Sbz    eg->v_ee.func = func;
176205345Sbz    eg->v_ee.ee_arg = arg;
177205345Sbz    eg->ee.ee_arg = &eg->v_ee;
178205345Sbz    eg->ee.ee_priority = priority;
179205345Sbz
180205345Sbz    return (eventhandler_register_internal(list, name, &eg->ee));
181205345Sbz}
182205345Sbz#endif
183205345Sbz
184327626Sianstatic void
185327626Sian_eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag,
186327626Sian    bool wait)
18750107Smsmith{
18850107Smsmith    struct eventhandler_entry	*ep = tag;
18950107Smsmith
190112111Sjhb    EHL_LOCK_ASSERT(list, MA_OWNED);
19150107Smsmith    if (ep != NULL) {
19250107Smsmith	/* remove just this entry */
193112111Sjhb	if (list->el_runcount == 0) {
194112111Sjhb	    CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep,
195112111Sjhb		list->el_name);
196112111Sjhb	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
197112111Sjhb	    free(ep, M_EVENTHANDLER);
198112111Sjhb	} else {
199112111Sjhb	    CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__,
200112111Sjhb		ep, list->el_name);
201112111Sjhb	    ep->ee_priority = EHE_DEAD_PRIORITY;
202112111Sjhb	}
20350107Smsmith    } else {
20450107Smsmith	/* remove entire list */
205112111Sjhb	if (list->el_runcount == 0) {
206112111Sjhb	    CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__,
207112111Sjhb		list->el_name);
208112111Sjhb	    while (!TAILQ_EMPTY(&list->el_entries)) {
209112111Sjhb		ep = TAILQ_FIRST(&list->el_entries);
210112111Sjhb		TAILQ_REMOVE(&list->el_entries, ep, ee_link);
211112111Sjhb		free(ep, M_EVENTHANDLER);
212112111Sjhb	    }
213112111Sjhb	} else {
214112111Sjhb	    CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead",
215112111Sjhb		__func__, list->el_name);
216112111Sjhb	    TAILQ_FOREACH(ep, &list->el_entries, ee_link)
217112111Sjhb		ep->ee_priority = EHE_DEAD_PRIORITY;
21850107Smsmith	}
21950107Smsmith    }
220327626Sian    while (wait && list->el_runcount > 0)
221200652Sthompsa	    mtx_sleep(list, &list->el_lock, 0, "evhrm", 0);
222112111Sjhb    EHL_UNLOCK(list);
22350107Smsmith}
22450107Smsmith
225327626Sianvoid
226327626Sianeventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
227327626Sian{
228327626Sian
229327626Sian	_eventhandler_deregister(list, tag, true);
230327626Sian}
231327626Sian
232327626Sianvoid
233327626Sianeventhandler_deregister_nowait(struct eventhandler_list *list,
234327626Sian    eventhandler_tag tag)
235327626Sian{
236327626Sian
237327626Sian	_eventhandler_deregister(list, tag, false);
238327626Sian}
239327626Sian
240112111Sjhb/*
241112111Sjhb * Internal version for use when eventhandler list is already locked.
242112111Sjhb */
243112111Sjhbstatic struct eventhandler_list *
244138439Sjkoshy_eventhandler_find_list(const char *name)
245112111Sjhb{
246112111Sjhb    struct eventhandler_list	*list;
247112111Sjhb
248112111Sjhb    mtx_assert(&eventhandler_mutex, MA_OWNED);
249112111Sjhb    TAILQ_FOREACH(list, &eventhandler_lists, el_link) {
250112111Sjhb	if (!strcmp(name, list->el_name))
251112111Sjhb	    break;
252112111Sjhb    }
253112111Sjhb    return (list);
254112111Sjhb}
255112111Sjhb
256112111Sjhb/*
257112111Sjhb * Lookup a "slow" list by name.  Returns with the list locked.
258112111Sjhb */
25950107Smsmithstruct eventhandler_list *
260138439Sjkoshyeventhandler_find_list(const char *name)
26150107Smsmith{
26250107Smsmith    struct eventhandler_list	*list;
26380703Sjake
26480703Sjake    if (!eventhandler_lists_initted)
26580703Sjake	return(NULL);
26666205Smsmith
26750107Smsmith    /* scan looking for the requested list */
26872200Sbmilekic    mtx_lock(&eventhandler_mutex);
269112111Sjhb    list = _eventhandler_find_list(name);
270112111Sjhb    if (list != NULL)
271112111Sjhb	EHL_LOCK(list);
27272200Sbmilekic    mtx_unlock(&eventhandler_mutex);
27366205Smsmith
27450107Smsmith    return(list);
27550107Smsmith}
27666205Smsmith
277112111Sjhb/*
278112111Sjhb * Prune "dead" entries from an eventhandler list.
279112111Sjhb */
280112111Sjhbvoid
281112111Sjhbeventhandler_prune_list(struct eventhandler_list *list)
282112111Sjhb{
283112111Sjhb    struct eventhandler_entry *ep, *en;
284200652Sthompsa    int pruned = 0;
285112111Sjhb
286112111Sjhb    CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name);
287112111Sjhb    EHL_LOCK_ASSERT(list, MA_OWNED);
288200652Sthompsa    TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
289112111Sjhb	if (ep->ee_priority == EHE_DEAD_PRIORITY) {
290112111Sjhb	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
291112111Sjhb	    free(ep, M_EVENTHANDLER);
292200652Sthompsa	    pruned++;
293112111Sjhb	}
294112111Sjhb    }
295200652Sthompsa    if (pruned > 0)
296200652Sthompsa	    wakeup(list);
297112111Sjhb}
298331727Smjoras
299331727Smjoras/*
300331727Smjoras * Create (or get the existing) list so the pointer can be stored by
301331727Smjoras * EVENTHANDLER_LIST_DEFINE.
302331727Smjoras */
303331727Smjorasstruct eventhandler_list *
304331727Smjoraseventhandler_create_list(const char *name)
305331727Smjoras{
306331727Smjoras	struct eventhandler_list *list;
307331727Smjoras
308331727Smjoras	KASSERT(eventhandler_lists_initted,
309331727Smjoras	    ("eventhandler list created too early"));
310331727Smjoras
311331727Smjoras	mtx_lock(&eventhandler_mutex);
312331727Smjoras	list = eventhandler_find_or_create_list(name);
313331727Smjoras	mtx_unlock(&eventhandler_mutex);
314331727Smjoras
315331727Smjoras	return (list);
316331727Smjoras}
317