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
27 static int loops = 1000;
28 static int threads = 40;
29
30 static volatile int sig_caught;
31 static int threads_running;
32
mkfile(int * size)33 static 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
58 static void exit_thread(void) __attribute__ ((noreturn));
59
exit_thread(void)60 static void exit_thread(void)
61 {
62 tst_atomic_dec(&threads_running);
63 pthread_exit(NULL);
64 }
65
map_write_unmap(void * args)66 void *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
sig_handler(int signal)100 static void sig_handler(int signal)
101 {
102 sig_caught = signal;
103 }
104
test_mmap(void)105 static 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
setup(void)131 static 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
cleanup(void)153 static 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
174 static 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