diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-05-26 17:29:58 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-05-26 17:29:58 -0400 |
commit | a463f9a9e04385f0729f7435a0a6dff7d89b25de (patch) | |
tree | 00ff42c305926c800e18b13df8440a4de1a1a041 /litmus/sched_cedf.c | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
GPUSync patch for Litmus 2012.1.
Diffstat (limited to 'litmus/sched_cedf.c')
-rw-r--r-- | litmus/sched_cedf.c | 1062 |
1 files changed, 1024 insertions, 38 deletions
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index 480c62bc895b..be14dbec6ed2 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #include <linux/percpu.h> | 29 | #include <linux/percpu.h> |
30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | 32 | #include <linux/uaccess.h> | |
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | 34 | ||
35 | #include <litmus/litmus.h> | 35 | #include <litmus/litmus.h> |
@@ -42,6 +42,16 @@ | |||
42 | #include <litmus/clustered.h> | 42 | #include <litmus/clustered.h> |
43 | 43 | ||
44 | #include <litmus/bheap.h> | 44 | #include <litmus/bheap.h> |
45 | #include <litmus/binheap.h> | ||
46 | |||
47 | #ifdef CONFIG_LITMUS_LOCKING | ||
48 | #include <litmus/kfmlp_lock.h> | ||
49 | #endif | ||
50 | |||
51 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
52 | #include <litmus/rsm_lock.h> | ||
53 | #include <litmus/ikglp_lock.h> | ||
54 | #endif | ||
45 | 55 | ||
46 | #ifdef CONFIG_SCHED_CPU_AFFINITY | 56 | #ifdef CONFIG_SCHED_CPU_AFFINITY |
47 | #include <litmus/affinity.h> | 57 | #include <litmus/affinity.h> |
@@ -49,7 +59,27 @@ | |||
49 | 59 | ||
50 | /* to configure the cluster size */ | 60 | /* to configure the cluster size */ |
51 | #include <litmus/litmus_proc.h> | 61 | #include <litmus/litmus_proc.h> |
52 | #include <linux/uaccess.h> | 62 | |
63 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
64 | #include <litmus/affinity.h> | ||
65 | #endif | ||
66 | |||
67 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
68 | #include <litmus/litmus_softirq.h> | ||
69 | #endif | ||
70 | |||
71 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
72 | #include <linux/interrupt.h> | ||
73 | #include <litmus/trace.h> | ||
74 | #endif | ||
75 | |||
76 | #ifdef CONFIG_LITMUS_NVIDIA | ||
77 | #include <litmus/nvidia_info.h> | ||
78 | #endif | ||
79 | |||
80 | #if defined(CONFIG_LITMUS_AFFINITY_LOCKING) && defined(CONFIG_LITMUS_NVIDIA) | ||
81 | #include <litmus/gpu_affinity.h> | ||
82 | #endif | ||
53 | 83 | ||
54 | /* Reference configuration variable. Determines which cache level is used to | 84 | /* Reference configuration variable. Determines which cache level is used to |
55 | * group CPUs into clusters. GLOBAL_CLUSTER, which is the default, means that | 85 | * group CPUs into clusters. GLOBAL_CLUSTER, which is the default, means that |
@@ -70,7 +100,7 @@ typedef struct { | |||
70 | struct task_struct* linked; /* only RT tasks */ | 100 | struct task_struct* linked; /* only RT tasks */ |
71 | struct task_struct* scheduled; /* only RT tasks */ | 101 | struct task_struct* scheduled; /* only RT tasks */ |
72 | atomic_t will_schedule; /* prevent unneeded IPIs */ | 102 | atomic_t will_schedule; /* prevent unneeded IPIs */ |
73 | struct bheap_node* hn; | 103 | struct binheap_node hn; |
74 | } cpu_entry_t; | 104 | } cpu_entry_t; |
75 | 105 | ||
76 | /* one cpu_entry_t per CPU */ | 106 | /* one cpu_entry_t per CPU */ |
@@ -83,6 +113,14 @@ DEFINE_PER_CPU(cpu_entry_t, cedf_cpu_entries); | |||
83 | #define test_will_schedule(cpu) \ | 113 | #define test_will_schedule(cpu) \ |
84 | (atomic_read(&per_cpu(cedf_cpu_entries, cpu).will_schedule)) | 114 | (atomic_read(&per_cpu(cedf_cpu_entries, cpu).will_schedule)) |
85 | 115 | ||
116 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
117 | struct tasklet_head | ||
118 | { | ||
119 | struct tasklet_struct *head; | ||
120 | struct tasklet_struct **tail; | ||
121 | }; | ||
122 | #endif | ||
123 | |||
86 | /* | 124 | /* |
87 | * In C-EDF there is a cedf domain _per_ cluster | 125 | * In C-EDF there is a cedf domain _per_ cluster |
88 | * The number of clusters is dynamically determined accordingly to the | 126 | * The number of clusters is dynamically determined accordingly to the |
@@ -96,10 +134,17 @@ typedef struct clusterdomain { | |||
96 | /* map of this cluster cpus */ | 134 | /* map of this cluster cpus */ |
97 | cpumask_var_t cpu_map; | 135 | cpumask_var_t cpu_map; |
98 | /* the cpus queue themselves according to priority in here */ | 136 | /* the cpus queue themselves according to priority in here */ |
99 | struct bheap_node *heap_node; | 137 | struct binheap_handle cpu_heap; |
100 | struct bheap cpu_heap; | ||
101 | /* lock for this cluster */ | 138 | /* lock for this cluster */ |
102 | #define cluster_lock domain.ready_lock | 139 | #define cluster_lock domain.ready_lock |
140 | |||
141 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
142 | struct tasklet_head pending_tasklets; | ||
143 | #endif | ||
144 | |||
145 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
146 | raw_spinlock_t dgl_lock; | ||
147 | #endif | ||
103 | } cedf_domain_t; | 148 | } cedf_domain_t; |
104 | 149 | ||
105 | /* a cedf_domain per cluster; allocation is done at init/activation time */ | 150 | /* a cedf_domain per cluster; allocation is done at init/activation time */ |
@@ -108,6 +153,22 @@ cedf_domain_t *cedf; | |||
108 | #define remote_cluster(cpu) ((cedf_domain_t *) per_cpu(cedf_cpu_entries, cpu).cluster) | 153 | #define remote_cluster(cpu) ((cedf_domain_t *) per_cpu(cedf_cpu_entries, cpu).cluster) |
109 | #define task_cpu_cluster(task) remote_cluster(get_partition(task)) | 154 | #define task_cpu_cluster(task) remote_cluster(get_partition(task)) |
110 | 155 | ||
156 | /* total number of cluster */ | ||
157 | static int num_clusters; | ||
158 | /* we do not support cluster of different sizes */ | ||
159 | static unsigned int cluster_size; | ||
160 | |||
161 | static int clusters_allocated = 0; | ||
162 | |||
163 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
164 | static raw_spinlock_t* cedf_get_dgl_spinlock(struct task_struct *t) | ||
165 | { | ||
166 | cedf_domain_t *cluster = task_cpu_cluster(t); | ||
167 | return(&cluster->dgl_lock); | ||
168 | } | ||
169 | #endif | ||
170 | |||
171 | |||
111 | /* Uncomment WANT_ALL_SCHED_EVENTS if you want to see all scheduling | 172 | /* Uncomment WANT_ALL_SCHED_EVENTS if you want to see all scheduling |
112 | * decisions in the TRACE() log; uncomment VERBOSE_INIT for verbose | 173 | * decisions in the TRACE() log; uncomment VERBOSE_INIT for verbose |
113 | * information during the initialization of the plugin (e.g., topology) | 174 | * information during the initialization of the plugin (e.g., topology) |
@@ -115,11 +176,11 @@ cedf_domain_t *cedf; | |||
115 | */ | 176 | */ |
116 | #define VERBOSE_INIT | 177 | #define VERBOSE_INIT |
117 | 178 | ||
118 | static int cpu_lower_prio(struct bheap_node *_a, struct bheap_node *_b) | 179 | static int cpu_lower_prio(struct binheap_node *_a, struct binheap_node *_b) |
119 | { | 180 | { |
120 | cpu_entry_t *a, *b; | 181 | cpu_entry_t *a = binheap_entry(_a, cpu_entry_t, hn); |
121 | a = _a->value; | 182 | cpu_entry_t *b = binheap_entry(_b, cpu_entry_t, hn); |
122 | b = _b->value; | 183 | |
123 | /* Note that a and b are inverted: we want the lowest-priority CPU at | 184 | /* Note that a and b are inverted: we want the lowest-priority CPU at |
124 | * the top of the heap. | 185 | * the top of the heap. |
125 | */ | 186 | */ |
@@ -133,20 +194,17 @@ static void update_cpu_position(cpu_entry_t *entry) | |||
133 | { | 194 | { |
134 | cedf_domain_t *cluster = entry->cluster; | 195 | cedf_domain_t *cluster = entry->cluster; |
135 | 196 | ||
136 | if (likely(bheap_node_in_heap(entry->hn))) | 197 | if (likely(binheap_is_in_heap(&entry->hn))) { |
137 | bheap_delete(cpu_lower_prio, | 198 | binheap_delete(&entry->hn, &cluster->cpu_heap); |
138 | &cluster->cpu_heap, | 199 | } |
139 | entry->hn); | ||
140 | 200 | ||
141 | bheap_insert(cpu_lower_prio, &cluster->cpu_heap, entry->hn); | 201 | binheap_add(&entry->hn, &cluster->cpu_heap, cpu_entry_t, hn); |
142 | } | 202 | } |
143 | 203 | ||
144 | /* caller must hold cedf lock */ | 204 | /* caller must hold cedf lock */ |
145 | static cpu_entry_t* lowest_prio_cpu(cedf_domain_t *cluster) | 205 | static cpu_entry_t* lowest_prio_cpu(cedf_domain_t *cluster) |
146 | { | 206 | { |
147 | struct bheap_node* hn; | 207 | return binheap_top_entry(&cluster->cpu_heap, cpu_entry_t, hn); |
148 | hn = bheap_peek(cpu_lower_prio, &cluster->cpu_heap); | ||
149 | return hn->value; | ||
150 | } | 208 | } |
151 | 209 | ||
152 | 210 | ||
@@ -208,7 +266,7 @@ static noinline void link_task_to_cpu(struct task_struct* linked, | |||
208 | } | 266 | } |
209 | 267 | ||
210 | /* unlink - Make sure a task is not linked any longer to an entry | 268 | /* unlink - Make sure a task is not linked any longer to an entry |
211 | * where it was linked before. Must hold cedf_lock. | 269 | * where it was linked before. Must hold cluster_lock. |
212 | */ | 270 | */ |
213 | static noinline void unlink(struct task_struct* t) | 271 | static noinline void unlink(struct task_struct* t) |
214 | { | 272 | { |
@@ -244,7 +302,7 @@ static void preempt(cpu_entry_t *entry) | |||
244 | } | 302 | } |
245 | 303 | ||
246 | /* requeue - Put an unlinked task into gsn-edf domain. | 304 | /* requeue - Put an unlinked task into gsn-edf domain. |
247 | * Caller must hold cedf_lock. | 305 | * Caller must hold cluster_lock. |
248 | */ | 306 | */ |
249 | static noinline void requeue(struct task_struct* task) | 307 | static noinline void requeue(struct task_struct* task) |
250 | { | 308 | { |
@@ -339,13 +397,17 @@ static void cedf_release_jobs(rt_domain_t* rt, struct bheap* tasks) | |||
339 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | 397 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); |
340 | } | 398 | } |
341 | 399 | ||
342 | /* caller holds cedf_lock */ | 400 | /* caller holds cluster_lock */ |
343 | static noinline void job_completion(struct task_struct *t, int forced) | 401 | static noinline void job_completion(struct task_struct *t, int forced) |
344 | { | 402 | { |
345 | BUG_ON(!t); | 403 | BUG_ON(!t); |
346 | 404 | ||
347 | sched_trace_task_completion(t, forced); | 405 | sched_trace_task_completion(t, forced); |
348 | 406 | ||
407 | #ifdef CONFIG_LITMUS_NVIDIA | ||
408 | atomic_set(&tsk_rt(t)->nv_int_count, 0); | ||
409 | #endif | ||
410 | |||
349 | TRACE_TASK(t, "job_completion().\n"); | 411 | TRACE_TASK(t, "job_completion().\n"); |
350 | 412 | ||
351 | /* set flags */ | 413 | /* set flags */ |
@@ -389,6 +451,314 @@ static void cedf_tick(struct task_struct* t) | |||
389 | } | 451 | } |
390 | } | 452 | } |
391 | 453 | ||
454 | |||
455 | |||
456 | |||
457 | |||
458 | |||
459 | |||
460 | |||
461 | |||
462 | |||
463 | |||
464 | |||
465 | |||
466 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
467 | |||
468 | |||
469 | static void __do_lit_tasklet(struct tasklet_struct* tasklet, unsigned long flushed) | ||
470 | { | ||
471 | if (!atomic_read(&tasklet->count)) { | ||
472 | if(tasklet->owner) { | ||
473 | sched_trace_tasklet_begin(tasklet->owner); | ||
474 | } | ||
475 | |||
476 | if (!test_and_clear_bit(TASKLET_STATE_SCHED, &tasklet->state)) | ||
477 | { | ||
478 | BUG(); | ||
479 | } | ||
480 | TRACE("%s: Invoking tasklet with owner pid = %d (flushed = %d).\n", | ||
481 | __FUNCTION__, | ||
482 | (tasklet->owner) ? tasklet->owner->pid : -1, | ||
483 | (tasklet->owner) ? 0 : 1); | ||
484 | tasklet->func(tasklet->data); | ||
485 | tasklet_unlock(tasklet); | ||
486 | |||
487 | if(tasklet->owner) { | ||
488 | sched_trace_tasklet_end(tasklet->owner, flushed); | ||
489 | } | ||
490 | } | ||
491 | else { | ||
492 | BUG(); | ||
493 | } | ||
494 | } | ||
495 | |||
496 | |||
497 | static void do_lit_tasklets(cedf_domain_t* cluster, struct task_struct* sched_task) | ||
498 | { | ||
499 | int work_to_do = 1; | ||
500 | struct tasklet_struct *tasklet = NULL; | ||
501 | unsigned long flags; | ||
502 | |||
503 | while(work_to_do) { | ||
504 | |||
505 | TS_NV_SCHED_BOTISR_START; | ||
506 | |||
507 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
508 | |||
509 | if(cluster->pending_tasklets.head != NULL) { | ||
510 | // remove tasklet at head. | ||
511 | struct tasklet_struct *prev = NULL; | ||
512 | tasklet = cluster->pending_tasklets.head; | ||
513 | |||
514 | // find a tasklet with prio to execute; skip ones where | ||
515 | // sched_task has a higher priority. | ||
516 | // We use the '!edf' test instead of swaping function arguments since | ||
517 | // both sched_task and owner could be NULL. In this case, we want to | ||
518 | // still execute the tasklet. | ||
519 | while(tasklet && !edf_higher_prio(tasklet->owner, sched_task)) { | ||
520 | prev = tasklet; | ||
521 | tasklet = tasklet->next; | ||
522 | } | ||
523 | |||
524 | if(tasklet) { // found something to execuite | ||
525 | // remove the tasklet from the queue | ||
526 | if(prev) { | ||
527 | prev->next = tasklet->next; | ||
528 | if(prev->next == NULL) { | ||
529 | TRACE("%s: Tasklet for %d is the last element in tasklet queue.\n", __FUNCTION__, tasklet->owner->pid); | ||
530 | cluster->pending_tasklets.tail = &(prev); | ||
531 | } | ||
532 | } | ||
533 | else { | ||
534 | cluster->pending_tasklets.head = tasklet->next; | ||
535 | if(tasklet->next == NULL) { | ||
536 | TRACE("%s: Tasklet for %d is the last element in tasklet queue.\n", __FUNCTION__, tasklet->owner->pid); | ||
537 | cluster->pending_tasklets.tail = &(cluster->pending_tasklets.head); | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | else { | ||
542 | TRACE("%s: No tasklets with eligible priority.\n", __FUNCTION__); | ||
543 | } | ||
544 | } | ||
545 | else { | ||
546 | TRACE("%s: Tasklet queue is empty.\n", __FUNCTION__); | ||
547 | } | ||
548 | |||
549 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
550 | |||
551 | if(tasklet) { | ||
552 | __do_lit_tasklet(tasklet, 0ul); | ||
553 | tasklet = NULL; | ||
554 | } | ||
555 | else { | ||
556 | work_to_do = 0; | ||
557 | } | ||
558 | |||
559 | TS_NV_SCHED_BOTISR_END; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | static void __add_pai_tasklet(struct tasklet_struct* tasklet, cedf_domain_t* cluster) | ||
564 | { | ||
565 | struct tasklet_struct* step; | ||
566 | |||
567 | tasklet->next = NULL; // make sure there are no old values floating around | ||
568 | |||
569 | step = cluster->pending_tasklets.head; | ||
570 | if(step == NULL) { | ||
571 | TRACE("%s: tasklet queue empty. inserting tasklet for %d at head.\n", __FUNCTION__, tasklet->owner->pid); | ||
572 | // insert at tail. | ||
573 | *(cluster->pending_tasklets.tail) = tasklet; | ||
574 | cluster->pending_tasklets.tail = &(tasklet->next); | ||
575 | } | ||
576 | else if((*(cluster->pending_tasklets.tail) != NULL) && | ||
577 | edf_higher_prio((*(cluster->pending_tasklets.tail))->owner, tasklet->owner)) { | ||
578 | // insert at tail. | ||
579 | TRACE("%s: tasklet belongs at end. inserting tasklet for %d at tail.\n", __FUNCTION__, tasklet->owner->pid); | ||
580 | |||
581 | *(cluster->pending_tasklets.tail) = tasklet; | ||
582 | cluster->pending_tasklets.tail = &(tasklet->next); | ||
583 | } | ||
584 | else { | ||
585 | |||
586 | // insert the tasklet somewhere in the middle. | ||
587 | |||
588 | TRACE("%s: tasklet belongs somewhere in the middle.\n", __FUNCTION__); | ||
589 | |||
590 | while(step->next && edf_higher_prio(step->next->owner, tasklet->owner)) { | ||
591 | step = step->next; | ||
592 | } | ||
593 | |||
594 | // insert tasklet right before step->next. | ||
595 | |||
596 | TRACE("%s: inserting tasklet for %d between %d and %d.\n", __FUNCTION__, | ||
597 | tasklet->owner->pid, | ||
598 | (step->owner) ? | ||
599 | step->owner->pid : | ||
600 | -1, | ||
601 | (step->next) ? | ||
602 | ((step->next->owner) ? | ||
603 | step->next->owner->pid : | ||
604 | -1) : | ||
605 | -1); | ||
606 | |||
607 | tasklet->next = step->next; | ||
608 | step->next = tasklet; | ||
609 | |||
610 | // patch up the head if needed. | ||
611 | if(cluster->pending_tasklets.head == step) | ||
612 | { | ||
613 | TRACE("%s: %d is the new tasklet queue head.\n", __FUNCTION__, tasklet->owner->pid); | ||
614 | cluster->pending_tasklets.head = tasklet; | ||
615 | } | ||
616 | } | ||
617 | } | ||
618 | |||
619 | static void cedf_run_tasklets(struct task_struct* sched_task) | ||
620 | { | ||
621 | cedf_domain_t* cluster; | ||
622 | |||
623 | preempt_disable(); | ||
624 | |||
625 | cluster = (is_realtime(sched_task)) ? | ||
626 | task_cpu_cluster(sched_task) : | ||
627 | remote_cluster(smp_processor_id()); | ||
628 | |||
629 | if(cluster && cluster->pending_tasklets.head != NULL) { | ||
630 | TRACE("%s: There are tasklets to process.\n", __FUNCTION__); | ||
631 | do_lit_tasklets(cluster, sched_task); | ||
632 | } | ||
633 | |||
634 | preempt_enable_no_resched(); | ||
635 | } | ||
636 | |||
637 | |||
638 | |||
639 | static int cedf_enqueue_pai_tasklet(struct tasklet_struct* tasklet) | ||
640 | { | ||
641 | #if 0 | ||
642 | cedf_domain_t *cluster = NULL; | ||
643 | cpu_entry_t *targetCPU = NULL; | ||
644 | int thisCPU; | ||
645 | int runLocal = 0; | ||
646 | int runNow = 0; | ||
647 | unsigned long flags; | ||
648 | |||
649 | if(unlikely((tasklet->owner == NULL) || !is_realtime(tasklet->owner))) | ||
650 | { | ||
651 | TRACE("%s: No owner associated with this tasklet!\n", __FUNCTION__); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | cluster = task_cpu_cluster(tasklet->owner); | ||
656 | |||
657 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
658 | |||
659 | thisCPU = smp_processor_id(); | ||
660 | |||
661 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
662 | { | ||
663 | cpu_entry_t* affinity = NULL; | ||
664 | |||
665 | // use this CPU if it is in our cluster and isn't running any RT work. | ||
666 | if(cpu_isset(thisCPU, *cluster->cpu_map) && (__get_cpu_var(cedf_cpu_entries).linked == NULL)) { | ||
667 | affinity = &(__get_cpu_var(cedf_cpu_entries)); | ||
668 | } | ||
669 | else { | ||
670 | // this CPU is busy or shouldn't run tasklet in this cluster. | ||
671 | // look for available near by CPUs. | ||
672 | // NOTE: Affinity towards owner and not this CPU. Is this right? | ||
673 | affinity = | ||
674 | cedf_get_nearest_available_cpu(cluster, | ||
675 | &per_cpu(cedf_cpu_entries, task_cpu(tasklet->owner))); | ||
676 | } | ||
677 | |||
678 | targetCPU = affinity; | ||
679 | } | ||
680 | #endif | ||
681 | |||
682 | if (targetCPU == NULL) { | ||
683 | targetCPU = lowest_prio_cpu(cluster); | ||
684 | } | ||
685 | |||
686 | if (edf_higher_prio(tasklet->owner, targetCPU->linked)) { | ||
687 | if (thisCPU == targetCPU->cpu) { | ||
688 | TRACE("%s: Run tasklet locally (and now).\n", __FUNCTION__); | ||
689 | runLocal = 1; | ||
690 | runNow = 1; | ||
691 | } | ||
692 | else { | ||
693 | TRACE("%s: Run tasklet remotely (and now).\n", __FUNCTION__); | ||
694 | runLocal = 0; | ||
695 | runNow = 1; | ||
696 | } | ||
697 | } | ||
698 | else { | ||
699 | runLocal = 0; | ||
700 | runNow = 0; | ||
701 | } | ||
702 | |||
703 | if(!runLocal) { | ||
704 | // enqueue the tasklet | ||
705 | __add_pai_tasklet(tasklet, cluster); | ||
706 | } | ||
707 | |||
708 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
709 | |||
710 | |||
711 | if (runLocal /*&& runNow */) { // runNow == 1 is implied | ||
712 | TRACE("%s: Running tasklet on CPU where it was received.\n", __FUNCTION__); | ||
713 | __do_lit_tasklet(tasklet, 0ul); | ||
714 | } | ||
715 | else if (runNow /*&& !runLocal */) { // runLocal == 0 is implied | ||
716 | TRACE("%s: Triggering CPU %d to run tasklet.\n", __FUNCTION__, targetCPU->cpu); | ||
717 | preempt(targetCPU); // need to be protected by cluster_lock? | ||
718 | } | ||
719 | else { | ||
720 | TRACE("%s: Scheduling of tasklet was deferred.\n", __FUNCTION__); | ||
721 | } | ||
722 | #else | ||
723 | TRACE("%s: Running tasklet on CPU where it was received.\n", __FUNCTION__); | ||
724 | __do_lit_tasklet(tasklet, 0ul); | ||
725 | #endif | ||
726 | return(1); // success | ||
727 | } | ||
728 | |||
729 | static void cedf_change_prio_pai_tasklet(struct task_struct *old_prio, | ||
730 | struct task_struct *new_prio) | ||
731 | { | ||
732 | struct tasklet_struct* step; | ||
733 | unsigned long flags; | ||
734 | cedf_domain_t *cluster; | ||
735 | struct task_struct *probe; | ||
736 | |||
737 | // identify the cluster by the assignment of these tasks. one should | ||
738 | // be non-NULL. | ||
739 | probe = (old_prio) ? old_prio : new_prio; | ||
740 | |||
741 | if(probe) { | ||
742 | cluster = task_cpu_cluster(probe); | ||
743 | |||
744 | if(cluster->pending_tasklets.head != NULL) { | ||
745 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
746 | for(step = cluster->pending_tasklets.head; step != NULL; step = step->next) { | ||
747 | if(step->owner == old_prio) { | ||
748 | TRACE("%s: Found tasklet to change: %d\n", __FUNCTION__, step->owner->pid); | ||
749 | step->owner = new_prio; | ||
750 | } | ||
751 | } | ||
752 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
753 | } | ||
754 | } | ||
755 | else { | ||
756 | TRACE("%s: Both priorities were NULL\n"); | ||
757 | } | ||
758 | } | ||
759 | |||
760 | #endif // PAI | ||
761 | |||
392 | /* Getting schedule() right is a bit tricky. schedule() may not make any | 762 | /* Getting schedule() right is a bit tricky. schedule() may not make any |
393 | * assumptions on the state of the current task since it may be called for a | 763 | * assumptions on the state of the current task since it may be called for a |
394 | * number of reasons. The reasons include a scheduler_tick() determined that it | 764 | * number of reasons. The reasons include a scheduler_tick() determined that it |
@@ -465,6 +835,19 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
465 | if (blocks) | 835 | if (blocks) |
466 | unlink(entry->scheduled); | 836 | unlink(entry->scheduled); |
467 | 837 | ||
838 | #if defined(CONFIG_LITMUS_NVIDIA) && defined(CONFIG_LITMUS_AFFINITY_LOCKING) | ||
839 | if(exists && is_realtime(entry->scheduled) && tsk_rt(entry->scheduled)->held_gpus) { | ||
840 | if(!blocks || tsk_rt(entry->scheduled)->suspend_gpu_tracker_on_block) { | ||
841 | // don't track preemptions or locking protocol suspensions. | ||
842 | TRACE_TASK(entry->scheduled, "stopping GPU tracker.\n"); | ||
843 | stop_gpu_tracker(entry->scheduled); | ||
844 | } | ||
845 | else if(blocks && !tsk_rt(entry->scheduled)->suspend_gpu_tracker_on_block) { | ||
846 | TRACE_TASK(entry->scheduled, "GPU tracker remains on during suspension.\n"); | ||
847 | } | ||
848 | } | ||
849 | #endif | ||
850 | |||
468 | /* Request a sys_exit_np() call if we would like to preempt but cannot. | 851 | /* Request a sys_exit_np() call if we would like to preempt but cannot. |
469 | * We need to make sure to update the link structure anyway in case | 852 | * We need to make sure to update the link structure anyway in case |
470 | * that we are still linked. Multiple calls to request_exit_np() don't | 853 | * that we are still linked. Multiple calls to request_exit_np() don't |
@@ -514,7 +897,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
514 | raw_spin_unlock(&cluster->cluster_lock); | 897 | raw_spin_unlock(&cluster->cluster_lock); |
515 | 898 | ||
516 | #ifdef WANT_ALL_SCHED_EVENTS | 899 | #ifdef WANT_ALL_SCHED_EVENTS |
517 | TRACE("cedf_lock released, next=0x%p\n", next); | 900 | TRACE("cluster_lock released, next=0x%p\n", next); |
518 | 901 | ||
519 | if (next) | 902 | if (next) |
520 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); | 903 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); |
@@ -522,7 +905,6 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
522 | TRACE("becomes idle at %llu.\n", litmus_clock()); | 905 | TRACE("becomes idle at %llu.\n", litmus_clock()); |
523 | #endif | 906 | #endif |
524 | 907 | ||
525 | |||
526 | return next; | 908 | return next; |
527 | } | 909 | } |
528 | 910 | ||
@@ -548,7 +930,7 @@ static void cedf_task_new(struct task_struct * t, int on_rq, int running) | |||
548 | cpu_entry_t* entry; | 930 | cpu_entry_t* entry; |
549 | cedf_domain_t* cluster; | 931 | cedf_domain_t* cluster; |
550 | 932 | ||
551 | TRACE("gsn edf: task new %d\n", t->pid); | 933 | TRACE("c-edf: task new %d\n", t->pid); |
552 | 934 | ||
553 | /* the cluster doesn't change even if t is running */ | 935 | /* the cluster doesn't change even if t is running */ |
554 | cluster = task_cpu_cluster(t); | 936 | cluster = task_cpu_cluster(t); |
@@ -586,7 +968,7 @@ static void cedf_task_new(struct task_struct * t, int on_rq, int running) | |||
586 | static void cedf_task_wake_up(struct task_struct *task) | 968 | static void cedf_task_wake_up(struct task_struct *task) |
587 | { | 969 | { |
588 | unsigned long flags; | 970 | unsigned long flags; |
589 | lt_t now; | 971 | //lt_t now; |
590 | cedf_domain_t *cluster; | 972 | cedf_domain_t *cluster; |
591 | 973 | ||
592 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); | 974 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); |
@@ -594,6 +976,8 @@ static void cedf_task_wake_up(struct task_struct *task) | |||
594 | cluster = task_cpu_cluster(task); | 976 | cluster = task_cpu_cluster(task); |
595 | 977 | ||
596 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | 978 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); |
979 | |||
980 | #if 0 // sproadic task model | ||
597 | /* We need to take suspensions because of semaphores into | 981 | /* We need to take suspensions because of semaphores into |
598 | * account! If a job resumes after being suspended due to acquiring | 982 | * account! If a job resumes after being suspended due to acquiring |
599 | * a semaphore, it should never be treated as a new job release. | 983 | * a semaphore, it should never be treated as a new job release. |
@@ -615,7 +999,13 @@ static void cedf_task_wake_up(struct task_struct *task) | |||
615 | } | 999 | } |
616 | } | 1000 | } |
617 | } | 1001 | } |
618 | cedf_job_arrival(task); | 1002 | #else |
1003 | set_rt_flags(task, RT_F_RUNNING); // periodic model | ||
1004 | #endif | ||
1005 | |||
1006 | if(tsk_rt(task)->linked_on == NO_CPU) | ||
1007 | cedf_job_arrival(task); | ||
1008 | |||
619 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | 1009 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); |
620 | } | 1010 | } |
621 | 1011 | ||
@@ -642,6 +1032,10 @@ static void cedf_task_exit(struct task_struct * t) | |||
642 | unsigned long flags; | 1032 | unsigned long flags; |
643 | cedf_domain_t *cluster = task_cpu_cluster(t); | 1033 | cedf_domain_t *cluster = task_cpu_cluster(t); |
644 | 1034 | ||
1035 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
1036 | cedf_change_prio_pai_tasklet(t, NULL); | ||
1037 | #endif | ||
1038 | |||
645 | /* unlink if necessary */ | 1039 | /* unlink if necessary */ |
646 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | 1040 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); |
647 | unlink(t); | 1041 | unlink(t); |
@@ -659,13 +1053,536 @@ static void cedf_task_exit(struct task_struct * t) | |||
659 | 1053 | ||
660 | static long cedf_admit_task(struct task_struct* tsk) | 1054 | static long cedf_admit_task(struct task_struct* tsk) |
661 | { | 1055 | { |
1056 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1057 | INIT_BINHEAP_HANDLE(&tsk_rt(tsk)->hp_blocked_tasks, | ||
1058 | edf_max_heap_base_priority_order); | ||
1059 | #endif | ||
1060 | |||
662 | return task_cpu(tsk) == tsk->rt_param.task_params.cpu ? 0 : -EINVAL; | 1061 | return task_cpu(tsk) == tsk->rt_param.task_params.cpu ? 0 : -EINVAL; |
663 | } | 1062 | } |
664 | 1063 | ||
665 | /* total number of cluster */ | 1064 | |
666 | static int num_clusters; | 1065 | |
667 | /* we do not support cluster of different sizes */ | 1066 | #ifdef CONFIG_LITMUS_LOCKING |
668 | static unsigned int cluster_size; | 1067 | |
1068 | #include <litmus/fdso.h> | ||
1069 | |||
1070 | |||
1071 | |||
1072 | /* called with IRQs off */ | ||
1073 | static void __increase_priority_inheritance(struct task_struct* t, | ||
1074 | struct task_struct* prio_inh) | ||
1075 | { | ||
1076 | int linked_on; | ||
1077 | int check_preempt = 0; | ||
1078 | |||
1079 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1080 | |||
1081 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1082 | /* this sanity check allows for weaker locking in protocols */ | ||
1083 | /* TODO (klitirqd): Skip this check if 't' is a proxy thread (???) */ | ||
1084 | if(__edf_higher_prio(prio_inh, BASE, t, EFFECTIVE)) { | ||
1085 | #endif | ||
1086 | TRACE_TASK(t, "inherits priority from %s/%d\n", | ||
1087 | prio_inh->comm, prio_inh->pid); | ||
1088 | tsk_rt(t)->inh_task = prio_inh; | ||
1089 | |||
1090 | linked_on = tsk_rt(t)->linked_on; | ||
1091 | |||
1092 | /* If it is scheduled, then we need to reorder the CPU heap. */ | ||
1093 | if (linked_on != NO_CPU) { | ||
1094 | TRACE_TASK(t, "%s: linked on %d\n", | ||
1095 | __FUNCTION__, linked_on); | ||
1096 | /* Holder is scheduled; need to re-order CPUs. | ||
1097 | * We can't use heap_decrease() here since | ||
1098 | * the cpu_heap is ordered in reverse direction, so | ||
1099 | * it is actually an increase. */ | ||
1100 | binheap_delete(&per_cpu(cedf_cpu_entries, linked_on).hn, | ||
1101 | &cluster->cpu_heap); | ||
1102 | binheap_add(&per_cpu(cedf_cpu_entries, linked_on).hn, | ||
1103 | &cluster->cpu_heap, cpu_entry_t, hn); | ||
1104 | |||
1105 | } else { | ||
1106 | /* holder may be queued: first stop queue changes */ | ||
1107 | raw_spin_lock(&cluster->domain.release_lock); | ||
1108 | if (is_queued(t)) { | ||
1109 | TRACE_TASK(t, "%s: is queued\n", | ||
1110 | __FUNCTION__); | ||
1111 | /* We need to update the position of holder in some | ||
1112 | * heap. Note that this could be a release heap if we | ||
1113 | * budget enforcement is used and this job overran. */ | ||
1114 | check_preempt = | ||
1115 | !bheap_decrease(edf_ready_order, tsk_rt(t)->heap_node); | ||
1116 | } else { | ||
1117 | /* Nothing to do: if it is not queued and not linked | ||
1118 | * then it is either sleeping or currently being moved | ||
1119 | * by other code (e.g., a timer interrupt handler) that | ||
1120 | * will use the correct priority when enqueuing the | ||
1121 | * task. */ | ||
1122 | TRACE_TASK(t, "%s: is NOT queued => Done.\n", | ||
1123 | __FUNCTION__); | ||
1124 | } | ||
1125 | raw_spin_unlock(&cluster->domain.release_lock); | ||
1126 | |||
1127 | /* If holder was enqueued in a release heap, then the following | ||
1128 | * preemption check is pointless, but we can't easily detect | ||
1129 | * that case. If you want to fix this, then consider that | ||
1130 | * simply adding a state flag requires O(n) time to update when | ||
1131 | * releasing n tasks, which conflicts with the goal to have | ||
1132 | * O(log n) merges. */ | ||
1133 | if (check_preempt) { | ||
1134 | /* heap_decrease() hit the top level of the heap: make | ||
1135 | * sure preemption checks get the right task, not the | ||
1136 | * potentially stale cache. */ | ||
1137 | bheap_uncache_min(edf_ready_order, | ||
1138 | &cluster->domain.ready_queue); | ||
1139 | check_for_preemptions(cluster); | ||
1140 | } | ||
1141 | } | ||
1142 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1143 | } | ||
1144 | else { | ||
1145 | TRACE_TASK(t, "Spurious invalid priority increase. " | ||
1146 | "Inheritance request: %s/%d [eff_prio = %s/%d] to inherit from %s/%d\n" | ||
1147 | "Occurance is likely okay: probably due to (hopefully safe) concurrent priority updates.\n", | ||
1148 | t->comm, t->pid, | ||
1149 | effective_priority(t)->comm, effective_priority(t)->pid, | ||
1150 | (prio_inh) ? prio_inh->comm : "nil", | ||
1151 | (prio_inh) ? prio_inh->pid : -1); | ||
1152 | WARN_ON(!prio_inh); | ||
1153 | } | ||
1154 | #endif | ||
1155 | } | ||
1156 | |||
1157 | /* called with IRQs off */ | ||
1158 | static void increase_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh) | ||
1159 | { | ||
1160 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1161 | |||
1162 | raw_spin_lock(&cluster->cluster_lock); | ||
1163 | |||
1164 | __increase_priority_inheritance(t, prio_inh); | ||
1165 | |||
1166 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1167 | if(tsk_rt(t)->cur_klitirqd != NULL) | ||
1168 | { | ||
1169 | TRACE_TASK(t, "%s/%d inherits a new priority!\n", | ||
1170 | tsk_rt(t)->cur_klitirqd->comm, tsk_rt(t)->cur_klitirqd->pid); | ||
1171 | |||
1172 | __increase_priority_inheritance(tsk_rt(t)->cur_klitirqd, prio_inh); | ||
1173 | } | ||
1174 | #endif | ||
1175 | |||
1176 | raw_spin_unlock(&cluster->cluster_lock); | ||
1177 | |||
1178 | #if defined(CONFIG_LITMUS_PAI_SOFTIRQD) && defined(CONFIG_LITMUS_NVIDIA) | ||
1179 | if(tsk_rt(t)->held_gpus) { | ||
1180 | int i; | ||
1181 | for(i = find_first_bit(&tsk_rt(t)->held_gpus, sizeof(tsk_rt(t)->held_gpus)); | ||
1182 | i < NV_DEVICE_NUM; | ||
1183 | i = find_next_bit(&tsk_rt(t)->held_gpus, sizeof(tsk_rt(t)->held_gpus), i+1)) { | ||
1184 | pai_check_priority_increase(t, i); | ||
1185 | } | ||
1186 | } | ||
1187 | #endif | ||
1188 | } | ||
1189 | |||
1190 | /* called with IRQs off */ | ||
1191 | static void __decrease_priority_inheritance(struct task_struct* t, | ||
1192 | struct task_struct* prio_inh) | ||
1193 | { | ||
1194 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1195 | if(__edf_higher_prio(t, EFFECTIVE, prio_inh, BASE)) { | ||
1196 | #endif | ||
1197 | /* A job only stops inheriting a priority when it releases a | ||
1198 | * resource. Thus we can make the following assumption.*/ | ||
1199 | if(prio_inh) | ||
1200 | TRACE_TASK(t, "EFFECTIVE priority decreased to %s/%d\n", | ||
1201 | prio_inh->comm, prio_inh->pid); | ||
1202 | else | ||
1203 | TRACE_TASK(t, "base priority restored.\n"); | ||
1204 | |||
1205 | tsk_rt(t)->inh_task = prio_inh; | ||
1206 | |||
1207 | if(tsk_rt(t)->scheduled_on != NO_CPU) { | ||
1208 | TRACE_TASK(t, "is scheduled.\n"); | ||
1209 | |||
1210 | /* Check if rescheduling is necessary. We can't use heap_decrease() | ||
1211 | * since the priority was effectively lowered. */ | ||
1212 | unlink(t); | ||
1213 | cedf_job_arrival(t); | ||
1214 | } | ||
1215 | else { | ||
1216 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1217 | /* task is queued */ | ||
1218 | raw_spin_lock(&cluster->domain.release_lock); | ||
1219 | if (is_queued(t)) { | ||
1220 | TRACE_TASK(t, "is queued.\n"); | ||
1221 | |||
1222 | /* decrease in priority, so we have to re-add to binomial heap */ | ||
1223 | unlink(t); | ||
1224 | cedf_job_arrival(t); | ||
1225 | } | ||
1226 | else { | ||
1227 | TRACE_TASK(t, "is not in scheduler. Probably on wait queue somewhere.\n"); | ||
1228 | } | ||
1229 | raw_spin_unlock(&cluster->domain.release_lock); | ||
1230 | } | ||
1231 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1232 | } | ||
1233 | else { | ||
1234 | TRACE_TASK(t, "Spurious invalid priority decrease. " | ||
1235 | "Inheritance request: %s/%d [eff_prio = %s/%d] to inherit from %s/%d\n" | ||
1236 | "Occurance is likely okay: probably due to (hopefully safe) concurrent priority updates.\n", | ||
1237 | t->comm, t->pid, | ||
1238 | effective_priority(t)->comm, effective_priority(t)->pid, | ||
1239 | (prio_inh) ? prio_inh->comm : "nil", | ||
1240 | (prio_inh) ? prio_inh->pid : -1); | ||
1241 | } | ||
1242 | #endif | ||
1243 | } | ||
1244 | |||
1245 | static void decrease_priority_inheritance(struct task_struct* t, | ||
1246 | struct task_struct* prio_inh) | ||
1247 | { | ||
1248 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1249 | |||
1250 | raw_spin_lock(&cluster->cluster_lock); | ||
1251 | __decrease_priority_inheritance(t, prio_inh); | ||
1252 | |||
1253 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1254 | if(tsk_rt(t)->cur_klitirqd != NULL) | ||
1255 | { | ||
1256 | TRACE_TASK(t, "%s/%d decreases in priority!\n", | ||
1257 | tsk_rt(t)->cur_klitirqd->comm, tsk_rt(t)->cur_klitirqd->pid); | ||
1258 | |||
1259 | __decrease_priority_inheritance(tsk_rt(t)->cur_klitirqd, prio_inh); | ||
1260 | } | ||
1261 | #endif | ||
1262 | |||
1263 | raw_spin_unlock(&cluster->cluster_lock); | ||
1264 | |||
1265 | #if defined(CONFIG_LITMUS_PAI_SOFTIRQD) && defined(CONFIG_LITMUS_NVIDIA) | ||
1266 | if(tsk_rt(t)->held_gpus) { | ||
1267 | int i; | ||
1268 | for(i = find_first_bit(&tsk_rt(t)->held_gpus, sizeof(tsk_rt(t)->held_gpus)); | ||
1269 | i < NV_DEVICE_NUM; | ||
1270 | i = find_next_bit(&tsk_rt(t)->held_gpus, sizeof(tsk_rt(t)->held_gpus), i+1)) { | ||
1271 | pai_check_priority_decrease(t, i); | ||
1272 | } | ||
1273 | } | ||
1274 | #endif | ||
1275 | } | ||
1276 | |||
1277 | |||
1278 | |||
1279 | |||
1280 | |||
1281 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1282 | /* called with IRQs off */ | ||
1283 | static void increase_priority_inheritance_klitirqd(struct task_struct* klitirqd, | ||
1284 | struct task_struct* old_owner, | ||
1285 | struct task_struct* new_owner) | ||
1286 | { | ||
1287 | cedf_domain_t* cluster = task_cpu_cluster(klitirqd); | ||
1288 | |||
1289 | BUG_ON(!(tsk_rt(klitirqd)->is_proxy_thread)); | ||
1290 | |||
1291 | raw_spin_lock(&cluster->cluster_lock); | ||
1292 | |||
1293 | if(old_owner != new_owner) | ||
1294 | { | ||
1295 | if(old_owner) | ||
1296 | { | ||
1297 | // unreachable? | ||
1298 | tsk_rt(old_owner)->cur_klitirqd = NULL; | ||
1299 | } | ||
1300 | |||
1301 | TRACE_TASK(klitirqd, "giving ownership to %s/%d.\n", | ||
1302 | new_owner->comm, new_owner->pid); | ||
1303 | |||
1304 | tsk_rt(new_owner)->cur_klitirqd = klitirqd; | ||
1305 | } | ||
1306 | |||
1307 | __decrease_priority_inheritance(klitirqd, NULL); // kludge to clear out cur prio. | ||
1308 | |||
1309 | __increase_priority_inheritance(klitirqd, | ||
1310 | (tsk_rt(new_owner)->inh_task == NULL) ? | ||
1311 | new_owner : | ||
1312 | tsk_rt(new_owner)->inh_task); | ||
1313 | |||
1314 | raw_spin_unlock(&cluster->cluster_lock); | ||
1315 | } | ||
1316 | |||
1317 | |||
1318 | /* called with IRQs off */ | ||
1319 | static void decrease_priority_inheritance_klitirqd(struct task_struct* klitirqd, | ||
1320 | struct task_struct* old_owner, | ||
1321 | struct task_struct* new_owner) | ||
1322 | { | ||
1323 | cedf_domain_t* cluster = task_cpu_cluster(klitirqd); | ||
1324 | |||
1325 | BUG_ON(!(tsk_rt(klitirqd)->is_proxy_thread)); | ||
1326 | |||
1327 | raw_spin_lock(&cluster->cluster_lock); | ||
1328 | |||
1329 | TRACE_TASK(klitirqd, "priority restored\n"); | ||
1330 | |||
1331 | __decrease_priority_inheritance(klitirqd, new_owner); | ||
1332 | |||
1333 | tsk_rt(old_owner)->cur_klitirqd = NULL; | ||
1334 | |||
1335 | raw_spin_unlock(&cluster->cluster_lock); | ||
1336 | } | ||
1337 | #endif // CONFIG_LITMUS_SOFTIRQD | ||
1338 | |||
1339 | |||
1340 | |||
1341 | |||
1342 | |||
1343 | |||
1344 | |||
1345 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1346 | |||
1347 | /* called with IRQs off */ | ||
1348 | /* preconditions: | ||
1349 | (1) The 'hp_blocked_tasks_lock' of task 't' is held. | ||
1350 | (2) The lock 'to_unlock' is held. | ||
1351 | */ | ||
1352 | static void nested_increase_priority_inheritance(struct task_struct* t, | ||
1353 | struct task_struct* prio_inh, | ||
1354 | raw_spinlock_t *to_unlock, | ||
1355 | unsigned long irqflags) | ||
1356 | { | ||
1357 | struct litmus_lock *blocked_lock = tsk_rt(t)->blocked_lock; | ||
1358 | |||
1359 | if(tsk_rt(t)->inh_task != prio_inh) { // shield redundent calls. | ||
1360 | increase_priority_inheritance(t, prio_inh); // increase our prio. | ||
1361 | } | ||
1362 | |||
1363 | raw_spin_unlock(&tsk_rt(t)->hp_blocked_tasks_lock); // unlock the t's heap. | ||
1364 | |||
1365 | |||
1366 | if(blocked_lock) { | ||
1367 | if(blocked_lock->ops->propagate_increase_inheritance) { | ||
1368 | TRACE_TASK(t, "Inheritor is blocked (...perhaps). Checking lock %d.\n", | ||
1369 | blocked_lock->ident); | ||
1370 | |||
1371 | // beware: recursion | ||
1372 | blocked_lock->ops->propagate_increase_inheritance(blocked_lock, | ||
1373 | t, to_unlock, | ||
1374 | irqflags); | ||
1375 | } | ||
1376 | else { | ||
1377 | TRACE_TASK(t, "Inheritor is blocked on lock (%d) that does not support nesting!\n", | ||
1378 | blocked_lock->ident); | ||
1379 | unlock_fine_irqrestore(to_unlock, irqflags); | ||
1380 | } | ||
1381 | } | ||
1382 | else { | ||
1383 | TRACE_TASK(t, "is not blocked. No propagation.\n"); | ||
1384 | unlock_fine_irqrestore(to_unlock, irqflags); | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | /* called with IRQs off */ | ||
1389 | /* preconditions: | ||
1390 | (1) The 'hp_blocked_tasks_lock' of task 't' is held. | ||
1391 | (2) The lock 'to_unlock' is held. | ||
1392 | */ | ||
1393 | static void nested_decrease_priority_inheritance(struct task_struct* t, | ||
1394 | struct task_struct* prio_inh, | ||
1395 | raw_spinlock_t *to_unlock, | ||
1396 | unsigned long irqflags) | ||
1397 | { | ||
1398 | struct litmus_lock *blocked_lock = tsk_rt(t)->blocked_lock; | ||
1399 | decrease_priority_inheritance(t, prio_inh); | ||
1400 | |||
1401 | raw_spin_unlock(&tsk_rt(t)->hp_blocked_tasks_lock); // unlock the t's heap. | ||
1402 | |||
1403 | if(blocked_lock) { | ||
1404 | if(blocked_lock->ops->propagate_decrease_inheritance) { | ||
1405 | TRACE_TASK(t, "Inheritor is blocked (...perhaps). Checking lock %d.\n", | ||
1406 | blocked_lock->ident); | ||
1407 | |||
1408 | // beware: recursion | ||
1409 | blocked_lock->ops->propagate_decrease_inheritance(blocked_lock, t, | ||
1410 | to_unlock, | ||
1411 | irqflags); | ||
1412 | } | ||
1413 | else { | ||
1414 | TRACE_TASK(t, "Inheritor is blocked on lock (%p) that does not support nesting!\n", | ||
1415 | blocked_lock); | ||
1416 | unlock_fine_irqrestore(to_unlock, irqflags); | ||
1417 | } | ||
1418 | } | ||
1419 | else { | ||
1420 | TRACE_TASK(t, "is not blocked. No propagation.\n"); | ||
1421 | unlock_fine_irqrestore(to_unlock, irqflags); | ||
1422 | } | ||
1423 | } | ||
1424 | |||
1425 | |||
1426 | /* ******************** RSM MUTEX ********************** */ | ||
1427 | |||
1428 | static struct litmus_lock_ops cedf_rsm_mutex_lock_ops = { | ||
1429 | .lock = rsm_mutex_lock, | ||
1430 | .unlock = rsm_mutex_unlock, | ||
1431 | .close = rsm_mutex_close, | ||
1432 | .deallocate = rsm_mutex_free, | ||
1433 | |||
1434 | .propagate_increase_inheritance = rsm_mutex_propagate_increase_inheritance, | ||
1435 | .propagate_decrease_inheritance = rsm_mutex_propagate_decrease_inheritance, | ||
1436 | |||
1437 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
1438 | .dgl_lock = rsm_mutex_dgl_lock, | ||
1439 | .is_owner = rsm_mutex_is_owner, | ||
1440 | .enable_priority = rsm_mutex_enable_priority, | ||
1441 | #endif | ||
1442 | }; | ||
1443 | |||
1444 | static struct litmus_lock* cedf_new_rsm_mutex(void) | ||
1445 | { | ||
1446 | return rsm_mutex_new(&cedf_rsm_mutex_lock_ops); | ||
1447 | } | ||
1448 | |||
1449 | /* ******************** IKGLP ********************** */ | ||
1450 | |||
1451 | static struct litmus_lock_ops cedf_ikglp_lock_ops = { | ||
1452 | .lock = ikglp_lock, | ||
1453 | .unlock = ikglp_unlock, | ||
1454 | .close = ikglp_close, | ||
1455 | .deallocate = ikglp_free, | ||
1456 | |||
1457 | // ikglp can only be an outer-most lock. | ||
1458 | .propagate_increase_inheritance = NULL, | ||
1459 | .propagate_decrease_inheritance = NULL, | ||
1460 | }; | ||
1461 | |||
1462 | static struct litmus_lock* cedf_new_ikglp(void* __user arg) | ||
1463 | { | ||
1464 | // assumes clusters of uniform size. | ||
1465 | return ikglp_new(cluster_size/num_clusters, &cedf_ikglp_lock_ops, arg); | ||
1466 | } | ||
1467 | |||
1468 | #endif /* CONFIG_LITMUS_NESTED_LOCKING */ | ||
1469 | |||
1470 | |||
1471 | |||
1472 | |||
1473 | /* ******************** KFMLP support ********************** */ | ||
1474 | |||
1475 | static struct litmus_lock_ops cedf_kfmlp_lock_ops = { | ||
1476 | .lock = kfmlp_lock, | ||
1477 | .unlock = kfmlp_unlock, | ||
1478 | .close = kfmlp_close, | ||
1479 | .deallocate = kfmlp_free, | ||
1480 | |||
1481 | // kfmlp can only be an outer-most lock. | ||
1482 | .propagate_increase_inheritance = NULL, | ||
1483 | .propagate_decrease_inheritance = NULL, | ||
1484 | }; | ||
1485 | |||
1486 | |||
1487 | static struct litmus_lock* cedf_new_kfmlp(void* __user arg) | ||
1488 | { | ||
1489 | return kfmlp_new(&cedf_kfmlp_lock_ops, arg); | ||
1490 | } | ||
1491 | |||
1492 | |||
1493 | /* **** lock constructor **** */ | ||
1494 | |||
1495 | static long cedf_allocate_lock(struct litmus_lock **lock, int type, | ||
1496 | void* __user args) | ||
1497 | { | ||
1498 | int err; | ||
1499 | |||
1500 | switch (type) { | ||
1501 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1502 | case RSM_MUTEX: | ||
1503 | *lock = cedf_new_rsm_mutex(); | ||
1504 | break; | ||
1505 | |||
1506 | case IKGLP_SEM: | ||
1507 | *lock = cedf_new_ikglp(args); | ||
1508 | break; | ||
1509 | #endif | ||
1510 | case KFMLP_SEM: | ||
1511 | *lock = cedf_new_kfmlp(args); | ||
1512 | break; | ||
1513 | |||
1514 | default: | ||
1515 | err = -ENXIO; | ||
1516 | goto UNSUPPORTED_LOCK; | ||
1517 | }; | ||
1518 | |||
1519 | if (*lock) | ||
1520 | err = 0; | ||
1521 | else | ||
1522 | err = -ENOMEM; | ||
1523 | |||
1524 | UNSUPPORTED_LOCK: | ||
1525 | return err; | ||
1526 | } | ||
1527 | |||
1528 | #endif // CONFIG_LITMUS_LOCKING | ||
1529 | |||
1530 | |||
1531 | #ifdef CONFIG_LITMUS_AFFINITY_LOCKING | ||
1532 | static struct affinity_observer_ops cedf_kfmlp_affinity_ops = { | ||
1533 | .close = kfmlp_aff_obs_close, | ||
1534 | .deallocate = kfmlp_aff_obs_free, | ||
1535 | }; | ||
1536 | |||
1537 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1538 | static struct affinity_observer_ops cedf_ikglp_affinity_ops = { | ||
1539 | .close = ikglp_aff_obs_close, | ||
1540 | .deallocate = ikglp_aff_obs_free, | ||
1541 | }; | ||
1542 | #endif | ||
1543 | |||
1544 | static long cedf_allocate_affinity_observer(struct affinity_observer **aff_obs, | ||
1545 | int type, | ||
1546 | void* __user args) | ||
1547 | { | ||
1548 | int err; | ||
1549 | |||
1550 | switch (type) { | ||
1551 | |||
1552 | case KFMLP_SIMPLE_GPU_AFF_OBS: | ||
1553 | *aff_obs = kfmlp_simple_gpu_aff_obs_new(&cedf_kfmlp_affinity_ops, args); | ||
1554 | break; | ||
1555 | |||
1556 | case KFMLP_GPU_AFF_OBS: | ||
1557 | *aff_obs = kfmlp_gpu_aff_obs_new(&cedf_kfmlp_affinity_ops, args); | ||
1558 | break; | ||
1559 | |||
1560 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1561 | case IKGLP_SIMPLE_GPU_AFF_OBS: | ||
1562 | *aff_obs = ikglp_simple_gpu_aff_obs_new(&cedf_ikglp_affinity_ops, args); | ||
1563 | break; | ||
1564 | |||
1565 | case IKGLP_GPU_AFF_OBS: | ||
1566 | *aff_obs = ikglp_gpu_aff_obs_new(&cedf_ikglp_affinity_ops, args); | ||
1567 | break; | ||
1568 | #endif | ||
1569 | default: | ||
1570 | err = -ENXIO; | ||
1571 | goto UNSUPPORTED_AFF_OBS; | ||
1572 | }; | ||
1573 | |||
1574 | if (*aff_obs) | ||
1575 | err = 0; | ||
1576 | else | ||
1577 | err = -ENOMEM; | ||
1578 | |||
1579 | UNSUPPORTED_AFF_OBS: | ||
1580 | return err; | ||
1581 | } | ||
1582 | #endif | ||
1583 | |||
1584 | |||
1585 | |||
669 | 1586 | ||
670 | #ifdef VERBOSE_INIT | 1587 | #ifdef VERBOSE_INIT |
671 | static void print_cluster_topology(cpumask_var_t mask, int cpu) | 1588 | static void print_cluster_topology(cpumask_var_t mask, int cpu) |
@@ -680,16 +1597,17 @@ static void print_cluster_topology(cpumask_var_t mask, int cpu) | |||
680 | } | 1597 | } |
681 | #endif | 1598 | #endif |
682 | 1599 | ||
683 | static int clusters_allocated = 0; | ||
684 | |||
685 | static void cleanup_cedf(void) | 1600 | static void cleanup_cedf(void) |
686 | { | 1601 | { |
687 | int i; | 1602 | int i; |
688 | 1603 | ||
1604 | #ifdef CONFIG_LITMUS_NVIDIA | ||
1605 | shutdown_nvidia_info(); | ||
1606 | #endif | ||
1607 | |||
689 | if (clusters_allocated) { | 1608 | if (clusters_allocated) { |
690 | for (i = 0; i < num_clusters; i++) { | 1609 | for (i = 0; i < num_clusters; i++) { |
691 | kfree(cedf[i].cpus); | 1610 | kfree(cedf[i].cpus); |
692 | kfree(cedf[i].heap_node); | ||
693 | free_cpumask_var(cedf[i].cpu_map); | 1611 | free_cpumask_var(cedf[i].cpu_map); |
694 | } | 1612 | } |
695 | 1613 | ||
@@ -749,12 +1667,16 @@ static long cedf_activate_plugin(void) | |||
749 | 1667 | ||
750 | cedf[i].cpus = kmalloc(cluster_size * sizeof(cpu_entry_t), | 1668 | cedf[i].cpus = kmalloc(cluster_size * sizeof(cpu_entry_t), |
751 | GFP_ATOMIC); | 1669 | GFP_ATOMIC); |
752 | cedf[i].heap_node = kmalloc( | 1670 | INIT_BINHEAP_HANDLE(&(cedf[i].cpu_heap), cpu_lower_prio); |
753 | cluster_size * sizeof(struct bheap_node), | ||
754 | GFP_ATOMIC); | ||
755 | bheap_init(&(cedf[i].cpu_heap)); | ||
756 | edf_domain_init(&(cedf[i].domain), NULL, cedf_release_jobs); | 1671 | edf_domain_init(&(cedf[i].domain), NULL, cedf_release_jobs); |
757 | 1672 | ||
1673 | |||
1674 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
1675 | cedf[i].pending_tasklets.head = NULL; | ||
1676 | cedf[i].pending_tasklets.tail = &(cedf[i].pending_tasklets.head); | ||
1677 | #endif | ||
1678 | |||
1679 | |||
758 | if(!zalloc_cpumask_var(&cedf[i].cpu_map, GFP_ATOMIC)) | 1680 | if(!zalloc_cpumask_var(&cedf[i].cpu_map, GFP_ATOMIC)) |
759 | return -ENOMEM; | 1681 | return -ENOMEM; |
760 | #ifdef CONFIG_RELEASE_MASTER | 1682 | #ifdef CONFIG_RELEASE_MASTER |
@@ -765,6 +1687,10 @@ static long cedf_activate_plugin(void) | |||
765 | /* cycle through cluster and add cpus to them */ | 1687 | /* cycle through cluster and add cpus to them */ |
766 | for (i = 0; i < num_clusters; i++) { | 1688 | for (i = 0; i < num_clusters; i++) { |
767 | 1689 | ||
1690 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
1691 | raw_spin_lock_init(&cedf[i].dgl_lock); | ||
1692 | #endif | ||
1693 | |||
768 | for_each_online_cpu(cpu) { | 1694 | for_each_online_cpu(cpu) { |
769 | /* check if the cpu is already in a cluster */ | 1695 | /* check if the cpu is already in a cluster */ |
770 | for (j = 0; j < num_clusters; j++) | 1696 | for (j = 0; j < num_clusters; j++) |
@@ -795,8 +1721,8 @@ static long cedf_activate_plugin(void) | |||
795 | atomic_set(&entry->will_schedule, 0); | 1721 | atomic_set(&entry->will_schedule, 0); |
796 | entry->cpu = ccpu; | 1722 | entry->cpu = ccpu; |
797 | entry->cluster = &cedf[i]; | 1723 | entry->cluster = &cedf[i]; |
798 | entry->hn = &(cedf[i].heap_node[cpu_count]); | 1724 | |
799 | bheap_node_init(&entry->hn, entry); | 1725 | INIT_BINHEAP_NODE(&entry->hn); |
800 | 1726 | ||
801 | cpu_count++; | 1727 | cpu_count++; |
802 | 1728 | ||
@@ -813,6 +1739,40 @@ static long cedf_activate_plugin(void) | |||
813 | } | 1739 | } |
814 | } | 1740 | } |
815 | 1741 | ||
1742 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1743 | { | ||
1744 | /* distribute the daemons evenly across the clusters. */ | ||
1745 | int* affinity = kmalloc(NR_LITMUS_SOFTIRQD * sizeof(int), GFP_ATOMIC); | ||
1746 | int num_daemons_per_cluster = NR_LITMUS_SOFTIRQD / num_clusters; | ||
1747 | int left_over = NR_LITMUS_SOFTIRQD % num_clusters; | ||
1748 | |||
1749 | int daemon = 0; | ||
1750 | for(i = 0; i < num_clusters; ++i) | ||
1751 | { | ||
1752 | int num_on_this_cluster = num_daemons_per_cluster; | ||
1753 | if(left_over) | ||
1754 | { | ||
1755 | ++num_on_this_cluster; | ||
1756 | --left_over; | ||
1757 | } | ||
1758 | |||
1759 | for(j = 0; j < num_on_this_cluster; ++j) | ||
1760 | { | ||
1761 | // first CPU of this cluster | ||
1762 | affinity[daemon++] = i*cluster_size; | ||
1763 | } | ||
1764 | } | ||
1765 | |||
1766 | spawn_klitirqd(affinity); | ||
1767 | |||
1768 | kfree(affinity); | ||
1769 | } | ||
1770 | #endif | ||
1771 | |||
1772 | #ifdef CONFIG_LITMUS_NVIDIA | ||
1773 | init_nvidia_info(); | ||
1774 | #endif | ||
1775 | |||
816 | free_cpumask_var(mask); | 1776 | free_cpumask_var(mask); |
817 | clusters_allocated = 1; | 1777 | clusters_allocated = 1; |
818 | return 0; | 1778 | return 0; |
@@ -831,6 +1791,32 @@ static struct sched_plugin cedf_plugin __cacheline_aligned_in_smp = { | |||
831 | .task_block = cedf_task_block, | 1791 | .task_block = cedf_task_block, |
832 | .admit_task = cedf_admit_task, | 1792 | .admit_task = cedf_admit_task, |
833 | .activate_plugin = cedf_activate_plugin, | 1793 | .activate_plugin = cedf_activate_plugin, |
1794 | .compare = edf_higher_prio, | ||
1795 | #ifdef CONFIG_LITMUS_LOCKING | ||
1796 | .allocate_lock = cedf_allocate_lock, | ||
1797 | .increase_prio = increase_priority_inheritance, | ||
1798 | .decrease_prio = decrease_priority_inheritance, | ||
1799 | #endif | ||
1800 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
1801 | .nested_increase_prio = nested_increase_priority_inheritance, | ||
1802 | .nested_decrease_prio = nested_decrease_priority_inheritance, | ||
1803 | .__compare = __edf_higher_prio, | ||
1804 | #endif | ||
1805 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
1806 | .get_dgl_spinlock = cedf_get_dgl_spinlock, | ||
1807 | #endif | ||
1808 | #ifdef CONFIG_LITMUS_AFFINITY_LOCKING | ||
1809 | .allocate_aff_obs = cedf_allocate_affinity_observer, | ||
1810 | #endif | ||
1811 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1812 | .increase_prio_klitirqd = increase_priority_inheritance_klitirqd, | ||
1813 | .decrease_prio_klitirqd = decrease_priority_inheritance_klitirqd, | ||
1814 | #endif | ||
1815 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
1816 | .enqueue_pai_tasklet = cedf_enqueue_pai_tasklet, | ||
1817 | .change_prio_pai_tasklet = cedf_change_prio_pai_tasklet, | ||
1818 | .run_tasklets = cedf_run_tasklets, | ||
1819 | #endif | ||
834 | }; | 1820 | }; |
835 | 1821 | ||
836 | static struct proc_dir_entry *cluster_file = NULL, *cedf_dir = NULL; | 1822 | static struct proc_dir_entry *cluster_file = NULL, *cedf_dir = NULL; |