summaryrefslogtreecommitdiffstats
path: root/baseline/source/extra.h
diff options
context:
space:
mode:
authorJoshua Bakita <jbakita@cs.unc.edu>2020-10-19 01:09:53 -0400
committerJoshua Bakita <jbakita@cs.unc.edu>2020-10-19 01:09:53 -0400
commita71fc97fd262e1b5770f827047ea60bbaf38d9a2 (patch)
treeb45ef48c63a35817f2db93dd2fec718778f58b99 /baseline/source/extra.h
parent41358857592f1908d0c0f9898b6c9acabc1ad161 (diff)
Unify all the versions of extra.h into a single multipurpose header
There was previously a huge amount of shared code that had to be copied back and forth. This should reduce the maintenance burden by containing all future changes to a single file. New unified library is fully backwards-compatible but also introduces and the easy-to-use `for_each_job` macro which replaces the specific `for(...) START_LOOP ... STOP_LOOP` format requirement and is generally much harder to abuse. New unified library also automatically cleans up its shared memory and semaphores, so this commit also removes the separate `cleanupSemaphores` binary. I also found a precursor of `extra.h` written by Sims in `litmusStuff.h`. This code is only interesting for historical purposes, so it is also removed in this commit. This commit also adds debug options to all the Makefiles and silences rm's complaints about non-existent files in make clean.
Diffstat (limited to 'baseline/source/extra.h')
-rw-r--r--baseline/source/extra.h350
1 files changed, 0 insertions, 350 deletions
diff --git a/baseline/source/extra.h b/baseline/source/extra.h
deleted file mode 100644
index e8f3d18..0000000
--- a/baseline/source/extra.h
+++ /dev/null
@@ -1,350 +0,0 @@
1/**
2 * Copyright 2019 Sims Hill Osborne and Joshua Bakita
3 *
4 * This header provides facilities by which to separably run and time TACLeBench
5 **/
6#define _GNU_SOURCE
7#include <time.h>
8#include <sys/mman.h>
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include <signal.h>
13#include <limits.h>
14#include <fcntl.h>
15#include <stdint.h>
16#include <sched.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 0
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
58float *_rt_exec_time;
59#if MMDC_PERF
60float *_rt_mmdc_read;
61float *_rt_mmdc_write;
62#endif
63long _rt_jobs_complete;
64long _rt_max_jobs;
65int _rt_core;
66int _rt_will_output;
67struct timespec _rt_start, _rt_end;
68
69char *_rt_run_id;
70char *_rt_our_prog_name;
71#define _RT_FILENAME_LEN 64
72
73#define LOAD_PARAMS_ITRL \
74 if (argc != 6) { \
75 fprintf(stderr, "Usage: %s <name> <loops> <my core> <runID> <save results?>\n", argv[0]);\
76 fprintf(stderr, " <loops> integer number of iterations. -1 for infitite.\n");\
77 fprintf(stderr, " <save results?> 1 to save results, 0 to discard.\n");\
78 fprintf(stderr, " <my core> UNUSED. Core is now auto-detected.\n");\
79 exit(1);\
80 }\
81 _rt_our_prog_name = argv[1];\
82 _rt_max_jobs = atol(argv[2]);\
83 _rt_core = sched_getcpu();\
84 _rt_run_id = argv[4];\
85 _rt_will_output = atoi(argv[5]);\
86 if (_rt_max_jobs < 0 && _rt_will_output != 0) {\
87 fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n");\
88 exit(1);\
89 }\
90 if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) {\
91 fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN);\
92 exit(1);\
93 }\
94 _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
95 if (!_rt_exec_time) {\
96 perror("Unable to allocate buffer for execution times");\
97 exit(1);\
98 }\
99 _rt_jobs_complete = 0;\
100 mlockall(MCL_CURRENT || MCL_FUTURE);
101
102#define SETUP_MMDC \
103 _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
104 _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\
105 if (!_rt_mmdc_read || !_rt_mmdc_write) {\
106 perror("Unable to allocate buffer for MMDC data");\
107 exit(1);\
108 }\
109 MMDC_PROFILE_RES_t mmdc_res;\
110 memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\
111 int fd = open("/dev/mem", O_RDWR, 0);\
112 if (fd < 0) {\
113 perror("Unable to open /dev/mem");\
114 exit(1);\
115 }\
116 pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\
117 if (mmdc == MAP_FAILED) {\
118 perror("Unable to map MMDC address space");\
119 exit(1);\
120 }\
121 mmdc->madpcr1 = axi_arm1;\
122 msync(&(mmdc->madpcr1),4,MS_SYNC);
123
124#define SETUP_LITMUS \
125 unsigned int wait = 0; \
126 if (be_migrate_to_domain(_rt_core) < 0) { \
127 perror("Unable to migrate to specified CPU"); \
128 exit(1); \
129 } \
130 struct reservation_config res; \
131 res.id = gettid(); \
132 res.cpu = cpu; \
133 res.priority = LITMUS_HIGHEST_PRIORITY; \
134 /* we take over half the CPU time (these are ns) */ \
135 res.polling_params.budget = ms2ns(3000); \
136 res.polling_params.period = ms2ns(3000); \
137 res.polling_params.offset = 0; \
138 res.polling_params.relative_deadline = ms2ns(3000); \
139 /* Not 100% sure that we should use periodic polling */ \
140 if (reservation_create(PERIODIC_POLLING, &res) < 0) { \
141 perror("Unable to create reservation"); \
142 exit(1); \
143 } \
144 struct rt_task rt_param; \
145 init_rt_task_param(&rt_param); \
146 /* Supposedly the next two parameters are irrelevant when reservations are enabled, but I'm leaving them anyway... */ \
147 rt_param.exec_cost = ms2ns(999); \
148 rt_param.period = ms2ns(1000); \
149 rt_param.priority = LITMUS_HIGHEST_PRIORITY; \
150 rt_param.cls = RT_CLASS_HARD; \
151 rt_param.release_policy = TASK_PERIODIC; \
152 rt_param.budget_policy = NO_ENFORCEMENT; \
153 rt_param.cpu = cpu; \
154 if (set_rt_task_param(gettid(), &rt_param) < 0) { \
155 perror("Unable to set real-time parameters"); \
156 exit(1); \
157 } \
158 if (init_litmus() != 0) { \
159 perror("init_litmus failed"); \
160 exit(1); \
161 } \
162 MC2_SETUP \
163 if (task_mode(LITMUS_RT_TASK) != 0) { \
164 perror("Unable to become real-time task"); \
165 exit(1); \
166 } \
167 if (wait && wait_for_ts_release() != 0) { \
168 perror("Unable to wait for taskset release"); \
169 exit(1); \
170 }
171
172#if MC2
173#define MC2_SETUP \
174 struct mc2_task mc2_param; \
175 mc2_param.res_id = gettid(); \
176 mc2_param.crit = CRIT_LEVEL_A; \
177 if (set_mc2_task_param(gettid(), &mc2_param) < 0) { \
178 perror("Unable to set MC^2 task params"); \
179 exit(1); \
180 } \
181 set_page_color(rt_param.cpu);
182#else
183#define MC2_SETUP
184#endif
185
186#define CLEANUP_LITMUS \
187 if (task_mode(BACKGROUND_TASK) != 0) { \
188 perror("Unable to become a real-time task"); \
189 exit(1); \
190 } \
191 reservation_destroy(gettid(), rt_param.cpu);
192
193#if __arm__
194// On ARM, manually flush the cache
195#define FLUSH_CACHES \
196 volatile uint8_t buffer[L2_SIZE * 4]; \
197 for (uint32_t j = 0; j < 4; j++) \
198 for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \
199 buffer[i]++;
200#else
201// On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0)
202#define FLUSH_CACHES \
203 FILE *fp = fopen("/proc/wbinvd", "r");\
204 if (fp == NULL) {\
205 perror("Cache flush module interface cannot be opened");\
206 exit(1);\
207 }\
208 char dummy;\
209 if (fread(&dummy, 1, 1, fp) == 0) {\
210 perror("Unable to access cache flush module interface");\
211 exit(1);\
212 }\
213 fclose(fp);
214#endif
215
216// Buffer timing result from a single job
217static void _rt_save_job_result() {
218 if (_rt_jobs_complete >= _rt_max_jobs) {
219 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);
220 exit(1);
221 }
222 if (_rt_jobs_complete > -1 && _rt_will_output) {
223 _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec;
224 _rt_exec_time[_rt_jobs_complete] *= 1e9;
225 _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec;
226#if MMDC_PROF
227 _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes;
228 _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes;
229#endif
230 }
231}
232
233// Save all buffered timing results to disk
234static void _rt_write_to_file() {
235 char fileName[_RT_FILENAME_LEN];
236 FILE *fp;
237 if (!_rt_will_output)
238 return;
239 munlockall();
240 strcpy(fileName, _rt_run_id);
241 strcat(fileName, ".txt");
242 fp = fopen(fileName, "a");
243 if (fp == NULL) {
244 perror("Unable to open _rt_will_output file");
245 exit(1);
246 }
247 // Same format as the paired results with "none" for unused fields
248 for (int i = 0; i < _rt_jobs_complete; i++){
249 fprintf(fp, "%s none %u none %ld %.f %s %d %.f %.f \n",
250 _rt_our_prog_name, _rt_core, _rt_max_jobs,
251 _rt_exec_time[i], _rt_run_id, i,
252#if MMDC_PROF
253 _rt_mmdc_read[i], _rt_mmdc_write[i]);
254#else
255 0.0, 0.0);
256#endif
257 }
258 fclose(fp);
259#if LITMUS
260 CLEANUP_LITMUS
261#endif
262}
263
264// Start a job
265static void _rt_start_loop() {
266#if LITMUS
267 if (sleep_next_period() != 0) {
268 perror("Unable to sleep for next period");
269 }
270#else
271 sched_yield();
272#endif
273 FLUSH_CACHES
274#if MMDC_PROF
275 /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */
276 start_mmdc_profiling(mmdc);
277#endif
278 clock_gettime(CLOCK_MONOTONIC, &_rt_start);
279}
280
281// Complete a job
282static void _rt_stop_loop() {
283 clock_gettime(CLOCK_MONOTONIC, &_rt_end);
284#if MMDC_PROF
285 /* This freezes the profiling and makes results available */
286 pause_mmdc_profiling(mmdc);
287 get_mmdc_profiling_results(mmdc, &mmdc_res);
288#endif
289 _rt_save_job_result();
290 _rt_jobs_complete++;
291}
292
293/****** New API ******
294 * Intended structure:
295 *
296 * |int main(int argc, char **argv) {
297 * | SET_UP
298 * | ...
299 * | for_each_job {
300 * | tacleInit();
301 * | tacleMain();
302 * | }
303 * | WRITE_TO_FILE
304 * |}
305 *
306 * The main() function must call its parameters argc and argv for SET_UP to be
307 * able to read them.
308 * Only SET_UP necessarily has to be in main().
309 *
310 * We use some niche C features, here's a quick explaination:
311 * 1. The && operator doesn't evaluate the right-hand side of the expression
312 * unless the left side evaluated to true. We use this to only execute
313 * _rt_start_loop() when the loop will actually run.
314 * 2. The comma operator executes the first expression and then throws away the
315 * result. We use this to call our void function from inside a comparison.
316 */
317#define for_each_job \
318 for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \
319 _rt_stop_loop())
320
321/****** Legacy API ******
322 * Intended structure:
323 *
324 * |int main(int argc, char **argv) {
325 * | SET_UP
326 * | for (jobsComplete=0; jobsComplete<maxJobs; jobsComplete++){
327 * | START_LOOP
328 * | tacleInit();
329 * | tacleMain();
330 * | STOP_LOOP
331 * | }
332 * | WRITE_TO_FILE
333 * | tacleReturn
334 * |}
335 *
336 * The main() function must call its parameters argc and argv for SET_UP to be
337 * able to read them.
338 */
339static int jobsComplete = 0;
340#define START_LOOP _rt_start_loop();
341#define STOP_LOOP _rt_stop_loop();
342#define WRITE_TO_FILE _rt_write_to_file();
343#define maxJobs _rt_max_jobs
344// Has been part of STOP_LOOP for quite some time
345#define SAVE_RESULTS \
346 #warning "The SAVE_RESULTS macro is deprecated and will soon be removed!";
347// Unclear if SLEEP is used anywhere.
348#define SLEEP \
349 #warning "The SLEEP macro is deprecated and may be removed!" \
350 nanosleep((const struct timespec[]){{0, 1000000}}, NULL);