summaryrefslogtreecommitdiffstats
path: root/SD-VBS/common/c/extra.h
diff options
context:
space:
mode:
Diffstat (limited to 'SD-VBS/common/c/extra.h')
-rw-r--r--SD-VBS/common/c/extra.h508
1 files changed, 0 insertions, 508 deletions
diff --git a/SD-VBS/common/c/extra.h b/SD-VBS/common/c/extra.h
deleted file mode 100644
index 9c72064..0000000
--- a/SD-VBS/common/c/extra.h
+++ /dev/null
@@ -1,508 +0,0 @@
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// This is a proxy for "case study mode" now
33#define LITMUS 1
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// Store state globally so that the job can be outside main()
45// Arrays use float as a comprimise between overflow and size
46// Paired arrays use long longs as precision is more important for those times
47#ifdef PAIRED
48long long *_rt_start_time;
49long long *_rt_end_time;
50#else
51float *_rt_exec_time;
52#endif
53#if MMDC_PERF
54float *_rt_mmdc_read;
55float *_rt_mmdc_write;
56#endif
57long _rt_jobs_complete;
58long _rt_max_jobs;
59int _rt_core;
60int _rt_will_output;
61struct timespec _rt_start, _rt_end;
62
63char *_rt_run_id;
64char *_rt_our_prog_name;
65char *_rt_other_prog_name;
66char *_rt_other_core;
67#define _RT_FILENAME_LEN 64
68#define _BILLION (1000*1000*1000)
69#ifdef PAIRED
70char *_rt_barrier;
71sem_t *_rt_first_sem, *_rt_second_sem;
72int _rt_lock_id;
73#define _ID_SZ 128
74char _rt_sem1_name[_ID_SZ] = "/_libextra_first_sem-";
75char _rt_sem2_name[_ID_SZ] = "/_libextra_second_sem-";
76char _rt_shm_name[_ID_SZ] = "/_libextra_barrier-";
77#endif /* PAIRED */
78
79#if LITMUS
80long unsigned int _rt_period;
81#endif
82
83static void _rt_load_params_itrl(int argc, char **argv) {
84#ifdef PAIRED
85 if (argc != (8 + LITMUS*2) && argc != (9 + LITMUS*2)) {
86 fprintf(stderr, "Usage: %s <name> <loops> <my core> <other core> <other name> <runID> <save results?>", argv[0]);
87#else
88 if (argc != (6 + LITMUS*2)) {
89 fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);
90#endif /* PAIRED */
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> integer core number. Only used for LITMUS-RT.\n");
94#ifdef PAIRED
95 fprintf(stderr, " <other core> integer for logging. Core of paired task.\n");
96 fprintf(stderr, " <other name> string for logging. Name of paired task.\n");
97#endif /* PAIRED */
98 fprintf(stderr, " <runID> string to append with .txt to yield output file name.\n");
99 fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");
100#ifdef PAIRED
101 fprintf(stderr, " <pairID> (optional).\n");
102#endif
103#if LITMUS
104 fprintf(stderr, " <task period> in ms\n");
105 fprintf(stderr, " <task criticality level> 0 for Level-A, 1 for Level-B, 2 for Level-C\n");
106#endif /* LITMUS */
107 exit(1);
108 }
109 _rt_our_prog_name = argv[1];
110 _rt_max_jobs = atol(argv[2]);
111#if !LITMUS
112 _rt_core = sched_getcpu();
113#else
114 _rt_core = atoi(argv[3]);
115#endif
116#ifdef PAIRED
117 _rt_other_core = argv[4];
118 _rt_other_prog_name = argv[5];
119 _rt_run_id = argv[6];
120 _rt_will_output = atoi(argv[7]);
121 char *pairId;
122 int end;
123 if (argc > 8) {
124 pairId = argv[8];
125 end = 8;
126 } else {
127 pairId = "none";
128 end = 9;
129 }
130#else
131 _rt_other_core = "none";
132 _rt_other_prog_name = "none";
133 _rt_run_id = argv[4];
134 _rt_will_output = atoi(argv[5]);
135 int end = 6;
136#endif /* PAIRED */
137 if (_rt_max_jobs < 0 && _rt_will_output != 0) {
138 fprintf(stderr, "Infinite loops only supported when output is disabled!\n");
139 exit(1);
140 }
141 if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {
142 fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);
143 exit(1);
144 }
145#ifdef PAIRED
146 // __rt_sem2_name happens to be the longest
147 if (strlen(pairId) + strlen(_rt_sem2_name) > _ID_SZ) {
148 fprintf(stderr, "PairID is too long! Maximum length is %ld characters.\n", _ID_SZ - strlen(_rt_sem2_name));
149 exit(1);
150 }
151 _rt_start_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long));
152 _rt_end_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long));
153 if (!_rt_end_time || !_rt_start_time) {
154 perror("Unable to allocate buffers for execution times");
155 exit(1);
156 }
157 // Use PairID to create unique semaphore and shared memory paths
158 strcat(_rt_sem1_name, pairId);
159 strcat(_rt_sem2_name, pairId);
160 strcat(_rt_shm_name, pairId);
161 _rt_first_sem = sem_open(_rt_sem1_name, O_CREAT, 644, 0);
162 _rt_second_sem = sem_open(_rt_sem2_name, O_CREAT, 644, 0);
163 if (_rt_first_sem == SEM_FAILED || _rt_second_sem == SEM_FAILED) {
164 perror("Error while creating semaphores");
165 exit(1);
166 }
167 // Create shared memory for barrier synchronization and infer lock ID
168 int barrier_file = shm_open(_rt_shm_name, O_CREAT | O_RDWR | O_EXCL, 644);
169 if (barrier_file == -1) {
170 // File already existed - we're the 2nd program and thus lock ID 2
171 _rt_lock_id = 2;
172 barrier_file = shm_open(_rt_shm_name, O_CREAT | O_RDWR, 644);
173 } else {
174 _rt_lock_id = 1;
175 }
176 if (barrier_file == -1) {
177 perror("Error while creating shared memory for barrier synchronization");
178 exit(1);
179 }
180 if (ftruncate(barrier_file, 2) == -1) {
181 perror("Error while setting size of shared memory for barrier synchronization");
182 exit(1);
183 }
184 _rt_barrier = mmap(NULL, 2, PROT_WRITE, MAP_SHARED, barrier_file, 0);
185 if (_rt_barrier == MAP_FAILED) {
186 perror("Error while mapping shared memory for barrier synchronization");
187 exit(1);
188 }
189 // If we're the 2nd user of this barrier, mark it as in-use
190 if (_rt_lock_id == 2 && !__sync_bool_compare_and_swap(_rt_barrier+1, 0, 1)) {
191 fprintf(stderr, "Pair ID already in use!\n");
192 exit(1);
193 }
194 *_rt_barrier = 0;
195#else
196 _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));
197 if (!_rt_exec_time) {
198 perror("Unable to allocate buffer for execution times");
199 exit(1);
200 }
201#endif /* PAIRED */
202 _rt_jobs_complete = 0;
203 mlockall(MCL_CURRENT || MCL_FUTURE);
204#if LITMUS
205 _rt_period = strtoul(argv[end], NULL, 10);
206 unsigned int crit = atoi(argv[end+1]);
207 unsigned int wait = 1;
208 if (be_migrate_to_domain(_rt_core) < 0) {
209 perror("Unable to migrate to specified CPU");
210 exit(1);
211 }
212 struct rt_task rt_param;
213 init_rt_task_param(&rt_param);
214 // Fake exec cost - this value ignored by the MC^2 scheduler
215 rt_param.exec_cost = _rt_period;
216 rt_param.period = ms2ns(_rt_period);
217 rt_param.relative_deadline = 0;
218 rt_param.phase = 0;
219 rt_param.priority = LITMUS_LOWEST_PRIORITY;
220 rt_param.cls = crit;
221 rt_param.release_policy = TASK_PERIODIC;
222 rt_param.budget_policy = NO_ENFORCEMENT;
223 rt_param.cpu = _rt_core;
224 if (set_rt_task_param(gettid(), &rt_param) < 0) {
225 perror("Unable to set real-time parameters");
226 exit(1);
227 }
228 if (init_litmus() != 0) {
229 perror("init_litmus failed");
230 exit(1);
231 }
232 if (task_mode(LITMUS_RT_TASK) != 0) {
233 perror("Unable to become real-time task");
234 exit(1);
235 }
236 if (wait && wait_for_ts_release() != 0) {
237 perror("Unable to wait for taskset release");
238 exit(1);
239 }
240#endif /* LITMUS */
241#if MMDC_PROF
242 SETUP_MMDC
243#endif
244}
245
246#define SETUP_MMDC \
247 _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
248 _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
249 if (!_rt_mmdc_read || !_rt_mmdc_write) {\
250 perror("Unable to allocate buffer for MMDC data");\
251 exit(1);\
252 }\
253 MMDC_PROFILE_RES_t mmdc_res;\
254 memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\
255 int fd = open("/dev/mem", O_RDWR, 0);\
256 if (fd < 0) {\
257 perror("Unable to open /dev/mem");\
258 exit(1);\
259 }\
260 pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\
261 if (mmdc == MAP_FAILED) {\
262 perror("Unable to map MMDC address space");\
263 exit(1);\
264 }\
265 mmdc->madpcr1 = axi_arm1;\
266 msync(&(mmdc->madpcr1),4,MS_SYNC);
267
268#if __arm__
269// On ARM, manually flush the cache
270#define FLUSH_CACHES \
271 volatile uint8_t buffer[L2_SIZE * 4]; \
272 for (uint32_t j = 0; j < 4; j++) \
273 for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \
274 buffer[i]++;
275#else
276// On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0)
277#define FLUSH_CACHES \
278 FILE *fp = fopen("/proc/wbinvd", "r");\
279 if (fp == NULL) {\
280 perror("Cache flush module interface cannot be opened");\
281 exit(1);\
282 }\
283 char dummy;\
284 if (fread(&dummy, 1, 1, fp) == 0) {\
285 perror("Unable to access cache flush module interface");\
286 exit(1);\
287 }\
288 fclose(fp);
289#endif
290
291// This semaphore-based synchronization is from Sims
292#define FIRST_UNLOCK \
293 if (_rt_lock_id == 1) {\
294 if (sem_post(_rt_second_sem) != 0) {\
295 perror("Unable to unlock second semaphore");\
296 exit(1);\
297 }\
298 } \
299 else {\
300 if (sem_post(_rt_first_sem) != 0) {\
301 perror("Unable to unlock first semaphore");\
302 exit(1);\
303 }\
304 } \
305
306#define FIRST_LOCK \
307 if (_rt_lock_id == 1) {\
308 if (sem_wait(_rt_first_sem) != 0) {\
309 perror("Unable to wait on first semaphore");\
310 exit(1);\
311 }\
312 }\
313 else {\
314 if (sem_wait(_rt_second_sem) != 0) {\
315 perror("Unable to wait on second semaphore");\
316 exit(1);\
317 }\
318 }
319
320// This ensures a very low difference between pair member start times
321#define BARRIER_SYNC \
322 if (__sync_bool_compare_and_swap(_rt_barrier, 0, 1)) {\
323 while (!__sync_bool_compare_and_swap(_rt_barrier, 0, 0)) {};\
324 }\
325 else {\
326 __sync_bool_compare_and_swap(_rt_barrier, 1, 0);\
327 }
328
329// Buffer timing result from a single job
330static void _rt_save_job_result() {
331 if (!_rt_will_output)
332 return;
333 if (_rt_jobs_complete >= _rt_max_jobs) {
334 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);
335 exit(1);
336 }
337#ifdef PAIRED
338 _rt_start_time[_rt_jobs_complete] = _rt_start.tv_sec;
339 _rt_start_time[_rt_jobs_complete] *= _BILLION;
340 _rt_start_time[_rt_jobs_complete] += _rt_start.tv_nsec;
341 _rt_end_time[_rt_jobs_complete] = _rt_end.tv_sec;
342 _rt_end_time[_rt_jobs_complete] *= _BILLION;
343 _rt_end_time[_rt_jobs_complete] += _rt_end.tv_nsec;
344#else
345 _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec;
346 _rt_exec_time[_rt_jobs_complete] *= _BILLION;
347 _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec;
348#endif /* PAIRED */
349#if MMDC_PROF
350 _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes;
351 _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes;
352#endif /* MMDC_PROF */
353}
354
355// Save all buffered timing results to disk
356static void _rt_write_to_file() {
357 char fileName[_RT_FILENAME_LEN];
358 FILE *fp;
359 munlockall();
360 if (!_rt_will_output)
361 goto out;
362 strcpy(fileName, _rt_run_id);
363 strcat(fileName, ".txt");
364 fp = fopen(fileName, "a");
365 if (fp == NULL) {
366 perror("Unable to open output file");
367 exit(1);
368 }
369 // Baseline output uses a similar format with "none" for unused fields
370 for (int i = 0; i < _rt_jobs_complete; i++){
371 fprintf(fp, "%s %s %u %s %ld", _rt_our_prog_name, _rt_other_prog_name,
372 _rt_core, _rt_other_core, _rt_max_jobs);
373#ifdef PAIRED
374 // For unclear legacy reasons, paired tasks emit sec and ns separately
375 fprintf(fp, " %lld %lld %lld %lld",
376 _rt_start_time[i] / _BILLION, _rt_start_time[i] % _BILLION,
377 _rt_end_time[i] / _BILLION, _rt_end_time[i] % _BILLION);
378#else
379 fprintf(fp, " %.f", _rt_exec_time[i]);
380#endif /* PAIRED */
381 fprintf(fp, " %s %d %.f %.f\n", _rt_run_id, i,
382#if MMDC_PROF
383 _rt_mmdc_read[i], _rt_mmdc_write[i]);
384#else
385 0.0, 0.0);
386#endif /* MMDC_PROF */
387 }
388 fclose(fp);
389out:
390#if LITMUS
391 if (task_mode(BACKGROUND_TASK) != 0) {
392 perror("Unable to become a real-time task");
393 exit(1);
394 }
395#endif /* LITMUS */
396#ifdef PAIRED
397 munmap(_rt_barrier, 2);
398 sem_unlink(_rt_sem1_name);
399 sem_unlink(_rt_sem2_name);
400 shm_unlink(_rt_shm_name);
401 free(_rt_start_time);
402 free(_rt_end_time);
403#else
404 free(_rt_exec_time);
405#endif /* PAIRED */
406#if MMDC_PROF
407 free(_rt_mmdc_read);
408 free(_rt_mmdc_write);
409#endif /* MMDC_PROF */
410}
411
412// Start a job
413static void _rt_start_loop() {
414#if LITMUS
415 if (sleep_next_period() != 0) {
416 perror("Unable to sleep for next period");
417 }
418#else
419 sched_yield();
420#endif /* LITMUS */
421#ifdef PAIRED
422 FIRST_UNLOCK
423 FIRST_LOCK
424#endif /* PAIRED */
425#if !LITMUS
426 FLUSH_CACHES
427#endif
428#ifdef PAIRED
429 BARRIER_SYNC
430#endif /* PAIRED */
431#if MMDC_PROF
432 /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */
433 start_mmdc_profiling(mmdc);
434#endif /* MMDC_PROF */
435 clock_gettime(CLOCK_MONOTONIC, &_rt_start);
436}
437
438// Complete a job
439static void _rt_stop_loop() {
440 clock_gettime(CLOCK_MONOTONIC, &_rt_end);
441#if MMDC_PROF
442 /* This freezes the profiling and makes results available */
443 pause_mmdc_profiling(mmdc);
444 get_mmdc_profiling_results(mmdc, &mmdc_res);
445#endif /* MMDC_PROF */
446 _rt_save_job_result();
447 _rt_jobs_complete++;
448}
449
450/****** New API ******
451 * Intended structure:
452 *
453 * |int main(int argc, char **argv) {
454 * | SET_UP
455 * | ...
456 * | for_each_job {
457 * | tacleInit();
458 * | tacleMain();
459 * | }
460 * | WRITE_TO_FILE
461 * |}
462 *
463 * The main() function must call its parameters argc and argv for SET_UP to be
464 * able to read them.
465 * Only SET_UP necessarily has to be in main().
466 *
467 * We use some niche C features, here's a quick explaination:
468 * 1. The && operator doesn't evaluate the right-hand side of the expression
469 * unless the left side evaluated to true. We use this to only execute
470 * _rt_start_loop() when the loop will actually run.
471 * 2. The comma operator executes the first expression and then throws away the
472 * result. We use this to call our void function from inside a comparison.
473 */
474#define for_each_job \
475 for (; (_rt_max_jobs == -1 || _rt_jobs_complete < _rt_max_jobs) && (_rt_start_loop(),1); \
476 _rt_stop_loop())
477
478/****** Legacy API ******
479 * Intended structure:
480 *
481 * |int main(int argc, char **argv) {
482 * | SET_UP
483 * | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){
484 * | START_LOOP
485 * | tacleInit();
486 * | tacleMain();
487 * | STOP_LOOP
488 * | }
489 * | WRITE_TO_FILE
490 * | tacleReturn
491 * |}
492 *
493 * The main() function must call its parameters argc and argv for SET_UP to be
494 * able to read them.
495 */
496static int jobsComplete = 0;
497#define SET_UP _rt_load_params_itrl(argc, argv);
498#define START_LOOP _rt_start_loop();
499#define STOP_LOOP _rt_stop_loop();
500#define WRITE_TO_FILE _rt_write_to_file();
501#define maxJobs _rt_max_jobs
502// Has been part of STOP_LOOP for quite some time
503#define SAVE_RESULTS \
504 #warning "The SAVE_RESULTS macro is deprecated and will soon be removed!";
505// Unclear if SLEEP is used anywhere.
506#define SLEEP \
507 #warning "The SLEEP macro is deprecated and may be removed!" \
508 nanosleep((const struct timespec[]){{0, 1000000}}, NULL);