]> rtime.felk.cvut.cz Git - hercules2020/jailhouse-build.git/blob - test/memguard-test.c
d983d16e4c0d2469ff867674f2bdd2ce4f214190
[hercules2020/jailhouse-build.git] / test / memguard-test.c
1 /* Run this as: for i in $(seq 0 5); do prem-test $i & done */
2
3 #define _GNU_SOURCE         /* See feature_test_macros(7) */
4 #include <err.h>
5 #include <sched.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <inttypes.h>
12 #include <sys/mman.h>
13 #include <stdbool.h>
14 #include <pthread.h>
15 #include <stdlib.h>
16 #include <error.h>
17
18 #ifndef SYS_prem_memguard_check
19 #define SYS_prem_memguard_check 793
20 #endif
21
22 /* Return value:
23  * 0-31  - Total time
24  * 32-55 - Sum of memory events
25  * 61    - Memory budget overrun
26  * 62    - Timeout
27  * 63    - Error */
28 #define MGRET_TIM_POS                   0
29 #define MGRET_MEM_POS                   32
30 #define MGRET_OVER_MEM_POS      61
31 #define MGRET_OVER_TIM_POS      62
32
33 #define MGRET_TIM_MASK          0xFFFFFFFFlu
34 #define MGRET_MEM_MASK          0xFFFFFFlu
35 #define MGRET_OVER_MEM_MASK     0x1lu
36 #define MGRET_OVER_TIM_MASK     0x1lu
37
38 #define MAX_CORES           6
39
40 char memory[128*10204*1024];
41
42 static pthread_barrier_t barrier;
43
44 struct mg_ret {
45         uint32_t time;
46         uint32_t mem;
47         bool time_ovf;
48         bool mem_ovf;
49 };
50
51 static struct mg_ret mgret(uint64_t retval)
52 {
53         struct mg_ret mgr = {};
54         mgr.time = (retval >> MGRET_TIM_POS) & MGRET_TIM_MASK;
55         mgr.mem = (retval >> MGRET_MEM_POS) & MGRET_MEM_MASK;
56         mgr.time_ovf = (retval >> MGRET_OVER_TIM_POS) & 1;
57         mgr.mem_ovf = (retval >> MGRET_OVER_MEM_POS) & 1;
58         return mgr;
59 }
60
61 void compute_kernel(int time_us)
62 {
63         struct timespec ts;
64         uint64_t current_us, end_us;
65         clock_gettime(CLOCK_MONOTONIC, &ts);
66         end_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000 + time_us;
67         do {
68                 clock_gettime(CLOCK_MONOTONIC, &ts);
69                 current_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
70         } while (current_us < end_us);
71 }
72
73 void read_memory(long lines)
74 {
75         uint64_t sum = 0;
76
77         for (uint64_t i = 0; i < lines; i++) {
78                 sum += memory[(i * 64) % sizeof(memory)];
79         }
80         /* Do not optimize this function out */
81         volatile uint64_t x = sum;
82         (void)x;
83 }
84
85 void write_memory(long lines)
86 {
87         uint64_t *ptr;
88         for (uint64_t i = 0; i < lines; i++) {
89                 ptr = (void*)&memory[(i * 64) % sizeof(memory)];
90                 *ptr = i;
91         }
92 }
93
94 void read_memory_rnd(long lines)
95 {
96         static unsigned rnd;
97         uint64_t sum = 0;
98
99         for (long i = 0; i < lines; i++) {
100                 rnd = (rnd + 523) * 253573;
101                 sum += memory[rnd % sizeof(memory)];
102         }
103         /* Do not optimize this function out */
104         volatile uint64_t x = sum;
105         (void)x;
106 }
107
108 /* Memguard flags */
109 #define MGF_PERIODIC                                                           \
110         (1 << 0) /* Chooses between periodic or one-shot budget replenishment */
111 #define MGF_MASK_INT                                                           \
112         (1                                                                     \
113          << 1) /* Mask (disable) low priority interrupts until next memguard call */
114
115 long prem_memguard_check(unsigned long timeout, unsigned long memory_budget,
116                          unsigned long flags)
117 {
118         return syscall(SYS_prem_memguard_check, timeout, memory_budget, flags);
119 }
120
121 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
122                             int64_t retval, const char *code)
123 {
124         char call[80];
125         snprintf(call, sizeof(call), "memguard(%luus, %lumiss, %c%c):",
126                  timeout_us, mem_budget,
127                  flags & MGF_PERIODIC ? 'P' : '-',
128                  flags & MGF_MASK_INT ? 'I' : '-'
129                 );
130         struct mg_ret r = mgret(retval);
131         printf("Testing \"CPU%d: %-40s %-25s ⇒ time:%8u%c mem:%8u%c\" in %s:\n",
132                sched_getcpu(), call, code,
133                r.time, r.time_ovf ? '!' : ' ',
134                r.mem, r.mem_ovf ? '!' : ' ',
135                __FILE__);
136 }
137
138 void wvtest_pass(bool cond, const char* file, int line, const char* str)
139 {
140         printf("! %s:%d  %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
141 }
142
143 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
144
145 #define MGTEST(timeout_us, mem_budget, flags, code)                            \
146         ({                                                                     \
147                 long retval;                                                   \
148                 retval = prem_memguard_check(timeout_us, mem_budget, flags);   \
149                 code;                                                          \
150                 retval = prem_memguard_check(0, 0, 0);                         \
151                 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
152                 mgret(retval);                                                 \
153         })
154
155 void *test_thread(void *ptr)
156 {
157         cpu_set_t set;
158         int cpu = (intptr_t)ptr;
159
160         /* Ensure that memory phase starts and ends on the same CPU */
161         CPU_ZERO(&set);
162         CPU_SET(cpu, &set);
163         if (sched_setaffinity(0, sizeof(set), &set) < 0)
164                 err(1, "sched_setaffinity");
165
166         pthread_barrier_wait(&barrier);
167
168         struct mg_ret r;
169         for (uint64_t flags = 0; flags < 4; flags++) {
170                 compute_kernel(1); /* warm up */
171
172                 ///////////////////////////////////////////////////////
173                 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
174                 WVPASS(!r.time_ovf);
175                 WVPASS(r.time > 900);
176
177                 r = MGTEST(500, 10000, flags, compute_kernel(1000));
178                 WVPASS(r.time_ovf);
179                 WVPASS(r.time > 900);
180
181                 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
182                 WVPASS(!r.time_ovf);
183                 WVPASS(r.time > 1900);
184
185                 r = MGTEST(500, 10000, flags, compute_kernel(2000));
186                 WVPASS(r.time_ovf);
187                 WVPASS(r.time > 1900);
188
189                 ///////////////////////////////////////////////////////
190                 r = MGTEST(100000, 500000, flags, read_memory(100000));
191                 WVPASS(!r.mem_ovf);
192                 WVPASS(r.mem >= 100000);
193
194                 r = MGTEST(100000, 50000,  flags, read_memory(100000));
195                 WVPASS(r.mem_ovf);
196                 WVPASS(r.mem >= 100000);
197
198                 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
199                 WVPASS(!r.mem_ovf);
200                 WVPASS(r.mem >= 100000);
201
202                 r = MGTEST(100000, 50000,  flags, read_memory_rnd(100000));
203                 WVPASS(r.mem_ovf);
204                 WVPASS(r.mem >= 100000);
205
206                 r = MGTEST(100000, 500000, flags, write_memory(100000));
207                 WVPASS(!r.mem_ovf);
208                 WVPASS(r.mem >= 100000);
209
210                 r = MGTEST(100000, 50000,  flags, write_memory(100000));
211                 WVPASS(r.mem_ovf);
212                 WVPASS(r.mem >= 100000);
213
214
215                 ///////////////////////////////////////////////////////
216                 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
217                 WVPASS(!r.mem_ovf);
218                 WVPASS(r.mem >= 900000);
219
220                 r = MGTEST(100000, 500000,  flags, read_memory(1000000));
221                 WVPASS(r.mem_ovf);
222                 WVPASS(r.mem >= 900000);
223
224                 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
225                 WVPASS(!r.mem_ovf);
226                 WVPASS(r.mem >= 900000);
227
228                 r = MGTEST(100000, 500000,  flags, read_memory_rnd(1000000));
229                 WVPASS(r.mem_ovf);
230                 WVPASS(r.mem >= 900000);
231
232                 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
233                 WVPASS(!r.mem_ovf);
234                 WVPASS(r.mem >= 900000);
235
236                 r = MGTEST(100000, 500000,  flags, write_memory(1000000));
237                 WVPASS(r.mem_ovf);
238                 WVPASS(r.mem >= 900000);
239
240                 printf("\n");
241         }
242
243         return NULL;
244 }
245
246 int main(int argc, char *argv[])
247 {
248         int cpu_mask = 1;
249         int cpu_count = 0;
250         pthread_t threads[MAX_CORES];
251
252         /* TODO: currently shared memory */
253         for (int i = 0; i < sizeof(memory); i += 64)
254                 memory[i] = 1;
255
256         if (argc > 1)
257                 cpu_mask = strtol(argv[1], NULL, 16);
258
259         for (int i = 0; i < MAX_CORES; i++) {
260                 if (cpu_mask & (1 << i)) {
261                         cpu_count++;
262                 }
263         }
264
265         printf("CPU count:%d  CPU mask:%#x\n", cpu_count, cpu_mask);
266         int s = pthread_barrier_init(&barrier, NULL, cpu_count);
267         if (s != 0)
268                 error(1, s, "pthread_barrier_init");
269
270         for (intptr_t i = 0; i < MAX_CORES; i++) {
271                 if (cpu_mask & (1 << i))
272                         pthread_create(&threads[i], NULL, test_thread, (void *)i);
273         }
274
275         for (int i = 0; i < MAX_CORES; i++) {
276                 if (cpu_mask & (1 << i)) {
277                         pthread_join(threads[i], NULL);
278                 }
279         }
280
281         return 0;
282 }