aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@cs.unc.edu>2011-07-26 21:12:16 -0400
committerBjoern B. Brandenburg <bbb@cs.unc.edu>2011-07-26 21:12:16 -0400
commita8defdf2a0a970dbf6a3facee6e5fcf468649195 (patch)
treef5c06cc9062a3a835300910db8e04ad94fb6ef2f
parentc06966f73c0ce3bfff605e4bbc15ab6453176c8d (diff)
GSN-EDF: add global OMLP support
Similar to the FMLP implementation, the only difference is that it uses a hybrid FIFO/priority queue.
-rw-r--r--litmus/sched_gsn_edf.c316
1 files changed, 308 insertions, 8 deletions
diff --git a/litmus/sched_gsn_edf.c b/litmus/sched_gsn_edf.c
index a0f3ba54ed8f..9e3eb22eed74 100644
--- a/litmus/sched_gsn_edf.c
+++ b/litmus/sched_gsn_edf.c
@@ -610,14 +610,14 @@ static long gsnedf_admit_task(struct task_struct* tsk)
610 610
611#include <litmus/fdso.h> 611#include <litmus/fdso.h>
612 612
613
614
613/* called with IRQs off */ 615/* called with IRQs off */
614static void set_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh) 616static void __set_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh)
615{ 617{
616 int linked_on; 618 int linked_on;
617 int check_preempt = 0; 619 int check_preempt = 0;
618 620
619 raw_spin_lock(&gsnedf_lock);
620
621 TRACE_TASK(t, "inherits priority from %s/%d\n", prio_inh->comm, prio_inh->pid); 621 TRACE_TASK(t, "inherits priority from %s/%d\n", prio_inh->comm, prio_inh->pid);
622 tsk_rt(t)->inh_task = prio_inh; 622 tsk_rt(t)->inh_task = prio_inh;
623 623
@@ -642,7 +642,7 @@ static void set_priority_inheritance(struct task_struct* t, struct task_struct*
642 TRACE_TASK(t, "%s: is queued\n", 642 TRACE_TASK(t, "%s: is queued\n",
643 __FUNCTION__); 643 __FUNCTION__);
644 /* We need to update the position of holder in some 644 /* We need to update the position of holder in some
645 * heap. Note that this could be a release heap if we 645 * heap. Note that this could be a release heap if
646 * budget enforcement is used and this job overran. */ 646 * budget enforcement is used and this job overran. */
647 check_preempt = 647 check_preempt =
648 !bheap_decrease(edf_ready_order, 648 !bheap_decrease(edf_ready_order,
@@ -673,15 +673,17 @@ static void set_priority_inheritance(struct task_struct* t, struct task_struct*
673 check_for_preemptions(); 673 check_for_preemptions();
674 } 674 }
675 } 675 }
676
677 raw_spin_unlock(&gsnedf_lock);
678} 676}
679 677
680/* called with IRQs off */ 678static void set_priority_inheritance(struct task_struct* t, struct task_struct* prio_inh)
681static void clear_priority_inheritance(struct task_struct* t)
682{ 679{
683 raw_spin_lock(&gsnedf_lock); 680 raw_spin_lock(&gsnedf_lock);
681 __set_priority_inheritance(t, prio_inh);
682 raw_spin_unlock(&gsnedf_lock);
683}
684 684
685static void __clear_priority_inheritance(struct task_struct* t)
686{
685 /* A job only stops inheriting a priority when it releases a 687 /* A job only stops inheriting a priority when it releases a
686 * resource. Thus we can make the following assumption.*/ 688 * resource. Thus we can make the following assumption.*/
687 BUG_ON(tsk_rt(t)->scheduled_on == NO_CPU); 689 BUG_ON(tsk_rt(t)->scheduled_on == NO_CPU);
@@ -693,6 +695,35 @@ static void clear_priority_inheritance(struct task_struct* t)
693 * since the priority was effectively lowered. */ 695 * since the priority was effectively lowered. */
694 unlink(t); 696 unlink(t);
695 gsnedf_job_arrival(t); 697 gsnedf_job_arrival(t);
698}
699
700/* called with IRQs off */
701static void clear_priority_inheritance(struct task_struct* t)
702{
703 raw_spin_lock(&gsnedf_lock);
704 __clear_priority_inheritance(t);
705 raw_spin_unlock(&gsnedf_lock);
706}
707
708/* set and clear at the same time to avoid having to
709 * acquire the runqueue lock twice */
710static void update_priority_inheritance(
711 struct task_struct* deprived,
712 struct task_struct* blocker,
713 struct task_struct* blocked)
714{
715 /* things to do:
716 * 1) deprived no longer inherits anything.
717 * 2) blocker gets blocked's priority.
718 */
719
720 raw_spin_lock(&gsnedf_lock);
721
722 if (tsk_rt(deprived)->inh_task)
723 __clear_priority_inheritance(deprived);
724
725 if (blocked)
726 __set_priority_inheritance(blocker, blocked);
696 727
697 raw_spin_unlock(&gsnedf_lock); 728 raw_spin_unlock(&gsnedf_lock);
698} 729}
@@ -898,6 +929,266 @@ static struct litmus_lock* gsnedf_new_fmlp(void)
898 return &sem->litmus_lock; 929 return &sem->litmus_lock;
899} 930}
900 931
932
933/* ******************** OMLP support ********************** */
934
935/* struct for semaphore with priority inheritance */
936struct omlp_semaphore {
937 struct litmus_lock litmus_lock;
938
939 /* current resource holder */
940 struct task_struct *owner;
941
942 /* highest-priority waiter */
943 struct task_struct *hp_waiter;
944
945 /* FIFO queue of waiting tasks */
946 wait_queue_head_t fifo_wait;
947 /* Priority queue of waiting tasks */
948 wait_queue_head_t prio_wait;
949
950 /* How many slots remaining in FIFO queue? */
951 unsigned int num_free;
952};
953
954static inline struct omlp_semaphore* omlp_from_lock(struct litmus_lock* lock)
955{
956 return container_of(lock, struct omlp_semaphore, litmus_lock);
957}
958
959/* already locked */
960static void omlp_enqueue(struct omlp_semaphore *sem, prio_wait_queue_t* wait)
961{
962 if (sem->num_free) {
963 /* there is space in the FIFO queue */
964 sem->num_free--;
965 __add_wait_queue_tail_exclusive(&sem->fifo_wait, &wait->wq);
966 } else {
967 /* nope, gotta go to the priority queue */
968 __add_wait_queue_prio_exclusive(&sem->prio_wait, wait);
969 }
970}
971
972/* already locked */
973static int omlp_move(struct omlp_semaphore *sem)
974{
975 struct list_head* first;
976
977 if (waitqueue_active(&sem->prio_wait)) {
978 first = sem->prio_wait.task_list.next;
979 list_move_tail(first, &sem->fifo_wait.task_list);
980 return 1;
981 }
982 else
983 return 0;
984}
985
986static struct task_struct* omlp_dequeue(struct omlp_semaphore *sem)
987{
988 struct task_struct* first = __waitqueue_remove_first(&sem->fifo_wait);
989
990 if (first && !omlp_move(sem))
991 sem->num_free++;
992
993 return first;
994}
995
996/* caller is responsible for locking */
997static struct task_struct* omlp_find_hp_waiter(struct omlp_semaphore *sem,
998 struct task_struct* skip)
999{
1000 struct list_head *pos;
1001 struct task_struct *queued, *found = NULL;
1002
1003 /* check FIFO queue first */
1004 list_for_each(pos, &sem->fifo_wait.task_list) {
1005 queued = (struct task_struct*) list_entry(pos, wait_queue_t,
1006 task_list)->private;
1007
1008 /* Compare task prios, find high prio task. */
1009 if (queued != skip && edf_higher_prio(queued, found))
1010 found = queued;
1011 }
1012
1013 /* check priority queue next */
1014 if (waitqueue_active(&sem->prio_wait)) {
1015 /* first has highest priority */
1016 pos = sem->prio_wait.task_list.next;
1017 queued = (struct task_struct*) list_entry(pos, wait_queue_t,
1018 task_list)->private;
1019 if (edf_higher_prio(queued, found))
1020 found = queued;
1021 }
1022
1023 return found;
1024}
1025
1026int gsnedf_omlp_lock(struct litmus_lock* l)
1027{
1028 struct task_struct* t = current;
1029 struct omlp_semaphore *sem = omlp_from_lock(l);
1030 prio_wait_queue_t wait;
1031 unsigned long flags;
1032
1033 if (!is_realtime(t))
1034 return -EPERM;
1035
1036 spin_lock_irqsave(&sem->fifo_wait.lock, flags);
1037
1038 if (sem->owner) {
1039 /* resource is not free => must suspend and wait */
1040
1041 init_prio_waitqueue_entry(&wait, t, get_deadline(t));
1042
1043 set_task_state(t, TASK_UNINTERRUPTIBLE);
1044
1045 omlp_enqueue(sem, &wait);
1046
1047 /* check if we need to activate priority inheritance */
1048 if (edf_higher_prio(t, sem->hp_waiter)) {
1049 sem->hp_waiter = t;
1050 if (edf_higher_prio(t, sem->owner))
1051 set_priority_inheritance(sem->owner, sem->hp_waiter);
1052 }
1053
1054 TS_LOCK_SUSPEND;
1055
1056 /* release lock before sleeping */
1057 spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
1058
1059 schedule();
1060
1061 TS_LOCK_RESUME;
1062
1063 /* Since we hold the lock, no other task will change
1064 * ->owner. We can thus check it without acquiring the spin
1065 * lock. */
1066 BUG_ON(sem->owner != t);
1067 } else {
1068 /* it's ours now */
1069 sem->owner = t;
1070
1071 spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
1072 }
1073
1074 return 0;
1075}
1076
1077static int gsnedf_omlp_unlock(struct litmus_lock* l)
1078{
1079 struct task_struct *t = current, *next, *blocked = NULL;
1080 struct omlp_semaphore *sem = omlp_from_lock(l);
1081 unsigned long flags;
1082 int err = 0;
1083
1084 spin_lock_irqsave(&sem->fifo_wait.lock, flags);
1085
1086 if (sem->owner != t) {
1087 err = -EINVAL;
1088 goto out;
1089 }
1090
1091 /* check if there are jobs waiting for this resource */
1092 next = omlp_dequeue(sem);
1093 if (next) {
1094 /* next becomes the resouce holder */
1095 sem->owner = next;
1096 TRACE_CUR("lock ownership passed to %s/%d\n", next->comm, next->pid);
1097
1098 /* determine new hp_waiter if necessary */
1099 if (next == sem->hp_waiter) {
1100 TRACE_TASK(next, "was highest-prio waiter\n");
1101 /* next has the highest priority --- it doesn't need to
1102 * inherit. However, we need to make sure that the
1103 * next-highest priority in the queue is reflected in
1104 * hp_waiter. */
1105 sem->hp_waiter = omlp_find_hp_waiter(sem, next);
1106 if (sem->hp_waiter)
1107 TRACE_TASK(sem->hp_waiter, "is new highest-prio waiter\n");
1108 else
1109 TRACE("no further waiters\n");
1110 } else {
1111 /* Well, if next is not the highest-priority waiter,
1112 * then it ought to inherit the highest-priority
1113 * waiter's priority. */
1114 blocked = sem->hp_waiter;
1115 }
1116
1117 /* wake up next */
1118 wake_up_process(next);
1119 } else
1120 /* becomes available */
1121 sem->owner = NULL;
1122
1123 /* we lose the benefit of priority inheritance (if any) */
1124 if (tsk_rt(t)->inh_task || blocked)
1125 update_priority_inheritance(t, next, blocked);
1126
1127out:
1128 spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
1129
1130 return err;
1131}
1132
1133static int gsnedf_omlp_close(struct litmus_lock* l)
1134{
1135 struct task_struct *t = current;
1136 struct omlp_semaphore *sem = omlp_from_lock(l);
1137 unsigned long flags;
1138
1139 int owner;
1140
1141 spin_lock_irqsave(&sem->fifo_wait.lock, flags);
1142
1143 owner = sem->owner == t;
1144
1145 spin_unlock_irqrestore(&sem->fifo_wait.lock, flags);
1146
1147 if (owner)
1148 gsnedf_omlp_unlock(l);
1149
1150 return 0;
1151}
1152
1153static void gsnedf_omlp_free(struct litmus_lock* lock)
1154{
1155 kfree(omlp_from_lock(lock));
1156}
1157
1158static struct litmus_lock_ops gsnedf_omlp_lock_ops = {
1159 .close = gsnedf_omlp_close,
1160 .lock = gsnedf_omlp_lock,
1161 .unlock = gsnedf_omlp_unlock,
1162 .deallocate = gsnedf_omlp_free,
1163};
1164
1165static struct litmus_lock* gsnedf_new_omlp(void)
1166{
1167 struct omlp_semaphore* sem;
1168
1169 sem = kmalloc(sizeof(*sem), GFP_KERNEL);
1170 if (!sem)
1171 return NULL;
1172
1173 sem->owner = NULL;
1174 sem->hp_waiter = NULL;
1175 init_waitqueue_head(&sem->fifo_wait);
1176 init_waitqueue_head(&sem->prio_wait);
1177 sem->litmus_lock.ops = &gsnedf_omlp_lock_ops;
1178 /* free = cpus -1 since ->owner is the head and also counted */
1179 sem->num_free = num_online_cpus() - 1;
1180
1181#ifdef CONFIG_RELEASE_MASTER
1182 /* If we use dedicated interrupt handling, then there are actually
1183 * only m - 1 CPUs around. */
1184 if (gsnedf.release_master != NO_CPU)
1185 sem->num_free -= 1;
1186#endif
1187
1188 return &sem->litmus_lock;
1189}
1190
1191
901/* **** lock constructor **** */ 1192/* **** lock constructor **** */
902 1193
903 1194
@@ -918,6 +1209,15 @@ static long gsnedf_allocate_lock(struct litmus_lock **lock, int type,
918 err = -ENOMEM; 1209 err = -ENOMEM;
919 break; 1210 break;
920 1211
1212 case OMLP_SEM:
1213 /* O(m) Multiprocessor Locking Protocol */
1214 *lock = gsnedf_new_omlp();
1215 if (*lock)
1216 err = 0;
1217 else
1218 err = -ENOMEM;
1219 break;
1220
921 }; 1221 };
922 1222
923 return err; 1223 return err;