summaryrefslogtreecommitdiffstats
path: root/SD-VBS/common/c/extra.h
diff options
context:
space:
mode:
authorLeo Chan <leochanj@live.unc.edu>2020-10-22 01:53:21 -0400
committerJoshua Bakita <jbakita@cs.unc.edu>2020-10-22 01:56:35 -0400
commitd17b33131c14864bd1eae275f49a3f148e21cf29 (patch)
tree0d8f77922e8d193cb0f6edab83018f057aad64a0 /SD-VBS/common/c/extra.h
parent601ed25a4c5b66cb75315832c15613a727db2c26 (diff)
Squashed commit of the sb-vbs branch.
Includes the SD-VBS benchmarks modified to: - Use libextra to loop as realtime jobs - Preallocate memory before starting their main computation - Accept input via stdin instead of via argc Does not include the SD-VBS matlab code. Fixes libextra execution in LITMUS^RT.
Diffstat (limited to 'SD-VBS/common/c/extra.h')
-rw-r--r--SD-VBS/common/c/extra.h479
1 files changed, 479 insertions, 0 deletions
diff --git a/SD-VBS/common/c/extra.h b/SD-VBS/common/c/extra.h
new file mode 100644
index 0000000..8c67b33
--- /dev/null
+++ b/SD-VBS/common/c/extra.h
@@ -0,0 +1,479 @@
1/**
2 * Copyright 2019 Sims Hill Osborne and 2020 Joshua Bakita
3 *
4 * This header provides facilities by which to separably run and time TACLeBench
5 * To use this for paired task timing, define PAIRED (pass CFLAGS=-DPAIRED to make)
6 **/
7#define _GNU_SOURCE
8#include <fcntl.h> // For O_CREAT and O_RDWR
9#include <sched.h> // For sched_yield()
10#include <semaphore.h> // For sem_{open, post, wait}()
11#include <stdio.h>
12#include <stdlib.h> // For exit()
13#include <string.h> // For strlen()
14#include <sys/mman.h> // For mlockall()
15#include <unistd.h> // For ftruncate()
16#include <time.h>
17
18// This is only visible if _GNU_SOURCE is defined, and that define does not
19// come along to places where this file is included. Address this by manually
20// forcing it into the global namespace.
21extern int sched_getcpu();
22
23// These constants correspond to the imx6q-sabredb platform
24#define LINE_SIZE 32
25#define L2_SIZE 16*2048*32
26
27#if __arm__
28#include <unistd.h>
29#include <sys/syscall.h>
30#endif
31
32#define LITMUS 1
33#define MC2 0
34#define MMDC_PROF 0
35
36#if LITMUS
37#include <litmus.h>
38#endif
39
40#if MMDC_PROF
41#include "/media/speedy/litmus/tools/mmdc/mmdc.h"
42#endif
43
44#if LITMUS
45#define SET_UP LOAD_PARAMS SETUP_LITMUS
46#else
47#define SET_UP LOAD_PARAMS
48#endif
49
50#if MMDC_PROF
51#define LOAD_PARAMS LOAD_PARAMS_ITRL SETUP_MMDC
52#else
53#define LOAD_PARAMS LOAD_PARAMS_ITRL
54#endif
55
56// Store state globally so that the job can be outside main()
57// Arrays use float as a comprimise between overflow and size
58// Paired arrays use long longs as precision is more important for those times
59#ifdef PAIRED
60long long *_rt_start_time;
61long long *_rt_end_time;
62#else
63float *_rt_exec_time;
64#endif
65#if MMDC_PERF
66float *_rt_mmdc_read;
67float *_rt_mmdc_write;
68#endif
69long _rt_jobs_complete;
70long _rt_max_jobs;
71int _rt_core;
72int _rt_will_output;
73struct timespec _rt_start, _rt_end;
74
75char *_rt_run_id;
76char *_rt_our_prog_name;
77char *_rt_other_prog_name;
78char *_rt_other_core;
79#define _RT_FILENAME_LEN 64
80#define _BILLION (1000*1000*1000)
81#ifdef PAIRED
82char *_rt_barrier;
83sem_t *_rt_first_sem, *_rt_second_sem;
84int _rt_lock_id;
85#endif
86
87static void _rt_load_params_itrl(int argc, char **argv) {
88#ifdef PAIRED
89 if (argc != 8) {
90 fprintf(stderr, "Usage: %s <name> <loops> <my core> <other core> <other name> <runID> <lockID>", argv[0]);
91 fprintf(stderr, " <name> string for logging. Name of this task.\n");
92 fprintf(stderr, " <loops> integer number of iterations. -1 for infinite.\n");
93 fprintf(stderr, " <my core> UNUSED. Core is now auto-detected.\n");
94 fprintf(stderr, " <other core> integer for logging. Core of paired task.\n");
95 fprintf(stderr, " <other name> string for logging. Name of paired task.\n");
96 fprintf(stderr, " <runID> string to append with .txt to yield output file name.\n");
97 fprintf(stderr, " <lockID> 1 to indicate this is pair member 1, otherwise pair member 2.\n");
98 exit(1);
99 }
100#else
101 if (argc != 6) {
102 fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);
103 fprintf(stderr, " <name> string for logging. Name of this task.\n");
104 fprintf(stderr, " <loops> integer number of iterations. -1 for infinite.\n");
105 fprintf(stderr, " <my core> UNUSED. Core is now auto-detected.\n");
106 fprintf(stderr, " <runID> string to append with .txt to yield output file name.\n");
107 fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");
108 exit(1);
109 }
110#endif
111 _rt_our_prog_name = argv[1];
112 _rt_max_jobs = atol(argv[2]);
113 _rt_core = sched_getcpu();
114#ifdef PAIRED
115 _rt_other_core = argv[4];
116 _rt_other_prog_name = argv[5];
117 _rt_run_id = argv[6];
118 _rt_lock_id = atoi(argv[7]);
119 // The paired version doesn't support disabling output (legacy compatibility)
120 _rt_will_output = 1;
121#else
122 _rt_other_core = "none";
123 _rt_other_prog_name = "none";
124 _rt_run_id = argv[4];
125 _rt_will_output = atoi(argv[5]);
126#endif /* PAIRED */
127 if (_rt_max_jobs < 0 && _rt_will_output != 0) {
128 fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n");
129 exit(1);
130 }
131 if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {
132 fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);
133 exit(1);
134 }
135#ifdef PAIRED
136 _rt_start_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long));
137 _rt_end_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long));
138 if (!_rt_end_time || !_rt_start_time) {
139 perror("Unable to allocate buffers for execution times");
140 exit(1);
141 }
142 _rt_first_sem = sem_open("/_libextra_first_sem", O_CREAT, 644, 0);
143 _rt_second_sem = sem_open("/_libextra_second_sem", O_CREAT, 644, 0);
144 if (_rt_first_sem == SEM_FAILED || _rt_second_sem == SEM_FAILED) {
145 perror("Error while creating semaphores");
146 exit(1);
147 }
148 int barrier_file = shm_open("/_libextra_barrier", O_CREAT | O_RDWR, 644);
149 if (barrier_file == -1) {
150 perror("Error while creating shared memory for barrier synchronization");
151 exit(1);
152 }
153 if (ftruncate(barrier_file, 1) == -1) {
154 perror("Error while setting size of shared memory for barrier synchronization");
155 exit(1);
156 }
157 _rt_barrier = mmap(NULL, 1, PROT_WRITE, MAP_SHARED, barrier_file, 0);
158 if (_rt_barrier == MAP_FAILED) {
159 perror("Error while mapping shared memory for barrier synchronization");
160 exit(1);
161 }
162 *_rt_barrier = 0;
163#else
164 _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));
165 if (!_rt_exec_time) {
166 perror("Unable to allocate buffer for execution times");
167 exit(1);
168 }
169#endif /* PAIRED */
170 _rt_jobs_complete = 0;
171 mlockall(MCL_CURRENT || MCL_FUTURE);
172}
173#define LOAD_PARAMS_ITRL _rt_load_params_itrl(argc, argv);
174
175#define SETUP_MMDC \
176 _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
177 _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
178 if (!_rt_mmdc_read || !_rt_mmdc_write) {\
179 perror("Unable to allocate buffer for MMDC data");\
180 exit(1);\
181 }\
182 MMDC_PROFILE_RES_t mmdc_res;\
183 memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\
184 int fd = open("/dev/mem", O_RDWR, 0);\
185 if (fd < 0) {\
186 perror("Unable to open /dev/mem");\
187 exit(1);\
188 }\
189 pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\
190 if (mmdc == MAP_FAILED) {\
191 perror("Unable to map MMDC address space");\
192 exit(1);\
193 }\
194 mmdc->madpcr1 = axi_arm1;\
195 msync(&(mmdc->madpcr1),4,MS_SYNC);
196
197#define SETUP_LITMUS \
198 unsigned int wait = 0; \
199 if (be_migrate_to_domain(_rt_core) < 0) { \
200 perror("Unable to migrate to specified CPU"); \
201 exit(1); \
202 } \
203 struct rt_task rt_param; \
204 init_rt_task_param(&rt_param); \
205 /* Supposedly the next two parameters are irrelevant when reservations are enabled, but I'm leaving them anyway... */ \
206 rt_param.exec_cost = ms2ns(999); \
207 rt_param.period = ms2ns(1000); \
208 rt_param.priority = LITMUS_HIGHEST_PRIORITY; \
209 rt_param.cls = RT_CLASS_HARD; \
210 rt_param.release_policy = TASK_PERIODIC; \
211 rt_param.budget_policy = NO_ENFORCEMENT; \
212 rt_param.cpu = _rt_core; \
213 if (set_rt_task_param(gettid(), &rt_param) < 0) { \
214 perror("Unable to set real-time parameters"); \
215 exit(1); \
216 } \
217 if (init_litmus() != 0) { \
218 perror("init_litmus failed"); \
219 exit(1); \
220 } \
221 MC2_SETUP \
222 if (task_mode(LITMUS_RT_TASK) != 0) { \
223 perror("Unable to become real-time task"); \
224 exit(1); \
225 } \
226 if (wait && wait_for_ts_release() != 0) { \
227 perror("Unable to wait for taskset release"); \
228 exit(1); \
229 }
230
231#if MC2
232#define MC2_SETUP \
233
234 set_page_color(rt_param.cpu);
235#else
236#define MC2_SETUP
237#endif
238
239#define CLEANUP_LITMUS \
240 if (task_mode(BACKGROUND_TASK) != 0) { \
241 perror("Unable to become a real-time task"); \
242 exit(1); \
243 } \
244
245#if __arm__
246// On ARM, manually flush the cache
247#define FLUSH_CACHES \
248 volatile uint8_t buffer[L2_SIZE * 4]; \
249 for (uint32_t j = 0; j < 4; j++) \
250 for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \
251 buffer[i]++;
252#else
253// On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0)
254#define FLUSH_CACHES \
255 FILE *fp = fopen("/proc/wbinvd", "r");\
256 if (fp == NULL) {\
257 perror("Cache flush module interface cannot be opened");\
258 exit(1);\
259 }\
260 char dummy;\
261 if (fread(&dummy, 1, 1, fp) == 0) {\
262 perror("Unable to access cache flush module interface");\
263 exit(1);\
264 }\
265 fclose(fp);
266#endif
267
268// This semaphore-based synchronization is from Sims
269#define FIRST_UNLOCK \
270 if (_rt_lock_id == 1) {\
271 if (sem_post(_rt_second_sem) != 0) {\
272 perror("Unable to unlock second semaphore");\
273 exit(1);\
274 }\
275 } \
276 else {\
277 if (sem_post(_rt_first_sem) != 0) {\
278 perror("Unable to unlock first semaphore");\
279 exit(1);\
280 }\
281 } \
282
283#define FIRST_LOCK \
284 if (_rt_lock_id == 1) {\
285 if (sem_wait(_rt_first_sem) != 0) {\
286 perror("Unable to wait on first semaphore");\
287 exit(1);\
288 }\
289 }\
290 else {\
291 if (sem_wait(_rt_second_sem) != 0) {\
292 perror("Unable to wait on second semaphore");\
293 exit(1);\
294 }\
295 }
296
297// This ensures a very low difference between pair member start times
298#define BARRIER_SYNC \
299 if (__sync_bool_compare_and_swap(_rt_barrier, 0, 1)) {\
300 while (!__sync_bool_compare_and_swap(_rt_barrier, 0, 0)) {};\
301 }\
302 else {\
303 __sync_bool_compare_and_swap(_rt_barrier, 1, 0);\
304 }
305
306// Buffer timing result from a single job
307static void _rt_save_job_result() {
308 if (_rt_jobs_complete >= _rt_max_jobs) {
309 fprintf(stderr, "Max jobs setting too small! Trying to record job #%ld when we only have space for %ld jobs. Exiting...\n", _rt_jobs_complete, _rt_max_jobs);
310 exit(1);
311 }
312 if (_rt_jobs_complete > -1 && _rt_will_output) {
313#ifdef PAIRED
314 _rt_start_time[_rt_jobs_complete] = _rt_start.tv_sec;
315 _rt_start_time[_rt_jobs_complete] *= _BILLION;
316 _rt_start_time[_rt_jobs_complete] += _rt_start.tv_nsec;
317 _rt_end_time[_rt_jobs_complete] = _rt_end.tv_sec;
318 _rt_end_time[_rt_jobs_complete] *= _BILLION;
319 _rt_end_time[_rt_jobs_complete] += _rt_end.tv_nsec;
320#else
321 _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec;
322 _rt_exec_time[_rt_jobs_complete] *= _BILLION;
323 _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec;
324#endif /* PAIRED */
325#if MMDC_PROF
326 _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes;
327 _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes;
328#endif
329 }
330}
331
332// Save all buffered timing results to disk
333static void _rt_write_to_file() {
334 char fileName[_RT_FILENAME_LEN];
335 FILE *fp;
336 munlockall();
337 if (!_rt_will_output)
338 goto out;
339 strcpy(fileName, _rt_run_id);
340 strcat(fileName, ".txt");
341 fp = fopen(fileName, "a");
342 if (fp == NULL) {
343 perror("Unable to open output file");
344 exit(1);
345 }
346 // Baseline output uses a similar format with "none" for unused fields
347 for (int i = 0; i < _rt_jobs_complete; i++){
348 fprintf(fp, "%s %s %u %s %ld", _rt_our_prog_name, _rt_other_prog_name,
349 _rt_core, _rt_other_core, _rt_max_jobs);
350#ifdef PAIRED
351 // For unclear legacy reasons, paired tasks emit sec and ns separately
352 fprintf(fp, " %lld %lld %lld %lld",
353 _rt_start_time[i] / _BILLION, _rt_start_time[i] % _BILLION,
354 _rt_end_time[i] / _BILLION, _rt_end_time[i] % _BILLION);
355#else
356 fprintf(fp, " %.f", _rt_exec_time[i]);
357#endif /* PAIRED */
358 fprintf(fp, " %s %d %.f %.f\n", _rt_run_id, i,
359#if MMDC_PROF
360 _rt_mmdc_read[i], _rt_mmdc_write[i]);
361#else
362 0.0, 0.0);
363#endif /* MMDC_PROF */
364 }
365 fclose(fp);
366out:
367#if LITMUS
368 CLEANUP_LITMUS
369#endif /* LITMUS */
370#ifdef PAIRED
371 munmap(_rt_barrier, 1);
372 shm_unlink("/_libextra_barrier");
373 sem_unlink("/_libextra_first_sem");
374 sem_unlink("/_libextra_second_sem");
375 free(_rt_start_time);
376 free(_rt_end_time);
377#else
378 free(_rt_exec_time);
379#endif /* PAIRED */
380#if MMDC_PROF
381 free(_rt_mmdc_read);
382 free(_rt_mmdc_write);
383#endif /* MMDC_PROF */
384}
385
386// Start a job
387static void _rt_start_loop() {
388#if LITMUS
389 if (sleep_next_period() != 0) {
390 perror("Unable to sleep for next period");
391 }
392#else
393 sched_yield();
394#endif /* LITMUS */
395#ifdef PAIRED
396 FIRST_UNLOCK
397 FIRST_LOCK
398#endif /* PAIRED */
399 FLUSH_CACHES
400#ifdef PAIRED
401 BARRIER_SYNC
402#endif /* PAIRED */
403#if MMDC_PROF
404 /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */
405 start_mmdc_profiling(mmdc);
406#endif /* MMDC_PROF */
407 clock_gettime(CLOCK_MONOTONIC, &_rt_start);
408}
409
410// Complete a job
411static void _rt_stop_loop() {
412 clock_gettime(CLOCK_MONOTONIC, &_rt_end);
413#if MMDC_PROF
414 /* This freezes the profiling and makes results available */
415 pause_mmdc_profiling(mmdc);
416 get_mmdc_profiling_results(mmdc, &mmdc_res);
417#endif /* MMDC_PROF */
418 _rt_save_job_result();
419 _rt_jobs_complete++;
420}
421
422/****** New API ******
423 * Intended structure:
424 *
425 * |int main(int argc, char **argv) {
426 * | SET_UP
427 * | ...
428 * | for_each_job {
429 * | tacleInit();
430 * | tacleMain();
431 * | }
432 * | WRITE_TO_FILE
433 * |}
434 *
435 * The main() function must call its parameters argc and argv for SET_UP to be
436 * able to read them.
437 * Only SET_UP necessarily has to be in main().
438 *
439 * We use some niche C features, here's a quick explaination:
440 * 1. The && operator doesn't evaluate the right-hand side of the expression
441 * unless the left side evaluated to true. We use this to only execute
442 * _rt_start_loop() when the loop will actually run.
443 * 2. The comma operator executes the first expression and then throws away the
444 * result. We use this to call our void function from inside a comparison.
445 */
446#define for_each_job \
447 for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \
448 _rt_stop_loop())
449
450/****** Legacy API ******
451 * Intended structure:
452 *
453 * |int main(int argc, char **argv) {
454 * | SET_UP
455 * | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){
456 * | START_LOOP
457 * | tacleInit();
458 * | tacleMain();
459 * | STOP_LOOP
460 * | }
461 * | WRITE_TO_FILE
462 * | tacleReturn
463 * |}
464 *
465 * The main() function must call its parameters argc and argv for SET_UP to be
466 * able to read them.
467 */
468static int jobsComplete = 0;
469#define START_LOOP _rt_start_loop();
470#define STOP_LOOP _rt_stop_loop();
471#define WRITE_TO_FILE _rt_write_to_file();
472#define maxJobs _rt_max_jobs
473// Has been part of STOP_LOOP for quite some time
474#define SAVE_RESULTS \
475 #warning "The SAVE_RESULTS macro is deprecated and will soon be removed!";
476// Unclear if SLEEP is used anywhere.
477#define SLEEP \
478 #warning "The SLEEP macro is deprecated and may be removed!" \
479 nanosleep((const struct timespec[]){{0, 1000000}}, NULL);