#include #include #include #include #include #include #include #include "litmus.h" #include "common.h" const char *usage_msg = "Usage: resctrl OPTIONS [INTERVAL-START,INTERVAL-END]*\n" " -n ID create new reservation with id ID\n" " -a PID attach already-running process PID to reservation\n" " -r ID specify which reservation to attach to (not needed with -n)\n" " -t TYPE type of reservation (polling-periodic, polling-sporadic, table-driven)\n" " -c CPU physical partition or cluster to assign to\n" " -b BUDGET polling reservation budget (in ms, default: 10ms)\n" " -p PERIOD polling reservation period (in ms, default: 100ms)\n" " -d DEADLINE relative deadline, implicit by default (in ms)\n" " -o OFFSET offset (also known as phase), zero by default (in ms)\n" " -q PRIORITY priority to use (EDF by default, highest priority = 1)\n" " -m MAJOR-CYCLE major cycle length (in ms, for table-driven reservations) \n" "\n"; void usage(char *error) { fprintf(stderr, "%s\n%s", error, usage_msg); exit(1); } static void attach_task(int attach_pid, struct reservation_config *config) { int ret; struct rt_task param; struct sched_param linux_param; ret = be_migrate_thread_to_cpu(attach_pid, config->cpu); if (ret < 0) { fprintf(stderr, "failed to migrate task %d to CPU %d\n", attach_pid, config->cpu); exit(4); } init_rt_task_param(¶m); /* dummy values */ param.exec_cost = ms2ns(100); param.period = ms2ns(100); /* specify reservation as "virtual" CPU */ param.cpu = config->id; ret = set_rt_task_param(attach_pid, ¶m); if (ret < 0) { fprintf(stderr, "failed to set RT task parameters for task %d (%m)\n", attach_pid); exit(2); } linux_param.sched_priority = 0; ret = sched_setscheduler(attach_pid, SCHED_LITMUS, &linux_param); if (ret < 0) { fprintf(stderr, "failed to transition task %d to LITMUS^RT class (%m)\n", attach_pid); exit(3); } } static struct lt_interval* parse_td_intervals(int argc, char** argv, unsigned int *num_intervals, lt_t major_cycle) { int i, matched; struct lt_interval *slots = malloc(sizeof(slots[0]) * argc); double start, end; *num_intervals = 0; for (i = 0; i < argc; i++) { matched = sscanf(argv[i], "[%lf,%lf]", &start, &end); if (matched != 2) { fprintf(stderr, "could not parse '%s' as interval\n", argv[i]); exit(5); } if (start < 0) { fprintf(stderr, "interval %s: must not start before zero\n", argv[i]); exit(5); } if (end <= start) { fprintf(stderr, "interval %s: end before start\n", argv[i]); exit(5); } slots[i].start = ms2ns(start); slots[i].end = ms2ns(end); if (i > 0 && slots[i - 1].end >= slots[i].start) { fprintf(stderr, "interval %s: overlaps with previous interval\n", argv[i]); exit(5); } if (slots[i].end >= major_cycle) { fprintf(stderr, "interval %s: exceeds major cycle length\n", argv[i]); exit(5); } (*num_intervals)++; } return slots; } #define OPTSTR "n:a:r:t:c:b:p:d:o:q:m:h" int main(int argc, char** argv) { int ret, opt; double budget_ms, period_ms, offset_ms, deadline_ms, major_cycle_ms; int create_new = 0; int attach_pid = 0; int res_type = SPORADIC_POLLING; struct reservation_config config; /* Reasonable defaults */ offset_ms = 0; deadline_ms = 0; budget_ms = 10; period_ms = 100; major_cycle_ms = 1000; config.id = 0; config.priority = LITMUS_NO_PRIORITY; /* use EDF by default */ config.cpu = 0; while ((opt = getopt(argc, argv, OPTSTR)) != -1) { switch (opt) { case 'b': budget_ms = atof(optarg); break; case 'p': period_ms = atof(optarg); break; case 'd': deadline_ms = atof(optarg); if (deadline_ms <= 0) { usage("The relative deadline must be a positive" " number."); } break; case 'o': offset_ms = atof(optarg); break; case 'm': major_cycle_ms = atof(optarg); break; case 'q': config.priority = atoi(optarg); if (!config.priority) usage("-q: invalid priority"); break; case 'c': config.cpu = atoi(optarg); break; case 'n': create_new = 1; config.id = atoi(optarg); break; case 'a': attach_pid = atoi(optarg); if (!attach_pid) usage("-a: invalid PID"); break; case 'r': config.id = atoi(optarg); break; case 't': if (strcmp(optarg, "polling-periodic") == 0) { res_type = PERIODIC_POLLING; } else if (strcmp(optarg, "polling-sporadic") == 0) { res_type = SPORADIC_POLLING; } else if (strcmp(optarg, "table-driven") == 0) { res_type = TABLE_DRIVEN; /* Default for table-driven reservations to * maximum priority. EDF has not meaning for * table-driven reservations. */ if (config.priority == LITMUS_NO_PRIORITY) config.priority = LITMUS_HIGHEST_PRIORITY; } else { usage("Unknown reservation type."); } break; case 'h': usage(""); break; case ':': usage("Argument missing."); break; case '?': default: usage("Bad argument."); break; } } if (res_type != TABLE_DRIVEN) { config.polling_params.budget = ms2ns(budget_ms); config.polling_params.period = ms2ns(period_ms); config.polling_params.offset = ms2ns(offset_ms); config.polling_params.relative_deadline = ms2ns(deadline_ms); if (config.polling_params.budget > config.polling_params.period) { usage("The budget must not exceed the period."); } } else { config.table_driven_params.major_cycle_length = ms2ns(major_cycle_ms); argc -= optind; argv += optind; config.table_driven_params.intervals = parse_td_intervals( argc, argv, &config.table_driven_params.num_intervals, config.table_driven_params.major_cycle_length); if (!config.table_driven_params.num_intervals) usage("Table-driven reservations require at least one interval to be specified."); } if (create_new) { ret = reservation_create(res_type, &config); if (ret < 0) { fprintf(stderr, "failed to create reservation %u (%m)\n", config.id); exit(1); } } if (attach_pid) attach_task(attach_pid, &config); return 0; }