]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/thread_call.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / thread_call.c
CommitLineData
1c79356b 1/*
f427ee49 2 * Copyright (c) 1993-1995, 1999-2020 Apple Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
0a7de745 28
1c79356b 29#include <mach/mach_types.h>
91447636 30#include <mach/thread_act.h>
1c79356b 31
91447636 32#include <kern/kern_types.h>
c910b4d9 33#include <kern/zalloc.h>
1c79356b
A
34#include <kern/sched_prim.h>
35#include <kern/clock.h>
36#include <kern/task.h>
37#include <kern/thread.h>
3e170ce0 38#include <kern/waitq.h>
39236c6e 39#include <kern/ledger.h>
5ba3f43e 40#include <kern/policy_internal.h>
91447636
A
41
42#include <vm/vm_pageout.h>
1c79356b
A
43
44#include <kern/thread_call.h>
1c79356b
A
45#include <kern/timer_call.h>
46
316670eb 47#include <libkern/OSAtomic.h>
39236c6e 48#include <kern/timer_queue.h>
316670eb 49
55e303ae 50#include <sys/kdebug.h>
4b17d6b6
A
51#if CONFIG_DTRACE
52#include <mach/sdt.h>
53#endif
39236c6e 54#include <machine/machine_routines.h>
1c79356b 55
f427ee49
A
56static ZONE_DECLARE(thread_call_zone, "thread_call",
57 sizeof(thread_call_data_t), ZC_NOENCRYPT);
58
5ba3f43e
A
59typedef enum {
60 TCF_ABSOLUTE = 0,
61 TCF_CONTINUOUS = 1,
62 TCF_COUNT = 2,
63} thread_call_flavor_t;
64
cb323159 65__options_decl(thread_call_group_flags_t, uint32_t, {
5ba3f43e
A
66 TCG_NONE = 0x0,
67 TCG_PARALLEL = 0x1,
68 TCG_DEALLOC_ACTIVE = 0x2,
cb323159 69});
5ba3f43e
A
70
71static struct thread_call_group {
f427ee49
A
72 __attribute__((aligned(128))) lck_ticket_t tcg_lock;
73
5ba3f43e
A
74 const char * tcg_name;
75
0a7de745
A
76 queue_head_t pending_queue;
77 uint32_t pending_count;
1c79356b 78
5ba3f43e 79 queue_head_t delayed_queues[TCF_COUNT];
f427ee49 80 struct priority_queue_deadline_min delayed_pqueues[TCF_COUNT];
5ba3f43e 81 timer_call_data_t delayed_timers[TCF_COUNT];
1c79356b 82
0a7de745 83 timer_call_data_t dealloc_timer;
1c79356b 84
0a7de745 85 struct waitq idle_waitq;
f427ee49 86 uint64_t idle_timestamp;
0a7de745 87 uint32_t idle_count, active_count, blocked_count;
1c79356b 88
5ba3f43e 89 uint32_t tcg_thread_pri;
0a7de745 90 uint32_t target_thread_count;
c910b4d9 91
f427ee49 92 thread_call_group_flags_t tcg_flags;
c3c9b80d
A
93
94 struct waitq waiters_waitq;
5ba3f43e
A
95} thread_call_groups[THREAD_CALL_INDEX_MAX] = {
96 [THREAD_CALL_INDEX_HIGH] = {
97 .tcg_name = "high",
98 .tcg_thread_pri = BASEPRI_PREEMPT_HIGH,
99 .target_thread_count = 4,
f427ee49 100 .tcg_flags = TCG_NONE,
5ba3f43e
A
101 },
102 [THREAD_CALL_INDEX_KERNEL] = {
103 .tcg_name = "kernel",
104 .tcg_thread_pri = BASEPRI_KERNEL,
105 .target_thread_count = 1,
f427ee49 106 .tcg_flags = TCG_PARALLEL,
5ba3f43e
A
107 },
108 [THREAD_CALL_INDEX_USER] = {
109 .tcg_name = "user",
110 .tcg_thread_pri = BASEPRI_DEFAULT,
111 .target_thread_count = 1,
f427ee49 112 .tcg_flags = TCG_PARALLEL,
5ba3f43e
A
113 },
114 [THREAD_CALL_INDEX_LOW] = {
115 .tcg_name = "low",
116 .tcg_thread_pri = MAXPRI_THROTTLE,
117 .target_thread_count = 1,
f427ee49 118 .tcg_flags = TCG_PARALLEL,
5ba3f43e
A
119 },
120 [THREAD_CALL_INDEX_KERNEL_HIGH] = {
121 .tcg_name = "kernel-high",
122 .tcg_thread_pri = BASEPRI_PREEMPT,
123 .target_thread_count = 2,
f427ee49 124 .tcg_flags = TCG_NONE,
5ba3f43e
A
125 },
126 [THREAD_CALL_INDEX_QOS_UI] = {
127 .tcg_name = "qos-ui",
128 .tcg_thread_pri = BASEPRI_FOREGROUND,
129 .target_thread_count = 1,
f427ee49 130 .tcg_flags = TCG_NONE,
5ba3f43e
A
131 },
132 [THREAD_CALL_INDEX_QOS_IN] = {
133 .tcg_name = "qos-in",
134 .tcg_thread_pri = BASEPRI_USER_INITIATED,
135 .target_thread_count = 1,
f427ee49 136 .tcg_flags = TCG_NONE,
5ba3f43e
A
137 },
138 [THREAD_CALL_INDEX_QOS_UT] = {
139 .tcg_name = "qos-ut",
140 .tcg_thread_pri = BASEPRI_UTILITY,
141 .target_thread_count = 1,
f427ee49 142 .tcg_flags = TCG_NONE,
5ba3f43e 143 },
316670eb 144};
c910b4d9 145
0a7de745 146typedef struct thread_call_group *thread_call_group_t;
c910b4d9 147
0a7de745 148#define INTERNAL_CALL_COUNT 768
5ba3f43e 149#define THREAD_CALL_DEALLOC_INTERVAL_NS (5 * NSEC_PER_MSEC) /* 5 ms */
0a7de745
A
150#define THREAD_CALL_ADD_RATIO 4
151#define THREAD_CALL_MACH_FACTOR_CAP 3
152#define THREAD_CALL_GROUP_MAX_THREADS 500
153
f427ee49
A
154struct thread_call_thread_state {
155 struct thread_call_group * thc_group;
156 struct thread_call * thc_call; /* debug only, may be deallocated */
157 uint64_t thc_call_start;
158 uint64_t thc_call_soft_deadline;
159 uint64_t thc_call_hard_deadline;
160 uint64_t thc_call_pending_timestamp;
161 uint64_t thc_IOTES_invocation_timestamp;
162 thread_call_func_t thc_func;
163 thread_call_param_t thc_param0;
164 thread_call_param_t thc_param1;
165};
166
167static bool thread_call_daemon_awake = true;
168/*
169 * This special waitq exists because the daemon thread
170 * might need to be woken while already holding a global waitq locked.
171 */
172static struct waitq daemon_waitq;
173
0a7de745
A
174static thread_call_data_t internal_call_storage[INTERNAL_CALL_COUNT];
175static queue_head_t thread_call_internal_queue;
176int thread_call_internal_queue_count = 0;
177static uint64_t thread_call_dealloc_interval_abs;
178
f427ee49
A
179static void _internal_call_init(void);
180
181static thread_call_t _internal_call_allocate(thread_call_func_t func, thread_call_param_t param0);
182static bool _is_internal_call(thread_call_t call);
183static void _internal_call_release(thread_call_t call);
184static bool _pending_call_enqueue(thread_call_t call, thread_call_group_t group, uint64_t now);
185static bool _delayed_call_enqueue(thread_call_t call, thread_call_group_t group,
0a7de745 186 uint64_t deadline, thread_call_flavor_t flavor);
f427ee49
A
187static bool _call_dequeue(thread_call_t call, thread_call_group_t group);
188static void thread_call_wake(thread_call_group_t group);
0a7de745
A
189static void thread_call_daemon(void *arg);
190static void thread_call_thread(thread_call_group_t group, wait_result_t wres);
191static void thread_call_dealloc_timer(timer_call_param_t p0, timer_call_param_t p1);
5ba3f43e 192static void thread_call_group_setup(thread_call_group_t group);
0a7de745
A
193static void sched_call_thread(int type, thread_t thread);
194static void thread_call_start_deallocate_timer(thread_call_group_t group);
195static void thread_call_wait_locked(thread_call_t call, spl_t s);
f427ee49 196static bool thread_call_wait_once_locked(thread_call_t call, spl_t s);
5ba3f43e 197
0a7de745
A
198static boolean_t thread_call_enter_delayed_internal(thread_call_t call,
199 thread_call_func_t alt_func, thread_call_param_t alt_param0,
200 thread_call_param_t param1, uint64_t deadline,
201 uint64_t leeway, unsigned int flags);
1c79356b 202
5ba3f43e
A
203/* non-static so dtrace can find it rdar://problem/31156135&31379348 */
204extern void thread_call_delayed_timer(timer_call_param_t p0, timer_call_param_t p1);
6d2010ae 205
f427ee49 206LCK_GRP_DECLARE(thread_call_lck_grp, "thread_call");
316670eb 207
6d2010ae 208
f427ee49
A
209static void
210thread_call_lock_spin(thread_call_group_t group)
211{
212 lck_ticket_lock(&group->tcg_lock, &thread_call_lck_grp);
213}
214
215static void
216thread_call_unlock(thread_call_group_t group)
217{
218 lck_ticket_unlock(&group->tcg_lock);
219}
6d2010ae 220
f427ee49
A
221static void __assert_only
222thread_call_assert_locked(thread_call_group_t group)
223{
224 lck_ticket_assert_owned(&group->tcg_lock);
225}
5ba3f43e 226
6d2010ae 227
f427ee49
A
228static spl_t
229disable_ints_and_lock(thread_call_group_t group)
316670eb 230{
5ba3f43e 231 spl_t s = splsched();
f427ee49 232 thread_call_lock_spin(group);
316670eb
A
233
234 return s;
235}
236
f427ee49
A
237static void
238enable_ints_and_unlock(thread_call_group_t group, spl_t s)
316670eb 239{
f427ee49 240 thread_call_unlock(group);
fe8ab488 241 splx(s);
316670eb
A
242}
243
f427ee49
A
244/* Lock held */
245static thread_call_group_t
246thread_call_get_group(thread_call_t call)
247{
248 thread_call_index_t index = call->tc_index;
249
250 assert(index >= 0 && index < THREAD_CALL_INDEX_MAX);
251
252 return &thread_call_groups[index];
253}
254
255/* Lock held */
256static thread_call_flavor_t
257thread_call_get_flavor(thread_call_t call)
258{
259 return (call->tc_flags & THREAD_CALL_FLAG_CONTINUOUS) ? TCF_CONTINUOUS : TCF_ABSOLUTE;
260}
261
262/* Lock held */
263static thread_call_flavor_t
264thread_call_set_flavor(thread_call_t call, thread_call_flavor_t flavor)
265{
266 assert(flavor == TCF_CONTINUOUS || flavor == TCF_ABSOLUTE);
267 thread_call_flavor_t old_flavor = thread_call_get_flavor(call);
268
269 if (old_flavor != flavor) {
270 if (flavor == TCF_CONTINUOUS) {
271 call->tc_flags |= THREAD_CALL_FLAG_CONTINUOUS;
272 } else {
273 call->tc_flags &= ~THREAD_CALL_FLAG_CONTINUOUS;
274 }
275 }
276
277 return old_flavor;
278}
279
280/* returns true if it was on a queue */
281static bool
282thread_call_enqueue_tail(
283 thread_call_t call,
284 queue_t new_queue)
285{
286 queue_t old_queue = call->tc_queue;
287
288 thread_call_group_t group = thread_call_get_group(call);
289 thread_call_flavor_t flavor = thread_call_get_flavor(call);
290
291 if (old_queue != NULL &&
292 old_queue != &group->delayed_queues[flavor]) {
293 panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
294 }
295
296 if (old_queue == &group->delayed_queues[flavor]) {
297 priority_queue_remove(&group->delayed_pqueues[flavor], &call->tc_pqlink);
298 }
299
300 if (old_queue == NULL) {
301 enqueue_tail(new_queue, &call->tc_qlink);
302 } else {
303 re_queue_tail(new_queue, &call->tc_qlink);
304 }
305
306 call->tc_queue = new_queue;
307
308 return old_queue != NULL;
309}
310
311static queue_head_t *
312thread_call_dequeue(
313 thread_call_t call)
314{
315 queue_t old_queue = call->tc_queue;
316
317 thread_call_group_t group = thread_call_get_group(call);
318 thread_call_flavor_t flavor = thread_call_get_flavor(call);
319
320 if (old_queue != NULL &&
321 old_queue != &group->pending_queue &&
322 old_queue != &group->delayed_queues[flavor]) {
323 panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
324 }
325
326 if (old_queue == &group->delayed_queues[flavor]) {
327 priority_queue_remove(&group->delayed_pqueues[flavor], &call->tc_pqlink);
328 }
329
330 if (old_queue != NULL) {
331 remqueue(&call->tc_qlink);
332
333 call->tc_queue = NULL;
334 }
335 return old_queue;
336}
337
338static queue_head_t *
339thread_call_enqueue_deadline(
340 thread_call_t call,
341 thread_call_group_t group,
342 thread_call_flavor_t flavor,
343 uint64_t deadline)
344{
345 queue_t old_queue = call->tc_queue;
346 queue_t new_queue = &group->delayed_queues[flavor];
347
348 thread_call_flavor_t old_flavor = thread_call_set_flavor(call, flavor);
349
350 if (old_queue != NULL &&
351 old_queue != &group->pending_queue &&
352 old_queue != &group->delayed_queues[old_flavor]) {
353 panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
354 }
355
356 if (old_queue == new_queue) {
357 /* optimize the same-queue case to avoid a full re-insert */
358 uint64_t old_deadline = call->tc_pqlink.deadline;
359 call->tc_pqlink.deadline = deadline;
360
361 if (old_deadline < deadline) {
362 priority_queue_entry_increased(&group->delayed_pqueues[flavor],
363 &call->tc_pqlink);
364 } else {
365 priority_queue_entry_decreased(&group->delayed_pqueues[flavor],
366 &call->tc_pqlink);
367 }
368 } else {
369 if (old_queue == &group->delayed_queues[old_flavor]) {
370 priority_queue_remove(&group->delayed_pqueues[old_flavor],
371 &call->tc_pqlink);
372 }
373
374 call->tc_pqlink.deadline = deadline;
375
376 priority_queue_insert(&group->delayed_pqueues[flavor], &call->tc_pqlink);
377 }
378
379 if (old_queue == NULL) {
380 enqueue_tail(new_queue, &call->tc_qlink);
381 } else if (old_queue != new_queue) {
382 re_queue_tail(new_queue, &call->tc_qlink);
383 }
384
385 call->tc_queue = new_queue;
386
387 return old_queue;
388}
389
390uint64_t
391thread_call_get_armed_deadline(thread_call_t call)
392{
393 return call->tc_pqlink.deadline;
394}
395
396
397static bool
316670eb
A
398group_isparallel(thread_call_group_t group)
399{
f427ee49 400 return (group->tcg_flags & TCG_PARALLEL) != 0;
316670eb
A
401}
402
f427ee49 403static bool
5ba3f43e 404thread_call_group_should_add_thread(thread_call_group_t group)
316670eb 405{
5ba3f43e
A
406 if ((group->active_count + group->blocked_count + group->idle_count) >= THREAD_CALL_GROUP_MAX_THREADS) {
407 panic("thread_call group '%s' reached max thread cap (%d): active: %d, blocked: %d, idle: %d",
0a7de745
A
408 group->tcg_name, THREAD_CALL_GROUP_MAX_THREADS,
409 group->active_count, group->blocked_count, group->idle_count);
5ba3f43e 410 }
316670eb 411
f427ee49 412 if (group_isparallel(group) == false) {
316670eb 413 if (group->pending_count > 0 && group->active_count == 0) {
f427ee49 414 return true;
316670eb
A
415 }
416
f427ee49 417 return false;
316670eb
A
418 }
419
420 if (group->pending_count > 0) {
421 if (group->idle_count > 0) {
f427ee49 422 return false;
316670eb
A
423 }
424
5ba3f43e 425 uint32_t thread_count = group->active_count;
316670eb
A
426
427 /*
428 * Add a thread if either there are no threads,
429 * the group has fewer than its target number of
430 * threads, or the amount of work is large relative
431 * to the number of threads. In the last case, pay attention
0a7de745 432 * to the total load on the system, and back off if
5ba3f43e 433 * it's high.
316670eb
A
434 */
435 if ((thread_count == 0) ||
0a7de745
A
436 (thread_count < group->target_thread_count) ||
437 ((group->pending_count > THREAD_CALL_ADD_RATIO * thread_count) &&
438 (sched_mach_factor < THREAD_CALL_MACH_FACTOR_CAP))) {
f427ee49 439 return true;
316670eb
A
440 }
441 }
316670eb 442
f427ee49 443 return false;
316670eb
A
444}
445
446static void
5ba3f43e 447thread_call_group_setup(thread_call_group_t group)
316670eb 448{
f427ee49
A
449 lck_ticket_init(&group->tcg_lock, &thread_call_lck_grp);
450
316670eb 451 queue_init(&group->pending_queue);
316670eb 452
f427ee49
A
453 for (thread_call_flavor_t flavor = 0; flavor < TCF_COUNT; flavor++) {
454 queue_init(&group->delayed_queues[flavor]);
455 priority_queue_init(&group->delayed_pqueues[flavor]);
456 timer_call_setup(&group->delayed_timers[flavor], thread_call_delayed_timer, group);
457 }
458
316670eb
A
459 timer_call_setup(&group->dealloc_timer, thread_call_dealloc_timer, group);
460
c3c9b80d
A
461 waitq_init(&group->waiters_waitq, SYNC_POLICY_DISABLE_IRQ);
462
5ba3f43e 463 /* Reverse the wait order so we re-use the most recently parked thread from the pool */
0a7de745 464 waitq_init(&group->idle_waitq, SYNC_POLICY_REVERSED | SYNC_POLICY_DISABLE_IRQ);
316670eb
A
465}
466
467/*
0a7de745 468 * Simple wrapper for creating threads bound to
316670eb
A
469 * thread call groups.
470 */
f427ee49 471static void
316670eb 472thread_call_thread_create(
0a7de745 473 thread_call_group_t group)
316670eb
A
474{
475 thread_t thread;
476 kern_return_t result;
477
5ba3f43e
A
478 int thread_pri = group->tcg_thread_pri;
479
480 result = kernel_thread_start_priority((thread_continue_t)thread_call_thread,
0a7de745 481 group, thread_pri, &thread);
316670eb 482 if (result != KERN_SUCCESS) {
f427ee49 483 panic("cannot create new thread call thread %d", result);
316670eb
A
484 }
485
5ba3f43e 486 if (thread_pri <= BASEPRI_KERNEL) {
316670eb 487 /*
5ba3f43e
A
488 * THREAD_CALL_PRIORITY_KERNEL and lower don't get to run to completion
489 * in kernel if there are higher priority threads available.
316670eb
A
490 */
491 thread_set_eager_preempt(thread);
492 }
493
5ba3f43e
A
494 char name[MAXTHREADNAMESIZE] = "";
495
496 int group_thread_count = group->idle_count + group->active_count + group->blocked_count;
497
498 snprintf(name, sizeof(name), "thread call %s #%d", group->tcg_name, group_thread_count);
499 thread_set_thread_name(thread, name);
500
316670eb 501 thread_deallocate(thread);
316670eb
A
502}
503
1c79356b 504/*
c910b4d9 505 * thread_call_initialize:
1c79356b 506 *
c910b4d9
A
507 * Initialize this module, called
508 * early during system initialization.
1c79356b 509 */
1c79356b
A
510void
511thread_call_initialize(void)
512{
316670eb 513 nanotime_to_absolutetime(0, THREAD_CALL_DEALLOC_INTERVAL_NS, &thread_call_dealloc_interval_abs);
39037602 514 waitq_init(&daemon_waitq, SYNC_POLICY_DISABLE_IRQ | SYNC_POLICY_FIFO);
c910b4d9 515
0a7de745 516 for (uint32_t i = 0; i < THREAD_CALL_INDEX_MAX; i++) {
5ba3f43e 517 thread_call_group_setup(&thread_call_groups[i]);
0a7de745 518 }
1c79356b 519
f427ee49 520 _internal_call_init();
1c79356b 521
5ba3f43e
A
522 thread_t thread;
523 kern_return_t result;
524
525 result = kernel_thread_start_priority((thread_continue_t)thread_call_daemon,
0a7de745
A
526 NULL, BASEPRI_PREEMPT_HIGH + 1, &thread);
527 if (result != KERN_SUCCESS) {
91447636 528 panic("thread_call_initialize");
0a7de745 529 }
91447636
A
530
531 thread_deallocate(thread);
1c79356b
A
532}
533
534void
c3c9b80d 535thread_call_setup_with_options(
0a7de745
A
536 thread_call_t call,
537 thread_call_func_t func,
c3c9b80d
A
538 thread_call_param_t param0,
539 thread_call_priority_t pri,
540 thread_call_options_t options)
1c79356b 541{
316670eb 542 bzero(call, sizeof(*call));
5ba3f43e 543
f427ee49
A
544 *call = (struct thread_call) {
545 .tc_func = func,
546 .tc_param0 = param0,
f427ee49 547 };
c3c9b80d
A
548
549 switch (pri) {
550 case THREAD_CALL_PRIORITY_HIGH:
551 call->tc_index = THREAD_CALL_INDEX_HIGH;
552 break;
553 case THREAD_CALL_PRIORITY_KERNEL:
554 call->tc_index = THREAD_CALL_INDEX_KERNEL;
555 break;
556 case THREAD_CALL_PRIORITY_USER:
557 call->tc_index = THREAD_CALL_INDEX_USER;
558 break;
559 case THREAD_CALL_PRIORITY_LOW:
560 call->tc_index = THREAD_CALL_INDEX_LOW;
561 break;
562 case THREAD_CALL_PRIORITY_KERNEL_HIGH:
563 call->tc_index = THREAD_CALL_INDEX_KERNEL_HIGH;
564 break;
565 default:
566 panic("Invalid thread call pri value: %d", pri);
567 break;
568 }
569
570 if (options & THREAD_CALL_OPTIONS_ONCE) {
571 call->tc_flags |= THREAD_CALL_ONCE;
572 }
573 if (options & THREAD_CALL_OPTIONS_SIGNAL) {
574 call->tc_flags |= THREAD_CALL_SIGNAL | THREAD_CALL_ONCE;
575 }
576}
577
578void
579thread_call_setup(
580 thread_call_t call,
581 thread_call_func_t func,
582 thread_call_param_t param0)
583{
584 thread_call_setup_with_options(call, func, param0,
585 THREAD_CALL_PRIORITY_HIGH, 0);
f427ee49
A
586}
587
588static void
589_internal_call_init(void)
590{
591 /* Function-only thread calls are only kept in the default HIGH group */
592 thread_call_group_t group = &thread_call_groups[THREAD_CALL_INDEX_HIGH];
593
594 spl_t s = disable_ints_and_lock(group);
595
596 queue_init(&thread_call_internal_queue);
597
598 for (unsigned i = 0; i < INTERNAL_CALL_COUNT; i++) {
599 enqueue_tail(&thread_call_internal_queue, &internal_call_storage[i].tc_qlink);
600 thread_call_internal_queue_count++;
601 }
5ba3f43e 602
f427ee49 603 enable_ints_and_unlock(group, s);
1c79356b
A
604}
605
606/*
c910b4d9 607 * _internal_call_allocate:
1c79356b 608 *
c910b4d9 609 * Allocate an internal callout entry.
1c79356b 610 *
c910b4d9 611 * Called with thread_call_lock held.
1c79356b 612 */
f427ee49 613static thread_call_t
39236c6e 614_internal_call_allocate(thread_call_func_t func, thread_call_param_t param0)
1c79356b 615{
f427ee49
A
616 /* Function-only thread calls are only kept in the default HIGH group */
617 thread_call_group_t group = &thread_call_groups[THREAD_CALL_INDEX_HIGH];
0a7de745 618
f427ee49 619 spl_t s = disable_ints_and_lock(group);
5ba3f43e 620
f427ee49
A
621 thread_call_t call = qe_dequeue_head(&thread_call_internal_queue,
622 struct thread_call, tc_qlink);
623
624 if (call == NULL) {
625 panic("_internal_call_allocate: thread_call_internal_queue empty");
626 }
5ba3f43e 627
0a7de745 628 thread_call_internal_queue_count--;
39236c6e 629
0a7de745 630 thread_call_setup(call, func, param0);
c3c9b80d
A
631 /* THREAD_CALL_ALLOC not set, do not free back to zone */
632 assert((call->tc_flags & THREAD_CALL_ALLOC) == 0);
f427ee49 633 enable_ints_and_unlock(group, s);
39236c6e 634
0a7de745 635 return call;
1c79356b
A
636}
637
f427ee49
A
638/* Check if a call is internal and needs to be returned to the internal pool. */
639static bool
640_is_internal_call(thread_call_t call)
641{
642 if (call >= internal_call_storage &&
643 call < &internal_call_storage[INTERNAL_CALL_COUNT]) {
644 assert((call->tc_flags & THREAD_CALL_ALLOC) == 0);
645 return true;
646 }
647 return false;
648}
649
1c79356b 650/*
c910b4d9 651 * _internal_call_release:
1c79356b 652 *
c910b4d9 653 * Release an internal callout entry which
f427ee49 654 * is no longer pending (or delayed).
1c79356b 655 *
0a7de745 656 * Called with thread_call_lock held.
1c79356b 657 */
f427ee49 658static void
5ba3f43e 659_internal_call_release(thread_call_t call)
1c79356b 660{
f427ee49
A
661 assert(_is_internal_call(call));
662
663 thread_call_group_t group = thread_call_get_group(call);
664
665 assert(group == &thread_call_groups[THREAD_CALL_INDEX_HIGH]);
666 thread_call_assert_locked(group);
667
668 enqueue_head(&thread_call_internal_queue, &call->tc_qlink);
669 thread_call_internal_queue_count++;
1c79356b
A
670}
671
672/*
c910b4d9 673 * _pending_call_enqueue:
1c79356b 674 *
c910b4d9
A
675 * Place an entry at the end of the
676 * pending queue, to be executed soon.
1c79356b 677 *
c910b4d9
A
678 * Returns TRUE if the entry was already
679 * on a queue.
1c79356b 680 *
c910b4d9 681 * Called with thread_call_lock held.
1c79356b 682 */
f427ee49
A
683static bool
684_pending_call_enqueue(thread_call_t call,
685 thread_call_group_t group,
686 uint64_t now)
1c79356b 687{
5ba3f43e 688 if ((THREAD_CALL_ONCE | THREAD_CALL_RUNNING)
0a7de745 689 == (call->tc_flags & (THREAD_CALL_ONCE | THREAD_CALL_RUNNING))) {
f427ee49 690 call->tc_pqlink.deadline = 0;
5ba3f43e 691
f427ee49 692 thread_call_flags_t flags = call->tc_flags;
5ba3f43e
A
693 call->tc_flags |= THREAD_CALL_RESCHEDULE;
694
f427ee49
A
695 assert(call->tc_queue == NULL);
696
697 return flags & THREAD_CALL_RESCHEDULE;
5ba3f43e 698 }
1c79356b 699
f427ee49 700 call->tc_pending_timestamp = now;
316670eb 701
f427ee49
A
702 bool was_on_queue = thread_call_enqueue_tail(call, &group->pending_queue);
703
704 if (!was_on_queue) {
316670eb
A
705 call->tc_submit_count++;
706 }
1c79356b 707
c910b4d9 708 group->pending_count++;
1c79356b 709
316670eb
A
710 thread_call_wake(group);
711
f427ee49 712 return was_on_queue;
1c79356b
A
713}
714
715/*
c910b4d9 716 * _delayed_call_enqueue:
1c79356b 717 *
c910b4d9
A
718 * Place an entry on the delayed queue,
719 * after existing entries with an earlier
0a7de745 720 * (or identical) deadline.
1c79356b 721 *
c910b4d9
A
722 * Returns TRUE if the entry was already
723 * on a queue.
1c79356b 724 *
c910b4d9 725 * Called with thread_call_lock held.
1c79356b 726 */
f427ee49 727static bool
1c79356b 728_delayed_call_enqueue(
5ba3f43e
A
729 thread_call_t call,
730 thread_call_group_t group,
731 uint64_t deadline,
732 thread_call_flavor_t flavor)
1c79356b 733{
5ba3f43e 734 if ((THREAD_CALL_ONCE | THREAD_CALL_RUNNING)
0a7de745 735 == (call->tc_flags & (THREAD_CALL_ONCE | THREAD_CALL_RUNNING))) {
f427ee49 736 call->tc_pqlink.deadline = deadline;
1c79356b 737
f427ee49 738 thread_call_flags_t flags = call->tc_flags;
5ba3f43e
A
739 call->tc_flags |= THREAD_CALL_RESCHEDULE;
740
f427ee49
A
741 assert(call->tc_queue == NULL);
742 thread_call_set_flavor(call, flavor);
743
744 return flags & THREAD_CALL_RESCHEDULE;
5ba3f43e
A
745 }
746
f427ee49 747 queue_head_t *old_queue = thread_call_enqueue_deadline(call, group, flavor, deadline);
c910b4d9 748
39037602 749 if (old_queue == &group->pending_queue) {
c910b4d9 750 group->pending_count--;
39037602 751 } else if (old_queue == NULL) {
316670eb 752 call->tc_submit_count++;
39037602 753 }
c910b4d9 754
0a7de745 755 return old_queue != NULL;
1c79356b
A
756}
757
758/*
c910b4d9 759 * _call_dequeue:
1c79356b 760 *
c910b4d9 761 * Remove an entry from a queue.
1c79356b 762 *
c910b4d9 763 * Returns TRUE if the entry was on a queue.
1c79356b 764 *
c910b4d9 765 * Called with thread_call_lock held.
1c79356b 766 */
f427ee49 767static bool
c910b4d9 768_call_dequeue(
0a7de745
A
769 thread_call_t call,
770 thread_call_group_t group)
1c79356b 771{
f427ee49 772 queue_head_t *old_queue = thread_call_dequeue(call);
c910b4d9 773
f427ee49
A
774 if (old_queue == NULL) {
775 return false;
776 }
c910b4d9 777
f427ee49 778 call->tc_finish_count++;
5ba3f43e 779
f427ee49
A
780 if (old_queue == &group->pending_queue) {
781 group->pending_count--;
316670eb 782 }
c910b4d9 783
f427ee49 784 return true;
1c79356b
A
785}
786
787/*
5ba3f43e 788 * _arm_delayed_call_timer:
1c79356b 789 *
5ba3f43e
A
790 * Check if the timer needs to be armed for this flavor,
791 * and if so, arm it.
1c79356b 792 *
5ba3f43e
A
793 * If call is non-NULL, only re-arm the timer if the specified call
794 * is the first in the queue.
795 *
796 * Returns true if the timer was armed/re-armed, false if it was left unset
797 * Caller should cancel the timer if need be.
798 *
799 * Called with thread_call_lock held.
1c79356b 800 */
5ba3f43e
A
801static bool
802_arm_delayed_call_timer(thread_call_t new_call,
0a7de745
A
803 thread_call_group_t group,
804 thread_call_flavor_t flavor)
1c79356b 805{
5ba3f43e 806 /* No calls implies no timer needed */
0a7de745 807 if (queue_empty(&group->delayed_queues[flavor])) {
5ba3f43e 808 return false;
0a7de745 809 }
5ba3f43e 810
f427ee49 811 thread_call_t call = priority_queue_min(&group->delayed_pqueues[flavor], struct thread_call, tc_pqlink);
5ba3f43e
A
812
813 /* We only need to change the hard timer if this new call is the first in the list */
0a7de745 814 if (new_call != NULL && new_call != call) {
5ba3f43e 815 return false;
0a7de745 816 }
39236c6e 817
f427ee49 818 assert((call->tc_soft_deadline != 0) && ((call->tc_soft_deadline <= call->tc_pqlink.deadline)));
39037602 819
5ba3f43e 820 uint64_t fire_at = call->tc_soft_deadline;
39037602 821
5ba3f43e 822 if (flavor == TCF_CONTINUOUS) {
f427ee49 823 assert(call->tc_flags & THREAD_CALL_FLAG_CONTINUOUS);
39037602 824 fire_at = continuoustime_to_absolutetime(fire_at);
5ba3f43e 825 } else {
f427ee49 826 assert((call->tc_flags & THREAD_CALL_FLAG_CONTINUOUS) == 0);
39037602 827 }
39236c6e 828
5ba3f43e
A
829 /*
830 * Note: This picks the soonest-deadline call's leeway as the hard timer's leeway,
831 * which does not take into account later-deadline timers with a larger leeway.
832 * This is a valid coalescing behavior, but masks a possible window to
833 * fire a timer instead of going idle.
834 */
f427ee49 835 uint64_t leeway = call->tc_pqlink.deadline - call->tc_soft_deadline;
5ba3f43e
A
836
837 timer_call_enter_with_leeway(&group->delayed_timers[flavor], (timer_call_param_t)flavor,
39037602 838 fire_at, leeway,
0a7de745 839 TIMER_CALL_SYS_CRITICAL | TIMER_CALL_LEEWAY,
fe8ab488 840 ((call->tc_flags & THREAD_CALL_RATELIMITED) == THREAD_CALL_RATELIMITED));
316670eb 841
5ba3f43e 842 return true;
1c79356b
A
843}
844
845/*
5ba3f43e 846 * _cancel_func_from_queue:
1c79356b 847 *
c910b4d9 848 * Remove the first (or all) matching
5ba3f43e 849 * entries from the specified queue.
1c79356b 850 *
5ba3f43e 851 * Returns TRUE if any matching entries
c910b4d9 852 * were found.
1c79356b 853 *
c910b4d9 854 * Called with thread_call_lock held.
1c79356b 855 */
c910b4d9 856static boolean_t
5ba3f43e 857_cancel_func_from_queue(thread_call_func_t func,
0a7de745
A
858 thread_call_param_t param0,
859 thread_call_group_t group,
860 boolean_t remove_all,
861 queue_head_t *queue)
1c79356b 862{
5ba3f43e
A
863 boolean_t call_removed = FALSE;
864 thread_call_t call;
316670eb 865
f427ee49
A
866 qe_foreach_element_safe(call, queue, tc_qlink) {
867 if (call->tc_func != func ||
868 call->tc_param0 != param0) {
5ba3f43e
A
869 continue;
870 }
316670eb 871
5ba3f43e 872 _call_dequeue(call, group);
316670eb 873
f427ee49
A
874 if (_is_internal_call(call)) {
875 _internal_call_release(call);
876 }
316670eb 877
5ba3f43e 878 call_removed = TRUE;
0a7de745 879 if (!remove_all) {
5ba3f43e 880 break;
0a7de745 881 }
316670eb
A
882 }
883
0a7de745 884 return call_removed;
1c79356b
A
885}
886
1c79356b 887/*
c910b4d9 888 * thread_call_func_delayed:
1c79356b 889 *
c910b4d9
A
890 * Enqueue a function callout to
891 * occur at the stated time.
1c79356b 892 */
1c79356b
A
893void
894thread_call_func_delayed(
0a7de745
A
895 thread_call_func_t func,
896 thread_call_param_t param,
897 uint64_t deadline)
1c79356b 898{
39236c6e
A
899 (void)thread_call_enter_delayed_internal(NULL, func, param, 0, deadline, 0, 0);
900}
316670eb 901
39236c6e
A
902/*
903 * thread_call_func_delayed_with_leeway:
904 *
905 * Same as thread_call_func_delayed(), but with
906 * leeway/flags threaded through.
907 */
316670eb 908
39236c6e
A
909void
910thread_call_func_delayed_with_leeway(
0a7de745
A
911 thread_call_func_t func,
912 thread_call_param_t param,
913 uint64_t deadline,
914 uint64_t leeway,
915 uint32_t flags)
39236c6e
A
916{
917 (void)thread_call_enter_delayed_internal(NULL, func, param, 0, deadline, leeway, flags);
1c79356b
A
918}
919
920/*
c910b4d9 921 * thread_call_func_cancel:
1c79356b 922 *
c910b4d9 923 * Dequeue a function callout.
1c79356b 924 *
c910b4d9
A
925 * Removes one (or all) { function, argument }
926 * instance(s) from either (or both)
927 * the pending and the delayed queue,
928 * in that order.
1c79356b 929 *
c910b4d9 930 * Returns TRUE if any calls were cancelled.
5ba3f43e
A
931 *
932 * This iterates all of the pending or delayed thread calls in the group,
933 * which is really inefficient. Switch to an allocated thread call instead.
f427ee49
A
934 *
935 * TODO: Give 'func' thread calls their own group, so this silliness doesn't
936 * affect the main 'high' group.
1c79356b 937 */
1c79356b
A
938boolean_t
939thread_call_func_cancel(
0a7de745
A
940 thread_call_func_t func,
941 thread_call_param_t param,
942 boolean_t cancel_all)
1c79356b 943{
0a7de745 944 boolean_t result;
1c79356b 945
39037602
A
946 assert(func != NULL);
947
5ba3f43e
A
948 /* Function-only thread calls are only kept in the default HIGH group */
949 thread_call_group_t group = &thread_call_groups[THREAD_CALL_INDEX_HIGH];
316670eb 950
f427ee49
A
951 spl_t s = disable_ints_and_lock(group);
952
5ba3f43e
A
953 if (cancel_all) {
954 /* exhaustively search every queue, and return true if any search found something */
955 result = _cancel_func_from_queue(func, param, group, cancel_all, &group->pending_queue) |
0a7de745
A
956 _cancel_func_from_queue(func, param, group, cancel_all, &group->delayed_queues[TCF_ABSOLUTE]) |
957 _cancel_func_from_queue(func, param, group, cancel_all, &group->delayed_queues[TCF_CONTINUOUS]);
5ba3f43e
A
958 } else {
959 /* early-exit as soon as we find something, don't search other queues */
960 result = _cancel_func_from_queue(func, param, group, cancel_all, &group->pending_queue) ||
0a7de745
A
961 _cancel_func_from_queue(func, param, group, cancel_all, &group->delayed_queues[TCF_ABSOLUTE]) ||
962 _cancel_func_from_queue(func, param, group, cancel_all, &group->delayed_queues[TCF_CONTINUOUS]);
5ba3f43e
A
963 }
964
f427ee49 965 enable_ints_and_unlock(group, s);
1c79356b 966
0a7de745 967 return result;
1c79356b
A
968}
969
316670eb 970/*
5ba3f43e
A
971 * Allocate a thread call with a given priority. Importances other than
972 * THREAD_CALL_PRIORITY_HIGH or THREAD_CALL_PRIORITY_KERNEL_HIGH will be run in threads
973 * with eager preemption enabled (i.e. may be aggressively preempted by higher-priority
974 * threads which are not in the normal "urgent" bands).
316670eb
A
975 */
976thread_call_t
977thread_call_allocate_with_priority(
0a7de745
A
978 thread_call_func_t func,
979 thread_call_param_t param0,
980 thread_call_priority_t pri)
316670eb 981{
5ba3f43e
A
982 return thread_call_allocate_with_options(func, param0, pri, 0);
983}
316670eb 984
5ba3f43e
A
985thread_call_t
986thread_call_allocate_with_options(
0a7de745
A
987 thread_call_func_t func,
988 thread_call_param_t param0,
989 thread_call_priority_t pri,
990 thread_call_options_t options)
5ba3f43e 991{
c3c9b80d 992 thread_call_t call = zalloc(thread_call_zone);
316670eb 993
c3c9b80d
A
994 thread_call_setup_with_options(call, func, param0, pri, options);
995 call->tc_refs = 1;
996 call->tc_flags |= THREAD_CALL_ALLOC;
316670eb
A
997
998 return call;
999}
1000
5ba3f43e
A
1001thread_call_t
1002thread_call_allocate_with_qos(thread_call_func_t func,
0a7de745
A
1003 thread_call_param_t param0,
1004 int qos_tier,
1005 thread_call_options_t options)
5ba3f43e
A
1006{
1007 thread_call_t call = thread_call_allocate(func, param0);
1008
1009 switch (qos_tier) {
0a7de745
A
1010 case THREAD_QOS_UNSPECIFIED:
1011 call->tc_index = THREAD_CALL_INDEX_HIGH;
1012 break;
1013 case THREAD_QOS_LEGACY:
1014 call->tc_index = THREAD_CALL_INDEX_USER;
1015 break;
1016 case THREAD_QOS_MAINTENANCE:
1017 case THREAD_QOS_BACKGROUND:
1018 call->tc_index = THREAD_CALL_INDEX_LOW;
1019 break;
1020 case THREAD_QOS_UTILITY:
1021 call->tc_index = THREAD_CALL_INDEX_QOS_UT;
1022 break;
1023 case THREAD_QOS_USER_INITIATED:
1024 call->tc_index = THREAD_CALL_INDEX_QOS_IN;
1025 break;
1026 case THREAD_QOS_USER_INTERACTIVE:
1027 call->tc_index = THREAD_CALL_INDEX_QOS_UI;
1028 break;
1029 default:
1030 panic("Invalid thread call qos value: %d", qos_tier);
1031 break;
5ba3f43e
A
1032 }
1033
0a7de745 1034 if (options & THREAD_CALL_OPTIONS_ONCE) {
5ba3f43e 1035 call->tc_flags |= THREAD_CALL_ONCE;
0a7de745 1036 }
5ba3f43e
A
1037
1038 /* does not support THREAD_CALL_OPTIONS_SIGNAL */
1039
1040 return call;
1041}
1042
1043
1c79356b 1044/*
c910b4d9 1045 * thread_call_allocate:
1c79356b 1046 *
c910b4d9 1047 * Allocate a callout entry.
1c79356b 1048 */
1c79356b
A
1049thread_call_t
1050thread_call_allocate(
0a7de745
A
1051 thread_call_func_t func,
1052 thread_call_param_t param0)
1c79356b 1053{
c3c9b80d
A
1054 return thread_call_allocate_with_options(func, param0,
1055 THREAD_CALL_PRIORITY_HIGH, 0);
1c79356b
A
1056}
1057
1058/*
c910b4d9 1059 * thread_call_free:
1c79356b 1060 *
316670eb
A
1061 * Release a callout. If the callout is currently
1062 * executing, it will be freed when all invocations
1063 * finish.
5ba3f43e
A
1064 *
1065 * If the callout is currently armed to fire again, then
1066 * freeing is not allowed and returns FALSE. The
1067 * client must have canceled the pending invocation before freeing.
1c79356b 1068 */
1c79356b
A
1069boolean_t
1070thread_call_free(
0a7de745 1071 thread_call_t call)
1c79356b 1072{
f427ee49
A
1073 thread_call_group_t group = thread_call_get_group(call);
1074
1075 spl_t s = disable_ints_and_lock(group);
1c79356b 1076
f427ee49 1077 if (call->tc_queue != NULL ||
0a7de745 1078 ((call->tc_flags & THREAD_CALL_RESCHEDULE) != 0)) {
f427ee49 1079 thread_call_unlock(group);
316670eb
A
1080 splx(s);
1081
0a7de745 1082 return FALSE;
316670eb
A
1083 }
1084
5ba3f43e 1085 int32_t refs = --call->tc_refs;
316670eb
A
1086 if (refs < 0) {
1087 panic("Refcount negative: %d\n", refs);
5ba3f43e 1088 }
316670eb 1089
5ba3f43e 1090 if ((THREAD_CALL_SIGNAL | THREAD_CALL_RUNNING)
0a7de745 1091 == ((THREAD_CALL_SIGNAL | THREAD_CALL_RUNNING) & call->tc_flags)) {
5ba3f43e
A
1092 thread_call_wait_once_locked(call, s);
1093 /* thread call lock has been unlocked */
1094 } else {
f427ee49 1095 enable_ints_and_unlock(group, s);
5ba3f43e 1096 }
316670eb
A
1097
1098 if (refs == 0) {
5ba3f43e 1099 assert(call->tc_finish_count == call->tc_submit_count);
316670eb
A
1100 zfree(thread_call_zone, call);
1101 }
1c79356b 1102
0a7de745 1103 return TRUE;
1c79356b
A
1104}
1105
1106/*
c910b4d9 1107 * thread_call_enter:
1c79356b 1108 *
c910b4d9 1109 * Enqueue a callout entry to occur "soon".
1c79356b 1110 *
c910b4d9
A
1111 * Returns TRUE if the call was
1112 * already on a queue.
1c79356b 1113 */
1c79356b
A
1114boolean_t
1115thread_call_enter(
0a7de745 1116 thread_call_t call)
1c79356b 1117{
39037602 1118 return thread_call_enter1(call, 0);
1c79356b
A
1119}
1120
1121boolean_t
1122thread_call_enter1(
0a7de745
A
1123 thread_call_t call,
1124 thread_call_param_t param1)
1c79356b 1125{
f427ee49 1126 assert(call->tc_func != NULL);
5ba3f43e
A
1127 assert((call->tc_flags & THREAD_CALL_SIGNAL) == 0);
1128
f427ee49
A
1129 thread_call_group_t group = thread_call_get_group(call);
1130 bool result = true;
316670eb 1131
f427ee49 1132 spl_t s = disable_ints_and_lock(group);
316670eb 1133
f427ee49
A
1134 if (call->tc_queue != &group->pending_queue) {
1135 result = _pending_call_enqueue(call, group, mach_absolute_time());
c910b4d9 1136 }
1c79356b 1137
f427ee49 1138 call->tc_param1 = param1;
1c79356b 1139
f427ee49 1140 enable_ints_and_unlock(group, s);
1c79356b 1141
0a7de745 1142 return result;
1c79356b
A
1143}
1144
1145/*
c910b4d9 1146 * thread_call_enter_delayed:
1c79356b 1147 *
c910b4d9
A
1148 * Enqueue a callout entry to occur
1149 * at the stated time.
1c79356b 1150 *
c910b4d9
A
1151 * Returns TRUE if the call was
1152 * already on a queue.
1c79356b 1153 */
1c79356b
A
1154boolean_t
1155thread_call_enter_delayed(
0a7de745
A
1156 thread_call_t call,
1157 uint64_t deadline)
1c79356b 1158{
39037602 1159 assert(call != NULL);
39236c6e 1160 return thread_call_enter_delayed_internal(call, NULL, 0, 0, deadline, 0, 0);
1c79356b
A
1161}
1162
1163boolean_t
1164thread_call_enter1_delayed(
0a7de745
A
1165 thread_call_t call,
1166 thread_call_param_t param1,
1167 uint64_t deadline)
39236c6e 1168{
39037602 1169 assert(call != NULL);
39236c6e
A
1170 return thread_call_enter_delayed_internal(call, NULL, 0, param1, deadline, 0, 0);
1171}
1172
1173boolean_t
1174thread_call_enter_delayed_with_leeway(
0a7de745
A
1175 thread_call_t call,
1176 thread_call_param_t param1,
1177 uint64_t deadline,
1178 uint64_t leeway,
1179 unsigned int flags)
39236c6e 1180{
39037602 1181 assert(call != NULL);
39236c6e
A
1182 return thread_call_enter_delayed_internal(call, NULL, 0, param1, deadline, leeway, flags);
1183}
1184
1185
1186/*
1187 * thread_call_enter_delayed_internal:
1188 * enqueue a callout entry to occur at the stated time
1189 *
1190 * Returns True if the call was already on a queue
1191 * params:
1192 * call - structure encapsulating state of the callout
1193 * alt_func/alt_param0 - if call is NULL, allocate temporary storage using these parameters
1194 * deadline - time deadline in nanoseconds
1195 * leeway - timer slack represented as delta of deadline.
1196 * flags - THREAD_CALL_DELAY_XXX : classification of caller's desires wrt timer coalescing.
1197 * THREAD_CALL_DELAY_LEEWAY : value in leeway is used for timer coalescing.
39037602 1198 * THREAD_CALL_CONTINUOUS: thread call will be called according to mach_continuous_time rather
0a7de745 1199 * than mach_absolute_time
39236c6e
A
1200 */
1201boolean_t
1202thread_call_enter_delayed_internal(
0a7de745
A
1203 thread_call_t call,
1204 thread_call_func_t alt_func,
1205 thread_call_param_t alt_param0,
1206 thread_call_param_t param1,
1207 uint64_t deadline,
1208 uint64_t leeway,
1209 unsigned int flags)
1c79356b 1210{
f427ee49 1211 uint64_t now, sdeadline;
5ba3f43e
A
1212
1213 thread_call_flavor_t flavor = (flags & THREAD_CALL_CONTINUOUS) ? TCF_CONTINUOUS : TCF_ABSOLUTE;
316670eb 1214
39236c6e 1215 /* direct mapping between thread_call, timer_call, and timeout_urgency values */
f427ee49 1216 uint32_t urgency = (flags & TIMEOUT_URGENCY_MASK);
39236c6e
A
1217
1218 if (call == NULL) {
1219 /* allocate a structure out of internal storage, as a convenience for BSD callers */
1220 call = _internal_call_allocate(alt_func, alt_param0);
1221 }
1222
f427ee49
A
1223 assert(call->tc_func != NULL);
1224 thread_call_group_t group = thread_call_get_group(call);
1225
1226 spl_t s = disable_ints_and_lock(group);
5ba3f43e 1227
f427ee49
A
1228 /*
1229 * kevent and IOTES let you change flavor for an existing timer, so we have to
1230 * support flipping flavors for enqueued thread calls.
1231 */
5ba3f43e
A
1232 if (flavor == TCF_CONTINUOUS) {
1233 now = mach_continuous_time();
5ba3f43e
A
1234 } else {
1235 now = mach_absolute_time();
39037602
A
1236 }
1237
39236c6e
A
1238 call->tc_flags |= THREAD_CALL_DELAYED;
1239
1240 call->tc_soft_deadline = sdeadline = deadline;
1241
1242 boolean_t ratelimited = FALSE;
f427ee49 1243 uint64_t slop = timer_call_slop(deadline, now, urgency, current_thread(), &ratelimited);
5ba3f43e 1244
0a7de745 1245 if ((flags & THREAD_CALL_DELAY_LEEWAY) != 0 && leeway > slop) {
39236c6e 1246 slop = leeway;
0a7de745 1247 }
39236c6e 1248
0a7de745 1249 if (UINT64_MAX - deadline <= slop) {
39236c6e 1250 deadline = UINT64_MAX;
0a7de745 1251 } else {
39236c6e 1252 deadline += slop;
0a7de745 1253 }
39236c6e 1254
39236c6e 1255 if (ratelimited) {
f427ee49 1256 call->tc_flags |= THREAD_CALL_RATELIMITED;
39236c6e 1257 } else {
f427ee49 1258 call->tc_flags &= ~THREAD_CALL_RATELIMITED;
39236c6e
A
1259 }
1260
f427ee49 1261 call->tc_param1 = param1;
39037602 1262
5ba3f43e 1263 call->tc_ttd = (sdeadline > now) ? (sdeadline - now) : 0;
1c79356b 1264
f427ee49 1265 bool result = _delayed_call_enqueue(call, group, deadline, flavor);
1c79356b 1266
5ba3f43e 1267 _arm_delayed_call_timer(call, group, flavor);
1c79356b 1268
4b17d6b6 1269#if CONFIG_DTRACE
f427ee49 1270 DTRACE_TMR5(thread_callout__create, thread_call_func_t, call->tc_func,
0a7de745
A
1271 uint64_t, (deadline - sdeadline), uint64_t, (call->tc_ttd >> 32),
1272 (unsigned) (call->tc_ttd & 0xFFFFFFFF), call);
4b17d6b6 1273#endif
39037602 1274
f427ee49 1275 enable_ints_and_unlock(group, s);
1c79356b 1276
0a7de745 1277 return result;
1c79356b
A
1278}
1279
1280/*
5ba3f43e
A
1281 * Remove a callout entry from the queue
1282 * Called with thread_call_lock held
1c79356b 1283 */
f427ee49 1284static bool
5ba3f43e 1285thread_call_cancel_locked(thread_call_t call)
1c79356b 1286{
f427ee49
A
1287 bool canceled;
1288
1289 if (call->tc_flags & THREAD_CALL_RESCHEDULE) {
1290 call->tc_flags &= ~THREAD_CALL_RESCHEDULE;
1291 canceled = true;
316670eb 1292
5ba3f43e 1293 /* if reschedule was set, it must not have been queued */
f427ee49 1294 assert(call->tc_queue == NULL);
5ba3f43e 1295 } else {
f427ee49 1296 bool queue_head_changed = false;
316670eb 1297
5ba3f43e
A
1298 thread_call_flavor_t flavor = thread_call_get_flavor(call);
1299 thread_call_group_t group = thread_call_get_group(call);
c910b4d9 1300
f427ee49
A
1301 if (call->tc_pqlink.deadline != 0 &&
1302 call == priority_queue_min(&group->delayed_pqueues[flavor], struct thread_call, tc_pqlink)) {
1303 assert(call->tc_queue == &group->delayed_queues[flavor]);
1304 queue_head_changed = true;
5ba3f43e 1305 }
39236c6e 1306
5ba3f43e 1307 canceled = _call_dequeue(call, group);
316670eb 1308
f427ee49 1309 if (queue_head_changed) {
0a7de745 1310 if (_arm_delayed_call_timer(NULL, group, flavor) == false) {
5ba3f43e 1311 timer_call_cancel(&group->delayed_timers[flavor]);
0a7de745 1312 }
39236c6e
A
1313 }
1314 }
1315
4b17d6b6 1316#if CONFIG_DTRACE
f427ee49 1317 DTRACE_TMR4(thread_callout__cancel, thread_call_func_t, call->tc_func,
0a7de745 1318 0, (call->tc_ttd >> 32), (unsigned) (call->tc_ttd & 0xFFFFFFFF));
4b17d6b6 1319#endif
1c79356b 1320
5ba3f43e
A
1321 return canceled;
1322}
1323
1324/*
1325 * thread_call_cancel:
1326 *
1327 * Dequeue a callout entry.
1328 *
1329 * Returns TRUE if the call was
1330 * on a queue.
1331 */
1332boolean_t
1333thread_call_cancel(thread_call_t call)
1334{
f427ee49
A
1335 thread_call_group_t group = thread_call_get_group(call);
1336
1337 spl_t s = disable_ints_and_lock(group);
5ba3f43e
A
1338
1339 boolean_t result = thread_call_cancel_locked(call);
1340
f427ee49 1341 enable_ints_and_unlock(group, s);
5ba3f43e
A
1342
1343 return result;
1c79356b
A
1344}
1345
316670eb
A
1346/*
1347 * Cancel a thread call. If it cannot be cancelled (i.e.
1348 * is already in flight), waits for the most recent invocation
1349 * to finish. Note that if clients re-submit this thread call,
1350 * it may still be pending or in flight when thread_call_cancel_wait
1351 * returns, but all requests to execute this work item prior
1352 * to the call to thread_call_cancel_wait will have finished.
1353 */
1354boolean_t
5ba3f43e 1355thread_call_cancel_wait(thread_call_t call)
316670eb 1356{
f427ee49
A
1357 thread_call_group_t group = thread_call_get_group(call);
1358
0a7de745 1359 if ((call->tc_flags & THREAD_CALL_ALLOC) == 0) {
5ba3f43e 1360 panic("thread_call_cancel_wait: can't wait on thread call whose storage I don't own");
0a7de745 1361 }
316670eb 1362
0a7de745 1363 if (!ml_get_interrupts_enabled()) {
5ba3f43e 1364 panic("unsafe thread_call_cancel_wait");
0a7de745 1365 }
316670eb 1366
f427ee49
A
1367 thread_t self = current_thread();
1368
1369 if ((thread_get_tag_internal(self) & THREAD_TAG_CALLOUT) &&
1370 self->thc_state && self->thc_state->thc_call == call) {
5ba3f43e 1371 panic("thread_call_cancel_wait: deadlock waiting on self from inside call: %p to function %p",
f427ee49 1372 call, call->tc_func);
0a7de745 1373 }
316670eb 1374
f427ee49 1375 spl_t s = disable_ints_and_lock(group);
316670eb 1376
5ba3f43e 1377 boolean_t canceled = thread_call_cancel_locked(call);
316670eb 1378
5ba3f43e
A
1379 if ((call->tc_flags & THREAD_CALL_ONCE) == THREAD_CALL_ONCE) {
1380 /*
1381 * A cancel-wait on a 'once' call will both cancel
1382 * the pending call and wait for the in-flight call
1383 */
316670eb 1384
5ba3f43e
A
1385 thread_call_wait_once_locked(call, s);
1386 /* thread call lock unlocked */
1387 } else {
1388 /*
1389 * A cancel-wait on a normal call will only wait for the in-flight calls
1390 * if it did not cancel the pending call.
1391 *
1392 * TODO: This seems less than useful - shouldn't it do the wait as well?
1393 */
1394
1395 if (canceled == FALSE) {
1396 thread_call_wait_locked(call, s);
1397 /* thread call lock unlocked */
1398 } else {
f427ee49 1399 enable_ints_and_unlock(group, s);
5ba3f43e
A
1400 }
1401 }
1402
1403 return canceled;
316670eb
A
1404}
1405
1406
1c79356b 1407/*
c910b4d9 1408 * thread_call_wake:
1c79356b 1409 *
c910b4d9
A
1410 * Wake a call thread to service
1411 * pending call entries. May wake
1412 * the daemon thread in order to
1413 * create additional call threads.
1c79356b 1414 *
c910b4d9 1415 * Called with thread_call_lock held.
316670eb
A
1416 *
1417 * For high-priority group, only does wakeup/creation if there are no threads
1418 * running.
1c79356b 1419 */
f427ee49 1420static void
c910b4d9 1421thread_call_wake(
0a7de745 1422 thread_call_group_t group)
1c79356b 1423{
0a7de745 1424 /*
316670eb
A
1425 * New behavior: use threads if you've got 'em.
1426 * Traditional behavior: wake only if no threads running.
1427 */
1428 if (group_isparallel(group) || group->active_count == 0) {
f427ee49
A
1429 if (group->idle_count) {
1430 __assert_only kern_return_t kr;
316670eb 1431
c3c9b80d 1432 kr = waitq_wakeup64_one(&group->idle_waitq, CAST_EVENT64_T(group),
f427ee49
A
1433 THREAD_AWAKENED, WAITQ_ALL_PRIORITIES);
1434 assert(kr == KERN_SUCCESS);
1435
1436 group->idle_count--;
1437 group->active_count++;
1438
1439 if (group->idle_count == 0 && (group->tcg_flags & TCG_DEALLOC_ACTIVE) == TCG_DEALLOC_ACTIVE) {
cc8bc92a 1440 if (timer_call_cancel(&group->dealloc_timer) == TRUE) {
f427ee49 1441 group->tcg_flags &= ~TCG_DEALLOC_ACTIVE;
cc8bc92a 1442 }
316670eb
A
1443 }
1444 } else {
f427ee49
A
1445 if (thread_call_group_should_add_thread(group) &&
1446 os_atomic_cmpxchg(&thread_call_daemon_awake,
1447 false, true, relaxed)) {
c3c9b80d 1448 waitq_wakeup64_all(&daemon_waitq, CAST_EVENT64_T(&thread_call_daemon_awake),
0a7de745 1449 THREAD_AWAKENED, WAITQ_ALL_PRIORITIES);
316670eb
A
1450 }
1451 }
1c79356b
A
1452 }
1453}
1454
9bccf70c 1455/*
2d21ac55 1456 * sched_call_thread:
9bccf70c 1457 *
5ba3f43e 1458 * Call out invoked by the scheduler.
9bccf70c 1459 */
2d21ac55
A
1460static void
1461sched_call_thread(
0a7de745
A
1462 int type,
1463 thread_t thread)
9bccf70c 1464{
0a7de745 1465 thread_call_group_t group;
316670eb 1466
f427ee49
A
1467 assert(thread_get_tag_internal(thread) & THREAD_TAG_CALLOUT);
1468 assert(thread->thc_state != NULL);
1469
1470 group = thread->thc_state->thc_group;
5ba3f43e 1471 assert((group - &thread_call_groups[0]) < THREAD_CALL_INDEX_MAX);
c910b4d9 1472
f427ee49 1473 thread_call_lock_spin(group);
9bccf70c 1474
2d21ac55 1475 switch (type) {
0a7de745
A
1476 case SCHED_CALL_BLOCK:
1477 assert(group->active_count);
1478 --group->active_count;
1479 group->blocked_count++;
1480 if (group->pending_count > 0) {
1481 thread_call_wake(group);
1482 }
1483 break;
9bccf70c 1484
0a7de745
A
1485 case SCHED_CALL_UNBLOCK:
1486 assert(group->blocked_count);
1487 --group->blocked_count;
1488 group->active_count++;
1489 break;
2d21ac55 1490 }
9bccf70c 1491
f427ee49 1492 thread_call_unlock(group);
9bccf70c 1493}
1c79356b 1494
0a7de745
A
1495/*
1496 * Interrupts disabled, lock held; returns the same way.
316670eb
A
1497 * Only called on thread calls whose storage we own. Wakes up
1498 * anyone who might be waiting on this work item and frees it
1499 * if the client has so requested.
1500 */
f427ee49 1501static bool
5ba3f43e 1502thread_call_finish(thread_call_t call, thread_call_group_t group, spl_t *s)
316670eb 1503{
f427ee49
A
1504 assert(thread_call_get_group(call) == group);
1505
1506 bool repend = false;
1507 bool signal = call->tc_flags & THREAD_CALL_SIGNAL;
c3c9b80d 1508 bool alloc = call->tc_flags & THREAD_CALL_ALLOC;
316670eb
A
1509
1510 call->tc_finish_count++;
5ba3f43e 1511
c3c9b80d 1512 if (!signal && alloc) {
5ba3f43e 1513 /* The thread call thread owns a ref until the call is finished */
0a7de745 1514 if (call->tc_refs <= 0) {
5ba3f43e 1515 panic("thread_call_finish: detected over-released thread call: %p", call);
0a7de745 1516 }
5ba3f43e 1517 call->tc_refs--;
0a7de745 1518 }
5ba3f43e 1519
f427ee49 1520 thread_call_flags_t old_flags = call->tc_flags;
5ba3f43e
A
1521 call->tc_flags &= ~(THREAD_CALL_RESCHEDULE | THREAD_CALL_RUNNING | THREAD_CALL_WAIT);
1522
c3c9b80d
A
1523 if ((!alloc || call->tc_refs != 0) &&
1524 (old_flags & THREAD_CALL_RESCHEDULE) != 0) {
f427ee49 1525 assert(old_flags & THREAD_CALL_ONCE);
5ba3f43e
A
1526 thread_call_flavor_t flavor = thread_call_get_flavor(call);
1527
f427ee49
A
1528 if (old_flags & THREAD_CALL_DELAYED) {
1529 uint64_t now = mach_absolute_time();
5ba3f43e 1530 if (flavor == TCF_CONTINUOUS) {
f427ee49 1531 now = absolutetime_to_continuoustime(now);
5ba3f43e 1532 }
f427ee49
A
1533 if (call->tc_soft_deadline <= now) {
1534 /* The deadline has already expired, go straight to pending */
1535 call->tc_flags &= ~(THREAD_CALL_DELAYED | THREAD_CALL_RATELIMITED);
1536 call->tc_pqlink.deadline = 0;
5ba3f43e
A
1537 }
1538 }
f427ee49
A
1539
1540 if (call->tc_pqlink.deadline) {
1541 _delayed_call_enqueue(call, group, call->tc_pqlink.deadline, flavor);
5ba3f43e
A
1542 if (!signal) {
1543 _arm_delayed_call_timer(call, group, flavor);
1544 }
1545 } else if (signal) {
1546 call->tc_submit_count++;
f427ee49 1547 repend = true;
5ba3f43e 1548 } else {
f427ee49 1549 _pending_call_enqueue(call, group, mach_absolute_time());
5ba3f43e
A
1550 }
1551 }
316670eb 1552
c3c9b80d 1553 if (!signal && alloc && call->tc_refs == 0) {
f427ee49
A
1554 if ((old_flags & THREAD_CALL_WAIT) != 0) {
1555 panic("Someone waiting on a thread call that is scheduled for free: %p\n", call->tc_func);
316670eb
A
1556 }
1557
5ba3f43e
A
1558 assert(call->tc_finish_count == call->tc_submit_count);
1559
f427ee49 1560 enable_ints_and_unlock(group, *s);
316670eb
A
1561
1562 zfree(thread_call_zone, call);
1563
f427ee49 1564 *s = disable_ints_and_lock(group);
316670eb
A
1565 }
1566
f427ee49 1567 if ((old_flags & THREAD_CALL_WAIT) != 0) {
d9a64523 1568 /*
c3c9b80d
A
1569 * This may wake up a thread with a registered sched_call.
1570 * That call might need the group lock, so we drop the lock
1571 * to avoid deadlocking.
1572 *
1573 * We also must use a separate waitq from the idle waitq, as
1574 * this path goes waitq lock->thread lock->group lock, but
1575 * the idle wait goes group lock->waitq_lock->thread_lock.
d9a64523 1576 */
f427ee49 1577 thread_call_unlock(group);
c3c9b80d
A
1578
1579 waitq_wakeup64_all(&group->waiters_waitq, CAST_EVENT64_T(call),
1580 THREAD_AWAKENED, WAITQ_ALL_PRIORITIES);
1581
f427ee49 1582 thread_call_lock_spin(group);
d9a64523
A
1583 /* THREAD_CALL_SIGNAL call may have been freed */
1584 }
1585
0a7de745 1586 return repend;
5ba3f43e
A
1587}
1588
1589/*
1590 * thread_call_invoke
1591 *
1592 * Invoke the function provided for this thread call
1593 *
1594 * Note that the thread call object can be deallocated by the function if we do not control its storage.
1595 */
1596static void __attribute__((noinline))
f427ee49
A
1597thread_call_invoke(thread_call_func_t func,
1598 thread_call_param_t param0,
1599 thread_call_param_t param1,
1600 __unused thread_call_t call)
5ba3f43e 1601{
5ba3f43e
A
1602#if DEVELOPMENT || DEBUG
1603 KERNEL_DEBUG_CONSTANT(
0a7de745
A
1604 MACHDBG_CODE(DBG_MACH_SCHED, MACH_CALLOUT) | DBG_FUNC_START,
1605 VM_KERNEL_UNSLIDE(func), VM_KERNEL_ADDRHIDE(param0), VM_KERNEL_ADDRHIDE(param1), 0, 0);
5ba3f43e
A
1606#endif /* DEVELOPMENT || DEBUG */
1607
1608#if CONFIG_DTRACE
1609 uint64_t tc_ttd = call->tc_ttd;
1610 boolean_t is_delayed = call->tc_flags & THREAD_CALL_DELAYED;
1611 DTRACE_TMR6(thread_callout__start, thread_call_func_t, func, int, 0, int, (tc_ttd >> 32),
0a7de745 1612 (unsigned) (tc_ttd & 0xFFFFFFFF), is_delayed, call);
5ba3f43e
A
1613#endif
1614
1615 (*func)(param0, param1);
1616
1617#if CONFIG_DTRACE
1618 DTRACE_TMR6(thread_callout__end, thread_call_func_t, func, int, 0, int, (tc_ttd >> 32),
0a7de745 1619 (unsigned) (tc_ttd & 0xFFFFFFFF), is_delayed, call);
5ba3f43e
A
1620#endif
1621
1622#if DEVELOPMENT || DEBUG
1623 KERNEL_DEBUG_CONSTANT(
0a7de745
A
1624 MACHDBG_CODE(DBG_MACH_SCHED, MACH_CALLOUT) | DBG_FUNC_END,
1625 VM_KERNEL_UNSLIDE(func), 0, 0, 0, 0);
5ba3f43e 1626#endif /* DEVELOPMENT || DEBUG */
316670eb
A
1627}
1628
1c79356b 1629/*
c910b4d9 1630 * thread_call_thread:
1c79356b 1631 */
c910b4d9
A
1632static void
1633thread_call_thread(
0a7de745
A
1634 thread_call_group_t group,
1635 wait_result_t wres)
1c79356b 1636{
f427ee49 1637 thread_t self = current_thread();
1c79356b 1638
0a7de745 1639 if ((thread_get_tag_internal(self) & THREAD_TAG_CALLOUT) == 0) {
4b17d6b6 1640 (void)thread_set_tag_internal(self, THREAD_TAG_CALLOUT);
0a7de745 1641 }
4b17d6b6 1642
316670eb 1643 /*
0a7de745 1644 * A wakeup with THREAD_INTERRUPTED indicates that
316670eb
A
1645 * we should terminate.
1646 */
1647 if (wres == THREAD_INTERRUPTED) {
1648 thread_terminate(self);
1649
1650 /* NOTREACHED */
1651 panic("thread_terminate() returned?");
1652 }
1653
f427ee49
A
1654 spl_t s = disable_ints_and_lock(group);
1655
1656 struct thread_call_thread_state thc_state = { .thc_group = group };
1657 self->thc_state = &thc_state;
1c79356b 1658
5ba3f43e 1659 thread_sched_call(self, sched_call_thread);
9bccf70c 1660
316670eb 1661 while (group->pending_count > 0) {
f427ee49
A
1662 thread_call_t call = qe_dequeue_head(&group->pending_queue,
1663 struct thread_call, tc_qlink);
39037602 1664 assert(call != NULL);
f427ee49 1665
c910b4d9 1666 group->pending_count--;
f427ee49
A
1667 if (group->pending_count == 0) {
1668 assert(queue_empty(&group->pending_queue));
1669 }
1c79356b 1670
f427ee49
A
1671 thread_call_func_t func = call->tc_func;
1672 thread_call_param_t param0 = call->tc_param0;
1673 thread_call_param_t param1 = call->tc_param1;
316670eb 1674
f427ee49 1675 call->tc_queue = NULL;
1c79356b 1676
f427ee49
A
1677 if (_is_internal_call(call)) {
1678 _internal_call_release(call);
1679 }
1c79356b 1680
316670eb
A
1681 /*
1682 * Can only do wakeups for thread calls whose storage
1683 * we control.
1684 */
f427ee49
A
1685 bool needs_finish = false;
1686 if (call->tc_flags & THREAD_CALL_ALLOC) {
c3c9b80d
A
1687 call->tc_refs++; /* Delay free until we're done */
1688 }
1689 if (call->tc_flags & (THREAD_CALL_ALLOC | THREAD_CALL_ONCE)) {
1690 /*
1691 * If THREAD_CALL_ONCE is used, and the timer wasn't
1692 * THREAD_CALL_ALLOC, then clients swear they will use
1693 * thread_call_cancel_wait() before destroying
1694 * the thread call.
1695 *
1696 * Else, the storage for the thread call might have
1697 * disappeared when thread_call_invoke() ran.
1698 */
f427ee49 1699 needs_finish = true;
5ba3f43e 1700 call->tc_flags |= THREAD_CALL_RUNNING;
0a7de745 1701 }
316670eb 1702
f427ee49
A
1703 thc_state.thc_call = call;
1704 thc_state.thc_call_pending_timestamp = call->tc_pending_timestamp;
1705 thc_state.thc_call_soft_deadline = call->tc_soft_deadline;
1706 thc_state.thc_call_hard_deadline = call->tc_pqlink.deadline;
1707 thc_state.thc_func = func;
1708 thc_state.thc_param0 = param0;
1709 thc_state.thc_param1 = param1;
1710 thc_state.thc_IOTES_invocation_timestamp = 0;
1711
1712 enable_ints_and_unlock(group, s);
1713
1714 thc_state.thc_call_start = mach_absolute_time();
1c79356b 1715
5ba3f43e 1716 thread_call_invoke(func, param0, param1, call);
39236c6e 1717
f427ee49
A
1718 thc_state.thc_call = NULL;
1719
6d2010ae
A
1720 if (get_preemption_level() != 0) {
1721 int pl = get_preemption_level();
1722 panic("thread_call_thread: preemption_level %d, last callout %p(%p, %p)",
0a7de745 1723 pl, (void *)VM_KERNEL_UNSLIDE(func), param0, param1);
6d2010ae 1724 }
316670eb 1725
f427ee49 1726 s = disable_ints_and_lock(group);
5ba3f43e 1727
f427ee49 1728 if (needs_finish) {
c3c9b80d 1729 /* Release refcount, may free, may temporarily drop lock */
5ba3f43e 1730 thread_call_finish(call, group, &s);
316670eb
A
1731 }
1732 }
9bccf70c 1733
2d21ac55 1734 thread_sched_call(self, NULL);
c910b4d9 1735 group->active_count--;
0a7de745 1736
39236c6e
A
1737 if (self->callout_woken_from_icontext && !self->callout_woke_thread) {
1738 ledger_credit(self->t_ledger, task_ledgers.interrupt_wakeups, 1);
0a7de745
A
1739 if (self->callout_woken_from_platform_idle) {
1740 ledger_credit(self->t_ledger, task_ledgers.platform_idle_wakeups, 1);
1741 }
39236c6e 1742 }
0a7de745 1743
39236c6e
A
1744 self->callout_woken_from_icontext = FALSE;
1745 self->callout_woken_from_platform_idle = FALSE;
1746 self->callout_woke_thread = FALSE;
9bccf70c 1747
f427ee49
A
1748 self->thc_state = NULL;
1749
316670eb
A
1750 if (group_isparallel(group)) {
1751 /*
0a7de745 1752 * For new style of thread group, thread always blocks.
316670eb 1753 * If we have more than the target number of threads,
0a7de745
A
1754 * and this is the first to block, and it isn't active
1755 * already, set a timer for deallocating a thread if we
316670eb
A
1756 * continue to have a surplus.
1757 */
c910b4d9 1758 group->idle_count++;
1c79356b 1759
316670eb
A
1760 if (group->idle_count == 1) {
1761 group->idle_timestamp = mach_absolute_time();
cc8bc92a 1762 }
316670eb 1763
f427ee49 1764 if (((group->tcg_flags & TCG_DEALLOC_ACTIVE) == 0) &&
cc8bc92a 1765 ((group->active_count + group->idle_count) > group->target_thread_count)) {
316670eb 1766 thread_call_start_deallocate_timer(group);
cc8bc92a 1767 }
316670eb
A
1768
1769 /* Wait for more work (or termination) */
c3c9b80d 1770 wres = waitq_assert_wait64(&group->idle_waitq, CAST_EVENT64_T(group), THREAD_INTERRUPTIBLE, 0);
316670eb
A
1771 if (wres != THREAD_WAITING) {
1772 panic("kcall worker unable to assert wait?");
cc8bc92a 1773 }
316670eb 1774
f427ee49 1775 enable_ints_and_unlock(group, s);
1c79356b 1776
c910b4d9 1777 thread_block_parameter((thread_continue_t)thread_call_thread, group);
316670eb
A
1778 } else {
1779 if (group->idle_count < group->target_thread_count) {
1780 group->idle_count++;
c910b4d9 1781
c3c9b80d 1782 waitq_assert_wait64(&group->idle_waitq, CAST_EVENT64_T(group), THREAD_UNINT, 0); /* Interrupted means to exit */
316670eb 1783
f427ee49 1784 enable_ints_and_unlock(group, s);
316670eb
A
1785
1786 thread_block_parameter((thread_continue_t)thread_call_thread, group);
1787 /* NOTREACHED */
1788 }
1789 }
1790
f427ee49 1791 enable_ints_and_unlock(group, s);
316670eb
A
1792
1793 thread_terminate(self);
1c79356b
A
1794 /* NOTREACHED */
1795}
1796
f427ee49
A
1797void
1798thread_call_start_iotes_invocation(__assert_only thread_call_t call)
1799{
1800 thread_t self = current_thread();
1801
1802 if ((thread_get_tag_internal(self) & THREAD_TAG_CALLOUT) == 0) {
1803 /* not a thread call thread, might be a workloop IOTES */
1804 return;
1805 }
1806
1807 assert(self->thc_state);
1808 assert(self->thc_state->thc_call == call);
1809
1810 self->thc_state->thc_IOTES_invocation_timestamp = mach_absolute_time();
1811}
1812
1813
1c79356b 1814/*
316670eb 1815 * thread_call_daemon: walk list of groups, allocating
0a7de745
A
1816 * threads if appropriate (as determined by
1817 * thread_call_group_should_add_thread()).
1c79356b 1818 */
c910b4d9 1819static void
316670eb 1820thread_call_daemon_continue(__unused void *arg)
1c79356b 1821{
f427ee49
A
1822 do {
1823 os_atomic_store(&thread_call_daemon_awake, false, relaxed);
316670eb 1824
f427ee49
A
1825 /* Starting at zero happens to be high-priority first. */
1826 for (int i = 0; i < THREAD_CALL_INDEX_MAX; i++) {
1827 thread_call_group_t group = &thread_call_groups[i];
1828
1829 spl_t s = disable_ints_and_lock(group);
316670eb 1830
f427ee49
A
1831 while (thread_call_group_should_add_thread(group)) {
1832 group->active_count++;
1833
1834 enable_ints_and_unlock(group, s);
1835
1836 thread_call_thread_create(group);
1837
1838 s = disable_ints_and_lock(group);
316670eb
A
1839 }
1840
f427ee49 1841 enable_ints_and_unlock(group, s);
316670eb 1842 }
f427ee49 1843 } while (os_atomic_load(&thread_call_daemon_awake, relaxed));
91447636 1844
c3c9b80d 1845 waitq_assert_wait64(&daemon_waitq, CAST_EVENT64_T(&thread_call_daemon_awake), THREAD_UNINT, 0);
55e303ae 1846
f427ee49
A
1847 if (os_atomic_load(&thread_call_daemon_awake, relaxed)) {
1848 clear_wait(current_thread(), THREAD_AWAKENED);
1849 }
c910b4d9 1850
316670eb 1851 thread_block_parameter((thread_continue_t)thread_call_daemon_continue, NULL);
1c79356b
A
1852 /* NOTREACHED */
1853}
1854
c910b4d9
A
1855static void
1856thread_call_daemon(
0a7de745 1857 __unused void *arg)
1c79356b 1858{
0a7de745 1859 thread_t self = current_thread();
1c79356b 1860
91447636 1861 self->options |= TH_OPT_VMPRIV;
0a7de745 1862 vm_page_free_reserve(2); /* XXX */
316670eb 1863
5ba3f43e
A
1864 thread_set_thread_name(self, "thread_call_daemon");
1865
316670eb
A
1866 thread_call_daemon_continue(NULL);
1867 /* NOTREACHED */
1868}
1869
1870/*
0a7de745 1871 * Schedule timer to deallocate a worker thread if we have a surplus
316670eb
A
1872 * of threads (in excess of the group's target) and at least one thread
1873 * is idle the whole time.
1874 */
1875static void
cc8bc92a 1876thread_call_start_deallocate_timer(thread_call_group_t group)
316670eb 1877{
f427ee49 1878 __assert_only bool already_enqueued;
316670eb
A
1879
1880 assert(group->idle_count > 0);
f427ee49 1881 assert((group->tcg_flags & TCG_DEALLOC_ACTIVE) == 0);
316670eb 1882
f427ee49 1883 group->tcg_flags |= TCG_DEALLOC_ACTIVE;
316670eb 1884
cc8bc92a
A
1885 uint64_t deadline = group->idle_timestamp + thread_call_dealloc_interval_abs;
1886
1887 already_enqueued = timer_call_enter(&group->dealloc_timer, deadline, 0);
1888
f427ee49 1889 assert(already_enqueued == false);
1c79356b
A
1890}
1891
5ba3f43e 1892/* non-static so dtrace can find it rdar://problem/31156135&31379348 */
6d2010ae 1893void
5ba3f43e 1894thread_call_delayed_timer(timer_call_param_t p0, timer_call_param_t p1)
1c79356b 1895{
5ba3f43e
A
1896 thread_call_group_t group = (thread_call_group_t) p0;
1897 thread_call_flavor_t flavor = (thread_call_flavor_t) p1;
1c79356b 1898
5ba3f43e
A
1899 thread_call_t call;
1900 uint64_t now;
316670eb 1901
f427ee49 1902 thread_call_lock_spin(group);
316670eb 1903
0a7de745 1904 if (flavor == TCF_CONTINUOUS) {
5ba3f43e 1905 now = mach_continuous_time();
0a7de745 1906 } else if (flavor == TCF_ABSOLUTE) {
5ba3f43e 1907 now = mach_absolute_time();
0a7de745 1908 } else {
5ba3f43e 1909 panic("invalid timer flavor: %d", flavor);
0a7de745 1910 }
5ba3f43e 1911
f427ee49
A
1912 while ((call = priority_queue_min(&group->delayed_pqueues[flavor],
1913 struct thread_call, tc_pqlink)) != NULL) {
1914 assert(thread_call_get_group(call) == group);
1915 assert(thread_call_get_flavor(call) == flavor);
5ba3f43e 1916
f427ee49
A
1917 /*
1918 * if we hit a call that isn't yet ready to expire,
1919 * then we're done for now
1920 * TODO: The next timer in the list could have a larger leeway
1921 * and therefore be ready to expire.
1922 */
1923 if (call->tc_soft_deadline > now) {
1924 break;
1925 }
39037602 1926
f427ee49
A
1927 /*
1928 * If we hit a rate-limited timer, don't eagerly wake it up.
1929 * Wait until it reaches the end of the leeway window.
1930 *
1931 * TODO: What if the next timer is not rate-limited?
1932 * Have a separate rate-limited queue to avoid this
1933 */
1934 if ((call->tc_flags & THREAD_CALL_RATELIMITED) &&
1935 (call->tc_pqlink.deadline > now) &&
1936 (ml_timer_forced_evaluation() == FALSE)) {
1937 break;
1938 }
316670eb 1939
f427ee49
A
1940 if (THREAD_CALL_SIGNAL & call->tc_flags) {
1941 __assert_only queue_head_t *old_queue;
1942 old_queue = thread_call_dequeue(call);
1943 assert(old_queue == &group->delayed_queues[flavor]);
1944
1945 do {
1946 thread_call_func_t func = call->tc_func;
1947 thread_call_param_t param0 = call->tc_param0;
1948 thread_call_param_t param1 = call->tc_param1;
1949
1950 call->tc_flags |= THREAD_CALL_RUNNING;
1951
1952 thread_call_unlock(group);
1953 thread_call_invoke(func, param0, param1, call);
1954 thread_call_lock_spin(group);
1955
1956 /* finish may detect that the call has been re-pended */
1957 } while (thread_call_finish(call, group, NULL));
1958 /* call may have been freed by the finish */
1959 } else {
1960 _pending_call_enqueue(call, group, now);
5ba3f43e 1961 }
f427ee49 1962 }
5ba3f43e
A
1963
1964 _arm_delayed_call_timer(call, group, flavor);
1c79356b 1965
f427ee49 1966 thread_call_unlock(group);
316670eb
A
1967}
1968
39236c6e 1969static void
5ba3f43e 1970thread_call_delayed_timer_rescan(thread_call_group_t group,
0a7de745 1971 thread_call_flavor_t flavor)
39236c6e 1972{
5ba3f43e
A
1973 thread_call_t call;
1974 uint64_t now;
39236c6e 1975
f427ee49 1976 spl_t s = disable_ints_and_lock(group);
39236c6e
A
1977
1978 assert(ml_timer_forced_evaluation() == TRUE);
39037602 1979
5ba3f43e
A
1980 if (flavor == TCF_CONTINUOUS) {
1981 now = mach_continuous_time();
39037602 1982 } else {
5ba3f43e 1983 now = mach_absolute_time();
39037602 1984 }
39236c6e 1985
f427ee49 1986 qe_foreach_element_safe(call, &group->delayed_queues[flavor], tc_qlink) {
5ba3f43e 1987 if (call->tc_soft_deadline <= now) {
f427ee49 1988 _pending_call_enqueue(call, group, now);
5ba3f43e 1989 } else {
f427ee49
A
1990 uint64_t skew = call->tc_pqlink.deadline - call->tc_soft_deadline;
1991 assert(call->tc_pqlink.deadline >= call->tc_soft_deadline);
5ba3f43e
A
1992 /*
1993 * On a latency quality-of-service level change,
39236c6e
A
1994 * re-sort potentially rate-limited callout. The platform
1995 * layer determines which timers require this.
f427ee49
A
1996 *
1997 * This trick works by updating the deadline value to
1998 * equal soft-deadline, effectively crushing away
1999 * timer coalescing slop values for any armed
2000 * timer in the queue.
2001 *
2002 * TODO: keep a hint on the timer to tell whether its inputs changed, so we
2003 * only have to crush coalescing for timers that need it.
2004 *
2005 * TODO: Keep a separate queue of timers above the re-sort
2006 * threshold, so we only have to look at those.
39236c6e
A
2007 */
2008 if (timer_resort_threshold(skew)) {
2009 _call_dequeue(call, group);
5ba3f43e 2010 _delayed_call_enqueue(call, group, call->tc_soft_deadline, flavor);
39236c6e 2011 }
39236c6e
A
2012 }
2013 }
2014
5ba3f43e
A
2015 _arm_delayed_call_timer(NULL, group, flavor);
2016
f427ee49 2017 enable_ints_and_unlock(group, s);
39236c6e
A
2018}
2019
2020void
0a7de745
A
2021thread_call_delayed_timer_rescan_all(void)
2022{
5ba3f43e 2023 for (int i = 0; i < THREAD_CALL_INDEX_MAX; i++) {
f427ee49
A
2024 for (thread_call_flavor_t flavor = 0; flavor < TCF_COUNT; flavor++) {
2025 thread_call_delayed_timer_rescan(&thread_call_groups[i], flavor);
2026 }
39037602 2027 }
39236c6e
A
2028}
2029
316670eb
A
2030/*
2031 * Timer callback to tell a thread to terminate if
2032 * we have an excess of threads and at least one has been
2033 * idle for a long time.
2034 */
2035static void
2036thread_call_dealloc_timer(
0a7de745
A
2037 timer_call_param_t p0,
2038 __unused timer_call_param_t p1)
316670eb
A
2039{
2040 thread_call_group_t group = (thread_call_group_t)p0;
2041 uint64_t now;
2042 kern_return_t res;
f427ee49 2043 bool terminated = false;
cc8bc92a 2044
f427ee49 2045 thread_call_lock_spin(group);
316670eb 2046
f427ee49 2047 assert(group->tcg_flags & TCG_DEALLOC_ACTIVE);
cc8bc92a 2048
316670eb 2049 now = mach_absolute_time();
cc8bc92a 2050
316670eb
A
2051 if (group->idle_count > 0) {
2052 if (now > group->idle_timestamp + thread_call_dealloc_interval_abs) {
f427ee49 2053 terminated = true;
316670eb 2054 group->idle_count--;
c3c9b80d 2055 res = waitq_wakeup64_one(&group->idle_waitq, CAST_EVENT64_T(group),
0a7de745 2056 THREAD_INTERRUPTED, WAITQ_ALL_PRIORITIES);
316670eb
A
2057 if (res != KERN_SUCCESS) {
2058 panic("Unable to wake up idle thread for termination?");
2059 }
2060 }
316670eb
A
2061 }
2062
f427ee49 2063 group->tcg_flags &= ~TCG_DEALLOC_ACTIVE;
cc8bc92a 2064
316670eb
A
2065 /*
2066 * If we still have an excess of threads, schedule another
2067 * invocation of this function.
2068 */
2069 if (group->idle_count > 0 && (group->idle_count + group->active_count > group->target_thread_count)) {
2070 /*
2071 * If we killed someone just now, push out the
2072 * next deadline.
2073 */
2074 if (terminated) {
2075 group->idle_timestamp = now;
2076 }
1c79356b 2077
316670eb 2078 thread_call_start_deallocate_timer(group);
316670eb
A
2079 }
2080
f427ee49 2081 thread_call_unlock(group);
1c79356b 2082}
316670eb 2083
5ba3f43e
A
2084/*
2085 * Wait for the invocation of the thread call to complete
2086 * We know there's only one in flight because of the 'once' flag.
2087 *
2088 * If a subsequent invocation comes in before we wake up, that's OK
2089 *
2090 * TODO: Here is where we will add priority inheritance to the thread executing
2091 * the thread call in case it's lower priority than the current thread
2092 * <rdar://problem/30321792> Priority inheritance for thread_call_wait_once
2093 *
2094 * Takes the thread call lock locked, returns unlocked
2095 * This lets us avoid a spurious take/drop after waking up from thread_block
c3c9b80d
A
2096 *
2097 * This thread could be a thread call thread itself, blocking and therefore making a
2098 * sched_call upcall into the thread call subsystem, needing the group lock.
2099 * However, we're saved from deadlock because the 'block' upcall is made in
2100 * thread_block, not in assert_wait.
5ba3f43e 2101 */
f427ee49 2102static bool
5ba3f43e
A
2103thread_call_wait_once_locked(thread_call_t call, spl_t s)
2104{
2105 assert(call->tc_flags & THREAD_CALL_ALLOC);
2106 assert(call->tc_flags & THREAD_CALL_ONCE);
2107
f427ee49
A
2108 thread_call_group_t group = thread_call_get_group(call);
2109
5ba3f43e 2110 if ((call->tc_flags & THREAD_CALL_RUNNING) == 0) {
f427ee49
A
2111 enable_ints_and_unlock(group, s);
2112 return false;
5ba3f43e
A
2113 }
2114
2115 /* call is running, so we have to wait for it */
2116 call->tc_flags |= THREAD_CALL_WAIT;
2117
c3c9b80d 2118 wait_result_t res = waitq_assert_wait64(&group->waiters_waitq, CAST_EVENT64_T(call), THREAD_UNINT, 0);
0a7de745 2119 if (res != THREAD_WAITING) {
5ba3f43e 2120 panic("Unable to assert wait: %d", res);
0a7de745 2121 }
5ba3f43e 2122
f427ee49 2123 enable_ints_and_unlock(group, s);
5ba3f43e
A
2124
2125 res = thread_block(THREAD_CONTINUE_NULL);
0a7de745 2126 if (res != THREAD_AWAKENED) {
5ba3f43e 2127 panic("Awoken with %d?", res);
0a7de745 2128 }
5ba3f43e
A
2129
2130 /* returns unlocked */
f427ee49 2131 return true;
5ba3f43e
A
2132}
2133
2134/*
2135 * Wait for an in-flight invocation to complete
2136 * Does NOT try to cancel, so the client doesn't need to hold their
2137 * lock while calling this function.
2138 *
2139 * Returns whether or not it had to wait.
2140 *
2141 * Only works for THREAD_CALL_ONCE calls.
2142 */
2143boolean_t
2144thread_call_wait_once(thread_call_t call)
2145{
0a7de745 2146 if ((call->tc_flags & THREAD_CALL_ALLOC) == 0) {
5ba3f43e 2147 panic("thread_call_wait_once: can't wait on thread call whose storage I don't own");
0a7de745 2148 }
5ba3f43e 2149
0a7de745 2150 if ((call->tc_flags & THREAD_CALL_ONCE) == 0) {
5ba3f43e 2151 panic("thread_call_wait_once: can't wait_once on a non-once call");
0a7de745 2152 }
5ba3f43e 2153
0a7de745 2154 if (!ml_get_interrupts_enabled()) {
5ba3f43e 2155 panic("unsafe thread_call_wait_once");
0a7de745 2156 }
5ba3f43e 2157
f427ee49
A
2158 thread_t self = current_thread();
2159
2160 if ((thread_get_tag_internal(self) & THREAD_TAG_CALLOUT) &&
2161 self->thc_state && self->thc_state->thc_call == call) {
5ba3f43e 2162 panic("thread_call_wait_once: deadlock waiting on self from inside call: %p to function %p",
f427ee49 2163 call, call->tc_func);
0a7de745 2164 }
5ba3f43e 2165
f427ee49
A
2166 thread_call_group_t group = thread_call_get_group(call);
2167
2168 spl_t s = disable_ints_and_lock(group);
5ba3f43e 2169
f427ee49 2170 bool waited = thread_call_wait_once_locked(call, s);
5ba3f43e
A
2171 /* thread call lock unlocked */
2172
2173 return waited;
2174}
2175
2176
316670eb
A
2177/*
2178 * Wait for all requested invocations of a thread call prior to now
5ba3f43e 2179 * to finish. Can only be invoked on thread calls whose storage we manage.
316670eb
A
2180 * Just waits for the finish count to catch up to the submit count we find
2181 * at the beginning of our wait.
5ba3f43e
A
2182 *
2183 * Called with thread_call_lock held. Returns with lock released.
316670eb
A
2184 */
2185static void
5ba3f43e 2186thread_call_wait_locked(thread_call_t call, spl_t s)
316670eb 2187{
f427ee49 2188 thread_call_group_t group = thread_call_get_group(call);
316670eb
A
2189
2190 assert(call->tc_flags & THREAD_CALL_ALLOC);
2191
f427ee49 2192 uint64_t submit_count = call->tc_submit_count;
316670eb
A
2193
2194 while (call->tc_finish_count < submit_count) {
2195 call->tc_flags |= THREAD_CALL_WAIT;
2196
c3c9b80d
A
2197 wait_result_t res = waitq_assert_wait64(&group->waiters_waitq,
2198 CAST_EVENT64_T(call), THREAD_UNINT, 0);
2199
0a7de745 2200 if (res != THREAD_WAITING) {
5ba3f43e 2201 panic("Unable to assert wait: %d", res);
0a7de745 2202 }
316670eb 2203
f427ee49 2204 enable_ints_and_unlock(group, s);
316670eb 2205
5ba3f43e 2206 res = thread_block(THREAD_CONTINUE_NULL);
0a7de745 2207 if (res != THREAD_AWAKENED) {
316670eb 2208 panic("Awoken with %d?", res);
0a7de745 2209 }
5ba3f43e 2210
f427ee49 2211 s = disable_ints_and_lock(group);
316670eb 2212 }
5ba3f43e 2213
f427ee49 2214 enable_ints_and_unlock(group, s);
316670eb
A
2215}
2216
2217/*
2218 * Determine whether a thread call is either on a queue or
2219 * currently being executed.
2220 */
2221boolean_t
0a7de745 2222thread_call_isactive(thread_call_t call)
316670eb 2223{
f427ee49 2224 thread_call_group_t group = thread_call_get_group(call);
316670eb 2225
f427ee49
A
2226 spl_t s = disable_ints_and_lock(group);
2227 boolean_t active = (call->tc_submit_count > call->tc_finish_count);
2228 enable_ints_and_unlock(group, s);
316670eb
A
2229
2230 return active;
2231}
39037602
A
2232
2233/*
2234 * adjust_cont_time_thread_calls
2235 * on wake, reenqueue delayed call timer for continuous time thread call groups
2236 */
2237void
2238adjust_cont_time_thread_calls(void)
2239{
5ba3f43e
A
2240 for (int i = 0; i < THREAD_CALL_INDEX_MAX; i++) {
2241 thread_call_group_t group = &thread_call_groups[i];
f427ee49 2242 spl_t s = disable_ints_and_lock(group);
39037602 2243
5ba3f43e
A
2244 /* only the continuous timers need to be re-armed */
2245
2246 _arm_delayed_call_timer(NULL, group, TCF_CONTINUOUS);
f427ee49 2247 enable_ints_and_unlock(group, s);
5ba3f43e 2248 }
39037602 2249}