diff options
Diffstat (limited to 'litmus/sched_cedf.c')
-rw-r--r-- | litmus/sched_cedf.c | 1275 |
1 files changed, 1267 insertions, 8 deletions
diff --git a/litmus/sched_cedf.c b/litmus/sched_cedf.c index 480c62bc895b..3251fb1602f8 100644 --- a/litmus/sched_cedf.c +++ b/litmus/sched_cedf.c | |||
@@ -29,6 +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 | #include <linux/uaccess.h> | ||
32 | 33 | ||
33 | #include <linux/module.h> | 34 | #include <linux/module.h> |
34 | 35 | ||
@@ -49,7 +50,23 @@ | |||
49 | 50 | ||
50 | /* to configure the cluster size */ | 51 | /* to configure the cluster size */ |
51 | #include <litmus/litmus_proc.h> | 52 | #include <litmus/litmus_proc.h> |
52 | #include <linux/uaccess.h> | 53 | |
54 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
55 | #include <litmus/affinity.h> | ||
56 | #endif | ||
57 | |||
58 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
59 | #include <litmus/litmus_softirq.h> | ||
60 | #endif | ||
61 | |||
62 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
63 | #include <linux/interrupt.h> | ||
64 | #include <litmus/trace.h> | ||
65 | #endif | ||
66 | |||
67 | #ifdef CONFIG_LITMUS_NVIDIA | ||
68 | #include <litmus/nvidia_info.h> | ||
69 | #endif | ||
53 | 70 | ||
54 | /* Reference configuration variable. Determines which cache level is used to | 71 | /* Reference configuration variable. Determines which cache level is used to |
55 | * group CPUs into clusters. GLOBAL_CLUSTER, which is the default, means that | 72 | * group CPUs into clusters. GLOBAL_CLUSTER, which is the default, means that |
@@ -83,6 +100,15 @@ DEFINE_PER_CPU(cpu_entry_t, cedf_cpu_entries); | |||
83 | #define test_will_schedule(cpu) \ | 100 | #define test_will_schedule(cpu) \ |
84 | (atomic_read(&per_cpu(cedf_cpu_entries, cpu).will_schedule)) | 101 | (atomic_read(&per_cpu(cedf_cpu_entries, cpu).will_schedule)) |
85 | 102 | ||
103 | |||
104 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
105 | struct tasklet_head | ||
106 | { | ||
107 | struct tasklet_struct *head; | ||
108 | struct tasklet_struct **tail; | ||
109 | }; | ||
110 | #endif | ||
111 | |||
86 | /* | 112 | /* |
87 | * In C-EDF there is a cedf domain _per_ cluster | 113 | * In C-EDF there is a cedf domain _per_ cluster |
88 | * The number of clusters is dynamically determined accordingly to the | 114 | * The number of clusters is dynamically determined accordingly to the |
@@ -100,6 +126,10 @@ typedef struct clusterdomain { | |||
100 | struct bheap cpu_heap; | 126 | struct bheap cpu_heap; |
101 | /* lock for this cluster */ | 127 | /* lock for this cluster */ |
102 | #define cluster_lock domain.ready_lock | 128 | #define cluster_lock domain.ready_lock |
129 | |||
130 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
131 | struct tasklet_head pending_tasklets; | ||
132 | #endif | ||
103 | } cedf_domain_t; | 133 | } cedf_domain_t; |
104 | 134 | ||
105 | /* a cedf_domain per cluster; allocation is done at init/activation time */ | 135 | /* a cedf_domain per cluster; allocation is done at init/activation time */ |
@@ -208,7 +238,7 @@ static noinline void link_task_to_cpu(struct task_struct* linked, | |||
208 | } | 238 | } |
209 | 239 | ||
210 | /* unlink - Make sure a task is not linked any longer to an entry | 240 | /* unlink - Make sure a task is not linked any longer to an entry |
211 | * where it was linked before. Must hold cedf_lock. | 241 | * where it was linked before. Must hold cluster_lock. |
212 | */ | 242 | */ |
213 | static noinline void unlink(struct task_struct* t) | 243 | static noinline void unlink(struct task_struct* t) |
214 | { | 244 | { |
@@ -244,7 +274,7 @@ static void preempt(cpu_entry_t *entry) | |||
244 | } | 274 | } |
245 | 275 | ||
246 | /* requeue - Put an unlinked task into gsn-edf domain. | 276 | /* requeue - Put an unlinked task into gsn-edf domain. |
247 | * Caller must hold cedf_lock. | 277 | * Caller must hold cluster_lock. |
248 | */ | 278 | */ |
249 | static noinline void requeue(struct task_struct* task) | 279 | static noinline void requeue(struct task_struct* task) |
250 | { | 280 | { |
@@ -339,13 +369,17 @@ static void cedf_release_jobs(rt_domain_t* rt, struct bheap* tasks) | |||
339 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | 369 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); |
340 | } | 370 | } |
341 | 371 | ||
342 | /* caller holds cedf_lock */ | 372 | /* caller holds cluster_lock */ |
343 | static noinline void job_completion(struct task_struct *t, int forced) | 373 | static noinline void job_completion(struct task_struct *t, int forced) |
344 | { | 374 | { |
345 | BUG_ON(!t); | 375 | BUG_ON(!t); |
346 | 376 | ||
347 | sched_trace_task_completion(t, forced); | 377 | sched_trace_task_completion(t, forced); |
348 | 378 | ||
379 | #ifdef CONFIG_LITMUS_NVIDIA | ||
380 | atomic_set(&tsk_rt(t)->nv_int_count, 0); | ||
381 | #endif | ||
382 | |||
349 | TRACE_TASK(t, "job_completion().\n"); | 383 | TRACE_TASK(t, "job_completion().\n"); |
350 | 384 | ||
351 | /* set flags */ | 385 | /* set flags */ |
@@ -389,6 +423,461 @@ static void cedf_tick(struct task_struct* t) | |||
389 | } | 423 | } |
390 | } | 424 | } |
391 | 425 | ||
426 | |||
427 | |||
428 | |||
429 | |||
430 | |||
431 | |||
432 | |||
433 | |||
434 | |||
435 | |||
436 | |||
437 | |||
438 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
439 | |||
440 | |||
441 | static void __do_lit_tasklet(struct tasklet_struct* tasklet, unsigned long flushed) | ||
442 | { | ||
443 | if (!atomic_read(&tasklet->count)) { | ||
444 | if(tasklet->owner) { | ||
445 | sched_trace_tasklet_begin(tasklet->owner); | ||
446 | } | ||
447 | |||
448 | if (!test_and_clear_bit(TASKLET_STATE_SCHED, &tasklet->state)) | ||
449 | { | ||
450 | BUG(); | ||
451 | } | ||
452 | TRACE("%s: Invoking tasklet with owner pid = %d (flushed = %d).\n", | ||
453 | __FUNCTION__, | ||
454 | (tasklet->owner) ? tasklet->owner->pid : -1, | ||
455 | (tasklet->owner) ? 0 : 1); | ||
456 | tasklet->func(tasklet->data); | ||
457 | tasklet_unlock(tasklet); | ||
458 | |||
459 | if(tasklet->owner) { | ||
460 | sched_trace_tasklet_end(tasklet->owner, flushed); | ||
461 | } | ||
462 | } | ||
463 | else { | ||
464 | BUG(); | ||
465 | } | ||
466 | } | ||
467 | |||
468 | |||
469 | static void __extract_tasklets(cedf_domain_t* cluster, struct task_struct* task, struct tasklet_head* task_tasklets) | ||
470 | { | ||
471 | struct tasklet_struct* step; | ||
472 | struct tasklet_struct* tasklet; | ||
473 | struct tasklet_struct* prev; | ||
474 | |||
475 | task_tasklets->head = NULL; | ||
476 | task_tasklets->tail = &(task_tasklets->head); | ||
477 | |||
478 | prev = NULL; | ||
479 | for(step = cluster->pending_tasklets.head; step != NULL; step = step->next) | ||
480 | { | ||
481 | if(step->owner == task) | ||
482 | { | ||
483 | TRACE("%s: Found tasklet to flush: %d\n", __FUNCTION__, step->owner->pid); | ||
484 | |||
485 | tasklet = step; | ||
486 | |||
487 | if(prev) { | ||
488 | prev->next = tasklet->next; | ||
489 | } | ||
490 | else if(cluster->pending_tasklets.head == tasklet) { | ||
491 | // we're at the head. | ||
492 | cluster->pending_tasklets.head = tasklet->next; | ||
493 | } | ||
494 | |||
495 | if(cluster->pending_tasklets.tail == &tasklet) { | ||
496 | // we're at the tail | ||
497 | if(prev) { | ||
498 | cluster->pending_tasklets.tail = &prev; | ||
499 | } | ||
500 | else { | ||
501 | cluster->pending_tasklets.tail = &(cluster->pending_tasklets.head); | ||
502 | } | ||
503 | } | ||
504 | |||
505 | tasklet->next = NULL; | ||
506 | *(task_tasklets->tail) = tasklet; | ||
507 | task_tasklets->tail = &(tasklet->next); | ||
508 | } | ||
509 | else { | ||
510 | prev = step; | ||
511 | } | ||
512 | } | ||
513 | } | ||
514 | |||
515 | static void flush_tasklets(cedf_domain_t* cluster, struct task_struct* task) | ||
516 | { | ||
517 | #if 0 | ||
518 | unsigned long flags; | ||
519 | struct tasklet_head task_tasklets; | ||
520 | struct tasklet_struct* step; | ||
521 | |||
522 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
523 | __extract_tasklets(cluster, task, &task_tasklets); | ||
524 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
525 | |||
526 | if(cluster->pending_tasklets.head != NULL) { | ||
527 | TRACE("%s: Flushing tasklets for %d...\n", __FUNCTION__, task->pid); | ||
528 | } | ||
529 | |||
530 | // now execute any flushed tasklets. | ||
531 | for(step = cluster->pending_tasklets.head; step != NULL; /**/) | ||
532 | { | ||
533 | struct tasklet_struct* temp = step->next; | ||
534 | |||
535 | step->next = NULL; | ||
536 | __do_lit_tasklet(step, 1ul); | ||
537 | |||
538 | step = temp; | ||
539 | } | ||
540 | #endif | ||
541 | |||
542 | // lazy flushing. | ||
543 | // just change ownership to NULL and let an idle processor | ||
544 | // take care of it. :P | ||
545 | |||
546 | struct tasklet_struct* step; | ||
547 | unsigned long flags; | ||
548 | |||
549 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
550 | |||
551 | for(step = cluster->pending_tasklets.head; step != NULL; step = step->next) | ||
552 | { | ||
553 | if(step->owner == task) | ||
554 | { | ||
555 | TRACE("%s: Found tasklet to flush: %d\n", __FUNCTION__, step->owner->pid); | ||
556 | step->owner = NULL; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
561 | } | ||
562 | |||
563 | |||
564 | static void do_lit_tasklets(cedf_domain_t* cluster, struct task_struct* sched_task) | ||
565 | { | ||
566 | int work_to_do = 1; | ||
567 | struct tasklet_struct *tasklet = NULL; | ||
568 | //struct tasklet_struct *step; | ||
569 | unsigned long flags; | ||
570 | |||
571 | while(work_to_do) { | ||
572 | |||
573 | TS_NV_SCHED_BOTISR_START; | ||
574 | |||
575 | // remove tasklet at head of list if it has higher priority. | ||
576 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
577 | |||
578 | /* | ||
579 | step = cluster->pending_tasklets.head; | ||
580 | TRACE("%s: (BEFORE) dumping tasklet queue...\n", __FUNCTION__); | ||
581 | while(step != NULL){ | ||
582 | TRACE("%s: %p (%d)\n", __FUNCTION__, step, step->owner->pid); | ||
583 | step = step->next; | ||
584 | } | ||
585 | TRACE("%s: tail = %p (%d)\n", __FUNCTION__, *(cluster->pending_tasklets.tail), (*(cluster->pending_tasklets.tail) != NULL) ? (*(cluster->pending_tasklets.tail))->owner->pid : -1); | ||
586 | TRACE("%s: done.\n", __FUNCTION__); | ||
587 | */ | ||
588 | |||
589 | if(cluster->pending_tasklets.head != NULL) { | ||
590 | // remove tasklet at head. | ||
591 | tasklet = cluster->pending_tasklets.head; | ||
592 | |||
593 | if(edf_higher_prio(tasklet->owner, sched_task)) { | ||
594 | |||
595 | if(NULL == tasklet->next) { | ||
596 | // tasklet is at the head, list only has one element | ||
597 | TRACE("%s: Tasklet for %d is the last element in tasklet queue.\n", __FUNCTION__, (tasklet->owner) ? tasklet->owner->pid : -1); | ||
598 | cluster->pending_tasklets.tail = &(cluster->pending_tasklets.head); | ||
599 | } | ||
600 | |||
601 | // remove the tasklet from the queue | ||
602 | cluster->pending_tasklets.head = tasklet->next; | ||
603 | |||
604 | TRACE("%s: Removed tasklet for %d from tasklet queue.\n", __FUNCTION__, (tasklet->owner) ? tasklet->owner->pid : -1); | ||
605 | } | ||
606 | else { | ||
607 | TRACE("%s: Pending tasklet (%d) does not have priority to run on this CPU (%d).\n", __FUNCTION__, (tasklet->owner) ? tasklet->owner->pid : -1, smp_processor_id()); | ||
608 | tasklet = NULL; | ||
609 | } | ||
610 | } | ||
611 | else { | ||
612 | TRACE("%s: Tasklet queue is empty.\n", __FUNCTION__); | ||
613 | } | ||
614 | |||
615 | |||
616 | /* | ||
617 | step = cluster->pending_tasklets.head; | ||
618 | TRACE("%s: (AFTER) dumping tasklet queue...\n", __FUNCTION__); | ||
619 | while(step != NULL){ | ||
620 | TRACE("%s: %p (%d)\n", __FUNCTION__, step, step->owner->pid); | ||
621 | step = step->next; | ||
622 | } | ||
623 | TRACE("%s: tail = %p (%d)\n", __FUNCTION__, *(cluster->pending_tasklets.tail), (*(cluster->pending_tasklets.tail) != NULL) ? (*(cluster->pending_tasklets.tail))->owner->pid : -1); | ||
624 | TRACE("%s: done.\n", __FUNCTION__); | ||
625 | */ | ||
626 | |||
627 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
628 | |||
629 | |||
630 | TS_NV_SCHED_BOTISR_END; | ||
631 | |||
632 | if(tasklet) { | ||
633 | __do_lit_tasklet(tasklet, 0ul); | ||
634 | tasklet = NULL; | ||
635 | } | ||
636 | else { | ||
637 | work_to_do = 0; | ||
638 | } | ||
639 | } | ||
640 | |||
641 | //TRACE("%s: exited.\n", __FUNCTION__); | ||
642 | } | ||
643 | |||
644 | |||
645 | static void run_tasklets(struct task_struct* sched_task) | ||
646 | { | ||
647 | cedf_domain_t* cluster; | ||
648 | |||
649 | #if 0 | ||
650 | int task_is_rt = is_realtime(sched_task); | ||
651 | cedf_domain_t* cluster; | ||
652 | |||
653 | if(is_realtime(sched_task)) { | ||
654 | cluster = task_cpu_cluster(sched_task); | ||
655 | } | ||
656 | else { | ||
657 | cluster = remote_cluster(get_cpu()); | ||
658 | } | ||
659 | |||
660 | if(cluster && cluster->pending_tasklets.head != NULL) { | ||
661 | TRACE("%s: There are tasklets to process.\n", __FUNCTION__); | ||
662 | |||
663 | do_lit_tasklets(cluster, sched_task); | ||
664 | } | ||
665 | |||
666 | if(!task_is_rt) { | ||
667 | put_cpu_no_resched(); | ||
668 | } | ||
669 | #else | ||
670 | |||
671 | preempt_disable(); | ||
672 | |||
673 | cluster = (is_realtime(sched_task)) ? | ||
674 | task_cpu_cluster(sched_task) : | ||
675 | remote_cluster(smp_processor_id()); | ||
676 | |||
677 | if(cluster && cluster->pending_tasklets.head != NULL) { | ||
678 | TRACE("%s: There are tasklets to process.\n", __FUNCTION__); | ||
679 | do_lit_tasklets(cluster, sched_task); | ||
680 | } | ||
681 | |||
682 | preempt_enable_no_resched(); | ||
683 | |||
684 | #endif | ||
685 | } | ||
686 | |||
687 | |||
688 | static void __add_pai_tasklet(struct tasklet_struct* tasklet, cedf_domain_t* cluster) | ||
689 | { | ||
690 | struct tasklet_struct* step; | ||
691 | |||
692 | /* | ||
693 | step = cluster->pending_tasklets.head; | ||
694 | TRACE("%s: (BEFORE) dumping tasklet queue...\n", __FUNCTION__); | ||
695 | while(step != NULL){ | ||
696 | TRACE("%s: %p (%d)\n", __FUNCTION__, step, step->owner->pid); | ||
697 | step = step->next; | ||
698 | } | ||
699 | TRACE("%s: tail = %p (%d)\n", __FUNCTION__, *(cluster->pending_tasklets.tail), (*(cluster->pending_tasklets.tail) != NULL) ? (*(cluster->pending_tasklets.tail))->owner->pid : -1); | ||
700 | TRACE("%s: done.\n", __FUNCTION__); | ||
701 | */ | ||
702 | |||
703 | |||
704 | tasklet->next = NULL; // make sure there are no old values floating around | ||
705 | |||
706 | step = cluster->pending_tasklets.head; | ||
707 | if(step == NULL) { | ||
708 | TRACE("%s: tasklet queue empty. inserting tasklet for %d at head.\n", __FUNCTION__, tasklet->owner->pid); | ||
709 | // insert at tail. | ||
710 | *(cluster->pending_tasklets.tail) = tasklet; | ||
711 | cluster->pending_tasklets.tail = &(tasklet->next); | ||
712 | } | ||
713 | else if((*(cluster->pending_tasklets.tail) != NULL) && | ||
714 | edf_higher_prio((*(cluster->pending_tasklets.tail))->owner, tasklet->owner)) { | ||
715 | // insert at tail. | ||
716 | TRACE("%s: tasklet belongs at end. inserting tasklet for %d at tail.\n", __FUNCTION__, tasklet->owner->pid); | ||
717 | |||
718 | *(cluster->pending_tasklets.tail) = tasklet; | ||
719 | cluster->pending_tasklets.tail = &(tasklet->next); | ||
720 | } | ||
721 | else { | ||
722 | |||
723 | //WARN_ON(1 == 1); | ||
724 | |||
725 | // insert the tasklet somewhere in the middle. | ||
726 | |||
727 | TRACE("%s: tasklet belongs somewhere in the middle.\n", __FUNCTION__); | ||
728 | |||
729 | while(step->next && edf_higher_prio(step->next->owner, tasklet->owner)) { | ||
730 | step = step->next; | ||
731 | } | ||
732 | |||
733 | // insert tasklet right before step->next. | ||
734 | |||
735 | TRACE("%s: inserting tasklet for %d between %d and %d.\n", __FUNCTION__, | ||
736 | tasklet->owner->pid, | ||
737 | (step->owner) ? | ||
738 | step->owner->pid : | ||
739 | -1, | ||
740 | (step->next) ? | ||
741 | ((step->next->owner) ? | ||
742 | step->next->owner->pid : | ||
743 | -1) : | ||
744 | -1); | ||
745 | |||
746 | tasklet->next = step->next; | ||
747 | step->next = tasklet; | ||
748 | |||
749 | // patch up the head if needed. | ||
750 | if(cluster->pending_tasklets.head == step) | ||
751 | { | ||
752 | TRACE("%s: %d is the new tasklet queue head.\n", __FUNCTION__, tasklet->owner->pid); | ||
753 | cluster->pending_tasklets.head = tasklet; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | /* | ||
758 | step = cluster->pending_tasklets.head; | ||
759 | TRACE("%s: (AFTER) dumping tasklet queue...\n", __FUNCTION__); | ||
760 | while(step != NULL){ | ||
761 | TRACE("%s: %p (%d)\n", __FUNCTION__, step, step->owner->pid); | ||
762 | step = step->next; | ||
763 | } | ||
764 | TRACE("%s: tail = %p (%d)\n", __FUNCTION__, *(cluster->pending_tasklets.tail), (*(cluster->pending_tasklets.tail) != NULL) ? (*(cluster->pending_tasklets.tail))->owner->pid : -1); | ||
765 | TRACE("%s: done.\n", __FUNCTION__); | ||
766 | */ | ||
767 | |||
768 | // TODO: Maintain this list in priority order. | ||
769 | // tasklet->next = NULL; | ||
770 | // *(cluster->pending_tasklets.tail) = tasklet; | ||
771 | // cluster->pending_tasklets.tail = &tasklet->next; | ||
772 | } | ||
773 | |||
774 | static int enqueue_pai_tasklet(struct tasklet_struct* tasklet) | ||
775 | { | ||
776 | cedf_domain_t *cluster = NULL; | ||
777 | cpu_entry_t *targetCPU = NULL; | ||
778 | int thisCPU; | ||
779 | int runLocal = 0; | ||
780 | int runNow = 0; | ||
781 | unsigned long flags; | ||
782 | |||
783 | if(unlikely((tasklet->owner == NULL) || !is_realtime(tasklet->owner))) | ||
784 | { | ||
785 | TRACE("%s: No owner associated with this tasklet!\n", __FUNCTION__); | ||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | cluster = task_cpu_cluster(tasklet->owner); | ||
790 | |||
791 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | ||
792 | |||
793 | thisCPU = smp_processor_id(); | ||
794 | |||
795 | #if 1 | ||
796 | #ifdef CONFIG_SCHED_CPU_AFFINITY | ||
797 | { | ||
798 | cpu_entry_t* affinity = NULL; | ||
799 | |||
800 | // use this CPU if it is in our cluster and isn't running any RT work. | ||
801 | if(cpu_isset(thisCPU, *cluster->cpu_map) && (__get_cpu_var(cedf_cpu_entries).linked == NULL)) { | ||
802 | affinity = &(__get_cpu_var(cedf_cpu_entries)); | ||
803 | } | ||
804 | else { | ||
805 | // this CPU is busy or shouldn't run tasklet in this cluster. | ||
806 | // look for available near by CPUs. | ||
807 | // NOTE: Affinity towards owner and not this CPU. Is this right? | ||
808 | affinity = | ||
809 | cedf_get_nearest_available_cpu(cluster, | ||
810 | &per_cpu(cedf_cpu_entries, task_cpu(tasklet->owner))); | ||
811 | } | ||
812 | |||
813 | targetCPU = affinity; | ||
814 | } | ||
815 | #endif | ||
816 | #endif | ||
817 | |||
818 | if (targetCPU == NULL) { | ||
819 | targetCPU = lowest_prio_cpu(cluster); | ||
820 | } | ||
821 | |||
822 | if (edf_higher_prio(tasklet->owner, targetCPU->linked)) { | ||
823 | if (thisCPU == targetCPU->cpu) { | ||
824 | TRACE("%s: Run tasklet locally (and now).\n", __FUNCTION__); | ||
825 | runLocal = 1; | ||
826 | runNow = 1; | ||
827 | } | ||
828 | else { | ||
829 | TRACE("%s: Run tasklet remotely (and now).\n", __FUNCTION__); | ||
830 | runLocal = 0; | ||
831 | runNow = 1; | ||
832 | } | ||
833 | } | ||
834 | else { | ||
835 | runLocal = 0; | ||
836 | runNow = 0; | ||
837 | } | ||
838 | |||
839 | if(!runLocal) { | ||
840 | // enqueue the tasklet | ||
841 | __add_pai_tasklet(tasklet, cluster); | ||
842 | } | ||
843 | |||
844 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | ||
845 | |||
846 | |||
847 | if (runLocal /*&& runNow */) { // runNow == 1 is implied | ||
848 | TRACE("%s: Running tasklet on CPU where it was received.\n", __FUNCTION__); | ||
849 | __do_lit_tasklet(tasklet, 0ul); | ||
850 | } | ||
851 | else if (runNow /*&& !runLocal */) { // runLocal == 0 is implied | ||
852 | TRACE("%s: Triggering CPU %d to run tasklet.\n", __FUNCTION__, targetCPU->cpu); | ||
853 | preempt(targetCPU); // need to be protected by cluster_lock? | ||
854 | } | ||
855 | else { | ||
856 | TRACE("%s: Scheduling of tasklet was deferred.\n", __FUNCTION__); | ||
857 | } | ||
858 | |||
859 | return(1); // success | ||
860 | } | ||
861 | |||
862 | |||
863 | #endif | ||
864 | |||
865 | |||
866 | |||
867 | |||
868 | |||
869 | |||
870 | |||
871 | |||
872 | |||
873 | |||
874 | |||
875 | |||
876 | |||
877 | |||
878 | |||
879 | |||
880 | |||
392 | /* Getting schedule() right is a bit tricky. schedule() may not make any | 881 | /* 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 | 882 | * 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 | 883 | * number of reasons. The reasons include a scheduler_tick() determined that it |
@@ -514,7 +1003,7 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
514 | raw_spin_unlock(&cluster->cluster_lock); | 1003 | raw_spin_unlock(&cluster->cluster_lock); |
515 | 1004 | ||
516 | #ifdef WANT_ALL_SCHED_EVENTS | 1005 | #ifdef WANT_ALL_SCHED_EVENTS |
517 | TRACE("cedf_lock released, next=0x%p\n", next); | 1006 | TRACE("cluster_lock released, next=0x%p\n", next); |
518 | 1007 | ||
519 | if (next) | 1008 | if (next) |
520 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); | 1009 | TRACE_TASK(next, "scheduled at %llu\n", litmus_clock()); |
@@ -522,7 +1011,6 @@ static struct task_struct* cedf_schedule(struct task_struct * prev) | |||
522 | TRACE("becomes idle at %llu.\n", litmus_clock()); | 1011 | TRACE("becomes idle at %llu.\n", litmus_clock()); |
523 | #endif | 1012 | #endif |
524 | 1013 | ||
525 | |||
526 | return next; | 1014 | return next; |
527 | } | 1015 | } |
528 | 1016 | ||
@@ -586,7 +1074,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) | 1074 | static void cedf_task_wake_up(struct task_struct *task) |
587 | { | 1075 | { |
588 | unsigned long flags; | 1076 | unsigned long flags; |
589 | lt_t now; | 1077 | //lt_t now; |
590 | cedf_domain_t *cluster; | 1078 | cedf_domain_t *cluster; |
591 | 1079 | ||
592 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); | 1080 | TRACE_TASK(task, "wake_up at %llu\n", litmus_clock()); |
@@ -594,6 +1082,8 @@ static void cedf_task_wake_up(struct task_struct *task) | |||
594 | cluster = task_cpu_cluster(task); | 1082 | cluster = task_cpu_cluster(task); |
595 | 1083 | ||
596 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | 1084 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); |
1085 | |||
1086 | #if 0 // sproadic task model | ||
597 | /* We need to take suspensions because of semaphores into | 1087 | /* We need to take suspensions because of semaphores into |
598 | * account! If a job resumes after being suspended due to acquiring | 1088 | * account! If a job resumes after being suspended due to acquiring |
599 | * a semaphore, it should never be treated as a new job release. | 1089 | * a semaphore, it should never be treated as a new job release. |
@@ -615,7 +1105,13 @@ static void cedf_task_wake_up(struct task_struct *task) | |||
615 | } | 1105 | } |
616 | } | 1106 | } |
617 | } | 1107 | } |
618 | cedf_job_arrival(task); | 1108 | #endif |
1109 | |||
1110 | set_rt_flags(task, RT_F_RUNNING); // periodic model | ||
1111 | |||
1112 | if(tsk_rt(task)->linked_on == NO_CPU) | ||
1113 | cedf_job_arrival(task); | ||
1114 | |||
619 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); | 1115 | raw_spin_unlock_irqrestore(&cluster->cluster_lock, flags); |
620 | } | 1116 | } |
621 | 1117 | ||
@@ -642,6 +1138,10 @@ static void cedf_task_exit(struct task_struct * t) | |||
642 | unsigned long flags; | 1138 | unsigned long flags; |
643 | cedf_domain_t *cluster = task_cpu_cluster(t); | 1139 | cedf_domain_t *cluster = task_cpu_cluster(t); |
644 | 1140 | ||
1141 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
1142 | flush_tasklets(cluster, t); | ||
1143 | #endif | ||
1144 | |||
645 | /* unlink if necessary */ | 1145 | /* unlink if necessary */ |
646 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); | 1146 | raw_spin_lock_irqsave(&cluster->cluster_lock, flags); |
647 | unlink(t); | 1147 | unlink(t); |
@@ -662,6 +1162,711 @@ static long cedf_admit_task(struct task_struct* tsk) | |||
662 | return task_cpu(tsk) == tsk->rt_param.task_params.cpu ? 0 : -EINVAL; | 1162 | return task_cpu(tsk) == tsk->rt_param.task_params.cpu ? 0 : -EINVAL; |
663 | } | 1163 | } |
664 | 1164 | ||
1165 | |||
1166 | |||
1167 | #ifdef CONFIG_LITMUS_LOCKING | ||
1168 | |||
1169 | #include <litmus/fdso.h> | ||
1170 | |||
1171 | |||
1172 | static void __set_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh) | ||
1173 | { | ||
1174 | int linked_on; | ||
1175 | int check_preempt = 0; | ||
1176 | |||
1177 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1178 | |||
1179 | if(prio_inh != NULL) | ||
1180 | TRACE_TASK(t, "inherits priority from %s/%d\n", prio_inh->comm, prio_inh->pid); | ||
1181 | else | ||
1182 | TRACE_TASK(t, "inherits priority from %p\n", prio_inh); | ||
1183 | |||
1184 | sched_trace_eff_prio_change(t, prio_inh); | ||
1185 | |||
1186 | tsk_rt(t)->inh_task = prio_inh; | ||
1187 | |||
1188 | linked_on = tsk_rt(t)->linked_on; | ||
1189 | |||
1190 | /* If it is scheduled, then we need to reorder the CPU heap. */ | ||
1191 | if (linked_on != NO_CPU) { | ||
1192 | TRACE_TASK(t, "%s: linked on %d\n", | ||
1193 | __FUNCTION__, linked_on); | ||
1194 | /* Holder is scheduled; need to re-order CPUs. | ||
1195 | * We can't use heap_decrease() here since | ||
1196 | * the cpu_heap is ordered in reverse direction, so | ||
1197 | * it is actually an increase. */ | ||
1198 | bheap_delete(cpu_lower_prio, &cluster->cpu_heap, | ||
1199 | per_cpu(cedf_cpu_entries, linked_on).hn); | ||
1200 | bheap_insert(cpu_lower_prio, &cluster->cpu_heap, | ||
1201 | per_cpu(cedf_cpu_entries, linked_on).hn); | ||
1202 | } else { | ||
1203 | /* holder may be queued: first stop queue changes */ | ||
1204 | raw_spin_lock(&cluster->domain.release_lock); | ||
1205 | if (is_queued(t)) { | ||
1206 | TRACE_TASK(t, "%s: is queued\n", __FUNCTION__); | ||
1207 | |||
1208 | /* We need to update the position of holder in some | ||
1209 | * heap. Note that this could be a release heap if we | ||
1210 | * budget enforcement is used and this job overran. */ | ||
1211 | check_preempt = !bheap_decrease(edf_ready_order, tsk_rt(t)->heap_node); | ||
1212 | |||
1213 | } else { | ||
1214 | /* Nothing to do: if it is not queued and not linked | ||
1215 | * then it is either sleeping or currently being moved | ||
1216 | * by other code (e.g., a timer interrupt handler) that | ||
1217 | * will use the correct priority when enqueuing the | ||
1218 | * task. */ | ||
1219 | TRACE_TASK(t, "%s: is NOT queued => Done.\n", __FUNCTION__); | ||
1220 | } | ||
1221 | raw_spin_unlock(&cluster->domain.release_lock); | ||
1222 | |||
1223 | /* If holder was enqueued in a release heap, then the following | ||
1224 | * preemption check is pointless, but we can't easily detect | ||
1225 | * that case. If you want to fix this, then consider that | ||
1226 | * simply adding a state flag requires O(n) time to update when | ||
1227 | * releasing n tasks, which conflicts with the goal to have | ||
1228 | * O(log n) merges. */ | ||
1229 | if (check_preempt) { | ||
1230 | /* heap_decrease() hit the top level of the heap: make | ||
1231 | * sure preemption checks get the right task, not the | ||
1232 | * potentially stale cache. */ | ||
1233 | bheap_uncache_min(edf_ready_order, &cluster->domain.ready_queue); | ||
1234 | check_for_preemptions(cluster); | ||
1235 | } | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | /* called with IRQs off */ | ||
1240 | static void set_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh) | ||
1241 | { | ||
1242 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1243 | |||
1244 | raw_spin_lock(&cluster->cluster_lock); | ||
1245 | |||
1246 | __set_priority_inheritance(t, prio_inh); | ||
1247 | |||
1248 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1249 | if(tsk_rt(t)->cur_klitirqd != NULL) | ||
1250 | { | ||
1251 | TRACE_TASK(t, "%s/%d inherits a new priority!\n", | ||
1252 | tsk_rt(t)->cur_klitirqd->comm, tsk_rt(t)->cur_klitirqd->pid); | ||
1253 | |||
1254 | __set_priority_inheritance(tsk_rt(t)->cur_klitirqd, prio_inh); | ||
1255 | } | ||
1256 | #endif | ||
1257 | |||
1258 | raw_spin_unlock(&cluster->cluster_lock); | ||
1259 | } | ||
1260 | |||
1261 | |||
1262 | /* called with IRQs off */ | ||
1263 | static void __clear_priority_inheritance(struct task_struct* t) | ||
1264 | { | ||
1265 | TRACE_TASK(t, "priority restored\n"); | ||
1266 | |||
1267 | if(tsk_rt(t)->scheduled_on != NO_CPU) | ||
1268 | { | ||
1269 | sched_trace_eff_prio_change(t, NULL); | ||
1270 | |||
1271 | tsk_rt(t)->inh_task = NULL; | ||
1272 | |||
1273 | /* Check if rescheduling is necessary. We can't use heap_decrease() | ||
1274 | * since the priority was effectively lowered. */ | ||
1275 | unlink(t); | ||
1276 | cedf_job_arrival(t); | ||
1277 | } | ||
1278 | else | ||
1279 | { | ||
1280 | __set_priority_inheritance(t, NULL); | ||
1281 | } | ||
1282 | |||
1283 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1284 | if(tsk_rt(t)->cur_klitirqd != NULL) | ||
1285 | { | ||
1286 | TRACE_TASK(t, "%s/%d inheritance set back to owner.\n", | ||
1287 | tsk_rt(t)->cur_klitirqd->comm, tsk_rt(t)->cur_klitirqd->pid); | ||
1288 | |||
1289 | if(tsk_rt(tsk_rt(t)->cur_klitirqd)->scheduled_on != NO_CPU) | ||
1290 | { | ||
1291 | sched_trace_eff_prio_change(tsk_rt(t)->cur_klitirqd, t); | ||
1292 | |||
1293 | tsk_rt(tsk_rt(t)->cur_klitirqd)->inh_task = t; | ||
1294 | |||
1295 | /* Check if rescheduling is necessary. We can't use heap_decrease() | ||
1296 | * since the priority was effectively lowered. */ | ||
1297 | unlink(tsk_rt(t)->cur_klitirqd); | ||
1298 | cedf_job_arrival(tsk_rt(t)->cur_klitirqd); | ||
1299 | } | ||
1300 | else | ||
1301 | { | ||
1302 | __set_priority_inheritance(tsk_rt(t)->cur_klitirqd, t); | ||
1303 | } | ||
1304 | } | ||
1305 | #endif | ||
1306 | } | ||
1307 | |||
1308 | /* called with IRQs off */ | ||
1309 | static void clear_priority_inheritance(struct task_struct* t) | ||
1310 | { | ||
1311 | cedf_domain_t* cluster = task_cpu_cluster(t); | ||
1312 | |||
1313 | raw_spin_lock(&cluster->cluster_lock); | ||
1314 | __clear_priority_inheritance(t); | ||
1315 | raw_spin_unlock(&cluster->cluster_lock); | ||
1316 | } | ||
1317 | |||
1318 | |||
1319 | |||
1320 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
1321 | /* called with IRQs off */ | ||
1322 | static void set_priority_inheritance_klitirqd(struct task_struct* klitirqd, | ||
1323 | struct task_struct* old_owner, | ||
1324 | struct task_struct* new_owner) | ||
1325 | { | ||
1326 | cedf_domain_t* cluster = task_cpu_cluster(klitirqd); | ||
1327 | |||
1328 | BUG_ON(!(tsk_rt(klitirqd)->is_proxy_thread)); | ||
1329 | |||
1330 | raw_spin_lock(&cluster->cluster_lock); | ||
1331 | |||
1332 | if(old_owner != new_owner) | ||
1333 | { | ||
1334 | if(old_owner) | ||
1335 | { | ||
1336 | // unreachable? | ||
1337 | tsk_rt(old_owner)->cur_klitirqd = NULL; | ||
1338 | } | ||
1339 | |||
1340 | TRACE_TASK(klitirqd, "giving ownership to %s/%d.\n", | ||
1341 | new_owner->comm, new_owner->pid); | ||
1342 | |||
1343 | tsk_rt(new_owner)->cur_klitirqd = klitirqd; | ||
1344 | } | ||
1345 | |||
1346 | __set_priority_inheritance(klitirqd, | ||
1347 | (tsk_rt(new_owner)->inh_task == NULL) ? | ||
1348 | new_owner : | ||
1349 | tsk_rt(new_owner)->inh_task); | ||
1350 | |||
1351 | raw_spin_unlock(&cluster->cluster_lock); | ||
1352 | } | ||
1353 | |||
1354 | /* called with IRQs off */ | ||
1355 | static void clear_priority_inheritance_klitirqd(struct task_struct* klitirqd, | ||
1356 | struct task_struct* old_owner) | ||
1357 | { | ||
1358 | cedf_domain_t* cluster = task_cpu_cluster(klitirqd); | ||
1359 | |||
1360 | BUG_ON(!(tsk_rt(klitirqd)->is_proxy_thread)); | ||
1361 | |||
1362 | raw_spin_lock(&cluster->cluster_lock); | ||
1363 | |||
1364 | TRACE_TASK(klitirqd, "priority restored\n"); | ||
1365 | |||
1366 | if(tsk_rt(klitirqd)->scheduled_on != NO_CPU) | ||
1367 | { | ||
1368 | tsk_rt(klitirqd)->inh_task = NULL; | ||
1369 | |||
1370 | /* Check if rescheduling is necessary. We can't use heap_decrease() | ||
1371 | * since the priority was effectively lowered. */ | ||
1372 | unlink(klitirqd); | ||
1373 | cedf_job_arrival(klitirqd); | ||
1374 | } | ||
1375 | else | ||
1376 | { | ||
1377 | __set_priority_inheritance(klitirqd, NULL); | ||
1378 | } | ||
1379 | |||
1380 | tsk_rt(old_owner)->cur_klitirqd = NULL; | ||
1381 | |||
1382 | raw_spin_unlock(&cluster->cluster_lock); | ||
1383 | } | ||
1384 | #endif // CONFIG_LITMUS_SOFTIRQD | ||
1385 | |||
1386 | |||
1387 | /* ******************** KFMLP support ********************** */ | ||
1388 | |||
1389 | /* struct for semaphore with priority inheritance */ | ||
1390 | struct kfmlp_queue | ||
1391 | { | ||
1392 | wait_queue_head_t wait; | ||
1393 | struct task_struct* owner; | ||
1394 | struct task_struct* hp_waiter; | ||
1395 | int count; /* number of waiters + holder */ | ||
1396 | }; | ||
1397 | |||
1398 | struct kfmlp_semaphore | ||
1399 | { | ||
1400 | struct litmus_lock litmus_lock; | ||
1401 | |||
1402 | spinlock_t lock; | ||
1403 | |||
1404 | int num_resources; /* aka k */ | ||
1405 | struct kfmlp_queue *queues; /* array */ | ||
1406 | struct kfmlp_queue *shortest_queue; /* pointer to shortest queue */ | ||
1407 | }; | ||
1408 | |||
1409 | static inline struct kfmlp_semaphore* kfmlp_from_lock(struct litmus_lock* lock) | ||
1410 | { | ||
1411 | return container_of(lock, struct kfmlp_semaphore, litmus_lock); | ||
1412 | } | ||
1413 | |||
1414 | static inline int kfmlp_get_idx(struct kfmlp_semaphore* sem, | ||
1415 | struct kfmlp_queue* queue) | ||
1416 | { | ||
1417 | return (queue - &sem->queues[0]); | ||
1418 | } | ||
1419 | |||
1420 | static inline struct kfmlp_queue* kfmlp_get_queue(struct kfmlp_semaphore* sem, | ||
1421 | struct task_struct* holder) | ||
1422 | { | ||
1423 | int i; | ||
1424 | for(i = 0; i < sem->num_resources; ++i) | ||
1425 | if(sem->queues[i].owner == holder) | ||
1426 | return(&sem->queues[i]); | ||
1427 | return(NULL); | ||
1428 | } | ||
1429 | |||
1430 | /* caller is responsible for locking */ | ||
1431 | static struct task_struct* kfmlp_find_hp_waiter(struct kfmlp_queue *kqueue, | ||
1432 | struct task_struct *skip) | ||
1433 | { | ||
1434 | struct list_head *pos; | ||
1435 | struct task_struct *queued, *found = NULL; | ||
1436 | |||
1437 | list_for_each(pos, &kqueue->wait.task_list) { | ||
1438 | queued = (struct task_struct*) list_entry(pos, wait_queue_t, | ||
1439 | task_list)->private; | ||
1440 | |||
1441 | /* Compare task prios, find high prio task. */ | ||
1442 | if (queued != skip && edf_higher_prio(queued, found)) | ||
1443 | found = queued; | ||
1444 | } | ||
1445 | return found; | ||
1446 | } | ||
1447 | |||
1448 | static inline struct kfmlp_queue* kfmlp_find_shortest( | ||
1449 | struct kfmlp_semaphore* sem, | ||
1450 | struct kfmlp_queue* search_start) | ||
1451 | { | ||
1452 | // we start our search at search_start instead of at the beginning of the | ||
1453 | // queue list to load-balance across all resources. | ||
1454 | struct kfmlp_queue* step = search_start; | ||
1455 | struct kfmlp_queue* shortest = sem->shortest_queue; | ||
1456 | |||
1457 | do | ||
1458 | { | ||
1459 | step = (step+1 != &sem->queues[sem->num_resources]) ? | ||
1460 | step+1 : &sem->queues[0]; | ||
1461 | if(step->count < shortest->count) | ||
1462 | { | ||
1463 | shortest = step; | ||
1464 | if(step->count == 0) | ||
1465 | break; /* can't get any shorter */ | ||
1466 | } | ||
1467 | }while(step != search_start); | ||
1468 | |||
1469 | return(shortest); | ||
1470 | } | ||
1471 | |||
1472 | static struct task_struct* kfmlp_remove_hp_waiter(struct kfmlp_semaphore* sem) | ||
1473 | { | ||
1474 | /* must hold sem->lock */ | ||
1475 | |||
1476 | struct kfmlp_queue *my_queue = NULL; | ||
1477 | struct task_struct *max_hp = NULL; | ||
1478 | |||
1479 | |||
1480 | struct list_head *pos; | ||
1481 | struct task_struct *queued; | ||
1482 | int i; | ||
1483 | |||
1484 | for(i = 0; i < sem->num_resources; ++i) | ||
1485 | { | ||
1486 | if( (sem->queues[i].count > 1) && | ||
1487 | ((my_queue == NULL) || | ||
1488 | (edf_higher_prio(sem->queues[i].hp_waiter, my_queue->hp_waiter))) ) | ||
1489 | { | ||
1490 | my_queue = &sem->queues[i]; | ||
1491 | } | ||
1492 | } | ||
1493 | |||
1494 | if(my_queue) | ||
1495 | { | ||
1496 | cedf_domain_t* cluster; | ||
1497 | |||
1498 | max_hp = my_queue->hp_waiter; | ||
1499 | BUG_ON(!max_hp); | ||
1500 | |||
1501 | TRACE_CUR("queue %d: stealing %s/%d from queue %d\n", | ||
1502 | kfmlp_get_idx(sem, my_queue), | ||
1503 | max_hp->comm, max_hp->pid, | ||
1504 | kfmlp_get_idx(sem, my_queue)); | ||
1505 | |||
1506 | my_queue->hp_waiter = kfmlp_find_hp_waiter(my_queue, max_hp); | ||
1507 | |||
1508 | /* | ||
1509 | if(my_queue->hp_waiter) | ||
1510 | TRACE_CUR("queue %d: new hp_waiter is %s/%d\n", | ||
1511 | kfmlp_get_idx(sem, my_queue), | ||
1512 | my_queue->hp_waiter->comm, | ||
1513 | my_queue->hp_waiter->pid); | ||
1514 | else | ||
1515 | TRACE_CUR("queue %d: new hp_waiter is %p\n", | ||
1516 | kfmlp_get_idx(sem, my_queue), NULL); | ||
1517 | */ | ||
1518 | |||
1519 | cluster = task_cpu_cluster(max_hp); | ||
1520 | |||
1521 | raw_spin_lock(&cluster->cluster_lock); | ||
1522 | |||
1523 | /* | ||
1524 | if(my_queue->owner) | ||
1525 | TRACE_CUR("queue %d: owner is %s/%d\n", | ||
1526 | kfmlp_get_idx(sem, my_queue), | ||
1527 | my_queue->owner->comm, | ||
1528 | my_queue->owner->pid); | ||
1529 | else | ||
1530 | TRACE_CUR("queue %d: owner is %p\n", | ||
1531 | kfmlp_get_idx(sem, my_queue), | ||
1532 | NULL); | ||
1533 | */ | ||
1534 | |||
1535 | if(tsk_rt(my_queue->owner)->inh_task == max_hp) | ||
1536 | { | ||
1537 | __clear_priority_inheritance(my_queue->owner); | ||
1538 | if(my_queue->hp_waiter != NULL) | ||
1539 | { | ||
1540 | __set_priority_inheritance(my_queue->owner, my_queue->hp_waiter); | ||
1541 | } | ||
1542 | } | ||
1543 | raw_spin_unlock(&cluster->cluster_lock); | ||
1544 | |||
1545 | list_for_each(pos, &my_queue->wait.task_list) | ||
1546 | { | ||
1547 | queued = (struct task_struct*) list_entry(pos, wait_queue_t, | ||
1548 | task_list)->private; | ||
1549 | /* Compare task prios, find high prio task. */ | ||
1550 | if (queued == max_hp) | ||
1551 | { | ||
1552 | /* | ||
1553 | TRACE_CUR("queue %d: found entry in wait queue. REMOVING!\n", | ||
1554 | kfmlp_get_idx(sem, my_queue)); | ||
1555 | */ | ||
1556 | __remove_wait_queue(&my_queue->wait, | ||
1557 | list_entry(pos, wait_queue_t, task_list)); | ||
1558 | break; | ||
1559 | } | ||
1560 | } | ||
1561 | --(my_queue->count); | ||
1562 | } | ||
1563 | |||
1564 | return(max_hp); | ||
1565 | } | ||
1566 | |||
1567 | int cedf_kfmlp_lock(struct litmus_lock* l) | ||
1568 | { | ||
1569 | struct task_struct* t = current; | ||
1570 | struct kfmlp_semaphore *sem = kfmlp_from_lock(l); | ||
1571 | struct kfmlp_queue* my_queue; | ||
1572 | wait_queue_t wait; | ||
1573 | unsigned long flags; | ||
1574 | |||
1575 | if (!is_realtime(t)) | ||
1576 | return -EPERM; | ||
1577 | |||
1578 | spin_lock_irqsave(&sem->lock, flags); | ||
1579 | |||
1580 | my_queue = sem->shortest_queue; | ||
1581 | |||
1582 | if (my_queue->owner) { | ||
1583 | /* resource is not free => must suspend and wait */ | ||
1584 | TRACE_CUR("queue %d: Resource is not free => must suspend and wait.\n", | ||
1585 | kfmlp_get_idx(sem, my_queue)); | ||
1586 | |||
1587 | init_waitqueue_entry(&wait, t); | ||
1588 | |||
1589 | /* FIXME: interruptible would be nice some day */ | ||
1590 | set_task_state(t, TASK_UNINTERRUPTIBLE); | ||
1591 | |||
1592 | __add_wait_queue_tail_exclusive(&my_queue->wait, &wait); | ||
1593 | |||
1594 | /* check if we need to activate priority inheritance */ | ||
1595 | if (edf_higher_prio(t, my_queue->hp_waiter)) | ||
1596 | { | ||
1597 | my_queue->hp_waiter = t; | ||
1598 | if (edf_higher_prio(t, my_queue->owner)) | ||
1599 | { | ||
1600 | set_priority_inheritance(my_queue->owner, my_queue->hp_waiter); | ||
1601 | } | ||
1602 | } | ||
1603 | |||
1604 | ++(my_queue->count); | ||
1605 | sem->shortest_queue = kfmlp_find_shortest(sem, my_queue); | ||
1606 | |||
1607 | /* release lock before sleeping */ | ||
1608 | spin_unlock_irqrestore(&sem->lock, flags); | ||
1609 | |||
1610 | /* We depend on the FIFO order. Thus, we don't need to recheck | ||
1611 | * when we wake up; we are guaranteed to have the lock since | ||
1612 | * there is only one wake up per release (or steal). | ||
1613 | */ | ||
1614 | schedule(); | ||
1615 | |||
1616 | |||
1617 | if(my_queue->owner == t) | ||
1618 | { | ||
1619 | TRACE_CUR("queue %d: acquired through waiting\n", | ||
1620 | kfmlp_get_idx(sem, my_queue)); | ||
1621 | } | ||
1622 | else | ||
1623 | { | ||
1624 | /* this case may happen if our wait entry was stolen | ||
1625 | between queues. record where we went.*/ | ||
1626 | my_queue = kfmlp_get_queue(sem, t); | ||
1627 | BUG_ON(!my_queue); | ||
1628 | TRACE_CUR("queue %d: acquired through stealing\n", | ||
1629 | kfmlp_get_idx(sem, my_queue)); | ||
1630 | } | ||
1631 | } | ||
1632 | else | ||
1633 | { | ||
1634 | TRACE_CUR("queue %d: acquired immediately\n", | ||
1635 | kfmlp_get_idx(sem, my_queue)); | ||
1636 | |||
1637 | my_queue->owner = t; | ||
1638 | |||
1639 | ++(my_queue->count); | ||
1640 | sem->shortest_queue = kfmlp_find_shortest(sem, my_queue); | ||
1641 | |||
1642 | spin_unlock_irqrestore(&sem->lock, flags); | ||
1643 | } | ||
1644 | |||
1645 | return kfmlp_get_idx(sem, my_queue); | ||
1646 | } | ||
1647 | |||
1648 | int cedf_kfmlp_unlock(struct litmus_lock* l) | ||
1649 | { | ||
1650 | struct task_struct *t = current, *next; | ||
1651 | struct kfmlp_semaphore *sem = kfmlp_from_lock(l); | ||
1652 | struct kfmlp_queue *my_queue; | ||
1653 | unsigned long flags; | ||
1654 | int err = 0; | ||
1655 | |||
1656 | spin_lock_irqsave(&sem->lock, flags); | ||
1657 | |||
1658 | my_queue = kfmlp_get_queue(sem, t); | ||
1659 | |||
1660 | if (!my_queue) { | ||
1661 | err = -EINVAL; | ||
1662 | goto out; | ||
1663 | } | ||
1664 | |||
1665 | /* check if there are jobs waiting for this resource */ | ||
1666 | next = __waitqueue_remove_first(&my_queue->wait); | ||
1667 | if (next) { | ||
1668 | /* | ||
1669 | TRACE_CUR("queue %d: ASSIGNING %s/%d as owner - next\n", | ||
1670 | kfmlp_get_idx(sem, my_queue), | ||
1671 | next->comm, next->pid); | ||
1672 | */ | ||
1673 | /* next becomes the resouce holder */ | ||
1674 | my_queue->owner = next; | ||
1675 | |||
1676 | --(my_queue->count); | ||
1677 | if(my_queue->count < sem->shortest_queue->count) | ||
1678 | { | ||
1679 | sem->shortest_queue = my_queue; | ||
1680 | } | ||
1681 | |||
1682 | TRACE_CUR("queue %d: lock ownership passed to %s/%d\n", | ||
1683 | kfmlp_get_idx(sem, my_queue), next->comm, next->pid); | ||
1684 | |||
1685 | /* determine new hp_waiter if necessary */ | ||
1686 | if (next == my_queue->hp_waiter) { | ||
1687 | TRACE_TASK(next, "was highest-prio waiter\n"); | ||
1688 | /* next has the highest priority --- it doesn't need to | ||
1689 | * inherit. However, we need to make sure that the | ||
1690 | * next-highest priority in the queue is reflected in | ||
1691 | * hp_waiter. */ | ||
1692 | my_queue->hp_waiter = kfmlp_find_hp_waiter(my_queue, next); | ||
1693 | if (my_queue->hp_waiter) | ||
1694 | TRACE_TASK(my_queue->hp_waiter, "queue %d: is new highest-prio waiter\n", kfmlp_get_idx(sem, my_queue)); | ||
1695 | else | ||
1696 | TRACE("queue %d: no further waiters\n", kfmlp_get_idx(sem, my_queue)); | ||
1697 | } else { | ||
1698 | /* Well, if next is not the highest-priority waiter, | ||
1699 | * then it ought to inherit the highest-priority | ||
1700 | * waiter's priority. */ | ||
1701 | set_priority_inheritance(next, my_queue->hp_waiter); | ||
1702 | } | ||
1703 | |||
1704 | /* wake up next */ | ||
1705 | wake_up_process(next); | ||
1706 | } | ||
1707 | else | ||
1708 | { | ||
1709 | TRACE_CUR("queue %d: looking to steal someone...\n", kfmlp_get_idx(sem, my_queue)); | ||
1710 | |||
1711 | next = kfmlp_remove_hp_waiter(sem); /* returns NULL if nothing to steal */ | ||
1712 | |||
1713 | /* | ||
1714 | if(next) | ||
1715 | TRACE_CUR("queue %d: ASSIGNING %s/%d as owner - steal\n", | ||
1716 | kfmlp_get_idx(sem, my_queue), | ||
1717 | next->comm, next->pid); | ||
1718 | */ | ||
1719 | |||
1720 | my_queue->owner = next; | ||
1721 | |||
1722 | if(next) | ||
1723 | { | ||
1724 | TRACE_CUR("queue %d: lock ownership passed to %s/%d (which was stolen)\n", | ||
1725 | kfmlp_get_idx(sem, my_queue), | ||
1726 | next->comm, next->pid); | ||
1727 | |||
1728 | /* wake up next */ | ||
1729 | wake_up_process(next); | ||
1730 | } | ||
1731 | else | ||
1732 | { | ||
1733 | TRACE_CUR("queue %d: no one to steal.\n", kfmlp_get_idx(sem, my_queue)); | ||
1734 | |||
1735 | --(my_queue->count); | ||
1736 | if(my_queue->count < sem->shortest_queue->count) | ||
1737 | { | ||
1738 | sem->shortest_queue = my_queue; | ||
1739 | } | ||
1740 | } | ||
1741 | } | ||
1742 | |||
1743 | /* we lose the benefit of priority inheritance (if any) */ | ||
1744 | if (tsk_rt(t)->inh_task) | ||
1745 | clear_priority_inheritance(t); | ||
1746 | |||
1747 | out: | ||
1748 | spin_unlock_irqrestore(&sem->lock, flags); | ||
1749 | |||
1750 | return err; | ||
1751 | } | ||
1752 | |||
1753 | int cedf_kfmlp_close(struct litmus_lock* l) | ||
1754 | { | ||
1755 | struct task_struct *t = current; | ||
1756 | struct kfmlp_semaphore *sem = kfmlp_from_lock(l); | ||
1757 | struct kfmlp_queue *my_queue; | ||
1758 | unsigned long flags; | ||
1759 | |||
1760 | int owner; | ||
1761 | |||
1762 | spin_lock_irqsave(&sem->lock, flags); | ||
1763 | |||
1764 | my_queue = kfmlp_get_queue(sem, t); | ||
1765 | owner = (my_queue) ? (my_queue->owner == t) : 0; | ||
1766 | |||
1767 | spin_unlock_irqrestore(&sem->lock, flags); | ||
1768 | |||
1769 | if (owner) | ||
1770 | cedf_kfmlp_unlock(l); | ||
1771 | |||
1772 | return 0; | ||
1773 | } | ||
1774 | |||
1775 | void cedf_kfmlp_free(struct litmus_lock* l) | ||
1776 | { | ||
1777 | struct kfmlp_semaphore *sem = kfmlp_from_lock(l); | ||
1778 | kfree(sem->queues); | ||
1779 | kfree(sem); | ||
1780 | } | ||
1781 | |||
1782 | static struct litmus_lock_ops cedf_kfmlp_lock_ops = { | ||
1783 | .close = cedf_kfmlp_close, | ||
1784 | .lock = cedf_kfmlp_lock, | ||
1785 | .unlock = cedf_kfmlp_unlock, | ||
1786 | .deallocate = cedf_kfmlp_free, | ||
1787 | }; | ||
1788 | |||
1789 | static struct litmus_lock* cedf_new_kfmlp(void* __user arg, int* ret_code) | ||
1790 | { | ||
1791 | struct kfmlp_semaphore* sem; | ||
1792 | int num_resources = 0; | ||
1793 | int i; | ||
1794 | |||
1795 | if(!access_ok(VERIFY_READ, arg, sizeof(num_resources))) | ||
1796 | { | ||
1797 | *ret_code = -EINVAL; | ||
1798 | return(NULL); | ||
1799 | } | ||
1800 | if(__copy_from_user(&num_resources, arg, sizeof(num_resources))) | ||
1801 | { | ||
1802 | *ret_code = -EINVAL; | ||
1803 | return(NULL); | ||
1804 | } | ||
1805 | if(num_resources < 1) | ||
1806 | { | ||
1807 | *ret_code = -EINVAL; | ||
1808 | return(NULL); | ||
1809 | } | ||
1810 | |||
1811 | sem = kmalloc(sizeof(*sem), GFP_KERNEL); | ||
1812 | if(!sem) | ||
1813 | { | ||
1814 | *ret_code = -ENOMEM; | ||
1815 | return NULL; | ||
1816 | } | ||
1817 | |||
1818 | sem->queues = kmalloc(sizeof(struct kfmlp_queue)*num_resources, GFP_KERNEL); | ||
1819 | if(!sem->queues) | ||
1820 | { | ||
1821 | kfree(sem); | ||
1822 | *ret_code = -ENOMEM; | ||
1823 | return NULL; | ||
1824 | } | ||
1825 | |||
1826 | sem->litmus_lock.ops = &cedf_kfmlp_lock_ops; | ||
1827 | spin_lock_init(&sem->lock); | ||
1828 | sem->num_resources = num_resources; | ||
1829 | |||
1830 | for(i = 0; i < num_resources; ++i) | ||
1831 | { | ||
1832 | sem->queues[i].owner = NULL; | ||
1833 | sem->queues[i].hp_waiter = NULL; | ||
1834 | init_waitqueue_head(&sem->queues[i].wait); | ||
1835 | sem->queues[i].count = 0; | ||
1836 | } | ||
1837 | |||
1838 | sem->shortest_queue = &sem->queues[0]; | ||
1839 | |||
1840 | *ret_code = 0; | ||
1841 | return &sem->litmus_lock; | ||
1842 | } | ||
1843 | |||
1844 | |||
1845 | /* **** lock constructor **** */ | ||
1846 | |||
1847 | static long cedf_allocate_lock(struct litmus_lock **lock, int type, | ||
1848 | void* __user arg) | ||
1849 | { | ||
1850 | int err = -ENXIO; | ||
1851 | |||
1852 | /* C-EDF currently only supports the FMLP for global resources | ||
1853 | WITHIN a given cluster. DO NOT USE CROSS-CLUSTER! */ | ||
1854 | switch (type) { | ||
1855 | case KFMLP_SEM: | ||
1856 | *lock = cedf_new_kfmlp(arg, &err); | ||
1857 | break; | ||
1858 | }; | ||
1859 | |||
1860 | return err; | ||
1861 | } | ||
1862 | |||
1863 | #endif // CONFIG_LITMUS_LOCKING | ||
1864 | |||
1865 | |||
1866 | |||
1867 | |||
1868 | |||
1869 | |||
665 | /* total number of cluster */ | 1870 | /* total number of cluster */ |
666 | static int num_clusters; | 1871 | static int num_clusters; |
667 | /* we do not support cluster of different sizes */ | 1872 | /* we do not support cluster of different sizes */ |
@@ -755,6 +1960,13 @@ static long cedf_activate_plugin(void) | |||
755 | bheap_init(&(cedf[i].cpu_heap)); | 1960 | bheap_init(&(cedf[i].cpu_heap)); |
756 | edf_domain_init(&(cedf[i].domain), NULL, cedf_release_jobs); | 1961 | edf_domain_init(&(cedf[i].domain), NULL, cedf_release_jobs); |
757 | 1962 | ||
1963 | |||
1964 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
1965 | cedf[i].pending_tasklets.head = NULL; | ||
1966 | cedf[i].pending_tasklets.tail = &(cedf[i].pending_tasklets.head); | ||
1967 | #endif | ||
1968 | |||
1969 | |||
758 | if(!zalloc_cpumask_var(&cedf[i].cpu_map, GFP_ATOMIC)) | 1970 | if(!zalloc_cpumask_var(&cedf[i].cpu_map, GFP_ATOMIC)) |
759 | return -ENOMEM; | 1971 | return -ENOMEM; |
760 | #ifdef CONFIG_RELEASE_MASTER | 1972 | #ifdef CONFIG_RELEASE_MASTER |
@@ -812,6 +2024,40 @@ static long cedf_activate_plugin(void) | |||
812 | break; | 2024 | break; |
813 | } | 2025 | } |
814 | } | 2026 | } |
2027 | |||
2028 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
2029 | { | ||
2030 | /* distribute the daemons evenly across the clusters. */ | ||
2031 | int* affinity = kmalloc(NR_LITMUS_SOFTIRQD * sizeof(int), GFP_ATOMIC); | ||
2032 | int num_daemons_per_cluster = NR_LITMUS_SOFTIRQD / num_clusters; | ||
2033 | int left_over = NR_LITMUS_SOFTIRQD % num_clusters; | ||
2034 | |||
2035 | int daemon = 0; | ||
2036 | for(i = 0; i < num_clusters; ++i) | ||
2037 | { | ||
2038 | int num_on_this_cluster = num_daemons_per_cluster; | ||
2039 | if(left_over) | ||
2040 | { | ||
2041 | ++num_on_this_cluster; | ||
2042 | --left_over; | ||
2043 | } | ||
2044 | |||
2045 | for(j = 0; j < num_on_this_cluster; ++j) | ||
2046 | { | ||
2047 | // first CPU of this cluster | ||
2048 | affinity[daemon++] = i*cluster_size; | ||
2049 | } | ||
2050 | } | ||
2051 | |||
2052 | spawn_klitirqd(affinity); | ||
2053 | |||
2054 | kfree(affinity); | ||
2055 | } | ||
2056 | #endif | ||
2057 | |||
2058 | #ifdef CONFIG_LITMUS_NVIDIA | ||
2059 | init_nvidia_info(); | ||
2060 | #endif | ||
815 | 2061 | ||
816 | free_cpumask_var(mask); | 2062 | free_cpumask_var(mask); |
817 | clusters_allocated = 1; | 2063 | clusters_allocated = 1; |
@@ -831,6 +2077,19 @@ static struct sched_plugin cedf_plugin __cacheline_aligned_in_smp = { | |||
831 | .task_block = cedf_task_block, | 2077 | .task_block = cedf_task_block, |
832 | .admit_task = cedf_admit_task, | 2078 | .admit_task = cedf_admit_task, |
833 | .activate_plugin = cedf_activate_plugin, | 2079 | .activate_plugin = cedf_activate_plugin, |
2080 | #ifdef CONFIG_LITMUS_LOCKING | ||
2081 | .allocate_lock = cedf_allocate_lock, | ||
2082 | .set_prio_inh = set_priority_inheritance, | ||
2083 | .clear_prio_inh = clear_priority_inheritance, | ||
2084 | #endif | ||
2085 | #ifdef CONFIG_LITMUS_SOFTIRQD | ||
2086 | .set_prio_inh_klitirqd = set_priority_inheritance_klitirqd, | ||
2087 | .clear_prio_inh_klitirqd = clear_priority_inheritance_klitirqd, | ||
2088 | #endif | ||
2089 | #ifdef CONFIG_LITMUS_PAI_SOFTIRQD | ||
2090 | .enqueue_pai_tasklet = enqueue_pai_tasklet, | ||
2091 | .run_tasklets = run_tasklets, | ||
2092 | #endif | ||
834 | }; | 2093 | }; |
835 | 2094 | ||
836 | static struct proc_dir_entry *cluster_file = NULL, *cedf_dir = NULL; | 2095 | static struct proc_dir_entry *cluster_file = NULL, *cedf_dir = NULL; |