1#!/bin/sh 2 3# 4# Copyright (c) 2014 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# contigmalloc(9) / free(9) test scenario. 30# malloc() a random number of buffers with random size and then free them. 31 32# A malloc pattern might look like this: 33# contigmalloc(186 pages) 34# contigmalloc(56 pages) 35# contigmalloc(9 pages) 36# contigmalloc(202 pages) 37# contigmalloc(49 pages) 38# contigmalloc(5 pages) 39 40# "panic: vm_reserv_alloc_contig: reserv 0xff... isn't free" seen. 41# http://people.freebsd.org/~pho/stress/log/contigmalloc.txt 42# Fixed by r271351. 43 44[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 45[ -d /usr/src/sys ] || exit 0 46builddir=`sysctl kern.version | grep @ | sed 's/.*://'` 47[ -d "$builddir" ] && export KERNBUILDDIR=$builddir || exit 0 48export SYSDIR=`echo $builddir | sed 's#/sys.*#/sys#'` 49 50. ../default.cfg 51 52odir=`pwd` 53dir=/tmp/contigmalloc 54rm -rf $dir; mkdir -p $dir 55cat > $dir/ctest.c <<EOF 56#include <sys/param.h> 57#include <sys/syscall.h> 58 59#include <err.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <time.h> 63#include <unistd.h> 64 65#define min(a,b) (((a)<(b))?(a):(b)) 66#define CAP (64 * 1024 * 1024) /* Total allocation */ 67#define MAXBUF (32 * 1024 * 1024) /* Max buffer size */ 68#define N 512 /* Max allocations */ 69#define RUNTIME 120 70#define TALLOC 1 71#define TFREE 2 72 73void *p[N]; 74long size[N]; 75int n; 76 77void 78test(int argc, char *argv[]) 79{ 80 long mw, s; 81 int i, no, ps, res; 82 83 if (argc == 3) { 84 no = atoi(argv[1]); 85 mw = atol(argv[2]); 86 } 87 if (argc != 3 || no == 0 || mw == 0) 88 errx(1, "Usage: %s <syscall number> <max wired>", argv[0]); 89 90 ps = getpagesize(); 91 s = 0; 92 n = arc4random() % N + 1; 93 mw = mw / 100 * 10 * ps; /* Use 10% of vm.max_user_wired */ 94 mw = min(mw, CAP); 95 for (i = 0; i < n; i++) { 96 size[i] = round_page((arc4random() % MAXBUF) + 1); 97 if (s + size[i] > mw) 98 continue; 99 res = syscall(no, TALLOC, &p[i], &size[i]); 100 if (res == -1) { 101 warn("contigmalloc(%lu pages) failed at loop %d", 102 size[i] / ps, i); 103 usleep(200000); 104 } else { 105#if defined(TEST) 106 fprintf(stderr, "contigmalloc(%lu pages)\n", 107 size[i] / ps); 108#endif 109 s += size[i]; 110 } 111 } 112 113 setproctitle("%ld Mb", s / 1024 / 1024); 114 115 for (i = 0; i < n; i++) { 116 if (p[i] != NULL) { 117 res = syscall(no, TFREE, &p[i], &size[i]); 118#if defined(TEST) 119 fprintf(stderr, "free(%lu pages)\n", 120 size[i] / ps); 121#endif 122 p[i] = NULL; 123 } 124 } 125} 126 127int 128main(int argc, char *argv[]) 129{ 130 time_t start; 131 132 start = time(NULL); 133 while (time(NULL) - start < RUNTIME) 134 test(argc, argv); 135 136 return (0); 137} 138 139EOF 140mycc -o /tmp/ctest -Wall -Wextra -O0 -g $dir/ctest.c || exit 1 141rm $dir/ctest.c 142 143cd $dir 144cat > Makefile <<EOF 145KMOD= cmalloc 146SRCS= cmalloc.c 147 148.include <bsd.kmod.mk> 149EOF 150 151sed '1,/^EOF2/d' < $odir/$0 > cmalloc.c 152make || exit 1 153kldload $dir/cmalloc.ko || exit 1 154 155cd $odir 156mw=`sysctl -n vm.max_user_wired` || exit 1 157/tmp/ctest `sysctl -n debug.cmalloc_offset` $mw 2>&1 | tail -5 158kldunload $dir/cmalloc.ko 159rm -rf $dir /tmp/ctest 160exit 0 161 162EOF2 163#include <sys/param.h> 164#include <sys/kernel.h> 165#include <sys/malloc.h> 166#include <sys/module.h> 167#include <sys/proc.h> 168#include <sys/sysctl.h> 169#include <sys/sysent.h> 170#include <sys/sysproto.h> 171#include <sys/systm.h> 172 173#define TALLOC 1 174#define TFREE 2 175 176/* 177 * Hook up a syscall for contigmalloc testing. 178 */ 179 180struct cmalloc_args { 181 int a_op; 182 void *a_ptr; 183 void *a_size; 184}; 185 186static int 187cmalloc(struct thread *td, struct cmalloc_args *uap) 188{ 189 void *p; 190 unsigned long size; 191 int error; 192 193 error = copyin(uap->a_size, &size, sizeof(size)); 194 if (error != 0) { 195 return (error); 196 } 197 switch (uap->a_op) { 198 case TFREE: 199 error = copyin(uap->a_ptr, &p, sizeof(p)); 200 if (error == 0) 201 free(p, M_TEMP); 202 return (error); 203 204 case TALLOC: 205 p = contigmalloc(size, M_TEMP, M_NOWAIT, 0ul, ~0ul, 4096, 0); 206 if (p != NULL) { 207 error = copyout(&p, uap->a_ptr, sizeof(p)); 208 return (error); 209 } 210 return (ENOMEM); 211 } 212 return (EINVAL); 213} 214 215/* 216 * The sysent for the new syscall 217 */ 218static struct sysent cmalloc_sysent = { 219 .sy_narg = 3, /* sy_narg */ 220 .sy_call = (sy_call_t *) cmalloc /* sy_call */ 221}; 222 223/* 224 * The offset in sysent where the syscall is allocated. 225 */ 226static int cmalloc_offset = NO_SYSCALL; 227 228SYSCTL_INT(_debug, OID_AUTO, cmalloc_offset, CTLFLAG_RD, &cmalloc_offset, 0, 229 "cmalloc syscall number"); 230 231/* 232 * The function called at load/unload. 233 */ 234 235static int 236cmalloc_load(struct module *module, int cmd, void *arg) 237{ 238 int error = 0; 239 240 switch (cmd) { 241 case MOD_LOAD : 242 break; 243 case MOD_UNLOAD : 244 break; 245 default : 246 error = EOPNOTSUPP; 247 break; 248 } 249 return (error); 250} 251 252SYSCALL_MODULE(cmalloc_syscall, &cmalloc_offset, &cmalloc_sysent, 253 cmalloc_load, NULL); 254