3e2057542d
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>
653 lines
19 KiB
C
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;
|
|
}
|