diff options
| author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2016-06-23 06:58:12 -0400 |
|---|---|---|
| committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2016-07-20 07:42:41 -0400 |
| commit | 9a8d7e447bbd1525f9f9f4660977072219bbc726 (patch) | |
| tree | ca4df9b22144b2d7e53c8a1c637dff6f8bb6629c | |
| parent | 98510c506a16b2a444eb6615ec8d78bc7c64e0aa (diff) | |
rtspin: Add sporadic task mode
While at it, also fix exec-times-from-CSV support.
| -rw-r--r-- | bin/rtspin.c | 259 |
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 | ||
| 17 | const char *usage_msg = | 18 | const 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 | */ |
| 80 | static int skip_to_next_line(FILE *fstream) | 91 | static 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 | ||
| 219 | static int job(double exec_time, double program_end, int lock_od, double cs_length) | 233 | static 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 | |||
| 255 | static 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::" |
| 250 | int main(int argc, char** argv) | 280 | int 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; |
