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