aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Brandenburg <bbb@mpi-sws.org>2014-07-22 01:53:03 -0400
committerBjoern Brandenburg <bbb@mpi-sws.org>2014-07-22 01:54:18 -0400
commita2675ecbfb665f5f9de5f541d7d22c6710b4ff97 (patch)
treebfb5fed4e218bb59b4f8d2d525c4815a16c033d5
parent8d323049edd004c25f370468aa30cdf1ba76e735 (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--.gitignore7
-rw-r--r--Makefile5
-rw-r--r--bin/resctrl.c235
3 files changed, 246 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index a52500b..f79d4fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
8tests/test_catalog.inc 10tests/test_catalog.inc
9*.d 11*.d
12*.d.*
10docs/ 13docs/
11 14
12# executables 15# executables
@@ -23,6 +26,8 @@ showst
23rtspin 26rtspin
24cycles 27cycles
25measure_syscall 28measure_syscall
29uncache
30resctrl
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
33include/litmus 38include/litmus
34arch/*/include/asm 39arch/*/include/asm
40arch/x86/include/generated
41arch/x86/include/uapi
diff --git a/Makefile b/Makefile
index 4742fd2..34c428f 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,7 @@ AR := ${CROSS_COMPILE}${AR}
73 73
74all = lib ${rt-apps} 74all = lib ${rt-apps}
75rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ 75rt-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
234obj-measure_syscall = null_call.o 234obj-measure_syscall = null_call.o
235lib-measure_syscall = -lm 235lib-measure_syscall = -lm
236 236
237obj-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
13const 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
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}