diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2010-07-12 15:25:40 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2010-08-03 12:42:59 -0400 |
commit | cf08bf1004f77f25569fa7a6f9fbe3d18e9d98aa (patch) | |
tree | 6e7f944df756033465b8e2389fde850dcb03e31d | |
parent | a0c6241d44317b55c790131d3215d52061ce4add (diff) |
Implement the pi semaphore stack.
This patch continues the work to implement a generic pi framework. Under this
framework, each task_struct maintains a stack of held locks, in the order they
are acquired. This stack is mainly used to determine the proper inheritance
when a lock is released. On release, the releasing job iterates through its
semaphore stack to determine if it blocks a higher priority job and inherits
the proper priority as needed. The stack is needed since a blocked job cannot
push this information to the lock holder because the inheritance may not take
place immediately when the job is blocked. Stack operations include push, pop,
update, and peek.
The semaphore stack is really only useful to protocols that support
traditional nesting. Protocols that do not use traditional nesting, such
as FMLP-Long, do not need a stack to track inheritance. So, struct rt_param
now also has a task_struct pointer, eff_priority. eff_priority is used in
almost the same way was rt_param.inh_task was used in the original FMLP
implementation. The slight difference is that eff_priority is set to equal
to self when a job acquires a semaphore with no inheritance. This is
stylistically similar to FMLP using a semaphore stack. Note that FMLP-Long
completely forgoes the use of semaphore stack and only uses eff_priority.
Finally, it is intended that eff_priority also be used with nested protocols.
A job using a nested protocol would set eff_priority equal to the highest
priority received from all held locks. Thus, eff_priority caches the priority
a job inherits (the semaphore stack does not need to be iterated through each
time the job's priority is required).
-rw-r--r-- | include/litmus/edf_common.h | 8 | ||||
-rw-r--r-- | include/litmus/prio_sem.h | 31 | ||||
-rw-r--r-- | include/litmus/rt_param.h | 35 | ||||
-rw-r--r-- | litmus/Makefile | 2 | ||||
-rw-r--r-- | litmus/edf_common.c | 19 | ||||
-rw-r--r-- | litmus/fmlp.c | 2 | ||||
-rw-r--r-- | litmus/prio_sem.c | 183 |
7 files changed, 265 insertions, 15 deletions
diff --git a/include/litmus/edf_common.h b/include/litmus/edf_common.h index 80d4321cc87e..b6081553d901 100644 --- a/include/litmus/edf_common.h +++ b/include/litmus/edf_common.h | |||
@@ -22,6 +22,10 @@ int edf_ready_order(struct bheap_node* a, struct bheap_node* b); | |||
22 | 22 | ||
23 | int edf_preemption_needed(rt_domain_t* rt, struct task_struct *t); | 23 | int edf_preemption_needed(rt_domain_t* rt, struct task_struct *t); |
24 | 24 | ||
25 | int edf_set_hp_task(struct pi_semaphore *sem); | 25 | #ifdef CONFIG_FMLP |
26 | int edf_set_hp_cpu_task(struct pi_semaphore *sem, int cpu); | 26 | int edf_set_hp_task(struct fmlp_semaphore *sem); |
27 | int edf_set_hp_cpu_task(struct fmlp_semaphore *sem, int cpu); | ||
27 | #endif | 28 | #endif |
29 | |||
30 | #endif | ||
31 | |||
diff --git a/include/litmus/prio_sem.h b/include/litmus/prio_sem.h new file mode 100644 index 000000000000..eb54c670947a --- /dev/null +++ b/include/litmus/prio_sem.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* Operations and structures for dealing with generic priority inheritance. */ | ||
2 | |||
3 | #ifndef _LINUX_PI_H_ | ||
4 | #include <linux/sched.h> | ||
5 | |||
6 | /* forward declaration */ | ||
7 | struct pi_semaphore; | ||
8 | |||
9 | /* stack operations */ | ||
10 | void push_pi_sem( | ||
11 | struct task_struct *tsk, | ||
12 | struct task_struct *inh, | ||
13 | struct pi_semaphore *sem); | ||
14 | |||
15 | void pop_pi_sem(struct task_struct *tsk); | ||
16 | |||
17 | pi_sem_record_t* peek_pi_sem(struct task_struct *tsk); | ||
18 | |||
19 | /* non-stack operations */ | ||
20 | int update_pi_sem( | ||
21 | struct task_struct *tsk, | ||
22 | struct task_struct *inh, | ||
23 | struct pi_semaphore *sem); | ||
24 | |||
25 | int remove_pi_sem( | ||
26 | struct task_struct *tsk, | ||
27 | struct pi_semaphore *sem); | ||
28 | |||
29 | int has_pi_sem(struct task_struct *tsk); | ||
30 | |||
31 | #endif | ||
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index 6f40b52f0802..9e2f06951d4b 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
@@ -102,7 +102,7 @@ struct rt_job { | |||
102 | * acquired. | 102 | * acquired. |
103 | */ | 103 | */ |
104 | struct pi_sem_record { | 104 | struct pi_sem_record { |
105 | /* Pointer to inheritance-donating job. | 105 | /* Task from which task inherits. |
106 | * May be set to "self" if there is no active | 106 | * May be set to "self" if there is no active |
107 | * inheritance from this semaphore. | 107 | * inheritance from this semaphore. |
108 | */ | 108 | */ |
@@ -136,13 +136,34 @@ struct rt_param { | |||
136 | /* timing parameters */ | 136 | /* timing parameters */ |
137 | struct rt_job job_params; | 137 | struct rt_job job_params; |
138 | 138 | ||
139 | /* task representing the current "inherited" task | 139 | #ifdef CONFIG_PI_SEMAPHORES |
140 | * priority, assigned by inherit_priority and | 140 | /* Stack of tasks representing the inheritance from |
141 | * return priority in the scheduler plugins. | 141 | * nested priority inheriting semaphores. Also provides |
142 | * could point to self if PI does not result in | 142 | * a list of currently held pi-inheriting semaphores. |
143 | * an increased task priority. | 143 | * |
144 | * End of List = Stack Top | ||
145 | * | ||
146 | * If a semaphore provides no inheritance, then record | ||
147 | * pointing to self should be pushed onto the stack (so | ||
148 | * inheritance record may be updated at a later time | ||
149 | * if a higher-priority job blocks). | ||
150 | * | ||
151 | * Other protocols may require additional information if | ||
152 | * a task can inherit multiple parameters from several other | ||
153 | * tasks. | ||
144 | */ | 154 | */ |
145 | struct task_struct* inh_task; | 155 | struct list_head pi_sem_stack; |
156 | |||
157 | /* It is useful to cache the highest priority inherited from | ||
158 | * all held locks (as recored by pi_sem_stack). eff_priority | ||
159 | * is this cache. Further, a protocol may not use traditional | ||
160 | * nesting, such as FMLP-Long. Such protocols may forgo the | ||
161 | * the use of pi_sem_stack and use eff_priority to track inheritance | ||
162 | * alone. | ||
163 | */ | ||
164 | struct task_struct* eff_priority; | ||
165 | #endif | ||
166 | |||
146 | 167 | ||
147 | #ifdef CONFIG_NP_SECTION | 168 | #ifdef CONFIG_NP_SECTION |
148 | /* For the FMLP under PSN-EDF, it is required to make the task | 169 | /* For the FMLP under PSN-EDF, it is required to make the task |
diff --git a/litmus/Makefile b/litmus/Makefile index 30369787ece2..1cf2b6267ae3 100644 --- a/litmus/Makefile +++ b/litmus/Makefile | |||
@@ -18,6 +18,8 @@ obj-y = sched_plugin.o litmus.o \ | |||
18 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o | 18 | obj-$(CONFIG_PLUGIN_CEDF) += sched_cedf.o |
19 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o | 19 | obj-$(CONFIG_PLUGIN_PFAIR) += sched_pfair.o |
20 | 20 | ||
21 | obj-$(CONFIG_PI_SEMAPHORES) += prio_sem.o | ||
22 | |||
21 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o | 23 | obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o |
22 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o | 24 | obj-$(CONFIG_SCHED_TASK_TRACE) += sched_task_trace.o |
23 | obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o | 25 | obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o |
diff --git a/litmus/edf_common.c b/litmus/edf_common.c index 06daec66c984..1e91848a71dd 100644 --- a/litmus/edf_common.c +++ b/litmus/edf_common.c | |||
@@ -18,6 +18,12 @@ | |||
18 | * than second. Deadline ties are broken by PID. | 18 | * than second. Deadline ties are broken by PID. |
19 | * | 19 | * |
20 | * both first and second may be NULL | 20 | * both first and second may be NULL |
21 | * | ||
22 | * WARNING: Assumes rt_param.eff_priority holds the effective | ||
23 | * priority if PI semaphores are in use. PI protocols should | ||
24 | * cache the highest priority received through all held locks | ||
25 | * in rt_param.eff_priority (or use a different edf_higher_prio() | ||
26 | * function). | ||
21 | */ | 27 | */ |
22 | int edf_higher_prio(struct task_struct* first, | 28 | int edf_higher_prio(struct task_struct* first, |
23 | struct task_struct* second) | 29 | struct task_struct* second) |
@@ -32,14 +38,15 @@ int edf_higher_prio(struct task_struct* first, | |||
32 | return 0; | 38 | return 0; |
33 | } | 39 | } |
34 | 40 | ||
35 | 41 | #ifdef CONFIG_PI_SEMAPHORES | |
36 | /* Check for inherited priorities. Change task | 42 | /* Check for inherited priorities. Change task |
37 | * used for comparison in such a case. | 43 | * used for comparison in such a case. |
38 | */ | 44 | */ |
39 | if (first && first->rt_param.inh_task) | 45 | if (first && (first->rt_param.eff_priority != first)) |
40 | first_task = first->rt_param.inh_task; | 46 | first_task = first->rt_param.eff_priority; |
41 | if (second && second->rt_param.inh_task) | 47 | if (second && (second->rt_param.eff_priority != second)) |
42 | second_task = second->rt_param.inh_task; | 48 | second_task = second->rt_param.eff_priority; |
49 | #endif | ||
43 | 50 | ||
44 | return | 51 | return |
45 | /* it has to exist in order to have higher priority */ | 52 | /* it has to exist in order to have higher priority */ |
@@ -65,7 +72,7 @@ int edf_higher_prio(struct task_struct* first, | |||
65 | * priority wins. | 72 | * priority wins. |
66 | */ | 73 | */ |
67 | (first_task->pid == second_task->pid && | 74 | (first_task->pid == second_task->pid && |
68 | !second->rt_param.inh_task)))); | 75 | (second == second_task))))); |
69 | } | 76 | } |
70 | 77 | ||
71 | int edf_ready_order(struct bheap_node* a, struct bheap_node* b) | 78 | int edf_ready_order(struct bheap_node* a, struct bheap_node* b) |
diff --git a/litmus/fmlp.c b/litmus/fmlp.c index de46e626f43f..36965efe37e5 100644 --- a/litmus/fmlp.c +++ b/litmus/fmlp.c | |||
@@ -36,8 +36,10 @@ static void* create_fmlp_semaphore(void) | |||
36 | for (i = 0; i < NR_CPUS; i++) | 36 | for (i = 0; i < NR_CPUS; i++) |
37 | sem->pi.hp.cpu_task[i] = NULL; | 37 | sem->pi.hp.cpu_task[i] = NULL; |
38 | 38 | ||
39 | /* FMLP-Long does not use semaphore stack. | ||
39 | sem->pi.stack_node.inh_task = NULL; | 40 | sem->pi.stack_node.inh_task = NULL; |
40 | INIT_LIST_HEAD(&sem->pi.stack_node.list); | 41 | INIT_LIST_HEAD(&sem->pi.stack_node.list); |
42 | */ | ||
41 | 43 | ||
42 | return sem; | 44 | return sem; |
43 | } | 45 | } |
diff --git a/litmus/prio_sem.c b/litmus/prio_sem.c new file mode 100644 index 000000000000..3c436656a8b4 --- /dev/null +++ b/litmus/prio_sem.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* Operations and structures for dealing with generic priority inheritance. */ | ||
2 | |||
3 | #include <litmus/litmus.h> | ||
4 | #include <litmus/prio_sem.h> | ||
5 | |||
6 | #include <litmus/sched_plugin.h> | ||
7 | |||
8 | /* Add a record to the pi sem history stack. | ||
9 | * | ||
10 | * Recommend that inh == tsk for "no inheritance", though this | ||
11 | * is up to the pi sem protocol implementation. inh could be | ||
12 | * NULL as long as the protocol implementation expects it. | ||
13 | */ | ||
14 | void push_pi_sem( | ||
15 | struct task_struct *tsk, | ||
16 | struct task_struct *inh, | ||
17 | struct pi_semaphore *sem) | ||
18 | { | ||
19 | sem->stack_node.inh_task = inh; | ||
20 | |||
21 | /* list_add() needs node to be initialized properly. Node may | ||
22 | * not be initialized yet. | ||
23 | */ | ||
24 | INIT_LIST_HEAD(&sem->stack_node.list); | ||
25 | |||
26 | list_add_tail(&tsk->rt_param.pi_sem_stack, &sem->stack_node.list); | ||
27 | |||
28 | TRACE_TASK(tsk, | ||
29 | "Pushed semaphore %p with inheritance from task %d\n", | ||
30 | sem, inh->pid); | ||
31 | } | ||
32 | |||
33 | /* Removes the last semaphore record. */ | ||
34 | void pop_pi_sem(struct task_struct *tsk) | ||
35 | { | ||
36 | int empty = list_empty(&tsk->rt_param.pi_sem_stack); | ||
37 | |||
38 | WARN_ON(empty); | ||
39 | |||
40 | if(!empty) | ||
41 | { | ||
42 | pi_sem_record_t *rec = | ||
43 | list_entry(tsk->rt_param.pi_sem_stack.prev, pi_sem_record_t, list); | ||
44 | list_del(&rec->list); | ||
45 | |||
46 | TRACE_TASK(tsk, "Popped sem/inh record %p / %d.\n", | ||
47 | container_of(rec, struct pi_semaphore, stack_node), | ||
48 | rec->inh_task->pid); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* Get a pointer to the last semaphore pushed on to the stack. | ||
53 | * Returns NULL if there is no record. | ||
54 | */ | ||
55 | pi_sem_record_t* peek_pi_sem(struct task_struct *tsk) | ||
56 | { | ||
57 | if(!list_empty(&tsk->rt_param.pi_sem_stack)) | ||
58 | { | ||
59 | pi_sem_record_t *rec = | ||
60 | list_entry(tsk->rt_param.pi_sem_stack.prev, | ||
61 | pi_sem_record_t, list); | ||
62 | return rec; | ||
63 | } | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | |||
68 | /* Update priority inheritance. */ | ||
69 | int update_pi_sem( | ||
70 | struct task_struct *tsk, | ||
71 | struct task_struct *inh, | ||
72 | struct pi_semaphore *sem) | ||
73 | { | ||
74 | int success = 0; | ||
75 | struct list_head *pos, *next; | ||
76 | pi_sem_record_t *rec; | ||
77 | |||
78 | /* Though we have a reference to the semaphore (sem), we need | ||
79 | * to search through tsk's stack to validate that tsk actually | ||
80 | * holds sem. If tsk holds sem, then sem will be in its stack. | ||
81 | * | ||
82 | * TODO: Add function pointer hooks to pi_semaphore for operations | ||
83 | * such as "int is_holder(struct task_struct *tsk)". pi_semaphore | ||
84 | * does not track its holder(s), though concrete protocols may, | ||
85 | * providing more efficient validation methods than this stack search. | ||
86 | * Another function may be "pi_sem_record_t* get_record(...)" if the | ||
87 | * semaphore protocol supports multiple holders (though | ||
88 | * pi_semaphore.stack_node will probably go unused or replaced in | ||
89 | * such cases). | ||
90 | */ | ||
91 | |||
92 | /* Assuming a sane locking order, the lock we're looking for | ||
93 | * is probably towards (at) the end. | ||
94 | */ | ||
95 | list_for_each_prev_safe(pos, next, &tsk->rt_param.pi_sem_stack) | ||
96 | { | ||
97 | struct pi_semaphore* which_sem; | ||
98 | rec = list_entry(pos, pi_sem_record_t, list); | ||
99 | |||
100 | which_sem = container_of(rec, struct pi_semaphore, stack_node); | ||
101 | if(which_sem == sem) | ||
102 | { | ||
103 | TRACE_TASK(tsk, "Updating inheritance from %d to %d on sem %p\n", | ||
104 | rec->inh_task->pid, inh->pid, sem); | ||
105 | |||
106 | rec->inh_task = inh; | ||
107 | success = 1; | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | if(!success) | ||
113 | { | ||
114 | TRACE_TASK(tsk, "Could not find record for sem %p to update inheritance.\n", | ||
115 | sem); | ||
116 | } | ||
117 | |||
118 | return success; | ||
119 | } | ||
120 | |||
121 | /* Removes a record from the semaphore stack if it exists. | ||
122 | * | ||
123 | * Note: pop_pi_sem() may be more appropriate if total ordering can be | ||
124 | * guaranteed. | ||
125 | */ | ||
126 | int remove_pi_sem(struct task_struct *tsk, struct pi_semaphore *sem) | ||
127 | { | ||
128 | int success = 0; | ||
129 | struct list_head *pos, *next; | ||
130 | pi_sem_record_t *rec; | ||
131 | |||
132 | /* Though we have a reference to the semaphore (sem), we need | ||
133 | * to search through tsk's stack to validate that tsk actually | ||
134 | * holds sem. If tsk holds sem, then sem will be in its stack. | ||
135 | * | ||
136 | * TODO: Add function pointer hooks to pi_semaphore for operations | ||
137 | * such as "int is_holder(struct task_struct *tsk)". pi_semaphore | ||
138 | * does not track its holder(s), though concrete protocols may, | ||
139 | * providing more efficient validation methods than this stack search. | ||
140 | * Another function may be "pi_sem_record_t* get_record(...)" if the | ||
141 | * semaphore protocol supports multiple holders (though | ||
142 | * pi_semaphore.stack_node will probably go unused or replaced in | ||
143 | * such cases). | ||
144 | */ | ||
145 | |||
146 | /* Assuming a sane locking order, the lock we're looking for | ||
147 | * is probably towards (at) the end. | ||
148 | */ | ||
149 | list_for_each_prev_safe(pos, next, &tsk->rt_param.pi_sem_stack) | ||
150 | { | ||
151 | struct pi_semaphore* which_sem; | ||
152 | rec = list_entry(pos, pi_sem_record_t, list); | ||
153 | |||
154 | which_sem = container_of(rec, struct pi_semaphore, stack_node); | ||
155 | if(which_sem == sem) | ||
156 | { | ||
157 | TRACE_TASK(tsk, "Removing semaphore stack record from %d with sem %p\n", | ||
158 | rec->inh_task->pid, sem); | ||
159 | |||
160 | list_del(&rec->list); | ||
161 | success = 1; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | if(!success) | ||
167 | { | ||
168 | TRACE_TASK(tsk, "Could not find record for sem %p to remove.\n", | ||
169 | sem); | ||
170 | } | ||
171 | |||
172 | return success; | ||
173 | } | ||
174 | |||
175 | /* Returns true of tsk holds ANY pi semaphores (as reportable by the stack). | ||
176 | * Function will return false if the protocol in use does not use the stack, | ||
177 | * such as FMLP-Long, which uses rt_params.eff_priority. | ||
178 | */ | ||
179 | int has_pi_sem(struct task_struct *tsk) | ||
180 | { | ||
181 | return !list_empty(&tsk->rt_param.pi_sem_stack); | ||
182 | } | ||
183 | |||