1178823Sadrian/* 2178823Sadrian * Copyright (c) 2002 Adrian Chadd <adrian@FreeBSD.org>. 3178823Sadrian * All rights reserved. 4178823Sadrian * 5178823Sadrian * This software was developed for the FreeBSD Project by Marshall 6178823Sadrian * Kirk McKusick and Network Associates Laboratories, the Security 7178823Sadrian * Research Division of Network Associates, Inc. under DARPA/SPAWAR 8178823Sadrian * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS 9178823Sadrian * research program. 10178823Sadrian * 11178823Sadrian * Copyright (c) 1980, 1989, 1993 12178823Sadrian * The Regents of the University of California. All rights reserved. 13178823Sadrian * 14178823Sadrian * Redistribution and use in source and binary forms, with or without 15178823Sadrian * modification, are permitted provided that the following conditions 16178823Sadrian * are met: 17178823Sadrian * 1. Redistributions of source code must retain the above copyright 18178823Sadrian * notice, this list of conditions and the following disclaimer. 19178823Sadrian * 2. Redistributions in binary form must reproduce the above copyright 20178823Sadrian * notice, this list of conditions and the following disclaimer in the 21178823Sadrian * documentation and/or other materials provided with the distribution. 22178823Sadrian * 4. Neither the name of the University nor the names of its contributors 23178823Sadrian * may be used to endorse or promote products derived from this software 24178823Sadrian * without specific prior written permission. 25178823Sadrian * 26178823Sadrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27178823Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28178823Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29178823Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30178823Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31178823Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32178823Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33178823Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34178823Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35178823Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36178823Sadrian * SUCH DAMAGE. 37178823Sadrian */ 38178823Sadrian 39178823Sadrian#include <sys/cdefs.h> 40178823Sadrian__FBSDID("$FreeBSD$"); 41178823Sadrian 42178823Sadrian#include <sys/types.h> 43282843Sngie#include <sys/disk.h> 44282843Sngie#include <sys/ioctl.h> 45178823Sadrian#include <sys/stat.h> 46282843Sngie#include <sys/time.h> 47178823Sadrian#include <aio.h> 48282843Sngie#include <assert.h> 49282843Sngie#include <ctype.h> 50282843Sngie#include <err.h> 51178823Sadrian#include <fcntl.h> 52282843Sngie#include <stdint.h> 53282843Sngie#include <stdio.h> 54282843Sngie#include <stdlib.h> 55178823Sadrian#include <string.h> 56282843Sngie#include <time.h> 57282843Sngie#include <unistd.h> 58178823Sadrian 59178823Sadrian/* 60178823Sadrian * This is a bit of a quick hack to do parallel IO testing through POSIX AIO. 61178823Sadrian * Its specifically designed to work under FreeBSD and its derivatives; 62178823Sadrian * note how I cheat by using aio_waitcomplete(). 63178823Sadrian * 64178823Sadrian * TODO: 65178823Sadrian * 66178823Sadrian * + Add write support; so we can make sure we're not hitting throughput issues 67178823Sadrian * with read/modify/write of entire tracks of the disk 68178823Sadrian * + Add in per-op stats - time and offset - so one could start mapping out 69178823Sadrian * the speed hotspots of the disk 70178823Sadrian * + Add in different distributions - random, normal, left/right skewed normal, 71178823Sadrian * zipf, etc - and perhaps add the ability to run concurrent distributions 72178823Sadrian * (so a normal and a zipf; and also a random read; zipf write, etc.) 73178823Sadrian * 74178823Sadrian * Adrian Chadd <adrian@creative.net.au> 75178823Sadrian */ 76178823Sadrian 77179017Sadriantypedef enum { 78179017Sadrian IOT_NONE = 0x00, 79179017Sadrian IOT_READ = 0x01, 80179017Sadrian IOT_WRITE = 0x02 81179017Sadrian} iot_t; 82179017Sadrian 83178823Sadrianstatic size_t 84178823Sadriandisk_getsize(int fd) 85178823Sadrian{ 86178823Sadrian off_t mediasize; 87178823Sadrian 88282843Sngie if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) 89282843Sngie err(1, "ioctl(DIOCGMEDIASIZE)"); 90282843Sngie return (mediasize); 91178823Sadrian} 92178823Sadrian 93282843Sngiestatic iot_t 94179017Sadrianchoose_aio(iot_t iomask) 95179017Sadrian{ 96179017Sadrian /* choose a random read or write event, limited by the mask */ 97179017Sadrian if (iomask == IOT_READ) 98179017Sadrian return IOT_READ; 99179017Sadrian else if (iomask == IOT_WRITE) 100179017Sadrian return IOT_WRITE; 101179017Sadrian return (random() & 0x01 ? IOT_READ : IOT_WRITE); 102179017Sadrian} 103178823Sadrian 104282843Sngiestatic void 105179017Sadrianset_aio(struct aiocb *a, iot_t iot, int fd, off_t offset, int size, char *buf) 106178823Sadrian{ 107178823Sadrian int r; 108178823Sadrian bzero(a, sizeof(*a)); 109178823Sadrian a->aio_fildes = fd; 110178823Sadrian a->aio_nbytes = size; 111178823Sadrian a->aio_offset = offset; 112178823Sadrian a->aio_buf = buf; 113179017Sadrian if (iot == IOT_READ) 114179017Sadrian r = aio_read(a); 115179017Sadrian else 116179017Sadrian r = aio_write(a); 117282843Sngie if (r != 0) 118282843Sngie err(1, "set_aio call failed"); 119178823Sadrian} 120178823Sadrian 121178823Sadrianint 122178823Sadrianmain(int argc, char *argv[]) 123178823Sadrian{ 124178823Sadrian int fd; 125178823Sadrian struct stat sb; 126178823Sadrian struct aiocb *aio; 127178823Sadrian char **abuf; 128178823Sadrian const char *fn; 129178823Sadrian int aio_len; 130178823Sadrian int io_size, nrun; 131178823Sadrian off_t file_size, offset; 132178823Sadrian struct aiocb *a; 133178823Sadrian int i, n; 134282843Sngie struct timeval st, et, rt; 135282843Sngie float f_rt; 136179017Sadrian iot_t iowhat; 137178823Sadrian 138178823Sadrian 139179017Sadrian if (argc < 6) { 140282843Sngie printf("Usage: %s <file> <io size> <number of runs> <concurrency> <ro|wo|rw>\n", 141282843Sngie argv[0]); 142178823Sadrian exit(1); 143178823Sadrian } 144178823Sadrian 145178823Sadrian fn = argv[1]; 146178823Sadrian io_size = atoi(argv[2]); 147282843Sngie if (io_size <= 0) 148282843Sngie errx(1, "the I/O size must be >0"); 149178823Sadrian nrun = atoi(argv[3]); 150282843Sngie if (nrun <= 0) 151282843Sngie errx(1, "the number of runs must be >0"); 152178823Sadrian aio_len = atoi(argv[4]); 153282843Sngie if (aio_len <= 0) 154282843Sngie errx(1, "AIO concurrency must be >0"); 155282843Sngie if (strcmp(argv[5], "ro") == 0) 156179017Sadrian iowhat = IOT_READ; 157282843Sngie else if (strcmp(argv[5], "rw") == 0) 158179017Sadrian iowhat = IOT_READ | IOT_WRITE; 159282843Sngie else if (strcmp(argv[5], "wo") == 0) 160179017Sadrian iowhat = IOT_WRITE; 161282843Sngie else 162282843Sngie errx(1, "the I/O type needs to be \"ro\", \"rw\", or \"wo\"!\n"); 163178823Sadrian 164178823Sadrian /* 165178823Sadrian * Random returns values between 0 and (2^32)-1; only good for 4 gig. 166178823Sadrian * Lets instead treat random() as returning a block offset w/ block size 167178823Sadrian * being "io_size", so we can handle > 4 gig files. 168178823Sadrian */ 169179017Sadrian if (iowhat == IOT_READ) 170179017Sadrian fd = open(fn, O_RDONLY | O_DIRECT); 171179017Sadrian else if (iowhat == IOT_WRITE) 172179017Sadrian fd = open(fn, O_WRONLY | O_DIRECT); 173179017Sadrian else 174179017Sadrian fd = open(fn, O_RDWR | O_DIRECT); 175178823Sadrian 176282843Sngie if (fd < 0) 177282843Sngie err(1, "open failed"); 178282843Sngie if (fstat(fd, &sb) < 0) 179282843Sngie err(1, "fstat failed"); 180178823Sadrian if (S_ISREG(sb.st_mode)) { 181178823Sadrian file_size = sb.st_size; 182178823Sadrian } else if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 183178823Sadrian file_size = disk_getsize(fd); 184282843Sngie } else 185282843Sngie errx(1, "unknown file type"); 186282843Sngie if (file_size <= 0) 187282843Sngie errx(1, "path provided too small"); 188282843Sngie 189253442Skevlo printf("File: %s; File size %jd bytes\n", fn, (intmax_t)file_size); 190178823Sadrian 191178823Sadrian aio = calloc(aio_len, sizeof(struct aiocb)); 192178823Sadrian abuf = calloc(aio_len, sizeof(char *)); 193282843Sngie for (i = 0; i < aio_len; i++) 194178823Sadrian abuf[i] = calloc(1, io_size * sizeof(char)); 195178823Sadrian 196178823Sadrian /* Fill with the initial contents */ 197282843Sngie gettimeofday(&st, NULL); 198178823Sadrian for (i = 0; i < aio_len; i++) { 199282843Sngie offset = random() % (file_size / io_size); 200282843Sngie offset *= io_size; 201179017Sadrian set_aio(aio + i, choose_aio(iowhat), fd, offset, io_size, abuf[i]); 202178823Sadrian } 203178823Sadrian 204178823Sadrian for (i = 0; i < nrun; i++) { 205178823Sadrian aio_waitcomplete(&a, NULL); 206178823Sadrian n = a - aio; 207178823Sadrian assert(n < aio_len); 208178823Sadrian assert(n >= 0); 209282843Sngie offset = random() % (file_size / io_size); 210282843Sngie offset *= io_size; 211179017Sadrian set_aio(aio + n, choose_aio(iowhat), fd, offset, io_size, abuf[n]); 212178823Sadrian } 213178823Sadrian 214282843Sngie gettimeofday(&et, NULL); 215282843Sngie timersub(&et, &st, &rt); 216282843Sngie f_rt = ((float) (rt.tv_usec)) / 1000000.0; 217282843Sngie f_rt += (float) (rt.tv_sec); 218282843Sngie printf("Runtime: %.2f seconds, ", f_rt); 219282843Sngie printf("Op rate: %.2f ops/sec, ", ((float) (nrun)) / f_rt); 220282843Sngie printf("Avg transfer rate: %.2f bytes/sec\n", ((float) (nrun)) * ((float)io_size) / f_rt); 221178823Sadrian 222178823Sadrian 223178823Sadrian 224178823Sadrian exit(0); 225178823Sadrian} 226