aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2016-06-23 06:58:12 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2016-07-20 07:42:41 -0400
commit9a8d7e447bbd1525f9f9f4660977072219bbc726 (patch)
treeca4df9b22144b2d7e53c8a1c637dff6f8bb6629c
parent98510c506a16b2a444eb6615ec8d78bc7c64e0aa (diff)
rtspin: Add sporadic task mode
While at it, also fix exec-times-from-CSV support.
-rw-r--r--bin/rtspin.c259
1 files changed, 160 insertions, 99 deletions
diff --git a/bin/rtspin.c b/bin/rtspin.c
index f14b549..d33e9b3 100644
--- a/bin/rtspin.c
+++ b/bin/rtspin.c
@@ -1,5 +1,6 @@
1#include <sys/time.h> 1#include <sys/time.h>
2 2
3#include <fcntl.h>
3#include <stdio.h> 4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <unistd.h> 6#include <unistd.h>
@@ -16,14 +17,20 @@
16 17
17const char *usage_msg = 18const char *usage_msg =
18 "Usage: (1) rtspin OPTIONS WCET PERIOD DURATION\n" 19 "Usage: (1) rtspin OPTIONS WCET PERIOD DURATION\n"
19 " (2) rtspin OPTIONS -F FILE [-C COLUMN] WCET PERIOD\n" 20 " (2) rtspin -S [INPUT] WCET PERIOD DURATION\n"
20 " (3) rtspin -l\n" 21 " (3) rtspin OPTIONS -F FILE [-C COLUMN] WCET PERIOD [DURATION]\n"
21 " (4) rtspin -B\n" 22 " (4) rtspin -l\n"
23 " (5) rtspin -B -m FOOTPRINT\n"
22 "\n" 24 "\n"
23 "Modes: (1) run as periodic task with given WCET and PERIOD\n" 25 "Modes: (1) run as periodic task with given WCET and PERIOD\n"
24 " (2) as (1), but load per-job execution times from a CSV file\n" 26 " (2) run as sporadic task with given WCET and PERIOD,\n"
25 " (3) Run calibration loop (how accurately are target runtimes met?)\n" 27 " using INPUT as a file from which events are received\n"
26 " (4) Run background, non-real-time cache-thrashing loop (w/ -m).\n" 28 " by means of blocking reads (default: read from STDIN)\n"
29 " (3) as (1) or (2), but load per-job execution times from\n"
30 " a CSV file\n"
31 " (4) Run calibration loop (how accurately are target\n"
32 " runtimes met?)\n"
33 " (5) Run background, non-real-time cache-thrashing loop.\n"
27 "\n" 34 "\n"
28 "Required arguments:\n" 35 "Required arguments:\n"
29 " WCET, PERIOD reservation parameters (in ms)\n" 36 " WCET, PERIOD reservation parameters (in ms)\n"
@@ -49,7 +56,11 @@ const char *usage_msg =
49 " -w wait for synchronous release\n" 56 " -w wait for synchronous release\n"
50 "\n" 57 "\n"
51 " -F FILE load per-job execution times from CSV file\n" 58 " -F FILE load per-job execution times from CSV file\n"
52 " -C COLUMNS specify column to read per-job execution times from (default: 1)\n" 59 " -C COLUMN specify column to read per-job execution times from (default: 1)\n"
60 "\n"
61 " -S [FILE] read from FILE to trigger sporadic job releases\n"
62 " default w/o -S: periodic job releases\n"
63 " default if FILE is omitted: read from STDIN\n"
53 "\n" 64 "\n"
54 " -X PROTOCOL access a shared resource protected by a locking protocol\n" 65 " -X PROTOCOL access a shared resource protected by a locking protocol\n"
55 " -L CS-LENGTH simulate a critical section length of CS-LENGTH milliseconds\n" 66 " -L CS-LENGTH simulate a critical section length of CS-LENGTH milliseconds\n"
@@ -79,8 +90,7 @@ static void usage(char *error) {
79 */ 90 */
80static int skip_to_next_line(FILE *fstream) 91static int skip_to_next_line(FILE *fstream)
81{ 92{
82 int ch; 93 int ch; for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream));
83 for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream));
84 return ch; 94 return ch;
85} 95}
86 96
@@ -186,10 +196,14 @@ static int loop_for(double exec_time, double emergency_exit)
186 now = cputime(); 196 now = cputime();
187 last_loop = now - loop_start; 197 last_loop = now - loop_start;
188 if (emergency_exit && wctime() > emergency_exit) { 198 if (emergency_exit && wctime() > emergency_exit) {
189 /* Oops --- this should only be possible if the execution time tracking 199 /* Oops --- this should only be possible if the
190 * is broken in the LITMUS^RT kernel. */ 200 * execution time tracking is broken in the LITMUS^RT
191 fprintf(stderr, "!!! rtspin/%d emergency exit!\n", getpid()); 201 * kernel or the user specified infeasible parameters.
192 fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n"); 202 */
203 fprintf(stderr, "!!! rtspin/%d emergency exit!\n",
204 getpid());
205 fprintf(stderr, "Reached experiment timeout while "
206 "spinning.\n");
193 break; 207 break;
194 } 208 }
195 } 209 }
@@ -216,37 +230,53 @@ static void debug_delay_loop(void)
216 } 230 }
217} 231}
218 232
219static int job(double exec_time, double program_end, int lock_od, double cs_length) 233static int wait_for_input(int event_fd)
234{
235 /* We do a blocking read, accepting up to 4KiB of data.
236 * For simplicity, for now, if there's more than 4KiB of data,
237 * we treat this as multiple jobs. Note that this means that
238 * tardiness can result in coalesced jobs. Ideally, there should
239 * be some sort of configurable job boundary marker, but that's
240 * not supported in this basic version yet. Patches welcome.
241 */
242 char buf[4096];
243 size_t consumed;
244
245 consumed = read(event_fd, buf, sizeof(buf));
246
247 if (consumed == 0)
248 fprintf(stderr, "reached end-of-file on input event stream\n");
249 if (consumed < 0)
250 fprintf(stderr, "error reading input event stream (%m)\n");
251
252 return consumed > 0;
253}
254
255static void job(double exec_time, double program_end, int lock_od, double cs_length)
220{ 256{
221 double chunk1, chunk2; 257 double chunk1, chunk2;
222 258
223 if (wctime() > program_end) 259 if (lock_od >= 0) {
224 return 0; 260 /* simulate critical section somewhere in the middle */
225 else { 261 chunk1 = drand48() * (exec_time - cs_length);
226 if (lock_od >= 0) { 262 chunk2 = exec_time - cs_length - chunk1;
227 /* simulate critical section somewhere in the middle */
228 chunk1 = drand48() * (exec_time - cs_length);
229 chunk2 = exec_time - cs_length - chunk1;
230 263
231 /* non-critical section */ 264 /* non-critical section */
232 loop_for(chunk1, program_end + 1); 265 loop_for(chunk1, program_end + 1);
233 266
234 /* critical section */ 267 /* critical section */
235 litmus_lock(lock_od); 268 litmus_lock(lock_od);
236 loop_for(cs_length, program_end + 1); 269 loop_for(cs_length, program_end + 1);
237 litmus_unlock(lock_od); 270 litmus_unlock(lock_od);
238 271
239 /* non-critical section */ 272 /* non-critical section */
240 loop_for(chunk2, program_end + 2); 273 loop_for(chunk2, program_end + 2);
241 } else { 274 } else {
242 loop_for(exec_time, program_end + 1); 275 loop_for(exec_time, program_end + 1);
243 }
244 sleep_next_period();
245 return 1;
246 } 276 }
247} 277}
248 278
249#define OPTSTR "p:c:wlveo:F:s:m:q:r:X:L:Q:iRu:Bhd:C:" 279#define OPTSTR "p:c:wlveo:F:s:m:q:r:X:L:Q:iRu:Bhd:C:S::"
250int main(int argc, char** argv) 280int main(int argc, char** argv)
251{ 281{
252 int ret; 282 int ret;
@@ -274,9 +304,12 @@ int main(int argc, char** argv)
274 int cur_job = 0, num_jobs = 0; 304 int cur_job = 0, num_jobs = 0;
275 struct rt_task param; 305 struct rt_task param;
276 306
277 int rss=0; 307 int rss = 0;
278 int idx; 308 int idx;
279 309
310 int sporadic = 0; /* trigger jobs sporadically? */
311 int event_fd = -1; /* file descriptor for sporadic events */
312
280 int verbose = 0; 313 int verbose = 0;
281 unsigned int job_no; 314 unsigned int job_no;
282 struct control_page* cp; 315 struct control_page* cp;
@@ -333,6 +366,19 @@ int main(int argc, char** argv)
333 case 'F': 366 case 'F':
334 file = optarg; 367 file = optarg;
335 break; 368 break;
369 case 'S':
370 sporadic = 1;
371 if (!optarg || strcmp(optarg, "-") == 0)
372 event_fd = STDIN_FILENO;
373 else
374 event_fd = open(optarg, O_RDONLY);
375 if (event_fd == -1) {
376 fprintf(stderr, "Could not open file '%s' "
377 "(%m)\n", optarg);
378 usage("-S requires a valid file path or '-' "
379 "for STDIN.");
380 }
381 break;
336 case 'm': 382 case 'm':
337 nr_of_pages = atoi(optarg); 383 nr_of_pages = atoi(optarg);
338 break; 384 break;
@@ -426,24 +472,8 @@ int main(int argc, char** argv)
426 return 0; 472 return 0;
427 } 473 }
428 474
429 if (file) { 475 if (argc - optind < 3 || (argc - optind < 2 && !file))
430 get_exec_times(file, column, &num_jobs, &exec_times); 476 usage("Arguments missing.");
431
432 if (argc - optind < 2)
433 usage("Arguments missing.");
434
435 for (cur_job = 0; cur_job < num_jobs; ++cur_job) {
436 /* convert the execution time to seconds */
437 duration += exec_times[cur_job] * 0.001;
438 }
439 } else {
440 /*
441 * if we're not reading from the CSV file, then we need
442 * three parameters
443 */
444 if (argc - optind < 3)
445 usage("Arguments missing.");
446 }
447 477
448 wcet_ms = atof(argv[optind + 0]); 478 wcet_ms = atof(argv[optind + 0]);
449 period_ms = atof(argv[optind + 1]); 479 period_ms = atof(argv[optind + 1]);
@@ -466,10 +496,15 @@ int main(int argc, char** argv)
466 "exceed the period."); 496 "exceed the period.");
467 } 497 }
468 498
469 if (!file) 499 if (file)
470 duration = atof(argv[optind + 2]); 500 get_exec_times(file, column, &num_jobs, &exec_times);
471 else if (file && num_jobs > 1) 501
472 duration += period_ms * 0.001 * (num_jobs - 1); 502 if (argc - optind < 3 && file)
503 /* If duration is not given explicitly,
504 * take duration from file. */
505 duration = num_jobs * period_ms * 0.001;
506 else
507 duration = atof(argv[optind + 2]);
473 508
474 if (migrate) { 509 if (migrate) {
475 ret = be_migrate_to_domain(cluster); 510 ret = be_migrate_to_domain(cluster);
@@ -541,51 +576,77 @@ int main(int argc, char** argv)
541 start = wctime(); 576 start = wctime();
542 } 577 }
543 578
544 if (file) { 579
545 /* use times read from the CSV file */ 580 /* main job loop */
546 for (cur_job = 0; cur_job < num_jobs; ++cur_job) { 581 cur_job = 0;
547 /* convert job's length to seconds */ 582 while (1) {
548 job(exec_times[cur_job] * 0.001 * scale, 583 double acet; /* actual execution time */
549 start + duration, 584
550 lock_od, cs_length * 0.001); 585 if (sporadic) {
586 /* sporadic job activations, sleep until
587 * we receive an "event" (= any data) from
588 * our input event channel */
589 if (!wait_for_input(event_fd))
590 /* error out of something goes wrong */
591 break;
551 } 592 }
552 } else { 593
553 double acet; 594 /* first, check if we have reached the end of the run */
554 do { 595 if (wctime() > start + duration)
555 if (verbose) { 596 break;
556 get_job_no(&job_no); 597
557 printf("rtspin/%d:%u @ %.4fms\n", gettid(), 598 if (verbose) {
558 job_no, (wctime() - start) * 1000); 599 get_job_no(&job_no);
559 if (cp) { 600 printf("rtspin/%d:%u @ %.4fms\n", gettid(),
560 double deadline, current; 601 job_no, (wctime() - start) * 1000);
561 deadline = cp->deadline * 1e-9; 602 if (cp) {
562 current = monotime(); 603 double deadline, current;
563 printf("\tdeadline: %" PRIu64 "ns (=%.2fs)\n", 604 deadline = cp->deadline * 1e-9;
564 (uint64_t) cp->deadline, deadline); 605 current = monotime();
565 printf("\tcurrent time: %.2fs, " 606 printf("\tdeadline: %" PRIu64 "ns (=%.2fs)\n",
566 "time until deadline: %.2fms\n", 607 (uint64_t) cp->deadline, deadline);
567 current, (deadline - current) * 1000); 608 printf("\tcurrent time: %.2fs, "
568 } 609 "time until deadline: %.2fms\n",
569 if (report_interrupts && cp) { 610 current, (deadline - current) * 1000);
570 uint64_t irq = cp->irq_count; 611 }
571 612 if (report_interrupts && cp) {
572 printf("\ttotal interrupts: %" PRIu64 613 uint64_t irq = cp->irq_count;
573 "; delta: %" PRIu64 "\n", 614
574 irq, irq - last_irq_count); 615 printf("\ttotal interrupts: %" PRIu64
575 last_irq_count = irq; 616 "; delta: %" PRIu64 "\n",
576 } 617 irq, irq - last_irq_count);
618 last_irq_count = irq;
577 } 619 }
578 /* convert to seconds and scale */ 620 }
621
622 /* figure out for how long this job should use the CPU */
623 cur_job++;
624
625 if (file) {
626 /* read from provided CSV file and convert to seconds */
627 acet = exec_times[cur_job % num_jobs] * 0.001;
628 } else {
629 /* randomize and convert to seconds */
579 acet = (wcet_ms - drand48() * underrun_ms) * 0.001; 630 acet = (wcet_ms - drand48() * underrun_ms) * 0.001;
580 if (acet < 0) 631 if (acet < 0)
581 acet = 0; 632 acet = 0;
582 acet *= scale; 633 }
583 if (verbose) 634 /* scale exec time */
584 printf("\ttarget exec. time: %6.2fms (%.2f%% of WCET)\n", 635 acet *= scale;
585 acet * 1000, 636
586 (acet * 1000 / wcet_ms) * 100); 637 if (verbose)
587 } while (job(acet, start + duration, 638 printf("\ttarget exec. time: %6.2fms (%.2f%% of WCET)\n",
588 lock_od, cs_length * 0.001)); 639 acet * 1000,
640 (acet * 1000 / wcet_ms) * 100);
641
642 /* burn cycles */
643 job(acet, start + duration, lock_od, cs_length * 0.001);
644
645 /* wait for periodic job activation (unless sporadic) */
646 if (!sporadic) {
647 /* periodic job activations */
648 sleep_next_period();
649 }
589 } 650 }
590 651
591 ret = task_mode(BACKGROUND_TASK); 652 ret = task_mode(BACKGROUND_TASK);
@@ -595,7 +656,7 @@ int main(int argc, char** argv)
595 if (file) 656 if (file)
596 free(exec_times); 657 free(exec_times);
597 658
598 if(base != MAP_FAILED) 659 if (base != MAP_FAILED)
599 munlock(base, rss); 660 munlock(base, rss);
600 661
601 return 0; 662 return 0;