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