aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2014-07-22 01:53:03 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2016-03-16 10:33:08 -0400
commit3015c398db2fe6baff8969ef3faf8f6600e3733d (patch)
tree3cff0be2f0f6f773a7632dd068ef54c0af625780 /bin
parent626d8bd9648dc794a1d9281644dd314f2946e642 (diff)
Add resctrl, a tool for setting up reservations
resctrl is a new tool intended to configure reservations in reservation-aware plugins. For now, it only supports the partitioned reservations (P-RES) plugin. (While at it, also update .gitignore)
Diffstat (limited to 'bin')
-rw-r--r--bin/resctl.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/bin/resctl.c b/bin/resctl.c
new file mode 100644
index 0000000..f5eda85
--- /dev/null
+++ b/bin/resctl.c
@@ -0,0 +1,235 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <limits.h>
6#include <signal.h>
7
8#include <sched.h>
9
10#include "litmus.h"
11#include "common.h"
12
13const char *usage_msg =
14 "Usage: resctl OPTIONS [INTERVAL-START,INTERVAL-END]*\n"
15 " -n ID create new reservation with id ID\n"
16 " -a PID attach already-running process PID to reservation\n"
17 " -r ID specify which reservation to attach to (not needed with -n)\n"
18 " -t TYPE type of reservation (polling-periodic, polling-sporadic, table-driven)\n"
19 " -c CPU physical partition or cluster to assign to\n"
20 " -b BUDGET polling reservation budget (in ms, default: 10ms)\n"
21 " -p PERIOD polling reservation period (in ms, default: 100ms)\n"
22 " -d DEADLINE relative deadline, implicit by default (in ms)\n"
23 " -o OFFSET offset (also known as phase), zero by default (in ms)\n"
24 " -q PRIORITY priority to use (EDF by default, max=0)\n"
25 " -m MAJOR-CYCLE major cycle length (in ms, for table-driven reservations) \n"
26 "\n";
27
28void usage(char *error) {
29 fprintf(stderr, "%s\n%s", error, usage_msg);
30 exit(1);
31}
32
33
34static void attach_task(int attach_pid, struct reservation_config *config)
35{
36 int ret;
37 struct rt_task param;
38 struct sched_param linux_param;
39
40 ret = be_migrate_thread_to_cpu(attach_pid, config->cpu);
41 if (ret < 0) {
42 fprintf(stderr, "failed to migrate task %d to CPU %d\n",
43 attach_pid, config->cpu);
44 exit(4);
45 }
46
47 init_rt_task_param(&param);
48 /* dummy values */
49 param.exec_cost = ms2ns(100);
50 param.period = ms2ns(100);
51 /* specify reservation as "virtual" CPU */
52 param.cpu = config->id;
53
54 ret = set_rt_task_param(attach_pid, &param);
55 if (ret < 0) {
56 fprintf(stderr, "failed to set RT task parameters for task %d (%m)\n",
57 attach_pid);
58 exit(2);
59 }
60
61 linux_param.sched_priority = 0;
62 ret = sched_setscheduler(attach_pid, SCHED_LITMUS, &linux_param);
63 if (ret < 0) {
64 fprintf(stderr, "failed to transition task %d to LITMUS^RT class (%m)\n",
65 attach_pid);
66 exit(3);
67 }
68}
69
70static struct lt_interval* parse_td_intervals(int argc, char** argv,
71 unsigned int *num_intervals, lt_t major_cycle)
72{
73 int i, matched;
74 struct lt_interval *slots = malloc(sizeof(slots[0]) * argc);
75 double start, end;
76
77 *num_intervals = 0;
78 for (i = 0; i < argc; i++) {
79 matched = sscanf(argv[i], "[%lf,%lf]", &start, &end);
80 if (matched != 2) {
81 fprintf(stderr, "could not parse '%s' as interval\n", argv[i]);
82 exit(5);
83 }
84 if (start < 0) {
85 fprintf(stderr, "interval %s: must not start before zero\n", argv[i]);
86 exit(5);
87 }
88 if (end <= start) {
89 fprintf(stderr, "interval %s: end before start\n", argv[i]);
90 exit(5);
91 }
92
93 slots[i].start = ms2ns(start);
94 slots[i].end = ms2ns(end);
95
96 if (i > 0 && slots[i - 1].end >= slots[i].start) {
97 fprintf(stderr, "interval %s: overlaps with previous interval\n", argv[i]);
98 exit(5);
99 }
100
101 if (slots[i].end >= major_cycle) {
102 fprintf(stderr, "interval %s: exceeds major cycle length\n", argv[i]);
103 exit(5);
104 }
105
106 (*num_intervals)++;
107 }
108
109 return slots;
110}
111
112#define OPTSTR "n:a:r:t:c:b:p:d:o:q:m:h"
113
114int main(int argc, char** argv)
115{
116 int ret, opt;
117 double budget_ms, period_ms, offset_ms, deadline_ms, major_cycle_ms;
118 int create_new = 0;
119 int attach_pid = 0;
120 int res_type = SPORADIC_POLLING;
121
122 struct reservation_config config;
123
124 /* Reasonable defaults */
125 offset_ms = 0;
126 deadline_ms = 0;
127 budget_ms = 10;
128 period_ms = 100;
129 major_cycle_ms = 1000;
130
131 config.id = 0;
132 config.priority = LITMUS_NO_PRIORITY; /* use EDF by default */
133 config.cpu = 0;
134
135 while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
136 switch (opt) {
137 case 'b':
138 budget_ms = atof(optarg);
139 break;
140 case 'p':
141 period_ms = atof(optarg);
142 break;
143 case 'd':
144 deadline_ms = atof(optarg);
145 if (deadline_ms <= 0) {
146 usage("The relative deadline must be a positive"
147 " number.");
148 }
149 break;
150 case 'o':
151 offset_ms = atof(optarg);
152 break;
153 case 'm':
154 major_cycle_ms = atof(optarg);
155 break;
156
157 case 'q':
158 config.priority = atoi(optarg);
159 break;
160 case 'c':
161 config.cpu = atoi(optarg);
162 break;
163
164 case 'n':
165 create_new = 1;
166 config.id = atoi(optarg);
167 break;
168 case 'a':
169 attach_pid = atoi(optarg);
170 if (!attach_pid)
171 usage("-a: invalid PID");
172 break;
173
174 case 'r':
175 config.id = atoi(optarg);
176 break;
177
178 case 't':
179 if (strcmp(optarg, "polling-periodic") == 0) {
180 res_type = PERIODIC_POLLING;
181 } else if (strcmp(optarg, "polling-sporadic") == 0) {
182 res_type = SPORADIC_POLLING;
183 } else if (strcmp(optarg, "table-driven") == 0) {
184 res_type = TABLE_DRIVEN;
185 } else {
186 usage("Unknown reservation type.");
187 }
188 break;
189
190 case 'h':
191 usage("");
192 break;
193 case ':':
194 usage("Argument missing.");
195 break;
196 case '?':
197 default:
198 usage("Bad argument.");
199 break;
200 }
201 }
202
203 if (res_type != TABLE_DRIVEN) {
204 config.polling_params.budget = ms2ns(budget_ms);
205 config.polling_params.period = ms2ns(period_ms);
206 config.polling_params.offset = ms2ns(offset_ms);
207 config.polling_params.relative_deadline = ms2ns(deadline_ms);
208 if (config.polling_params.budget > config.polling_params.period) {
209 usage("The budget must not exceed the period.");
210 }
211 } else {
212 config.table_driven_params.major_cycle_length = ms2ns(major_cycle_ms);
213 argc -= optind;
214 argv += optind;
215 config.table_driven_params.intervals = parse_td_intervals(
216 argc, argv, &config.table_driven_params.num_intervals,
217 config.table_driven_params.major_cycle_length);
218 if (!config.table_driven_params.num_intervals)
219 usage("Table-driven reservations require at least one interval to be specified.");
220 }
221
222 if (create_new) {
223 ret = reservation_create(res_type, &config);
224 if (ret < 0) {
225 fprintf(stderr, "failed to create reservation %u (%m)\n",
226 config.id);
227 exit(1);
228 }
229 }
230
231 if (attach_pid)
232 attach_task(attach_pid, &config);
233
234 return 0;
235}