Sophie

Sophie

distrib > Fedora > 13 > i386 > media > os > by-pkgid > 8471bd520ded073b5cb4876bff0c70ca > files > 142

glusterfs-common-2.0.9-2.fc13.i686.rpm

/*
  Copyright (c) 2008 Gluster, Inc. <http://www.gluster.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;
}