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 }, { 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 unsigned int eg_size; /* size of eventhandler_entry_generic */
84static struct mcache *eg_cache; /* mcache for eventhandler_entry_generic */
85
86static unsigned int el_size; /* size of eventhandler_list */
87static struct mcache *el_cache; /* mcache for eventhandler_list */
88
89static lck_grp_attr_t *el_lock_grp_attr;
90lck_grp_t *el_lock_grp;
91lck_attr_t *el_lock_attr;
92
93struct eventhandler_entry_generic
94{
95 struct eventhandler_entry ee;
96 void (* func)(void);
97};
98
99static struct eventhandler_list *_eventhandler_find_list(
100 struct eventhandler_lists_ctxt *evthdlr_lists_ctxt, const char *name);
101
102void
103eventhandler_lists_ctxt_init(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
104{
105 VERIFY(evthdlr_lists_ctxt != NULL);
106
107 TAILQ_INIT(&evthdlr_lists_ctxt->eventhandler_lists);
108 evthdlr_lists_ctxt->eventhandler_lists_initted = 1;
109 lck_mtx_init(&evthdlr_lists_ctxt->eventhandler_mutex,
110 eventhandler_mutex_grp, eventhandler_mutex_attr);
111}
112
113/*
114 * Initialize the eventhandler mutex and list.
115 */
116void
117eventhandler_init(void)
118{
119 eventhandler_mutex_grp_attr = lck_grp_attr_alloc_init();
120 eventhandler_mutex_grp = lck_grp_alloc_init("eventhandler",
121 eventhandler_mutex_grp_attr);
122 eventhandler_mutex_attr = lck_attr_alloc_init();
123
124 el_lock_grp_attr = lck_grp_attr_alloc_init();
125 el_lock_grp = lck_grp_alloc_init("eventhandler list",
126 el_lock_grp_attr);
127 el_lock_attr = lck_attr_alloc_init();
128
129 eventhandler_lists_ctxt_init(&evthdlr_lists_ctxt_glb);
130
131 eg_size = sizeof (struct eventhandler_entry_generic);
132 eg_cache = mcache_create("eventhdlr_generic", eg_size,
133 sizeof (uint64_t), 0, MCR_SLEEP);
134
135 el_size = sizeof (struct eventhandler_list);
136 el_cache = mcache_create("eventhdlr_list", el_size,
137 sizeof (uint64_t), 0, MCR_SLEEP);
138}
139
140void
141eventhandler_reap_caches(boolean_t purge)
142{
143 mcache_reap_now(eg_cache, purge);
144 mcache_reap_now(el_cache, purge);
145}
146
147/*
148 * Insertion is O(n) due to the priority scan, but optimises to O(1)
149 * if all priorities are identical.
150 */
151static eventhandler_tag
152eventhandler_register_internal(
153 struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
154 struct eventhandler_list *list,
155 const char *name, eventhandler_tag epn)
156{
157 struct eventhandler_list *new_list;
158 struct eventhandler_entry *ep;
159
160 VERIFY(strlen(name) <= (sizeof (new_list->el_name) - 1));
161
162 if (evthdlr_lists_ctxt == NULL)
163 evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
164
165 VERIFY(evthdlr_lists_ctxt->eventhandler_lists_initted); /* eventhandler registered too early */
166 VERIFY(epn != NULL); /* cannot register NULL event */
167
168 /* lock the eventhandler lists */
169 lck_mtx_lock_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
170
171 /* Do we need to find/create the (slow) list? */
172 if (list == NULL) {
173 /* look for a matching, existing list */
174 list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
175
176 /* Do we need to create the list? */
177 if (list == NULL) {
178 lck_mtx_convert_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
179 new_list = mcache_alloc(el_cache, MCR_SLEEP);
180 bzero(new_list, el_size);
181 evhlog((LOG_DEBUG, "%s: creating list \"%s\"", __func__, name));
182 list = new_list;
183 list->el_flags = 0;
184 list->el_runcount = 0;
185 bzero(&list->el_lock, sizeof(list->el_lock));
186 (void) snprintf(list->el_name, sizeof (list->el_name), "%s", name);
187 TAILQ_INSERT_HEAD(&evthdlr_lists_ctxt->eventhandler_lists, list, el_link);
188 }
189 }
190 if (!(list->el_flags & EHL_INITTED)) {
191 TAILQ_INIT(&list->el_entries);
192 EHL_LOCK_INIT(list);
193 list->el_flags |= EHL_INITTED;
194 }
195 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
196
197 KASSERT(epn->ee_priority != EHE_DEAD_PRIORITY,
198 ("%s: handler for %s registered with dead priority", __func__, name));
199
200 /* sort it into the list */
201 evhlog((LOG_DEBUG, "%s: adding item %p (function %p to \"%s\"", __func__, VM_KERNEL_ADDRPERM(epn),
202 VM_KERNEL_UNSLIDE(((struct eventhandler_entry_generic *)epn)->func), name));
203 EHL_LOCK(list);
204 TAILQ_FOREACH(ep, &list->el_entries, ee_link) {
205 if (ep->ee_priority != EHE_DEAD_PRIORITY &&
206 epn->ee_priority < ep->ee_priority) {
207 TAILQ_INSERT_BEFORE(ep, epn, ee_link);
208 break;
209 }
210 }
211 if (ep == NULL)
212 TAILQ_INSERT_TAIL(&list->el_entries, epn, ee_link);
213 EHL_UNLOCK(list);
214 return(epn);
215}
216
217eventhandler_tag
218eventhandler_register(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
219 struct eventhandler_list *list, const char *name,
220 void *func, struct eventhandler_entry_arg arg, int priority)
221{
222 struct eventhandler_entry_generic *eg;
223
224 /* allocate an entry for this handler, populate it */
225 eg = mcache_alloc(eg_cache, MCR_SLEEP);
226 bzero(eg, eg_size);
227 eg->func = func;
228 eg->ee.ee_arg = arg;
229 eg->ee.ee_priority = priority;
230
231 return (eventhandler_register_internal(evthdlr_lists_ctxt, list, name, &eg->ee));
232}
233
234void
235eventhandler_deregister(struct eventhandler_list *list, eventhandler_tag tag)
236{
237 struct eventhandler_entry *ep = tag;
238
239 EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
240 if (ep != NULL) {
241 /* remove just this entry */
242 if (list->el_runcount == 0) {
243 evhlog((LOG_DEBUG, "%s: removing item %p from \"%s\"", __func__, VM_KERNEL_ADDRPERM(ep),
244 list->el_name));
245 /*
246 * We may have purged the list because of certain events.
247 * Make sure that is not the case when a specific entry
248 * is being removed.
249 */
250 if (!TAILQ_EMPTY(&list->el_entries))
251 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
252 EHL_LOCK_CONVERT(list);
253 mcache_free(eg_cache, ep);
254 } else {
255 evhlog((LOG_DEBUG, "%s: marking item %p from \"%s\" as dead", __func__,
256 VM_KERNEL_ADDRPERM(ep), list->el_name));
257 ep->ee_priority = EHE_DEAD_PRIORITY;
258 }
259 } else {
260 /* remove entire list */
261 if (list->el_runcount == 0) {
262 evhlog((LOG_DEBUG, "%s: removing all items from \"%s\"", __func__,
263 list->el_name));
264 EHL_LOCK_CONVERT(list);
265 while (!TAILQ_EMPTY(&list->el_entries)) {
266 ep = TAILQ_FIRST(&list->el_entries);
267 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
268 mcache_free(eg_cache, ep);
269 }
270 } else {
271 evhlog((LOG_DEBUG, "%s: marking all items from \"%s\" as dead",
272 __func__, list->el_name));
273 TAILQ_FOREACH(ep, &list->el_entries, ee_link)
274 ep->ee_priority = EHE_DEAD_PRIORITY;
275 }
276 }
277 while (list->el_runcount > 0)
278 msleep((caddr_t)list, &list->el_lock, PSPIN, "evhrm", 0);
279 EHL_UNLOCK(list);
280}
281
282/*
283 * Internal version for use when eventhandler list is already locked.
284 */
285static struct eventhandler_list *
286_eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
287 const char *name)
288{
289 struct eventhandler_list *list;
290
291 VERIFY(evthdlr_lists_ctxt != NULL);
292
293 LCK_MTX_ASSERT(&evthdlr_lists_ctxt->eventhandler_mutex, LCK_MTX_ASSERT_OWNED);
294 TAILQ_FOREACH(list, &evthdlr_lists_ctxt->eventhandler_lists, el_link) {
295 if (!strcmp(name, list->el_name))
296 break;
297 }
298 return (list);
299}
300
301/*
302 * Lookup a "slow" list by name. Returns with the list locked.
303 */
304struct eventhandler_list *
305eventhandler_find_list(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt,
306 const char *name)
307{
308 struct eventhandler_list *list;
309
310 if (evthdlr_lists_ctxt == NULL)
311 evthdlr_lists_ctxt = &evthdlr_lists_ctxt_glb;
312
313 if (!evthdlr_lists_ctxt->eventhandler_lists_initted)
314 return(NULL);
315
316 /* scan looking for the requested list */
317 lck_mtx_lock_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
318 list = _eventhandler_find_list(evthdlr_lists_ctxt, name);
319 if (list != NULL) {
320 lck_mtx_convert_spin(&evthdlr_lists_ctxt->eventhandler_mutex);
321 EHL_LOCK_SPIN(list);
322 }
323 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
324
325 return(list);
326}
327
328/*
329 * Prune "dead" entries from an eventhandler list.
330 */
331void
332eventhandler_prune_list(struct eventhandler_list *list)
333{
334 struct eventhandler_entry *ep, *en;
335 int pruned = 0;
336
337 evhlog((LOG_DEBUG, "%s: pruning list \"%s\"", __func__, list->el_name));
338 EHL_LOCK_ASSERT(list, LCK_MTX_ASSERT_OWNED);
339 TAILQ_FOREACH_SAFE(ep, &list->el_entries, ee_link, en) {
340 if (ep->ee_priority == EHE_DEAD_PRIORITY) {
341 TAILQ_REMOVE(&list->el_entries, ep, ee_link);
342 mcache_free(eg_cache, ep);
343 pruned++;
344 }
345 }
346 if (pruned > 0)
347 wakeup(list);
348}
349
350/*
351 * This should be called when last reference to an object
352 * is being released.
353 * The individual event type lists must be purged when the object
354 * becomes defunct.
355 */
356void
357eventhandler_lists_ctxt_destroy(struct eventhandler_lists_ctxt *evthdlr_lists_ctxt)
358{
359 struct eventhandler_list *list = NULL;
360 struct eventhandler_list *list_next = NULL;
361
362 lck_mtx_lock(&evthdlr_lists_ctxt->eventhandler_mutex);
363 TAILQ_FOREACH_SAFE(list, &evthdlr_lists_ctxt->eventhandler_lists,
364 el_link, list_next) {
365 VERIFY(TAILQ_EMPTY(&list->el_entries));
366 EHL_LOCK_DESTROY(list);
367 mcache_free(el_cache, list);
368 }
369 lck_mtx_unlock(&evthdlr_lists_ctxt->eventhandler_mutex);
370 lck_mtx_destroy(&evthdlr_lists_ctxt->eventhandler_mutex,
371 eventhandler_mutex_grp);
372 return;
373}
374