Merge branch 'i2c/for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[sfrench/cifs-2.6.git] / tools / testing / selftests / powerpc / mm / tlbie_test.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4  * Copyright 2019, Nick Piggin, Gautham R. Shenoy, Aneesh Kumar K.V, IBM Corp.
5  */
6
7 /*
8  *
9  * Test tlbie/mtpidr race. We have 4 threads doing flush/load/compare/store
10  * sequence in a loop. The same threads also rung a context switch task
11  * that does sched_yield() in loop.
12  *
13  * The snapshot thread mark the mmap area PROT_READ in between, make a copy
14  * and copy it back to the original area. This helps us to detect if any
15  * store continued to happen after we marked the memory PROT_READ.
16  */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <sys/mman.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <sys/ipc.h>
24 #include <sys/shm.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <linux/futex.h>
28 #include <unistd.h>
29 #include <asm/unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <sched.h>
34 #include <time.h>
35 #include <stdarg.h>
36 #include <sched.h>
37 #include <pthread.h>
38 #include <signal.h>
39 #include <sys/prctl.h>
40
41 static inline void dcbf(volatile unsigned int *addr)
42 {
43         __asm__ __volatile__ ("dcbf %y0; sync" : : "Z"(*(unsigned char *)addr) : "memory");
44 }
45
46 static void err_msg(char *msg)
47 {
48
49         time_t now;
50         time(&now);
51         printf("=================================\n");
52         printf("    Error: %s\n", msg);
53         printf("    %s", ctime(&now));
54         printf("=================================\n");
55         exit(1);
56 }
57
58 static char *map1;
59 static char *map2;
60 static pid_t rim_process_pid;
61
62 /*
63  * A "rim-sequence" is defined to be the sequence of the following
64  * operations performed on a memory word:
65  *      1) FLUSH the contents of that word.
66  *      2) LOAD the contents of that word.
67  *      3) COMPARE the contents of that word with the content that was
68  *                 previously stored at that word
69  *      4) STORE new content into that word.
70  *
71  * The threads in this test that perform the rim-sequence are termed
72  * as rim_threads.
73  */
74
75 /*
76  * A "corruption" is defined to be the failed COMPARE operation in a
77  * rim-sequence.
78  *
79  * A rim_thread that detects a corruption informs about it to all the
80  * other rim_threads, and the mem_snapshot thread.
81  */
82 static volatile unsigned int corruption_found;
83
84 /*
85  * This defines the maximum number of rim_threads in this test.
86  *
87  * The THREAD_ID_BITS denote the number of bits required
88  * to represent the thread_ids [0..MAX_THREADS - 1].
89  * We are being a bit paranoid here and set it to 8 bits,
90  * though 6 bits suffice.
91  *
92  */
93 #define MAX_THREADS             64
94 #define THREAD_ID_BITS          8
95 #define THREAD_ID_MASK          ((1 << THREAD_ID_BITS) - 1)
96 static unsigned int rim_thread_ids[MAX_THREADS];
97 static pthread_t rim_threads[MAX_THREADS];
98
99
100 /*
101  * Each rim_thread works on an exclusive "chunk" of size
102  * RIM_CHUNK_SIZE.
103  *
104  * The ith rim_thread works on the ith chunk.
105  *
106  * The ith chunk begins at
107  * map1 + (i * RIM_CHUNK_SIZE)
108  */
109 #define RIM_CHUNK_SIZE          1024
110 #define BITS_PER_BYTE           8
111 #define WORD_SIZE               (sizeof(unsigned int))
112 #define WORD_BITS               (WORD_SIZE * BITS_PER_BYTE)
113 #define WORDS_PER_CHUNK         (RIM_CHUNK_SIZE/WORD_SIZE)
114
115 static inline char *compute_chunk_start_addr(unsigned int thread_id)
116 {
117         char *chunk_start;
118
119         chunk_start = (char *)((unsigned long)map1 +
120                                (thread_id * RIM_CHUNK_SIZE));
121
122         return chunk_start;
123 }
124
125 /*
126  * The "word-offset" of a word-aligned address inside a chunk, is
127  * defined to be the number of words that precede the address in that
128  * chunk.
129  *
130  * WORD_OFFSET_BITS denote the number of bits required to represent
131  * the word-offsets of all the word-aligned addresses of a chunk.
132  */
133 #define WORD_OFFSET_BITS        (__builtin_ctz(WORDS_PER_CHUNK))
134 #define WORD_OFFSET_MASK        ((1 << WORD_OFFSET_BITS) - 1)
135
136 static inline unsigned int compute_word_offset(char *start, unsigned int *addr)
137 {
138         unsigned int delta_bytes, ret;
139         delta_bytes = (unsigned long)addr - (unsigned long)start;
140
141         ret = delta_bytes/WORD_SIZE;
142
143         return ret;
144 }
145
146 /*
147  * A "sweep" is defined to be the sequential execution of the
148  * rim-sequence by a rim_thread on its chunk one word at a time,
149  * starting from the first word of its chunk and ending with the last
150  * word of its chunk.
151  *
152  * Each sweep of a rim_thread is uniquely identified by a sweep_id.
153  * SWEEP_ID_BITS denote the number of bits required to represent
154  * the sweep_ids of rim_threads.
155  *
156  * As to why SWEEP_ID_BITS are computed as a function of THREAD_ID_BITS,
157  * WORD_OFFSET_BITS, and WORD_BITS, see the "store-pattern" below.
158  */
159 #define SWEEP_ID_BITS           (WORD_BITS - (THREAD_ID_BITS + WORD_OFFSET_BITS))
160 #define SWEEP_ID_MASK           ((1 << SWEEP_ID_BITS) - 1)
161
162 /*
163  * A "store-pattern" is the word-pattern that is stored into a word
164  * location in the 4)STORE step of the rim-sequence.
165  *
166  * In the store-pattern, we shall encode:
167  *
168  *      - The thread-id of the rim_thread performing the store
169  *        (The most significant THREAD_ID_BITS)
170  *
171  *      - The word-offset of the address into which the store is being
172  *        performed (The next WORD_OFFSET_BITS)
173  *
174  *      - The sweep_id of the current sweep in which the store is
175  *        being performed. (The lower SWEEP_ID_BITS)
176  *
177  * Store Pattern: 32 bits
178  * |------------------|--------------------|---------------------------------|
179  * |    Thread id     |  Word offset       |         sweep_id                |
180  * |------------------|--------------------|---------------------------------|
181  *    THREAD_ID_BITS     WORD_OFFSET_BITS          SWEEP_ID_BITS
182  *
183  * In the store pattern, the (Thread-id + Word-offset) uniquely identify the
184  * address to which the store is being performed i.e,
185  *    address == map1 +
186  *              (Thread-id * RIM_CHUNK_SIZE) + (Word-offset * WORD_SIZE)
187  *
188  * And the sweep_id in the store pattern identifies the time when the
189  * store was performed by the rim_thread.
190  *
191  * We shall use this property in the 3)COMPARE step of the
192  * rim-sequence.
193  */
194 #define SWEEP_ID_SHIFT  0
195 #define WORD_OFFSET_SHIFT       (SWEEP_ID_BITS)
196 #define THREAD_ID_SHIFT         (WORD_OFFSET_BITS + SWEEP_ID_BITS)
197
198 /*
199  * Compute the store pattern for a given thread with id @tid, at
200  * location @addr in the sweep identified by @sweep_id
201  */
202 static inline unsigned int compute_store_pattern(unsigned int tid,
203                                                  unsigned int *addr,
204                                                  unsigned int sweep_id)
205 {
206         unsigned int ret = 0;
207         char *start = compute_chunk_start_addr(tid);
208         unsigned int word_offset = compute_word_offset(start, addr);
209
210         ret += (tid & THREAD_ID_MASK) << THREAD_ID_SHIFT;
211         ret += (word_offset & WORD_OFFSET_MASK) << WORD_OFFSET_SHIFT;
212         ret += (sweep_id & SWEEP_ID_MASK) << SWEEP_ID_SHIFT;
213         return ret;
214 }
215
216 /* Extract the thread-id from the given store-pattern */
217 static inline unsigned int extract_tid(unsigned int pattern)
218 {
219         unsigned int ret;
220
221         ret = (pattern >> THREAD_ID_SHIFT) & THREAD_ID_MASK;
222         return ret;
223 }
224
225 /* Extract the word-offset from the given store-pattern */
226 static inline unsigned int extract_word_offset(unsigned int pattern)
227 {
228         unsigned int ret;
229
230         ret = (pattern >> WORD_OFFSET_SHIFT) & WORD_OFFSET_MASK;
231
232         return ret;
233 }
234
235 /* Extract the sweep-id from the given store-pattern */
236 static inline unsigned int extract_sweep_id(unsigned int pattern)
237
238 {
239         unsigned int ret;
240
241         ret = (pattern >> SWEEP_ID_SHIFT) & SWEEP_ID_MASK;
242
243         return ret;
244 }
245
246 /************************************************************
247  *                                                          *
248  *          Logging the output of the verification          *
249  *                                                          *
250  ************************************************************/
251 #define LOGDIR_NAME_SIZE 100
252 static char logdir[LOGDIR_NAME_SIZE];
253
254 static FILE *fp[MAX_THREADS];
255 static const char logfilename[] ="Thread-%02d-Chunk";
256
257 static inline void start_verification_log(unsigned int tid,
258                                           unsigned int *addr,
259                                           unsigned int cur_sweep_id,
260                                           unsigned int prev_sweep_id)
261 {
262         FILE *f;
263         char logfile[30];
264         char path[LOGDIR_NAME_SIZE + 30];
265         char separator[2] = "/";
266         char *chunk_start = compute_chunk_start_addr(tid);
267         unsigned int size = RIM_CHUNK_SIZE;
268
269         sprintf(logfile, logfilename, tid);
270         strcpy(path, logdir);
271         strcat(path, separator);
272         strcat(path, logfile);
273         f = fopen(path, "w");
274
275         if (!f) {
276                 err_msg("Unable to create logfile\n");
277         }
278
279         fp[tid] = f;
280
281         fprintf(f, "----------------------------------------------------------\n");
282         fprintf(f, "PID                = %d\n", rim_process_pid);
283         fprintf(f, "Thread id          = %02d\n", tid);
284         fprintf(f, "Chunk Start Addr   = 0x%016lx\n", (unsigned long)chunk_start);
285         fprintf(f, "Chunk Size         = %d\n", size);
286         fprintf(f, "Next Store Addr    = 0x%016lx\n", (unsigned long)addr);
287         fprintf(f, "Current sweep-id   = 0x%08x\n", cur_sweep_id);
288         fprintf(f, "Previous sweep-id  = 0x%08x\n", prev_sweep_id);
289         fprintf(f, "----------------------------------------------------------\n");
290 }
291
292 static inline void log_anamoly(unsigned int tid, unsigned int *addr,
293                                unsigned int expected, unsigned int observed)
294 {
295         FILE *f = fp[tid];
296
297         fprintf(f, "Thread %02d: Addr 0x%lx: Expected 0x%x, Observed 0x%x\n",
298                 tid, (unsigned long)addr, expected, observed);
299         fprintf(f, "Thread %02d: Expected Thread id   = %02d\n", tid, extract_tid(expected));
300         fprintf(f, "Thread %02d: Observed Thread id   = %02d\n", tid, extract_tid(observed));
301         fprintf(f, "Thread %02d: Expected Word offset = %03d\n", tid, extract_word_offset(expected));
302         fprintf(f, "Thread %02d: Observed Word offset = %03d\n", tid, extract_word_offset(observed));
303         fprintf(f, "Thread %02d: Expected sweep-id    = 0x%x\n", tid, extract_sweep_id(expected));
304         fprintf(f, "Thread %02d: Observed sweep-id    = 0x%x\n", tid, extract_sweep_id(observed));
305         fprintf(f, "----------------------------------------------------------\n");
306 }
307
308 static inline void end_verification_log(unsigned int tid, unsigned nr_anamolies)
309 {
310         FILE *f = fp[tid];
311         char logfile[30];
312         char path[LOGDIR_NAME_SIZE + 30];
313         char separator[] = "/";
314
315         fclose(f);
316
317         if (nr_anamolies == 0) {
318                 remove(path);
319                 return;
320         }
321
322         sprintf(logfile, logfilename, tid);
323         strcpy(path, logdir);
324         strcat(path, separator);
325         strcat(path, logfile);
326
327         printf("Thread %02d chunk has %d corrupted words. For details check %s\n",
328                 tid, nr_anamolies, path);
329 }
330
331 /*
332  * When a COMPARE step of a rim-sequence fails, the rim_thread informs
333  * everyone else via the shared_memory pointed to by
334  * corruption_found variable. On seeing this, every thread verifies the
335  * content of its chunk as follows.
336  *
337  * Suppose a thread identified with @tid was about to store (but not
338  * yet stored) to @next_store_addr in its current sweep identified
339  * @cur_sweep_id. Let @prev_sweep_id indicate the previous sweep_id.
340  *
341  * This implies that for all the addresses @addr < @next_store_addr,
342  * Thread @tid has already performed a store as part of its current
343  * sweep. Hence we expect the content of such @addr to be:
344  *    |-------------------------------------------------|
345  *    | tid   | word_offset(addr) |    cur_sweep_id     |
346  *    |-------------------------------------------------|
347  *
348  * Since Thread @tid is yet to perform stores on address
349  * @next_store_addr and above, we expect the content of such an
350  * address @addr to be:
351  *    |-------------------------------------------------|
352  *    | tid   | word_offset(addr) |    prev_sweep_id    |
353  *    |-------------------------------------------------|
354  *
355  * The verifier function @verify_chunk does this verification and logs
356  * any anamolies that it finds.
357  */
358 static void verify_chunk(unsigned int tid, unsigned int *next_store_addr,
359                   unsigned int cur_sweep_id,
360                   unsigned int prev_sweep_id)
361 {
362         unsigned int *iter_ptr;
363         unsigned int size = RIM_CHUNK_SIZE;
364         unsigned int expected;
365         unsigned int observed;
366         char *chunk_start = compute_chunk_start_addr(tid);
367
368         int nr_anamolies = 0;
369
370         start_verification_log(tid, next_store_addr,
371                                cur_sweep_id, prev_sweep_id);
372
373         for (iter_ptr = (unsigned int *)chunk_start;
374              (unsigned long)iter_ptr < (unsigned long)chunk_start + size;
375              iter_ptr++) {
376                 unsigned int expected_sweep_id;
377
378                 if (iter_ptr < next_store_addr) {
379                         expected_sweep_id = cur_sweep_id;
380                 } else {
381                         expected_sweep_id = prev_sweep_id;
382                 }
383
384                 expected = compute_store_pattern(tid, iter_ptr, expected_sweep_id);
385
386                 dcbf((volatile unsigned int*)iter_ptr); //Flush before reading
387                 observed = *iter_ptr;
388
389                 if (observed != expected) {
390                         nr_anamolies++;
391                         log_anamoly(tid, iter_ptr, expected, observed);
392                 }
393         }
394
395         end_verification_log(tid, nr_anamolies);
396 }
397
398 static void set_pthread_cpu(pthread_t th, int cpu)
399 {
400         cpu_set_t run_cpu_mask;
401         struct sched_param param;
402
403         CPU_ZERO(&run_cpu_mask);
404         CPU_SET(cpu, &run_cpu_mask);
405         pthread_setaffinity_np(th, sizeof(cpu_set_t), &run_cpu_mask);
406
407         param.sched_priority = 1;
408         if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
409                 /* haven't reproduced with this setting, it kills random preemption which may be a factor */
410                 fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
411         }
412 }
413
414 static void set_mycpu(int cpu)
415 {
416         cpu_set_t run_cpu_mask;
417         struct sched_param param;
418
419         CPU_ZERO(&run_cpu_mask);
420         CPU_SET(cpu, &run_cpu_mask);
421         sched_setaffinity(0, sizeof(cpu_set_t), &run_cpu_mask);
422
423         param.sched_priority = 1;
424         if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
425                 fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
426         }
427 }
428
429 static volatile int segv_wait;
430
431 static void segv_handler(int signo, siginfo_t *info, void *extra)
432 {
433         while (segv_wait) {
434                 sched_yield();
435         }
436
437 }
438
439 static void set_segv_handler(void)
440 {
441         struct sigaction sa;
442
443         sa.sa_flags = SA_SIGINFO;
444         sa.sa_sigaction = segv_handler;
445
446         if (sigaction(SIGSEGV, &sa, NULL) == -1) {
447                 perror("sigaction");
448                 exit(EXIT_FAILURE);
449         }
450 }
451
452 int timeout = 0;
453 /*
454  * This function is executed by every rim_thread.
455  *
456  * This function performs sweeps over the exclusive chunks of the
457  * rim_threads executing the rim-sequence one word at a time.
458  */
459 static void *rim_fn(void *arg)
460 {
461         unsigned int tid = *((unsigned int *)arg);
462
463         int size = RIM_CHUNK_SIZE;
464         char *chunk_start = compute_chunk_start_addr(tid);
465
466         unsigned int prev_sweep_id;
467         unsigned int cur_sweep_id = 0;
468
469         /* word access */
470         unsigned int pattern = cur_sweep_id;
471         unsigned int *pattern_ptr = &pattern;
472         unsigned int *w_ptr, read_data;
473
474         set_segv_handler();
475
476         /*
477          * Let us initialize the chunk:
478          *
479          * Each word-aligned address addr in the chunk,
480          * is initialized to :
481          *    |-------------------------------------------------|
482          *    | tid   | word_offset(addr) |         0           |
483          *    |-------------------------------------------------|
484          */
485         for (w_ptr = (unsigned int *)chunk_start;
486              (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
487              w_ptr++) {
488
489                 *pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
490                 *w_ptr = *pattern_ptr;
491         }
492
493         while (!corruption_found && !timeout) {
494                 prev_sweep_id = cur_sweep_id;
495                 cur_sweep_id = cur_sweep_id + 1;
496
497                 for (w_ptr = (unsigned int *)chunk_start;
498                      (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
499                      w_ptr++)  {
500                         unsigned int old_pattern;
501
502                         /*
503                          * Compute the pattern that we would have
504                          * stored at this location in the previous
505                          * sweep.
506                          */
507                         old_pattern = compute_store_pattern(tid, w_ptr, prev_sweep_id);
508
509                         /*
510                          * FLUSH:Ensure that we flush the contents of
511                          *       the cache before loading
512                          */
513                         dcbf((volatile unsigned int*)w_ptr); //Flush
514
515                         /* LOAD: Read the value */
516                         read_data = *w_ptr; //Load
517
518                         /*
519                          * COMPARE: Is it the same as what we had stored
520                          *          in the previous sweep ? It better be!
521                          */
522                         if (read_data != old_pattern) {
523                                 /* No it isn't! Tell everyone */
524                                 corruption_found = 1;
525                         }
526
527                         /*
528                          * Before performing a store, let us check if
529                          * any rim_thread has found a corruption.
530                          */
531                         if (corruption_found || timeout) {
532                                 /*
533                                  * Yes. Someone (including us!) has found
534                                  * a corruption :(
535                                  *
536                                  * Let us verify that our chunk is
537                                  * correct.
538                                  */
539                                 /* But first, let us allow the dust to settle down! */
540                                 verify_chunk(tid, w_ptr, cur_sweep_id, prev_sweep_id);
541
542                                 return 0;
543                         }
544
545                         /*
546                          * Compute the new pattern that we are going
547                          * to write to this location
548                          */
549                         *pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
550
551                         /*
552                          * STORE: Now let us write this pattern into
553                          *        the location
554                          */
555                         *w_ptr = *pattern_ptr;
556                 }
557         }
558
559         return NULL;
560 }
561
562
563 static unsigned long start_cpu = 0;
564 static unsigned long nrthreads = 4;
565
566 static pthread_t mem_snapshot_thread;
567
568 static void *mem_snapshot_fn(void *arg)
569 {
570         int page_size = getpagesize();
571         size_t size = page_size;
572         void *tmp = malloc(size);
573
574         while (!corruption_found && !timeout) {
575                 /* Stop memory migration once corruption is found */
576                 segv_wait = 1;
577
578                 mprotect(map1, size, PROT_READ);
579
580                 /*
581                  * Load from the working alias (map1). Loading from map2
582                  * also fails.
583                  */
584                 memcpy(tmp, map1, size);
585
586                 /*
587                  * Stores must go via map2 which has write permissions, but
588                  * the corrupted data tends to be seen in the snapshot buffer,
589                  * so corruption does not appear to be introduced at the
590                  * copy-back via map2 alias here.
591                  */
592                 memcpy(map2, tmp, size);
593                 /*
594                  * Before releasing other threads, must ensure the copy
595                  * back to
596                  */
597                 asm volatile("sync" ::: "memory");
598                 mprotect(map1, size, PROT_READ|PROT_WRITE);
599                 asm volatile("sync" ::: "memory");
600                 segv_wait = 0;
601
602                 usleep(1); /* This value makes a big difference */
603         }
604
605         return 0;
606 }
607
608 void alrm_sighandler(int sig)
609 {
610         timeout = 1;
611 }
612
613 int main(int argc, char *argv[])
614 {
615         int c;
616         int page_size = getpagesize();
617         time_t now;
618         int i, dir_error;
619         pthread_attr_t attr;
620         key_t shm_key = (key_t) getpid();
621         int shmid, run_time = 20 * 60;
622         struct sigaction sa_alrm;
623
624         snprintf(logdir, LOGDIR_NAME_SIZE,
625                  "/tmp/logdir-%u", (unsigned int)getpid());
626         while ((c = getopt(argc, argv, "r:hn:l:t:")) != -1) {
627                 switch(c) {
628                 case 'r':
629                         start_cpu = strtoul(optarg, NULL, 10);
630                         break;
631                 case 'h':
632                         printf("%s [-r <start_cpu>] [-n <nrthreads>] [-l <logdir>] [-t <timeout>]\n", argv[0]);
633                         exit(0);
634                         break;
635                 case 'n':
636                         nrthreads = strtoul(optarg, NULL, 10);
637                         break;
638                 case 'l':
639                         strncpy(logdir, optarg, LOGDIR_NAME_SIZE);
640                         break;
641                 case 't':
642                         run_time = strtoul(optarg, NULL, 10);
643                         break;
644                 default:
645                         printf("invalid option\n");
646                         exit(0);
647                         break;
648                 }
649         }
650
651         if (nrthreads > MAX_THREADS)
652                 nrthreads = MAX_THREADS;
653
654         shmid = shmget(shm_key, page_size, IPC_CREAT|0666);
655         if (shmid < 0) {
656                 err_msg("Failed shmget\n");
657         }
658
659         map1 = shmat(shmid, NULL, 0);
660         if (map1 == (void *) -1) {
661                 err_msg("Failed shmat");
662         }
663
664         map2 = shmat(shmid, NULL, 0);
665         if (map2 == (void *) -1) {
666                 err_msg("Failed shmat");
667         }
668
669         dir_error = mkdir(logdir, 0755);
670
671         if (dir_error) {
672                 err_msg("Failed mkdir");
673         }
674
675         printf("start_cpu list:%lu\n", start_cpu);
676         printf("number of worker threads:%lu + 1 snapshot thread\n", nrthreads);
677         printf("Allocated address:0x%016lx + secondary map:0x%016lx\n", (unsigned long)map1, (unsigned long)map2);
678         printf("logdir at : %s\n", logdir);
679         printf("Timeout: %d seconds\n", run_time);
680
681         time(&now);
682         printf("=================================\n");
683         printf("     Starting Test\n");
684         printf("     %s", ctime(&now));
685         printf("=================================\n");
686
687         for (i = 0; i < nrthreads; i++) {
688                 if (1 && !fork()) {
689                         prctl(PR_SET_PDEATHSIG, SIGKILL);
690                         set_mycpu(start_cpu + i);
691                         for (;;)
692                                 sched_yield();
693                         exit(0);
694                 }
695         }
696
697
698         sa_alrm.sa_handler = &alrm_sighandler;
699         sigemptyset(&sa_alrm.sa_mask);
700         sa_alrm.sa_flags = 0;
701
702         if (sigaction(SIGALRM, &sa_alrm, 0) == -1) {
703                 err_msg("Failed signal handler registration\n");
704         }
705
706         alarm(run_time);
707
708         pthread_attr_init(&attr);
709         for (i = 0; i < nrthreads; i++) {
710                 rim_thread_ids[i] = i;
711                 pthread_create(&rim_threads[i], &attr, rim_fn, &rim_thread_ids[i]);
712                 set_pthread_cpu(rim_threads[i], start_cpu + i);
713         }
714
715         pthread_create(&mem_snapshot_thread, &attr, mem_snapshot_fn, map1);
716         set_pthread_cpu(mem_snapshot_thread, start_cpu + i);
717
718
719         pthread_join(mem_snapshot_thread, NULL);
720         for (i = 0; i < nrthreads; i++) {
721                 pthread_join(rim_threads[i], NULL);
722         }
723
724         if (!timeout) {
725                 time(&now);
726                 printf("=================================\n");
727                 printf("      Data Corruption Detected\n");
728                 printf("      %s", ctime(&now));
729                 printf("      See logfiles in %s\n", logdir);
730                 printf("=================================\n");
731                 return 1;
732         }
733         return 0;
734 }