]> git.saurik.com Git - apple/libdispatch.git/blob - src/voucher.c
libdispatch-442.1.4.tar.gz
[apple/libdispatch.git] / src / voucher.c
1 /*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 #include "internal.h"
22
23 #if VOUCHER_USE_MACH_VOUCHER
24
25 #include <mach/mach_voucher.h>
26
27 // <rdar://16363550>
28 #ifndef VM_MEMORY_GENEALOGY
29 #define VM_MEMORY_GENEALOGY 78
30 #endif
31
32 #ifndef VOUCHER_ATM_COLLECT_THRESHOLD
33 #define VOUCHER_ATM_COLLECT_THRESHOLD 1
34 #endif
35 #define VATM_COLLECT_THRESHOLD_VALUE(t) (((t) - 1) * 2)
36 static volatile long _voucher_atm_collect_level;
37 static long _voucher_atm_collect_threshold =
38 VATM_COLLECT_THRESHOLD_VALUE(VOUCHER_ATM_COLLECT_THRESHOLD);
39 static unsigned long _voucher_atm_subid_bits;
40
41 typedef struct _voucher_atm_s *_voucher_atm_t;
42
43 static void _voucher_activity_atfork_child(void);
44 static inline mach_voucher_t _voucher_get_atm_mach_voucher(voucher_t voucher);
45 static inline mach_voucher_t _voucher_activity_get_atm_mach_voucher(
46 _voucher_activity_t act);
47 static inline _voucher_activity_t _voucher_activity_get(voucher_t voucher);
48 static _voucher_activity_t _voucher_activity_copy_from_mach_voucher(
49 mach_voucher_t kv, voucher_activity_id_t va_id);
50 static inline _voucher_activity_t _voucher_activity_retain(
51 _voucher_activity_t act);
52 static inline void _voucher_activity_release(_voucher_activity_t act);
53
54 #pragma mark -
55 #pragma mark voucher_t
56
57 #if USE_OBJC
58 OS_OBJECT_OBJC_CLASS_DECL(voucher);
59 #define VOUCHER_CLASS OS_OBJECT_OBJC_CLASS(voucher)
60 #else
61 const _os_object_class_s _voucher_class = {
62 ._os_obj_xref_dispose = (void(*)(_os_object_t))_voucher_xref_dispose,
63 ._os_obj_dispose = (void(*)(_os_object_t))_voucher_dispose,
64 };
65 #define VOUCHER_CLASS &_voucher_class
66 #endif // USE_OBJC
67
68 static const voucher_activity_trace_id_t _voucher_activity_trace_id_release =
69 (voucher_activity_trace_id_t)voucher_activity_tracepoint_type_release <<
70 _voucher_activity_trace_id_type_shift;
71 static const unsigned int _voucher_max_activities = 16;
72
73 DISPATCH_ALWAYS_INLINE
74 static inline void
75 _voucher_recipes_init(mach_voucher_attr_recipe_data_t *recipes,
76 mach_voucher_attr_content_size_t bits_size)
77 {
78 static const mach_voucher_attr_recipe_data_t base_recipe = {
79 .key = MACH_VOUCHER_ATTR_KEY_ALL,
80 .command = MACH_VOUCHER_ATTR_COPY,
81 };
82 _voucher_recipes_base(recipes) = base_recipe;
83 static const mach_voucher_attr_recipe_data_t atm_recipe = {
84 .key = MACH_VOUCHER_ATTR_KEY_ATM,
85 .command = MACH_VOUCHER_ATTR_COPY,
86 };
87 _voucher_recipes_atm(recipes) = atm_recipe;
88 static const mach_voucher_attr_recipe_data_t bits_recipe = {
89 .key = MACH_VOUCHER_ATTR_KEY_USER_DATA,
90 .command = MACH_VOUCHER_ATTR_USER_DATA_STORE,
91 };
92 _voucher_recipes_bits(recipes) = bits_recipe;
93 if (!bits_size) return;
94 _voucher_recipes_bits(recipes).content_size = bits_size;
95 *_voucher_recipes_magic(recipes) = _voucher_magic_v1;
96 }
97
98 static inline voucher_t
99 _voucher_alloc(unsigned int activities, pthread_priority_t priority,
100 mach_voucher_attr_recipe_size_t extra)
101 {
102 if (activities > _voucher_max_activities) {
103 activities = _voucher_max_activities;
104 }
105 voucher_t voucher;
106 size_t voucher_size, recipes_size;
107 mach_voucher_attr_content_size_t bits_size;
108 recipes_size = (priority||activities||extra) ? _voucher_recipes_size() : 0;
109 bits_size = recipes_size ? _voucher_bits_size(activities) : 0;
110 voucher_size = sizeof(voucher_s) + recipes_size + bits_size + extra;
111 voucher = (voucher_t)_os_object_alloc_realized(VOUCHER_CLASS, voucher_size);
112 #if VOUCHER_ENABLE_RECIPE_OBJECTS
113 voucher->v_recipe_extra_size = extra;
114 voucher->v_recipe_extra_offset = voucher_size - extra;
115 #else
116 dispatch_assert(!extra);
117 #endif
118 voucher->v_has_priority = priority ? 1 : 0;
119 voucher->v_activities = activities;
120 if (!recipes_size) return voucher;
121 _voucher_recipes_init(voucher->v_recipes, bits_size);
122 *_voucher_priority(voucher) = (_voucher_priority_t)priority;
123 _dispatch_voucher_debug("alloc", voucher);
124 return voucher;
125 }
126
127 #if VOUCHER_ENABLE_RECIPE_OBJECTS
128 voucher_t
129 voucher_create(voucher_recipe_t recipe)
130 {
131 // TODO: capture current activities or current kvoucher ?
132 mach_voucher_attr_recipe_size_t extra = recipe ? recipe->vr_size : 0;
133 voucher_t voucher = _voucher_alloc(0, 0, extra);
134 if (extra) {
135 memcpy(_voucher_extra_recipes(voucher), recipe->vr_data, extra);
136 }
137 return voucher;
138 }
139 #endif
140
141 voucher_t
142 voucher_adopt(voucher_t voucher)
143 {
144 return _voucher_adopt(voucher);
145 }
146
147 voucher_t
148 voucher_copy(void)
149 {
150 return _voucher_copy();
151 }
152
153 voucher_t
154 voucher_copy_without_importance(void)
155 {
156 return _voucher_copy_without_importance();
157 }
158
159 void
160 _voucher_thread_cleanup(void *voucher)
161 {
162 _voucher_swap(voucher, NULL);
163 }
164
165 DISPATCH_CACHELINE_ALIGN
166 static TAILQ_HEAD(, voucher_s) _vouchers[VL_HASH_SIZE];
167 #define _vouchers(kv) (&_vouchers[VL_HASH((kv))])
168 static os_lock_handoff_s _vouchers_lock = OS_LOCK_HANDOFF_INIT;
169 #define _vouchers_lock_lock() os_lock_lock(&_vouchers_lock)
170 #define _vouchers_lock_unlock() os_lock_unlock(&_vouchers_lock)
171
172 static voucher_t
173 _voucher_find_and_retain(mach_voucher_t kv)
174 {
175 voucher_t v;
176 if (!kv) return NULL;
177 _vouchers_lock_lock();
178 TAILQ_FOREACH(v, _vouchers(kv), v_list) {
179 if (v->v_ipc_kvoucher == kv) {
180 int xref_cnt = dispatch_atomic_inc2o(v, os_obj_xref_cnt, relaxed);
181 _dispatch_voucher_debug("retain -> %d", v, xref_cnt + 1);
182 if (slowpath(xref_cnt < 0)) {
183 _dispatch_voucher_debug("overrelease", v);
184 DISPATCH_CRASH("Voucher overrelease");
185 }
186 if (xref_cnt == 0) {
187 // resurrection: raced with _voucher_remove
188 (void)dispatch_atomic_inc2o(v, os_obj_ref_cnt, relaxed);
189 }
190 break;
191 }
192 }
193 _vouchers_lock_unlock();
194 return v;
195 }
196
197 static void
198 _voucher_insert(voucher_t v)
199 {
200 mach_voucher_t kv = v->v_ipc_kvoucher;
201 if (!kv) return;
202 _vouchers_lock_lock();
203 if (slowpath(_TAILQ_IS_ENQUEUED(v, v_list))) {
204 _dispatch_voucher_debug("corruption", v);
205 DISPATCH_CRASH("Voucher corruption");
206 }
207 TAILQ_INSERT_TAIL(_vouchers(kv), v, v_list);
208 _vouchers_lock_unlock();
209 }
210
211 static void
212 _voucher_remove(voucher_t v)
213 {
214 mach_voucher_t kv = v->v_ipc_kvoucher;
215 if (!_TAILQ_IS_ENQUEUED(v, v_list)) return;
216 _vouchers_lock_lock();
217 if (slowpath(!kv)) {
218 _dispatch_voucher_debug("corruption", v);
219 DISPATCH_CRASH("Voucher corruption");
220 }
221 // check for resurrection race with _voucher_find_and_retain
222 if (dispatch_atomic_load2o(v, os_obj_xref_cnt, seq_cst) < 0 &&
223 _TAILQ_IS_ENQUEUED(v, v_list)) {
224 TAILQ_REMOVE(_vouchers(kv), v, v_list);
225 _TAILQ_MARK_NOT_ENQUEUED(v, v_list);
226 v->v_list.tqe_next = (void*)~0ull;
227 }
228 _vouchers_lock_unlock();
229 }
230
231 void
232 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
233 {
234 _dispatch_kvoucher_debug("dealloc", kv);
235 _dispatch_voucher_debug_machport(kv);
236 kern_return_t kr = mach_voucher_deallocate(kv);
237 DISPATCH_VERIFY_MIG(kr);
238 (void)dispatch_assume_zero(kr);
239 }
240
241 static inline kern_return_t
242 _voucher_create_mach_voucher(const mach_voucher_attr_recipe_data_t *recipes,
243 size_t recipes_size, mach_voucher_t *kvp)
244 {
245 kern_return_t kr;
246 mach_port_t mhp = _dispatch_get_mach_host_port();
247 mach_voucher_t kv = MACH_VOUCHER_NULL;
248 mach_voucher_attr_raw_recipe_array_t kvr;
249 mach_voucher_attr_recipe_size_t kvr_size;
250 kvr = (mach_voucher_attr_raw_recipe_array_t)recipes;
251 kvr_size = (mach_voucher_attr_recipe_size_t)recipes_size;
252 kr = host_create_mach_voucher(mhp, kvr, kvr_size, &kv);
253 DISPATCH_VERIFY_MIG(kr);
254 if (!kr) {
255 _dispatch_kvoucher_debug("create", kv);
256 _dispatch_voucher_debug_machport(kv);
257 }
258 *kvp = kv;
259 return kr;
260 }
261
262 #if __has_include(<bank/bank_types.h>) && !defined(VOUCHER_USE_ATTR_BANK)
263 #include <bank/bank_types.h>
264 #define VOUCHER_USE_ATTR_BANK 1
265 mach_voucher_t _voucher_default_task_mach_voucher;
266 #endif
267
268 void
269 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED)
270 {
271 #if VOUCHER_USE_ATTR_BANK
272 kern_return_t kr;
273 mach_voucher_t kv;
274 static const mach_voucher_attr_recipe_data_t task_create_recipe = {
275 .key = MACH_VOUCHER_ATTR_KEY_BANK,
276 .command = MACH_VOUCHER_ATTR_BANK_CREATE,
277 };
278 kr = _voucher_create_mach_voucher(&task_create_recipe,
279 sizeof(task_create_recipe), &kv);
280 if (dispatch_assume_zero(kr)) {
281 DISPATCH_CLIENT_CRASH("Could not create task mach voucher");
282 }
283 _voucher_default_task_mach_voucher = kv;
284 _voucher_task_mach_voucher = kv;
285 #endif
286 }
287
288 void
289 voucher_replace_default_voucher(void)
290 {
291 #if VOUCHER_USE_ATTR_BANK
292 (void)_voucher_get_task_mach_voucher(); // initalize task mach voucher
293 mach_voucher_t kv, tkv = MACH_VOUCHER_NULL;
294 voucher_t v = _voucher_get();
295 if (v && v->v_kvoucher) {
296 kern_return_t kr;
297 kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
298 const mach_voucher_attr_recipe_data_t task_copy_recipe = {
299 .key = MACH_VOUCHER_ATTR_KEY_BANK,
300 .command = MACH_VOUCHER_ATTR_COPY,
301 .previous_voucher = kv,
302 };
303 kr = _voucher_create_mach_voucher(&task_copy_recipe,
304 sizeof(task_copy_recipe), &tkv);
305 if (dispatch_assume_zero(kr)) {
306 tkv = MACH_VOUCHER_NULL;
307 }
308 }
309 if (!tkv) tkv = _voucher_default_task_mach_voucher;
310 kv = dispatch_atomic_xchg(&_voucher_task_mach_voucher, tkv, relaxed);
311 if (kv && kv != _voucher_default_task_mach_voucher) {
312 _voucher_dealloc_mach_voucher(kv);
313 }
314 _dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v, tkv);
315 #endif
316 }
317
318 static inline mach_voucher_t
319 _voucher_get_atm_mach_voucher(voucher_t voucher)
320 {
321 _voucher_activity_t act = _voucher_activity_get(voucher);
322 return _voucher_activity_get_atm_mach_voucher(act);
323 }
324
325 mach_voucher_t
326 _voucher_get_mach_voucher(voucher_t voucher)
327 {
328 if (!voucher) return MACH_VOUCHER_NULL;
329 if (voucher->v_ipc_kvoucher) return voucher->v_ipc_kvoucher;
330 mach_voucher_t kvb = voucher->v_kvoucher;
331 if (!kvb) kvb = _voucher_get_task_mach_voucher();
332 if (!voucher->v_has_priority && !voucher->v_activities &&
333 !_voucher_extra_size(voucher)) {
334 return kvb;
335 }
336 kern_return_t kr;
337 mach_voucher_t kv, kvo;
338 _voucher_base_recipe(voucher).previous_voucher = kvb;
339 _voucher_atm_recipe(voucher).previous_voucher =
340 _voucher_get_atm_mach_voucher(voucher);
341 kr = _voucher_create_mach_voucher(voucher->v_recipes,
342 _voucher_recipes_size() + _voucher_extra_size(voucher) +
343 _voucher_bits_recipe(voucher).content_size, &kv);
344 if (dispatch_assume_zero(kr) || !kv){
345 return MACH_VOUCHER_NULL;
346 }
347 if (!dispatch_atomic_cmpxchgv2o(voucher, v_ipc_kvoucher, MACH_VOUCHER_NULL,
348 kv, &kvo, relaxed)) {
349 _voucher_dealloc_mach_voucher(kv);
350 kv = kvo;
351 } else {
352 if (kv == voucher->v_kvoucher) {
353 // if v_kvoucher == v_ipc_kvoucher we keep only one reference
354 _voucher_dealloc_mach_voucher(kv);
355 }
356 _voucher_insert(voucher);
357 _dispatch_voucher_debug("kvoucher[0x%08x] create", voucher, kv);
358 }
359 return kv;
360 }
361
362 mach_voucher_t
363 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
364 pthread_priority_t priority)
365 {
366 if (priority == _voucher_get_priority(voucher)) {
367 return MACH_VOUCHER_NULL; // caller will use _voucher_get_mach_voucher
368 }
369 kern_return_t kr;
370 mach_voucher_t kv, kvb = voucher ? voucher->v_kvoucher : MACH_VOUCHER_NULL;
371 if (!kvb) kvb = _voucher_get_task_mach_voucher();
372 mach_voucher_attr_recipe_data_t *recipes;
373 size_t recipes_size = _voucher_recipes_size();
374 if (voucher && (voucher->v_has_priority || voucher->v_activities ||
375 _voucher_extra_size(voucher))) {
376 recipes_size += _voucher_bits_recipe(voucher).content_size +
377 _voucher_extra_size(voucher);
378 recipes = alloca(recipes_size);
379 memcpy(recipes, voucher->v_recipes, recipes_size);
380 _voucher_recipes_atm(recipes).previous_voucher =
381 _voucher_get_atm_mach_voucher(voucher);
382 } else {
383 mach_voucher_attr_content_size_t bits_size = _voucher_bits_size(0);
384 recipes_size += bits_size;
385 recipes = alloca(recipes_size);
386 _voucher_recipes_init(recipes, bits_size);
387 }
388 _voucher_recipes_base(recipes).previous_voucher = kvb;
389 *_voucher_recipes_priority(recipes) = (_voucher_priority_t)priority;
390 kr = _voucher_create_mach_voucher(recipes, recipes_size, &kv);
391 if (dispatch_assume_zero(kr) || !kv){
392 return MACH_VOUCHER_NULL;
393 }
394 _dispatch_kvoucher_debug("create with priority from voucher[%p]", kv,
395 voucher);
396 return kv;
397 }
398
399 static voucher_t
400 _voucher_create_with_mach_voucher(mach_voucher_t kv)
401 {
402 if (!kv) return NULL;
403 kern_return_t kr;
404 mach_voucher_t rkv;
405 mach_voucher_attr_recipe_t vr;
406 size_t vr_size;
407 mach_voucher_attr_recipe_size_t kvr_size = 0;
408 const mach_voucher_attr_recipe_data_t redeem_recipe[] = {
409 [0] = {
410 .key = MACH_VOUCHER_ATTR_KEY_ALL,
411 .command = MACH_VOUCHER_ATTR_COPY,
412 .previous_voucher = kv,
413 },
414 #if VOUCHER_USE_ATTR_BANK
415 [1] = {
416 .key = MACH_VOUCHER_ATTR_KEY_BANK,
417 .command = MACH_VOUCHER_ATTR_REDEEM,
418 },
419 #endif
420 };
421 kr = _voucher_create_mach_voucher(redeem_recipe, sizeof(redeem_recipe),
422 &rkv);
423 if (!dispatch_assume_zero(kr)) {
424 _voucher_dealloc_mach_voucher(kv);
425 } else {
426 _dispatch_voucher_debug_machport(kv);
427 rkv = kv;
428 }
429 voucher_t v = _voucher_find_and_retain(rkv);
430 if (v) {
431 _dispatch_voucher_debug("kvoucher[0x%08x] find with 0x%08x", v, rkv,kv);
432 _voucher_dealloc_mach_voucher(rkv);
433 return v;
434 }
435 vr_size = sizeof(*vr) + _voucher_bits_size(_voucher_max_activities);
436 vr = alloca(vr_size);
437 if (rkv) {
438 kvr_size = (mach_voucher_attr_recipe_size_t)vr_size;
439 kr = mach_voucher_extract_attr_recipe(rkv,
440 MACH_VOUCHER_ATTR_KEY_USER_DATA, (void*)vr, &kvr_size);
441 DISPATCH_VERIFY_MIG(kr);
442 if (dispatch_assume_zero(kr)) kvr_size = 0;
443 }
444 mach_voucher_attr_content_size_t content_size = vr->content_size;
445 uint8_t *content = vr->content;
446 bool valid = false, has_priority = false;
447 unsigned int activities = 0;
448 if (kvr_size >= sizeof(*vr) + sizeof(_voucher_magic_t)) {
449 valid = (*(_voucher_magic_t*)content == _voucher_magic_v1);
450 content += sizeof(_voucher_magic_t);
451 content_size -= sizeof(_voucher_magic_t);
452 }
453 if (valid) {
454 has_priority = (content_size >= sizeof(_voucher_priority_t));
455 activities = has_priority ? (content_size - sizeof(_voucher_priority_t))
456 / sizeof(voucher_activity_id_t) : 0;
457 }
458 pthread_priority_t priority = 0;
459 if (has_priority) {
460 priority = (pthread_priority_t)*(_voucher_priority_t*)content;
461 content += sizeof(_voucher_priority_t);
462 content_size -= sizeof(_voucher_priority_t);
463 }
464 voucher_activity_id_t va_id = 0, va_base_id = 0;
465 _voucher_activity_t act = NULL;
466 if (activities) {
467 va_id = *(voucher_activity_id_t*)content;
468 act = _voucher_activity_copy_from_mach_voucher(rkv, va_id);
469 if (!act && _voucher_activity_default) {
470 activities++;
471 // default to _voucher_activity_default base activity
472 va_base_id = _voucher_activity_default->va_id;
473 } else if (act && act->va_id != va_id) {
474 activities++;
475 va_base_id = act->va_id;
476 }
477 }
478 v = _voucher_alloc(activities, priority, 0);
479 v->v_activity = act;
480 voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
481 if (activities && va_base_id) {
482 *activity_ids++ = va_base_id;
483 activities--;
484 }
485 if (activities) {
486 memcpy(activity_ids, content, content_size);
487 }
488 v->v_ipc_kvoucher = v->v_kvoucher = rkv;
489 _voucher_insert(v);
490 _dispatch_voucher_debug("kvoucher[0x%08x] create with 0x%08x", v, rkv, kv);
491 return v;
492 }
493
494 voucher_t
495 _voucher_create_with_priority_and_mach_voucher(voucher_t ov,
496 pthread_priority_t priority, mach_voucher_t kv)
497 {
498 if (priority == _voucher_get_priority(ov)) {
499 if (kv) _voucher_dealloc_mach_voucher(kv);
500 return ov ? _voucher_retain(ov) : NULL;
501 }
502 voucher_t v = _voucher_find_and_retain(kv);
503 if (v) {
504 _dispatch_voucher_debug("kvoucher[0x%08x] find", v, kv);
505 _voucher_dealloc_mach_voucher(kv);
506 return v;
507 }
508 unsigned int activities = ov ? ov->v_activities : 0;
509 mach_voucher_attr_recipe_size_t extra = ov ? _voucher_extra_size(ov) : 0;
510 v = _voucher_alloc(activities, priority, extra);
511 if (extra) {
512 memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
513 }
514 if (activities) {
515 if (ov->v_activity) {
516 v->v_activity = _voucher_activity_retain(ov->v_activity);
517 }
518 memcpy(_voucher_activity_ids(v), _voucher_activity_ids(ov),
519 activities * sizeof(voucher_activity_id_t));
520 }
521 if (kv) {
522 v->v_ipc_kvoucher = v->v_kvoucher = kv;
523 _voucher_insert(v);
524 _dispatch_voucher_debug("kvoucher[0x%08x] create with priority from "
525 "voucher[%p]", v, kv, ov);
526 _dispatch_voucher_debug_machport(kv);
527 } else if (ov && ov->v_kvoucher) {
528 voucher_t kvb = ov->v_kvbase ? ov->v_kvbase : ov;
529 v->v_kvbase = _voucher_retain(kvb);
530 v->v_kvoucher = kvb->v_kvoucher;
531 }
532 return v;
533 }
534
535 voucher_t
536 _voucher_create_without_importance(voucher_t ov)
537 {
538 // Nothing to do unless the old voucher has a kernel voucher. If it
539 // doesn't, it can't have any importance, now or in the future.
540 if (!ov) return NULL;
541 // TODO: 17487167: track presence of importance attribute
542 if (!ov->v_kvoucher) return _voucher_retain(ov);
543 kern_return_t kr;
544 mach_voucher_t kv, okv;
545 // Copy kernel voucher, removing importance.
546 okv = ov->v_ipc_kvoucher ? ov->v_ipc_kvoucher : ov->v_kvoucher;
547 const mach_voucher_attr_recipe_data_t importance_remove_recipe[] = {
548 [0] = {
549 .key = MACH_VOUCHER_ATTR_KEY_ALL,
550 .command = MACH_VOUCHER_ATTR_COPY,
551 .previous_voucher = okv,
552 },
553 [1] = {
554 .key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
555 .command = MACH_VOUCHER_ATTR_REMOVE,
556 },
557 };
558 kr = _voucher_create_mach_voucher(importance_remove_recipe,
559 sizeof(importance_remove_recipe), &kv);
560 if (dispatch_assume_zero(kr) || !kv){
561 if (ov->v_ipc_kvoucher) return NULL;
562 kv = MACH_VOUCHER_NULL;
563 }
564 if (kv == okv) {
565 _voucher_dealloc_mach_voucher(kv);
566 return _voucher_retain(ov);
567 }
568 voucher_t v = _voucher_find_and_retain(kv);
569 if (v && ov->v_ipc_kvoucher) {
570 _dispatch_voucher_debug("kvoucher[0x%08x] find without importance "
571 "from voucher[%p]", v, kv, ov);
572 _voucher_dealloc_mach_voucher(kv);
573 return v;
574 }
575 voucher_t kvbase = v;
576 // Copy userspace contents
577 unsigned int activities = ov->v_activities;
578 pthread_priority_t priority = _voucher_get_priority(ov);
579 mach_voucher_attr_recipe_size_t extra = _voucher_extra_size(ov);
580 v = _voucher_alloc(activities, priority, extra);
581 if (extra) {
582 memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
583 }
584 if (activities) {
585 if (ov->v_activity) {
586 v->v_activity = _voucher_activity_retain(ov->v_activity);
587 }
588 memcpy(_voucher_activity_ids(v), _voucher_activity_ids(ov),
589 activities * sizeof(voucher_activity_id_t));
590 }
591 v->v_kvoucher = kv;
592 if (ov->v_ipc_kvoucher) {
593 v->v_ipc_kvoucher = kv;
594 _voucher_insert(v);
595 } else if (kvbase) {
596 v->v_kvbase = kvbase;
597 _voucher_dealloc_mach_voucher(kv); // borrow base reference
598 }
599 if (!kvbase) {
600 _dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
601 "from voucher[%p]", v, kv, ov);
602 }
603 return v;
604 }
605
606 voucher_t
607 voucher_create_with_mach_msg(mach_msg_header_t *msg)
608 {
609 voucher_t v = _voucher_create_with_mach_voucher(_voucher_mach_msg_get(msg));
610 _voucher_activity_trace_msg(v, msg, receive);
611 return v;
612 }
613
614 #ifndef MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
615 #define MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL 2
616 #endif
617
618 void
619 voucher_decrement_importance_count4CF(voucher_t v)
620 {
621 if (!v || !v->v_kvoucher) return;
622 // TODO: 17487167: track presence of importance attribute
623 kern_return_t kr;
624 mach_voucher_t kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
625 uint32_t dec = 1;
626 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&dec;
627 mach_voucher_attr_content_size_t kvc_in_size = sizeof(dec);
628 mach_voucher_attr_content_t kvc_out = NULL;
629 mach_voucher_attr_content_size_t kvc_out_size = 0;
630 #if DISPATCH_DEBUG
631 uint32_t count = UINT32_MAX;
632 kvc_out = (mach_voucher_attr_content_t)&count;
633 kvc_out_size = sizeof(count);
634 #endif
635 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
636 MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL, kvc_in, kvc_in_size,
637 kvc_out, &kvc_out_size);
638 DISPATCH_VERIFY_MIG(kr);
639 #if DISPATCH_DEBUG
640 _dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
641 " %s - 0x%x", v, kv, count, mach_error_string(kr), kr);
642 #endif
643 if (kr != KERN_INVALID_ARGUMENT &&
644 dispatch_assume_zero(kr) == KERN_FAILURE) {
645 // TODO: 17487167: skip KERN_INVALID_ARGUMENT check
646 DISPATCH_CLIENT_CRASH("Voucher importance count underflow");
647 }
648 }
649
650 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
651 mach_voucher_t
652 voucher_get_mach_voucher(voucher_t voucher)
653 {
654 return _voucher_get_mach_voucher(voucher);
655 }
656 #endif
657
658 void
659 _voucher_xref_dispose(voucher_t voucher)
660 {
661 _dispatch_voucher_debug("xref_dispose", voucher);
662 _voucher_remove(voucher);
663 return _os_object_release_internal_inline((_os_object_t)voucher);
664 }
665
666 void
667 _voucher_dispose(voucher_t voucher)
668 {
669 _dispatch_voucher_debug("dispose", voucher);
670 if (slowpath(_TAILQ_IS_ENQUEUED(voucher, v_list))) {
671 _dispatch_voucher_debug("corruption", voucher);
672 DISPATCH_CRASH("Voucher corruption");
673 }
674 voucher->v_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
675 if (voucher->v_ipc_kvoucher) {
676 if (voucher->v_ipc_kvoucher != voucher->v_kvoucher) {
677 _voucher_dealloc_mach_voucher(voucher->v_ipc_kvoucher);
678 }
679 voucher->v_ipc_kvoucher = MACH_VOUCHER_NULL;
680 }
681 if (voucher->v_kvoucher) {
682 if (!voucher->v_kvbase) {
683 _voucher_dealloc_mach_voucher(voucher->v_kvoucher);
684 }
685 voucher->v_kvoucher = MACH_VOUCHER_NULL;
686 }
687 if (voucher->v_kvbase) {
688 _voucher_release(voucher->v_kvbase);
689 voucher->v_kvbase = NULL;
690 }
691 if (voucher->v_activity) {
692 _voucher_activity_release(voucher->v_activity);
693 voucher->v_activity = NULL;
694 }
695 voucher->v_has_priority= 0;
696 voucher->v_activities = 0;
697 #if VOUCHER_ENABLE_RECIPE_OBJECTS
698 voucher->v_recipe_extra_size = 0;
699 voucher->v_recipe_extra_offset = 0;
700 #endif
701 return _os_object_dealloc((_os_object_t)voucher);
702 }
703
704 void
705 _voucher_atfork_child(void)
706 {
707 _voucher_activity_atfork_child();
708 _dispatch_thread_setspecific(dispatch_voucher_key, NULL);
709 _voucher_task_mach_voucher_pred = 0;
710 _voucher_task_mach_voucher = MACH_VOUCHER_NULL;
711
712 // TODO: voucher/activity inheritance on fork ?
713 }
714
715 #pragma mark -
716 #pragma mark _voucher_init
717
718 boolean_t
719 voucher_mach_msg_set(mach_msg_header_t *msg)
720 {
721 voucher_t v = _voucher_get();
722 bool clear_voucher = _voucher_mach_msg_set(msg, v);
723 if (clear_voucher) _voucher_activity_trace_msg(v, msg, send);
724 return clear_voucher;
725 }
726
727 void
728 voucher_mach_msg_clear(mach_msg_header_t *msg)
729 {
730 (void)_voucher_mach_msg_clear(msg, false);
731 }
732
733 voucher_mach_msg_state_t
734 voucher_mach_msg_adopt(mach_msg_header_t *msg)
735 {
736 mach_voucher_t kv = _voucher_mach_msg_get(msg);
737 if (!kv) return VOUCHER_MACH_MSG_STATE_UNCHANGED;
738 voucher_t v = _voucher_create_with_mach_voucher(kv);
739 _voucher_activity_trace_msg(v, msg, receive);
740 return (voucher_mach_msg_state_t)_voucher_adopt(v);
741 }
742
743 void
744 voucher_mach_msg_revert(voucher_mach_msg_state_t state)
745 {
746 if (state == VOUCHER_MACH_MSG_STATE_UNCHANGED) return;
747 _voucher_replace((voucher_t)state);
748 }
749
750 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
751 #include <_libkernel_init.h>
752
753 static const struct _libkernel_voucher_functions _voucher_libkernel_functions =
754 {
755 .version = 1,
756 .voucher_mach_msg_set = voucher_mach_msg_set,
757 .voucher_mach_msg_clear = voucher_mach_msg_clear,
758 .voucher_mach_msg_adopt = voucher_mach_msg_adopt,
759 .voucher_mach_msg_revert = voucher_mach_msg_revert,
760 };
761
762 static void
763 _voucher_libkernel_init(void)
764 {
765 kern_return_t kr = __libkernel_voucher_init(&_voucher_libkernel_functions);
766 dispatch_assert(!kr);
767 }
768 #else
769 #define _voucher_libkernel_init()
770 #endif
771
772 void
773 _voucher_init(void)
774 {
775 _voucher_libkernel_init();
776 char *e, *end;
777 unsigned int i;
778 for (i = 0; i < VL_HASH_SIZE; i++) {
779 TAILQ_INIT(&_vouchers[i]);
780 }
781 voucher_activity_mode_t mode;
782 mode = DISPATCH_DEBUG ? voucher_activity_mode_debug
783 : voucher_activity_mode_release;
784 e = getenv("OS_ACTIVITY_MODE");
785 if (e) {
786 if (strcmp(e, "release") == 0) {
787 mode = voucher_activity_mode_release;
788 } else if (strcmp(e, "debug") == 0) {
789 mode = voucher_activity_mode_debug;
790 } else if (strcmp(e, "stream") == 0) {
791 mode = voucher_activity_mode_stream;
792 } else if (strcmp(e, "disable") == 0) {
793 mode = voucher_activity_mode_disable;
794 }
795 }
796 _voucher_activity_mode = mode;
797 if (_voucher_activity_disabled()) return;
798
799 e = getenv("LIBDISPATCH_ACTIVITY_ATM_SUBID_BITS");
800 if (e) {
801 unsigned long v = strtoul(e, &end, 0);
802 if (v && !*end) {
803 _voucher_atm_subid_bits = v;
804 }
805 }
806 e = getenv("LIBDISPATCH_ACTIVITY_ATM_COLLECT_THRESHOLD");
807 if (e) {
808 unsigned long v = strtoul(e, &end, 0);
809 if (v && v < LONG_MAX/2 && !*end) {
810 _voucher_atm_collect_threshold =
811 VATM_COLLECT_THRESHOLD_VALUE((long)v);
812 }
813 }
814 // default task activity
815 bool default_task_activity = DISPATCH_DEBUG;
816 e = getenv("LIBDISPATCH_DEFAULT_TASK_ACTIVITY");
817 if (e) default_task_activity = atoi(e);
818 if (default_task_activity) {
819 (void)voucher_activity_start(_voucher_activity_trace_id_release, 0);
820 }
821 }
822
823 #pragma mark -
824 #pragma mark _voucher_activity_lock_s
825
826 DISPATCH_ALWAYS_INLINE
827 static inline void
828 _voucher_activity_lock_init(_voucher_activity_lock_s *lock) {
829 static const os_lock_handoff_s _os_lock_handoff_init = OS_LOCK_HANDOFF_INIT;
830 *lock = _os_lock_handoff_init;
831 }
832
833 DISPATCH_ALWAYS_INLINE
834 static inline void
835 _voucher_activity_lock_lock(_voucher_activity_lock_s *lock) {
836 return os_lock_lock(lock);
837 }
838
839 DISPATCH_ALWAYS_INLINE
840 static inline void
841 _voucher_activity_lock_unlock(_voucher_activity_lock_s *lock) {
842 return os_lock_unlock(lock);
843 }
844
845 #pragma mark -
846 #pragma mark _voucher_activity_heap
847
848 #if __has_extension(c_static_assert)
849 _Static_assert(sizeof(struct _voucher_activity_tracepoint_s) == 64,
850 "Tracepoint too large");
851 _Static_assert(sizeof(struct _voucher_activity_buffer_header_s) <=
852 sizeof(struct _voucher_activity_tracepoint_s),
853 "Buffer header too large");
854 _Static_assert(offsetof(struct _voucher_activity_s, va_flags2) ==
855 sizeof(struct _voucher_activity_tracepoint_s),
856 "Extended activity object misaligned");
857 #if __LP64__
858 _Static_assert(sizeof(struct _voucher_activity_s) ==
859 3 * sizeof(struct _voucher_activity_tracepoint_s),
860 "Activity object too large");
861 _Static_assert(offsetof(struct _voucher_activity_s, va_flags3) ==
862 2 * sizeof(struct _voucher_activity_tracepoint_s),
863 "Extended activity object misaligned");
864 _Static_assert(offsetof(struct _voucher_atm_s, vatm_activities_lock) % 64 == 0,
865 "Bad ATM padding");
866 _Static_assert(sizeof(struct _voucher_atm_s) <= 128,
867 "ATM too large");
868 #else
869 _Static_assert(sizeof(struct _voucher_activity_s) ==
870 2 * sizeof(struct _voucher_activity_tracepoint_s),
871 "Activity object too large");
872 _Static_assert(sizeof(struct _voucher_atm_s) <= 64,
873 "ATM too large");
874 #endif
875 _Static_assert(sizeof(_voucher_activity_buffer_t) ==
876 sizeof(struct {char x[_voucher_activity_buffer_size];}),
877 "Buffer too large");
878 _Static_assert(sizeof(struct _voucher_activity_metadata_s) <=
879 sizeof(struct _voucher_activity_metadata_opaque_s),
880 "Metadata too large");
881 _Static_assert(sizeof(_voucher_activity_bitmap_t) % 64 == 0,
882 "Bad metadata bitmap size");
883 _Static_assert(offsetof(struct _voucher_activity_metadata_s,
884 vam_atm_mbox_bitmap) % 64 == 0,
885 "Bad metadata padding");
886 _Static_assert(offsetof(struct _voucher_activity_metadata_s,
887 vam_base_atm_subid) % 64 == 0,
888 "Bad metadata padding");
889 _Static_assert(offsetof(struct _voucher_activity_metadata_s, vam_base_atm_lock)
890 % 32 == 0,
891 "Bad metadata padding");
892 _Static_assert(offsetof(struct _voucher_activity_metadata_s, vam_atms) % 64 ==0,
893 "Bad metadata padding");
894 _Static_assert(sizeof(_voucher_activity_bitmap_t) * 8 *
895 sizeof(atm_mailbox_offset_t) <=
896 sizeof(((_voucher_activity_metadata_t)NULL)->vam_kernel_metadata),
897 "Bad kernel metadata bitmap");
898 _Static_assert(sizeof(atm_mailbox_offset_t) == 2 * sizeof(atm_subaid32_t),
899 "Bad kernel ATM mailbox sizes");
900 #endif
901
902 static const size_t _voucher_atm_mailboxes =
903 sizeof(((_voucher_activity_metadata_t)NULL)->vam_kernel_metadata) /
904 sizeof(atm_mailbox_offset_t);
905
906 #define va_buffers_lock(va) (&(va)->va_buffers_lock)
907 #define vatm_activities_lock(vatm) (&(vatm)->vatm_activities_lock)
908 #define vatm_activities(vatm) (&(vatm)->vatm_activities)
909 #define vatm_used_activities(vatm) (&(vatm)->vatm_used_activities)
910 #define vam_base_atm_lock() (&_voucher_activity_heap->vam_base_atm_lock)
911 #define vam_nested_atm_lock() (&_voucher_activity_heap->vam_nested_atm_lock)
912 #define vam_atms_lock() (&_voucher_activity_heap->vam_atms_lock)
913 #define vam_activities_lock() (&_voucher_activity_heap->vam_activities_lock)
914 #define vam_atms(hash) (&_voucher_activity_heap->vam_atms[hash])
915 #define vam_activities(hash) (&_voucher_activity_heap->vam_activities[hash])
916 #define vam_buffer_bitmap() (_voucher_activity_heap->vam_buffer_bitmap)
917 #define vam_atm_mbox_bitmap() (_voucher_activity_heap->vam_atm_mbox_bitmap)
918 #define vam_pressure_locked_bitmap() \
919 (_voucher_activity_heap->vam_pressure_locked_bitmap)
920 #define vam_buffer(i) ((void*)((char*)_voucher_activity_heap + \
921 (i) * _voucher_activity_buffer_size))
922
923 static _voucher_activity_t _voucher_activity_create_with_atm(
924 _voucher_atm_t vatm, voucher_activity_id_t va_id,
925 voucher_activity_trace_id_t trace_id, uint64_t location,
926 _voucher_activity_buffer_header_t buffer);
927 static _voucher_atm_t _voucher_atm_create(mach_voucher_t kv, atm_aid_t atm_id);
928 static voucher_activity_id_t _voucher_atm_nested_atm_id_make(void);
929
930 DISPATCH_ALWAYS_INLINE
931 static inline uint32_t
932 _voucher_default_activity_buffer_limit()
933 {
934 switch (_voucher_activity_mode) {
935 case voucher_activity_mode_debug:
936 case voucher_activity_mode_stream:
937 // High-profile modes: Default activity can use 1/32nd of the heap
938 // (twice as much as non-default activities)
939 return MAX(_voucher_activity_buffers_per_heap / 32, 3) - 1;
940 }
941 #if TARGET_OS_EMBEDDED
942 // Low-profile modes: Default activity can use a total of 3 buffers.
943 return 2;
944 #else
945 // Low-profile modes: Default activity can use a total of 8 buffers.
946 return 7;
947 #endif
948 }
949
950 DISPATCH_ALWAYS_INLINE
951 static inline uint32_t
952 _voucher_activity_buffer_limit()
953 {
954 switch (_voucher_activity_mode) {
955 case voucher_activity_mode_debug:
956 case voucher_activity_mode_stream:
957 // High-profile modes: 64 activities, each of which can use 1/64th
958 // of the entire heap.
959 return MAX(_voucher_activity_buffers_per_heap / 64, 2) - 1;
960 }
961 #if TARGET_OS_EMBEDDED
962 // Low-profile modes: Each activity can use a total of 2 buffers.
963 return 1;
964 #else
965 // Low-profile modes: Each activity can use a total of 4 buffers.
966 return 3;
967 #endif
968 }
969
970 // The two functions above return the number of *additional* buffers activities
971 // may allocate, hence the gymnastics with - 1.
972
973 DISPATCH_ALWAYS_INLINE
974 static inline uint32_t
975 _voucher_heap_buffer_limit()
976 {
977 switch (_voucher_activity_mode) {
978 case voucher_activity_mode_debug:
979 case voucher_activity_mode_stream:
980 // High-profile modes: Use it all.
981 return _voucher_activity_buffers_per_heap;
982 }
983 #if TARGET_OS_EMBEDDED
984 // Low-profile modes: 3 activities, each of which can use 2 buffers;
985 // plus the default activity, which can use 3; plus 3 buffers of overhead.
986 return 12;
987 #else
988 // Low-profile modes: 13 activities, each of which can use 4 buffers;
989 // plus the default activity, which can use 8; plus 3 buffers of overhead.
990 return 64;
991 #endif
992 }
993
994 #define NO_BITS_WERE_UNSET (UINT_MAX)
995
996 DISPATCH_ALWAYS_INLINE
997 static inline size_t
998 _voucher_activity_bitmap_set_first_unset_bit_upto(
999 _voucher_activity_bitmap_t volatile bitmap,
1000 unsigned int max_index)
1001 {
1002 dispatch_assert(max_index != 0);
1003 unsigned int index = NO_BITS_WERE_UNSET, max_map, max_bit, i;
1004 max_map = max_index / _voucher_activity_bits_per_bitmap_base_t;
1005 max_map = MIN(max_map, _voucher_activity_bitmaps_per_heap - 1);
1006 max_bit = max_index % _voucher_activity_bits_per_bitmap_base_t;
1007 for (i = 0; i < max_map; i++) {
1008 index = dispatch_atomic_set_first_bit(&bitmap[i], UINT_MAX);
1009 if (fastpath(index < NO_BITS_WERE_UNSET)) {
1010 return index + i * _voucher_activity_bits_per_bitmap_base_t;
1011 }
1012 }
1013 index = dispatch_atomic_set_first_bit(&bitmap[i], max_bit);
1014 if (fastpath(index < NO_BITS_WERE_UNSET)) {
1015 return index + i * _voucher_activity_bits_per_bitmap_base_t;
1016 }
1017 return index;
1018 }
1019
1020 DISPATCH_ALWAYS_INLINE
1021 static inline size_t
1022 _voucher_activity_bitmap_set_first_unset_bit(
1023 _voucher_activity_bitmap_t volatile bitmap)
1024 {
1025 return _voucher_activity_bitmap_set_first_unset_bit_upto(bitmap, UINT_MAX);
1026 }
1027
1028
1029 DISPATCH_ALWAYS_INLINE
1030 static inline void
1031 _voucher_activity_bitmap_clear_bit(
1032 _voucher_activity_bitmap_t volatile bitmap, size_t index)
1033 {
1034 size_t i = index / _voucher_activity_bits_per_bitmap_base_t;
1035 _voucher_activity_bitmap_base_t mask = ((typeof(mask))1) <<
1036 (index % _voucher_activity_bits_per_bitmap_base_t);
1037 if (slowpath((bitmap[i] & mask) == 0)) {
1038 DISPATCH_CRASH("Corruption: failed to clear bit exclusively");
1039 }
1040 (void)dispatch_atomic_and(&bitmap[i], ~mask, release);
1041 }
1042
1043 _voucher_activity_metadata_t _voucher_activity_heap;
1044 static dispatch_once_t _voucher_activity_heap_pred;
1045
1046 static void
1047 _voucher_activity_heap_init(void *ctxt DISPATCH_UNUSED)
1048 {
1049 if (_voucher_activity_disabled()) return;
1050 kern_return_t kr;
1051 mach_vm_size_t vm_size = _voucher_activity_buffer_size *
1052 _voucher_activity_buffers_per_heap;
1053 mach_vm_address_t vm_addr = vm_page_size;
1054 while (slowpath(kr = mach_vm_map(mach_task_self(), &vm_addr, vm_size,
1055 0, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_GENEALOGY),
1056 MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_ALL,
1057 VM_INHERIT_NONE))) {
1058 if (kr != KERN_NO_SPACE) {
1059 (void)dispatch_assume_zero(kr);
1060 _voucher_activity_mode = voucher_activity_mode_disable;
1061 return;
1062 }
1063 _dispatch_temporary_resource_shortage();
1064 vm_addr = vm_page_size;
1065 }
1066 _voucher_activity_metadata_t heap;
1067 task_trace_memory_info_data_t trace_memory_info = {
1068 .user_memory_address = vm_addr,
1069 .buffer_size = vm_size,
1070 .mailbox_array_size = sizeof(heap->vam_kernel_metadata),
1071 };
1072 kr = task_set_info(mach_task_self(), TASK_TRACE_MEMORY_INFO,
1073 (task_info_t)&trace_memory_info, TASK_TRACE_MEMORY_INFO_COUNT);
1074 DISPATCH_VERIFY_MIG(kr);
1075 if (kr) {
1076 if (kr != KERN_NOT_SUPPORTED) (void)dispatch_assume_zero(kr);
1077 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
1078 (void)dispatch_assume_zero(kr);
1079 _voucher_activity_mode = voucher_activity_mode_disable;
1080 return;
1081 }
1082 heap = (void*)vm_addr;
1083 heap->vam_self_metadata.vasm_baseaddr = (void*)vm_addr;
1084 heap->vam_buffer_bitmap[0] = 0xf; // first four buffers are reserved
1085 uint32_t i;
1086 for (i = 0; i < _voucher_activity_hash_size; i++) {
1087 TAILQ_INIT(&heap->vam_activities[i]);
1088 TAILQ_INIT(&heap->vam_atms[i]);
1089 }
1090 uint32_t subid_max = VATM_SUBID_MAX;
1091 if (_voucher_atm_subid_bits &&
1092 _voucher_atm_subid_bits < VATM_SUBID_MAXBITS) {
1093 subid_max = MIN(VATM_SUBID_BITS2MAX(_voucher_atm_subid_bits),
1094 VATM_SUBID_MAX);
1095 }
1096 heap->vam_base_atm_subid_max = subid_max;
1097 _voucher_activity_lock_init(&heap->vam_base_atm_lock);
1098 _voucher_activity_lock_init(&heap->vam_nested_atm_lock);
1099 _voucher_activity_lock_init(&heap->vam_atms_lock);
1100 _voucher_activity_lock_init(&heap->vam_activities_lock);
1101 _voucher_activity_heap = heap;
1102
1103 _voucher_atm_t vatm = _voucher_atm_create(0, 0);
1104 dispatch_assert(vatm->vatm_kvoucher);
1105 heap->vam_default_activity_atm = vatm;
1106 _voucher_activity_buffer_header_t buffer = vam_buffer(3); // reserved index
1107 // consumes vatm reference:
1108 _voucher_activity_t va = _voucher_activity_create_with_atm(vatm,
1109 VATM_ACTID(vatm, _voucher_default_activity_subid), 0, 0, buffer);
1110 dispatch_assert(va);
1111 va->va_buffer_limit = _voucher_default_activity_buffer_limit();
1112 _voucher_activity_default = va;
1113 heap->vam_base_atm = _voucher_atm_create(0, 0);
1114 heap->vam_nested_atm_id = _voucher_atm_nested_atm_id_make();
1115 }
1116
1117 static void
1118 _voucher_activity_atfork_child(void)
1119 {
1120 _voucher_activity_heap_pred = 0;
1121 _voucher_activity_heap = NULL; // activity heap is VM_INHERIT_NONE
1122 _voucher_activity_default = NULL;
1123 }
1124
1125 void*
1126 voucher_activity_get_metadata_buffer(size_t *length)
1127 {
1128 dispatch_once_f(&_voucher_activity_heap_pred, NULL,
1129 _voucher_activity_heap_init);
1130 if (_voucher_activity_disabled()) {
1131 *length = 0;
1132 return NULL;
1133 }
1134 *length = sizeof(_voucher_activity_heap->vam_client_metadata);
1135 return _voucher_activity_heap->vam_client_metadata;
1136 }
1137
1138 DISPATCH_ALWAYS_INLINE
1139 static inline _voucher_activity_buffer_header_t
1140 _voucher_activity_heap_buffer_alloc(void)
1141 {
1142 _voucher_activity_buffer_header_t buffer = NULL;
1143 size_t index;
1144 index = _voucher_activity_bitmap_set_first_unset_bit_upto(
1145 vam_buffer_bitmap(), _voucher_heap_buffer_limit() - 1);
1146 if (index < NO_BITS_WERE_UNSET) {
1147 buffer = vam_buffer(index);
1148 }
1149 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1150 _dispatch_debug("activity heap alloc %zd (%p)", index, buffer);
1151 #endif
1152 return buffer;
1153 }
1154
1155 DISPATCH_ALWAYS_INLINE
1156 static inline void
1157 _voucher_activity_heap_buffer_free(_voucher_activity_buffer_header_t buffer)
1158 {
1159 buffer->vabh_flags = _voucher_activity_trace_flag_buffer_empty;
1160 size_t index = (size_t)((char*)buffer - (char*)_voucher_activity_heap) /
1161 _voucher_activity_buffer_size;
1162 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1163 _dispatch_debug("activity heap free %zd (%p)", index, buffer);
1164 #endif
1165 _voucher_activity_bitmap_clear_bit(vam_buffer_bitmap(), index);
1166 }
1167
1168 #define _voucher_activity_heap_can_madvise() \
1169 (PAGE_SIZE == _voucher_activity_buffer_size) // <rdar://17445544>
1170
1171 DISPATCH_ALWAYS_INLINE
1172 static inline void
1173 _voucher_activity_heap_madvise(size_t bitmap_num, unsigned int start,
1174 unsigned int len)
1175 {
1176 size_t base = bitmap_num * _voucher_activity_bits_per_bitmap_base_t;
1177 #if DISPATCH_DEBUG
1178 #if DISPATCH_VOUCHER_ACTIVITY_DEBUG
1179 _dispatch_debug("activity heap madvise %zd (%p) -> %zd (%p)", base + start,
1180 vam_buffer(base + start), base + start + len,
1181 vam_buffer(base + start + len));
1182 #endif
1183 dispatch_assert(!(len * _voucher_activity_buffer_size % vm_page_size));
1184 const uint64_t pattern = 0xFACEFACEFACEFACE;
1185 _voucher_activity_buffer_header_t buffer = vam_buffer(base + start);
1186 for (unsigned int i = 0; i < len; i++, buffer++) {
1187 memset_pattern8((char*)buffer + sizeof(buffer->vabh_flags), &pattern,
1188 _voucher_activity_buffer_size - sizeof(buffer->vabh_flags));
1189 }
1190 #endif
1191 (void)dispatch_assume_zero(madvise(vam_buffer(base + start),
1192 len * _voucher_activity_buffer_size, MADV_FREE));
1193 }
1194
1195 DISPATCH_ALWAYS_INLINE
1196 static inline void
1197 _voucher_activity_heap_madvise_contiguous(size_t bitmap_num,
1198 _voucher_activity_bitmap_base_t bits)
1199 {
1200 // TODO: x86 has fast ctz; arm has fast clz; haswell has fast ctz
1201 dispatch_assert(_voucher_activity_heap_can_madvise());
1202 if (bits == 0) {
1203 return;
1204 } else if (~bits == 0) {
1205 _voucher_activity_heap_madvise(bitmap_num, 0,
1206 _voucher_activity_bits_per_bitmap_base_t);
1207 } else while (bits != 0) {
1208 unsigned int start = (typeof(start))__builtin_ctzl(bits), len;
1209 typeof(bits) inverse = ~bits >> start;
1210 if (inverse) {
1211 len = (typeof(len))__builtin_ctzl(inverse);
1212 } else {
1213 len = _voucher_activity_bits_per_bitmap_base_t - start;
1214 }
1215 typeof(bits) mask = ((((typeof(bits))1) << len) - 1) << start;
1216 bits &= ~mask;
1217 _voucher_activity_heap_madvise(bitmap_num, start, len);
1218 }
1219 }
1220
1221 void
1222 _voucher_activity_heap_pressure_warn(void)
1223 {
1224 if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap) {
1225 return;
1226 }
1227 volatile _voucher_activity_bitmap_base_t *bitmap, *pressure_locked_bitmap;
1228 bitmap = vam_buffer_bitmap();
1229 pressure_locked_bitmap = vam_pressure_locked_bitmap();
1230
1231 // number of bitmaps needed to map the current buffer limit =
1232 // ceil(buffer limit / bits per bitmap)
1233 size_t nbuffers = _voucher_heap_buffer_limit();
1234 size_t nbitmaps_quot = nbuffers / _voucher_activity_bits_per_bitmap_base_t;
1235 size_t nbitmaps_rem = nbuffers % _voucher_activity_bits_per_bitmap_base_t;
1236 size_t nbitmaps = nbitmaps_quot + ((nbitmaps_rem == 0) ? 0 : 1);
1237
1238 for (size_t i = 0; i < nbitmaps; i++) {
1239 _voucher_activity_bitmap_base_t got_bits;
1240 got_bits = dispatch_atomic_or_orig(&bitmap[i], ~((typeof(bitmap[i]))0),
1241 relaxed);
1242 got_bits = ~got_bits; // Now 1 means 'acquired this one, madvise it'
1243 _voucher_activity_heap_madvise_contiguous(i, got_bits);
1244 pressure_locked_bitmap[i] |= got_bits;
1245 }
1246 }
1247
1248 void
1249 _voucher_activity_heap_pressure_normal(void)
1250 {
1251 if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap) {
1252 return;
1253 }
1254 volatile _voucher_activity_bitmap_base_t *bitmap, *pressure_locked_bitmap;
1255 bitmap = vam_buffer_bitmap();
1256 pressure_locked_bitmap = vam_pressure_locked_bitmap();
1257 for (size_t i = 0; i < _voucher_activity_bitmaps_per_heap; i++) {
1258 _voucher_activity_bitmap_base_t free_bits = pressure_locked_bitmap[i];
1259 pressure_locked_bitmap[i] = 0;
1260 if (free_bits != 0) {
1261 (void)dispatch_atomic_and(&bitmap[i], ~free_bits, release);
1262 }
1263 }
1264 }
1265
1266 DISPATCH_ALWAYS_INLINE
1267 static inline void
1268 _voucher_activity_buffer_init(_voucher_activity_t act,
1269 _voucher_activity_buffer_header_t buffer, bool reuse)
1270 {
1271 if (!reuse) {
1272 buffer->vabh_flags = _voucher_activity_trace_flag_buffer_header;
1273 buffer->vabh_activity_id = act->va_id;
1274 }
1275 buffer->vabh_timestamp = _voucher_activity_timestamp();
1276 buffer->vabh_next_tracepoint_idx = 1;
1277 buffer->vabh_sequence_no = dispatch_atomic_inc2o(act, va_max_sequence_no,
1278 relaxed);
1279 }
1280
1281 static _voucher_activity_buffer_header_t
1282 _voucher_activity_buffer_alloc_slow(_voucher_activity_t act,
1283 _voucher_activity_buffer_header_t current)
1284 {
1285 _voucher_activity_buffer_header_t buffer;
1286 _voucher_activity_lock_lock(va_buffers_lock(act)); // TODO: revisit locking
1287 buffer = act->va_current_buffer;
1288 if (buffer != current) goto out;
1289 buffer = TAILQ_FIRST(&act->va_buffers);
1290 if (buffer) {
1291 _voucher_activity_buffer_init(act, buffer, true);
1292 if (buffer != TAILQ_LAST(&act->va_buffers,
1293 _voucher_activity_buffer_list_s)) {
1294 TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1295 TAILQ_INSERT_TAIL(&act->va_buffers, buffer, vabh_list);
1296 }
1297 }
1298 if (!dispatch_atomic_cmpxchgv2o(act, va_current_buffer, current, buffer,
1299 &current, release)) {
1300 if (buffer) {
1301 TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1302 _voucher_activity_heap_buffer_free(buffer);
1303 }
1304 buffer = current;
1305 }
1306 out:
1307 _voucher_activity_lock_unlock(va_buffers_lock(act));
1308 _dispatch_voucher_activity_debug("buffer reuse %p", act, buffer);
1309 return buffer;
1310 }
1311
1312 static _voucher_activity_buffer_header_t
1313 _voucher_activity_buffer_alloc(_voucher_activity_t act,
1314 _voucher_activity_buffer_header_t current)
1315 {
1316 _voucher_activity_buffer_header_t buffer = NULL;
1317 if (act->va_max_sequence_no < act->va_buffer_limit) {
1318 buffer = _voucher_activity_heap_buffer_alloc();
1319 }
1320 if (!buffer) return _voucher_activity_buffer_alloc_slow(act, current);
1321 _voucher_activity_buffer_init(act, buffer, false);
1322 if (dispatch_atomic_cmpxchgv2o(act, va_current_buffer, current, buffer,
1323 &current, release)) {
1324 _voucher_activity_lock_lock(va_buffers_lock(act));
1325 TAILQ_INSERT_TAIL(&act->va_buffers, buffer, vabh_list);
1326 _voucher_activity_lock_unlock(va_buffers_lock(act));
1327 } else {
1328 _voucher_activity_heap_buffer_free(buffer);
1329 buffer = current;
1330 }
1331 _dispatch_voucher_activity_debug("buffer alloc %p", act, buffer);
1332 return buffer;
1333 }
1334
1335 #pragma mark -
1336 #pragma mark _voucher_activity_t
1337
1338 #define _voucher_activity_ordered_insert(_act, head, field) do { \
1339 typeof(_act) _vai; \
1340 TAILQ_FOREACH(_vai, (head), field) { \
1341 if (_act->va_id < _vai->va_id) break; \
1342 } \
1343 if (_vai) { \
1344 TAILQ_INSERT_BEFORE(_vai, _act, field); \
1345 } else { \
1346 TAILQ_INSERT_TAIL((head), _act, field); \
1347 } } while (0);
1348
1349 static void _voucher_activity_dispose(_voucher_activity_t act);
1350 static _voucher_activity_t _voucher_atm_activity_mark_used(
1351 _voucher_activity_t act);
1352 static void _voucher_atm_activity_mark_unused(_voucher_activity_t act);
1353 static _voucher_atm_t _voucher_atm_copy(atm_aid_t atm_id);
1354 static inline void _voucher_atm_release(_voucher_atm_t vatm);
1355 static void _voucher_atm_activity_insert(_voucher_atm_t vatm,
1356 _voucher_activity_t act);
1357 static void _voucher_atm_activity_remove(_voucher_activity_t act);
1358 static atm_aid_t _voucher_mach_voucher_get_atm_id(mach_voucher_t kv);
1359
1360 DISPATCH_ALWAYS_INLINE
1361 static inline bool
1362 _voucher_activity_copy(_voucher_activity_t act)
1363 {
1364 int use_cnt = dispatch_atomic_inc2o(act, va_use_count, relaxed);
1365 _dispatch_voucher_activity_debug("retain -> %d", act, use_cnt + 1);
1366 if (slowpath(use_cnt < 0)) {
1367 _dispatch_voucher_activity_debug("overrelease", act);
1368 DISPATCH_CRASH("Activity overrelease");
1369 }
1370 return (use_cnt == 0);
1371 }
1372
1373 DISPATCH_ALWAYS_INLINE
1374 static inline _voucher_activity_t
1375 _voucher_activity_retain(_voucher_activity_t act)
1376 {
1377 if (_voucher_activity_copy(act)) {
1378 _dispatch_voucher_activity_debug("invalid resurrection", act);
1379 DISPATCH_CRASH("Invalid activity resurrection");
1380 }
1381 return act;
1382 }
1383
1384 DISPATCH_ALWAYS_INLINE
1385 static inline void
1386 _voucher_activity_release(_voucher_activity_t act)
1387 {
1388 int use_cnt = dispatch_atomic_dec2o(act, va_use_count, relaxed);
1389 _dispatch_voucher_activity_debug("release -> %d", act, use_cnt + 1);
1390 if (fastpath(use_cnt >= 0)) {
1391 return;
1392 }
1393 if (slowpath(use_cnt < -1)) {
1394 _dispatch_voucher_activity_debug("overrelease", act);
1395 DISPATCH_CRASH("Activity overrelease");
1396 }
1397 return _voucher_atm_activity_mark_unused(act);
1398 }
1399
1400 DISPATCH_ALWAYS_INLINE
1401 static inline _voucher_activity_t
1402 _voucher_activity_atm_retain(_voucher_activity_t act)
1403 {
1404 int refcnt = dispatch_atomic_inc2o(act, va_refcnt, relaxed);
1405 _dispatch_voucher_activity_debug("atm retain -> %d", act, refcnt + 1);
1406 if (slowpath(refcnt <= 0)) {
1407 _dispatch_voucher_activity_debug("atm resurrection", act);
1408 DISPATCH_CRASH("Activity ATM resurrection");
1409 }
1410 return act;
1411 }
1412
1413 DISPATCH_ALWAYS_INLINE
1414 static inline void
1415 _voucher_activity_atm_release(_voucher_activity_t act)
1416 {
1417 int refcnt = dispatch_atomic_dec2o(act, va_refcnt, relaxed);
1418 _dispatch_voucher_activity_debug("atm release -> %d", act, refcnt + 1);
1419 if (fastpath(refcnt >= 0)) {
1420 return;
1421 }
1422 if (slowpath(refcnt < -1)) {
1423 _dispatch_voucher_activity_debug("atm overrelease", act);
1424 DISPATCH_CRASH("Activity ATM overrelease");
1425 }
1426 return _voucher_activity_dispose(act);
1427 }
1428
1429 static inline _voucher_activity_t
1430 _voucher_activity_get(voucher_t v)
1431 {
1432 _voucher_activity_t act;
1433 act = v && v->v_activity ? v->v_activity : _voucher_activity_default;
1434 return act;
1435 }
1436
1437 static _voucher_activity_t
1438 _voucher_activity_find(voucher_activity_id_t va_id, uint32_t hash)
1439 {
1440 // assumes vam_activities_lock held
1441 _voucher_activity_t act;
1442 TAILQ_FOREACH(act, vam_activities(hash), va_list){
1443 if (act->va_id == va_id) break;
1444 }
1445 return act;
1446 }
1447
1448 static _voucher_activity_t
1449 _voucher_activity_copy_from_id(voucher_activity_id_t va_id)
1450 {
1451 bool resurrect = false;
1452 uint32_t hash = VACTID_HASH(va_id);
1453 _voucher_activity_lock_lock(vam_activities_lock());
1454 _voucher_activity_t act = _voucher_activity_find(va_id, hash);
1455 if (act) {
1456 resurrect = _voucher_activity_copy(act);
1457 _dispatch_voucher_activity_debug("copy from id 0x%llx", act, va_id);
1458 }
1459 _voucher_activity_lock_unlock(vam_activities_lock());
1460 if (resurrect) return _voucher_atm_activity_mark_used(act);
1461 return act;
1462 }
1463
1464 static _voucher_activity_t
1465 _voucher_activity_try_insert(_voucher_activity_t act_new)
1466 {
1467 bool resurrect = false;
1468 voucher_activity_id_t va_id = act_new->va_id;
1469 uint32_t hash = VACTID_HASH(va_id);
1470 _voucher_activity_lock_lock(vam_activities_lock());
1471 _voucher_activity_t act = _voucher_activity_find(va_id, hash);
1472 if (act) {
1473 resurrect = _voucher_activity_copy(act);
1474 _dispatch_voucher_activity_debug("try insert: failed (%p)", act,act_new);
1475 } else {
1476 if (slowpath(_TAILQ_IS_ENQUEUED(act_new, va_list))) {
1477 _dispatch_voucher_activity_debug("corruption", act_new);
1478 DISPATCH_CRASH("Activity corruption");
1479 }
1480 TAILQ_INSERT_TAIL(vam_activities(hash), act_new, va_list);
1481 _dispatch_voucher_activity_debug("try insert: succeeded", act_new);
1482 }
1483 _voucher_activity_lock_unlock(vam_activities_lock());
1484 if (resurrect) return _voucher_atm_activity_mark_used(act);
1485 return act;
1486 }
1487
1488 static bool
1489 _voucher_activity_try_remove(_voucher_activity_t act)
1490 {
1491 bool r;
1492 voucher_activity_id_t va_id = act->va_id;
1493 uint32_t hash = VACTID_HASH(va_id);
1494 _voucher_activity_lock_lock(vam_activities_lock());
1495 if (slowpath(!va_id)) {
1496 _dispatch_voucher_activity_debug("corruption", act);
1497 DISPATCH_CRASH("Activity corruption");
1498 }
1499 if ((r = (dispatch_atomic_load2o(act, va_use_count, seq_cst) < 0 &&
1500 _TAILQ_IS_ENQUEUED(act, va_list)))) {
1501 TAILQ_REMOVE(vam_activities(hash), act, va_list);
1502 _TAILQ_MARK_NOT_ENQUEUED(act, va_list);
1503 act->va_list.tqe_next = (void*)~0ull;
1504 }
1505 _dispatch_voucher_activity_debug("try remove: %s", act, r ? "succeeded" :
1506 "failed");
1507 _voucher_activity_lock_unlock(vam_activities_lock());
1508 return r;
1509 }
1510
1511 static _voucher_activity_t
1512 _voucher_activity_create_with_atm(_voucher_atm_t vatm,
1513 voucher_activity_id_t va_id, voucher_activity_trace_id_t trace_id,
1514 uint64_t location, _voucher_activity_buffer_header_t buffer)
1515 {
1516 if (!buffer) buffer = _voucher_activity_heap_buffer_alloc();
1517 if (!buffer) {
1518 _dispatch_voucher_atm_debug("no buffer", vatm);
1519 _voucher_atm_release(vatm); // consume vatm reference
1520 return NULL;
1521 }
1522 if (!trace_id) trace_id = _voucher_activity_trace_id_release;
1523 _voucher_activity_tracepoint_t vat = (_voucher_activity_tracepoint_t)buffer;
1524 _voucher_activity_tracepoint_init_with_id(vat, trace_id, ~1ull);
1525 _voucher_activity_t act = (_voucher_activity_t)buffer;
1526 act->va_flags = _voucher_activity_trace_flag_buffer_header |
1527 _voucher_activity_trace_flag_activity |
1528 _voucher_activity_trace_flag_start |
1529 _voucher_activity_trace_flag_wide_first;
1530 act->vabh_next_tracepoint_idx = sizeof(*act)/sizeof(*vat);
1531 act->va_max_sequence_no = 0;
1532 act->va_id = va_id ? va_id : VATM_ACTID(vatm, 0);
1533 act->va_use_count = 0;
1534 act->va_buffer_limit = _voucher_activity_buffer_limit();
1535 TAILQ_INIT(&act->va_buffers);
1536 act->va_flags2 = _voucher_activity_trace_flag_activity |
1537 _voucher_activity_trace_flag_wide_second;
1538 #if __LP64__
1539 act->va_flags3 = act->va_flags2;
1540 #endif
1541 act->va_refcnt = 0;
1542 act->va_location = location;
1543 act->va_current_buffer = buffer;
1544 act->va_atm = vatm; // transfer vatm reference
1545 _voucher_activity_lock_init(va_buffers_lock(act));
1546 _TAILQ_MARK_NOT_ENQUEUED(act, va_list);
1547 _TAILQ_MARK_NOT_ENQUEUED(act, va_atm_list);
1548 _TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1549 _voucher_activity_t actx = _voucher_activity_try_insert(act);
1550 if (actx) {
1551 _voucher_activity_dispose(act);
1552 act = actx;
1553 } else {
1554 _voucher_atm_activity_insert(vatm, act);
1555 }
1556 _dispatch_voucher_activity_debug("create", act);
1557 return act;
1558 }
1559
1560 static void
1561 _voucher_activity_dispose(_voucher_activity_t act)
1562 {
1563 _dispatch_voucher_activity_debug("dispose", act);
1564 _voucher_atm_release(act->va_atm);
1565 if (slowpath(_TAILQ_IS_ENQUEUED(act, va_list))) {
1566 _dispatch_voucher_activity_debug("corruption", act);
1567 DISPATCH_CRASH("Activity corruption");
1568 }
1569 act->va_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
1570 dispatch_assert(!_TAILQ_IS_ENQUEUED(act, va_atm_list));
1571 dispatch_assert(!_TAILQ_IS_ENQUEUED(act, va_atm_used_list));
1572 _voucher_activity_buffer_header_t buffer, tmp;
1573 TAILQ_FOREACH_SAFE(buffer, &act->va_buffers, vabh_list, tmp) {
1574 _dispatch_voucher_activity_debug("buffer free %p", act, buffer);
1575 TAILQ_REMOVE(&act->va_buffers, buffer, vabh_list);
1576 _voucher_activity_heap_buffer_free(buffer);
1577 }
1578 buffer = (_voucher_activity_buffer_header_t)act;
1579 _voucher_activity_heap_buffer_free(buffer);
1580 }
1581
1582 static void
1583 _voucher_activity_collect(_voucher_activity_t act)
1584 {
1585 _dispatch_voucher_activity_debug("collect", act);
1586 if (_voucher_activity_try_remove(act)) {
1587 _voucher_atm_activity_remove(act);
1588 }
1589 }
1590
1591 static _voucher_activity_t
1592 _voucher_activity_copy_from_mach_voucher(mach_voucher_t kv,
1593 voucher_activity_id_t va_id)
1594 {
1595 dispatch_once_f(&_voucher_activity_heap_pred, NULL,
1596 _voucher_activity_heap_init);
1597 if (_voucher_activity_disabled()) return NULL;
1598 _voucher_activity_t act = NULL;
1599 if (dispatch_assume(va_id)) {
1600 if ((act = _voucher_activity_copy_from_id(va_id))) return act;
1601 }
1602 atm_aid_t atm_id = _voucher_mach_voucher_get_atm_id(kv);
1603 if (!dispatch_assume(atm_id)) return NULL;
1604 _voucher_activity_buffer_header_t buffer;
1605 buffer = _voucher_activity_heap_buffer_alloc();
1606 if (!buffer) return NULL;
1607 _dispatch_kvoucher_debug("atm copy/create from <%lld>", kv, atm_id);
1608 _voucher_atm_t vatm = _voucher_atm_copy(atm_id);
1609 if (!vatm) vatm = _voucher_atm_create(kv, atm_id);
1610 if (!vatm) {
1611 _voucher_activity_heap_buffer_free(buffer);
1612 return NULL;
1613 }
1614 if (VACTID_BASEID(va_id) != VATMID2ACTID(atm_id)) va_id = 0;
1615 // consumes vatm reference:
1616 act = _voucher_activity_create_with_atm(vatm, va_id, 0, 0, buffer);
1617 _dispatch_voucher_activity_debug("copy from kvoucher[0x%08x]", act, kv);
1618 return act;
1619 }
1620
1621 #pragma mark -
1622 #pragma mark _voucher_atm_mailbox
1623
1624 DISPATCH_ALWAYS_INLINE
1625 static inline atm_mailbox_offset_t
1626 _voucher_atm_mailbox_alloc(void)
1627 {
1628 atm_mailbox_offset_t mailbox_offset = MAILBOX_OFFSET_UNSET;
1629 size_t index;
1630 index = _voucher_activity_bitmap_set_first_unset_bit(vam_atm_mbox_bitmap());
1631 if (index < NO_BITS_WERE_UNSET) {
1632 mailbox_offset = index * sizeof(atm_mailbox_offset_t);
1633 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1634 _dispatch_debug("mailbox alloc %zd (%lld)", index, mailbox_offset);
1635 #endif
1636 }
1637 return mailbox_offset;
1638 }
1639
1640 DISPATCH_ALWAYS_INLINE
1641 static inline void
1642 _voucher_atm_mailbox_free(atm_mailbox_offset_t mailbox_offset)
1643 {
1644 if (mailbox_offset == MAILBOX_OFFSET_UNSET) return;
1645 size_t index = (size_t)mailbox_offset / sizeof(atm_mailbox_offset_t);
1646 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1647 _dispatch_debug("mailbox free %zd (%lld)", index, mailbox_offset);
1648 #endif
1649 _voucher_activity_bitmap_clear_bit(vam_atm_mbox_bitmap(), index);
1650 }
1651
1652 DISPATCH_ALWAYS_INLINE
1653 static inline bool
1654 _voucher_atm_mailbox_set(atm_mailbox_offset_t mailbox_offset,
1655 atm_subaid32_t subaid, bool max_present)
1656 {
1657 if (mailbox_offset == MAILBOX_OFFSET_UNSET) return false;
1658 char *mailbox_base = (char*)_voucher_activity_heap->vam_kernel_metadata;
1659 atm_subaid32_t *mailbox = (atm_subaid32_t*)(mailbox_base + mailbox_offset);
1660 if (max_present) mailbox++; // second atm_subaid32_t in atm_mailbox_offset_t
1661 if (*mailbox == subaid) return false;
1662 *mailbox = subaid;
1663 return true;
1664 }
1665
1666 #pragma mark -
1667 #pragma mark _voucher_atm_t
1668
1669 static bool _voucher_atm_try_remove(_voucher_atm_t vatm);
1670 static void _voucher_atm_dispose(_voucher_atm_t vatm, bool unregister);
1671 static inline void _voucher_atm_collect_if_needed(bool updated);
1672
1673 DISPATCH_ALWAYS_INLINE
1674 static inline _voucher_atm_t
1675 _voucher_atm_retain(_voucher_atm_t vatm)
1676 {
1677 // assumes vam_atms_lock or vam_base_atm_lock held
1678 int refcnt = dispatch_atomic_inc2o(vatm, vatm_refcnt, relaxed);
1679 _dispatch_voucher_atm_debug("retain -> %d", vatm, refcnt + 1);
1680 if (slowpath(refcnt < 0)) {
1681 _dispatch_voucher_atm_debug("overrelease", vatm);
1682 DISPATCH_CRASH("ATM overrelease");
1683 }
1684 return vatm;
1685 }
1686
1687 DISPATCH_ALWAYS_INLINE
1688 static inline void
1689 _voucher_atm_release(_voucher_atm_t vatm)
1690 {
1691 int refcnt = dispatch_atomic_dec2o(vatm, vatm_refcnt, relaxed);
1692 _dispatch_voucher_atm_debug("release -> %d", vatm, refcnt + 1);
1693 if (fastpath(refcnt >= 0)) {
1694 return;
1695 }
1696 if (slowpath(refcnt < -1)) {
1697 _dispatch_voucher_atm_debug("overrelease", vatm);
1698 DISPATCH_CRASH("ATM overrelease");
1699 }
1700 if (_voucher_atm_try_remove(vatm)) {
1701 _voucher_atm_dispose(vatm, true);
1702 }
1703 }
1704
1705 static _voucher_atm_t
1706 _voucher_atm_find(atm_aid_t atm_id, uint32_t hash)
1707 {
1708 // assumes vam_atms_lock held
1709 _voucher_atm_t vatm;
1710 TAILQ_FOREACH(vatm, vam_atms(hash), vatm_list){
1711 if (vatm->vatm_id == atm_id) break;
1712 }
1713 return vatm;
1714 }
1715
1716 static _voucher_atm_t
1717 _voucher_atm_copy(atm_aid_t atm_id)
1718 {
1719 uint32_t hash = VATMID_HASH(atm_id);
1720 _voucher_activity_lock_lock(vam_atms_lock());
1721 _voucher_atm_t vatm = _voucher_atm_find(atm_id, hash);
1722 if (vatm) {
1723 _voucher_atm_retain(vatm);
1724 _dispatch_voucher_atm_debug("copy", vatm);
1725 }
1726 _voucher_activity_lock_unlock(vam_atms_lock());
1727 return vatm;
1728 }
1729
1730 static _voucher_atm_t
1731 _voucher_atm_try_insert(_voucher_atm_t vatm_new)
1732 {
1733 atm_aid_t atm_id = vatm_new->vatm_id;
1734 uint32_t hash = VATMID_HASH(atm_id);
1735 _voucher_activity_lock_lock(vam_atms_lock());
1736 _voucher_atm_t vatm = _voucher_atm_find(atm_id, hash);
1737 if (vatm) {
1738 _voucher_atm_retain(vatm);
1739 _dispatch_voucher_atm_debug("try insert: failed (%p)", vatm, vatm_new);
1740 } else {
1741 if (slowpath(_TAILQ_IS_ENQUEUED(vatm_new, vatm_list))) {
1742 _dispatch_voucher_atm_debug("corruption", vatm_new);
1743 DISPATCH_CRASH("ATM corruption");
1744 }
1745 TAILQ_INSERT_TAIL(vam_atms(hash), vatm_new, vatm_list);
1746 _dispatch_voucher_atm_debug("try insert: succeeded", vatm_new);
1747 }
1748 _voucher_activity_lock_unlock(vam_atms_lock());
1749 return vatm;
1750 }
1751
1752 static bool
1753 _voucher_atm_try_remove(_voucher_atm_t vatm)
1754 {
1755 bool r;
1756 atm_aid_t atm_id = vatm->vatm_id;
1757 uint32_t hash = VATMID_HASH(atm_id);
1758 _voucher_activity_lock_lock(vam_atms_lock());
1759 if (slowpath(!atm_id)) {
1760 _dispatch_voucher_atm_debug("corruption", vatm);
1761 DISPATCH_CRASH("ATM corruption");
1762 }
1763 if ((r = (dispatch_atomic_load2o(vatm, vatm_refcnt, seq_cst) < 0 &&
1764 _TAILQ_IS_ENQUEUED(vatm, vatm_list)))) {
1765 TAILQ_REMOVE(vam_atms(hash), vatm, vatm_list);
1766 _TAILQ_MARK_NOT_ENQUEUED(vatm, vatm_list);
1767 vatm->vatm_list.tqe_next = (void*)~0ull;
1768 }
1769 _dispatch_voucher_atm_debug("try remove: %s", vatm, r ? "succeeded" :
1770 "failed");
1771 _voucher_activity_lock_unlock(vam_atms_lock());
1772 return r;
1773 }
1774
1775 static bool
1776 _voucher_atm_update_mailbox(_voucher_atm_t vatm)
1777 {
1778 // Update kernel mailbox with largest allocated subaid for this atm_id
1779 // assumes atm_activities_lock held
1780 _voucher_activity_t act = TAILQ_LAST(vatm_activities(vatm),
1781 _voucher_atm_activities_s);
1782 atm_subaid32_t subaid = act ? VACTID_SUBID(act->va_id) : 0;
1783 bool r = _voucher_atm_mailbox_set(vatm->vatm_mailbox_offset, subaid, true);
1784 if (r) {
1785 _dispatch_voucher_atm_debug("update max-present subaid 0x%x", vatm,
1786 subaid);
1787 }
1788 return r;
1789 }
1790
1791 static bool
1792 _voucher_atm_update_used_mailbox(_voucher_atm_t vatm)
1793 {
1794 // Update kernel mailbox with smallest in-use subaid for this atm_id
1795 // assumes atm_activities_lock held
1796 _voucher_activity_t act = TAILQ_FIRST(vatm_used_activities(vatm));
1797 atm_subaid32_t subaid = act ? VACTID_SUBID(act->va_id) : ATM_SUBAID32_MAX;
1798 bool r = _voucher_atm_mailbox_set(vatm->vatm_mailbox_offset, subaid, false);
1799 if (r) {
1800 _dispatch_voucher_atm_debug("update min-used subaid 0x%x", vatm,
1801 subaid);
1802 }
1803 return r;
1804 }
1805
1806 static void
1807 _voucher_atm_activity_insert(_voucher_atm_t vatm, _voucher_activity_t act)
1808 {
1809 _voucher_activity_lock_lock(vatm_activities_lock(vatm));
1810 if (!_TAILQ_IS_ENQUEUED(act, va_atm_list)) {
1811 _voucher_activity_ordered_insert(act, vatm_activities(vatm),
1812 va_atm_list);
1813 _voucher_atm_update_mailbox(vatm);
1814 }
1815 if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1816 _voucher_activity_ordered_insert(act, vatm_used_activities(vatm),
1817 va_atm_used_list);
1818 _voucher_atm_update_used_mailbox(vatm);
1819 }
1820 _dispatch_voucher_activity_debug("atm insert", act);
1821 _voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1822 }
1823
1824 static void
1825 _voucher_atm_activity_remove(_voucher_activity_t act)
1826 {
1827 _voucher_atm_t vatm = act->va_atm;
1828 _voucher_activity_lock_lock(vatm_activities_lock(vatm));
1829 _dispatch_voucher_activity_debug("atm remove", act);
1830 if (_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1831 TAILQ_REMOVE(vatm_activities(vatm), act, va_atm_used_list);
1832 _TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1833 _voucher_atm_update_used_mailbox(vatm);
1834 }
1835 if (_TAILQ_IS_ENQUEUED(act, va_atm_list)) {
1836 TAILQ_REMOVE(vatm_activities(vatm), act, va_atm_list);
1837 _TAILQ_MARK_NOT_ENQUEUED(act, va_atm_list);
1838 _voucher_atm_update_mailbox(vatm);
1839 // Balance initial creation refcnt. Caller must hold additional
1840 // reference to ensure this does not release vatm before the unlock,
1841 // see _voucher_atm_activity_collect
1842 _voucher_activity_atm_release(act);
1843 }
1844 _voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1845 }
1846
1847 static _voucher_activity_t
1848 _voucher_atm_activity_mark_used(_voucher_activity_t act)
1849 {
1850 _voucher_atm_t vatm = act->va_atm;
1851 _voucher_activity_lock_lock(vatm_activities_lock(vatm));
1852 if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1853 _voucher_activity_ordered_insert(act, vatm_used_activities(vatm),
1854 va_atm_used_list);
1855 _voucher_atm_update_used_mailbox(vatm);
1856 _dispatch_voucher_activity_debug("mark used", act);
1857 }
1858 _voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1859 return act;
1860 }
1861
1862 static void
1863 _voucher_atm_activity_mark_unused(_voucher_activity_t act)
1864 {
1865 bool atm_collect = false, updated = false;
1866 _voucher_atm_t vatm = act->va_atm;
1867 _voucher_activity_lock_lock(vatm_activities_lock(vatm));
1868 if (_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1869 _dispatch_voucher_activity_debug("mark unused", act);
1870 TAILQ_REMOVE(&vatm->vatm_used_activities, act, va_atm_used_list);
1871 _TAILQ_MARK_NOT_ENQUEUED(act, va_atm_used_list);
1872 atm_collect = true;
1873 _voucher_atm_retain(vatm);
1874 updated = _voucher_atm_update_used_mailbox(vatm);
1875 }
1876 _voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1877 if (atm_collect) {
1878 _voucher_atm_release(vatm);
1879 _voucher_atm_collect_if_needed(updated);
1880 }
1881 }
1882
1883 static void
1884 _voucher_atm_activity_collect(_voucher_atm_t vatm, atm_subaid32_t min_subaid)
1885 {
1886 _dispatch_voucher_atm_debug("collect min subaid 0x%x", vatm, min_subaid);
1887 voucher_activity_id_t min_va_id = VATM_ACTID(vatm, min_subaid);
1888 _voucher_activity_t act;
1889 do {
1890 _voucher_activity_lock_lock(vatm_activities_lock(vatm));
1891 TAILQ_FOREACH(act, vatm_activities(vatm), va_atm_list) {
1892 if (act->va_id >= min_va_id) {
1893 act = NULL;
1894 break;
1895 }
1896 if (!_TAILQ_IS_ENQUEUED(act, va_atm_used_list)) {
1897 _voucher_activity_atm_retain(act);
1898 break;
1899 }
1900 }
1901 _voucher_activity_lock_unlock(vatm_activities_lock(vatm));
1902 if (act) {
1903 _voucher_activity_collect(act);
1904 _voucher_activity_atm_release(act);
1905 }
1906 } while (act);
1907 }
1908
1909 DISPATCH_NOINLINE
1910 static void
1911 _voucher_atm_collect(void)
1912 {
1913 _voucher_atm_t vatms[_voucher_atm_mailboxes], vatm;
1914 atm_aid_t aids[_voucher_atm_mailboxes];
1915 mach_atm_subaid_t subaids[_voucher_atm_mailboxes];
1916 uint32_t i, a = 0, s;
1917
1918 _voucher_activity_lock_lock(vam_atms_lock());
1919 for (i = 0; i < _voucher_activity_hash_size; i++) {
1920 TAILQ_FOREACH(vatm, vam_atms(i), vatm_list){
1921 if (vatm == _voucher_activity_heap->vam_default_activity_atm ||
1922 vatm->vatm_mailbox_offset == MAILBOX_OFFSET_UNSET) continue;
1923 _dispatch_voucher_atm_debug("find min subaid", vatm);
1924 vatms[a] = _voucher_atm_retain(vatm);
1925 aids[a] = vatm->vatm_id;
1926 if (++a == _voucher_atm_mailboxes) goto out;
1927 }
1928 }
1929 out:
1930 _voucher_activity_lock_unlock(vam_atms_lock());
1931 if (!a) return;
1932 kern_return_t kr;
1933 mach_voucher_t kv = vatms[0]->vatm_kvoucher;
1934 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&aids;
1935 mach_voucher_attr_content_size_t kvc_in_size = sizeof(atm_aid_t) * a;
1936 mach_voucher_attr_content_t kvc_out = (mach_voucher_attr_content_t)&subaids;
1937 mach_voucher_attr_content_size_t kvc_out_size = sizeof(mach_atm_subaid_t)*a;
1938 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
1939 ATM_FIND_MIN_SUB_AID, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
1940 DISPATCH_VERIFY_MIG(kr);
1941 (void)dispatch_assume_zero(kr);
1942 s = kvc_out_size / sizeof(mach_atm_subaid_t);
1943 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1944 _dispatch_debug("found min subaids (%u out of %u)", s, a);
1945 #endif
1946 for (i = 0; i < a; i++) {
1947 if (i < s) _voucher_atm_activity_collect(vatms[i],
1948 (atm_subaid32_t)subaids[i]);
1949 _voucher_atm_release(vatms[i]);
1950 }
1951 }
1952
1953 static inline void
1954 _voucher_atm_collect_if_needed(bool updated)
1955 {
1956 long level;
1957 if (updated) {
1958 level = dispatch_atomic_add(&_voucher_atm_collect_level, 2ul, relaxed);
1959 } else {
1960 level = _voucher_atm_collect_level;
1961 if (!level) return;
1962 }
1963 if (level & 1 || level <= _voucher_atm_collect_threshold) return;
1964 if (!dispatch_atomic_cmpxchg(&_voucher_atm_collect_level, level, level + 1,
1965 acquire)) return;
1966 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1967 _dispatch_debug("atm collect: reached level %ld", level/2);
1968 #endif
1969 if (slowpath(level < 0)) {
1970 DISPATCH_CRASH("ATM collection level corruption");
1971 }
1972 _voucher_atm_collect();
1973 dispatch_atomic_sub(&_voucher_atm_collect_level, level + 1, release);
1974 }
1975
1976 DISPATCH_NOINLINE
1977 static void
1978 _voucher_atm_fault(mach_voucher_attr_command_t kvc_cmd)
1979 {
1980 _voucher_activity_t act = _voucher_activity_get(_voucher_get());
1981 mach_voucher_t kv = _voucher_activity_get_atm_mach_voucher(act);
1982 if (!kv) return;
1983
1984 kern_return_t kr;
1985 mach_atm_subaid_t subaid = VACTID_SUBID(act->va_id);
1986 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&subaid;
1987 mach_voucher_attr_content_size_t kvc_in_size = sizeof(mach_atm_subaid_t);
1988 mach_voucher_attr_content_t kvc_out = (mach_voucher_attr_content_t)&subaid;
1989 mach_voucher_attr_content_size_t kvc_out_size = sizeof(mach_atm_subaid_t);
1990 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
1991 kvc_cmd, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
1992 DISPATCH_VERIFY_MIG(kr);
1993 (void)dispatch_assume_zero(kr);
1994 }
1995
1996 static atm_aid_t
1997 _voucher_mach_voucher_get_atm_id(mach_voucher_t kv)
1998 {
1999 kern_return_t kr;
2000 atm_aid_t atm_id = 0;
2001 mach_voucher_attr_content_t kvc = (mach_voucher_attr_content_t)&atm_id;
2002 mach_voucher_attr_content_size_t kvc_size = sizeof(atm_id);
2003 kr = mach_voucher_extract_attr_content(kv, MACH_VOUCHER_ATTR_KEY_ATM, kvc,
2004 &kvc_size);
2005 DISPATCH_VERIFY_MIG(kr);
2006 (void)dispatch_assume_zero(kr);
2007 return atm_id;
2008 }
2009
2010 static mach_voucher_t
2011 _voucher_atm_mach_voucher_create(atm_aid_t *atm_id_ptr)
2012 {
2013 kern_return_t kr;
2014 mach_voucher_t kv;
2015 static const mach_voucher_attr_recipe_data_t atm_create_recipe = {
2016 .key = MACH_VOUCHER_ATTR_KEY_ATM,
2017 .command = MACH_VOUCHER_ATTR_ATM_CREATE,
2018 };
2019 kr = _voucher_create_mach_voucher(&atm_create_recipe,
2020 sizeof(atm_create_recipe), &kv);
2021 if (dispatch_assume_zero(kr)) {
2022 DISPATCH_CLIENT_CRASH("Could not create ATM mach voucher");
2023 }
2024 atm_aid_t atm_id = _voucher_mach_voucher_get_atm_id(kv);
2025 if (!dispatch_assume(atm_id)) {
2026 DISPATCH_CLIENT_CRASH("Could not extract ATM ID");
2027 }
2028 _dispatch_kvoucher_debug("atm create <%lld>", kv, atm_id);
2029 *atm_id_ptr = atm_id;
2030 return kv;
2031 }
2032
2033 static void
2034 _voucher_atm_mailbox_mach_voucher_register(_voucher_atm_t vatm,
2035 mach_voucher_t kv)
2036 {
2037 _dispatch_voucher_atm_debug("mailbox register %lld with kvoucher[0x%08x]",
2038 vatm, vatm->vatm_mailbox_offset, kv);
2039 kern_return_t kr;
2040 mach_voucher_t akv;
2041 atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2042 mach_voucher_attr_recipe_t vr;
2043 size_t vr_size;
2044 static const mach_voucher_attr_recipe_data_t atm_register_recipe = {
2045 .key = MACH_VOUCHER_ATTR_KEY_ATM,
2046 .command = MACH_VOUCHER_ATTR_ATM_REGISTER,
2047 .content_size = sizeof(offset),
2048 };
2049 vr_size = sizeof(atm_register_recipe) + atm_register_recipe.content_size;
2050 vr = alloca(vr_size);
2051 *vr = atm_register_recipe;
2052 vr->previous_voucher = kv;
2053 memcpy(&vr->content, &offset, sizeof(offset));
2054 kr = _voucher_create_mach_voucher(vr, vr_size, &akv);
2055 if (dispatch_assume_zero(kr)) {
2056 DISPATCH_CLIENT_CRASH("Could not register ATM ID");
2057 }
2058 if (!vatm->vatm_kvoucher) {
2059 vatm->vatm_kvoucher = akv;
2060 } else {
2061 #if !RDAR_17510224
2062 if (akv != vatm->vatm_kvoucher) {
2063 DISPATCH_CRASH("Unexpected mach voucher returned by ATM ID "
2064 "registration");
2065 }
2066 _voucher_dealloc_mach_voucher(akv);
2067 #else
2068 DISPATCH_CRASH("Registered invalid ATM object");
2069 #endif
2070 }
2071 _dispatch_voucher_atm_debug("mailbox registered %lld", vatm,
2072 vatm->vatm_mailbox_offset);
2073 }
2074
2075 static void
2076 _voucher_atm_mailbox_register(_voucher_atm_t vatm)
2077 {
2078 mach_voucher_t kv = vatm->vatm_kvoucher;
2079 if (!kv) return;
2080 #if !RDAR_17510224
2081 _voucher_atm_mailbox_mach_voucher_register(vatm, kv);
2082 #else // RDAR_17510224
2083 _dispatch_voucher_atm_debug("mailbox register %lld", vatm,
2084 vatm->vatm_mailbox_offset);
2085 kern_return_t kr;
2086 atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2087 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&offset;
2088 mach_voucher_attr_content_size_t kvc_in_size = sizeof(offset);
2089 mach_voucher_attr_content_t kvc_out = NULL;
2090 mach_voucher_attr_content_size_t kvc_out_size = 0;
2091 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
2092 ATM_ACTION_REGISTER, kvc_in, kvc_in_size, kvc_out,
2093 &kvc_out_size);
2094 DISPATCH_VERIFY_MIG(kr);
2095 if (dispatch_assume_zero(kr)) {
2096 DISPATCH_CLIENT_CRASH("Could not register ATM ID");
2097 }
2098 _dispatch_voucher_atm_debug("mailbox registered %lld", vatm,
2099 vatm->vatm_mailbox_offset);
2100 #endif // RDAR_17510224
2101 }
2102
2103 static bool
2104 _voucher_atm_mailbox_unregister(_voucher_atm_t vatm)
2105 {
2106 if (vatm->vatm_mailbox_offset == MAILBOX_OFFSET_UNSET) return false;
2107 _dispatch_voucher_atm_debug("mailbox unregister %lld", vatm,
2108 vatm->vatm_mailbox_offset);
2109 mach_voucher_t kv = vatm->vatm_kvoucher;
2110 dispatch_assert(kv);
2111 kern_return_t kr;
2112 atm_mailbox_offset_t offset = vatm->vatm_mailbox_offset;
2113 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&offset;
2114 mach_voucher_attr_content_size_t kvc_in_size = sizeof(offset);
2115 mach_voucher_attr_content_t kvc_out = NULL;
2116 mach_voucher_attr_content_size_t kvc_out_size = 0;
2117 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_ATM,
2118 ATM_ACTION_UNREGISTER, kvc_in, kvc_in_size, kvc_out, &kvc_out_size);
2119 DISPATCH_VERIFY_MIG(kr);
2120 if (kr && kr != KERN_INVALID_VALUE) {
2121 (void)dispatch_assume_zero(kr);
2122 DISPATCH_CLIENT_CRASH("Could not unregister ATM ID");
2123 }
2124 _dispatch_voucher_atm_debug("mailbox unregistered %lld", vatm,
2125 vatm->vatm_mailbox_offset);
2126 return true;
2127 }
2128
2129 static _voucher_atm_t
2130 _voucher_atm_create(mach_voucher_t kv, atm_aid_t atm_id)
2131 {
2132 atm_mailbox_offset_t mailbox_offset = _voucher_atm_mailbox_alloc();
2133 if (kv && mailbox_offset == MAILBOX_OFFSET_UNSET) return NULL;
2134 _voucher_atm_t vatm = _dispatch_calloc(1ul, sizeof(struct _voucher_atm_s));
2135 if (!kv) {
2136 kv = _voucher_atm_mach_voucher_create(&atm_id);
2137 if (mailbox_offset == MAILBOX_OFFSET_UNSET) {
2138 _voucher_dealloc_mach_voucher(kv);
2139 } else {
2140 vatm->vatm_kvoucher = kv;
2141 }
2142 kv = MACH_VOUCHER_NULL;
2143 }
2144 vatm->vatm_id = atm_id;
2145 vatm->vatm_mailbox_offset = mailbox_offset;
2146 _voucher_activity_lock_init(vatm_activities_lock(vatm));
2147 TAILQ_INIT(&vatm->vatm_activities);
2148 TAILQ_INIT(&vatm->vatm_used_activities);
2149 _voucher_atm_mailbox_set(mailbox_offset, 0, true);
2150 _voucher_atm_mailbox_set(mailbox_offset, ATM_SUBAID32_MAX, false);
2151 _voucher_atm_t vatmx = _voucher_atm_try_insert(vatm);
2152 if (vatmx) {
2153 _voucher_atm_dispose(vatm, false);
2154 vatm = vatmx;
2155 } else if (kv) {
2156 _voucher_atm_mailbox_mach_voucher_register(vatm, kv);
2157 } else {
2158 _voucher_atm_mailbox_register(vatm);
2159 }
2160 _dispatch_voucher_atm_debug("create with kvoucher[0x%08x]", vatm, kv);
2161 return vatm;
2162 }
2163
2164 static void
2165 _voucher_atm_dispose(_voucher_atm_t vatm, bool unregister)
2166 {
2167 _dispatch_voucher_atm_debug("dispose", vatm);
2168 dispatch_assert(TAILQ_EMPTY(&vatm->vatm_activities));
2169 dispatch_assert(TAILQ_EMPTY(&vatm->vatm_used_activities));
2170 if (slowpath(_TAILQ_IS_ENQUEUED(vatm, vatm_list))) {
2171 _dispatch_voucher_atm_debug("corruption", vatm);
2172 DISPATCH_CRASH("ATM corruption");
2173 }
2174 vatm->vatm_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
2175 bool free_mailbox = (vatm->vatm_mailbox_offset != MAILBOX_OFFSET_UNSET);
2176 if (vatm->vatm_kvoucher) {
2177 if (unregister) free_mailbox = _voucher_atm_mailbox_unregister(vatm);
2178 _voucher_dealloc_mach_voucher(vatm->vatm_kvoucher);
2179 vatm->vatm_kvoucher = MACH_VOUCHER_NULL;
2180 }
2181 if (free_mailbox) {
2182 _voucher_atm_mailbox_free(vatm->vatm_mailbox_offset);
2183 vatm->vatm_mailbox_offset = MAILBOX_OFFSET_UNSET;
2184 }
2185 free(vatm);
2186 }
2187
2188 static inline mach_voucher_t
2189 _voucher_activity_get_atm_mach_voucher(_voucher_activity_t act)
2190 {
2191 mach_voucher_t kv;
2192 kv = act && act->va_atm ? act->va_atm->vatm_kvoucher : MACH_VOUCHER_NULL;
2193 return kv;
2194 }
2195
2196 DISPATCH_NOINLINE
2197 static _voucher_atm_t
2198 _voucher_atm_base_copy_and_activity_id_make(voucher_activity_id_t *va_id_ptr)
2199 {
2200 _voucher_atm_subid_t subid;
2201 _voucher_atm_t vatm, vatm_old = NULL, vatm_new = NULL;
2202 if (_voucher_activity_heap->vam_base_atm_subid_max == 1) {
2203 vatm = _voucher_atm_create(0, 0);
2204 subid = 1;
2205 goto out;
2206 }
2207 _voucher_activity_lock_lock(vam_base_atm_lock());
2208 vatm = _voucher_activity_heap->vam_base_atm;
2209 retry:
2210 _voucher_atm_retain(vatm);
2211 subid = _voucher_activity_heap->vam_base_atm_subid;
2212 if (subid++ >= _voucher_activity_heap->vam_base_atm_subid_max) {
2213 _voucher_activity_lock_unlock(vam_base_atm_lock());
2214 if (!vatm_new) vatm_new = _voucher_atm_create(0, 0);
2215 _voucher_activity_lock_lock(vam_base_atm_lock());
2216 _voucher_atm_release(vatm);
2217 vatm_old = vatm;
2218 vatm = _voucher_activity_heap->vam_base_atm;
2219 if (vatm != vatm_old) {
2220 vatm_old = NULL;
2221 goto retry;
2222 }
2223 _voucher_activity_heap->vam_base_atm = vatm = vatm_new;
2224 _voucher_activity_heap->vam_base_atm_subid = subid = 1;
2225 vatm_new = NULL;
2226 _voucher_atm_retain(vatm);
2227 _dispatch_voucher_atm_debug("base replace", vatm);
2228 } else {
2229 _voucher_activity_heap->vam_base_atm_subid = subid;
2230 _dispatch_voucher_atm_debug("base copy", vatm);
2231 }
2232 _voucher_activity_lock_unlock(vam_base_atm_lock());
2233 if (vatm_old) _voucher_atm_release(vatm_old);
2234 if (vatm_new) _voucher_atm_release(vatm_new);
2235 out:
2236 *va_id_ptr = VATM_ACTID(vatm, subid);
2237 return vatm;
2238 }
2239
2240 static voucher_activity_id_t
2241 _voucher_atm_nested_atm_id_make(void)
2242 {
2243 atm_aid_t atm_id;
2244 mach_voucher_t kv = _voucher_atm_mach_voucher_create(&atm_id);
2245 _voucher_dealloc_mach_voucher(kv); // just need the unique ID
2246 return VATMID2ACTID(atm_id);
2247 }
2248
2249 static voucher_activity_id_t
2250 _voucher_atm_nested_activity_id_make(void)
2251 {
2252 voucher_activity_id_t va_id, va_id_old, va_id_new;
2253 _voucher_atm_subid_t subid;
2254 _voucher_activity_lock_lock(vam_nested_atm_lock());
2255 va_id = _voucher_activity_heap->vam_nested_atm_id;
2256 retry:
2257 subid = _voucher_activity_heap->vam_nested_atm_subid;
2258 if (subid++ >= VATM_SUBID_MAX) {
2259 _voucher_activity_lock_unlock(vam_nested_atm_lock());
2260 va_id_new = _voucher_atm_nested_atm_id_make();
2261 va_id_old = va_id;
2262 _voucher_activity_lock_lock(vam_nested_atm_lock());
2263 va_id = _voucher_activity_heap->vam_nested_atm_id;
2264 if (va_id != va_id_old) goto retry;
2265 _voucher_activity_heap->vam_nested_atm_id = va_id = va_id_new;
2266 subid = 1;
2267 }
2268 _voucher_activity_heap->vam_nested_atm_subid = subid;
2269 _voucher_activity_lock_unlock(vam_nested_atm_lock());
2270 return va_id + subid;
2271 }
2272
2273 #pragma mark -
2274 #pragma mark voucher_activity_id_t
2275
2276 voucher_activity_id_t
2277 voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,
2278 uint64_t location, voucher_activity_flag_t flags)
2279 {
2280 dispatch_once_f(&_voucher_activity_heap_pred, NULL,
2281 _voucher_activity_heap_init);
2282 if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2283 voucher_activity_id_t va_id = 0, va_base_id = 0;
2284 _voucher_atm_t vatm = NULL;
2285 _voucher_activity_t act = NULL;
2286 _voucher_activity_tracepoint_t vat = NULL;
2287 unsigned int activities = 1, oactivities = 0;
2288 voucher_t ov = _voucher_get();
2289 if (!(flags & voucher_activity_flag_force) && ov && ov->v_activities) {
2290 oactivities = ov->v_activities;
2291 activities += oactivities;
2292 if (activities > _voucher_max_activities) {
2293 va_id = _voucher_atm_nested_activity_id_make();
2294 goto out;
2295 }
2296 }
2297 if (activities == 1) {
2298 vatm = _voucher_atm_base_copy_and_activity_id_make(&va_id);
2299 if (vatm->vatm_kvoucher) {
2300 // consumes vatm reference:
2301 act = _voucher_activity_create_with_atm(vatm, va_id, trace_id,
2302 location, NULL);
2303 vat = (_voucher_activity_tracepoint_t)act;
2304 } else {
2305 _voucher_atm_release(vatm);
2306 }
2307 if (!act) {
2308 activities++;
2309 // default to _voucher_activity_default base activity
2310 va_base_id = _voucher_activity_default->va_id;
2311 }
2312 }
2313 pthread_priority_t priority = _voucher_get_priority(ov);
2314 mach_voucher_attr_recipe_size_t extra = ov ? _voucher_extra_size(ov) : 0;
2315 voucher_t v = _voucher_alloc(activities, priority, extra);
2316 if (extra) {
2317 memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov), extra);
2318 }
2319 if (ov && ov->v_kvoucher) {
2320 voucher_t kvb = ov->v_kvbase ? ov->v_kvbase : ov;
2321 v->v_kvbase = _voucher_retain(kvb);
2322 v->v_kvoucher = kvb->v_kvoucher;
2323 }
2324 voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2325 if (oactivities) {
2326 memcpy(activity_ids, _voucher_activity_ids(ov),
2327 oactivities * sizeof(voucher_activity_id_t));
2328 }
2329 if (!va_id) {
2330 va_id = _voucher_atm_nested_activity_id_make();
2331 if (ov && ov->v_activity) {
2332 act = _voucher_activity_retain(ov->v_activity);
2333 }
2334 }
2335 if (va_base_id) activity_ids[0] = va_base_id;
2336 activity_ids[activities-1] = va_id;
2337 v->v_activity = act;
2338 _voucher_swap(ov, v);
2339 if (vat) return va_id; // new _voucher_activity_s contains trace info
2340 out:
2341 vat = _voucher_activity_trace_with_id(trace_id);
2342 if (vat) {
2343 vat->vat_flags |= _voucher_activity_trace_flag_activity |
2344 _voucher_activity_trace_flag_start;
2345 vat->vat_data[0] = va_id;
2346 }
2347 return va_id;
2348 }
2349
2350 voucher_activity_id_t
2351 voucher_activity_start(voucher_activity_trace_id_t trace_id,
2352 voucher_activity_flag_t flags)
2353 {
2354 return voucher_activity_start_with_location(trace_id, 0, flags);
2355 }
2356
2357 void
2358 voucher_activity_end(voucher_activity_id_t va_id)
2359 {
2360 if (!va_id) return;
2361 _voucher_activity_tracepoint_t vat;
2362 vat = _voucher_activity_trace_with_id(_voucher_activity_trace_id_release);
2363 if (vat) {
2364 vat->vat_flags |= _voucher_activity_trace_flag_activity |
2365 _voucher_activity_trace_flag_end;
2366 vat->vat_data[0] = va_id;
2367 }
2368 voucher_t v = _voucher_get();
2369 if (!v) return;
2370 unsigned int activities = v->v_activities, act_idx = activities;
2371 voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2372 while (act_idx) {
2373 if (activity_ids[act_idx-1] == va_id) break;
2374 act_idx--;
2375 }
2376 if (!act_idx) return; // activity_id not found
2377 pthread_priority_t priority = _voucher_get_priority(v);
2378 mach_voucher_attr_recipe_size_t extra = _voucher_extra_size(v);
2379 voucher_t nv = NULL;
2380 if (act_idx > 1 || activities == 1) --activities;
2381 if (priority || activities || extra || v->v_kvoucher) {
2382 nv = _voucher_alloc(activities, priority, extra);
2383 if (extra) {
2384 memcpy(_voucher_extra_recipes(nv), _voucher_extra_recipes(v),extra);
2385 }
2386 }
2387 if (v->v_kvoucher) {
2388 voucher_t kvb = v->v_kvbase ? v->v_kvbase : v;
2389 nv->v_kvbase = _voucher_retain(kvb);
2390 nv->v_kvoucher = kvb->v_kvoucher;
2391 }
2392 bool atm_collect = !activities;
2393 if (activities) {
2394 voucher_activity_id_t *new_activity_ids = _voucher_activity_ids(nv);
2395 if (act_idx == 1 && _voucher_activity_default) {
2396 atm_collect = true;
2397 // default to _voucher_activity_default base activity
2398 new_activity_ids[0] = _voucher_activity_default->va_id;
2399 memcpy(&new_activity_ids[1], &activity_ids[1],
2400 (activities - 1) * sizeof(voucher_activity_id_t));
2401 } else {
2402 if (v->v_activity) {
2403 nv->v_activity = _voucher_activity_retain(v->v_activity);
2404 }
2405 memcpy(new_activity_ids, activity_ids,
2406 --act_idx * sizeof(voucher_activity_id_t));
2407 if (act_idx < activities) {
2408 memcpy(&new_activity_ids[act_idx], &activity_ids[act_idx+1],
2409 (activities - act_idx) * sizeof(voucher_activity_id_t));
2410 }
2411 }
2412 }
2413 _voucher_swap(v, nv);
2414 }
2415
2416 unsigned int
2417 voucher_get_activities(voucher_activity_id_t *entries, unsigned int *count)
2418 {
2419 voucher_t v = _voucher_get();
2420 if (!v || !count) return 0;
2421 unsigned int activities = v->v_activities;
2422 if (*count < activities) activities = *count;
2423 *count = v->v_activities;
2424 voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2425 if (activities && entries) {
2426 memcpy(entries, activity_ids, activities *
2427 sizeof(voucher_activity_id_t));
2428 }
2429 return activities;
2430 }
2431
2432 uint8_t
2433 voucher_activity_get_namespace(void)
2434 {
2435 voucher_t v = _voucher_get();
2436 if (!v || !v->v_activity) return 0;
2437 return v->v_activity->va_namespace;
2438 }
2439
2440 DISPATCH_NOINLINE
2441 _voucher_activity_tracepoint_t
2442 _voucher_activity_tracepoint_get_slow(unsigned int slots)
2443 {
2444 _voucher_activity_t act;
2445 _voucher_activity_buffer_header_t vab;
2446 _voucher_activity_tracepoint_t vat = NULL;
2447 voucher_t v = _voucher_get();
2448 if (v && v->v_activity) {
2449 act = v->v_activity;
2450 } else {
2451 dispatch_once_f(&_voucher_activity_heap_pred, NULL,
2452 _voucher_activity_heap_init);
2453 if (_voucher_activity_disabled()) return NULL;
2454 act = _voucher_activity_default;
2455 }
2456 vab = act->va_current_buffer;
2457 if (vab && vab->vabh_next_tracepoint_idx <=
2458 _voucher_activity_tracepoints_per_buffer) {
2459 goto retry; // another slowpath raced us
2460 }
2461 do {
2462 vab = _voucher_activity_buffer_alloc(act, vab);
2463 if (!vab) break;
2464 retry:
2465 vat = _voucher_activity_buffer_tracepoint_get(vab, slots);
2466 } while (!vat);
2467 return vat;
2468 }
2469
2470 static inline void
2471 _voucher_activity_trace_fault(voucher_activity_trace_id_t trace_id)
2472 {
2473 if (!slowpath(_voucher_activity_trace_id_is_subtype(trace_id, error))) {
2474 return;
2475 }
2476 mach_voucher_attr_command_t atm_cmd = ATM_ACTION_COLLECT;
2477 if (_voucher_activity_trace_id_is_subtype(trace_id, fault)) {
2478 atm_cmd = ATM_ACTION_LOGFAIL;
2479 }
2480 return _voucher_atm_fault(atm_cmd);
2481 }
2482
2483 uint64_t
2484 voucher_activity_trace(voucher_activity_trace_id_t trace_id, uint64_t location,
2485 void *buffer, size_t length)
2486 {
2487 if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2488 _voucher_activity_tracepoint_t vat;
2489 const unsigned int slots = length <= sizeof(vat->vat_data) ? 1 : 2;
2490 vat = _voucher_activity_tracepoint_get(slots);
2491 if (!vat) vat = _voucher_activity_tracepoint_get_slow(slots);
2492 if (!vat) return 0;
2493 uint64_t timestamp = _voucher_activity_tracepoint_init_with_id(vat,
2494 trace_id, location);
2495 void *tbuf = vat->vat_data;
2496 size_t tlen = sizeof(vat->vat_data);
2497 if (length < tlen) {
2498 memcpy(tbuf, buffer, length);
2499 } else {
2500 memcpy(tbuf, buffer, tlen);
2501 }
2502 if (length > tlen) {
2503 vat->vat_flags |= _voucher_activity_trace_flag_wide_first;
2504 buffer += tlen;
2505 length -= tlen;
2506 (++vat)->vat_flags = _voucher_activity_trace_flag_tracepoint |
2507 _voucher_activity_trace_flag_wide_second;
2508 vat->vat_type = 0; vat->vat_namespace = 0;
2509 tbuf = (void*)vat + offsetof(typeof(*vat), vat_code);
2510 tlen = sizeof(*vat) - offsetof(typeof(*vat), vat_code);
2511 if (length < tlen) {
2512 memcpy(tbuf, buffer, length);
2513 } else {
2514 memcpy(tbuf, buffer, tlen);
2515 }
2516 }
2517 _voucher_activity_trace_fault(trace_id);
2518 return timestamp;
2519 }
2520
2521 uint64_t
2522 voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,
2523 uint64_t location, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
2524 uintptr_t arg4)
2525 {
2526 if (!_voucher_activity_trace_id_enabled(trace_id)) return 0;
2527 _voucher_activity_tracepoint_t vat;
2528 vat = _voucher_activity_tracepoint_get(1);
2529 if (!vat) vat = _voucher_activity_tracepoint_get_slow(1);
2530 if (!vat) return 0;
2531 uint64_t timestamp = _voucher_activity_tracepoint_init_with_id(vat,
2532 trace_id, location);
2533 vat->vat_flags |= _voucher_activity_trace_flag_tracepoint_args;
2534 vat->vat_data[0] = arg1;
2535 vat->vat_data[1] = arg2;
2536 vat->vat_data[2] = arg3;
2537 vat->vat_data[3] = arg4;
2538 _voucher_activity_trace_fault(trace_id);
2539 return timestamp;
2540 }
2541
2542 #pragma mark -
2543 #pragma mark _voucher_debug
2544
2545 size_t
2546 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
2547 {
2548 size_t offset = 0;
2549 #define bufprintf(...) \
2550 offset += dsnprintf(&buf[offset], bufsiz - offset, ##__VA_ARGS__)
2551 bufprintf("voucher[%p] = { xrefcnt = 0x%x, refcnt = 0x%x, ", v,
2552 v->os_obj_xref_cnt + 1, v->os_obj_ref_cnt + 1);
2553
2554 if (v->v_kvbase) {
2555 bufprintf("base voucher %p, ", v->v_kvbase);
2556 }
2557 if (v->v_kvoucher) {
2558 bufprintf("kvoucher%s 0x%x, ", v->v_kvoucher == v->v_ipc_kvoucher ?
2559 " & ipc kvoucher" : "", v->v_kvoucher);
2560 }
2561 if (v->v_ipc_kvoucher && v->v_ipc_kvoucher != v->v_kvoucher) {
2562 bufprintf("ipc kvoucher 0x%x, ", v->v_ipc_kvoucher);
2563 }
2564 if (v->v_has_priority) {
2565 bufprintf("QOS 0x%x, ", *_voucher_priority(v));
2566 }
2567 if (v->v_activities) {
2568 voucher_activity_id_t *activity_ids = _voucher_activity_ids(v);
2569 bufprintf("activity IDs = { ");
2570 unsigned int i;
2571 for (i = 0; i < v->v_activities; i++) {
2572 bufprintf("0x%llx, ", *activity_ids++);
2573 }
2574 bufprintf("}, ");
2575 }
2576 if (v->v_activity) {
2577 _voucher_activity_t va = v->v_activity;
2578 _voucher_atm_t vatm = va->va_atm;
2579 bufprintf("activity[%p] = { ID 0x%llx, use %d, atm[%p] = { "
2580 "AID 0x%llx, ref %d, kvoucher 0x%x } }, ", va, va->va_id,
2581 va->va_use_count + 1, va->va_atm, vatm->vatm_id,
2582 vatm->vatm_refcnt + 1, vatm->vatm_kvoucher);
2583 }
2584 bufprintf("}");
2585 return offset;
2586 }
2587
2588 #else // VOUCHER_USE_MACH_VOUCHER
2589
2590 #pragma mark -
2591 #pragma mark Simulator / vouchers disabled
2592
2593 #if VOUCHER_ENABLE_RECIPE_OBJECTS
2594 voucher_t
2595 voucher_create(voucher_recipe_t recipe)
2596 {
2597 (void)recipe;
2598 return NULL;
2599 }
2600 #endif
2601
2602 voucher_t
2603 voucher_adopt(voucher_t voucher)
2604 {
2605 return voucher;
2606 }
2607
2608 voucher_t
2609 voucher_copy(void)
2610 {
2611 return NULL;
2612 }
2613
2614 voucher_t
2615 voucher_copy_without_importance(void)
2616 {
2617 return NULL;
2618 }
2619
2620 void
2621 voucher_replace_default_voucher(void)
2622 {
2623 }
2624
2625 void
2626 voucher_decrement_importance_count4CF(voucher_t v)
2627 {
2628 (void)v;
2629 }
2630
2631 void
2632 _voucher_thread_cleanup(void *voucher)
2633 {
2634 (void)voucher;
2635 }
2636
2637 void
2638 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
2639 {
2640 (void)kv;
2641 }
2642
2643 mach_voucher_t
2644 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
2645 pthread_priority_t priority)
2646 {
2647 (void)voucher; (void)priority;
2648 return MACH_VOUCHER_NULL;
2649 }
2650
2651 voucher_t
2652 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher,
2653 pthread_priority_t priority, mach_voucher_t kv)
2654 {
2655 (void)voucher; (void)priority; (void)kv;
2656 return NULL;
2657 }
2658
2659 voucher_t
2660 voucher_create_with_mach_msg(mach_msg_header_t *msg)
2661 {
2662 (void)msg;
2663 return NULL;
2664 }
2665
2666 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
2667 mach_voucher_t
2668 voucher_get_mach_voucher(voucher_t voucher)
2669 {
2670 (void)voucher;
2671 return 0;
2672 }
2673 #endif
2674
2675 void
2676 _voucher_xref_dispose(voucher_t voucher)
2677 {
2678 (void)voucher;
2679 }
2680
2681 void
2682 _voucher_dispose(voucher_t voucher)
2683 {
2684 (void)voucher;
2685 }
2686
2687 void
2688 _voucher_atfork_child(void)
2689 {
2690 }
2691
2692 void
2693 _voucher_init(void)
2694 {
2695 }
2696
2697 void*
2698 voucher_activity_get_metadata_buffer(size_t *length)
2699 {
2700 *length = 0;
2701 return NULL;
2702 }
2703
2704 void
2705 _voucher_activity_heap_pressure_normal(void)
2706 {
2707 }
2708
2709 void
2710 _voucher_activity_heap_pressure_warn(void)
2711 {
2712 }
2713
2714 voucher_activity_id_t
2715 voucher_activity_start_with_location(voucher_activity_trace_id_t trace_id,
2716 uint64_t location, voucher_activity_flag_t flags)
2717 {
2718 (void)trace_id; (void)location; (void)flags;
2719 return 0;
2720 }
2721
2722 voucher_activity_id_t
2723 voucher_activity_start(voucher_activity_trace_id_t trace_id,
2724 voucher_activity_flag_t flags)
2725 {
2726 (void)trace_id; (void)flags;
2727 return 0;
2728 }
2729
2730 void
2731 voucher_activity_end(voucher_activity_id_t activity_id)
2732 {
2733 (void)activity_id;
2734 }
2735
2736 unsigned int
2737 voucher_get_activities(voucher_activity_id_t *entries, unsigned int *count)
2738 {
2739 (void)entries; (void)count;
2740 return 0;
2741 }
2742
2743 uint8_t
2744 voucher_activity_get_namespace(void)
2745 {
2746 return 0;
2747 }
2748
2749 uint64_t
2750 voucher_activity_trace(voucher_activity_trace_id_t trace_id, uint64_t location,
2751 void *buffer, size_t length)
2752 {
2753 (void)trace_id; (void)location; (void)buffer; (void)length;
2754 return 0;
2755 }
2756
2757 uint64_t
2758 voucher_activity_trace_args(voucher_activity_trace_id_t trace_id,
2759 uint64_t location, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
2760 uintptr_t arg4)
2761 {
2762 (void)trace_id; (void)location;
2763 (void)arg1; (void)arg2; (void)arg3; (void)arg4;
2764 return 0;
2765 }
2766
2767 size_t
2768 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
2769 {
2770 (void)v; (void)buf; (void)bufsiz;
2771 return 0;
2772 }
2773
2774 #endif // VOUCHER_USE_MACH_VOUCHER