diff options
author | Jeremy Erickson <jerickso@cs.unc.edu> | 2012-08-22 18:55:51 -0400 |
---|---|---|
committer | Jeremy Erickson <jerickso@cs.unc.edu> | 2012-08-22 18:55:51 -0400 |
commit | 82e55e27d4eab2388ee6aee447bb10cfc4058d86 (patch) | |
tree | 81445de20a5466c18b9f9ba4e2ee3a60a338257f | |
parent | da7a2586be45bc784480c440fd8d627a796df671 (diff) |
Add FMLP Test Task
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | bin/fmlp_test_task.c | 281 |
2 files changed, 285 insertions, 1 deletions
@@ -70,7 +70,7 @@ AR := ${CROSS_COMPILE}${AR} | |||
70 | # Targets | 70 | # Targets |
71 | 71 | ||
72 | all = lib ${rt-apps} | 72 | all = lib ${rt-apps} |
73 | rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ | 73 | rt-apps = cycles base_task rt_launch rtspin fmlp_test_task release_ts measure_syscall \ |
74 | base_mt_task runtests | 74 | base_mt_task runtests |
75 | 75 | ||
76 | .PHONY: all lib clean dump-config TAGS tags cscope help | 76 | .PHONY: all lib clean dump-config TAGS tags cscope help |
@@ -211,6 +211,9 @@ obj-rt_launch = rt_launch.o common.o | |||
211 | obj-rtspin = rtspin.o common.o | 211 | obj-rtspin = rtspin.o common.o |
212 | lib-rtspin = -lrt | 212 | lib-rtspin = -lrt |
213 | 213 | ||
214 | obj-fmlp_test_task = fmlp_test_task.o common.o | ||
215 | lib-fmlp_test_task = -lrt | ||
216 | |||
214 | obj-release_ts = release_ts.o | 217 | obj-release_ts = release_ts.o |
215 | 218 | ||
216 | obj-measure_syscall = null_call.o | 219 | obj-measure_syscall = null_call.o |
diff --git a/bin/fmlp_test_task.c b/bin/fmlp_test_task.c new file mode 100644 index 0000000..6f8e2ec --- /dev/null +++ b/bin/fmlp_test_task.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* based_mt_task.c -- A basic multi-threaded real-time task skeleton. | ||
2 | * | ||
3 | * This (by itself useless) task demos how to setup a multi-threaded LITMUS^RT | ||
4 | * real-time task. Familiarity with the single threaded example (base_task.c) | ||
5 | * is assumed. | ||
6 | * | ||
7 | * Currently, liblitmus still lacks automated support for real-time | ||
8 | * tasks, but internaly it is thread-safe, and thus can be used together | ||
9 | * with pthreads. | ||
10 | */ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | /* Extras */ | ||
15 | #include <stdint.h> | ||
16 | #include <unistd.h> | ||
17 | #include <assert.h> | ||
18 | #include <errno.h> | ||
19 | #include <sys/types.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <fcntl.h> | ||
22 | |||
23 | /* Include gettid() */ | ||
24 | #include <sys/types.h> | ||
25 | |||
26 | /* Include threading support. */ | ||
27 | #include <pthread.h> | ||
28 | |||
29 | /* Include the LITMUS^RT API.*/ | ||
30 | #include "litmus.h" | ||
31 | |||
32 | /* Let's create 4 threads in the example, | ||
33 | */ | ||
34 | #define NUM_THREADS 4 | ||
35 | #define MAX_PHASES 10 | ||
36 | |||
37 | /* The information passed to each thread. Could be anything. */ | ||
38 | struct thread_context { | ||
39 | int id; | ||
40 | int fd; | ||
41 | int semaphore; | ||
42 | lt_t exec; | ||
43 | lt_t period; | ||
44 | int split; | ||
45 | int phases; | ||
46 | double initial_phase; | ||
47 | double locked[MAX_PHASES]; | ||
48 | double unlocked[MAX_PHASES]; | ||
49 | }; | ||
50 | |||
51 | /* The real-time thread program. Doesn't have to be the same for | ||
52 | * all threads. Here, we only have one that will invoke job(). | ||
53 | */ | ||
54 | void* rt_thread(void *tcontext); | ||
55 | |||
56 | /* Declare the periodically invoked job. | ||
57 | * Returns 1 -> task should exit. | ||
58 | * 0 -> task should continue. | ||
59 | */ | ||
60 | int job(struct thread_context* ctx); | ||
61 | |||
62 | |||
63 | /* Catch errors. | ||
64 | */ | ||
65 | #if 0 | ||
66 | #define CALL( exp ) do { \ | ||
67 | int ret; \ | ||
68 | ret = exp; \ | ||
69 | if (ret != 0) \ | ||
70 | fprintf(stderr, "%s failed: %m\n", #exp);\ | ||
71 | else \ | ||
72 | fprintf(stderr, "%s ok.\n", #exp); \ | ||
73 | } while (0) | ||
74 | #endif | ||
75 | #define CALL( exp ) exp | ||
76 | |||
77 | /* Basic setup is the same as in the single-threaded example. However, | ||
78 | * we do some thread initiliazation first before invoking the job. | ||
79 | */ | ||
80 | int main(int argc, char** argv) | ||
81 | { | ||
82 | int i; | ||
83 | struct thread_context ctx[NUM_THREADS]; | ||
84 | pthread_t task[NUM_THREADS]; | ||
85 | int fd; | ||
86 | |||
87 | /* The task is in background mode upon startup. */ | ||
88 | |||
89 | |||
90 | /***** | ||
91 | * 1) Command line paramter parsing would be done here. | ||
92 | */ | ||
93 | |||
94 | |||
95 | |||
96 | /***** | ||
97 | * 2) Work environment (e.g., global data structures, file data, etc.) would | ||
98 | * be setup here. | ||
99 | */ | ||
100 | |||
101 | |||
102 | |||
103 | /***** | ||
104 | * 3) Initialize LITMUS^RT. | ||
105 | * Task parameters will be specified per thread. | ||
106 | */ | ||
107 | fd = open("semaphores", O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR); | ||
108 | CALL( init_litmus() ); | ||
109 | |||
110 | |||
111 | /***** | ||
112 | * 4) Launch threads. | ||
113 | */ | ||
114 | |||
115 | ctx[0].id = 0; | ||
116 | ctx[0].fd = fd; | ||
117 | ctx[0].exec = 10000000; | ||
118 | ctx[0].period = 20000000; | ||
119 | ctx[0].split = 5; | ||
120 | ctx[0].phases = 1; | ||
121 | ctx[0].initial_phase = .0025; | ||
122 | ctx[0].locked[0] = .003; | ||
123 | ctx[0].unlocked[0] = .0045; | ||
124 | |||
125 | ctx[1].id = 1; | ||
126 | ctx[1].fd = fd; | ||
127 | ctx[1].exec = 10000000; | ||
128 | ctx[1].period = 20000000; | ||
129 | ctx[1].split = 5; | ||
130 | ctx[1].phases = 1; | ||
131 | ctx[1].initial_phase = .003; | ||
132 | ctx[1].locked[0] = .002; | ||
133 | ctx[1].unlocked[0] = .005; | ||
134 | |||
135 | ctx[2].id = 2; | ||
136 | ctx[2].fd = fd; | ||
137 | ctx[2].exec = 4000000; | ||
138 | ctx[2].period = 10000000; | ||
139 | ctx[2].split = 1; | ||
140 | ctx[2].phases = 0; | ||
141 | ctx[2].initial_phase = .004; | ||
142 | |||
143 | ctx[3].id = 3; | ||
144 | ctx[3].fd = fd; | ||
145 | ctx[3].exec = 4000000; | ||
146 | ctx[3].period = 10000000; | ||
147 | ctx[3].split = 1; | ||
148 | ctx[3].phases = 0; | ||
149 | ctx[3].initial_phase = .004; | ||
150 | |||
151 | for (i = 0; i < NUM_THREADS; i++){ | ||
152 | pthread_create(task + i, NULL, rt_thread, (void *) (ctx + i)); | ||
153 | } | ||
154 | |||
155 | |||
156 | /***** | ||
157 | * 5) Wait for RT threads to terminate. | ||
158 | */ | ||
159 | for (i = 0; i < NUM_THREADS; i++) | ||
160 | pthread_join(task[i], NULL); | ||
161 | |||
162 | |||
163 | /***** | ||
164 | * 6) Clean up, maybe print results and stats, and exit. | ||
165 | */ | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | |||
170 | |||
171 | /* A real-time thread is very similar to the main function of a single-threaded | ||
172 | * real-time app. Notice, that init_rt_thread() is called to initialized per-thread | ||
173 | * data structures of the LITMUS^RT user space libary. | ||
174 | */ | ||
175 | void* rt_thread(void *tcontext) | ||
176 | { | ||
177 | int do_exit = 0; | ||
178 | struct thread_context *ctx = (struct thread_context *) tcontext; | ||
179 | |||
180 | /* Make presence visible. */ | ||
181 | printf("RT Thread %d active.\n", ctx->id); | ||
182 | |||
183 | /***** | ||
184 | * 1) Initialize real-time settings. | ||
185 | */ | ||
186 | CALL( init_rt_thread() ); | ||
187 | |||
188 | ctx->semaphore = open_fmlp_sem(ctx->fd, 0); | ||
189 | CALL( sporadic_task_ns(ctx->exec, ctx->period, 0, ctx->split, 0, | ||
190 | RT_CLASS_HARD, PRECISE_ENFORCEMENT, 0)); | ||
191 | |||
192 | |||
193 | /***** | ||
194 | * 2) Transition to real-time mode. | ||
195 | */ | ||
196 | CALL( task_mode(LITMUS_RT_TASK) ); | ||
197 | |||
198 | /* The task is now executing as a real-time task if the call didn't fail. | ||
199 | */ | ||
200 | |||
201 | printf("[%d] Waiting for TS release.\n ", ctx->id); | ||
202 | wait_for_ts_release(); | ||
203 | |||
204 | |||
205 | /***** | ||
206 | * 3) Invoke real-time jobs. | ||
207 | */ | ||
208 | do { | ||
209 | /* Wait until the next job is released. */ | ||
210 | sleep_next_period(); | ||
211 | /* Invoke job. */ | ||
212 | do_exit = job(ctx); | ||
213 | } while (!do_exit); | ||
214 | |||
215 | |||
216 | |||
217 | /***** | ||
218 | * 4) Transition to background mode. | ||
219 | */ | ||
220 | CALL( task_mode(BACKGROUND_TASK) ); | ||
221 | |||
222 | |||
223 | return NULL; | ||
224 | } | ||
225 | |||
226 | #define NUMS 4096 | ||
227 | static int num[NUMS]; | ||
228 | |||
229 | static int loop_once(void) | ||
230 | { | ||
231 | int i, j = 0; | ||
232 | for (i = 0; i < NUMS; i++) | ||
233 | j += num[i]++; | ||
234 | return j; | ||
235 | } | ||
236 | |||
237 | static int loop_for(double exec_time, double emergency_exit) | ||
238 | { | ||
239 | double last_loop = 0, loop_start; | ||
240 | int tmp = 0; | ||
241 | |||
242 | double start = cputime(); | ||
243 | double now = cputime(); | ||
244 | while (now + last_loop < start + exec_time) { | ||
245 | loop_start = now; | ||
246 | tmp += loop_once(); | ||
247 | now = cputime(); | ||
248 | last_loop = now - loop_start; | ||
249 | if (emergency_exit && wctime() > emergency_exit) { | ||
250 | /* Oops --- this should only be possible if the execution time tracking | ||
251 | * is broken in the LITMUS^RT kernel. */ | ||
252 | fprintf(stderr, "!!! fmlp_test_task/%d emergency exit!\n", getpid()); | ||
253 | fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n"); | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | return tmp; | ||
259 | } | ||
260 | |||
261 | int job(struct thread_context* ctx) | ||
262 | { | ||
263 | int i; | ||
264 | /* Do real-time calculation. */ | ||
265 | double emergency_exit = ctx->initial_phase; | ||
266 | for (i = 0; i < ctx->phases; i++) { | ||
267 | emergency_exit += ctx->locked[i]; | ||
268 | emergency_exit += ctx->unlocked[i]; | ||
269 | } | ||
270 | emergency_exit *= 200; | ||
271 | emergency_exit += wctime(); | ||
272 | loop_for(ctx->initial_phase, emergency_exit); | ||
273 | for (i = 0; i < ctx->phases; i++) { | ||
274 | CALL( litmus_lock( 0 )); | ||
275 | loop_for(ctx->locked[i], emergency_exit); | ||
276 | CALL( litmus_unlock( 0 )); | ||
277 | loop_for(ctx->unlocked[i], emergency_exit); | ||
278 | } | ||
279 | /* Don't exit. */ | ||
280 | return 0; | ||
281 | } | ||