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