diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | bin/dglspin.c | 335 |
2 files changed, 338 insertions, 2 deletions
@@ -71,7 +71,7 @@ AR := ${CROSS_COMPILE}${AR} | |||
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 release_ts measure_syscall \ |
74 | base_mt_task runtests dgl_test | 74 | base_mt_task runtests dglspin |
75 | 75 | ||
76 | .PHONY: all lib clean dump-config TAGS tags cscope help | 76 | .PHONY: all lib clean dump-config TAGS tags cscope help |
77 | 77 | ||
@@ -203,7 +203,8 @@ obj-cycles = cycles.o | |||
203 | 203 | ||
204 | obj-base_task = base_task.o | 204 | obj-base_task = base_task.o |
205 | 205 | ||
206 | obj-dgl_test = dgl_test.o | 206 | obj-dglspin = dglspin.o common.o |
207 | lib-dglspin = -lrt | ||
207 | 208 | ||
208 | obj-base_mt_task = base_mt_task.o | 209 | obj-base_mt_task = base_mt_task.o |
209 | ldf-base_mt_task = -pthread | 210 | ldf-base_mt_task = -pthread |
diff --git a/bin/dglspin.c b/bin/dglspin.c new file mode 100644 index 0000000..805fd85 --- /dev/null +++ b/bin/dglspin.c | |||
@@ -0,0 +1,335 @@ | |||
1 | #include <sys/time.h> | ||
2 | |||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <unistd.h> | ||
6 | #include <time.h> | ||
7 | #include <assert.h> | ||
8 | #include <fcntl.h> | ||
9 | |||
10 | |||
11 | #include "litmus.h" | ||
12 | #include "common.h" | ||
13 | |||
14 | |||
15 | |||
16 | static void usage(char *error) { | ||
17 | fprintf(stderr, "Error: %s\n", error); | ||
18 | fprintf(stderr, | ||
19 | "Usage:\n" | ||
20 | " rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n" | ||
21 | " rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n" | ||
22 | " rt_spin -l\n" | ||
23 | "\n" | ||
24 | "COMMON-OPTS = [-w] [-p PARTITION] [-c CLASS] [-s SCALE]\n" | ||
25 | "\n" | ||
26 | "WCET and PERIOD are milliseconds, DURATION is seconds.\n"); | ||
27 | exit(EXIT_FAILURE); | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * returns the character that made processing stop, newline or EOF | ||
32 | */ | ||
33 | static int skip_to_next_line(FILE *fstream) | ||
34 | { | ||
35 | int ch; | ||
36 | for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream)); | ||
37 | return ch; | ||
38 | } | ||
39 | |||
40 | static void skip_comments(FILE *fstream) | ||
41 | { | ||
42 | int ch; | ||
43 | for (ch = fgetc(fstream); ch == '#'; ch = fgetc(fstream)) | ||
44 | skip_to_next_line(fstream); | ||
45 | ungetc(ch, fstream); | ||
46 | } | ||
47 | |||
48 | static void get_exec_times(const char *file, const int column, | ||
49 | int *num_jobs, double **exec_times) | ||
50 | { | ||
51 | FILE *fstream; | ||
52 | int cur_job, cur_col, ch; | ||
53 | *num_jobs = 0; | ||
54 | |||
55 | fstream = fopen(file, "r"); | ||
56 | if (!fstream) | ||
57 | bail_out("could not open execution time file"); | ||
58 | |||
59 | /* figure out the number of jobs */ | ||
60 | do { | ||
61 | skip_comments(fstream); | ||
62 | ch = skip_to_next_line(fstream); | ||
63 | if (ch != EOF) | ||
64 | ++(*num_jobs); | ||
65 | } while (ch != EOF); | ||
66 | |||
67 | if (-1 == fseek(fstream, 0L, SEEK_SET)) | ||
68 | bail_out("rewinding file failed"); | ||
69 | |||
70 | /* allocate space for exec times */ | ||
71 | *exec_times = calloc(*num_jobs, sizeof(*exec_times)); | ||
72 | if (!*exec_times) | ||
73 | bail_out("couldn't allocate memory"); | ||
74 | |||
75 | for (cur_job = 0; cur_job < *num_jobs && !feof(fstream); ++cur_job) { | ||
76 | |||
77 | skip_comments(fstream); | ||
78 | |||
79 | for (cur_col = 1; cur_col < column; ++cur_col) { | ||
80 | /* discard input until we get to the column we want */ | ||
81 | fscanf(fstream, "%*s,"); | ||
82 | } | ||
83 | |||
84 | /* get the desired exec. time */ | ||
85 | if (1 != fscanf(fstream, "%lf", (*exec_times)+cur_job)) { | ||
86 | fprintf(stderr, "invalid execution time near line %d\n", | ||
87 | cur_job); | ||
88 | exit(EXIT_FAILURE); | ||
89 | } | ||
90 | |||
91 | skip_to_next_line(fstream); | ||
92 | } | ||
93 | |||
94 | assert(cur_job == *num_jobs); | ||
95 | fclose(fstream); | ||
96 | } | ||
97 | |||
98 | #define NUMS 4096 | ||
99 | static int num[NUMS]; | ||
100 | static char* progname; | ||
101 | |||
102 | static int loop_once(void) | ||
103 | { | ||
104 | int i, j = 0; | ||
105 | for (i = 0; i < NUMS; i++) | ||
106 | j += num[i]++; | ||
107 | return j; | ||
108 | } | ||
109 | |||
110 | static int loop_for(double exec_time, double emergency_exit, int mask) | ||
111 | { | ||
112 | double last_loop = 0, loop_start; | ||
113 | int tmp = 0; | ||
114 | |||
115 | double start = cputime(); | ||
116 | double now = cputime(); | ||
117 | |||
118 | while (now + last_loop < start + exec_time) { | ||
119 | loop_start = now; | ||
120 | dynamic_group_lock(mask); | ||
121 | tmp += loop_once(); | ||
122 | dynamic_group_unlock(mask); | ||
123 | now = cputime(); | ||
124 | last_loop = now - loop_start; | ||
125 | if (emergency_exit && wctime() > emergency_exit) { | ||
126 | /* Oops --- this should only be possible if the execution time tracking | ||
127 | * is broken in the LITMUS^RT kernel. */ | ||
128 | fprintf(stderr, "!!! rtspin/%d emergency exit!\n", getpid()); | ||
129 | fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n"); | ||
130 | break; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return tmp; | ||
135 | } | ||
136 | |||
137 | |||
138 | static void debug_delay_loop(void) | ||
139 | { | ||
140 | double start, end, delay; | ||
141 | |||
142 | while (1) { | ||
143 | for (delay = 0.5; delay > 0.01; delay -= 0.01) { | ||
144 | start = wctime(); | ||
145 | loop_for(delay, 0, 1); | ||
146 | end = wctime(); | ||
147 | printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", | ||
148 | delay, | ||
149 | end - start, | ||
150 | end - start - delay, | ||
151 | 100 * (end - start - delay) / delay); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static int job(double exec_time, double program_end, int mask) | ||
157 | { | ||
158 | if (wctime() > program_end) | ||
159 | return 0; | ||
160 | else { | ||
161 | loop_for(exec_time, program_end + 1, mask); | ||
162 | sleep_next_period(); | ||
163 | return 1; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | #define OPTSTR "p:c:wlveo:f:s:q:" | ||
168 | |||
169 | int main(int argc, char** argv) | ||
170 | { | ||
171 | int ret; | ||
172 | lt_t wcet; | ||
173 | lt_t period; | ||
174 | double wcet_ms, period_ms; | ||
175 | unsigned int priority = LITMUS_LOWEST_PRIORITY; | ||
176 | int migrate = 0; | ||
177 | int cpu = 0; | ||
178 | int opt; | ||
179 | int wait = 0; | ||
180 | int test_loop = 0; | ||
181 | int column = 1; | ||
182 | const char *file = NULL; | ||
183 | int want_enforcement = 0; | ||
184 | double duration = 0, start; | ||
185 | double *exec_times = NULL; | ||
186 | double scale = 1.0; | ||
187 | task_class_t class = RT_CLASS_HARD; | ||
188 | int cur_job, num_jobs; | ||
189 | |||
190 | int fd, od_org, od_new, mask; | ||
191 | |||
192 | progname = argv[0]; | ||
193 | |||
194 | while ((opt = getopt(argc, argv, OPTSTR)) != -1) { | ||
195 | switch (opt) { | ||
196 | case 'w': | ||
197 | wait = 1; | ||
198 | break; | ||
199 | case 'p': | ||
200 | cpu = atoi(optarg); | ||
201 | migrate = 1; | ||
202 | break; | ||
203 | case 'q': | ||
204 | priority = atoi(optarg); | ||
205 | if (!litmus_is_valid_fixed_prio(priority)) | ||
206 | usage("Invalid priority."); | ||
207 | break; | ||
208 | case 'c': | ||
209 | class = str2class(optarg); | ||
210 | if (class == -1) | ||
211 | usage("Unknown task class."); | ||
212 | break; | ||
213 | case 'e': | ||
214 | want_enforcement = 1; | ||
215 | break; | ||
216 | case 'l': | ||
217 | test_loop = 1; | ||
218 | break; | ||
219 | case 'o': | ||
220 | column = atoi(optarg); | ||
221 | break; | ||
222 | case 'f': | ||
223 | file = optarg; | ||
224 | break; | ||
225 | case 's': | ||
226 | scale = atof(optarg); | ||
227 | break; | ||
228 | case ':': | ||
229 | usage("Argument missing."); | ||
230 | break; | ||
231 | case '?': | ||
232 | default: | ||
233 | usage("Bad argument."); | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | if (test_loop) { | ||
239 | debug_delay_loop(); | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | if (file) { | ||
244 | get_exec_times(file, column, &num_jobs, &exec_times); | ||
245 | |||
246 | if (argc - optind < 2) | ||
247 | usage("Arguments missing."); | ||
248 | |||
249 | for (cur_job = 0; cur_job < num_jobs; ++cur_job) { | ||
250 | /* convert the execution time to seconds */ | ||
251 | duration += exec_times[cur_job] * 0.001; | ||
252 | } | ||
253 | } else { | ||
254 | /* | ||
255 | * if we're not reading from the CSV file, then we need | ||
256 | * three parameters | ||
257 | */ | ||
258 | if (argc - optind < 3) | ||
259 | usage("Arguments missing."); | ||
260 | } | ||
261 | |||
262 | wcet_ms = atof(argv[optind + 0]); | ||
263 | period_ms = atof(argv[optind + 1]); | ||
264 | |||
265 | wcet = wcet_ms * __NS_PER_MS; | ||
266 | period = period_ms * __NS_PER_MS; | ||
267 | if (wcet <= 0) | ||
268 | usage("The worst-case execution time must be a " | ||
269 | "positive number."); | ||
270 | if (period <= 0) | ||
271 | usage("The period must be a positive number."); | ||
272 | if (!file && wcet > period) { | ||
273 | usage("The worst-case execution time must not " | ||
274 | "exceed the period."); | ||
275 | } | ||
276 | |||
277 | if (!file) | ||
278 | duration = atof(argv[optind + 2]); | ||
279 | else if (file && num_jobs > 1) | ||
280 | duration += period_ms * 0.001 * (num_jobs - 1); | ||
281 | |||
282 | if (migrate) { | ||
283 | ret = be_migrate_to(cpu); | ||
284 | if (ret < 0) | ||
285 | bail_out("could not migrate to target partition"); | ||
286 | } | ||
287 | |||
288 | ret = sporadic_task_ns(wcet, period, 0, cpu, priority, class, | ||
289 | want_enforcement ? PRECISE_ENFORCEMENT | ||
290 | : NO_ENFORCEMENT, | ||
291 | migrate); | ||
292 | if (ret < 0) | ||
293 | bail_out("could not setup rt task params"); | ||
294 | |||
295 | fd = open(".dgl_locks", O_RDONLY | O_CREAT); | ||
296 | |||
297 | init_litmus(); | ||
298 | |||
299 | ret = task_mode(LITMUS_RT_TASK); | ||
300 | if (ret != 0) | ||
301 | bail_out("could not become RT task"); | ||
302 | |||
303 | if (wait) { | ||
304 | ret = wait_for_ts_release(); | ||
305 | if (ret != 0) | ||
306 | bail_out("wait_for_ts_release()"); | ||
307 | } | ||
308 | |||
309 | od_org = open_new_dgl_sem(fd, 0); | ||
310 | od_new = attach_dgl_sem(fd, 1, od_org); | ||
311 | mask = (1<<od_org) & (1<<od_new); | ||
312 | |||
313 | start = wctime(); | ||
314 | |||
315 | if (file) { | ||
316 | /* use times read from the CSV file */ | ||
317 | for (cur_job = 0; cur_job < num_jobs; ++cur_job) { | ||
318 | /* convert job's length to seconds */ | ||
319 | job(exec_times[cur_job] * 0.001 * scale, | ||
320 | start + duration, mask); | ||
321 | } | ||
322 | } else { | ||
323 | /* conver to seconds and scale */ | ||
324 | while (job(wcet_ms * 0.001 * scale, start + duration, mask)); | ||
325 | } | ||
326 | |||
327 | ret = task_mode(BACKGROUND_TASK); | ||
328 | if (ret != 0) | ||
329 | bail_out("could not become regular task (huh?)"); | ||
330 | |||
331 | if (file) | ||
332 | free(exec_times); | ||
333 | |||
334 | return 0; | ||
335 | } | ||