All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christoph Hellwig <hch@lst.de>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] qemu-io - an I/O path exerciser
Date: Sun, 29 Mar 2009 10:49:53 +0200	[thread overview]
Message-ID: <20090329084953.GA13311@lst.de> (raw)

This patch adds a new qemu-io tool that links against the block layer and
image formats and allow to exercise them without needing a guest image.
It is inspired by the xfs_io tool which does the same for plain file I/O.
In fact the libxcmd library which is the backend of xfs_io is reused by this
tool in a limited fashing (cmd.[ch] files).

This version tests out most of the plain block I/O commands with the
most notable absent commands beeing snapshot handling and real aio.

This tool is the basis of the I/O path test suite I'm working on right now.


Signed-off-by: Christoph Hellwig <hch@lst.de>

Index: qemu/Makefile
===================================================================
--- qemu.orig/Makefile	2009-03-19 21:48:45.726977776 +0100
+++ qemu/Makefile	2009-03-19 21:49:00.029984217 +0100
@@ -206,7 +206,9 @@ qemu-img$(EXESUF): qemu-img.o qemu-tool.
 
 qemu-nbd$(EXESUF):  qemu-nbd.o qemu-tool.o osdep.o $(BLOCK_OBJS)
 
-qemu-img$(EXESUF) qemu-nbd$(EXESUF): LIBS += -lz
+qemu-io$(EXESUF):  qemu-io.o qemu-tool.o osdep.o cmd.o $(BLOCK_OBJS)
+
+qemu-img$(EXESUF) qemu-nbd$(EXESUF) qemu-io$(EXESUF): LIBS += -lz
 
 clean:
 # avoid old build problems by removing potentially incorrect old files
Index: qemu/configure
===================================================================
--- qemu.orig/configure	2009-03-19 21:48:45.730977587 +0100
+++ qemu/configure	2009-03-19 21:49:00.029984217 +0100
@@ -1535,7 +1535,7 @@ esac
 
 tools=
 if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then
-  tools="qemu-img\$(EXESUF) $tools"
+  tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
   if [ "$linux" = "yes" ] ; then
       tools="qemu-nbd\$(EXESUF) $tools"
   fi
Index: qemu/qemu-io.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/qemu-io.c	2009-03-19 22:35:35.930978729 +0100
@@ -0,0 +1,1074 @@
+/*
+ * Command line utility to exercise the QEMU I/O path.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "cmd.h"
+
+#define VERSION	"0.0.1"
+
+#define CMD_NOFILE_OK	0x01
+
+char *progname;
+static BlockDriverState *bs;
+
+static int misalign;
+
+/*
+ * Memory allocation helpers.
+ *
+ * Make sure memory is aligned by default, or purposefully misaligned if
+ * that is specified on the command line.
+ */
+
+#define MISALIGN_OFFSET		16
+static void *qemu_io_alloc(size_t len, int pattern)
+{
+	void *buf;
+
+	if (misalign)
+		len += MISALIGN_OFFSET;
+	buf = qemu_memalign(512, len);
+	memset(buf, pattern, len);
+	if (misalign)
+		buf += MISALIGN_OFFSET;
+	return buf;
+}
+
+static void qemu_io_free(void *p)
+{
+	if (misalign)
+		p -= MISALIGN_OFFSET;
+	qemu_vfree(p);
+}
+
+static void
+dump_buffer(char *buffer, int64_t offset, int len)
+{
+	int i, j;
+	char *p;
+
+	for (i = 0, p = buffer; i < len; i += 16) {
+		char    *s = p;
+
+		printf("%08llx:  ", (unsigned long long)offset + i);
+		for (j = 0; j < 16 && i + j < len; j++, p++)
+			printf("%02x ", *p);
+		printf(" ");
+		for (j = 0; j < 16 && i + j < len; j++, s++) {
+			if (isalnum((int)*s))
+				printf("%c", *s);
+			else
+				printf(".");
+		}
+		printf("\n");
+	}
+}
+
+static void
+print_report(const char *op, struct timeval *t, int64_t offset,
+		int count, int total, int cnt, int Cflag)
+{
+	char s1[64], s2[64], ts[64];
+
+	timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
+	if (!Cflag) {
+		cvtstr((double)total, s1, sizeof(s1));
+		cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
+		printf("%s %d/%d bytes at offset %lld\n",
+			op, total, count, (long long)offset);
+		printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
+			s1, cnt, ts, s2, tdiv((double)cnt, *t));
+	} else {/* bytes,ops,time,bytes/sec,ops/sec */
+		printf("%d,%d,%s,%.3f,%.3f\n",
+			total, cnt, ts,
+			tdiv((double)total, *t),
+			tdiv((double)cnt, *t));
+	}
+}
+
+static int do_read(char *buf, int64_t offset, int count, int *total)
+{
+	int ret;
+
+	ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+	if (ret < 0)
+		return ret;
+	*total = count;
+	return 1;
+}
+
+static int do_write(char *buf, int64_t offset, int count, int *total)
+{
+	int ret;
+
+	ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+	if (ret < 0)
+		return ret;
+	*total = count;
+	return 1;
+}
+
+static int do_pread(char *buf, int64_t offset, int count, int *total)
+{
+	*total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
+	if (*total < 0)
+		return *total;
+	return 1;
+}
+
+static int do_pwrite(char *buf, int64_t offset, int count, int *total)
+{
+	*total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
+	if (*total < 0)
+		return *total;
+	return 1;
+}
+
+#define NOT_DONE 0x7fffffff
+static void aio_rw_done(void *opaque, int ret)
+{
+	*(int *)opaque = ret;
+}
+
+static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total)
+{
+	BlockDriverAIOCB *acb;
+	int async_ret = NOT_DONE;
+
+	acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
+			     aio_rw_done, &async_ret);
+	if (!acb)
+		return -EIO;
+
+	while (async_ret == NOT_DONE)
+		qemu_aio_wait();
+
+	*total = qiov->size;
+	return async_ret < 0 ? async_ret : 1;
+}
+
+static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
+{
+	BlockDriverAIOCB *acb;
+	int async_ret = NOT_DONE;
+
+	acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
+			      aio_rw_done, &async_ret);
+	if (!acb)
+		return -EIO;
+
+	while (async_ret == NOT_DONE)
+		qemu_aio_wait();
+
+	*total = qiov->size >> 9;
+	return async_ret < 0 ? async_ret : 1;
+}
+
+
+static const cmdinfo_t read_cmd;
+
+static void
+read_help(void)
+{
+	printf(
+"\n"
+" reads a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'read -v 512 1k' - dumps 1 kilobyte read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" -p, -- use bdrv_pread to read the file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -v, -- dump buffer to standard output\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int
+read_f(int argc, char **argv)
+{
+	struct timeval t1, t2;
+	int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
+	int c, cnt;
+	char *buf;
+	int64_t offset;
+	int count, total;
+
+	while ((c = getopt(argc, argv, "Cpqv")) != EOF) {
+		switch (c) {
+		case 'C':
+			Cflag = 1;
+			break;
+		case 'p':
+			pflag = 1;
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		default:
+			return command_usage(&read_cmd);
+		}
+	}
+
+	if (optind != argc - 2)
+		return command_usage(&read_cmd);
+
+	offset = cvtnum(argv[optind]);
+	if (offset < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+
+	optind++;
+	count = cvtnum(argv[optind]);
+	if (count < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+
+	if (!pflag)
+		if (offset & 0x1ff) {
+			printf("offset %lld is not sector aligned\n",
+				(long long)offset);
+			return 0;
+
+		if (count & 0x1ff) {
+			printf("count %d is not sector aligned\n",
+				count);
+			return 0;
+		}
+	}
+
+	buf = qemu_io_alloc(count, 0xab);
+
+	gettimeofday(&t1, NULL);
+	if (pflag)
+		cnt = do_pread(buf, offset, count, &total);
+	else
+		cnt = do_read(buf, offset, count, &total);
+	gettimeofday(&t2, NULL);
+
+	if (cnt < 0) {
+		printf("read failed: %s\n", strerror(-cnt));
+		return 0;
+	}
+
+	if (qflag)
+		return 0;
+
+        if (vflag)
+		dump_buffer(buf, offset, count);
+
+	/* Finally, report back -- -C gives a parsable format */
+	t2 = tsub(t2, t1);
+	print_report("read", &t2, offset, count, total, cnt, Cflag);
+
+	qemu_io_free(buf);
+
+	return 0;
+}
+
+static const cmdinfo_t read_cmd = {
+	.name		= "read",
+	.altname	= "r",
+	.cfunc		= read_f,
+	.argmin		= 2,
+	.argmax		= -1,
+	.args		= "[-aCpqv] off len",
+	.oneline	= "reads a number of bytes at a specified offset",
+	.help		= read_help,
+};
+
+static const cmdinfo_t readv_cmd;
+
+static void
+readv_help(void)
+{
+	printf(
+"\n"
+" reads a range of bytes from the given offset into multiple buffers\n"
+"\n"
+" Example:\n"
+" 'readv -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" Uses multiple iovec buffers if more than one byte range is specified.\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -v, -- dump buffer to standard output\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int
+readv_f(int argc, char **argv)
+{
+	struct timeval t1, t2;
+	int Cflag = 0, qflag = 0, vflag = 0;
+	int c, cnt;
+	char *buf, *p;
+	int64_t offset;
+	int count = 0, total;
+	int nr_iov, i;
+	QEMUIOVector qiov;
+
+	while ((c = getopt(argc, argv, "Cqv")) != EOF) {
+		switch (c) {
+		case 'C':
+			Cflag = 1;
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		default:
+			return command_usage(&readv_cmd);
+		}
+	}
+
+	if (optind > argc - 2)
+		return command_usage(&readv_cmd);
+
+
+	offset = cvtnum(argv[optind]);
+	if (offset < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+	optind++;
+
+	if (offset & 0x1ff) {
+		printf("offset %lld is not sector aligned\n",
+			(long long)offset);
+		return 0;
+	}
+
+	if (count & 0x1ff) {
+		printf("count %d is not sector aligned\n",
+			count);
+		return 0;
+	}
+
+	for (i = optind; i < argc; i++) {
+	        size_t len;
+
+		len = cvtnum(argv[i]);
+		if (len < 0) {
+			printf("non-numeric length argument -- %s\n", argv[i]);
+			return 0;
+		}
+		count += len;
+	}
+
+	nr_iov = argc - optind;
+	qemu_iovec_init(&qiov, nr_iov);
+	buf = p = qemu_io_alloc(count, 0xab);
+	for (i = 0; i < nr_iov; i++) {
+	        size_t len;
+
+		len = cvtnum(argv[optind]);
+		if (len < 0) {
+			printf("non-numeric length argument -- %s\n",
+				argv[optind]);
+			return 0;
+		}
+
+		qemu_iovec_add(&qiov, p, len);
+		p += len;
+		optind++;
+	}
+
+	gettimeofday(&t1, NULL);
+	cnt = do_aio_readv(&qiov, offset, &total);
+	gettimeofday(&t2, NULL);
+
+	if (cnt < 0) {
+		printf("readv failed: %s\n", strerror(-cnt));
+		return 0;
+	}
+
+	if (qflag)
+		return 0;
+
+        if (vflag)
+		dump_buffer(buf, offset, qiov.size);
+
+	/* Finally, report back -- -C gives a parsable format */
+	t2 = tsub(t2, t1);
+	print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
+
+	qemu_io_free(buf);
+
+	return 0;
+}
+
+static const cmdinfo_t readv_cmd = {
+	.name		= "readv",
+	.cfunc		= readv_f,
+	.argmin		= 2,
+	.argmax		= -1,
+	.args		= "[-Cqv] off len [len..]",
+	.oneline	= "reads a number of bytes at a specified offset",
+	.help		= readv_help,
+};
+
+static const cmdinfo_t write_cmd;
+
+static void
+write_help(void)
+{
+	printf(
+"\n"
+" writes a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'write 512 1k' - writes 1 kilobyte at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" -p, -- use bdrv_pwrite to write the file\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int
+write_f(int argc, char **argv)
+{
+	struct timeval t1, t2;
+	int Cflag = 0, pflag = 0, qflag = 0;
+	int c, cnt;
+	char *buf;
+	int64_t offset;
+	int count, total;
+	int pattern = 0xcd;
+
+	while ((c = getopt(argc, argv, "CpP:q")) != EOF) {
+		switch (c) {
+		case 'C':
+			Cflag = 1;
+			break;
+		case 'p':
+			pflag = 1;
+			break;
+		case 'P':
+			pattern = atoi(optarg);
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		default:
+			return command_usage(&write_cmd);
+		}
+	}
+
+	if (optind != argc - 2)
+		return command_usage(&write_cmd);
+
+	offset = cvtnum(argv[optind]);
+	if (offset < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+
+	optind++;
+	count = cvtnum(argv[optind]);
+	if (count < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+
+	if (!pflag) {
+		if (offset & 0x1ff) {
+			printf("offset %lld is not sector aligned\n",
+				(long long)offset);
+			return 0;
+		}
+
+		if (count & 0x1ff) {
+			printf("count %d is not sector aligned\n",
+				count);
+			return 0;
+		}
+	}
+
+	buf = qemu_io_alloc(count, pattern);
+
+	gettimeofday(&t1, NULL);
+	if (pflag)
+		cnt = do_pwrite(buf, offset, count, &total);
+	else
+		cnt = do_write(buf, offset, count, &total);
+	gettimeofday(&t2, NULL);
+
+	if (cnt < 0) {
+		printf("write failed: %s\n", strerror(-cnt));
+		return 0;
+	}
+
+	if (qflag)
+		return 0;
+
+	/* Finally, report back -- -C gives a parsable format */
+	t2 = tsub(t2, t1);
+	print_report("wrote", &t2, offset, count, total, cnt, Cflag);
+
+	qemu_io_free(buf);
+
+	return 0;
+}
+
+static const cmdinfo_t write_cmd = {
+	.name		= "write",
+	.altname	= "w",
+	.cfunc		= write_f,
+	.argmin		= 2,
+	.argmax		= -1,
+	.args		= "[-aCpq] [-P pattern ] off len",
+	.oneline	= "writes a number of bytes at a specified offset",
+	.help		= write_help,
+};
+
+static const cmdinfo_t writev_cmd;
+
+static void
+writev_help(void)
+{
+	printf(
+"\n"
+" writes a range of bytes from the given offset source from multiple buffers\n"
+"\n"
+" Example:\n"
+" 'write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quite mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int
+writev_f(int argc, char **argv)
+{
+	struct timeval t1, t2;
+	int Cflag = 0, qflag = 0;
+	int c, cnt;
+	char *buf, *p;
+	int64_t offset;
+	int count = 0, total;
+	int nr_iov, i;
+	int pattern = 0xcd;
+	QEMUIOVector qiov;
+
+	while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+		switch (c) {
+		case 'C':
+			Cflag = 1;
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		case 'P':
+			pattern = atoi(optarg);
+			break;
+		default:
+			return command_usage(&writev_cmd);
+		}
+	}
+
+	if (optind > argc - 2)
+		return command_usage(&writev_cmd);
+
+	offset = cvtnum(argv[optind]);
+	if (offset < 0) {
+		printf("non-numeric length argument -- %s\n", argv[optind]);
+		return 0;
+	}
+	optind++;
+
+	if (offset & 0x1ff) {
+		printf("offset %lld is not sector aligned\n",
+			(long long)offset);
+		return 0;
+	}
+
+	if (count & 0x1ff) {
+		printf("count %d is not sector aligned\n",
+			count);
+		return 0;
+	}
+
+
+	for (i = optind; i < argc; i++) {
+	        size_t len;
+
+		len = cvtnum(argv[optind]);
+		if (len < 0) {
+			printf("non-numeric length argument -- %s\n", argv[i]);
+			return 0;
+		}
+		count += len;
+	}
+
+	nr_iov = argc - optind;
+	qemu_iovec_init(&qiov, nr_iov);
+	buf = p = qemu_io_alloc(count, 0xab);
+	for (i = 0; i < nr_iov; i++) {
+	        size_t len;
+
+		len = cvtnum(argv[optind]);
+		if (len < 0) {
+			printf("non-numeric length argument -- %s\n",
+				argv[optind]);
+			return 0;
+		}
+
+		qemu_iovec_add(&qiov, p, len);
+		p += len;
+		optind++;
+	}
+
+	gettimeofday(&t1, NULL);
+	cnt = do_aio_writev(&qiov, offset, &total);
+	gettimeofday(&t2, NULL);
+
+	if (cnt < 0) {
+		printf("writev failed: %s\n", strerror(-cnt));
+		return 0;
+	}
+
+	if (qflag)
+		return 0;
+
+	/* Finally, report back -- -C gives a parsable format */
+	t2 = tsub(t2, t1);
+	print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
+
+	qemu_io_free(buf);
+
+	return 0;
+}
+
+static const cmdinfo_t writev_cmd = {
+	.name		= "writev",
+	.cfunc		= writev_f,
+	.argmin		= 2,
+	.argmax		= -1,
+	.args		= "[-Cq] [-P pattern ] off len [len..]",
+	.oneline	= "writes a number of bytes at a specified offset",
+	.help		= writev_help,
+};
+
+static int
+flush_f(int argc, char **argv)
+{
+	bdrv_flush(bs);
+	return 0;
+}
+
+static const cmdinfo_t flush_cmd = {
+	.name		= "flush",
+	.altname	= "f",
+	.cfunc		= flush_f,
+	.oneline	= "flush all in-core file state to disk",
+};
+
+static int
+truncate_f(int argc, char **argv)
+{
+	int64_t offset;
+	int ret;
+
+	offset = cvtnum(argv[1]);
+	if (offset < 0) {
+		printf("non-numeric truncate argument -- %s\n", argv[1]);
+		return 0;
+	}
+
+	ret = bdrv_truncate(bs, offset);
+	if (ret < 0) {
+		printf("truncate: %s", strerror(ret));
+		return 0;
+	}
+
+	return 0;
+}
+
+static const cmdinfo_t truncate_cmd = {
+	.name		= "truncate",
+	.altname	= "t",
+	.cfunc		= truncate_f,
+	.argmin		= 1,
+	.argmax		= 1,
+	.args		= "off",
+	.oneline	= "truncates the current file at the given offset",
+};
+
+static int
+length_f(int argc, char **argv)
+{
+        int64_t size;
+	char s1[64];
+
+	size = bdrv_getlength(bs);
+	if (size < 0) {
+		printf("getlength: %s", strerror(size));
+		return 0;
+	}
+
+	cvtstr(size, s1, sizeof(s1));
+	printf("%s\n", s1);
+	return 0;
+}
+
+
+static const cmdinfo_t length_cmd = {
+	.name		= "length",
+	.altname	= "l",
+	.cfunc		= length_f,
+	.oneline	= "gets the length of the current file",
+};
+
+
+static int
+info_f(int argc, char **argv)
+{
+	BlockDriverInfo bdi;
+	char s1[64], s2[64];
+	int ret;
+
+	if (bs->drv && bs->drv->format_name)
+		printf("format name: %s\n", bs->drv->format_name);
+	if (bs->drv && bs->drv->protocol_name)
+		printf("format name: %s\n", bs->drv->protocol_name);
+
+	ret = bdrv_get_info(bs, &bdi);
+	if (ret)
+		return 0;
+
+	cvtstr(bdi.cluster_size, s1, sizeof(s1));
+	cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
+
+	printf("cluster size: %s\n", s1);
+	printf("vm state offset: %s\n", s2);
+
+	return 0;
+}
+
+
+
+static const cmdinfo_t info_cmd = {
+	.name		= "info",
+	.altname	= "i",
+	.cfunc		= info_f,
+	.oneline	= "prints information about the current file",
+};
+
+static int
+alloc_f(int argc, char **argv)
+{
+	int64_t offset;
+	int nb_sectors;
+	char s1[64];
+	int num;
+	int ret;
+
+	offset = cvtnum(argv[1]);
+	if (offset & 0x1ff) {
+		printf("offset %lld is not sector aligned\n",
+			(long long)offset);
+		return 0;
+	}
+
+	if (argc == 3)
+		nb_sectors = cvtnum(argv[2]);
+	else
+		nb_sectors = 1;
+
+	ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
+	if (ret) {
+		printf("is_allocated: %s", strerror(ret));
+		return 0;
+	}
+
+	cvtstr(offset, s1, sizeof(s1));
+
+	if (nb_sectors == 1)
+		printf("sector allocated at offset %s\n", s1);
+	else
+		printf("%d/%d sectors allocated at offset %s\n",
+			num, nb_sectors, s1);
+	return 0;
+}
+
+static const cmdinfo_t alloc_cmd = {
+	.name		= "alloc",
+	.altname	= "a",
+	.argmin		= 1,
+	.argmax		= 2,
+	.cfunc		= alloc_f,
+	.args		= "off [sectors]",
+	.oneline	= "checks if a sector is present in the file",
+};
+
+static int
+close_f(int argc, char **argv)
+{
+	bdrv_close(bs);
+	bs = NULL;
+	return 0;
+}
+
+static const cmdinfo_t close_cmd = {
+	.name		= "close",
+	.altname	= "c",
+	.cfunc		= close_f,
+	.oneline	= "close the current open file",
+};
+
+static int openfile(char *name, int flags)
+{
+	if (bs) {
+		fprintf(stderr, "file open already, try 'help close'\n");
+		return 1;
+	}
+
+	bs = bdrv_new("hda");
+	if (!bs)
+		return 1;
+
+	if (bdrv_open(bs, name, flags) == -1) {
+		fprintf(stderr, "%s: can't open device %s\n", progname, name);
+		bs = NULL;
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+open_help(void)
+{
+	printf(
+"\n"
+" opens a new file in the requested mode\n"
+"\n"
+" Example:\n"
+" 'open -Cn /tmp/data' - creates/opens data file read-write and uncached\n"
+"\n"
+" Opens a file for subsequent use by all of the other qemu-io commands.\n"
+" -C, -- create new file if it doesn't exist\n"
+" -r, -- open file read-only\n"
+" -s, -- use snapshot file\n"
+" -n, -- disable host cache\n"
+"\n");
+}
+
+static const cmdinfo_t open_cmd;
+
+static int
+open_f(int argc, char **argv)
+{
+	int flags = 0;
+	int readonly = 0;
+	int c;
+
+	while ((c = getopt(argc, argv, "snCr")) != EOF) {
+		switch (c) {
+		case 's':
+			flags |= BDRV_O_SNAPSHOT;
+			break;
+		case 'n':
+			flags |= BDRV_O_NOCACHE;
+			break;
+		case 'C':
+			flags |= BDRV_O_CREAT;
+			break;
+		case 'r':
+			readonly = 1;
+			break;
+		default:
+			return command_usage(&open_cmd);
+		}
+	}
+
+	if (readonly)
+		flags |= BDRV_O_RDONLY;
+	else
+		flags |= BDRV_O_RDWR;
+
+	if (optind != argc - 1)
+		return command_usage(&open_cmd);
+
+	return openfile(argv[optind], flags);
+}
+
+static const cmdinfo_t open_cmd = {
+	.name		= "open",
+	.altname	= "o",
+	.cfunc		= open_f,
+	.argmin		= 1,
+	.argmax		= -1,
+	.flags		= CMD_NOFILE_OK,
+	.args		= "[-Crsn] [path]",
+	.oneline	= "open the file specified by path",
+	.help		= open_help,
+};
+
+static int
+init_args_command(
+        int     index)
+{
+	/* only one device allowed so far */
+	if (index >= 1)
+		return 0;
+	return ++index;
+}
+
+static int
+init_check_command(
+	const cmdinfo_t *ct)
+{
+	if (ct->flags & CMD_FLAG_GLOBAL)
+		return 1;
+	if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
+		fprintf(stderr, "no file open, try 'help open'\n");
+		return 0;
+	}
+	return 1;
+}
+
+static void usage(const char *name)
+{
+	printf(
+"Usage: %s [-h] [-V] [-Crsnm] [-c cmd] ... [file]\n"
+"QEMU Disk excerciser\n"
+"\n"
+"  -C, --create         create new file if it doesn't exist\n"
+"  -c, --cmd            command to execute\n"
+"  -r, --read-only      export read-only\n"
+"  -s, --snapshot       use snapshot file\n"
+"  -n, --nocache        disable host cache\n"
+"  -m, --misalign       misalign allocations for O_DIRECT\n"
+"  -h, --help           display this help and exit\n"
+"  -V, --version        output version information and exit\n"
+"\n",
+	name);
+}
+
+
+int main(int argc, char **argv)
+{
+	int readonly = 0;
+	const char *sopt = "hVc:Crsnm";
+	struct option lopt[] = {
+		{ "help", 0, 0, 'h' },
+		{ "version", 0, 0, 'V' },
+		{ "offset", 1, 0, 'o' },
+		{ "cmd", 1, 0, 'c' },
+		{ "create", 0, 0, 'C' },
+		{ "read-only", 0, 0, 'r' },
+		{ "snapshot", 0, 0, 's' },
+		{ "nocache", 0, 0, 'n' },
+		{ "misalign", 0, 0, 'm' },
+		{ NULL, 0, 0, 0 }
+	};
+	int c;
+	int opt_index = 0;
+	int flags = 0;
+
+	progname = basename(argv[0]);
+
+	while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
+		switch (c) {
+		case 's':
+			flags |= BDRV_O_SNAPSHOT;
+			break;
+		case 'n':
+			flags |= BDRV_O_NOCACHE;
+			break;
+		case 'c':
+			add_user_command(optarg);
+			break;
+		case 'C':
+			flags |= BDRV_O_CREAT;
+			break;
+		case 'r':
+			readonly = 1;
+			break;
+		case 'm':
+			misalign = 1;
+			break;
+		case 'V':
+			printf("%s version %s\n", progname, VERSION);
+			exit(0);
+		case 'h':
+			usage(progname);
+			exit(0);
+		default:
+			usage(progname);
+			exit(1);
+		}
+	}
+
+	if ((argc - optind) > 1) {
+		usage(progname);
+		exit(1);
+	}
+
+	bdrv_init();
+
+	/* initialize commands */
+	quit_init();
+	help_init();
+	add_command(&open_cmd);
+	add_command(&close_cmd);
+	add_command(&read_cmd);
+	add_command(&readv_cmd);
+	add_command(&write_cmd);
+	add_command(&writev_cmd);
+	add_command(&flush_cmd);
+	add_command(&truncate_cmd);
+	add_command(&length_cmd);
+	add_command(&info_cmd);
+	add_command(&alloc_cmd);
+
+	add_args_command(init_args_command);
+	add_check_command(init_check_command);
+
+	/* open the device */
+	if (readonly)
+		flags |= BDRV_O_RDONLY;
+	else
+		flags |= BDRV_O_RDWR;
+
+	if ((argc - optind) == 1)
+		openfile(argv[optind], flags);
+	command_loop();
+
+	if (bs)
+		bdrv_close(bs);
+	return 0;
+}
Index: qemu/cmd.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/cmd.c	2009-03-19 21:49:00.031978745 +0100
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would 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, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "cmd.h"
+
+#define _(x)	x	/* not gettext support yet */
+
+extern int optind;
+
+/* from libxcmd/command.c */
+
+cmdinfo_t	*cmdtab;
+int		ncmds;
+
+static argsfunc_t	args_func;
+static checkfunc_t	check_func;
+static int		ncmdline;
+static char		**cmdline;
+
+static int
+compare(const void *a, const void *b)
+{
+	return strcmp(((const cmdinfo_t *)a)->name,
+		      ((const cmdinfo_t *)b)->name);
+}
+
+void
+add_command(
+	const cmdinfo_t	*ci)
+{
+	cmdtab = realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
+	cmdtab[ncmds - 1] = *ci;
+	qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
+}
+
+static int
+check_command(
+	const cmdinfo_t	*ci)
+{
+	if (check_func)
+		return check_func(ci);
+	return 1;
+}
+
+void
+add_check_command(
+	checkfunc_t	cf)
+{
+	check_func = cf;
+}
+
+int
+command_usage(
+	const cmdinfo_t *ci)
+{
+	printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
+	return 0;
+}
+
+int
+command(
+	const cmdinfo_t	*ct,
+	int		argc,
+	char		**argv)
+{
+	char		*cmd = argv[0];
+
+	if (!check_command(ct))
+		return 0;
+
+	if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
+		if (ct->argmax == -1)
+			fprintf(stderr,
+	_("bad argument count %d to %s, expected at least %d arguments\n"),
+				argc-1, cmd, ct->argmin);
+		else if (ct->argmin == ct->argmax)
+			fprintf(stderr,
+	_("bad argument count %d to %s, expected %d arguments\n"),
+				argc-1, cmd, ct->argmin);
+		else
+			fprintf(stderr,
+	_("bad argument count %d to %s, expected between %d and %d arguments\n"),
+			argc-1, cmd, ct->argmin, ct->argmax);
+		return 0;
+	}
+	optind = 0;
+	return ct->cfunc(argc, argv);
+}
+
+const cmdinfo_t *
+find_command(
+	const char	*cmd)
+{
+	cmdinfo_t	*ct;
+
+	for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
+		if (strcmp(ct->name, cmd) == 0 ||
+		    (ct->altname && strcmp(ct->altname, cmd) == 0))
+			return (const cmdinfo_t *)ct;
+	}
+	return NULL;
+}
+
+void
+add_user_command(char *optarg)
+{
+	ncmdline++;
+	cmdline = realloc(cmdline, sizeof(char*) * (ncmdline));
+	if (!cmdline) {
+		perror("realloc");
+		exit(1);
+	}
+	cmdline[ncmdline-1] = optarg;
+}
+
+static int
+args_command(
+	int	index)
+{
+	if (args_func)
+		return args_func(index);
+	return 0;
+}
+
+void
+add_args_command(
+	argsfunc_t	af)
+{
+	args_func = af;
+}
+
+void
+command_loop(void)
+{
+	int		c, i, j = 0, done = 0;
+	char		*input;
+	char		**v;
+	const cmdinfo_t	*ct;
+
+	for (i = 0; !done && i < ncmdline; i++) {
+		input = strdup(cmdline[i]);
+		if (!input) {
+			fprintf(stderr,
+				_("cannot strdup command '%s': %s\n"),
+				cmdline[i], strerror(errno));
+			exit(1);
+		}
+		v = breakline(input, &c);
+		if (c) {
+			ct = find_command(v[0]);
+			if (ct) {
+				if (ct->flags & CMD_FLAG_GLOBAL)
+					done = command(ct, c, v);
+				else {
+					j = 0;
+					while (!done && (j = args_command(j)))
+						done = command(ct, c, v);
+				}
+			} else
+				fprintf(stderr, _("command \"%s\" not found\n"),
+					v[0]);
+		}
+		doneline(input, v);
+	}
+	if (cmdline) {
+		free(cmdline);
+		return;
+	}
+	while (!done) {
+		if ((input = fetchline()) == NULL)
+			break;
+		v = breakline(input, &c);
+		if (c) {
+			ct = find_command(v[0]);
+			if (ct)
+				done = command(ct, c, v);
+			else
+				fprintf(stderr, _("command \"%s\" not found\n"),
+					v[0]);
+		}
+		doneline(input, v);
+	}
+}
+
+/* from libxcmd/input.c */
+
+#if defined(ENABLE_READLINE)
+# include <readline/history.h>
+# include <readline/readline.h>
+#elif defined(ENABLE_EDITLINE)
+# include <histedit.h>
+#endif
+
+extern char *progname;
+
+static char *
+get_prompt(void)
+{
+	static char	prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
+
+	if (!prompt[0])
+		snprintf(prompt, sizeof(prompt), "%s> ", progname);
+	return prompt;
+}
+
+#if defined(ENABLE_READLINE)
+char *
+fetchline(void)
+{
+	char	*line;
+
+	line = readline(get_prompt());
+	if (line && *line)
+		add_history(line);
+	return line;
+}
+#elif defined(ENABLE_EDITLINE)
+static char *el_get_prompt(EditLine *e) { return get_prompt(); }
+char *
+fetchline(void)
+{
+	static EditLine	*el;
+	static History	*hist;
+	HistEvent	hevent;
+	char		*line;
+	int		count;
+
+	if (!el) {
+		hist = history_init();
+		history(hist, &hevent, H_SETSIZE, 100);
+		el = el_init(progname, stdin, stdout, stderr);
+		el_source(el, NULL);
+		el_set(el, EL_SIGNAL, 1);
+		el_set(el, EL_PROMPT, el_get_prompt);
+		el_set(el, EL_HIST, history, (const char *)hist);
+	}
+	line = strdup(el_gets(el, &count));
+	if (line) {
+		if (count > 0)
+			line[count-1] = '\0';
+		if (*line)
+			history(hist, &hevent, H_ENTER, line);
+	}
+	return line;
+}
+#else
+# define MAXREADLINESZ	1024
+char *
+fetchline(void)
+{
+	char	*p, *line = malloc(MAXREADLINESZ);
+
+	if (!line)
+		return NULL;
+	printf("%s", get_prompt());
+	fflush(stdout);
+	if (!fgets(line, MAXREADLINESZ, stdin)) {
+		free(line);
+		return NULL;
+	}
+	p = line + strlen(line);
+	if (p != line && p[-1] == '\n')
+		p[-1] = '\0';
+	return line;
+}
+#endif
+
+char **
+breakline(
+	char	*input,
+	int	*count)
+{
+	int	c = 0;
+	char	*p;
+	char	**rval = calloc(sizeof(char *), 1);
+
+	while (rval && (p = strsep(&input, " ")) != NULL) {
+		if (!*p)
+			continue;
+		c++;
+		rval = realloc(rval, sizeof(*rval) * (c + 1));
+		if (!rval) {
+			c = 0;
+			break;
+		}
+		rval[c - 1] = p;
+		rval[c] = NULL;
+	}
+	*count = c;
+	return rval;
+}
+
+void
+doneline(
+	char	*input,
+	char	**vec)
+{
+	free(input);
+	free(vec);
+}
+
+#define EXABYTES(x)	((long long)(x) << 60)
+#define PETABYTES(x)	((long long)(x) << 50)
+#define TERABYTES(x)	((long long)(x) << 40)
+#define GIGABYTES(x)	((long long)(x) << 30)
+#define MEGABYTES(x)	((long long)(x) << 20)
+#define KILOBYTES(x)	((long long)(x) << 10)
+
+long long
+cvtnum(
+	char		*s)
+{
+	long long	i;
+	char		*sp;
+	int		c;
+
+	i = strtoll(s, &sp, 0);
+	if (i == 0 && sp == s)
+		return -1LL;
+	if (*sp == '\0')
+		return i;
+
+	if (sp[1] != '\0')
+		return -1LL;
+
+	c = tolower(*sp);
+	switch (c) {
+	default:
+		return i;
+	case 'k':
+		return KILOBYTES(i);
+	case 'm':
+		return MEGABYTES(i);
+	case 'g':
+		return GIGABYTES(i);
+	case 't':
+		return TERABYTES(i);
+	case 'p':
+		return PETABYTES(i);
+	case 'e':
+		return  EXABYTES(i);
+	}
+	return -1LL;
+}
+
+#define TO_EXABYTES(x)	((x) / EXABYTES(1))
+#define TO_PETABYTES(x)	((x) / PETABYTES(1))
+#define TO_TERABYTES(x)	((x) / TERABYTES(1))
+#define TO_GIGABYTES(x)	((x) / GIGABYTES(1))
+#define TO_MEGABYTES(x)	((x) / MEGABYTES(1))
+#define TO_KILOBYTES(x)	((x) / KILOBYTES(1))
+
+void
+cvtstr(
+	double		value,
+	char		*str,
+	size_t		size)
+{
+	const char	*fmt;
+	int		precise;
+
+	precise = ((double)value * 1000 == (double)(int)value * 1000);
+
+	if (value >= EXABYTES(1)) {
+		fmt = precise ? "%.f EiB" : "%.3f EiB";
+		snprintf(str, size, fmt, TO_EXABYTES(value));
+	} else if (value >= PETABYTES(1)) {
+		fmt = precise ? "%.f PiB" : "%.3f PiB";
+		snprintf(str, size, fmt, TO_PETABYTES(value));
+	} else if (value >= TERABYTES(1)) {
+		fmt = precise ? "%.f TiB" : "%.3f TiB";
+		snprintf(str, size, fmt, TO_TERABYTES(value));
+	} else if (value >= GIGABYTES(1)) {
+		fmt = precise ? "%.f GiB" : "%.3f GiB";
+		snprintf(str, size, fmt, TO_GIGABYTES(value));
+	} else if (value >= MEGABYTES(1)) {
+		fmt = precise ? "%.f MiB" : "%.3f MiB";
+		snprintf(str, size, fmt, TO_MEGABYTES(value));
+	} else if (value >= KILOBYTES(1)) {
+		fmt = precise ? "%.f KiB" : "%.3f KiB";
+		snprintf(str, size, fmt, TO_KILOBYTES(value));
+	} else {
+		snprintf(str, size, "%f bytes", value);
+	}
+}
+
+struct timeval
+tsub(struct timeval t1, struct timeval t2)
+{
+	t1.tv_usec -= t2.tv_usec;
+	if (t1.tv_usec < 0) {
+		t1.tv_usec += 1000000;
+		t1.tv_sec--;
+	}
+	t1.tv_sec -= t2.tv_sec;
+	return t1;
+}
+
+double
+tdiv(double value, struct timeval tv)
+{
+	return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
+}
+
+#define HOURS(sec)	((sec) / (60 * 60))
+#define MINUTES(sec)	(((sec) % (60 * 60)) / 60)
+#define SECONDS(sec)	((sec) % 60)
+
+void
+timestr(
+	struct timeval	*tv,
+	char		*ts,
+	size_t		size,
+	int		format)
+{
+	double		usec = (double)tv->tv_usec / 1000000.0;
+
+	if (format & TERSE_FIXED_TIME) {
+		if (!HOURS(tv->tv_sec)) {
+			snprintf(ts, size, "%u:%02u.%02u",
+				(unsigned int) MINUTES(tv->tv_sec),
+				(unsigned int) SECONDS(tv->tv_sec),
+				(unsigned int) usec * 100);
+			return;
+		}
+		format |= VERBOSE_FIXED_TIME;	/* fallback if hours needed */
+	}
+
+	if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
+		snprintf(ts, size, "%u:%02u:%02u.%02u",
+			(unsigned int) HOURS(tv->tv_sec),
+			(unsigned int) MINUTES(tv->tv_sec),
+			(unsigned int) SECONDS(tv->tv_sec),
+			(unsigned int) usec * 100);
+	} else {
+		snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000);
+	}
+}
+
+
+/* from libxcmd/quit.c */
+
+static cmdinfo_t quit_cmd;
+
+/* ARGSUSED */
+static int
+quit_f(
+	int	argc,
+	char	**argv)
+{
+	return 1;
+}
+
+void
+quit_init(void)
+{
+	quit_cmd.name = _("quit");
+	quit_cmd.altname = _("q");
+	quit_cmd.cfunc = quit_f;
+	quit_cmd.argmin = -1;
+	quit_cmd.argmax = -1;
+	quit_cmd.flags = CMD_FLAG_GLOBAL;
+	quit_cmd.oneline = _("exit the program");
+
+	add_command(&quit_cmd);
+}
+
+/* from libxcmd/help.c */
+
+static cmdinfo_t help_cmd;
+static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
+static void help_oneline(const char *cmd, const cmdinfo_t *ct);
+
+static void
+help_all(void)
+{
+	const cmdinfo_t	*ct;
+
+	for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
+		help_oneline(ct->name, ct);
+	printf(_("\nUse 'help commandname' for extended help.\n"));
+}
+
+static int
+help_f(
+	int		argc,
+	char		**argv)
+{
+	const cmdinfo_t	*ct;
+
+	if (argc == 1) {
+		help_all();
+		return 0;
+	}
+	ct = find_command(argv[1]);
+	if (ct == NULL) {
+		printf(_("command %s not found\n"), argv[1]);
+		return 0;
+	}
+	help_onecmd(argv[1], ct);
+	return 0;
+}
+
+static void
+help_onecmd(
+	const char	*cmd,
+	const cmdinfo_t	*ct)
+{
+	help_oneline(cmd, ct);
+	if (ct->help)
+		ct->help();
+}
+
+static void
+help_oneline(
+	const char	*cmd,
+	const cmdinfo_t	*ct)
+{
+	if (cmd)
+		printf("%s ", cmd);
+	else {
+		printf("%s ", ct->name);
+		if (ct->altname)
+			printf("(or %s) ", ct->altname);
+	}
+	if (ct->args)
+		printf("%s ", ct->args);
+	printf("-- %s\n", ct->oneline);
+}
+
+void
+help_init(void)
+{
+	help_cmd.name = _("help");
+	help_cmd.altname = _("?");
+	help_cmd.cfunc = help_f;
+	help_cmd.argmin = 0;
+	help_cmd.argmax = 1;
+	help_cmd.flags = CMD_FLAG_GLOBAL;
+	help_cmd.args = _("[command]");
+	help_cmd.oneline = _("help for one or all commands");
+
+	add_command(&help_cmd);
+}
Index: qemu/cmd.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ qemu/cmd.h	2009-03-19 21:49:00.031978745 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would 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, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __COMMAND_H__
+#define __COMMAND_H__
+
+#define CMD_FLAG_GLOBAL	((int)0x80000000)	/* don't iterate "args" */
+
+typedef int (*cfunc_t)(int argc, char **argv);
+typedef void (*helpfunc_t)(void);
+
+typedef struct cmdinfo {
+	const char	*name;
+	const char	*altname;
+	cfunc_t		cfunc;
+	int		argmin;
+	int		argmax;
+	int		canpush;
+	int		flags;
+	const char	*args;
+	const char	*oneline;
+	helpfunc_t      help;
+} cmdinfo_t;
+
+extern cmdinfo_t	*cmdtab;
+extern int		ncmds;
+
+extern void		help_init(void);
+extern void		quit_init(void);
+
+typedef int (*argsfunc_t)(int index);
+typedef int (*checkfunc_t)(const cmdinfo_t *ci);
+
+extern void		add_command(const cmdinfo_t *ci);
+extern void		add_user_command(char *optarg);
+extern void		add_args_command(argsfunc_t af);
+extern void		add_check_command(checkfunc_t cf);
+
+extern const cmdinfo_t	*find_command(const char *cmd);
+
+extern void		command_loop(void);
+extern int		command_usage(const cmdinfo_t *ci);
+extern int		command(const cmdinfo_t *ci, int argc, char **argv);
+
+/* from input.h */
+extern char	**breakline(char *input, int *count);
+extern void	doneline(char *input, char **vec);
+extern char	*fetchline(void);
+
+extern long long cvtnum(char *s);
+extern void	cvtstr(double value, char *str, size_t sz);
+
+extern struct timeval tsub(struct timeval t1, struct timeval t2);
+extern double	tdiv(double value, struct timeval tv);
+
+enum {
+	DEFAULT_TIME		= 0x0,
+	TERSE_FIXED_TIME	= 0x1,
+	VERBOSE_FIXED_TIME	= 0x2
+};
+
+extern void	timestr(struct timeval *tv, char *str, size_t sz, int flags);
+
+#endif	/* __COMMAND_H__ */

             reply	other threads:[~2009-03-29  8:50 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-03-29  8:49 Christoph Hellwig [this message]
2009-04-05 18:44 ` [Qemu-devel] [PATCH] qemu-io - an I/O path exerciser Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090329084953.GA13311@lst.de \
    --to=hch@lst.de \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.