]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/thread_group.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / thread_group.c
1 /*
2 * Copyright (c) 2016-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <mach/mach_types.h>
30 #include <kern/kern_types.h>
31 #include <kern/processor.h>
32 #include <kern/thread.h>
33 #include <kern/thread_group.h>
34 #include <kern/zalloc.h>
35 #include <kern/task.h>
36 #include <kern/machine.h>
37 #include <kern/coalition.h>
38 #include <sys/errno.h>
39 #include <kern/queue.h>
40 #include <kern/locks.h>
41 #include <kern/thread_group.h>
42 #include <kern/sched_clutch.h>
43
44 #if CONFIG_THREAD_GROUPS
45
46 #define CACHELINE_SIZE (1 << MMU_CLINE)
47
48 struct thread_group {
49 uint64_t tg_id;
50 char tg_name[THREAD_GROUP_MAXNAME];
51 struct os_refcnt tg_refcount;
52 uint32_t tg_flags;
53 cluster_type_t tg_recommendation;
54 queue_chain_t tg_queue_chain;
55 #if CONFIG_SCHED_CLUTCH
56 struct sched_clutch tg_sched_clutch;
57 #endif /* CONFIG_SCHED_CLUTCH */
58 // 16 bytes of padding here
59 uint8_t tg_machine_data[] __attribute__((aligned(CACHELINE_SIZE)));
60 } __attribute__((aligned(8)));
61
62 static SECURITY_READ_ONLY_LATE(zone_t) tg_zone;
63 static uint32_t tg_count;
64 static queue_head_t tg_queue;
65 static LCK_GRP_DECLARE(tg_lck_grp, "thread_group");
66 static LCK_MTX_DECLARE(tg_lock, &tg_lck_grp);
67 static LCK_SPIN_DECLARE(tg_flags_update_lock, &tg_lck_grp);
68
69 static uint64_t tg_next_id = 0;
70 static uint32_t tg_size;
71 static uint32_t tg_machine_data_size;
72 static struct thread_group *tg_system;
73 static struct thread_group *tg_background;
74 static struct thread_group *tg_adaptive;
75 static struct thread_group *tg_vm;
76 static struct thread_group *tg_io_storage;
77 static struct thread_group *tg_perf_controller;
78 int tg_set_by_bankvoucher;
79
80 static bool thread_group_retain_try(struct thread_group *tg);
81
82 /*
83 * Initialize thread groups at boot
84 */
85 void
86 thread_group_init(void)
87 {
88 // Get thread group structure extension from EDT or boot-args (which can override EDT)
89 if (!PE_parse_boot_argn("kern.thread_group_extra_bytes", &tg_machine_data_size, sizeof(tg_machine_data_size))) {
90 if (!PE_get_default("kern.thread_group_extra_bytes", &tg_machine_data_size, sizeof(tg_machine_data_size))) {
91 tg_machine_data_size = 8;
92 }
93 }
94
95 // Check if thread group can be set by voucher adoption from EDT or boot-args (which can override EDT)
96 if (!PE_parse_boot_argn("kern.thread_group_set_by_bankvoucher", &tg_set_by_bankvoucher, sizeof(tg_set_by_bankvoucher))) {
97 if (!PE_get_default("kern.thread_group_set_by_bankvoucher", &tg_set_by_bankvoucher, sizeof(tg_set_by_bankvoucher))) {
98 tg_set_by_bankvoucher = 1;
99 }
100 }
101
102 tg_size = sizeof(struct thread_group) + tg_machine_data_size;
103 if (tg_size % CACHELINE_SIZE) {
104 tg_size += CACHELINE_SIZE - (tg_size % CACHELINE_SIZE);
105 }
106 tg_machine_data_size = tg_size - sizeof(struct thread_group);
107 // printf("tg_size=%d(%lu+%d)\n", tg_size, sizeof(struct thread_group), tg_machine_data_size);
108 assert(offsetof(struct thread_group, tg_machine_data) % CACHELINE_SIZE == 0);
109 tg_zone = zone_create("thread_groups", tg_size, ZC_NOENCRYPT | ZC_ALIGNMENT_REQUIRED);
110
111 queue_head_init(tg_queue);
112 tg_system = thread_group_create_and_retain();
113 thread_group_set_name(tg_system, "system");
114 tg_background = thread_group_create_and_retain();
115 thread_group_set_name(tg_background, "background");
116 tg_adaptive = thread_group_create_and_retain();
117 thread_group_set_name(tg_adaptive, "adaptive");
118 tg_vm = thread_group_create_and_retain();
119 thread_group_set_name(tg_vm, "VM");
120 tg_io_storage = thread_group_create_and_retain();
121 thread_group_set_name(tg_io_storage, "io storage");
122 tg_perf_controller = thread_group_create_and_retain();
123 thread_group_set_name(tg_perf_controller, "perf_controller");
124
125 /*
126 * If CLPC is disabled, it would recommend SMP for all thread groups.
127 * In that mode, the scheduler would like to restrict the kernel thread
128 * groups to the E-cluster while all other thread groups are run on the
129 * P-cluster. To identify the kernel thread groups, mark them with a
130 * special flag THREAD_GROUP_FLAGS_SMP_RESTRICT which is looked at by
131 * recommended_pset_type().
132 */
133 tg_system->tg_flags |= THREAD_GROUP_FLAGS_SMP_RESTRICT;
134 tg_vm->tg_flags |= THREAD_GROUP_FLAGS_SMP_RESTRICT;
135 tg_io_storage->tg_flags |= THREAD_GROUP_FLAGS_SMP_RESTRICT;
136 tg_perf_controller->tg_flags |= THREAD_GROUP_FLAGS_SMP_RESTRICT;
137 }
138
139 #if CONFIG_SCHED_CLUTCH
140 /*
141 * sched_clutch_for_thread
142 *
143 * The routine provides a back linkage from the thread to the
144 * sched_clutch it belongs to. This relationship is based on the
145 * thread group membership of the thread. Since that membership is
146 * changed from the thread context with the thread lock held, this
147 * linkage should be looked at only with the thread lock held or
148 * when the thread cannot be running (for eg. the thread is in the
149 * runq and being removed as part of thread_select().
150 */
151 sched_clutch_t
152 sched_clutch_for_thread(thread_t thread)
153 {
154 assert(thread->thread_group != NULL);
155 return &(thread->thread_group->tg_sched_clutch);
156 }
157
158 sched_clutch_t
159 sched_clutch_for_thread_group(struct thread_group *thread_group)
160 {
161 return &(thread_group->tg_sched_clutch);
162 }
163
164 /*
165 * Translate the TG flags to a priority boost for the sched_clutch.
166 * This priority boost will apply to the entire clutch represented
167 * by the thread group.
168 */
169 static void
170 sched_clutch_update_tg_flags(sched_clutch_t clutch, uint8_t flags)
171 {
172 sched_clutch_tg_priority_t sc_tg_pri = 0;
173 if (flags & THREAD_GROUP_FLAGS_UI_APP) {
174 sc_tg_pri = SCHED_CLUTCH_TG_PRI_HIGH;
175 } else if (flags & THREAD_GROUP_FLAGS_EFFICIENT) {
176 sc_tg_pri = SCHED_CLUTCH_TG_PRI_LOW;
177 } else {
178 sc_tg_pri = SCHED_CLUTCH_TG_PRI_MED;
179 }
180 os_atomic_store(&clutch->sc_tg_priority, sc_tg_pri, relaxed);
181 }
182
183 #endif /* CONFIG_SCHED_CLUTCH */
184
185 /*
186 * Use a spinlock to protect all thread group flag updates.
187 * The lock should not have heavy contention since these flag updates should
188 * be infrequent. If this lock has contention issues, it should be changed to
189 * a per thread-group lock.
190 *
191 * The lock protects the flags field in the thread_group structure. It is also
192 * held while doing callouts to CLPC to reflect these flag changes.
193 */
194
195 void
196 thread_group_flags_update_lock(void)
197 {
198 lck_spin_lock_grp(&tg_flags_update_lock, &tg_lck_grp);
199 }
200
201 void
202 thread_group_flags_update_unlock(void)
203 {
204 lck_spin_unlock(&tg_flags_update_lock);
205 }
206
207 /*
208 * Inform platform code about already existing thread groups
209 * or ask it to free state for all thread groups
210 */
211 void
212 thread_group_resync(boolean_t create)
213 {
214 struct thread_group *tg;
215
216 lck_mtx_lock(&tg_lock);
217 qe_foreach_element(tg, &tg_queue, tg_queue_chain) {
218 if (create) {
219 machine_thread_group_init(tg);
220 } else {
221 machine_thread_group_deinit(tg);
222 }
223 }
224 lck_mtx_unlock(&tg_lock);
225 }
226
227 /*
228 * Create new thread group and add new reference to it.
229 */
230 struct thread_group *
231 thread_group_create_and_retain(void)
232 {
233 struct thread_group *tg;
234
235 tg = (struct thread_group *)zalloc(tg_zone);
236 if (tg == NULL) {
237 panic("thread group zone over commit");
238 }
239 assert((uintptr_t)tg % CACHELINE_SIZE == 0);
240 bzero(tg, sizeof(struct thread_group));
241
242 #if CONFIG_SCHED_CLUTCH
243 /*
244 * The clutch scheduler maintains a bunch of runqs per thread group. For
245 * each thread group it maintains a sched_clutch structure. The lifetime
246 * of that structure is tied directly to the lifetime of the thread group.
247 */
248 sched_clutch_init_with_thread_group(&(tg->tg_sched_clutch), tg);
249
250 /*
251 * Since the thread group flags are used to determine any priority promotions
252 * for the threads in the thread group, initialize them to 0.
253 */
254 sched_clutch_update_tg_flags(&(tg->tg_sched_clutch), 0);
255
256 #endif /* CONFIG_SCHED_CLUTCH */
257
258 lck_mtx_lock(&tg_lock);
259 tg->tg_id = tg_next_id++;
260 tg->tg_recommendation = CLUSTER_TYPE_SMP; // no recommendation yet
261 os_ref_init(&tg->tg_refcount, NULL);
262 tg_count++;
263 enqueue_tail(&tg_queue, &tg->tg_queue_chain);
264 lck_mtx_unlock(&tg_lock);
265
266 // call machine layer init before this thread group becomes visible
267 machine_thread_group_init(tg);
268
269 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_NEW), tg->tg_id);
270
271 return tg;
272 }
273
274 /*
275 * Point newly created thread to its home thread group
276 */
277 void
278 thread_group_init_thread(thread_t t, task_t task)
279 {
280 struct thread_group *tg = task_coalition_get_thread_group(task);
281 t->thread_group = tg;
282 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_SET),
283 THREAD_GROUP_INVALID, tg->tg_id, (uintptr_t)thread_tid(t));
284 }
285
286 /*
287 * Set thread group name
288 */
289 void
290 thread_group_set_name(__unused struct thread_group *tg, __unused const char *name)
291 {
292 if (name == NULL) {
293 return;
294 }
295 if (!thread_group_retain_try(tg)) {
296 return;
297 }
298 if (tg->tg_name[0] == '\0') {
299 strncpy(&tg->tg_name[0], name, THREAD_GROUP_MAXNAME);
300 #if defined(__LP64__)
301 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_NAME),
302 tg->tg_id,
303 *(uint64_t*)(void*)&tg->tg_name[0],
304 *(uint64_t*)(void*)&tg->tg_name[sizeof(uint64_t)]
305 );
306 #else /* defined(__LP64__) */
307 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_NAME),
308 tg->tg_id,
309 *(uint32_t*)(void*)&tg->tg_name[0],
310 *(uint32_t*)(void*)&tg->tg_name[sizeof(uint32_t)]
311 );
312 #endif /* defined(__LP64__) */
313 }
314 thread_group_release(tg);
315 }
316
317 void
318 thread_group_set_flags(struct thread_group *tg, uint64_t flags)
319 {
320 thread_group_flags_update_lock();
321 thread_group_set_flags_locked(tg, flags);
322 thread_group_flags_update_unlock();
323 }
324
325 void
326 thread_group_clear_flags(struct thread_group *tg, uint64_t flags)
327 {
328 thread_group_flags_update_lock();
329 thread_group_clear_flags_locked(tg, flags);
330 thread_group_flags_update_unlock();
331 }
332
333 /*
334 * Set thread group flags and perform related actions.
335 * The tg_flags_update_lock should be held.
336 * Currently supported flags are:
337 * - THREAD_GROUP_FLAGS_EFFICIENT
338 * - THREAD_GROUP_FLAGS_UI_APP
339 */
340
341 void
342 thread_group_set_flags_locked(struct thread_group *tg, uint64_t flags)
343 {
344 if ((flags & THREAD_GROUP_FLAGS_VALID) != flags) {
345 panic("thread_group_set_flags: Invalid flags %llu", flags);
346 }
347
348 if ((tg->tg_flags & flags) == flags) {
349 return;
350 }
351
352 __kdebug_only uint64_t old_flags = tg->tg_flags;
353 tg->tg_flags |= flags;
354 machine_thread_group_flags_update(tg, tg->tg_flags);
355 #if CONFIG_SCHED_CLUTCH
356 sched_clutch_update_tg_flags(&(tg->tg_sched_clutch), tg->tg_flags);
357 #endif /* CONFIG_SCHED_CLUTCH */
358 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_FLAGS),
359 tg->tg_id, tg->tg_flags, old_flags);
360 }
361
362 /*
363 * Clear thread group flags and perform related actions
364 * The tg_flags_update_lock should be held.
365 * Currently supported flags are:
366 * - THREAD_GROUP_FLAGS_EFFICIENT
367 * - THREAD_GROUP_FLAGS_UI_APP
368 */
369
370 void
371 thread_group_clear_flags_locked(struct thread_group *tg, uint64_t flags)
372 {
373 if ((flags & THREAD_GROUP_FLAGS_VALID) != flags) {
374 panic("thread_group_clear_flags: Invalid flags %llu", flags);
375 }
376
377 if ((tg->tg_flags & flags) == 0) {
378 return;
379 }
380
381 __kdebug_only uint64_t old_flags = tg->tg_flags;
382 tg->tg_flags &= ~flags;
383 #if CONFIG_SCHED_CLUTCH
384 sched_clutch_update_tg_flags(&(tg->tg_sched_clutch), tg->tg_flags);
385 #endif /* CONFIG_SCHED_CLUTCH */
386 machine_thread_group_flags_update(tg, tg->tg_flags);
387 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_FLAGS),
388 tg->tg_id, tg->tg_flags, old_flags);
389 }
390
391
392
393 /*
394 * Find thread group with specified name and put new reference to it.
395 */
396 struct thread_group *
397 thread_group_find_by_name_and_retain(char *name)
398 {
399 struct thread_group *result = NULL;
400
401 if (name == NULL) {
402 return NULL;
403 }
404
405 if (strncmp("system", name, THREAD_GROUP_MAXNAME) == 0) {
406 return thread_group_retain(tg_system);
407 } else if (strncmp("background", name, THREAD_GROUP_MAXNAME) == 0) {
408 return thread_group_retain(tg_background);
409 } else if (strncmp("adaptive", name, THREAD_GROUP_MAXNAME) == 0) {
410 return thread_group_retain(tg_adaptive);
411 } else if (strncmp("perf_controller", name, THREAD_GROUP_MAXNAME) == 0) {
412 return thread_group_retain(tg_perf_controller);
413 }
414
415 struct thread_group *tg;
416 lck_mtx_lock(&tg_lock);
417 qe_foreach_element(tg, &tg_queue, tg_queue_chain) {
418 if (strncmp(tg->tg_name, name, THREAD_GROUP_MAXNAME) == 0 &&
419 thread_group_retain_try(tg)) {
420 result = tg;
421 break;
422 }
423 }
424 lck_mtx_unlock(&tg_lock);
425 return result;
426 }
427
428 /*
429 * Find thread group with specified ID and add new reference to it.
430 */
431 struct thread_group *
432 thread_group_find_by_id_and_retain(uint64_t id)
433 {
434 struct thread_group *tg = NULL;
435 struct thread_group *result = NULL;
436
437 switch (id) {
438 case THREAD_GROUP_SYSTEM:
439 result = tg_system;
440 thread_group_retain(tg_system);
441 break;
442 case THREAD_GROUP_BACKGROUND:
443 result = tg_background;
444 thread_group_retain(tg_background);
445 break;
446 case THREAD_GROUP_ADAPTIVE:
447 result = tg_adaptive;
448 thread_group_retain(tg_adaptive);
449 break;
450 case THREAD_GROUP_VM:
451 result = tg_vm;
452 thread_group_retain(tg_vm);
453 break;
454 case THREAD_GROUP_IO_STORAGE:
455 result = tg_io_storage;
456 thread_group_retain(tg_io_storage);
457 break;
458 case THREAD_GROUP_PERF_CONTROLLER:
459 result = tg_perf_controller;
460 thread_group_retain(tg_perf_controller);
461 break;
462 default:
463 lck_mtx_lock(&tg_lock);
464 qe_foreach_element(tg, &tg_queue, tg_queue_chain) {
465 if (tg->tg_id == id && thread_group_retain_try(tg)) {
466 result = tg;
467 break;
468 }
469 }
470 lck_mtx_unlock(&tg_lock);
471 }
472 return result;
473 }
474
475 /*
476 * Add new reference to specified thread group
477 */
478 struct thread_group *
479 thread_group_retain(struct thread_group *tg)
480 {
481 os_ref_retain(&tg->tg_refcount);
482 return tg;
483 }
484
485 /*
486 * Similar to thread_group_retain, but fails for thread groups with a
487 * zero reference count. Returns true if retained successfully.
488 */
489 static bool
490 thread_group_retain_try(struct thread_group *tg)
491 {
492 return os_ref_retain_try(&tg->tg_refcount);
493 }
494
495 /*
496 * Drop a reference to specified thread group
497 */
498 void
499 thread_group_release(struct thread_group *tg)
500 {
501 if (os_ref_release(&tg->tg_refcount) == 0) {
502 lck_mtx_lock(&tg_lock);
503 tg_count--;
504 remqueue(&tg->tg_queue_chain);
505 lck_mtx_unlock(&tg_lock);
506 static_assert(THREAD_GROUP_MAXNAME >= (sizeof(uint64_t) * 2), "thread group name is too short");
507 static_assert(__alignof(struct thread_group) >= __alignof(uint64_t), "thread group name is not 8 bytes aligned");
508 #if defined(__LP64__)
509 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_NAME_FREE),
510 tg->tg_id,
511 *(uint64_t*)(void*)&tg->tg_name[0],
512 *(uint64_t*)(void*)&tg->tg_name[sizeof(uint64_t)]
513 );
514 #else /* defined(__LP64__) */
515 KDBG(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_NAME_FREE),
516 tg->tg_id,
517 *(uint32_t*)(void*)&tg->tg_name[0],
518 *(uint32_t*)(void*)&tg->tg_name[sizeof(uint32_t)]
519 );
520 #endif /* defined(__LP64__) */
521 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_FREE), tg->tg_id);
522 #if CONFIG_SCHED_CLUTCH
523 sched_clutch_destroy(&(tg->tg_sched_clutch));
524 #endif /* CONFIG_SCHED_CLUTCH */
525 machine_thread_group_deinit(tg);
526 zfree(tg_zone, tg);
527 }
528 }
529
530 /*
531 * Get thread's current thread group
532 */
533 inline struct thread_group *
534 thread_group_get(thread_t t)
535 {
536 return t->thread_group;
537 }
538
539 struct thread_group *
540 thread_group_get_home_group(thread_t t)
541 {
542 return task_coalition_get_thread_group(t->task);
543 }
544
545 #if CONFIG_SCHED_AUTO_JOIN
546
547 /*
548 * thread_set_thread_group_auto_join()
549 *
550 * Sets the thread group of a thread based on auto-join rules.
551 *
552 * Preconditions:
553 * - Thread must not be part of a runq (freshly made runnable threads or terminating only)
554 * - Thread must be locked by the caller already
555 */
556 static void
557 thread_set_thread_group_auto_join(thread_t t, struct thread_group *tg, __unused struct thread_group *old_tg)
558 {
559 assert(t->runq == PROCESSOR_NULL);
560 t->thread_group = tg;
561
562 /*
563 * If the thread group is being changed for the current thread, callout to
564 * CLPC to update the thread's information at that layer. This makes sure CLPC
565 * has consistent state when the current thread is going off-core.
566 */
567 if (t == current_thread()) {
568 uint64_t ctime = mach_approximate_time();
569 uint64_t arg1, arg2;
570 machine_thread_going_on_core(t, thread_get_urgency(t, &arg1, &arg2), 0, 0, ctime);
571 machine_switch_perfcontrol_state_update(THREAD_GROUP_UPDATE, ctime, PERFCONTROL_CALLOUT_WAKE_UNSAFE, t);
572 }
573 }
574
575 #endif /* CONFIG_SCHED_AUTO_JOIN */
576
577 /*
578 * thread_set_thread_group_explicit()
579 *
580 * Sets the thread group of a thread based on default non auto-join rules.
581 *
582 * Preconditions:
583 * - Thread must be the current thread
584 * - Caller must not have the thread locked
585 * - Interrupts must be disabled
586 */
587 static void
588 thread_set_thread_group_explicit(thread_t t, struct thread_group *tg, __unused struct thread_group *old_tg)
589 {
590 assert(t == current_thread());
591 /*
592 * In the clutch scheduler world, the runq membership of the thread
593 * is based on its thread group membership and its scheduling bucket.
594 * In order to synchronize with the priority (and therefore bucket)
595 * getting updated concurrently, it is important to perform the
596 * thread group change also under the thread lock.
597 */
598 thread_lock(t);
599 t->thread_group = tg;
600
601 #if CONFIG_SCHED_CLUTCH
602 sched_clutch_t old_clutch = (old_tg) ? &(old_tg->tg_sched_clutch) : NULL;
603 sched_clutch_t new_clutch = (tg) ? &(tg->tg_sched_clutch) : NULL;
604 if (SCHED_CLUTCH_THREAD_ELIGIBLE(t)) {
605 sched_clutch_thread_clutch_update(t, old_clutch, new_clutch);
606 }
607 #endif /* CONFIG_SCHED_CLUTCH */
608
609 thread_unlock(t);
610
611 uint64_t ctime = mach_approximate_time();
612 uint64_t arg1, arg2;
613 machine_thread_going_on_core(t, thread_get_urgency(t, &arg1, &arg2), 0, 0, ctime);
614 machine_switch_perfcontrol_state_update(THREAD_GROUP_UPDATE, ctime, 0, t);
615 }
616
617 /*
618 * thread_set_thread_group()
619 *
620 * Overrides the current home thread group with an override group. However,
621 * an adopted work interval overrides the override. Does not take a reference
622 * on the group, so caller must guarantee group lifetime lasts as long as the
623 * group is set.
624 *
625 * The thread group is set according to a hierarchy:
626 *
627 * 1) work interval specified group (explicit API)
628 * 2) Auto-join thread group (wakeup tracking for special work intervals)
629 * 3) bank voucher carried group (implicitly set)
630 * 4) coalition default thread group (ambient)
631 */
632 static void
633 thread_set_thread_group(thread_t t, struct thread_group *tg, bool auto_join)
634 {
635 struct thread_group *home_tg = thread_group_get_home_group(t);
636 struct thread_group *old_tg = NULL;
637
638 if (tg == NULL) {
639 /* when removing an override, revert to home group */
640 tg = home_tg;
641 }
642
643 spl_t s = splsched();
644
645 old_tg = t->thread_group;
646
647 if (old_tg != tg) {
648 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_THREAD_GROUP, MACH_THREAD_GROUP_SET),
649 t->thread_group ? t->thread_group->tg_id : 0,
650 tg->tg_id, (uintptr_t)thread_tid(t), home_tg->tg_id);
651
652 /*
653 * Based on whether this is a change due to auto-join, the join does
654 * different things and has different expectations.
655 */
656 if (auto_join) {
657 #if CONFIG_SCHED_AUTO_JOIN
658 /*
659 * set thread group with auto-join rules. This has the
660 * implicit assumption that the thread lock is already held.
661 * Also this could happen to any thread (current or thread
662 * being context switched).
663 */
664 thread_set_thread_group_auto_join(t, tg, old_tg);
665 #else /* CONFIG_SCHED_AUTO_JOIN */
666 panic("Auto-Join unsupported on this platform");
667 #endif /* CONFIG_SCHED_AUTO_JOIN */
668 } else {
669 /*
670 * set thread group with the explicit join rules. This has
671 * the implicit assumption that the thread is not locked. Also
672 * this would be done only to the current thread.
673 */
674 thread_set_thread_group_explicit(t, tg, old_tg);
675 }
676 }
677
678 splx(s);
679 }
680
681 void
682 thread_group_set_bank(thread_t t, struct thread_group *tg)
683 {
684 /* work interval group overrides any bank override group */
685 if (t->th_work_interval) {
686 return;
687 }
688
689 /* boot arg disables groups in bank */
690 if (tg_set_by_bankvoucher == FALSE) {
691 return;
692 }
693
694 thread_set_thread_group(t, tg, false);
695 }
696
697 /*
698 * thread_set_work_interval_thread_group()
699 *
700 * Sets the thread's group to the work interval thread group.
701 * If auto_join == true, thread group is being overriden through scheduler
702 * auto-join policies.
703 *
704 * Preconditions for auto-join case:
705 * - t is not current_thread and t should be locked.
706 * - t should not be running on a remote core; thread context switching is a valid state for this.
707 */
708 void
709 thread_set_work_interval_thread_group(thread_t t, struct thread_group *tg, bool auto_join)
710 {
711 if (tg == NULL) {
712 /*
713 * when removing a work interval override, fall back
714 * to the current voucher override.
715 *
716 * In the auto_join case, the thread is already locked by the caller so
717 * its unsafe to get the thread group from the current voucher (since
718 * that might require taking task lock and ivac lock). However, the
719 * auto-join policy does not allow threads to switch thread groups based
720 * on voucher overrides.
721 *
722 * For the normal case, lookup the thread group from the currently adopted
723 * voucher and use that as the fallback tg.
724 */
725
726 if (auto_join == false) {
727 tg = thread_get_current_voucher_thread_group(t);
728 }
729 }
730
731 thread_set_thread_group(t, tg, auto_join);
732 }
733
734 inline cluster_type_t
735 thread_group_recommendation(struct thread_group *tg)
736 {
737 if (tg == NULL) {
738 return CLUSTER_TYPE_SMP;
739 } else {
740 return tg->tg_recommendation;
741 }
742 }
743
744 inline uint64_t
745 thread_group_get_id(struct thread_group *tg)
746 {
747 return tg->tg_id;
748 }
749
750 uint32_t
751 thread_group_count(void)
752 {
753 return tg_count;
754 }
755
756 /*
757 * Can only be called while tg cannot be destroyed
758 */
759 inline const char*
760 thread_group_get_name(struct thread_group *tg)
761 {
762 return tg->tg_name;
763 }
764
765 inline void *
766 thread_group_get_machine_data(struct thread_group *tg)
767 {
768 return &tg->tg_machine_data;
769 }
770
771 inline uint32_t
772 thread_group_machine_data_size(void)
773 {
774 return tg_machine_data_size;
775 }
776
777 kern_return_t
778 thread_group_iterate_stackshot(thread_group_iterate_fn_t callout, void *arg)
779 {
780 struct thread_group *tg;
781 int i = 0;
782 qe_foreach_element(tg, &tg_queue, tg_queue_chain) {
783 if (tg == NULL || !ml_validate_nofault((vm_offset_t)tg, sizeof(struct thread_group))) {
784 return KERN_FAILURE;
785 }
786 callout(arg, i, tg);
787 i++;
788 }
789 return KERN_SUCCESS;
790 }
791
792 void
793 thread_group_join_io_storage(void)
794 {
795 struct thread_group *tg = thread_group_find_by_id_and_retain(THREAD_GROUP_IO_STORAGE);
796 assert(tg != NULL);
797 thread_set_thread_group(current_thread(), tg, false);
798 }
799
800 void
801 thread_group_join_perf_controller(void)
802 {
803 struct thread_group *tg = thread_group_find_by_id_and_retain(THREAD_GROUP_PERF_CONTROLLER);
804 assert(tg != NULL);
805 thread_set_thread_group(current_thread(), tg, false);
806 }
807
808 void
809 thread_group_vm_add(void)
810 {
811 assert(tg_vm != NULL);
812 thread_set_thread_group(current_thread(), thread_group_find_by_id_and_retain(THREAD_GROUP_VM), false);
813 }
814
815 uint32_t
816 thread_group_get_flags(struct thread_group *tg)
817 {
818 return tg->tg_flags;
819 }
820
821 /*
822 * Returns whether the thread group is restricted to the E-cluster when CLPC is
823 * turned off.
824 */
825 boolean_t
826 thread_group_smp_restricted(struct thread_group *tg)
827 {
828 if (tg->tg_flags & THREAD_GROUP_FLAGS_SMP_RESTRICT) {
829 return true;
830 } else {
831 return false;
832 }
833 }
834
835 void
836 thread_group_update_recommendation(struct thread_group *tg, cluster_type_t new_recommendation)
837 {
838 /*
839 * Since the tg->tg_recommendation field is read by CPUs trying to determine
840 * where a thread/thread group needs to be placed, it is important to use
841 * atomic operations to update the recommendation.
842 */
843 os_atomic_store(&tg->tg_recommendation, new_recommendation, relaxed);
844 }
845
846 #if CONFIG_SCHED_EDGE
847
848 int sched_edge_restrict_ut = 1;
849 int sched_edge_restrict_bg = 1;
850
851 void
852 sched_perfcontrol_thread_group_recommend(__unused void *machine_data, __unused cluster_type_t new_recommendation)
853 {
854 struct thread_group *tg = (struct thread_group *)((uintptr_t)machine_data - offsetof(struct thread_group, tg_machine_data));
855 /*
856 * CLUSTER_TYPE_SMP was used for some debugging support when CLPC dynamic control was turned off.
857 * In more recent implementations, CLPC simply recommends "P-spill" when dynamic control is turned off. So it should
858 * never be recommending CLUSTER_TYPE_SMP for thread groups.
859 */
860 assert(new_recommendation != CLUSTER_TYPE_SMP);
861 /*
862 * The Edge scheduler expects preferred cluster recommendations for each QoS level within a TG. Until the new CLPC
863 * routine is being called, fake out the call from the old CLPC interface.
864 */
865 uint32_t tg_bucket_preferred_cluster[TH_BUCKET_SCHED_MAX] = {0};
866 /*
867 * For all buckets higher than UT, apply the recommendation to the thread group bucket
868 */
869 for (sched_bucket_t bucket = TH_BUCKET_FIXPRI; bucket < TH_BUCKET_SHARE_UT; bucket++) {
870 tg_bucket_preferred_cluster[bucket] = (new_recommendation == pset_type_for_id(0)) ? 0 : 1;
871 }
872 /* For UT & BG QoS, set the recommendation only if they havent been restricted via sysctls */
873 if (!sched_edge_restrict_ut) {
874 tg_bucket_preferred_cluster[TH_BUCKET_SHARE_UT] = (new_recommendation == pset_type_for_id(0)) ? 0 : 1;
875 }
876 if (!sched_edge_restrict_bg) {
877 tg_bucket_preferred_cluster[TH_BUCKET_SHARE_BG] = (new_recommendation == pset_type_for_id(0)) ? 0 : 1;
878 }
879 sched_perfcontrol_preferred_cluster_options_t options = 0;
880 if (new_recommendation == CLUSTER_TYPE_P) {
881 options |= SCHED_PERFCONTROL_PREFERRED_CLUSTER_MIGRATE_RUNNING;
882 }
883 sched_edge_tg_preferred_cluster_change(tg, tg_bucket_preferred_cluster, options);
884 }
885
886 void
887 sched_perfcontrol_edge_matrix_get(sched_clutch_edge *edge_matrix, bool *edge_request_bitmap, uint64_t flags, uint64_t matrix_order)
888 {
889 sched_edge_matrix_get(edge_matrix, edge_request_bitmap, flags, matrix_order);
890 }
891
892 void
893 sched_perfcontrol_edge_matrix_set(sched_clutch_edge *edge_matrix, bool *edge_changes_bitmap, uint64_t flags, uint64_t matrix_order)
894 {
895 sched_edge_matrix_set(edge_matrix, edge_changes_bitmap, flags, matrix_order);
896 }
897
898 void
899 sched_perfcontrol_thread_group_preferred_clusters_set(void *machine_data, uint32_t tg_preferred_cluster,
900 uint32_t overrides[PERFCONTROL_CLASS_MAX], sched_perfcontrol_preferred_cluster_options_t options)
901 {
902 struct thread_group *tg = (struct thread_group *)((uintptr_t)machine_data - offsetof(struct thread_group, tg_machine_data));
903 uint32_t tg_bucket_preferred_cluster[TH_BUCKET_SCHED_MAX] = {
904 [TH_BUCKET_FIXPRI] = (overrides[PERFCONTROL_CLASS_ABOVEUI] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_ABOVEUI] : tg_preferred_cluster,
905 [TH_BUCKET_SHARE_FG] = (overrides[PERFCONTROL_CLASS_UI] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_UI] : tg_preferred_cluster,
906 [TH_BUCKET_SHARE_IN] = (overrides[PERFCONTROL_CLASS_UI] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_UI] : tg_preferred_cluster,
907 [TH_BUCKET_SHARE_DF] = (overrides[PERFCONTROL_CLASS_NONUI] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_NONUI] : tg_preferred_cluster,
908 [TH_BUCKET_SHARE_UT] = (overrides[PERFCONTROL_CLASS_UTILITY] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_UTILITY] : tg_preferred_cluster,
909 [TH_BUCKET_SHARE_BG] = (overrides[PERFCONTROL_CLASS_BACKGROUND] != SCHED_PERFCONTROL_PREFERRED_CLUSTER_OVERRIDE_NONE) ? overrides[PERFCONTROL_CLASS_BACKGROUND] : tg_preferred_cluster,
910 };
911 sched_edge_tg_preferred_cluster_change(tg, tg_bucket_preferred_cluster, options);
912 }
913
914 #else /* CONFIG_SCHED_EDGE */
915
916 void
917 sched_perfcontrol_thread_group_recommend(__unused void *machine_data, __unused cluster_type_t new_recommendation)
918 {
919 struct thread_group *tg = (struct thread_group *)((uintptr_t)machine_data - offsetof(struct thread_group, tg_machine_data));
920 SCHED(thread_group_recommendation_change)(tg, new_recommendation);
921 }
922
923 void
924 sched_perfcontrol_edge_matrix_get(__unused sched_clutch_edge *edge_matrix, __unused bool *edge_request_bitmap, __unused uint64_t flags, __unused uint64_t matrix_order)
925 {
926 }
927
928 void
929 sched_perfcontrol_edge_matrix_set(__unused sched_clutch_edge *edge_matrix, __unused bool *edge_changes_bitmap, __unused uint64_t flags, __unused uint64_t matrix_order)
930 {
931 }
932
933 void
934 sched_perfcontrol_thread_group_preferred_clusters_set(__unused void *machine_data, __unused uint32_t tg_preferred_cluster,
935 __unused uint32_t overrides[PERFCONTROL_CLASS_MAX], __unused sched_perfcontrol_preferred_cluster_options_t options)
936 {
937 }
938
939 #endif /* CONFIG_SCHED_EDGE */
940
941 #endif /* CONFIG_THREAD_GROUPS */