2 * Copyright (c) 2009 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>
31 #include <mach/policy.h>
32 #include <mach/sync_policy.h>
33 #include <mach/thread_act.h>
35 #include <machine/machine_routines.h>
36 #include <machine/sched_param.h>
37 #include <machine/machine_cpu.h>
39 #include <kern/kern_types.h>
40 #include <kern/clock.h>
41 #include <kern/counters.h>
42 #include <kern/cpu_number.h>
43 #include <kern/cpu_data.h>
44 #include <kern/debug.h>
45 #include <kern/lock.h>
46 #include <kern/macro_help.h>
47 #include <kern/machine.h>
48 #include <kern/misc_protos.h>
49 #include <kern/processor.h>
50 #include <kern/queue.h>
51 #include <kern/sched.h>
52 #include <kern/sched_prim.h>
53 #include <kern/syscall_subr.h>
54 #include <kern/task.h>
55 #include <kern/thread.h>
56 #include <kern/wait_queue.h>
59 #include <vm/vm_kern.h>
60 #include <vm/vm_map.h>
64 #include <sys/kdebug.h>
66 #if defined(CONFIG_SCHED_GRRR_CORE)
69 grrr_priority_mapping_init(void);
87 grrr_sorted_list_insert_group(grrr_run_queue_t rq
,
91 grrr_rescale_work(grrr_run_queue_t rq
);
94 grrr_runqueue_init(grrr_run_queue_t runq
);
96 /* Map Mach priorities to ones suitable for proportional sharing */
97 static grrr_proportional_priority_t grrr_priority_mapping
[NRQS
];
99 /* Map each proportional priority to its group */
100 static grrr_group_index_t grrr_group_mapping
[NUM_GRRR_PROPORTIONAL_PRIORITIES
];
102 uint32_t grrr_rescale_tick
;
104 #endif /* defined(CONFIG_SCHED_GRRR_CORE) */
106 #if defined(CONFIG_SCHED_GRRR)
109 sched_grrr_init(void);
112 sched_grrr_timebase_init(void);
115 sched_grrr_processor_init(processor_t processor
);
118 sched_grrr_pset_init(processor_set_t pset
);
121 sched_grrr_maintenance_continuation(void);
124 sched_grrr_choose_thread(processor_t processor
,
128 sched_grrr_steal_thread(processor_set_t pset
);
131 sched_grrr_compute_priority(thread_t thread
,
132 boolean_t override_depress
);
135 sched_grrr_choose_processor( processor_set_t pset
,
136 processor_t processor
,
140 sched_grrr_processor_enqueue(
141 processor_t processor
,
146 sched_grrr_processor_queue_shutdown(
147 processor_t processor
);
150 sched_grrr_processor_queue_remove(
151 processor_t processor
,
155 sched_grrr_processor_queue_empty(processor_t processor
);
158 sched_grrr_processor_queue_has_priority(processor_t processor
,
163 sched_grrr_priority_is_urgent(int priority
);
166 sched_grrr_processor_csw_check(processor_t processor
);
169 sched_grrr_initial_quantum_size(thread_t thread
);
172 sched_grrr_initial_thread_sched_mode(task_t parent_task
);
175 sched_grrr_supports_timeshare_mode(void);
178 sched_grrr_can_update_priority(thread_t thread
);
181 sched_grrr_update_priority(thread_t thread
);
184 sched_grrr_lightweight_update_priority(thread_t thread
);
187 sched_grrr_quantum_expire(thread_t thread
);
190 sched_grrr_should_current_thread_rechoose_processor(processor_t processor
);
193 sched_grrr_processor_runq_count(processor_t processor
);
196 sched_grrr_processor_runq_stats_count_sum(processor_t processor
);
198 const struct sched_dispatch_table sched_grrr_dispatch
= {
200 sched_grrr_timebase_init
,
201 sched_grrr_processor_init
,
202 sched_grrr_pset_init
,
203 sched_grrr_maintenance_continuation
,
204 sched_grrr_choose_thread
,
205 sched_grrr_steal_thread
,
206 sched_grrr_compute_priority
,
207 sched_grrr_choose_processor
,
208 sched_grrr_processor_enqueue
,
209 sched_grrr_processor_queue_shutdown
,
210 sched_grrr_processor_queue_remove
,
211 sched_grrr_processor_queue_empty
,
212 sched_grrr_priority_is_urgent
,
213 sched_grrr_processor_csw_check
,
214 sched_grrr_processor_queue_has_priority
,
215 sched_grrr_initial_quantum_size
,
216 sched_grrr_initial_thread_sched_mode
,
217 sched_grrr_supports_timeshare_mode
,
218 sched_grrr_can_update_priority
,
219 sched_grrr_update_priority
,
220 sched_grrr_lightweight_update_priority
,
221 sched_grrr_quantum_expire
,
222 sched_grrr_should_current_thread_rechoose_processor
,
223 sched_grrr_processor_runq_count
,
224 sched_grrr_processor_runq_stats_count_sum
,
225 sched_grrr_fairshare_init
,
226 sched_grrr_fairshare_runq_count
,
227 sched_grrr_fairshare_runq_stats_count_sum
,
228 sched_grrr_fairshare_enqueue
,
229 sched_grrr_fairshare_dequeue
,
230 sched_grrr_fairshare_queue_remove
,
231 TRUE
/* direct_dispatch_to_idle_processors */
234 extern int max_unsafe_quanta
;
236 static uint32_t grrr_quantum_us
;
237 static uint32_t grrr_quantum
;
239 static uint64_t sched_grrr_tick_deadline
;
242 sched_grrr_init(void)
244 if (default_preemption_rate
< 1)
245 default_preemption_rate
= 100;
246 grrr_quantum_us
= (1000 * 1000) / default_preemption_rate
;
248 printf("standard grrr timeslicing quantum is %d us\n", grrr_quantum_us
);
250 grrr_priority_mapping_init();
254 sched_grrr_timebase_init(void)
258 /* standard timeslicing quantum */
259 clock_interval_to_absolutetime_interval(
260 grrr_quantum_us
, NSEC_PER_USEC
, &abstime
);
261 assert((abstime
>> 32) == 0 && (uint32_t)abstime
!= 0);
262 grrr_quantum
= (uint32_t)abstime
;
264 thread_depress_time
= 1 * grrr_quantum
;
265 default_timeshare_computation
= grrr_quantum
/ 2;
266 default_timeshare_constraint
= grrr_quantum
;
268 max_unsafe_computation
= max_unsafe_quanta
* grrr_quantum
;
269 sched_safe_duration
= 2 * max_unsafe_quanta
* grrr_quantum
;
274 sched_grrr_processor_init(processor_t processor
)
276 grrr_runqueue_init(&processor
->grrr_runq
);
280 sched_grrr_pset_init(processor_set_t pset __unused
)
285 sched_grrr_maintenance_continuation(void)
287 uint64_t abstime
= mach_absolute_time();
292 * Compute various averages.
296 if (sched_grrr_tick_deadline
== 0)
297 sched_grrr_tick_deadline
= abstime
;
299 clock_deadline_for_periodic_event(10*sched_one_second_interval
, abstime
,
300 &sched_grrr_tick_deadline
);
302 assert_wait_deadline((event_t
)sched_grrr_maintenance_continuation
, THREAD_UNINT
, sched_grrr_tick_deadline
);
303 thread_block((thread_continue_t
)sched_grrr_maintenance_continuation
);
309 sched_grrr_choose_thread(processor_t processor
,
310 int priority __unused
)
312 grrr_run_queue_t rq
= &processor
->grrr_runq
;
314 return grrr_select(rq
);
318 sched_grrr_steal_thread(processor_set_t pset
)
322 return (THREAD_NULL
);
327 sched_grrr_compute_priority(thread_t thread
,
328 boolean_t override_depress __unused
)
330 set_sched_pri(thread
, thread
->priority
);
334 sched_grrr_choose_processor( processor_set_t pset
,
335 processor_t processor
,
338 return choose_processor(pset
, processor
, thread
);
342 sched_grrr_processor_enqueue(
343 processor_t processor
,
345 integer_t options __unused
)
347 grrr_run_queue_t rq
= &processor
->grrr_runq
;
350 result
= grrr_enqueue(rq
, thread
);
352 thread
->runq
= processor
;
358 sched_grrr_processor_queue_shutdown(
359 processor_t processor
)
361 processor_set_t pset
= processor
->processor_set
;
363 queue_head_t tqueue
, bqueue
;
368 while ((thread
= sched_grrr_choose_thread(processor
, IDLEPRI
)) != THREAD_NULL
) {
369 if (thread
->bound_processor
== PROCESSOR_NULL
) {
370 enqueue_tail(&tqueue
, (queue_entry_t
)thread
);
372 enqueue_tail(&bqueue
, (queue_entry_t
)thread
);
376 while ((thread
= (thread_t
)dequeue_head(&bqueue
)) != THREAD_NULL
) {
377 sched_grrr_processor_enqueue(processor
, thread
, SCHED_TAILQ
);
382 while ((thread
= (thread_t
)dequeue_head(&tqueue
)) != THREAD_NULL
) {
385 thread_setrun(thread
, SCHED_TAILQ
);
387 thread_unlock(thread
);
392 sched_grrr_processor_queue_remove(
393 processor_t processor
,
398 rqlock
= &processor
->processor_set
->sched_lock
;
401 if (processor
== thread
->runq
) {
403 * Thread is on a run queue and we have a lock on
406 grrr_run_queue_t rq
= &processor
->grrr_runq
;
408 grrr_remove(rq
, thread
);
411 * The thread left the run queue before we could
412 * lock the run queue.
414 assert(thread
->runq
== PROCESSOR_NULL
);
415 processor
= PROCESSOR_NULL
;
418 simple_unlock(rqlock
);
420 return (processor
!= PROCESSOR_NULL
);
424 sched_grrr_processor_queue_empty(processor_t processor __unused
)
428 result
= (processor
->grrr_runq
.count
== 0);
434 sched_grrr_processor_queue_has_priority(processor_t processor
,
436 boolean_t gte __unused
)
438 grrr_run_queue_t rq
= &processor
->grrr_runq
;
441 i
= grrr_group_mapping
[grrr_priority_mapping
[priority
]];
442 for ( ; i
< NUM_GRRR_GROUPS
; i
++) {
443 if (rq
->groups
[i
].count
> 0)
450 /* Implement sched_preempt_pri in code */
452 sched_grrr_priority_is_urgent(int priority
)
454 if (priority
<= BASEPRI_FOREGROUND
)
457 if (priority
< MINPRI_KERNEL
)
460 if (priority
>= BASEPRI_PREEMPT
)
467 sched_grrr_processor_csw_check(processor_t processor
)
471 count
= sched_grrr_processor_runq_count(processor
);
482 sched_grrr_initial_quantum_size(thread_t thread __unused
)
488 sched_grrr_initial_thread_sched_mode(task_t parent_task
)
490 if (parent_task
== kernel_task
)
491 return TH_MODE_FIXED
;
493 return TH_MODE_TIMESHARE
;
497 sched_grrr_supports_timeshare_mode(void)
503 sched_grrr_can_update_priority(thread_t thread __unused
)
509 sched_grrr_update_priority(thread_t thread __unused
)
515 sched_grrr_lightweight_update_priority(thread_t thread __unused
)
521 sched_grrr_quantum_expire(
522 thread_t thread __unused
)
528 sched_grrr_should_current_thread_rechoose_processor(processor_t processor __unused
)
534 sched_grrr_processor_runq_count(processor_t processor
)
536 return processor
->grrr_runq
.count
;
540 sched_grrr_processor_runq_stats_count_sum(processor_t processor
)
542 return processor
->grrr_runq
.runq_stats
.count_sum
;
545 #endif /* defined(CONFIG_SCHED_GRRR) */
547 #if defined(CONFIG_SCHED_GRRR_CORE)
550 grrr_priority_mapping_init(void)
554 /* Map 0->0 up to 10->20 */
555 for (i
=0; i
<= 10; i
++) {
556 grrr_priority_mapping
[i
] = 2*i
;
559 /* Map user priorities 11->33 up to 51 -> 153 */
560 for (i
=11; i
<= 51; i
++) {
561 grrr_priority_mapping
[i
] = 3*i
;
564 /* Map high priorities 52->180 up to 127->255 */
565 for (i
=52; i
<= 127; i
++) {
566 grrr_priority_mapping
[i
] = 128 + i
;
569 for (i
= 0; i
< NUM_GRRR_PROPORTIONAL_PRIORITIES
; i
++) {
573 /* Calculate log(i); */
574 for (j
=0, k
=1; k
<= i
; j
++, k
*= 2);
578 grrr_group_mapping
[i
] = i
>> 2;
584 grrr_intragroup_schedule(grrr_group_t group
)
588 if (group
->count
== 0) {
592 thread
= group
->current_client
;
593 if (thread
== THREAD_NULL
) {
594 thread
= (thread_t
)queue_first(&group
->clients
);
597 if (1 /* deficit */) {
598 group
->current_client
= (thread_t
)queue_next((queue_entry_t
)thread
);
599 if (queue_end(&group
->clients
, (queue_entry_t
)group
->current_client
)) {
600 group
->current_client
= (thread_t
)queue_first(&group
->clients
);
603 thread
= group
->current_client
;
610 grrr_intergroup_schedule(grrr_run_queue_t rq
)
615 if (rq
->count
== 0) {
619 group
= rq
->current_group
;
621 if (group
== GRRR_GROUP_NULL
) {
622 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
625 thread
= grrr_intragroup_schedule(group
);
627 if ((group
->work
>= (UINT32_MAX
-256)) || (rq
->last_rescale_tick
!= grrr_rescale_tick
)) {
628 grrr_rescale_work(rq
);
632 if (queue_end(&rq
->sorted_group_list
, queue_next((queue_entry_t
)group
))) {
633 /* last group, go back to beginning */
634 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
636 grrr_group_t nextgroup
= (grrr_group_t
)queue_next((queue_entry_t
)group
);
637 uint64_t orderleft
, orderright
;
640 * The well-ordering condition for intergroup selection is:
642 * (group->work+1) / (nextgroup->work+1) > (group->weight) / (nextgroup->weight)
644 * Multiply both sides by their denominators to avoid division
647 orderleft
= (group
->work
+ 1) * ((uint64_t)nextgroup
->weight
);
648 orderright
= (nextgroup
->work
+ 1) * ((uint64_t)group
->weight
);
649 if (orderleft
> orderright
) {
652 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
656 rq
->current_group
= group
;
662 grrr_runqueue_init(grrr_run_queue_t runq
)
664 grrr_group_index_t index
;
668 for (index
= 0; index
< NUM_GRRR_GROUPS
; index
++) {
669 unsigned int prisearch
;
672 prisearch
< NUM_GRRR_PROPORTIONAL_PRIORITIES
;
674 if (grrr_group_mapping
[prisearch
] == index
) {
675 runq
->groups
[index
].minpriority
= (grrr_proportional_priority_t
)prisearch
;
680 runq
->groups
[index
].index
= index
;
682 queue_init(&runq
->groups
[index
].clients
);
683 runq
->groups
[index
].count
= 0;
684 runq
->groups
[index
].weight
= 0;
685 runq
->groups
[index
].work
= 0;
686 runq
->groups
[index
].current_client
= THREAD_NULL
;
689 queue_init(&runq
->sorted_group_list
);
691 runq
->current_group
= GRRR_GROUP_NULL
;
695 grrr_rescale_work(grrr_run_queue_t rq
)
697 grrr_group_index_t index
;
699 /* avoid overflow by scaling by 1/8th */
700 for (index
= 0; index
< NUM_GRRR_GROUPS
; index
++) {
701 rq
->groups
[index
].work
>>= 3;
704 rq
->last_rescale_tick
= grrr_rescale_tick
;
712 grrr_proportional_priority_t gpriority
;
713 grrr_group_index_t gindex
;
716 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
717 gindex
= grrr_group_mapping
[gpriority
];
718 group
= &rq
->groups
[gindex
];
721 thread
->grrr_deficit
= 0;
724 if (group
->count
== 0) {
725 /* Empty group, this is the first client */
726 enqueue_tail(&group
->clients
, (queue_entry_t
)thread
);
728 group
->weight
= gpriority
;
729 group
->current_client
= thread
;
731 /* Insert before the current client */
732 if (group
->current_client
== THREAD_NULL
||
733 queue_first(&group
->clients
) == (queue_entry_t
)group
->current_client
) {
734 enqueue_head(&group
->clients
, (queue_entry_t
)thread
);
736 insque((queue_entry_t
)thread
, queue_prev((queue_entry_t
)group
->current_client
));
738 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
740 group
->weight
+= gpriority
;
742 /* Since there was already a client, this is on the per-processor sorted list already */
743 remqueue((queue_entry_t
)group
);
746 grrr_sorted_list_insert_group(rq
, group
);
749 rq
->weight
+= gpriority
;
755 grrr_select(grrr_run_queue_t rq
)
759 thread
= grrr_intergroup_schedule(rq
);
760 if (thread
!= THREAD_NULL
) {
761 grrr_proportional_priority_t gpriority
;
762 grrr_group_index_t gindex
;
765 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
766 gindex
= grrr_group_mapping
[gpriority
];
767 group
= &rq
->groups
[gindex
];
769 remqueue((queue_entry_t
)thread
);
770 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
772 group
->weight
-= gpriority
;
773 if (group
->current_client
== thread
) {
774 group
->current_client
= THREAD_NULL
;
777 remqueue((queue_entry_t
)group
);
778 if (group
->count
== 0) {
779 if (rq
->current_group
== group
) {
780 rq
->current_group
= GRRR_GROUP_NULL
;
783 /* Need to re-insert in sorted location */
784 grrr_sorted_list_insert_group(rq
, group
);
788 rq
->weight
-= gpriority
;
790 thread
->runq
= PROCESSOR_NULL
;
802 grrr_proportional_priority_t gpriority
;
803 grrr_group_index_t gindex
;
806 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
807 gindex
= grrr_group_mapping
[gpriority
];
808 group
= &rq
->groups
[gindex
];
810 remqueue((queue_entry_t
)thread
);
811 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
813 group
->weight
-= gpriority
;
814 if (group
->current_client
== thread
) {
815 group
->current_client
= THREAD_NULL
;
818 remqueue((queue_entry_t
)group
);
819 if (group
->count
== 0) {
820 if (rq
->current_group
== group
) {
821 rq
->current_group
= GRRR_GROUP_NULL
;
824 /* Need to re-insert in sorted location */
825 grrr_sorted_list_insert_group(rq
, group
);
829 rq
->weight
-= gpriority
;
831 thread
->runq
= PROCESSOR_NULL
;
835 grrr_sorted_list_insert_group(grrr_run_queue_t rq
,
838 /* Simple insertion sort */
839 if (queue_empty(&rq
->sorted_group_list
)) {
840 enqueue_tail(&rq
->sorted_group_list
, (queue_entry_t
)group
);
842 grrr_group_t search_group
;
844 /* Start searching from the head (heaviest weight) for the first
845 * element less than us, so we can insert before it
847 search_group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
848 while (!queue_end(&rq
->sorted_group_list
, (queue_entry_t
)search_group
) ) {
850 if (search_group
->weight
< group
->weight
) {
851 /* we should be before this */
852 search_group
= (grrr_group_t
)queue_prev((queue_entry_t
)search_group
);
854 } if (search_group
->weight
== group
->weight
) {
855 /* Use group index as a tie breaker */
856 if (search_group
->index
< group
->index
) {
857 search_group
= (grrr_group_t
)queue_prev((queue_entry_t
)search_group
);
862 /* otherwise, our weight is too small, keep going */
863 search_group
= (grrr_group_t
)queue_next((queue_entry_t
)search_group
);
866 if (queue_end(&rq
->sorted_group_list
, (queue_entry_t
)search_group
)) {
867 enqueue_tail(&rq
->sorted_group_list
, (queue_entry_t
)group
);
869 insque((queue_entry_t
)group
, (queue_entry_t
)search_group
);
874 #endif /* defined(CONFIG_SCHED_GRRR_CORE) */
876 #if defined(CONFIG_SCHED_GRRR) || defined(CONFIG_SCHED_FIXEDPRIORITY)
878 static struct grrr_run_queue fs_grrr_runq
;
879 #define FS_GRRR_RUNQ ((processor_t)-2)
880 decl_simple_lock_data(static,fs_grrr_lock
);
883 sched_grrr_fairshare_init(void)
885 grrr_priority_mapping_init();
887 simple_lock_init(&fs_grrr_lock
, 0);
888 grrr_runqueue_init(&fs_grrr_runq
);
893 sched_grrr_fairshare_runq_count(void)
895 return fs_grrr_runq
.count
;
899 sched_grrr_fairshare_runq_stats_count_sum(void)
901 return fs_grrr_runq
.runq_stats
.count_sum
;
905 sched_grrr_fairshare_enqueue(thread_t thread
)
907 simple_lock(&fs_grrr_lock
);
909 (void)grrr_enqueue(&fs_grrr_runq
, thread
);
911 thread
->runq
= FS_GRRR_RUNQ
;
913 simple_unlock(&fs_grrr_lock
);
916 thread_t
sched_grrr_fairshare_dequeue(void)
920 simple_lock(&fs_grrr_lock
);
921 if (fs_grrr_runq
.count
> 0) {
922 thread
= grrr_select(&fs_grrr_runq
);
924 simple_unlock(&fs_grrr_lock
);
928 simple_unlock(&fs_grrr_lock
);
933 boolean_t
sched_grrr_fairshare_queue_remove(thread_t thread
)
936 simple_lock(&fs_grrr_lock
);
938 if (FS_GRRR_RUNQ
== thread
->runq
) {
939 grrr_remove(&fs_grrr_runq
, thread
);
941 simple_unlock(&fs_grrr_lock
);
946 * The thread left the run queue before we could
947 * lock the run queue.
949 assert(thread
->runq
== PROCESSOR_NULL
);
950 simple_unlock(&fs_grrr_lock
);
955 #endif /* defined(CONFIG_SCHED_GRRR) || defined(CONFIG_SCHED_FIXEDPRIORITY) */