1 /*- 2 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/kernel.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 39 static MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records"); 40 41 /* List of all eventhandler lists */ 42 static TAILQ_HEAD(, eventhandler_list) eventhandler_lists; 43 static int eventhandler_lists_initted = 0; 44 static struct mtx eventhandler_mutex; 45 46 struct eventhandler_entry_generic 47 { 48 struct eventhandler_entry ee; 49 void (* func)(void); 50 }; 51 52 static struct eventhandler_list *_eventhandler_find_list(const char *name); 53 54 /* 55 * Initialize the eventhandler mutex and list. 56 */ 57 static void 58 eventhandler_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 } 64 SYSINIT(eventhandlers, SI_SUB_EVENTHANDLER, SI_ORDER_FIRST, eventhandler_init, 65 NULL); 66 67 static struct eventhandler_list * 68 eventhandler_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 mtx_init(&list->el_lock, name, "eventhandler list", MTX_DEF); 92 list->el_name = (char *)(list + 1); 93 strcpy(list->el_name, name); 94 TAILQ_INSERT_HEAD(&eventhandler_lists, list, el_link); 95 } 96 } 97 return (list); 98 } 99 100 /* 101 * Insertion is O(n) due to the priority scan, but optimises to O(1) 102 * if all priorities are identical. 103 */ 104 static eventhandler_tag 105 eventhandler_register_internal(struct eventhandler_list *list, 106 const char *name, eventhandler_tag epn) 107 { 108 struct eventhandler_entry *ep; 109 110 KASSERT(eventhandler_lists_initted, ("eventhandler registered too early")); 111 KASSERT(epn != NULL, ("%s: cannot register NULL event", __func__)); 112 113 /* Do we need to find/create the list? */ 114 if (list == NULL) { 115 mtx_lock(&eventhandler_mutex); 116 list = eventhandler_find_or_create_list(name); 117 mtx_unlock(&eventhandler_mutex); 118 } 119 120 KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY, 121 ("%s: handler for %s registered with dead priority", __func__, name)); 122 123 /* sort it into the list */ 124 CTR4(KTR_EVH, "%s: adding item %p (function %p) to \"%s\"", __func__, epn, 125 ((struct eventhandler_entry_generic *)epn)->func, name); 126 EHL_LOCK(list); 127 TAILQ_FOREACH(ep, &list->el_entries, ee_link) { 128 if (ep->ee_priority != EHE_DEAD_PRIORITY && 129 epn->ee_priority < ep->ee_priority) { 130 TAILQ_INSERT_BEFORE(ep, epn, ee_link); 131 break; 132 } 133 } 134 if (ep == NULL) 135 TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link); 136 EHL_UNLOCK(list); 137 return(epn); 138 } 139 140 eventhandler_tag 141 eventhandler_register(struct eventhandler_list *list, const char *name, 142 void *func, void *arg, int priority) 143 { 144 struct eventhandler_entry_generic *eg; 145 146 /* allocate an entry for this handler, populate it */ 147 eg = malloc(sizeof(struct eventhandler_entry_generic), M_EVENTHANDLER, 148 M_WAITOK | M_ZERO); 149 eg->func = func; 150 eg->ee.ee_arg = arg; 151 eg->ee.ee_priority = priority; 152 153 return (eventhandler_register_internal(list, name, &eg->ee)); 154 } 155 156 #ifdef VIMAGE 157 struct eventhandler_entry_generic_vimage 158 { 159 struct eventhandler_entry ee; 160 vimage_iterator_func_t func; /* Vimage iterator function. */ 161 struct eventhandler_entry_vimage v_ee; /* Original func, arg. */ 162 }; 163 164 eventhandler_tag 165 vimage_eventhandler_register(struct eventhandler_list *list, const char *name, 166 void *func, void *arg, int priority, vimage_iterator_func_t iterfunc) 167 { 168 struct eventhandler_entry_generic_vimage *eg; 169 170 /* allocate an entry for this handler, populate it */ 171 eg = malloc(sizeof(struct eventhandler_entry_generic_vimage), 172 M_EVENTHANDLER, M_WAITOK | M_ZERO); 173 eg->func = iterfunc; 174 eg->v_ee.func = func; 175 eg->v_ee.ee_arg = arg; 176 eg->ee.ee_arg = &eg->v_ee; 177 eg->ee.ee_priority = priority; 178 179 return (eventhandler_register_internal(list, name, &eg->ee)); 180 } 181 #endif 182 183 static void 184 _eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag, 185 bool wait) 186 { 187 struct eventhandler_entry *ep = tag; 188 189 EHL_LOCK_ASSERT(list, MA_OWNED); 190 if (ep != NULL) { 191 /* remove just this entry */ 192 if (list->el_runcount == 0) { 193 CTR3(KTR_EVH, "%s: removing item %p from \"%s\"", __func__, ep, 194 list->el_name); 195 TAILQ_REMOVE(&list->el_entries, ep, ee_link); 196 free(ep, M_EVENTHANDLER); 197 } else { 198 CTR3(KTR_EVH, "%s: marking item %p from \"%s\" as dead", __func__, 199 ep, list->el_name); 200 ep->ee_priority = EHE_DEAD_PRIORITY; 201 } 202 } else { 203 /* remove entire list */ 204 if (list->el_runcount == 0) { 205 CTR2(KTR_EVH, "%s: removing all items from \"%s\"", __func__, 206 list->el_name); 207 while (!TAILQ_EMPTY(&list->el_entries)) { 208 ep = TAILQ_FIRST(&list->el_entries); 209 TAILQ_REMOVE(&list->el_entries, ep, ee_link); 210 free(ep, M_EVENTHANDLER); 211 } 212 } else { 213 CTR2(KTR_EVH, "%s: marking all items from \"%s\" as dead", 214 __func__, list->el_name); 215 TAILQ_FOREACH(ep, &list->el_entries, ee_link) 216 ep->ee_priority = EHE_DEAD_PRIORITY; 217 } 218 } 219 while (wait && list->el_runcount > 0) 220 mtx_sleep(list, &list->el_lock, 0, "evhrm", 0); 221 EHL_UNLOCK(list); 222 } 223 224 void 225 eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag) 226 { 227 228 _eventhandler_deregister(list, tag, true); 229 } 230 231 void 232 eventhandler_deregister_nowait(struct eventhandler_list *list, 233 eventhandler_tag tag) 234 { 235 236 _eventhandler_deregister(list, tag, false); 237 } 238 239 /* 240 * Internal version for use when eventhandler list is already locked. 241 */ 242 static struct eventhandler_list * 243 _eventhandler_find_list(const char *name) 244 { 245 struct eventhandler_list *list; 246 247 mtx_assert(&eventhandler_mutex, MA_OWNED); 248 TAILQ_FOREACH(list, &eventhandler_lists, el_link) { 249 if (!strcmp(name, list->el_name)) 250 break; 251 } 252 return (list); 253 } 254 255 /* 256 * Lookup a "slow" list by name. Returns with the list locked. 257 */ 258 struct eventhandler_list * 259 eventhandler_find_list(const char *name) 260 { 261 struct eventhandler_list *list; 262 263 if (!eventhandler_lists_initted) 264 return(NULL); 265 266 /* scan looking for the requested list */ 267 mtx_lock(&eventhandler_mutex); 268 list = _eventhandler_find_list(name); 269 if (list != NULL) 270 EHL_LOCK(list); 271 mtx_unlock(&eventhandler_mutex); 272 273 return(list); 274 } 275 276 /* 277 * Prune "dead" entries from an eventhandler list. 278 */ 279 void 280 eventhandler_prune_list(struct eventhandler_list *list) 281 { 282 struct eventhandler_entry *ep, *en; 283 int pruned = 0; 284 285 CTR2(KTR_EVH, "%s: pruning list \"%s\"", __func__, list->el_name); 286 EHL_LOCK_ASSERT(list, MA_OWNED); 287 TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) { 288 if (ep->ee_priority == EHE_DEAD_PRIORITY) { 289 TAILQ_REMOVE(&list->el_entries, ep, ee_link); 290 free(ep, M_EVENTHANDLER); 291 pruned++; 292 } 293 } 294 if (pruned > 0) 295 wakeup(list); 296 } 297 298 /* 299 * Create (or get the existing) list so the pointer can be stored by 300 * EVENTHANDLER_LIST_DEFINE. 301 */ 302 struct eventhandler_list * 303 eventhandler_create_list(const char *name) 304 { 305 struct eventhandler_list *list; 306 307 KASSERT(eventhandler_lists_initted, 308 ("eventhandler list created too early")); 309 310 mtx_lock(&eventhandler_mutex); 311 list = eventhandler_find_or_create_list(name); 312 mtx_unlock(&eventhandler_mutex); 313 314 return (list); 315 } 316