1 #define _GNU_SOURCE /* See feature_test_macros(7) */
17 #define SYS_memguard 793
21 * 0-31 - Sum of memory events
23 * 61 - Memory budget overrun
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
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)
40 char memory[128*10204*1024];
42 static pthread_barrier_t barrier;
51 static struct mg_ret mgret(uint64_t retval)
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;
61 void compute_kernel(int time_us)
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;
68 clock_gettime(CLOCK_MONOTONIC, &ts);
69 current_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
70 } while (current_us < end_us);
73 void read_memory(long lines)
77 for (uint64_t i = 0; i < lines; i++) {
78 sum += memory[(i * 64) % sizeof(memory)];
80 /* Do not optimize this function out */
81 volatile uint64_t x = sum;
85 void write_memory(long lines)
88 for (uint64_t i = 0; i < lines; i++) {
89 ptr = (void*)&memory[(i * 64) % sizeof(memory)];
94 void read_memory_rnd(long lines)
99 for (long i = 0; i < lines; i++) {
100 rnd = (rnd + 523) * 253573;
101 sum += memory[rnd % sizeof(memory)];
103 /* Do not optimize this function out */
104 volatile uint64_t x = sum;
109 #define MGF_PERIODIC \
110 (1 << 0) /* Chooses between periodic or one-shot budget replenishment */
111 #define MGF_MASK_INT \
113 << 1) /* Mask (disable) low priority interrupts until next memguard call */
115 long memguard(unsigned long timeout, unsigned long memory_budget,
118 return syscall(SYS_memguard, timeout, memory_budget, flags);
121 void wvtest_pass(bool cond, const char* file, int line, const char* str)
123 printf("! %s:%d %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
126 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
128 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
129 int64_t retval, const char *code)
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' : '-'
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 ? '!' : ' ',
143 WVPASS((retval & MGRET_ERROR_MASK) == 0);
146 pthread_mutex_t wvtest_lock = PTHREAD_MUTEX_INITIALIZER;
148 #define MGTEST(timeout_us, mem_budget, flags, code) \
151 retval = memguard(timeout_us, mem_budget, flags); \
153 retval = memguard(0, 0, 0); \
154 pthread_mutex_lock(&wvtest_lock); \
155 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
159 #define MGTESTEND() pthread_mutex_unlock(&wvtest_lock)
161 void *test_thread(void *ptr)
164 int cpu = (intptr_t)ptr;
166 /* Ensure that our test thread does not migrate to another CPU
167 * during memguarding */
170 if (sched_setaffinity(0, sizeof(set), &set) < 0)
171 err(1, "sched_setaffinity");
173 pthread_barrier_wait(&barrier);
177 for (uint64_t flags = 0; flags < 4; flags++) {
178 compute_kernel(1); /* warm up */
180 ///////////////////////////////////////////////////////
181 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
183 WVPASS(r.time > 900);
184 WVPASS(!r.mem_ovf < 10000);
185 WVPASS(r.mem < 10000);
188 r = MGTEST(500, 10000, flags, compute_kernel(1000));
190 WVPASS(r.time > 900);
193 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
195 WVPASS(r.time > 1900);
198 r = MGTEST(500, 10000, flags, compute_kernel(2000));
200 WVPASS(r.time > 1900);
203 ///////////////////////////////////////////////////////
204 r = MGTEST(100000, 500000, flags, read_memory(100000));
206 WVPASS(r.mem >= 90000);
209 r = MGTEST(100000, 50000, flags, read_memory(100000));
211 WVPASS(r.mem >= 90000);
214 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
216 WVPASS(r.mem >= 90000);
219 r = MGTEST(100000, 50000, flags, read_memory_rnd(100000));
221 WVPASS(r.mem >= 90000);
224 r = MGTEST(100000, 500000, flags, write_memory(100000));
226 WVPASS(r.mem >= 90000);
229 r = MGTEST(100000, 50000, flags, write_memory(100000));
231 WVPASS(r.mem >= 90000);
235 ///////////////////////////////////////////////////////
236 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
238 WVPASS(r.mem >= 900000);
241 r = MGTEST(100000, 500000, flags, read_memory(1000000));
243 WVPASS(r.mem >= 900000);
246 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
248 WVPASS(r.mem >= 900000);
251 r = MGTEST(100000, 500000, flags, read_memory_rnd(1000000));
253 WVPASS(r.mem >= 900000);
256 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
258 WVPASS(r.mem >= 900000);
261 r = MGTEST(100000, 500000, flags, write_memory(1000000));
263 WVPASS(r.mem >= 900000);
269 /* Throttling tests */
270 struct mg_ret r1, r2, r3;
271 r1 = MGTEST(10000, 9000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
272 WVPASS(r1.time > 100*1000);
275 r2 = MGTEST(10000, 3000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
276 WVPASS(r2.time > 2 * r1.time);
279 r3 = MGTEST(10000, 1000, MGF_PERIODIC | MGF_MASK_INT, read_memory(100000));
280 WVPASS(r3.time > 2 * r2.time);
286 int main(int argc, char *argv[])
290 pthread_t threads[MAX_CORES];
292 /* TODO: currently shared memory */
293 for (int i = 0; i < sizeof(memory); i += 64)
297 cpu_mask = strtol(argv[1], NULL, 16);
299 for (int i = 0; i < MAX_CORES; i++) {
300 if (cpu_mask & (1 << i)) {
305 printf("CPU count:%d CPU mask:%#x\n", cpu_count, cpu_mask);
306 int s = pthread_barrier_init(&barrier, NULL, cpu_count);
308 error(1, s, "pthread_barrier_init");
310 for (intptr_t i = 0; i < MAX_CORES; i++) {
311 if (cpu_mask & (1 << i))
312 pthread_create(&threads[i], NULL, test_thread, (void *)i);
315 for (int i = 0; i < MAX_CORES; i++) {
316 if (cpu_mask & (1 << i)) {
317 pthread_join(threads[i], NULL);