1 /*
2  * Create lots of VMA's mapped by lots of tasks.  To tickle objrmap and the
3  * virtual scan.
4  */
5 
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <time.h>
13 #include <sys/mman.h>
14 #include <sys/signal.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 
18 char *progname;
19 char *filename;
20 void *mapped_mem;
21 
22 int niters;
23 int ntasks = 100;
24 int nvmas = 100;
25 int vmasize = 1024*1024;
26 int vmas_to_do = -1;
27 int pagesize;
28 int fd;
29 char **vma_addresses;
30 volatile int *nr_children_running;
31 int verbose;
32 
33 enum access_pattern {
34 	ap_random,
35 	ap_linear,
36 	ap_half
37 } access_pattern = ap_linear;
38 
open_file()39 void open_file()
40 {
41 	fd = open(filename, O_RDWR|O_TRUNC|O_CREAT, 0666);
42 	if (fd < 0) {
43 		fprintf(stderr, "%s: Cannot open `%s': %s\n",
44 			progname, filename, strerror(errno));
45 		exit(1);
46 	}
47 }
48 
usage(void)49 void usage(void)
50 {
51 	fprintf(stderr, "Usage: %s [-hlrvV] [-iN] [-nN] [-sN] [-tN] filename\n",
52 				progname);
53 	fprintf(stderr, "     -h:          Pattern: half of memory is busy\n");
54 	fprintf(stderr, "     -l:          Pattern: linear\n");
55 	fprintf(stderr, "     -r:          Pattern: random\n");
56 	fprintf(stderr, "     -iN:         Number of iterations\n");
57 	fprintf(stderr, "     -nN:         Number of VMAs\n");
58 	fprintf(stderr, "     -sN:         VMA size (pages)\n");
59 	fprintf(stderr, "     -tN:         Run N tasks\n");
60 	fprintf(stderr, "     -VN:         Number of VMAs to process\n");
61 	fprintf(stderr, "     -v:          Verbose\n");
62 	exit(1);
63 }
64 
touch_pages(int nr_vmas)65 void touch_pages(int nr_vmas)
66 {
67 	int i;
68 
69 	for (i = 0; i < nr_vmas; i++) {
70 		char *p = vma_addresses[i];
71 		int page;
72 
73 		for (page = 0; page < vmasize; page++)
74 			p[page * pagesize]++;
75 	}
76 }
77 
msync_file(int nr_vmas)78 void msync_file(int nr_vmas)
79 {
80 	int i;
81 
82 	for (i = 0; i < nr_vmas; i++) {
83 		char *p = vma_addresses[i];
84 
85 		msync(p, vmasize * pagesize, MS_ASYNC);
86 	}
87 }
88 
touch_random_pages(void)89 void touch_random_pages(void)
90 {
91 	int vma;
92 	int page;
93 
94 	srand(getpid() * time(0));
95 
96 	for (vma = 0; vma < vmas_to_do; vma++) {
97 		for (page = 0; page < vmasize; page++) {
98 			int rand_vma;
99 			int rand_page;
100 			char *p;
101 
102 			rand_vma = rand() % nvmas;
103 			rand_page = rand() % vmasize;
104 			p = vma_addresses[rand_vma] + rand_page * pagesize;
105 			(*p)++;
106 		}
107 		if (verbose > 1)
108 			printf("vma %d/%d done\n", vma, nvmas);
109 	}
110 }
111 
child(int childno)112 void child(int childno)
113 {
114 	int iter;
115 
116 	sleep(1);
117 	if (access_pattern == ap_half && childno == 0) {
118 		while (*nr_children_running > 1) {
119 			touch_pages(nvmas / 2);
120 		}
121 		return;
122 	}
123 
124 	for (iter = 0; iter < niters; iter++) {
125 		if (access_pattern == ap_random) {
126 			touch_random_pages();
127 		} else if (access_pattern == ap_linear) {
128 			touch_pages(nvmas);
129 		} else if (access_pattern == ap_half) {
130 			touch_pages(nvmas);
131 		}
132 		if (verbose > 0)
133 			printf("%d/%d\n", iter, niters);
134 	}
135 }
136 
main(int argc,char * argv[])137 int main(int argc, char *argv[])
138 {
139 	int c;
140 	int i;
141 	loff_t offset;
142 	loff_t file_size;
143 	int childno;
144 
145 	progname = argv[0];
146 
147 	while ((c = getopt(argc, argv, "vrlhi:n:s:t:V:")) != -1) {
148 		switch (c) {
149 		case 'h':
150 			access_pattern = ap_half;
151 			break;
152 		case 'l':
153 			access_pattern = ap_linear;
154 			break;
155 		case 'r':
156 			access_pattern = ap_random;
157 			break;
158 		case 'i':
159 			niters = strtol(optarg, NULL, 10);
160 			break;
161 		case 'n':
162 			nvmas = strtol(optarg, NULL, 10);
163 			break;
164 		case 's':
165 			vmasize = strtol(optarg, NULL, 10);
166 			break;
167 		case 't':
168 			ntasks = strtol(optarg, NULL, 10);
169 			break;
170 		case 'V':
171 			vmas_to_do = strtol(optarg, NULL, 10);
172 			break;
173 		case 'v':
174 			verbose++;
175 			break;
176 		}
177 	}
178 
179 	if (optind == argc)
180 		usage();
181 	filename = argv[optind++];
182 	if (optind != argc)
183 		usage();
184 
185 	if (vmas_to_do == -1)
186 		vmas_to_do = nvmas;
187 
188 	pagesize = getpagesize();
189 	open_file();
190 
191 	file_size = nvmas;
192 	file_size *= vmasize;
193 	file_size += nvmas - 1;
194 	file_size *= pagesize;
195 
196 	printf("Total file size: %lldk, Total memory: %lldk\n",
197 		file_size / 1024,
198 		((long long)nvmas * vmasize * pagesize) / 1024);
199 
200 	if (ftruncate(fd, file_size) < 0) {
201 		perror("ftruncate");
202 		exit(1);
203 	}
204 
205 	vma_addresses = malloc(nvmas * sizeof(*vma_addresses));
206 	nr_children_running = (int *)mmap(0, sizeof(*nr_children_running),
207 				PROT_READ|PROT_WRITE,
208 				MAP_SHARED|MAP_ANONYMOUS,
209 				-1,
210 				0);
211 	if (nr_children_running == MAP_FAILED) {
212 		perror("mmap1");
213 		exit(1);
214 	}
215 
216 	offset = 0;
217 
218 	for (i = 0; i < nvmas; i++) {
219 		char *p;
220 
221 		p = mmap(0, vmasize * pagesize, PROT_READ|PROT_WRITE,
222 				MAP_SHARED, fd, offset);
223 		if (p == MAP_FAILED) {
224 			perror("mmap");
225 			exit(1);
226 		}
227 		vma_addresses[i] = p;
228 		offset += vmasize * pagesize + pagesize;
229 	}
230 
231 	touch_pages(nvmas);
232 	msync_file(nvmas);
233 	*nr_children_running = ntasks;
234 
235 	for (childno = 0; childno < ntasks; childno++) {
236 		if (fork() == 0) {
237 			child(childno);
238 			exit(0);
239 		}
240 	}
241 
242 	signal(SIGINT, SIG_IGN);
243 
244 	for (i = 0; i < ntasks; i++) {
245 		pid_t pid;
246 		int status;
247 
248 		/* Catch each child error status and report. */
249 		pid = wait3(&status, 0, 0);
250 		if (pid < 0)	/* No more children? */
251 			break;
252 		(*nr_children_running)--;
253 	}
254 	exit(0);
255 }
256