]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_importance.c
xnu-4570.71.2.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_importance.c
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <mach/mach_types.h>
30#include <mach/notify.h>
31#include <ipc/ipc_types.h>
32#include <ipc/ipc_importance.h>
33#include <ipc/ipc_port.h>
34#include <ipc/ipc_voucher.h>
35#include <kern/ipc_kobject.h>
36#include <kern/ipc_tt.h>
37#include <kern/mach_param.h>
38#include <kern/misc_protos.h>
39#include <kern/kalloc.h>
40#include <kern/zalloc.h>
41#include <kern/queue.h>
42#include <kern/task.h>
39037602 43#include <kern/policy_internal.h>
fe8ab488
A
44
45#include <sys/kdebug.h>
46
47#include <mach/mach_voucher_attr_control.h>
48#include <mach/machine/sdt.h>
49
50extern int proc_pid(void *);
51extern int proc_selfpid(void);
52extern uint64_t proc_uniqueid(void *p);
53extern char *proc_name_address(void *p);
54
55/*
56 * Globals for delayed boost drop processing.
57 */
58static queue_head_t ipc_importance_delayed_drop_queue;
59static thread_call_t ipc_importance_delayed_drop_call;
60static uint64_t ipc_importance_delayed_drop_timestamp;
61static boolean_t ipc_importance_delayed_drop_call_requested = FALSE;
62
63#define DENAP_DROP_TARGET (1000 * NSEC_PER_MSEC) /* optimum denap delay */
64#define DENAP_DROP_SKEW (100 * NSEC_PER_MSEC) /* request skew for wakeup */
65#define DENAP_DROP_LEEWAY (2 * DENAP_DROP_SKEW) /* specified wakeup leeway */
66
67#define DENAP_DROP_DELAY (DENAP_DROP_TARGET + DENAP_DROP_SKEW)
68#define DENAP_DROP_FLAGS (THREAD_CALL_DELAY_SYS_NORMAL | THREAD_CALL_DELAY_LEEWAY)
69
70/*
71 * Importance Voucher Attribute Manager
72 */
73
74static lck_spin_t ipc_importance_lock_data; /* single lock for now */
75
76
77#define ipc_importance_lock_init() \
78 lck_spin_init(&ipc_importance_lock_data, &ipc_lck_grp, &ipc_lck_attr)
79#define ipc_importance_lock_destroy() \
80 lck_spin_destroy(&ipc_importance_lock_data, &ipc_lck_grp)
81#define ipc_importance_lock() \
82 lck_spin_lock(&ipc_importance_lock_data)
83#define ipc_importance_lock_try() \
84 lck_spin_try_lock(&ipc_importance_lock_data)
85#define ipc_importance_unlock() \
86 lck_spin_unlock(&ipc_importance_lock_data)
5ba3f43e
A
87#define ipc_importance_assert_held() \
88 lck_spin_assert(&ipc_importance_lock_data, LCK_ASSERT_OWNED)
fe8ab488
A
89#define ipc_importance_sleep(elem) lck_spin_sleep(&ipc_importance_lock_data, \
90 LCK_SLEEP_DEFAULT, \
91 (event_t)(elem), \
92 THREAD_UNINT)
93#define ipc_importance_wakeup(elem) thread_wakeup((event_t)(elem))
94
95#if IIE_REF_DEBUG
96#define incr_ref_counter(x) (hw_atomic_add(&(x), 1))
97
98static inline
99uint32_t ipc_importance_reference_internal(ipc_importance_elem_t elem)
100{
101 incr_ref_counter(elem->iie_refs_added);
102 return (hw_atomic_add(&elem->iie_bits, 1) & IIE_REFS_MASK);
103}
104
105static inline
106uint32_t ipc_importance_release_internal(ipc_importance_elem_t elem)
107{
108 incr_ref_counter(elem->iie_refs_dropped);
109 return (hw_atomic_sub(&elem->iie_bits, 1) & IIE_REFS_MASK);
110}
111
112static inline
113uint32_t ipc_importance_task_reference_internal(ipc_importance_task_t task_imp)
114{
115 uint32_t out;
116 out = ipc_importance_reference_internal(&task_imp->iit_elem);
117 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added);
118 return out;
119}
120
121static inline
122uint32_t ipc_importance_task_release_internal(ipc_importance_task_t task_imp)
123{
124 uint32_t out;
125
126 assert(1 < IIT_REFS(task_imp));
127 incr_ref_counter(task_imp->iit_elem.iie_task_refs_dropped);
128 out = ipc_importance_release_internal(&task_imp->iit_elem);
129 return out;
130}
131
132static inline
133void ipc_importance_counter_init(ipc_importance_elem_t elem)
134{
135
136 elem->iie_refs_added = 0;
137 elem->iie_refs_dropped = 0;
138 elem->iie_kmsg_refs_added = 0;
139 elem->iie_kmsg_refs_inherited = 0;
140 elem->iie_kmsg_refs_coalesced = 0;
141 elem->iie_kmsg_refs_dropped = 0;
142 elem->iie_task_refs_added = 0;
143 elem->iie_task_refs_added_inherit_from = 0;
144 elem->iie_task_refs_added_transition = 0;
145 elem->iie_task_refs_self_added = 0;
146 elem->iie_task_refs_inherited = 0;
147 elem->iie_task_refs_coalesced = 0;
148 elem->iie_task_refs_dropped = 0;
149}
150#else
151#define incr_ref_counter(x)
152#endif
153
154#if DEVELOPMENT || DEBUG
155static queue_head_t global_iit_alloc_queue;
156#endif
157
158/* TODO: remove this varibale when interactive daemon audit is complete */
159boolean_t ipc_importance_interactive_receiver = FALSE;
160
161static zone_t ipc_importance_task_zone;
162static zone_t ipc_importance_inherit_zone;
163
164static ipc_voucher_attr_control_t ipc_importance_control;
165
743345f9
A
166static boolean_t ipc_importance_task_check_transition(ipc_importance_task_t task_imp,
167 iit_update_type_t type, uint32_t delta);
168
169static void ipc_importance_task_propagate_assertion_locked(ipc_importance_task_t task_imp,
170 iit_update_type_t type, boolean_t update_task_imp);
171
172static ipc_importance_inherit_t ipc_importance_inherit_from_task(task_t from_task, task_t to_task);
173
fe8ab488
A
174/*
175 * Routine: ipc_importance_kmsg_link
176 * Purpose:
177 * Link the kmsg onto the appropriate propagation chain.
178 * If the element is a task importance, we link directly
179 * on its propagation chain. Otherwise, we link onto the
180 * destination task of the inherit.
181 * Conditions:
182 * Importance lock held.
183 * Caller is donating an importance elem reference to the kmsg.
184 */
185static void
186ipc_importance_kmsg_link(
187 ipc_kmsg_t kmsg,
188 ipc_importance_elem_t elem)
189{
190 ipc_importance_elem_t link_elem;
191
192 assert(IIE_NULL == kmsg->ikm_importance);
193
194 link_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
195 (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
196 elem;
197
198 queue_enter(&link_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
199 kmsg->ikm_importance = elem;
200}
201
202/*
203 * Routine: ipc_importance_kmsg_unlink
204 * Purpose:
205 * Unlink the kmsg from its current propagation chain.
206 * If the element is a task importance, we unlink directly
207 * from its propagation chain. Otherwise, we unlink from the
208 * destination task of the inherit.
209 * Returns:
210 * The reference to the importance element it was linked on.
211 * Conditions:
212 * Importance lock held.
213 * Caller is responsible for dropping reference on returned elem.
214 */
215static ipc_importance_elem_t
216ipc_importance_kmsg_unlink(
217 ipc_kmsg_t kmsg)
218{
219 ipc_importance_elem_t elem = kmsg->ikm_importance;
220
221 if (IIE_NULL != elem) {
222 ipc_importance_elem_t unlink_elem;
223
224 unlink_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
225 (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
226 elem;
227
228 queue_remove(&unlink_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
229 kmsg->ikm_importance = IIE_NULL;
230 }
231 return elem;
232}
233
234/*
235 * Routine: ipc_importance_inherit_link
236 * Purpose:
237 * Link the inherit onto the appropriate propagation chain.
238 * If the element is a task importance, we link directly
239 * on its propagation chain. Otherwise, we link onto the
240 * destination task of the inherit.
241 * Conditions:
242 * Importance lock held.
243 * Caller is donating an elem importance reference to the inherit.
244 */
245static void
246ipc_importance_inherit_link(
247 ipc_importance_inherit_t inherit,
248 ipc_importance_elem_t elem)
249{
39037602 250 ipc_importance_task_t link_task;
fe8ab488
A
251
252 assert(IIE_NULL == inherit->iii_from_elem);
39037602
A
253 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
254 ((ipc_importance_inherit_t)elem)->iii_to_task :
255 (ipc_importance_task_t)elem;
fe8ab488 256
39037602 257 queue_enter(&link_task->iit_inherits, inherit,
fe8ab488
A
258 ipc_importance_inherit_t, iii_inheritance);
259 inherit->iii_from_elem = elem;
260}
261
39037602
A
262/*
263 * Routine: ipc_importance_inherit_find
264 * Purpose:
265 * Find an existing inherit that links the from element to the
266 * to_task at a given nesting depth. As inherits from other
267 * inherits are actually linked off the original inherit's donation
268 * receiving task, we have to conduct our search from there if
269 * the from element is an inherit.
270 * Returns:
271 * A pointer (not a reference) to the matching inherit.
272 * Conditions:
273 * Importance lock held.
274 */
275static ipc_importance_inherit_t
276ipc_importance_inherit_find(
277 ipc_importance_elem_t from,
278 ipc_importance_task_t to_task,
279 unsigned int depth)
280{
281 ipc_importance_task_t link_task;
282 ipc_importance_inherit_t inherit;
283
284 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(from)) ?
285 ((ipc_importance_inherit_t)from)->iii_to_task :
286 (ipc_importance_task_t)from;
287
288 queue_iterate(&link_task->iit_inherits, inherit,
289 ipc_importance_inherit_t, iii_inheritance) {
743345f9 290 if (inherit->iii_to_task == to_task && inherit->iii_depth == depth) {
39037602 291 return inherit;
743345f9 292 }
39037602
A
293 }
294 return III_NULL;
295}
296
fe8ab488
A
297/*
298 * Routine: ipc_importance_inherit_unlink
299 * Purpose:
300 * Unlink the inherit from its current propagation chain.
301 * If the element is a task importance, we unlink directly
302 * from its propagation chain. Otherwise, we unlink from the
303 * destination task of the inherit.
304 * Returns:
305 * The reference to the importance element it was linked on.
306 * Conditions:
307 * Importance lock held.
308 * Caller is responsible for dropping reference on returned elem.
309 */
310static ipc_importance_elem_t
311ipc_importance_inherit_unlink(
312 ipc_importance_inherit_t inherit)
313{
314 ipc_importance_elem_t elem = inherit->iii_from_elem;
315
316 if (IIE_NULL != elem) {
39037602 317 ipc_importance_task_t unlink_task;
fe8ab488 318
39037602
A
319 unlink_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
320 ((ipc_importance_inherit_t)elem)->iii_to_task :
321 (ipc_importance_task_t)elem;
fe8ab488 322
39037602 323 queue_remove(&unlink_task->iit_inherits, inherit,
fe8ab488
A
324 ipc_importance_inherit_t, iii_inheritance);
325 inherit->iii_from_elem = IIE_NULL;
326 }
327 return elem;
328}
329
330/*
331 * Routine: ipc_importance_reference
332 * Purpose:
333 * Add a reference to the importance element.
334 * Conditions:
335 * Caller must hold a reference on the element.
336 */
337void
338ipc_importance_reference(ipc_importance_elem_t elem)
339{
340 assert(0 < IIE_REFS(elem));
341 ipc_importance_reference_internal(elem);
342}
343
344/*
345 * Routine: ipc_importance_release_locked
346 * Purpose:
347 * Release a reference on an importance attribute value,
348 * unlinking and deallocating the attribute if the last reference.
349 * Conditions:
350 * Entered with importance lock held, leaves with it unlocked.
351 */
352static void
353ipc_importance_release_locked(ipc_importance_elem_t elem)
354{
355 assert(0 < IIE_REFS(elem));
356
5ba3f43e 357#if IMPORTANCE_DEBUG
39037602
A
358 ipc_importance_inherit_t temp_inherit;
359 ipc_importance_task_t link_task;
360 ipc_kmsg_t temp_kmsg;
361 uint32_t expected = 0;
362
363 if (0 < elem->iie_made)
364 expected++;
365
366 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
367 ((ipc_importance_inherit_t)elem)->iii_to_task :
368 (ipc_importance_task_t)elem;
369
370 queue_iterate(&link_task->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance)
371 if (temp_kmsg->ikm_importance == elem)
372 expected++;
373 queue_iterate(&link_task->iit_inherits, temp_inherit,
374 ipc_importance_inherit_t, iii_inheritance)
375 if (temp_inherit->iii_from_elem == elem)
376 expected++;
377 if (IIE_REFS(elem) < expected + 1)
378 panic("ipc_importance_release_locked (%p)", elem);
5ba3f43e 379#endif /* IMPORTANCE_DEBUG */
39037602
A
380
381 if (0 < ipc_importance_release_internal(elem)) {
fe8ab488
A
382 ipc_importance_unlock();
383 return;
384 }
385
386 /* last ref */
fe8ab488
A
387
388 switch (IIE_TYPE(elem)) {
389
390 /* just a "from" task reference to drop */
391 case IIE_TYPE_TASK:
392 {
393 ipc_importance_task_t task_elem;
394
395 task_elem = (ipc_importance_task_t)elem;
39037602
A
396
397 /* the task can't still hold a reference on the task importance */
fe8ab488
A
398 assert(TASK_NULL == task_elem->iit_task);
399
400#if DEVELOPMENT || DEBUG
401 queue_remove(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
402#endif
403
404 ipc_importance_unlock();
405
406 zfree(ipc_importance_task_zone, task_elem);
407 break;
408 }
409
410 /* dropping an inherit element */
411 case IIE_TYPE_INHERIT:
412 {
743345f9
A
413 ipc_importance_inherit_t inherit = (ipc_importance_inherit_t)elem;
414 ipc_importance_task_t to_task = inherit->iii_to_task;
fe8ab488 415 ipc_importance_elem_t from_elem;
fe8ab488 416
fe8ab488 417 assert(IIT_NULL != to_task);
fe8ab488 418 assert(ipc_importance_task_is_any_receiver_type(to_task));
743345f9
A
419
420 /* unlink the inherit from its source element */
fe8ab488
A
421 from_elem = ipc_importance_inherit_unlink(inherit);
422 assert(IIE_NULL != from_elem);
743345f9
A
423
424 /*
425 * The attribute might have pending external boosts if the attribute
426 * was given out during exec, drop them from the appropriate destination
427 * task.
428 *
429 * The attribute will not have any pending external boosts if the
430 * attribute was given out to voucher system since it would have been
431 * dropped by ipc_importance_release_value, but there is not way to
432 * detect that, thus if the attribute has a pending external boost,
433 * drop them from the appropriate destination task.
434 *
435 * The inherit attribute from exec and voucher system would not
436 * get deduped to each other, thus dropping the external boost
437 * from destination task at two different places will not have
438 * any unintended side effects.
439 */
440 assert(inherit->iii_externcnt >= inherit->iii_externdrop);
441 if (inherit->iii_donating) {
442 uint32_t assertcnt = III_EXTERN(inherit);
443
444 assert(ipc_importance_task_is_any_receiver_type(to_task));
445 assert(to_task->iit_externcnt >= inherit->iii_externcnt);
446 assert(to_task->iit_externdrop >= inherit->iii_externdrop);
447 to_task->iit_externcnt -= inherit->iii_externcnt;
448 to_task->iit_externdrop -= inherit->iii_externdrop;
449 inherit->iii_externcnt = 0;
450 inherit->iii_externdrop = 0;
451 inherit->iii_donating = FALSE;
452
453 /* adjust the internal assertions - and propagate as needed */
454 if (ipc_importance_task_check_transition(to_task, IIT_UPDATE_DROP, assertcnt)) {
455 ipc_importance_task_propagate_assertion_locked(to_task, IIT_UPDATE_DROP, TRUE);
456 }
457 } else {
458 inherit->iii_externcnt = 0;
459 inherit->iii_externdrop = 0;
460 }
461
462 /* release the reference on the source element */
fe8ab488
A
463 ipc_importance_release_locked(from_elem);
464 /* unlocked on return */
465
743345f9 466 /* release the reference on the destination task */
fe8ab488
A
467 ipc_importance_task_release(to_task);
468
743345f9 469 /* free the inherit */
fe8ab488
A
470 zfree(ipc_importance_inherit_zone, inherit);
471 break;
472 }
473 }
474}
475
476/*
477 * Routine: ipc_importance_release
478 * Purpose:
479 * Release a reference on an importance attribute value,
480 * unlinking and deallocating the attribute if the last reference.
481 * Conditions:
482 * nothing locked on entrance, nothing locked on exit.
483 * May block.
484 */
485void
486ipc_importance_release(ipc_importance_elem_t elem)
487{
488 if (IIE_NULL == elem)
489 return;
490
491 ipc_importance_lock();
492 ipc_importance_release_locked(elem);
493 /* unlocked */
494}
495
496/*
497 * Routine: ipc_importance_task_reference
498
499
500 * Purpose:
501 * Retain a reference on a task importance attribute value.
502 * Conditions:
503 * nothing locked on entrance, nothing locked on exit.
504 * caller holds a reference already.
505 */
506void
507ipc_importance_task_reference(ipc_importance_task_t task_elem)
508{
509 if (IIT_NULL == task_elem)
510 return;
511#if IIE_REF_DEBUG
512 incr_ref_counter(task_elem->iit_elem.iie_task_refs_added);
513#endif
514 ipc_importance_reference(&task_elem->iit_elem);
515}
516
517/*
518 * Routine: ipc_importance_task_release
519 * Purpose:
520 * Release a reference on a task importance attribute value,
521 * unlinking and deallocating the attribute if the last reference.
522 * Conditions:
523 * nothing locked on entrance, nothing locked on exit.
524 * May block.
525 */
526void
527ipc_importance_task_release(ipc_importance_task_t task_elem)
528{
529 if (IIT_NULL == task_elem)
530 return;
531
532 ipc_importance_lock();
533#if IIE_REF_DEBUG
534 incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
535#endif
536 ipc_importance_release_locked(&task_elem->iit_elem);
537 /* unlocked */
538}
539
540/*
541 * Routine: ipc_importance_task_release_locked
542 * Purpose:
543 * Release a reference on a task importance attribute value,
544 * unlinking and deallocating the attribute if the last reference.
545 * Conditions:
546 * importance lock held on entry, nothing locked on exit.
547 * May block.
548 */
549static void
550ipc_importance_task_release_locked(ipc_importance_task_t task_elem)
551{
552 if (IIT_NULL == task_elem) {
553 ipc_importance_unlock();
554 return;
555 }
556#if IIE_REF_DEBUG
557 incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
558#endif
559 ipc_importance_release_locked(&task_elem->iit_elem);
560 /* unlocked */
561}
562
563/*
564 * Routines for importance donation/inheritance/boosting
565 */
566
567
568/*
569 * External importance assertions are managed by the process in userspace
570 * Internal importance assertions are the responsibility of the kernel
571 * Assertions are changed from internal to external via task_importance_externalize_assertion
572 */
573
574/*
575 * Routine: ipc_importance_task_check_transition
576 * Purpose:
577 * Increase or decrement the internal task importance counter of the
578 * specified task and determine if propagation and a task policy
579 * update is required.
580 *
581 * If it is already enqueued for a policy update, steal it from that queue
582 * (as we are reversing that update before it happens).
583 *
584 * Conditions:
585 * Called with the importance lock held.
586 * It is the caller's responsibility to perform the propagation of the
587 * transition and/or policy changes by checking the return value.
588 */
589static boolean_t
590ipc_importance_task_check_transition(
591 ipc_importance_task_t task_imp,
592 iit_update_type_t type,
593 uint32_t delta)
594{
5ba3f43e 595#if IMPORTANCE_TRACE
fe8ab488 596 task_t target_task = task_imp->iit_task;
5ba3f43e 597#endif
fe8ab488
A
598 boolean_t boost = (IIT_UPDATE_HOLD == type);
599 boolean_t before_boosted, after_boosted;
600
5ba3f43e
A
601 ipc_importance_assert_held();
602
fe8ab488
A
603 if (!ipc_importance_task_is_any_receiver_type(task_imp))
604 return FALSE;
605
5ba3f43e 606#if IMPORTANCE_TRACE
3e170ce0 607 int target_pid = task_pid(target_task);
fe8ab488
A
608
609 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_START,
610 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
611#endif
612
613 /* snapshot the effective boosting status before making any changes */
614 before_boosted = (task_imp->iit_assertcnt > 0);
615
616 /* Adjust the assertcnt appropriately */
617 if (boost) {
618 task_imp->iit_assertcnt += delta;
5ba3f43e 619#if IMPORTANCE_TRACE
fe8ab488
A
620 DTRACE_BOOST6(send_boost, task_t, target_task, int, target_pid,
621 task_t, current_task(), int, proc_selfpid(), int, delta, int, task_imp->iit_assertcnt);
622#endif
623 } else {
624 // assert(delta <= task_imp->iit_assertcnt);
39037602 625 if (task_imp->iit_assertcnt < delta + IIT_EXTERN(task_imp)) {
fe8ab488 626 /* TODO: Turn this back into a panic <rdar://problem/12592649> */
fe8ab488
A
627 task_imp->iit_assertcnt = IIT_EXTERN(task_imp);
628 } else {
629 task_imp->iit_assertcnt -= delta;
630 }
5ba3f43e 631#if IMPORTANCE_TRACE
fe8ab488
A
632 // This convers both legacy and voucher-based importance.
633 DTRACE_BOOST4(drop_boost, task_t, target_task, int, target_pid, int, delta, int, task_imp->iit_assertcnt);
634#endif
635 }
636
5ba3f43e 637#if IMPORTANCE_TRACE
fe8ab488
A
638 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_END,
639 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
640#endif
641
642 /* did the change result in an effective donor status change? */
643 after_boosted = (task_imp->iit_assertcnt > 0);
644
645 if (after_boosted != before_boosted) {
646
647 /*
648 * If the task importance is already on an update queue, we just reversed the need for a
649 * pending policy update. If the queue is any other than the delayed-drop-queue, pull it
650 * off that queue and release the reference it got going onto the update queue. If it is
651 * the delayed-drop-queue we leave it in place in case it comes back into the drop state
652 * before its time delay is up.
653 *
654 * We still need to propagate the change downstream to reverse the assertcnt effects,
655 * but we no longer need to update this task's boost policy state.
656 *
657 * Otherwise, mark it as needing a policy update.
658 */
659 assert(0 == task_imp->iit_updatepolicy);
660 if (NULL != task_imp->iit_updateq) {
661 if (&ipc_importance_delayed_drop_queue != task_imp->iit_updateq) {
662 queue_remove(task_imp->iit_updateq, task_imp, ipc_importance_task_t, iit_updates);
663 task_imp->iit_updateq = NULL;
664 ipc_importance_task_release_internal(task_imp); /* can't be last ref */
665 }
666 } else {
667 task_imp->iit_updatepolicy = 1;
668 }
669 return TRUE;
670 }
671
672 return FALSE;
673}
674
675
676/*
677 * Routine: ipc_importance_task_propagate_helper
678 * Purpose:
679 * Increase or decrement the internal task importance counter of all
680 * importance tasks inheriting from the specified one. If this causes
681 * that importance task to change state, add it to the list of tasks
682 * to do a policy update against.
683 * Conditions:
684 * Called with the importance lock held.
685 * It is the caller's responsibility to iterate down the generated list
686 * and propagate any subsequent assertion changes from there.
687 */
688static void
689ipc_importance_task_propagate_helper(
690 ipc_importance_task_t task_imp,
691 iit_update_type_t type,
692 queue_t propagation)
693{
694 ipc_importance_task_t temp_task_imp;
695
696 /*
697 * iterate the downstream kmsgs, adjust their boosts,
698 * and capture the next task to adjust for each message
699 */
700
701 ipc_kmsg_t temp_kmsg;
702
703 queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
704 mach_msg_header_t *hdr = temp_kmsg->ikm_header;
705 mach_port_delta_t delta;
706 ipc_port_t port;
707
708 /* toggle the kmsg importance bit as a barrier to parallel adjusts */
709 if (IIT_UPDATE_HOLD == type) {
710 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
711 continue;
712 }
713
714 /* mark the message as now carrying importance */
715 hdr->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
716 delta = 1;
717 } else {
718 if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
719 continue;
720 }
721
722 /* clear the message as now carrying importance */
723 hdr->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
724 delta = -1;
725 }
726
727 /* determine the task importance to adjust as result (if any) */
728 port = (ipc_port_t) hdr->msgh_remote_port;
729 assert(IP_VALID(port));
730 ip_lock(port);
731 temp_task_imp = IIT_NULL;
4bd07ac2 732 if (!ipc_port_importance_delta_internal(port, IPID_OPTION_NORMAL, &delta, &temp_task_imp)) {
fe8ab488
A
733 ip_unlock(port);
734 }
735
736 /* no task importance to adjust associated with the port? */
737 if (IIT_NULL == temp_task_imp) {
738 continue;
739 }
740
741 /* hold a reference on temp_task_imp */
742
743 /* Adjust the task assertions and determine if an edge was crossed */
744 if (ipc_importance_task_check_transition(temp_task_imp, type, 1)) {
5ba3f43e 745 incr_ref_counter(temp_task_imp->iit_elem.iie_task_refs_added_transition);
fe8ab488
A
746 queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
747 /* reference donated */
748 } else {
749 ipc_importance_task_release_internal(temp_task_imp);
750 }
751 }
752
753 /*
754 * iterate the downstream importance inherits
755 * and capture the next task importance to boost for each
756 */
757 ipc_importance_inherit_t temp_inherit;
758
759 queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
760 uint32_t assertcnt = III_EXTERN(temp_inherit);
761
762 temp_task_imp = temp_inherit->iii_to_task;
763 assert(IIT_NULL != temp_task_imp);
764
765 if (IIT_UPDATE_HOLD == type) {
766 /* if no undropped externcnts in the inherit, nothing to do */
767 if (0 == assertcnt) {
768 assert(temp_inherit->iii_donating == FALSE);
769 continue;
770 }
771
772 /* nothing to do if the inherit is already donating (forced donation) */
773 if (temp_inherit->iii_donating) {
774 continue;
775 }
776
777 /* mark it donating and contribute to the task externcnts */
778 temp_inherit->iii_donating = TRUE;
779 temp_task_imp->iit_externcnt += temp_inherit->iii_externcnt;
780 temp_task_imp->iit_externdrop += temp_inherit->iii_externdrop;
781
782 } else {
783 /* if no contributing assertions, move on */
784 if (0 == assertcnt) {
785 assert(temp_inherit->iii_donating == FALSE);
786 continue;
787 }
788
789 /* nothing to do if the inherit is not donating */
790 if (!temp_inherit->iii_donating) {
791 continue;
792 }
793
794 /* mark it no longer donating */
795 temp_inherit->iii_donating = FALSE;
796
797 /* remove the contribution the inherit made to the to-task */
798 assert(IIT_EXTERN(temp_task_imp) >= III_EXTERN(temp_inherit));
799 assert(temp_task_imp->iit_externcnt >= temp_inherit->iii_externcnt);
800 assert(temp_task_imp->iit_externdrop >= temp_inherit->iii_externdrop);
801 temp_task_imp->iit_externcnt -= temp_inherit->iii_externcnt;
802 temp_task_imp->iit_externdrop -= temp_inherit->iii_externdrop;
803
804 }
805
806 /* Adjust the task assertions and determine if an edge was crossed */
807 assert(ipc_importance_task_is_any_receiver_type(temp_task_imp));
808 if (ipc_importance_task_check_transition(temp_task_imp, type, assertcnt)) {
809 ipc_importance_task_reference(temp_task_imp);
5ba3f43e 810 incr_ref_counter(temp_task_imp->iit_elem.iie_task_refs_added_transition);
fe8ab488
A
811 queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
812 }
813 }
814}
815
816/*
817 * Routine: ipc_importance_task_process_updates
818 * Purpose:
819 * Process the queue of task importances and apply the policy
820 * update called for. Only process tasks in the queue with an
821 * update timestamp less than the supplied max.
822 * Conditions:
823 * Called and returns with importance locked.
824 * May drop importance lock and block temporarily.
825 */
826static void
827ipc_importance_task_process_updates(
828 queue_t supplied_queue,
829 boolean_t boost,
830 uint64_t max_timestamp)
831{
832 ipc_importance_task_t task_imp;
833 queue_head_t second_chance;
834 queue_t queue = supplied_queue;
835
836 /*
837 * This queue will hold the task's we couldn't trylock on first pass.
838 * By using a second (private) queue, we guarantee all tasks that get
839 * entered on this queue have a timestamp under the maximum.
840 */
841 queue_init(&second_chance);
842
843 /* process any resulting policy updates */
844 retry:
845 while(!queue_empty(queue)) {
846 task_t target_task;
847 struct task_pend_token pend_token = {};
848
849 task_imp = (ipc_importance_task_t)queue_first(queue);
850 assert(0 == task_imp->iit_updatepolicy);
851 assert(queue == task_imp->iit_updateq);
852
853 /* if timestamp is too big, we're done */
854 if (task_imp->iit_updatetime > max_timestamp) {
855 break;
856 }
857
858 /* we were given a reference on each task in the queue */
859
860 /* remove it from the supplied queue */
861 queue_remove(queue, task_imp, ipc_importance_task_t, iit_updates);
862 task_imp->iit_updateq = NULL;
863
864 target_task = task_imp->iit_task;
865
866 /* Is it well on the way to exiting? */
867 if (TASK_NULL == target_task) {
868 ipc_importance_task_release_locked(task_imp);
869 /* importance unlocked */
870 ipc_importance_lock();
871 continue;
872 }
873
874 /* Has the update been reversed on the hysteresis queue? */
875 if (0 < task_imp->iit_assertcnt &&
876 queue == &ipc_importance_delayed_drop_queue) {
877 ipc_importance_task_release_locked(task_imp);
878 /* importance unlocked */
879 ipc_importance_lock();
880 continue;
881 }
882
883 /*
884 * Can we get the task lock out-of-order?
885 * If not, stick this back on the second-chance queue.
886 */
887 if (!task_lock_try(target_task)) {
888 boolean_t should_wait_lock = (queue == &second_chance);
889 task_imp->iit_updateq = &second_chance;
890
891 /*
892 * If we're already processing second-chances on
893 * tasks, keep this task on the front of the queue.
894 * We will wait for the task lock before coming
895 * back and trying again, and we have a better
896 * chance of re-acquiring the lock if we come back
897 * to it right away.
898 */
899 if (should_wait_lock){
900 task_reference(target_task);
901 queue_enter_first(&second_chance, task_imp,
902 ipc_importance_task_t, iit_updates);
903 } else {
904 queue_enter(&second_chance, task_imp,
905 ipc_importance_task_t, iit_updates);
906 }
907 ipc_importance_unlock();
908
909 if (should_wait_lock) {
910 task_lock(target_task);
911 task_unlock(target_task);
912 task_deallocate(target_task);
913 }
914
915 ipc_importance_lock();
916 continue;
917 }
918
919 /* is it going away? */
920 if (!target_task->active) {
921 task_unlock(target_task);
922 ipc_importance_task_release_locked(task_imp);
923 /* importance unlocked */
924 ipc_importance_lock();
925 continue;
926 }
927
928 /* take a task reference for while we don't have the importance lock */
929 task_reference(target_task);
930
931 /* count the transition */
932 if (boost)
933 task_imp->iit_transitions++;
934
935 ipc_importance_unlock();
936
937 /* apply the policy adjust to the target task (while it is still locked) */
938 task_update_boost_locked(target_task, boost, &pend_token);
939
940 /* complete the policy update with the task unlocked */
941 ipc_importance_task_release(task_imp);
942 task_unlock(target_task);
39037602 943 task_policy_update_complete_unlocked(target_task, &pend_token);
fe8ab488
A
944 task_deallocate(target_task);
945
946 ipc_importance_lock();
947 }
948
949 /* If there are tasks we couldn't update the first time, try again */
950 if (!queue_empty(&second_chance)) {
951 queue = &second_chance;
952 goto retry;
953 }
954}
955
956
957/*
958 * Routine: ipc_importance_task_delayed_drop_scan
959 * Purpose:
960 * The thread call routine to scan the delayed drop queue,
961 * requesting all updates with a deadline up to the last target
962 * for the thread-call (which is DENAP_DROP_SKEW beyond the first
963 * thread's optimum delay).
964 * update to drop its boost.
965 * Conditions:
966 * Nothing locked
967 */
968static void
969ipc_importance_task_delayed_drop_scan(
970 __unused void *arg1,
971 __unused void *arg2)
972{
973 ipc_importance_lock();
974
975 /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
976 ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue,
977 FALSE,
978 ipc_importance_delayed_drop_timestamp);
979
980 /* importance lock may have been temporarily dropped */
981
982 /* If there are any entries left in the queue, re-arm the call here */
983 if (!queue_empty(&ipc_importance_delayed_drop_queue)) {
984 ipc_importance_task_t task_imp;
985 uint64_t deadline;
986 uint64_t leeway;
987
988 task_imp = (ipc_importance_task_t)queue_first(&ipc_importance_delayed_drop_queue);
989
990 nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
991 deadline += task_imp->iit_updatetime;
992 ipc_importance_delayed_drop_timestamp = deadline;
993
994 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
995
996 thread_call_enter_delayed_with_leeway(
997 ipc_importance_delayed_drop_call,
998 NULL,
999 deadline,
1000 leeway,
1001 DENAP_DROP_FLAGS);
1002 } else {
1003 ipc_importance_delayed_drop_call_requested = FALSE;
1004 }
1005 ipc_importance_unlock();
1006}
1007
1008/*
1009 * Routine: ipc_importance_task_delayed_drop
1010 * Purpose:
1011 * Queue the specified task importance for delayed policy
1012 * update to drop its boost.
1013 * Conditions:
1014 * Called with the importance lock held.
1015 */
1016static void
1017ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp)
1018{
1019 uint64_t timestamp = mach_absolute_time(); /* no mach_approximate_time() in kernel */
1020
1021 assert(ipc_importance_delayed_drop_call != NULL);
1022
1023 /*
1024 * If still on an update queue from a previous change,
1025 * remove it first (and use that reference). Otherwise, take
1026 * a new reference for the delay drop update queue.
1027 */
1028 if (NULL != task_imp->iit_updateq) {
1029 queue_remove(task_imp->iit_updateq, task_imp,
1030 ipc_importance_task_t, iit_updates);
1031 } else {
1032 ipc_importance_task_reference_internal(task_imp);
1033 }
1034
1035 task_imp->iit_updateq = &ipc_importance_delayed_drop_queue;
1036 task_imp->iit_updatetime = timestamp;
1037
1038 queue_enter(&ipc_importance_delayed_drop_queue, task_imp,
1039 ipc_importance_task_t, iit_updates);
1040
1041 /* request the delayed thread-call if not already requested */
1042 if (!ipc_importance_delayed_drop_call_requested) {
1043 uint64_t deadline;
1044 uint64_t leeway;
1045
1046 nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
1047 deadline += task_imp->iit_updatetime;
1048 ipc_importance_delayed_drop_timestamp = deadline;
1049
1050 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
1051
1052 ipc_importance_delayed_drop_call_requested = TRUE;
1053 thread_call_enter_delayed_with_leeway(
1054 ipc_importance_delayed_drop_call,
1055 NULL,
1056 deadline,
1057 leeway,
1058 DENAP_DROP_FLAGS);
1059 }
1060}
1061
1062
1063/*
1064 * Routine: ipc_importance_task_propagate_assertion_locked
1065 * Purpose:
1066 * Propagate the importance transition type to every item
1067 * If this causes a boost to be applied, determine if that
1068 * boost should propagate downstream.
1069 * Conditions:
1070 * Called with the importance lock held.
1071 */
1072static void
1073ipc_importance_task_propagate_assertion_locked(
1074 ipc_importance_task_t task_imp,
1075 iit_update_type_t type,
1076 boolean_t update_task_imp)
1077{
1078 boolean_t boost = (IIT_UPDATE_HOLD == type);
1079 ipc_importance_task_t temp_task_imp;
1080 queue_head_t propagate;
1081 queue_head_t updates;
1082
1083 queue_init(&updates);
1084 queue_init(&propagate);
1085
5ba3f43e
A
1086 ipc_importance_assert_held();
1087
fe8ab488
A
1088 /*
1089 * If we're going to update the policy for the provided task,
1090 * enqueue it on the propagate queue itself. Otherwise, only
1091 * enqueue downstream things.
1092 */
1093 if (update_task_imp) {
5ba3f43e
A
1094 ipc_importance_task_reference(task_imp);
1095 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_transition);
fe8ab488
A
1096 queue_enter(&propagate, task_imp, ipc_importance_task_t, iit_props);
1097 } else {
1098 ipc_importance_task_propagate_helper(task_imp, type, &propagate);
1099 }
1100
1101 /*
1102 * for each item on the propagation list, propagate any change downstream,
1103 * adding new tasks to propagate further if they transistioned as well.
1104 */
1105 while (!queue_empty(&propagate)) {
1106 boolean_t need_update;
1107
1108 queue_remove_first(&propagate, temp_task_imp, ipc_importance_task_t, iit_props);
5ba3f43e
A
1109 /* hold a reference on temp_task_imp */
1110
fe8ab488
A
1111 assert(IIT_NULL != temp_task_imp);
1112
1113 /* only propagate for receivers not already marked as a donor */
1114 if (!ipc_importance_task_is_marked_donor(temp_task_imp) &&
1115 ipc_importance_task_is_marked_receiver(temp_task_imp)) {
1116 ipc_importance_task_propagate_helper(temp_task_imp, type, &propagate);
1117 }
1118
1119 /* if we have a policy update to apply, enqueue a reference for later processing */
1120 need_update = (0 != temp_task_imp->iit_updatepolicy);
1121 temp_task_imp->iit_updatepolicy = 0;
1122 if (need_update && TASK_NULL != temp_task_imp->iit_task) {
1123 if (NULL == temp_task_imp->iit_updateq) {
3e170ce0
A
1124
1125 /*
1126 * If a downstream task that needs an update is subjects to AppNap,
1127 * drop boosts according to the delay hysteresis. Otherwise,
1128 * immediate update it.
1129 */
1130 if (!boost && temp_task_imp != task_imp &&
1131 ipc_importance_delayed_drop_call != NULL &&
1132 ipc_importance_task_is_marked_denap_receiver(temp_task_imp)) {
1133 ipc_importance_task_delayed_drop(temp_task_imp);
fe8ab488 1134 } else {
3e170ce0
A
1135 temp_task_imp->iit_updatetime = 0;
1136 temp_task_imp->iit_updateq = &updates;
1137 ipc_importance_task_reference_internal(temp_task_imp);
1138 if (boost) {
1139 queue_enter(&updates, temp_task_imp,
1140 ipc_importance_task_t, iit_updates);
1141 } else {
1142 queue_enter_first(&updates, temp_task_imp,
1143 ipc_importance_task_t, iit_updates);
1144 }
fe8ab488
A
1145 }
1146 } else {
1147 /* Must already be on the AppNap hysteresis queue */
3e170ce0 1148 assert(ipc_importance_delayed_drop_call != NULL);
fe8ab488
A
1149 assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp));
1150 }
1151 }
5ba3f43e
A
1152
1153 ipc_importance_task_release_internal(temp_task_imp);
fe8ab488
A
1154 }
1155
1156 /* apply updates to task (may drop importance lock) */
1157 if (!queue_empty(&updates)) {
1158 ipc_importance_task_process_updates(&updates, boost, 0);
1159 }
1160}
1161
1162/*
1163 * Routine: ipc_importance_task_hold_internal_assertion_locked
1164 * Purpose:
1165 * Increment the assertion count on the task importance.
1166 * If this results in a boost state change in that task,
1167 * prepare to update task policy for this task AND, if
1168 * if not just waking out of App Nap, all down-stream
1169 * tasks that have a similar transition through inheriting
1170 * this update.
1171 * Conditions:
1172 * importance locked on entry and exit.
1173 * May temporarily drop importance lock and block.
1174 */
1175static kern_return_t
1176ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
1177{
1178 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_HOLD, count)) {
1179 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_HOLD, TRUE);
1180 }
1181 return KERN_SUCCESS;
1182}
1183
1184/*
1185 * Routine: ipc_importance_task_drop_internal_assertion_locked
1186 * Purpose:
1187 * Decrement the assertion count on the task importance.
1188 * If this results in a boost state change in that task,
1189 * prepare to update task policy for this task AND, if
1190 * if not just waking out of App Nap, all down-stream
1191 * tasks that have a similar transition through inheriting
1192 * this update.
1193 * Conditions:
1194 * importance locked on entry and exit.
1195 * May temporarily drop importance lock and block.
1196 */
1197static kern_return_t
1198ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
1199{
1200 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
1201 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
1202 }
1203 return KERN_SUCCESS;
1204}
1205
1206/*
1207 * Routine: ipc_importance_task_hold_internal_assertion
1208 * Purpose:
1209 * Increment the assertion count on the task importance.
1210 * If this results in a 0->1 change in that count,
1211 * prepare to update task policy for this task AND
1212 * (potentially) all down-stream tasks that have a
1213 * similar transition through inheriting this update.
1214 * Conditions:
1215 * Nothing locked
1216 * May block after dropping importance lock.
1217 */
1218int
1219ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
1220{
1221 int ret = KERN_SUCCESS;
1222
1223 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1224 ipc_importance_lock();
1225 ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
1226 ipc_importance_unlock();
1227 }
1228 return ret;
1229}
1230
1231/*
1232 * Routine: ipc_importance_task_drop_internal_assertion
1233 * Purpose:
1234 * Decrement the assertion count on the task importance.
1235 * If this results in a X->0 change in that count,
1236 * prepare to update task policy for this task AND
1237 * all down-stream tasks that have a similar transition
1238 * through inheriting this drop update.
1239 * Conditions:
1240 * Nothing locked on entry.
1241 * May block after dropping importance lock.
1242 */
1243kern_return_t
1244ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
1245{
1246 kern_return_t ret = KERN_SUCCESS;
1247
1248 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1249 ipc_importance_lock();
1250 ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
1251 ipc_importance_unlock();
1252 }
1253 return ret;
1254}
1255
1256/*
1257 * Routine: ipc_importance_task_hold_file_lock_assertion
1258 * Purpose:
1259 * Increment the file lock assertion count on the task importance.
1260 * If this results in a 0->1 change in that count,
1261 * prepare to update task policy for this task AND
1262 * (potentially) all down-stream tasks that have a
1263 * similar transition through inheriting this update.
1264 * Conditions:
1265 * Nothing locked
1266 * May block after dropping importance lock.
1267 */
1268kern_return_t
1269ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
1270{
1271 kern_return_t ret = KERN_SUCCESS;
1272
1273 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1274 ipc_importance_lock();
1275 ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
1276 if (KERN_SUCCESS == ret) {
1277 task_imp->iit_filelocks += count;
1278 }
1279 ipc_importance_unlock();
1280 }
1281 return ret;
1282}
1283
1284/*
1285 * Routine: ipc_importance_task_drop_file_lock_assertion
1286 * Purpose:
1287 * Decrement the assertion count on the task importance.
1288 * If this results in a X->0 change in that count,
1289 * prepare to update task policy for this task AND
1290 * all down-stream tasks that have a similar transition
1291 * through inheriting this drop update.
1292 * Conditions:
1293 * Nothing locked on entry.
1294 * May block after dropping importance lock.
1295 */
1296kern_return_t
1297ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
1298{
1299 kern_return_t ret = KERN_SUCCESS;
1300
1301 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1302 ipc_importance_lock();
1303 if (count <= task_imp->iit_filelocks) {
1304 task_imp->iit_filelocks -= count;
1305 ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
1306 } else {
1307 ret = KERN_INVALID_ARGUMENT;
1308 }
1309 ipc_importance_unlock();
1310 }
1311 return ret;
1312}
1313
1314/*
1315 * Routine: ipc_importance_task_hold_legacy_external_assertion
1316 * Purpose:
1317 * Increment the external assertion count on the task importance.
1318 * This cannot result in an 0->1 transition, as the caller must
1319 * already hold an external boost.
1320 * Conditions:
1321 * Nothing locked on entry.
1322 * May block after dropping importance lock.
1323 * A queue of task importance structures is returned
1324 * by ipc_importance_task_hold_assertion_locked(). Each
1325 * needs to be updated (outside the importance lock hold).
1326 */
1327kern_return_t
1328ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
1329{
1330 task_t target_task;
1331 uint32_t target_assertcnt;
1332 uint32_t target_externcnt;
1333 uint32_t target_legacycnt;
1334
1335 kern_return_t ret;
1336
1337 ipc_importance_lock();
1338 target_task = task_imp->iit_task;
1339
5ba3f43e 1340#if IMPORTANCE_TRACE
3e170ce0 1341 int target_pid = task_pid(target_task);
fe8ab488
A
1342
1343 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
1344 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1345#endif
1346
1347 if (IIT_LEGACY_EXTERN(task_imp) == 0) {
1348 /* Only allowed to take a new boost assertion when holding an external boost */
1349 /* save data for diagnostic printf below */
1350 target_assertcnt = task_imp->iit_assertcnt;
1351 target_externcnt = IIT_EXTERN(task_imp);
1352 target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
1353 ret = KERN_FAILURE;
1354 count = 0;
1355 } else {
1356 assert(ipc_importance_task_is_any_receiver_type(task_imp));
1357 assert(0 < task_imp->iit_assertcnt);
1358 assert(0 < IIT_EXTERN(task_imp));
1359 task_imp->iit_assertcnt += count;
1360 task_imp->iit_externcnt += count;
1361 task_imp->iit_legacy_externcnt += count;
1362 ret = KERN_SUCCESS;
1363 }
1364 ipc_importance_unlock();
1365
5ba3f43e 1366#if IMPORTANCE_TRACE
fe8ab488
A
1367 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
1368 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1369 // This covers the legacy case where a task takes an extra boost.
1370 DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, proc_selfpid(), int, count, int, task_imp->iit_assertcnt);
1371#endif
1372
1373 if (KERN_FAILURE == ret && target_task != TASK_NULL) {
1374 printf("BUG in process %s[%d]: "
1375 "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
1376 "(%d total, %d external, %d legacy-external)\n",
3e170ce0 1377 proc_name_address(target_task->bsd_info), task_pid(target_task),
fe8ab488
A
1378 target_assertcnt, target_externcnt, target_legacycnt);
1379 }
1380
1381 return(ret);
1382}
1383
1384/*
1385 * Routine: ipc_importance_task_drop_legacy_external_assertion
1386 * Purpose:
1387 * Drop the legacy external assertion count on the task and
1388 * reflect that change to total external assertion count and
1389 * then onto the internal importance count.
1390 *
1391 * If this results in a X->0 change in the internal,
1392 * count, prepare to update task policy for this task AND
1393 * all down-stream tasks that have a similar transition
1394 * through inheriting this update.
1395 * Conditions:
1396 * Nothing locked on entry.
1397 */
1398kern_return_t
1399ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
1400{
1401 int ret = KERN_SUCCESS;
1402 task_t target_task;
1403 uint32_t target_assertcnt;
1404 uint32_t target_externcnt;
1405 uint32_t target_legacycnt;
1406
1407 if (count > 1) {
1408 return KERN_INVALID_ARGUMENT;
1409 }
1410
1411 ipc_importance_lock();
1412 target_task = task_imp->iit_task;
1413
5ba3f43e 1414#if IMPORTANCE_TRACE
3e170ce0 1415 int target_pid = task_pid(target_task);
fe8ab488
A
1416
1417 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
1418 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1419#endif
1420
1421 if (count > IIT_LEGACY_EXTERN(task_imp)) {
1422 /* Process over-released its boost count - save data for diagnostic printf */
1423 /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
1424 target_assertcnt = task_imp->iit_assertcnt;
1425 target_externcnt = IIT_EXTERN(task_imp);
1426 target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
1427 ret = KERN_FAILURE;
1428 } else {
1429 /*
1430 * decrement legacy external count from the top level and reflect
1431 * into internal for this and all subsequent updates.
1432 */
1433 assert(ipc_importance_task_is_any_receiver_type(task_imp));
1434 assert(IIT_EXTERN(task_imp) >= count);
1435
1436 task_imp->iit_legacy_externdrop += count;
1437 task_imp->iit_externdrop += count;
1438
1439 /* reset extern counters (if appropriate) */
1440 if (IIT_LEGACY_EXTERN(task_imp) == 0) {
1441 if (IIT_EXTERN(task_imp) != 0) {
1442 task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
1443 task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
1444 } else {
1445 task_imp->iit_externcnt = 0;
1446 task_imp->iit_externdrop = 0;
1447 }
1448 task_imp->iit_legacy_externcnt = 0;
1449 task_imp->iit_legacy_externdrop = 0;
1450 }
1451
1452 /* reflect the drop to the internal assertion count (and effect any importance change) */
1453 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
1454 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
1455 }
1456 ret = KERN_SUCCESS;
1457 }
1458
5ba3f43e 1459#if IMPORTANCE_TRACE
fe8ab488
A
1460 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
1461 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1462#endif
1463
1464 ipc_importance_unlock();
1465
1466 /* delayed printf for user-supplied data failures */
1467 if (KERN_FAILURE == ret && TASK_NULL != target_task) {
1468 printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
3e170ce0 1469 proc_name_address(target_task->bsd_info), task_pid(target_task),
fe8ab488
A
1470 target_assertcnt, target_externcnt, target_legacycnt);
1471 }
1472
1473 return(ret);
1474}
1475
1476
1477
1478/* Transfer an assertion to legacy userspace responsibility */
1479static kern_return_t
1480ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp, uint32_t count, __unused int sender_pid)
1481{
1482 task_t target_task;
1483
1484 assert(IIT_NULL != task_imp);
1485 target_task = task_imp->iit_task;
1486
1487 if (TASK_NULL == target_task ||
1488 !ipc_importance_task_is_any_receiver_type(task_imp)) {
1489 return KERN_FAILURE;
1490 }
1491
5ba3f43e 1492#if IMPORTANCE_TRACE
3e170ce0 1493 int target_pid = task_pid(target_task);
fe8ab488
A
1494
1495 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_START,
1496 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
1497#endif
1498
1499 ipc_importance_lock();
1500 /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
1501 assert(IIT_EXTERN(task_imp) >= IIT_LEGACY_EXTERN(task_imp));
1502 task_imp->iit_legacy_externcnt += count;
1503 task_imp->iit_externcnt += count;
1504 ipc_importance_unlock();
1505
5ba3f43e 1506#if IMPORTANCE_TRACE
fe8ab488
A
1507 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_END,
1508 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1509 // This is the legacy boosting path
1510 DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, sender_pid, int, count, int, IIT_LEGACY_EXTERN(task_imp));
5ba3f43e 1511#endif /* IMPORTANCE_TRACE */
fe8ab488
A
1512
1513 return(KERN_SUCCESS);
1514}
1515
1516/*
1517 * Routine: ipc_importance_task_update_live_donor
1518 * Purpose:
1519 * Read the live donor status and update the live_donor bit/propagate the change in importance.
1520 * Conditions:
1521 * Nothing locked on entrance, nothing locked on exit.
1522 *
1523 * TODO: Need tracepoints around this function...
1524 */
1525void
1526ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp)
1527{
1528 uint32_t task_live_donor;
1529 boolean_t before_donor;
1530 boolean_t after_donor;
1531 task_t target_task;
1532
1533 assert(task_imp != NULL);
1534
1535 /*
1536 * Nothing to do if the task is not marked as expecting
1537 * live donor updates.
1538 */
1539 if (!ipc_importance_task_is_marked_live_donor(task_imp)) {
1540 return;
1541 }
1542
1543 ipc_importance_lock();
1544
1545 /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
1546 target_task = task_imp->iit_task;
1547 if (TASK_NULL == target_task) {
1548 ipc_importance_unlock();
1549 return;
1550 }
1551 before_donor = ipc_importance_task_is_marked_donor(task_imp);
1552
1553 /* snapshot task live donor status - may change, but another call will accompany the change */
39037602 1554 task_live_donor = target_task->effective_policy.tep_live_donor;
fe8ab488 1555
5ba3f43e 1556#if IMPORTANCE_TRACE
3e170ce0 1557 int target_pid = task_pid(target_task);
fe8ab488
A
1558
1559 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1560 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_START,
1561 target_pid, task_imp->iit_donor, task_live_donor, before_donor, 0);
1562#endif
1563
1564 /* update the task importance live donor status based on the task's value */
1565 task_imp->iit_donor = task_live_donor;
1566
1567 after_donor = ipc_importance_task_is_marked_donor(task_imp);
1568
1569 /* Has the effectiveness of being a donor changed as a result of this update? */
1570 if (before_donor != after_donor) {
1571 iit_update_type_t type;
1572
1573 /* propagate assertions without updating the current task policy (already handled) */
1574 if (0 == before_donor) {
1575 task_imp->iit_transitions++;
1576 type = IIT_UPDATE_HOLD;
1577 } else {
1578 type = IIT_UPDATE_DROP;
1579 }
1580 ipc_importance_task_propagate_assertion_locked(task_imp, type, FALSE);
1581 }
1582
5ba3f43e 1583#if IMPORTANCE_TRACE
fe8ab488
A
1584 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1585 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_END,
1586 target_pid, task_imp->iit_donor, task_live_donor, after_donor, 0);
1587#endif
1588
1589 ipc_importance_unlock();
1590}
1591
1592
1593/*
1594 * Routine: ipc_importance_task_mark_donor
1595 * Purpose:
1596 * Set the task importance donor flag.
1597 * Conditions:
1598 * Nothing locked on entrance, nothing locked on exit.
1599 *
1600 * This is only called while the task is being constructed,
1601 * so no need to update task policy or propagate downstream.
1602 */
1603void
1604ipc_importance_task_mark_donor(ipc_importance_task_t task_imp, boolean_t donating)
1605{
1606 assert(task_imp != NULL);
1607
1608 ipc_importance_lock();
1609
1610 int old_donor = task_imp->iit_donor;
1611
1612 task_imp->iit_donor = (donating ? 1 : 0);
1613
1614 if (task_imp->iit_donor > 0 && old_donor == 0)
1615 task_imp->iit_transitions++;
1616
1617 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1618 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_INIT_DONOR_STATE)) | DBG_FUNC_NONE,
3e170ce0 1619 task_pid(task_imp->iit_task), donating,
fe8ab488
A
1620 old_donor, task_imp->iit_donor, 0);
1621
1622 ipc_importance_unlock();
1623}
1624
1625/*
1626 * Routine: ipc_importance_task_marked_donor
1627 * Purpose:
1628 * Query the donor flag for the given task importance.
1629 * Conditions:
1630 * May be called without taking the importance lock.
1631 * In that case, donor status can change so you must
1632 * check only once for each donation event.
1633 */
1634boolean_t
1635ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp)
1636{
1637 if (IIT_NULL == task_imp) {
1638 return FALSE;
1639 }
1640 return (0 != task_imp->iit_donor);
1641}
1642
1643/*
1644 * Routine: ipc_importance_task_mark_live_donor
1645 * Purpose:
1646 * Indicate that the task is eligible for live donor updates.
1647 * Conditions:
1648 * Nothing locked on entrance, nothing locked on exit.
1649 *
1650 * This is only called while the task is being constructed.
1651 */
1652void
1653ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp, boolean_t live_donating)
1654{
1655 assert(task_imp != NULL);
1656
1657 ipc_importance_lock();
1658 task_imp->iit_live_donor = (live_donating ? 1 : 0);
1659 ipc_importance_unlock();
1660}
1661
1662/*
1663 * Routine: ipc_importance_task_marked_live_donor
1664 * Purpose:
1665 * Query the live donor and donor flags for the given task importance.
1666 * Conditions:
1667 * May be called without taking the importance lock.
1668 * In that case, donor status can change so you must
1669 * check only once for each donation event.
1670 */
1671boolean_t
1672ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp)
1673{
1674 if (IIT_NULL == task_imp) {
1675 return FALSE;
1676 }
1677 return (0 != task_imp->iit_live_donor);
1678}
1679
1680/*
1681 * Routine: ipc_importance_task_is_donor
1682 * Purpose:
1683 * Query the full donor status for the given task importance.
1684 * Conditions:
1685 * May be called without taking the importance lock.
1686 * In that case, donor status can change so you must
1687 * check only once for each donation event.
1688 */
1689boolean_t
1690ipc_importance_task_is_donor(ipc_importance_task_t task_imp)
1691{
1692 if (IIT_NULL == task_imp) {
1693 return FALSE;
1694 }
1695 return (ipc_importance_task_is_marked_donor(task_imp) ||
1696 (ipc_importance_task_is_marked_receiver(task_imp) &&
1697 task_imp->iit_assertcnt > 0));
1698}
1699
1700/*
1701 * Routine: ipc_importance_task_is_never_donor
1702 * Purpose:
1703 * Query if a given task can ever donate importance.
1704 * Conditions:
1705 * May be called without taking the importance lock.
1706 * Condition is permanent for a give task.
1707 */
1708boolean_t
1709ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp)
1710{
1711 if (IIT_NULL == task_imp) {
1712 return FALSE;
1713 }
1714 return (!ipc_importance_task_is_marked_donor(task_imp) &&
1715 !ipc_importance_task_is_marked_live_donor(task_imp) &&
1716 !ipc_importance_task_is_marked_receiver(task_imp));
1717}
1718
1719/*
1720 * Routine: ipc_importance_task_mark_receiver
1721 * Purpose:
1722 * Update the task importance receiver flag.
1723 * Conditions:
1724 * Nothing locked on entrance, nothing locked on exit.
1725 * This can only be invoked before the task is discoverable,
1726 * so no worries about atomicity(?)
1727 */
1728void
1729ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp, boolean_t receiving)
1730{
1731 assert(task_imp != NULL);
1732
1733 ipc_importance_lock();
1734 if (receiving) {
1735 assert(task_imp->iit_assertcnt == 0);
1736 assert(task_imp->iit_externcnt == 0);
1737 assert(task_imp->iit_externdrop == 0);
1738 assert(task_imp->iit_denap == 0);
1739 task_imp->iit_receiver = 1; /* task can receive importance boost */
1740 } else if (task_imp->iit_receiver) {
1741 assert(task_imp->iit_denap == 0);
1742 if (task_imp->iit_assertcnt != 0 || IIT_EXTERN(task_imp) != 0) {
1743 panic("disabling imp_receiver on task with pending importance boosts!");
1744 }
1745 task_imp->iit_receiver = 0;
1746 }
1747 ipc_importance_unlock();
1748}
1749
1750
1751/*
1752 * Routine: ipc_importance_task_marked_receiver
1753 * Purpose:
1754 * Query the receiver flag for the given task importance.
1755 * Conditions:
1756 * May be called without taking the importance lock as
1757 * the importance flag can never change after task init.
1758 */
1759boolean_t
1760ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp)
1761{
1762 return (IIT_NULL != task_imp && 0 != task_imp->iit_receiver);
1763}
1764
1765
1766/*
1767 * Routine: ipc_importance_task_mark_denap_receiver
1768 * Purpose:
1769 * Update the task importance de-nap receiver flag.
1770 * Conditions:
1771 * Nothing locked on entrance, nothing locked on exit.
1772 * This can only be invoked before the task is discoverable,
1773 * so no worries about atomicity(?)
1774 */
1775void
1776ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp, boolean_t denap)
1777{
1778 assert(task_imp != NULL);
1779
1780 ipc_importance_lock();
1781 if (denap) {
1782 assert(task_imp->iit_assertcnt == 0);
1783 assert(task_imp->iit_externcnt == 0);
1784 assert(task_imp->iit_receiver == 0);
1785 task_imp->iit_denap = 1; /* task can receive de-nap boost */
1786 } else if (task_imp->iit_denap) {
1787 assert(task_imp->iit_receiver == 0);
1788 if (0 < task_imp->iit_assertcnt || 0 < IIT_EXTERN(task_imp)) {
1789 panic("disabling de-nap on task with pending de-nap boosts!");
1790 }
1791 task_imp->iit_denap = 0;
1792 }
1793 ipc_importance_unlock();
1794}
1795
1796
1797/*
1798 * Routine: ipc_importance_task_marked_denap_receiver
1799 * Purpose:
1800 * Query the de-nap receiver flag for the given task importance.
1801 * Conditions:
1802 * May be called without taking the importance lock as
1803 * the de-nap flag can never change after task init.
1804 */
1805boolean_t
1806ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp)
1807{
1808 return (IIT_NULL != task_imp && 0 != task_imp->iit_denap);
1809}
1810
1811/*
1812 * Routine: ipc_importance_task_is_denap_receiver
1813 * Purpose:
1814 * Query the full de-nap receiver status for the given task importance.
1815 * For now, that is simply whether the receiver flag is set.
1816 * Conditions:
1817 * May be called without taking the importance lock as
1818 * the de-nap receiver flag can never change after task init.
1819 */
1820boolean_t
1821ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp)
1822{
1823 return (ipc_importance_task_is_marked_denap_receiver(task_imp));
1824}
1825
1826/*
1827 * Routine: ipc_importance_task_is_any_receiver_type
1828 * Purpose:
1829 * Query if the task is marked to receive boosts - either
1830 * importance or denap.
1831 * Conditions:
1832 * May be called without taking the importance lock as both
1833 * the importance and de-nap receiver flags can never change
1834 * after task init.
1835 */
1836boolean_t
1837ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp)
1838{
1839 return (ipc_importance_task_is_marked_receiver(task_imp) ||
1840 ipc_importance_task_is_marked_denap_receiver(task_imp));
1841}
1842
1843#if 0 /* currently unused */
1844
1845/*
1846 * Routine: ipc_importance_inherit_reference
1847 * Purpose:
1848 * Add a reference to the inherit importance element.
1849 * Conditions:
1850 * Caller most hold a reference on the inherit element.
1851 */
1852static inline void
1853ipc_importance_inherit_reference(ipc_importance_inherit_t inherit)
1854{
1855 ipc_importance_reference(&inherit->iii_elem);
1856}
1857#endif /* currently unused */
1858
1859/*
1860 * Routine: ipc_importance_inherit_release_locked
1861 * Purpose:
1862 * Release a reference on an inherit importance attribute value,
1863 * unlinking and deallocating the attribute if the last reference.
1864 * Conditions:
1865 * Entered with importance lock held, leaves with it unlocked.
1866 */
1867static inline void
1868ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit)
1869{
1870 ipc_importance_release_locked(&inherit->iii_elem);
1871}
1872
1873#if 0 /* currently unused */
1874/*
1875 * Routine: ipc_importance_inherit_release
1876 * Purpose:
1877 * Release a reference on an inherit importance attribute value,
1878 * unlinking and deallocating the attribute if the last reference.
1879 * Conditions:
1880 * nothing locked on entrance, nothing locked on exit.
1881 * May block.
1882 */
1883void
1884ipc_importance_inherit_release(ipc_importance_inherit_t inherit)
1885{
1886 if (III_NULL != inherit)
1887 ipc_importance_release(&inherit->iii_elem);
1888}
1889#endif /* 0 currently unused */
1890
1891/*
1892 * Routine: ipc_importance_for_task
1893 * Purpose:
1894 * Create a reference for the specified task's base importance
1895 * element. If the base importance element doesn't exist, make it and
1896 * bind it to the active task. If the task is inactive, there isn't
1897 * any need to return a new reference.
1898 * Conditions:
1899 * If made is true, a "made" reference is returned (for donating to
1900 * the voucher system). Otherwise an internal reference is returned.
1901 *
1902 * Nothing locked on entry. May block.
1903 */
1904ipc_importance_task_t
1905ipc_importance_for_task(task_t task, boolean_t made)
1906{
1907 ipc_importance_task_t task_elem;
1908 boolean_t first_pass = TRUE;
1909
1910 assert(TASK_NULL != task);
1911
1912 retry:
1913 /* No use returning anything for inactive task */
1914 if (!task->active)
1915 return IIT_NULL;
1916
1917 ipc_importance_lock();
1918 task_elem = task->task_imp_base;
1919 if (IIT_NULL != task_elem) {
1920 /* Add a made reference (borrowing active task ref to do it) */
1921 if (made) {
1922 if (0 == task_elem->iit_made++) {
1923 assert(IIT_REFS_MAX > IIT_REFS(task_elem));
1924 ipc_importance_task_reference_internal(task_elem);
1925 }
1926 } else {
1927 assert(IIT_REFS_MAX > IIT_REFS(task_elem));
1928 ipc_importance_task_reference_internal(task_elem);
1929 }
1930 ipc_importance_unlock();
1931 return task_elem;
1932 }
1933 ipc_importance_unlock();
1934
1935 if (!first_pass)
1936 return IIT_NULL;
1937 first_pass = FALSE;
1938
1939 /* Need to make one - may race with others (be prepared to drop) */
1940 task_elem = (ipc_importance_task_t)zalloc(ipc_importance_task_zone);
1941 if (IIT_NULL == task_elem)
1942 goto retry;
1943
1944 task_elem->iit_bits = IIE_TYPE_TASK | 2; /* one for task, one for return/made */
1945 task_elem->iit_made = (made) ? 1 : 0;
1946 task_elem->iit_task = task; /* take actual ref when we're sure */
1947 task_elem->iit_updateq = NULL;
1948 task_elem->iit_receiver = 0;
1949 task_elem->iit_denap = 0;
1950 task_elem->iit_donor = 0;
1951 task_elem->iit_live_donor = 0;
1952 task_elem->iit_updatepolicy = 0;
1953 task_elem->iit_reserved = 0;
1954 task_elem->iit_filelocks = 0;
1955 task_elem->iit_updatetime = 0;
1956 task_elem->iit_transitions = 0;
1957 task_elem->iit_assertcnt = 0;
1958 task_elem->iit_externcnt = 0;
1959 task_elem->iit_externdrop = 0;
1960 task_elem->iit_legacy_externcnt = 0;
1961 task_elem->iit_legacy_externdrop = 0;
1962#if IIE_REF_DEBUG
1963 ipc_importance_counter_init(&task_elem->iit_elem);
1964#endif
1965 queue_init(&task_elem->iit_kmsgs);
1966 queue_init(&task_elem->iit_inherits);
1967
1968 ipc_importance_lock();
1969 if (!task->active) {
1970 ipc_importance_unlock();
1971 zfree(ipc_importance_task_zone, task_elem);
1972 return IIT_NULL;
1973 }
1974
1975 /* did we lose the race? */
1976 if (IIT_NULL != task->task_imp_base) {
1977 ipc_importance_unlock();
1978 zfree(ipc_importance_task_zone, task_elem);
1979 goto retry;
1980 }
1981
1982 /* we won the race */
1983 task->task_imp_base = task_elem;
1984 task_reference(task);
1985#if DEVELOPMENT || DEBUG
1986 queue_enter(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
1987 task_importance_update_owner_info(task);
1988#endif
1989 ipc_importance_unlock();
1990
1991 return task_elem;
1992}
1993
1994#if DEVELOPMENT || DEBUG
1995void task_importance_update_owner_info(task_t task) {
1996
1997 if (task != TASK_NULL && task->task_imp_base != IIT_NULL) {
1998 ipc_importance_task_t task_elem = task->task_imp_base;
1999
3e170ce0 2000 task_elem->iit_bsd_pid = task_pid(task);
fe8ab488
A
2001 if (task->bsd_info) {
2002 strncpy(&task_elem->iit_procname[0], proc_name_address(task->bsd_info), 16);
2003 task_elem->iit_procname[16] = '\0';
2004 } else {
2005 strncpy(&task_elem->iit_procname[0], "unknown", 16);
2006 }
2007 }
2008}
2009#endif
2010
2011/*
2012 * Routine: ipc_importance_reset_locked
2013 * Purpose:
2014 * Reset a task's IPC importance (the task is going away or exec'ing)
2015 *
2016 * Remove the donor bit and legacy externalized assertions from the
2017 * current task importance and see if that wipes out downstream donations.
2018 * Conditions:
2019 * importance lock held.
2020 */
2021
2022static void
2023ipc_importance_reset_locked(ipc_importance_task_t task_imp, boolean_t donor)
2024{
2025 boolean_t before_donor, after_donor;
2026
2027 /* remove the donor bit, live-donor bit and externalized boosts */
2028 before_donor = ipc_importance_task_is_donor(task_imp);
2029 if (donor) {
2030 task_imp->iit_donor = 0;
2031 }
2032 assert(IIT_LEGACY_EXTERN(task_imp) <= IIT_EXTERN(task_imp));
2033 assert(task_imp->iit_legacy_externcnt <= task_imp->iit_externcnt);
2034 assert(task_imp->iit_legacy_externdrop <= task_imp->iit_externdrop);
2035 task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
2036 task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
2037
2038 /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
39037602 2039 if (IIT_EXTERN(task_imp) < task_imp->iit_assertcnt) {
fe8ab488
A
2040 task_imp->iit_assertcnt -= IIT_LEGACY_EXTERN(task_imp);
2041 } else {
39037602 2042 task_imp->iit_assertcnt = IIT_EXTERN(task_imp);
fe8ab488
A
2043 }
2044 task_imp->iit_legacy_externcnt = 0;
2045 task_imp->iit_legacy_externdrop = 0;
2046 after_donor = ipc_importance_task_is_donor(task_imp);
2047
2048#if DEVELOPMENT || DEBUG
2049 if (task_imp->iit_assertcnt > 0 && task_imp->iit_live_donor) {
2050 printf("Live donor task %s[%d] still has %d importance assertions after reset\n",
2051 task_imp->iit_procname, task_imp->iit_bsd_pid, task_imp->iit_assertcnt);
2052 }
2053#endif
2054
2055 /* propagate a downstream drop if there was a change in donor status */
2056 if (after_donor != before_donor) {
2057 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, FALSE);
2058 }
2059}
2060
2061/*
2062 * Routine: ipc_importance_reset
2063 * Purpose:
2064 * Reset a task's IPC importance
2065 *
2066 * The task is being reset, although staying around. Arrange to have the
2067 * external state of the task reset from the importance.
2068 * Conditions:
2069 * importance lock not held.
2070 */
2071
2072void
2073ipc_importance_reset(ipc_importance_task_t task_imp, boolean_t donor)
2074{
2075 if (IIT_NULL == task_imp) {
2076 return;
2077 }
2078 ipc_importance_lock();
2079 ipc_importance_reset_locked(task_imp, donor);
2080 ipc_importance_unlock();
2081}
2082
2083/*
2084 * Routine: ipc_importance_disconnect_task
2085 * Purpose:
2086 * Disconnect a task from its importance.
2087 *
2088 * Clear the task pointer from the importance and drop the
2089 * reference the task held on the importance object. Before
2090 * doing that, reset the effects the current task holds on
2091 * the importance and see if that wipes out downstream donations.
2092 *
2093 * We allow the upstream boosts to continue to affect downstream
2094 * even though the local task is being effectively pulled from
2095 * the chain.
2096 * Conditions:
2097 * Nothing locked.
2098 */
2099void
2100ipc_importance_disconnect_task(task_t task)
2101{
2102 ipc_importance_task_t task_imp;
2103
2104 task_lock(task);
2105 ipc_importance_lock();
2106 task_imp = task->task_imp_base;
2107
2108 /* did somebody beat us to it? */
2109 if (IIT_NULL == task_imp) {
2110 ipc_importance_unlock();
2111 task_unlock(task);
2112 return;
2113 }
2114
2115 /* disconnect the task from this importance */
2116 assert(task_imp->iit_task == task);
2117 task_imp->iit_task = TASK_NULL;
2118 task->task_imp_base = IIT_NULL;
2119 task_unlock(task);
2120
2121 /* reset the effects the current task hold on the importance */
2122 ipc_importance_reset_locked(task_imp, TRUE);
2123
2124 ipc_importance_task_release_locked(task_imp);
2125 /* importance unlocked */
2126
2127 /* deallocate the task now that the importance is unlocked */
2128 task_deallocate(task);
2129}
2130
743345f9
A
2131/*
2132 * Routine: ipc_importance_exec_switch_task
2133 * Purpose:
2134 * Switch importance task base from old task to new task in exec.
2135 *
2136 * Create an ipc importance linkage from old task to new task,
2137 * once the linkage is created, switch the importance task base
2138 * from old task to new task. After the switch, the linkage will
2139 * represent importance linkage from new task to old task with
2140 * watch port importance inheritance linked to new task.
2141 * Conditions:
2142 * Nothing locked.
2143 * Returns a reference on importance inherit.
2144 */
2145ipc_importance_inherit_t
2146ipc_importance_exec_switch_task(
2147 task_t old_task,
2148 task_t new_task)
2149{
2150 ipc_importance_inherit_t inherit = III_NULL;
2151 ipc_importance_task_t old_task_imp = IIT_NULL;
2152 ipc_importance_task_t new_task_imp = IIT_NULL;
2153
2154 task_importance_reset(old_task);
2155
2156 /* Create an importance linkage from old_task to new_task */
2157 inherit = ipc_importance_inherit_from_task(old_task, new_task);
2158 if (inherit == III_NULL) {
2159 return inherit;
2160 }
2161
2162 /* Switch task importance base from old task to new task */
2163 ipc_importance_lock();
2164
2165 old_task_imp = old_task->task_imp_base;
2166 new_task_imp = new_task->task_imp_base;
2167
2168 old_task_imp->iit_task = new_task;
2169 new_task_imp->iit_task = old_task;
2170
2171 old_task->task_imp_base = new_task_imp;
2172 new_task->task_imp_base = old_task_imp;
2173
2174#if DEVELOPMENT || DEBUG
2175 /*
2176 * Update the pid an proc name for importance base if any
2177 */
2178 task_importance_update_owner_info(new_task);
2179#endif
2180 ipc_importance_unlock();
2181
2182 return inherit;
2183}
2184
4bd07ac2
A
2185/*
2186 * Routine: ipc_importance_check_circularity
2187 * Purpose:
2188 * Check if queueing "port" in a message for "dest"
2189 * would create a circular group of ports and messages.
2190 *
2191 * If no circularity (FALSE returned), then "port"
2192 * is changed from "in limbo" to "in transit".
2193 *
2194 * That is, we want to set port->ip_destination == dest,
2195 * but guaranteeing that this doesn't create a circle
2196 * port->ip_destination->ip_destination->... == port
2197 *
2198 * Additionally, if port was successfully changed to "in transit",
2199 * propagate boost assertions from the "in limbo" port to all
2200 * the ports in the chain, and, if the destination task accepts
2201 * boosts, to the destination task.
2202 *
2203 * Conditions:
2204 * No ports locked. References held for "port" and "dest".
2205 */
2206
2207boolean_t
2208ipc_importance_check_circularity(
2209 ipc_port_t port,
2210 ipc_port_t dest)
2211{
2212 ipc_importance_task_t imp_task = IIT_NULL;
2213 ipc_importance_task_t release_imp_task = IIT_NULL;
2214 boolean_t imp_lock_held = FALSE;
2215 int assertcnt = 0;
2216 ipc_port_t base;
5ba3f43e
A
2217 sync_qos_count_t sync_qos_delta_add[THREAD_QOS_LAST] = {0};
2218 sync_qos_count_t sync_qos_delta_sub[THREAD_QOS_LAST] = {0};
2219 boolean_t update_knote = FALSE;
4bd07ac2
A
2220
2221 assert(port != IP_NULL);
2222 assert(dest != IP_NULL);
2223
2224 if (port == dest)
2225 return TRUE;
2226 base = dest;
2227
2228 /* port is in limbo, so donation status is safe to latch */
2229 if (port->ip_impdonation != 0) {
2230 imp_lock_held = TRUE;
2231 ipc_importance_lock();
2232 }
2233
2234 /*
2235 * First try a quick check that can run in parallel.
2236 * No circularity if dest is not in transit.
2237 */
2238 ip_lock(port);
2239
2240 /*
2241 * Even if port is just carrying assertions for others,
2242 * we need the importance lock.
2243 */
2244 if (port->ip_impcount > 0 && !imp_lock_held) {
2245 if (!ipc_importance_lock_try()) {
2246 ip_unlock(port);
2247 ipc_importance_lock();
2248 ip_lock(port);
2249 }
2250 imp_lock_held = TRUE;
2251 }
2252
2253 if (ip_lock_try(dest)) {
2254 if (!ip_active(dest) ||
2255 (dest->ip_receiver_name != MACH_PORT_NULL) ||
2256 (dest->ip_destination == IP_NULL))
2257 goto not_circular;
2258
2259 /* dest is in transit; further checking necessary */
2260
2261 ip_unlock(dest);
2262 }
2263 ip_unlock(port);
2264
2265 /*
2266 * We're about to pay the cost to serialize,
2267 * just go ahead and grab importance lock.
2268 */
2269 if (!imp_lock_held) {
2270 ipc_importance_lock();
2271 imp_lock_held = TRUE;
2272 }
2273
2274 ipc_port_multiple_lock(); /* massive serialization */
2275
2276 /*
2277 * Search for the end of the chain (a port not in transit),
2278 * acquiring locks along the way.
2279 */
2280
2281 for (;;) {
2282 ip_lock(base);
2283
2284 if (!ip_active(base) ||
2285 (base->ip_receiver_name != MACH_PORT_NULL) ||
2286 (base->ip_destination == IP_NULL))
2287 break;
2288
2289 base = base->ip_destination;
2290 }
2291
2292 /* all ports in chain from dest to base, inclusive, are locked */
2293
2294 if (port == base) {
2295 /* circularity detected! */
2296
2297 ipc_port_multiple_unlock();
2298
2299 /* port (== base) is in limbo */
2300
2301 assert(ip_active(port));
2302 assert(port->ip_receiver_name == MACH_PORT_NULL);
2303 assert(port->ip_destination == IP_NULL);
2304
2305 while (dest != IP_NULL) {
2306 ipc_port_t next;
2307
2308 /* dest is in transit or in limbo */
2309
2310 assert(ip_active(dest));
2311 assert(dest->ip_receiver_name == MACH_PORT_NULL);
2312
2313 next = dest->ip_destination;
2314 ip_unlock(dest);
2315 dest = next;
2316 }
2317
2318 if (imp_lock_held)
2319 ipc_importance_unlock();
2320
2321 return TRUE;
2322 }
2323
2324 /*
2325 * The guarantee: lock port while the entire chain is locked.
2326 * Once port is locked, we can take a reference to dest,
2327 * add port to the chain, and unlock everything.
2328 */
2329
2330 ip_lock(port);
2331 ipc_port_multiple_unlock();
2332
5ba3f43e
A
2333not_circular:
2334 imq_lock(&base->ip_messages);
4bd07ac2
A
2335
2336 /* port is in limbo */
2337
2338 assert(ip_active(port));
2339 assert(port->ip_receiver_name == MACH_PORT_NULL);
2340 assert(port->ip_destination == IP_NULL);
2341
2342 ip_reference(dest);
2343 port->ip_destination = dest;
2344
2345 /* must have been in limbo or still bound to a task */
2346 assert(port->ip_tempowner != 0);
2347
2348 /*
2349 * We delayed dropping assertions from a specific task.
2350 * Cache that info now (we'll drop assertions and the
2351 * task reference below).
2352 */
2353 release_imp_task = port->ip_imp_task;
2354 if (IIT_NULL != release_imp_task) {
2355 port->ip_imp_task = IIT_NULL;
2356 }
2357 assertcnt = port->ip_impcount;
2358
2359 /* take the port out of limbo w.r.t. assertions */
2360 port->ip_tempowner = 0;
2361
5ba3f43e
A
2362 /* Capture the sync qos count delta */
2363 for (int i = 0; i < THREAD_QOS_LAST; i++) {
2364 sync_qos_delta_add[i] = port_sync_qos(port, i);
2365 }
2366
4bd07ac2
A
2367 /* now unlock chain */
2368
2369 ip_unlock(port);
2370
2371 for (;;) {
2372
2373 /* every port along chain track assertions behind it */
2374 ipc_port_impcount_delta(dest, assertcnt, base);
5ba3f43e 2375 update_knote = ipc_port_sync_qos_delta(dest, sync_qos_delta_add, sync_qos_delta_sub);
4bd07ac2
A
2376
2377 if (dest == base)
2378 break;
2379
2380 /* port is in transit */
2381
2382 assert(ip_active(dest));
2383 assert(dest->ip_receiver_name == MACH_PORT_NULL);
2384 assert(dest->ip_destination != IP_NULL);
2385 assert(dest->ip_tempowner == 0);
2386
2387 port = dest->ip_destination;
2388 ip_unlock(dest);
2389 dest = port;
2390 }
2391
2392 /* base is not in transit */
2393 assert(!ip_active(base) ||
2394 (base->ip_receiver_name != MACH_PORT_NULL) ||
2395 (base->ip_destination == IP_NULL));
2396
2397 /*
2398 * Find the task to boost (if any).
2399 * We will boost "through" ports that don't know
2400 * about inheritance to deliver receive rights that
2401 * do.
2402 */
2403 if (ip_active(base) && (assertcnt > 0)) {
2404 assert(imp_lock_held);
2405 if (base->ip_tempowner != 0) {
2406 if (IIT_NULL != base->ip_imp_task) {
2407 /* specified tempowner task */
2408 imp_task = base->ip_imp_task;
2409 assert(ipc_importance_task_is_any_receiver_type(imp_task));
2410 }
2411 /* otherwise don't boost current task */
2412
2413 } else if (base->ip_receiver_name != MACH_PORT_NULL) {
2414 ipc_space_t space = base->ip_receiver;
2415
2416 /* only spaces with boost-accepting tasks */
2417 if (space->is_task != TASK_NULL &&
2418 ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base))
2419 imp_task = space->is_task->task_imp_base;
2420 }
2421
2422 /* take reference before unlocking base */
2423 if (imp_task != IIT_NULL) {
2424 ipc_importance_task_reference(imp_task);
2425 }
2426 }
2427
5ba3f43e
A
2428 if (update_knote) {
2429 KNOTE(&base->ip_messages.imq_klist, 0);
2430 }
2431 imq_unlock(&base->ip_messages);
4bd07ac2
A
2432 ip_unlock(base);
2433
2434 /*
2435 * Transfer assertions now that the ports are unlocked.
2436 * Avoid extra overhead if transferring to/from the same task.
2437 *
2438 * NOTE: If a transfer is occurring, the new assertions will
2439 * be added to imp_task BEFORE the importance lock is unlocked.
2440 * This is critical - to avoid decrements coming from the kmsgs
2441 * beating the increment to the task.
2442 */
2443 boolean_t transfer_assertions = (imp_task != release_imp_task);
2444
2445 if (imp_task != IIT_NULL) {
2446 assert(imp_lock_held);
2447 if (transfer_assertions)
2448 ipc_importance_task_hold_internal_assertion_locked(imp_task, assertcnt);
2449 }
2450
2451 if (release_imp_task != IIT_NULL) {
2452 assert(imp_lock_held);
2453 if (transfer_assertions)
2454 ipc_importance_task_drop_internal_assertion_locked(release_imp_task, assertcnt);
2455 }
2456
2457 if (imp_lock_held)
2458 ipc_importance_unlock();
2459
2460 if (imp_task != IIT_NULL)
2461 ipc_importance_task_release(imp_task);
2462
2463 if (release_imp_task != IIT_NULL)
2464 ipc_importance_task_release(release_imp_task);
2465
2466 return FALSE;
2467}
2468
fe8ab488
A
2469/*
2470 * Routine: ipc_importance_send
2471 * Purpose:
2472 * Post the importance voucher attribute [if sent] or a static
2473 * importance boost depending upon options and conditions.
2474 * Conditions:
2475 * Destination port locked on entry and exit, may be dropped during the call.
2476 * Returns:
2477 * A boolean identifying if the port lock was tempoarily dropped.
2478 */
2479boolean_t
2480ipc_importance_send(
2481 ipc_kmsg_t kmsg,
2482 mach_msg_option_t option)
2483{
2484 ipc_port_t port = (ipc_port_t) kmsg->ikm_header->msgh_remote_port;
2485 boolean_t port_lock_dropped = FALSE;
2486 ipc_importance_elem_t elem;
2487 task_t task;
2488 ipc_importance_task_t task_imp;
2489 kern_return_t kr;
2490
fe8ab488
A
2491 assert(IP_VALID(port));
2492
2493 /* If no donation to be made, return quickly */
2494 if ((port->ip_impdonation == 0) ||
2495 (option & MACH_SEND_NOIMPORTANCE) != 0) {
2496 return port_lock_dropped;
2497 }
2498
2499 task = current_task();
2500
2501 /* If forced sending a static boost, go update the port */
2502 if ((option & MACH_SEND_IMPORTANCE) != 0) {
5ba3f43e
A
2503 /* acquire the importance lock while trying to hang on to port lock */
2504 if (!ipc_importance_lock_try()) {
2505 port_lock_dropped = TRUE;
2506 ip_unlock(port);
2507 ipc_importance_lock();
2508 }
fe8ab488
A
2509 goto portupdate;
2510 }
2511
2512 task_imp = task->task_imp_base;
2513 assert(IIT_NULL != task_imp);
2514
2515 /* If the sender can never donate importance, nothing to do */
2516 if (ipc_importance_task_is_never_donor(task_imp)) {
2517 return port_lock_dropped;
2518 }
2519
2520 elem = IIE_NULL;
2521
2522 /* If importance receiver and passing a voucher, look for importance in there */
2523 if (IP_VALID(kmsg->ikm_voucher) &&
2524 ipc_importance_task_is_marked_receiver(task_imp)) {
2525 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2526 mach_voucher_attr_value_handle_array_size_t val_count;
2527 ipc_voucher_t voucher;
2528
2529 assert(ip_kotype(kmsg->ikm_voucher) == IKOT_VOUCHER);
2530 voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
2531
2532 /* check to see if the voucher has an importance attribute */
2533 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
2534 kr = mach_voucher_attr_control_get_values(ipc_importance_control, voucher,
2535 vals, &val_count);
2536 assert(KERN_SUCCESS == kr);
2537
2538 /*
2539 * Only use importance associated with our task (either directly
2540 * or through an inherit that donates to our task).
2541 */
2542 if (0 < val_count) {
2543 ipc_importance_elem_t check_elem;
2544
2545 check_elem = (ipc_importance_elem_t)vals[0];
2546 assert(IIE_NULL != check_elem);
2547 if (IIE_TYPE_INHERIT == IIE_TYPE(check_elem)) {
2548 ipc_importance_inherit_t inherit;
2549 inherit = (ipc_importance_inherit_t) check_elem;
2550 if (inherit->iii_to_task == task_imp) {
2551 elem = check_elem;
2552 }
2553 } else if (check_elem == (ipc_importance_elem_t)task_imp) {
2554 elem = check_elem;
2555 }
2556 }
2557 }
2558
2559 /* If we haven't found an importance attribute to send yet, use the task's */
2560 if (IIE_NULL == elem) {
2561 elem = (ipc_importance_elem_t)task_imp;
2562 }
2563
2564 /* take a reference for the message to hold */
2565 ipc_importance_reference_internal(elem);
2566
2567 /* acquire the importance lock while trying to hang on to port lock */
2568 if (!ipc_importance_lock_try()) {
2569 port_lock_dropped = TRUE;
2570 ip_unlock(port);
2571 ipc_importance_lock();
2572 }
2573
2574 /* link kmsg onto the donor element propagation chain */
2575 ipc_importance_kmsg_link(kmsg, elem);
2576 /* elem reference transfered to kmsg */
2577
2578 incr_ref_counter(elem->iie_kmsg_refs_added);
2579
2580 /* If the sender isn't currently a donor, no need to apply boost */
2581 if (!ipc_importance_task_is_donor(task_imp)) {
2582 ipc_importance_unlock();
2583
2584 /* re-acquire port lock, if needed */
2585 if (TRUE == port_lock_dropped)
2586 ip_lock(port);
2587
2588 return port_lock_dropped;
2589 }
2590
5ba3f43e 2591portupdate:
fe8ab488
A
2592 /* Mark the fact that we are (currently) donating through this message */
2593 kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
2594
2595 /*
2596 * If we need to relock the port, do it with the importance still locked.
2597 * This assures we get to add the importance boost through the port to
4bd07ac2 2598 * the task BEFORE anyone else can attempt to undo that operation if
fe8ab488
A
2599 * the sender lost donor status.
2600 */
2601 if (TRUE == port_lock_dropped) {
2602 ip_lock(port);
2603 }
fe8ab488 2604
5ba3f43e
A
2605 ipc_importance_assert_held();
2606
2607#if IMPORTANCE_TRACE
fe8ab488
A
2608 if (kdebug_enable) {
2609 mach_msg_max_trailer_t *dbgtrailer = (mach_msg_max_trailer_t *)
2610 ((vm_offset_t)kmsg->ikm_header + round_msg(kmsg->ikm_header->msgh_size));
2611 unsigned int sender_pid = dbgtrailer->msgh_audit.val[5];
2612 mach_msg_id_t imp_msgh_id = kmsg->ikm_header->msgh_id;
2613 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_SEND)) | DBG_FUNC_START,
3e170ce0 2614 task_pid(task), sender_pid, imp_msgh_id, 0, 0);
fe8ab488 2615 }
5ba3f43e 2616#endif /* IMPORTANCE_TRACE */
fe8ab488 2617
4bd07ac2
A
2618 mach_port_delta_t delta = 1;
2619 boolean_t need_port_lock;
2620 task_imp = IIT_NULL;
2621
2622 /* adjust port boost count (with importance and port locked) */
2623 need_port_lock = ipc_port_importance_delta_internal(port, IPID_OPTION_NORMAL, &delta, &task_imp);
5ba3f43e 2624 /* hold a reference on task_imp */
4bd07ac2
A
2625
2626 /* if we need to adjust a task importance as a result, apply that here */
2627 if (IIT_NULL != task_imp && delta != 0) {
2628 assert(delta == 1);
2629
2630 /* if this results in a change of state, propagate the transistion */
2631 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_HOLD, delta)) {
2632
2633 /* can't hold the port lock during task transition(s) */
2634 if (!need_port_lock) {
2635 need_port_lock = TRUE;
2636 ip_unlock(port);
2637 }
2638 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_HOLD, TRUE);
2639 }
2640 }
2641
5ba3f43e
A
2642 if (task_imp) {
2643 ipc_importance_task_release_locked(task_imp);
2644 /* importance unlocked */
2645 } else {
2646 ipc_importance_unlock();
2647 }
4bd07ac2
A
2648
2649 if (need_port_lock) {
fe8ab488
A
2650 port_lock_dropped = TRUE;
2651 ip_lock(port);
2652 }
4bd07ac2 2653
fe8ab488
A
2654 return port_lock_dropped;
2655}
2656
2657/*
743345f9 2658 * Routine: ipc_importance_inherit_from_kmsg
fe8ab488
A
2659 * Purpose:
2660 * Create a "made" reference for an importance attribute representing
2661 * an inheritance between the sender of a message (if linked) and the
2662 * current task importance. If the message is not linked, a static
2663 * boost may be created, based on the boost state of the message.
2664 *
2665 * Any transfer from kmsg linkage to inherit linkage must be atomic.
2666 *
2667 * If the task is inactive, there isn't any need to return a new reference.
2668 * Conditions:
2669 * Nothing locked on entry. May block.
2670 */
2671static ipc_importance_inherit_t
743345f9 2672ipc_importance_inherit_from_kmsg(ipc_kmsg_t kmsg)
fe8ab488
A
2673{
2674 ipc_importance_task_t task_imp = IIT_NULL;
2675 ipc_importance_elem_t from_elem = kmsg->ikm_importance;
2676 ipc_importance_elem_t elem;
2677 task_t task_self = current_task();
2678
2679 ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
2680 ipc_importance_inherit_t inherit = III_NULL;
2681 ipc_importance_inherit_t alloc = III_NULL;
fe8ab488
A
2682 boolean_t cleared_self_donation = FALSE;
2683 boolean_t donating;
2684 uint32_t depth = 1;
2685
2686 /* The kmsg must have an importance donor or static boost to proceed */
2687 if (IIE_NULL == kmsg->ikm_importance &&
2688 !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
2689 return III_NULL;
2690 }
2691
2692 /*
2693 * No need to set up an inherit linkage if the dest isn't a receiver
2694 * of one type or the other.
2695 */
2696 if (!ipc_importance_task_is_any_receiver_type(task_self->task_imp_base)) {
2697 ipc_importance_lock();
2698 goto out_locked;
2699 }
2700
2701 /* Grab a reference on the importance of the destination */
2702 task_imp = ipc_importance_for_task(task_self, FALSE);
2703
2704 ipc_importance_lock();
2705
2706 if (IIT_NULL == task_imp) {
2707 goto out_locked;
2708 }
2709
2710 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_inherit_from);
2711
2712 /* If message is already associated with an inherit... */
2713 if (IIE_TYPE_INHERIT == IIE_TYPE(from_elem)) {
2714 ipc_importance_inherit_t from_inherit = (ipc_importance_inherit_t)from_elem;
2715
2716 /* already targeting our task? - just use it */
2717 if (from_inherit->iii_to_task == task_imp) {
2718 /* clear self-donation if not also present in inherit */
2719 if (!from_inherit->iii_donating &&
2720 MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
2721 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
2722 cleared_self_donation = TRUE;
2723 }
2724 inherit = from_inherit;
2725
2726 } else if (III_DEPTH_MAX == III_DEPTH(from_inherit)) {
2727 ipc_importance_task_t to_task;
2728 ipc_importance_elem_t unlinked_from;
2729
2730 /*
2731 * Chain too long. Switch to looking
2732 * directly at the from_inherit's to-task
2733 * as our source of importance.
2734 */
2735 to_task = from_inherit->iii_to_task;
2736 ipc_importance_task_reference(to_task);
2737 from_elem = (ipc_importance_elem_t)to_task;
2738 depth = III_DEPTH_RESET | 1;
2739
2740 /* Fixup the kmsg linkage to reflect change */
2741 unlinked_from = ipc_importance_kmsg_unlink(kmsg);
2742 assert(unlinked_from == (ipc_importance_elem_t)from_inherit);
2743 ipc_importance_kmsg_link(kmsg, from_elem);
2744 ipc_importance_inherit_release_locked(from_inherit);
2745 /* importance unlocked */
2746 ipc_importance_lock();
2747
2748 } else {
2749 /* inheriting from an inherit */
2750 depth = from_inherit->iii_depth + 1;
2751 }
2752 }
2753
2754 /*
2755 * Don't allow a task to inherit from itself (would keep it permanently
2756 * boosted even if all other donors to the task went away).
2757 */
2758
2759 if (from_elem == (ipc_importance_elem_t)task_imp) {
2760 goto out_locked;
2761 }
2762
2763 /*
2764 * But if the message isn't associated with any linked source, it is
2765 * intended to be permanently boosting (static boost from kernel).
2766 * In that case DO let the process permanently boost itself.
2767 */
2768 if (IIE_NULL == from_elem) {
2769 assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
2770 ipc_importance_task_reference_internal(task_imp);
2771 from_elem = (ipc_importance_elem_t)task_imp;
2772 }
2773
2774 /*
2775 * Now that we have the from_elem figured out,
2776 * check to see if we already have an inherit for this pairing
2777 */
2778 while (III_NULL == inherit) {
39037602 2779 inherit = ipc_importance_inherit_find(from_elem, task_imp, depth);
fe8ab488
A
2780
2781 /* Do we have to allocate a new inherit */
2782 if (III_NULL == inherit) {
2783 if (III_NULL != alloc) {
2784 break;
2785 }
2786
2787 /* allocate space */
2788 ipc_importance_unlock();
2789 alloc = (ipc_importance_inherit_t)
2790 zalloc(ipc_importance_inherit_zone);
2791 ipc_importance_lock();
2792 }
2793 }
2794
2795 /* snapshot the donating status while we have importance locked */
2796 donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
2797
2798 if (III_NULL != inherit) {
2799 /* We found one, piggyback on that */
2800 assert(0 < III_REFS(inherit));
2801 assert(0 < IIE_REFS(inherit->iii_from_elem));
2802 assert(inherit->iii_externcnt >= inherit->iii_made);
2803
2804 /* add in a made reference */
2805 if (0 == inherit->iii_made++) {
2806 assert(III_REFS_MAX > III_REFS(inherit));
2807 ipc_importance_inherit_reference_internal(inherit);
2808 }
2809
2810 /* Reflect the inherit's change of status into the task boosts */
2811 if (0 == III_EXTERN(inherit)) {
2812 assert(!inherit->iii_donating);
2813 inherit->iii_donating = donating;
2814 if (donating) {
2815 task_imp->iit_externcnt += inherit->iii_externcnt;
2816 task_imp->iit_externdrop += inherit->iii_externdrop;
2817 }
2818 } else {
2819 assert(donating == inherit->iii_donating);
2820 }
2821
2822 /* add in a external reference for this use of the inherit */
2823 inherit->iii_externcnt++;
fe8ab488
A
2824 } else {
2825 /* initialize the previously allocated space */
2826 inherit = alloc;
2827 inherit->iii_bits = IIE_TYPE_INHERIT | 1;
2828 inherit->iii_made = 1;
2829 inherit->iii_externcnt = 1;
2830 inherit->iii_externdrop = 0;
2831 inherit->iii_depth = depth;
2832 inherit->iii_to_task = task_imp;
2833 inherit->iii_from_elem = IIE_NULL;
2834 queue_init(&inherit->iii_kmsgs);
fe8ab488 2835
fe8ab488
A
2836 if (donating) {
2837 inherit->iii_donating = TRUE;
fe8ab488
A
2838 } else {
2839 inherit->iii_donating = FALSE;
2840 }
2841
2842 /*
2843 * Chain our new inherit on the element it inherits from.
2844 * The new inherit takes our reference on from_elem.
2845 */
2846 ipc_importance_inherit_link(inherit, from_elem);
2847
2848#if IIE_REF_DEBUG
2849 ipc_importance_counter_init(&inherit->iii_elem);
2850 from_elem->iie_kmsg_refs_inherited++;
2851 task_imp->iit_elem.iie_task_refs_inherited++;
2852#endif
2853 }
2854
2855 out_locked:
2856 /*
2857 * for those paths that came straight here: snapshot the donating status
2858 * (this should match previous snapshot for other paths).
2859 */
2860 donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
2861
2862 /* unlink the kmsg inheritance (if any) */
2863 elem = ipc_importance_kmsg_unlink(kmsg);
2864 assert(elem == from_elem);
2865
39037602
A
2866 /* If found inherit and donating, reflect that in the task externcnt */
2867 if (III_NULL != inherit && donating) {
2868 task_imp->iit_externcnt++;
2869 /* The owner of receive right might have changed, take the internal assertion */
2870 ipc_importance_task_hold_internal_assertion_locked(task_imp, 1);
2871 /* may have dropped and retaken importance lock */
2872 }
2873
fe8ab488
A
2874 /* If we didn't create a new inherit, we have some resources to release */
2875 if (III_NULL == inherit || inherit != alloc) {
2876 if (IIE_NULL != from_elem) {
2877 if (III_NULL != inherit) {
2878 incr_ref_counter(from_elem->iie_kmsg_refs_coalesced);
2879 } else {
2880 incr_ref_counter(from_elem->iie_kmsg_refs_dropped);
2881 }
2882 ipc_importance_release_locked(from_elem);
2883 /* importance unlocked */
2884 } else {
2885 ipc_importance_unlock();
2886 }
2887
2888 if (IIT_NULL != task_imp) {
2889 if (III_NULL != inherit) {
2890 incr_ref_counter(task_imp->iit_elem.iie_task_refs_coalesced);
2891 }
2892 ipc_importance_task_release(task_imp);
2893 }
2894
2895 if (III_NULL != alloc)
2896 zfree(ipc_importance_inherit_zone, alloc);
2897 } else {
2898 /* from_elem and task_imp references transferred to new inherit */
2899 ipc_importance_unlock();
2900 }
2901
4bd07ac2
A
2902 /*
2903 * decrement port boost count
2904 * This is OK to do without the importance lock as we atomically
2905 * unlinked the kmsg and snapshot the donating state while holding
2906 * the importance lock
2907 */
39037602 2908 if (donating || cleared_self_donation) {
fe8ab488 2909 ip_lock(port);
39037602 2910 /* drop importance from port and destination task */
4bd07ac2 2911 if (ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
fe8ab488
A
2912 ip_unlock(port);
2913 }
2914 }
2915
2916 if (III_NULL != inherit) {
2917 /* have an associated importance attr, even if currently not donating */
2918 kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
2919 } else {
2920 /* we won't have an importance attribute associated with our message */
2921 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
2922 }
2923
2924 return inherit;
2925}
2926
743345f9
A
2927/*
2928 * Routine: ipc_importance_inherit_from_task
2929 * Purpose:
2930 * Create a reference for an importance attribute representing
2931 * an inheritance between the to_task and from_task. The iii
2932 * created will be marked as III_FLAGS_FOR_OTHERS.
2933 *
2934 * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
2935 *
2936 * If the task is inactive, there isn't any need to return a new reference.
2937 * Conditions:
2938 * Nothing locked on entry. May block.
2939 * It should not be called from voucher subsystem.
2940 */
2941static ipc_importance_inherit_t
2942ipc_importance_inherit_from_task(
2943 task_t from_task,
2944 task_t to_task)
2945{
2946 ipc_importance_task_t to_task_imp = IIT_NULL;
2947 ipc_importance_task_t from_task_imp = IIT_NULL;
2948 ipc_importance_elem_t from_elem = IIE_NULL;
2949
2950 ipc_importance_inherit_t inherit = III_NULL;
2951 ipc_importance_inherit_t alloc = III_NULL;
2952 boolean_t donating;
2953 uint32_t depth = 1;
2954
2955 to_task_imp = ipc_importance_for_task(to_task, FALSE);
2956 from_task_imp = ipc_importance_for_task(from_task, FALSE);
2957 from_elem = (ipc_importance_elem_t)from_task_imp;
2958
2959 ipc_importance_lock();
2960
2961 if (IIT_NULL == to_task_imp || IIT_NULL == from_task_imp) {
2962 goto out_locked;
2963 }
2964
2965 /*
2966 * No need to set up an inherit linkage if the to_task or from_task
2967 * isn't a receiver of one type or the other.
2968 */
2969 if (!ipc_importance_task_is_any_receiver_type(to_task_imp) ||
2970 !ipc_importance_task_is_any_receiver_type(from_task_imp)) {
2971 goto out_locked;
2972 }
2973
2974 /* Do not allow to create a linkage to self */
2975 if (to_task_imp == from_task_imp) {
2976 goto out_locked;
2977 }
2978
2979 incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_added_inherit_from);
2980 incr_ref_counter(from_elem->iie_kmsg_refs_added);
2981
2982 /*
2983 * Now that we have the from_elem figured out,
2984 * check to see if we already have an inherit for this pairing
2985 */
2986 while (III_NULL == inherit) {
2987 inherit = ipc_importance_inherit_find(from_elem, to_task_imp, depth);
2988
2989 /* Do we have to allocate a new inherit */
2990 if (III_NULL == inherit) {
2991 if (III_NULL != alloc) {
2992 break;
2993 }
2994
2995 /* allocate space */
2996 ipc_importance_unlock();
2997 alloc = (ipc_importance_inherit_t)
2998 zalloc(ipc_importance_inherit_zone);
2999 ipc_importance_lock();
3000 }
3001 }
3002
3003 /* snapshot the donating status while we have importance locked */
3004 donating = ipc_importance_task_is_donor(from_task_imp);
3005
3006 if (III_NULL != inherit) {
3007 /* We found one, piggyback on that */
3008 assert(0 < III_REFS(inherit));
3009 assert(0 < IIE_REFS(inherit->iii_from_elem));
3010
3011 /* Take a reference for inherit */
3012 assert(III_REFS_MAX > III_REFS(inherit));
3013 ipc_importance_inherit_reference_internal(inherit);
3014
3015 /* Reflect the inherit's change of status into the task boosts */
3016 if (0 == III_EXTERN(inherit)) {
3017 assert(!inherit->iii_donating);
3018 inherit->iii_donating = donating;
3019 if (donating) {
3020 to_task_imp->iit_externcnt += inherit->iii_externcnt;
3021 to_task_imp->iit_externdrop += inherit->iii_externdrop;
3022 }
3023 } else {
3024 assert(donating == inherit->iii_donating);
3025 }
3026
3027 /* add in a external reference for this use of the inherit */
3028 inherit->iii_externcnt++;
3029 } else {
3030 /* initialize the previously allocated space */
3031 inherit = alloc;
3032 inherit->iii_bits = IIE_TYPE_INHERIT | 1;
3033 inherit->iii_made = 0;
3034 inherit->iii_externcnt = 1;
3035 inherit->iii_externdrop = 0;
3036 inherit->iii_depth = depth;
3037 inherit->iii_to_task = to_task_imp;
3038 inherit->iii_from_elem = IIE_NULL;
3039 queue_init(&inherit->iii_kmsgs);
3040
3041 if (donating) {
3042 inherit->iii_donating = TRUE;
3043 } else {
3044 inherit->iii_donating = FALSE;
3045 }
3046
3047 /*
3048 * Chain our new inherit on the element it inherits from.
3049 * The new inherit takes our reference on from_elem.
3050 */
3051 ipc_importance_inherit_link(inherit, from_elem);
3052
3053#if IIE_REF_DEBUG
3054 ipc_importance_counter_init(&inherit->iii_elem);
3055 from_elem->iie_kmsg_refs_inherited++;
3056 task_imp->iit_elem.iie_task_refs_inherited++;
3057#endif
3058 }
3059
3060out_locked:
3061
3062 /* If found inherit and donating, reflect that in the task externcnt */
3063 if (III_NULL != inherit && donating) {
3064 to_task_imp->iit_externcnt++;
3065 /* take the internal assertion */
3066 ipc_importance_task_hold_internal_assertion_locked(to_task_imp, 1);
3067 /* may have dropped and retaken importance lock */
3068 }
3069
3070 /* If we didn't create a new inherit, we have some resources to release */
3071 if (III_NULL == inherit || inherit != alloc) {
3072 if (IIE_NULL != from_elem) {
3073 if (III_NULL != inherit) {
3074 incr_ref_counter(from_elem->iie_kmsg_refs_coalesced);
3075 } else {
3076 incr_ref_counter(from_elem->iie_kmsg_refs_dropped);
3077 }
3078 ipc_importance_release_locked(from_elem);
3079 /* importance unlocked */
3080 } else {
3081 ipc_importance_unlock();
3082 }
3083
3084 if (IIT_NULL != to_task_imp) {
3085 if (III_NULL != inherit) {
3086 incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_coalesced);
3087 }
3088 ipc_importance_task_release(to_task_imp);
3089 }
3090
3091 if (III_NULL != alloc) {
3092 zfree(ipc_importance_inherit_zone, alloc);
3093 }
3094 } else {
3095 /* from_elem and to_task_imp references transferred to new inherit */
3096 ipc_importance_unlock();
3097 }
3098
3099 return inherit;
3100}
3101
fe8ab488
A
3102/*
3103 * Routine: ipc_importance_receive
3104 * Purpose:
3105 * Process importance attributes in a received message.
3106 *
3107 * If an importance voucher attribute was sent, transform
3108 * that into an attribute value reflecting the inheritance
3109 * from the sender to the receiver.
3110 *
3111 * If a static boost is received (or the voucher isn't on
3112 * a voucher-based boost), export a static boost.
3113 * Conditions:
3114 * Nothing locked.
3115 */
3116void
3117ipc_importance_receive(
3118 ipc_kmsg_t kmsg,
3119 mach_msg_option_t option)
3120{
3121 unsigned int sender_pid = ((mach_msg_max_trailer_t *)
3122 ((vm_offset_t)kmsg->ikm_header +
3123 round_msg(kmsg->ikm_header->msgh_size)))->msgh_audit.val[5];
3124 task_t task_self = current_task();
3125 int impresult = -1;
3126
3127 /* convert to a voucher with an inherit importance attribute? */
3128 if ((option & MACH_RCV_VOUCHER) != 0) {
3129 uint8_t recipes[2 * sizeof(ipc_voucher_attr_recipe_data_t) +
3130 sizeof(mach_voucher_attr_value_handle_t)];
3131 ipc_voucher_attr_raw_recipe_array_size_t recipe_size = 0;
3132 ipc_voucher_attr_recipe_t recipe = (ipc_voucher_attr_recipe_t)recipes;
3133 ipc_voucher_t recv_voucher;
3134 mach_voucher_attr_value_handle_t handle;
3135 ipc_importance_inherit_t inherit;
3136 kern_return_t kr;
3137
3138 /* set up recipe to copy the old voucher */
3139 if (IP_VALID(kmsg->ikm_voucher)) {
3140 ipc_voucher_t sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
3141
3142 recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
3143 recipe->command = MACH_VOUCHER_ATTR_COPY;
3144 recipe->previous_voucher = sent_voucher;
3145 recipe->content_size = 0;
3146 recipe_size += sizeof(*recipe);
3147 }
3148
3149 /*
3150 * create an inheritance attribute from the kmsg (may be NULL)
3151 * transferring any boosts from the kmsg linkage through the
3152 * port directly to the new inheritance object.
3153 */
743345f9 3154 inherit = ipc_importance_inherit_from_kmsg(kmsg);
fe8ab488
A
3155 handle = (mach_voucher_attr_value_handle_t)inherit;
3156
3157 assert(IIE_NULL == kmsg->ikm_importance);
3158
3e170ce0
A
3159 /*
3160 * Only create a new voucher if we have an inherit object
3161 * (from the ikm_importance field of the incoming message), OR
3162 * we have a valid incoming voucher. If we have neither of
3163 * these things then there is no need to create a new voucher.
3164 */
3165 if (IP_VALID(kmsg->ikm_voucher) || inherit != III_NULL) {
3166 /* replace the importance attribute with the handle we created */
3167 /* our made reference on the inherit is donated to the voucher */
3168 recipe = (ipc_voucher_attr_recipe_t)&recipes[recipe_size];
3169 recipe->key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE;
3170 recipe->command = MACH_VOUCHER_ATTR_SET_VALUE_HANDLE;
3171 recipe->previous_voucher = IPC_VOUCHER_NULL;
3172 recipe->content_size = sizeof(mach_voucher_attr_value_handle_t);
3173 *(mach_voucher_attr_value_handle_t *)(void *)recipe->content = handle;
3174 recipe_size += sizeof(*recipe) + sizeof(mach_voucher_attr_value_handle_t);
3175
3176 kr = ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control,
3177 recipes,
3178 recipe_size,
3179 &recv_voucher);
3180 assert(KERN_SUCCESS == kr);
3181
3182 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
3183 kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
3184 ipc_port_release_send(kmsg->ikm_voucher);
3185 kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
3186 if (III_NULL != inherit)
3187 impresult = 2;
3188 }
fe8ab488
A
3189 } else { /* Don't want a voucher */
3190
3191 /* got linked importance? have to drop */
3192 if (IIE_NULL != kmsg->ikm_importance) {
3193 ipc_importance_elem_t elem;
3194
3195 ipc_importance_lock();
3196 elem = ipc_importance_kmsg_unlink(kmsg);
3197#if IIE_REF_DEBUG
3198 elem->iie_kmsg_refs_dropped++;
3199#endif
3200 ipc_importance_release_locked(elem);
3201 /* importance unlocked */
3202 }
3203
3204 /* With kmsg unlinked, can safely examine message importance attribute. */
3205 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3206 ipc_importance_task_t task_imp = task_self->task_imp_base;
3207 ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
3208
39037602
A
3209 /* The owner of receive right might have changed, take the internal assertion */
3210 if (KERN_SUCCESS == ipc_importance_task_hold_internal_assertion(task_imp, 1)) {
3211 ipc_importance_task_externalize_legacy_assertion(task_imp, 1, sender_pid);
fe8ab488
A
3212 impresult = 1;
3213 } else {
3214 /* The importance boost never applied to task (clear the bit) */
3215 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3216 impresult = 0;
3217 }
39037602
A
3218
3219 /* Drop the boost on the port and the owner of the receive right */
3220 ip_lock(port);
3221 if (ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
3222 ip_unlock(port);
3223 }
fe8ab488
A
3224 }
3225 }
3226
5ba3f43e 3227#if IMPORTANCE_TRACE
fe8ab488
A
3228 if (-1 < impresult)
3229 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_DELV)) | DBG_FUNC_NONE,
3e170ce0 3230 sender_pid, task_pid(task_self),
fe8ab488
A
3231 kmsg->ikm_header->msgh_id, impresult, 0);
3232 if (impresult == 2){
3233 /*
3234 * This probe only covers new voucher-based path. Legacy importance
3235 * will trigger the probe in ipc_importance_task_externalize_assertion()
3236 * above and have impresult==1 here.
3237 */
3e170ce0 3238 DTRACE_BOOST5(receive_boost, task_t, task_self, int, task_pid(task_self), int, sender_pid, int, 1, int, task_self->task_imp_base->iit_assertcnt);
fe8ab488 3239 }
5ba3f43e 3240#endif /* IMPORTANCE_TRACE */
fe8ab488
A
3241}
3242
3243/*
3244 * Routine: ipc_importance_unreceive
3245 * Purpose:
3246 * Undo receive of importance attributes in a message.
3247 *
3248 * Conditions:
3249 * Nothing locked.
3250 */
3251void
3252ipc_importance_unreceive(
3253 ipc_kmsg_t kmsg,
3254 mach_msg_option_t __unused option)
3255{
3256 /* importance should already be in the voucher and out of the kmsg */
3257 assert(IIE_NULL == kmsg->ikm_importance);
3258
3259 /* See if there is a legacy boost to be dropped from receiver */
3260 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3261 ipc_importance_task_t task_imp;
3262
3263 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3264 task_imp = current_task()->task_imp_base;
3265 if (!IP_VALID(kmsg->ikm_voucher) && IIT_NULL != task_imp) {
3266 ipc_importance_task_drop_legacy_external_assertion(task_imp, 1);
3267 }
3268 /*
3269 * ipc_kmsg_copyout_dest() will consume the voucher
3270 * and any contained importance.
3271 */
3272 }
3273}
3274
3275/*
3276 * Routine: ipc_importance_clean
3277 * Purpose:
3278 * Clean up importance state in a kmsg that is being cleaned.
3279 * Unlink the importance chain if one was set up, and drop
3280 * the reference this kmsg held on the donor. Then check to
3281 * if importance was carried to the port, and remove that if
3282 * needed.
3283 * Conditions:
3284 * Nothing locked.
3285 */
3286void
3287ipc_importance_clean(
3288 ipc_kmsg_t kmsg)
3289{
3290 ipc_port_t port;
3291
3292 /* Is the kmsg still linked? If so, remove that first */
3293 if (IIE_NULL != kmsg->ikm_importance) {
3294 ipc_importance_elem_t elem;
3295
3296 ipc_importance_lock();
3297 elem = ipc_importance_kmsg_unlink(kmsg);
3298 assert(IIE_NULL != elem);
3299 ipc_importance_release_locked(elem);
3300 /* importance unlocked */
3301 }
3302
3303 /* See if there is a legacy importance boost to be dropped from port */
3304 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3305 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3306 port = kmsg->ikm_header->msgh_remote_port;
3307 if (IP_VALID(port)) {
3308 ip_lock(port);
3309 /* inactive ports already had their importance boosts dropped */
3310 if (!ip_active(port) ||
4bd07ac2 3311 ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
fe8ab488
A
3312 ip_unlock(port);
3313 }
3314 }
3315 }
3316}
3317
3318void
3319ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg)
3320{
3321 assert(IIE_NULL == kmsg->ikm_importance);
3322 assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
3323}
3324
3325/*
3326 * IPC Importance Attribute Manager definition
3327 */
3328
3329static kern_return_t
3330ipc_importance_release_value(
3331 ipc_voucher_attr_manager_t manager,
3332 mach_voucher_attr_key_t key,
3333 mach_voucher_attr_value_handle_t value,
3334 mach_voucher_attr_value_reference_t sync);
3335
3336static kern_return_t
3337ipc_importance_get_value(
3338 ipc_voucher_attr_manager_t manager,
3339 mach_voucher_attr_key_t key,
3340 mach_voucher_attr_recipe_command_t command,
3341 mach_voucher_attr_value_handle_array_t prev_values,
3342 mach_voucher_attr_value_handle_array_size_t prev_value_count,
3343 mach_voucher_attr_content_t content,
3344 mach_voucher_attr_content_size_t content_size,
3345 mach_voucher_attr_value_handle_t *out_value,
490019cf 3346 mach_voucher_attr_value_flags_t *out_flags,
fe8ab488
A
3347 ipc_voucher_t *out_value_voucher);
3348
3349static kern_return_t
3350ipc_importance_extract_content(
3351 ipc_voucher_attr_manager_t manager,
3352 mach_voucher_attr_key_t key,
3353 mach_voucher_attr_value_handle_array_t values,
3354 mach_voucher_attr_value_handle_array_size_t value_count,
3355 mach_voucher_attr_recipe_command_t *out_command,
3356 mach_voucher_attr_content_t out_content,
3357 mach_voucher_attr_content_size_t *in_out_content_size);
3358
3359static kern_return_t
3360ipc_importance_command(
3361 ipc_voucher_attr_manager_t manager,
3362 mach_voucher_attr_key_t key,
3363 mach_voucher_attr_value_handle_array_t values,
3364 mach_msg_type_number_t value_count,
3365 mach_voucher_attr_command_t command,
3366 mach_voucher_attr_content_t in_content,
3367 mach_voucher_attr_content_size_t in_content_size,
3368 mach_voucher_attr_content_t out_content,
3369 mach_voucher_attr_content_size_t *out_content_size);
3370
3371static void
3372ipc_importance_manager_release(
3373 ipc_voucher_attr_manager_t manager);
3374
3375struct ipc_voucher_attr_manager ipc_importance_manager = {
3376 .ivam_release_value = ipc_importance_release_value,
3377 .ivam_get_value = ipc_importance_get_value,
3378 .ivam_extract_content = ipc_importance_extract_content,
3379 .ivam_command = ipc_importance_command,
3380 .ivam_release = ipc_importance_manager_release,
490019cf 3381 .ivam_flags = IVAM_FLAGS_NONE,
fe8ab488
A
3382};
3383
3384#define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
3385#define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
3386
3387/*
3388 * Routine: ipc_importance_release_value [Voucher Attribute Manager Interface]
3389 * Purpose:
3390 * Release what the voucher system believes is the last "made" reference
3391 * on an importance attribute value handle. The sync parameter is used to
3392 * avoid races with new made references concurrently being returned to the
3393 * voucher system in other threads.
3394 * Conditions:
3395 * Nothing locked on entry. May block.
3396 */
3397static kern_return_t
3398ipc_importance_release_value(
3399 ipc_voucher_attr_manager_t __assert_only manager,
3400 mach_voucher_attr_key_t __assert_only key,
3401 mach_voucher_attr_value_handle_t value,
3402 mach_voucher_attr_value_reference_t sync)
3403{
3404 ipc_importance_elem_t elem;
3405
3406 IMPORTANCE_ASSERT_MANAGER(manager);
3407 IMPORTANCE_ASSERT_KEY(key);
3408 assert(0 < sync);
3409
3410 elem = (ipc_importance_elem_t)value;
3411
3412 ipc_importance_lock();
3413
3414 /* Any oustanding made refs? */
3415 if (sync != elem->iie_made) {
3416 assert(sync < elem->iie_made);
3417 ipc_importance_unlock();
3418 return KERN_FAILURE;
3419 }
3420
3421 /* clear made */
3422 elem->iie_made = 0;
3423
743345f9
A
3424 /*
3425 * If there are pending external boosts represented by this attribute,
3426 * drop them from the apropriate task
fe8ab488
A
3427 */
3428 if (IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
3429 ipc_importance_inherit_t inherit = (ipc_importance_inherit_t)elem;
3430
3431 assert(inherit->iii_externcnt >= inherit->iii_externdrop);
3432
3433 if (inherit->iii_donating) {
3434 ipc_importance_task_t imp_task = inherit->iii_to_task;
3435 uint32_t assertcnt = III_EXTERN(inherit);
3436
3437 assert(ipc_importance_task_is_any_receiver_type(imp_task));
3438 assert(imp_task->iit_externcnt >= inherit->iii_externcnt);
3439 assert(imp_task->iit_externdrop >= inherit->iii_externdrop);
3440 imp_task->iit_externcnt -= inherit->iii_externcnt;
3441 imp_task->iit_externdrop -= inherit->iii_externdrop;
3442 inherit->iii_externcnt = 0;
3443 inherit->iii_externdrop = 0;
3444 inherit->iii_donating = FALSE;
3445
3446 /* adjust the internal assertions - and propagate if needed */
3447 if (ipc_importance_task_check_transition(imp_task, IIT_UPDATE_DROP, assertcnt)) {
3448 ipc_importance_task_propagate_assertion_locked(imp_task, IIT_UPDATE_DROP, TRUE);
3449 }
3450 } else {
3451 inherit->iii_externcnt = 0;
3452 inherit->iii_externdrop = 0;
3453 }
743345f9 3454 }
fe8ab488
A
3455
3456 /* drop the made reference on elem */
3457 ipc_importance_release_locked(elem);
3458 /* returns unlocked */
3459
3460 return KERN_SUCCESS;
3461}
3462
3463
3464/*
3465 * Routine: ipc_importance_get_value [Voucher Attribute Manager Interface]
3466 * Purpose:
3467 * Convert command and content data into a reference on a [potentially new]
3468 * attribute value. The importance attribute manager will only allow the
3469 * caller to get a value for the current task's importance, or to redeem
3470 * an importance attribute from an existing voucher.
3471 * Conditions:
3472 * Nothing locked on entry. May block.
3473 */
3474static kern_return_t
3475ipc_importance_get_value(
3476 ipc_voucher_attr_manager_t __assert_only manager,
3477 mach_voucher_attr_key_t __assert_only key,
3478 mach_voucher_attr_recipe_command_t command,
3479 mach_voucher_attr_value_handle_array_t prev_values,
3480 mach_voucher_attr_value_handle_array_size_t prev_value_count,
3481 mach_voucher_attr_content_t __unused content,
3482 mach_voucher_attr_content_size_t content_size,
3483 mach_voucher_attr_value_handle_t *out_value,
490019cf 3484 mach_voucher_attr_value_flags_t *out_flags,
fe8ab488
A
3485 ipc_voucher_t *out_value_voucher)
3486{
3487 ipc_importance_elem_t elem;
3488 task_t self;
3489
3490 IMPORTANCE_ASSERT_MANAGER(manager);
3491 IMPORTANCE_ASSERT_KEY(key);
3492
3493 if (0 != content_size)
3494 return KERN_INVALID_ARGUMENT;
3495
490019cf 3496 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
fe8ab488
A
3497 /* never an out voucher */
3498
3499 switch (command) {
3500
3501 case MACH_VOUCHER_ATTR_REDEEM:
3502
3503 /* redeem of previous values is the value */
3504 if (0 < prev_value_count) {
3505 elem = (ipc_importance_elem_t)prev_values[0];
3506 assert(IIE_NULL != elem);
3507
3508 ipc_importance_lock();
3509 assert(0 < elem->iie_made);
3510 elem->iie_made++;
3511 ipc_importance_unlock();
3512
3513 *out_value = prev_values[0];
3514 return KERN_SUCCESS;
3515 }
3516
3517 /* redeem of default is default */
3518 *out_value = 0;
3519 *out_value_voucher = IPC_VOUCHER_NULL;
3520 return KERN_SUCCESS;
3521
3522 case MACH_VOUCHER_ATTR_IMPORTANCE_SELF:
3523 self = current_task();
3524
3525 elem = (ipc_importance_elem_t)ipc_importance_for_task(self, TRUE);
3526 /* made reference added (or IIE_NULL which isn't referenced) */
3527
3528 *out_value = (mach_voucher_attr_value_handle_t)elem;
3529 *out_value_voucher = IPC_VOUCHER_NULL;
3530 return KERN_SUCCESS;
3531
3532 default:
3533 /*
3534 * every other command is unknown
3535 *
3536 * Specifically, there is no mechanism provided to construct an
3537 * importance attribute for a task/process from just a pid or
3538 * task port. It has to be copied (or redeemed) from a previous
3539 * voucher that has it.
3540 */
3541 return KERN_INVALID_ARGUMENT;
3542 }
3543}
3544
3545/*
3546 * Routine: ipc_importance_extract_content [Voucher Attribute Manager Interface]
3547 * Purpose:
3548 * Extract meaning from the attribute value present in a voucher. While
3549 * the real goal is to provide commands and data that can reproduce the
3550 * voucher's value "out of thin air", this isn't possible with importance
3551 * attribute values. Instead, return debug info to help track down dependencies.
3552 * Conditions:
3553 * Nothing locked on entry. May block.
3554 */
3555static kern_return_t
3556ipc_importance_extract_content(
3557 ipc_voucher_attr_manager_t __assert_only manager,
3558 mach_voucher_attr_key_t __assert_only key,
3559 mach_voucher_attr_value_handle_array_t values,
3560 mach_voucher_attr_value_handle_array_size_t value_count,
3561 mach_voucher_attr_recipe_command_t *out_command,
3562 mach_voucher_attr_content_t out_content,
3563 mach_voucher_attr_content_size_t *in_out_content_size)
3564{
3565 mach_voucher_attr_content_size_t size = 0;
3566 ipc_importance_elem_t elem;
3567 unsigned int i;
3568
3569 IMPORTANCE_ASSERT_MANAGER(manager);
3570 IMPORTANCE_ASSERT_KEY(key);
3571
3572 /* the first non-default value provides the data */
3573 for (i = 0; i < value_count ; i++) {
3574 elem = (ipc_importance_elem_t)values[i];
3575 if (IIE_NULL == elem)
3576 continue;
3577
3578 snprintf((char *)out_content, *in_out_content_size, "Importance for pid ");
3579 size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
3580
3581 for(;;) {
3582 ipc_importance_inherit_t inherit = III_NULL;
3583 ipc_importance_task_t task_imp;
3584 task_t task;
3e170ce0 3585 int t_pid;
fe8ab488
A
3586
3587 if (IIE_TYPE_TASK == IIE_TYPE(elem)) {
3588 task_imp = (ipc_importance_task_t)elem;
3589 task = task_imp->iit_task;
3e170ce0
A
3590 t_pid = (TASK_NULL != task) ?
3591 task_pid(task) : -1;
3592 snprintf((char *)out_content + size, *in_out_content_size - size, "%d", t_pid);
fe8ab488
A
3593 } else {
3594 inherit = (ipc_importance_inherit_t)elem;
3595 task_imp = inherit->iii_to_task;
3596 task = task_imp->iit_task;
3e170ce0
A
3597 t_pid = (TASK_NULL != task) ?
3598 task_pid(task) : -1;
fe8ab488 3599 snprintf((char *)out_content + size, *in_out_content_size - size,
3e170ce0 3600 "%d (%d of %d boosts) %s from pid ", t_pid,
fe8ab488
A
3601 III_EXTERN(inherit), inherit->iii_externcnt,
3602 (inherit->iii_donating) ? "donated" : "linked");
3603 }
3604
3605 size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
3606
3607 if (III_NULL == inherit)
3608 break;
3609
3610 elem = inherit->iii_from_elem;
3611 }
3612 size++; /* account for NULL */
3613 }
3614 *out_command = MACH_VOUCHER_ATTR_NOOP; /* cannot be used to regenerate value */
3615 *in_out_content_size = size;
3616 return KERN_SUCCESS;
3617}
3618
3619/*
3620 * Routine: ipc_importance_command [Voucher Attribute Manager Interface]
3621 * Purpose:
3622 * Run commands against the importance attribute value found in a voucher.
3623 * No such commands are currently supported.
3624 * Conditions:
3625 * Nothing locked on entry. May block.
3626 */
3627static kern_return_t
3628ipc_importance_command(
3629 ipc_voucher_attr_manager_t __assert_only manager,
3630 mach_voucher_attr_key_t __assert_only key,
3631 mach_voucher_attr_value_handle_array_t values,
3632 mach_msg_type_number_t value_count,
3633 mach_voucher_attr_command_t command,
3634 mach_voucher_attr_content_t in_content,
3635 mach_voucher_attr_content_size_t in_content_size,
3636 mach_voucher_attr_content_t out_content,
3637 mach_voucher_attr_content_size_t *out_content_size)
3638{
3639 ipc_importance_inherit_t inherit;
3640 ipc_importance_task_t to_task;
3641 uint32_t refs, *outrefsp;
3642 mach_msg_type_number_t i;
3643 uint32_t externcnt;
3644
3645 IMPORTANCE_ASSERT_MANAGER(manager);
3646 IMPORTANCE_ASSERT_KEY(key);
3647
3648 if (in_content_size != sizeof(refs) ||
3649 (*out_content_size != 0 && *out_content_size != sizeof(refs))) {
3650 return KERN_INVALID_ARGUMENT;
3651 }
3652 refs = *(uint32_t *)(void *)in_content;
3653 outrefsp = (*out_content_size != 0) ? (uint32_t *)(void *)out_content : NULL;
3654
3655 if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL != command) {
3656 return KERN_NOT_SUPPORTED;
3657 }
3658
3659 /* the first non-default value of the apropos type provides the data */
3660 inherit = III_NULL;
3661 for (i = 0; i < value_count; i++) {
3662 ipc_importance_elem_t elem = (ipc_importance_elem_t)values[i];
3663
3664 if (IIE_NULL != elem && IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
3665 inherit = (ipc_importance_inherit_t)elem;
3666 break;
3667 }
3668 }
3669 if (III_NULL == inherit) {
3670 return KERN_INVALID_ARGUMENT;
3671 }
3672
3673 ipc_importance_lock();
3674
3675 if (0 == refs) {
3676 if (NULL != outrefsp) {
3677 *outrefsp = III_EXTERN(inherit);
3678 }
3679 ipc_importance_unlock();
3680 return KERN_SUCCESS;
3681 }
3682
3e170ce0
A
3683 to_task = inherit->iii_to_task;
3684 assert(ipc_importance_task_is_any_receiver_type(to_task));
3685
3686 /* if not donating to a denap receiver, it was called incorrectly */
3687 if (!ipc_importance_task_is_marked_denap_receiver(to_task)) {
3688 ipc_importance_unlock();
39037602 3689 return KERN_INVALID_TASK; /* keeps dispatch happy */
3e170ce0
A
3690 }
3691
fe8ab488
A
3692 /* Enough external references left to drop? */
3693 if (III_EXTERN(inherit) < refs) {
3694 ipc_importance_unlock();
3695 return KERN_FAILURE;
3696 }
3697
fe8ab488
A
3698 /* re-base external and internal counters at the inherit and the to-task (if apropos) */
3699 if (inherit->iii_donating) {
3700 assert(IIT_EXTERN(to_task) >= III_EXTERN(inherit));
3701 assert(to_task->iit_externcnt >= inherit->iii_externcnt);
3702 assert(to_task->iit_externdrop >= inherit->iii_externdrop);
3703 inherit->iii_externdrop += refs;
3704 to_task->iit_externdrop += refs;
3705 externcnt = III_EXTERN(inherit);
3706 if (0 == externcnt) {
3707 inherit->iii_donating = FALSE;
3708 to_task->iit_externcnt -= inherit->iii_externcnt;
3709 to_task->iit_externdrop -= inherit->iii_externdrop;
3710
3711
3712 /* Start AppNap delay hysteresis - even if not the last boost for the task. */
3713 if (ipc_importance_delayed_drop_call != NULL &&
3714 ipc_importance_task_is_marked_denap_receiver(to_task)) {
3715 ipc_importance_task_delayed_drop(to_task);
3716 }
3717
3718 /* drop task assertions associated with the dropped boosts */
3719 if (ipc_importance_task_check_transition(to_task, IIT_UPDATE_DROP, refs)) {
3720 ipc_importance_task_propagate_assertion_locked(to_task, IIT_UPDATE_DROP, TRUE);
3721 /* may have dropped and retaken importance lock */
3722 }
3723 } else {
3724 /* assert(to_task->iit_assertcnt >= refs + externcnt); */
3725 /* defensive deduction in case of assertcnt underflow */
3726 if (to_task->iit_assertcnt > refs + externcnt) {
3727 to_task->iit_assertcnt -= refs;
3728 } else {
3729 to_task->iit_assertcnt = externcnt;
3730 }
3731 }
3732 } else {
3733 inherit->iii_externdrop += refs;
3734 externcnt = III_EXTERN(inherit);
3735 }
3736
3737 /* capture result (if requested) */
3738 if (NULL != outrefsp) {
3739 *outrefsp = externcnt;
3740 }
3741
3742 ipc_importance_unlock();
3743 return KERN_SUCCESS;
3744}
3745
3746/*
3747 * Routine: ipc_importance_manager_release [Voucher Attribute Manager Interface]
3748 * Purpose:
3749 * Release the Voucher system's reference on the IPC importance attribute
3750 * manager.
3751 * Conditions:
3752 * As this can only occur after the manager drops the Attribute control
3753 * reference granted back at registration time, and that reference is never
3754 * dropped, this should never be called.
3755 */
3756static void
3757ipc_importance_manager_release(
3758 ipc_voucher_attr_manager_t __assert_only manager)
3759{
3760 IMPORTANCE_ASSERT_MANAGER(manager);
3761 panic("Voucher importance manager released");
3762}
3763
3764/*
3765 * Routine: ipc_importance_init
3766 * Purpose:
3767 * Initialize the IPC importance manager.
3768 * Conditions:
3769 * Zones and Vouchers are already initialized.
3770 */
3771void
3772ipc_importance_init(void)
3773{
3774 natural_t ipc_importance_max = (task_max + thread_max) * 2;
3775 char temp_buf[26];
3776 kern_return_t kr;
3777
3778 if (PE_parse_boot_argn("imp_interactive_receiver", temp_buf, sizeof(temp_buf))) {
3779 ipc_importance_interactive_receiver = TRUE;
3780 }
3781
3782 ipc_importance_task_zone = zinit(sizeof(struct ipc_importance_task),
3783 ipc_importance_max * sizeof(struct ipc_importance_task),
3784 sizeof(struct ipc_importance_task),
3785 "ipc task importance");
3786 zone_change(ipc_importance_task_zone, Z_NOENCRYPT, TRUE);
3787
3788 ipc_importance_inherit_zone = zinit(sizeof(struct ipc_importance_inherit),
3789 ipc_importance_max * sizeof(struct ipc_importance_inherit),
3790 sizeof(struct ipc_importance_inherit),
3791 "ipc importance inherit");
3792 zone_change(ipc_importance_inherit_zone, Z_NOENCRYPT, TRUE);
3793
3794
3795#if DEVELOPMENT || DEBUG
3796 queue_init(&global_iit_alloc_queue);
3797#endif
3798
3799 /* initialize global locking */
3800 ipc_importance_lock_init();
3801
3802 kr = ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager,
3803 (mach_voucher_attr_value_handle_t)0,
3804 MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
3805 &ipc_importance_control);
3806 if (KERN_SUCCESS != kr)
3807 printf("Voucher importance manager register returned %d", kr);
3808}
3809
3810/*
3811 * Routine: ipc_importance_thread_call_init
3812 * Purpose:
3813 * Initialize the IPC importance code dependent upon
3814 * thread-call support being available.
3815 * Conditions:
3816 * Thread-call mechanism is already initialized.
3817 */
3818void
3819ipc_importance_thread_call_init(void)
3820{
3821 /* initialize delayed drop queue and thread-call */
3822 queue_init(&ipc_importance_delayed_drop_queue);
3823 ipc_importance_delayed_drop_call =
3824 thread_call_allocate(ipc_importance_task_delayed_drop_scan, NULL);
3825 if (NULL == ipc_importance_delayed_drop_call) {
3826 panic("ipc_importance_init");
3827 }
3828}
3829
3830/*
3831 * Routing: task_importance_list_pids
3832 * Purpose: list pids where task in donating importance.
3833 * Conditions: To be called only from kdp stackshot code.
3834 * Will panic the system otherwise.
3835 */
3836extern int
3e170ce0 3837task_importance_list_pids(task_t task, int flags, char *pid_list, unsigned int max_count)
fe8ab488 3838{
3e170ce0 3839 if (kdp_lck_spin_is_acquired(&ipc_importance_lock_data) ||
fe8ab488
A
3840 max_count < 1 ||
3841 task->task_imp_base == IIT_NULL ||
3842 pid_list == NULL ||
3843 flags != TASK_IMP_LIST_DONATING_PIDS) {
3844 return 0;
3845 }
3846 unsigned int pidcount = 0;
3847 task_t temp_task;
3848 ipc_importance_task_t task_imp = task->task_imp_base;
3849 ipc_kmsg_t temp_kmsg;
3850 ipc_importance_inherit_t temp_inherit;
3851 ipc_importance_elem_t elem;
3e170ce0 3852 int target_pid = 0, previous_pid;
fe8ab488
A
3853
3854 queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
3855 /* check space in buffer */
3856 if (pidcount >= max_count)
3857 break;
3e170ce0 3858 previous_pid = target_pid;
fe8ab488
A
3859 target_pid = -1;
3860
3861 if (temp_inherit->iii_donating) {
3862
3863#if DEVELOPMENT || DEBUG
3864 target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
3865#else
3866 temp_task = temp_inherit->iii_to_task->iit_task;
3867 if (temp_task != TASK_NULL) {
3e170ce0 3868 target_pid = task_pid(temp_task);
fe8ab488
A
3869 }
3870#endif
3871 }
3872
3e170ce0
A
3873 if (target_pid != -1 && previous_pid != target_pid) {
3874 memcpy(pid_list, &target_pid, sizeof(target_pid));
3875 pid_list += sizeof(target_pid);
3876 pidcount++;
fe8ab488
A
3877 }
3878
3879 }
3880
3e170ce0 3881 target_pid = 0;
fe8ab488
A
3882 queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
3883 if (pidcount >= max_count)
3884 break;
3e170ce0 3885 previous_pid = target_pid;
fe8ab488
A
3886 target_pid = -1;
3887 elem = temp_kmsg->ikm_importance;
3888 temp_task = TASK_NULL;
3889
3890 if (elem == IIE_NULL) {
3891 continue;
3892 }
3893
3894 if (!(temp_kmsg->ikm_header && MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg->ikm_header->msgh_bits))) {
3895 continue;
3896 }
3897
3898 if (IIE_TYPE_TASK == IIE_TYPE(elem) &&
3899 (((ipc_importance_task_t)elem)->iit_task != TASK_NULL)) {
3e170ce0 3900 target_pid = task_pid(((ipc_importance_task_t)elem)->iit_task);
fe8ab488
A
3901 } else {
3902 temp_inherit = (ipc_importance_inherit_t)elem;
3903#if DEVELOPMENT || DEBUG
3904 target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
3905#else
3906 temp_task = temp_inherit->iii_to_task->iit_task;
3907 if (temp_task != TASK_NULL) {
3e170ce0 3908 target_pid = task_pid(temp_task);
fe8ab488
A
3909 }
3910#endif
3911 }
3912
3e170ce0
A
3913 if (target_pid != -1 && previous_pid != target_pid) {
3914 memcpy(pid_list, &target_pid, sizeof(target_pid));
3915 pid_list += sizeof(target_pid);
3916 pidcount++;
fe8ab488
A
3917 }
3918 }
3919
3920 return pidcount;
3921}
3922