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