]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/subr_eventhandler.c
xnu-4570.1.46.tar.gz
[apple/xnu.git] / bsd / kern / subr_eventhandler.c
CommitLineData
5ba3f43e
A
1/*
2 * Copyright (c) 2016-2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*-
29 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 */
53
54#include <sys/cdefs.h>
55#include <sys/param.h>
56#include <sys/kernel.h>
57#include <kern/queue.h>
58#include <kern/locks.h>
59#include <sys/malloc.h>
60#include <sys/proc.h>
61#include <sys/systm.h>
62#include <sys/mcache.h>
63#include <sys/eventhandler.h>
64#include <sys/sysctl.h>
65
66int evh_debug = 0;
67
68MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler", "Event handler records");
69
70SYSCTL_NODE(_kern, OID_AUTO, eventhandler, CTLFLAG_RW | CTLFLAG_LOCKED,
71 0, "Eventhandler");
72SYSCTL_INT(_kern_eventhandler, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
73 &evh_debug, 0, "Eventhandler debug mode");
74
75struct eventhandler_entry_arg eventhandler_entry_dummy_arg = {{0}};
76
77/* List of 'slow' lists */
78static struct eventhandler_lists_ctxt evthdlr_lists_ctxt_glb;
79static lck_grp_attr_t *eventhandler_mutex_grp_attr;
80static lck_grp_t *eventhandler_mutex_grp;
81static lck_attr_t *eventhandler_mutex_attr;
82
83static lck_grp_attr_t *el_lock_grp_attr;
84lck_grp_t *el_lock_grp;
85lck_attr_t *el_lock_attr;
86
87struct eventhandler_entry_generic
88{
89 struct eventhandler_entry ee;
90 void (* func)(void);
91};
92
93static struct eventhandler_list *_eventhandler_find_list(
94 struct eventhandler_lists_ctxt *evthdlr_lists_ctxt, const char *name);
95
96void
97eventhandler_lists_ctxt_init(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
98{
99 VERIFY(evthdlr_lists_ctxt != NULL);
100
101 TAILQ_INIT(&evthdlr_lists_ctxt->eventhandler_lists);
102 evthdlr_lists_ctxt->eventhandler_lists_initted = 1;
103 lck_mtx_init(&evthdlr_lists_ctxt->eventhandler_mutex,
104 eventhandler_mutex_grp, eventhandler_mutex_attr);
105}
106
107/*
108 * Initialize the eventhandler mutex and list.
109 */
110void
111eventhandler_init(void)
112{
113 eventhandler_mutex_grp_attr = lck_grp_attr_alloc_init();
114 eventhandler_mutex_grp = lck_grp_alloc_init("eventhandler",
115 eventhandler_mutex_grp_attr);
116 eventhandler_mutex_attr = lck_attr_alloc_init();
117
118 el_lock_grp_attr = lck_grp_attr_alloc_init();
119 el_lock_grp = lck_grp_alloc_init("eventhandler list",
120 el_lock_grp_attr);
121 el_lock_attr = lck_attr_alloc_init();
122
123 eventhandler_lists_ctxt_init(&evthdlr_lists_ctxt_glb);
124}
125
126/*
127 * Insertion is O(n) due to the priority scan, but optimises to O(1)
128 * if all priorities are identical.
129 */
130static eventhandler_tag
131eventhandler_register_internal(
132 struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
133 struct eventhandler_list *list,
134 const char *name, eventhandler_tag epn)
135{
136 struct eventhandler_list *new_list;
137 struct eventhandler_entry *ep;
138
139 if (evthdlr_lists_ctxt == NULL)
140 evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
141
142 VERIFY(evthdlr_lists_ctxt->eventhandler_lists_initted); /* eventhandler registered too early */
143 VERIFY(epn != NULL); /* cannot register NULL event */
144
145 /* lock the eventhandler lists */
146 lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
147
148 /* Do we need to find/create the (slow) list? */
149 if (list == NULL) {
150 /* look for a matching, existing list */
151 list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
152
153 /* Do we need to create the list? */
154 if (list == NULL) {
155 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
156
157 MALLOC(new_list, struct eventhandler_list *,
158 sizeof(struct eventhandler_list) + strlen(name) + 1,
159 M_EVENTHANDLER, M_WAITOK);
160
161 /* If someone else created it already, then use that one. */
162 lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
163 list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
164 if (list != NULL) {
165 FREE(new_list, M_EVENTHANDLER);
166 } else {
167 evhlog((LOG_DEBUG, "%s: creating list \"%s\"", __func__, name));
168 list = new_list;
169 list->el_flags = 0;
170 list->el_runcount = 0;
171 bzero(&list->el_lock, sizeof(list->el_lock));
172 list->el_name = (char *)list + sizeof(struct eventhandler_list);
173 strlcpy(list->el_name, name, strlen(name) + 1);
174 TAILQ_INSERT_HEAD(&evthdlr_lists_ctxt->eventhandler_lists, list, el_link);
175 }
176 }
177 }
178 if (!(list->el_flags & EHL_INITTED)) {
179 TAILQ_INIT(&list->el_entries);
180 EHL_LOCK_INIT(list);
181 list->el_flags |= EHL_INITTED;
182 }
183 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
184
185 KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
186 ("%s: handler for %s registered with dead priority", __func__, name));
187
188 /* sort it into the list */
189 evhlog((LOG_DEBUG, "%s: adding item %p (function %p to \"%s\"", __func__, VM_KERNEL_ADDRPERM(epn),
190 VM_KERNEL_UNSLIDE(((struct eventhandler_entry_generic *)epn)->func), name));
191 EHL_LOCK(list);
192 TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
193 if (ep->ee_priority != EHE_DEAD_PRIORITY &&
194 epn->ee_priority < ep->ee_priority) {
195 TAILQ_INSERT_BEFORE(ep, epn, ee_link);
196 break;
197 }
198 }
199 if (ep == NULL)
200 TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
201 EHL_UNLOCK(list);
202 return(epn);
203}
204
205eventhandler_tag
206eventhandler_register(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
207 struct eventhandler_list *list, const char *name,
208 void *func, struct eventhandler_entry_arg arg, int priority)
209{
210 struct eventhandler_entry_generic *eg;
211
212 /* allocate an entry for this handler, populate it */
213 MALLOC(eg, struct eventhandler_entry_generic *,
214 sizeof(struct eventhandler_entry_generic),
215 M_EVENTHANDLER, M_WAITOK | M_ZERO);
216
217 eg->func = func;
218 eg->ee.ee_arg = arg;
219 eg->ee.ee_priority = priority;
220
221 return (eventhandler_register_internal(evthdlr_lists_ctxt, list, name, &eg->ee));
222}
223
224void
225eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
226{
227 struct eventhandler_entry *ep = tag;
228
229 EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
230 if (ep != NULL) {
231 /* remove just this entry */
232 if (list->el_runcount == 0) {
233 evhlog((LOG_DEBUG, "%s: removing item %p from \"%s\"", __func__, VM_KERNEL_ADDRPERM(ep),
234 list->el_name));
235 /*
236 * We may have purged the list because of certain events.
237 * Make sure that is not the case when a specific entry
238 * is being removed.
239 */
240 if (!TAILQ_EMPTY(&list->el_entries))
241 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
242 FREE(ep, M_EVENTHANDLER);
243 } else {
244 evhlog((LOG_DEBUG, "%s: marking item %p from \"%s\" as dead", __func__,
245 VM_KERNEL_ADDRPERM(ep), list->el_name));
246 ep->ee_priority = EHE_DEAD_PRIORITY;
247 }
248 } else {
249 /* remove entire list */
250 if (list->el_runcount == 0) {
251 evhlog((LOG_DEBUG, "%s: removing all items from \"%s\"", __func__,
252 list->el_name));
253 while (!TAILQ_EMPTY(&list->el_entries)) {
254 ep = TAILQ_FIRST(&list->el_entries);
255 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
256 FREE(ep, M_EVENTHANDLER);
257 }
258 } else {
259 evhlog((LOG_DEBUG, "%s: marking all items from \"%s\" as dead",
260 __func__, list->el_name));
261 TAILQ_FOREACH(ep, &list->el_entries, ee_link)
262 ep->ee_priority = EHE_DEAD_PRIORITY;
263 }
264 }
265 while (list->el_runcount > 0)
266 msleep((caddr_t)list, &list->el_lock, 0, "evhrm", 0);
267 EHL_UNLOCK(list);
268}
269
270/*
271 * Internal version for use when eventhandler list is already locked.
272 */
273static struct eventhandler_list *
274_eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
275 const char *name)
276{
277 struct eventhandler_list *list;
278
279 VERIFY(evthdlr_lists_ctxt != NULL);
280
281 LCK_MTX_ASSERT(&evthdlr_lists_ctxt->eventhandler_mutex, LCK_MTX_ASSERT_OWNED);
282 TAILQ_FOREACH(list, &evthdlr_lists_ctxt->eventhandler_lists, el_link) {
283 if (!strcmp(name, list->el_name))
284 break;
285 }
286 return (list);
287}
288
289/*
290 * Lookup a "slow" list by name. Returns with the list locked.
291 */
292struct eventhandler_list *
293eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
294 const char *name)
295{
296 struct eventhandler_list *list;
297
298 if (evthdlr_lists_ctxt == NULL)
299 evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
300
301 if (!evthdlr_lists_ctxt->eventhandler_lists_initted)
302 return(NULL);
303
304 /* scan looking for the requested list */
305 lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
306 list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
307 if (list != NULL)
308 EHL_LOCK(list);
309 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
310
311 return(list);
312}
313
314/*
315 * Prune "dead" entries from an eventhandler list.
316 */
317void
318eventhandler_prune_list(struct eventhandler_list *list)
319{
320 struct eventhandler_entry *ep, *en;
321 int pruned = 0;
322
323 evhlog((LOG_DEBUG, "%s: pruning list \"%s\"", __func__, list->el_name));
324 EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
325 TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
326 if (ep->ee_priority == EHE_DEAD_PRIORITY) {
327 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
328 FREE(ep, M_EVENTHANDLER);
329 pruned++;
330 }
331 }
332 if (pruned > 0)
333 wakeup(list);
334}
335
336/*
337 * This should be called when last reference to an object
338 * is being released.
339 * The individual event type lists must be purged when the object
340 * becomes defunct.
341 */
342void
343eventhandler_lists_ctxt_destroy(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
344{
345 struct eventhandler_list *list = NULL;
346 struct eventhandler_list *list_next = NULL;
347
348 lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
349 TAILQ_FOREACH_SAFE(list, &evthdlr_lists_ctxt->eventhandler_lists,
350 el_link, list_next) {
351 VERIFY(TAILQ_EMPTY(&list->el_entries));
352 EHL_LOCK_DESTROY(list);
353 FREE(list, M_EVENTHANDLER);
354 }
355 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
356 lck_mtx_destroy(&evthdlr_lists_ctxt->eventhandler_mutex,
357 eventhandler_mutex_grp);
358 return;
359}