diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2012-04-08 15:08:50 -0400 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2012-04-08 15:08:50 -0400 |
commit | 7bcc57e302810ba4ad9f77588fbacc3e02385b45 (patch) | |
tree | badb53d366e1484872b05ca8ff3a560c2d91ddde | |
parent | 4fe84175df26830e9450a5d583ffc1d979c3f24f (diff) |
Added bespin
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | bin/bespin.c | 379 |
2 files changed, 384 insertions, 1 deletions
@@ -71,7 +71,7 @@ AR := ${CROSS_COMPILE}${AR} | |||
71 | 71 | ||
72 | all = lib ${rt-apps} | 72 | all = lib ${rt-apps} |
73 | rt-apps = cycles base_task rt_launch locktest rtspin rtspin.ovh rtspin.beta release_ts \ | 73 | rt-apps = cycles base_task rt_launch locktest rtspin rtspin.ovh rtspin.beta release_ts \ |
74 | measure_syscall base_mt_task runtests | 74 | measure_syscall base_mt_task runtests bespin |
75 | 75 | ||
76 | .PHONY: all lib clean dump-config TAGS tags cscope help | 76 | .PHONY: all lib clean dump-config TAGS tags cscope help |
77 | 77 | ||
@@ -219,7 +219,11 @@ lib-rtspin.ovh = -lrt | |||
219 | obj-rtspin.beta = rtspin.beta.o common.o | 219 | obj-rtspin.beta = rtspin.beta.o common.o |
220 | lib-rtspin.beta = -lrt $(shell gsl-config --libs) | 220 | lib-rtspin.beta = -lrt $(shell gsl-config --libs) |
221 | 221 | ||
222 | obj-bespin = bespin.o common.o | ||
223 | lib-bespin = -lrt -lgsl -lgslcblas | ||
224 | |||
222 | obj-release_ts = release_ts.o | 225 | obj-release_ts = release_ts.o |
226 | |||
223 | obj-locktest = locktest.o | 227 | obj-locktest = locktest.o |
224 | lib-locktest = -lrt -pthread | 228 | lib-locktest = -lrt -pthread |
225 | 229 | ||
diff --git a/bin/bespin.c b/bin/bespin.c new file mode 100644 index 0000000..f3b7e20 --- /dev/null +++ b/bin/bespin.c | |||
@@ -0,0 +1,379 @@ | |||
1 | #include <sys/time.h> | ||
2 | |||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <unistd.h> | ||
6 | #include <time.h> | ||
7 | #include <assert.h> | ||
8 | #include <string.h> | ||
9 | #include <limits.h> | ||
10 | |||
11 | #include <sched.h> | ||
12 | #include <sys/time.h> | ||
13 | #include <sys/resource.h> | ||
14 | |||
15 | #include <gsl/gsl_rng.h> | ||
16 | #include <gsl/gsl_randist.h> | ||
17 | |||
18 | #include "litmus.h" | ||
19 | #include "common.h" | ||
20 | |||
21 | #define UINT_WANT 4294967295 | ||
22 | #if UINT_MAX < UINT_WANT | ||
23 | #error Unsigned int not big enough. | ||
24 | #endif | ||
25 | |||
26 | /* | ||
27 | * Limit the execution times. | ||
28 | */ | ||
29 | #define EXEC_MIN (2 * __NS_PER_MS) | ||
30 | #define EXEC_MAX (100 * __NS_PER_MS) | ||
31 | |||
32 | /* | ||
33 | * Limit interarrival times. | ||
34 | */ | ||
35 | #define IA_MIN (0 * __NS_PER_MS) | ||
36 | #define IA_MAX (200 * __NS_PER_MS) | ||
37 | |||
38 | |||
39 | static void usage(char *error) { | ||
40 | fprintf(stderr, "Error: %s\n", error); | ||
41 | fprintf(stderr, | ||
42 | "Usage:\n" | ||
43 | " bespin [-w] [-o] [-n] [-s seed] [-f res-time file] OS_TYPE DURATION\n" | ||
44 | "OS_TYPE is litmus or linux\n" | ||
45 | "DURATION is milliseconds.\n"); | ||
46 | exit(EXIT_FAILURE); | ||
47 | } | ||
48 | |||
49 | #define NUMS 4096 | ||
50 | static int num[NUMS]; | ||
51 | static char* progname; | ||
52 | |||
53 | static gsl_rng *exec_rng; | ||
54 | static gsl_rng *ia_rng; | ||
55 | static struct timespec sleep_ts = { .tv_sec = 0 }; | ||
56 | |||
57 | static void setup_rng(unsigned long seed) | ||
58 | { | ||
59 | exec_rng = gsl_rng_alloc(gsl_rng_taus); | ||
60 | ia_rng = gsl_rng_alloc(gsl_rng_taus); | ||
61 | if (!exec_rng || !ia_rng) | ||
62 | bail_out("Could not initialize RNG"); | ||
63 | gsl_rng_set(exec_rng, seed); | ||
64 | gsl_rng_set(ia_rng, seed + 1); | ||
65 | } | ||
66 | |||
67 | static lt_t round_double_lt(const double d) | ||
68 | { | ||
69 | /* if needed, add one so round up */ | ||
70 | if (d - ((lt_t)d) > 0.0) | ||
71 | return ((lt_t)(d + 1.0)); | ||
72 | else | ||
73 | return ((lt_t)d); | ||
74 | } | ||
75 | |||
76 | static lt_t get_exec_time(const lt_t exec_mean) | ||
77 | { | ||
78 | double e = gsl_ran_exponential(exec_rng, exec_mean); | ||
79 | e = (e < EXEC_MIN) ? EXEC_MIN : e; | ||
80 | e = (e > EXEC_MAX) ? EXEC_MAX : e; | ||
81 | return (round_double_lt(e)); | ||
82 | } | ||
83 | |||
84 | static lt_t get_ia_time(const lt_t ia_mean) | ||
85 | { | ||
86 | double ia = gsl_ran_exponential(ia_rng, ia_mean); | ||
87 | ia = (ia < IA_MIN) ? IA_MIN : ia; | ||
88 | ia = (ia > IA_MAX) ? IA_MAX : ia; | ||
89 | return (round_double_lt(ia)); | ||
90 | } | ||
91 | |||
92 | #define LITMUS_STR "litmus" | ||
93 | #define LINUX_STR "linux" | ||
94 | static int using_os(const char *const os, const char *const test) | ||
95 | { | ||
96 | /* LITMUS_STR > LINUX_STR */ | ||
97 | return (0 == strncasecmp(os, test, sizeof(LITMUS_STR))); | ||
98 | } | ||
99 | |||
100 | static int valid_os(char *os) | ||
101 | { | ||
102 | return (using_os(LITMUS_STR, os) || using_os(LINUX_STR, os)); | ||
103 | } | ||
104 | |||
105 | static inline lt_t ts_to_ns(const struct timespec *ts) | ||
106 | { | ||
107 | return ( (((lt_t)ts->tv_sec) * 1e9) + ts->tv_nsec ); | ||
108 | } | ||
109 | |||
110 | static void mono_time_ts(struct timespec *ts) | ||
111 | { | ||
112 | int ret = clock_gettime(CLOCK_MONOTONIC, ts); | ||
113 | if (ret) | ||
114 | bail_out("could not get monotonic time"); | ||
115 | } | ||
116 | |||
117 | static lt_t mono_time(void) | ||
118 | { | ||
119 | struct timespec ts; | ||
120 | mono_time_ts(&ts); | ||
121 | return (ts_to_ns(&ts)); | ||
122 | } | ||
123 | |||
124 | static void add_to_ts(struct timespec *ts, const lt_t t) | ||
125 | { | ||
126 | /* convert the lt_t to seconds and nanoseconds */ | ||
127 | const lt_t sec = t / 1e9; | ||
128 | long nsec = t - sec; | ||
129 | |||
130 | /* figure out carry for nsec field */ | ||
131 | const long nsec_sum_large = ts->tv_nsec + nsec; | ||
132 | const time_t sec_carry = nsec_sum_large / 1e9; | ||
133 | |||
134 | ts->tv_sec += sec + sec_carry; | ||
135 | ts->tv_nsec = nsec_sum_large - sec_carry * 1e9; | ||
136 | assert(ts->tv_nsec < 1e9); | ||
137 | } | ||
138 | |||
139 | static lt_t etime(void) | ||
140 | { | ||
141 | struct timespec ts; | ||
142 | int err; | ||
143 | err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); | ||
144 | if (err) | ||
145 | bail_out("could not get thread CPU time"); | ||
146 | return (ts_to_ns(&ts)); | ||
147 | } | ||
148 | |||
149 | static int loop_once(void) | ||
150 | { | ||
151 | int i, j = 0; | ||
152 | for (i = 0; i < NUMS; i++) | ||
153 | j += num[i]++; | ||
154 | return j; | ||
155 | } | ||
156 | |||
157 | static int loop_for(lt_t exec_time, lt_t emergency_exit) | ||
158 | { | ||
159 | double last_loop = 0, loop_start; | ||
160 | int job_finished = 1; | ||
161 | int tmp = 0; | ||
162 | |||
163 | lt_t start = etime(); | ||
164 | lt_t now = etime(); | ||
165 | |||
166 | while (now + last_loop < start + exec_time) { | ||
167 | loop_start = now; | ||
168 | tmp += loop_once(); | ||
169 | now = etime(); | ||
170 | last_loop = now - loop_start; | ||
171 | if (emergency_exit && mono_time() > emergency_exit) | ||
172 | job_finished = 0; | ||
173 | } | ||
174 | |||
175 | return job_finished; | ||
176 | } | ||
177 | |||
178 | static void do_sleep(const lt_t ia_mean) | ||
179 | { | ||
180 | const lt_t ia = get_ia_time(ia_mean); | ||
181 | int err; | ||
182 | |||
183 | add_to_ts(&sleep_ts, ia); | ||
184 | err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, | ||
185 | &sleep_ts, NULL); | ||
186 | |||
187 | /* | ||
188 | * For some reason, nanosleep returns non-zero even when things are | ||
189 | * okay the last time. */ | ||
190 | if (err) | ||
191 | bail_out("could not nanosleep!"); | ||
192 | } | ||
193 | |||
194 | static int job(const lt_t exec_mean, const lt_t ia_mean, FILE *out_f, | ||
195 | const lt_t program_end) | ||
196 | { | ||
197 | const lt_t now = mono_time(); | ||
198 | const lt_t exec = get_exec_time(exec_mean); | ||
199 | int job_finished; | ||
200 | lt_t end_time; | ||
201 | |||
202 | if (now > program_end || now + exec > program_end) | ||
203 | { | ||
204 | /* this job is either after we want to stop, or would be, | ||
205 | * so just quit now */ | ||
206 | return 0; | ||
207 | } else { | ||
208 | /* add NS per MS as an error margin */ | ||
209 | job_finished = loop_for(exec, program_end + __NS_PER_MS); | ||
210 | end_time = mono_time(); | ||
211 | |||
212 | /* record response time if wanted and the job finished */ | ||
213 | if (out_f && job_finished) | ||
214 | { | ||
215 | /* print start time, end time, exec time */ | ||
216 | fprintf(out_f, "%llu, %llu, %llu\n", | ||
217 | ts_to_ns(&sleep_ts), end_time, exec); | ||
218 | } | ||
219 | |||
220 | if (end_time < program_end) { | ||
221 | /* we can go to sleep some time */ | ||
222 | do_sleep(ia_mean); | ||
223 | return 1; | ||
224 | } else { | ||
225 | /* that was the last job */ | ||
226 | return 0; | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | static void setup_litmus_task(const double phase_ms) | ||
232 | { | ||
233 | const lt_t phase = phase_ms * __NS_PER_MS; | ||
234 | int ret; | ||
235 | |||
236 | /* Best-Effort task has no WCET or period, just use one */ | ||
237 | ret = sporadic_task_ns(1, 1, phase, 0, RT_CLASS_BEST_EFFORT, | ||
238 | NO_ENFORCEMENT, 0); | ||
239 | if (ret < 0) | ||
240 | bail_out("could not setup rt task params"); | ||
241 | |||
242 | init_litmus(); | ||
243 | |||
244 | ret = task_mode(LITMUS_RT_TASK); | ||
245 | if (ret != 0) | ||
246 | bail_out("could not become RT task"); | ||
247 | } | ||
248 | |||
249 | #define OPTSTR "wnos:f:" | ||
250 | |||
251 | int main(int argc, char** argv) | ||
252 | { | ||
253 | unsigned long seed = 1; | ||
254 | double duration_ms; | ||
255 | double exec_mean_ms = 10.0, ia_mean_ms = 100.0, phase_ms = 0.0; | ||
256 | lt_t duration, start, exec_mean, ia_mean; | ||
257 | char *os_type, *out_fname = NULL; | ||
258 | FILE *out_f = NULL; | ||
259 | int opt; | ||
260 | int ret; | ||
261 | int wait = 0; | ||
262 | int nice = 0; | ||
263 | int fifo = 0; | ||
264 | struct sched_param param; | ||
265 | |||
266 | progname = argv[0]; | ||
267 | |||
268 | while ((opt = getopt(argc, argv, OPTSTR)) != -1) { | ||
269 | switch (opt) { | ||
270 | case 'w': | ||
271 | wait = 1; | ||
272 | break; | ||
273 | case 'n': | ||
274 | nice = 1; | ||
275 | break; | ||
276 | case 'o': | ||
277 | fifo = 1; | ||
278 | break; | ||
279 | case 's': | ||
280 | seed = atoi(optarg); | ||
281 | break; | ||
282 | case 'h': | ||
283 | phase_ms = atof(optarg); | ||
284 | break; | ||
285 | case 'e': | ||
286 | exec_mean_ms = atof(optarg); | ||
287 | break; | ||
288 | case 'i': | ||
289 | ia_mean_ms = atof(optarg); | ||
290 | break; | ||
291 | case 'f': | ||
292 | out_fname = optarg; | ||
293 | break; | ||
294 | case ':': | ||
295 | usage("Argument missing."); | ||
296 | break; | ||
297 | case '?': | ||
298 | default: | ||
299 | usage("Bad argument."); | ||
300 | break; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | if (argc - optind != 2) | ||
305 | usage("Arguments missing."); | ||
306 | |||
307 | os_type = argv[optind + 0]; | ||
308 | duration_ms = atof(argv[optind + 1]); | ||
309 | |||
310 | if (!valid_os(os_type)) | ||
311 | bail_out("Invalid OS type"); | ||
312 | |||
313 | if (duration_ms < 0) | ||
314 | bail_out("duration < 0 not allowed"); | ||
315 | |||
316 | if (exec_mean_ms < 0) | ||
317 | bail_out("execution time mean < 0 not allowed"); | ||
318 | |||
319 | if (ia_mean_ms < 0) | ||
320 | bail_out("IA time mean < 0 not allowed"); | ||
321 | |||
322 | if (exec_mean_ms * __NS_PER_MS > UINT_MAX >> 1) | ||
323 | bail_out("Exec time too big"); | ||
324 | |||
325 | if (seed < 1) | ||
326 | bail_out("seed < 1 not allowed"); | ||
327 | |||
328 | if (phase_ms < 0) | ||
329 | bail_out("phase < 0 not allowed"); | ||
330 | |||
331 | if (wait && using_os(LINUX_STR, os_type)) | ||
332 | bail_out("Linux cannot use the wait flag"); | ||
333 | |||
334 | duration = duration_ms * __NS_PER_MS; | ||
335 | exec_mean = exec_mean_ms * __NS_PER_MS; | ||
336 | ia_mean = ia_mean_ms * __NS_PER_MS; | ||
337 | |||
338 | if (out_fname) | ||
339 | { | ||
340 | out_f = fopen(out_fname, "w"); | ||
341 | if (!out_f) | ||
342 | bail_out("Could not open response-time file"); | ||
343 | } | ||
344 | |||
345 | setup_rng(seed); | ||
346 | |||
347 | if (using_os(LITMUS_STR, os_type)) { | ||
348 | setup_litmus_task(phase_ms); | ||
349 | |||
350 | if (wait) { | ||
351 | ret = wait_for_ts_release(); | ||
352 | if (ret != 0) | ||
353 | bail_out("wait_for_ts_release()"); | ||
354 | } | ||
355 | } else if (fifo) { | ||
356 | param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 2; | ||
357 | ret = sched_setscheduler(0, SCHED_FIFO, ¶m); | ||
358 | if (ret) | ||
359 | bail_out("Could not switch to FIFO scheduler"); | ||
360 | } else if (nice) { | ||
361 | ret = setpriority(PRIO_PROCESS, 0, 5); | ||
362 | if (ret) | ||
363 | bail_out("Could not set task priority"); | ||
364 | } | ||
365 | |||
366 | /* initialize the start time */ | ||
367 | mono_time_ts(&sleep_ts); | ||
368 | start = ts_to_ns(&sleep_ts); | ||
369 | |||
370 | while (job(exec_mean, ia_mean, out_f, start + duration)); | ||
371 | |||
372 | if (using_os(LITMUS_STR, os_type)) { | ||
373 | ret = task_mode(BACKGROUND_TASK); | ||
374 | if (ret != 0) | ||
375 | bail_out("could not become regular task (huh?)"); | ||
376 | } | ||
377 | |||
378 | return 0; | ||
379 | } | ||