]>
Commit | Line | Data |
---|---|---|
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 | ||
49 | extern int proc_pid(void *); | |
50 | extern int proc_selfpid(void); | |
51 | extern uint64_t proc_uniqueid(void *p); | |
52 | extern char *proc_name_address(void *p); | |
53 | ||
54 | /* | |
55 | * Globals for delayed boost drop processing. | |
56 | */ | |
57 | static queue_head_t ipc_importance_delayed_drop_queue; | |
58 | static thread_call_t ipc_importance_delayed_drop_call; | |
59 | static uint64_t ipc_importance_delayed_drop_timestamp; | |
60 | static 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 | ||
73 | static 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 | ||
95 | static inline | |
96 | uint32_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 | ||
102 | static inline | |
103 | uint32_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 | ||
109 | static inline | |
110 | uint32_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 | ||
118 | static inline | |
119 | uint32_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 | ||
129 | static inline | |
130 | void 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 | |
152 | static queue_head_t global_iit_alloc_queue; | |
153 | #endif | |
154 | ||
155 | /* TODO: remove this varibale when interactive daemon audit is complete */ | |
156 | boolean_t ipc_importance_interactive_receiver = FALSE; | |
157 | ||
158 | static zone_t ipc_importance_task_zone; | |
159 | static zone_t ipc_importance_inherit_zone; | |
160 | ||
161 | static 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 | */ | |
174 | static void | |
175 | ipc_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 | */ | |
204 | static ipc_importance_elem_t | |
205 | ipc_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 | */ | |
234 | static void | |
235 | ipc_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 | */ | |
264 | static ipc_importance_elem_t | |
265 | ipc_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 | */ | |
291 | void | |
292 | ipc_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 | */ | |
306 | static void | |
307 | ipc_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 | */ | |
403 | void | |
404 | ipc_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 | */ | |
424 | void | |
425 | ipc_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 | */ | |
444 | void | |
445 | ipc_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 | */ | |
467 | static void | |
468 | ipc_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 | */ | |
507 | static boolean_t | |
508 | ipc_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 | */ | |
612 | static void | |
613 | ipc_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 | */ | |
750 | static void | |
751 | ipc_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 | */ | |
892 | static void | |
893 | ipc_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 | */ | |
940 | static void | |
941 | ipc_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 | */ | |
996 | static void | |
997 | ipc_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 | */ | |
1091 | static kern_return_t | |
1092 | ipc_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 | */ | |
1113 | static kern_return_t | |
1114 | ipc_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 | */ | |
1134 | int | |
1135 | ipc_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 | */ | |
1159 | kern_return_t | |
1160 | ipc_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 | */ | |
1184 | kern_return_t | |
1185 | ipc_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 | */ | |
1212 | kern_return_t | |
1213 | ipc_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 | */ | |
1243 | kern_return_t | |
1244 | ipc_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 | */ | |
1314 | kern_return_t | |
1315 | ipc_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 */ | |
1395 | static kern_return_t | |
1396 | ipc_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 | */ | |
1441 | void | |
1442 | ipc_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 | */ | |
1519 | void | |
1520 | ipc_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 | */ | |
1550 | boolean_t | |
1551 | ipc_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 | */ | |
1568 | void | |
1569 | ipc_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 | */ | |
1587 | boolean_t | |
1588 | ipc_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 | */ | |
1605 | boolean_t | |
1606 | ipc_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 | */ | |
1624 | boolean_t | |
1625 | ipc_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 | */ | |
1644 | void | |
1645 | ipc_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 | */ | |
1675 | boolean_t | |
1676 | ipc_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 | */ | |
1691 | void | |
1692 | ipc_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 | */ | |
1721 | boolean_t | |
1722 | ipc_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 | */ | |
1736 | boolean_t | |
1737 | ipc_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 | */ | |
1752 | boolean_t | |
1753 | ipc_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 | */ | |
1768 | static inline void | |
1769 | ipc_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 | */ | |
1783 | static inline void | |
1784 | ipc_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 | */ | |
1799 | void | |
1800 | ipc_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 | */ | |
1820 | ipc_importance_task_t | |
1821 | ipc_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 | |
1911 | void 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 | ||
1938 | static void | |
1939 | ipc_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 | ||
1989 | void | |
1990 | ipc_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 | */ | |
2016 | void | |
2017 | ipc_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 | ||
2070 | boolean_t | |
2071 | ipc_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 | */ | |
2328 | boolean_t | |
2329 | ipc_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 | */ | |
2508 | static ipc_importance_inherit_t | |
2509 | ipc_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 | */ | |
2796 | void | |
2797 | ipc_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 | */ | |
2928 | void | |
2929 | ipc_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 | */ | |
2963 | void | |
2964 | ipc_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 | ||
2995 | void | |
2996 | ipc_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 | ||
3006 | static kern_return_t | |
3007 | ipc_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 | ||
3013 | static kern_return_t | |
3014 | ipc_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 | ||
3026 | static kern_return_t | |
3027 | ipc_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 | ||
3036 | static kern_return_t | |
3037 | ipc_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 | ||
3048 | static void | |
3049 | ipc_importance_manager_release( | |
3050 | ipc_voucher_attr_manager_t manager); | |
3051 | ||
3052 | struct 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 | */ | |
3074 | static kern_return_t | |
3075 | ipc_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 | */ | |
3151 | static kern_return_t | |
3152 | ipc_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 | */ | |
3232 | static kern_return_t | |
3233 | ipc_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 | */ | |
3304 | static kern_return_t | |
3305 | ipc_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 | */ | |
3433 | static void | |
3434 | ipc_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 | */ | |
3448 | void | |
3449 | ipc_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 | */ | |
3495 | void | |
3496 | ipc_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 | */ | |
3513 | extern int | |
3e170ce0 | 3514 | task_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 |