xref: /freebsd/tools/test/stress2/misc/radix.sh (revision 19261079b74319502c6ffa1249920079f0f69a72)
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 -e
50parallel=1
51usermem=`sysctl hw.usermem | sed 's/.* //'`
52pagesize=`pagesize`
53start=`date +%s`
54while true; do
55	/tmp/radix $parallel > $log; s=$?
56	[ $s -ne 0 ] && cat $log
57	used=`awk '{print $4}' < $log`
58	[ -z "$used" ] && break
59	[ $((`date +%s` - start)) -gt 300 ] && break
60	[ $used -gt $((usermem / pagesize)) ] && break
61	[ $parallel -eq 1 ] &&
62	    parallel=$((usermem / pagesize / used))
63	parallel=$((parallel + 1))
64	echo "`date +%T` parallel=$parallel" # XXX
65done
66cat /tmp/radix.log
67
68rm -f /tmp/radix $log
69exit $s
70
71EOF
72/*
73   On Wed, 17 Apr 2013 18:57:00 -0500 alc wrote:
74
75   Suppose that I write a program for i386 that creates giant VM objects,
76   perhaps, using shm_open() + ftruncate(), and touches pages 0, 1, 8, 9,
77   64, 65, 72, 73, 512, 513, 520, 521, 576, 577, 584, 585, 4096, 4097,
78   4104, 4105, ... in each of the VM objects. (The sequence would be
79   different on amd64.) I could work around the 32-bit address space
80   limitation by mmap(2)ing and munmap(2)ing windows onto a VM object.
81   Each of the VM objects would have only one less interior node in the
82   radix tree than pages. If I create enough of these VM objects, then I
83   can consume all of the available pages and an almost equal number of
84   interior nodes. (Maybe it's worth writing this program so that some
85   experiments could be done?)
86*/
87
88#include <sys/param.h>
89
90#include <err.h>
91#include <errno.h>
92#include <fcntl.h>
93#include <sched.h>
94#include <signal.h>
95#include <stdint.h>
96#include <stdio.h>
97#include <stdlib.h>
98#include <sys/mman.h>
99#include <sys/wait.h>
100#include <unistd.h>
101
102#ifdef __LP64__
103#define	WIDTH	4
104#else
105#define	WIDTH	3
106#endif
107#define	N	(int)howmany(sizeof(uint64_t) * NBBY, WIDTH)
108
109typedef uint64_t state_t[N];
110
111static uint64_t pgs;
112static int fds[2];
113static int parallel;
114static volatile sig_atomic_t s1;
115static int ps;
116
117static void
118init(state_t state)
119{
120	int i;
121
122	for (i = 0; i < N; i++)
123		state[i] = 0;
124}
125
126static uint64_t
127generator(state_t state)
128{
129	uint64_t value;
130	int i;
131
132	value = 0;
133	for (i = 0; i < N; i++)
134		value += state[i] << (i * WIDTH);
135	for (i = 0; i < N; i++)
136		if (state[i] == 0)
137			break;
138	if (i < N)
139		state[i]++;
140	for (i--; i >= 0; i--)
141		state[i]--;
142	return (value);
143}
144
145static int
146wr(int fd, off_t pno)
147{
148	off_t len, offset;
149	void *p;
150
151	offset = pno * ps;
152	len = ps;
153	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NOSYNC,
154	    fd, offset);
155	if (p == MAP_FAILED) {
156		if (errno == ENOMEM)
157			return (1);
158		err(1, "mmap(len 0x%jx, offset 0x%jx). %s:%d", len, offset,
159		    __FILE__, __LINE__);
160	}
161	*(char *)p = 1;
162	pgs++;
163
164	return (0);
165}
166
167static void
168handler(int s __unused)
169{
170	s1++;
171}
172
173static void
174ihandler(int s __unused)
175{
176	_exit(1);
177}
178
179static int
180radix(void)
181{
182	FILE *f;
183	int r;
184
185	if ((f = popen("vmstat -z | grep RADIX | awk -F',' '{print $3}'", "r")) == NULL)
186		err(1, "popen");
187	fscanf(f, "%d", &r);
188	pclose(f);
189
190	return (r);
191}
192
193static void
194test(void)
195{
196	state_t state;
197	off_t offset;
198	int fd, i;
199
200	signal(SIGHUP, ihandler);
201	for (;;) {
202		if (access("rendezvous", R_OK) == 0)
203			break;
204		usleep(2000);
205	}
206
207	if ((fd = open("/dev/zero", O_RDWR)) == -1)
208		err(1, "open()");
209
210	init(state);
211	offset = generator(state);
212	do {
213		if (wr(fd, offset) != 0)
214			break;
215		offset = generator(state);
216	} while (offset != 0);
217
218	if (write(fds[1], &pgs, sizeof(pgs)) != sizeof(pgs))
219		err(1, "ewrite pipe");
220	kill(getppid(), SIGHUP);
221	for (i = 0; i < 180; i++)
222		sleep(1);
223	close(fd);
224
225	_exit(0);
226}
227
228int
229main(int argc, char **argv)
230{
231	uint64_t pages;
232	pid_t *pids;
233	int i, r1, r2, rfd;
234
235	if (argc != 2)
236		errx(1, "Usage: %s <number of parallel processes>.", argv[0]);
237	parallel = atoi(argv[1]);
238
239	ps = getpagesize();
240	signal(SIGALRM, ihandler);
241	signal(SIGHUP, handler);
242	unlink("rendezvous");
243	pids = malloc(parallel * sizeof(pid_t));
244	if (pipe(fds) == -1)
245		err(1, "pipe");
246	r1 = radix();
247	for (i = 0; i < parallel; i++) {
248		if ((pids[i] = fork()) == 0)
249			test();
250	}
251	if ((rfd = open("rendezvous", O_CREAT, 0644)) == -1)
252		err(1, "open()");
253	close(rfd);
254	alarm(300);
255	while (s1 != parallel) {
256		usleep(10000);
257	}
258	r2 = radix();
259	pages = 0;
260	for (i = 0; i < parallel; i++) {
261		kill(pids[i], SIGHUP);
262		if (read(fds[0], &pgs, sizeof(pgs)) != sizeof(pgs))
263			err(1, "read pipe");
264		pages += pgs;
265	}
266	fprintf(stdout, "A total of %jd pages (%.1f MB) touched, %d"
267	    " RADIX nodes used, p/r = %.1f, parallel = %d.\n",
268	    pages, pages * ps / 1024. / 1024, r2 - r1,
269	    pages / (r2 - r1 + 0.), parallel);
270
271	for (i = 0; i < parallel; i++) {
272		wait(NULL);
273	}
274	unlink("rendezvous");
275	return (0);
276}
277