diff options
author | Zelin Tong <ztong@ludwig.cs.unc.edu> | 2020-07-02 00:58:09 -0400 |
---|---|---|
committer | Zelin Tong <ztong@ludwig.cs.unc.edu> | 2020-07-02 00:58:09 -0400 |
commit | 098a298ef73dd8dbacf0d697eef2a6f2daa2081c (patch) | |
tree | 546de13acc94765ec9c116b8d8b42632139179a5 | |
parent | e4c5fa6df346a78dfb683d601fd5ad34e6de3375 (diff) |
FINAL BUG FIXED VERSION
-rw-r--r-- | include/litmus/rt_param.h | 1 | ||||
-rw-r--r-- | include/litmus/trace.h | 4 | ||||
-rw-r--r-- | kernel/sched/core.c | 1 | ||||
-rw-r--r-- | litmus/sched_edfsc.c | 886 |
4 files changed, 492 insertions, 400 deletions
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index 22ce3da19245..fd8410aaabbf 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
@@ -122,7 +122,6 @@ struct edfsc_params { | |||
122 | cont_domain_t *domain; | 122 | cont_domain_t *domain; |
123 | struct task_struct *container_task; | 123 | struct task_struct *container_task; |
124 | int id; | 124 | int id; |
125 | int can_release; //only used for containers | ||
126 | // Moved these to the struct task_struct in include/linux/sched.h so that | 125 | // Moved these to the struct task_struct in include/linux/sched.h so that |
127 | // the runtime can build | 126 | // the runtime can build |
128 | //struct list_head qnode; | 127 | //struct list_head qnode; |
diff --git a/include/litmus/trace.h b/include/litmus/trace.h index 2646136e3881..dbbd817d2bd1 100644 --- a/include/litmus/trace.h +++ b/include/litmus/trace.h | |||
@@ -140,8 +140,8 @@ feather_callback void save_cpu_task_latency(unsigned long event, unsigned long w | |||
140 | #define TS_PLUGIN_SCHED_START /* TIMESTAMP(120) */ /* currently unused */ | 140 | #define TS_PLUGIN_SCHED_START /* TIMESTAMP(120) */ /* currently unused */ |
141 | #define TS_PLUGIN_SCHED_END /* TIMESTAMP(121) */ | 141 | #define TS_PLUGIN_SCHED_END /* TIMESTAMP(121) */ |
142 | 142 | ||
143 | #define TS_PLUGIN_TICK_START /* TIMESTAMP(130) */ | 143 | #define TS_PLUGIN_TICK_START /* CPU_TIMESTAMP(130) */ |
144 | #define TS_PLUGIN_TICK_END /* TIMESTAMP(131) */ | 144 | #define TS_PLUGIN_TICK_END /* CPU_TIMESTAMP(131) */ |
145 | 145 | ||
146 | #define TS_ENTER_NP_START CPU_TIMESTAMP(140) | 146 | #define TS_ENTER_NP_START CPU_TIMESTAMP(140) |
147 | #define TS_ENTER_NP_END CPU_TIMESTAMP(141) | 147 | #define TS_ENTER_NP_END CPU_TIMESTAMP(141) |
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 98ad911a0c58..fe986d548abf 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c | |||
@@ -3479,6 +3479,7 @@ static void __sched notrace __schedule(bool preempt) | |||
3479 | } else { | 3479 | } else { |
3480 | lockdep_unpin_lock(&rq->lock, cookie); | 3480 | lockdep_unpin_lock(&rq->lock, cookie); |
3481 | TS_SCHED_END(prev); | 3481 | TS_SCHED_END(prev); |
3482 | litmus->finish_switch(prev); | ||
3482 | raw_spin_unlock_irq(&rq->lock); | 3483 | raw_spin_unlock_irq(&rq->lock); |
3483 | } | 3484 | } |
3484 | 3485 | ||
diff --git a/litmus/sched_edfsc.c b/litmus/sched_edfsc.c index fae6feeac76f..ea2dce57b337 100644 --- a/litmus/sched_edfsc.c +++ b/litmus/sched_edfsc.c | |||
@@ -29,7 +29,6 @@ typedef struct cont_domain { | |||
29 | struct task_struct *container; | 29 | struct task_struct *container; |
30 | struct task_struct *scheduled; //fixed task | 30 | struct task_struct *scheduled; //fixed task |
31 | lt_t scheduled_last_exec_time; //exec_time of the scheduled task when it was last scheduled | 31 | lt_t scheduled_last_exec_time; //exec_time of the scheduled task when it was last scheduled |
32 | lt_t changed_budget; //change to scheduled task's exec time due to container budget constraints | ||
33 | u64 f_util; | 32 | u64 f_util; |
34 | struct bheap_node *hn; | 33 | struct bheap_node *hn; |
35 | struct hrtimer idle_enforcement_timer; | 34 | struct hrtimer idle_enforcement_timer; |
@@ -52,6 +51,8 @@ struct list_head pending_adds; | |||
52 | 51 | ||
53 | struct list_head migrating_tasks; | 52 | struct list_head migrating_tasks; |
54 | 53 | ||
54 | struct list_head pending_removes; | ||
55 | |||
55 | struct hrtimer container_release_timer; | 56 | struct hrtimer container_release_timer; |
56 | 57 | ||
57 | DEFINE_PER_CPU(cpu_entry_t, edfsc_cpu_entries); | 58 | DEFINE_PER_CPU(cpu_entry_t, edfsc_cpu_entries); |
@@ -72,7 +73,7 @@ u64 sys_util; | |||
72 | int sys_changed; | 73 | int sys_changed; |
73 | 74 | ||
74 | #define is_container(task) ((task) && tsk_rt(task)->edfsc_params.domain != NULL && tsk_rt(task)->domain == &gsched_domain) | 75 | #define is_container(task) ((task) && tsk_rt(task)->edfsc_params.domain != NULL && tsk_rt(task)->domain == &gsched_domain) |
75 | #define is_fixed(task) ((task) && tsk_rt(task)->edfsc_params.container_task != NULL) | 76 | #define is_fixed(task) ((task) && tsk_rt(task)->domain && tsk_rt(task)->domain != &gsched_domain) |
76 | #define is_migrating(task) ((task) && tsk_rt(task)->edfsc_params.domain == NULL && tsk_rt(task)->domain == &gsched_domain) | 77 | #define is_migrating(task) ((task) && tsk_rt(task)->edfsc_params.domain == NULL && tsk_rt(task)->domain == &gsched_domain) |
77 | 78 | ||
78 | #define FP_SHIFT 20 | 79 | #define FP_SHIFT 20 |
@@ -105,16 +106,23 @@ int count_migrating_tasks(void) | |||
105 | /* Do a backwards comparison based on f_util so that heavier containers | 106 | /* Do a backwards comparison based on f_util so that heavier containers |
106 | * will come first | 107 | * will come first |
107 | */ | 108 | */ |
109 | // Used for best-fit | ||
108 | static int container_lower_prio(const void *_a, const void *_b) | 110 | static int container_lower_prio(const void *_a, const void *_b) |
109 | { | 111 | { |
110 | const cont_domain_t *a = (const cont_domain_t *)(_a); | 112 | const cont_domain_t* a = *(const cont_domain_t**)(_a); |
111 | const cont_domain_t *b = (const cont_domain_t *)(_b); | 113 | const cont_domain_t* b = *(const cont_domain_t**)(_b); |
112 | if (a->f_util < b->f_util) return 1; | 114 | return (b->f_util - a->f_util); |
113 | if (a->f_util > b->f_util) return -1; | 115 | } |
114 | return 0; | 116 | |
117 | // Used for worst-fit | ||
118 | static int container_higher_prio(const void *_a, const void *_b) | ||
119 | { | ||
120 | const cont_domain_t* a = *(const cont_domain_t**)(_a); | ||
121 | const cont_domain_t* b = *(const cont_domain_t**)(_b); | ||
122 | return (a->f_util - b->f_util); | ||
115 | } | 123 | } |
116 | 124 | ||
117 | /* Finds the task_struct of the hrtimer set by task_exit | 125 | /* Finds the task_struct of a list node |
118 | */ | 126 | */ |
119 | static struct task_struct* task_of_list_node(struct list_head *node) | 127 | static struct task_struct* task_of_list_node(struct list_head *node) |
120 | { | 128 | { |
@@ -128,7 +136,11 @@ static noinline void requeue(struct task_struct* task) | |||
128 | BUG_ON(!task); | 136 | BUG_ON(!task); |
129 | /* sanity check before insertion */ | 137 | /* sanity check before insertion */ |
130 | BUG_ON(is_queued(task)); | 138 | BUG_ON(is_queued(task)); |
131 | BUG_ON(!is_realtime(task)); | 139 | BUG_ON(is_migrating(task) && task->rt_param.edfsc_params.container_task != NULL); |
140 | //BUG_ON(task && tsk_rt(task)->linked_on != NO_CPU); | ||
141 | //BUG_ON(is_completed(task) || (budget_enforced(task) && budget_exhausted(task))); | ||
142 | //BUG_ON(is_container(task) && ((cont_domain_t*)task->rt_param.edfsc_params.domain)->timer_armed); | ||
143 | //BUG_ON(task && is_completed(task)); | ||
132 | 144 | ||
133 | if (is_early_releasing(task) || is_released(task, litmus_clock())) { | 145 | if (is_early_releasing(task) || is_released(task, litmus_clock())) { |
134 | __add_ready((rt_domain_t *) tsk_rt(task)->domain, task); | 146 | __add_ready((rt_domain_t *) tsk_rt(task)->domain, task); |
@@ -147,10 +159,12 @@ static noinline void requeue(struct task_struct* task) | |||
147 | static void preempt(cpu_entry_t *entry) | 159 | static void preempt(cpu_entry_t *entry) |
148 | { | 160 | { |
149 | BUG_ON(!entry); | 161 | BUG_ON(!entry); |
150 | if (is_container(entry->scheduled)) | 162 | if (is_container(entry->scheduled)) { |
151 | preempt_if_preemptable(tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled, entry->cpu); | 163 | preempt_if_preemptable(tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled, entry->cpu); |
152 | else | 164 | } |
165 | else { | ||
153 | preempt_if_preemptable(entry->scheduled, entry->cpu); | 166 | preempt_if_preemptable(entry->scheduled, entry->cpu); |
167 | } | ||
154 | } | 168 | } |
155 | 169 | ||
156 | ///////////////////////////////////////////////////////////////////////////////////// | 170 | ///////////////////////////////////////////////////////////////////////////////////// |
@@ -161,7 +175,7 @@ static void preempt(cpu_entry_t *entry) | |||
161 | */ | 175 | */ |
162 | 176 | ||
163 | static struct bheap_node* edfsc_cpu_heap_node; // Array of cpu heap nodes | 177 | static struct bheap_node* edfsc_cpu_heap_node; // Array of cpu heap nodes |
164 | static struct bheap edfsc_cpu_heap; | 178 | static struct bheap edfsc_cpu_heap; // Cpu heap |
165 | 179 | ||
166 | static int cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) | 180 | static int cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) |
167 | { | 181 | { |
@@ -215,7 +229,15 @@ static void update_cpu_position(cpu_entry_t *entry) | |||
215 | * | 229 | * |
216 | */ | 230 | */ |
217 | 231 | ||
218 | //timeout for timer enforcing budget of empty container | 232 | // updates exec_time for container budget tracking |
233 | static void update_container_budget(struct task_struct* t) { | ||
234 | lt_t now = litmus_clock(); | ||
235 | tsk_rt(t)->job_params.exec_time += now | ||
236 | - tsk_rt(t)->edfsc_params.domain->scheduled_last_exec_time; | ||
237 | tsk_rt(t)->edfsc_params.domain->scheduled_last_exec_time = now; | ||
238 | } | ||
239 | |||
240 | // timeout for timer enforcing budget of empty container | ||
219 | static enum hrtimer_restart on_idle_enforcement_timeout(struct hrtimer *timer) | 241 | static enum hrtimer_restart on_idle_enforcement_timeout(struct hrtimer *timer) |
220 | { | 242 | { |
221 | cont_domain_t* domain = container_of(timer, cont_domain_t, idle_enforcement_timer); | 243 | cont_domain_t* domain = container_of(timer, cont_domain_t, idle_enforcement_timer); |
@@ -223,7 +245,9 @@ static enum hrtimer_restart on_idle_enforcement_timeout(struct hrtimer *timer) | |||
223 | unsigned long flags; | 245 | unsigned long flags; |
224 | 246 | ||
225 | local_irq_save(flags); | 247 | local_irq_save(flags); |
248 | BUG_ON(tsk_rt(domain->container)->edfsc_params.id != this_cpu_ptr(&edfsc_cpu_entries)->cpu); | ||
226 | domain->timer_armed = 0; | 249 | domain->timer_armed = 0; |
250 | tsk_rt(domain->container)->completed = 1; | ||
227 | litmus_reschedule_local(); | 251 | litmus_reschedule_local(); |
228 | local_irq_restore(flags); | 252 | local_irq_restore(flags); |
229 | 253 | ||
@@ -236,21 +260,17 @@ void manage_idle_enforcement_timer(struct task_struct* t) | |||
236 | 260 | ||
237 | cont_domain_t* domain = tsk_rt(t)->edfsc_params.domain; | 261 | cont_domain_t* domain = tsk_rt(t)->edfsc_params.domain; |
238 | now = litmus_clock(); | 262 | now = litmus_clock(); |
239 | domain->scheduled_last_exec_time = now; | 263 | BUG_ON(is_completed(t)); |
240 | if (budget_precisely_enforced(t)) { | 264 | BUG_ON(budget_exhausted(t) && !is_np(t)); |
241 | BUG_ON(budget_exhausted(t) && !is_np(t)); | 265 | |
242 | if (likely(!is_np(t))) { | 266 | if (!domain->timer_armed) { |
243 | //hrtimer_start cancels the timer so don't have to check | 267 | domain->scheduled_last_exec_time = now; |
244 | //if it is already armed | 268 | //hrtimer_start cancels the timer so don't have to check |
245 | hrtimer_start(&(domain->idle_enforcement_timer), | 269 | //if it is already armed |
246 | ns_to_ktime(now + budget_remaining(t)), | 270 | hrtimer_start(&(domain->idle_enforcement_timer), |
247 | HRTIMER_MODE_ABS_PINNED); | 271 | ns_to_ktime(now + budget_remaining(t)), |
248 | domain->timer_armed = 1; | 272 | HRTIMER_MODE_ABS_PINNED); |
249 | } | 273 | domain->timer_armed = 1; |
250 | } | ||
251 | else if (domain->timer_armed) { | ||
252 | hrtimer_try_to_cancel(&(domain->idle_enforcement_timer)); | ||
253 | domain->timer_armed = 0; | ||
254 | } | 274 | } |
255 | } | 275 | } |
256 | 276 | ||
@@ -263,18 +283,19 @@ void cancel_idle_enforcement_timer(struct task_struct* t) | |||
263 | 283 | ||
264 | /* link_task_to_cpu - Links a migrating task or container to a CPU | 284 | /* link_task_to_cpu - Links a migrating task or container to a CPU |
265 | * Update the link of a CPU. | 285 | * Update the link of a CPU. |
266 | * Handles the case where the to-be-linked task is already | ||
267 | * scheduled on a different CPU. | ||
268 | */ | 286 | */ |
269 | static noinline void link_task_to_cpu(struct task_struct* linked, | 287 | static noinline void link_task_to_cpu(struct task_struct* linked, |
270 | cpu_entry_t *entry) | 288 | cpu_entry_t *entry) |
271 | { | 289 | { |
272 | BUG_ON(linked && !is_realtime(linked)); | ||
273 | BUG_ON(is_fixed(linked)); | 290 | BUG_ON(is_fixed(linked)); |
274 | BUG_ON(is_container(linked) && tsk_rt(linked)->edfsc_params.id != entry->cpu); | 291 | BUG_ON(is_container(linked) && tsk_rt(linked)->edfsc_params.id != entry->cpu); |
292 | BUG_ON(linked && is_queued(linked)); | ||
293 | //BUG_ON(linked && ((budget_enforced(linked) && budget_exhausted(linked)) || is_completed(linked))); | ||
294 | BUG_ON(linked && !is_released(linked, litmus_clock())); | ||
295 | //BUG_ON(is_container(linked) && linked->rt_param.edfsc_params.domain->timer_armed); | ||
275 | 296 | ||
276 | /* Currently linked task is set to be unlinked. */ | 297 | /* Currently linked task is set to be unlinked. */ |
277 | if (entry->linked) | 298 | if (entry->linked && entry->linked->rt_param.linked_on == entry->cpu) |
278 | entry->linked->rt_param.linked_on = NO_CPU; | 299 | entry->linked->rt_param.linked_on = NO_CPU; |
279 | 300 | ||
280 | /* Link new task to CPU. */ | 301 | /* Link new task to CPU. */ |
@@ -282,6 +303,7 @@ static noinline void link_task_to_cpu(struct task_struct* linked, | |||
282 | linked->rt_param.linked_on = entry->cpu; | 303 | linked->rt_param.linked_on = entry->cpu; |
283 | 304 | ||
284 | entry->linked = linked; | 305 | entry->linked = linked; |
306 | BUG_ON(entry->linked && entry->linked->rt_param.linked_on != entry->cpu); | ||
285 | #ifdef WANT_ALL_SCHED_EVENTS | 307 | #ifdef WANT_ALL_SCHED_EVENTS |
286 | if (linked) | 308 | if (linked) |
287 | TRACE_TASK(linked, "linked to %d.\n", entry->cpu); | 309 | TRACE_TASK(linked, "linked to %d.\n", entry->cpu); |
@@ -302,6 +324,7 @@ static noinline void unlink(struct task_struct* t) | |||
302 | if (t->rt_param.linked_on != NO_CPU) { | 324 | if (t->rt_param.linked_on != NO_CPU) { |
303 | /* unlink */ | 325 | /* unlink */ |
304 | entry = &per_cpu(edfsc_cpu_entries, t->rt_param.linked_on); | 326 | entry = &per_cpu(edfsc_cpu_entries, t->rt_param.linked_on); |
327 | BUG_ON(entry->cpu != t->rt_param.linked_on); | ||
305 | t->rt_param.linked_on = NO_CPU; | 328 | t->rt_param.linked_on = NO_CPU; |
306 | link_task_to_cpu(NULL, entry); | 329 | link_task_to_cpu(NULL, entry); |
307 | BUG_ON(entry->linked || t->rt_param.linked_on != NO_CPU); | 330 | BUG_ON(entry->linked || t->rt_param.linked_on != NO_CPU); |
@@ -320,9 +343,12 @@ static noinline void unlink(struct task_struct* t) | |||
320 | //TODO change local linking | 343 | //TODO change local linking |
321 | static void g_preempt_check(void) | 344 | static void g_preempt_check(void) |
322 | { | 345 | { |
323 | struct task_struct *task; | 346 | struct task_struct *task, *temp; |
324 | cpu_entry_t *last, *target; | 347 | cpu_entry_t *last, *target; |
325 | 348 | ||
349 | if (!bheap_peek(cpu_lower_prio, &edfsc_cpu_heap)) | ||
350 | return; | ||
351 | |||
326 | // Loop through CPUs in priority order, checking if anything needs preemption | 352 | // Loop through CPUs in priority order, checking if anything needs preemption |
327 | for (last = lowest_prio_cpu(); | 353 | for (last = lowest_prio_cpu(); |
328 | edf_preemption_needed(&gsched_domain, last->linked); | 354 | edf_preemption_needed(&gsched_domain, last->linked); |
@@ -331,29 +357,33 @@ static void g_preempt_check(void) | |||
331 | 357 | ||
332 | /* preemption necessary */ | 358 | /* preemption necessary */ |
333 | task = __take_ready(&gsched_domain); | 359 | task = __take_ready(&gsched_domain); |
334 | // Don't requeue if budget is exhausted or job is completed | 360 | // Preempt_check can be called before gschedule, and therefore g_job_completion. |
335 | if (requeue_preempted_job(last->linked)) | 361 | // So, a task can be temporarily added to the ready queue, but will quickly be rectified |
336 | requeue(last->linked); | 362 | // by either this, or g_job_completion |
337 | 363 | if (requeue_preempted_job(task)) { | |
338 | // If we're dequeuing a container, put it on the appropriate core and | 364 | // Update container budget tracking |
339 | // move whatever was there before to `last` | 365 | if (is_container(task)) { |
340 | if (is_container(task)) { | 366 | last = &per_cpu(edfsc_cpu_entries, tsk_rt(task)->edfsc_params.id); |
341 | target = &per_cpu(edfsc_cpu_entries, tsk_rt(task)->edfsc_params.id); | 367 | } |
342 | TRACE("g_preempt_check: swapping tasks linked on %d and %d\n", | 368 | else if (is_container(last->linked)) { |
343 | last->cpu, target->cpu); | 369 | if (tsk_rt(last->linked)->edfsc_params.domain->timer_armed) { |
344 | link_task_to_cpu(target->linked, last); | 370 | update_container_budget(last->linked); |
371 | } | ||
372 | } | ||
373 | if (requeue_preempted_job(last->linked)) { | ||
374 | requeue(last->linked); | ||
375 | } | ||
376 | TRACE("g_preempt_check: attempting to link task %d to %d\n", | ||
377 | task->pid, target->cpu); | ||
378 | link_task_to_cpu(task, last); | ||
345 | preempt(last); | 379 | preempt(last); |
346 | } | 380 | } |
347 | TRACE("g_preempt_check: attempting to link task %d to %d\n", | ||
348 | task->pid, target->cpu); | ||
349 | link_task_to_cpu(task, target); | ||
350 | preempt(target); | ||
351 | } | 381 | } |
352 | } | 382 | } |
353 | 383 | ||
354 | static int c_preempt_check(cont_domain_t *container) | 384 | static int c_preempt_check(cont_domain_t *container) |
355 | { | 385 | { |
356 | if (is_migrating(container->scheduled) | 386 | if ((is_migrating(container->scheduled) && __peek_ready(&container->domain)) |
357 | || edf_preemption_needed(&container->domain, container->scheduled)) { | 387 | || edf_preemption_needed(&container->domain, container->scheduled)) { |
358 | preempt(&per_cpu(edfsc_cpu_entries, tsk_rt(container->container)->edfsc_params.id)); | 388 | preempt(&per_cpu(edfsc_cpu_entries, tsk_rt(container->container)->edfsc_params.id)); |
359 | return 1; | 389 | return 1; |
@@ -362,6 +392,7 @@ static int c_preempt_check(cont_domain_t *container) | |||
362 | } | 392 | } |
363 | } | 393 | } |
364 | 394 | ||
395 | // Callback for new global job release | ||
365 | static void g_release_jobs(rt_domain_t* rt, struct bheap* tasks) | 396 | static void g_release_jobs(rt_domain_t* rt, struct bheap* tasks) |
366 | { | 397 | { |
367 | unsigned long flags; | 398 | unsigned long flags; |
@@ -374,6 +405,7 @@ static void g_release_jobs(rt_domain_t* rt, struct bheap* tasks) | |||
374 | raw_spin_unlock_irqrestore(&g_lock, flags); | 405 | raw_spin_unlock_irqrestore(&g_lock, flags); |
375 | } | 406 | } |
376 | 407 | ||
408 | // Callback for new container release | ||
377 | static int c_check_resched(rt_domain_t *edf) | 409 | static int c_check_resched(rt_domain_t *edf) |
378 | { | 410 | { |
379 | cont_domain_t *cont_dom = container_of(edf, cont_domain_t, domain); | 411 | cont_domain_t *cont_dom = container_of(edf, cont_domain_t, domain); |
@@ -386,6 +418,7 @@ static int c_check_resched(rt_domain_t *edf) | |||
386 | static void g_remove_task(struct task_struct *t) | 418 | static void g_remove_task(struct task_struct *t) |
387 | { | 419 | { |
388 | BUG_ON(is_container(t)); | 420 | BUG_ON(is_container(t)); |
421 | //BUG_ON(get_rt_utilization(t) > m_util); | ||
389 | m_util -= get_rt_utilization(t); | 422 | m_util -= get_rt_utilization(t); |
390 | sys_util -= get_rt_utilization(t); | 423 | sys_util -= get_rt_utilization(t); |
391 | sys_changed = 1; | 424 | sys_changed = 1; |
@@ -393,9 +426,8 @@ static void g_remove_task(struct task_struct *t) | |||
393 | 426 | ||
394 | static void c_remove_task(struct task_struct *t) | 427 | static void c_remove_task(struct task_struct *t) |
395 | { | 428 | { |
396 | struct task_struct* container_task = tsk_rt(t)->edfsc_params.container_task; | 429 | //BUG_ON(get_rt_utilization(t) > container_domains[tsk_rt(t)->task_params.cpu].f_util); |
397 | tsk_rt(container_task)->edfsc_params.domain->f_util -= | 430 | container_domains[tsk_rt(t)->task_params.cpu].f_util -= get_rt_utilization(t); |
398 | get_rt_utilization(t); | ||
399 | sys_util -= get_rt_utilization(t); | 431 | sys_util -= get_rt_utilization(t); |
400 | sys_changed = 1; | 432 | sys_changed = 1; |
401 | } | 433 | } |
@@ -415,10 +447,11 @@ static void migrate_task(struct task_struct *t) | |||
415 | remove(tsk_rt(t)->domain, t); | 447 | remove(tsk_rt(t)->domain, t); |
416 | // Remove the util of the "fake reservation task"(specified by the paper) from the system | 448 | // Remove the util of the "fake reservation task"(specified by the paper) from the system |
417 | sys_util -= get_rt_utilization(t); | 449 | sys_util -= get_rt_utilization(t); |
418 | prepare_for_next_period(t); | 450 | m_util -= get_rt_utilization(t); |
419 | tsk_rt(t)->domain = (rt_domain_t*)tsk_rt(t)->edfsc_params.move_to; | 451 | tsk_rt(t)->domain = (rt_domain_t*)tsk_rt(t)->edfsc_params.move_to; |
420 | tsk_rt(t)->edfsc_params.container_task = tsk_rt(t)->edfsc_params.move_to->container; | 452 | tsk_rt(t)->edfsc_params.container_task = tsk_rt(t)->edfsc_params.move_to->container; |
421 | requeue(t); | 453 | requeue(t); |
454 | c_preempt_check((cont_domain_t*)tsk_rt(t)->domain); | ||
422 | tsk_rt(t)->edfsc_params.move_to = NULL; | 455 | tsk_rt(t)->edfsc_params.move_to = NULL; |
423 | sys_changed = 1; | 456 | sys_changed = 1; |
424 | } | 457 | } |
@@ -429,11 +462,16 @@ static void migrate_task(struct task_struct *t) | |||
429 | * Note: This is shared by container_boundary() and g_task_completion(). | 462 | * Note: This is shared by container_boundary() and g_task_completion(). |
430 | */ | 463 | */ |
431 | static void c_release(struct task_struct *t) { | 464 | static void c_release(struct task_struct *t) { |
432 | cpu_entry_t* entry = &per_cpu(edfsc_cpu_entries, tsk_rt(t)->edfsc_params.id); | 465 | cpu_entry_t* entry; |
466 | |||
467 | BUG_ON(!is_container(t)); | ||
468 | BUG_ON(t->rt_param.edfsc_params.domain->timer_armed); | ||
469 | |||
470 | entry = &per_cpu(edfsc_cpu_entries, tsk_rt(t)->edfsc_params.id); | ||
471 | tsk_rt(t)->task_params.exec_cost = from_fp(get_rt_utilization(t) * get_rt_period(t)); | ||
433 | prepare_for_next_period(t); | 472 | prepare_for_next_period(t); |
434 | if (is_early_releasing(t) || is_released(t, litmus_clock())) | 473 | if (is_early_releasing(t) || is_released(t, litmus_clock())) |
435 | sched_trace_task_release(t); | 474 | sched_trace_task_release(t); |
436 | tsk_rt(t)->task_params.exec_cost = from_fp(get_rt_utilization(t) * get_rt_period(t)); | ||
437 | /* If this container is fully provisioned, remove it from gsched_domain, | 475 | /* If this container is fully provisioned, remove it from gsched_domain, |
438 | * edfsc_cpu_heap, and disable the idle enforcement timer. If not, restore. | 476 | * edfsc_cpu_heap, and disable the idle enforcement timer. If not, restore. |
439 | */ | 477 | */ |
@@ -441,14 +479,18 @@ static void c_release(struct task_struct *t) { | |||
441 | // Make this cpu unavailable to the global scheduler | 479 | // Make this cpu unavailable to the global scheduler |
442 | if (bheap_node_in_heap(entry->hn)) | 480 | if (bheap_node_in_heap(entry->hn)) |
443 | remove_cpu_from_global(entry); | 481 | remove_cpu_from_global(entry); |
444 | // Fully provisioned containers always run, so just set this here | ||
445 | if (entry->linked != t) | ||
446 | link_task_to_cpu(t, entry); | ||
447 | // Note that we no longer need the global scheduler to schedule us | 482 | // Note that we no longer need the global scheduler to schedule us |
448 | if (is_queued(t)) | 483 | if (is_queued(t)) { |
449 | remove(&gsched_domain, t); | 484 | remove(&gsched_domain, t); |
450 | // Fully provisioned containers always run, so idle enforcement is superfluous | 485 | } |
451 | cancel_idle_enforcement_timer(t); | 486 | // Fully provisioned containers always run, so just set this here |
487 | if (entry->linked != t) { | ||
488 | BUG_ON(is_container(entry->linked)); | ||
489 | if (requeue_preempted_job(entry->linked)) { | ||
490 | requeue(entry->linked); | ||
491 | } | ||
492 | link_task_to_cpu(t, entry); | ||
493 | } | ||
452 | tsk_rt(t)->edfsc_params.domain->scheduled_last_exec_time = litmus_clock(); | 494 | tsk_rt(t)->edfsc_params.domain->scheduled_last_exec_time = litmus_clock(); |
453 | // Run schedule again to make sure that we're run | 495 | // Run schedule again to make sure that we're run |
454 | preempt(entry); | 496 | preempt(entry); |
@@ -457,13 +499,9 @@ static void c_release(struct task_struct *t) { | |||
457 | if (!bheap_node_in_heap(entry->hn)) | 499 | if (!bheap_node_in_heap(entry->hn)) |
458 | add_cpu_to_global(entry); | 500 | add_cpu_to_global(entry); |
459 | // Note that container's aren't real tasks and thus can't block | 501 | // Note that container's aren't real tasks and thus can't block |
460 | // Let g_preempt_check() decide what to run, don't impose | ||
461 | unlink(t); | 502 | unlink(t); |
462 | // Request to be scheduled globally again | 503 | // Request to be scheduled globally again |
463 | if (!is_queued(t)) | 504 | requeue(t); |
464 | requeue(t); | ||
465 | // Re-run our EDF scheduling to adjust for the added core | ||
466 | g_preempt_check(); | ||
467 | } | 505 | } |
468 | } | 506 | } |
469 | 507 | ||
@@ -476,15 +514,19 @@ static noinline void g_job_completion(struct task_struct* t, int forced) | |||
476 | 514 | ||
477 | TRACE_TASK(t, "g_job_completion(forced=%d).\n", forced); | 515 | TRACE_TASK(t, "g_job_completion(forced=%d).\n", forced); |
478 | 516 | ||
479 | tsk_rt(t)->completed = 0; | ||
480 | unlink(t); | 517 | unlink(t); |
518 | tsk_rt(t)->completed = 0; | ||
481 | 519 | ||
482 | // When a migrating task is being turned turned into a fixed task | 520 | // When a migrating task is being turned turned into a fixed task |
483 | if (is_migrating(t) && tsk_rt(t)->edfsc_params.move_to) { | 521 | if (is_migrating(t) && tsk_rt(t)->edfsc_params.move_to) { |
484 | if (t->rt_param.job_params.lateness > 0) { | 522 | prepare_for_next_period(t); |
485 | // Don't wait if late | 523 | if (is_early_releasing(t) || is_released(t, litmus_clock())) |
524 | sched_trace_task_release(t); | ||
525 | if (tsk_rt(t)->job_params.lateness > 0) { | ||
526 | // Don't wait if prev job was tardy | ||
486 | migrate_task(t); | 527 | migrate_task(t); |
487 | } else { | 528 | } else { |
529 | list_add(&t->edfsc_qnode, &pending_removes); | ||
488 | hrtimer_start(&t->edfsc_deadline_timer, ns_to_ktime(get_deadline(t)), | 530 | hrtimer_start(&t->edfsc_deadline_timer, ns_to_ktime(get_deadline(t)), |
489 | HRTIMER_MODE_ABS_PINNED); | 531 | HRTIMER_MODE_ABS_PINNED); |
490 | } | 532 | } |
@@ -499,46 +541,17 @@ static noinline void g_job_completion(struct task_struct* t, int forced) | |||
499 | requeue(t); | 541 | requeue(t); |
500 | g_preempt_check(); | 542 | g_preempt_check(); |
501 | } | 543 | } |
502 | /* A container may be in several different states when it finishes. It may: | ||
503 | * - Be scheduling a migrating task that is finished, blocked, or out of budget | ||
504 | * - Be scheduling a fixed task | ||
505 | * - Be scheduling nothing | ||
506 | * If there's a migrating task being scheduled, we can't unconditionally | ||
507 | * requeue it. Often, we may actually have to call g_job_completion() on | ||
508 | * that migrating task. If we finish while running a fixed task, we just | ||
509 | * "freeze" it in the container - edfsc_cschedule() will take care of | ||
510 | * processing its state when the container is rescheduled. | ||
511 | * | ||
512 | * If the container is tardy, we process its scheduled task as in the non- | ||
513 | * tardy case, then just immediately call c_release() on the container. | ||
514 | */ | ||
515 | } else if (is_container(t)) { | 544 | } else if (is_container(t)) { |
516 | /* | ||
517 | struct task_struct** child = &tsk_rt(t)->edfsc_params.domain->scheduled; | ||
518 | // No need to handle fixed tasks, cschedule will do that when it runs next | ||
519 | if (*child && is_migrating(*child)) { | ||
520 | BUG_ON(is_queued(*child)); | ||
521 | // If migrating and done | ||
522 | if (is_completed(*child) || (budget_enforced(*child) && budget_exhausted(*child))) { | ||
523 | g_job_completion(*child, budget_enforced(*child) && budget_exhausted(*child)); | ||
524 | // If migrating and blocked | ||
525 | } else if (!is_current_running()) { | ||
526 | unlink(*child); | ||
527 | // Otherwise it can keep running globally | ||
528 | } else { | ||
529 | requeue(*child); | ||
530 | } | ||
531 | // Regardless, we never "freeze" a migrating task in a container | ||
532 | *child = NULL; | ||
533 | } | ||
534 | */ | ||
535 | // When a container job finishes late, release it immediately | 545 | // When a container job finishes late, release it immediately |
536 | if (tsk_rt(t)->edfsc_params.can_release) { | 546 | if (get_deadline(t) < litmus_clock()) { |
537 | tsk_rt(t)->edfsc_params.can_release = 0; | ||
538 | c_release(t); | 547 | c_release(t); |
548 | g_preempt_check(); | ||
539 | if (get_rt_utilization(t) == to_fp(1)) | 549 | if (get_rt_utilization(t) == to_fp(1)) |
540 | manage_idle_enforcement_timer(t); | 550 | manage_idle_enforcement_timer(t); |
541 | } | 551 | } |
552 | else { | ||
553 | tsk_rt(t)->completed = 1; | ||
554 | } | ||
542 | } | 555 | } |
543 | } | 556 | } |
544 | 557 | ||
@@ -557,33 +570,25 @@ static void c_job_completion(struct task_struct* t, int forced) | |||
557 | // As long as this only touches CPU-local state, it shouldn't need g_lock: | 570 | // As long as this only touches CPU-local state, it shouldn't need g_lock: |
558 | static void g_finish_switch(struct task_struct *prev) | 571 | static void g_finish_switch(struct task_struct *prev) |
559 | { | 572 | { |
573 | unsigned long flags; | ||
560 | cpu_entry_t* entry = this_cpu_ptr(&edfsc_cpu_entries); | 574 | cpu_entry_t* entry = this_cpu_ptr(&edfsc_cpu_entries); |
561 | struct task_struct* container = &container_tasks[entry->cpu]; | 575 | struct task_struct* container = &container_tasks[entry->cpu]; |
562 | unsigned long flags; | 576 | raw_spin_lock_irqsave(&g_lock, flags); |
563 | BUG_ON(is_realtime(current) && tsk_rt(current)->domain == NULL); | ||
564 | |||
565 | // FIXME: It's really expensive to put a lock in here, but since we touch | ||
566 | // members of entry multiple times, we have to lock. Otherwise we | ||
567 | // may make an if branch based off entry->linked, and then have it | ||
568 | // change before we can set entry->scheduled. | ||
569 | //raw_spin_lock_irqsave(&g_lock, flags); | ||
570 | preempt_disable(); | ||
571 | entry->scheduled = is_realtime(current) ? current : NULL; | 577 | entry->scheduled = is_realtime(current) ? current : NULL; |
572 | // If we're scheduling a task in a container, set entry->scheduled to the container | 578 | // If we're scheduling a task in a container, set entry->scheduled to the container |
573 | if (entry->scheduled) { | 579 | if (entry->scheduled) { |
574 | if (tsk_rt(container)->edfsc_params.domain->scheduled == entry->scheduled) | 580 | if (entry->scheduled->rt_param.edfsc_params.container_task) { |
575 | entry->scheduled = container; | 581 | entry->scheduled = entry->scheduled->rt_param.edfsc_params.container_task; |
582 | } | ||
576 | } | 583 | } |
577 | // occurs when current is non-rt, and linked is a container | 584 | // occurs when current is non-rt, and linked is a container |
578 | // this happens when an empty container "task" is supposed to be current | 585 | // this happens when an empty container "task" is supposed to be current |
579 | // but because it's not a real task, a non-rt task is current instead | 586 | // but because it's not a real task, a non-rt task is current instead |
580 | else if (is_container(entry->linked)) { | 587 | else if (tsk_rt(container)->scheduled_on != NO_CPU){ |
581 | entry->scheduled = entry->linked; | 588 | entry->scheduled = container; |
582 | } | 589 | } |
583 | 590 | ||
584 | BUG_ON(is_fixed(entry->scheduled)); | 591 | raw_spin_unlock_irqrestore(&g_lock, flags); |
585 | //raw_spin_unlock_irqrestore(&g_lock, flags); | ||
586 | preempt_enable(); | ||
587 | #ifdef WANT_ALL_SCHED_EVENTS | 592 | #ifdef WANT_ALL_SCHED_EVENTS |
588 | TRACE_TASK(prev, "switched away from\n"); | 593 | TRACE_TASK(prev, "switched away from\n"); |
589 | #endif | 594 | #endif |
@@ -600,12 +605,14 @@ static int fifo_prio(struct bheap_node* _a, struct bheap_node* _b) | |||
600 | * @param cedf Pointer to tsk_rt(container)->edfsc_params->domain | 605 | * @param cedf Pointer to tsk_rt(container)->edfsc_params->domain |
601 | * @param prev Previous task running on this processor before schedule was called | 606 | * @param prev Previous task running on this processor before schedule was called |
602 | */ | 607 | */ |
603 | static void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) | 608 | static noinline void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) |
604 | { | 609 | { |
605 | rt_domain_t *edf = &cedf->domain; | 610 | rt_domain_t *edf = &cedf->domain; |
606 | 611 | ||
607 | struct task_struct* next; | 612 | struct task_struct* next; |
613 | struct task_struct* other_t; | ||
608 | struct bheap temp; | 614 | struct bheap temp; |
615 | cpu_entry_t *this_entry, *other_entry; | ||
609 | int out_of_time, sleep, preempt, | 616 | int out_of_time, sleep, preempt, |
610 | np, exists, blocks, resched; | 617 | np, exists, blocks, resched; |
611 | // XXX: The scheduler we copied this from also used `cont_out_of_time`. Is | 618 | // XXX: The scheduler we copied this from also used `cont_out_of_time`. Is |
@@ -620,13 +627,13 @@ static void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) | |||
620 | 627 | ||
621 | /* (0) Determine state */ | 628 | /* (0) Determine state */ |
622 | exists = cedf->scheduled != NULL; | 629 | exists = cedf->scheduled != NULL; |
623 | blocks = exists && !is_current_running(); | 630 | blocks = exists && current == cedf->scheduled && !is_current_running(); |
624 | out_of_time = exists && budget_enforced(cedf->scheduled) | 631 | out_of_time = exists && budget_enforced(cedf->scheduled) |
625 | && budget_exhausted(cedf->scheduled); | 632 | && budget_exhausted(cedf->scheduled); |
626 | np = exists && is_np(cedf->scheduled); | 633 | np = exists && is_np(cedf->scheduled); |
627 | sleep = exists && is_completed(cedf->scheduled); | 634 | sleep = exists && is_completed(cedf->scheduled); |
628 | preempt = (is_migrating(cedf->scheduled) && __peek_ready(edf)) || | 635 | preempt = (is_migrating(cedf->scheduled) && __peek_ready(edf)) || |
629 | (exists && edf_preemption_needed(edf, cedf->scheduled)); | 636 | edf_preemption_needed(edf, cedf->scheduled); |
630 | 637 | ||
631 | /* If we need to preempt do so. | 638 | /* If we need to preempt do so. |
632 | * The following checks set resched to 1 in case of special | 639 | * The following checks set resched to 1 in case of special |
@@ -647,15 +654,19 @@ static void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) | |||
647 | if (!np && (out_of_time || sleep)) { | 654 | if (!np && (out_of_time || sleep)) { |
648 | if (is_fixed(cedf->scheduled)) | 655 | if (is_fixed(cedf->scheduled)) |
649 | c_job_completion(cedf->scheduled, !sleep); | 656 | c_job_completion(cedf->scheduled, !sleep); |
650 | else | 657 | else { |
658 | tsk_rt(cedf->scheduled)->edfsc_params.container_task = NULL; | ||
651 | g_job_completion(cedf->scheduled, !sleep); | 659 | g_job_completion(cedf->scheduled, !sleep); |
660 | } | ||
652 | resched = 1; | 661 | resched = 1; |
653 | } | 662 | } |
654 | |||
655 | // Deschedule any background jobs if a fixed task is ready | 663 | // Deschedule any background jobs if a fixed task is ready |
656 | if (is_migrating(cedf->scheduled) || preempt) { | 664 | else if (!np && preempt) { |
657 | if (!sleep && !out_of_time && !blocks && !is_queued(cedf->scheduled)) | 665 | if (!blocks && cedf->scheduled && !is_queued(cedf->scheduled)) { |
666 | if (is_migrating(cedf->scheduled)) | ||
667 | tsk_rt(cedf->scheduled)->edfsc_params.container_task = NULL; | ||
658 | requeue(cedf->scheduled); | 668 | requeue(cedf->scheduled); |
669 | } | ||
659 | resched = 1; | 670 | resched = 1; |
660 | } | 671 | } |
661 | 672 | ||
@@ -665,28 +676,40 @@ static void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) | |||
665 | */ | 676 | */ |
666 | next = NULL; | 677 | next = NULL; |
667 | if (blocks || !exists || (!np && resched)) { | 678 | if (blocks || !exists || (!np && resched)) { |
668 | /*if (exists && !out_of_time && !sleep && !is_queued(cedf->scheduled)) { | 679 | BUG_ON(cedf->scheduled && !blocks && !out_of_time && !sleep && !is_migrating(cedf->scheduled) && !is_queued(cedf->scheduled)); |
669 | requeue(cedf->scheduled); | ||
670 | }*/ | ||
671 | next = __take_ready(edf); | 680 | next = __take_ready(edf); |
681 | // Check for direct swap (1->2, 2->1) scenarios, which can cause deadlock | ||
682 | /*if (next) { | ||
683 | other_entry = &per_cpu(edfsc_cpu_entries, next->cpu); | ||
684 | this_entry = this_cpu_ptr(&edfsc_cpu_entries); | ||
685 | if (other_entry != this_entry | ||
686 | && other_entry->cpu == this_entry->scheduled->cpu) { | ||
687 | requeue(next); | ||
688 | next = NULL; | ||
689 | } | ||
690 | }*/ | ||
672 | } else if (exists) { | 691 | } else if (exists) { |
673 | // This is safe when background scheduling, as we can only get here if | 692 | // This is safe when background scheduling, as we can only get here if |
674 | // there were no other fixed tasks ready to run. | 693 | // there were no other fixed tasks ready to run. |
694 | BUG_ON(is_queued(cedf->scheduled)); | ||
675 | next = cedf->scheduled; | 695 | next = cedf->scheduled; |
676 | } | 696 | } |
677 | 697 | ||
698 | this_entry = this_cpu_ptr(&edfsc_cpu_entries); | ||
678 | if (next) { | 699 | if (next) { |
679 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); | 700 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); |
701 | // Give the container a little breathing room, otherwise, the core will be pounded with work | ||
702 | // Will often trigger watchdog due to continous execution | ||
680 | } else { | 703 | } else { |
681 | // Find a task in gsched_domain that isn't a container to background schedule | 704 | // Find a task in gsched_domain that isn't a container to background schedule |
682 | bheap_init(&temp); // XXX this seems inefficient - maybe use a global temp? | 705 | bheap_init(&temp); |
683 | next = __take_ready(&gsched_domain); | 706 | next = __take_ready(&gsched_domain); |
684 | while (is_container(next)) { | 707 | while (is_container(next) || (is_migrating(next) && next->cpu != this_entry->cpu)) { |
685 | bheap_insert(fifo_prio, &temp, tsk_rt(next)->heap_node); | 708 | bheap_insert(fifo_prio, &temp, tsk_rt(next)->heap_node); |
686 | next = __take_ready(&gsched_domain); | 709 | next = __take_ready(&gsched_domain); |
687 | BUG_ON(next && is_queued(next)); | ||
688 | } | 710 | } |
689 | if (next) { | 711 | if (next) { |
712 | tsk_rt(next)->edfsc_params.container_task = cedf->container; | ||
690 | TRACE_TASK(next, "background scheduling at %llu\n", litmus_clock()); | 713 | TRACE_TASK(next, "background scheduling at %llu\n", litmus_clock()); |
691 | } else { | 714 | } else { |
692 | TRACE("container becomes idle at %llu\n", litmus_clock()); | 715 | TRACE("container becomes idle at %llu\n", litmus_clock()); |
@@ -696,6 +719,22 @@ static void edfsc_cschedule(cont_domain_t* cedf, struct task_struct * prev) | |||
696 | } | 719 | } |
697 | } | 720 | } |
698 | 721 | ||
722 | if (next && next->cpu != this_entry->cpu) { | ||
723 | other_entry = &per_cpu(edfsc_cpu_entries, next->cpu); | ||
724 | other_t = is_container(other_entry->linked) ? | ||
725 | other_entry->linked->rt_param.edfsc_params.domain->scheduled : other_entry->linked; | ||
726 | // If we detect a direct swap, and the other task has already gone through gschedule | ||
727 | // To prevent a deadlock, we let them go first and reschedule | ||
728 | if (other_t && other_t->cpu == this_entry->cpu) { | ||
729 | if (is_migrating(other_t) || other_entry->linked->rt_param.scheduled_on == other_entry->cpu) { | ||
730 | if (is_migrating(next)) | ||
731 | next->rt_param.edfsc_params.container_task = NULL; | ||
732 | requeue(next); | ||
733 | next = NULL; | ||
734 | } | ||
735 | } | ||
736 | } | ||
737 | |||
699 | cedf->scheduled = next; | 738 | cedf->scheduled = next; |
700 | } | 739 | } |
701 | 740 | ||
@@ -712,53 +751,49 @@ static struct task_struct *edfsc_gschedule(struct task_struct *prev) | |||
712 | 751 | ||
713 | /* sanity checking */ | 752 | /* sanity checking */ |
714 | BUG_ON(entry->scheduled && entry->scheduled != prev && !is_container(entry->scheduled)); | 753 | BUG_ON(entry->scheduled && entry->scheduled != prev && !is_container(entry->scheduled)); |
715 | //BUG_ON(entry->scheduled && entry->scheduled != prev && is_realtime(prev) && | ||
716 | // (cont_domain_t*)tsk_rt(prev)->domain != tsk_rt(entry->scheduled)->edfsc_params.domain); | ||
717 | //BUG_ON(entry->scheduled && entry->scheduled != prev && is_realtime(prev) && | ||
718 | // prev != tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled); | ||
719 | // It's okay for the previously scheduled task to not be rt if we think a | 754 | // It's okay for the previously scheduled task to not be rt if we think a |
720 | // container task is scheduled and the container doesn't have any pending | 755 | // container task is scheduled and the container doesn't have any pending |
721 | // jobs of fixed tasks. | 756 | // jobs of fixed tasks. |
722 | BUG_ON(entry->scheduled && !is_container(entry->scheduled) && !is_realtime(prev)); | 757 | BUG_ON(entry->scheduled && !is_container(entry->scheduled) && !is_realtime(prev)); |
723 | // Bug if we didn't think anything was scheduled, but a realtime task was running on our CPU | 758 | // Bug if we didn't think anything was scheduled, but a realtime task was running on our CPU |
724 | BUG_ON(is_realtime(prev) && tsk_rt(prev)->linked_on != NO_CPU && !entry->scheduled); | 759 | //BUG_ON(is_realtime(prev) && tsk_rt(prev)->linked_on != NO_CPU && !entry->scheduled); |
725 | |||
726 | if (is_container(entry->scheduled)) { | ||
727 | lt_t now = litmus_clock(); | ||
728 | tsk_rt(entry->scheduled)->job_params.exec_time += now | ||
729 | - tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled_last_exec_time; | ||
730 | tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled_last_exec_time = now; | ||
731 | } | ||
732 | 760 | ||
733 | /* (0) Determine state */ | 761 | /* (0) Determine state */ |
734 | exists = entry->scheduled != NULL; | 762 | exists = entry->scheduled != NULL; |
735 | is_cont = is_container(entry->scheduled); | 763 | is_cont = is_container(entry->scheduled); |
736 | blocks = exists && !is_cont && !is_current_running(); | 764 | blocks = exists && !is_cont && !is_current_running(); |
737 | out_of_time = exists && budget_enforced(entry->scheduled) | ||
738 | && budget_exhausted(entry->scheduled); | ||
739 | np = exists && !is_cont && is_np(entry->scheduled); | 765 | np = exists && !is_cont && is_np(entry->scheduled); |
740 | sleep = exists && !is_cont && is_completed(entry->scheduled); | 766 | sleep = exists && is_completed(entry->scheduled); |
741 | preempted = entry->scheduled != entry->linked; | 767 | preempted = entry->scheduled != entry->linked; |
742 | 768 | ||
769 | /* Manually track container budget */ | ||
770 | if (is_cont && (tsk_rt(entry->scheduled)->edfsc_params.domain->timer_armed || sleep)) { | ||
771 | update_container_budget(entry->scheduled); | ||
772 | out_of_time = exists && budget_enforced(entry->scheduled) | ||
773 | && budget_exhausted(entry->scheduled); | ||
774 | /* Cancel container enforcement timer if container is fully provisioned and out of sync with | ||
775 | * container_boundary, or if it is currently being scheduled in gedf | ||
776 | */ | ||
777 | if (bheap_node_in_heap(entry->hn) || (!bheap_node_in_heap(entry->hn) && out_of_time)) | ||
778 | cancel_idle_enforcement_timer(entry->scheduled); | ||
779 | } | ||
780 | else { | ||
781 | out_of_time = exists && budget_enforced(entry->scheduled) | ||
782 | && budget_exhausted(entry->scheduled); | ||
783 | } | ||
743 | 784 | ||
744 | if (exists) | 785 | if (exists) |
745 | TRACE_TASK(prev, | 786 | TRACE_TASK(prev, |
746 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " | 787 | "blocks:%d out_of_time:%d np:%d sleep:%d preempt:%d " |
747 | "state:%d sig:%d is_cont:%d\n", | 788 | "state:%d sig:%d is_cont:%d\n", |
748 | blocks, out_of_time, np, sleep, preempt, | 789 | blocks, out_of_time, np, sleep, preempt, |
749 | prev->state, signal_pending(prev), is_cont); | 790 | prev->state, signal_pending(prev), is_cont); |
750 | 791 | ||
751 | if (entry->linked && preempted) | 792 | if (entry->linked && preempted) |
752 | TRACE_TASK(prev, "will be preempted by %s/%d\n", | 793 | TRACE_TASK(prev, "will be preempted by %s/%d\n", |
753 | entry->linked->comm, entry->linked->pid); | 794 | entry->linked->comm, entry->linked->pid); |
754 | |||
755 | if (exists && preempted && !is_queued(entry->scheduled)) | ||
756 | requeue(entry->scheduled); | ||
757 | 795 | ||
758 | /* If a task blocks we have no choice but to reschedule. | 796 | // If a task blocks we have no choice but to reschedule. |
759 | * Note: containers never block, so if blocks is true and we're background | ||
760 | * scheduling, we want to unlink `prev` NOT `entry->scheduled`. | ||
761 | */ | ||
762 | if (blocks) | 797 | if (blocks) |
763 | unlink(prev); | 798 | unlink(prev); |
764 | 799 | ||
@@ -778,76 +813,49 @@ static struct task_struct *edfsc_gschedule(struct task_struct *prev) | |||
778 | * for blocked jobs). | 813 | * for blocked jobs). |
779 | */ | 814 | */ |
780 | if (!np && (out_of_time || sleep)) { | 815 | if (!np && (out_of_time || sleep)) { |
781 | // This is not a global job completion if we're in a fully provisioned container | 816 | g_job_completion(entry->scheduled, !sleep); |
782 | if (bheap_node_in_heap(entry->hn)) | ||
783 | g_job_completion(entry->scheduled, !sleep); | ||
784 | else | ||
785 | unlink(entry->scheduled); | ||
786 | } | 817 | } |
787 | 818 | ||
788 | // We should have descheduled globally scheduled tasks without budget by now | 819 | BUG_ON(!bheap_node_in_heap(entry->hn) && entry->linked && !is_container(entry->linked)); |
789 | BUG_ON(entry->linked && budget_enforced(entry->linked) && budget_exhausted(entry->linked)); | 820 | |
790 | 821 | if (!entry->linked && bheap_node_in_heap(entry->hn)) { | |
791 | // Determine what to run next (set entry->linked) | 822 | g_preempt_check(); |
792 | if (!entry->linked) { | ||
793 | struct task_struct* task = __take_ready(&gsched_domain); | ||
794 | // Make sure that containers are only scheduled on cores with same id | ||
795 | if (is_container(task) && entry->cpu != tsk_rt(task)->edfsc_params.id) { | ||
796 | // Get cpu_entry for task's core assignment | ||
797 | cpu_entry_t* target = &per_cpu(edfsc_cpu_entries, tsk_rt(task)->edfsc_params.id); | ||
798 | // Make sure that someone didn't requeue `task` without unlinking it | ||
799 | BUG_ON(target->linked && target->linked == task); | ||
800 | // Move their linked task to us | ||
801 | link_task_to_cpu(target->linked, entry); | ||
802 | // Setup the container to run next on the remote core | ||
803 | link_task_to_cpu(task, target); | ||
804 | // Alert the remote core that it now needs to reschedule | ||
805 | preempt(target); | ||
806 | } else if (task) { | ||
807 | // We'll now schedule the ready task here | ||
808 | link_task_to_cpu(task, entry); | ||
809 | // Tasks on the ready queue should never be out of budget, so it's safe | ||
810 | // to continue the scheduling process from this point on. | ||
811 | } | ||
812 | } | 823 | } |
813 | 824 | ||
814 | BUG_ON(entry->linked && budget_enforced(entry->linked) && budget_exhausted(entry->linked)); | 825 | BUG_ON(entry->linked && is_queued(entry->linked)); |
815 | BUG_ON(!bheap_node_in_heap(entry->hn) && entry->linked && tsk_rt(entry->linked)->edfsc_params.id != entry->cpu); | 826 | BUG_ON(!bheap_node_in_heap(entry->hn) && entry->linked |
827 | && tsk_rt(entry->linked)->edfsc_params.id != entry->cpu); | ||
816 | BUG_ON(is_container(entry->linked) && tsk_rt(entry->linked)->edfsc_params.id != entry->cpu); | 828 | BUG_ON(is_container(entry->linked) && tsk_rt(entry->linked)->edfsc_params.id != entry->cpu); |
817 | 829 | ||
818 | /* The final scheduling decision. Do we need to switch for some reason? | 830 | /* The final scheduling decision. Do we need to switch for some reason? |
819 | * If linked is different from scheduled, then select linked as next. | 831 | * If linked is different from scheduled, then select linked as next. |
820 | */ | 832 | */ |
821 | if ((!np || blocks) && entry->linked != entry->scheduled) { | 833 | if ((!np || blocks) && entry->linked != entry->scheduled) { |
822 | /* Schedule a linked job? */ | 834 | // Set the newly linked job to be scheduled |
823 | if (entry->linked) { | 835 | if (entry->linked) { |
824 | next = entry->linked; | 836 | next = entry->linked; |
837 | tsk_rt(entry->linked)->scheduled_on = entry->cpu; | ||
838 | BUG_ON(is_queued(entry->linked)); | ||
825 | TRACE_TASK(next, "scheduled on P%d\n", smp_processor_id()); | 839 | TRACE_TASK(next, "scheduled on P%d\n", smp_processor_id()); |
826 | } | 840 | } |
827 | // Note what was running before | 841 | // Set the previously linked to to be unscheduled |
828 | if (entry->scheduled) { | 842 | if (entry->scheduled) { |
843 | /* When a scheduled is linked to another cpu, from this cpu, there's no guarantee on the order | ||
844 | * in which gschedule is called on both cpus. If it has already have scheduled_on set to the other | ||
845 | * cpu, then we have to preserve it and can't just set it to NO_CPU | ||
846 | */ | ||
847 | if (tsk_rt(entry->scheduled)->scheduled_on == entry->cpu) { | ||
848 | tsk_rt(entry->scheduled)->scheduled_on = NO_CPU; | ||
849 | } | ||
829 | TRACE_TASK(entry->scheduled, "descheduled\n"); | 850 | TRACE_TASK(entry->scheduled, "descheduled\n"); |
830 | } | 851 | } |
831 | } else if (entry->scheduled) { | 852 | } else if (entry->scheduled) { |
832 | // If we've been running a container, make sure that it has nothing new to schedule | 853 | next = entry->scheduled; |
833 | if (is_container(entry->scheduled)) | 854 | tsk_rt(next)->scheduled_on = entry->cpu; |
834 | next = entry->scheduled; | ||
835 | // Otherwise we can keep running any tasks we previously scheduled | ||
836 | else if (is_realtime(prev)) | ||
837 | next = prev; | ||
838 | } | 855 | } |
856 | BUG_ON(next && get_exec_time(next) > get_exec_cost(next)); | ||
839 | 857 | ||
840 | // Tell LITMUS^RT that we choose a task and are done scheduling after return | 858 | // If next is a container, then perform cschedule to determine the fixed task to schedule |
841 | sched_state_task_picked(); | ||
842 | |||
843 | // When we transition from doing background scheduling to doing normal | ||
844 | // scheduling, we may schedule the same task. Unfortunately, when this | ||
845 | // happens, g_finish_switch() will /not/ be called. Fix the state manually. | ||
846 | temp = entry->scheduled; | ||
847 | entry->scheduled = next; | ||
848 | |||
849 | // if no fixed tasks to be scheduled by the container, then container->scheduled | ||
850 | // should be the previous non-rt task if any | ||
851 | if (is_container(next)) { | 859 | if (is_container(next)) { |
852 | edfsc_cschedule(tsk_rt(next)->edfsc_params.domain, prev); | 860 | edfsc_cschedule(tsk_rt(next)->edfsc_params.domain, prev); |
853 | if (bheap_node_in_heap(entry->hn)) | 861 | if (bheap_node_in_heap(entry->hn)) |
@@ -855,26 +863,30 @@ static struct task_struct *edfsc_gschedule(struct task_struct *prev) | |||
855 | next = tsk_rt(next)->edfsc_params.domain->scheduled; | 863 | next = tsk_rt(next)->edfsc_params.domain->scheduled; |
856 | } | 864 | } |
857 | // When next is migrating, but previously scheduled realtime task is a container | 865 | // When next is migrating, but previously scheduled realtime task is a container |
858 | // must properly restore background scheduled task to its correct queue/heap | 866 | // must properly restore background scheduled task(if any) to its correct queue/heap |
859 | else if (is_container(temp) && next != temp) { | 867 | else if (is_container(entry->scheduled) && next != entry->scheduled) { |
860 | struct task_struct** child = &tsk_rt(temp)->edfsc_params.domain->scheduled; | 868 | struct task_struct** child = &tsk_rt(entry->scheduled)->edfsc_params.domain->scheduled; |
861 | // No need to handle fixed tasks, cschedule will do that when it runs next | 869 | // No need to handle fixed tasks, cschedule will do that when it runs next |
862 | if (*child && is_migrating(*child)) { | 870 | if (*child && is_migrating(*child)) { |
871 | int background_out_of_time = budget_enforced(*child) && budget_exhausted(*child); | ||
863 | BUG_ON(is_queued(*child)); | 872 | BUG_ON(is_queued(*child)); |
873 | BUG_ON(tsk_rt(*child)->linked_on != NO_CPU); | ||
874 | tsk_rt(*child)->edfsc_params.container_task = NULL; | ||
864 | // If migrating and done | 875 | // If migrating and done |
865 | if (is_completed(*child) || (budget_enforced(*child) && budget_exhausted(*child))) { | 876 | if (is_completed(*child) || background_out_of_time) { |
866 | g_job_completion(*child, budget_enforced(*child) && budget_exhausted(*child)); | 877 | g_job_completion(*child, background_out_of_time); |
867 | // If migrating and blocked | 878 | // If migrating and not blocked |
868 | } else if (!is_current_running()) { | 879 | } else if (is_current_running()) { |
869 | unlink(*child); | ||
870 | // Otherwise it can keep running globally | ||
871 | } else { | ||
872 | requeue(*child); | 880 | requeue(*child); |
873 | } | 881 | } |
874 | // Regardless, we never "freeze" a migrating task in a container | 882 | // Regardless, we never "freeze" a migrating task in a container |
875 | *child = NULL; | 883 | *child = NULL; |
876 | } | 884 | } |
877 | } | 885 | } |
886 | BUG_ON(is_migrating(entry->scheduled) && !tsk_rt(entry->scheduled)->edfsc_params.container_task | ||
887 | && !blocks && tsk_rt(entry->scheduled)->linked_on == NO_CPU && !is_queued(entry->scheduled)); | ||
888 | // Tell LITMUS^RT that we choose a task and are done scheduling after return | ||
889 | sched_state_task_picked(); | ||
878 | 890 | ||
879 | raw_spin_unlock_irqrestore(&g_lock, flags); | 891 | raw_spin_unlock_irqrestore(&g_lock, flags); |
880 | 892 | ||
@@ -899,39 +911,32 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
899 | int i; | 911 | int i; |
900 | struct list_head *it; | 912 | struct list_head *it; |
901 | struct list_head *temp; | 913 | struct list_head *temp; |
902 | u64 u_extra; | 914 | u64 u_extra, leeway; |
903 | cont_domain_t *container; | 915 | cont_domain_t *container; |
904 | struct task_struct *t; | 916 | struct task_struct *t; |
905 | lt_t now; | ||
906 | int num_cpus = num_online_cpus(); | 917 | int num_cpus = num_online_cpus(); |
907 | unsigned long flags; | 918 | unsigned long flags; |
908 | 919 | ||
909 | raw_spin_lock_irqsave(&g_lock, flags); | 920 | TS_SCHED_TIMER_START |
910 | |||
911 | now = litmus_clock(); | ||
912 | 921 | ||
913 | // Update budget tracking for containers | 922 | raw_spin_lock_irqsave(&g_lock, flags); |
914 | for (i = 0; i < num_cpus; i++) { | ||
915 | t = container_list[i]->container; | ||
916 | if (container_list[i]->timer_armed) | ||
917 | tsk_rt(t)->job_params.exec_time += now - container_list[i]->scheduled_last_exec_time; | ||
918 | else | ||
919 | tsk_rt(t)->job_params.exec_time = get_exec_cost(t); | ||
920 | } | ||
921 | 923 | ||
922 | t = NULL; | 924 | t = NULL; |
925 | leeway = fp_div(1, 50); | ||
923 | 926 | ||
924 | // Try to add tasks from the queue | 927 | // Try to add tasks from the queue |
925 | list_for_each_safe(it, temp, &pending_adds) { | 928 | list_for_each_safe(it, temp, &pending_adds) { |
926 | u_extra = to_fp(num_cpus) - sys_util; | ||
927 | container = NULL; | 929 | container = NULL; |
928 | t = task_of_list_node(it); | 930 | t = task_of_list_node(it); |
929 | list_del_init(it); | 931 | list_del_init(it); |
930 | if (u_extra >= get_rt_utilization(t)) { | 932 | //sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_lower_prio, NULL); // Best fit |
933 | sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_higher_prio, NULL); // Worst fit | ||
934 | if (to_fp(num_cpus) > get_rt_utilization(t) + sys_util + leeway) { | ||
931 | for (i = 0; i < num_cpus; i++) { | 935 | for (i = 0; i < num_cpus; i++) { |
932 | u64 leftover = to_fp(1) - container_domains[i].f_util; | 936 | if (to_fp(1) > get_rt_utilization(t) + container_list[i]->f_util + leeway) { |
933 | if (leftover >= get_rt_utilization(t)) { | 937 | //if (to_fp(1) > get_rt_utilization(t) + container_domains[i].f_util + leeway) { |
934 | container = &(container_domains[i]); | 938 | //container = &(container_domains[i]); |
939 | container = container_list[i]; // Used for best/worst fit | ||
935 | break; | 940 | break; |
936 | } | 941 | } |
937 | } | 942 | } |
@@ -944,13 +949,10 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
944 | tsk_rt(t)->domain = &gsched_domain; | 949 | tsk_rt(t)->domain = &gsched_domain; |
945 | tsk_rt(t)->edfsc_params.container_task = NULL; | 950 | tsk_rt(t)->edfsc_params.container_task = NULL; |
946 | m_util += get_rt_utilization(t); | 951 | m_util += get_rt_utilization(t); |
947 | //list_add(&tsk_rt(t)->edfsc_params.qnode, &migrating_tasks); | ||
948 | list_add(&t->edfsc_qnode, &migrating_tasks); | 952 | list_add(&t->edfsc_qnode, &migrating_tasks); |
949 | } | 953 | } |
950 | sys_util += get_rt_utilization(t); | 954 | sys_util += get_rt_utilization(t); |
951 | sys_changed = 1; | 955 | sys_changed = 1; |
952 | // Setup the release time for the first job to be now | ||
953 | release_at(t, litmus_clock()); | ||
954 | } | 956 | } |
955 | /* Unblock the task waiting on our admission decision. They will detect | 957 | /* Unblock the task waiting on our admission decision. They will detect |
956 | * if they have been admitted by examining if tsk_rt(t)->domain != NULL | 958 | * if they have been admitted by examining if tsk_rt(t)->domain != NULL |
@@ -962,7 +964,7 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
962 | * longer than we should. | 964 | * longer than we should. |
963 | */ | 965 | */ |
964 | raw_spin_unlock_irqrestore(&g_lock, flags); | 966 | raw_spin_unlock_irqrestore(&g_lock, flags); |
965 | wake_up_new_task(t); | 967 | BUG_ON(!wake_up_process(t)); |
966 | raw_spin_lock_irqsave(&g_lock, flags); | 968 | raw_spin_lock_irqsave(&g_lock, flags); |
967 | } | 969 | } |
968 | 970 | ||
@@ -971,34 +973,38 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
971 | // migrating tasks and potentially all the containers every period for a | 973 | // migrating tasks and potentially all the containers every period for a |
972 | // best-case Omega(m) and worst-case O(m^2) work---only once the scheduler | 974 | // best-case Omega(m) and worst-case O(m^2) work---only once the scheduler |
973 | // is actually working | 975 | // is actually working |
976 | // Done, only does stabilization when stuff changes in the system | ||
974 | // According to the paper, when we migrate, we must reserve space in the container | 977 | // According to the paper, when we migrate, we must reserve space in the container |
975 | // We do this by adding a fake task that ultimately doesn't release any jobs | 978 | // We do this by adding a fake task that ultimately doesn't release any jobs |
976 | // This is represented here by adding the utilization to sys_util | 979 | // This is represented here by adding the utilization to sys_util |
977 | // which will be subtracted when the migrating task is actually changed to fixed | 980 | // which will be subtracted when the migrating task is actually changed to fixed |
978 | if (sys_changed) { | 981 | if (sys_changed) { // change this to false to disable stabilization |
979 | list_for_each(it, &migrating_tasks) { | 982 | list_for_each_safe(it, temp, &migrating_tasks) { |
980 | struct task_struct* t = task_of_list_node(it); | 983 | struct task_struct* t = task_of_list_node(it); |
981 | // Although technically selecting the migrating tasks to be moved into containers | 984 | // Although technically selecting the migrating tasks to be moved into containers |
982 | // doesn't change m_util and the container's f_util until after the move, | 985 | // doesn't change m_util and the container's f_util until after the move, |
983 | // but since the move is guaranteed to happen before the next container_boundary | 986 | // but since the move is guaranteed to happen before the next container_boundary |
984 | // where we check all the utilization stuff, it's fine to account for it now | 987 | // where we check all the utilization stuff, it's fine to account for it now |
985 | if (!(tsk_rt(t)->edfsc_params.move_to) && !is_released(t, now) | 988 | if (!(tsk_rt(t)->edfsc_params.move_to)) { |
986 | && get_deadline(t) < get_deadline(&container_tasks[0]) + get_rt_period(&container_tasks[0])) { | ||
987 | tsk_rt(t)->edfsc_params.move_to = NULL; | 989 | tsk_rt(t)->edfsc_params.move_to = NULL; |
988 | 990 | ||
989 | container = NULL; | 991 | container = NULL; |
992 | //sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_lower_prio, NULL); // Best fit | ||
993 | //sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_higher_prio, NULL); // Worst fit | ||
990 | for (i = 0; i < num_cpus; i++) { | 994 | for (i = 0; i < num_cpus; i++) { |
991 | u64 leftover = to_fp(1) - container_domains[i].f_util; | 995 | u64 leftover = to_fp(1) - container_domains[i].f_util - leeway; |
992 | if (leftover>=get_rt_utilization(t) && to_fp(num_cpus)>=get_rt_utilization(t)+sys_util) { | 996 | //if (to_fp(1) > get_rt_utilization(t) + container_list[i]->f_util + leeway && |
997 | if (to_fp(1) > get_rt_utilization(t) + container_domains[i].f_util + leeway && | ||
998 | to_fp(num_cpus) > get_rt_utilization(t) + sys_util + leeway) { | ||
993 | container = &(container_domains[i]); | 999 | container = &(container_domains[i]); |
1000 | //container = container_list[i]; // Used for best/worst fit | ||
994 | break; | 1001 | break; |
995 | } | 1002 | } |
996 | } | 1003 | } |
997 | 1004 | ||
998 | if (container) { | 1005 | if (container) { |
999 | list_del_init(&t->edfsc_qnode); | 1006 | list_del_init(it); |
1000 | container->f_util += get_rt_utilization(t); | 1007 | container->f_util += get_rt_utilization(t); |
1001 | m_util -= get_rt_utilization(t); | ||
1002 | sys_util += get_rt_utilization(t); | 1008 | sys_util += get_rt_utilization(t); |
1003 | tsk_rt(t)->edfsc_params.move_to = container; | 1009 | tsk_rt(t)->edfsc_params.move_to = container; |
1004 | sys_changed = 1; | 1010 | sys_changed = 1; |
@@ -1012,7 +1018,7 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
1012 | int remaining; | 1018 | int remaining; |
1013 | // Sort containers by the utilization of their fixed tasks | 1019 | // Sort containers by the utilization of their fixed tasks |
1014 | sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_lower_prio, NULL); | 1020 | sort(container_list, num_cpus, sizeof(cont_domain_t *), &container_lower_prio, NULL); |
1015 | u_extra = to_fp(num_cpus) - sys_util; | 1021 | u_extra = to_fp(num_cpus) - sys_util - leeway; |
1016 | // Fully provision all the container tasks we can | 1022 | // Fully provision all the container tasks we can |
1017 | for (i = 0; i < num_cpus && u_extra >= to_fp(1) - container_list[i]->f_util; i++) { | 1023 | for (i = 0; i < num_cpus && u_extra >= to_fp(1) - container_list[i]->f_util; i++) { |
1018 | struct task_struct* t = container_list[i]->container; | 1024 | struct task_struct* t = container_list[i]->container; |
@@ -1034,7 +1040,9 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
1034 | remaining = num_cpus - i; | 1040 | remaining = num_cpus - i; |
1035 | for (; i < num_cpus; i++) { | 1041 | for (; i < num_cpus; i++) { |
1036 | struct task_struct* t = container_list[i]->container; | 1042 | struct task_struct* t = container_list[i]->container; |
1037 | tsk_rt(t)->task_params.utilization = container_list[i]->f_util + u_extra / remaining; | 1043 | u64 temp_val = container_list[i]->f_util + u_extra / remaining; |
1044 | tsk_rt(t)->task_params.utilization = (temp_val < to_fp(1)) ? temp_val : to_fp(1); | ||
1045 | BUG_ON(tsk_rt(t)->task_params.utilization > to_fp(1)); | ||
1038 | } | 1046 | } |
1039 | } | 1047 | } |
1040 | sys_changed = 0; | 1048 | sys_changed = 0; |
@@ -1043,51 +1051,73 @@ static enum hrtimer_restart container_boundary(struct hrtimer *timer) | |||
1043 | 1051 | ||
1044 | // Re-release container tasks, or tell them they can if they're tardy | 1052 | // Re-release container tasks, or tell them they can if they're tardy |
1045 | for (i = 0; i < num_cpus; i++) { | 1053 | for (i = 0; i < num_cpus; i++) { |
1046 | // will first iterate through fully provisioned containers, then not fully provisioned ones | 1054 | t = container_list[i]->container; |
1047 | struct task_struct* t = container_list[i]->container; | 1055 | int armed = container_list[i]->timer_armed; |
1048 | // If the last job completed on time, release it now | 1056 | // If the container tasks are currently scheduled, update their budget |
1049 | if (budget_exhausted(t)) { | 1057 | if (armed) { |
1058 | update_container_budget(t); | ||
1059 | } | ||
1060 | |||
1061 | /* Either container has completed, or it is fully provisioned and in sync | ||
1062 | * (thus not requiring a budget enforcement timer). | ||
1063 | */ | ||
1064 | if ((!armed && get_rt_period(t) == get_exec_cost(t)) || budget_exhausted(t) || is_completed(t)) { | ||
1050 | BUG_ON(is_queued(t)); | 1065 | BUG_ON(is_queued(t)); |
1066 | sched_trace_task_completion(t, 0); | ||
1067 | if (armed) | ||
1068 | cancel_idle_enforcement_timer(t); | ||
1069 | tsk_rt(t)->completed = 0; | ||
1051 | c_release(t); | 1070 | c_release(t); |
1052 | // Otherwise let it release itself when it completes | ||
1053 | } else { | ||
1054 | tsk_rt(t)->edfsc_params.can_release = 1; | ||
1055 | manage_idle_enforcement_timer(t); | ||
1056 | } | 1071 | } |
1057 | } | 1072 | } |
1073 | g_preempt_check(); | ||
1058 | 1074 | ||
1059 | raw_spin_unlock_irqrestore(&g_lock, flags); | 1075 | raw_spin_unlock_irqrestore(&g_lock, flags); |
1060 | 1076 | ||
1077 | TS_SCHED_TIMER_END | ||
1078 | |||
1061 | hrtimer_add_expires_ns(timer, LITMUS_QUANTUM_LENGTH_NS); | 1079 | hrtimer_add_expires_ns(timer, LITMUS_QUANTUM_LENGTH_NS); |
1062 | return HRTIMER_RESTART; | 1080 | return HRTIMER_RESTART; |
1063 | } | 1081 | } |
1064 | 1082 | ||
1083 | /* | ||
1084 | * When preempt check scheduled a task to multiple cores(due to swapping and multiple | ||
1085 | * multiple invocations of preempt_check), we should not wait for stack, and reschedule | ||
1086 | */ | ||
1087 | static bool edfsc_should_wait_for_stack(struct task_struct* t) { | ||
1088 | cpu_entry_t* entry = this_cpu_ptr(&edfsc_cpu_entries); | ||
1089 | struct task_struct* tsk = tsk_rt(t)->edfsc_params.container_task; | ||
1090 | tsk = tsk ? tsk : t; | ||
1091 | return tsk_rt(tsk)->linked_on == tsk_rt(tsk)->scheduled_on && tsk_rt(tsk)->linked_on == entry->cpu; | ||
1092 | } | ||
1093 | |||
1065 | /** | 1094 | /** |
1066 | * Fired when a task reaches its deadline and is pending deletion or migration | 1095 | * Fired when a task reaches its deadline and is pending deletion or migration |
1067 | */ | 1096 | */ |
1068 | static enum hrtimer_restart task_deadline_callback(struct hrtimer* timer) { | 1097 | static enum hrtimer_restart task_deadline_callback(struct hrtimer* timer) { |
1098 | unsigned long flags; | ||
1069 | struct task_struct *t = container_of(timer, struct task_struct, edfsc_deadline_timer); | 1099 | struct task_struct *t = container_of(timer, struct task_struct, edfsc_deadline_timer); |
1070 | 1100 | ||
1101 | raw_spin_lock_irqsave(&g_lock, flags); | ||
1071 | BUG_ON(is_container(t)); | 1102 | BUG_ON(is_container(t)); |
1072 | printk("util: %d\n", sys_util); | ||
1073 | // This is true only if set to be migrating from container_boundary | 1103 | // This is true only if set to be migrating from container_boundary |
1074 | if (tsk_rt(t)->edfsc_params.move_to) { | 1104 | if (tsk_rt(t)->edfsc_params.move_to) { |
1075 | // Migrate here if the task is not late, otherwise migrate in job_complete | 1105 | // Can only be here when called from g_job_completion |
1076 | if (!is_released(t, litmus_clock()) | 1106 | migrate_task(t); |
1077 | || (budget_enforced(t) && budget_exhausted(t)) | 1107 | // In the else case, only task_params is guaranteed to be valid |
1078 | || is_completed(t)) | 1108 | // However, in task_exit, we stored information in task_param.cpu |
1079 | migrate_task(t); | 1109 | // To help up do remove operations |
1080 | } else { | 1110 | } else { |
1081 | // A move to NULL means deletion | 1111 | // A move to NULL means deletion |
1082 | // HACK: See comment in edfsc_task_exit() | 1112 | if (tsk_rt(t)->task_params.cpu == NO_CPU) |
1083 | tsk_rt(t)->edfsc_params.container_task = (struct task_struct*)tsk_rt(t)->task_params.phase; | ||
1084 | if (is_fixed(t)) | ||
1085 | c_remove_task(t); | ||
1086 | else | ||
1087 | g_remove_task(t); | 1113 | g_remove_task(t); |
1114 | else | ||
1115 | c_remove_task(t); | ||
1088 | // Release our reference to the task struct | 1116 | // Release our reference to the task struct |
1089 | put_task_struct(t); | 1117 | put_task_struct(t); |
1090 | } | 1118 | } |
1119 | list_del_init(&t->edfsc_qnode); | ||
1120 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1091 | return HRTIMER_NORESTART; | 1121 | return HRTIMER_NORESTART; |
1092 | } | 1122 | } |
1093 | 1123 | ||
@@ -1103,20 +1133,30 @@ static void edfsc_task_new(struct task_struct* t, int on_rq, int is_scheduled) | |||
1103 | 1133 | ||
1104 | tsk_rt(t)->sporadic_release = 0; | 1134 | tsk_rt(t)->sporadic_release = 0; |
1105 | 1135 | ||
1136 | TRACE("EDF-sc: task new %d\n", t->pid); | ||
1137 | |||
1106 | // Create a timer that we'll use to delay accounting during migrations | 1138 | // Create a timer that we'll use to delay accounting during migrations |
1107 | hrtimer_init(&t->edfsc_deadline_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | 1139 | hrtimer_init(&t->edfsc_deadline_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); |
1108 | t->edfsc_deadline_timer.function = task_deadline_callback; | 1140 | t->edfsc_deadline_timer.function = task_deadline_callback; |
1109 | 1141 | ||
1110 | raw_spin_lock_irqsave(&g_lock, flags); | 1142 | raw_spin_lock_irqsave(&g_lock, flags); |
1111 | // Queue this task and request a reschedule | ||
1112 | requeue(t); | ||
1113 | preempt(entry); | ||
1114 | 1143 | ||
1115 | // Since `t` is not going to run again until we schedule, harmonize state | 1144 | release_at(t, litmus_clock()); |
1116 | t->rt_param.linked_on = NO_CPU; | 1145 | sched_trace_task_release(t); |
1146 | tsk_rt(t)->linked_on = NO_CPU; | ||
1147 | tsk_rt(t)->scheduled_on = NO_CPU; | ||
1148 | // Queue this task and request a reschedule | ||
1149 | if (on_rq || is_scheduled) { | ||
1150 | requeue(t); | ||
1151 | if (is_migrating(t)) { | ||
1152 | g_preempt_check(); | ||
1153 | } | ||
1154 | else if (is_fixed(t)) { | ||
1155 | c_preempt_check((cont_domain_t*)tsk_rt(t)->domain); | ||
1156 | } | ||
1157 | preempt(entry); | ||
1158 | } | ||
1117 | raw_spin_unlock_irqrestore(&g_lock, flags); | 1159 | raw_spin_unlock_irqrestore(&g_lock, flags); |
1118 | |||
1119 | TRACE("EDF-sc: task new %d\n", t->pid); | ||
1120 | } | 1160 | } |
1121 | 1161 | ||
1122 | /** | 1162 | /** |
@@ -1127,18 +1167,32 @@ static void edfsc_task_new(struct task_struct* t, int on_rq, int is_scheduled) | |||
1127 | static void edfsc_task_wake_up(struct task_struct *task) | 1167 | static void edfsc_task_wake_up(struct task_struct *task) |
1128 | { | 1168 | { |
1129 | unsigned long flags; | 1169 | unsigned long flags; |
1130 | 1170 | lt_t now = litmus_clock(); | |
1131 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); | 1171 | cpu_entry_t* entry = &per_cpu(edfsc_cpu_entries, task_cpu(task)); |
1172 | TRACE_TASK(task, "wake_up at %llu\n", now); | ||
1132 | raw_spin_lock_irqsave(&g_lock, flags); | 1173 | raw_spin_lock_irqsave(&g_lock, flags); |
1133 | // TODO: Look into handling sporadic tasks as sched_gsnedf.c does | 1174 | now = litmus_clock(); |
1134 | requeue(task); | 1175 | if (is_sporadic(task) && is_tardy(task, now)) { |
1135 | // TODO: Look into queuing preemption as sched_gsnedf.c does? | 1176 | inferred_sporadic_job_release_at(task, now); |
1177 | } | ||
1178 | if (!is_queued(task) && tsk_rt(task)->domain) | ||
1179 | requeue(task); | ||
1180 | if (is_migrating(task)) | ||
1181 | g_preempt_check(); | ||
1182 | else if (is_fixed(task)) | ||
1183 | c_preempt_check((cont_domain_t*)tsk_rt(task)->domain); | ||
1184 | preempt(entry); | ||
1136 | raw_spin_unlock_irqrestore(&g_lock, flags); | 1185 | raw_spin_unlock_irqrestore(&g_lock, flags); |
1137 | } | 1186 | } |
1138 | 1187 | ||
1139 | static void edfsc_task_block(struct task_struct *t) | 1188 | static void edfsc_task_block(struct task_struct *t) |
1140 | { | 1189 | { |
1141 | // TODO | 1190 | unsigned long flags; |
1191 | raw_spin_lock_irqsave(&g_lock, flags); | ||
1192 | if (is_migrating(t) && tsk_rt(t)->edfsc_params.container_task) { | ||
1193 | tsk_rt(t)->edfsc_params.container_task = NULL; | ||
1194 | } | ||
1195 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1142 | } | 1196 | } |
1143 | 1197 | ||
1144 | /** | 1198 | /** |
@@ -1152,35 +1206,37 @@ static void edfsc_task_exit(struct task_struct* t) | |||
1152 | { | 1206 | { |
1153 | unsigned long flags; | 1207 | unsigned long flags; |
1154 | lt_t now, unaccount_time = 0; | 1208 | lt_t now, unaccount_time = 0; |
1155 | cpu_entry_t* entry; | 1209 | cpu_entry_t* entry = &per_cpu(edfsc_cpu_entries, task_cpu(t)); |
1156 | 1210 | ||
1157 | BUG_ON(is_container(t)); | 1211 | BUG_ON(is_container(t)); |
1158 | raw_spin_lock_irqsave(&g_lock, flags); | 1212 | raw_spin_lock_irqsave(&g_lock, flags); |
1159 | TRACE_TASK(t, "called edfsc_task_exit\n"); | 1213 | TRACE_TASK(t, "called edfsc_task_exit\n"); |
1160 | 1214 | ||
1161 | // Remove this task from all members of its scheduling domain | 1215 | // Remove this task from all members of its scheduling domain |
1162 | unlink(t); | 1216 | if (is_fixed(t)) { |
1163 | if (is_queued(t)) { | 1217 | tsk_rt(t)->task_params.cpu=((cont_domain_t*)tsk_rt(t)->domain)->container->rt_param.edfsc_params.id; |
1164 | remove(tsk_rt(t)->domain, t); | 1218 | if (is_queued(t)) |
1165 | } else if (is_fixed(t)) { | 1219 | remove(tsk_rt(t)->domain, t); |
1166 | // If we're fixed and not on the ready queues, we should be currently running | 1220 | else { |
1167 | BUG_ON(((cont_domain_t*)tsk_rt(t)->domain)->scheduled != t); | 1221 | // If we're fixed and not on the ready queues, we should be currently running |
1168 | BUG_ON(t != current); | 1222 | BUG_ON(((cont_domain_t*)tsk_rt(t)->domain)->scheduled != t); |
1169 | ((cont_domain_t*)tsk_rt(t)->domain)->scheduled = NULL; | 1223 | BUG_ON(t != current); |
1224 | ((cont_domain_t*)tsk_rt(t)->domain)->scheduled = NULL; | ||
1225 | } | ||
1170 | } else { | 1226 | } else { |
1171 | // We're in the global domain and not on the ready queues, so we must be running | 1227 | tsk_rt(t)->task_params.cpu = NO_CPU; |
1172 | BUG_ON(t != current); | 1228 | list_del_init(&t->edfsc_qnode); |
1173 | list_del(&t->edfsc_qnode); | 1229 | if (tsk_rt(t)->edfsc_params.container_task != NULL) { |
1174 | entry = &per_cpu(edfsc_cpu_entries, task_cpu(t)); | 1230 | BUG_ON(tsk_rt(t)->edfsc_params.container_task->rt_param.edfsc_params.domain->scheduled != t); |
1175 | // Handle the case where we exit while being background scheduled | 1231 | tsk_rt(t)->edfsc_params.container_task->rt_param.edfsc_params.domain->scheduled = NULL; |
1176 | if (is_container(entry->scheduled)) { | 1232 | } |
1177 | BUG_ON(entry->scheduled->rt_param.edfsc_params.domain->scheduled != t); | 1233 | else { |
1178 | entry->scheduled->rt_param.edfsc_params.domain->scheduled = NULL; | 1234 | unlink(t); |
1179 | } else { | ||
1180 | BUG_ON(entry->scheduled != t); | ||
1181 | entry->scheduled = NULL; | 1235 | entry->scheduled = NULL; |
1182 | } | 1236 | } |
1183 | } | 1237 | } |
1238 | tsk_rt(t)->domain = NULL; | ||
1239 | BUG_ON(is_queued(t)); | ||
1184 | 1240 | ||
1185 | /* To preserve EDF-sc scheduling invariants, we can only release a task's | 1241 | /* To preserve EDF-sc scheduling invariants, we can only release a task's |
1186 | * utilization at the greater of completion or deadline boundary. Thus, here | 1242 | * utilization at the greater of completion or deadline boundary. Thus, here |
@@ -1209,20 +1265,19 @@ static void edfsc_task_exit(struct task_struct* t) | |||
1209 | get_task_struct(t); | 1265 | get_task_struct(t); |
1210 | // Make it clear that this task is going away | 1266 | // Make it clear that this task is going away |
1211 | tsk_rt(t)->edfsc_params.move_to = NULL; | 1267 | tsk_rt(t)->edfsc_params.move_to = NULL; |
1212 | /* HACK: Unfortunately, even though we hold a reference to the task struct, | ||
1213 | * LITMUS clears edfsc_params before our timer expires. `task_params` seems | ||
1214 | * untouched, so hijack `task_params.phase` to link to the container task | ||
1215 | */ | ||
1216 | tsk_rt(t)->task_params.phase = (lt_t)tsk_rt(t)->edfsc_params.container_task; | ||
1217 | 1268 | ||
1218 | if (unaccount_time == 0) | 1269 | |
1270 | if (unaccount_time == 0) { | ||
1271 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1219 | // Don't bother setting a zero-length timer - just skip straight to the callback | 1272 | // Don't bother setting a zero-length timer - just skip straight to the callback |
1220 | task_deadline_callback(&t->edfsc_deadline_timer); | 1273 | task_deadline_callback(&t->edfsc_deadline_timer); |
1221 | else | 1274 | } |
1275 | else { | ||
1276 | list_add(&t->edfsc_qnode, &pending_removes); | ||
1277 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1222 | hrtimer_start(&t->edfsc_deadline_timer, ns_to_ktime(unaccount_time), | 1278 | hrtimer_start(&t->edfsc_deadline_timer, ns_to_ktime(unaccount_time), |
1223 | HRTIMER_MODE_ABS_PINNED); | 1279 | HRTIMER_MODE_ABS_PINNED); |
1224 | 1280 | } | |
1225 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1226 | } | 1281 | } |
1227 | 1282 | ||
1228 | static struct domain_proc_info edfsc_domain_proc_info; | 1283 | static struct domain_proc_info edfsc_domain_proc_info; |
@@ -1274,23 +1329,130 @@ static long edfsc_activate_plugin(void) | |||
1274 | * be reusable if we don't destroy them when the plugin is deactivated) | 1329 | * be reusable if we don't destroy them when the plugin is deactivated) |
1275 | * - ... | 1330 | * - ... |
1276 | */ | 1331 | */ |
1332 | int i; | ||
1333 | lt_t now; | ||
1334 | cpu_entry_t* entry; | ||
1335 | struct task_struct* t; | ||
1336 | |||
1337 | edfsc_setup_domain_proc(); | ||
1338 | |||
1339 | INIT_LIST_HEAD(&pending_adds); | ||
1340 | INIT_LIST_HEAD(&migrating_tasks); | ||
1341 | INIT_LIST_HEAD(&pending_removes); | ||
1342 | bheap_init(&edfsc_cpu_heap); | ||
1343 | |||
1344 | // Set up the container boundary timer | ||
1345 | hrtimer_init(&container_release_timer, CLOCK_MONOTONIC, | ||
1346 | HRTIMER_MODE_ABS_PINNED); | ||
1347 | container_release_timer.function = container_boundary; | ||
1348 | |||
1349 | edf_domain_init(&gsched_domain, NULL, g_release_jobs); | ||
1350 | |||
1351 | container_tasks = kmalloc(sizeof(struct task_struct) * num_online_cpus(), GFP_KERNEL); | ||
1352 | container_domains = kmalloc(sizeof(cont_domain_t) * num_online_cpus(), GFP_KERNEL); | ||
1353 | container_list = kmalloc(sizeof(cont_domain_t*) * num_online_cpus(), GFP_KERNEL); | ||
1354 | edfsc_cpu_heap_node = kmalloc(sizeof(struct bheap_node) * num_online_cpus(), GFP_KERNEL); | ||
1355 | |||
1356 | sys_util = to_fp(0); | ||
1357 | m_util = to_fp(0); | ||
1358 | sys_changed = 1; | ||
1359 | |||
1360 | memset(container_tasks, 0, sizeof(struct task_struct) * num_online_cpus()); | ||
1361 | memset(container_domains, 0, sizeof(cont_domain_t) * num_online_cpus()); | ||
1362 | |||
1363 | // Initialize container domains | ||
1364 | for (i = 0; i < num_online_cpus(); i++) { | ||
1365 | edf_domain_init(&container_domains[i].domain, c_check_resched, NULL); | ||
1366 | container_domains[i].scheduled = NULL; | ||
1367 | container_domains[i].container = &container_tasks[i]; | ||
1368 | container_domains[i].f_util = to_fp(0); | ||
1369 | hrtimer_init(&(container_domains[i].idle_enforcement_timer), CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
1370 | container_domains[i].idle_enforcement_timer.function = on_idle_enforcement_timeout; | ||
1371 | |||
1372 | |||
1373 | // Name the task its container ID mapped to ASCII | ||
1374 | snprintf(container_tasks[i].comm, TASK_COMM_LEN, "%d", i); | ||
1375 | container_tasks[i].pid = -i; | ||
1376 | tsk_rt(&container_tasks[i])->task_params.exec_cost = LITMUS_QUANTUM_LENGTH_NS; | ||
1377 | tsk_rt(&container_tasks[i])->task_params.period = | ||
1378 | LITMUS_QUANTUM_LENGTH_NS; | ||
1379 | tsk_rt(&container_tasks[i])->task_params.utilization = to_fp(1); | ||
1380 | tsk_rt(&container_tasks[i])->task_params.relative_deadline = | ||
1381 | LITMUS_QUANTUM_LENGTH_NS; | ||
1382 | tsk_rt(&container_tasks[i])->task_params.budget_policy = PRECISE_ENFORCEMENT; | ||
1383 | tsk_rt(&container_tasks[i])->edfsc_params.container_task = NULL; | ||
1384 | tsk_rt(&container_tasks[i])->domain = &gsched_domain; | ||
1385 | tsk_rt(&container_tasks[i])->edfsc_params.domain = &container_domains[i]; | ||
1386 | tsk_rt(&container_tasks[i])->sporadic_release = 0; | ||
1387 | tsk_rt(&container_tasks[i])->edfsc_params.id = i; | ||
1388 | tsk_rt(&container_tasks[i])->heap_node = bheap_node_alloc(GFP_ATOMIC); | ||
1389 | tsk_rt(&container_tasks[i])->rel_heap = release_heap_alloc(GFP_ATOMIC); | ||
1390 | tsk_rt(&container_tasks[i])->linked_on = NO_CPU; | ||
1391 | tsk_rt(&container_tasks[i])->scheduled_on = NO_CPU; | ||
1392 | |||
1393 | if (!tsk_rt(&container_tasks[i])->heap_node || !tsk_rt(&container_tasks[i])->rel_heap) { | ||
1394 | printk(KERN_WARNING "litmus: no more heap node memory!?\n"); | ||
1395 | return -ENOMEM; | ||
1396 | } else { | ||
1397 | bheap_node_init(&tsk_rt(&container_tasks[i])->heap_node, &container_tasks[i]); | ||
1398 | } | ||
1399 | |||
1400 | container_tasks[i].policy = SCHED_LITMUS; | ||
1401 | |||
1402 | // Populate the container_list while we're at it. | ||
1403 | container_list[i] = &container_domains[i]; | ||
1404 | |||
1405 | // Link heap nodes to CPU structures | ||
1406 | entry = &per_cpu(edfsc_cpu_entries, i); | ||
1407 | entry->cpu = i; | ||
1408 | entry->scheduled = NULL; | ||
1409 | entry->linked = NULL; | ||
1410 | entry->hn = &edfsc_cpu_heap_node[i]; | ||
1411 | bheap_node_init(&entry->hn, entry); | ||
1412 | } | ||
1413 | |||
1414 | now = litmus_clock(); | ||
1415 | for (i = 0; i < num_online_cpus(); i++) { | ||
1416 | t = &container_tasks[i]; | ||
1417 | entry = &per_cpu(edfsc_cpu_entries, tsk_rt(t)->edfsc_params.id); | ||
1418 | ((cont_domain_t*)tsk_rt(t)->edfsc_params.domain)->scheduled_last_exec_time = now; | ||
1419 | release_at(t, now); | ||
1420 | link_task_to_cpu(t, entry); | ||
1421 | } | ||
1277 | 1422 | ||
1278 | // Start the container boundary timer | 1423 | // Start the container boundary timer |
1279 | hrtimer_start(&container_release_timer, | 1424 | hrtimer_start(&container_release_timer, |
1280 | ns_to_ktime(litmus_clock() + LITMUS_QUANTUM_LENGTH_NS), | 1425 | ns_to_ktime(now + LITMUS_QUANTUM_LENGTH_NS), |
1281 | HRTIMER_MODE_ABS_PINNED); | 1426 | HRTIMER_MODE_ABS_PINNED); |
1282 | 1427 | ||
1283 | edfsc_setup_domain_proc(); | ||
1284 | |||
1285 | return 0; | 1428 | return 0; |
1286 | } | 1429 | } |
1287 | 1430 | ||
1288 | static long edfsc_deactivate_plugin(void) | 1431 | static long edfsc_deactivate_plugin(void) |
1289 | { | 1432 | { |
1290 | // TODO: Reset our internal state | 1433 | int i; |
1434 | struct list_head *l, *temp; | ||
1435 | struct task_struct* t; | ||
1291 | 1436 | ||
1292 | // Stop the container boundary timer | 1437 | // Stop the container boundary timer |
1293 | hrtimer_cancel(&container_release_timer); | 1438 | hrtimer_try_to_cancel(&container_release_timer); |
1439 | |||
1440 | list_for_each_safe(l, temp, &pending_removes) { | ||
1441 | t = task_of_list_node(l); | ||
1442 | list_del_init(l); | ||
1443 | hrtimer_try_to_cancel(&t->edfsc_deadline_timer); | ||
1444 | } | ||
1445 | |||
1446 | for (i = 0; i < num_online_cpus(); i++) { | ||
1447 | bheap_node_free(tsk_rt(&container_tasks[i])->heap_node); | ||
1448 | release_heap_free(tsk_rt(&container_tasks[i])->rel_heap); | ||
1449 | hrtimer_try_to_cancel(&container_domains[i].idle_enforcement_timer); | ||
1450 | } | ||
1451 | |||
1452 | kfree(container_tasks); | ||
1453 | kfree(container_domains); | ||
1454 | kfree(container_list); | ||
1455 | kfree(edfsc_cpu_heap_node); | ||
1294 | 1456 | ||
1295 | destroy_domain_proc_info(&edfsc_domain_proc_info); | 1457 | destroy_domain_proc_info(&edfsc_domain_proc_info); |
1296 | return 0; | 1458 | return 0; |
@@ -1303,9 +1465,11 @@ static long edfsc_deactivate_plugin(void) | |||
1303 | */ | 1465 | */ |
1304 | static long edfsc_admit_task(struct task_struct* tsk) | 1466 | static long edfsc_admit_task(struct task_struct* tsk) |
1305 | { | 1467 | { |
1468 | unsigned long flags; | ||
1306 | // We assume that we're running in the context of `tsk` | 1469 | // We assume that we're running in the context of `tsk` |
1307 | BUG_ON(tsk != current); | 1470 | BUG_ON(tsk != current); |
1308 | 1471 | ||
1472 | raw_spin_lock_irqsave(&g_lock, flags); | ||
1309 | // Make sure that edfsc_params doesn't contain garbage | 1473 | // Make sure that edfsc_params doesn't contain garbage |
1310 | // Note that edfsc_params->domain will always be NULL for non-container tasks | 1474 | // Note that edfsc_params->domain will always be NULL for non-container tasks |
1311 | memset(&tsk_rt(tsk)->edfsc_params, 0, sizeof(struct edfsc_params)); | 1475 | memset(&tsk_rt(tsk)->edfsc_params, 0, sizeof(struct edfsc_params)); |
@@ -1317,6 +1481,7 @@ static long edfsc_admit_task(struct task_struct* tsk) | |||
1317 | tsk_rt(tsk)->task_params.utilization = fp_div(get_exec_cost(tsk), get_rt_period(tsk)); | 1481 | tsk_rt(tsk)->task_params.utilization = fp_div(get_exec_cost(tsk), get_rt_period(tsk)); |
1318 | // Add us to the queue of tasks waiting on admission | 1482 | // Add us to the queue of tasks waiting on admission |
1319 | list_add_tail(&tsk->edfsc_qnode, &pending_adds); | 1483 | list_add_tail(&tsk->edfsc_qnode, &pending_adds); |
1484 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1320 | // We don't know if we can be admitted until a container job boundry is reached, | 1485 | // We don't know if we can be admitted until a container job boundry is reached, |
1321 | // so block until the scheduler can make that decision | 1486 | // so block until the scheduler can make that decision |
1322 | set_current_state(TASK_INTERRUPTIBLE); // Changed from TASK_RUNNING | 1487 | set_current_state(TASK_INTERRUPTIBLE); // Changed from TASK_RUNNING |
@@ -1329,14 +1494,18 @@ static long edfsc_admit_task(struct task_struct* tsk) | |||
1329 | if (tsk_rt(tsk)->domain != NULL) | 1494 | if (tsk_rt(tsk)->domain != NULL) |
1330 | return 0; // Successfully admitted | 1495 | return 0; // Successfully admitted |
1331 | else { | 1496 | else { |
1497 | raw_spin_lock_irqsave(&g_lock, flags); | ||
1332 | // We'll still be on pending_adds if interrupted by a signal | 1498 | // We'll still be on pending_adds if interrupted by a signal |
1333 | struct list_head* l; | 1499 | struct list_head* l; |
1334 | list_for_each(l, &pending_adds) { | 1500 | struct list_head *temp; |
1335 | if (l == &tsk->edfsc_qnode) { | 1501 | list_for_each_safe(l, temp, &pending_adds) { |
1336 | list_del(l); | 1502 | if (task_of_list_node(l) == tsk) { |
1503 | list_del_init(l); | ||
1504 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1337 | return -EINTR; // Interrupted | 1505 | return -EINTR; // Interrupted |
1338 | } | 1506 | } |
1339 | } | 1507 | } |
1508 | raw_spin_unlock_irqrestore(&g_lock, flags); | ||
1340 | return -ENOSPC; // Rejected | 1509 | return -ENOSPC; // Rejected |
1341 | } | 1510 | } |
1342 | } | 1511 | } |
@@ -1354,90 +1523,13 @@ static struct sched_plugin edfsc_plugin __cacheline_aligned_in_smp = { | |||
1354 | .admit_task = edfsc_admit_task, | 1523 | .admit_task = edfsc_admit_task, |
1355 | .activate_plugin = edfsc_activate_plugin, | 1524 | .activate_plugin = edfsc_activate_plugin, |
1356 | .deactivate_plugin = edfsc_deactivate_plugin, | 1525 | .deactivate_plugin = edfsc_deactivate_plugin, |
1526 | .should_wait_for_stack = edfsc_should_wait_for_stack, | ||
1357 | .get_domain_proc_info = edfsc_get_domain_proc_info, | 1527 | .get_domain_proc_info = edfsc_get_domain_proc_info, |
1358 | }; | 1528 | }; |
1359 | 1529 | ||
1360 | 1530 | ||
1361 | static int __init init_edfsc(void) | 1531 | static int __init init_edfsc(void) |
1362 | { | 1532 | { |
1363 | int i; | ||
1364 | cpu_entry_t *entry; | ||
1365 | |||
1366 | INIT_LIST_HEAD(&pending_adds); | ||
1367 | INIT_LIST_HEAD(&migrating_tasks); | ||
1368 | |||
1369 | bheap_init(&edfsc_cpu_heap); | ||
1370 | |||
1371 | edf_domain_init(&gsched_domain, NULL, g_release_jobs); | ||
1372 | |||
1373 | // Set up the container boundary timer | ||
1374 | hrtimer_init(&container_release_timer, CLOCK_MONOTONIC, | ||
1375 | HRTIMER_MODE_ABS_PINNED); | ||
1376 | container_release_timer.function = container_boundary; | ||
1377 | |||
1378 | container_tasks = kmalloc(sizeof(struct task_struct) * num_online_cpus(), GFP_KERNEL); | ||
1379 | container_domains = kmalloc(sizeof(cont_domain_t) * num_online_cpus(), GFP_KERNEL); | ||
1380 | container_list = kmalloc(sizeof(cont_domain_t*) * num_online_cpus(), GFP_KERNEL); | ||
1381 | edfsc_cpu_heap_node = kmalloc(sizeof(struct bheap_node) * num_online_cpus(), GFP_KERNEL); | ||
1382 | |||
1383 | sys_util = to_fp(0); | ||
1384 | m_util = to_fp(0); | ||
1385 | sys_changed = 1; | ||
1386 | |||
1387 | memset(container_tasks, 0, sizeof(struct task_struct) * num_online_cpus()); | ||
1388 | memset(container_domains, 0, sizeof(cont_domain_t) * num_online_cpus()); | ||
1389 | |||
1390 | // Initialize container domains | ||
1391 | for (i = 0; i < num_online_cpus(); i++) { | ||
1392 | edf_domain_init(&container_domains[i].domain, c_check_resched, NULL); | ||
1393 | container_domains[i].scheduled = NULL; | ||
1394 | container_domains[i].container = &container_tasks[i]; | ||
1395 | container_domains[i].f_util = to_fp(0); | ||
1396 | hrtimer_init(&(container_domains[i].idle_enforcement_timer), CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
1397 | container_domains[i].idle_enforcement_timer.function = on_idle_enforcement_timeout; | ||
1398 | |||
1399 | |||
1400 | // Name the task its container ID mapped to ASCII | ||
1401 | snprintf(container_tasks[i].comm, TASK_COMM_LEN, "%d", i); | ||
1402 | tsk_rt(&container_tasks[i])->task_params.exec_cost = LITMUS_QUANTUM_LENGTH_NS / 2; | ||
1403 | tsk_rt(&container_tasks[i])->task_params.period = | ||
1404 | LITMUS_QUANTUM_LENGTH_NS; | ||
1405 | tsk_rt(&container_tasks[i])->task_params.relative_deadline = | ||
1406 | LITMUS_QUANTUM_LENGTH_NS; | ||
1407 | tsk_rt(&container_tasks[i])->task_params.budget_policy = PRECISE_ENFORCEMENT; | ||
1408 | tsk_rt(&container_tasks[i])->edfsc_params.container_task = NULL; | ||
1409 | tsk_rt(&container_tasks[i])->domain = &gsched_domain; | ||
1410 | tsk_rt(&container_tasks[i])->edfsc_params.domain = &container_domains[i]; | ||
1411 | tsk_rt(&container_tasks[i])->edfsc_params.can_release = 0; | ||
1412 | tsk_rt(&container_tasks[i])->sporadic_release = 0; | ||
1413 | tsk_rt(&container_tasks[i])->edfsc_params.id = i; | ||
1414 | tsk_rt(&container_tasks[i])->heap_node = bheap_node_alloc(GFP_ATOMIC); | ||
1415 | tsk_rt(&container_tasks[i])->rel_heap = release_heap_alloc(GFP_ATOMIC); | ||
1416 | |||
1417 | if (!tsk_rt(&container_tasks[i])->heap_node || !tsk_rt(&container_tasks[i])->rel_heap) { | ||
1418 | printk(KERN_WARNING "litmus: no more heap node memory!?\n"); | ||
1419 | return -ENOMEM; | ||
1420 | } else { | ||
1421 | bheap_node_init(&tsk_rt(&container_tasks[i])->heap_node, &container_tasks[i]); | ||
1422 | } | ||
1423 | |||
1424 | container_tasks[i].policy = SCHED_LITMUS; | ||
1425 | release_at(&container_tasks[i], litmus_clock()); | ||
1426 | requeue(&container_tasks[i]); | ||
1427 | |||
1428 | // Populate the container_list while we're at it. | ||
1429 | container_list[i] = &container_domains[i]; | ||
1430 | |||
1431 | // Link heap nodes to CPU structures | ||
1432 | entry = &per_cpu(edfsc_cpu_entries, i); | ||
1433 | entry->cpu = i; | ||
1434 | entry->scheduled = NULL; | ||
1435 | entry->linked = NULL; | ||
1436 | entry->hn = &edfsc_cpu_heap_node[i]; | ||
1437 | bheap_node_init(&entry->hn, entry); | ||
1438 | entry->scheduled = NULL; | ||
1439 | } | ||
1440 | |||
1441 | return register_sched_plugin(&edfsc_plugin); | 1533 | return register_sched_plugin(&edfsc_plugin); |
1442 | } | 1534 | } |
1443 | 1535 | ||