aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2013-03-08 01:25:35 -0500
committerBjoern Brandenburg <bbb@mpi-sws.org>2013-03-12 09:47:01 -0400
commit072b486393ab702eea9ea0a7fef569dbf8be0a32 (patch)
treedda3128725f9324908c4f3daae992208030faaff
parent7051511d6b059097f2eb5f650294e0ead7d8f488 (diff)
uncachedev: uncache test tool.
This adds the uncache test tool (bin/uncache.c). The tool can be used to test Litmus's char device driver for allocating uncacheable CPU memory. The tool runs various checks and gathers basic cache vs. main memory statistics. In the future, uncache could be extended to quantify the benefits of the L1, L2, and L3 caches, instead of treating them as a black box. Note: Uncache works best when compiled with '-O2'. While '-O2' has not been added to the Makefile, other code was updated (code in tests/ and rtspin), to compile with -O2. DEPENDS UPON LITMUS-RT PATCH 888d097deb6d1fdc0c89a4f9667fd81cf416cfc7.
-rw-r--r--Makefile5
-rw-r--r--bin/rtspin.c6
-rw-r--r--bin/uncache.c381
-rw-r--r--tests/fdso.c4
-rw-r--r--tests/locks.c8
-rw-r--r--tests/pcp.c8
6 files changed, 398 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index 8195752..5432edd 100644
--- a/Makefile
+++ b/Makefile
@@ -71,7 +71,7 @@ AR := ${CROSS_COMPILE}${AR}
71 71
72all = lib ${rt-apps} 72all = lib ${rt-apps}
73rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \ 73rt-apps = cycles base_task rt_launch rtspin release_ts measure_syscall \
74 base_mt_task runtests 74 base_mt_task uncache runtests
75 75
76.PHONY: all lib clean dump-config TAGS tags cscope help 76.PHONY: all lib clean dump-config TAGS tags cscope help
77 77
@@ -215,6 +215,9 @@ obj-rt_launch = rt_launch.o common.o
215obj-rtspin = rtspin.o common.o 215obj-rtspin = rtspin.o common.o
216lib-rtspin = -lrt 216lib-rtspin = -lrt
217 217
218obj-uncache = uncache.o
219lib-uncache = -lrt
220
218obj-release_ts = release_ts.o 221obj-release_ts = release_ts.o
219 222
220obj-measure_syscall = null_call.o 223obj-measure_syscall = null_call.o
diff --git a/bin/rtspin.c b/bin/rtspin.c
index 5054d0b..657a94c 100644
--- a/bin/rtspin.c
+++ b/bin/rtspin.c
@@ -80,7 +80,7 @@ static void get_exec_times(const char *file, const int column,
80 80
81 for (cur_col = 1; cur_col < column; ++cur_col) { 81 for (cur_col = 1; cur_col < column; ++cur_col) {
82 /* discard input until we get to the column we want */ 82 /* discard input until we get to the column we want */
83 fscanf(fstream, "%*s,"); 83 int unused __attribute__ ((unused)) = fscanf(fstream, "%*s,");
84 } 84 }
85 85
86 /* get the desired exec. time */ 86 /* get the desired exec. time */
@@ -200,11 +200,11 @@ int main(int argc, char** argv)
200 int column = 1; 200 int column = 1;
201 const char *file = NULL; 201 const char *file = NULL;
202 int want_enforcement = 0; 202 int want_enforcement = 0;
203 double duration = 0, start; 203 double duration = 0, start = 0;
204 double *exec_times = NULL; 204 double *exec_times = NULL;
205 double scale = 1.0; 205 double scale = 1.0;
206 task_class_t class = RT_CLASS_HARD; 206 task_class_t class = RT_CLASS_HARD;
207 int cur_job, num_jobs; 207 int cur_job = 0, num_jobs = 0;
208 208
209 /* locking */ 209 /* locking */
210 int lock_od = -1; 210 int lock_od = -1;
diff --git a/bin/uncache.c b/bin/uncache.c
new file mode 100644
index 0000000..b6f6913
--- /dev/null
+++ b/bin/uncache.c
@@ -0,0 +1,381 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <time.h>
5#include <sched.h>
6#include <assert.h>
7#include <string.h>
8#include <stdint.h>
9#include <sys/fcntl.h>
10#include <sys/mman.h>
11
12/* Test tool for validating Litmus's uncache device. */
13/* Tool also capable basic cache vs. sysmem statistics. */
14/* Compile with '-O2' for significaintly greater margins */
15/* in performance between cache and sysmem: */
16/* (Intel Xeon X5650) */
17/* -g -> uncache is 30x slower */
18/* -O2 -> uncache is >100x slower */
19
20int PAGE_SIZE;
21#define NR_PAGES 16
22
23#define UNCACHE_DEV "/dev/litmus/uncache"
24
25/* volatile forces a read from memory (or cache) on every reference. Note
26 that volatile does not keep data out of the cache! */
27typedef volatile char* pbuf_t;
28
29/* hit the first byte in each page.
30 addr must be page aligned. */
31inline int linear_write(pbuf_t addr, int size, char val)
32{
33 pbuf_t end = addr + size;
34 pbuf_t step;
35 int nr_pages = (unsigned long)(end - addr)/PAGE_SIZE;
36 int times = nr_pages * PAGE_SIZE;
37 int i;
38
39 for (i = 0; i < times; ++i)
40 for(step = addr; step < end; step += PAGE_SIZE)
41 *step = val;
42 return 0;
43}
44inline int linear_read(pbuf_t addr, int size, char val)
45{
46 pbuf_t end = addr + size;
47 pbuf_t step;
48 int nr_pages = (unsigned long)(end - addr)/PAGE_SIZE;
49 int times = nr_pages * PAGE_SIZE;
50 int i;
51
52 for (i = 0; i < times; ++i)
53 for(step = addr; step < end; step += PAGE_SIZE) {
54 if (*step != val)
55 return -1;
56 }
57 return 0;
58}
59
60/* write to *data nr times. */
61inline int hammer_write(pbuf_t data, char val, int nr)
62{
63 int i;
64 for (i = 0; i < nr; ++i)
65 *data = val;
66 return 0;
67}
68
69/* read from *data nr times. */
70inline int hammer_read(pbuf_t data, char val, int nr)
71{
72 int i;
73 for (i = 0; i < nr; ++i) {
74 if (*data != val)
75 return -1;
76 }
77 return 0;
78}
79
80inline int test(pbuf_t data, int size, int trials)
81{
82 int HAMMER_TIME = 10000; /* can't cache this! */
83 char VAL = 0x55;
84 int t;
85 for(t = 0; t < trials; ++t) {
86
87#if 0
88 if (linear_write(data, size, VAL) != 0) {
89 printf("failed linear_write()\n");
90 return -1;
91 }
92 if (linear_read(data, size, VAL) != 0) {
93 printf("failed linear_read()\n");
94 return -1;
95 }
96#endif
97
98 /* hammer at the first byte in the array */
99 if (hammer_write(data, VAL, HAMMER_TIME) != 0) {
100 printf("failed hammer_write()\n");
101 return -1;
102 }
103 if (hammer_read(data, VAL, HAMMER_TIME) != 0) {
104 printf("failed hammer_read()\n");
105 return -1;
106 }
107 }
108 return 0;
109}
110
111inline void timespec_normalize(struct timespec* ts, time_t sec, int64_t nsec)
112{
113 while(nsec > 1000000000LL) {
114 asm("" : "+rm"(nsec));
115 nsec -= 1000000000LL;
116 ++sec;
117 }
118 while(nsec < 0) {
119 asm("" : "+rm"(nsec));
120 nsec += 1000000000LL;
121 --sec;
122 }
123
124 ts->tv_sec = sec;
125 ts->tv_nsec = nsec;
126}
127
128inline struct timespec timespec_sub(struct timespec lhs, struct timespec rhs)
129{
130 struct timespec delta;
131 timespec_normalize(&delta, lhs.tv_sec - rhs.tv_sec, lhs.tv_nsec - rhs.tv_nsec);
132 return delta;
133}
134
135inline struct timespec timespec_add(struct timespec lhs, struct timespec rhs)
136{
137 struct timespec delta;
138 timespec_normalize(&delta, lhs.tv_sec + rhs.tv_sec, lhs.tv_nsec + rhs.tv_nsec);
139 return delta;
140}
141
142inline int64_t timespec_to_us(struct timespec ts)
143{
144 int64_t t;
145 t = ts.tv_sec * 1000000LL;
146 t += ts.tv_nsec / 1000LL;
147 return t;
148}
149
150/* hammers away at the first byte in each mmaped page and
151 times how long it took. */
152int do_data(int do_uncache, int64_t* time)
153{
154 int size;
155 int prot = PROT_READ | PROT_WRITE;
156 int flags = MAP_PRIVATE;
157
158 pbuf_t data;
159
160 struct sched_param fifo_params;
161
162 struct timespec start, end;
163 int64_t elapsed;
164 int trials = 1000;
165
166 printf("Running data access test.\n");
167
168 mlockall(MCL_CURRENT | MCL_FUTURE);
169
170 memset(&fifo_params, 0, sizeof(fifo_params));
171 fifo_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
172
173 size = PAGE_SIZE*NR_PAGES;
174
175 printf("Allocating %d %s pages.\n", NR_PAGES, (do_uncache) ?
176 "uncacheable" : "cacheable");
177 if (do_uncache) {
178 int fd = open(UNCACHE_DEV, O_RDWR);
179 data = mmap(NULL, size, prot, flags, fd, 0);
180 close(fd);
181 }
182 else {
183 /* Accessed data will probably fit in L1, so this will go VERY fast.
184 Code should also have little-to-no pipeline stalls. */
185 flags |= MAP_ANONYMOUS;
186 data = mmap(NULL, size, prot, flags, -1, 0);
187 }
188 if (data == MAP_FAILED) {
189 printf("Failed to alloc data! "
190 "Are you running Litmus? "
191 "Is Litmus broken?\n");
192 return -1;
193 }
194 else {
195 printf("Data allocated at %p.\n", data);
196 }
197
198 printf("Beginning tests...\n");
199 if (sched_setscheduler(getpid(), SCHED_FIFO, &fifo_params)) {
200 printf("(Could not become SCHED_FIFO task.) Are you running as root?\n");
201 }
202
203 /* observations suggest that no warmup phase is needed. */
204 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
205 if (test(data, size, trials) != 0) {
206 printf("Test failed!\n");
207 munmap((char*)data, size);
208 return -1;
209 }
210 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
211 elapsed = timespec_to_us(timespec_sub(end, start));
212 printf("%s Time: %ldus\n", (do_uncache) ?
213 "Uncache" : "Cache", elapsed);
214
215 munmap((char*)data, size);
216
217 if(time)
218 *time = elapsed;
219
220 return 0;
221}
222
223/* compares runtime of cached vs. uncached */
224int do_data_compare()
225{
226 const double thresh = 1.3;
227 int ret = 0;
228 double ratio;
229 int64_t cache_time = 0, uncache_time = 0;
230
231 printf("Timing cached pages...\n");
232 ret = do_data(0, &cache_time);
233 if (ret != 0)
234 goto out;
235
236 printf("Timing uncached pages...\n");
237 ret = do_data(1, &uncache_time);
238 if (ret != 0)
239 goto out;
240
241 ratio = (double)uncache_time/(double)cache_time;
242 printf("Uncached/Cached Ratio: %f\n", ratio);
243
244 if (ratio < thresh) {
245 printf("Ratio is unexpectedly small (< %f)! "
246 " Uncache broken? Are you on kvm?\n", thresh);
247 ret = -1;
248 }
249
250out:
251 return ret;
252}
253
254/* tries to max out uncache allocations.
255 under normal conditions (non-mlock),
256 pages should spill into swap. uncache
257 pages are not locked in memory. */
258int do_max_alloc(void)
259{
260 int fd;
261 int good = 1;
262 int count = 0;
263 uint64_t mmap_size = PAGE_SIZE; /* start at one page per mmap */
264
265 /* half of default limit on ubuntu. (see /proc/sys/vm/max_map_count) */
266 int max_mmaps = 32765;
267 volatile char** maps = calloc(max_mmaps, sizeof(pbuf_t));
268
269 if (!maps) {
270 printf("failed to alloc pointers for pages\n");
271 return -1;
272 }
273
274 printf("Testing max amount of uncache data. System may get wonkie (OOM Killer)!\n");
275
276 fd = open(UNCACHE_DEV, O_RDWR);
277 do {
278 int i;
279 int nr_pages = mmap_size/PAGE_SIZE;
280 printf("Testing mmaps of %d pages.\n", nr_pages);
281
282 count = 0;
283 for (i = 0; (i < max_mmaps) && good; ++i) {
284 pbuf_t data = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_POPULATE, fd, 0);
285
286 if (data != MAP_FAILED) {
287 maps[i] = data;
288 ++count;
289 }
290 else {
291 perror(NULL);
292 good = 0;
293 }
294 }
295 for (i = 0; i < count; ++i) {
296 if (maps[i])
297 munmap((char*)(maps[i]), mmap_size);
298 }
299 memset(maps, 0, sizeof(maps[0])*max_mmaps);
300
301 mmap_size *= 2; /* let's do it again with bigger allocations */
302 }while(good);
303
304 free(maps);
305 close(fd);
306
307 printf("Maxed out allocs with %d mmaps of %lu pages in size.\n",
308 count, mmap_size/PAGE_SIZE);
309
310 return 0;
311}
312
313typedef enum
314{
315 UNCACHE,
316 CACHE,
317 COMPARE,
318 MAX_ALLOC
319} test_t;
320
321#define OPTSTR "ucxa"
322int main(int argc, char** argv)
323{
324 int ret;
325 test_t test = UNCACHE;
326 int opt;
327 PAGE_SIZE = sysconf(_SC_PAGE_SIZE);
328
329 while((opt = getopt(argc, argv, OPTSTR)) != -1) {
330 switch(opt) {
331 case 'c':
332 test = CACHE;
333 break;
334 case 'u':
335 test = UNCACHE;
336 break;
337 case 'x':
338 test = COMPARE;
339 break;
340 case 'a':
341 test = MAX_ALLOC;
342 break;
343 case ':':
344 printf("missing option\n");
345 exit(-1);
346 case '?':
347 default:
348 printf("bad argument\n");
349 exit(-1);
350 }
351 }
352
353
354 printf("Page Size: %d\n", PAGE_SIZE);
355
356 switch(test)
357 {
358 case CACHE:
359 ret = do_data(0, NULL);
360 break;
361 case UNCACHE:
362 ret = do_data(1, NULL);
363 break;
364 case COMPARE:
365 ret = do_data_compare();
366 break;
367 case MAX_ALLOC:
368 ret = do_max_alloc();
369 break;
370 default:
371 printf("invalid test\n");
372 ret = -1;
373 break;
374 }
375
376 if (ret != 0) {
377 printf("Test failed.\n");
378 }
379
380 return ret;
381}
diff --git a/tests/fdso.c b/tests/fdso.c
index 8a2a0d0..fda343f 100644
--- a/tests/fdso.c
+++ b/tests/fdso.c
@@ -16,7 +16,7 @@ TESTCASE(fmlp_not_active, C_EDF | PFAIR | LINUX,
16{ 16{
17 int fd; 17 int fd;
18 18
19 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) ); 19 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
20 20
21 ASSERT(fd != -1); 21 ASSERT(fd != -1);
22 22
@@ -57,7 +57,7 @@ TESTCASE(not_inherit_od, GSN_EDF | PSN_EDF,
57{ 57{
58 int fd, od, pid, status; 58 int fd, od, pid, status;
59 59
60 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) ); 60 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
61 61
62 SYSCALL( od = open_fmlp_sem(fd, 0) ); 62 SYSCALL( od = open_fmlp_sem(fd, 0) );
63 63
diff --git a/tests/locks.c b/tests/locks.c
index d7ebfe2..9a928b3 100644
--- a/tests/locks.c
+++ b/tests/locks.c
@@ -11,7 +11,7 @@ TESTCASE(not_lock_fmlp_be, GSN_EDF | PSN_EDF | P_FP,
11{ 11{
12 int fd, od; 12 int fd, od;
13 13
14 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) ); 14 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
15 15
16 SYSCALL( od = open_fmlp_sem(fd, 0) ); 16 SYSCALL( od = open_fmlp_sem(fd, 0) );
17 17
@@ -34,7 +34,7 @@ TESTCASE(not_lock_srp_be, PSN_EDF | P_FP,
34{ 34{
35 int fd, od; 35 int fd, od;
36 36
37 SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT) ); 37 SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
38 38
39 /* BE tasks may not open SRP semaphores */ 39 /* BE tasks may not open SRP semaphores */
40 40
@@ -51,7 +51,7 @@ TESTCASE(lock_srp, PSN_EDF | P_FP,
51{ 51{
52 int fd, od; 52 int fd, od;
53 53
54 SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT) ); 54 SYSCALL( fd = open(".srp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
55 55
56 SYSCALL( sporadic_partitioned(10, 100, 0) ); 56 SYSCALL( sporadic_partitioned(10, 100, 0) );
57 SYSCALL( task_mode(LITMUS_RT_TASK) ); 57 SYSCALL( task_mode(LITMUS_RT_TASK) );
@@ -83,7 +83,7 @@ TESTCASE(lock_fmlp, PSN_EDF | GSN_EDF | P_FP,
83{ 83{
84 int fd, od; 84 int fd, od;
85 85
86 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT) ); 86 SYSCALL( fd = open(".fmlp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
87 87
88 SYSCALL( sporadic_partitioned(10, 100, 0) ); 88 SYSCALL( sporadic_partitioned(10, 100, 0) );
89 SYSCALL( task_mode(LITMUS_RT_TASK) ); 89 SYSCALL( task_mode(LITMUS_RT_TASK) );
diff --git a/tests/pcp.c b/tests/pcp.c
index 52ee959..ff4259c 100644
--- a/tests/pcp.c
+++ b/tests/pcp.c
@@ -13,7 +13,7 @@ TESTCASE(lock_pcp, P_FP,
13{ 13{
14 int fd, od, cpu = 0; 14 int fd, od, cpu = 0;
15 15
16 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) ); 16 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
17 17
18 SYSCALL( sporadic_partitioned(10, 100, cpu) ); 18 SYSCALL( sporadic_partitioned(10, 100, cpu) );
19 SYSCALL( task_mode(LITMUS_RT_TASK) ); 19 SYSCALL( task_mode(LITMUS_RT_TASK) );
@@ -250,7 +250,7 @@ TESTCASE(lock_dpcp, P_FP,
250{ 250{
251 int fd, od, cpu = 1; 251 int fd, od, cpu = 1;
252 252
253 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) ); 253 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
254 254
255 SYSCALL( sporadic_partitioned(10, 100, 0) ); 255 SYSCALL( sporadic_partitioned(10, 100, 0) );
256 SYSCALL( task_mode(LITMUS_RT_TASK) ); 256 SYSCALL( task_mode(LITMUS_RT_TASK) );
@@ -281,7 +281,7 @@ TESTCASE(not_lock_pcp_be, P_FP,
281{ 281{
282 int fd, od; 282 int fd, od;
283 283
284 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) ); 284 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
285 285
286 /* BE tasks are not even allowed to open a PCP semaphore */ 286 /* BE tasks are not even allowed to open a PCP semaphore */
287 SYSCALL_FAILS(EPERM, od = open_pcp_sem(fd, 0, 1) ); 287 SYSCALL_FAILS(EPERM, od = open_pcp_sem(fd, 0, 1) );
@@ -303,7 +303,7 @@ TESTCASE(lock_mpcp, P_FP,
303{ 303{
304 int fd, od; 304 int fd, od;
305 305
306 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT) ); 306 SYSCALL( fd = open(".pcp_locks", O_RDONLY | O_CREAT, S_IRUSR) );
307 307
308 SYSCALL( sporadic_partitioned(10, 100, 0) ); 308 SYSCALL( sporadic_partitioned(10, 100, 0) );
309 SYSCALL( task_mode(LITMUS_RT_TASK) ); 309 SYSCALL( task_mode(LITMUS_RT_TASK) );