diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2011-06-21 01:29:34 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2011-08-27 11:58:39 -0400 |
commit | 592eaca1409e55407e980f71b2ec604ca3610ba5 (patch) | |
tree | 43cadd8d3f9cc150a7b108696bfabcddcff55650 | |
parent | fb8d6602af1cbc09115544056b872b976c6349c3 (diff) |
Avoid needlessly costly migrations. CONFIG_SCHED_CPU_AFFINITY
Given a choice between several available CPUs (unlinked) on which
to schedule a task, let the scheduler select the CPU closest to
where that task was previously scheduled. Hopefully, this will
reduce cache migration penalties.
Notes: SCHED_CPU_AFFINITY is dependent upon x86 (only x86 is
supported at this time). Also PFair/PD^2 does not make use of
this feature.
Signed-off-by: Andrea Bastoni <bastoni@cs.unc.edu>
-rw-r--r-- | include/litmus/affinity.h | 79 | ||||
-rw-r--r-- | litmus/Kconfig | 19 | ||||
-rw-r--r-- | litmus/Makefile | 1 | ||||
-rw-r--r-- | litmus/affinity.c | 44 | ||||
-rw-r--r-- | litmus/litmus.c | 8 | ||||
-rw-r--r-- | litmus/sched_cedf.c | 33 | ||||
-rw-r--r-- | litmus/sched_gsn_edf.c | 41 |
7 files changed, 222 insertions, 3 deletions
diff --git a/include/litmus/affinity.h b/include/litmus/affinity.h new file mode 100644 index 000000000000..5eee0eaa170d --- /dev/null +++ b/include/litmus/affinity.h | |||
@@ -0,0 +1,79 @@ | |||
1 | #ifndef __LITMUS_AFFINITY_H | ||
2 | #define __LITMUS_AFFINITY_H | ||
3 | |||
4 | #include <linux/cpumask.h> | ||
5 | |||
6 | /* | ||
7 | L1 (instr) = depth 0 | ||
8 | L1 (data) = depth 1 | ||
9 | L2 = depth 2 | ||
10 | L3 = depth 3 | ||
11 | */ | ||
12 | #define NUM_CACHE_LEVELS 4 | ||
13 | |||
14 | struct neighborhood | ||
15 | { | ||
16 | unsigned int size[NUM_CACHE_LEVELS]; | ||
17 | cpumask_var_t neighbors[NUM_CACHE_LEVELS]; | ||
18 | }; | ||
19 | |||
20 | /* topology info is stored redundently in a big array for fast lookups */ | ||
21 | extern struct neighborhood neigh_info[NR_CPUS]; | ||
22 | |||
23 | void init_topology(void); /* called by Litmus module's _init_litmus() */ | ||
24 | |||
25 | /* Works like: | ||
26 | void get_nearest_available_cpu( | ||
27 | cpu_entry_t* nearest, | ||
28 | cpu_entry_t* start, | ||
29 | cpu_entry_t* entries, | ||
30 | int release_master) | ||
31 | |||
32 | Set release_master = -1 for no RM. | ||
33 | |||
34 | We use a macro here to exploit the fact that C-EDF and G-EDF | ||
35 | have similar structures for their cpu_entry_t structs, even though | ||
36 | they do not share a common base-struct. The macro allows us to | ||
37 | avoid code duplication. | ||
38 | |||
39 | TODO: Factor out the job-to-processor linking from C/G-EDF into | ||
40 | a reusable "processor mapping". (See B.B.'s RTSS'09 paper & | ||
41 | dissertation.) | ||
42 | */ | ||
43 | #define get_nearest_available_cpu(nearest, start, entries, release_master) \ | ||
44 | { \ | ||
45 | (nearest) = NULL; \ | ||
46 | if (!(start)->linked) { \ | ||
47 | (nearest) = (start); \ | ||
48 | } else { \ | ||
49 | int __level; \ | ||
50 | int __cpu; \ | ||
51 | struct neighborhood* __neighbors = &neigh_info[(start)->cpu]; \ | ||
52 | \ | ||
53 | for (__level = 0; (__level < NUM_CACHE_LEVELS) && !(nearest); ++__level) { \ | ||
54 | if (__neighbors->size[__level] > 1) { \ | ||
55 | for_each_cpu(__cpu, __neighbors->neighbors[__level]) { \ | ||
56 | if (__cpu != (release_master)) { \ | ||
57 | cpu_entry_t* __entry = &per_cpu((entries), __cpu); \ | ||
58 | if (!__entry->linked) { \ | ||
59 | (nearest) = __entry; \ | ||
60 | break; \ | ||
61 | } \ | ||
62 | } \ | ||
63 | } \ | ||
64 | } else if (__neighbors->size[__level] == 0) { \ | ||
65 | break; \ | ||
66 | } \ | ||
67 | } \ | ||
68 | } \ | ||
69 | \ | ||
70 | if ((nearest)) { \ | ||
71 | TRACE("P%d is closest available CPU to P%d\n", \ | ||
72 | (nearest)->cpu, (start)->cpu); \ | ||
73 | } else { \ | ||
74 | TRACE("Could not find an available CPU close to P%d\n", \ | ||
75 | (start)->cpu); \ | ||
76 | } \ | ||
77 | } | ||
78 | |||
79 | #endif | ||
diff --git a/litmus/Kconfig b/litmus/Kconfig index ad8dc8308cf0..d7fde6f97e14 100644 --- a/litmus/Kconfig +++ b/litmus/Kconfig | |||
@@ -62,6 +62,25 @@ config LITMUS_LOCKING | |||
62 | 62 | ||
63 | endmenu | 63 | endmenu |
64 | 64 | ||
65 | menu "Performance Enhancements" | ||
66 | |||
67 | config SCHED_CPU_AFFINITY | ||
68 | bool "Local Migration Affinity" | ||
69 | depends on X86 | ||
70 | default y | ||
71 | help | ||
72 | Rescheduled tasks prefer CPUs near to their previously used CPU. This | ||
73 | may improve performance through possible preservation of cache affinity. | ||
74 | |||
75 | Warning: May make bugs harder to find since tasks may migrate less often. | ||
76 | |||
77 | NOTES: | ||
78 | * Feature is not utilized by PFair/PD^2. | ||
79 | |||
80 | Say Yes if unsure. | ||
81 | |||
82 | endmenu | ||
83 | |||
65 | menu "Tracing" | 84 | menu "Tracing" |
66 | 85 | ||
67 | config FEATHER_TRACE | 86 | config FEATHER_TRACE |
diff --git a/litmus/Makefile b/litmus/Makefile index ad9936e07b83..7338180f196f 100644 --- a/litmus/Makefile +++ b/litmus/Makefile | |||
@@ -21,6 +21,7 @@ obj-y = sched_plugin.o litmus.o \ | |||
21 | 21 | ||
22 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o | 22 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o |
23 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o | 23 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o |
24 | obj-$(CONFIG_SCHED_CPU_AFFINITY) += affinity.o | ||
24 | 25 | ||
25 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o | 26 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o |
26 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o | 27 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o |
diff --git a/litmus/affinity.c b/litmus/affinity.c new file mode 100644 index 000000000000..9adab7a3bcd7 --- /dev/null +++ b/litmus/affinity.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #include <linux/cpu.h> | ||
2 | |||
3 | #include <litmus/affinity.h> | ||
4 | |||
5 | struct neighborhood neigh_info[NR_CPUS]; | ||
6 | |||
7 | /* called by _init_litmus() */ | ||
8 | void init_topology(void) { | ||
9 | int cpu; | ||
10 | int i; | ||
11 | int chk; | ||
12 | int depth = num_cache_leaves; | ||
13 | |||
14 | if (depth > NUM_CACHE_LEVELS) | ||
15 | depth = NUM_CACHE_LEVELS; | ||
16 | |||
17 | for_each_online_cpu(cpu) { | ||
18 | for (i = 0; i < depth; ++i) { | ||
19 | long unsigned int firstbits; | ||
20 | |||
21 | chk = get_shared_cpu_map((struct cpumask *)&neigh_info[cpu].neighbors[i], cpu, i); | ||
22 | if (chk) { | ||
23 | /* failed */ | ||
24 | neigh_info[cpu].size[i] = 0; | ||
25 | } else { | ||
26 | /* size = num bits in mask */ | ||
27 | neigh_info[cpu].size[i] = | ||
28 | cpumask_weight((struct cpumask *)&neigh_info[cpu].neighbors[i]); | ||
29 | } | ||
30 | firstbits = *neigh_info[cpu].neighbors[i]->bits; | ||
31 | printk("CPU %d has %d neighbors at level %d. (mask = %lx)\n", | ||
32 | cpu, neigh_info[cpu].size[i], i, firstbits); | ||
33 | } | ||
34 | |||
35 | /* set data for non-existent levels */ | ||
36 | for (; i < NUM_CACHE_LEVELS; ++i) { | ||
37 | neigh_info[cpu].size[i] = 0; | ||
38 | |||
39 | printk("CPU %d has %d neighbors at level %d. (mask = %lx)\n", | ||
40 | cpu, neigh_info[cpu].size[i], i, 0lu); | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
diff --git a/litmus/litmus.c b/litmus/litmus.c index bb8c6c7e9dd1..73af6c3010d6 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
@@ -17,6 +17,10 @@ | |||
17 | #include <litmus/litmus_proc.h> | 17 | #include <litmus/litmus_proc.h> |
18 | #include <litmus/sched_trace.h> | 18 | #include <litmus/sched_trace.h> |
19 | 19 | ||
20 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
21 | #include <litmus/affinity.h> | ||
22 | #endif | ||
23 | |||
20 | /* Number of RT tasks that exist in the system */ | 24 | /* Number of RT tasks that exist in the system */ |
21 | atomic_t rt_task_count = ATOMIC_INIT(0); | 25 | atomic_t rt_task_count = ATOMIC_INIT(0); |
22 | static DEFINE_RAW_SPINLOCK(task_transition_lock); | 26 | static DEFINE_RAW_SPINLOCK(task_transition_lock); |
@@ -540,6 +544,10 @@ static int __init _init_litmus(void) | |||
540 | 544 | ||
541 | init_litmus_proc(); | 545 | init_litmus_proc(); |
542 | 546 | ||
547 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
548 | init_topology(); | ||
549 | #endif | ||
550 | |||
543 | return 0; | 551 | return 0; |
544 | } | 552 | } |
545 | 553 | ||
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index 73fe1c442a0d..e29a9fe2a8e8 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -43,6 +43,10 @@ | |||
43 | 43 | ||
44 | #include <litmus/bheap.h> | 44 | #include <litmus/bheap.h> |
45 | 45 | ||
46 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
47 | #include <litmus/affinity.h> | ||
48 | #endif | ||
49 | |||
46 | /* to configure the cluster size */ | 50 | /* to configure the cluster size */ |
47 | #include <litmus/litmus_proc.h> | 51 | #include <litmus/litmus_proc.h> |
48 | #include <linux/uaccess.h> | 52 | #include <linux/uaccess.h> |
@@ -257,6 +261,23 @@ static noinline void requeue(struct task_struct* task) | |||
257 | } | 261 | } |
258 | } | 262 | } |
259 | 263 | ||
264 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
265 | static cpu_entry_t* cedf_get_nearest_available_cpu( | ||
266 | cedf_domain_t *cluster, cpu_entry_t* start) | ||
267 | { | ||
268 | cpu_entry_t* affinity; | ||
269 | |||
270 | get_nearest_available_cpu(affinity, start, cedf_cpu_entries, -1); | ||
271 | |||
272 | /* make sure CPU is in our cluster */ | ||
273 | if (affinity && cpu_isset(affinity->cpu, *cluster->cpu_map)) | ||
274 | return(affinity); | ||
275 | else | ||
276 | return(NULL); | ||
277 | } | ||
278 | #endif | ||
279 | |||
280 | |||
260 | /* check for any necessary preemptions */ | 281 | /* check for any necessary preemptions */ |
261 | static void check_for_preemptions(cedf_domain_t *cluster) | 282 | static void check_for_preemptions(cedf_domain_t *cluster) |
262 | { | 283 | { |
@@ -270,8 +291,20 @@ static void check_for_preemptions(cedf_domain_t *cluster) | |||
270 | task = __take_ready(&cluster->domain); | 291 | task = __take_ready(&cluster->domain); |
271 | TRACE("check_for_preemptions: attempting to link task %d to %d\n", | 292 | TRACE("check_for_preemptions: attempting to link task %d to %d\n", |
272 | task->pid, last->cpu); | 293 | task->pid, last->cpu); |
294 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
295 | { | ||
296 | cpu_entry_t* affinity = | ||
297 | cedf_get_nearest_available_cpu(cluster, | ||
298 | &per_cpu(cedf_cpu_entries, task_cpu(task))); | ||
299 | if(affinity) | ||
300 | last = affinity; | ||
301 | else if(last->linked) | ||
302 | requeue(last->linked); | ||
303 | } | ||
304 | #else | ||
273 | if (last->linked) | 305 | if (last->linked) |
274 | requeue(last->linked); | 306 | requeue(last->linked); |
307 | #endif | ||
275 | link_task_to_cpu(task, last); | 308 | link_task_to_cpu(task, last); |
276 | preempt(last); | 309 | preempt(last); |
277 | } | 310 | } |
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c index 3092797480f8..17926e9fccdc 100644 --- a/litmus/sched_gsn_edf.c +++ b/litmus/sched_gsn_edf.c | |||
@@ -23,6 +23,10 @@ | |||
23 | 23 | ||
24 | #include <litmus/bheap.h> | 24 | #include <litmus/bheap.h> |
25 | 25 | ||
26 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
27 | #include <litmus/affinity.h> | ||
28 | #endif | ||
29 | |||
26 | #include <linux/module.h> | 30 | #include <linux/module.h> |
27 | 31 | ||
28 | /* Overview of GSN-EDF operations. | 32 | /* Overview of GSN-EDF operations. |
@@ -253,21 +257,52 @@ static noinline void requeue(struct task_struct* task) | |||
253 | } | 257 | } |
254 | } | 258 | } |
255 | 259 | ||
260 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
261 | static cpu_entry_t* gsnedf_get_nearest_available_cpu(cpu_entry_t* start) | ||
262 | { | ||
263 | cpu_entry_t* affinity; | ||
264 | |||
265 | get_nearest_available_cpu(affinity, start, gsnedf_cpu_entries, | ||
266 | #ifdef CONFIG_RELEASE_MASTER | ||
267 | gsnedf.release_master | ||
268 | #else | ||
269 | -1 | ||
270 | #endif | ||
271 | ); | ||
272 | |||
273 | return(affinity); | ||
274 | } | ||
275 | #endif | ||
276 | |||
256 | /* check for any necessary preemptions */ | 277 | /* check for any necessary preemptions */ |
257 | static void check_for_preemptions(void) | 278 | static void check_for_preemptions(void) |
258 | { | 279 | { |
259 | struct task_struct *task; | 280 | struct task_struct *task; |
260 | cpu_entry_t* last; | 281 | cpu_entry_t* last; |
261 | 282 | ||
262 | for(last = lowest_prio_cpu(); | 283 | for (last = lowest_prio_cpu(); |
263 | edf_preemption_needed(&gsnedf, last->linked); | 284 | edf_preemption_needed(&gsnedf, last->linked); |
264 | last = lowest_prio_cpu()) { | 285 | last = lowest_prio_cpu()) { |
265 | /* preemption necessary */ | 286 | /* preemption necessary */ |
266 | task = __take_ready(&gsnedf); | 287 | task = __take_ready(&gsnedf); |
267 | TRACE("check_for_preemptions: attempting to link task %d to %d\n", | 288 | TRACE("check_for_preemptions: attempting to link task %d to %d\n", |
268 | task->pid, last->cpu); | 289 | task->pid, last->cpu); |
290 | |||
291 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
292 | { | ||
293 | cpu_entry_t* affinity = | ||
294 | gsnedf_get_nearest_available_cpu( | ||
295 | &per_cpu(gsnedf_cpu_entries, task_cpu(task))); | ||
296 | if (affinity) | ||
297 | last = affinity; | ||
298 | else if (last->linked) | ||
299 | requeue(last->linked); | ||
300 | } | ||
301 | #else | ||
269 | if (last->linked) | 302 | if (last->linked) |
270 | requeue(last->linked); | 303 | requeue(last->linked); |
304 | #endif | ||
305 | |||
271 | link_task_to_cpu(task, last); | 306 | link_task_to_cpu(task, last); |
272 | preempt(last); | 307 | preempt(last); |
273 | } | 308 | } |