diff options
author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-07-22 01:53:03 -0400 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-07-22 01:54:18 -0400 |
commit | a2675ecbfb665f5f9de5f541d7d22c6710b4ff97 (patch) | |
tree | bfb5fed4e218bb59b4f8d2d525c4815a16c033d5 | |
parent | 8d323049edd004c25f370468aa30cdf1ba76e735 (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)
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | bin/resctrl.c | 235 |
3 files changed, 246 insertions, 1 deletions
@@ -3,10 +3,13 @@ | |||
3 | *.a | 3 | *.a |
4 | 4 | ||
5 | 5 | ||
6 | # editor backup files | ||
7 | *~ | ||
6 | 8 | ||
7 | # generated files | 9 | # generated files |
8 | tests/test_catalog.inc | 10 | tests/test_catalog.inc |
9 | *.d | 11 | *.d |
12 | *.d.* | ||
10 | docs/ | 13 | docs/ |
11 | 14 | ||
12 | # executables | 15 | # executables |
@@ -23,6 +26,8 @@ showst | |||
23 | rtspin | 26 | rtspin |
24 | cycles | 27 | cycles |
25 | measure_syscall | 28 | measure_syscall |
29 | uncache | ||
30 | resctrl | ||
26 | 31 | ||
27 | # build system files | 32 | # build system files |
28 | .config | 33 | .config |
@@ -32,3 +37,5 @@ test_catalog.inc | |||
32 | # files copied in from the kernel tree | 37 | # files copied in from the kernel tree |
33 | include/litmus | 38 | include/litmus |
34 | arch/*/include/asm | 39 | arch/*/include/asm |
40 | arch/x86/include/generated | ||
41 | arch/x86/include/uapi | ||
@@ -73,7 +73,7 @@ AR := ${CROSS_COMPILE}${AR} | |||
73 | 73 | ||
74 | all = lib ${rt-apps} | 74 | all = lib ${rt-apps} |
75 | rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ | 75 | rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ |
76 | base_mt_task uncache runtests | 76 | base_mt_task uncache runtests resctrl |
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 | ||
@@ -234,6 +234,9 @@ obj-release_ts = release_ts.o | |||
234 | obj-measure_syscall = null_call.o | 234 | obj-measure_syscall = null_call.o |
235 | lib-measure_syscall = -lm | 235 | lib-measure_syscall = -lm |
236 | 236 | ||
237 | obj-resctrl = resctrl.o | ||
238 | |||
239 | |||
237 | # ############################################################################## | 240 | # ############################################################################## |
238 | # Build everything that depends on liblitmus. | 241 | # Build everything that depends on liblitmus. |
239 | 242 | ||
diff --git a/bin/resctrl.c b/bin/resctrl.c new file mode 100644 index 0000000..917f49a --- /dev/null +++ b/bin/resctrl.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 | |||
13 | const char *usage_msg = | ||
14 | "Usage: resctrl 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 | |||
28 | void usage(char *error) { | ||
29 | fprintf(stderr, "%s\n%s", error, usage_msg); | ||
30 | exit(1); | ||
31 | } | ||
32 | |||
33 | |||
34 | static 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(¶m); | ||
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, ¶m); | ||
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 | |||
70 | static 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 | |||
114 | int 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 | } | ||