xref: /freebsd/tools/test/stress2/misc/radix.sh (revision 3494f7c0)
1#!/bin/sh
2
3#
4# Copyright (c) 2013 EMC Corp.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28
29# Consume VM radix nodes
30
31# "panic: default pager with handle" seen with WiP kernel code.
32# https://people.freebsd.org/~pho/stress/log/kostik1243.txt
33
34# "panic: ASan: Invalid access, 8-byte read at ..., MallocRedZone(fb)" seen
35
36[ `sysctl vm.swap_total | sed 's/.* //'` -eq 0 ] && exit 0
37
38. ../default.cfg
39
40log=/tmp/radix.log
41dir=/tmp
42odir=`pwd`
43cd $dir
44sed '1,/^EOF/d' < $odir/$0 > $dir/radix.c
45mycc -o radix -Wall -Wextra radix.c || exit 1
46rm -f radix.c
47cd $odir
48
49set -u
50trap "rm -f rendezvous" EXIT INT
51parallel=1
52usermem=`sysctl hw.usermem | sed 's/.* //'`
53pagesize=`pagesize`
54start=`date +%s`
55while true; do
56	timeout 20m /tmp/radix $parallel > $log; s=$?
57	[ $s -eq 124 ] && { echo "Timed out"; break; }
58	[ $s -ne 0 ] && cat $log
59	used=`awk '{print $4}' < $log`
60	[ -z "$used" ] && break
61	[ $((`date +%s` - start)) -gt 300 ] && break
62	[ $used -gt $((usermem / pagesize)) ] && break
63	[ $parallel -eq 1 ] &&
64	    parallel=$((usermem / pagesize / used))
65	parallel=$((parallel + 1))
66	[ $parallel -gt 10 ] && parallel=10
67done
68cat /tmp/radix.log
69
70rm -f /tmp/radix $log
71exit $s
72
73EOF
74/*
75   On Wed, 17 Apr 2013 18:57:00 -0500 alc wrote:
76
77   Suppose that I write a program for i386 that creates giant VM objects,
78   perhaps, using shm_open() + ftruncate(), and touches pages 0, 1, 8, 9,
79   64, 65, 72, 73, 512, 513, 520, 521, 576, 577, 584, 585, 4096, 4097,
80   4104, 4105, ... in each of the VM objects. (The sequence would be
81   different on amd64.) I could work around the 32-bit address space
82   limitation by mmap(2)ing and munmap(2)ing windows onto a VM object.
83   Each of the VM objects would have only one less interior node in the
84   radix tree than pages. If I create enough of these VM objects, then I
85   can consume all of the available pages and an almost equal number of
86   interior nodes. (Maybe it's worth writing this program so that some
87   experiments could be done?)
88*/
89
90#include <sys/param.h>
91
92#include <err.h>
93#include <errno.h>
94#include <fcntl.h>
95#include <sched.h>
96#include <signal.h>
97#include <stdint.h>
98#include <stdio.h>
99#include <stdlib.h>
100#include <sys/mman.h>
101#include <sys/wait.h>
102#include <unistd.h>
103
104#ifdef __LP64__
105#define	WIDTH	4
106#else
107#define	WIDTH	3
108#endif
109#define	N	(int)howmany(sizeof(uint64_t) * NBBY, WIDTH)
110
111typedef uint64_t state_t[N];
112
113static uint64_t pgs;
114static int fds[2];
115static int parallel;
116static volatile sig_atomic_t s1;
117static int ps;
118
119static void
120init(state_t state)
121{
122	int i;
123
124	for (i = 0; i < N; i++)
125		state[i] = 0;
126}
127
128static uint64_t
129generator(state_t state)
130{
131	uint64_t value;
132	int i;
133
134	value = 0;
135	for (i = 0; i < N; i++)
136		value += state[i] << (i * WIDTH);
137	for (i = 0; i < N; i++)
138		if (state[i] == 0)
139			break;
140	if (i < N)
141		state[i]++;
142	for (i--; i >= 0; i--)
143		state[i]--;
144	return (value);
145}
146
147static int
148wr(int fd, off_t pno)
149{
150	off_t len, offset;
151	void *p;
152
153	offset = pno * ps;
154	len = ps;
155	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NOSYNC,
156	    fd, offset);
157	if (p == MAP_FAILED) {
158		if (errno == ENOMEM)
159			return (1);
160		err(1, "mmap(len 0x%jx, offset 0x%jx). %s:%d", len, offset,
161		    __FILE__, __LINE__);
162	}
163	*(char *)p = 1;
164	pgs++;
165
166	return (0);
167}
168
169static void
170handler(int s __unused)
171{
172	s1++;
173}
174
175static void
176ihandler(int s __unused)
177{
178	_exit(1);
179}
180
181static int
182radix(void)
183{
184	FILE *f;
185	int r;
186
187	if ((f = popen("vmstat -z | grep RADIX | awk -F',' '{print $3}'", "r")) == NULL)
188		err(1, "popen");
189	fscanf(f, "%d", &r);
190	pclose(f);
191
192	return (r);
193}
194
195static void
196test(void)
197{
198	state_t state;
199	off_t offset;
200	int fd, i;
201
202	signal(SIGHUP, ihandler);
203	for (;;) {
204		if (access("rendezvous", R_OK) == 0)
205			break;
206		usleep(2000);
207	}
208
209	if ((fd = open("/dev/zero", O_RDWR)) == -1)
210		err(1, "open()");
211
212	init(state);
213	offset = generator(state);
214	do {
215		if (wr(fd, offset) != 0)
216			break;
217		offset = generator(state);
218	} while (offset != 0);
219
220	if (write(fds[1], &pgs, sizeof(pgs)) != sizeof(pgs))
221		err(1, "ewrite pipe");
222	kill(getppid(), SIGHUP);
223	for (i = 0; i < 180; i++)
224		sleep(1);
225	close(fd);
226
227	_exit(0);
228}
229
230int
231main(int argc, char **argv)
232{
233	uint64_t pages;
234	pid_t *pids;
235	int i, r1, r2, rfd;
236
237	if (argc != 2)
238		errx(1, "Usage: %s <number of parallel processes>.", argv[0]);
239	parallel = atoi(argv[1]);
240
241	ps = getpagesize();
242	signal(SIGALRM, ihandler);
243	signal(SIGHUP, handler);
244	unlink("rendezvous");
245	pids = malloc(parallel * sizeof(pid_t));
246	if (pipe(fds) == -1)
247		err(1, "pipe");
248	r1 = radix();
249	for (i = 0; i < parallel; i++) {
250		if ((pids[i] = fork()) == 0)
251			test();
252	}
253	if ((rfd = open("rendezvous", O_CREAT, 0644)) == -1)
254		err(1, "open()");
255	close(rfd);
256	alarm(300);
257	while (s1 != parallel) {
258		usleep(10000);
259	}
260	r2 = radix();
261	pages = 0;
262	for (i = 0; i < parallel; i++) {
263		kill(pids[i], SIGHUP);
264		if (read(fds[0], &pgs, sizeof(pgs)) != sizeof(pgs))
265			err(1, "read pipe");
266		pages += pgs;
267	}
268	fprintf(stdout, "A total of %jd pages (%.1f MB) touched, %d"
269	    " RADIX nodes used, p/r = %.1f, parallel = %d.\n",
270	    pages, pages * ps / 1024. / 1024, r2 - r1,
271	    pages / (r2 - r1 + 0.), parallel);
272
273	for (i = 0; i < parallel; i++) {
274		wait(NULL);
275	}
276	unlink("rendezvous");
277	return (0);
278}
279