1 /* Run this as: for i in $(seq 0 5); do prem-test $i & done */
3 #define _GNU_SOURCE /* See feature_test_macros(7) */
18 #ifndef SYS_prem_memguard_check
19 #define SYS_prem_memguard_check 793
24 * 32-55 - Sum of memory events
25 * 61 - Memory budget overrun
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
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
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_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;
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 prem_memguard_check(unsigned long timeout, unsigned long memory_budget,
118 return syscall(SYS_prem_memguard_check, timeout, memory_budget, flags);
121 static void print_test_info(uint64_t timeout_us, uint64_t mem_budget, uint64_t flags,
122 int64_t retval, const char *code)
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' : '-'
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 ? '!' : ' ',
138 void wvtest_pass(bool cond, const char* file, int line, const char* str)
140 printf("! %s:%d %s %s\n", file, line, str, cond ? "ok" : "FAILURE");
143 #define WVPASS(cond) wvtest_pass(cond, __FILE__, __LINE__, #cond)
145 #define MGTEST(timeout_us, mem_budget, flags, code) \
148 retval = prem_memguard_check(timeout_us, mem_budget, flags); \
150 retval = prem_memguard_check(0, 0, 0); \
151 print_test_info(timeout_us, mem_budget, flags, retval, #code); \
155 void *test_thread(void *ptr)
158 int cpu = (intptr_t)ptr;
160 /* Ensure that memory phase starts and ends on the same CPU */
163 if (sched_setaffinity(0, sizeof(set), &set) < 0)
164 err(1, "sched_setaffinity");
166 pthread_barrier_wait(&barrier);
169 for (uint64_t flags = 0; flags < 4; flags++) {
170 compute_kernel(1); /* warm up */
172 ///////////////////////////////////////////////////////
173 r = MGTEST(5000, 10000, flags, compute_kernel(1000));
175 WVPASS(r.time > 900);
177 r = MGTEST(500, 10000, flags, compute_kernel(1000));
179 WVPASS(r.time > 900);
181 r = MGTEST(5000, 10000, flags, compute_kernel(2000));
183 WVPASS(r.time > 1900);
185 r = MGTEST(500, 10000, flags, compute_kernel(2000));
187 WVPASS(r.time > 1900);
189 ///////////////////////////////////////////////////////
190 r = MGTEST(100000, 500000, flags, read_memory(100000));
192 WVPASS(r.mem >= 100000);
194 r = MGTEST(100000, 50000, flags, read_memory(100000));
196 WVPASS(r.mem >= 100000);
198 r = MGTEST(100000, 500000, flags, read_memory_rnd(100000));
200 WVPASS(r.mem >= 100000);
202 r = MGTEST(100000, 50000, flags, read_memory_rnd(100000));
204 WVPASS(r.mem >= 100000);
206 r = MGTEST(100000, 500000, flags, write_memory(100000));
208 WVPASS(r.mem >= 100000);
210 r = MGTEST(100000, 50000, flags, write_memory(100000));
212 WVPASS(r.mem >= 100000);
215 ///////////////////////////////////////////////////////
216 r = MGTEST(100000, 5000000, flags, read_memory(1000000));
218 WVPASS(r.mem >= 900000);
220 r = MGTEST(100000, 500000, flags, read_memory(1000000));
222 WVPASS(r.mem >= 900000);
224 r = MGTEST(100000, 5000000, flags, read_memory_rnd(1000000));
226 WVPASS(r.mem >= 900000);
228 r = MGTEST(100000, 500000, flags, read_memory_rnd(1000000));
230 WVPASS(r.mem >= 900000);
232 r = MGTEST(100000, 5000000, flags, write_memory(1000000));
234 WVPASS(r.mem >= 900000);
236 r = MGTEST(100000, 500000, flags, write_memory(1000000));
238 WVPASS(r.mem >= 900000);
246 int main(int argc, char *argv[])
250 pthread_t threads[MAX_CORES];
252 /* TODO: currently shared memory */
253 for (int i = 0; i < sizeof(memory); i += 64)
257 cpu_mask = strtol(argv[1], NULL, 16);
259 for (int i = 0; i < MAX_CORES; i++) {
260 if (cpu_mask & (1 << i)) {
265 printf("CPU count:%d CPU mask:%#x\n", cpu_count, cpu_mask);
266 int s = pthread_barrier_init(&barrier, NULL, cpu_count);
268 error(1, s, "pthread_barrier_init");
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);
275 for (int i = 0; i < MAX_CORES; i++) {
276 if (cpu_mask & (1 << i)) {
277 pthread_join(threads[i], NULL);