aboutsummaryrefslogtreecommitdiff
path: root/benchmarks/raidtest
diff options
context:
space:
mode:
authorDavid E. O'Brien <obrien@FreeBSD.org>2004-12-05 04:13:29 +0000
committerDavid E. O'Brien <obrien@FreeBSD.org>2004-12-05 04:13:29 +0000
commit6e46048a1b0f670c5000756952d61fd355c95eb7 (patch)
tree3042ea6aa25dbe5d15d16c4c8d16602965f9379a /benchmarks/raidtest
parent844009e13b00c1e938acdf9331ad5857124514ed (diff)
downloadports-6e46048a1b0f670c5000756952d61fd355c95eb7.tar.gz
ports-6e46048a1b0f670c5000756952d61fd355c95eb7.zip
pjd's src/tools/tools/raidtest as a port.
Notes
Notes: svn path=/head/; revision=123171
Diffstat (limited to 'benchmarks/raidtest')
-rw-r--r--benchmarks/raidtest/Makefile25
-rw-r--r--benchmarks/raidtest/files/Makefile8
-rw-r--r--benchmarks/raidtest/files/raidtest.c418
-rw-r--r--benchmarks/raidtest/pkg-descr29
4 files changed, 480 insertions, 0 deletions
diff --git a/benchmarks/raidtest/Makefile b/benchmarks/raidtest/Makefile
new file mode 100644
index 000000000000..473ffd444c9e
--- /dev/null
+++ b/benchmarks/raidtest/Makefile
@@ -0,0 +1,25 @@
+# ex:ts=8
+# Ports collection makefile for: raidtest
+# Date created: Sat Oct 16, 2004
+# Whom: David O'Brien (obrien@FreeBSD.org)
+#
+# $FreeBSD$
+#
+
+PORTNAME= raidtest
+PORTVERSION= 1.0
+CATEGORIES= benchmarks
+
+MAINTAINER= pjd@FreeBSD.org
+COMMENT= used to test performance of storage devices
+
+NO_WRKSUBDIR= yes
+PLIST_FILES= bin/raidtest
+
+do-fetch:
+
+do-extract:
+ ${MKDIR} ${WRKDIR}
+ ${LN} -sf ${FILESDIR}/${PORTNAME}.c ${FILESDIR}/Makefile ${WRKDIR}
+
+.include <bsd.port.mk>
diff --git a/benchmarks/raidtest/files/Makefile b/benchmarks/raidtest/files/Makefile
new file mode 100644
index 000000000000..be5ebd287f16
--- /dev/null
+++ b/benchmarks/raidtest/files/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= raidtest
+NOMAN= true
+WARNS?= 6
+BINDIR?= ${PREFIX}/bin
+
+.include <bsd.prog.mk>
diff --git a/benchmarks/raidtest/files/raidtest.c b/benchmarks/raidtest/files/raidtest.c
new file mode 100644
index 000000000000..f5eb962f9d72
--- /dev/null
+++ b/benchmarks/raidtest/files/raidtest.c
@@ -0,0 +1,418 @@
+/*-
+ * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+
+#define DEFAULT_DATA_FILE "raidtest.data"
+#define MAX_IO_LENGTH 131072
+#define IO_TYPE_READ 0
+#define IO_TYPE_WRITE 1
+
+struct ioreq {
+ off_t iorq_offset;
+ unsigned iorq_length;
+ unsigned iorq_type;
+};
+
+struct iorec {
+ uint64_t iorc_offset;
+ uint32_t iorc_length;
+ uint8_t iorc_type;
+};
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s genfile [-frw] <-s mediasize> [-S sectorsize] <-n nrequests> [file]\n", getprogname());
+ fprintf(stderr, " %s test [-rw] <-d device> [-n processes] [file]\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static unsigned
+gen_size(unsigned secsize)
+{
+ unsigned maxsec;
+
+ maxsec = MAX_IO_LENGTH / secsize;
+ return (secsize * ((arc4random() % maxsec) + 1));
+}
+
+static int
+read_ioreq(int fd, struct ioreq *iorq)
+{
+ struct iorec iorc;
+
+ if (read(fd, &iorc, sizeof(iorc)) != sizeof(iorc))
+ return (1);
+ iorq->iorq_offset = le64dec(&iorc.iorc_offset);
+ iorq->iorq_length = le32dec(&iorc.iorc_length);
+ iorq->iorq_type = iorc.iorc_type;
+ return (0);
+}
+
+static int
+write_ioreq(int fd, struct ioreq *iorq)
+{
+ struct iorec iorc;
+
+ le64enc(&iorc.iorc_offset, iorq->iorq_offset);
+ le32enc(&iorc.iorc_length, iorq->iorq_length);
+ iorc.iorc_type = iorq->iorq_type;
+ return (write(fd, &iorc, sizeof(iorc)) != sizeof(iorc));
+}
+
+static void
+raidtest_genfile(int argc, char *argv[])
+{
+ uintmax_t i, nreqs, mediasize, nsectors, nbytes, nrreqs, nwreqs;
+ unsigned secsize, maxsec;
+ const char *file = NULL;
+ struct ioreq iorq;
+ int ch, fd, flags, rdonly, wronly;
+
+ nreqs = 0;
+ mediasize = 0;
+ secsize = 512;
+ rdonly = wronly = 0;
+ flags = O_WRONLY | O_CREAT | O_EXCL | O_TRUNC;
+ while ((ch = getopt(argc, argv, "fn:rs:S:w")) != -1) {
+ switch (ch) {
+ case 'f':
+ flags &= ~O_EXCL;
+ break;
+ case 'n':
+ errno = 0;
+ nreqs = strtoumax(optarg, NULL, 0);
+ if (errno != 0) {
+ err(EXIT_FAILURE,
+ "Invalid value for '%c' argument.", ch);
+ }
+ break;
+ case 'r':
+ rdonly = 1;
+ break;
+ case 's':
+ errno = 0;
+ mediasize = strtoumax(optarg, NULL, 0);
+ if (errno != 0) {
+ err(EXIT_FAILURE,
+ "Invalid value for '%c' argument.", ch);
+ }
+ break;
+ case 'S':
+ errno = 0;
+ secsize = strtoul(optarg, NULL, 0);
+ if (errno != 0) {
+ err(EXIT_FAILURE,
+ "Invalid value for '%c' argument.", ch);
+ }
+ break;
+ case 'w':
+ wronly = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (nreqs == 0)
+ errx(EXIT_FAILURE, "Option '%c' not specified.", 'n');
+ if (mediasize == 0)
+ errx(EXIT_FAILURE, "Option '%c' not specified.", 's');
+ if (rdonly && wronly) {
+ errx(EXIT_FAILURE, "Both '%c' and '%c' options were specified.",
+ 'r', 'w');
+ }
+ if (argc == 0)
+ file = DEFAULT_DATA_FILE;
+ else if (argc == 1)
+ file = argv[0];
+ else
+ usage();
+ fd = open(file, flags, 0644);
+ if (fd < 0)
+ err(EXIT_FAILURE, "Cannot create '%s' file", file);
+ nsectors = mediasize / secsize;
+ nbytes = nrreqs = nwreqs = 0;
+ for (i = 0; i < nreqs; i++) {
+ /* Generate I/O request length. */
+ iorq.iorq_length = gen_size(secsize);
+ /* Generate I/O request offset. */
+ maxsec = nsectors - (iorq.iorq_length / secsize);
+ iorq.iorq_offset = (arc4random() % maxsec) * secsize;
+ /* Generate I/O request type. */
+ if (rdonly)
+ iorq.iorq_type = IO_TYPE_READ;
+ else if (wronly)
+ iorq.iorq_type = IO_TYPE_WRITE;
+ else
+ iorq.iorq_type = arc4random() % 2;
+ nbytes += iorq.iorq_length;
+ switch (iorq.iorq_type) {
+ case IO_TYPE_READ:
+ nrreqs++;
+ break;
+ case IO_TYPE_WRITE:
+ nwreqs++;
+ break;
+ }
+ if (write_ioreq(fd, &iorq) != 0) {
+ unlink(file);
+ err(EXIT_FAILURE, "Error while writing");
+ }
+ }
+ printf("File %s generated.\n", file);
+ printf("Number of READ requests: %ju.\n", nrreqs);
+ printf("Number of WRITE requests: %ju.\n", nwreqs);
+ printf("Number of bytes to transmit: %ju.\n", nbytes);
+}
+
+static void
+test_start(int fd, struct ioreq *iorqs, uintmax_t nreqs)
+{
+ unsigned char data[MAX_IO_LENGTH];
+ struct ioreq *iorq;
+ uintmax_t n;
+
+ for (n = 0; n < nreqs; n++) {
+ iorq = &iorqs[n];
+ switch (iorq->iorq_type) {
+ case IO_TYPE_READ:
+ if (pread(fd, data, iorq->iorq_length,
+ iorq->iorq_offset) != (ssize_t)iorq->iorq_length) {
+ fprintf(stderr,
+ "%u: read(%jd, %u) failed: %s.\n", getpid(),
+ (intmax_t)iorq->iorq_offset,
+ iorq->iorq_length, strerror(errno));
+ }
+ break;
+ case IO_TYPE_WRITE:
+ if (pwrite(fd, data, iorq->iorq_length,
+ iorq->iorq_offset) != (ssize_t)iorq->iorq_length) {
+ fprintf(stderr,
+ "%u: write(%jd, %u) failed: %s.\n",
+ getpid(), (intmax_t)iorq->iorq_offset,
+ iorq->iorq_length, strerror(errno));
+ }
+ break;
+ default:
+ fprintf(stderr, "%u: Invalid request type: %u.\n",
+ getpid(), iorq->iorq_type);
+ break;
+ }
+ }
+}
+
+static void
+show_stats(long secs, uintmax_t nbytes, uintmax_t nreqs)
+{
+
+ printf("Bytes per second: %ju\n", nbytes / secs);
+ printf("Requests per second: %ju\n", nreqs / secs);
+}
+
+static void
+raidtest_test(int argc, char *argv[])
+{
+ uintmax_t i, nbytes, nreqs, nrreqs, nwreqs, reqs_per_proc, nstart;
+ const char *dev, *file = NULL;
+ struct timeval tstart, tend;
+ struct ioreq *iorqs;
+ unsigned nprocs;
+ struct stat sb;
+ pid_t *procs;
+ int ch, fdd, fdf, j, rdonly, wronly;
+
+ dev = NULL;
+ nprocs = 1;
+ rdonly = wronly = 0;
+ while ((ch = getopt(argc, argv, "d:n:rvw")) != -1) {
+ switch (ch) {
+ case 'd':
+ dev = optarg;
+ break;
+ case 'n':
+ errno = 0;
+ nprocs = strtoul(optarg, NULL, 0);
+ if (errno != 0) {
+ err(EXIT_FAILURE,
+ "Invalid value for '%c' argument.", ch);
+ }
+ break;
+ case 'r':
+ rdonly = 1;
+ break;
+ case 'w':
+ wronly = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (dev == NULL)
+ errx(EXIT_FAILURE, "Option '%c' not specified.", 'd');
+ if (nprocs < 1)
+ errx(EXIT_FAILURE, "Invalid number of processes");
+ if (rdonly && wronly) {
+ errx(EXIT_FAILURE, "Both '%c' and '%c' options were specified.",
+ 'r', 'w');
+ }
+ if (argc == 0)
+ file = DEFAULT_DATA_FILE;
+ else if (argc == 1)
+ file = argv[0];
+ else
+ usage();
+ fdf = open(file, O_RDONLY);
+ if (fdf < 0)
+ err(EXIT_FAILURE, "Cannot open '%s' file", file);
+ if (fstat(fdf, &sb) < 0)
+ err(EXIT_FAILURE, "Cannot stat '%s' file", file);
+ if ((sb.st_size % sizeof(struct iorec)) != 0)
+ err(EXIT_FAILURE, "Invalid size of '%s' file", file);
+ fdd = open(dev, O_RDWR | O_DIRECT);
+ if (fdd < 0)
+ err(EXIT_FAILURE, "Cannot open '%s' device", file);
+ procs = malloc(sizeof(pid_t) * nprocs);
+ if (procs == NULL) {
+ close(fdf);
+ close(fdd);
+ errx(EXIT_FAILURE, "Cannot allocate %u bytes of memory.",
+ sizeof(pid_t) * nprocs);
+ }
+ iorqs =
+ malloc((sb.st_size / sizeof(struct iorec)) * sizeof(struct ioreq));
+ if (iorqs == NULL) {
+ close(fdf);
+ close(fdd);
+ free(procs);
+ errx(EXIT_FAILURE, "Cannot allocate %jd bytes of memory.",
+ (intmax_t)(sb.st_size / sizeof(struct iorec)) *
+ sizeof(struct ioreq));
+ }
+ nreqs = sb.st_size / sizeof(struct iorec);
+ nbytes = nrreqs = nwreqs = 0;
+ for (i = 0; i < nreqs; i++) {
+ if (read_ioreq(fdf, &iorqs[i]))
+ err(EXIT_FAILURE, "Error while reading");
+ if (rdonly)
+ iorqs[i].iorq_type = IO_TYPE_READ;
+ else if (wronly)
+ iorqs[i].iorq_type = IO_TYPE_WRITE;
+ nbytes += iorqs[i].iorq_length;
+ switch (iorqs[i].iorq_type) {
+ case IO_TYPE_READ:
+ nrreqs++;
+ break;
+ case IO_TYPE_WRITE:
+ nwreqs++;
+ break;
+ default:
+ fprintf(stderr, "Invalid request type: %u.\n",
+ iorqs[i].iorq_type);
+ break;
+ }
+ }
+ close(fdf);
+ printf("Read %ju requests from %s.\n", nreqs, file);
+ printf("Number of READ requests: %ju.\n", nrreqs);
+ printf("Number of WRITE requests: %ju.\n", nwreqs);
+ printf("Number of bytes to transmit: %ju.\n", nbytes);
+ printf("Number of processes: %u.\n", nprocs);
+ fflush(stdout);
+ reqs_per_proc = nreqs / nprocs;
+ nstart = 0;
+ gettimeofday(&tstart, NULL);
+ for (j = 0; j < (int)nprocs; j++) {
+ procs[i] = fork();
+ switch (procs[i]) {
+ case 0:
+ free(procs);
+ test_start(fdd, &iorqs[nstart], reqs_per_proc);
+ free(iorqs);
+ close(fdd);
+ exit(EXIT_SUCCESS);
+ case -1:
+ fprintf(stderr, "Cannot create process %u: %s\n",
+ (unsigned)i, strerror(errno));
+ for (j--; j >= 0; j--)
+ kill(procs[j], SIGKILL);
+ free(procs);
+ free(iorqs);
+ close(fdd);
+ exit(EXIT_FAILURE);
+ }
+ nstart += reqs_per_proc;
+ }
+ free(iorqs);
+ free(procs);
+ for (j = 0; j < (int)nprocs; j++) {
+ int status;
+
+ wait(&status);
+ }
+ gettimeofday(&tend, NULL);
+ show_stats(tend.tv_sec - tstart.tv_sec, nbytes, nreqs);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc < 2)
+ usage();
+ argc--;
+ argv++;
+ if (strcmp(argv[0], "genfile") == 0)
+ raidtest_genfile(argc, argv);
+ else if (strcmp(argv[0], "test") == 0)
+ raidtest_test(argc, argv);
+ else
+ usage();
+ exit(EXIT_SUCCESS);
+}
diff --git a/benchmarks/raidtest/pkg-descr b/benchmarks/raidtest/pkg-descr
new file mode 100644
index 000000000000..82296c96eee4
--- /dev/null
+++ b/benchmarks/raidtest/pkg-descr
@@ -0,0 +1,29 @@
+$FreeBSD$
+
+This utility can be used to test performance of storage devices.
+First, one need to generate file with I/O operations:
+
+ # set mediasize=`diskinfo /dev/<device> | awk '{print $3}'`
+ # set sectorsize=`diskinfo /dev/<device> | awk '{print $2}'`
+ # raidtest genfile -s $mediasize -S $sectorsize -n 50000
+
+It will generate test which contains 50000 I/O requests with random
+size and random offset. Size is a multiple of sectorsize, but less than or
+equal to 128kB (maxium size of I/O request). I/O request type (READ or WRITE)
+is random as well.
+All test data are stored in 'raidtest.data' file in current working directory.
+
+To run test, one should type:
+
+ # raidtest test -d /dev/<device> -n 10
+
+This command will read test data from 'raidtest.data' file, run 10 processes
+which will be used to send requests to the given device in parallel.
+When test is finished you will see statistics:
+
+ Bytes per second: <x>
+ Requests per second: <y>
+
+If you compare performance of two storage devices, use the same data file!
+
+ -- Pawel Jakub Dawidek <pjd@FreeBSD.org>