diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2010-11-10 12:10:49 -0500 |
---|---|---|
committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2010-11-11 17:57:44 -0500 |
commit | fb3df2ec261d8cd6bcb8206d9d985355214d7767 (patch) | |
tree | c41f8818ad4f1b699afbe292d131c1073b4d7c6e | |
parent | 516b6601bb5f71035e8859735a25dea0da4a0211 (diff) |
Implement proper remote preemption support
To date, Litmus has just hooked into the smp_send_reschedule() IPI
handler and marked tasks as having to reschedule to implement remote
preemptions. This was never particularly clean, but so far we got away
with it. However, changes in the underlying Linux, and peculartities
of the ARM code (interrupts enabled before context switch) break this
naive approach. This patch introduces new state-machine based remote
preemption support. By examining the local state before calling
set_tsk_need_resched(), we avoid confusing the underlying Linux
scheduler. Further, this patch avoids sending unncessary IPIs.
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | include/litmus/preempt.h | 164 | ||||
-rw-r--r-- | kernel/sched.c | 3 | ||||
-rw-r--r-- | litmus/Makefile | 1 | ||||
-rw-r--r-- | litmus/budget.c | 3 | ||||
-rw-r--r-- | litmus/preempt.c | 131 | ||||
-rw-r--r-- | litmus/sched_cedf.c | 4 | ||||
-rw-r--r-- | litmus/sched_gsn_edf.c | 19 | ||||
-rw-r--r-- | litmus/sched_litmus.c | 5 | ||||
-rw-r--r-- | litmus/sched_pfair.c | 11 | ||||
-rw-r--r-- | litmus/sched_plugin.c | 45 | ||||
-rw-r--r-- | litmus/sched_psn_edf.c | 4 |
12 files changed, 341 insertions, 51 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 708523b06920..c9ac4fc837ba 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -96,6 +96,7 @@ struct sched_param { | |||
96 | #include <asm/processor.h> | 96 | #include <asm/processor.h> |
97 | 97 | ||
98 | #include <litmus/rt_param.h> | 98 | #include <litmus/rt_param.h> |
99 | #include <litmus/preempt.h> | ||
99 | 100 | ||
100 | struct exec_domain; | 101 | struct exec_domain; |
101 | struct futex_pi_state; | 102 | struct futex_pi_state; |
@@ -2301,6 +2302,7 @@ static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag) | |||
2301 | static inline void set_tsk_need_resched(struct task_struct *tsk) | 2302 | static inline void set_tsk_need_resched(struct task_struct *tsk) |
2302 | { | 2303 | { |
2303 | set_tsk_thread_flag(tsk,TIF_NEED_RESCHED); | 2304 | set_tsk_thread_flag(tsk,TIF_NEED_RESCHED); |
2305 | sched_state_will_schedule(tsk); | ||
2304 | } | 2306 | } |
2305 | 2307 | ||
2306 | static inline void clear_tsk_need_resched(struct task_struct *tsk) | 2308 | static inline void clear_tsk_need_resched(struct task_struct *tsk) |
diff --git a/include/litmus/preempt.h b/include/litmus/preempt.h new file mode 100644 index 000000000000..260c6fe17986 --- /dev/null +++ b/include/litmus/preempt.h | |||
@@ -0,0 +1,164 @@ | |||
1 | #ifndef LITMUS_PREEMPT_H | ||
2 | #define LITMUS_PREEMPT_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <linux/cache.h> | ||
6 | #include <linux/percpu.h> | ||
7 | #include <asm/atomic.h> | ||
8 | |||
9 | #include <litmus/debug_trace.h> | ||
10 | |||
11 | extern DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, resched_state); | ||
12 | |||
13 | #ifdef CONFIG_DEBUG_KERNEL | ||
14 | const char* sched_state_name(int s); | ||
15 | #define TRACE_STATE(fmt, args...) TRACE("SCHED_STATE " fmt, args) | ||
16 | #else | ||
17 | #define TRACE_STATE(fmt, args...) /* ignore */ | ||
18 | #endif | ||
19 | |||
20 | #define VERIFY_SCHED_STATE(x) \ | ||
21 | do { int __s = get_sched_state(); \ | ||
22 | if ((__s & (x)) == 0) \ | ||
23 | TRACE_STATE("INVALID s=0x%x (%s) not " \ | ||
24 | "in 0x%x (%s) [%s]\n", \ | ||
25 | __s, sched_state_name(__s), \ | ||
26 | (x), #x, __FUNCTION__); \ | ||
27 | } while (0); | ||
28 | |||
29 | #define TRACE_SCHED_STATE_CHANGE(x, y, cpu) \ | ||
30 | TRACE_STATE("[P%d] 0x%x (%s) -> 0x%x (%s)\n", \ | ||
31 | cpu, (x), sched_state_name(x), \ | ||
32 | (y), sched_state_name(y)) | ||
33 | |||
34 | |||
35 | typedef enum scheduling_state { | ||
36 | TASK_SCHEDULED = (1 << 0), /* The currently scheduled task is the one that | ||
37 | * should be scheduled, and the processor does not | ||
38 | * plan to invoke schedule(). */ | ||
39 | SHOULD_SCHEDULE = (1 << 1), /* A remote processor has determined that the | ||
40 | * processor should reschedule, but this has not | ||
41 | * been communicated yet (IPI still pending). */ | ||
42 | WILL_SCHEDULE = (1 << 2), /* The processor has noticed that it has to | ||
43 | * reschedule and will do so shortly. */ | ||
44 | TASK_PICKED = (1 << 3), /* The processor is currently executing schedule(), | ||
45 | * has selected a new task to schedule, but has not | ||
46 | * yet performed the actual context switch. */ | ||
47 | PICKED_WRONG_TASK = (1 << 4), /* The processor has not yet performed the context | ||
48 | * switch, but a remote processor has already | ||
49 | * determined that a higher-priority task became | ||
50 | * eligible after the task was picked. */ | ||
51 | } sched_state_t; | ||
52 | |||
53 | static inline sched_state_t get_sched_state_on(int cpu) | ||
54 | { | ||
55 | return atomic_read(&per_cpu(resched_state, cpu)); | ||
56 | } | ||
57 | |||
58 | static inline sched_state_t get_sched_state(void) | ||
59 | { | ||
60 | return atomic_read(&__get_cpu_var(resched_state)); | ||
61 | } | ||
62 | |||
63 | static inline int is_in_sched_state(int possible_states) | ||
64 | { | ||
65 | return get_sched_state() & possible_states; | ||
66 | } | ||
67 | |||
68 | static inline int cpu_is_in_sched_state(int cpu, int possible_states) | ||
69 | { | ||
70 | return get_sched_state_on(cpu) & possible_states; | ||
71 | } | ||
72 | |||
73 | static inline void set_sched_state(sched_state_t s) | ||
74 | { | ||
75 | TRACE_SCHED_STATE_CHANGE(get_sched_state(), s, smp_processor_id()); | ||
76 | atomic_set(&__get_cpu_var(resched_state), s); | ||
77 | } | ||
78 | |||
79 | static inline int sched_state_transition(sched_state_t from, sched_state_t to) | ||
80 | { | ||
81 | sched_state_t old_state; | ||
82 | |||
83 | old_state = atomic_cmpxchg(&__get_cpu_var(resched_state), from, to); | ||
84 | if (old_state == from) { | ||
85 | TRACE_SCHED_STATE_CHANGE(from, to, smp_processor_id()); | ||
86 | return 1; | ||
87 | } else | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static inline int sched_state_transition_on(int cpu, | ||
92 | sched_state_t from, | ||
93 | sched_state_t to) | ||
94 | { | ||
95 | sched_state_t old_state; | ||
96 | |||
97 | old_state = atomic_cmpxchg(&per_cpu(resched_state, cpu), from, to); | ||
98 | if (old_state == from) { | ||
99 | TRACE_SCHED_STATE_CHANGE(from, to, cpu); | ||
100 | return 1; | ||
101 | } else | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | /* Plugins must call this function after they have decided which job to | ||
106 | * schedule next. IMPORTANT: this function must be called while still holding | ||
107 | * the lock that is used to serialize scheduling decisions. | ||
108 | * | ||
109 | * (Ideally, we would like to use runqueue locks for this purpose, but that | ||
110 | * would lead to deadlocks with the migration code.) | ||
111 | */ | ||
112 | static inline void sched_state_task_picked(void) | ||
113 | { | ||
114 | VERIFY_SCHED_STATE(WILL_SCHEDULE); | ||
115 | |||
116 | /* WILL_SCHEDULE has only a local tansition => simple store is ok */ | ||
117 | set_sched_state(TASK_PICKED); | ||
118 | } | ||
119 | |||
120 | static inline void sched_state_entered_schedule(void) | ||
121 | { | ||
122 | /* Update state for the case that we entered schedule() not due to | ||
123 | * set_tsk_need_resched() */ | ||
124 | set_sched_state(WILL_SCHEDULE); | ||
125 | } | ||
126 | |||
127 | /* Called by schedule() to check if the scheduling decision is still valid | ||
128 | * after a context switch. Returns 1 if the CPU needs to reschdule. */ | ||
129 | static inline int sched_state_validate_switch(void) | ||
130 | { | ||
131 | int left_state_ok = 0; | ||
132 | |||
133 | VERIFY_SCHED_STATE(PICKED_WRONG_TASK | TASK_PICKED); | ||
134 | |||
135 | if (is_in_sched_state(TASK_PICKED)) { | ||
136 | /* Might be good; let's try to transition out of this | ||
137 | * state. This must be done atomically since remote processors | ||
138 | * may try to change the state, too. */ | ||
139 | left_state_ok = sched_state_transition(TASK_PICKED, TASK_SCHEDULED); | ||
140 | } | ||
141 | |||
142 | if (!left_state_ok) { | ||
143 | /* We raced with a higher-priority task arrival => not | ||
144 | * valid. The CPU needs to reschedule. */ | ||
145 | set_sched_state(WILL_SCHEDULE); | ||
146 | return 1; | ||
147 | } else | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /* State transition events. See litmus/preempt.c for details. */ | ||
152 | void sched_state_will_schedule(struct task_struct* tsk); | ||
153 | void sched_state_ipi(void); | ||
154 | /* Cause a CPU (remote or local) to reschedule. */ | ||
155 | void litmus_reschedule(int cpu); | ||
156 | void litmus_reschedule_local(void); | ||
157 | |||
158 | #ifdef CONFIG_DEBUG_KERNEL | ||
159 | void sched_state_plugin_check(void); | ||
160 | #else | ||
161 | #define sched_state_plugin_check() /* no check */ | ||
162 | #endif | ||
163 | |||
164 | #endif | ||
diff --git a/kernel/sched.c b/kernel/sched.c index a9dd6f96c731..60fbae0c747c 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -3794,6 +3794,7 @@ asmlinkage void __sched schedule(void) | |||
3794 | 3794 | ||
3795 | need_resched: | 3795 | need_resched: |
3796 | preempt_disable(); | 3796 | preempt_disable(); |
3797 | sched_state_entered_schedule(); | ||
3797 | cpu = smp_processor_id(); | 3798 | cpu = smp_processor_id(); |
3798 | rq = cpu_rq(cpu); | 3799 | rq = cpu_rq(cpu); |
3799 | rcu_note_context_switch(cpu); | 3800 | rcu_note_context_switch(cpu); |
@@ -3872,7 +3873,7 @@ need_resched_nonpreemptible: | |||
3872 | 3873 | ||
3873 | post_schedule(rq); | 3874 | post_schedule(rq); |
3874 | 3875 | ||
3875 | if (unlikely(reacquire_kernel_lock(prev))) | 3876 | if (sched_state_validate_switch() || unlikely(reacquire_kernel_lock(prev))) |
3876 | goto need_resched_nonpreemptible; | 3877 | goto need_resched_nonpreemptible; |
3877 | 3878 | ||
3878 | preempt_enable_no_resched(); | 3879 | preempt_enable_no_resched(); |
diff --git a/litmus/Makefile b/litmus/Makefile index 7bd1abdcb84a..b7366b530749 100644 --- a/litmus/Makefile +++ b/litmus/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y = sched_plugin.o litmus.o \ | 5 | obj-y = sched_plugin.o litmus.o \ |
6 | preempt.o \ | ||
6 | litmus_proc.o \ | 7 | litmus_proc.o \ |
7 | budget.o \ | 8 | budget.o \ |
8 | jobs.o \ | 9 | jobs.o \ |
diff --git a/litmus/budget.c b/litmus/budget.c index f6f5ca81c9d6..310e9a3d4172 100644 --- a/litmus/budget.c +++ b/litmus/budget.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <linux/hrtimer.h> | 3 | #include <linux/hrtimer.h> |
4 | 4 | ||
5 | #include <litmus/litmus.h> | 5 | #include <litmus/litmus.h> |
6 | #include <litmus/preempt.h> | ||
6 | 7 | ||
7 | struct enforcement_timer { | 8 | struct enforcement_timer { |
8 | /* The enforcement timer is used to accurately police | 9 | /* The enforcement timer is used to accurately police |
@@ -24,7 +25,7 @@ static enum hrtimer_restart on_enforcement_timeout(struct hrtimer *timer) | |||
24 | TRACE("enforcement timer fired.\n"); | 25 | TRACE("enforcement timer fired.\n"); |
25 | et->armed = 0; | 26 | et->armed = 0; |
26 | /* activate scheduler */ | 27 | /* activate scheduler */ |
27 | set_tsk_need_resched(current); | 28 | litmus_reschedule_local(); |
28 | local_irq_restore(flags); | 29 | local_irq_restore(flags); |
29 | 30 | ||
30 | return HRTIMER_NORESTART; | 31 | return HRTIMER_NORESTART; |
diff --git a/litmus/preempt.c b/litmus/preempt.c new file mode 100644 index 000000000000..ebe2e3461895 --- /dev/null +++ b/litmus/preempt.c | |||
@@ -0,0 +1,131 @@ | |||
1 | #include <linux/sched.h> | ||
2 | |||
3 | #include <litmus/litmus.h> | ||
4 | #include <litmus/preempt.h> | ||
5 | |||
6 | /* The rescheduling state of each processor. | ||
7 | */ | ||
8 | DEFINE_PER_CPU_SHARED_ALIGNED(atomic_t, resched_state); | ||
9 | |||
10 | void sched_state_will_schedule(struct task_struct* tsk) | ||
11 | { | ||
12 | /* Litmus hack: we only care about processor-local invocations of | ||
13 | * set_tsk_need_resched(). We can't reliably set the flag remotely | ||
14 | * since it might race with other updates to the scheduling state. We | ||
15 | * can't rely on the runqueue lock protecting updates to the sched | ||
16 | * state since processors do not acquire the runqueue locks for all | ||
17 | * updates to the sched state (to avoid acquiring two runqueue locks at | ||
18 | * the same time). Further, if tsk is residing on a remote processor, | ||
19 | * then that processor doesn't actually know yet that it is going to | ||
20 | * reschedule; it still must receive an IPI (unless a local invocation | ||
21 | * races). | ||
22 | */ | ||
23 | if (likely(task_cpu(tsk) == smp_processor_id())) { | ||
24 | VERIFY_SCHED_STATE(TASK_SCHEDULED | SHOULD_SCHEDULE | TASK_PICKED | WILL_SCHEDULE); | ||
25 | if (is_in_sched_state(TASK_PICKED | PICKED_WRONG_TASK)) | ||
26 | set_sched_state(PICKED_WRONG_TASK); | ||
27 | else | ||
28 | set_sched_state(WILL_SCHEDULE); | ||
29 | } else | ||
30 | /* Litmus tasks should never be subject to a remote | ||
31 | * set_tsk_need_resched(). */ | ||
32 | BUG_ON(is_realtime(tsk)); | ||
33 | TRACE_TASK(tsk, "set_tsk_need_resched() ret:%p\n", | ||
34 | __builtin_return_address(0)); | ||
35 | } | ||
36 | |||
37 | /* Called by the IPI handler after another CPU called smp_send_resched(). */ | ||
38 | void sched_state_ipi(void) | ||
39 | { | ||
40 | /* If the IPI was slow, we might be in any state right now. The IPI is | ||
41 | * only meaningful if we are in SHOULD_SCHEDULE. */ | ||
42 | if (is_in_sched_state(SHOULD_SCHEDULE)) { | ||
43 | /* Cause scheduler to be invoked. | ||
44 | * This will cause a transition to WILL_SCHEDULE. */ | ||
45 | set_tsk_need_resched(current); | ||
46 | TRACE_STATE("IPI -> set_tsk_need_resched(%s/%d)\n", | ||
47 | current->comm, current->pid); | ||
48 | } else { | ||
49 | /* ignore */ | ||
50 | TRACE_STATE("ignoring IPI in state %x (%s)\n", | ||
51 | get_sched_state(), | ||
52 | sched_state_name(get_sched_state())); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /* Called by plugins to cause a CPU to reschedule. IMPORTANT: the caller must | ||
57 | * hold the lock that is used to serialize scheduling decisions. */ | ||
58 | void litmus_reschedule(int cpu) | ||
59 | { | ||
60 | int picked_transition_ok = 0; | ||
61 | int scheduled_transition_ok = 0; | ||
62 | |||
63 | /* The (remote) CPU could be in any state. */ | ||
64 | |||
65 | /* The critical states are TASK_PICKED and TASK_SCHEDULED, as the CPU | ||
66 | * is not aware of the need to reschedule at this point. */ | ||
67 | |||
68 | /* is a context switch in progress? */ | ||
69 | if (cpu_is_in_sched_state(cpu, TASK_PICKED)) | ||
70 | picked_transition_ok = sched_state_transition_on( | ||
71 | cpu, TASK_PICKED, PICKED_WRONG_TASK); | ||
72 | |||
73 | if (!picked_transition_ok && | ||
74 | cpu_is_in_sched_state(cpu, TASK_SCHEDULED)) { | ||
75 | /* We either raced with the end of the context switch, or the | ||
76 | * CPU was in TASK_SCHEDULED anyway. */ | ||
77 | scheduled_transition_ok = sched_state_transition_on( | ||
78 | cpu, TASK_SCHEDULED, SHOULD_SCHEDULE); | ||
79 | } | ||
80 | |||
81 | /* If the CPU was in state TASK_SCHEDULED, then we need to cause the | ||
82 | * scheduler to be invoked. */ | ||
83 | if (scheduled_transition_ok) { | ||
84 | if (smp_processor_id() == cpu) | ||
85 | set_tsk_need_resched(current); | ||
86 | else | ||
87 | smp_send_reschedule(cpu); | ||
88 | } | ||
89 | |||
90 | TRACE_STATE("%s picked-ok:%d sched-ok:%d\n", | ||
91 | __FUNCTION__, | ||
92 | picked_transition_ok, | ||
93 | scheduled_transition_ok); | ||
94 | } | ||
95 | |||
96 | void litmus_reschedule_local(void) | ||
97 | { | ||
98 | if (is_in_sched_state(TASK_PICKED)) | ||
99 | set_sched_state(PICKED_WRONG_TASK); | ||
100 | else if (is_in_sched_state(TASK_SCHEDULED | SHOULD_SCHEDULE)) { | ||
101 | set_sched_state(WILL_SCHEDULE); | ||
102 | set_tsk_need_resched(current); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | #ifdef CONFIG_DEBUG_KERNEL | ||
107 | |||
108 | void sched_state_plugin_check(void) | ||
109 | { | ||
110 | if (!is_in_sched_state(TASK_PICKED | PICKED_WRONG_TASK)) { | ||
111 | TRACE("!!!! plugin did not call sched_state_task_picked()!" | ||
112 | "Calling sched_state_task_picked() is mandatory---fix this.\n"); | ||
113 | set_sched_state(TASK_PICKED); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | #define NAME_CHECK(x) case x: return #x | ||
118 | const char* sched_state_name(int s) | ||
119 | { | ||
120 | switch (s) { | ||
121 | NAME_CHECK(TASK_SCHEDULED); | ||
122 | NAME_CHECK(SHOULD_SCHEDULE); | ||
123 | NAME_CHECK(WILL_SCHEDULE); | ||
124 | NAME_CHECK(TASK_PICKED); | ||
125 | NAME_CHECK(PICKED_WRONG_TASK); | ||
126 | default: | ||
127 | return "UNKNOWN"; | ||
128 | }; | ||
129 | } | ||
130 | |||
131 | #endif | ||
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index a729d97535e9..615560f21d60 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #include <litmus/litmus.h> | 35 | #include <litmus/litmus.h> |
36 | #include <litmus/jobs.h> | 36 | #include <litmus/jobs.h> |
37 | #include <litmus/preempt.h> | ||
37 | #include <litmus/sched_plugin.h> | 38 | #include <litmus/sched_plugin.h> |
38 | #include <litmus/edf_common.h> | 39 | #include <litmus/edf_common.h> |
39 | #include <litmus/sched_trace.h> | 40 | #include <litmus/sched_trace.h> |
@@ -341,7 +342,7 @@ static void cedf_tick(struct task_struct* t) | |||
341 | /* np tasks will be preempted when they become | 342 | /* np tasks will be preempted when they become |
342 | * preemptable again | 343 | * preemptable again |
343 | */ | 344 | */ |
344 | set_tsk_need_resched(t); | 345 | litmus_reschedule_local(); |
345 | set_will_schedule(); | 346 | set_will_schedule(); |
346 | TRACE("cedf_scheduler_tick: " | 347 | TRACE("cedf_scheduler_tick: " |
347 | "%d is preemptable " | 348 | "%d is preemptable " |
@@ -466,6 +467,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
466 | if (exists) | 467 | if (exists) |
467 | next = prev; | 468 | next = prev; |
468 | 469 | ||
470 | sched_state_task_picked(); | ||
469 | raw_spin_unlock(&cluster->lock); | 471 | raw_spin_unlock(&cluster->lock); |
470 | 472 | ||
471 | #ifdef WANT_ALL_SCHED_EVENTS | 473 | #ifdef WANT_ALL_SCHED_EVENTS |
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c index e101768740ad..f0337cfd8631 100644 --- a/litmus/sched_gsn_edf.c +++ b/litmus/sched_gsn_edf.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <litmus/edf_common.h> | 18 | #include <litmus/edf_common.h> |
19 | #include <litmus/sched_trace.h> | 19 | #include <litmus/sched_trace.h> |
20 | 20 | ||
21 | #include <litmus/preempt.h> | ||
22 | |||
21 | #include <litmus/bheap.h> | 23 | #include <litmus/bheap.h> |
22 | 24 | ||
23 | #include <linux/module.h> | 25 | #include <linux/module.h> |
@@ -95,21 +97,12 @@ typedef struct { | |||
95 | int cpu; | 97 | int cpu; |
96 | struct task_struct* linked; /* only RT tasks */ | 98 | struct task_struct* linked; /* only RT tasks */ |
97 | struct task_struct* scheduled; /* only RT tasks */ | 99 | struct task_struct* scheduled; /* only RT tasks */ |
98 | atomic_t will_schedule; /* prevent unneeded IPIs */ | ||
99 | struct bheap_node* hn; | 100 | struct bheap_node* hn; |
100 | } cpu_entry_t; | 101 | } cpu_entry_t; |
101 | DEFINE_PER_CPU(cpu_entry_t, gsnedf_cpu_entries); | 102 | DEFINE_PER_CPU(cpu_entry_t, gsnedf_cpu_entries); |
102 | 103 | ||
103 | cpu_entry_t* gsnedf_cpus[NR_CPUS]; | 104 | cpu_entry_t* gsnedf_cpus[NR_CPUS]; |
104 | 105 | ||
105 | #define set_will_schedule() \ | ||
106 | (atomic_set(&__get_cpu_var(gsnedf_cpu_entries).will_schedule, 1)) | ||
107 | #define clear_will_schedule() \ | ||
108 | (atomic_set(&__get_cpu_var(gsnedf_cpu_entries).will_schedule, 0)) | ||
109 | #define test_will_schedule(cpu) \ | ||
110 | (atomic_read(&per_cpu(gsnedf_cpu_entries, cpu).will_schedule)) | ||
111 | |||
112 | |||
113 | /* the cpus queue themselves according to priority in here */ | 106 | /* the cpus queue themselves according to priority in here */ |
114 | static struct bheap_node gsnedf_heap_node[NR_CPUS]; | 107 | static struct bheap_node gsnedf_heap_node[NR_CPUS]; |
115 | static struct bheap gsnedf_cpu_heap; | 108 | static struct bheap gsnedf_cpu_heap; |
@@ -341,8 +334,7 @@ static void gsnedf_tick(struct task_struct* t) | |||
341 | /* np tasks will be preempted when they become | 334 | /* np tasks will be preempted when they become |
342 | * preemptable again | 335 | * preemptable again |
343 | */ | 336 | */ |
344 | set_tsk_need_resched(t); | 337 | litmus_reschedule_local(); |
345 | set_will_schedule(); | ||
346 | TRACE("gsnedf_scheduler_tick: " | 338 | TRACE("gsnedf_scheduler_tick: " |
347 | "%d is preemptable " | 339 | "%d is preemptable " |
348 | " => FORCE_RESCHED\n", t->pid); | 340 | " => FORCE_RESCHED\n", t->pid); |
@@ -391,7 +383,6 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
391 | #endif | 383 | #endif |
392 | 384 | ||
393 | raw_spin_lock(&gsnedf_lock); | 385 | raw_spin_lock(&gsnedf_lock); |
394 | clear_will_schedule(); | ||
395 | 386 | ||
396 | /* sanity checking */ | 387 | /* sanity checking */ |
397 | BUG_ON(entry->scheduled && entry->scheduled != prev); | 388 | BUG_ON(entry->scheduled && entry->scheduled != prev); |
@@ -473,6 +464,8 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
473 | if (exists) | 464 | if (exists) |
474 | next = prev; | 465 | next = prev; |
475 | 466 | ||
467 | sched_state_task_picked(); | ||
468 | |||
476 | raw_spin_unlock(&gsnedf_lock); | 469 | raw_spin_unlock(&gsnedf_lock); |
477 | 470 | ||
478 | #ifdef WANT_ALL_SCHED_EVENTS | 471 | #ifdef WANT_ALL_SCHED_EVENTS |
@@ -780,7 +773,6 @@ static long gsnedf_activate_plugin(void) | |||
780 | for_each_online_cpu(cpu) { | 773 | for_each_online_cpu(cpu) { |
781 | entry = &per_cpu(gsnedf_cpu_entries, cpu); | 774 | entry = &per_cpu(gsnedf_cpu_entries, cpu); |
782 | bheap_node_init(&entry->hn, entry); | 775 | bheap_node_init(&entry->hn, entry); |
783 | atomic_set(&entry->will_schedule, 0); | ||
784 | entry->linked = NULL; | 776 | entry->linked = NULL; |
785 | entry->scheduled = NULL; | 777 | entry->scheduled = NULL; |
786 | #ifdef CONFIG_RELEASE_MASTER | 778 | #ifdef CONFIG_RELEASE_MASTER |
@@ -829,7 +821,6 @@ static int __init init_gsn_edf(void) | |||
829 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | 821 | for (cpu = 0; cpu < NR_CPUS; cpu++) { |
830 | entry = &per_cpu(gsnedf_cpu_entries, cpu); | 822 | entry = &per_cpu(gsnedf_cpu_entries, cpu); |
831 | gsnedf_cpus[cpu] = entry; | 823 | gsnedf_cpus[cpu] = entry; |
832 | atomic_set(&entry->will_schedule, 0); | ||
833 | entry->cpu = cpu; | 824 | entry->cpu = cpu; |
834 | entry->hn = &gsnedf_heap_node[cpu]; | 825 | entry->hn = &gsnedf_heap_node[cpu]; |
835 | bheap_node_init(&entry->hn, entry); | 826 | bheap_node_init(&entry->hn, entry); |
diff --git a/litmus/sched_litmus.c b/litmus/sched_litmus.c index 65873152e68f..e6952896dc4b 100644 --- a/litmus/sched_litmus.c +++ b/litmus/sched_litmus.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <litmus/litmus.h> | 3 | #include <litmus/litmus.h> |
4 | #include <litmus/budget.h> | 4 | #include <litmus/budget.h> |
5 | #include <litmus/sched_plugin.h> | 5 | #include <litmus/sched_plugin.h> |
6 | #include <litmus/preempt.h> | ||
6 | 7 | ||
7 | static void update_time_litmus(struct rq *rq, struct task_struct *p) | 8 | static void update_time_litmus(struct rq *rq, struct task_struct *p) |
8 | { | 9 | { |
@@ -52,6 +53,8 @@ litmus_schedule(struct rq *rq, struct task_struct *prev) | |||
52 | /* let the plugin schedule */ | 53 | /* let the plugin schedule */ |
53 | next = litmus->schedule(prev); | 54 | next = litmus->schedule(prev); |
54 | 55 | ||
56 | sched_state_plugin_check(); | ||
57 | |||
55 | /* check if a global plugin pulled a task from a different RQ */ | 58 | /* check if a global plugin pulled a task from a different RQ */ |
56 | if (next && task_rq(next) != rq) { | 59 | if (next && task_rq(next) != rq) { |
57 | /* we need to migrate the task */ | 60 | /* we need to migrate the task */ |
@@ -198,7 +201,7 @@ static void yield_task_litmus(struct rq *rq) | |||
198 | * then determine if a preemption is still required. | 201 | * then determine if a preemption is still required. |
199 | */ | 202 | */ |
200 | clear_exit_np(current); | 203 | clear_exit_np(current); |
201 | set_tsk_need_resched(current); | 204 | litmus_reschedule_local(); |
202 | } | 205 | } |
203 | 206 | ||
204 | /* Plugins are responsible for this. | 207 | /* Plugins are responsible for this. |
diff --git a/litmus/sched_pfair.c b/litmus/sched_pfair.c index ea77d3295290..c7d5cf7aa2b3 100644 --- a/litmus/sched_pfair.c +++ b/litmus/sched_pfair.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | #include <litmus/litmus.h> | 17 | #include <litmus/litmus.h> |
18 | #include <litmus/jobs.h> | 18 | #include <litmus/jobs.h> |
19 | #include <litmus/preempt.h> | ||
19 | #include <litmus/rt_domain.h> | 20 | #include <litmus/rt_domain.h> |
20 | #include <litmus/sched_plugin.h> | 21 | #include <litmus/sched_plugin.h> |
21 | #include <litmus/sched_trace.h> | 22 | #include <litmus/sched_trace.h> |
@@ -241,11 +242,7 @@ static void check_preempt(struct task_struct* t) | |||
241 | PTRACE_TASK(t, "linked_on:%d, scheduled_on:%d\n", | 242 | PTRACE_TASK(t, "linked_on:%d, scheduled_on:%d\n", |
242 | tsk_rt(t)->linked_on, tsk_rt(t)->scheduled_on); | 243 | tsk_rt(t)->linked_on, tsk_rt(t)->scheduled_on); |
243 | /* preempt */ | 244 | /* preempt */ |
244 | if (cpu == smp_processor_id()) | 245 | litmus_reschedule(cpu); |
245 | set_tsk_need_resched(current); | ||
246 | else { | ||
247 | smp_send_reschedule(cpu); | ||
248 | } | ||
249 | } | 246 | } |
250 | } | 247 | } |
251 | 248 | ||
@@ -545,7 +542,7 @@ static void pfair_tick(struct task_struct* t) | |||
545 | 542 | ||
546 | if (state->local != current | 543 | if (state->local != current |
547 | && (is_realtime(current) || is_present(state->local))) | 544 | && (is_realtime(current) || is_present(state->local))) |
548 | set_tsk_need_resched(current); | 545 | litmus_reschedule_local(); |
549 | } | 546 | } |
550 | 547 | ||
551 | static int safe_to_schedule(struct task_struct* t, int cpu) | 548 | static int safe_to_schedule(struct task_struct* t, int cpu) |
@@ -577,7 +574,7 @@ static struct task_struct* pfair_schedule(struct task_struct * prev) | |||
577 | if (next) | 574 | if (next) |
578 | tsk_rt(next)->scheduled_on = state->cpu; | 575 | tsk_rt(next)->scheduled_on = state->cpu; |
579 | } | 576 | } |
580 | 577 | sched_state_task_picked(); | |
581 | raw_spin_unlock(&pfair_lock); | 578 | raw_spin_unlock(&pfair_lock); |
582 | 579 | ||
583 | if (next) | 580 | if (next) |
diff --git a/litmus/sched_plugin.c b/litmus/sched_plugin.c index ec04454a0cf9..d912a6494d20 100644 --- a/litmus/sched_plugin.c +++ b/litmus/sched_plugin.c | |||
@@ -10,7 +10,7 @@ | |||
10 | 10 | ||
11 | #include <litmus/litmus.h> | 11 | #include <litmus/litmus.h> |
12 | #include <litmus/sched_plugin.h> | 12 | #include <litmus/sched_plugin.h> |
13 | 13 | #include <litmus/preempt.h> | |
14 | #include <litmus/jobs.h> | 14 | #include <litmus/jobs.h> |
15 | 15 | ||
16 | /* | 16 | /* |
@@ -19,36 +19,30 @@ | |||
19 | * non-preemptive section aware and does not invoke the scheduler / send | 19 | * non-preemptive section aware and does not invoke the scheduler / send |
20 | * IPIs if the to-be-preempted task is actually non-preemptive. | 20 | * IPIs if the to-be-preempted task is actually non-preemptive. |
21 | */ | 21 | */ |
22 | void preempt_if_preemptable(struct task_struct* t, int on_cpu) | 22 | void preempt_if_preemptable(struct task_struct* t, int cpu) |
23 | { | 23 | { |
24 | /* t is the real-time task executing on CPU on_cpu If t is NULL, then | 24 | /* t is the real-time task executing on CPU on_cpu If t is NULL, then |
25 | * on_cpu is currently scheduling background work. | 25 | * on_cpu is currently scheduling background work. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | int send_ipi; | 28 | int reschedule = 0; |
29 | 29 | ||
30 | if (smp_processor_id() == on_cpu) { | 30 | if (!t) |
31 | /* local CPU case */ | 31 | /* move non-real-time task out of the way */ |
32 | if (t) { | 32 | reschedule = 1; |
33 | else { | ||
34 | if (smp_processor_id() == cpu) { | ||
35 | /* local CPU case */ | ||
33 | /* check if we need to poke userspace */ | 36 | /* check if we need to poke userspace */ |
34 | if (is_user_np(t)) | 37 | if (is_user_np(t)) |
35 | /* yes, poke it */ | 38 | /* yes, poke it */ |
36 | request_exit_np(t); | 39 | request_exit_np(t); |
37 | else | 40 | else if (!is_kernel_np(t)) |
38 | /* no, see if we are allowed to preempt the | 41 | /* only if we are allowed to preempt the |
39 | * currently-executing task */ | 42 | * currently-executing task */ |
40 | if (!is_kernel_np(t)) | 43 | reschedule = 1; |
41 | set_tsk_need_resched(t); | 44 | } else { |
42 | } else | 45 | /* remote CPU case */ |
43 | /* move non-real-time task out of the way */ | ||
44 | set_tsk_need_resched(current); | ||
45 | } else { | ||
46 | /* remote CPU case */ | ||
47 | if (!t) | ||
48 | /* currently schedules non-real-time work */ | ||
49 | send_ipi = 1; | ||
50 | else { | ||
51 | /* currently schedules real-time work */ | ||
52 | if (is_user_np(t)) { | 46 | if (is_user_np(t)) { |
53 | /* need to notify user space of delayed | 47 | /* need to notify user space of delayed |
54 | * preemption */ | 48 | * preemption */ |
@@ -60,14 +54,14 @@ void preempt_if_preemptable(struct task_struct* t, int on_cpu) | |||
60 | mb(); | 54 | mb(); |
61 | } | 55 | } |
62 | /* Only send an ipi if remote task might have raced our | 56 | /* Only send an ipi if remote task might have raced our |
63 | * request, i.e., send an IPI to make sure if it exited | 57 | * request, i.e., send an IPI to make sure in case it |
64 | * its critical section. | 58 | * exited its critical section. |
65 | */ | 59 | */ |
66 | send_ipi = !is_np(t) && !is_kernel_np(t); | 60 | reschedule = !is_np(t) && !is_kernel_np(t); |
67 | } | 61 | } |
68 | if (likely(send_ipi)) | ||
69 | smp_send_reschedule(on_cpu); | ||
70 | } | 62 | } |
63 | if (likely(reschedule)) | ||
64 | litmus_reschedule(cpu); | ||
71 | } | 65 | } |
72 | 66 | ||
73 | 67 | ||
@@ -81,6 +75,7 @@ static void litmus_dummy_finish_switch(struct task_struct * prev) | |||
81 | 75 | ||
82 | static struct task_struct* litmus_dummy_schedule(struct task_struct * prev) | 76 | static struct task_struct* litmus_dummy_schedule(struct task_struct * prev) |
83 | { | 77 | { |
78 | sched_state_task_picked(); | ||
84 | return NULL; | 79 | return NULL; |
85 | } | 80 | } |
86 | 81 | ||
diff --git a/litmus/sched_psn_edf.c b/litmus/sched_psn_edf.c index aa567f2b91b9..b89823d5c026 100644 --- a/litmus/sched_psn_edf.c +++ b/litmus/sched_psn_edf.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | #include <litmus/litmus.h> | 17 | #include <litmus/litmus.h> |
18 | #include <litmus/jobs.h> | 18 | #include <litmus/jobs.h> |
19 | #include <litmus/preempt.h> | ||
19 | #include <litmus/sched_plugin.h> | 20 | #include <litmus/sched_plugin.h> |
20 | #include <litmus/edf_common.h> | 21 | #include <litmus/edf_common.h> |
21 | #include <litmus/sched_trace.h> | 22 | #include <litmus/sched_trace.h> |
@@ -108,7 +109,7 @@ static void psnedf_tick(struct task_struct *t) | |||
108 | 109 | ||
109 | if (is_realtime(t) && budget_enforced(t) && budget_exhausted(t)) { | 110 | if (is_realtime(t) && budget_enforced(t) && budget_exhausted(t)) { |
110 | if (!is_np(t)) { | 111 | if (!is_np(t)) { |
111 | set_tsk_need_resched(t); | 112 | litmus_reschedule_local(); |
112 | TRACE("psnedf_scheduler_tick: " | 113 | TRACE("psnedf_scheduler_tick: " |
113 | "%d is preemptable " | 114 | "%d is preemptable " |
114 | " => FORCE_RESCHED\n", t->pid); | 115 | " => FORCE_RESCHED\n", t->pid); |
@@ -204,6 +205,7 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
204 | } | 205 | } |
205 | 206 | ||
206 | pedf->scheduled = next; | 207 | pedf->scheduled = next; |
208 | sched_state_task_picked(); | ||
207 | raw_spin_unlock(&pedf->slock); | 209 | raw_spin_unlock(&pedf->slock); |
208 | 210 | ||
209 | return next; | 211 | return next; |