aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNamhoon Kim <namhoonk@cs.unc.edu>2015-01-08 14:04:12 -0500
committerNamhoon Kim <namhoonk@cs.unc.edu>2015-01-08 14:04:12 -0500
commit5fd42b4dc877890668affe69f8cecc968e277148 (patch)
tree1a85442e5b43ad340483e5f153c05d5908c41ac8
parentd7d397d028d6c755cff140f2ef54537f608d3e62 (diff)
Added mc2spin
-rw-r--r--Makefile7
-rw-r--r--bin/mc2spin.c541
-rw-r--r--include/litmus.h3
-rwxr-xr-xmc2spinbin0 -> 102692 bytes
-rw-r--r--src/syscalls.c5
5 files changed, 554 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 34c428f..70e79d4 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,7 @@ AR := ${CROSS_COMPILE}${AR}
73 73
74all = lib ${rt-apps} 74all = lib ${rt-apps}
75rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ 75rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \
76 base_mt_task uncache runtests resctrl 76 base_mt_task uncache runtests resctrl mc2spin
77 77
78.PHONY: all lib clean dump-config TAGS tags cscope help doc 78.PHONY: all lib clean dump-config TAGS tags cscope help doc
79 79
@@ -168,7 +168,8 @@ litmus-headers = \
168 include/litmus/rt_param.h \ 168 include/litmus/rt_param.h \
169 include/litmus/fpmath.h \ 169 include/litmus/fpmath.h \
170 include/litmus/unistd_32.h \ 170 include/litmus/unistd_32.h \
171 include/litmus/unistd_64.h 171 include/litmus/unistd_64.h \
172 include/litmus/mc2_common.h
172 173
173unistd-headers = \ 174unistd-headers = \
174 $(foreach file,${unistd-${ARCH}},arch/${include-${ARCH}}/include/$(file)) 175 $(foreach file,${unistd-${ARCH}},arch/${include-${ARCH}}/include/$(file))
@@ -236,6 +237,8 @@ lib-measure_syscall = -lm
236 237
237obj-resctrl = resctrl.o 238obj-resctrl = resctrl.o
238 239
240obj-mc2spin = mc2spin.o common.o
241lib-mc2spin = -lrt
239 242
240# ############################################################################## 243# ##############################################################################
241# Build everything that depends on liblitmus. 244# Build everything that depends on liblitmus.
diff --git a/bin/mc2spin.c b/bin/mc2spin.c
new file mode 100644
index 0000000..1ef3082
--- /dev/null
+++ b/bin/mc2spin.c
@@ -0,0 +1,541 @@
1#include <sys/time.h>
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <time.h>
7#include <string.h>
8#include <assert.h>
9#include <limits.h>
10
11
12#include "litmus.h"
13#include "common.h"
14
15
16
17static void usage(char *error) {
18 fprintf(stderr, "Error: %s\n", error);
19 fprintf(stderr,
20 "Usage:\n"
21 " rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n"
22 " rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n"
23 " rt_spin -l\n"
24 "\n"
25 "COMMON-OPTS = [-w] [-s SCALE]\n"
26 " [-p PARTITION/CLUSTER [-z CLUSTER SIZE]] [-c CLASS] [-m CRITICALITY LEVEL]\n"
27 " [-X LOCKING-PROTOCOL] [-L CRITICAL SECTION LENGTH] [-Q RESOURCE-ID]\n"
28 " [-i [start,end]:[start,end]...]\n"
29 "\n"
30 "WCET and PERIOD are milliseconds, DURATION is seconds.\n"
31 "CRITICAL SECTION LENGTH is in milliseconds.\n");
32 exit(EXIT_FAILURE);
33}
34
35/*
36 * returns the character that made processing stop, newline or EOF
37 */
38static int skip_to_next_line(FILE *fstream)
39{
40 int ch;
41 for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream));
42 return ch;
43}
44
45static void skip_comments(FILE *fstream)
46{
47 int ch;
48 for (ch = fgetc(fstream); ch == '#'; ch = fgetc(fstream))
49 skip_to_next_line(fstream);
50 ungetc(ch, fstream);
51}
52
53static void get_exec_times(const char *file, const int column,
54 int *num_jobs, double **exec_times)
55{
56 FILE *fstream;
57 int cur_job, cur_col, ch;
58 *num_jobs = 0;
59
60 fstream = fopen(file, "r");
61 if (!fstream)
62 bail_out("could not open execution time file");
63
64 /* figure out the number of jobs */
65 do {
66 skip_comments(fstream);
67 ch = skip_to_next_line(fstream);
68 if (ch != EOF)
69 ++(*num_jobs);
70 } while (ch != EOF);
71
72 if (-1 == fseek(fstream, 0L, SEEK_SET))
73 bail_out("rewinding file failed");
74
75 /* allocate space for exec times */
76 *exec_times = calloc(*num_jobs, sizeof(*exec_times));
77 if (!*exec_times)
78 bail_out("couldn't allocate memory");
79
80 for (cur_job = 0; cur_job < *num_jobs && !feof(fstream); ++cur_job) {
81
82 skip_comments(fstream);
83
84 for (cur_col = 1; cur_col < column; ++cur_col) {
85 /* discard input until we get to the column we want */
86 int unused __attribute__ ((unused)) = fscanf(fstream, "%*s,");
87 }
88
89 /* get the desired exec. time */
90 if (1 != fscanf(fstream, "%lf", (*exec_times)+cur_job)) {
91 fprintf(stderr, "invalid execution time near line %d\n",
92 cur_job);
93 exit(EXIT_FAILURE);
94 }
95
96 skip_to_next_line(fstream);
97 }
98
99 assert(cur_job == *num_jobs);
100 fclose(fstream);
101}
102
103#define NUMS 4096
104static int num[NUMS];
105static char* progname;
106
107static int loop_once(void)
108{
109 int i, j = 0;
110 for (i = 0; i < NUMS; i++)
111 j += num[i]++;
112 return j;
113}
114
115static int loop_for(double exec_time, double emergency_exit)
116{
117 double last_loop = 0, loop_start;
118 int tmp = 0;
119
120 double start = cputime();
121 double now = cputime();
122
123 while (now + last_loop < start + exec_time) {
124 loop_start = now;
125 tmp += loop_once();
126 now = cputime();
127 last_loop = now - loop_start;
128 if (emergency_exit && wctime() > emergency_exit) {
129 /* Oops --- this should only be possible if the execution time tracking
130 * is broken in the LITMUS^RT kernel. */
131 fprintf(stderr, "!!! rtspin/%d emergency exit!\n", getpid());
132 fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n");
133 break;
134 }
135 }
136
137 return tmp;
138}
139
140
141static void debug_delay_loop(void)
142{
143 double start, end, delay;
144
145 while (1) {
146 for (delay = 0.5; delay > 0.01; delay -= 0.01) {
147 start = wctime();
148 loop_for(delay, 0);
149 end = wctime();
150 printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n",
151 delay,
152 end - start,
153 end - start - delay,
154 100 * (end - start - delay) / delay);
155 }
156 }
157}
158
159static int job(double exec_time, double program_end, int lock_od, double cs_length)
160{
161 double chunk1, chunk2;
162
163 if (wctime() > program_end)
164 return 0;
165 else {
166 if (lock_od >= 0) {
167 /* simulate critical section somewhere in the middle */
168 chunk1 = drand48() * (exec_time - cs_length);
169 chunk2 = exec_time - cs_length - chunk1;
170
171 /* non-critical section */
172 loop_for(chunk1, program_end + 1);
173
174 /* critical section */
175 litmus_lock(lock_od);
176 loop_for(cs_length, program_end + 1);
177 litmus_unlock(lock_od);
178
179 /* non-critical section */
180 loop_for(chunk2, program_end + 2);
181 } else {
182 loop_for(exec_time, program_end + 1);
183 }
184 sleep_next_period();
185 return 1;
186 }
187}
188
189struct lt_interval* parse_td_intervals(int num, char* optarg, unsigned int *num_intervals)
190{
191 int i, matched;
192 struct lt_interval *slots = malloc(sizeof(slots[0]) * num);
193 char** arg = (char**)malloc(sizeof(char*) * num);
194 char *token, *saveptr;
195 double start, end;
196
197 for (i = 0; i < num; i++) {
198 arg[i] = (char*)malloc(sizeof(char)*100);
199 }
200
201 i = 0;
202 token = strtok_r(optarg, ":", &saveptr);
203 while(token != NULL) {
204 sprintf(arg[i++], "%s", token);
205 token = strtok_r(NULL, ":", &saveptr);
206 }
207
208 *num_intervals = 0;
209
210 for (i=0; i<num; i++) {
211 matched = sscanf(arg[i], "[%lf,%lf]", &start, &end);
212 if (matched != 2) {
213 fprintf(stderr, "could not parse '%s' as interval\n", arg[i]);
214 exit(5);
215 }
216 if (start < 0) {
217 fprintf(stderr, "interval %s: must not start before zero\n", arg[i]);
218 exit(5);
219 }
220 if (end <= start) {
221 fprintf(stderr, "interval %s: end before start\n", arg[i]);
222 exit(5);
223 }
224
225 slots[i].start = ms2ns(start);
226 slots[i].end = ms2ns(end);
227
228 if (i > 0 && slots[i - 1].end >= slots[i].start) {
229 fprintf(stderr, "interval %s: overlaps with previous interval\n", arg[i]);
230 exit(5);
231 }
232
233 (*num_intervals)++;
234 }
235
236 for (i=0; i<num; i++) {
237 free(arg[i]);
238 }
239 free(arg);
240 return slots;
241}
242
243#define OPTSTR "p:c:wlveo:f:s:q:X:L:Q:vh:m:i:b:"
244int main(int argc, char** argv)
245{
246 int ret;
247 lt_t wcet;
248 lt_t period;
249 lt_t hyperperiod;
250 lt_t budget;
251 double wcet_ms, period_ms, hyperperiod_ms, budget_ms;
252 unsigned int priority = LITMUS_LOWEST_PRIORITY;
253 int migrate = 0;
254 int cluster = 0;
255 int opt;
256 int wait = 0;
257 int test_loop = 0;
258 int column = 1;
259 const char *file = NULL;
260 int want_enforcement = 0;
261 double duration = 0, start = 0;
262 double *exec_times = NULL;
263 double scale = 1.0;
264 task_class_t class = RT_CLASS_HARD;
265 int cur_job = 0, num_jobs = 0;
266 struct rt_task param;
267 struct mc2_task mc2_param;
268 struct reservation_config config;
269 int res_type = SPORADIC_POLLING;
270 int n_str, num_int = 0;
271
272 int verbose = 0;
273 unsigned int job_no;
274
275 /* locking */
276 int lock_od = -1;
277 int resource_id = 0;
278 const char *lock_namespace = "./rtspin-locks";
279 int protocol = -1;
280 double cs_length = 1; /* millisecond */
281
282 progname = argv[0];
283
284 /* default for reservation */
285 config.id = 0;
286 config.priority = LITMUS_NO_PRIORITY; /* use EDF by default */
287 config.cpu = 0;
288
289 mc2_param.crit = CRIT_LEVEL_C;
290
291 hyperperiod_ms = 1000;
292 budget_ms = 10;
293
294 while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
295 switch (opt) {
296 case 'w':
297 wait = 1;
298 break;
299 case 'p':
300 cluster = atoi(optarg);
301 migrate = 1;
302 config.cpu = cluster;
303 break;
304 case 'q':
305 priority = atoi(optarg);
306 if (!litmus_is_valid_fixed_prio(priority))
307 usage("Invalid priority.");
308 break;
309 case 'c':
310 class = str2class(optarg);
311 if (class == -1)
312 usage("Unknown task class.");
313 break;
314 case 'e':
315 want_enforcement = 1;
316 break;
317 case 'l':
318 test_loop = 1;
319 break;
320 case 'o':
321 column = atoi(optarg);
322 break;
323 case 'f':
324 file = optarg;
325 break;
326 case 's':
327 scale = atof(optarg);
328 break;
329 case 'X':
330 protocol = lock_protocol_for_name(optarg);
331 if (protocol < 0)
332 usage("Unknown locking protocol specified.");
333 break;
334 case 'L':
335 cs_length = atof(optarg);
336 if (cs_length <= 0)
337 usage("Invalid critical section length.");
338 break;
339 case 'Q':
340 resource_id = atoi(optarg);
341 if (resource_id <= 0 && strcmp(optarg, "0"))
342 usage("Invalid resource ID.");
343 break;
344 case 'v':
345 verbose = 1;
346 break;
347 case 'm':
348 mc2_param.crit = atoi(optarg);
349 if (mc2_param.crit == CRIT_LEVEL_A) {
350 res_type = TABLE_DRIVEN;
351 }
352 else if (mc2_param.crit == CRIT_LEVEL_B) {
353 res_type = PERIODIC_POLLING;
354 }
355 else
356 usage("Invalid criticality level.");
357 break;
358 case 'h':
359 hyperperiod_ms = atof(optarg);
360 break;
361 case 'b':
362 budget_ms = atof(optarg);
363 break;
364 case 'i':
365 n_str = strlen(optarg);
366 while(n_str--) {
367 if (optarg[n_str] == ',')
368 num_int++;
369 }
370 if (!num_int)
371 usage("Bad argument.");
372 config.table_driven_params.intervals = parse_td_intervals(num_int, optarg, &config.table_driven_params.num_intervals);
373 break;
374 case ':':
375 usage("Argument missing.");
376 break;
377 case '?':
378 default:
379 usage("Bad argument.");
380 break;
381 }
382 }
383
384 if (test_loop) {
385 debug_delay_loop();
386 return 0;
387 }
388
389 srand(getpid());
390
391 if (file) {
392 get_exec_times(file, column, &num_jobs, &exec_times);
393
394 if (argc - optind < 2)
395 usage("Arguments missing.");
396
397 for (cur_job = 0; cur_job < num_jobs; ++cur_job) {
398 /* convert the execution time to seconds */
399 duration += exec_times[cur_job] * 0.001;
400 }
401 } else {
402 /*
403 * if we're not reading from the CSV file, then we need
404 * three parameters
405 */
406 if (argc - optind < 3)
407 usage("Arguments missing.");
408 }
409
410 wcet_ms = atof(argv[optind + 0]);
411 period_ms = atof(argv[optind + 1]);
412
413 wcet = ms2ns(wcet_ms);
414 period = ms2ns(period_ms);
415 budget = ms2ns(budget_ms);
416 hyperperiod = ms2ns(hyperperiod_ms);
417
418 if (wcet <= 0)
419 usage("The worst-case execution time must be a "
420 "positive number.");
421 if (period <= 0)
422 usage("The period must be a positive number.");
423 if (!file && wcet > period) {
424 usage("The worst-case execution time must not "
425 "exceed the period.");
426 }
427
428 if (!file)
429 duration = atof(argv[optind + 2]);
430 else if (file && num_jobs > 1)
431 duration += period_ms * 0.001 * (num_jobs - 1);
432
433 if (migrate) {
434 ret = be_migrate_to_domain(cluster);
435 if (ret < 0)
436 bail_out("could not migrate to target partition or cluster.");
437 }
438
439 /* reservation config */
440 config.id = gettid();
441
442 if (hyperperiod%period != 0 ) {
443 bail_out("hyperperiod must be multiple of period");
444 }
445
446 if (mc2_param.crit == CRIT_LEVEL_A) {
447 config.table_driven_params.major_cycle_length = period;
448 }
449 else if (mc2_param.crit == CRIT_LEVEL_B) {
450 config.polling_params.budget = budget;
451 config.polling_params.period = period;
452 config.polling_params.offset = 0;
453 config.polling_params.relative_deadline = 0;
454 if (config.polling_params.budget > config.polling_params.period) {
455 usage("The budget must not exceed the period.");
456 }
457 }
458
459 /* create a reservation */
460 ret = reservation_create(res_type, &config);
461 if (ret < 0) {
462 bail_out("failed to create reservation.");
463 }
464
465 init_rt_task_param(&param);
466 param.exec_cost = wcet;
467 param.period = period;
468 param.priority = priority;
469 param.cls = class;
470 param.release_policy = TASK_PERIODIC;
471 param.budget_policy = (want_enforcement) ?
472 PRECISE_ENFORCEMENT : NO_ENFORCEMENT;
473 if (migrate) {
474 param.cpu = gettid();
475 }
476 ret = set_rt_task_param(gettid(), &param);
477 if (ret < 0)
478 bail_out("could not setup rt task params");
479
480 mc2_param.pid = gettid();
481 mc2_param.hyperperiod = hyperperiod;
482 ret = set_mc2_task_param(gettid(), &mc2_param);
483 if (ret < 0)
484 bail_out("could not setup mc2 task params");
485 init_litmus();
486
487 start = wctime();
488 ret = task_mode(LITMUS_RT_TASK);
489 if (ret != 0)
490 bail_out("could not become RT task");
491
492 if (protocol >= 0) {
493 /* open reference to semaphore */
494 lock_od = litmus_open_lock(protocol, resource_id, lock_namespace, &cluster);
495 if (lock_od < 0) {
496 perror("litmus_open_lock");
497 usage("Could not open lock.");
498 }
499 }
500
501
502 if (wait) {
503 ret = wait_for_ts_release();
504 if (ret != 0)
505 bail_out("wait_for_ts_release()");
506 start = wctime();
507 }
508
509 if (file) {
510 /* use times read from the CSV file */
511 for (cur_job = 0; cur_job < num_jobs; ++cur_job) {
512 /* convert job's length to seconds */
513 job(exec_times[cur_job] * 0.001 * scale,
514 start + duration,
515 lock_od, cs_length * 0.001);
516 }
517 } else {
518 do {
519 if (verbose) {
520 get_job_no(&job_no);
521 printf("rtspin/%d:%u @ %.4fms\n", gettid(),
522 job_no, (wctime() - start) * 1000);
523 }
524 /* convert to seconds and scale */
525 } while (job(wcet_ms * 0.001 * scale, start + duration,
526 lock_od, cs_length * 0.001));
527 }
528
529 ret = task_mode(BACKGROUND_TASK);
530 if (ret != 0)
531 bail_out("could not become regular task (huh?)");
532
533 if (file)
534 free(exec_times);
535
536 reservation_destroy(gettid(), config.cpu);
537 if (mc2_param.crit == CRIT_LEVEL_A) {
538 free(config.table_driven_params.intervals);
539 }
540 return 0;
541}
diff --git a/include/litmus.h b/include/litmus.h
index 3bd6b92..b90a83d 100644
--- a/include/litmus.h
+++ b/include/litmus.h
@@ -40,6 +40,8 @@ extern "C" {
40 40
41#include "migration.h" 41#include "migration.h"
42 42
43#include "litmus/mc2_common.h"
44
43/** 45/**
44 * @private 46 * @private
45 * Number of semaphore protocol object types 47 * Number of semaphore protocol object types
@@ -420,6 +422,7 @@ int reservation_create(int rtype, void *config);
420 422
421int reservation_destroy(unsigned int reservation_id, int cpu); 423int reservation_destroy(unsigned int reservation_id, int cpu);
422 424
425int set_mc2_task_param(pid_t pid, struct mc2_task* param);
423 426
424#ifdef __cplusplus 427#ifdef __cplusplus
425} 428}
diff --git a/mc2spin b/mc2spin
new file mode 100755
index 0000000..00b20c0
--- /dev/null
+++ b/mc2spin
Binary files differ
diff --git a/src/syscalls.c b/src/syscalls.c
index fbb8604..b07d135 100644
--- a/src/syscalls.c
+++ b/src/syscalls.c
@@ -96,3 +96,8 @@ int reservation_destroy(unsigned int reservation_id, int cpu)
96{ 96{
97 return syscall(__NR_reservation_destroy, reservation_id, cpu); 97 return syscall(__NR_reservation_destroy, reservation_id, cpu);
98} 98}
99
100int set_mc2_task_param(pid_t pid, struct mc2_task *param)
101{
102 return syscall(__NR_set_mc2_task_param, pid, param);
103} \ No newline at end of file