2 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <mach/mach_types.h>
30 #include <mach/machine.h>
32 #include <machine/machine_routines.h>
33 #include <machine/sched_param.h>
34 #include <machine/machine_cpu.h>
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>
49 #include <sys/kdebug.h>
54 sched_amp_steal_thread(processor_set_t pset
);
57 sched_amp_thread_update_scan(sched_update_scan_context_t scan_context
);
60 sched_amp_processor_enqueue(processor_t processor
, thread_t thread
,
61 sched_options_t options
);
64 sched_amp_processor_queue_remove(processor_t processor
, thread_t thread
);
67 sched_amp_processor_csw_check(processor_t processor
);
70 sched_amp_processor_queue_has_priority(processor_t processor
, int priority
, boolean_t gte
);
73 sched_amp_runq_count(processor_t processor
);
76 sched_amp_processor_queue_empty(processor_t processor
);
79 sched_amp_runq_stats_count_sum(processor_t processor
);
82 sched_amp_processor_bound_count(processor_t processor
);
85 sched_amp_pset_init(processor_set_t pset
);
88 sched_amp_processor_init(processor_t processor
);
91 sched_amp_choose_thread(processor_t processor
, int priority
, ast_t reason
);
94 sched_amp_processor_queue_shutdown(processor_t processor
);
97 sched_amp_initial_thread_sched_mode(task_t parent_task
);
100 sched_amp_choose_processor(processor_set_t pset
, processor_t processor
, thread_t thread
);
103 sched_amp_thread_avoid_processor(processor_t processor
, thread_t thread
);
106 sched_amp_thread_should_yield(processor_t processor
, thread_t thread
);
109 sched_amp_thread_group_recommendation_change(struct thread_group
*tg
, cluster_type_t new_recommendation
);
111 const struct sched_dispatch_table sched_amp_dispatch
= {
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
,
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
,
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
,
163 extern processor_set_t ecore_set
;
164 extern processor_set_t pcore_set
;
166 __attribute__((always_inline
))
167 static inline run_queue_t
168 amp_main_runq(processor_t processor
)
170 return &processor
->processor_set
->pset_runq
;
173 __attribute__((always_inline
))
174 static inline run_queue_t
175 amp_bound_runq(processor_t processor
)
177 return &processor
->runq
;
180 __attribute__((always_inline
))
181 static inline run_queue_t
182 amp_runq_for_thread(processor_t processor
, thread_t thread
)
184 if (thread
->bound_processor
== PROCESSOR_NULL
) {
185 return amp_main_runq(processor
);
187 assert(thread
->bound_processor
== processor
);
188 return amp_bound_runq(processor
);
193 sched_amp_initial_thread_sched_mode(task_t parent_task
)
195 if (parent_task
== kernel_task
) {
196 return TH_MODE_FIXED
;
198 return TH_MODE_TIMESHARE
;
203 sched_amp_processor_init(processor_t processor
)
205 run_queue_init(&processor
->runq
);
209 sched_amp_pset_init(processor_set_t pset
)
211 run_queue_init(&pset
->pset_runq
);
215 sched_amp_choose_thread(
216 processor_t processor
,
218 __unused ast_t reason
)
220 processor_set_t pset
= processor
->processor_set
;
221 bool spill_pending
= false;
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
;
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
;
233 if ((bound_runq
->highq
< priority
) &&
234 (main_runq
->highq
< priority
) &&
235 (spill_pri
< priority
)) {
239 if ((spill_pri
> bound_runq
->highq
) &&
240 (spill_pri
> main_runq
->highq
)) {
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.
249 if (bound_runq
->highq
>= main_runq
->highq
) {
250 chosen_runq
= bound_runq
;
252 chosen_runq
= main_runq
;
255 return run_queue_dequeue(chosen_runq
, SCHED_HEADQ
);
259 sched_amp_processor_enqueue(
260 processor_t processor
,
262 sched_options_t options
)
264 run_queue_t rq
= amp_runq_for_thread(processor
, thread
);
267 result
= run_queue_enqueue(rq
, thread
, options
);
268 thread
->runq
= processor
;
274 sched_amp_processor_queue_empty(processor_t processor
)
276 processor_set_t pset
= processor
->processor_set
;
277 bool spill_pending
= bit_test(pset
->pending_spill_cpu_mask
, processor
->cpu_id
);
279 return (amp_main_runq(processor
)->count
== 0) &&
280 (amp_bound_runq(processor
)->count
== 0) &&
285 sched_amp_thread_should_yield(processor_t processor
, thread_t thread
)
287 if (!sched_amp_processor_queue_empty(processor
) || (rt_runq_count(processor
->processor_set
) > 0)) {
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;
299 sched_amp_processor_csw_check(processor_t processor
)
301 boolean_t has_higher
;
304 run_queue_t main_runq
= amp_main_runq(processor
);
305 run_queue_t bound_runq
= amp_bound_runq(processor
);
307 assert(processor
->active_thread
!= NULL
);
309 processor_set_t pset
= processor
->processor_set
;
310 bool spill_pending
= false;
312 int spill_urgency
= 0;
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
;
320 pri
= MAX(main_runq
->highq
, bound_runq
->highq
);
322 pri
= MAX(pri
, spill_pri
);
325 if (processor
->first_timeslice
) {
326 has_higher
= (pri
> processor
->current_pri
);
328 has_higher
= (pri
>= processor
->current_pri
);
332 if (main_runq
->urgency
> 0) {
333 return AST_PREEMPT
| AST_URGENT
;
336 if (bound_runq
->urgency
> 0) {
337 return AST_PREEMPT
| AST_URGENT
;
340 if (spill_urgency
> 0) {
341 return AST_PREEMPT
| AST_URGENT
;
351 sched_amp_processor_queue_has_priority(processor_t processor
,
355 bool spill_pending
= false;
357 processor_set_t pset
= processor
->processor_set
;
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
;
363 run_queue_t main_runq
= amp_main_runq(processor
);
364 run_queue_t bound_runq
= amp_bound_runq(processor
);
366 int qpri
= MAX(main_runq
->highq
, bound_runq
->highq
);
368 qpri
= MAX(qpri
, spill_pri
);
372 return qpri
>= priority
;
374 return qpri
> priority
;
379 sched_amp_runq_count(processor_t processor
)
381 return amp_main_runq(processor
)->count
+ amp_bound_runq(processor
)->count
;
385 sched_amp_runq_stats_count_sum(processor_t processor
)
387 uint64_t bound_sum
= amp_bound_runq(processor
)->runq_stats
.count_sum
;
389 if (processor
->cpu_id
== processor
->processor_set
->cpu_set_low
) {
390 return bound_sum
+ amp_main_runq(processor
)->runq_stats
.count_sum
;
396 sched_amp_processor_bound_count(processor_t processor
)
398 return amp_bound_runq(processor
)->count
;
402 sched_amp_processor_queue_shutdown(processor_t processor
)
404 processor_set_t pset
= processor
->processor_set
;
405 run_queue_t rq
= amp_main_runq(processor
);
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
)) {
417 while (rq
->count
> 0) {
418 thread
= run_queue_dequeue(rq
, SCHED_HEADQ
);
419 enqueue_tail(&tqueue
, &thread
->runq_links
);
424 qe_foreach_element_safe(thread
, &tqueue
, runq_links
) {
425 remqueue(&thread
->runq_links
);
429 thread_setrun(thread
, SCHED_TAILQ
);
431 thread_unlock(thread
);
436 sched_amp_processor_queue_remove(
437 processor_t processor
,
441 processor_set_t pset
= processor
->processor_set
;
445 rq
= amp_runq_for_thread(processor
, thread
);
447 if (processor
== thread
->runq
) {
449 * Thread is on a run queue and we have a lock on
452 run_queue_remove(rq
, thread
);
455 * The thread left the run queue before we could
456 * lock the run queue.
458 assert(thread
->runq
== PROCESSOR_NULL
);
459 processor
= PROCESSOR_NULL
;
464 return processor
!= PROCESSOR_NULL
;
468 * sched_amp_steal_thread()
472 sched_amp_steal_thread(processor_set_t pset
)
474 thread_t thread
= THREAD_NULL
;
475 processor_set_t nset
= pset
;
477 assert(pset
->pset_cluster_type
!= PSET_AMP_P
);
479 processor_t processor
= current_processor();
480 assert(pset
== processor
->processor_set
);
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
);
487 assert(nset
!= pset
);
489 if (sched_get_pset_load_average(nset
) >= sched_amp_steal_threshold(nset
, spill_pending
)) {
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
);
513 sched_amp_thread_update_scan(sched_update_scan_context_t scan_context
)
515 boolean_t restart_needed
= FALSE
;
516 processor_t processor
= processor_list
;
517 processor_set_t pset
;
522 * We update the threads associated with each processor (bound and idle threads)
523 * and then update the threads in each pset runqueue.
528 pset
= processor
->processor_set
;
533 restart_needed
= runq_scan(amp_bound_runq(processor
), scan_context
);
538 if (restart_needed
) {
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
;
549 } while ((processor
= processor
->processor_list
) != NULL
);
551 /* Ok, we now have a collection of candidates -- fix them. */
552 thread_update_process_threads();
553 } while (restart_needed
);
555 pset_node_t node
= &pset_node0
;
560 restart_needed
= FALSE
;
561 while (pset
!= NULL
) {
565 restart_needed
= runq_scan(&pset
->pset_runq
, scan_context
);
570 if (restart_needed
) {
574 pset
= pset
->pset_list
;
577 if (restart_needed
) {
580 } while (((node
= node
->node_list
) != NULL
) && ((pset
= node
->psets
) != NULL
));
582 /* Ok, we now have a collection of candidates -- fix them. */
583 thread_update_process_threads();
584 } while (restart_needed
);
588 pcores_recommended(thread_t thread
)
590 if (pcore_set
->online_processor_count
== 0) {
591 /* No pcores available */
595 if (!pset_is_recommended(ecore_set
)) {
596 /* No E cores recommended, must use P cores */
600 if (recommended_pset_type(thread
) == PSET_AMP_E
) {
604 return pset_is_recommended(pcore_set
);
607 /* Return true if this thread should not continue running on this processor */
609 sched_amp_thread_avoid_processor(processor_t processor
, thread_t thread
)
611 if (processor
->processor_set
->pset_cluster_type
== PSET_AMP_E
) {
612 if (pcores_recommended(thread
)) {
615 } else if (processor
->processor_set
->pset_cluster_type
== PSET_AMP_P
) {
616 if (!pcores_recommended(thread
)) {
625 sched_amp_choose_processor(processor_set_t pset
, processor_t processor
, thread_t thread
)
627 /* Bound threads don't call this function */
628 assert(thread
->bound_processor
== PROCESSOR_NULL
);
630 processor_set_t nset
= pset
;
634 choose_pcores
= pcores_recommended(thread
);
636 if (choose_pcores
&& (pset
->pset_cluster_type
!= PSET_AMP_P
)) {
638 assert(nset
!= NULL
);
639 } else if (!choose_pcores
&& (pset
->pset_cluster_type
!= PSET_AMP_E
)) {
641 assert(nset
!= NULL
);
649 /* Now that the chosen pset is definitely locked, make sure nothing important has changed */
650 if (!pset_is_recommended(nset
)) {
655 return choose_processor(nset
, processor
, thread
);
659 sched_amp_thread_group_recommendation_change(struct thread_group
*tg
, cluster_type_t new_recommendation
)
661 thread_group_update_recommendation(tg
, new_recommendation
);
663 if (new_recommendation
!= CLUSTER_TYPE_P
) {
667 sched_amp_bounce_thread_group_from_ecores(ecore_set
, tg
);
670 #if DEVELOPMENT || DEBUG
671 extern int32_t sysctl_get_bound_cpuid(void);
673 sysctl_get_bound_cpuid(void)
676 thread_t self
= current_thread();
678 processor_t processor
= self
->bound_processor
;
679 if (processor
== NULL
) {
682 cpuid
= processor
->cpu_id
;
688 extern void sysctl_thread_bind_cpuid(int32_t cpuid
);
690 sysctl_thread_bind_cpuid(int32_t cpuid
)
692 if (cpuid
< 0 || cpuid
>= MAX_SCHED_CPUS
) {
696 processor_t processor
= processor_array
[cpuid
];
697 if (processor
== PROCESSOR_NULL
) {
701 thread_bind(processor
);
703 thread_block(THREAD_CONTINUE_NULL
);
706 extern char sysctl_get_bound_cluster_type(void);
708 sysctl_get_bound_cluster_type(void)
710 thread_t self
= current_thread();
712 if (self
->sched_flags
& TH_SFLAG_ECORE_ONLY
) {
714 } else if (self
->sched_flags
& TH_SFLAG_PCORE_ONLY
) {
721 extern void sysctl_thread_bind_cluster_type(char cluster_type
);
723 sysctl_thread_bind_cluster_type(char cluster_type
)
725 thread_bind_cluster_type(cluster_type
);
728 extern char sysctl_get_task_cluster_type(void);
730 sysctl_get_task_cluster_type(void)
732 thread_t thread
= current_thread();
733 task_t task
= thread
->task
;
735 if (task
->pset_hint
== ecore_set
) {
737 } else if (task
->pset_hint
== pcore_set
) {
744 extern void sysctl_task_set_cluster_type(char cluster_type
);
746 sysctl_task_set_cluster_type(char cluster_type
)
748 thread_t thread
= current_thread();
749 task_t task
= thread
->task
;
751 switch (cluster_type
) {
754 task
->pset_hint
= ecore_set
;
758 task
->pset_hint
= pcore_set
;
764 thread_block(THREAD_CONTINUE_NULL
);