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
23 static char *str_loops;
24 static char *str_threads;
25 static char *map_private;
26 static char *str_exec_time;
27
28 static int loops = 1000;
29 static int threads = 40;
30 static float exec_time = 24;
31
32 static volatile int sig_caught;
33 static int threads_running;
34
mkfile(int * size)35 static int mkfile(int *size)
36 {
37 int fd;
38 int index = 0;
39 char buf[4096];
40 char template[PATH_MAX];
41
42 memset(buf, 'a', 4096);
43 snprintf(template, PATH_MAX, "ashfileXXXXXX");
44 if ((fd = mkstemp(template)) == -1)
45 tst_brk(TBROK | TERRNO, "mkstemp()");
46 unlink(template);
47
48 *size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096;
49
50 while (index < *size) {
51 index += sizeof(buf);
52 SAFE_WRITE(1, fd, buf, sizeof(buf));
53 }
54
55 fsync(fd);
56
57 return fd;
58 }
59
60 static void exit_thread(void) __attribute__ ((noreturn));
61
exit_thread(void)62 static void exit_thread(void)
63 {
64 tst_atomic_dec(&threads_running);
65 pthread_exit(NULL);
66 }
67
map_write_unmap(void * args)68 void *map_write_unmap(void *args)
69 {
70 int fsize;
71 int fd;
72 int i;
73 void *addr;
74 long tid = (long)args;
75
76 tst_atomic_inc(&threads_running);
77
78 for (i = 0; i < loops; i++) {
79 if (sig_caught)
80 exit_thread();
81
82 if ((fd = mkfile(&fsize)) == -1)
83 exit_thread();
84
85 addr = SAFE_MMAP(NULL, fsize, PROT_WRITE | PROT_READ,
86 map_private ? MAP_PRIVATE : MAP_SHARED, fd, 0);
87
88 memset(addr, 'A', fsize);
89
90 tst_res(TINFO, "Thread %4li, addr [%p], size %4ikB, iter %4d",
91 tid, addr, fsize/1024, i);
92
93 usleep(1);
94
95 SAFE_MUNMAP(addr, fsize);
96 SAFE_CLOSE(fd);
97 }
98
99 exit_thread();
100 }
101
sig_handler(int signal)102 static void sig_handler(int signal)
103 {
104 sig_caught = signal;
105 }
106
test_mmap(void)107 static void test_mmap(void)
108 {
109 long i;
110 pthread_t thids[threads];
111
112 alarm(exec_time * 3600);
113
114 while (!sig_caught) {
115 for (i = 0; i < threads; i++) {
116 SAFE_PTHREAD_CREATE(&thids[i], NULL,
117 map_write_unmap, (void*)i);
118 sched_yield();
119 }
120
121 for (i = 0; i < threads; i++)
122 SAFE_PTHREAD_JOIN(thids[i], NULL);
123 }
124
125 if (sig_caught == SIGALRM) {
126 tst_res(TPASS, "Test passed");
127 } else {
128 tst_res(TFAIL, "Unexpected signal caught %s",
129 tst_strsig(sig_caught));
130 }
131 }
132
setup(void)133 static void setup(void)
134 {
135 if (tst_parse_int(str_loops, &loops, 1, INT_MAX))
136 tst_brk(TBROK, "Invalid number of loops '%s'", str_loops);
137
138 if (tst_parse_int(str_threads, &threads, 1, INT_MAX))
139 tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
140
141 if (tst_parse_float(str_exec_time, &exec_time, 0.0005, 9000))
142 tst_brk(TBROK, "Invalid execution time '%s'", str_exec_time);
143
144 tst_set_timeout(exec_time * 3600 + 300);
145
146 SAFE_SIGNAL(SIGALRM, sig_handler);
147 SAFE_SIGNAL(SIGBUS, sig_handler);
148 SAFE_SIGNAL(SIGSEGV, sig_handler);
149
150 unsigned int seed = time(NULL) % 100;
151
152 srand(seed);
153
154 tst_res(TINFO, "Seed %u", seed);
155 tst_res(TINFO, "Number of loops %i", loops);
156 tst_res(TINFO, "Number of threads %i", threads);
157 tst_res(TINFO, "MAP_PRIVATE = %i", map_private ? 1 : 0);
158 tst_res(TINFO, "Execution time %fH", exec_time);
159 }
160
cleanup(void)161 static void cleanup(void)
162 {
163 static int flag;
164
165 if (tst_atomic_inc(&flag) != 1)
166 exit_thread();
167
168 if (!threads_running)
169 return;
170
171 tst_res(TINFO, "Waiting for %i threads to terminate", threads_running);
172
173 sig_caught = 1;
174
175 while ((volatile int)threads_running > 1) {
176 tst_res(TINFO, "Running threads %i",
177 (volatile int)threads_running);
178 usleep(500000);
179 }
180 }
181
182 static struct tst_test test = {
183 .options = (struct tst_option[]) {
184 {"l:", &str_loops, "Number of map-write-unmap loops"},
185 {"n:", &str_threads, "Number of worker threads"},
186 {"p", &map_private, "Turns on MAP_PRIVATE (default MAP_SHARED)"},
187 {"x:", &str_exec_time, "float Execution time in hours (default 24H)"},
188 {}
189 },
190 .needs_tmpdir = 1,
191 .setup = setup,
192 .cleanup = cleanup,
193 .test_all = test_mmap,
194 };
195