aboutsummaryrefslogtreecommitdiffstats
path: root/litmus/srp.c
diff options
context:
space:
mode:
Diffstat (limited to 'litmus/srp.c')
-rw-r--r--litmus/srp.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/litmus/srp.c b/litmus/srp.c
new file mode 100644
index 000000000000..71639b991630
--- /dev/null
+++ b/litmus/srp.c
@@ -0,0 +1,318 @@
1/* ************************************************************************** */
2/* STACK RESOURCE POLICY */
3/* ************************************************************************** */
4
5#include <asm/atomic.h>
6#include <linux/wait.h>
7#include <litmus/litmus.h>
8#include <litmus/sched_plugin.h>
9
10#include <litmus/fdso.h>
11
12#include <litmus/trace.h>
13
14
15#ifdef CONFIG_SRP
16
17struct srp_priority {
18 struct list_head list;
19 unsigned int period;
20 pid_t pid;
21};
22
23#define list2prio(l) list_entry(l, struct srp_priority, list)
24
25/* SRP task priority comparison function. Smaller periods have highest
26 * priority, tie-break is PID. Special case: period == 0 <=> no priority
27 */
28static int srp_higher_prio(struct srp_priority* first,
29 struct srp_priority* second)
30{
31 if (!first->period)
32 return 0;
33 else
34 return !second->period ||
35 first->period < second->period || (
36 first->period == second->period &&
37 first->pid < second->pid);
38}
39
40struct srp {
41 struct list_head ceiling;
42 wait_queue_head_t ceiling_blocked;
43};
44
45
46atomic_t srp_objects_in_use = ATOMIC_INIT(0);
47
48DEFINE_PER_CPU(struct srp, srp);
49
50
51/* Initialize SRP semaphores at boot time. */
52static int __init srp_init(void)
53{
54 int i;
55
56 printk("Initializing SRP per-CPU ceilings...");
57 for (i = 0; i < NR_CPUS; i++) {
58 init_waitqueue_head(&per_cpu(srp, i).ceiling_blocked);
59 INIT_LIST_HEAD(&per_cpu(srp, i).ceiling);
60 }
61 printk(" done!\n");
62
63 return 0;
64}
65module_init(srp_init);
66
67
68#define system_ceiling(srp) list2prio(srp->ceiling.next)
69
70
71#define UNDEF_SEM -2
72
73
74/* struct for uniprocessor SRP "semaphore" */
75struct srp_semaphore {
76 struct srp_priority ceiling;
77 struct task_struct* owner;
78 int cpu; /* cpu associated with this "semaphore" and resource */
79};
80
81#define ceiling2sem(c) container_of(c, struct srp_semaphore, ceiling)
82
83static int srp_exceeds_ceiling(struct task_struct* first,
84 struct srp* srp)
85{
86 return list_empty(&srp->ceiling) ||
87 get_rt_period(first) < system_ceiling(srp)->period ||
88 (get_rt_period(first) == system_ceiling(srp)->period &&
89 first->pid < system_ceiling(srp)->pid) ||
90 ceiling2sem(system_ceiling(srp))->owner == first;
91}
92
93static void srp_add_prio(struct srp* srp, struct srp_priority* prio)
94{
95 struct list_head *pos;
96 if (in_list(&prio->list)) {
97 printk(KERN_CRIT "WARNING: SRP violation detected, prio is already in "
98 "ceiling list! cpu=%d, srp=%p\n", smp_processor_id(), ceiling2sem(prio));
99 return;
100 }
101 list_for_each(pos, &srp->ceiling)
102 if (unlikely(srp_higher_prio(prio, list2prio(pos)))) {
103 __list_add(&prio->list, pos->prev, pos);
104 return;
105 }
106
107 list_add_tail(&prio->list, &srp->ceiling);
108}
109
110
111static void* create_srp_semaphore(void)
112{
113 struct srp_semaphore* sem;
114
115 sem = kmalloc(sizeof(*sem), GFP_KERNEL);
116 if (!sem)
117 return NULL;
118
119 INIT_LIST_HEAD(&sem->ceiling.list);
120 sem->ceiling.period = 0;
121 sem->cpu = UNDEF_SEM;
122 sem->owner = NULL;
123 atomic_inc(&srp_objects_in_use);
124 return sem;
125}
126
127static noinline int open_srp_semaphore(struct od_table_entry* entry, void* __user arg)
128{
129 struct srp_semaphore* sem = (struct srp_semaphore*) entry->obj->obj;
130 int ret = 0;
131 struct task_struct* t = current;
132 struct srp_priority t_prio;
133
134 TRACE("opening SRP semaphore %p, cpu=%d\n", sem, sem->cpu);
135 if (!srp_active())
136 return -EBUSY;
137
138 if (sem->cpu == UNDEF_SEM)
139 sem->cpu = get_partition(t);
140 else if (sem->cpu != get_partition(t))
141 ret = -EPERM;
142
143 if (ret == 0) {
144 t_prio.period = get_rt_period(t);
145 t_prio.pid = t->pid;
146 if (srp_higher_prio(&t_prio, &sem->ceiling)) {
147 sem->ceiling.period = t_prio.period;
148 sem->ceiling.pid = t_prio.pid;
149 }
150 }
151
152 return ret;
153}
154
155static void destroy_srp_semaphore(void* sem)
156{
157 /* XXX invariants */
158 atomic_dec(&srp_objects_in_use);
159 kfree(sem);
160}
161
162struct fdso_ops srp_sem_ops = {
163 .create = create_srp_semaphore,
164 .open = open_srp_semaphore,
165 .destroy = destroy_srp_semaphore
166};
167
168
169static void do_srp_down(struct srp_semaphore* sem)
170{
171 /* Update ceiling. */
172 srp_add_prio(&__get_cpu_var(srp), &sem->ceiling);
173 WARN_ON(sem->owner != NULL);
174 sem->owner = current;
175 TRACE_CUR("acquired srp 0x%p\n", sem);
176}
177
178static void do_srp_up(struct srp_semaphore* sem)
179{
180 /* Determine new system priority ceiling for this CPU. */
181 WARN_ON(!in_list(&sem->ceiling.list));
182 if (in_list(&sem->ceiling.list))
183 list_del(&sem->ceiling.list);
184
185 sem->owner = NULL;
186
187 /* Wake tasks on this CPU, if they exceed current ceiling. */
188 TRACE_CUR("released srp 0x%p\n", sem);
189 wake_up_all(&__get_cpu_var(srp).ceiling_blocked);
190}
191
192/* Adjust the system-wide priority ceiling if resource is claimed. */
193asmlinkage long sys_srp_down(int sem_od)
194{
195 int cpu;
196 int ret = -EINVAL;
197 struct srp_semaphore* sem;
198
199 /* disabling preemptions is sufficient protection since
200 * SRP is strictly per CPU and we don't interfere with any
201 * interrupt handlers
202 */
203 preempt_disable();
204 TS_SRP_DOWN_START;
205
206 cpu = smp_processor_id();
207 sem = lookup_srp_sem(sem_od);
208 if (sem && sem->cpu == cpu) {
209 do_srp_down(sem);
210 ret = 0;
211 }
212
213 TS_SRP_DOWN_END;
214 preempt_enable();
215 return ret;
216}
217
218/* Adjust the system-wide priority ceiling if resource is freed. */
219asmlinkage long sys_srp_up(int sem_od)
220{
221 int cpu;
222 int ret = -EINVAL;
223 struct srp_semaphore* sem;
224
225 preempt_disable();
226 TS_SRP_UP_START;
227
228 cpu = smp_processor_id();
229 sem = lookup_srp_sem(sem_od);
230
231 if (sem && sem->cpu == cpu) {
232 do_srp_up(sem);
233 ret = 0;
234 }
235
236 TS_SRP_UP_END;
237 preempt_enable();
238 return ret;
239}
240
241static int srp_wake_up(wait_queue_t *wait, unsigned mode, int sync,
242 void *key)
243{
244 int cpu = smp_processor_id();
245 struct task_struct *tsk = wait->private;
246 if (cpu != get_partition(tsk))
247 TRACE_TASK(tsk, "srp_wake_up on wrong cpu, partition is %d\b",
248 get_partition(tsk));
249 else if (srp_exceeds_ceiling(tsk, &__get_cpu_var(srp)))
250 return default_wake_function(wait, mode, sync, key);
251 return 0;
252}
253
254
255
256static void do_ceiling_block(struct task_struct *tsk)
257{
258 wait_queue_t wait = {
259 .private = tsk,
260 .func = srp_wake_up,
261 .task_list = {NULL, NULL}
262 };
263
264 tsk->state = TASK_UNINTERRUPTIBLE;
265 add_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait);
266 tsk->rt_param.srp_non_recurse = 1;
267 preempt_enable_no_resched();
268 schedule();
269 preempt_disable();
270 tsk->rt_param.srp_non_recurse = 0;
271 remove_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait);
272}
273
274/* Wait for current task priority to exceed system-wide priority ceiling.
275 */
276void srp_ceiling_block(void)
277{
278 struct task_struct *tsk = current;
279
280 /* Only applies to real-time tasks, but optimize for RT tasks. */
281 if (unlikely(!is_realtime(tsk)))
282 return;
283
284 /* Avoid recursive ceiling blocking. */
285 if (unlikely(tsk->rt_param.srp_non_recurse))
286 return;
287
288 /* Bail out early if there aren't any SRP resources around. */
289 if (likely(!atomic_read(&srp_objects_in_use)))
290 return;
291
292 preempt_disable();
293 if (!srp_exceeds_ceiling(tsk, &__get_cpu_var(srp))) {
294 TRACE_CUR("is priority ceiling blocked.\n");
295 while (!srp_exceeds_ceiling(tsk, &__get_cpu_var(srp)))
296 do_ceiling_block(tsk);
297 TRACE_CUR("finally exceeds system ceiling.\n");
298 } else
299 TRACE_CUR("is not priority ceiling blocked\n");
300 preempt_enable();
301}
302
303
304#else
305
306asmlinkage long sys_srp_down(int sem_od)
307{
308 return -ENOSYS;
309}
310
311asmlinkage long sys_srp_up(int sem_od)
312{
313 return -ENOSYS;
314}
315
316struct fdso_ops srp_sem_ops = {};
317
318#endif