diff options
Diffstat (limited to 'extras/test/rdd.c')
-rw-r--r-- | extras/test/rdd.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/extras/test/rdd.c b/extras/test/rdd.c new file mode 100644 index 000000000..3636c636d --- /dev/null +++ b/extras/test/rdd.c @@ -0,0 +1,457 @@ +/* + Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <argp.h> + +#define TWO_POWER(power) (2UL << (power)) + +#define RDD_INTEGER_VALUE ((TWO_POWER ((sizeof (int) * 8))) - 1) + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +struct rdd_file { + char path[UNIX_PATH_MAX]; + struct stat st; + int fd; +}; + +struct rdd_config { + long iters; + long max_ops_per_seq; + size_t max_bs; + size_t min_bs; + int thread_count; + pthread_t *threads; + pthread_barrier_t barrier; + pthread_mutex_t lock; + struct rdd_file in_file; + struct rdd_file out_file; +}; +static struct rdd_config rdd_config; + +enum rdd_keys { + RDD_MIN_BS_KEY = 1, + RDD_MAX_BS_KEY, +}; + +static error_t +rdd_parse_opts (int key, char *arg, + struct argp_state *_state) +{ + switch (key) { + case 'o': + { + int len = 0; + len = strlen (arg); + if (len > UNIX_PATH_MAX) { + fprintf (stderr, "output file name too long (%s)\n", arg); + return -1; + } + + strncpy (rdd_config.out_file.path, arg, len); + } + break; + + case 'i': + { + int len = 0; + len = strlen (arg); + if (len > UNIX_PATH_MAX) { + fprintf (stderr, "input file name too long (%s)\n", arg); + return -1; + } + + strncpy (rdd_config.in_file.path, arg, len); + } + break; + + case RDD_MIN_BS_KEY: + { + char *tmp = NULL; + long bs = 0; + bs = strtol (arg, &tmp, 10); + if ((bs == LONG_MAX) || (bs == LONG_MIN) || (tmp && *tmp)) { + fprintf (stderr, "invalid argument for minimum block size (%s)\n", arg); + return -1; + } + + rdd_config.min_bs = bs; + } + break; + + case RDD_MAX_BS_KEY: + { + char *tmp = NULL; + long bs = 0; + bs = strtol (arg, &tmp, 10); + if ((bs == LONG_MAX) || (bs == LONG_MIN) || (tmp && *tmp)) { + fprintf (stderr, "invalid argument for maximum block size (%s)\n", arg); + return -1; + } + + rdd_config.max_bs = bs; + } + break; + + case 'r': + { + char *tmp = NULL; + long iters = 0; + iters = strtol (arg, &tmp, 10); + if ((iters == LONG_MAX) || (iters == LONG_MIN) || (tmp && *tmp)) { + fprintf (stderr, "invalid argument for iterations (%s)\n", arg); + return -1; + } + + rdd_config.iters = iters; + } + break; + + case 'm': + { + char *tmp = NULL; + long max_ops = 0; + max_ops = strtol (arg, &tmp, 10); + if ((max_ops == LONG_MAX) || (max_ops == LONG_MIN) || (tmp && *tmp)) { + fprintf (stderr, "invalid argument for max-ops (%s)\n", arg); + return -1; + } + + rdd_config.max_ops_per_seq = max_ops; + } + break; + + case 't': + { + char *tmp = NULL; + long threads = 0; + threads = strtol (arg, &tmp, 10); + if ((threads == LONG_MAX) || (threads == LONG_MIN) || (tmp && *tmp)) { + fprintf (stderr, "invalid argument for thread count (%s)\n", arg); + return -1; + } + + rdd_config.thread_count = threads; + } + break; + + case ARGP_KEY_NO_ARGS: + break; + case ARGP_KEY_ARG: + break; + case ARGP_KEY_END: + if (_state->argc == 1) { + argp_usage (_state); + } + + } + + return 0; +} + +static struct argp_option rdd_options[] = { + {"if", 'i', "INPUT_FILE", 0, "input-file"}, + {"of", 'o', "OUTPUT_FILE", 0, "output-file"}, + {"threads", 't', "COUNT", 0, "number of threads to spawn (defaults to 2)"}, + {"min-bs", RDD_MIN_BS_KEY, "MIN_BLOCK_SIZE", 0, + "Minimum block size in bytes (defaults to 1024)"}, + {"max-bs", RDD_MAX_BS_KEY, "MAX_BLOCK_SIZE", 0, + "Maximum block size in bytes (defaults to 4096)"}, + {"iters", 'r', "ITERS", 0, + "Number of read-write sequences (defaults to 1000000)"}, + {"max-ops", 'm', "MAXOPS", 0, + "maximum number of read-writes to be performed in a sequence (defaults to 1)"}, + {0, 0, 0, 0, 0} +}; + +static struct argp argp = { + rdd_options, + rdd_parse_opts, + "", + "random dd - tool to do a sequence of random block-sized continuous read writes starting at a random offset" +}; + + +static void +rdd_default_config (void) +{ + rdd_config.thread_count = 2; + rdd_config.iters = 1000000; + rdd_config.max_bs = 4096; + rdd_config.min_bs = 1024; + rdd_config.in_file.fd = rdd_config.out_file.fd = -1; + rdd_config.max_ops_per_seq = 1; + + return; +} + + +static char +rdd_valid_config (void) +{ + char ret = 1; + int fd = -1; + + fd = open (rdd_config.in_file.path, O_RDONLY); + if (fd == -1) { + ret = 0; + goto out; + } + close (fd); + + if (rdd_config.min_bs > rdd_config.max_bs) { + ret = 0; + goto out; + } + + if (strlen (rdd_config.out_file.path) == 0) { + sprintf (rdd_config.out_file.path, "%s.rddout", rdd_config.in_file.path); + } + +out: + return ret; +} + + +static void * +rdd_read_write (void *arg) +{ + int i = 0, ret = 0; + size_t bs = 0; + off_t offset = 0; + long rand = 0; + long max_ops = 0; + char *buf = NULL; + + buf = CALLOC (1, rdd_config.max_bs); + if (!buf) { + fprintf (stderr, "calloc failed (%s)\n", strerror (errno)); + ret = -1; + goto out; + } + + for (i = 0; i < rdd_config.iters; i++) + { + pthread_mutex_lock (&rdd_config.lock); + { + int bytes = 0; + rand = random (); + + if (rdd_config.min_bs == rdd_config.max_bs) { + bs = rdd_config.max_bs; + } else { + bs = rdd_config.min_bs + (rand % (rdd_config.max_bs - rdd_config.min_bs)); + } + + offset = rand % rdd_config.in_file.st.st_size; + max_ops = rand % rdd_config.max_ops_per_seq; + if (!max_ops) { + max_ops ++; + } + + ret = lseek (rdd_config.in_file.fd, offset, SEEK_SET); + if (ret != offset) { + fprintf (stderr, "lseek failed (%s)\n", strerror (errno)); + ret = -1; + goto unlock; + } + + ret = lseek (rdd_config.out_file.fd, offset, SEEK_SET); + if (ret != offset) { + fprintf (stderr, "lseek failed (%s)\n", strerror (errno)); + ret = -1; + goto unlock; + } + + while (max_ops--) + { + bytes = read (rdd_config.in_file.fd, buf, bs); + if (!bytes) { + break; + } + + if (bytes == -1) { + fprintf (stderr, "read failed (%s)\n", strerror (errno)); + ret = -1; + goto unlock; + } + + if (write (rdd_config.out_file.fd, buf, bytes) != bytes) { + fprintf (stderr, "write failed (%s)\n", strerror (errno)); + ret = -1; + goto unlock; + } + } + } + unlock: + pthread_mutex_unlock (&rdd_config.lock); + if (ret == -1) { + goto out; + } + ret = 0; + } +out: + free (buf); + pthread_barrier_wait (&rdd_config.barrier); + + return NULL; +} + + +static int +rdd_spawn_threads (void) +{ + int i = 0, ret = -1, fd = -1; + char buf[4096]; + + fd = open (rdd_config.in_file.path, O_RDONLY); + if (fd < 0) { + fprintf (stderr, "cannot open %s (%s)\n", rdd_config.in_file.path, strerror (errno)); + ret = -1; + goto out; + } + ret = fstat (fd, &rdd_config.in_file.st); + if (ret != 0) { + close (fd); + fprintf (stderr, "cannot stat %s (%s)\n", rdd_config.in_file.path, strerror (errno)); + ret = -1; + goto out; + } + rdd_config.in_file.fd = fd; + + fd = open (rdd_config.out_file.path, O_WRONLY | O_CREAT, S_IRWXU | S_IROTH); + if (fd < 0) { + close (rdd_config.in_file.fd); + rdd_config.in_file.fd = -1; + fprintf (stderr, "cannot open %s (%s)\n", rdd_config.out_file.path, strerror (errno)); + ret = -1; + goto out; + } + rdd_config.out_file.fd = fd; + + while ((ret = read (rdd_config.in_file.fd, buf, 4096)) > 0) { + if (write (rdd_config.out_file.fd, buf, ret) != ret) { + fprintf (stderr, "write failed (%s)\n", strerror (errno)); + close (rdd_config.in_file.fd); + close (rdd_config.out_file.fd); + rdd_config.in_file.fd = rdd_config.out_file.fd = -1; + ret = -1; + goto out; + } + } + + rdd_config.threads = CALLOC (rdd_config.thread_count, sizeof (pthread_t)); + if (rdd_config.threads == NULL) { + fprintf (stderr, "calloc() failed (%s)\n", strerror (errno)); + + ret = -1; + close (rdd_config.in_file.fd); + close (rdd_config.out_file.fd); + rdd_config.in_file.fd = rdd_config.out_file.fd = -1; + goto out; + } + + ret = pthread_barrier_init (&rdd_config.barrier, NULL, rdd_config.thread_count + 1); + if (ret != 0) { + fprintf (stderr, "pthread_barrier_init() failed (%s)\n", strerror (ret)); + + free (rdd_config.threads); + close (rdd_config.in_file.fd); + close (rdd_config.out_file.fd); + rdd_config.in_file.fd = rdd_config.out_file.fd = -1; + ret = -1; + goto out; + } + + ret = pthread_mutex_init (&rdd_config.lock, NULL); + if (ret != 0) { + fprintf (stderr, "pthread_mutex_init() failed (%s)\n", strerror (ret)); + + free (rdd_config.threads); + pthread_barrier_destroy (&rdd_config.barrier); + close (rdd_config.in_file.fd); + close (rdd_config.out_file.fd); + rdd_config.in_file.fd = rdd_config.out_file.fd = -1; + ret = -1; + goto out; + } + + for (i = 0; i < rdd_config.thread_count; i++) + { + ret = pthread_create (&rdd_config.threads[i], NULL, rdd_read_write, NULL); + if (ret != 0) { + fprintf (stderr, "pthread_create failed (%s)\n", strerror (errno)); + exit (1); + } + } + +out: + return ret; +} + + +static void +rdd_wait_for_completion (void) +{ + pthread_barrier_wait (&rdd_config.barrier); +} + + +int +main (int argc, char *argv[]) +{ + int ret = -1; + + rdd_default_config (); + + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret != 0) { + ret = -1; + fprintf (stderr, "%s: argp_parse() failed\n", argv[0]); + goto err; + } + + if (!rdd_valid_config ()) { + ret = -1; + fprintf (stderr, "%s: configuration validation failed\n", argv[0]); + goto err; + } + + ret = rdd_spawn_threads (); + if (ret != 0) { + fprintf (stderr, "%s: spawning threads failed\n", argv[0]); + goto err; + } + + rdd_wait_for_completion (); + +err: + return ret; +} |