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