root/tools/testing/selftests/powerpc/mm/tlbie_test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dcbf
  2. err_msg
  3. compute_chunk_start_addr
  4. compute_word_offset
  5. compute_store_pattern
  6. extract_tid
  7. extract_word_offset
  8. extract_sweep_id
  9. start_verification_log
  10. log_anamoly
  11. end_verification_log
  12. verify_chunk
  13. set_pthread_cpu
  14. set_mycpu
  15. segv_handler
  16. set_segv_handler
  17. rim_fn
  18. mem_snapshot_fn
  19. alrm_sighandler
  20. main

   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 - 1);
 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 }

/* [<][>][^][v][top][bottom][index][help] */