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
18char *progname;
19char *filename;
20void *mapped_mem;
21
22int niters;
23int ntasks = 100;
24int nvmas = 100;
25int vmasize = 1024*1024;
26int vmas_to_do = -1;
27int pagesize;
28int fd;
29char **vma_addresses;
30volatile int *nr_children_running;
31int verbose;
32
33enum access_pattern {
34	ap_random,
35	ap_linear,
36	ap_half
37} access_pattern = ap_linear;
38
39void 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
49void 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
65void 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
78void 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
89void 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
112void 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
137int 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