diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-21 18:43:43 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-21 18:47:18 -0400 |
commit | 7bbf3205ae1979cb41fd2a0dfdd103656bf8e84e (patch) | |
tree | b7ffd5eacbd6f11c42ec1e4cee0bc2041bcabd79 | |
parent | 469aaad39c956446b8a31d351ee36bedd87ac18a (diff) |
SOBLIV draining support for C-EDF.wip-2012.3-gpu
Adds support for suspension-oblivous budget draining
to C-EDF. Also changes how jobs with exhausted budget
in C-EDF are treated: jobs are early released until
they catch up.
-rw-r--r-- | include/litmus/budget.h | 27 | ||||
-rw-r--r-- | include/litmus/litmus.h | 6 | ||||
-rw-r--r-- | include/litmus/locking.h | 8 | ||||
-rw-r--r-- | include/litmus/rt_param.h | 30 | ||||
-rw-r--r-- | include/litmus/sched_trace.h | 9 | ||||
-rw-r--r-- | litmus/budget.c | 77 | ||||
-rw-r--r-- | litmus/edf_common.c | 2 | ||||
-rw-r--r-- | litmus/litmus.c | 2 | ||||
-rw-r--r-- | litmus/sched_cedf.c | 196 | ||||
-rw-r--r-- | litmus/sched_gsn_edf.c | 13 | ||||
-rw-r--r-- | litmus/sched_pfp.c | 13 | ||||
-rw-r--r-- | litmus/sched_psn_edf.c | 13 | ||||
-rw-r--r-- | litmus/sched_task_trace.c | 4 |
13 files changed, 337 insertions, 63 deletions
diff --git a/include/litmus/budget.h b/include/litmus/budget.h index 2a3511245f7a..72f04777e0b0 100644 --- a/include/litmus/budget.h +++ b/include/litmus/budget.h | |||
@@ -54,15 +54,17 @@ struct enforcement_timer | |||
54 | 54 | ||
55 | typedef void (*scheduled_t)(struct task_struct* t); | 55 | typedef void (*scheduled_t)(struct task_struct* t); |
56 | typedef void (*blocked_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); | 57 | typedef void (*preempt_t)(struct task_struct* t); |
58 | typedef void (*exhausted_t)(struct task_struct* t); | 58 | typedef void (*sleep_t)(struct task_struct* t); |
59 | typedef enum hrtimer_restart (*exhausted_t)(struct task_struct* t); | ||
59 | typedef void (*exit_t)(struct task_struct* t); | 60 | typedef void (*exit_t)(struct task_struct* t); |
60 | 61 | ||
61 | struct budget_tracker_ops | 62 | struct budget_tracker_ops |
62 | { | 63 | { |
63 | scheduled_t on_scheduled; /* called from litmus_schedule(). */ | 64 | scheduled_t on_scheduled; /* called from litmus_schedule(). */ |
64 | blocked_t on_blocked; /* called from plugin::schedule() */ | 65 | blocked_t on_blocked; /* called from plugin::schedule() */ |
65 | preempt_or_sleep_t on_preempt_or_sleep; /* called from plugin::schedule() */ | 66 | preempt_t on_preempt; /* called from plugin::schedule() */ |
67 | sleep_t on_sleep; /* called from plugin::schedule() */ | ||
66 | 68 | ||
67 | exit_t on_exit; /* task exiting rt mode */ | 69 | exit_t on_exit; /* task exiting rt mode */ |
68 | 70 | ||
@@ -84,15 +86,30 @@ enum BT_FLAGS | |||
84 | }; | 86 | }; |
85 | 87 | ||
86 | /* Functions for simple DRAIN_SIMPLE policy common | 88 | /* Functions for simple DRAIN_SIMPLE policy common |
87 | * to every scheduler. Scheduler must provided | 89 | * to every scheduler. Scheduler must provide |
88 | * implementation for simple_on_exhausted(). | 90 | * implementation for simple_on_exhausted(). |
89 | */ | 91 | */ |
90 | void simple_on_scheduled(struct task_struct* t); | 92 | void simple_on_scheduled(struct task_struct* t); |
91 | void simple_on_blocked(struct task_struct* t); | 93 | void simple_on_blocked(struct task_struct* t); |
92 | void simple_on_preempt_or_sleep(struct task_struct* t); | 94 | void simple_on_preempt(struct task_struct* t); |
95 | void simple_on_sleep(struct task_struct* t); | ||
93 | void simple_on_exit(struct task_struct* t); | 96 | void simple_on_exit(struct task_struct* t); |
94 | 97 | ||
95 | 98 | ||
99 | /* Functions for DRAIN_SOBLIV policy common | ||
100 | * to every scheduler. Scheduler must provide | ||
101 | * implementation for sobliv_on_exhausted(). | ||
102 | * | ||
103 | * Limitation: Quantum budget tracking is unsupported. | ||
104 | */ | ||
105 | void sobliv_on_scheduled(struct task_struct* t); | ||
106 | void sobliv_on_blocked(struct task_struct* t); | ||
107 | void sobliv_on_sleep(struct task_struct* t); | ||
108 | /* Use the DRAIN_SIMPLE implementations */ | ||
109 | #define sobliv_on_preempt simple_on_preempt | ||
110 | #define sobliv_on_exit simple_on_exit | ||
111 | |||
112 | |||
96 | void init_budget_tracker(struct budget_tracker* bt, | 113 | void init_budget_tracker(struct budget_tracker* bt, |
97 | const struct budget_tracker_ops* ops); | 114 | const struct budget_tracker_ops* ops); |
98 | 115 | ||
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h index f6ea5f6e80ee..ce24e62eee81 100644 --- a/include/litmus/litmus.h +++ b/include/litmus/litmus.h | |||
@@ -70,7 +70,11 @@ void litmus_exit_task(struct task_struct *tsk); | |||
70 | #define get_period(t) (tsk_rt(t)->task_params.period) | 70 | #define get_period(t) (tsk_rt(t)->task_params.period) |
71 | #define get_release(t) (tsk_rt(t)->job_params.release) | 71 | #define get_release(t) (tsk_rt(t)->job_params.release) |
72 | #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) | 73 | #define get_backlog(t) (tsk_rt(t)->job_params.backlog) |
74 | |||
75 | #define has_backlog(t) (get_backlog(t) != 0) | ||
76 | |||
77 | #define get_budget_timer(t) (tsk_rt(t)->budget) | ||
74 | 78 | ||
75 | #define effective_priority(t) ((!(tsk_rt(t)->inh_task)) ? t : tsk_rt(t)->inh_task) | 79 | #define effective_priority(t) ((!(tsk_rt(t)->inh_task)) ? t : tsk_rt(t)->inh_task) |
76 | #define base_priority(t) (t) | 80 | #define base_priority(t) (t) |
diff --git a/include/litmus/locking.h b/include/litmus/locking.h index 3ae6692dbe95..962ad5e6726a 100644 --- a/include/litmus/locking.h +++ b/include/litmus/locking.h | |||
@@ -229,5 +229,13 @@ struct litmus_lock_ops { | |||
229 | void suspend_for_lock(void); | 229 | void suspend_for_lock(void); |
230 | int wake_up_for_lock(struct task_struct* t); | 230 | int wake_up_for_lock(struct task_struct* t); |
231 | 231 | ||
232 | /* thread safe?? */ | ||
233 | #ifndef CONFIG_LITMUS_NESTED_LOCKING | ||
234 | #define holds_locks(tsk) \ | ||
235 | (tsk_rt(t)->num_locks_held || tsk_rt(t)->num_local_locks_held) | ||
236 | #else | ||
237 | #define holds_locks(tsk) \ | ||
238 | (tsk_rt(t)->num_locks_held || tsk_rt(t)->num_local_locks_held || !binheap_empty(&tsk_rt(t)->hp_blocked_tasks)) | ||
232 | #endif | 239 | #endif |
233 | 240 | ||
241 | #endif | ||
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index 887075b908ca..499ecd899fcd 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
@@ -257,6 +257,19 @@ struct rt_job { | |||
257 | * Increase this sequence number when a job is released. | 257 | * Increase this sequence number when a job is released. |
258 | */ | 258 | */ |
259 | unsigned int job_no; | 259 | unsigned int job_no; |
260 | |||
261 | /* Increments each time a job is forced to complete by | ||
262 | * budget exhaustion. If a job completes without remaining | ||
263 | * budget, the next job will be early-released _without_ | ||
264 | * pushing back its deadline. job_backlog is decremented once | ||
265 | * per early release. This behavior continues until | ||
266 | * backlog == 0. | ||
267 | */ | ||
268 | unsigned int backlog; | ||
269 | |||
270 | /* denotes if the current job is a backlogged job that was caused | ||
271 | * by an earlier budget exhaustion */ | ||
272 | unsigned int is_backlogged_job:1; | ||
260 | }; | 273 | }; |
261 | 274 | ||
262 | struct pfair_param; | 275 | struct pfair_param; |
@@ -387,6 +400,14 @@ struct rt_param { | |||
387 | unsigned int num_local_locks_held; | 400 | unsigned int num_local_locks_held; |
388 | #endif | 401 | #endif |
389 | 402 | ||
403 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
404 | raw_spinlock_t hp_blocked_tasks_lock; | ||
405 | struct binheap hp_blocked_tasks; | ||
406 | |||
407 | /* pointer to lock upon which is currently blocked */ | ||
408 | struct litmus_lock* blocked_lock; | ||
409 | #endif | ||
410 | |||
390 | /* user controlled parameters */ | 411 | /* user controlled parameters */ |
391 | struct rt_task task_params; | 412 | struct rt_task task_params; |
392 | 413 | ||
@@ -401,15 +422,6 @@ struct rt_param { | |||
401 | */ | 422 | */ |
402 | struct task_struct* inh_task; | 423 | struct task_struct* inh_task; |
403 | 424 | ||
404 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
405 | raw_spinlock_t hp_blocked_tasks_lock; | ||
406 | struct binheap hp_blocked_tasks; | ||
407 | |||
408 | /* pointer to lock upon which is currently blocked */ | ||
409 | struct litmus_lock* blocked_lock; | ||
410 | #endif | ||
411 | |||
412 | |||
413 | #ifdef CONFIG_REALTIME_AUX_TASKS | 425 | #ifdef CONFIG_REALTIME_AUX_TASKS |
414 | unsigned int is_aux_task:1; | 426 | unsigned int is_aux_task:1; |
415 | unsigned int aux_ready:1; | 427 | unsigned int aux_ready:1; |
diff --git a/include/litmus/sched_trace.h b/include/litmus/sched_trace.h index 0785db39b2fc..9a7e6fa1e6b6 100644 --- a/include/litmus/sched_trace.h +++ b/include/litmus/sched_trace.h | |||
@@ -52,11 +52,10 @@ struct st_switch_away_data { /* A process was switched away from on a given CPU. | |||
52 | 52 | ||
53 | struct st_completion_data { /* A job completed. */ | 53 | struct st_completion_data { /* A job completed. */ |
54 | u64 when; | 54 | u64 when; |
55 | u8 forced:1; /* Set to 1 if job overran and kernel advanced to the | 55 | u64 backlog_remaining:62; |
56 | * next task automatically; set to 0 otherwise. | 56 | u8 was_backlog_job:1; |
57 | */ | 57 | u8 forced:1; /* Set to 1 if job overran and kernel advanced to the |
58 | u8 __uflags:7; | 58 | * next task automatically; set to 0 otherwise. */ |
59 | u8 __unused[7]; | ||
60 | } __attribute__((packed)); | 59 | } __attribute__((packed)); |
61 | 60 | ||
62 | struct st_block_data { /* A task blocks. */ | 61 | struct st_block_data { /* A task blocks. */ |
diff --git a/litmus/budget.c b/litmus/budget.c index 559c54709acc..15de83bc584e 100644 --- a/litmus/budget.c +++ b/litmus/budget.c | |||
@@ -38,10 +38,13 @@ inline static void cancel_enforcement_timer(struct task_struct* t) | |||
38 | } | 38 | } |
39 | } | 39 | } |
40 | 40 | ||
41 | |||
42 | |||
41 | inline static void arm_enforcement_timer(struct task_struct* t) | 43 | inline static void arm_enforcement_timer(struct task_struct* t) |
42 | { | 44 | { |
43 | struct enforcement_timer* et; | 45 | struct enforcement_timer* et; |
44 | lt_t when_to_fire; | 46 | lt_t when_to_fire, remaining_budget; |
47 | lt_t now; | ||
45 | unsigned long flags; | 48 | unsigned long flags; |
46 | 49 | ||
47 | BUG_ON(!t); | 50 | BUG_ON(!t); |
@@ -80,9 +83,11 @@ inline static void arm_enforcement_timer(struct task_struct* t) | |||
80 | goto out; | 83 | goto out; |
81 | } | 84 | } |
82 | 85 | ||
83 | when_to_fire = litmus_clock() + budget_remaining(t); | 86 | now = litmus_clock(); |
87 | remaining_budget = budget_remaining(t); | ||
88 | when_to_fire = now + remaining_budget; | ||
84 | 89 | ||
85 | TRACE_TASK(t, "bremaining: %ld, when_to_fire: %ld\n", budget_remaining(t), when_to_fire); | 90 | TRACE_TASK(t, "budget remaining: %ld, when_to_fire: %ld\n", remaining_budget, when_to_fire); |
86 | 91 | ||
87 | __hrtimer_start_range_ns(&et->timer, | 92 | __hrtimer_start_range_ns(&et->timer, |
88 | ns_to_ktime(when_to_fire), | 93 | ns_to_ktime(when_to_fire), |
@@ -107,6 +112,9 @@ void send_sigbudget(struct task_struct* t) | |||
107 | } | 112 | } |
108 | } | 113 | } |
109 | 114 | ||
115 | /* | ||
116 | * DRAIN_SIMPLE | ||
117 | */ | ||
110 | 118 | ||
111 | void simple_on_scheduled(struct task_struct* t) | 119 | void simple_on_scheduled(struct task_struct* t) |
112 | { | 120 | { |
@@ -118,7 +126,7 @@ void simple_on_scheduled(struct task_struct* t) | |||
118 | } | 126 | } |
119 | } | 127 | } |
120 | 128 | ||
121 | static void __simple_on_unscheduled(struct task_struct* t) | 129 | inline static void __simple_on_unscheduled(struct task_struct* t) |
122 | { | 130 | { |
123 | BUG_ON(!t); | 131 | BUG_ON(!t); |
124 | 132 | ||
@@ -132,7 +140,12 @@ void simple_on_blocked(struct task_struct* t) | |||
132 | __simple_on_unscheduled(t); | 140 | __simple_on_unscheduled(t); |
133 | } | 141 | } |
134 | 142 | ||
135 | void simple_on_preempt_or_sleep(struct task_struct* t) | 143 | void simple_on_preempt(struct task_struct* t) |
144 | { | ||
145 | __simple_on_unscheduled(t); | ||
146 | } | ||
147 | |||
148 | void simple_on_sleep(struct task_struct* t) | ||
136 | { | 149 | { |
137 | __simple_on_unscheduled(t); | 150 | __simple_on_unscheduled(t); |
138 | } | 151 | } |
@@ -142,12 +155,53 @@ void simple_on_exit(struct task_struct* t) | |||
142 | __simple_on_unscheduled(t); | 155 | __simple_on_unscheduled(t); |
143 | } | 156 | } |
144 | 157 | ||
158 | /* | ||
159 | * DRAIN_SOBLIV | ||
160 | */ | ||
145 | 161 | ||
162 | void sobliv_on_scheduled(struct task_struct* t) | ||
163 | { | ||
164 | BUG_ON(!t); | ||
165 | |||
166 | if (!bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
167 | if (tsk_rt(t)->budget.timer.armed) { | ||
168 | TRACE_TASK(t, "budget timer already armed.\n"); | ||
169 | } | ||
170 | else { | ||
171 | arm_enforcement_timer(t); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | void sobliv_on_blocked(struct task_struct* t) | ||
177 | { | ||
178 | /* NOOP */ | ||
179 | TRACE_TASK(t, "sobliv: budget drains while suspended.\n"); | ||
180 | } | ||
181 | |||
182 | void sobliv_on_sleep(struct task_struct* t) | ||
183 | { | ||
184 | if (budget_precisely_tracked(t)) { | ||
185 | /* kludge. callback called before job_completion logic runs, so | ||
186 | * we need to do some logic of our own to figure out if there is a | ||
187 | * backlog after this job (it is completing since sleep is asserted) | ||
188 | * completes. */ | ||
189 | int no_backlog = (!has_backlog(t) || /* no backlog */ | ||
190 | /* the last backlogged job is completing */ | ||
191 | (get_backlog(t) == 1 && tsk_rt(t)->job_params.is_backlogged_job)); | ||
192 | if (no_backlog) | ||
193 | cancel_enforcement_timer(t); | ||
194 | else | ||
195 | TRACE_TASK(t, "not cancelling timer because there is time for backlogged work.\n"); | ||
196 | } | ||
197 | } | ||
146 | 198 | ||
147 | 199 | ||
148 | static enum hrtimer_restart __on_timeout(struct hrtimer *timer) | 200 | static enum hrtimer_restart __on_timeout(struct hrtimer *timer) |
149 | { | 201 | { |
202 | enum hrtimer_restart restart; | ||
150 | unsigned long flags; | 203 | unsigned long flags; |
204 | |||
151 | struct budget_tracker* bt = | 205 | struct budget_tracker* bt = |
152 | container_of( | 206 | container_of( |
153 | container_of(timer, | 207 | container_of(timer, |
@@ -168,9 +222,13 @@ static enum hrtimer_restart __on_timeout(struct hrtimer *timer) | |||
168 | tsk_rt(t)->budget.timer.armed = 0; | 222 | tsk_rt(t)->budget.timer.armed = 0; |
169 | raw_spin_unlock_irqrestore(&bt->timer.lock, flags); | 223 | raw_spin_unlock_irqrestore(&bt->timer.lock, flags); |
170 | 224 | ||
171 | bt->ops->on_exhausted(t); | 225 | restart = bt->ops->on_exhausted(t); |
226 | |||
227 | raw_spin_lock_irqsave(&bt->timer.lock, flags); | ||
228 | tsk_rt(t)->budget.timer.armed = (restart == HRTIMER_RESTART); | ||
229 | raw_spin_unlock_irqrestore(&bt->timer.lock, flags); | ||
172 | 230 | ||
173 | return HRTIMER_NORESTART; | 231 | return restart; |
174 | } | 232 | } |
175 | 233 | ||
176 | 234 | ||
@@ -181,7 +239,8 @@ void init_budget_tracker(struct budget_tracker* bt, const struct budget_tracker_ | |||
181 | 239 | ||
182 | BUG_ON(!ops->on_scheduled); | 240 | BUG_ON(!ops->on_scheduled); |
183 | BUG_ON(!ops->on_blocked); | 241 | BUG_ON(!ops->on_blocked); |
184 | BUG_ON(!ops->on_preempt_or_sleep); | 242 | BUG_ON(!ops->on_preempt); |
243 | BUG_ON(!ops->on_sleep); | ||
185 | BUG_ON(!ops->on_exhausted); | 244 | BUG_ON(!ops->on_exhausted); |
186 | 245 | ||
187 | memset(bt, 0, sizeof(*bt)); | 246 | memset(bt, 0, sizeof(*bt)); |
@@ -189,4 +248,4 @@ void init_budget_tracker(struct budget_tracker* bt, const struct budget_tracker_ | |||
189 | hrtimer_init(&bt->timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | 248 | hrtimer_init(&bt->timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); |
190 | bt->timer.timer.function = __on_timeout; | 249 | bt->timer.timer.function = __on_timeout; |
191 | bt->ops = ops; | 250 | bt->ops = ops; |
192 | } \ No newline at end of file | 251 | } |
diff --git a/litmus/edf_common.c b/litmus/edf_common.c index 52ccac998142..76ed1056ef6f 100644 --- a/litmus/edf_common.c +++ b/litmus/edf_common.c | |||
@@ -214,6 +214,8 @@ klmirqd_tie_break: | |||
214 | */ | 214 | */ |
215 | int pid_break; | 215 | int pid_break; |
216 | 216 | ||
217 | /* TODO: INCORPERATE job_params::backlog INTO TIE-BREAKING */ | ||
218 | |||
217 | #if defined(CONFIG_EDF_TIE_BREAK_LATENESS) | 219 | #if defined(CONFIG_EDF_TIE_BREAK_LATENESS) |
218 | /* Tie break by lateness. Jobs with greater lateness get | 220 | /* Tie break by lateness. Jobs with greater lateness get |
219 | * priority. This should spread tardiness across all tasks, | 221 | * priority. This should spread tardiness across all tasks, |
diff --git a/litmus/litmus.c b/litmus/litmus.c index e8130e362c84..10d9e545a831 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
@@ -442,8 +442,6 @@ static void reinit_litmus_state(struct task_struct* p, int restore) | |||
442 | binheap_order_t prio_order = NULL; | 442 | binheap_order_t prio_order = NULL; |
443 | #endif | 443 | #endif |
444 | 444 | ||
445 | TRACE_TASK(p, "reinit_litmus_state: restore = %d\n", restore); | ||
446 | |||
447 | if (restore) { | 445 | if (restore) { |
448 | /* Safe user-space provided configuration data. | 446 | /* Safe user-space provided configuration data. |
449 | * and allocated page. */ | 447 | * and allocated page. */ |
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index 8fe646f1f0c5..fd1b80ac6090 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -315,7 +315,8 @@ static noinline void requeue(struct task_struct* task) | |||
315 | /* sanity check before insertion */ | 315 | /* sanity check before insertion */ |
316 | BUG_ON(is_queued(task)); | 316 | BUG_ON(is_queued(task)); |
317 | 317 | ||
318 | if (is_early_releasing(task) || is_released(task, litmus_clock())) { | 318 | if (is_early_releasing(task) || is_released(task, litmus_clock()) || |
319 | tsk_rt(task)->job_params.is_backlogged_job) { | ||
319 | #ifdef CONFIG_REALTIME_AUX_TASKS | 320 | #ifdef CONFIG_REALTIME_AUX_TASKS |
320 | if (unlikely(tsk_rt(task)->is_aux_task && task->state != TASK_RUNNING && !tsk_rt(task)->aux_ready)) { | 321 | if (unlikely(tsk_rt(task)->is_aux_task && task->state != TASK_RUNNING && !tsk_rt(task)->aux_ready)) { |
321 | /* aux_task probably transitioned to real-time while it was blocked */ | 322 | /* aux_task probably transitioned to real-time while it was blocked */ |
@@ -327,7 +328,7 @@ static noinline void requeue(struct task_struct* task) | |||
327 | __add_ready(&cluster->domain, task); | 328 | __add_ready(&cluster->domain, task); |
328 | } | 329 | } |
329 | else { | 330 | else { |
330 | TRACE_TASK(task, "not requeueing non-yet-released job\n"); | 331 | TRACE_TASK(task, "not requeueing not-yet-released job\n"); |
331 | } | 332 | } |
332 | } | 333 | } |
333 | 334 | ||
@@ -413,18 +414,59 @@ static void cedf_release_jobs(rt_domain_t* rt, struct bheap* tasks) | |||
413 | static noinline void job_completion(struct task_struct *t, int forced) | 414 | static noinline void job_completion(struct task_struct *t, int forced) |
414 | { | 415 | { |
415 | int do_release = 0; | 416 | int do_release = 0; |
417 | int do_backlogged_job = 0; | ||
416 | lt_t now; | 418 | lt_t now; |
419 | |||
417 | BUG_ON(!t); | 420 | BUG_ON(!t); |
418 | 421 | ||
422 | now = litmus_clock(); | ||
423 | |||
424 | /* DO BACKLOG TRACKING */ | ||
425 | |||
426 | /* job completed with budget remaining */ | ||
427 | if (get_release_policy(t) != SPORADIC) { | ||
428 | /* only jobs we know that will call sleep_next_job() can use backlogging */ | ||
429 | if (!forced) { | ||
430 | /* was it a backlogged job that completed? */ | ||
431 | if (tsk_rt(t)->job_params.is_backlogged_job) { | ||
432 | BUG_ON(!get_backlog(t)); | ||
433 | --get_backlog(t); | ||
434 | |||
435 | TRACE_TASK(t, "completed backlogged job\n"); | ||
436 | } | ||
437 | } | ||
438 | else { | ||
439 | /* budget was exhausted - force early release */ | ||
440 | ++get_backlog(t); | ||
441 | TRACE_TASK(t, "adding backlogged job\n"); | ||
442 | } | ||
443 | do_backlogged_job = has_backlog(t); | ||
444 | TRACE_TASK(t, "number of backlogged jobs: %u\n", | ||
445 | get_backlog(t)); | ||
446 | } | ||
447 | |||
448 | |||
449 | /* SETUP FOR THE NEXT JOB */ | ||
450 | |||
419 | sched_trace_task_completion(t, forced); | 451 | sched_trace_task_completion(t, forced); |
420 | 452 | ||
421 | now = litmus_clock(); | 453 | TRACE_TASK(t, "job_completion() at %llu (forced = %d).\n", |
422 | TRACE_TASK(t, "job_completion() at %llu.\n", now); | 454 | now, forced); |
423 | 455 | ||
424 | /* set flags */ | 456 | /* set flags */ |
425 | tsk_rt(t)->completed = 1; | 457 | tsk_rt(t)->completed = 1; |
426 | /* prepare for next period */ | 458 | |
427 | prepare_for_next_period(t); | 459 | if (!forced && do_backlogged_job) { |
460 | /* don't advance deadline/refresh budget. use the remaining budget for | ||
461 | * the backlogged job. */ | ||
462 | } | ||
463 | else { | ||
464 | if (do_backlogged_job) { | ||
465 | TRACE_TASK(t, "refreshing budget with early release for backlogged job.\n"); | ||
466 | } | ||
467 | |||
468 | prepare_for_next_period(t); | ||
469 | } | ||
428 | 470 | ||
429 | do_release = (is_early_releasing(t) || is_released(t, now)); | 471 | do_release = (is_early_releasing(t) || is_released(t, now)); |
430 | if (do_release) { | 472 | if (do_release) { |
@@ -437,14 +479,30 @@ static noinline void job_completion(struct task_struct *t, int forced) | |||
437 | /* release or arm next job */ | 479 | /* release or arm next job */ |
438 | tsk_rt(t)->completed = 0; | 480 | tsk_rt(t)->completed = 0; |
439 | if (is_running(t)) { | 481 | if (is_running(t)) { |
440 | if (!do_release) | 482 | /* is our next job a backlogged job? */ |
441 | add_release(&task_cpu_cluster(t)->domain, t); | 483 | if (do_backlogged_job) { |
442 | else | 484 | TRACE_TASK(t, "next job is a backlogged job.\n"); |
485 | tsk_rt(t)->job_params.is_backlogged_job = 1; | ||
486 | } | ||
487 | else { | ||
488 | TRACE_TASK(t, "next job is a regular job.\n"); | ||
489 | tsk_rt(t)->job_params.is_backlogged_job = 0; | ||
490 | } | ||
491 | |||
492 | if (do_release || do_backlogged_job) { | ||
443 | cedf_job_arrival(t); | 493 | cedf_job_arrival(t); |
494 | } | ||
495 | else { | ||
496 | add_release(&task_cpu_cluster(t)->domain, t); | ||
497 | } | ||
498 | } | ||
499 | else { | ||
500 | BUG_ON(!forced); | ||
501 | TRACE_TASK(t, "job exhausted budget while sleeping\n"); | ||
444 | } | 502 | } |
445 | } | 503 | } |
446 | 504 | ||
447 | static void cedf_simple_on_exhausted(struct task_struct *t) | 505 | static enum hrtimer_restart cedf_simple_on_exhausted(struct task_struct *t) |
448 | { | 506 | { |
449 | /* Assumption: t is scheduled on the CPU executing this callback */ | 507 | /* Assumption: t is scheduled on the CPU executing this callback */ |
450 | 508 | ||
@@ -458,18 +516,94 @@ static void cedf_simple_on_exhausted(struct task_struct *t) | |||
458 | /* np tasks will be preempted when they become | 516 | /* np tasks will be preempted when they become |
459 | * preemptable again | 517 | * preemptable again |
460 | */ | 518 | */ |
519 | TRACE_TASK(t, "is preemptable => FORCE_RESCHED\n"); | ||
520 | |||
461 | litmus_reschedule_local(); | 521 | litmus_reschedule_local(); |
462 | set_will_schedule(); | 522 | 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)) { | 523 | } else if (is_user_np(t)) { |
467 | TRACE("cedf_scheduler_tick: " | 524 | TRACE_TASK(t, "is non-preemptable, preemption delayed.\n"); |
468 | "%d is non-preemptable, " | 525 | |
469 | "preemption delayed.\n", t->pid); | ||
470 | request_exit_np(t); | 526 | request_exit_np(t); |
471 | } | 527 | } |
472 | } | 528 | } |
529 | |||
530 | return HRTIMER_NORESTART; | ||
531 | } | ||
532 | |||
533 | static enum hrtimer_restart cedf_sobliv_on_exhausted(struct task_struct *t) | ||
534 | { | ||
535 | enum hrtimer_restart restart = HRTIMER_NORESTART; | ||
536 | |||
537 | /* t may or may not be scheduled */ | ||
538 | |||
539 | if (budget_signalled(t) && !bt_flag_is_set(t, BTF_SIG_BUDGET_SENT)) { | ||
540 | /* signal exhaustion */ | ||
541 | |||
542 | /* Tasks should block SIG_BUDGET if they cannot gracefully respond to | ||
543 | * the signal while suspended. SIG_BUDGET is an rt-signal, so it will | ||
544 | * be queued and received when SIG_BUDGET is unblocked */ | ||
545 | send_sigbudget(t); /* will set BTF_SIG_BUDGET_SENT */ | ||
546 | } | ||
547 | |||
548 | if (budget_enforced(t) && !bt_flag_is_set(t, BTF_BUDGET_EXHAUSTED)) { | ||
549 | if (is_np(t) && is_user_np(t)) { | ||
550 | TRACE_TASK(t, "is non-preemptable, preemption delayed.\n"); | ||
551 | |||
552 | bt_flag_set(t, BTF_BUDGET_EXHAUSTED); | ||
553 | request_exit_np(t); | ||
554 | } | ||
555 | else { | ||
556 | /* where do we need to call resched? */ | ||
557 | int cpu = (tsk_rt(t)->linked_on != NO_CPU) ? | ||
558 | tsk_rt(t)->linked_on : tsk_rt(t)->scheduled_on; | ||
559 | if (cpu == smp_processor_id()) { | ||
560 | TRACE_TASK(t, "is preemptable => FORCE_RESCHED\n"); | ||
561 | |||
562 | bt_flag_set(t, BTF_BUDGET_EXHAUSTED); | ||
563 | litmus_reschedule_local(); | ||
564 | set_will_schedule(); | ||
565 | } | ||
566 | else if (cpu != NO_CPU) { | ||
567 | TRACE_TASK(t, "is preemptable on remote cpu (%d) => FORCE_RESCHED\n", cpu); | ||
568 | |||
569 | bt_flag_set(t, BTF_BUDGET_EXHAUSTED); | ||
570 | litmus_reschedule(cpu); | ||
571 | } | ||
572 | else { | ||
573 | BUG_ON(cpu != NO_CPU); | ||
574 | #ifdef CONFIG_LITMUS_LOCKING | ||
575 | if (holds_locks(t)) { | ||
576 | /* TODO: Integration with Litmus locking protocols */ | ||
577 | TRACE_TASK(t, "prevented lock holder from postponing deadline.\n"); | ||
578 | } | ||
579 | else { | ||
580 | #endif | ||
581 | /* force job completion */ | ||
582 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
583 | unsigned long flags; | ||
584 | lt_t remaining; | ||
585 | |||
586 | TRACE_TASK(t, "blocked, postponing deadline\n"); | ||
587 | |||
588 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
589 | job_completion(t, 1); /* refreshes budget */ | ||
590 | |||
591 | hrtimer_forward_now(&get_budget_timer(t).timer.timer, | ||
592 | ns_to_ktime(budget_remaining(t))); | ||
593 | remaining = hrtimer_get_expires_ns(&get_budget_timer(t).timer.timer); | ||
594 | |||
595 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
596 | |||
597 | TRACE_TASK(t, "rearmed timer to %ld\n", remaining); | ||
598 | restart = HRTIMER_RESTART; | ||
599 | #ifdef CONFIG_LITMUS_LOCKING | ||
600 | } | ||
601 | #endif | ||
602 | } | ||
603 | } | ||
604 | } | ||
605 | |||
606 | return restart; | ||
473 | } | 607 | } |
474 | 608 | ||
475 | 609 | ||
@@ -873,8 +1007,10 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
873 | if (tsk_rt(prev)->budget.ops) { | 1007 | if (tsk_rt(prev)->budget.ops) { |
874 | if (blocks) | 1008 | if (blocks) |
875 | tsk_rt(prev)->budget.ops->on_blocked(prev); | 1009 | tsk_rt(prev)->budget.ops->on_blocked(prev); |
876 | else if (preempt || sleep) | 1010 | else if (sleep) |
877 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | 1011 | tsk_rt(prev)->budget.ops->on_sleep(prev); |
1012 | else if (preempt) | ||
1013 | tsk_rt(prev)->budget.ops->on_preempt(prev); | ||
878 | } | 1014 | } |
879 | 1015 | ||
880 | /* If a task blocks we have no choice but to reschedule. | 1016 | /* If a task blocks we have no choice but to reschedule. |
@@ -1156,12 +1292,23 @@ static struct budget_tracker_ops cedf_drain_simple_ops = | |||
1156 | { | 1292 | { |
1157 | .on_scheduled = simple_on_scheduled, | 1293 | .on_scheduled = simple_on_scheduled, |
1158 | .on_blocked = simple_on_blocked, | 1294 | .on_blocked = simple_on_blocked, |
1159 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | 1295 | .on_preempt = simple_on_preempt, |
1296 | .on_sleep = simple_on_sleep, | ||
1160 | .on_exit = simple_on_exit, | 1297 | .on_exit = simple_on_exit, |
1161 | 1298 | ||
1162 | .on_exhausted = cedf_simple_on_exhausted, | 1299 | .on_exhausted = cedf_simple_on_exhausted, |
1163 | }; | 1300 | }; |
1164 | 1301 | ||
1302 | static struct budget_tracker_ops cedf_drain_sobliv_ops = | ||
1303 | { | ||
1304 | .on_scheduled = sobliv_on_scheduled, | ||
1305 | .on_blocked = sobliv_on_blocked, | ||
1306 | .on_preempt = sobliv_on_preempt, | ||
1307 | .on_sleep = sobliv_on_sleep, | ||
1308 | .on_exit = sobliv_on_exit, | ||
1309 | |||
1310 | .on_exhausted = cedf_sobliv_on_exhausted, | ||
1311 | }; | ||
1165 | 1312 | ||
1166 | static long cedf_admit_task(struct task_struct* tsk) | 1313 | static long cedf_admit_task(struct task_struct* tsk) |
1167 | { | 1314 | { |
@@ -1173,6 +1320,17 @@ static long cedf_admit_task(struct task_struct* tsk) | |||
1173 | case DRAIN_SIMPLE: | 1320 | case DRAIN_SIMPLE: |
1174 | init_budget_tracker(&tsk_rt(tsk)->budget, &cedf_drain_simple_ops); | 1321 | init_budget_tracker(&tsk_rt(tsk)->budget, &cedf_drain_simple_ops); |
1175 | break; | 1322 | break; |
1323 | case DRAIN_SOBLIV: | ||
1324 | /* budget_policy and budget_signal_policy cannot be quantum-based */ | ||
1325 | if (!budget_quantum_tracked(tsk) && budget_precisely_tracked(tsk)) { | ||
1326 | init_budget_tracker(&tsk_rt(tsk)->budget, &cedf_drain_sobliv_ops); | ||
1327 | } | ||
1328 | else { | ||
1329 | TRACE_TASK(tsk, "QUANTUM_ENFORCEMENT and QUANTUM_SIGNALS is " | ||
1330 | "unsupported with DRAIN_SOBLIV.\n"); | ||
1331 | return -EINVAL; | ||
1332 | } | ||
1333 | break; | ||
1176 | default: | 1334 | default: |
1177 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); | 1335 | TRACE_TASK(tsk, "Unsupported budget draining mode.\n"); |
1178 | return -EINVAL; | 1336 | return -EINVAL; |
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c index 15ac94038702..2950e39b054e 100644 --- a/litmus/sched_gsn_edf.c +++ b/litmus/sched_gsn_edf.c | |||
@@ -421,7 +421,7 @@ 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) | 424 | static enum hrtimer_restart gsnedf_simple_on_exhausted(struct task_struct *t) |
425 | { | 425 | { |
426 | /* Assumption: t is scheduled on the CPU executing this callback */ | 426 | /* Assumption: t is scheduled on the CPU executing this callback */ |
427 | 427 | ||
@@ -446,6 +446,8 @@ static void gsnedf_simple_on_exhausted(struct task_struct *t) | |||
446 | request_exit_np(t); | 446 | request_exit_np(t); |
447 | } | 447 | } |
448 | } | 448 | } |
449 | |||
450 | return HRTIMER_NORESTART; | ||
449 | } | 451 | } |
450 | 452 | ||
451 | /* gsnedf_tick - this function is called for every local timer | 453 | /* gsnedf_tick - this function is called for every local timer |
@@ -849,8 +851,10 @@ static struct task_struct* gsnedf_schedule(struct task_struct * prev) | |||
849 | if (tsk_rt(prev)->budget.ops) { | 851 | if (tsk_rt(prev)->budget.ops) { |
850 | if (blocks) | 852 | if (blocks) |
851 | tsk_rt(prev)->budget.ops->on_blocked(prev); | 853 | tsk_rt(prev)->budget.ops->on_blocked(prev); |
852 | else if (preempt || sleep) | 854 | else if (sleep) |
853 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | 855 | tsk_rt(prev)->budget.ops->on_sleep(prev); |
856 | else if (preempt) | ||
857 | tsk_rt(prev)->budget.ops->on_preempt(prev); | ||
854 | } | 858 | } |
855 | 859 | ||
856 | /* If a task blocks we have no choice but to reschedule. | 860 | /* If a task blocks we have no choice but to reschedule. |
@@ -1122,7 +1126,8 @@ static struct budget_tracker_ops gsnedf_drain_simple_ops = | |||
1122 | { | 1126 | { |
1123 | .on_scheduled = simple_on_scheduled, | 1127 | .on_scheduled = simple_on_scheduled, |
1124 | .on_blocked = simple_on_blocked, | 1128 | .on_blocked = simple_on_blocked, |
1125 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | 1129 | .on_preempt = simple_on_preempt, |
1130 | .on_sleep = simple_on_sleep, | ||
1126 | .on_exit = simple_on_exit, | 1131 | .on_exit = simple_on_exit, |
1127 | 1132 | ||
1128 | .on_exhausted = gsnedf_simple_on_exhausted, | 1133 | .on_exhausted = gsnedf_simple_on_exhausted, |
diff --git a/litmus/sched_pfp.c b/litmus/sched_pfp.c index 4a8b8e084f6e..33f861ab0056 100644 --- a/litmus/sched_pfp.c +++ b/litmus/sched_pfp.c | |||
@@ -132,7 +132,7 @@ 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) | 135 | static enum hrtimer_restart pfp_simple_on_exhausted(struct task_struct *t) |
136 | { | 136 | { |
137 | /* Assumption: t is scheduled on the CPU executing this callback */ | 137 | /* Assumption: t is scheduled on the CPU executing this callback */ |
138 | 138 | ||
@@ -157,6 +157,8 @@ static void pfp_simple_on_exhausted(struct task_struct *t) | |||
157 | request_exit_np(t); | 157 | request_exit_np(t); |
158 | } | 158 | } |
159 | } | 159 | } |
160 | |||
161 | return HRTIMER_NORESTART; | ||
160 | } | 162 | } |
161 | 163 | ||
162 | static void pfp_tick(struct task_struct *t) | 164 | static void pfp_tick(struct task_struct *t) |
@@ -214,8 +216,10 @@ static struct task_struct* pfp_schedule(struct task_struct * prev) | |||
214 | if (tsk_rt(prev)->budget.ops) { | 216 | if (tsk_rt(prev)->budget.ops) { |
215 | if (blocks) | 217 | if (blocks) |
216 | tsk_rt(prev)->budget.ops->on_blocked(prev); | 218 | tsk_rt(prev)->budget.ops->on_blocked(prev); |
217 | else if (preempt || sleep) | 219 | else if (sleep) |
218 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | 220 | tsk_rt(prev)->budget.ops->on_sleep(prev); |
221 | else if (preempt) | ||
222 | tsk_rt(prev)->budget.ops->on_preempt(prev); | ||
219 | } | 223 | } |
220 | 224 | ||
221 | /* If a task blocks we have no choice but to reschedule. | 225 | /* If a task blocks we have no choice but to reschedule. |
@@ -1713,7 +1717,8 @@ static struct budget_tracker_ops pfp_drain_simple_ops = | |||
1713 | { | 1717 | { |
1714 | .on_scheduled = simple_on_scheduled, | 1718 | .on_scheduled = simple_on_scheduled, |
1715 | .on_blocked = simple_on_blocked, | 1719 | .on_blocked = simple_on_blocked, |
1716 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | 1720 | .on_preempt = simple_on_preempt, |
1721 | .on_sleep = simple_on_sleep, | ||
1717 | .on_exit = simple_on_exit, | 1722 | .on_exit = simple_on_exit, |
1718 | 1723 | ||
1719 | .on_exhausted = pfp_simple_on_exhausted, | 1724 | .on_exhausted = pfp_simple_on_exhausted, |
diff --git a/litmus/sched_psn_edf.c b/litmus/sched_psn_edf.c index 3b3edfe908ff..c06db8b434cd 100644 --- a/litmus/sched_psn_edf.c +++ b/litmus/sched_psn_edf.c | |||
@@ -164,7 +164,7 @@ 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) | 167 | static enum hrtimer_restart psnedf_simple_on_exhausted(struct task_struct *t) |
168 | { | 168 | { |
169 | /* Assumption: t is scheduled on the CPU executing this callback */ | 169 | /* Assumption: t is scheduled on the CPU executing this callback */ |
170 | 170 | ||
@@ -189,6 +189,8 @@ static void psnedf_simple_on_exhausted(struct task_struct *t) | |||
189 | request_exit_np(t); | 189 | request_exit_np(t); |
190 | } | 190 | } |
191 | } | 191 | } |
192 | |||
193 | return HRTIMER_NORESTART; | ||
192 | } | 194 | } |
193 | 195 | ||
194 | static void psnedf_tick(struct task_struct *t) | 196 | static void psnedf_tick(struct task_struct *t) |
@@ -246,8 +248,10 @@ static struct task_struct* psnedf_schedule(struct task_struct * prev) | |||
246 | if (tsk_rt(prev)->budget.ops) { | 248 | if (tsk_rt(prev)->budget.ops) { |
247 | if (blocks) | 249 | if (blocks) |
248 | tsk_rt(prev)->budget.ops->on_blocked(prev); | 250 | tsk_rt(prev)->budget.ops->on_blocked(prev); |
249 | else if (preempt || sleep) | 251 | else if (sleep) |
250 | tsk_rt(prev)->budget.ops->on_preempt_or_sleep(prev); | 252 | tsk_rt(prev)->budget.ops->on_sleep(prev); |
253 | else if (preempt) | ||
254 | tsk_rt(prev)->budget.ops->on_preempt(prev); | ||
251 | } | 255 | } |
252 | 256 | ||
253 | /* If a task blocks we have no choice but to reschedule. | 257 | /* If a task blocks we have no choice but to reschedule. |
@@ -647,7 +651,8 @@ static struct budget_tracker_ops psnedf_drain_simple_ops = | |||
647 | { | 651 | { |
648 | .on_scheduled = simple_on_scheduled, | 652 | .on_scheduled = simple_on_scheduled, |
649 | .on_blocked = simple_on_blocked, | 653 | .on_blocked = simple_on_blocked, |
650 | .on_preempt_or_sleep = simple_on_preempt_or_sleep, | 654 | .on_preempt = simple_on_preempt, |
655 | .on_sleep = simple_on_sleep, | ||
651 | .on_exit = simple_on_exit, | 656 | .on_exit = simple_on_exit, |
652 | 657 | ||
653 | .on_exhausted = psnedf_simple_on_exhausted, | 658 | .on_exhausted = psnedf_simple_on_exhausted, |
diff --git a/litmus/sched_task_trace.c b/litmus/sched_task_trace.c index 8d75437e7771..e243b8007826 100644 --- a/litmus/sched_task_trace.c +++ b/litmus/sched_task_trace.c | |||
@@ -192,7 +192,9 @@ feather_callback void do_sched_trace_task_completion(unsigned long id, | |||
192 | struct task_struct *t = (struct task_struct*) _task; | 192 | struct task_struct *t = (struct task_struct*) _task; |
193 | struct st_event_record* rec = get_record(ST_COMPLETION, t); | 193 | struct st_event_record* rec = get_record(ST_COMPLETION, t); |
194 | if (rec) { | 194 | if (rec) { |
195 | rec->data.completion.when = now(); | 195 | rec->data.completion.when = now(); |
196 | rec->data.completion.backlog_remaining = tsk_rt(t)->job_params.job_backlog; | ||
197 | rec->data.completion.was_backlog_job = tsk_rt(t)->job_params.is_backlogged_job; | ||
196 | rec->data.completion.forced = forced; | 198 | rec->data.completion.forced = forced; |
197 | put_record(rec); | 199 | put_record(rec); |
198 | } | 200 | } |