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$");
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
4150107Smsmith/* List of 'slow' 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
6750107Smsmith/*
6850107Smsmith * Insertion is O(n) due to the priority scan, but optimises to O(1)
6950107Smsmith * if all priorities are identical.
7050107Smsmith */
71205345Sbzstatic eventhandler_tag
72205345Sbzeventhandler_register_internal(struct eventhandler_list *list,
73205345Sbz    const char *name, eventhandler_tag epn)
7450107Smsmith{
75112111Sjhb    struct eventhandler_list		*new_list;
7650107Smsmith    struct eventhandler_entry		*ep;
7750107Smsmith
7869893Sjhb    KASSERT(eventhandler_lists_initted, ("eventhandler registered too early"));
79205345Sbz    KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__));
8050107Smsmith
8166205Smsmith    /* lock the eventhandler lists */
8272200Sbmilekic    mtx_lock(&eventhandler_mutex);
8366205Smsmith
8450107Smsmith    /* Do we need to find/create the (slow) list? */
8550107Smsmith    if (list == NULL) {
8650107Smsmith	/* look for a matching, existing list */
87112111Sjhb	list = _eventhandler_find_list(name);
8850107Smsmith
8950107Smsmith	/* Do we need to create the list? */
9050107Smsmith	if (list == NULL) {
91112111Sjhb	    mtx_unlock(&eventhandler_mutex);
92112111Sjhb
93112111Sjhb	    new_list = malloc(sizeof(struct eventhandler_list) +
94112111Sjhb		strlen(name) + 1, M_EVENTHANDLER, M_WAITOK);
95112111Sjhb
96112111Sjhb	    /* If someone else created it already, then use that one. */
97112111Sjhb	    mtx_lock(&eventhandler_mutex);
98112111Sjhb	    list = _eventhandler_find_list(name);
99112111Sjhb	    if (list != NULL) {
100112111Sjhb		free(new_list, M_EVENTHANDLER);
101112111Sjhb	    } else {
102112111Sjhb		CTR2(KTR_EVH, "%s: creating list \"%s\"", __func__, name);
103112111Sjhb		list = new_list;
104112111Sjhb		list->el_flags = 0;
105112111Sjhb		list->el_runcount = 0;
106112111Sjhb		bzero(&list->el_lock, sizeof(list->el_lock));
107112111Sjhb		list->el_name = (char *)list + sizeof(struct eventhandler_list);
108112111Sjhb		strcpy(list->el_name, name);
109112111Sjhb		TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link);
11066205Smsmith	    }
11150107Smsmith	}
11250107Smsmith    }
113112111Sjhb    if (!(list->el_flags & EHL_INITTED)) {
11450107Smsmith	TAILQ_INIT(&list->el_entries);
115112111Sjhb	mtx_init(&list->el_lock, name, "eventhandler list", MTX_DEF);
116112111Sjhb	atomic_store_rel_int(&list->el_flags, EHL_INITTED);
11750107Smsmith    }
11893743Salfred    mtx_unlock(&eventhandler_mutex);
119112111Sjhb
120205345Sbz    KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
121112111Sjhb	("%s: handler for %s registered with dead priority", __func__, name));
122112111Sjhb
12350107Smsmith    /* sort it into the list */
124205345Sbz    CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn,
125205345Sbz	((struct eventhandler_entry_generic *)epn)->func, name);
126112111Sjhb    EHL_LOCK(list);
127112111Sjhb    TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
128112111Sjhb	if (ep->ee_priority != EHE_DEAD_PRIORITY &&
129205345Sbz	    epn->ee_priority < ep->ee_priority) {
130205345Sbz	    TAILQ_INSERT_BEFORE(ep, epn, ee_link);
13150107Smsmith	    break;
13250107Smsmith	}
13350107Smsmith    }
13450107Smsmith    if (ep == NULL)
135205345Sbz	TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
136112111Sjhb    EHL_UNLOCK(list);
137205345Sbz    return(epn);
13850107Smsmith}
13950107Smsmith
140205345Sbzeventhandler_tag
141205345Sbzeventhandler_register(struct eventhandler_list *list, const char *name,
142205345Sbz		      void *func, void *arg, int priority)
143205345Sbz{
144205345Sbz    struct eventhandler_entry_generic	*eg;
145205345Sbz
146205345Sbz    /* allocate an entry for this handler, populate it */
147205345Sbz    eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER,
148205345Sbz	M_WAITOK | M_ZERO);
149205345Sbz    eg->func = func;
150205345Sbz    eg->ee.ee_arg = arg;
151205345Sbz    eg->ee.ee_priority = priority;
152205345Sbz
153205345Sbz    return (eventhandler_register_internal(list, name, &eg->ee));
154205345Sbz}
155205345Sbz
156205345Sbz#ifdef VIMAGE
157205345Sbzstruct eventhandler_entry_generic_vimage
158205345Sbz{
159205345Sbz    struct eventhandler_entry		ee;
160205345Sbz    vimage_iterator_func_t		func;		/* Vimage iterator function. */
161205345Sbz    struct eventhandler_entry_vimage	v_ee;		/* Original func, arg. */
162205345Sbz};
163205345Sbz
164205345Sbzeventhandler_tag
165205345Sbzvimage_eventhandler_register(struct eventhandler_list *list, const char *name,
166205345Sbz    void *func, void *arg, int priority, vimage_iterator_func_t iterfunc)
167205345Sbz{
168205345Sbz    struct eventhandler_entry_generic_vimage	*eg;
169205345Sbz
170205345Sbz    /* allocate an entry for this handler, populate it */
171205345Sbz    eg = malloc(sizeof(struct eventhandler_entry_generic_vimage),
172205345Sbz	M_EVENTHANDLER, M_WAITOK | M_ZERO);
173205345Sbz    eg->func = iterfunc;
174205345Sbz    eg->v_ee.func = func;
175205345Sbz    eg->v_ee.ee_arg = arg;
176205345Sbz    eg->ee.ee_arg = &eg->v_ee;
177205345Sbz    eg->ee.ee_priority = priority;
178205345Sbz
179205345Sbz    return (eventhandler_register_internal(list, name, &eg->ee));
180205345Sbz}
181205345Sbz#endif
182205345Sbz
18350107Smsmithvoid
18450107Smsmitheventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
18550107Smsmith{
18650107Smsmith    struct eventhandler_entry	*ep = tag;
18750107Smsmith
188112111Sjhb    EHL_LOCK_ASSERT(list, MA_OWNED);
18950107Smsmith    if (ep != NULL) {
19050107Smsmith	/* remove just this entry */
191112111Sjhb	if (list->el_runcount == 0) {
192112111Sjhb	    CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep,
193112111Sjhb		list->el_name);
194112111Sjhb	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
195112111Sjhb	    free(ep, M_EVENTHANDLER);
196112111Sjhb	} else {
197112111Sjhb	    CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__,
198112111Sjhb		ep, list->el_name);
199112111Sjhb	    ep->ee_priority = EHE_DEAD_PRIORITY;
200112111Sjhb	}
20150107Smsmith    } else {
20250107Smsmith	/* remove entire list */
203112111Sjhb	if (list->el_runcount == 0) {
204112111Sjhb	    CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__,
205112111Sjhb		list->el_name);
206112111Sjhb	    while (!TAILQ_EMPTY(&list->el_entries)) {
207112111Sjhb		ep = TAILQ_FIRST(&list->el_entries);
208112111Sjhb		TAILQ_REMOVE(&list->el_entries, ep, ee_link);
209112111Sjhb		free(ep, M_EVENTHANDLER);
210112111Sjhb	    }
211112111Sjhb	} else {
212112111Sjhb	    CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead",
213112111Sjhb		__func__, list->el_name);
214112111Sjhb	    TAILQ_FOREACH(ep, &list->el_entries, ee_link)
215112111Sjhb		ep->ee_priority = EHE_DEAD_PRIORITY;
21650107Smsmith	}
21750107Smsmith    }
218200652Sthompsa    while (list->el_runcount > 0)
219200652Sthompsa	    mtx_sleep(list, &list->el_lock, 0, "evhrm", 0);
220112111Sjhb    EHL_UNLOCK(list);
22150107Smsmith}
22250107Smsmith
223112111Sjhb/*
224112111Sjhb * Internal version for use when eventhandler list is already locked.
225112111Sjhb */
226112111Sjhbstatic struct eventhandler_list *
227138439Sjkoshy_eventhandler_find_list(const char *name)
228112111Sjhb{
229112111Sjhb    struct eventhandler_list	*list;
230112111Sjhb
231112111Sjhb    mtx_assert(&eventhandler_mutex, MA_OWNED);
232112111Sjhb    TAILQ_FOREACH(list, &eventhandler_lists, el_link) {
233112111Sjhb	if (!strcmp(name, list->el_name))
234112111Sjhb	    break;
235112111Sjhb    }
236112111Sjhb    return (list);
237112111Sjhb}
238112111Sjhb
239112111Sjhb/*
240112111Sjhb * Lookup a "slow" list by name.  Returns with the list locked.
241112111Sjhb */
24250107Smsmithstruct eventhandler_list *
243138439Sjkoshyeventhandler_find_list(const char *name)
24450107Smsmith{
24550107Smsmith    struct eventhandler_list	*list;
24680703Sjake
24780703Sjake    if (!eventhandler_lists_initted)
24880703Sjake	return(NULL);
24966205Smsmith
25050107Smsmith    /* scan looking for the requested list */
25172200Sbmilekic    mtx_lock(&eventhandler_mutex);
252112111Sjhb    list = _eventhandler_find_list(name);
253112111Sjhb    if (list != NULL)
254112111Sjhb	EHL_LOCK(list);
25572200Sbmilekic    mtx_unlock(&eventhandler_mutex);
25666205Smsmith
25750107Smsmith    return(list);
25850107Smsmith}
25966205Smsmith
260112111Sjhb/*
261112111Sjhb * Prune "dead" entries from an eventhandler list.
262112111Sjhb */
263112111Sjhbvoid
264112111Sjhbeventhandler_prune_list(struct eventhandler_list *list)
265112111Sjhb{
266112111Sjhb    struct eventhandler_entry *ep, *en;
267200652Sthompsa    int pruned = 0;
268112111Sjhb
269112111Sjhb    CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name);
270112111Sjhb    EHL_LOCK_ASSERT(list, MA_OWNED);
271200652Sthompsa    TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
272112111Sjhb	if (ep->ee_priority == EHE_DEAD_PRIORITY) {
273112111Sjhb	    TAILQ_REMOVE(&list->el_entries, ep, ee_link);
274112111Sjhb	    free(ep, M_EVENTHANDLER);
275200652Sthompsa	    pruned++;
276112111Sjhb	}
277112111Sjhb    }
278200652Sthompsa    if (pruned > 0)
279200652Sthompsa	    wakeup(list);
280112111Sjhb}
281