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