]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/sched_dualq.c
xnu-6153.121.1.tar.gz
[apple/xnu.git] / osfmk / kern / sched_dualq.c
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2013 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
47#include <sys/kdebug.h>
48
49static void
50sched_dualq_init(void);
51
52static thread_t
53sched_dualq_steal_thread(processor_set_t pset);
54
55static void
3e170ce0 56sched_dualq_thread_update_scan(sched_update_scan_context_t scan_context);
fe8ab488
A
57
58static boolean_t
cb323159
A
59sched_dualq_processor_enqueue(processor_t processor, thread_t thread,
60 sched_options_t options);
fe8ab488
A
61
62static boolean_t
63sched_dualq_processor_queue_remove(processor_t processor, thread_t thread);
64
65static ast_t
66sched_dualq_processor_csw_check(processor_t processor);
67
68static boolean_t
69sched_dualq_processor_queue_has_priority(processor_t processor, int priority, boolean_t gte);
70
71static int
72sched_dualq_runq_count(processor_t processor);
73
74static boolean_t
75sched_dualq_processor_queue_empty(processor_t processor);
76
77static uint64_t
78sched_dualq_runq_stats_count_sum(processor_t processor);
79
80static int
81sched_dualq_processor_bound_count(processor_t processor);
82
83static void
84sched_dualq_pset_init(processor_set_t pset);
85
86static void
87sched_dualq_processor_init(processor_t processor);
88
89static thread_t
90sched_dualq_choose_thread(processor_t processor, int priority, ast_t reason);
91
92static void
93sched_dualq_processor_queue_shutdown(processor_t processor);
94
95static sched_mode_t
96sched_dualq_initial_thread_sched_mode(task_t parent_task);
97
d9a64523
A
98static bool
99sched_dualq_thread_avoid_processor(processor_t processor, thread_t thread);
100
fe8ab488 101const struct sched_dispatch_table sched_dualq_dispatch = {
3e170ce0 102 .sched_name = "dualq",
fe8ab488 103 .init = sched_dualq_init,
3e170ce0 104 .timebase_init = sched_timeshare_timebase_init,
fe8ab488
A
105 .processor_init = sched_dualq_processor_init,
106 .pset_init = sched_dualq_pset_init,
3e170ce0 107 .maintenance_continuation = sched_timeshare_maintenance_continue,
fe8ab488 108 .choose_thread = sched_dualq_choose_thread,
0a7de745 109 .steal_thread_enabled = sched_steal_thread_enabled,
fe8ab488 110 .steal_thread = sched_dualq_steal_thread,
3e170ce0 111 .compute_timeshare_priority = sched_compute_timeshare_priority,
fe8ab488
A
112 .choose_processor = choose_processor,
113 .processor_enqueue = sched_dualq_processor_enqueue,
114 .processor_queue_shutdown = sched_dualq_processor_queue_shutdown,
115 .processor_queue_remove = sched_dualq_processor_queue_remove,
116 .processor_queue_empty = sched_dualq_processor_queue_empty,
117 .priority_is_urgent = priority_is_urgent,
118 .processor_csw_check = sched_dualq_processor_csw_check,
119 .processor_queue_has_priority = sched_dualq_processor_queue_has_priority,
3e170ce0 120 .initial_quantum_size = sched_timeshare_initial_quantum_size,
fe8ab488
A
121 .initial_thread_sched_mode = sched_dualq_initial_thread_sched_mode,
122 .can_update_priority = can_update_priority,
123 .update_priority = update_priority,
124 .lightweight_update_priority = lightweight_update_priority,
3e170ce0 125 .quantum_expire = sched_default_quantum_expire,
fe8ab488
A
126 .processor_runq_count = sched_dualq_runq_count,
127 .processor_runq_stats_count_sum = sched_dualq_runq_stats_count_sum,
fe8ab488
A
128 .processor_bound_count = sched_dualq_processor_bound_count,
129 .thread_update_scan = sched_dualq_thread_update_scan,
3e170ce0
A
130 .multiple_psets_enabled = TRUE,
131 .sched_groups_enabled = FALSE,
d9a64523
A
132 .avoid_processor_enabled = TRUE,
133 .thread_avoid_processor = sched_dualq_thread_avoid_processor,
5ba3f43e
A
134 .processor_balance = sched_SMT_balance,
135
136 .rt_runq = sched_rtglobal_runq,
137 .rt_init = sched_rtglobal_init,
138 .rt_queue_shutdown = sched_rtglobal_queue_shutdown,
139 .rt_runq_scan = sched_rtglobal_runq_scan,
140 .rt_runq_count_sum = sched_rtglobal_runq_count_sum,
141
142 .qos_max_parallelism = sched_qos_max_parallelism,
143 .check_spill = sched_check_spill,
144 .ipi_policy = sched_ipi_policy,
145 .thread_should_yield = sched_thread_should_yield,
cb323159
A
146 .run_count_incr = sched_run_incr,
147 .run_count_decr = sched_run_decr,
148 .update_thread_bucket = sched_update_thread_bucket,
149 .pset_made_schedulable = sched_pset_made_schedulable,
fe8ab488
A
150};
151
152__attribute__((always_inline))
0a7de745
A
153static inline run_queue_t
154dualq_main_runq(processor_t processor)
fe8ab488
A
155{
156 return &processor->processor_set->pset_runq;
157}
158
159__attribute__((always_inline))
0a7de745
A
160static inline run_queue_t
161dualq_bound_runq(processor_t processor)
fe8ab488
A
162{
163 return &processor->runq;
164}
165
166__attribute__((always_inline))
0a7de745
A
167static inline run_queue_t
168dualq_runq_for_thread(processor_t processor, thread_t thread)
fe8ab488
A
169{
170 if (thread->bound_processor == PROCESSOR_NULL) {
171 return dualq_main_runq(processor);
172 } else {
173 assert(thread->bound_processor == processor);
174 return dualq_bound_runq(processor);
175 }
176}
177
178static sched_mode_t
179sched_dualq_initial_thread_sched_mode(task_t parent_task)
180{
0a7de745 181 if (parent_task == kernel_task) {
fe8ab488 182 return TH_MODE_FIXED;
0a7de745 183 } else {
fe8ab488 184 return TH_MODE_TIMESHARE;
0a7de745 185 }
fe8ab488
A
186}
187
188static void
189sched_dualq_processor_init(processor_t processor)
190{
191 run_queue_init(&processor->runq);
192}
193
194static void
195sched_dualq_pset_init(processor_set_t pset)
196{
197 run_queue_init(&pset->pset_runq);
198}
199
0a7de745 200extern int sched_allow_NO_SMT_threads;
fe8ab488
A
201static void
202sched_dualq_init(void)
203{
3e170ce0 204 sched_timeshare_init();
0a7de745
A
205
206 if (PE_parse_boot_argn("disable_NO_SMT_threads", NULL, 0)) {
207 sched_allow_NO_SMT_threads = 0;
208 }
fe8ab488
A
209}
210
211static thread_t
212sched_dualq_choose_thread(
0a7de745
A
213 processor_t processor,
214 int priority,
215 __unused ast_t reason)
fe8ab488
A
216{
217 run_queue_t main_runq = dualq_main_runq(processor);
218 run_queue_t bound_runq = dualq_bound_runq(processor);
219 run_queue_t chosen_runq;
220
221 if (bound_runq->highq < priority &&
0a7de745 222 main_runq->highq < priority) {
fe8ab488 223 return THREAD_NULL;
0a7de745 224 }
fe8ab488
A
225
226 if (bound_runq->count && main_runq->count) {
227 if (bound_runq->highq >= main_runq->highq) {
228 chosen_runq = bound_runq;
229 } else {
230 chosen_runq = main_runq;
231 }
232 } else if (bound_runq->count) {
233 chosen_runq = bound_runq;
234 } else if (main_runq->count) {
235 chosen_runq = main_runq;
236 } else {
0a7de745
A
237 return THREAD_NULL;
238 }
239
240 if (chosen_runq == bound_runq) {
241 return run_queue_dequeue(chosen_runq, SCHED_HEADQ);
242 }
243
244 if (processor->is_SMT) {
cb323159 245 thread_t potential_thread = run_queue_peek(chosen_runq);
0a7de745
A
246 if (potential_thread == THREAD_NULL) {
247 return THREAD_NULL;
248 }
249 if (processor->processor_primary != processor) {
250 /*
251 * Secondary processor may not run a NO_SMT thread,
252 * nor any thread if the primary is running a NO_SMT thread.
253 */
254 if (thread_no_smt(potential_thread)) {
255 processor->must_idle = true;
256 return THREAD_NULL;
257 }
258 processor_t primary = processor->processor_primary;
259 if (primary->state == PROCESSOR_RUNNING) {
260 if (processor_active_thread_no_smt(primary)) {
261 processor->must_idle = true;
262 return THREAD_NULL;
263 }
264 }
265 } else if (processor->processor_secondary != PROCESSOR_NULL) {
266 processor_t secondary = processor->processor_secondary;
267 /*
268 * Primary processor may not run a NO_SMT thread if
269 * its secondary is running a bound thread.
270 */
271 if (secondary->state == PROCESSOR_RUNNING) {
272 if (thread_no_smt(potential_thread) && secondary->current_is_bound) {
273 processor->must_idle = true;
274 return THREAD_NULL;
275 }
276 }
277 }
fe8ab488
A
278 }
279
280 return run_queue_dequeue(chosen_runq, SCHED_HEADQ);
281}
282
283static boolean_t
284sched_dualq_processor_enqueue(
0a7de745
A
285 processor_t processor,
286 thread_t thread,
cb323159 287 sched_options_t options)
fe8ab488
A
288{
289 run_queue_t rq = dualq_runq_for_thread(processor, thread);
290 boolean_t result;
291
292 result = run_queue_enqueue(rq, thread, options);
293 thread->runq = processor;
294
0a7de745 295 return result;
fe8ab488
A
296}
297
298static boolean_t
299sched_dualq_processor_queue_empty(processor_t processor)
300{
0a7de745 301 return dualq_main_runq(processor)->count == 0 &&
fe8ab488
A
302 dualq_bound_runq(processor)->count == 0;
303}
304
305static ast_t
306sched_dualq_processor_csw_check(processor_t processor)
307{
308 boolean_t has_higher;
309 int pri;
310
d9a64523 311 if (sched_dualq_thread_avoid_processor(processor, current_thread())) {
0a7de745 312 return AST_PREEMPT | AST_URGENT;
d9a64523
A
313 }
314
fe8ab488
A
315 run_queue_t main_runq = dualq_main_runq(processor);
316 run_queue_t bound_runq = dualq_bound_runq(processor);
317
318 assert(processor->active_thread != NULL);
319
320 pri = MAX(main_runq->highq, bound_runq->highq);
321
3e170ce0 322 if (processor->first_timeslice) {
fe8ab488
A
323 has_higher = (pri > processor->current_pri);
324 } else {
325 has_higher = (pri >= processor->current_pri);
326 }
327
328 if (has_higher) {
0a7de745
A
329 if (main_runq->urgency > 0) {
330 return AST_PREEMPT | AST_URGENT;
331 }
332
333 if (bound_runq->urgency > 0) {
334 return AST_PREEMPT | AST_URGENT;
335 }
fe8ab488 336
fe8ab488
A
337 return AST_PREEMPT;
338 }
339
340 return AST_NONE;
341}
342
343static boolean_t
344sched_dualq_processor_queue_has_priority(processor_t processor,
0a7de745
A
345 int priority,
346 boolean_t gte)
fe8ab488 347{
39037602
A
348 run_queue_t main_runq = dualq_main_runq(processor);
349 run_queue_t bound_runq = dualq_bound_runq(processor);
350
39037602 351 int qpri = MAX(main_runq->highq, bound_runq->highq);
fe8ab488 352
0a7de745 353 if (gte) {
fe8ab488 354 return qpri >= priority;
0a7de745 355 } else {
fe8ab488 356 return qpri > priority;
0a7de745 357 }
fe8ab488
A
358}
359
fe8ab488
A
360static int
361sched_dualq_runq_count(processor_t processor)
362{
363 return dualq_main_runq(processor)->count + dualq_bound_runq(processor)->count;
364}
365
366static uint64_t
367sched_dualq_runq_stats_count_sum(processor_t processor)
368{
369 uint64_t bound_sum = dualq_bound_runq(processor)->runq_stats.count_sum;
370
0a7de745 371 if (processor->cpu_id == processor->processor_set->cpu_set_low) {
fe8ab488 372 return bound_sum + dualq_main_runq(processor)->runq_stats.count_sum;
0a7de745 373 } else {
fe8ab488 374 return bound_sum;
0a7de745 375 }
fe8ab488
A
376}
377static int
378sched_dualq_processor_bound_count(processor_t processor)
379{
380 return dualq_bound_runq(processor)->count;
381}
382
383static void
384sched_dualq_processor_queue_shutdown(processor_t processor)
385{
386 processor_set_t pset = processor->processor_set;
387 run_queue_t rq = dualq_main_runq(processor);
388 thread_t thread;
389 queue_head_t tqueue;
390
391 /* We only need to migrate threads if this is the last active processor in the pset */
392 if (pset->online_processor_count > 0) {
393 pset_unlock(pset);
394 return;
395 }
396
397 queue_init(&tqueue);
398
399 while (rq->count > 0) {
400 thread = run_queue_dequeue(rq, SCHED_HEADQ);
39037602 401 enqueue_tail(&tqueue, &thread->runq_links);
fe8ab488
A
402 }
403
404 pset_unlock(pset);
405
39037602 406 qe_foreach_element_safe(thread, &tqueue, runq_links) {
39037602
A
407 remqueue(&thread->runq_links);
408
fe8ab488
A
409 thread_lock(thread);
410
411 thread_setrun(thread, SCHED_TAILQ);
412
413 thread_unlock(thread);
414 }
415}
416
417static boolean_t
418sched_dualq_processor_queue_remove(
0a7de745
A
419 processor_t processor,
420 thread_t thread)
fe8ab488
A
421{
422 run_queue_t rq;
423 processor_set_t pset = processor->processor_set;
424
425 pset_lock(pset);
426
427 rq = dualq_runq_for_thread(processor, thread);
428
429 if (processor == thread->runq) {
430 /*
431 * Thread is on a run queue and we have a lock on
432 * that run queue.
433 */
434 run_queue_remove(rq, thread);
0a7de745 435 } else {
fe8ab488
A
436 /*
437 * The thread left the run queue before we could
438 * lock the run queue.
439 */
440 assert(thread->runq == PROCESSOR_NULL);
441 processor = PROCESSOR_NULL;
442 }
443
444 pset_unlock(pset);
445
0a7de745 446 return processor != PROCESSOR_NULL;
fe8ab488
A
447}
448
449static thread_t
450sched_dualq_steal_thread(processor_set_t pset)
451{
0a7de745
A
452 processor_set_t cset = pset;
453 processor_set_t nset = next_pset(cset);
fe8ab488
A
454 thread_t thread;
455
0a7de745
A
456 while (nset != pset) {
457 pset_unlock(cset);
458 cset = nset;
459 pset_lock(cset);
460
fe8ab488 461 if (cset->pset_runq.count > 0) {
0a7de745 462 /* Need task_restrict logic here */
fe8ab488
A
463 thread = run_queue_dequeue(&cset->pset_runq, SCHED_HEADQ);
464 pset_unlock(cset);
0a7de745 465 return thread;
fe8ab488
A
466 }
467
468 nset = next_pset(cset);
0a7de745 469 }
fe8ab488
A
470
471 pset_unlock(cset);
472
0a7de745 473 return THREAD_NULL;
fe8ab488
A
474}
475
476static void
3e170ce0 477sched_dualq_thread_update_scan(sched_update_scan_context_t scan_context)
fe8ab488
A
478{
479 boolean_t restart_needed = FALSE;
480 processor_t processor = processor_list;
481 processor_set_t pset;
482 thread_t thread;
483 spl_t s;
484
485 /*
486 * We update the threads associated with each processor (bound and idle threads)
487 * and then update the threads in each pset runqueue.
488 */
489
490 do {
491 do {
492 pset = processor->processor_set;
493
494 s = splsched();
495 pset_lock(pset);
496
3e170ce0 497 restart_needed = runq_scan(dualq_bound_runq(processor), scan_context);
fe8ab488
A
498
499 pset_unlock(pset);
500 splx(s);
501
0a7de745 502 if (restart_needed) {
fe8ab488 503 break;
0a7de745 504 }
fe8ab488
A
505
506 thread = processor->idle_thread;
507 if (thread != THREAD_NULL && thread->sched_stamp != sched_tick) {
508 if (thread_update_add_thread(thread) == FALSE) {
509 restart_needed = TRUE;
510 break;
511 }
512 }
513 } while ((processor = processor->processor_list) != NULL);
514
515 /* Ok, we now have a collection of candidates -- fix them. */
516 thread_update_process_threads();
fe8ab488
A
517 } while (restart_needed);
518
519 pset = &pset0;
520
521 do {
522 do {
523 s = splsched();
524 pset_lock(pset);
525
3e170ce0 526 restart_needed = runq_scan(&pset->pset_runq, scan_context);
fe8ab488
A
527
528 pset_unlock(pset);
529 splx(s);
530
0a7de745 531 if (restart_needed) {
fe8ab488 532 break;
0a7de745 533 }
fe8ab488
A
534 } while ((pset = pset->pset_list) != NULL);
535
536 /* Ok, we now have a collection of candidates -- fix them. */
537 thread_update_process_threads();
fe8ab488
A
538 } while (restart_needed);
539}
540
d9a64523
A
541extern int sched_allow_rt_smt;
542
543/* Return true if this thread should not continue running on this processor */
544static bool
545sched_dualq_thread_avoid_processor(processor_t processor, thread_t thread)
546{
0a7de745
A
547 if (thread->bound_processor == processor) {
548 /* Thread is bound here */
549 return false;
550 }
551
d9a64523
A
552 if (processor->processor_primary != processor) {
553 /*
554 * This is a secondary SMT processor. If the primary is running
555 * a realtime thread, only allow realtime threads on the secondary.
556 */
0a7de745
A
557 processor_t primary = processor->processor_primary;
558 if ((primary->current_pri >= BASEPRI_RTQUEUES) && ((thread->sched_pri < BASEPRI_RTQUEUES) || !sched_allow_rt_smt)) {
d9a64523
A
559 return true;
560 }
0a7de745
A
561
562 /* NO_SMT threads are not allowed on secondary processors */
563 if (thread_no_smt(thread)) {
564 return true;
565 }
566
567 if (primary->state == PROCESSOR_RUNNING) {
568 if (processor_active_thread_no_smt(primary)) {
569 /* No threads allowed on secondary if primary has NO_SMT */
570 return true;
571 }
572 }
573 }
574
575 if (processor->processor_secondary != PROCESSOR_NULL) {
576 /*
577 * This is a primary SMT processor. If the secondary is running
578 * a bound thread, the primary may not run a NO_SMT thread.
579 */
580 processor_t secondary = processor->processor_secondary;
581
582 if (secondary->state == PROCESSOR_RUNNING) {
583 if (secondary->current_is_bound && thread_no_smt(thread)) {
584 return true;
585 }
586 }
d9a64523 587 }
fe8ab488 588
d9a64523
A
589 return false;
590}