2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
23 #if VOUCHER_USE_MACH_VOUCHER
25 #include <mach/mach_voucher.h>
28 #ifndef VM_MEMORY_GENEALOGY
29 #define VM_MEMORY_GENEALOGY 78
32 #ifndef VOUCHER_ATM_COLLECT_THRESHOLD
33 #define VOUCHER_ATM_COLLECT_THRESHOLD 1
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
;
41 typedef struct _voucher_atm_s
*_voucher_atm_t
;
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
);
55 #pragma mark voucher_t
58 OS_OBJECT_OBJC_CLASS_DECL(voucher
);
59 #define VOUCHER_CLASS OS_OBJECT_OBJC_CLASS(voucher)
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
,
65 #define VOUCHER_CLASS &_voucher_class
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;
73 DISPATCH_ALWAYS_INLINE
75 _voucher_recipes_init(mach_voucher_attr_recipe_data_t
*recipes
,
76 mach_voucher_attr_content_size_t bits_size
)
78 static const mach_voucher_attr_recipe_data_t base_recipe
= {
79 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
80 .command
= MACH_VOUCHER_ATTR_COPY
,
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
,
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
,
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
;
98 static inline voucher_t
99 _voucher_alloc(unsigned int activities
, pthread_priority_t priority
,
100 mach_voucher_attr_recipe_size_t extra
)
102 if (activities
> _voucher_max_activities
) {
103 activities
= _voucher_max_activities
;
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
;
116 dispatch_assert(!extra
);
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
);
127 #if VOUCHER_ENABLE_RECIPE_OBJECTS
129 voucher_create(voucher_recipe_t recipe
)
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
);
135 memcpy(_voucher_extra_recipes(voucher
), recipe
->vr_data
, extra
);
142 voucher_adopt(voucher_t voucher
)
144 return _voucher_adopt(voucher
);
150 return _voucher_copy();
154 voucher_copy_without_importance(void)
156 return _voucher_copy_without_importance();
160 _voucher_thread_cleanup(void *voucher
)
162 _voucher_swap(voucher
, NULL
);
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)
173 _voucher_find_and_retain(mach_voucher_t kv
)
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");
187 // resurrection: raced with _voucher_remove
188 (void)dispatch_atomic_inc2o(v
, os_obj_ref_cnt
, relaxed
);
193 _vouchers_lock_unlock();
198 _voucher_insert(voucher_t v
)
200 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
202 _vouchers_lock_lock();
203 if (slowpath(_TAILQ_IS_ENQUEUED(v
, v_list
))) {
204 _dispatch_voucher_debug("corruption", v
);
205 DISPATCH_CRASH("Voucher corruption");
207 TAILQ_INSERT_TAIL(_vouchers(kv
), v
, v_list
);
208 _vouchers_lock_unlock();
212 _voucher_remove(voucher_t v
)
214 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
215 if (!_TAILQ_IS_ENQUEUED(v
, v_list
)) return;
216 _vouchers_lock_lock();
218 _dispatch_voucher_debug("corruption", v
);
219 DISPATCH_CRASH("Voucher corruption");
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;
228 _vouchers_lock_unlock();
232 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
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
);
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
)
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
);
255 _dispatch_kvoucher_debug("create", kv
);
256 _dispatch_voucher_debug_machport(kv
);
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
;
269 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED
)
271 #if VOUCHER_USE_ATTR_BANK
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
,
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");
283 _voucher_default_task_mach_voucher
= kv
;
284 _voucher_task_mach_voucher
= kv
;
289 voucher_replace_default_voucher(void)
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
) {
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
,
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
;
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
);
314 _dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v
, tkv
);
318 static inline mach_voucher_t
319 _voucher_get_atm_mach_voucher(voucher_t voucher
)
321 _voucher_activity_t act
= _voucher_activity_get(voucher
);
322 return _voucher_activity_get_atm_mach_voucher(act
);
326 _voucher_get_mach_voucher(voucher_t voucher
)
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
)) {
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
;
347 if (!dispatch_atomic_cmpxchgv2o(voucher
, v_ipc_kvoucher
, MACH_VOUCHER_NULL
,
348 kv
, &kvo
, relaxed
)) {
349 _voucher_dealloc_mach_voucher(kv
);
352 if (kv
== voucher
->v_kvoucher
) {
353 // if v_kvoucher == v_ipc_kvoucher we keep only one reference
354 _voucher_dealloc_mach_voucher(kv
);
356 _voucher_insert(voucher
);
357 _dispatch_voucher_debug("kvoucher[0x%08x] create", voucher
, kv
);
363 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
364 pthread_priority_t priority
)
366 if (priority
== _voucher_get_priority(voucher
)) {
367 return MACH_VOUCHER_NULL
; // caller will use _voucher_get_mach_voucher
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
);
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
);
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
;
394 _dispatch_kvoucher_debug("create with priority from voucher[%p]", kv
,
400 _voucher_create_with_mach_voucher(mach_voucher_t kv
)
402 if (!kv
) return NULL
;
405 mach_voucher_attr_recipe_t vr
;
407 mach_voucher_attr_recipe_size_t kvr_size
= 0;
408 const mach_voucher_attr_recipe_data_t redeem_recipe
[] = {
410 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
411 .command
= MACH_VOUCHER_ATTR_COPY
,
412 .previous_voucher
= kv
,
414 #if VOUCHER_USE_ATTR_BANK
416 .key
= MACH_VOUCHER_ATTR_KEY_BANK
,
417 .command
= MACH_VOUCHER_ATTR_REDEEM
,
421 kr
= _voucher_create_mach_voucher(redeem_recipe
, sizeof(redeem_recipe
),
423 if (!dispatch_assume_zero(kr
)) {
424 _voucher_dealloc_mach_voucher(kv
);
426 _dispatch_voucher_debug_machport(kv
);
429 voucher_t v
= _voucher_find_and_retain(rkv
);
431 _dispatch_voucher_debug("kvoucher[0x%08x] find with 0x%08x", v
, rkv
,kv
);
432 _voucher_dealloc_mach_voucher(rkv
);
435 vr_size
= sizeof(*vr
) + _voucher_bits_size(_voucher_max_activities
);
436 vr
= alloca(vr_size
);
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;
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
);
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;
458 pthread_priority_t priority
= 0;
460 priority
= (pthread_priority_t
)*(_voucher_priority_t
*)content
;
461 content
+= sizeof(_voucher_priority_t
);
462 content_size
-= sizeof(_voucher_priority_t
);
464 voucher_activity_id_t va_id
= 0, va_base_id
= 0;
465 _voucher_activity_t act
= NULL
;
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
) {
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
) {
475 va_base_id
= act
->va_id
;
478 v
= _voucher_alloc(activities
, priority
, 0);
480 voucher_activity_id_t
*activity_ids
= _voucher_activity_ids(v
);
481 if (activities
&& va_base_id
) {
482 *activity_ids
++ = va_base_id
;
486 memcpy(activity_ids
, content
, content_size
);
488 v
->v_ipc_kvoucher
= v
->v_kvoucher
= rkv
;
490 _dispatch_voucher_debug("kvoucher[0x%08x] create with 0x%08x", v
, rkv
, kv
);
495 _voucher_create_with_priority_and_mach_voucher(voucher_t ov
,
496 pthread_priority_t priority
, mach_voucher_t kv
)
498 if (priority
== _voucher_get_priority(ov
)) {
499 if (kv
) _voucher_dealloc_mach_voucher(kv
);
500 return ov
? _voucher_retain(ov
) : NULL
;
502 voucher_t v
= _voucher_find_and_retain(kv
);
504 _dispatch_voucher_debug("kvoucher[0x%08x] find", v
, kv
);
505 _voucher_dealloc_mach_voucher(kv
);
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
);
512 memcpy(_voucher_extra_recipes(v
), _voucher_extra_recipes(ov
), extra
);
515 if (ov
->v_activity
) {
516 v
->v_activity
= _voucher_activity_retain(ov
->v_activity
);
518 memcpy(_voucher_activity_ids(v
), _voucher_activity_ids(ov
),
519 activities
* sizeof(voucher_activity_id_t
));
522 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
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
;
536 _voucher_create_without_importance(voucher_t ov
)
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
);
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
[] = {
549 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
550 .command
= MACH_VOUCHER_ATTR_COPY
,
551 .previous_voucher
= okv
,
554 .key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
555 .command
= MACH_VOUCHER_ATTR_REMOVE
,
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
;
565 _voucher_dealloc_mach_voucher(kv
);
566 return _voucher_retain(ov
);
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
);
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
);
582 memcpy(_voucher_extra_recipes(v
), _voucher_extra_recipes(ov
), extra
);
585 if (ov
->v_activity
) {
586 v
->v_activity
= _voucher_activity_retain(ov
->v_activity
);
588 memcpy(_voucher_activity_ids(v
), _voucher_activity_ids(ov
),
589 activities
* sizeof(voucher_activity_id_t
));
592 if (ov
->v_ipc_kvoucher
) {
593 v
->v_ipc_kvoucher
= kv
;
596 v
->v_kvbase
= kvbase
;
597 _voucher_dealloc_mach_voucher(kv
); // borrow base reference
600 _dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
601 "from voucher[%p]", v
, kv
, ov
);
607 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
609 voucher_t v
= _voucher_create_with_mach_voucher(_voucher_mach_msg_get(msg
));
610 _voucher_activity_trace_msg(v
, msg
, receive
);
614 #ifndef MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
615 #define MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL 2
619 voucher_decrement_importance_count4CF(voucher_t v
)
621 if (!v
|| !v
->v_kvoucher
) return;
622 // TODO: 17487167: track presence of importance attribute
624 mach_voucher_t kv
= v
->v_ipc_kvoucher
? v
->v_ipc_kvoucher
: v
->v_kvoucher
;
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;
631 uint32_t count
= UINT32_MAX
;
632 kvc_out
= (mach_voucher_attr_content_t
)&count
;
633 kvc_out_size
= sizeof(count
);
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
);
640 _dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
641 " %s - 0x%x", v
, kv
, count
, mach_error_string(kr
), kr
);
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");
650 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
652 voucher_get_mach_voucher(voucher_t voucher
)
654 return _voucher_get_mach_voucher(voucher
);
659 _voucher_xref_dispose(voucher_t voucher
)
661 _dispatch_voucher_debug("xref_dispose", voucher
);
662 _voucher_remove(voucher
);
663 return _os_object_release_internal_inline((_os_object_t
)voucher
);
667 _voucher_dispose(voucher_t voucher
)
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");
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
);
679 voucher
->v_ipc_kvoucher
= MACH_VOUCHER_NULL
;
681 if (voucher
->v_kvoucher
) {
682 if (!voucher
->v_kvbase
) {
683 _voucher_dealloc_mach_voucher(voucher
->v_kvoucher
);
685 voucher
->v_kvoucher
= MACH_VOUCHER_NULL
;
687 if (voucher
->v_kvbase
) {
688 _voucher_release(voucher
->v_kvbase
);
689 voucher
->v_kvbase
= NULL
;
691 if (voucher
->v_activity
) {
692 _voucher_activity_release(voucher
->v_activity
);
693 voucher
->v_activity
= NULL
;
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;
701 return _os_object_dealloc((_os_object_t
)voucher
);
705 _voucher_atfork_child(void)
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
;
712 // TODO: voucher/activity inheritance on fork ?
716 #pragma mark _voucher_init
719 voucher_mach_msg_set(mach_msg_header_t
*msg
)
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
;
728 voucher_mach_msg_clear(mach_msg_header_t
*msg
)
730 (void)_voucher_mach_msg_clear(msg
, false);
733 voucher_mach_msg_state_t
734 voucher_mach_msg_adopt(mach_msg_header_t
*msg
)
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
);
744 voucher_mach_msg_revert(voucher_mach_msg_state_t state
)
746 if (state
== VOUCHER_MACH_MSG_STATE_UNCHANGED
) return;
747 _voucher_replace((voucher_t
)state
);
750 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
751 #include <_libkernel_init.h>
753 static const struct _libkernel_voucher_functions _voucher_libkernel_functions
=
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
,
763 _voucher_libkernel_init(void)
765 kern_return_t kr
= __libkernel_voucher_init(&_voucher_libkernel_functions
);
766 dispatch_assert(!kr
);
769 #define _voucher_libkernel_init()
775 _voucher_libkernel_init();
778 for (i
= 0; i
< VL_HASH_SIZE
; i
++) {
779 TAILQ_INIT(&_vouchers
[i
]);
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");
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
;
796 _voucher_activity_mode
= mode
;
797 if (_voucher_activity_disabled()) return;
799 e
= getenv("LIBDISPATCH_ACTIVITY_ATM_SUBID_BITS");
801 unsigned long v
= strtoul(e
, &end
, 0);
803 _voucher_atm_subid_bits
= v
;
806 e
= getenv("LIBDISPATCH_ACTIVITY_ATM_COLLECT_THRESHOLD");
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
);
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);
824 #pragma mark _voucher_activity_lock_s
826 DISPATCH_ALWAYS_INLINE
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
;
833 DISPATCH_ALWAYS_INLINE
835 _voucher_activity_lock_lock(_voucher_activity_lock_s
*lock
) {
836 return os_lock_lock(lock
);
839 DISPATCH_ALWAYS_INLINE
841 _voucher_activity_lock_unlock(_voucher_activity_lock_s
*lock
) {
842 return os_lock_unlock(lock
);
846 #pragma mark _voucher_activity_heap
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");
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,
866 _Static_assert(sizeof(struct _voucher_atm_s
) <= 128,
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,
875 _Static_assert(sizeof(_voucher_activity_buffer_t
) ==
876 sizeof(struct {char x
[_voucher_activity_buffer_size
];}),
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
)
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");
902 static const size_t _voucher_atm_mailboxes
=
903 sizeof(((_voucher_activity_metadata_t
)NULL
)->vam_kernel_metadata
) /
904 sizeof(atm_mailbox_offset_t
);
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))
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);
930 DISPATCH_ALWAYS_INLINE
931 static inline uint32_t
932 _voucher_default_activity_buffer_limit()
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;
941 #if TARGET_OS_EMBEDDED
942 // Low-profile modes: Default activity can use a total of 3 buffers.
945 // Low-profile modes: Default activity can use a total of 8 buffers.
950 DISPATCH_ALWAYS_INLINE
951 static inline uint32_t
952 _voucher_activity_buffer_limit()
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;
961 #if TARGET_OS_EMBEDDED
962 // Low-profile modes: Each activity can use a total of 2 buffers.
965 // Low-profile modes: Each activity can use a total of 4 buffers.
970 // The two functions above return the number of *additional* buffers activities
971 // may allocate, hence the gymnastics with - 1.
973 DISPATCH_ALWAYS_INLINE
974 static inline uint32_t
975 _voucher_heap_buffer_limit()
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
;
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.
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.
994 #define NO_BITS_WERE_UNSET (UINT_MAX)
996 DISPATCH_ALWAYS_INLINE
998 _voucher_activity_bitmap_set_first_unset_bit_upto(
999 _voucher_activity_bitmap_t
volatile bitmap
,
1000 unsigned int max_index
)
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
;
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
;
1020 DISPATCH_ALWAYS_INLINE
1021 static inline size_t
1022 _voucher_activity_bitmap_set_first_unset_bit(
1023 _voucher_activity_bitmap_t
volatile bitmap
)
1025 return _voucher_activity_bitmap_set_first_unset_bit_upto(bitmap
, UINT_MAX
);
1029 DISPATCH_ALWAYS_INLINE
1031 _voucher_activity_bitmap_clear_bit(
1032 _voucher_activity_bitmap_t
volatile bitmap
, size_t index
)
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");
1040 (void)dispatch_atomic_and(&bitmap
[i
], ~mask
, release
);
1043 _voucher_activity_metadata_t _voucher_activity_heap
;
1044 static dispatch_once_t _voucher_activity_heap_pred
;
1047 _voucher_activity_heap_init(void *ctxt DISPATCH_UNUSED
)
1049 if (_voucher_activity_disabled()) return;
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
;
1063 _dispatch_temporary_resource_shortage();
1064 vm_addr
= vm_page_size
;
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
),
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
);
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
;
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
1086 for (i
= 0; i
< _voucher_activity_hash_size
; i
++) {
1087 TAILQ_INIT(&heap
->vam_activities
[i
]);
1088 TAILQ_INIT(&heap
->vam_atms
[i
]);
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
),
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
;
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();
1118 _voucher_activity_atfork_child(void)
1120 _voucher_activity_heap_pred
= 0;
1121 _voucher_activity_heap
= NULL
; // activity heap is VM_INHERIT_NONE
1122 _voucher_activity_default
= NULL
;
1126 voucher_activity_get_metadata_buffer(size_t *length
)
1128 dispatch_once_f(&_voucher_activity_heap_pred
, NULL
,
1129 _voucher_activity_heap_init
);
1130 if (_voucher_activity_disabled()) {
1134 *length
= sizeof(_voucher_activity_heap
->vam_client_metadata
);
1135 return _voucher_activity_heap
->vam_client_metadata
;
1138 DISPATCH_ALWAYS_INLINE
1139 static inline _voucher_activity_buffer_header_t
1140 _voucher_activity_heap_buffer_alloc(void)
1142 _voucher_activity_buffer_header_t buffer
= NULL
;
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
);
1149 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1150 _dispatch_debug("activity heap alloc %zd (%p)", index
, buffer
);
1155 DISPATCH_ALWAYS_INLINE
1157 _voucher_activity_heap_buffer_free(_voucher_activity_buffer_header_t buffer
)
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
);
1165 _voucher_activity_bitmap_clear_bit(vam_buffer_bitmap(), index
);
1168 #define _voucher_activity_heap_can_madvise() \
1169 (PAGE_SIZE == _voucher_activity_buffer_size) // <rdar://17445544>
1171 DISPATCH_ALWAYS_INLINE
1173 _voucher_activity_heap_madvise(size_t bitmap_num
, unsigned int start
,
1176 size_t base
= bitmap_num
* _voucher_activity_bits_per_bitmap_base_t
;
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
));
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
));
1191 (void)dispatch_assume_zero(madvise(vam_buffer(base
+ start
),
1192 len
* _voucher_activity_buffer_size
, MADV_FREE
));
1195 DISPATCH_ALWAYS_INLINE
1197 _voucher_activity_heap_madvise_contiguous(size_t bitmap_num
,
1198 _voucher_activity_bitmap_base_t bits
)
1200 // TODO: x86 has fast ctz; arm has fast clz; haswell has fast ctz
1201 dispatch_assert(_voucher_activity_heap_can_madvise());
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
;
1211 len
= (typeof(len
))__builtin_ctzl(inverse
);
1213 len
= _voucher_activity_bits_per_bitmap_base_t
- start
;
1215 typeof(bits
) mask
= ((((typeof(bits
))1) << len
) - 1) << start
;
1217 _voucher_activity_heap_madvise(bitmap_num
, start
, len
);
1222 _voucher_activity_heap_pressure_warn(void)
1224 if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap
) {
1227 volatile _voucher_activity_bitmap_base_t
*bitmap
, *pressure_locked_bitmap
;
1228 bitmap
= vam_buffer_bitmap();
1229 pressure_locked_bitmap
= vam_pressure_locked_bitmap();
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);
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),
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
;
1249 _voucher_activity_heap_pressure_normal(void)
1251 if (!_voucher_activity_heap_can_madvise() || !_voucher_activity_heap
) {
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
);
1266 DISPATCH_ALWAYS_INLINE
1268 _voucher_activity_buffer_init(_voucher_activity_t act
,
1269 _voucher_activity_buffer_header_t buffer
, bool reuse
)
1272 buffer
->vabh_flags
= _voucher_activity_trace_flag_buffer_header
;
1273 buffer
->vabh_activity_id
= act
->va_id
;
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
,
1281 static _voucher_activity_buffer_header_t
1282 _voucher_activity_buffer_alloc_slow(_voucher_activity_t act
,
1283 _voucher_activity_buffer_header_t current
)
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
);
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
);
1298 if (!dispatch_atomic_cmpxchgv2o(act
, va_current_buffer
, current
, buffer
,
1299 ¤t
, release
)) {
1301 TAILQ_REMOVE(&act
->va_buffers
, buffer
, vabh_list
);
1302 _voucher_activity_heap_buffer_free(buffer
);
1307 _voucher_activity_lock_unlock(va_buffers_lock(act
));
1308 _dispatch_voucher_activity_debug("buffer reuse %p", act
, buffer
);
1312 static _voucher_activity_buffer_header_t
1313 _voucher_activity_buffer_alloc(_voucher_activity_t act
,
1314 _voucher_activity_buffer_header_t current
)
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();
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 ¤t
, 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
));
1328 _voucher_activity_heap_buffer_free(buffer
);
1331 _dispatch_voucher_activity_debug("buffer alloc %p", act
, buffer
);
1336 #pragma mark _voucher_activity_t
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; \
1344 TAILQ_INSERT_BEFORE(_vai, _act, field); \
1346 TAILQ_INSERT_TAIL((head), _act, field); \
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
);
1360 DISPATCH_ALWAYS_INLINE
1362 _voucher_activity_copy(_voucher_activity_t act
)
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");
1370 return (use_cnt
== 0);
1373 DISPATCH_ALWAYS_INLINE
1374 static inline _voucher_activity_t
1375 _voucher_activity_retain(_voucher_activity_t act
)
1377 if (_voucher_activity_copy(act
)) {
1378 _dispatch_voucher_activity_debug("invalid resurrection", act
);
1379 DISPATCH_CRASH("Invalid activity resurrection");
1384 DISPATCH_ALWAYS_INLINE
1386 _voucher_activity_release(_voucher_activity_t act
)
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)) {
1393 if (slowpath(use_cnt
< -1)) {
1394 _dispatch_voucher_activity_debug("overrelease", act
);
1395 DISPATCH_CRASH("Activity overrelease");
1397 return _voucher_atm_activity_mark_unused(act
);
1400 DISPATCH_ALWAYS_INLINE
1401 static inline _voucher_activity_t
1402 _voucher_activity_atm_retain(_voucher_activity_t act
)
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");
1413 DISPATCH_ALWAYS_INLINE
1415 _voucher_activity_atm_release(_voucher_activity_t act
)
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)) {
1422 if (slowpath(refcnt
< -1)) {
1423 _dispatch_voucher_activity_debug("atm overrelease", act
);
1424 DISPATCH_CRASH("Activity ATM overrelease");
1426 return _voucher_activity_dispose(act
);
1429 static inline _voucher_activity_t
1430 _voucher_activity_get(voucher_t v
)
1432 _voucher_activity_t act
;
1433 act
= v
&& v
->v_activity
? v
->v_activity
: _voucher_activity_default
;
1437 static _voucher_activity_t
1438 _voucher_activity_find(voucher_activity_id_t va_id
, uint32_t hash
)
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;
1448 static _voucher_activity_t
1449 _voucher_activity_copy_from_id(voucher_activity_id_t va_id
)
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
);
1456 resurrect
= _voucher_activity_copy(act
);
1457 _dispatch_voucher_activity_debug("copy from id 0x%llx", act
, va_id
);
1459 _voucher_activity_lock_unlock(vam_activities_lock());
1460 if (resurrect
) return _voucher_atm_activity_mark_used(act
);
1464 static _voucher_activity_t
1465 _voucher_activity_try_insert(_voucher_activity_t act_new
)
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
);
1473 resurrect
= _voucher_activity_copy(act
);
1474 _dispatch_voucher_activity_debug("try insert: failed (%p)", act
,act_new
);
1476 if (slowpath(_TAILQ_IS_ENQUEUED(act_new
, va_list))) {
1477 _dispatch_voucher_activity_debug("corruption", act_new
);
1478 DISPATCH_CRASH("Activity corruption");
1480 TAILQ_INSERT_TAIL(vam_activities(hash
), act_new
, va_list);
1481 _dispatch_voucher_activity_debug("try insert: succeeded", act_new
);
1483 _voucher_activity_lock_unlock(vam_activities_lock());
1484 if (resurrect
) return _voucher_atm_activity_mark_used(act
);
1489 _voucher_activity_try_remove(_voucher_activity_t act
)
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");
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;
1505 _dispatch_voucher_activity_debug("try remove: %s", act
, r
? "succeeded" :
1507 _voucher_activity_lock_unlock(vam_activities_lock());
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
)
1516 if (!buffer
) buffer
= _voucher_activity_heap_buffer_alloc();
1518 _dispatch_voucher_atm_debug("no buffer", vatm
);
1519 _voucher_atm_release(vatm
); // consume vatm reference
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
;
1539 act
->va_flags3
= act
->va_flags2
;
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
);
1551 _voucher_activity_dispose(act
);
1554 _voucher_atm_activity_insert(vatm
, act
);
1556 _dispatch_voucher_activity_debug("create", act
);
1561 _voucher_activity_dispose(_voucher_activity_t act
)
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");
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
);
1578 buffer
= (_voucher_activity_buffer_header_t
)act
;
1579 _voucher_activity_heap_buffer_free(buffer
);
1583 _voucher_activity_collect(_voucher_activity_t act
)
1585 _dispatch_voucher_activity_debug("collect", act
);
1586 if (_voucher_activity_try_remove(act
)) {
1587 _voucher_atm_activity_remove(act
);
1591 static _voucher_activity_t
1592 _voucher_activity_copy_from_mach_voucher(mach_voucher_t kv
,
1593 voucher_activity_id_t va_id
)
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
;
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
);
1611 _voucher_activity_heap_buffer_free(buffer
);
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
);
1622 #pragma mark _voucher_atm_mailbox
1624 DISPATCH_ALWAYS_INLINE
1625 static inline atm_mailbox_offset_t
1626 _voucher_atm_mailbox_alloc(void)
1628 atm_mailbox_offset_t mailbox_offset
= MAILBOX_OFFSET_UNSET
;
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
);
1637 return mailbox_offset
;
1640 DISPATCH_ALWAYS_INLINE
1642 _voucher_atm_mailbox_free(atm_mailbox_offset_t mailbox_offset
)
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
);
1649 _voucher_activity_bitmap_clear_bit(vam_atm_mbox_bitmap(), index
);
1652 DISPATCH_ALWAYS_INLINE
1654 _voucher_atm_mailbox_set(atm_mailbox_offset_t mailbox_offset
,
1655 atm_subaid32_t subaid
, bool max_present
)
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;
1667 #pragma mark _voucher_atm_t
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
);
1673 DISPATCH_ALWAYS_INLINE
1674 static inline _voucher_atm_t
1675 _voucher_atm_retain(_voucher_atm_t vatm
)
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");
1687 DISPATCH_ALWAYS_INLINE
1689 _voucher_atm_release(_voucher_atm_t vatm
)
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)) {
1696 if (slowpath(refcnt
< -1)) {
1697 _dispatch_voucher_atm_debug("overrelease", vatm
);
1698 DISPATCH_CRASH("ATM overrelease");
1700 if (_voucher_atm_try_remove(vatm
)) {
1701 _voucher_atm_dispose(vatm
, true);
1705 static _voucher_atm_t
1706 _voucher_atm_find(atm_aid_t atm_id
, uint32_t hash
)
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;
1716 static _voucher_atm_t
1717 _voucher_atm_copy(atm_aid_t atm_id
)
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
);
1723 _voucher_atm_retain(vatm
);
1724 _dispatch_voucher_atm_debug("copy", vatm
);
1726 _voucher_activity_lock_unlock(vam_atms_lock());
1730 static _voucher_atm_t
1731 _voucher_atm_try_insert(_voucher_atm_t vatm_new
)
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
);
1738 _voucher_atm_retain(vatm
);
1739 _dispatch_voucher_atm_debug("try insert: failed (%p)", vatm
, vatm_new
);
1741 if (slowpath(_TAILQ_IS_ENQUEUED(vatm_new
, vatm_list
))) {
1742 _dispatch_voucher_atm_debug("corruption", vatm_new
);
1743 DISPATCH_CRASH("ATM corruption");
1745 TAILQ_INSERT_TAIL(vam_atms(hash
), vatm_new
, vatm_list
);
1746 _dispatch_voucher_atm_debug("try insert: succeeded", vatm_new
);
1748 _voucher_activity_lock_unlock(vam_atms_lock());
1753 _voucher_atm_try_remove(_voucher_atm_t vatm
)
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");
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;
1769 _dispatch_voucher_atm_debug("try remove: %s", vatm
, r
? "succeeded" :
1771 _voucher_activity_lock_unlock(vam_atms_lock());
1776 _voucher_atm_update_mailbox(_voucher_atm_t vatm
)
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);
1785 _dispatch_voucher_atm_debug("update max-present subaid 0x%x", vatm
,
1792 _voucher_atm_update_used_mailbox(_voucher_atm_t vatm
)
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);
1800 _dispatch_voucher_atm_debug("update min-used subaid 0x%x", vatm
,
1807 _voucher_atm_activity_insert(_voucher_atm_t vatm
, _voucher_activity_t act
)
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
),
1813 _voucher_atm_update_mailbox(vatm
);
1815 if (!_TAILQ_IS_ENQUEUED(act
, va_atm_used_list
)) {
1816 _voucher_activity_ordered_insert(act
, vatm_used_activities(vatm
),
1818 _voucher_atm_update_used_mailbox(vatm
);
1820 _dispatch_voucher_activity_debug("atm insert", act
);
1821 _voucher_activity_lock_unlock(vatm_activities_lock(vatm
));
1825 _voucher_atm_activity_remove(_voucher_activity_t act
)
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
);
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
);
1844 _voucher_activity_lock_unlock(vatm_activities_lock(vatm
));
1847 static _voucher_activity_t
1848 _voucher_atm_activity_mark_used(_voucher_activity_t act
)
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
),
1855 _voucher_atm_update_used_mailbox(vatm
);
1856 _dispatch_voucher_activity_debug("mark used", act
);
1858 _voucher_activity_lock_unlock(vatm_activities_lock(vatm
));
1863 _voucher_atm_activity_mark_unused(_voucher_activity_t act
)
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
);
1873 _voucher_atm_retain(vatm
);
1874 updated
= _voucher_atm_update_used_mailbox(vatm
);
1876 _voucher_activity_lock_unlock(vatm_activities_lock(vatm
));
1878 _voucher_atm_release(vatm
);
1879 _voucher_atm_collect_if_needed(updated
);
1884 _voucher_atm_activity_collect(_voucher_atm_t vatm
, atm_subaid32_t min_subaid
)
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
;
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
) {
1896 if (!_TAILQ_IS_ENQUEUED(act
, va_atm_used_list
)) {
1897 _voucher_activity_atm_retain(act
);
1901 _voucher_activity_lock_unlock(vatm_activities_lock(vatm
));
1903 _voucher_activity_collect(act
);
1904 _voucher_activity_atm_release(act
);
1911 _voucher_atm_collect(void)
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
;
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
;
1930 _voucher_activity_lock_unlock(vam_atms_lock());
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
);
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
]);
1954 _voucher_atm_collect_if_needed(bool updated
)
1958 level
= dispatch_atomic_add(&_voucher_atm_collect_level
, 2ul, relaxed
);
1960 level
= _voucher_atm_collect_level
;
1963 if (level
& 1 || level
<= _voucher_atm_collect_threshold
) return;
1964 if (!dispatch_atomic_cmpxchg(&_voucher_atm_collect_level
, level
, level
+ 1,
1966 #if DISPATCH_DEBUG && DISPATCH_VOUCHER_ACTIVITY_DEBUG
1967 _dispatch_debug("atm collect: reached level %ld", level
/2);
1969 if (slowpath(level
< 0)) {
1970 DISPATCH_CRASH("ATM collection level corruption");
1972 _voucher_atm_collect();
1973 dispatch_atomic_sub(&_voucher_atm_collect_level
, level
+ 1, release
);
1978 _voucher_atm_fault(mach_voucher_attr_command_t kvc_cmd
)
1980 _voucher_activity_t act
= _voucher_activity_get(_voucher_get());
1981 mach_voucher_t kv
= _voucher_activity_get_atm_mach_voucher(act
);
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
);
1997 _voucher_mach_voucher_get_atm_id(mach_voucher_t kv
)
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
,
2005 DISPATCH_VERIFY_MIG(kr
);
2006 (void)dispatch_assume_zero(kr
);
2010 static mach_voucher_t
2011 _voucher_atm_mach_voucher_create(atm_aid_t
*atm_id_ptr
)
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
,
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");
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");
2028 _dispatch_kvoucher_debug("atm create <%lld>", kv
, atm_id
);
2029 *atm_id_ptr
= atm_id
;
2034 _voucher_atm_mailbox_mach_voucher_register(_voucher_atm_t vatm
,
2037 _dispatch_voucher_atm_debug("mailbox register %lld with kvoucher[0x%08x]",
2038 vatm
, vatm
->vatm_mailbox_offset
, kv
);
2041 atm_mailbox_offset_t offset
= vatm
->vatm_mailbox_offset
;
2042 mach_voucher_attr_recipe_t vr
;
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
),
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");
2058 if (!vatm
->vatm_kvoucher
) {
2059 vatm
->vatm_kvoucher
= akv
;
2062 if (akv
!= vatm
->vatm_kvoucher
) {
2063 DISPATCH_CRASH("Unexpected mach voucher returned by ATM ID "
2066 _voucher_dealloc_mach_voucher(akv
);
2068 DISPATCH_CRASH("Registered invalid ATM object");
2071 _dispatch_voucher_atm_debug("mailbox registered %lld", vatm
,
2072 vatm
->vatm_mailbox_offset
);
2076 _voucher_atm_mailbox_register(_voucher_atm_t vatm
)
2078 mach_voucher_t kv
= vatm
->vatm_kvoucher
;
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
);
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
,
2094 DISPATCH_VERIFY_MIG(kr
);
2095 if (dispatch_assume_zero(kr
)) {
2096 DISPATCH_CLIENT_CRASH("Could not register ATM ID");
2098 _dispatch_voucher_atm_debug("mailbox registered %lld", vatm
,
2099 vatm
->vatm_mailbox_offset
);
2100 #endif // RDAR_17510224
2104 _voucher_atm_mailbox_unregister(_voucher_atm_t vatm
)
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
);
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");
2124 _dispatch_voucher_atm_debug("mailbox unregistered %lld", vatm
,
2125 vatm
->vatm_mailbox_offset
);
2129 static _voucher_atm_t
2130 _voucher_atm_create(mach_voucher_t kv
, atm_aid_t atm_id
)
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
));
2136 kv
= _voucher_atm_mach_voucher_create(&atm_id
);
2137 if (mailbox_offset
== MAILBOX_OFFSET_UNSET
) {
2138 _voucher_dealloc_mach_voucher(kv
);
2140 vatm
->vatm_kvoucher
= kv
;
2142 kv
= MACH_VOUCHER_NULL
;
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
);
2153 _voucher_atm_dispose(vatm
, false);
2156 _voucher_atm_mailbox_mach_voucher_register(vatm
, kv
);
2158 _voucher_atm_mailbox_register(vatm
);
2160 _dispatch_voucher_atm_debug("create with kvoucher[0x%08x]", vatm
, kv
);
2165 _voucher_atm_dispose(_voucher_atm_t vatm
, bool unregister
)
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");
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
;
2182 _voucher_atm_mailbox_free(vatm
->vatm_mailbox_offset
);
2183 vatm
->vatm_mailbox_offset
= MAILBOX_OFFSET_UNSET
;
2188 static inline mach_voucher_t
2189 _voucher_activity_get_atm_mach_voucher(_voucher_activity_t act
)
2192 kv
= act
&& act
->va_atm
? act
->va_atm
->vatm_kvoucher
: MACH_VOUCHER_NULL
;
2197 static _voucher_atm_t
2198 _voucher_atm_base_copy_and_activity_id_make(voucher_activity_id_t
*va_id_ptr
)
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);
2207 _voucher_activity_lock_lock(vam_base_atm_lock());
2208 vatm
= _voucher_activity_heap
->vam_base_atm
;
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
);
2218 vatm
= _voucher_activity_heap
->vam_base_atm
;
2219 if (vatm
!= vatm_old
) {
2223 _voucher_activity_heap
->vam_base_atm
= vatm
= vatm_new
;
2224 _voucher_activity_heap
->vam_base_atm_subid
= subid
= 1;
2226 _voucher_atm_retain(vatm
);
2227 _dispatch_voucher_atm_debug("base replace", vatm
);
2229 _voucher_activity_heap
->vam_base_atm_subid
= subid
;
2230 _dispatch_voucher_atm_debug("base copy", vatm
);
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
);
2236 *va_id_ptr
= VATM_ACTID(vatm
, subid
);
2240 static voucher_activity_id_t
2241 _voucher_atm_nested_atm_id_make(void)
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
);
2249 static voucher_activity_id_t
2250 _voucher_atm_nested_activity_id_make(void)
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
;
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();
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
;
2268 _voucher_activity_heap
->vam_nested_atm_subid
= subid
;
2269 _voucher_activity_lock_unlock(vam_nested_atm_lock());
2270 return va_id
+ subid
;
2274 #pragma mark voucher_activity_id_t
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
)
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();
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
,
2303 vat
= (_voucher_activity_tracepoint_t
)act
;
2305 _voucher_atm_release(vatm
);
2309 // default to _voucher_activity_default base activity
2310 va_base_id
= _voucher_activity_default
->va_id
;
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
);
2317 memcpy(_voucher_extra_recipes(v
), _voucher_extra_recipes(ov
), extra
);
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
;
2324 voucher_activity_id_t
*activity_ids
= _voucher_activity_ids(v
);
2326 memcpy(activity_ids
, _voucher_activity_ids(ov
),
2327 oactivities
* sizeof(voucher_activity_id_t
));
2330 va_id
= _voucher_atm_nested_activity_id_make();
2331 if (ov
&& ov
->v_activity
) {
2332 act
= _voucher_activity_retain(ov
->v_activity
);
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
2341 vat
= _voucher_activity_trace_with_id(trace_id
);
2343 vat
->vat_flags
|= _voucher_activity_trace_flag_activity
|
2344 _voucher_activity_trace_flag_start
;
2345 vat
->vat_data
[0] = va_id
;
2350 voucher_activity_id_t
2351 voucher_activity_start(voucher_activity_trace_id_t trace_id
,
2352 voucher_activity_flag_t flags
)
2354 return voucher_activity_start_with_location(trace_id
, 0, flags
);
2358 voucher_activity_end(voucher_activity_id_t va_id
)
2361 _voucher_activity_tracepoint_t vat
;
2362 vat
= _voucher_activity_trace_with_id(_voucher_activity_trace_id_release
);
2364 vat
->vat_flags
|= _voucher_activity_trace_flag_activity
|
2365 _voucher_activity_trace_flag_end
;
2366 vat
->vat_data
[0] = va_id
;
2368 voucher_t v
= _voucher_get();
2370 unsigned int activities
= v
->v_activities
, act_idx
= activities
;
2371 voucher_activity_id_t
*activity_ids
= _voucher_activity_ids(v
);
2373 if (activity_ids
[act_idx
-1] == va_id
) break;
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
);
2384 memcpy(_voucher_extra_recipes(nv
), _voucher_extra_recipes(v
),extra
);
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
;
2392 bool atm_collect
= !activities
;
2394 voucher_activity_id_t
*new_activity_ids
= _voucher_activity_ids(nv
);
2395 if (act_idx
== 1 && _voucher_activity_default
) {
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
));
2402 if (v
->v_activity
) {
2403 nv
->v_activity
= _voucher_activity_retain(v
->v_activity
);
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
));
2413 _voucher_swap(v
, nv
);
2417 voucher_get_activities(voucher_activity_id_t
*entries
, unsigned int *count
)
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
));
2433 voucher_activity_get_namespace(void)
2435 voucher_t v
= _voucher_get();
2436 if (!v
|| !v
->v_activity
) return 0;
2437 return v
->v_activity
->va_namespace
;
2441 _voucher_activity_tracepoint_t
2442 _voucher_activity_tracepoint_get_slow(unsigned int slots
)
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
;
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
;
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
2462 vab
= _voucher_activity_buffer_alloc(act
, vab
);
2465 vat
= _voucher_activity_buffer_tracepoint_get(vab
, slots
);
2471 _voucher_activity_trace_fault(voucher_activity_trace_id_t trace_id
)
2473 if (!slowpath(_voucher_activity_trace_id_is_subtype(trace_id
, error
))) {
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
;
2480 return _voucher_atm_fault(atm_cmd
);
2484 voucher_activity_trace(voucher_activity_trace_id_t trace_id
, uint64_t location
,
2485 void *buffer
, size_t length
)
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
);
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
);
2500 memcpy(tbuf
, buffer
, tlen
);
2502 if (length
> tlen
) {
2503 vat
->vat_flags
|= _voucher_activity_trace_flag_wide_first
;
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
);
2514 memcpy(tbuf
, buffer
, tlen
);
2517 _voucher_activity_trace_fault(trace_id
);
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
,
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);
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
);
2543 #pragma mark _voucher_debug
2546 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
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);
2555 bufprintf("base voucher %p, ", v
->v_kvbase
);
2557 if (v
->v_kvoucher
) {
2558 bufprintf("kvoucher%s 0x%x, ", v
->v_kvoucher
== v
->v_ipc_kvoucher
?
2559 " & ipc kvoucher" : "", v
->v_kvoucher
);
2561 if (v
->v_ipc_kvoucher
&& v
->v_ipc_kvoucher
!= v
->v_kvoucher
) {
2562 bufprintf("ipc kvoucher 0x%x, ", v
->v_ipc_kvoucher
);
2564 if (v
->v_has_priority
) {
2565 bufprintf("QOS 0x%x, ", *_voucher_priority(v
));
2567 if (v
->v_activities
) {
2568 voucher_activity_id_t
*activity_ids
= _voucher_activity_ids(v
);
2569 bufprintf("activity IDs = { ");
2571 for (i
= 0; i
< v
->v_activities
; i
++) {
2572 bufprintf("0x%llx, ", *activity_ids
++);
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
);
2588 #else // VOUCHER_USE_MACH_VOUCHER
2591 #pragma mark Simulator / vouchers disabled
2593 #if VOUCHER_ENABLE_RECIPE_OBJECTS
2595 voucher_create(voucher_recipe_t recipe
)
2603 voucher_adopt(voucher_t voucher
)
2615 voucher_copy_without_importance(void)
2621 voucher_replace_default_voucher(void)
2626 voucher_decrement_importance_count4CF(voucher_t v
)
2632 _voucher_thread_cleanup(void *voucher
)
2638 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
2644 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
2645 pthread_priority_t priority
)
2647 (void)voucher
; (void)priority
;
2648 return MACH_VOUCHER_NULL
;
2652 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher
,
2653 pthread_priority_t priority
, mach_voucher_t kv
)
2655 (void)voucher
; (void)priority
; (void)kv
;
2660 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
2666 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
2668 voucher_get_mach_voucher(voucher_t voucher
)
2676 _voucher_xref_dispose(voucher_t voucher
)
2682 _voucher_dispose(voucher_t voucher
)
2688 _voucher_atfork_child(void)
2698 voucher_activity_get_metadata_buffer(size_t *length
)
2705 _voucher_activity_heap_pressure_normal(void)
2710 _voucher_activity_heap_pressure_warn(void)
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
)
2718 (void)trace_id
; (void)location
; (void)flags
;
2722 voucher_activity_id_t
2723 voucher_activity_start(voucher_activity_trace_id_t trace_id
,
2724 voucher_activity_flag_t flags
)
2726 (void)trace_id
; (void)flags
;
2731 voucher_activity_end(voucher_activity_id_t activity_id
)
2737 voucher_get_activities(voucher_activity_id_t
*entries
, unsigned int *count
)
2739 (void)entries
; (void)count
;
2744 voucher_activity_get_namespace(void)
2750 voucher_activity_trace(voucher_activity_trace_id_t trace_id
, uint64_t location
,
2751 void *buffer
, size_t length
)
2753 (void)trace_id
; (void)location
; (void)buffer
; (void)length
;
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
,
2762 (void)trace_id
; (void)location
;
2763 (void)arg1
; (void)arg2
; (void)arg3
; (void)arg4
;
2768 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
2770 (void)v
; (void)buf
; (void)bufsiz
;
2774 #endif // VOUCHER_USE_MACH_VOUCHER