diff options
Diffstat (limited to 'src/litmus.c')
-rw-r--r-- | src/litmus.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/litmus.c b/src/litmus.c new file mode 100644 index 0000000..65ad294 --- /dev/null +++ b/src/litmus.c | |||
@@ -0,0 +1,295 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include <sys/types.h> | ||
5 | #include <unistd.h> | ||
6 | #include <errno.h> | ||
7 | #include <signal.h> | ||
8 | #include <sys/mman.h> | ||
9 | |||
10 | |||
11 | #include "litmus.h" | ||
12 | |||
13 | |||
14 | /* this is missing in newer linux/unistd.h versions */ | ||
15 | |||
16 | #define _syscall0(type,name) \ | ||
17 | type name(void) \ | ||
18 | {\ | ||
19 | return syscall(__NR_##name);\ | ||
20 | } | ||
21 | |||
22 | #define _syscall1(type,name,type1,arg1) \ | ||
23 | type name(type1 arg1) \ | ||
24 | {\ | ||
25 | return syscall(__NR_##name, arg1);\ | ||
26 | } | ||
27 | |||
28 | |||
29 | #define _syscall2(type,name,type1,arg1,type2,arg2) \ | ||
30 | type name(type1 arg1,type2 arg2) \ | ||
31 | {\ | ||
32 | return syscall(__NR_##name, arg1, arg2);\ | ||
33 | } | ||
34 | |||
35 | |||
36 | /* clear the TID in the child */ | ||
37 | #define CLONE_CHILD_CLEARTID 0x00200000 | ||
38 | /* set the TID in the child */ | ||
39 | #define CLONE_CHILD_SETTID 0x01000000 | ||
40 | /* don't let the child run before we have completed setup */ | ||
41 | #define CLONE_STOPPED 0x02000000 | ||
42 | /* litmus clone flag */ | ||
43 | #define CLONE_REALTIME 0x10000000 | ||
44 | |||
45 | /* CLONE_REALTIME is necessary because CLONE_STOPPED will put a SIGSTOP in | ||
46 | * the pending signal queue. Thus the first thing a newly created task will | ||
47 | * do after it is released is to stop, which is not what we want | ||
48 | * | ||
49 | * CLONE_REALTIME just sets the status to TASK_STOPPED without queueing a | ||
50 | * signal. | ||
51 | */ | ||
52 | |||
53 | |||
54 | /* this is essentially a fork with CLONE_STOPPED */ | ||
55 | /* #define CLONE_LITMUS CLONE_STOPPED | CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID */ | ||
56 | #define CLONE_LITMUS CLONE_REALTIME | CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | ||
57 | |||
58 | /* we need to override libc because it wants to be clever | ||
59 | * and rejects our call without presenting it even to the kernel | ||
60 | */ | ||
61 | #define __NR_raw_clone 120 | ||
62 | |||
63 | |||
64 | _syscall2(int, raw_clone, unsigned long, flags, unsigned long, child_stack) | ||
65 | |||
66 | |||
67 | const char* get_scheduler_name(spolicy scheduler) | ||
68 | { | ||
69 | const char* name; | ||
70 | |||
71 | switch (scheduler){ | ||
72 | case SCHED_LINUX : | ||
73 | name = "Linux"; | ||
74 | break; | ||
75 | case SCHED_PFAIR: | ||
76 | name = "Pfair"; | ||
77 | break; | ||
78 | case SCHED_PFAIR_STAGGER: | ||
79 | name = "Pfair (staggered)"; | ||
80 | break; | ||
81 | case SCHED_PART_EDF: | ||
82 | name = "Partioned EDF"; | ||
83 | break; | ||
84 | case SCHED_PART_EEVDF: | ||
85 | name = "Partioned EEVDF"; | ||
86 | break; | ||
87 | case SCHED_GLOBAL_EDF: | ||
88 | name = "Global EDF"; | ||
89 | break; | ||
90 | case SCHED_EDF_HSB: | ||
91 | name = "EDF-HSB"; | ||
92 | break; | ||
93 | case SCHED_GSN_EDF: | ||
94 | name = "GSN-EDF"; | ||
95 | break; | ||
96 | case SCHED_PSN_EDF: | ||
97 | name = "PSN-EDF"; | ||
98 | break; | ||
99 | default: | ||
100 | name = "Unkown"; | ||
101 | break; | ||
102 | } | ||
103 | return name; | ||
104 | } | ||
105 | |||
106 | int create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period) { | ||
107 | return __create_rt_task(rt_prog, arg, cpu, wcet, period, RT_CLASS_HARD); | ||
108 | } | ||
109 | |||
110 | int __create_rt_task(rt_fn_t rt_prog, void *arg, int cpu, int wcet, int period, | ||
111 | task_class_t class) | ||
112 | { | ||
113 | int ret; | ||
114 | rt_param_t params; | ||
115 | int rt_task = raw_clone(CLONE_LITMUS, 0); | ||
116 | |||
117 | if (rt_task < 0) | ||
118 | return rt_task; | ||
119 | |||
120 | if (rt_task > 0) { | ||
121 | /* we are the controller task */ | ||
122 | params.period = period; | ||
123 | params.exec_cost = wcet; | ||
124 | params.cpu = cpu; | ||
125 | params.cls = class; | ||
126 | ret = set_rt_task_param(rt_task, ¶ms); | ||
127 | if (ret < 0) { | ||
128 | /* we have a problem: we created the task but | ||
129 | * for some stupid reason we cannot set the real-time | ||
130 | * parameters. We must clean up the stopped task. | ||
131 | */ | ||
132 | kill(rt_task, SIGKILL); | ||
133 | /* syscall will have set errno, we don't have to do | ||
134 | * anything | ||
135 | */ | ||
136 | return -1; | ||
137 | } | ||
138 | ret = prepare_rt_task(rt_task); | ||
139 | if (ret < 0) { | ||
140 | /* same problem as above*/ | ||
141 | //kill(rt_task, SIGKILL); | ||
142 | rt_task = -1; | ||
143 | } | ||
144 | return rt_task; | ||
145 | } | ||
146 | else { | ||
147 | /* we are the real-time task | ||
148 | * launch task and die when it is done | ||
149 | */ | ||
150 | |||
151 | exit(rt_prog(arg)); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | |||
156 | void show_rt_param(rt_param_t* tp) | ||
157 | { | ||
158 | printf("rt params:\n\t" | ||
159 | "exec_cost:\t%ld\n\tperiod:\t\t%ld\n\tcpu:\t%d\n", | ||
160 | tp->exec_cost, tp->period, tp->cpu); | ||
161 | } | ||
162 | |||
163 | task_class_t str2class(const char* str) | ||
164 | { | ||
165 | if (!strcmp(str, "hrt")) | ||
166 | return RT_CLASS_HARD; | ||
167 | else if (!strcmp(str, "srt")) | ||
168 | return RT_CLASS_SOFT; | ||
169 | else if (!strcmp(str, "be")) | ||
170 | return RT_CLASS_BEST_EFFORT; | ||
171 | else | ||
172 | return -1; | ||
173 | } | ||
174 | |||
175 | |||
176 | |||
177 | struct np_flag { | ||
178 | #define RT_PREEMPTIVE 0x2050 /* = NP */ | ||
179 | #define RT_NON_PREEMPTIVE 0x4e50 /* = P */ | ||
180 | unsigned short preemptivity; | ||
181 | |||
182 | #define RT_EXIT_NP_REQUESTED 0x5251 /* = RQ */ | ||
183 | unsigned short request; | ||
184 | |||
185 | unsigned int ctr; | ||
186 | }; | ||
187 | |||
188 | static struct np_flag np_flag; | ||
189 | |||
190 | int register_np_flag(struct np_flag* flag); | ||
191 | int signal_exit_np(void); | ||
192 | |||
193 | |||
194 | static inline void barrier(void) | ||
195 | { | ||
196 | __asm__ __volatile__("sfence": : :"memory"); | ||
197 | } | ||
198 | |||
199 | void enter_np(void) | ||
200 | { | ||
201 | if (++np_flag.ctr == 1) | ||
202 | { | ||
203 | np_flag.request = 0; | ||
204 | barrier(); | ||
205 | np_flag.preemptivity = RT_NON_PREEMPTIVE; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | |||
210 | void exit_np(void) | ||
211 | { | ||
212 | if (--np_flag.ctr == 0) | ||
213 | { | ||
214 | np_flag.preemptivity = RT_PREEMPTIVE; | ||
215 | barrier(); | ||
216 | if (np_flag.request == RT_EXIT_NP_REQUESTED) | ||
217 | signal_exit_np(); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | |||
222 | #define check(str) if (ret == -1) {perror(str); fprintf(stderr, \ | ||
223 | "Could not initialize LITMUS^RT, aborting...\n"); exit(1);} | ||
224 | |||
225 | void init_litmus(void) | ||
226 | { | ||
227 | int ret; | ||
228 | np_flag.preemptivity = RT_PREEMPTIVE; | ||
229 | np_flag.ctr = 0; | ||
230 | |||
231 | ret = mlockall(MCL_CURRENT | MCL_FUTURE); | ||
232 | check("mlockall"); | ||
233 | ret = register_np_flag(&np_flag); | ||
234 | check("register_np_flag"); | ||
235 | } | ||
236 | |||
237 | |||
238 | |||
239 | /* Litmus syscalls definitions */ | ||
240 | #define __NR_sched_setpolicy 320 | ||
241 | #define __NR_sched_getpolicy 321 | ||
242 | #define __NR_set_rt_mode 322 | ||
243 | #define __NR_set_rt_task_param 323 | ||
244 | #define __NR_get_rt_task_param 324 | ||
245 | #define __NR_prepare_rt_task 325 | ||
246 | #define __NR_reset_stat 326 | ||
247 | #define __NR_sleep_next_period 327 | ||
248 | #define __NR_scheduler_setup 328 | ||
249 | #define __NR_register_np_flag 329 | ||
250 | #define __NR_signal_exit_np 330 | ||
251 | #define __NR_pi_sema_init 331 | ||
252 | #define __NR_pi_down 332 | ||
253 | #define __NR_pi_up 333 | ||
254 | #define __NR_pi_sema_free 334 | ||
255 | #define __NR_sema_init 335 | ||
256 | #define __NR_down 336 | ||
257 | #define __NR_up 337 | ||
258 | #define __NR_sema_free 338 | ||
259 | #define __NR_srp_sema_init 339 | ||
260 | #define __NR_srp_down 340 | ||
261 | #define __NR_srp_up 341 | ||
262 | #define __NR_reg_task_srp_sem 342 | ||
263 | #define __NR_srp_sema_free 343 | ||
264 | #define __NR_get_job_no 344 | ||
265 | #define __NR_wait_for_job_release 345 | ||
266 | |||
267 | |||
268 | /* Syscall stub for setting RT mode and scheduling options */ | ||
269 | _syscall1(spolicy, sched_setpolicy, spolicy, arg1); | ||
270 | _syscall0(spolicy, sched_getpolicy); | ||
271 | _syscall1(int, set_rt_mode, int, arg1); | ||
272 | _syscall2(int, set_rt_task_param, pid_t, pid, rt_param_t*, arg1); | ||
273 | _syscall2(int, get_rt_task_param, pid_t, pid, rt_param_t*, arg1); | ||
274 | _syscall1(int, prepare_rt_task, pid_t, pid); | ||
275 | _syscall0(int, reset_stat); | ||
276 | _syscall0(int, sleep_next_period); | ||
277 | _syscall2(int, scheduler_setup, int, cmd, void*, param); | ||
278 | _syscall1(int, register_np_flag, struct np_flag*, flag); | ||
279 | _syscall0(int, signal_exit_np); | ||
280 | _syscall0(int, pi_sema_init); | ||
281 | _syscall1(int, pi_down, pi_sema_id, sem_id); | ||
282 | _syscall1(int, pi_up, pi_sema_id, sem_id); | ||
283 | _syscall1(int, pi_sema_free, pi_sema_id, sem_id); | ||
284 | _syscall0(int, sema_init); | ||
285 | _syscall1(int, down, sema_id, sem_id); | ||
286 | _syscall1(int, up, sema_id, sem_id); | ||
287 | _syscall1(int, sema_free, sema_id, sem_id); | ||
288 | _syscall0(int, srp_sema_init); | ||
289 | _syscall1(int, srp_down, srp_sema_id, sem_id); | ||
290 | _syscall1(int, srp_up, srp_sema_id, sem_id); | ||
291 | _syscall2(int, reg_task_srp_sem, srp_sema_id, sem_id, pid_t, t_pid); | ||
292 | _syscall1(int, srp_sema_free, srp_sema_id, sem_id); | ||
293 | _syscall1(int, get_job_no, unsigned int*, job_no); | ||
294 | _syscall1(int, wait_for_job_release, unsigned int, job_no); | ||
295 | |||