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