/* * Copyright (c) 2013 * The President and Fellows of Harvard College. * Written by David A. Holland. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if 0 /* XXX: should add this to libc */ #include #else typedef unsigned bool; #define false 0 #define true 1 #endif #include "ops.h" #include "workloads.h" //////////////////////////////////////////////////////////// // support code static unsigned long getnum(const char *str) { #if 0 /* sigh */ unsigned long val; char *x; errno = 0; val = strtoul(str, &x, 0); if (errno) { err(1, "Invalid number %s", str); } if (strlen(x) > 0) { errx(1, "Invalid junk at end of number %s", str); } return val; #else return atoi(str); #endif } //////////////////////////////////////////////////////////// // standard sizes enum sizes { SIZE_ONE, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE, SIZE_LARGEPLUS, }; /* * This only returns the sizes that can be selected from the command * line. */ static enum sizes strtosize(const char *word) { if (!strcmp(word, "small")) { return SIZE_SMALL; } if (!strcmp(word, "medium")) { return SIZE_MEDIUM; } if (!strcmp(word, "large")) { return SIZE_LARGE; } errx(1, "Invalid size %s (try small, medium, or large)", word); /* XXX errx should be declared noreturn */ return SIZE_SMALL; } static enum sizes randsize(void) { switch (random() % 7) { case 0: return SIZE_ONE; case 1: case 2: case 3: return SIZE_SMALL; case 4: case 5: return SIZE_MEDIUM; case 6: #if 0 /* too annoying */ return SIZE_LARGE; #else return SIZE_MEDIUM; #endif } return SIZE_ONE; } static enum sizes nextsmallersize(enum sizes sz) { switch (sz) { case SIZE_ONE: break; case SIZE_SMALL: return SIZE_ONE; case SIZE_MEDIUM: return SIZE_SMALL; case SIZE_LARGE: return SIZE_MEDIUM; case SIZE_LARGEPLUS: return SIZE_LARGE; } assert(0); return SIZE_ONE; } static enum sizes nextlargersize(enum sizes sz) { switch (sz) { case SIZE_ONE: return SIZE_SMALL; case SIZE_SMALL: return SIZE_MEDIUM; case SIZE_MEDIUM: return SIZE_LARGE; case SIZE_LARGE: return SIZE_LARGEPLUS; case SIZE_LARGEPLUS: break; } assert(0); return SIZE_LARGEPLUS; } static unsigned sizeblocks(enum sizes sz) { /* * XXX for now hardwire things we know about SFS */ #define BLOCKSIZE /*SFS_BLOCKSIZE*/ 512 static const unsigned ndb = /*SFS_NDIRECT*/ 15; static const unsigned dbperidb = /*SFS_DBPERIDB*/ 128; switch (sz) { case SIZE_ONE: /* one block; 512 bytes */ return 1; case SIZE_SMALL: /* fits in direct blocks only; 7.5K */ return ndb; case SIZE_MEDIUM: /* uses an indirect block; ~40K */ return ndb + dbperidb / 2; case SIZE_LARGE: /* uses a double-indirect block; 4.2M */ return ndb + dbperidb + dbperidb * dbperidb / 2; case SIZE_LARGEPLUS: /* requires a triple-indirect block; 8.5M */ return ndb + dbperidb + dbperidb * dbperidb + dbperidb / 2; } assert(0); return 12; } static unsigned sizebytes(enum sizes sz) { return BLOCKSIZE * sizeblocks(sz); } //////////////////////////////////////////////////////////// // common suboperations static void file_randomwrite(struct file *f, enum sizes sz, unsigned startskip, unsigned endskip) { unsigned nblocks, nwrites, i; unsigned blocknum; off_t pos; nblocks = sizeblocks(sz); assert(nblocks > startskip + endskip); nwrites = nblocks/6; if (nwrites < 2) { nwrites = 2; } nblocks -= startskip + endskip; for (i=0; i static void wl_mktree_sub(unsigned testcode, unsigned depth) { unsigned i, numthings = 4; for (i=0; i 0) { return; } break; case 2: case 3: writenewfile(testcode, numhere/*filenum*/, SIZE_ONE); (*ct)++; numhere++; break; } } } static void rmrandtree_sub(unsigned depth, unsigned *ct, unsigned numthings) { unsigned numhere; numhere = 0; while (*ct < numthings) { switch (random() % 4) { case 0: (*ct)++; op_chdir(numhere/*filenum*/); rmrandtree_sub(depth + 1, ct, numthings); op_chdirup(); op_rmdir(numhere/*filenum*/); numhere++; break; case 1: if (depth > 0) { return; } break; case 2: case 3: op_unlink(numhere/*filenum*/); (*ct)++; numhere++; break; } } } void wl_mkrandtree(const char *seed) { unsigned testcode = 103; unsigned numthings, count; unsigned long seednum = getnum(seed); srandom(seednum); numthings = random() % 44 + 12; count = 0; mkrandtree_sub(testcode, 0, &count, numthings); } //////////////////////////////////////////////////////////// // deleting /* * Delete a file. (We have to create the file first and sync it.) */ void wl_rmfile(void) { unsigned testcode = 150; writenewfile(testcode, 0/*filenum*/, SIZE_ONE); writeemptyfile(1/*filenum*/); op_sync(); op_unlink(0/*filenum*/); } /* * Delete a directory. (We have to mkdir first and sync it.) */ void wl_rmdir(void) { op_mkdir(0/*filenum*/); writeemptyfile(1/*filenum*/); op_sync(); op_rmdir(0/*filenum*/); } /* * Delete a file, with delayed reclaim. * * (Should there be a form of this that syncs after unlinking but * before closing?) */ void wl_rmfiledelayed(void) { unsigned testcode = 151; struct file *f; writenewfile(testcode, 0/*filenum*/, SIZE_ONE); writeemptyfile(1/*filenum*/); op_sync(); f = op_open(testcode, 0/*filenum*/, 0); op_unlink(0/*filenum*/); op_close(f); } /* * Delete a file, with delayed reclaim, and append to the file before * reclaiming. */ void wl_rmfiledelayedappend(void) { unsigned testcode = 152; struct file *f; enum sizes sz = SIZE_SMALL; writenewfile(testcode, 0/*filenum*/, sz); writeemptyfile(1/*filenum*/); op_sync(); f = op_open(testcode, 0/*filenum*/, 0); op_unlink(0/*filenum*/); op_write(f, sizebytes(sz), 6 * BLOCKSIZE); op_close(f); } /* * Delete a directory, with delayed reclaim. */ void wl_rmdirdelayed(void) { struct dir *d; op_mkdir(0/*filenum*/); writeemptyfile(1/*filenum*/); op_sync(); d = op_opendir(0/*filenum*/); op_rmdir(0/*filenum*/); op_closedir(d); } /* * Delete many files. */ void wl_rmmanyfile(void) { unsigned testcode = 153; unsigned numfiles = 27; unsigned i; for (i=0; i