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/macro_help.h>
46 #include <kern/machine.h>
47 #include <kern/misc_protos.h>
48 #include <kern/processor.h>
49 #include <kern/queue.h>
50 #include <kern/sched.h>
51 #include <kern/sched_prim.h>
52 #include <kern/syscall_subr.h>
53 #include <kern/task.h>
54 #include <kern/thread.h>
57 #include <vm/vm_kern.h>
58 #include <vm/vm_map.h>
62 #include <sys/kdebug.h>
64 #if defined(CONFIG_SCHED_GRRR_CORE)
67 grrr_priority_mapping_init(void);
85 grrr_sorted_list_insert_group(grrr_run_queue_t rq
,
89 grrr_rescale_work(grrr_run_queue_t rq
);
92 grrr_runqueue_init(grrr_run_queue_t runq
);
94 /* Map Mach priorities to ones suitable for proportional sharing */
95 static grrr_proportional_priority_t grrr_priority_mapping
[NRQS
];
97 /* Map each proportional priority to its group */
98 static grrr_group_index_t grrr_group_mapping
[NUM_GRRR_PROPORTIONAL_PRIORITIES
];
100 uint32_t grrr_rescale_tick
;
102 #endif /* defined(CONFIG_SCHED_GRRR_CORE) */
104 #if defined(CONFIG_SCHED_GRRR)
107 sched_grrr_init(void);
110 sched_grrr_timebase_init(void);
113 sched_grrr_processor_init(processor_t processor
);
116 sched_grrr_pset_init(processor_set_t pset
);
119 sched_grrr_maintenance_continuation(void);
122 sched_grrr_choose_thread(processor_t processor
,
127 sched_grrr_steal_thread(processor_set_t pset
);
130 sched_grrr_compute_priority(thread_t thread
);
133 sched_grrr_choose_processor( processor_set_t pset
,
134 processor_t processor
,
138 sched_grrr_processor_enqueue(
139 processor_t processor
,
144 sched_grrr_processor_queue_shutdown(
145 processor_t processor
);
148 sched_grrr_processor_queue_remove(
149 processor_t processor
,
153 sched_grrr_processor_queue_empty(processor_t processor
);
156 sched_grrr_processor_queue_has_priority(processor_t processor
,
161 sched_grrr_priority_is_urgent(int priority
);
164 sched_grrr_processor_csw_check(processor_t processor
);
167 sched_grrr_initial_quantum_size(thread_t thread
);
170 sched_grrr_initial_thread_sched_mode(task_t parent_task
);
173 sched_grrr_can_update_priority(thread_t thread
);
176 sched_grrr_update_priority(thread_t thread
);
179 sched_grrr_lightweight_update_priority(thread_t thread
);
182 sched_grrr_processor_runq_count(processor_t processor
);
185 sched_grrr_processor_runq_stats_count_sum(processor_t processor
);
188 sched_grrr_processor_bound_count(processor_t processor
);
191 sched_grrr_thread_update_scan(sched_update_scan_context_t scan_context
);
193 const struct sched_dispatch_table sched_grrr_dispatch
= {
194 .sched_name
= "grrr",
195 .init
= sched_grrr_init
,
196 .timebase_init
= sched_grrr_timebase_init
,
197 .processor_init
= sched_grrr_processor_init
,
198 .pset_init
= sched_grrr_pset_init
,
199 .maintenance_continuation
= sched_grrr_maintenance_continuation
,
200 .choose_thread
= sched_grrr_choose_thread
,
201 .steal_thread_enabled
= FALSE
,
202 .steal_thread
= sched_grrr_steal_thread
,
203 .compute_timeshare_priority
= sched_grrr_compute_priority
,
204 .choose_processor
= sched_grrr_choose_processor
,
205 .processor_enqueue
= sched_grrr_processor_enqueue
,
206 .processor_queue_shutdown
= sched_grrr_processor_queue_shutdown
,
207 .processor_queue_remove
= sched_grrr_processor_queue_remove
,
208 .processor_queue_empty
= sched_grrr_processor_queue_empty
,
209 .priority_is_urgent
= sched_grrr_priority_is_urgent
,
210 .processor_csw_check
= sched_grrr_processor_csw_check
,
211 .processor_queue_has_priority
= sched_grrr_processor_queue_has_priority
,
212 .initial_quantum_size
= sched_grrr_initial_quantum_size
,
213 .initial_thread_sched_mode
= sched_grrr_initial_thread_sched_mode
,
214 .can_update_priority
= sched_grrr_can_update_priority
,
215 .update_priority
= sched_grrr_update_priority
,
216 .lightweight_update_priority
= sched_grrr_lightweight_update_priority
,
217 .quantum_expire
= sched_default_quantum_expire
,
218 .processor_runq_count
= sched_grrr_processor_runq_count
,
219 .processor_runq_stats_count_sum
= sched_grrr_processor_runq_stats_count_sum
,
220 .processor_bound_count
= sched_grrr_processor_bound_count
,
221 .thread_update_scan
= sched_grrr_thread_update_scan
,
222 .direct_dispatch_to_idle_processors
= TRUE
,
223 .multiple_psets_enabled
= TRUE
,
224 .sched_groups_enabled
= FALSE
,
227 extern int max_unsafe_quanta
;
229 static uint32_t grrr_quantum_us
;
230 static uint32_t grrr_quantum
;
232 static uint64_t sched_grrr_tick_deadline
;
235 sched_grrr_init(void)
237 if (default_preemption_rate
< 1)
238 default_preemption_rate
= 100;
239 grrr_quantum_us
= (1000 * 1000) / default_preemption_rate
;
241 printf("standard grrr timeslicing quantum is %d us\n", grrr_quantum_us
);
243 grrr_priority_mapping_init();
247 sched_grrr_timebase_init(void)
251 /* standard timeslicing quantum */
252 clock_interval_to_absolutetime_interval(
253 grrr_quantum_us
, NSEC_PER_USEC
, &abstime
);
254 assert((abstime
>> 32) == 0 && (uint32_t)abstime
!= 0);
255 grrr_quantum
= (uint32_t)abstime
;
257 thread_depress_time
= 1 * grrr_quantum
;
258 default_timeshare_computation
= grrr_quantum
/ 2;
259 default_timeshare_constraint
= grrr_quantum
;
261 max_unsafe_computation
= max_unsafe_quanta
* grrr_quantum
;
262 sched_safe_duration
= 2 * max_unsafe_quanta
* grrr_quantum
;
267 sched_grrr_processor_init(processor_t processor
)
269 grrr_runqueue_init(&processor
->grrr_runq
);
273 sched_grrr_pset_init(processor_set_t pset __unused
)
278 sched_grrr_maintenance_continuation(void)
280 uint64_t abstime
= mach_absolute_time();
285 * Compute various averages.
289 if (sched_grrr_tick_deadline
== 0)
290 sched_grrr_tick_deadline
= abstime
;
292 clock_deadline_for_periodic_event(10*sched_one_second_interval
, abstime
,
293 &sched_grrr_tick_deadline
);
295 assert_wait_deadline((event_t
)sched_grrr_maintenance_continuation
, THREAD_UNINT
, sched_grrr_tick_deadline
);
296 thread_block((thread_continue_t
)sched_grrr_maintenance_continuation
);
302 sched_grrr_choose_thread(processor_t processor
,
303 int priority __unused
,
304 ast_t reason __unused
)
306 grrr_run_queue_t rq
= &processor
->grrr_runq
;
308 return grrr_select(rq
);
312 sched_grrr_steal_thread(processor_set_t pset
)
316 return (THREAD_NULL
);
321 sched_grrr_compute_priority(thread_t thread
)
323 return thread
->base_pri
;
327 sched_grrr_choose_processor( processor_set_t pset
,
328 processor_t processor
,
331 return choose_processor(pset
, processor
, thread
);
335 sched_grrr_processor_enqueue(
336 processor_t processor
,
338 integer_t options __unused
)
340 grrr_run_queue_t rq
= &processor
->grrr_runq
;
343 result
= grrr_enqueue(rq
, thread
);
345 thread
->runq
= processor
;
351 sched_grrr_processor_queue_shutdown(
352 processor_t processor
)
354 processor_set_t pset
= processor
->processor_set
;
356 queue_head_t tqueue
, bqueue
;
361 while ((thread
= sched_grrr_choose_thread(processor
, IDLEPRI
, AST_NONE
)) != THREAD_NULL
) {
362 if (thread
->bound_processor
== PROCESSOR_NULL
) {
363 enqueue_tail(&tqueue
, (queue_entry_t
)thread
);
365 enqueue_tail(&bqueue
, (queue_entry_t
)thread
);
369 while ((thread
= (thread_t
)(void *)dequeue_head(&bqueue
)) != THREAD_NULL
) {
370 sched_grrr_processor_enqueue(processor
, thread
, SCHED_TAILQ
);
375 while ((thread
= (thread_t
)(void *)dequeue_head(&tqueue
)) != THREAD_NULL
) {
378 thread_setrun(thread
, SCHED_TAILQ
);
380 thread_unlock(thread
);
385 sched_grrr_processor_queue_remove(
386 processor_t processor
,
391 rqlock
= &processor
->processor_set
->sched_lock
;
394 if (processor
== thread
->runq
) {
396 * Thread is on a run queue and we have a lock on
399 grrr_run_queue_t rq
= &processor
->grrr_runq
;
401 grrr_remove(rq
, thread
);
404 * The thread left the run queue before we could
405 * lock the run queue.
407 assert(thread
->runq
== PROCESSOR_NULL
);
408 processor
= PROCESSOR_NULL
;
411 simple_unlock(rqlock
);
413 return (processor
!= PROCESSOR_NULL
);
417 sched_grrr_processor_queue_empty(processor_t processor __unused
)
421 result
= (processor
->grrr_runq
.count
== 0);
427 sched_grrr_processor_queue_has_priority(processor_t processor
,
429 boolean_t gte __unused
)
431 grrr_run_queue_t rq
= &processor
->grrr_runq
;
434 i
= grrr_group_mapping
[grrr_priority_mapping
[priority
]];
435 for ( ; i
< NUM_GRRR_GROUPS
; i
++) {
436 if (rq
->groups
[i
].count
> 0)
443 /* Implement sched_preempt_pri in code */
445 sched_grrr_priority_is_urgent(int priority
)
447 if (priority
<= BASEPRI_FOREGROUND
)
450 if (priority
< MINPRI_KERNEL
)
453 if (priority
>= BASEPRI_PREEMPT
)
460 sched_grrr_processor_csw_check(processor_t processor
)
464 count
= sched_grrr_processor_runq_count(processor
);
475 sched_grrr_initial_quantum_size(thread_t thread __unused
)
481 sched_grrr_initial_thread_sched_mode(task_t parent_task
)
483 if (parent_task
== kernel_task
)
484 return TH_MODE_FIXED
;
486 return TH_MODE_TIMESHARE
;
490 sched_grrr_can_update_priority(thread_t thread __unused
)
496 sched_grrr_update_priority(thread_t thread __unused
)
502 sched_grrr_lightweight_update_priority(thread_t thread __unused
)
508 sched_grrr_processor_runq_count(processor_t processor
)
510 return processor
->grrr_runq
.count
;
514 sched_grrr_processor_runq_stats_count_sum(processor_t processor
)
516 return processor
->grrr_runq
.runq_stats
.count_sum
;
520 sched_grrr_processor_bound_count(__unused processor_t processor
)
526 sched_grrr_thread_update_scan(__unused sched_update_scan_context_t scan_context
)
531 #endif /* defined(CONFIG_SCHED_GRRR) */
533 #if defined(CONFIG_SCHED_GRRR_CORE)
536 grrr_priority_mapping_init(void)
540 /* Map 0->0 up to 10->20 */
541 for (i
=0; i
<= 10; i
++) {
542 grrr_priority_mapping
[i
] = 2*i
;
545 /* Map user priorities 11->33 up to 51 -> 153 */
546 for (i
=11; i
<= 51; i
++) {
547 grrr_priority_mapping
[i
] = 3*i
;
550 /* Map high priorities 52->180 up to 127->255 */
551 for (i
=52; i
<= 127; i
++) {
552 grrr_priority_mapping
[i
] = 128 + i
;
555 for (i
= 0; i
< NUM_GRRR_PROPORTIONAL_PRIORITIES
; i
++) {
559 /* Calculate log(i); */
560 for (j
=0, k
=1; k
<= i
; j
++, k
*= 2);
564 grrr_group_mapping
[i
] = i
>> 2;
570 grrr_intragroup_schedule(grrr_group_t group
)
574 if (group
->count
== 0) {
578 thread
= group
->current_client
;
579 if (thread
== THREAD_NULL
) {
580 thread
= (thread_t
)(void *)queue_first(&group
->clients
);
583 if (1 /* deficit */) {
584 group
->current_client
= (thread_t
)(void *)queue_next((queue_entry_t
)thread
);
585 if (queue_end(&group
->clients
, (queue_entry_t
)group
->current_client
)) {
586 group
->current_client
= (thread_t
)(void *)queue_first(&group
->clients
);
589 thread
= group
->current_client
;
596 grrr_intergroup_schedule(grrr_run_queue_t rq
)
601 if (rq
->count
== 0) {
605 group
= rq
->current_group
;
607 if (group
== GRRR_GROUP_NULL
) {
608 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
611 thread
= grrr_intragroup_schedule(group
);
613 if ((group
->work
>= (UINT32_MAX
-256)) || (rq
->last_rescale_tick
!= grrr_rescale_tick
)) {
614 grrr_rescale_work(rq
);
618 if (queue_end(&rq
->sorted_group_list
, queue_next((queue_entry_t
)group
))) {
619 /* last group, go back to beginning */
620 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
622 grrr_group_t nextgroup
= (grrr_group_t
)queue_next((queue_entry_t
)group
);
623 uint64_t orderleft
, orderright
;
626 * The well-ordering condition for intergroup selection is:
628 * (group->work+1) / (nextgroup->work+1) > (group->weight) / (nextgroup->weight)
630 * Multiply both sides by their denominators to avoid division
633 orderleft
= (group
->work
+ 1) * ((uint64_t)nextgroup
->weight
);
634 orderright
= (nextgroup
->work
+ 1) * ((uint64_t)group
->weight
);
635 if (orderleft
> orderright
) {
638 group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
642 rq
->current_group
= group
;
648 grrr_runqueue_init(grrr_run_queue_t runq
)
650 grrr_group_index_t index
;
654 for (index
= 0; index
< NUM_GRRR_GROUPS
; index
++) {
655 unsigned int prisearch
;
658 prisearch
< NUM_GRRR_PROPORTIONAL_PRIORITIES
;
660 if (grrr_group_mapping
[prisearch
] == index
) {
661 runq
->groups
[index
].minpriority
= (grrr_proportional_priority_t
)prisearch
;
666 runq
->groups
[index
].index
= index
;
668 queue_init(&runq
->groups
[index
].clients
);
669 runq
->groups
[index
].count
= 0;
670 runq
->groups
[index
].weight
= 0;
671 runq
->groups
[index
].work
= 0;
672 runq
->groups
[index
].current_client
= THREAD_NULL
;
675 queue_init(&runq
->sorted_group_list
);
677 runq
->current_group
= GRRR_GROUP_NULL
;
681 grrr_rescale_work(grrr_run_queue_t rq
)
683 grrr_group_index_t index
;
685 /* avoid overflow by scaling by 1/8th */
686 for (index
= 0; index
< NUM_GRRR_GROUPS
; index
++) {
687 rq
->groups
[index
].work
>>= 3;
690 rq
->last_rescale_tick
= grrr_rescale_tick
;
698 grrr_proportional_priority_t gpriority
;
699 grrr_group_index_t gindex
;
702 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
703 gindex
= grrr_group_mapping
[gpriority
];
704 group
= &rq
->groups
[gindex
];
707 thread
->grrr_deficit
= 0;
710 if (group
->count
== 0) {
711 /* Empty group, this is the first client */
712 enqueue_tail(&group
->clients
, (queue_entry_t
)thread
);
714 group
->weight
= gpriority
;
715 group
->current_client
= thread
;
717 /* Insert before the current client */
718 if (group
->current_client
== THREAD_NULL
||
719 queue_first(&group
->clients
) == (queue_entry_t
)group
->current_client
) {
720 enqueue_head(&group
->clients
, (queue_entry_t
)thread
);
722 insque((queue_entry_t
)thread
, queue_prev((queue_entry_t
)group
->current_client
));
724 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
726 group
->weight
+= gpriority
;
728 /* Since there was already a client, this is on the per-processor sorted list already */
729 remqueue((queue_entry_t
)group
);
732 grrr_sorted_list_insert_group(rq
, group
);
735 rq
->weight
+= gpriority
;
741 grrr_select(grrr_run_queue_t rq
)
745 thread
= grrr_intergroup_schedule(rq
);
746 if (thread
!= THREAD_NULL
) {
747 grrr_proportional_priority_t gpriority
;
748 grrr_group_index_t gindex
;
751 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
752 gindex
= grrr_group_mapping
[gpriority
];
753 group
= &rq
->groups
[gindex
];
755 remqueue((queue_entry_t
)thread
);
756 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
758 group
->weight
-= gpriority
;
759 if (group
->current_client
== thread
) {
760 group
->current_client
= THREAD_NULL
;
763 remqueue((queue_entry_t
)group
);
764 if (group
->count
== 0) {
765 if (rq
->current_group
== group
) {
766 rq
->current_group
= GRRR_GROUP_NULL
;
769 /* Need to re-insert in sorted location */
770 grrr_sorted_list_insert_group(rq
, group
);
774 rq
->weight
-= gpriority
;
776 thread
->runq
= PROCESSOR_NULL
;
788 grrr_proportional_priority_t gpriority
;
789 grrr_group_index_t gindex
;
792 gpriority
= grrr_priority_mapping
[thread
->sched_pri
];
793 gindex
= grrr_group_mapping
[gpriority
];
794 group
= &rq
->groups
[gindex
];
796 remqueue((queue_entry_t
)thread
);
797 SCHED_STATS_RUNQ_CHANGE(&rq
->runq_stats
, rq
->count
);
799 group
->weight
-= gpriority
;
800 if (group
->current_client
== thread
) {
801 group
->current_client
= THREAD_NULL
;
804 remqueue((queue_entry_t
)group
);
805 if (group
->count
== 0) {
806 if (rq
->current_group
== group
) {
807 rq
->current_group
= GRRR_GROUP_NULL
;
810 /* Need to re-insert in sorted location */
811 grrr_sorted_list_insert_group(rq
, group
);
815 rq
->weight
-= gpriority
;
817 thread
->runq
= PROCESSOR_NULL
;
821 grrr_sorted_list_insert_group(grrr_run_queue_t rq
,
824 /* Simple insertion sort */
825 if (queue_empty(&rq
->sorted_group_list
)) {
826 enqueue_tail(&rq
->sorted_group_list
, (queue_entry_t
)group
);
828 grrr_group_t search_group
;
830 /* Start searching from the head (heaviest weight) for the first
831 * element less than us, so we can insert before it
833 search_group
= (grrr_group_t
)queue_first(&rq
->sorted_group_list
);
834 while (!queue_end(&rq
->sorted_group_list
, (queue_entry_t
)search_group
) ) {
836 if (search_group
->weight
< group
->weight
) {
837 /* we should be before this */
838 search_group
= (grrr_group_t
)queue_prev((queue_entry_t
)search_group
);
840 } if (search_group
->weight
== group
->weight
) {
841 /* Use group index as a tie breaker */
842 if (search_group
->index
< group
->index
) {
843 search_group
= (grrr_group_t
)queue_prev((queue_entry_t
)search_group
);
848 /* otherwise, our weight is too small, keep going */
849 search_group
= (grrr_group_t
)queue_next((queue_entry_t
)search_group
);
852 if (queue_end(&rq
->sorted_group_list
, (queue_entry_t
)search_group
)) {
853 enqueue_tail(&rq
->sorted_group_list
, (queue_entry_t
)group
);
855 insque((queue_entry_t
)group
, (queue_entry_t
)search_group
);
860 #endif /* defined(CONFIG_SCHED_GRRR_CORE) */