2 * Copyright (c) 2013 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>
47 #include <sys/kdebug.h>
50 sched_dualq_init(void);
53 sched_dualq_steal_thread(processor_set_t pset
);
56 sched_dualq_thread_update_scan(sched_update_scan_context_t scan_context
);
59 sched_dualq_processor_enqueue(processor_t processor
, thread_t thread
,
60 sched_options_t options
);
63 sched_dualq_processor_queue_remove(processor_t processor
, thread_t thread
);
66 sched_dualq_processor_csw_check(processor_t processor
);
69 sched_dualq_processor_queue_has_priority(processor_t processor
, int priority
, boolean_t gte
);
72 sched_dualq_runq_count(processor_t processor
);
75 sched_dualq_processor_queue_empty(processor_t processor
);
78 sched_dualq_runq_stats_count_sum(processor_t processor
);
81 sched_dualq_processor_bound_count(processor_t processor
);
84 sched_dualq_pset_init(processor_set_t pset
);
87 sched_dualq_processor_init(processor_t processor
);
90 sched_dualq_choose_thread(processor_t processor
, int priority
, ast_t reason
);
93 sched_dualq_processor_queue_shutdown(processor_t processor
);
96 sched_dualq_initial_thread_sched_mode(task_t parent_task
);
99 sched_dualq_thread_avoid_processor(processor_t processor
, thread_t thread
);
101 const struct sched_dispatch_table sched_dualq_dispatch
= {
102 .sched_name
= "dualq",
103 .init
= sched_dualq_init
,
104 .timebase_init
= sched_timeshare_timebase_init
,
105 .processor_init
= sched_dualq_processor_init
,
106 .pset_init
= sched_dualq_pset_init
,
107 .maintenance_continuation
= sched_timeshare_maintenance_continue
,
108 .choose_thread
= sched_dualq_choose_thread
,
109 .steal_thread_enabled
= sched_steal_thread_enabled
,
110 .steal_thread
= sched_dualq_steal_thread
,
111 .compute_timeshare_priority
= sched_compute_timeshare_priority
,
112 .choose_node
= sched_choose_node
,
113 .choose_processor
= choose_processor
,
114 .processor_enqueue
= sched_dualq_processor_enqueue
,
115 .processor_queue_shutdown
= sched_dualq_processor_queue_shutdown
,
116 .processor_queue_remove
= sched_dualq_processor_queue_remove
,
117 .processor_queue_empty
= sched_dualq_processor_queue_empty
,
118 .priority_is_urgent
= priority_is_urgent
,
119 .processor_csw_check
= sched_dualq_processor_csw_check
,
120 .processor_queue_has_priority
= sched_dualq_processor_queue_has_priority
,
121 .initial_quantum_size
= sched_timeshare_initial_quantum_size
,
122 .initial_thread_sched_mode
= sched_dualq_initial_thread_sched_mode
,
123 .can_update_priority
= can_update_priority
,
124 .update_priority
= update_priority
,
125 .lightweight_update_priority
= lightweight_update_priority
,
126 .quantum_expire
= sched_default_quantum_expire
,
127 .processor_runq_count
= sched_dualq_runq_count
,
128 .processor_runq_stats_count_sum
= sched_dualq_runq_stats_count_sum
,
129 .processor_bound_count
= sched_dualq_processor_bound_count
,
130 .thread_update_scan
= sched_dualq_thread_update_scan
,
131 .multiple_psets_enabled
= TRUE
,
132 .sched_groups_enabled
= FALSE
,
133 .avoid_processor_enabled
= TRUE
,
134 .thread_avoid_processor
= sched_dualq_thread_avoid_processor
,
135 .processor_balance
= sched_SMT_balance
,
137 .rt_runq
= sched_rtlocal_runq
,
138 .rt_init
= sched_rtlocal_init
,
139 .rt_queue_shutdown
= sched_rtlocal_queue_shutdown
,
140 .rt_runq_scan
= sched_rtlocal_runq_scan
,
141 .rt_runq_count_sum
= sched_rtlocal_runq_count_sum
,
143 .qos_max_parallelism
= sched_qos_max_parallelism
,
144 .check_spill
= sched_check_spill
,
145 .ipi_policy
= sched_ipi_policy
,
146 .thread_should_yield
= sched_thread_should_yield
,
147 .run_count_incr
= sched_smt_run_incr
,
148 .run_count_decr
= sched_smt_run_decr
,
149 .update_thread_bucket
= sched_smt_update_thread_bucket
,
150 .pset_made_schedulable
= sched_pset_made_schedulable
,
153 __attribute__((always_inline
))
154 static inline run_queue_t
155 dualq_main_runq(processor_t processor
)
157 return &processor
->processor_set
->pset_runq
;
160 __attribute__((always_inline
))
161 static inline run_queue_t
162 dualq_bound_runq(processor_t processor
)
164 return &processor
->runq
;
167 __attribute__((always_inline
))
168 static inline run_queue_t
169 dualq_runq_for_thread(processor_t processor
, thread_t thread
)
171 if (thread
->bound_processor
== PROCESSOR_NULL
) {
172 return dualq_main_runq(processor
);
174 assert(thread
->bound_processor
== processor
);
175 return dualq_bound_runq(processor
);
180 sched_dualq_initial_thread_sched_mode(task_t parent_task
)
182 if (parent_task
== kernel_task
) {
183 return TH_MODE_FIXED
;
185 return TH_MODE_TIMESHARE
;
190 sched_dualq_processor_init(processor_t processor
)
192 run_queue_init(&processor
->runq
);
196 sched_dualq_pset_init(processor_set_t pset
)
198 run_queue_init(&pset
->pset_runq
);
201 extern int sched_allow_NO_SMT_threads
;
203 sched_dualq_init(void)
205 sched_timeshare_init();
207 if (PE_parse_boot_argn("disable_NO_SMT_threads", NULL
, 0)) {
208 sched_allow_NO_SMT_threads
= 0;
213 sched_dualq_choose_thread(
214 processor_t processor
,
216 __unused ast_t reason
)
218 run_queue_t main_runq
= dualq_main_runq(processor
);
219 run_queue_t bound_runq
= dualq_bound_runq(processor
);
220 run_queue_t chosen_runq
;
222 if (bound_runq
->highq
< priority
&&
223 main_runq
->highq
< priority
) {
227 if (bound_runq
->count
&& main_runq
->count
) {
228 if (bound_runq
->highq
>= main_runq
->highq
) {
229 chosen_runq
= bound_runq
;
231 chosen_runq
= main_runq
;
233 } else if (bound_runq
->count
) {
234 chosen_runq
= bound_runq
;
235 } else if (main_runq
->count
) {
236 chosen_runq
= main_runq
;
241 if (chosen_runq
== bound_runq
) {
242 return run_queue_dequeue(chosen_runq
, SCHED_HEADQ
);
245 if (processor
->is_SMT
) {
246 thread_t potential_thread
= run_queue_peek(chosen_runq
);
247 if (potential_thread
== THREAD_NULL
) {
250 if (processor
->processor_primary
!= processor
) {
252 * Secondary processor may not run a NO_SMT thread,
253 * nor any thread if the primary is running a NO_SMT thread.
255 if (thread_no_smt(potential_thread
)) {
256 processor
->must_idle
= true;
259 processor_t primary
= processor
->processor_primary
;
260 if (primary
->state
== PROCESSOR_RUNNING
) {
261 if (processor_active_thread_no_smt(primary
)) {
262 processor
->must_idle
= true;
266 } else if (processor
->processor_secondary
!= PROCESSOR_NULL
) {
267 processor_t secondary
= processor
->processor_secondary
;
269 * Primary processor may not run a NO_SMT thread if
270 * its secondary is running a bound thread.
272 if (secondary
->state
== PROCESSOR_RUNNING
) {
273 if (thread_no_smt(potential_thread
) && secondary
->current_is_bound
) {
274 processor
->must_idle
= true;
281 return run_queue_dequeue(chosen_runq
, SCHED_HEADQ
);
285 sched_dualq_processor_enqueue(
286 processor_t processor
,
288 sched_options_t options
)
290 run_queue_t rq
= dualq_runq_for_thread(processor
, thread
);
293 result
= run_queue_enqueue(rq
, thread
, options
);
294 thread
->runq
= processor
;
300 sched_dualq_processor_queue_empty(processor_t processor
)
302 return dualq_main_runq(processor
)->count
== 0 &&
303 dualq_bound_runq(processor
)->count
== 0;
307 sched_dualq_processor_csw_check(processor_t processor
)
309 boolean_t has_higher
;
312 if (sched_dualq_thread_avoid_processor(processor
, current_thread())) {
313 return AST_PREEMPT
| AST_URGENT
;
316 run_queue_t main_runq
= dualq_main_runq(processor
);
317 run_queue_t bound_runq
= dualq_bound_runq(processor
);
319 assert(processor
->active_thread
!= NULL
);
321 pri
= MAX(main_runq
->highq
, bound_runq
->highq
);
323 if (processor
->first_timeslice
) {
324 has_higher
= (pri
> processor
->current_pri
);
326 has_higher
= (pri
>= processor
->current_pri
);
330 if (main_runq
->urgency
> 0) {
331 return AST_PREEMPT
| AST_URGENT
;
334 if (bound_runq
->urgency
> 0) {
335 return AST_PREEMPT
| AST_URGENT
;
345 sched_dualq_processor_queue_has_priority(processor_t processor
,
349 run_queue_t main_runq
= dualq_main_runq(processor
);
350 run_queue_t bound_runq
= dualq_bound_runq(processor
);
352 int qpri
= MAX(main_runq
->highq
, bound_runq
->highq
);
355 return qpri
>= priority
;
357 return qpri
> priority
;
362 sched_dualq_runq_count(processor_t processor
)
364 return dualq_main_runq(processor
)->count
+ dualq_bound_runq(processor
)->count
;
368 sched_dualq_runq_stats_count_sum(processor_t processor
)
370 uint64_t bound_sum
= dualq_bound_runq(processor
)->runq_stats
.count_sum
;
372 if (processor
->cpu_id
== processor
->processor_set
->cpu_set_low
) {
373 return bound_sum
+ dualq_main_runq(processor
)->runq_stats
.count_sum
;
379 sched_dualq_processor_bound_count(processor_t processor
)
381 return dualq_bound_runq(processor
)->count
;
385 sched_dualq_processor_queue_shutdown(processor_t processor
)
387 processor_set_t pset
= processor
->processor_set
;
388 run_queue_t rq
= dualq_main_runq(processor
);
392 /* We only need to migrate threads if this is the last active processor in the pset */
393 if (pset
->online_processor_count
> 0) {
400 while (rq
->count
> 0) {
401 thread
= run_queue_dequeue(rq
, SCHED_HEADQ
);
402 enqueue_tail(&tqueue
, &thread
->runq_links
);
407 qe_foreach_element_safe(thread
, &tqueue
, runq_links
) {
408 remqueue(&thread
->runq_links
);
412 thread_setrun(thread
, SCHED_TAILQ
);
414 thread_unlock(thread
);
419 sched_dualq_processor_queue_remove(
420 processor_t processor
,
424 processor_set_t pset
= processor
->processor_set
;
428 rq
= dualq_runq_for_thread(processor
, thread
);
430 if (processor
== thread
->runq
) {
432 * Thread is on a run queue and we have a lock on
435 run_queue_remove(rq
, thread
);
438 * The thread left the run queue before we could
439 * lock the run queue.
441 assert(thread
->runq
== PROCESSOR_NULL
);
442 processor
= PROCESSOR_NULL
;
447 return processor
!= PROCESSOR_NULL
;
451 sched_dualq_steal_thread(processor_set_t pset
)
453 processor_set_t cset
= pset
;
454 processor_set_t nset
= next_pset(cset
);
457 /* Secondary processors on SMT systems never steal */
458 assert(current_processor()->processor_primary
== current_processor());
460 while (nset
!= pset
) {
465 if (pset_has_stealable_threads(cset
)) {
466 /* Need task_restrict logic here */
467 thread
= run_queue_dequeue(&cset
->pset_runq
, SCHED_HEADQ
);
472 nset
= next_pset(cset
);
481 sched_dualq_thread_update_scan(sched_update_scan_context_t scan_context
)
483 boolean_t restart_needed
= FALSE
;
484 processor_t processor
= processor_list
;
485 processor_set_t pset
;
490 * We update the threads associated with each processor (bound and idle threads)
491 * and then update the threads in each pset runqueue.
496 pset
= processor
->processor_set
;
501 restart_needed
= runq_scan(dualq_bound_runq(processor
), scan_context
);
506 if (restart_needed
) {
510 thread
= processor
->idle_thread
;
511 if (thread
!= THREAD_NULL
&& thread
->sched_stamp
!= sched_tick
) {
512 if (thread_update_add_thread(thread
) == FALSE
) {
513 restart_needed
= TRUE
;
517 } while ((processor
= processor
->processor_list
) != NULL
);
519 /* Ok, we now have a collection of candidates -- fix them. */
520 thread_update_process_threads();
521 } while (restart_needed
);
530 restart_needed
= runq_scan(&pset
->pset_runq
, scan_context
);
535 if (restart_needed
) {
538 } while ((pset
= pset
->pset_list
) != NULL
);
540 /* Ok, we now have a collection of candidates -- fix them. */
541 thread_update_process_threads();
542 } while (restart_needed
);
545 extern int sched_allow_rt_smt
;
547 /* Return true if this thread should not continue running on this processor */
549 sched_dualq_thread_avoid_processor(processor_t processor
, thread_t thread
)
551 if (thread
->bound_processor
== processor
) {
552 /* Thread is bound here */
556 if (processor
->processor_primary
!= processor
) {
558 * This is a secondary SMT processor. If the primary is running
559 * a realtime thread, only allow realtime threads on the secondary.
561 processor_t primary
= processor
->processor_primary
;
562 if ((primary
->current_pri
>= BASEPRI_RTQUEUES
) && ((thread
->sched_pri
< BASEPRI_RTQUEUES
) || !sched_allow_rt_smt
)) {
566 /* NO_SMT threads are not allowed on secondary processors */
567 if (thread_no_smt(thread
)) {
571 if (primary
->state
== PROCESSOR_RUNNING
) {
572 if (processor_active_thread_no_smt(primary
)) {
573 /* No threads allowed on secondary if primary has NO_SMT */
579 if (processor
->processor_secondary
!= PROCESSOR_NULL
) {
581 * This is a primary SMT processor. If the secondary is running
582 * a bound thread, the primary may not run a NO_SMT thread.
584 processor_t secondary
= processor
->processor_secondary
;
586 if (secondary
->state
== PROCESSOR_RUNNING
) {
587 if (secondary
->current_is_bound
&& thread_no_smt(thread
)) {