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