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 | |
66 | int evh_debug = 0; |
67 | |
68 | MALLOC_DEFINE(M_EVENTHANDLER, "eventhandler" , "Event handler records" ); |
69 | |
70 | SYSCTL_NODE(_kern, OID_AUTO, eventhandler, CTLFLAG_RW | CTLFLAG_LOCKED, |
71 | 0, "Eventhandler" ); |
72 | SYSCTL_INT(_kern_eventhandler, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED, |
73 | &evh_debug, 0, "Eventhandler debug mode" ); |
74 | |
75 | struct eventhandler_entry_arg eventhandler_entry_dummy_arg = { { 0 }, { 0 } }; |
76 | |
77 | /* List of 'slow' lists */ |
78 | static struct eventhandler_lists_ctxt evthdlr_lists_ctxt_glb; |
79 | static lck_grp_attr_t *eventhandler_mutex_grp_attr; |
80 | static lck_grp_t *eventhandler_mutex_grp; |
81 | static lck_attr_t *eventhandler_mutex_attr; |
82 | |
83 | static unsigned int eg_size; /* size of eventhandler_entry_generic */ |
84 | static struct mcache *eg_cache; /* mcache for eventhandler_entry_generic */ |
85 | |
86 | static unsigned int el_size; /* size of eventhandler_list */ |
87 | static struct mcache *el_cache; /* mcache for eventhandler_list */ |
88 | |
89 | static lck_grp_attr_t *el_lock_grp_attr; |
90 | lck_grp_t *el_lock_grp; |
91 | lck_attr_t *el_lock_attr; |
92 | |
93 | struct eventhandler_entry_generic |
94 | { |
95 | struct eventhandler_entry ee; |
96 | void (* func)(void); |
97 | }; |
98 | |
99 | static struct eventhandler_list *_eventhandler_find_list( |
100 | struct eventhandler_lists_ctxt *evthdlr_lists_ctxt, const char *name); |
101 | |
102 | void |
103 | eventhandler_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 | */ |
116 | void |
117 | eventhandler_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 | |
140 | void |
141 | eventhandler_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 | */ |
151 | static eventhandler_tag |
152 | eventhandler_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 | |
217 | eventhandler_tag |
218 | eventhandler_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 | |
234 | void |
235 | eventhandler_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 | */ |
285 | static 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 | */ |
304 | struct eventhandler_list * |
305 | eventhandler_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 | */ |
331 | void |
332 | eventhandler_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 | */ |
356 | void |
357 | eventhandler_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 | |