glusterfs/extras/benchmarking/rdd.c
Varun Shastry 3e2057542d All: License message change
License message changed for server-side, dual license GPLV2 and LGPLv3+.

Change-Id: Ia9e53061b9d2df3b3ef3bc9778dceff77db46a09
BUG: 852318
Signed-off-by: Varun Shastry <vshastry@redhat.com>
Reviewed-on: http://review.gluster.org/3940
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Kaleb KEITHLEY <kkeithle@redhat.com>
Reviewed-by: Anand Avati <avati@redhat.com>
2012-09-13 13:19:37 -07:00

653 lines
19 KiB
C

/*
Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
#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
#define UNIT_KB 1024ULL
#define UNIT_MB UNIT_KB*1024ULL
#define UNIT_GB UNIT_MB*1024ULL
#define UNIT_TB UNIT_GB*1024ULL
#define UNIT_PB UNIT_TB*1024ULL
#define UNIT_KB_STRING "KB"
#define UNIT_MB_STRING "MB"
#define UNIT_GB_STRING "GB"
#define UNIT_TB_STRING "TB"
#define UNIT_PB_STRING "PB"
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;
ssize_t file_size;
};
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);
rdd_config.in_file.path[len] = '\0';
}
break;
case 'f':
{
char *tmp = NULL;
unsigned long long fs = 0;
if (string2bytesize (arg, &fs) == -1) {
fprintf (stderr, "invalid argument for file size "
"(%s)\n", arg);
return -1;
}
rdd_config.file_size = fs;
}
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;
}
int
string2bytesize (const char *str, unsigned long long *n)
{
unsigned long long value = 0ULL;
char *tail = NULL;
int old_errno = 0;
const char *s = NULL;
if (str == NULL || n == NULL)
{
errno = EINVAL;
return -1;
}
for (s = str; *s != '\0'; s++)
{
if (isspace (*s))
{
continue;
}
if (*s == '-')
{
return -1;
}
break;
}
old_errno = errno;
errno = 0;
value = strtoull (str, &tail, 10);
if (errno == ERANGE || errno == EINVAL)
{
return -1;
}
if (errno == 0)
{
errno = old_errno;
}
if (tail[0] != '\0')
{
if (strcasecmp (tail, UNIT_KB_STRING) == 0)
{
value *= UNIT_KB;
}
else if (strcasecmp (tail, UNIT_MB_STRING) == 0)
{
value *= UNIT_MB;
}
else if (strcasecmp (tail, UNIT_GB_STRING) == 0)
{
value *= UNIT_GB;
}
else if (strcasecmp (tail, UNIT_TB_STRING) == 0)
{
value *= UNIT_TB;
}
else if (strcasecmp (tail, UNIT_PB_STRING) == 0)
{
value *= UNIT_PB;
}
else
{
return -1;
}
}
*n = value;
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)"},
{"file-size", 'f', "FILESIZE", 0,
"the size of the file which will be created and upon it I/O will be done"
" (defaults to 100MB"},
{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)
{
char *tmp_path = "rdd.in";
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;
strncpy (rdd_config.in_file.path, tmp_path, strlen (tmp_path));
rdd_config.file_size = 104857600;
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 && (errno != ENOENT)) {
fprintf (stderr, "open: (%s)", strerror (errno));
ret = 0;
goto out;
}
close (fd);
if (rdd_config.min_bs > rdd_config.max_bs) {
fprintf (stderr, "minimum blocksize %ld is greater than the "
"maximum blocksize %ld", 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 void
cleanup (void)
{
close (rdd_config.in_file.fd);
close (rdd_config.out_file.fd);
rdd_config.in_file.fd = rdd_config.out_file.fd = -1;
}
static int
check_and_create (void)
{
int ret = -1;
char buf[4096] = {0,};
struct stat stbuf = {0,};
int fd[2] = {-1,};
size_t total_size = -1;
total_size = rdd_config.file_size;
ret = stat (rdd_config.in_file.path, &stbuf);
if (ret == -1 && (errno != ENOENT))
goto out;
fd[1] = open (rdd_config.in_file.path, O_CREAT | O_WRONLY | O_TRUNC);
if (fd[1] == -1)
goto out;
fd[0] = open ("/dev/urandom", O_RDONLY);
if (fd[0] == -1)
goto out;
while (total_size > 0) {
if (total_size >= 4096) {
ret = read (fd[0], buf, 4096);
if (ret == -1)
goto out;
ret = write (fd[1], buf, 4096);
if (ret == -1)
goto out;
total_size = total_size - 4096;
} else {
ret = read (fd[0], buf, total_size);
if (ret == -1)
goto out;
ret = write (fd[1], buf, total_size);
if (ret == -1)
goto out;
total_size = total_size - total_size;
}
}
ret = 0;
out:
if (fd[0] > 0)
close (fd[0]);
if (fd[1] > 0)
close (fd[1]);
return ret;
}
static int
rdd_spawn_threads (void)
{
int i = 0, ret = -1, fd = -1;
char buf[4096];
ret = check_and_create ();
if (ret == -1)
goto out;
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 | O_TRUNC,
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));
cleanup ();
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;
cleanup ();
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);
cleanup ();
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);
cleanup ();
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;
}