]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/sched_amp.c
xnu-6153.121.1.tar.gz
[apple/xnu.git] / osfmk / kern / sched_amp.c
CommitLineData
c6bf4f31
A
1/*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <mach/mach_types.h>
30#include <mach/machine.h>
31
32#include <machine/machine_routines.h>
33#include <machine/sched_param.h>
34#include <machine/machine_cpu.h>
35
36#include <kern/kern_types.h>
37#include <kern/debug.h>
38#include <kern/machine.h>
39#include <kern/misc_protos.h>
40#include <kern/processor.h>
41#include <kern/queue.h>
42#include <kern/sched.h>
43#include <kern/sched_prim.h>
44#include <kern/task.h>
45#include <kern/thread.h>
46#include <kern/thread_group.h>
47#include <kern/sched_amp_common.h>
48
49#include <sys/kdebug.h>
50
51#if __AMP__
52
53static thread_t
54sched_amp_steal_thread(processor_set_t pset);
55
56static void
57sched_amp_thread_update_scan(sched_update_scan_context_t scan_context);
58
59static boolean_t
60sched_amp_processor_enqueue(processor_t processor, thread_t thread,
61 sched_options_t options);
62
63static boolean_t
64sched_amp_processor_queue_remove(processor_t processor, thread_t thread);
65
66static ast_t
67sched_amp_processor_csw_check(processor_t processor);
68
69static boolean_t
70sched_amp_processor_queue_has_priority(processor_t processor, int priority, boolean_t gte);
71
72static int
73sched_amp_runq_count(processor_t processor);
74
75static boolean_t
76sched_amp_processor_queue_empty(processor_t processor);
77
78static uint64_t
79sched_amp_runq_stats_count_sum(processor_t processor);
80
81static int
82sched_amp_processor_bound_count(processor_t processor);
83
84static void
85sched_amp_pset_init(processor_set_t pset);
86
87static void
88sched_amp_processor_init(processor_t processor);
89
90static thread_t
91sched_amp_choose_thread(processor_t processor, int priority, ast_t reason);
92
93static void
94sched_amp_processor_queue_shutdown(processor_t processor);
95
96static sched_mode_t
97sched_amp_initial_thread_sched_mode(task_t parent_task);
98
99static processor_t
100sched_amp_choose_processor(processor_set_t pset, processor_t processor, thread_t thread);
101
102static bool
103sched_amp_thread_avoid_processor(processor_t processor, thread_t thread);
104
105static bool
106sched_amp_thread_should_yield(processor_t processor, thread_t thread);
107
108static void
109sched_amp_thread_group_recommendation_change(struct thread_group *tg, cluster_type_t new_recommendation);
110
111const struct sched_dispatch_table sched_amp_dispatch = {
112 .sched_name = "amp",
113 .init = sched_amp_init,
114 .timebase_init = sched_timeshare_timebase_init,
115 .processor_init = sched_amp_processor_init,
116 .pset_init = sched_amp_pset_init,
117 .maintenance_continuation = sched_timeshare_maintenance_continue,
118 .choose_thread = sched_amp_choose_thread,
119 .steal_thread_enabled = sched_amp_steal_thread_enabled,
120 .steal_thread = sched_amp_steal_thread,
121 .compute_timeshare_priority = sched_compute_timeshare_priority,
122 .choose_processor = sched_amp_choose_processor,
123 .processor_enqueue = sched_amp_processor_enqueue,
124 .processor_queue_shutdown = sched_amp_processor_queue_shutdown,
125 .processor_queue_remove = sched_amp_processor_queue_remove,
126 .processor_queue_empty = sched_amp_processor_queue_empty,
127 .priority_is_urgent = priority_is_urgent,
128 .processor_csw_check = sched_amp_processor_csw_check,
129 .processor_queue_has_priority = sched_amp_processor_queue_has_priority,
130 .initial_quantum_size = sched_timeshare_initial_quantum_size,
131 .initial_thread_sched_mode = sched_amp_initial_thread_sched_mode,
132 .can_update_priority = can_update_priority,
133 .update_priority = update_priority,
134 .lightweight_update_priority = lightweight_update_priority,
135 .quantum_expire = sched_default_quantum_expire,
136 .processor_runq_count = sched_amp_runq_count,
137 .processor_runq_stats_count_sum = sched_amp_runq_stats_count_sum,
138 .processor_bound_count = sched_amp_processor_bound_count,
139 .thread_update_scan = sched_amp_thread_update_scan,
140 .multiple_psets_enabled = TRUE,
141 .sched_groups_enabled = FALSE,
142 .avoid_processor_enabled = TRUE,
143 .thread_avoid_processor = sched_amp_thread_avoid_processor,
144 .processor_balance = sched_amp_balance,
145
146 .rt_runq = sched_amp_rt_runq,
147 .rt_init = sched_amp_rt_init,
148 .rt_queue_shutdown = sched_amp_rt_queue_shutdown,
149 .rt_runq_scan = sched_amp_rt_runq_scan,
150 .rt_runq_count_sum = sched_amp_rt_runq_count_sum,
151
152 .qos_max_parallelism = sched_amp_qos_max_parallelism,
153 .check_spill = sched_amp_check_spill,
154 .ipi_policy = sched_amp_ipi_policy,
155 .thread_should_yield = sched_amp_thread_should_yield,
156 .run_count_incr = sched_run_incr,
157 .run_count_decr = sched_run_decr,
158 .update_thread_bucket = sched_update_thread_bucket,
159 .pset_made_schedulable = sched_pset_made_schedulable,
160 .thread_group_recommendation_change = sched_amp_thread_group_recommendation_change,
161};
162
163extern processor_set_t ecore_set;
164extern processor_set_t pcore_set;
165
166__attribute__((always_inline))
167static inline run_queue_t
168amp_main_runq(processor_t processor)
169{
170 return &processor->processor_set->pset_runq;
171}
172
173__attribute__((always_inline))
174static inline run_queue_t
175amp_bound_runq(processor_t processor)
176{
177 return &processor->runq;
178}
179
180__attribute__((always_inline))
181static inline run_queue_t
182amp_runq_for_thread(processor_t processor, thread_t thread)
183{
184 if (thread->bound_processor == PROCESSOR_NULL) {
185 return amp_main_runq(processor);
186 } else {
187 assert(thread->bound_processor == processor);
188 return amp_bound_runq(processor);
189 }
190}
191
192static sched_mode_t
193sched_amp_initial_thread_sched_mode(task_t parent_task)
194{
195 if (parent_task == kernel_task) {
196 return TH_MODE_FIXED;
197 } else {
198 return TH_MODE_TIMESHARE;
199 }
200}
201
202static void
203sched_amp_processor_init(processor_t processor)
204{
205 run_queue_init(&processor->runq);
206}
207
208static void
209sched_amp_pset_init(processor_set_t pset)
210{
211 run_queue_init(&pset->pset_runq);
212}
213
214static thread_t
215sched_amp_choose_thread(
216 processor_t processor,
217 int priority,
218 __unused ast_t reason)
219{
220 processor_set_t pset = processor->processor_set;
221 bool spill_pending = false;
222 int spill_pri = -1;
223
224 if (pset == ecore_set && bit_test(pset->pending_spill_cpu_mask, processor->cpu_id)) {
225 spill_pending = true;
226 spill_pri = pcore_set->pset_runq.highq;
227 }
228
229 run_queue_t main_runq = amp_main_runq(processor);
230 run_queue_t bound_runq = amp_bound_runq(processor);
231 run_queue_t chosen_runq;
232
233 if ((bound_runq->highq < priority) &&
234 (main_runq->highq < priority) &&
235 (spill_pri < priority)) {
236 return THREAD_NULL;
237 }
238
239 if ((spill_pri > bound_runq->highq) &&
240 (spill_pri > main_runq->highq)) {
241 /*
242 * There is a higher priority thread on the P-core runq,
243 * so returning THREAD_NULL here will cause thread_select()
244 * to call sched_amp_steal_thread() to try to get it.
245 */
246 return THREAD_NULL;
247 }
248
249 if (bound_runq->highq >= main_runq->highq) {
250 chosen_runq = bound_runq;
251 } else {
252 chosen_runq = main_runq;
253 }
254
255 return run_queue_dequeue(chosen_runq, SCHED_HEADQ);
256}
257
258static boolean_t
259sched_amp_processor_enqueue(
260 processor_t processor,
261 thread_t thread,
262 sched_options_t options)
263{
264 run_queue_t rq = amp_runq_for_thread(processor, thread);
265 boolean_t result;
266
267 result = run_queue_enqueue(rq, thread, options);
268 thread->runq = processor;
269
270 return result;
271}
272
273static boolean_t
274sched_amp_processor_queue_empty(processor_t processor)
275{
276 processor_set_t pset = processor->processor_set;
277 bool spill_pending = bit_test(pset->pending_spill_cpu_mask, processor->cpu_id);
278
279 return (amp_main_runq(processor)->count == 0) &&
280 (amp_bound_runq(processor)->count == 0) &&
281 !spill_pending;
282}
283
284static bool
285sched_amp_thread_should_yield(processor_t processor, thread_t thread)
286{
287 if (!sched_amp_processor_queue_empty(processor) || (rt_runq_count(processor->processor_set) > 0)) {
288 return true;
289 }
290
291 if ((processor->processor_set->pset_cluster_type == PSET_AMP_E) && (recommended_pset_type(thread) == PSET_AMP_P)) {
292 return pcore_set->pset_runq.count > 0;
293 }
294
295 return false;
296}
297
298static ast_t
299sched_amp_processor_csw_check(processor_t processor)
300{
301 boolean_t has_higher;
302 int pri;
303
304 run_queue_t main_runq = amp_main_runq(processor);
305 run_queue_t bound_runq = amp_bound_runq(processor);
306
307 assert(processor->active_thread != NULL);
308
309 processor_set_t pset = processor->processor_set;
310 bool spill_pending = false;
311 int spill_pri = -1;
312 int spill_urgency = 0;
313
314 if (pset == ecore_set && bit_test(pset->pending_spill_cpu_mask, processor->cpu_id)) {
315 spill_pending = true;
316 spill_pri = pcore_set->pset_runq.highq;
317 spill_urgency = pcore_set->pset_runq.urgency;
318 }
319
320 pri = MAX(main_runq->highq, bound_runq->highq);
321 if (spill_pending) {
322 pri = MAX(pri, spill_pri);
323 }
324
325 if (processor->first_timeslice) {
326 has_higher = (pri > processor->current_pri);
327 } else {
328 has_higher = (pri >= processor->current_pri);
329 }
330
331 if (has_higher) {
332 if (main_runq->urgency > 0) {
333 return AST_PREEMPT | AST_URGENT;
334 }
335
336 if (bound_runq->urgency > 0) {
337 return AST_PREEMPT | AST_URGENT;
338 }
339
340 if (spill_urgency > 0) {
341 return AST_PREEMPT | AST_URGENT;
342 }
343
344 return AST_PREEMPT;
345 }
346
347 return AST_NONE;
348}
349
350static boolean_t
351sched_amp_processor_queue_has_priority(processor_t processor,
352 int priority,
353 boolean_t gte)
354{
355 bool spill_pending = false;
356 int spill_pri = -1;
357 processor_set_t pset = processor->processor_set;
358
359 if (pset == ecore_set && bit_test(pset->pending_spill_cpu_mask, processor->cpu_id)) {
360 spill_pending = true;
361 spill_pri = pcore_set->pset_runq.highq;
362 }
363 run_queue_t main_runq = amp_main_runq(processor);
364 run_queue_t bound_runq = amp_bound_runq(processor);
365
366 int qpri = MAX(main_runq->highq, bound_runq->highq);
367 if (spill_pending) {
368 qpri = MAX(qpri, spill_pri);
369 }
370
371 if (gte) {
372 return qpri >= priority;
373 } else {
374 return qpri > priority;
375 }
376}
377
378static int
379sched_amp_runq_count(processor_t processor)
380{
381 return amp_main_runq(processor)->count + amp_bound_runq(processor)->count;
382}
383
384static uint64_t
385sched_amp_runq_stats_count_sum(processor_t processor)
386{
387 uint64_t bound_sum = amp_bound_runq(processor)->runq_stats.count_sum;
388
389 if (processor->cpu_id == processor->processor_set->cpu_set_low) {
390 return bound_sum + amp_main_runq(processor)->runq_stats.count_sum;
391 } else {
392 return bound_sum;
393 }
394}
395static int
396sched_amp_processor_bound_count(processor_t processor)
397{
398 return amp_bound_runq(processor)->count;
399}
400
401static void
402sched_amp_processor_queue_shutdown(processor_t processor)
403{
404 processor_set_t pset = processor->processor_set;
405 run_queue_t rq = amp_main_runq(processor);
406 thread_t thread;
407 queue_head_t tqueue;
408
409 /* We only need to migrate threads if this is the last active or last recommended processor in the pset */
410 if ((pset->online_processor_count > 0) && pset_is_recommended(pset)) {
411 pset_unlock(pset);
412 return;
413 }
414
415 queue_init(&tqueue);
416
417 while (rq->count > 0) {
418 thread = run_queue_dequeue(rq, SCHED_HEADQ);
419 enqueue_tail(&tqueue, &thread->runq_links);
420 }
421
422 pset_unlock(pset);
423
424 qe_foreach_element_safe(thread, &tqueue, runq_links) {
425 remqueue(&thread->runq_links);
426
427 thread_lock(thread);
428
429 thread_setrun(thread, SCHED_TAILQ);
430
431 thread_unlock(thread);
432 }
433}
434
435static boolean_t
436sched_amp_processor_queue_remove(
437 processor_t processor,
438 thread_t thread)
439{
440 run_queue_t rq;
441 processor_set_t pset = processor->processor_set;
442
443 pset_lock(pset);
444
445 rq = amp_runq_for_thread(processor, thread);
446
447 if (processor == thread->runq) {
448 /*
449 * Thread is on a run queue and we have a lock on
450 * that run queue.
451 */
452 run_queue_remove(rq, thread);
453 } else {
454 /*
455 * The thread left the run queue before we could
456 * lock the run queue.
457 */
458 assert(thread->runq == PROCESSOR_NULL);
459 processor = PROCESSOR_NULL;
460 }
461
462 pset_unlock(pset);
463
464 return processor != PROCESSOR_NULL;
465}
466
467/*
468 * sched_amp_steal_thread()
469 *
470 */
471thread_t
472sched_amp_steal_thread(processor_set_t pset)
473{
474 thread_t thread = THREAD_NULL;
475 processor_set_t nset = pset;
476
477 assert(pset->pset_cluster_type != PSET_AMP_P);
478
479 processor_t processor = current_processor();
480 assert(pset == processor->processor_set);
481
482 bool spill_pending = bit_test(pset->pending_spill_cpu_mask, processor->cpu_id);
483 bit_clear(pset->pending_spill_cpu_mask, processor->cpu_id);
484
485 nset = pcore_set;
486
487 assert(nset != pset);
488
489 if (sched_get_pset_load_average(nset) >= sched_amp_steal_threshold(nset, spill_pending)) {
490 pset_unlock(pset);
491
492 pset = nset;
493
494 pset_lock(pset);
495
496 /* Allow steal if load average still OK, no idle cores, and more threads on runq than active cores DISPATCHING */
497 if ((sched_get_pset_load_average(pset) >= sched_amp_steal_threshold(pset, spill_pending)) &&
498 (pset->pset_runq.count > bit_count(pset->cpu_state_map[PROCESSOR_DISPATCHING])) &&
499 (bit_count(pset->recommended_bitmask & pset->cpu_state_map[PROCESSOR_IDLE]) == 0)) {
500 thread = run_queue_dequeue(&pset->pset_runq, SCHED_HEADQ);
501 KDBG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_AMP_STEAL) | DBG_FUNC_NONE, spill_pending, 0, 0, 0);
502 sched_update_pset_load_average(pset);
503 }
504 }
505
506 pset_unlock(pset);
507 return thread;
508}
509
510
511
512static void
513sched_amp_thread_update_scan(sched_update_scan_context_t scan_context)
514{
515 boolean_t restart_needed = FALSE;
516 processor_t processor = processor_list;
517 processor_set_t pset;
518 thread_t thread;
519 spl_t s;
520
521 /*
522 * We update the threads associated with each processor (bound and idle threads)
523 * and then update the threads in each pset runqueue.
524 */
525
526 do {
527 do {
528 pset = processor->processor_set;
529
530 s = splsched();
531 pset_lock(pset);
532
533 restart_needed = runq_scan(amp_bound_runq(processor), scan_context);
534
535 pset_unlock(pset);
536 splx(s);
537
538 if (restart_needed) {
539 break;
540 }
541
542 thread = processor->idle_thread;
543 if (thread != THREAD_NULL && thread->sched_stamp != sched_tick) {
544 if (thread_update_add_thread(thread) == FALSE) {
545 restart_needed = TRUE;
546 break;
547 }
548 }
549 } while ((processor = processor->processor_list) != NULL);
550
551 /* Ok, we now have a collection of candidates -- fix them. */
552 thread_update_process_threads();
553 } while (restart_needed);
554
555 pset_node_t node = &pset_node0;
556 pset = node->psets;
557
558 do {
559 do {
560 restart_needed = FALSE;
561 while (pset != NULL) {
562 s = splsched();
563 pset_lock(pset);
564
565 restart_needed = runq_scan(&pset->pset_runq, scan_context);
566
567 pset_unlock(pset);
568 splx(s);
569
570 if (restart_needed) {
571 break;
572 }
573
574 pset = pset->pset_list;
575 }
576
577 if (restart_needed) {
578 break;
579 }
580 } while (((node = node->node_list) != NULL) && ((pset = node->psets) != NULL));
581
582 /* Ok, we now have a collection of candidates -- fix them. */
583 thread_update_process_threads();
584 } while (restart_needed);
585}
586
587static bool
588pcores_recommended(thread_t thread)
589{
590 if (pcore_set->online_processor_count == 0) {
591 /* No pcores available */
592 return false;
593 }
594
595 if (!pset_is_recommended(ecore_set)) {
596 /* No E cores recommended, must use P cores */
597 return true;
598 }
599
600 if (recommended_pset_type(thread) == PSET_AMP_E) {
601 return false;
602 }
603
604 return pset_is_recommended(pcore_set);
605}
606
607/* Return true if this thread should not continue running on this processor */
608static bool
609sched_amp_thread_avoid_processor(processor_t processor, thread_t thread)
610{
611 if (processor->processor_set->pset_cluster_type == PSET_AMP_E) {
612 if (pcores_recommended(thread)) {
613 return true;
614 }
615 } else if (processor->processor_set->pset_cluster_type == PSET_AMP_P) {
616 if (!pcores_recommended(thread)) {
617 return true;
618 }
619 }
620
621 return false;
622}
623
624static processor_t
625sched_amp_choose_processor(processor_set_t pset, processor_t processor, thread_t thread)
626{
627 /* Bound threads don't call this function */
628 assert(thread->bound_processor == PROCESSOR_NULL);
629
630 processor_set_t nset = pset;
631 bool choose_pcores;
632
633again:
634 choose_pcores = pcores_recommended(thread);
635
636 if (choose_pcores && (pset->pset_cluster_type != PSET_AMP_P)) {
637 nset = pcore_set;
638 assert(nset != NULL);
639 } else if (!choose_pcores && (pset->pset_cluster_type != PSET_AMP_E)) {
640 nset = ecore_set;
641 assert(nset != NULL);
642 }
643
644 if (nset != pset) {
645 pset_unlock(pset);
646 pset_lock(nset);
647 }
648
649 /* Now that the chosen pset is definitely locked, make sure nothing important has changed */
650 if (!pset_is_recommended(nset)) {
651 pset = nset;
652 goto again;
653 }
654
655 return choose_processor(nset, processor, thread);
656}
657
658void
659sched_amp_thread_group_recommendation_change(struct thread_group *tg, cluster_type_t new_recommendation)
660{
661 thread_group_update_recommendation(tg, new_recommendation);
662
663 if (new_recommendation != CLUSTER_TYPE_P) {
664 return;
665 }
666
667 sched_amp_bounce_thread_group_from_ecores(ecore_set, tg);
668}
669
670#if DEVELOPMENT || DEBUG
671extern int32_t sysctl_get_bound_cpuid(void);
672int32_t
673sysctl_get_bound_cpuid(void)
674{
675 int32_t cpuid = -1;
676 thread_t self = current_thread();
677
678 processor_t processor = self->bound_processor;
679 if (processor == NULL) {
680 cpuid = -1;
681 } else {
682 cpuid = processor->cpu_id;
683 }
684
685 return cpuid;
686}
687
688extern void sysctl_thread_bind_cpuid(int32_t cpuid);
689void
690sysctl_thread_bind_cpuid(int32_t cpuid)
691{
692 if (cpuid < 0 || cpuid >= MAX_SCHED_CPUS) {
693 return;
694 }
695
696 processor_t processor = processor_array[cpuid];
697 if (processor == PROCESSOR_NULL) {
698 return;
699 }
700
701 thread_bind(processor);
702
703 thread_block(THREAD_CONTINUE_NULL);
704}
705
706extern char sysctl_get_bound_cluster_type(void);
707char
708sysctl_get_bound_cluster_type(void)
709{
710 thread_t self = current_thread();
711
712 if (self->sched_flags & TH_SFLAG_ECORE_ONLY) {
713 return 'E';
714 } else if (self->sched_flags & TH_SFLAG_PCORE_ONLY) {
715 return 'P';
716 }
717
718 return '0';
719}
720
721extern void sysctl_thread_bind_cluster_type(char cluster_type);
722void
723sysctl_thread_bind_cluster_type(char cluster_type)
724{
725 thread_bind_cluster_type(cluster_type);
726}
727
728extern char sysctl_get_task_cluster_type(void);
729char
730sysctl_get_task_cluster_type(void)
731{
732 thread_t thread = current_thread();
733 task_t task = thread->task;
734
735 if (task->pset_hint == ecore_set) {
736 return 'E';
737 } else if (task->pset_hint == pcore_set) {
738 return 'P';
739 }
740
741 return '0';
742}
743
744extern void sysctl_task_set_cluster_type(char cluster_type);
745void
746sysctl_task_set_cluster_type(char cluster_type)
747{
748 thread_t thread = current_thread();
749 task_t task = thread->task;
750
751 switch (cluster_type) {
752 case 'e':
753 case 'E':
754 task->pset_hint = ecore_set;
755 break;
756 case 'p':
757 case 'P':
758 task->pset_hint = pcore_set;
759 break;
760 default:
761 break;
762 }
763
764 thread_block(THREAD_CONTINUE_NULL);
765}
766#endif
767
768#endif