diff options
Diffstat (limited to 'bin/rtspin.c')
-rw-r--r-- | bin/rtspin.c | 261 |
1 files changed, 161 insertions, 100 deletions
diff --git a/bin/rtspin.c b/bin/rtspin.c index 20ce734..28c4a4e 100644 --- a/bin/rtspin.c +++ b/bin/rtspin.c | |||
@@ -4,40 +4,98 @@ | |||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include <time.h> | 6 | #include <time.h> |
7 | #include <assert.h> | ||
7 | 8 | ||
8 | 9 | ||
9 | #include "litmus.h" | 10 | #include "litmus.h" |
10 | #include "common.h" | 11 | #include "common.h" |
11 | 12 | ||
12 | 13 | ||
13 | static double cputime() | 14 | |
15 | static void usage(char *error) { | ||
16 | fprintf(stderr, "Error: %s\n", error); | ||
17 | fprintf(stderr, | ||
18 | "Usage:\n" | ||
19 | " rt_spin [COMMON-OPTS] WCET PERIOD DURATION\n" | ||
20 | " rt_spin [COMMON-OPTS] -f FILE [-o COLUMN] WCET PERIOD\n" | ||
21 | " rt_spin -l\n" | ||
22 | "\n" | ||
23 | "COMMON-OPTS = [-w] [-p PARTITION] [-c CLASS] [-s SCALE]\n" | ||
24 | "\n" | ||
25 | "WCET and PERIOD are milliseconds, DURATION is seconds.\n"); | ||
26 | exit(EXIT_FAILURE); | ||
27 | } | ||
28 | |||
29 | /* | ||
30 | * returns the character that made processing stop, newline or EOF | ||
31 | */ | ||
32 | static int skip_to_next_line(FILE *fstream) | ||
14 | { | 33 | { |
15 | struct timespec ts; | 34 | int ch; |
16 | int err; | 35 | for (ch = fgetc(fstream); ch != EOF && ch != '\n'; ch = fgetc(fstream)); |
17 | err = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); | 36 | return ch; |
18 | if (err != 0) | ||
19 | perror("clock_gettime"); | ||
20 | return (ts.tv_sec + 1E-9 * ts.tv_nsec); | ||
21 | } | 37 | } |
22 | 38 | ||
23 | static double wctime() | 39 | static void skip_comments(FILE *fstream) |
24 | { | 40 | { |
25 | struct timeval tv; | 41 | int ch; |
26 | gettimeofday(&tv, NULL); | 42 | for (ch = fgetc(fstream); ch == '#'; ch = fgetc(fstream)) |
27 | return (tv.tv_sec + 1E-6 * tv.tv_usec); | 43 | skip_to_next_line(fstream); |
44 | ungetc(ch, fstream); | ||
28 | } | 45 | } |
29 | 46 | ||
30 | void usage(char *error) { | 47 | static void get_exec_times(const char *file, const int column, |
31 | fprintf(stderr, "Error: %s\n", error); | 48 | int *num_jobs, double **exec_times) |
32 | fprintf(stderr, | 49 | { |
33 | "Usage: rt_spin [-w] [-p PARTITION] [-c CLASS] WCET PERIOD DURATION\n" | 50 | FILE *fstream; |
34 | " rt_spin -l\n"); | 51 | int cur_job, cur_col, ch; |
35 | exit(1); | 52 | *num_jobs = 0; |
53 | |||
54 | fstream = fopen(file, "r"); | ||
55 | if (!fstream) | ||
56 | bail_out("could not open execution time file"); | ||
57 | |||
58 | /* figure out the number of jobs */ | ||
59 | do { | ||
60 | skip_comments(fstream); | ||
61 | ch = skip_to_next_line(fstream); | ||
62 | if (ch != EOF) | ||
63 | ++(*num_jobs); | ||
64 | } while (ch != EOF); | ||
65 | |||
66 | if (-1 == fseek(fstream, 0L, SEEK_SET)) | ||
67 | bail_out("rewinding file failed"); | ||
68 | |||
69 | /* allocate space for exec times */ | ||
70 | *exec_times = calloc(*num_jobs, sizeof(*exec_times)); | ||
71 | if (!*exec_times) | ||
72 | bail_out("couldn't allocate memory"); | ||
73 | |||
74 | for (cur_job = 0; cur_job < *num_jobs && !feof(fstream); ++cur_job) { | ||
75 | |||
76 | skip_comments(fstream); | ||
77 | |||
78 | for (cur_col = 1; cur_col < column; ++cur_col) { | ||
79 | /* discard input until we get to the column we want */ | ||
80 | fscanf(fstream, "%*s,"); | ||
81 | } | ||
82 | |||
83 | /* get the desired exec. time */ | ||
84 | if (1 != fscanf(fstream, "%lf", (*exec_times)+cur_job)) { | ||
85 | fprintf(stderr, "invalid execution time near line %d\n", | ||
86 | cur_job); | ||
87 | exit(EXIT_FAILURE); | ||
88 | } | ||
89 | |||
90 | skip_to_next_line(fstream); | ||
91 | } | ||
92 | |||
93 | assert(cur_job == *num_jobs); | ||
94 | fclose(fstream); | ||
36 | } | 95 | } |
37 | 96 | ||
38 | #define NUMS 4096 | 97 | #define NUMS 4096 |
39 | static int num[NUMS]; | 98 | static int num[NUMS]; |
40 | static double loop_length = 1.0; | ||
41 | static char* progname; | 99 | static char* progname; |
42 | 100 | ||
43 | static int loop_once(void) | 101 | static int loop_once(void) |
@@ -48,74 +106,40 @@ static int loop_once(void) | |||
48 | return j; | 106 | return j; |
49 | } | 107 | } |
50 | 108 | ||
51 | static int loop_for(double exec_time) | 109 | static int loop_for(double exec_time, double emergency_exit) |
52 | { | 110 | { |
53 | double t = 0; | 111 | double last_loop = 0, loop_start; |
54 | int tmp = 0; | 112 | int tmp = 0; |
55 | /* while (t + loop_length < exec_time) { | 113 | |
56 | tmp += loop_once(); | ||
57 | t += loop_length; | ||
58 | } | ||
59 | */ | ||
60 | double start = cputime(); | 114 | double start = cputime(); |
61 | double now = cputime(); | 115 | double now = cputime(); |
62 | while (now + loop_length < start + exec_time) { | 116 | |
117 | while (now + last_loop < start + exec_time) { | ||
118 | loop_start = now; | ||
63 | tmp += loop_once(); | 119 | tmp += loop_once(); |
64 | t += loop_length; | ||
65 | now = cputime(); | 120 | now = cputime(); |
121 | last_loop = now - loop_start; | ||
122 | if (emergency_exit && wctime() > emergency_exit) { | ||
123 | /* Oops --- this should only be possible if the execution time tracking | ||
124 | * is broken in the LITMUS^RT kernel. */ | ||
125 | fprintf(stderr, "!!! rtspin/%d emergency exit!\n", getpid()); | ||
126 | fprintf(stderr, "Something is seriously wrong! Do not ignore this.\n"); | ||
127 | break; | ||
128 | } | ||
66 | } | 129 | } |
67 | 130 | ||
68 | return tmp; | 131 | return tmp; |
69 | } | 132 | } |
70 | 133 | ||
71 | static void fine_tune(double interval) | ||
72 | { | ||
73 | double start, end, delta; | ||
74 | |||
75 | start = wctime(); | ||
76 | loop_for(interval); | ||
77 | end = wctime(); | ||
78 | delta = (end - start - interval) / interval; | ||
79 | if (delta != 0) | ||
80 | loop_length = loop_length / (1 - delta); | ||
81 | } | ||
82 | |||
83 | static void configure_loop(void) | ||
84 | { | ||
85 | double start; | ||
86 | |||
87 | /* prime cache */ | ||
88 | loop_once(); | ||
89 | loop_once(); | ||
90 | loop_once(); | ||
91 | |||
92 | /* measure */ | ||
93 | start = wctime(); | ||
94 | loop_once(); /* hope we didn't get preempted */ | ||
95 | loop_length = wctime(); | ||
96 | loop_length -= start; | ||
97 | |||
98 | /* fine tune */ | ||
99 | fine_tune(0.1); | ||
100 | fine_tune(0.1); | ||
101 | fine_tune(0.1); | ||
102 | } | ||
103 | |||
104 | static void show_loop_length(void) | ||
105 | { | ||
106 | printf("%s/%d: loop_length=%f (%ldus)\n", | ||
107 | progname, getpid(), loop_length, | ||
108 | (long) (loop_length * 1000000)); | ||
109 | } | ||
110 | 134 | ||
111 | static void debug_delay_loop(void) | 135 | static void debug_delay_loop(void) |
112 | { | 136 | { |
113 | double start, end, delay; | 137 | double start, end, delay; |
114 | show_loop_length(); | 138 | |
115 | while (1) { | 139 | while (1) { |
116 | for (delay = 0.5; delay > 0.01; delay -= 0.01) { | 140 | for (delay = 0.5; delay > 0.01; delay -= 0.01) { |
117 | start = wctime(); | 141 | start = wctime(); |
118 | loop_for(delay); | 142 | loop_for(delay, 0); |
119 | end = wctime(); | 143 | end = wctime(); |
120 | printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", | 144 | printf("%6.4fs: looped for %10.8fs, delta=%11.8fs, error=%7.4f%%\n", |
121 | delay, | 145 | delay, |
@@ -126,16 +150,20 @@ static void debug_delay_loop(void) | |||
126 | } | 150 | } |
127 | } | 151 | } |
128 | 152 | ||
129 | static int job(double exec_time) | 153 | static int job(double exec_time, double program_end) |
130 | { | 154 | { |
131 | loop_for(exec_time); | 155 | if (wctime() > program_end) |
132 | sleep_next_period(); | 156 | return 0; |
133 | return 0; | 157 | else { |
158 | loop_for(exec_time, program_end + 1); | ||
159 | sleep_next_period(); | ||
160 | return 1; | ||
161 | } | ||
134 | } | 162 | } |
135 | 163 | ||
136 | #define OPTSTR "p:c:m:wld:ve" | 164 | #define OPTSTR "p:c:wldveo:f:s:m:" |
137 | 165 | ||
138 | int main(int argc, char** argv) | 166 | int main(int argc, char** argv) |
139 | { | 167 | { |
140 | int ret; | 168 | int ret; |
141 | lt_t wcet; | 169 | lt_t wcet; |
@@ -146,11 +174,14 @@ int main(int argc, char** argv) | |||
146 | int opt; | 174 | int opt; |
147 | int wait = 0; | 175 | int wait = 0; |
148 | int test_loop = 0; | 176 | int test_loop = 0; |
149 | int skip_config = 0; | 177 | int column = 1; |
150 | int verbose = 0; | 178 | const char *file = NULL; |
151 | int want_enforcement = 0; | 179 | int want_enforcement = 0; |
152 | double duration, start; | 180 | double duration = 0, start; |
181 | double *exec_times = NULL; | ||
182 | double scale = 1.0; | ||
153 | task_class_t class = RT_CLASS_HARD; | 183 | task_class_t class = RT_CLASS_HARD; |
184 | int cur_job, num_jobs; | ||
154 | crit_level_t crit = CRIT_LEVEL_C; | 185 | crit_level_t crit = CRIT_LEVEL_C; |
155 | 186 | ||
156 | progname = argv[0]; | 187 | progname = argv[0]; |
@@ -179,14 +210,14 @@ int main(int argc, char** argv) | |||
179 | case 'l': | 210 | case 'l': |
180 | test_loop = 1; | 211 | test_loop = 1; |
181 | break; | 212 | break; |
182 | case 'd': | 213 | case 'o': |
183 | /* manually configure delay per loop iteration | 214 | column = atoi(optarg); |
184 | * unit: microseconds */ | 215 | break; |
185 | loop_length = atof(optarg) / 1000000; | 216 | case 'f': |
186 | skip_config = 1; | 217 | file = optarg; |
187 | break; | 218 | break; |
188 | case 'v': | 219 | case 's': |
189 | verbose = 1; | 220 | scale = atof(optarg); |
190 | break; | 221 | break; |
191 | case ':': | 222 | case ':': |
192 | usage("Argument missing."); | 223 | usage("Argument missing."); |
@@ -198,32 +229,50 @@ int main(int argc, char** argv) | |||
198 | } | 229 | } |
199 | } | 230 | } |
200 | 231 | ||
201 | |||
202 | if (!skip_config) | ||
203 | configure_loop(); | ||
204 | |||
205 | if (test_loop) { | 232 | if (test_loop) { |
206 | debug_delay_loop(); | 233 | debug_delay_loop(); |
207 | return 0; | 234 | return 0; |
208 | } | 235 | } |
209 | 236 | ||
210 | if (argc - optind < 3) | 237 | if (file) { |
211 | usage("Arguments missing."); | 238 | get_exec_times(file, column, &num_jobs, &exec_times); |
239 | |||
240 | if (argc - optind < 2) | ||
241 | usage("Arguments missing."); | ||
242 | |||
243 | for (cur_job = 0; cur_job < num_jobs; ++cur_job) { | ||
244 | /* convert the execution time to seconds */ | ||
245 | duration += exec_times[cur_job] * 0.001; | ||
246 | } | ||
247 | } else { | ||
248 | /* | ||
249 | * if we're not reading from the CSV file, then we need | ||
250 | * three parameters | ||
251 | */ | ||
252 | if (argc - optind < 3) | ||
253 | usage("Arguments missing."); | ||
254 | } | ||
255 | |||
212 | wcet_ms = atof(argv[optind + 0]); | 256 | wcet_ms = atof(argv[optind + 0]); |
213 | period_ms = atof(argv[optind + 1]); | 257 | period_ms = atof(argv[optind + 1]); |
214 | duration = atof(argv[optind + 2]); | 258 | |
215 | wcet = wcet_ms * __NS_PER_MS; | 259 | wcet = wcet_ms * __NS_PER_MS; |
216 | period = period_ms * __NS_PER_MS; | 260 | period = period_ms * __NS_PER_MS; |
217 | if (wcet <= 0) | 261 | if (wcet <= 0) |
218 | usage("The worst-case execution time must be a " | 262 | usage("The worst-case execution time must be a " |
219 | "positive number."); | 263 | "positive number."); |
220 | if (period <= 0) | 264 | if (period <= 0) |
221 | usage("The period must be a positive number."); | 265 | usage("The period must be a positive number."); |
222 | if (wcet > period) { | 266 | if (!file && wcet > period) { |
223 | usage("The worst-case execution time must not " | 267 | usage("The worst-case execution time must not " |
224 | "exceed the period."); | 268 | "exceed the period."); |
225 | } | 269 | } |
226 | 270 | ||
271 | if (!file) | ||
272 | duration = atof(argv[optind + 2]); | ||
273 | else if (file && num_jobs > 1) | ||
274 | duration += period_ms * 0.001 * (num_jobs - 1); | ||
275 | |||
227 | if (migrate) { | 276 | if (migrate) { |
228 | ret = be_migrate_to(cpu); | 277 | ret = be_migrate_to(cpu); |
229 | if (ret < 0) | 278 | if (ret < 0) |
@@ -238,9 +287,6 @@ int main(int argc, char** argv) | |||
238 | if (ret < 0) | 287 | if (ret < 0) |
239 | bail_out("could not setup rt task params"); | 288 | bail_out("could not setup rt task params"); |
240 | 289 | ||
241 | if (verbose) | ||
242 | show_loop_length(); | ||
243 | |||
244 | init_litmus(); | 290 | init_litmus(); |
245 | 291 | ||
246 | ret = task_mode(LITMUS_RT_TASK); | 292 | ret = task_mode(LITMUS_RT_TASK); |
@@ -255,9 +301,24 @@ int main(int argc, char** argv) | |||
255 | 301 | ||
256 | start = wctime(); | 302 | start = wctime(); |
257 | 303 | ||
258 | while (start + duration > wctime()) { | 304 | if (file) { |
259 | job(wcet_ms * 0.0009); /* 90% wcet, in seconds */ | 305 | /* use times read from the CSV file */ |
306 | for (cur_job = 0; cur_job < num_jobs; ++cur_job) { | ||
307 | /* convert job's length to seconds */ | ||
308 | job(exec_times[cur_job] * 0.001 * scale, | ||
309 | start + duration); | ||
310 | } | ||
311 | } else { | ||
312 | /* conver to seconds and scale */ | ||
313 | while (job(wcet_ms * 0.001 * scale, start + duration)); | ||
260 | } | 314 | } |
261 | 315 | ||
316 | ret = task_mode(BACKGROUND_TASK); | ||
317 | if (ret != 0) | ||
318 | bail_out("could not become regular task (huh?)"); | ||
319 | |||
320 | if (file) | ||
321 | free(exec_times); | ||
322 | |||
262 | return 0; | 323 | return 0; |
263 | } | 324 | } |