diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-17 17:23:36 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-17 17:23:36 -0400 |
commit | 469aaad39c956446b8a31d351ee36bedd87ac18a (patch) | |
tree | 3e2864a01df8a04ab7a406342627d3dc850760af | |
parent | 9374a7c30b6906d01c548833fb8a7b65ba4b5ccc (diff) |
Per-task budget high-resolution timers (hrtimers).
As a step towards implementing more complex budget tracking
method (ex. BWI, VXR, etc.), we need per-task budget trackers
because we may be required to drain budget from a task, even
while it is suspended or blocked.
This patch does:
1) Replaces the per-CPU hrtimers with per-task hrtimers.
2) Plugin architecture for different budget policies.
This patch creates three budget draining policies:
SIMPLE, SAWARE (suspension-aware), and SOBLIV (suspension-oblivious).
However, only SIMPLE is supported by this patch.
SIMPLE (default):
Budget drains while the task is scheduled. Budget is preserved
across self-suspensions (but not job completions, of course).
Only SIMPLE is supported in this patch. (Maintaining current Litmus
functionality.)
SAWARE:
Draining according to suspension-aware analysis. Budget should drain
whenever a task is among the top-m tasks in its cluster, where
m is the number of processors in said cluster. This draining should
happen whether or not the task is actually scheduled.
SOBLIV:
Draining according to suspension-oblivious analysis. Budget should
drain whenever the task is scheduled or suspended (but not due to
preemption). Exception: Draining should halt when we can prove that
the task is not among the top-m tasks blocked on the same lock (i.e.,
on the PQ in the OMLP-family locking protocols).
-rw-r--r-- | include/litmus/budget.h | 106 | ||||
-rw-r--r-- | include/litmus/fdso.h | 2 | ||||
-rw-r--r-- | include/litmus/gpu_affinity.h | 8 | ||||
-rw-r--r-- | include/litmus/litmus.h | 2 | ||||
-rw-r--r-- | include/litmus/rt_param.h | 26 | ||||
-rw-r--r-- | include/litmus/sched_plugin.h | 1 | ||||
-rw-r--r-- | litmus/budget.c | 217 | ||||
-rw-r--r-- | litmus/jobs.c | 4 | ||||
-rw-r--r-- | litmus/litmus.c | 2 | ||||
-rw-r--r-- | litmus/sched_cedf.c | 129 | ||||
-rw-r--r-- | litmus/sched_gsn_edf.c | 114 | ||||
-rw-r--r-- | litmus/sched_litmus.c | 6 | ||||
-rw-r--r-- | litmus/sched_pfair.c | 10 | ||||
-rw-r--r-- | litmus/sched_pfp.c | 107 | ||||
-rw-r--r-- | litmus/sched_psn_edf.c | 107 |
15 files changed, 566 insertions, 275 deletions
diff --git a/include/litmus/budget.h b/include/litmus/budget.h index 763b31c0e9f6..2a3511245f7a 100644 --- a/include/litmus/budget.h +++ b/include/litmus/budget.h | |||
@@ -1,26 +1,14 @@ | |||
1 | #ifndef _LITMUS_BUDGET_H_ | 1 | #ifndef _LITMUS_BUDGET_H_ |
2 | #define _LITMUS_BUDGET_H_ | 2 | #define _LITMUS_BUDGET_H_ |
3 | 3 | ||
4 | /* Update the per-processor enforcement timer (arm/reproram/cancel) for | 4 | #include <linux/hrtimer.h> |
5 | * the next task. */ | 5 | #include <linux/semaphore.h> |
6 | void update_enforcement_timer(struct task_struct* t); | ||
7 | 6 | ||
8 | /* Send SIG_BUDGET to a real-time task. */ | 7 | #define budget_exhausted(t) \ |
9 | void send_sigbudget(struct task_struct* t); | 8 | (get_exec_time(t) >= get_exec_cost(t)) |
10 | 9 | ||
11 | inline static int budget_exhausted(struct task_struct* t) | 10 | #define budget_remaining(t) \ |
12 | { | 11 | ((!budget_exhausted(t)) ? (get_exec_cost(t) - get_exec_time(t)) : 0) |
13 | return get_exec_time(t) >= get_exec_cost(t); | ||
14 | } | ||
15 | |||
16 | inline static lt_t budget_remaining(struct task_struct* t) | ||
17 | { | ||
18 | if (!budget_exhausted(t)) | ||
19 | return get_exec_cost(t) - get_exec_time(t); | ||
20 | else | ||
21 | /* avoid overflow */ | ||
22 | return 0; | ||
23 | } | ||
24 | 12 | ||
25 | #define budget_enforced(t) (\ | 13 | #define budget_enforced(t) (\ |
26 | tsk_rt(t)->task_params.budget_policy != NO_ENFORCEMENT) | 14 | tsk_rt(t)->task_params.budget_policy != NO_ENFORCEMENT) |
@@ -29,21 +17,87 @@ inline static lt_t budget_remaining(struct task_struct* t) | |||
29 | tsk_rt(t)->task_params.budget_policy == PRECISE_ENFORCEMENT || \ | 17 | tsk_rt(t)->task_params.budget_policy == PRECISE_ENFORCEMENT || \ |
30 | tsk_rt(t)->task_params.budget_signal_policy == PRECISE_SIGNALS) | 18 | tsk_rt(t)->task_params.budget_signal_policy == PRECISE_SIGNALS) |
31 | 19 | ||
20 | #define budget_quantum_tracked(t) (\ | ||
21 | tsk_rt(t)->task_params.budget_policy == QUANTUM_ENFORCEMENT || \ | ||
22 | tsk_rt(t)->task_params.budget_signal_policy == QUANTUM_SIGNALS) | ||
23 | |||
32 | #define budget_signalled(t) (\ | 24 | #define budget_signalled(t) (\ |
33 | tsk_rt(t)->task_params.budget_signal_policy != NO_SIGNALS) | 25 | tsk_rt(t)->task_params.budget_signal_policy != NO_SIGNALS) |
34 | 26 | ||
35 | #define budget_precisely_signalled(t) (\ | 27 | #define budget_precisely_signalled(t) (\ |
36 | tsk_rt(t)->task_params.budget_policy == PRECISE_SIGNALS) | 28 | tsk_rt(t)->task_params.budget_policy == PRECISE_SIGNALS) |
37 | 29 | ||
38 | #define sigbudget_sent(t) (\ | 30 | #define bt_flag_is_set(t, flag_nr) (\ |
39 | test_bit(RT_JOB_SIG_BUDGET_SENT, &tsk_rt(t)->job_params.flags)) | 31 | test_bit(flag_nr, &tsk_rt(t)->budget.flags)) |
32 | |||
33 | #define bt_flag_test_and_set(t, flag_nr) (\ | ||
34 | test_and_set_bit(flag_nr, &tsk_rt(t)->budget.flags)) | ||
35 | |||
36 | #define bt_flag_set(t, flag_nr) (\ | ||
37 | set_bit(flag_nr, &tsk_rt(t)->budget.flags)) | ||
38 | |||
39 | #define bt_flag_clear(t, flag_nr) (\ | ||
40 | clear_bit(flag_nr, &tsk_rt(t)->budget.flags)) | ||
41 | |||
42 | #define bt_flags_reset(t) (\ | ||
43 | tsk_rt(t)->budget.flags = 0) | ||
44 | |||
45 | #define requeue_preempted_job(t) \ | ||
46 | (t && (!budget_exhausted(t) || !budget_enforced(t))) | ||
47 | |||
48 | struct enforcement_timer | ||
49 | { | ||
50 | raw_spinlock_t lock; | ||
51 | struct hrtimer timer; | ||
52 | int armed:1; | ||
53 | }; | ||
54 | |||
55 | typedef void (*scheduled_t)(struct task_struct* t); | ||
56 | typedef void (*blocked_t)(struct task_struct* t); | ||
57 | typedef void (*preempt_or_sleep_t)(struct task_struct* t); | ||
58 | typedef void (*exhausted_t)(struct task_struct* t); | ||
59 | typedef void (*exit_t)(struct task_struct* t); | ||
40 | 60 | ||
41 | static inline int requeue_preempted_job(struct task_struct* t) | 61 | struct budget_tracker_ops |
42 | { | 62 | { |
43 | /* Add task to ready queue only if not subject to budget enforcement or | 63 | scheduled_t on_scheduled; /* called from litmus_schedule(). */ |
44 | * if the job has budget remaining. t may be NULL. | 64 | blocked_t on_blocked; /* called from plugin::schedule() */ |
45 | */ | 65 | preempt_or_sleep_t on_preempt_or_sleep; /* called from plugin::schedule() */ |
46 | return t && (!budget_exhausted(t) || !budget_enforced(t)); | 66 | |
47 | } | 67 | exit_t on_exit; /* task exiting rt mode */ |
68 | |||
69 | exhausted_t on_exhausted; /* called by plugin::tick() or timer interrupt */ | ||
70 | }; | ||
71 | |||
72 | struct budget_tracker | ||
73 | { | ||
74 | struct enforcement_timer timer; | ||
75 | const struct budget_tracker_ops* ops; | ||
76 | unsigned long flags; | ||
77 | }; | ||
78 | |||
79 | /* budget tracker flags */ | ||
80 | enum BT_FLAGS | ||
81 | { | ||
82 | BTF_BUDGET_EXHAUSTED = 0, | ||
83 | BTF_SIG_BUDGET_SENT = 1, | ||
84 | }; | ||
85 | |||
86 | /* Functions for simple DRAIN_SIMPLE policy common | ||
87 | * to every scheduler. Scheduler must provided | ||
88 | * implementation for simple_on_exhausted(). | ||
89 | */ | ||
90 | void simple_on_scheduled(struct task_struct* t); | ||
91 | void simple_on_blocked(struct task_struct* t); | ||
92 | void simple_on_preempt_or_sleep(struct task_struct* t); | ||
93 | void simple_on_exit(struct task_struct* t); | ||
94 | |||
95 | |||
96 | void init_budget_tracker(struct budget_tracker* bt, | ||
97 | const struct budget_tracker_ops* ops); | ||
98 | |||
99 | |||
100 | /* Send SIG_BUDGET to a real-time task. */ | ||
101 | void send_sigbudget(struct task_struct* t); | ||
48 | 102 | ||
49 | #endif | 103 | #endif |
diff --git a/include/litmus/fdso.h b/include/litmus/fdso.h index f7887288d8f5..2d8e6c43d908 100644 --- a/include/litmus/fdso.h +++ b/include/litmus/fdso.h | |||
@@ -36,7 +36,7 @@ typedef enum { | |||
36 | KFMLP_GPU_AFF_OBS = 12, | 36 | KFMLP_GPU_AFF_OBS = 12, |
37 | 37 | ||
38 | PRIOQ_MUTEX = 13, | 38 | PRIOQ_MUTEX = 13, |
39 | 39 | ||
40 | MAX_OBJ_TYPE = 13 | 40 | MAX_OBJ_TYPE = 13 |
41 | } obj_type_t; | 41 | } obj_type_t; |
42 | 42 | ||
diff --git a/include/litmus/gpu_affinity.h b/include/litmus/gpu_affinity.h index 47da725717b0..f610f58b1f3b 100644 --- a/include/litmus/gpu_affinity.h +++ b/include/litmus/gpu_affinity.h | |||
@@ -11,6 +11,7 @@ gpu_migration_dist_t gpu_migration_distance(int a, int b); | |||
11 | static inline void reset_gpu_tracker(struct task_struct* t) | 11 | static inline void reset_gpu_tracker(struct task_struct* t) |
12 | { | 12 | { |
13 | t->rt_param.accum_gpu_time = 0; | 13 | t->rt_param.accum_gpu_time = 0; |
14 | t->rt_param.gpu_time_stamp = 0; | ||
14 | } | 15 | } |
15 | 16 | ||
16 | static inline void start_gpu_tracker(struct task_struct* t) | 17 | static inline void start_gpu_tracker(struct task_struct* t) |
@@ -22,11 +23,16 @@ static inline void stop_gpu_tracker(struct task_struct* t) | |||
22 | { | 23 | { |
23 | lt_t now = litmus_clock(); | 24 | lt_t now = litmus_clock(); |
24 | t->rt_param.accum_gpu_time += (now - t->rt_param.gpu_time_stamp); | 25 | t->rt_param.accum_gpu_time += (now - t->rt_param.gpu_time_stamp); |
26 | t->rt_param.gpu_time_stamp = 0; | ||
25 | } | 27 | } |
26 | 28 | ||
27 | static inline lt_t get_gpu_time(struct task_struct* t) | 29 | static inline lt_t get_gpu_time(struct task_struct* t) |
28 | { | 30 | { |
29 | return t->rt_param.accum_gpu_time; | 31 | lt_t accum = t->rt_param.accum_gpu_time; |
32 | if (t->rt_param.gpu_time_stamp != 0) { | ||
33 | accum += (litmus_clock() - t->rt_param.gpu_time_stamp); | ||
34 | } | ||
35 | return accum; | ||
30 | } | 36 | } |
31 | 37 | ||
32 | static inline lt_t get_gpu_estimate(struct task_struct* t, gpu_migration_dist_t dist) | 38 | static inline lt_t get_gpu_estimate(struct task_struct* t, gpu_migration_dist_t dist) |
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h index 70b421d59d34..f6ea5f6e80ee 100644 --- a/include/litmus/litmus.h +++ b/include/litmus/litmus.h | |||
@@ -62,6 +62,7 @@ void litmus_exit_task(struct task_struct *tsk); | |||
62 | #define get_priority(t) (tsk_rt(t)->task_params.priority) | 62 | #define get_priority(t) (tsk_rt(t)->task_params.priority) |
63 | #define get_class(t) (tsk_rt(t)->task_params.cls) | 63 | #define get_class(t) (tsk_rt(t)->task_params.cls) |
64 | #define get_release_policy(t) (tsk_rt(t)->task_params.release_policy) | 64 | #define get_release_policy(t) (tsk_rt(t)->task_params.release_policy) |
65 | #define get_drain_policy(t) (tsk_rt(t)->task_params.drain_policy) | ||
65 | 66 | ||
66 | /* job_param macros */ | 67 | /* job_param macros */ |
67 | #define get_exec_time(t) (tsk_rt(t)->job_params.exec_time) | 68 | #define get_exec_time(t) (tsk_rt(t)->job_params.exec_time) |
@@ -69,6 +70,7 @@ void litmus_exit_task(struct task_struct *tsk); | |||
69 | #define get_period(t) (tsk_rt(t)->task_params.period) | 70 | #define get_period(t) (tsk_rt(t)->task_params.period) |
70 | #define get_release(t) (tsk_rt(t)->job_params.release) | 71 | #define get_release(t) (tsk_rt(t)->job_params.release) |
71 | #define get_lateness(t) (tsk_rt(t)->job_params.lateness) | 72 | #define get_lateness(t) (tsk_rt(t)->job_params.lateness) |
73 | #define get_budget_timer(t) (tsk_rt(t)->job_params.budget_timer) | ||
72 | 74 | ||
73 | #define effective_priority(t) ((!(tsk_rt(t)->inh_task)) ? t : tsk_rt(t)->inh_task) | 75 | #define effective_priority(t) ((!(tsk_rt(t)->inh_task)) ? t : tsk_rt(t)->inh_task) |
74 | #define base_priority(t) (t) | 76 | #define base_priority(t) (t) |
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index bf0ee8dbae6e..887075b908ca 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
@@ -36,6 +36,14 @@ typedef enum { | |||
36 | } budget_policy_t; | 36 | } budget_policy_t; |
37 | 37 | ||
38 | typedef enum { | 38 | typedef enum { |
39 | /* all drain mechanisms are ignored if budget enforcement or signalling | ||
40 | is not in use. */ | ||
41 | DRAIN_SIMPLE, /* drains while task is linked */ | ||
42 | DRAIN_SAWARE, /* drains according to suspension-aware analysis */ | ||
43 | DRAIN_SOBLIV, /* drains according to suspension-obliv analysis */ | ||
44 | } budget_drain_policy_t; | ||
45 | |||
46 | typedef enum { | ||
39 | NO_SIGNALS, /* job receives no signals when it exhausts its budget */ | 47 | NO_SIGNALS, /* job receives no signals when it exhausts its budget */ |
40 | QUANTUM_SIGNALS, /* budget signals are only sent on quantum boundaries */ | 48 | QUANTUM_SIGNALS, /* budget signals are only sent on quantum boundaries */ |
41 | PRECISE_SIGNALS, /* budget signals are triggered with hrtimers */ | 49 | PRECISE_SIGNALS, /* budget signals are triggered with hrtimers */ |
@@ -132,6 +140,7 @@ struct rt_task { | |||
132 | unsigned int priority; | 140 | unsigned int priority; |
133 | task_class_t cls; | 141 | task_class_t cls; |
134 | budget_policy_t budget_policy; /* ignored by pfair */ | 142 | budget_policy_t budget_policy; /* ignored by pfair */ |
143 | budget_drain_policy_t drain_policy; | ||
135 | budget_signal_policy_t budget_signal_policy; /* currently ignored by pfair */ | 144 | budget_signal_policy_t budget_signal_policy; /* currently ignored by pfair */ |
136 | release_policy_t release_policy; | 145 | release_policy_t release_policy; |
137 | }; | 146 | }; |
@@ -213,8 +222,9 @@ struct control_page { | |||
213 | /* don't export internal data structures to user space (liblitmus) */ | 222 | /* don't export internal data structures to user space (liblitmus) */ |
214 | #ifdef __KERNEL__ | 223 | #ifdef __KERNEL__ |
215 | 224 | ||
216 | #include <litmus/binheap.h> | ||
217 | #include <linux/semaphore.h> | 225 | #include <linux/semaphore.h> |
226 | #include <litmus/budget.h> | ||
227 | #include <litmus/binheap.h> | ||
218 | 228 | ||
219 | #ifdef CONFIG_LITMUS_SOFTIRQD | 229 | #ifdef CONFIG_LITMUS_SOFTIRQD |
220 | #include <linux/interrupt.h> | 230 | #include <linux/interrupt.h> |
@@ -247,15 +257,8 @@ struct rt_job { | |||
247 | * Increase this sequence number when a job is released. | 257 | * Increase this sequence number when a job is released. |
248 | */ | 258 | */ |
249 | unsigned int job_no; | 259 | unsigned int job_no; |
250 | |||
251 | /* bits: | ||
252 | * 0th: Set if a budget exhaustion signal has already been sent for | ||
253 | * the current job. */ | ||
254 | unsigned long flags; | ||
255 | }; | 260 | }; |
256 | 261 | ||
257 | #define RT_JOB_SIG_BUDGET_SENT 0 | ||
258 | |||
259 | struct pfair_param; | 262 | struct pfair_param; |
260 | 263 | ||
261 | enum klmirqd_sem_status | 264 | enum klmirqd_sem_status |
@@ -278,12 +281,12 @@ typedef enum gpu_migration_dist | |||
278 | MIG_LAST = MIG_NONE | 281 | MIG_LAST = MIG_NONE |
279 | } gpu_migration_dist_t; | 282 | } gpu_migration_dist_t; |
280 | 283 | ||
281 | typedef struct feedback_est{ | 284 | typedef struct feedback_est |
285 | { | ||
282 | fp_t est; | 286 | fp_t est; |
283 | fp_t accum_err; | 287 | fp_t accum_err; |
284 | } feedback_est_t; | 288 | } feedback_est_t; |
285 | 289 | ||
286 | |||
287 | #define AVG_EST_WINDOW_SIZE 20 | 290 | #define AVG_EST_WINDOW_SIZE 20 |
288 | 291 | ||
289 | typedef int (*notify_rsrc_exit_t)(struct task_struct* tsk); | 292 | typedef int (*notify_rsrc_exit_t)(struct task_struct* tsk); |
@@ -417,7 +420,6 @@ struct rt_param { | |||
417 | struct binheap_node aux_task_owner_node; | 420 | struct binheap_node aux_task_owner_node; |
418 | #endif | 421 | #endif |
419 | 422 | ||
420 | |||
421 | #ifdef CONFIG_NP_SECTION | 423 | #ifdef CONFIG_NP_SECTION |
422 | /* For the FMLP under PSN-EDF, it is required to make the task | 424 | /* For the FMLP under PSN-EDF, it is required to make the task |
423 | * non-preemptive from kernel space. In order not to interfere with | 425 | * non-preemptive from kernel space. In order not to interfere with |
@@ -427,6 +429,8 @@ struct rt_param { | |||
427 | unsigned int kernel_np; | 429 | unsigned int kernel_np; |
428 | #endif | 430 | #endif |
429 | 431 | ||
432 | struct budget_tracker budget; | ||
433 | |||
430 | /* This field can be used by plugins to store where the task | 434 | /* This field can be used by plugins to store where the task |
431 | * is currently scheduled. It is the responsibility of the | 435 | * is currently scheduled. It is the responsibility of the |
432 | * plugin to avoid race conditions. | 436 | * plugin to avoid race conditions. |
diff --git a/include/litmus/sched_plugin.h b/include/litmus/sched_plugin.h index 78cec30866ac..6e7d6df2fb78 100644 --- a/include/litmus/sched_plugin.h +++ b/include/litmus/sched_plugin.h | |||
@@ -130,6 +130,7 @@ struct sched_plugin { | |||
130 | schedule_t schedule; | 130 | schedule_t schedule; |
131 | finish_switch_t finish_switch; | 131 | finish_switch_t finish_switch; |
132 | 132 | ||
133 | |||
133 | /* syscall backend */ | 134 | /* syscall backend */ |
134 | complete_job_t complete_job; | 135 | complete_job_t complete_job; |
135 | release_at_t release_at; | 136 | release_at_t release_at; |
diff --git a/litmus/budget.c b/litmus/budget.c index 2ec9d383c332..559c54709acc 100644 --- a/litmus/budget.c +++ b/litmus/budget.c | |||
@@ -5,102 +5,100 @@ | |||
5 | 5 | ||
6 | #include <litmus/litmus.h> | 6 | #include <litmus/litmus.h> |
7 | #include <litmus/preempt.h> | 7 | #include <litmus/preempt.h> |
8 | 8 | #include <litmus/sched_plugin.h> | |
9 | #include <litmus/budget.h> | 9 | #include <litmus/budget.h> |
10 | #include <litmus/signal.h> | 10 | #include <litmus/signal.h> |
11 | 11 | ||
12 | struct enforcement_timer { | 12 | inline static void cancel_enforcement_timer(struct task_struct* t) |
13 | /* The enforcement timer is used to accurately police | ||
14 | * slice budgets. */ | ||
15 | struct hrtimer timer; | ||
16 | int armed; | ||
17 | }; | ||
18 | |||
19 | static DEFINE_PER_CPU(struct enforcement_timer, budget_timer); | ||
20 | |||
21 | static enum hrtimer_restart on_enforcement_timeout(struct hrtimer *timer) | ||
22 | { | 13 | { |
23 | struct enforcement_timer* et = container_of(timer, | 14 | struct enforcement_timer* et; |
24 | struct enforcement_timer, | 15 | int ret; |
25 | timer); | ||
26 | unsigned long flags; | 16 | unsigned long flags; |
27 | 17 | ||
28 | local_irq_save(flags); | 18 | BUG_ON(!t); |
29 | TRACE("enforcement timer fired.\n"); | 19 | BUG_ON(!is_realtime(t)); |
30 | et->armed = 0; | ||
31 | /* activate scheduler */ | ||
32 | litmus_reschedule_local(); | ||
33 | local_irq_restore(flags); | ||
34 | |||
35 | return HRTIMER_NORESTART; | ||
36 | } | ||
37 | 20 | ||
38 | /* assumes called with IRQs off */ | 21 | et = &tsk_rt(t)->budget.timer; |
39 | static void cancel_enforcement_timer(struct enforcement_timer* et) | ||
40 | { | ||
41 | int ret; | ||
42 | 22 | ||
43 | TRACE("cancelling enforcement timer.\n"); | 23 | TRACE("cancelling enforcement timer.\n"); |
44 | 24 | ||
45 | /* Since interrupts are disabled and et->armed is only | ||
46 | * modified locally, we do not need any locks. | ||
47 | */ | ||
48 | |||
49 | if (et->armed) { | 25 | if (et->armed) { |
50 | ret = hrtimer_try_to_cancel(&et->timer); | 26 | raw_spin_lock_irqsave(&et->lock, flags); |
51 | /* Should never be inactive. */ | 27 | if (et->armed) { |
52 | BUG_ON(ret == 0); | 28 | ret = hrtimer_try_to_cancel(&et->timer); |
53 | /* Should never be running concurrently. */ | 29 | et->armed = 0; |
54 | BUG_ON(ret == -1); | 30 | } |
55 | 31 | else { | |
56 | et->armed = 0; | 32 | TRACE("timer was not armed (race).\n"); |
33 | } | ||
34 | raw_spin_unlock_irqrestore(&et->lock, flags); | ||
35 | } | ||
36 | else { | ||
37 | TRACE("timer was not armed.\n"); | ||
57 | } | 38 | } |
58 | } | 39 | } |
59 | 40 | ||
60 | /* assumes called with IRQs off */ | 41 | inline static void arm_enforcement_timer(struct task_struct* t) |
61 | static void arm_enforcement_timer(struct enforcement_timer* et, | ||
62 | struct task_struct* t) | ||
63 | { | 42 | { |
43 | struct enforcement_timer* et; | ||
64 | lt_t when_to_fire; | 44 | lt_t when_to_fire; |
65 | TRACE_TASK(t, "arming enforcement timer.\n"); | 45 | unsigned long flags; |
46 | |||
47 | BUG_ON(!t); | ||
48 | BUG_ON(!is_realtime(t)); | ||
49 | |||
50 | et = &tsk_rt(t)->budget.timer; | ||
51 | if (et->armed) { | ||
52 | TRACE_TASK(t, "timer already armed!\n"); | ||
53 | return; | ||
54 | } | ||
66 | 55 | ||
67 | /* Calling this when there is no budget left for the task | 56 | /* Calling this when there is no budget left for the task |
68 | * makes no sense, unless the task is non-preemptive. */ | 57 | * makes no sense, unless the task is non-preemptive. */ |
69 | BUG_ON(budget_exhausted(t) && !is_np(t)); | 58 | if (budget_exhausted(t)) { |
59 | TRACE_TASK(t, "can't arm timer because no budget remaining\n"); | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | if ( (!budget_enforced(t) || | ||
64 | (budget_enforced(t) && bt_flag_is_set(t, BTF_BUDGET_EXHAUSTED))) | ||
65 | && | ||
66 | (!budget_signalled(t) || | ||
67 | (budget_signalled(t) && bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)))) { | ||
68 | TRACE_TASK(t, "trying to arm timer when budget has already been exhausted.\n"); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | TRACE_TASK(t, "arming enforcement timer.\n"); | ||
70 | 73 | ||
71 | /* __hrtimer_start_range_ns() cancels the timer | 74 | /* __hrtimer_start_range_ns() cancels the timer |
72 | * anyway, so we don't have to check whether it is still armed */ | 75 | * anyway, so we don't have to check whether it is still armed */ |
76 | raw_spin_lock_irqsave(&et->lock, flags); | ||
73 | 77 | ||
74 | if (likely(!is_np(t))) { | 78 | if (et->armed) { |
75 | when_to_fire = litmus_clock() + budget_remaining(t); | 79 | TRACE_TASK(t, "timer already armed (race)!\n"); |
76 | __hrtimer_start_range_ns(&et->timer, | 80 | goto out; |
77 | ns_to_ktime(when_to_fire), | ||
78 | 0 /* delta */, | ||
79 | HRTIMER_MODE_ABS_PINNED, | ||
80 | 0 /* no wakeup */); | ||
81 | et->armed = 1; | ||
82 | } | 81 | } |
83 | } | ||
84 | 82 | ||
83 | when_to_fire = litmus_clock() + budget_remaining(t); | ||
85 | 84 | ||
86 | /* expects to be called with IRQs off */ | 85 | TRACE_TASK(t, "bremaining: %ld, when_to_fire: %ld\n", budget_remaining(t), when_to_fire); |
87 | void update_enforcement_timer(struct task_struct* t) | 86 | |
88 | { | 87 | __hrtimer_start_range_ns(&et->timer, |
89 | struct enforcement_timer* et = &__get_cpu_var(budget_timer); | 88 | ns_to_ktime(when_to_fire), |
90 | 89 | 0 /* delta */, | |
91 | if (t && budget_precisely_tracked(t) && !sigbudget_sent(t)) { | 90 | HRTIMER_MODE_ABS_PINNED, // TODO: need to use non-pinned? |
92 | /* Make sure we call into the scheduler when this budget | 91 | 0 /* no wakeup */); |
93 | * expires. */ | 92 | et->armed = 1; |
94 | arm_enforcement_timer(et, t); | 93 | |
95 | } else if (et->armed) { | 94 | out: |
96 | /* Make sure we don't cause unnecessary interrupts. */ | 95 | raw_spin_unlock_irqrestore(&et->lock, flags); |
97 | cancel_enforcement_timer(et); | ||
98 | } | ||
99 | } | 96 | } |
100 | 97 | ||
98 | |||
101 | void send_sigbudget(struct task_struct* t) | 99 | void send_sigbudget(struct task_struct* t) |
102 | { | 100 | { |
103 | if (!test_and_set_bit(RT_JOB_SIG_BUDGET_SENT, &tsk_rt(t)->job_params.flags)) { | 101 | if (!bt_flag_test_and_set(t, BTF_SIG_BUDGET_SENT)) { |
104 | /* signal has not yet been sent and we are responsible for sending | 102 | /* signal has not yet been sent and we are responsible for sending |
105 | * since we just set the sent-bit when it was previously 0. */ | 103 | * since we just set the sent-bit when it was previously 0. */ |
106 | 104 | ||
@@ -109,17 +107,86 @@ void send_sigbudget(struct task_struct* t) | |||
109 | } | 107 | } |
110 | } | 108 | } |
111 | 109 | ||
112 | static int __init init_budget_enforcement(void) | 110 | |
111 | void simple_on_scheduled(struct task_struct* t) | ||
113 | { | 112 | { |
114 | int cpu; | 113 | BUG_ON(!t); |
115 | struct enforcement_timer* et; | 114 | |
115 | if (budget_precisely_tracked(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
116 | BUG_ON(tsk_rt(t)->budget.timer.armed); | ||
117 | arm_enforcement_timer(t); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void __simple_on_unscheduled(struct task_struct* t) | ||
122 | { | ||
123 | BUG_ON(!t); | ||
116 | 124 | ||
117 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | 125 | if (budget_precisely_tracked(t)) { |
118 | et = &per_cpu(budget_timer, cpu); | 126 | cancel_enforcement_timer(t); |
119 | hrtimer_init(&et->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
120 | et->timer.function = on_enforcement_timeout; | ||
121 | } | 127 | } |
122 | return 0; | ||
123 | } | 128 | } |
124 | 129 | ||
125 | module_init(init_budget_enforcement); | 130 | void simple_on_blocked(struct task_struct* t) |
131 | { | ||
132 | __simple_on_unscheduled(t); | ||
133 | } | ||
134 | |||
135 | void simple_on_preempt_or_sleep(struct task_struct* t) | ||
136 | { | ||
137 | __simple_on_unscheduled(t); | ||
138 | } | ||
139 | |||
140 | void simple_on_exit(struct task_struct* t) | ||
141 | { | ||
142 | __simple_on_unscheduled(t); | ||
143 | } | ||
144 | |||
145 | |||
146 | |||
147 | |||
148 | static enum hrtimer_restart __on_timeout(struct hrtimer *timer) | ||
149 | { | ||
150 | unsigned long flags; | ||
151 | struct budget_tracker* bt = | ||
152 | container_of( | ||
153 | container_of(timer, | ||
154 | struct enforcement_timer, | ||
155 | timer), | ||
156 | struct budget_tracker, | ||
157 | timer); | ||
158 | |||
159 | struct task_struct* t = | ||
160 | container_of( | ||
161 | container_of(bt, struct rt_param, budget), | ||
162 | struct task_struct, | ||
163 | rt_param); | ||
164 | |||
165 | TRACE_TASK(t, "budget timer interrupt fired at time %lu\n", litmus_clock()); | ||
166 | |||
167 | raw_spin_lock_irqsave(&bt->timer.lock, flags); | ||
168 | tsk_rt(t)->budget.timer.armed = 0; | ||
169 | raw_spin_unlock_irqrestore(&bt->timer.lock, flags); | ||
170 | |||
171 | bt->ops->on_exhausted(t); | ||
172 | |||
173 | return HRTIMER_NORESTART; | ||
174 | } | ||
175 | |||
176 | |||
177 | void init_budget_tracker(struct budget_tracker* bt, const struct budget_tracker_ops* ops) | ||
178 | { | ||
179 | BUG_ON(!bt); | ||
180 | BUG_ON(!ops); | ||
181 | |||
182 | BUG_ON(!ops->on_scheduled); | ||
183 | BUG_ON(!ops->on_blocked); | ||
184 | BUG_ON(!ops->on_preempt_or_sleep); | ||
185 | BUG_ON(!ops->on_exhausted); | ||
186 | |||
187 | memset(bt, 0, sizeof(*bt)); | ||
188 | raw_spin_lock_init(&bt->timer.lock); | ||
189 | hrtimer_init(&bt->timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
190 | bt->timer.timer.function = __on_timeout; | ||
191 | bt->ops = ops; | ||
192 | } \ No newline at end of file | ||
diff --git a/litmus/jobs.c b/litmus/jobs.c index 991c6e60be74..1479cddad9de 100644 --- a/litmus/jobs.c +++ b/litmus/jobs.c | |||
@@ -13,7 +13,9 @@ static inline void setup_release(struct task_struct *t, lt_t release) | |||
13 | t->rt_param.job_params.deadline = release + get_rt_relative_deadline(t); | 13 | t->rt_param.job_params.deadline = release + get_rt_relative_deadline(t); |
14 | t->rt_param.job_params.exec_time = 0; | 14 | t->rt_param.job_params.exec_time = 0; |
15 | 15 | ||
16 | clear_bit(RT_JOB_SIG_BUDGET_SENT, &t->rt_param.job_params.flags); | 16 | /* kludge - TODO: Move this to budget.h/.c */ |
17 | if (t->rt_param.budget.ops) | ||
18 | bt_flags_reset(t); | ||
17 | 19 | ||
18 | /* update job sequence number */ | 20 | /* update job sequence number */ |
19 | t->rt_param.job_params.job_no++; | 21 | t->rt_param.job_params.job_no++; |
diff --git a/litmus/litmus.c b/litmus/litmus.c index 97cbe0461a93..e8130e362c84 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <litmus/litmus_proc.h> | 19 | #include <litmus/litmus_proc.h> |
20 | #include <litmus/sched_trace.h> | 20 | #include <litmus/sched_trace.h> |
21 | 21 | ||
22 | #include <litmus/budget.h> | ||
23 | |||
22 | #ifdef CONFIG_SCHED_CPU_AFFINITY | 24 | #ifdef CONFIG_SCHED_CPU_AFFINITY |
23 | #include <litmus/affinity.h> | 25 | #include <litmus/affinity.h> |
24 | #endif | 26 | #endif |
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index 78ae5a080138..8fe646f1f0c5 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -444,6 +444,35 @@ static noinline void job_completion(struct task_struct *t, int forced) | |||
444 | } | 444 | } |
445 | } | 445 | } |
446 | 446 | ||
447 | static void cedf_simple_on_exhausted(struct task_struct *t) | ||
448 | { | ||
449 | /* Assumption: t is scheduled on the CPU executing this callback */ | ||
450 | |||
451 | if (budget_signalled(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
452 | /* signal exhaustion */ | ||
453 | send_sigbudget(t); /* will set BTF_SIG_BUDGET_SENT */ | ||
454 | } | ||
455 | |||
456 | if (budget_enforced(t) && !bt_flag_test_and_set(t, BTF_BUDGET_EXHAUSTED)) { | ||
457 | if (!is_np(t)) { | ||
458 | /* np tasks will be preempted when they become | ||
459 | * preemptable again | ||
460 | */ | ||
461 | litmus_reschedule_local(); | ||
462 | set_will_schedule(); | ||
463 | TRACE("cedf_scheduler_tick: " | ||
464 | "%d is preemptable " | ||
465 | " => FORCE_RESCHED\n", t->pid); | ||
466 | } else if (is_user_np(t)) { | ||
467 | TRACE("cedf_scheduler_tick: " | ||
468 | "%d is non-preemptable, " | ||
469 | "preemption delayed.\n", t->pid); | ||
470 | request_exit_np(t); | ||
471 | } | ||
472 | } | ||
473 | } | ||
474 | |||
475 | |||
447 | /* cedf_tick - this function is called for every local timer | 476 | /* cedf_tick - this function is called for every local timer |
448 | * interrupt. | 477 | * interrupt. |
449 | * | 478 | * |
@@ -452,48 +481,18 @@ static noinline void job_completion(struct task_struct *t, int forced) | |||
452 | */ | 481 | */ |
453 | static void cedf_tick(struct task_struct* t) | 482 | static void cedf_tick(struct task_struct* t) |
454 | { | 483 | { |
455 | if (is_realtime(t) && budget_exhausted(t)) | 484 | if (is_realtime(t) && |
456 | { | 485 | tsk_rt(t)->budget.ops && budget_quantum_tracked(t) && |
457 | if (budget_signalled(t) && !sigbudget_sent(t)) { | 486 | budget_exhausted(t)) { |
458 | /* signal exhaustion */ | 487 | TRACE_TASK(t, "budget exhausted\n"); |
459 | send_sigbudget(t); | 488 | tsk_rt(t)->budget.ops->on_exhausted(t); |
460 | } | ||
461 | |||
462 | if (budget_enforced(t)) { | ||
463 | if (!is_np(t)) { | ||
464 | /* np tasks will be preempted when they become | ||
465 | * preemptable again | ||
466 | */ | ||
467 | litmus_reschedule_local(); | ||
468 | set_will_schedule(); | ||
469 | TRACE("cedf_scheduler_tick: " | ||
470 | "%d is preemptable " | ||
471 | " => FORCE_RESCHED\n", t->pid); | ||
472 | } else if (is_user_np(t)) { | ||
473 | TRACE("cedf_scheduler_tick: " | ||
474 | "%d is non-preemptable, " | ||
475 | "preemption delayed.\n", t->pid); | ||
476 | request_exit_np(t); | ||
477 | } | ||
478 | } | ||
479 | } | 489 | } |
480 | } | 490 | } |
481 | 491 | ||
482 | 492 | ||
483 | 493 | ||
484 | |||
485 | |||
486 | |||
487 | |||
488 | |||
489 | |||
490 | |||
491 | |||
492 | |||
493 | |||
494 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | 494 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD |
495 | 495 | ||
496 | |||
497 | static void __do_lit_tasklet(struct tasklet_struct* tasklet, unsigned long flushed) | 496 | static void __do_lit_tasklet(struct tasklet_struct* tasklet, unsigned long flushed) |
498 | { | 497 | { |
499 | if (!atomic_read(&tasklet->count)) { | 498 | if (!atomic_read(&tasklet->count)) { |
@@ -787,6 +786,7 @@ static void cedf_change_prio_pai_tasklet(struct task_struct *old_prio, | |||
787 | 786 | ||
788 | #endif // PAI | 787 | #endif // PAI |
789 | 788 | ||
789 | |||
790 | /* Getting schedule() right is a bit tricky. schedule() may not make any | 790 | /* Getting schedule() right is a bit tricky. schedule() may not make any |
791 | * assumptions on the state of the current task since it may be called for a | 791 | * assumptions on the state of the current task since it may be called for a |
792 | * number of reasons. The reasons include a scheduler_tick() determined that it | 792 | * number of reasons. The reasons include a scheduler_tick() determined that it |
@@ -812,7 +812,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
812 | { | 812 | { |
813 | cpu_entry_t* entry = &__get_cpu_var(cedf_cpu_entries); | 813 | cpu_entry_t* entry = &__get_cpu_var(cedf_cpu_entries); |
814 | cedf_domain_t *cluster = entry->cluster; | 814 | cedf_domain_t *cluster = entry->cluster; |
815 | int out_of_time, signal_budget, sleep, preempt, np, exists, blocks; | 815 | int out_of_time, sleep, preempt, np, exists, blocks; |
816 | struct task_struct* next = NULL; | 816 | struct task_struct* next = NULL; |
817 | 817 | ||
818 | #ifdef CONFIG_RELEASE_MASTER | 818 | #ifdef CONFIG_RELEASE_MASTER |
@@ -838,11 +838,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
838 | blocks = exists && !is_running(entry->scheduled); | 838 | blocks = exists && !is_running(entry->scheduled); |
839 | out_of_time = exists && | 839 | out_of_time = exists && |
840 | budget_enforced(entry->scheduled) && | 840 | budget_enforced(entry->scheduled) && |
841 | budget_exhausted(entry->scheduled); | 841 | bt_flag_is_set(entry->scheduled, BTF_BUDGET_EXHAUSTED); |
842 | signal_budget = exists && | ||
843 | budget_signalled(entry->scheduled) && | ||
844 | budget_exhausted(entry->scheduled) && | ||
845 | !sigbudget_sent(entry->scheduled); | ||
846 | np = exists && is_np(entry->scheduled); | 842 | np = exists && is_np(entry->scheduled); |
847 | sleep = exists && is_completed(entry->scheduled); | 843 | sleep = exists && is_completed(entry->scheduled); |
848 | preempt = entry->scheduled != entry->linked; | 844 | preempt = entry->scheduled != entry->linked; |
@@ -851,12 +847,13 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
851 | TRACE_TASK(prev, "invoked cedf_schedule.\n"); | 847 | TRACE_TASK(prev, "invoked cedf_schedule.\n"); |
852 | #endif | 848 | #endif |
853 | 849 | ||
854 | if (exists) | 850 | if (exists) { |
855 | TRACE_TASK(prev, | 851 | TRACE_TASK(prev, |
856 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " | 852 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " |
857 | "state:%d sig:%d\n", | 853 | "state:%d sig:%d\n", |
858 | blocks, out_of_time, np, sleep, preempt, | 854 | blocks, out_of_time, np, sleep, preempt, |
859 | prev->state, signal_pending(prev)); | 855 | prev->state, signal_pending(prev)); |
856 | } | ||
860 | if (entry->linked && preempt) | 857 | if (entry->linked && preempt) |
861 | TRACE_TASK(prev, "will be preempted by %s/%d\n", | 858 | TRACE_TASK(prev, "will be preempted by %s/%d\n", |
862 | entry->linked->comm, entry->linked->pid); | 859 | entry->linked->comm, entry->linked->pid); |
@@ -872,9 +869,13 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
872 | } | 869 | } |
873 | #endif | 870 | #endif |
874 | 871 | ||
875 | /* Send the signal that the budget has been exhausted */ | 872 | /* Do budget stuff */ |
876 | if (signal_budget) | 873 | if (tsk_rt(prev)->budget.ops) { |
877 | send_sigbudget(entry->scheduled); | 874 | if (blocks) |
875 | tsk_rt(prev)->budget.ops->on_blocked(prev); | ||
876 | else if (preempt || sleep) | ||
877 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | ||
878 | } | ||
878 | 879 | ||
879 | /* If a task blocks we have no choice but to reschedule. | 880 | /* If a task blocks we have no choice but to reschedule. |
880 | */ | 881 | */ |
@@ -942,6 +943,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
942 | } | 943 | } |
943 | } | 944 | } |
944 | 945 | ||
946 | |||
945 | #ifdef CONFIG_REALTIME_AUX_TASKS | 947 | #ifdef CONFIG_REALTIME_AUX_TASKS |
946 | out_set_state: | 948 | out_set_state: |
947 | #endif | 949 | #endif |
@@ -1111,6 +1113,10 @@ static void cedf_task_exit(struct task_struct * t) | |||
1111 | /* unlink if necessary */ | 1113 | /* unlink if necessary */ |
1112 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | 1114 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); |
1113 | 1115 | ||
1116 | /* disable budget enforcement */ | ||
1117 | if (tsk_rt(t)->budget.ops) | ||
1118 | tsk_rt(t)->budget.ops->on_exit(t); | ||
1119 | |||
1114 | #ifdef CONFIG_REALTIME_AUX_TASKS | 1120 | #ifdef CONFIG_REALTIME_AUX_TASKS |
1115 | /* make sure we clean up on our way out */ | 1121 | /* make sure we clean up on our way out */ |
1116 | if (unlikely(tsk_rt(t)->is_aux_task)) { | 1122 | if (unlikely(tsk_rt(t)->is_aux_task)) { |
@@ -1141,15 +1147,44 @@ static void cedf_task_exit(struct task_struct * t) | |||
1141 | TRACE_TASK(t, "RIP\n"); | 1147 | TRACE_TASK(t, "RIP\n"); |
1142 | } | 1148 | } |
1143 | 1149 | ||
1150 | |||
1151 | |||
1152 | |||
1153 | |||
1154 | |||
1155 | static struct budget_tracker_ops cedf_drain_simple_ops = | ||
1156 | { | ||
1157 | .on_scheduled = simple_on_scheduled, | ||
1158 | .on_blocked = simple_on_blocked, | ||
1159 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | ||
1160 | .on_exit = simple_on_exit, | ||
1161 | |||
1162 | .on_exhausted = cedf_simple_on_exhausted, | ||
1163 | }; | ||
1164 | |||
1165 | |||
1144 | static long cedf_admit_task(struct task_struct* tsk) | 1166 | static long cedf_admit_task(struct task_struct* tsk) |
1145 | { | 1167 | { |
1168 | if (remote_cluster(task_cpu(tsk)) != task_cpu_cluster(tsk)) | ||
1169 | return -EINVAL; | ||
1170 | |||
1171 | if (budget_enforced(tsk) || budget_signalled(tsk)) { | ||
1172 | switch(get_drain_policy(tsk)) { | ||
1173 | case DRAIN_SIMPLE: | ||
1174 | init_budget_tracker(&tsk_rt(tsk)->budget, &cedf_drain_simple_ops); | ||
1175 | break; | ||
1176 | default: | ||
1177 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); | ||
1178 | return -EINVAL; | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1146 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | 1182 | #ifdef CONFIG_LITMUS_NESTED_LOCKING |
1147 | INIT_BINHEAP_HANDLE(&tsk_rt(tsk)->hp_blocked_tasks, | 1183 | INIT_BINHEAP_HANDLE(&tsk_rt(tsk)->hp_blocked_tasks, |
1148 | edf_max_heap_base_priority_order); | 1184 | edf_max_heap_base_priority_order); |
1149 | #endif | 1185 | #endif |
1150 | 1186 | ||
1151 | return (remote_cluster(task_cpu(tsk)) == task_cpu_cluster(tsk)) ? | 1187 | return 0; |
1152 | 0 : -EINVAL; | ||
1153 | } | 1188 | } |
1154 | 1189 | ||
1155 | 1190 | ||
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c index 08cdf5c0e492..15ac94038702 100644 --- a/litmus/sched_gsn_edf.c +++ b/litmus/sched_gsn_edf.c | |||
@@ -421,6 +421,33 @@ static noinline void job_completion(struct task_struct *t, int forced) | |||
421 | gsnedf_job_arrival(t); | 421 | gsnedf_job_arrival(t); |
422 | } | 422 | } |
423 | 423 | ||
424 | static void gsnedf_simple_on_exhausted(struct task_struct *t) | ||
425 | { | ||
426 | /* Assumption: t is scheduled on the CPU executing this callback */ | ||
427 | |||
428 | if (budget_signalled(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
429 | /* signal exhaustion */ | ||
430 | send_sigbudget(t); /* will set BTF_SIG_BUDGET_SENT */ | ||
431 | } | ||
432 | |||
433 | if (budget_enforced(t) && !bt_flag_test_and_set(t, BTF_BUDGET_EXHAUSTED)) { | ||
434 | if (!is_np(t)) { | ||
435 | /* np tasks will be preempted when they become | ||
436 | * preemptable again | ||
437 | */ | ||
438 | litmus_reschedule_local(); | ||
439 | TRACE("cedf_scheduler_tick: " | ||
440 | "%d is preemptable " | ||
441 | " => FORCE_RESCHED\n", t->pid); | ||
442 | } else if (is_user_np(t)) { | ||
443 | TRACE("cedf_scheduler_tick: " | ||
444 | "%d is non-preemptable, " | ||
445 | "preemption delayed.\n", t->pid); | ||
446 | request_exit_np(t); | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | |||
424 | /* gsnedf_tick - this function is called for every local timer | 451 | /* gsnedf_tick - this function is called for every local timer |
425 | * interrupt. | 452 | * interrupt. |
426 | * | 453 | * |
@@ -429,41 +456,16 @@ static noinline void job_completion(struct task_struct *t, int forced) | |||
429 | */ | 456 | */ |
430 | static void gsnedf_tick(struct task_struct* t) | 457 | static void gsnedf_tick(struct task_struct* t) |
431 | { | 458 | { |
432 | if (is_realtime(t) && budget_exhausted(t)) | 459 | if (is_realtime(t) && |
433 | { | 460 | tsk_rt(t)->budget.ops && budget_quantum_tracked(t) && |
434 | if (budget_signalled(t) && !sigbudget_sent(t)) { | 461 | budget_exhausted(t)) { |
435 | /* signal exhaustion */ | 462 | TRACE_TASK(t, "budget exhausted\n"); |
436 | send_sigbudget(t); | 463 | tsk_rt(t)->budget.ops->on_exhausted(t); |
437 | } | ||
438 | |||
439 | if (budget_enforced(t)) { | ||
440 | if (!is_np(t)) { | ||
441 | /* np tasks will be preempted when they become | ||
442 | * preemptable again | ||
443 | */ | ||
444 | litmus_reschedule_local(); | ||
445 | TRACE("gsnedf_scheduler_tick: " | ||
446 | "%d is preemptable " | ||
447 | " => FORCE_RESCHED\n", t->pid); | ||
448 | } else if (is_user_np(t)) { | ||
449 | TRACE("gsnedf_scheduler_tick: " | ||
450 | "%d is non-preemptable, " | ||
451 | "preemption delayed.\n", t->pid); | ||
452 | request_exit_np(t); | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | if(is_realtime(t)) { | ||
459 | TRACE_TASK(t, "tick %llu\n", litmus_clock()); | ||
460 | } | 464 | } |
461 | */ | ||
462 | } | 465 | } |
463 | 466 | ||
464 | 467 | ||
465 | 468 | ||
466 | |||
467 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | 469 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD |
468 | 470 | ||
469 | 471 | ||
@@ -797,11 +799,9 @@ static void gsnedf_change_prio_pai_tasklet(struct task_struct *old_prio, | |||
797 | static struct task_struct* gsnedf_schedule(struct task_struct * prev) | 799 | static struct task_struct* gsnedf_schedule(struct task_struct * prev) |
798 | { | 800 | { |
799 | cpu_entry_t* entry = &__get_cpu_var(gsnedf_cpu_entries); | 801 | cpu_entry_t* entry = &__get_cpu_var(gsnedf_cpu_entries); |
800 | int out_of_time, signal_budget, sleep, preempt, np, exists, blocks; | 802 | int out_of_time, sleep, preempt, np, exists, blocks; |
801 | struct task_struct* next = NULL; | 803 | struct task_struct* next = NULL; |
802 | 804 | ||
803 | //int completion = 0; | ||
804 | |||
805 | #ifdef CONFIG_RELEASE_MASTER | 805 | #ifdef CONFIG_RELEASE_MASTER |
806 | /* Bail out early if we are the release master. | 806 | /* Bail out early if we are the release master. |
807 | * The release master never schedules any real-time tasks. | 807 | * The release master never schedules any real-time tasks. |
@@ -824,11 +824,7 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
824 | blocks = exists && !is_running(entry->scheduled); | 824 | blocks = exists && !is_running(entry->scheduled); |
825 | out_of_time = exists && | 825 | out_of_time = exists && |
826 | budget_enforced(entry->scheduled) && | 826 | budget_enforced(entry->scheduled) && |
827 | budget_exhausted(entry->scheduled); | 827 | bt_flag_is_set(entry->scheduled, BTF_BUDGET_EXHAUSTED); |
828 | signal_budget = exists && | ||
829 | budget_signalled(entry->scheduled) && | ||
830 | budget_exhausted(entry->scheduled) && | ||
831 | !sigbudget_sent(entry->scheduled); | ||
832 | np = exists && is_np(entry->scheduled); | 828 | np = exists && is_np(entry->scheduled); |
833 | sleep = exists && is_completed(entry->scheduled); | 829 | sleep = exists && is_completed(entry->scheduled); |
834 | preempt = entry->scheduled != entry->linked; | 830 | preempt = entry->scheduled != entry->linked; |
@@ -839,9 +835,9 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
839 | 835 | ||
840 | if (exists) { | 836 | if (exists) { |
841 | TRACE_TASK(prev, | 837 | TRACE_TASK(prev, |
842 | "blocks:%d out_of_time:%d signal_budget: %d np:%d sleep:%d preempt:%d " | 838 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " |
843 | "state:%d sig:%d\n", | 839 | "state:%d sig:%d\n", |
844 | blocks, out_of_time, signal_budget, np, sleep, preempt, | 840 | blocks, out_of_time, np, sleep, preempt, |
845 | prev->state, signal_pending(prev)); | 841 | prev->state, signal_pending(prev)); |
846 | } | 842 | } |
847 | 843 | ||
@@ -849,9 +845,12 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
849 | TRACE_TASK(prev, "will be preempted by %s/%d\n", | 845 | TRACE_TASK(prev, "will be preempted by %s/%d\n", |
850 | entry->linked->comm, entry->linked->pid); | 846 | entry->linked->comm, entry->linked->pid); |
851 | 847 | ||
852 | /* Send the signal that the budget has been exhausted */ | 848 | /* Do budget stuff */ |
853 | if (signal_budget) { | 849 | if (tsk_rt(prev)->budget.ops) { |
854 | send_sigbudget(entry->scheduled); | 850 | if (blocks) |
851 | tsk_rt(prev)->budget.ops->on_blocked(prev); | ||
852 | else if (preempt || sleep) | ||
853 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | ||
855 | } | 854 | } |
856 | 855 | ||
857 | /* If a task blocks we have no choice but to reschedule. | 856 | /* If a task blocks we have no choice but to reschedule. |
@@ -1086,6 +1085,10 @@ static void gsnedf_task_exit(struct task_struct * t) | |||
1086 | /* unlink if necessary */ | 1085 | /* unlink if necessary */ |
1087 | raw_spin_lock_irqsave(&gsnedf_lock, flags); | 1086 | raw_spin_lock_irqsave(&gsnedf_lock, flags); |
1088 | 1087 | ||
1088 | /* disable budget enforcement */ | ||
1089 | if (tsk_rt(t)->budget.ops) | ||
1090 | tsk_rt(t)->budget.ops->on_exit(t); | ||
1091 | |||
1089 | #ifdef CONFIG_REALTIME_AUX_TASKS | 1092 | #ifdef CONFIG_REALTIME_AUX_TASKS |
1090 | /* make sure we clean up on our way out */ | 1093 | /* make sure we clean up on our way out */ |
1091 | if (unlikely(tsk_rt(t)->is_aux_task)) { | 1094 | if (unlikely(tsk_rt(t)->is_aux_task)) { |
@@ -1115,8 +1118,29 @@ static void gsnedf_task_exit(struct task_struct * t) | |||
1115 | } | 1118 | } |
1116 | 1119 | ||
1117 | 1120 | ||
1121 | static struct budget_tracker_ops gsnedf_drain_simple_ops = | ||
1122 | { | ||
1123 | .on_scheduled = simple_on_scheduled, | ||
1124 | .on_blocked = simple_on_blocked, | ||
1125 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | ||
1126 | .on_exit = simple_on_exit, | ||
1127 | |||
1128 | .on_exhausted = gsnedf_simple_on_exhausted, | ||
1129 | }; | ||
1130 | |||
1118 | static long gsnedf_admit_task(struct task_struct* tsk) | 1131 | static long gsnedf_admit_task(struct task_struct* tsk) |
1119 | { | 1132 | { |
1133 | if (budget_enforced(tsk) || budget_signalled(tsk)) { | ||
1134 | switch(get_drain_policy(tsk)) { | ||
1135 | case DRAIN_SIMPLE: | ||
1136 | init_budget_tracker(&tsk_rt(tsk)->budget, &gsnedf_drain_simple_ops); | ||
1137 | break; | ||
1138 | default: | ||
1139 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); | ||
1140 | return -EINVAL; | ||
1141 | } | ||
1142 | } | ||
1143 | |||
1120 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | 1144 | #ifdef CONFIG_LITMUS_NESTED_LOCKING |
1121 | INIT_BINHEAP_HANDLE(&tsk_rt(tsk)->hp_blocked_tasks, | 1145 | INIT_BINHEAP_HANDLE(&tsk_rt(tsk)->hp_blocked_tasks, |
1122 | edf_max_heap_base_priority_order); | 1146 | edf_max_heap_base_priority_order); |
@@ -1126,10 +1150,6 @@ static long gsnedf_admit_task(struct task_struct* tsk) | |||
1126 | } | 1150 | } |
1127 | 1151 | ||
1128 | 1152 | ||
1129 | |||
1130 | |||
1131 | |||
1132 | |||
1133 | #ifdef CONFIG_LITMUS_LOCKING | 1153 | #ifdef CONFIG_LITMUS_LOCKING |
1134 | 1154 | ||
1135 | #include <litmus/fdso.h> | 1155 | #include <litmus/fdso.h> |
diff --git a/litmus/sched_litmus.c b/litmus/sched_litmus.c index 9de03c95b825..60b58bb29ac4 100644 --- a/litmus/sched_litmus.c +++ b/litmus/sched_litmus.c | |||
@@ -51,7 +51,7 @@ litmus_schedule(struct rq *rq, struct task_struct *prev) | |||
51 | lt_t _maybe_deadlock = 0; | 51 | lt_t _maybe_deadlock = 0; |
52 | 52 | ||
53 | /* let the plugin schedule */ | 53 | /* let the plugin schedule */ |
54 | next = litmus->schedule(prev); | 54 | next = litmus->schedule(prev); /* may disable prev's budget timer */ |
55 | 55 | ||
56 | sched_state_plugin_check(); | 56 | sched_state_plugin_check(); |
57 | 57 | ||
@@ -150,9 +150,11 @@ litmus_schedule(struct rq *rq, struct task_struct *prev) | |||
150 | if (next) { | 150 | if (next) { |
151 | next->rt_param.stack_in_use = rq->cpu; | 151 | next->rt_param.stack_in_use = rq->cpu; |
152 | next->se.exec_start = rq->clock; | 152 | next->se.exec_start = rq->clock; |
153 | |||
154 | if (is_realtime(next) && tsk_rt(next)->budget.ops) | ||
155 | tsk_rt(next)->budget.ops->on_scheduled(next); | ||
153 | } | 156 | } |
154 | 157 | ||
155 | update_enforcement_timer(next); | ||
156 | return next; | 158 | return next; |
157 | } | 159 | } |
158 | 160 | ||
diff --git a/litmus/sched_pfair.c b/litmus/sched_pfair.c index d5fb3a832adc..c06326faf9ce 100644 --- a/litmus/sched_pfair.c +++ b/litmus/sched_pfair.c | |||
@@ -837,6 +837,16 @@ static void dump_subtasks(struct task_struct* t) | |||
837 | t->rt_param.pfair->subtasks[i].group_deadline); | 837 | t->rt_param.pfair->subtasks[i].group_deadline); |
838 | } | 838 | } |
839 | 839 | ||
840 | static struct budget_tracker_ops pfair_drain_simple_ops = | ||
841 | { | ||
842 | .on_scheduled = simple_on_scheduled, | ||
843 | .on_blocked = simple_on_blocked, | ||
844 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | ||
845 | .on_exit = simple_on_exit, | ||
846 | |||
847 | .on_exhausted = pfair_simple_on_exhausted, | ||
848 | }; | ||
849 | |||
840 | static long pfair_admit_task(struct task_struct* t) | 850 | static long pfair_admit_task(struct task_struct* t) |
841 | { | 851 | { |
842 | lt_t quanta; | 852 | lt_t quanta; |
diff --git a/litmus/sched_pfp.c b/litmus/sched_pfp.c index 6edec830f063..4a8b8e084f6e 100644 --- a/litmus/sched_pfp.c +++ b/litmus/sched_pfp.c | |||
@@ -132,6 +132,33 @@ static void job_completion(struct task_struct* t, int forced) | |||
132 | sched_trace_task_release(t); | 132 | sched_trace_task_release(t); |
133 | } | 133 | } |
134 | 134 | ||
135 | static void pfp_simple_on_exhausted(struct task_struct *t) | ||
136 | { | ||
137 | /* Assumption: t is scheduled on the CPU executing this callback */ | ||
138 | |||
139 | if (budget_signalled(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
140 | /* signal exhaustion */ | ||
141 | send_sigbudget(t); /* will set BTF_SIG_BUDGET_SENT */ | ||
142 | } | ||
143 | |||
144 | if (budget_enforced(t) && !bt_flag_test_and_set(t, BTF_BUDGET_EXHAUSTED)) { | ||
145 | if (!is_np(t)) { | ||
146 | /* np tasks will be preempted when they become | ||
147 | * preemptable again | ||
148 | */ | ||
149 | litmus_reschedule_local(); | ||
150 | TRACE("cedf_scheduler_tick: " | ||
151 | "%d is preemptable " | ||
152 | " => FORCE_RESCHED\n", t->pid); | ||
153 | } else if (is_user_np(t)) { | ||
154 | TRACE("cedf_scheduler_tick: " | ||
155 | "%d is non-preemptable, " | ||
156 | "preemption delayed.\n", t->pid); | ||
157 | request_exit_np(t); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
135 | static void pfp_tick(struct task_struct *t) | 162 | static void pfp_tick(struct task_struct *t) |
136 | { | 163 | { |
137 | pfp_domain_t *pfp = local_pfp; | 164 | pfp_domain_t *pfp = local_pfp; |
@@ -142,26 +169,11 @@ static void pfp_tick(struct task_struct *t) | |||
142 | */ | 169 | */ |
143 | BUG_ON(is_realtime(t) && t != pfp->scheduled); | 170 | BUG_ON(is_realtime(t) && t != pfp->scheduled); |
144 | 171 | ||
145 | if (is_realtime(t) && budget_exhausted(t)) | 172 | if (is_realtime(t) && |
146 | { | 173 | tsk_rt(t)->budget.ops && budget_quantum_tracked(t) && |
147 | if (budget_signalled(t) && !sigbudget_sent(t)) { | 174 | budget_exhausted(t)) { |
148 | /* signal exhaustion */ | 175 | TRACE_TASK(t, "budget exhausted\n"); |
149 | send_sigbudget(t); | 176 | tsk_rt(t)->budget.ops->on_exhausted(t); |
150 | } | ||
151 | |||
152 | if (budget_enforced(t)) { | ||
153 | if (!is_np(t)) { | ||
154 | litmus_reschedule_local(); | ||
155 | TRACE("pfp_scheduler_tick: " | ||
156 | "%d is preemptable " | ||
157 | " => FORCE_RESCHED\n", t->pid); | ||
158 | } else if (is_user_np(t)) { | ||
159 | TRACE("pfp_scheduler_tick: " | ||
160 | "%d is non-preemptable, " | ||
161 | "preemption delayed.\n", t->pid); | ||
162 | request_exit_np(t); | ||
163 | } | ||
164 | } | ||
165 | } | 177 | } |
166 | } | 178 | } |
167 | 179 | ||
@@ -170,7 +182,7 @@ static struct task_struct* pfp_schedule(struct task_struct * prev) | |||
170 | pfp_domain_t* pfp = local_pfp; | 182 | pfp_domain_t* pfp = local_pfp; |
171 | struct task_struct* next; | 183 | struct task_struct* next; |
172 | 184 | ||
173 | int out_of_time, signal_budget, sleep, preempt, np, exists, blocks, resched, migrate; | 185 | int out_of_time, sleep, preempt, np, exists, blocks, resched, migrate; |
174 | 186 | ||
175 | raw_spin_lock(&pfp->slock); | 187 | raw_spin_lock(&pfp->slock); |
176 | 188 | ||
@@ -186,11 +198,7 @@ static struct task_struct* pfp_schedule(struct task_struct * prev) | |||
186 | blocks = exists && !is_running(pfp->scheduled); | 198 | blocks = exists && !is_running(pfp->scheduled); |
187 | out_of_time = exists && | 199 | out_of_time = exists && |
188 | budget_enforced(pfp->scheduled) && | 200 | budget_enforced(pfp->scheduled) && |
189 | budget_exhausted(pfp->scheduled); | 201 | bt_flag_is_set(pfp->scheduled, BTF_BUDGET_EXHAUSTED); |
190 | signal_budget = exists && | ||
191 | budget_signalled(pfp->scheduled) && | ||
192 | budget_exhausted(pfp->scheduled) && | ||
193 | !sigbudget_sent(pfp->scheduled); | ||
194 | np = exists && is_np(pfp->scheduled); | 202 | np = exists && is_np(pfp->scheduled); |
195 | sleep = exists && is_completed(pfp->scheduled); | 203 | sleep = exists && is_completed(pfp->scheduled); |
196 | migrate = exists && get_partition(pfp->scheduled) != pfp->cpu; | 204 | migrate = exists && get_partition(pfp->scheduled) != pfp->cpu; |
@@ -202,9 +210,13 @@ static struct task_struct* pfp_schedule(struct task_struct * prev) | |||
202 | */ | 210 | */ |
203 | resched = preempt; | 211 | resched = preempt; |
204 | 212 | ||
205 | /* Send the signal that the budget has been exhausted */ | 213 | /* Do budget stuff */ |
206 | if (signal_budget) | 214 | if (tsk_rt(prev)->budget.ops) { |
207 | send_sigbudget(pfp->scheduled); | 215 | if (blocks) |
216 | tsk_rt(prev)->budget.ops->on_blocked(prev); | ||
217 | else if (preempt || sleep) | ||
218 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | ||
219 | } | ||
208 | 220 | ||
209 | /* If a task blocks we have no choice but to reschedule. | 221 | /* If a task blocks we have no choice but to reschedule. |
210 | */ | 222 | */ |
@@ -418,6 +430,11 @@ static void pfp_task_exit(struct task_struct * t) | |||
418 | rt_domain_t* dom; | 430 | rt_domain_t* dom; |
419 | 431 | ||
420 | raw_spin_lock_irqsave(&pfp->slock, flags); | 432 | raw_spin_lock_irqsave(&pfp->slock, flags); |
433 | |||
434 | /* disable budget enforcement */ | ||
435 | if (tsk_rt(t)->budget.ops) | ||
436 | tsk_rt(t)->budget.ops->on_exit(t); | ||
437 | |||
421 | if (is_queued(t)) { | 438 | if (is_queued(t)) { |
422 | BUG(); /* This currently doesn't work. */ | 439 | BUG(); /* This currently doesn't work. */ |
423 | /* dequeue */ | 440 | /* dequeue */ |
@@ -1692,17 +1709,43 @@ static long pfp_allocate_lock(struct litmus_lock **lock, int type, | |||
1692 | 1709 | ||
1693 | #endif | 1710 | #endif |
1694 | 1711 | ||
1712 | static struct budget_tracker_ops pfp_drain_simple_ops = | ||
1713 | { | ||
1714 | .on_scheduled = simple_on_scheduled, | ||
1715 | .on_blocked = simple_on_blocked, | ||
1716 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | ||
1717 | .on_exit = simple_on_exit, | ||
1718 | |||
1719 | .on_exhausted = pfp_simple_on_exhausted, | ||
1720 | }; | ||
1721 | |||
1695 | static long pfp_admit_task(struct task_struct* tsk) | 1722 | static long pfp_admit_task(struct task_struct* tsk) |
1696 | { | 1723 | { |
1724 | long ret = 0; | ||
1725 | |||
1697 | if (task_cpu(tsk) == tsk->rt_param.task_params.cpu && | 1726 | if (task_cpu(tsk) == tsk->rt_param.task_params.cpu && |
1698 | #ifdef CONFIG_RELEASE_MASTER | 1727 | #ifdef CONFIG_RELEASE_MASTER |
1699 | /* don't allow tasks on release master CPU */ | 1728 | /* don't allow tasks on release master CPU */ |
1700 | task_cpu(tsk) != remote_dom(task_cpu(tsk))->release_master && | 1729 | task_cpu(tsk) != remote_dom(task_cpu(tsk))->release_master && |
1701 | #endif | 1730 | #endif |
1702 | litmus_is_valid_fixed_prio(get_priority(tsk))) | 1731 | litmus_is_valid_fixed_prio(get_priority(tsk))) { |
1703 | return 0; | 1732 | |
1733 | if (budget_enforced(tsk) || budget_signalled(tsk)) { | ||
1734 | switch(get_drain_policy(tsk)) { | ||
1735 | case DRAIN_SIMPLE: | ||
1736 | init_budget_tracker(&tsk_rt(tsk)->budget, &pfp_drain_simple_ops); | ||
1737 | break; | ||
1738 | default: | ||
1739 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); | ||
1740 | ret = -EINVAL; | ||
1741 | break; | ||
1742 | } | ||
1743 | } | ||
1744 | } | ||
1704 | else | 1745 | else |
1705 | return -EINVAL; | 1746 | ret = -EINVAL; |
1747 | |||
1748 | return ret; | ||
1706 | } | 1749 | } |
1707 | 1750 | ||
1708 | static long pfp_activate_plugin(void) | 1751 | static long pfp_activate_plugin(void) |
diff --git a/litmus/sched_psn_edf.c b/litmus/sched_psn_edf.c index d1177cab152d..3b3edfe908ff 100644 --- a/litmus/sched_psn_edf.c +++ b/litmus/sched_psn_edf.c | |||
@@ -164,6 +164,33 @@ static void job_completion(struct task_struct* t, int forced) | |||
164 | prepare_for_next_period(t); | 164 | prepare_for_next_period(t); |
165 | } | 165 | } |
166 | 166 | ||
167 | static void psnedf_simple_on_exhausted(struct task_struct *t) | ||
168 | { | ||
169 | /* Assumption: t is scheduled on the CPU executing this callback */ | ||
170 | |||
171 | if (budget_signalled(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
172 | /* signal exhaustion */ | ||
173 | send_sigbudget(t); /* will set BTF_SIG_BUDGET_SENT */ | ||
174 | } | ||
175 | |||
176 | if (budget_enforced(t) && !bt_flag_test_and_set(t, BTF_BUDGET_EXHAUSTED)) { | ||
177 | if (!is_np(t)) { | ||
178 | /* np tasks will be preempted when they become | ||
179 | * preemptable again | ||
180 | */ | ||
181 | litmus_reschedule_local(); | ||
182 | TRACE("cedf_scheduler_tick: " | ||
183 | "%d is preemptable " | ||
184 | " => FORCE_RESCHED\n", t->pid); | ||
185 | } else if (is_user_np(t)) { | ||
186 | TRACE("cedf_scheduler_tick: " | ||
187 | "%d is non-preemptable, " | ||
188 | "preemption delayed.\n", t->pid); | ||
189 | request_exit_np(t); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
167 | static void psnedf_tick(struct task_struct *t) | 194 | static void psnedf_tick(struct task_struct *t) |
168 | { | 195 | { |
169 | psnedf_domain_t *pedf = local_pedf; | 196 | psnedf_domain_t *pedf = local_pedf; |
@@ -174,26 +201,11 @@ static void psnedf_tick(struct task_struct *t) | |||
174 | */ | 201 | */ |
175 | BUG_ON(is_realtime(t) && t != pedf->scheduled); | 202 | BUG_ON(is_realtime(t) && t != pedf->scheduled); |
176 | 203 | ||
177 | if (is_realtime(t) && budget_exhausted(t)) | 204 | if (is_realtime(t) && |
178 | { | 205 | tsk_rt(t)->budget.ops && budget_quantum_tracked(t) && |
179 | if (budget_signalled(t) && !sigbudget_sent(t)) { | 206 | budget_exhausted(t)) { |
180 | /* signal exhaustion */ | 207 | TRACE_TASK(t, "budget exhausted\n"); |
181 | send_sigbudget(t); | 208 | tsk_rt(t)->budget.ops->on_exhausted(t); |
182 | } | ||
183 | |||
184 | if (budget_enforced(t)) { | ||
185 | if (!is_np(t)) { | ||
186 | litmus_reschedule_local(); | ||
187 | TRACE("psnedf_scheduler_tick: " | ||
188 | "%d is preemptable " | ||
189 | " => FORCE_RESCHED\n", t->pid); | ||
190 | } else if (is_user_np(t)) { | ||
191 | TRACE("psnedf_scheduler_tick: " | ||
192 | "%d is non-preemptable, " | ||
193 | "preemption delayed.\n", t->pid); | ||
194 | request_exit_np(t); | ||
195 | } | ||
196 | } | ||
197 | } | 209 | } |
198 | } | 210 | } |
199 | 211 | ||
@@ -203,7 +215,7 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
203 | rt_domain_t* edf = &pedf->domain; | 215 | rt_domain_t* edf = &pedf->domain; |
204 | struct task_struct* next; | 216 | struct task_struct* next; |
205 | 217 | ||
206 | int out_of_time, signal_budget, sleep, preempt, np, exists, blocks, resched; | 218 | int out_of_time, sleep, preempt, np, exists, blocks, resched; |
207 | 219 | ||
208 | raw_spin_lock(&pedf->slock); | 220 | raw_spin_lock(&pedf->slock); |
209 | 221 | ||
@@ -219,11 +231,7 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
219 | blocks = exists && !is_running(pedf->scheduled); | 231 | blocks = exists && !is_running(pedf->scheduled); |
220 | out_of_time = exists && | 232 | out_of_time = exists && |
221 | budget_enforced(pedf->scheduled) && | 233 | budget_enforced(pedf->scheduled) && |
222 | budget_exhausted(pedf->scheduled); | 234 | bt_flag_is_set(pedf->scheduled, BTF_BUDGET_EXHAUSTED); |
223 | signal_budget = exists && | ||
224 | budget_signalled(pedf->scheduled) && | ||
225 | budget_exhausted(pedf->scheduled) && | ||
226 | !sigbudget_sent(pedf->scheduled); | ||
227 | np = exists && is_np(pedf->scheduled); | 235 | np = exists && is_np(pedf->scheduled); |
228 | sleep = exists && is_completed(pedf->scheduled); | 236 | sleep = exists && is_completed(pedf->scheduled); |
229 | preempt = edf_preemption_needed(edf, prev); | 237 | preempt = edf_preemption_needed(edf, prev); |
@@ -234,9 +242,13 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
234 | */ | 242 | */ |
235 | resched = preempt; | 243 | resched = preempt; |
236 | 244 | ||
237 | /* Send the signal that the budget has been exhausted */ | 245 | /* Do budget stuff */ |
238 | if (signal_budget) | 246 | if (tsk_rt(prev)->budget.ops) { |
239 | send_sigbudget(pedf->scheduled); | 247 | if (blocks) |
248 | tsk_rt(prev)->budget.ops->on_blocked(prev); | ||
249 | else if (preempt || sleep) | ||
250 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | ||
251 | } | ||
240 | 252 | ||
241 | /* If a task blocks we have no choice but to reschedule. | 253 | /* If a task blocks we have no choice but to reschedule. |
242 | */ | 254 | */ |
@@ -380,6 +392,11 @@ static void psnedf_task_exit(struct task_struct * t) | |||
380 | rt_domain_t* edf; | 392 | rt_domain_t* edf; |
381 | 393 | ||
382 | raw_spin_lock_irqsave(&pedf->slock, flags); | 394 | raw_spin_lock_irqsave(&pedf->slock, flags); |
395 | |||
396 | /* disable budget enforcement */ | ||
397 | if (tsk_rt(t)->budget.ops) | ||
398 | tsk_rt(t)->budget.ops->on_exit(t); | ||
399 | |||
383 | if (is_queued(t)) { | 400 | if (is_queued(t)) { |
384 | /* dequeue */ | 401 | /* dequeue */ |
385 | edf = task_edf(t); | 402 | edf = task_edf(t); |
@@ -626,17 +643,43 @@ static long psnedf_activate_plugin(void) | |||
626 | return 0; | 643 | return 0; |
627 | } | 644 | } |
628 | 645 | ||
646 | static struct budget_tracker_ops psnedf_drain_simple_ops = | ||
647 | { | ||
648 | .on_scheduled = simple_on_scheduled, | ||
649 | .on_blocked = simple_on_blocked, | ||
650 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | ||
651 | .on_exit = simple_on_exit, | ||
652 | |||
653 | .on_exhausted = psnedf_simple_on_exhausted, | ||
654 | }; | ||
655 | |||
629 | static long psnedf_admit_task(struct task_struct* tsk) | 656 | static long psnedf_admit_task(struct task_struct* tsk) |
630 | { | 657 | { |
658 | long ret = 0; | ||
659 | |||
631 | if (task_cpu(tsk) == tsk->rt_param.task_params.cpu | 660 | if (task_cpu(tsk) == tsk->rt_param.task_params.cpu |
632 | #ifdef CONFIG_RELEASE_MASTER | 661 | #ifdef CONFIG_RELEASE_MASTER |
633 | /* don't allow tasks on release master CPU */ | 662 | /* don't allow tasks on release master CPU */ |
634 | && task_cpu(tsk) != remote_edf(task_cpu(tsk))->release_master | 663 | && task_cpu(tsk) != remote_edf(task_cpu(tsk))->release_master |
635 | #endif | 664 | #endif |
636 | ) | 665 | ) { |
637 | return 0; | 666 | |
667 | if (budget_enforced(tsk) || budget_signalled(tsk)) { | ||
668 | switch(get_drain_policy(tsk)) { | ||
669 | case DRAIN_SIMPLE: | ||
670 | init_budget_tracker(&tsk_rt(tsk)->budget, &psnedf_drain_simple_ops); | ||
671 | break; | ||
672 | default: | ||
673 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); | ||
674 | ret = -EINVAL; | ||
675 | break; | ||
676 | } | ||
677 | } | ||
678 | } | ||
638 | else | 679 | else |
639 | return -EINVAL; | 680 | ret = -EINVAL; |
681 | |||
682 | return ret; | ||
640 | } | 683 | } |
641 | 684 | ||
642 | /* Plugin object */ | 685 | /* Plugin object */ |