1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) International Business Machines Corp., 2001 4 * Copyright (C) 2016 Cyril Hrubis <chrubis@suse.cz> 5 * Author: Manoj Iyer, IBM Austin TX <manjo@austin.ibm.com>, 2001 6 * 7 * Tests the LINUX memory manager. The program is aimed at stressing the memory 8 * manager by repeaded map/write/unmap of file/memory of random size (maximum 9 * 1GB) this is done by multiple threads. 10 * 11 * Create a file of random size upto 1000 times 4096, map it, change the 12 * contents of the file and unmap it. This is repeated several times for the 13 * specified number of hours by a certain number of threads. 14 */ 15 16#include <stdio.h> 17#include <stdlib.h> 18#include <limits.h> 19#include <pthread.h> 20#include "tst_safe_pthread.h" 21#include "tst_test.h" 22 23static char *str_loops; 24static char *str_threads; 25static char *map_private; 26 27static int loops = 1000; 28static int threads = 40; 29 30static volatile int sig_caught; 31static int threads_running; 32 33static int mkfile(int *size) 34{ 35 int fd; 36 int index = 0; 37 char buf[4096]; 38 char template[PATH_MAX]; 39 40 memset(buf, 'a', 4096); 41 snprintf(template, PATH_MAX, "ashfileXXXXXX"); 42 if ((fd = mkstemp(template)) == -1) 43 tst_brk(TBROK | TERRNO, "mkstemp()"); 44 unlink(template); 45 46 *size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096; 47 48 while (index < *size) { 49 index += sizeof(buf); 50 SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, sizeof(buf)); 51 } 52 53 fsync(fd); 54 55 return fd; 56} 57 58static void exit_thread(void) __attribute__ ((noreturn)); 59 60static void exit_thread(void) 61{ 62 tst_atomic_dec(&threads_running); 63 pthread_exit(NULL); 64} 65 66void *map_write_unmap(void *args) 67{ 68 int fsize; 69 int fd; 70 int i; 71 void *addr; 72 long tid = (long)args; 73 74 tst_atomic_inc(&threads_running); 75 76 for (i = 0; i < loops; i++) { 77 if (sig_caught) 78 exit_thread(); 79 80 if ((fd = mkfile(&fsize)) == -1) 81 exit_thread(); 82 83 addr = SAFE_MMAP(NULL, fsize, PROT_WRITE | PROT_READ, 84 map_private ? MAP_PRIVATE : MAP_SHARED, fd, 0); 85 86 memset(addr, 'A', fsize); 87 88 tst_res(TINFO, "Thread %4li, addr [%p], size %4ikB, iter %4d", 89 tid, addr, fsize/1024, i); 90 91 usleep(1); 92 93 SAFE_MUNMAP(addr, fsize); 94 SAFE_CLOSE(fd); 95 } 96 97 exit_thread(); 98} 99 100static void sig_handler(int signal) 101{ 102 sig_caught = signal; 103} 104 105static void test_mmap(void) 106{ 107 long i; 108 pthread_t thids[threads]; 109 110 alarm(tst_remaining_runtime()); 111 112 while (!sig_caught) { 113 for (i = 0; i < threads; i++) { 114 SAFE_PTHREAD_CREATE(&thids[i], NULL, 115 map_write_unmap, (void*)i); 116 sched_yield(); 117 } 118 119 for (i = 0; i < threads; i++) 120 SAFE_PTHREAD_JOIN(thids[i], NULL); 121 } 122 123 if (sig_caught == SIGALRM) { 124 tst_res(TPASS, "Test passed"); 125 } else { 126 tst_res(TFAIL, "Unexpected signal caught %s", 127 tst_strsig(sig_caught)); 128 } 129} 130 131static void setup(void) 132{ 133 if (tst_parse_int(str_loops, &loops, 1, INT_MAX)) 134 tst_brk(TBROK, "Invalid number of loops '%s'", str_loops); 135 136 if (tst_parse_int(str_threads, &threads, 1, INT_MAX)) 137 tst_brk(TBROK, "Invalid number of threads '%s'", str_threads); 138 139 SAFE_SIGNAL(SIGALRM, sig_handler); 140 SAFE_SIGNAL(SIGBUS, sig_handler); 141 SAFE_SIGNAL(SIGSEGV, sig_handler); 142 143 unsigned int seed = time(NULL) % 100; 144 145 srand(seed); 146 147 tst_res(TINFO, "Seed %u", seed); 148 tst_res(TINFO, "Number of loops %i", loops); 149 tst_res(TINFO, "Number of threads %i", threads); 150 tst_res(TINFO, "MAP_PRIVATE = %i", map_private ? 1 : 0); 151} 152 153static void cleanup(void) 154{ 155 static int flag; 156 157 if (tst_atomic_inc(&flag) != 1) 158 exit_thread(); 159 160 if (!threads_running) 161 return; 162 163 tst_res(TINFO, "Waiting for %i threads to terminate", threads_running); 164 165 sig_caught = 1; 166 167 while ((volatile int)threads_running > 1) { 168 tst_res(TINFO, "Running threads %i", 169 (volatile int)threads_running); 170 usleep(500000); 171 } 172} 173 174static struct tst_test test = { 175 .options = (struct tst_option[]) { 176 {"l:", &str_loops, "Number of map-write-unmap loops"}, 177 {"n:", &str_threads, "Number of worker threads"}, 178 {"p", &map_private, "Turns on MAP_PRIVATE (default MAP_SHARED)"}, 179 {} 180 }, 181 .needs_tmpdir = 1, 182 .setup = setup, 183 .cleanup = cleanup, 184 .test_all = test_mmap, 185 .max_runtime = 60, 186}; 187