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