]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/sched_amp.c
xnu-6153.121.1.tar.gz
[apple/xnu.git] / osfmk / kern / sched_amp.c
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
53 static thread_t
54 sched_amp_steal_thread(processor_set_t pset);
55
56 static void
57 sched_amp_thread_update_scan(sched_update_scan_context_t scan_context);
58
59 static boolean_t
60 sched_amp_processor_enqueue(processor_t processor, thread_t thread,
61 sched_options_t options);
62
63 static boolean_t
64 sched_amp_processor_queue_remove(processor_t processor, thread_t thread);
65
66 static ast_t
67 sched_amp_processor_csw_check(processor_t processor);
68
69 static boolean_t
70 sched_amp_processor_queue_has_priority(processor_t processor, int priority, boolean_t gte);
71
72 static int
73 sched_amp_runq_count(processor_t processor);
74
75 static boolean_t
76 sched_amp_processor_queue_empty(processor_t processor);
77
78 static uint64_t
79 sched_amp_runq_stats_count_sum(processor_t processor);
80
81 static int
82 sched_amp_processor_bound_count(processor_t processor);
83
84 static void
85 sched_amp_pset_init(processor_set_t pset);
86
87 static void
88 sched_amp_processor_init(processor_t processor);
89
90 static thread_t
91 sched_amp_choose_thread(processor_t processor, int priority, ast_t reason);
92
93 static void
94 sched_amp_processor_queue_shutdown(processor_t processor);
95
96 static sched_mode_t
97 sched_amp_initial_thread_sched_mode(task_t parent_task);
98
99 static processor_t
100 sched_amp_choose_processor(processor_set_t pset, processor_t processor, thread_t thread);
101
102 static bool
103 sched_amp_thread_avoid_processor(processor_t processor, thread_t thread);
104
105 static bool
106 sched_amp_thread_should_yield(processor_t processor, thread_t thread);
107
108 static void
109 sched_amp_thread_group_recommendation_change(struct thread_group *tg, cluster_type_t new_recommendation);
110
111 const 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
163 extern processor_set_t ecore_set;
164 extern processor_set_t pcore_set;
165
166 __attribute__((always_inline))
167 static inline run_queue_t
168 amp_main_runq(processor_t processor)
169 {
170 return &processor->processor_set->pset_runq;
171 }
172
173 __attribute__((always_inline))
174 static inline run_queue_t
175 amp_bound_runq(processor_t processor)
176 {
177 return &processor->runq;
178 }
179
180 __attribute__((always_inline))
181 static inline run_queue_t
182 amp_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
192 static sched_mode_t
193 sched_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
202 static void
203 sched_amp_processor_init(processor_t processor)
204 {
205 run_queue_init(&processor->runq);
206 }
207
208 static void
209 sched_amp_pset_init(processor_set_t pset)
210 {
211 run_queue_init(&pset->pset_runq);
212 }
213
214 static thread_t
215 sched_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
258 static boolean_t
259 sched_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
273 static boolean_t
274 sched_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
284 static bool
285 sched_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
298 static ast_t
299 sched_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
350 static boolean_t
351 sched_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
378 static int
379 sched_amp_runq_count(processor_t processor)
380 {
381 return amp_main_runq(processor)->count + amp_bound_runq(processor)->count;
382 }
383
384 static uint64_t
385 sched_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 }
395 static int
396 sched_amp_processor_bound_count(processor_t processor)
397 {
398 return amp_bound_runq(processor)->count;
399 }
400
401 static void
402 sched_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
435 static boolean_t
436 sched_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 */
471 thread_t
472 sched_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
512 static void
513 sched_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
587 static bool
588 pcores_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 */
608 static bool
609 sched_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
624 static processor_t
625 sched_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
633 again:
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
658 void
659 sched_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
671 extern int32_t sysctl_get_bound_cpuid(void);
672 int32_t
673 sysctl_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
688 extern void sysctl_thread_bind_cpuid(int32_t cpuid);
689 void
690 sysctl_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
706 extern char sysctl_get_bound_cluster_type(void);
707 char
708 sysctl_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
721 extern void sysctl_thread_bind_cluster_type(char cluster_type);
722 void
723 sysctl_thread_bind_cluster_type(char cluster_type)
724 {
725 thread_bind_cluster_type(cluster_type);
726 }
727
728 extern char sysctl_get_task_cluster_type(void);
729 char
730 sysctl_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
744 extern void sysctl_task_set_cluster_type(char cluster_type);
745 void
746 sysctl_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