]> rtime.felk.cvut.cz Git - hercules2020/jailhouse-build.git/blob - test/memguard-test.c
Update memgaurd test and add test for throttling correctness
[hercules2020/jailhouse-build.git] / test / memguard-test.c
1 #define _GNU_SOURCE         /* See feature_test_macros(7) */
2 #include <err.h>
3 #include <sched.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <time.h>
9 #include <inttypes.h>
10 #include <sys/mman.h>
11 #include <stdbool.h>
12 #include <pthread.h>
13 #include <stdlib.h>
14 #include <error.h>
15
16 #ifndef SYS_memguard
17 #define SYS_memguard 793
18 #endif
19
20 /* Return value:
21  * 0-31  - Sum of memory events
22  * 32-55 - Total time
23  * 61    - Memory budget overrun
24  * 62    - Timeout
25  * 63    - Error */
26 #define MGRET_MEM_POS           0
27 #define MGRET_TIM_POS           32
28 #define MGRET_OVER_MEM_POS      61
29 #define MGRET_OVER_TIM_POS      62
30 #define MGRET_ERROR_POS         63
31
32 #define MGRET_TIM_MASK          (0x00FFFFFFul << MGRET_TIM_POS)
33 #define MGRET_MEM_MASK          (0xFFFFFFFFul << MGRET_MEM_POS)
34 #define MGRET_OVER_MEM_MASK     (1ul << MGRET_OVER_MEM_POS)
35 #define MGRET_OVER_TIM_MASK     (1ul << MGRET_OVER_TIM_POS)
36 #define MGRET_ERROR_MASK        (1ul << MGRET_ERROR_POS)
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_MASK) >> MGRET_TIM_POS;
55         mgr.mem = (retval & MGRET_MEM_MASK) >> MGRET_MEM_POS;
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 memguard(unsigned long timeout, unsigned long memory_budget,
116               unsigned long flags)
117 {
118         return syscall(SYS_memguard, timeout, memory_budget, flags);
119 }
120
121 void wvtest_pass(bool cond, const char* file, int line, const char* str)
122 {
123         printf("! %s:%d  %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
124 }
125
126 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
127
128 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
129                             int64_t retval, const char *code)
130 {
131         char call[80];
132         snprintf(call, sizeof(call), "memguard(%luus, %lumiss, %c%c):",
133                  timeout_us, mem_budget,
134                  flags & MGF_PERIODIC ? 'P' : '-',
135                  flags & MGF_MASK_INT ? 'I' : '-'
136                 );
137         struct mg_ret r = mgret(retval);
138         printf("Testing \"CPU%d: %-40s %-25s ⇒ time:%8u%c mem:%8u%c\" in %s:\n",
139                sched_getcpu(), call, code,
140                r.time, r.time_ovf ? '!' : ' ',
141                r.mem, r.mem_ovf ? '!' : ' ',
142                __FILE__);
143         WVPASS((retval & MGRET_ERROR_MASK) == 0);
144 }
145
146 #define MGTEST(timeout_us, mem_budget, flags, code)                     \
147         ({                                                              \
148                 long retval;                                            \
149                 retval = memguard(timeout_us, mem_budget, flags);       \
150                 code;                                                   \
151                 retval = memguard(0, 0, 0);                             \
152                 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
153                 mgret(retval);                                          \
154         })
155
156 void *test_thread(void *ptr)
157 {
158         cpu_set_t set;
159         int cpu = (intptr_t)ptr;
160
161         /* Ensure that our test thread does not migrate to another CPU
162          * during memguarding */
163         CPU_ZERO(&set);
164         CPU_SET(cpu, &set);
165         if (sched_setaffinity(0, sizeof(set), &set) < 0)
166                 err(1, "sched_setaffinity");
167
168         pthread_barrier_wait(&barrier);
169
170         struct mg_ret r;
171
172         for (uint64_t flags = 0; flags < 4; flags++) {
173                 compute_kernel(1); /* warm up */
174
175                 r = MGTEST(500, 10000, flags, compute_kernel(17*1000*1000));
176                 continue;
177
178                 ///////////////////////////////////////////////////////
179                 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
180                 WVPASS(!r.time_ovf);
181                 WVPASS(r.time > 900);
182
183                 r = MGTEST(500, 10000, flags, compute_kernel(1000));
184                 WVPASS(r.time_ovf);
185                 WVPASS(r.time > 900);
186
187                 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
188                 WVPASS(!r.time_ovf);
189                 WVPASS(r.time > 1900);
190
191                 r = MGTEST(500, 10000, flags, compute_kernel(2000));
192                 WVPASS(r.time_ovf);
193                 WVPASS(r.time > 1900);
194
195                 ///////////////////////////////////////////////////////
196                 r = MGTEST(100000, 500000, flags, read_memory(100000));
197                 WVPASS(!r.mem_ovf);
198                 WVPASS(r.mem >= 90000);
199
200                 r = MGTEST(100000, 50000,  flags, read_memory(100000));
201                 WVPASS(r.mem_ovf);
202                 WVPASS(r.mem >= 90000);
203
204                 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
205                 WVPASS(!r.mem_ovf);
206                 WVPASS(r.mem >= 90000);
207
208                 r = MGTEST(100000, 50000,  flags, read_memory_rnd(100000));
209                 WVPASS(r.mem_ovf);
210                 WVPASS(r.mem >= 90000);
211
212                 r = MGTEST(100000, 500000, flags, write_memory(100000));
213                 WVPASS(!r.mem_ovf);
214                 WVPASS(r.mem >= 90000);
215
216                 r = MGTEST(100000, 50000,  flags, write_memory(100000));
217                 WVPASS(r.mem_ovf);
218                 WVPASS(r.mem >= 90000);
219
220
221                 ///////////////////////////////////////////////////////
222                 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
223                 WVPASS(!r.mem_ovf);
224                 WVPASS(r.mem >= 900000);
225
226                 r = MGTEST(100000, 500000,  flags, read_memory(1000000));
227                 WVPASS(r.mem_ovf);
228                 WVPASS(r.mem >= 900000);
229
230                 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
231                 WVPASS(!r.mem_ovf);
232                 WVPASS(r.mem >= 900000);
233
234                 r = MGTEST(100000, 500000,  flags, read_memory_rnd(1000000));
235                 WVPASS(r.mem_ovf);
236                 WVPASS(r.mem >= 900000);
237
238                 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
239                 WVPASS(!r.mem_ovf);
240                 WVPASS(r.mem >= 900000);
241
242                 r = MGTEST(100000, 500000,  flags, write_memory(1000000));
243                 WVPASS(r.mem_ovf);
244                 WVPASS(r.mem >= 900000);
245
246                 printf("\n");
247         }
248
249         /* Throttling tests */
250         struct mg_ret r1, r2, r3;
251         r1 = MGTEST(10000, 9000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
252         WVPASS(r1.time > 100*1000);
253         r2 = MGTEST(10000, 3000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
254         WVPASS(r2.time > 2 * r1.time);
255         r3 = MGTEST(10000, 1000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
256         WVPASS(r3.time > 2 * r2.time);
257
258         return NULL;
259 }
260
261 int main(int argc, char *argv[])
262 {
263         int cpu_mask = 1;
264         int cpu_count = 0;
265         pthread_t threads[MAX_CORES];
266
267         /* TODO: currently shared memory */
268         for (int i = 0; i < sizeof(memory); i += 64)
269                 memory[i] = 1;
270
271         if (argc > 1)
272                 cpu_mask = strtol(argv[1], NULL, 16);
273
274         for (int i = 0; i < MAX_CORES; i++) {
275                 if (cpu_mask & (1 << i)) {
276                         cpu_count++;
277                 }
278         }
279
280         printf("CPU count:%d  CPU mask:%#x\n", cpu_count, cpu_mask);
281         int s = pthread_barrier_init(&barrier, NULL, cpu_count);
282         if (s != 0)
283                 error(1, s, "pthread_barrier_init");
284
285         for (intptr_t i = 0; i < MAX_CORES; i++) {
286                 if (cpu_mask & (1 << i))
287                         pthread_create(&threads[i], NULL, test_thread, (void *)i);
288         }
289
290         for (int i = 0; i < MAX_CORES; i++) {
291                 if (cpu_mask & (1 << i)) {
292                         pthread_join(threads[i], NULL);
293                 }
294         }
295
296         return 0;
297 }