From 293c51a6ee369228633a8428ab689f14c045ff98 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 5 Jun 2013 10:33:14 +0200 Subject: blockdev: reset werror/rerror on drive_del Paolo Bonzini suggested the following test case: 1. Launch a guest and wait at the GRUB boot menu: qemu-system-x86_64 -enable-kvm -m 1024 \ -drive if=none,cache=none,file=test.img,id=foo,werror=stop,rerror=stop -device virtio-blk-pci,drive=foo,id=virtio0,addr=4 2. Hot unplug the device: (qemu) drive_del foo 3. Select the first boot menu entry Without this patch the guest pauses due to ENOMEDIUM. The guest is stuck in a continuous pause loop since the I/O request is retried and fails immediately again when the guest is resumed. With this patch the error is reported to the guest. Note that this scenario actually happens sometimes during libvirt disk hot unplug, where device_del is followed by drive_del. I/O may still be submitted to the drive after drive_del if the guest does not process the PCI hot unplug notification. Reported-by: Dafna Ron Signed-off-by: Stefan Hajnoczi Reviewed-by: Kevin Wolf --- blockdev.c | 4 ++++ hmp-commands.hx | 2 ++ 2 files changed, 6 insertions(+) diff --git a/blockdev.c b/blockdev.c index b9b2d10e6b..9937311aac 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1180,6 +1180,10 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) */ if (bdrv_get_attached_dev(bs)) { bdrv_make_anon(bs); + + /* Further I/O must not pause the guest */ + bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); } else { drive_uninit(drive_get_by_blockdev(bs)); } diff --git a/hmp-commands.hx b/hmp-commands.hx index 9cea4151b9..4f5a3fd7d5 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -185,6 +185,8 @@ Remove host block device. The result is that guest generated IO is no longer submitted against the host device underlying the disk. Once a drive has been deleted, the QEMU Block layer returns -EIO which results in IO errors in the guest for applications that are reading/writing to the device. +These errors are always reported to the guest, regardless of the drive's error +actions (drive options rerror, werror). ETEXI { -- cgit v1.2.1 From a23818f4ff3d7981f49453b739f589e4205930b5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:26 +0200 Subject: qemu-io: Remove unused args_command The original intention seems to be something with handling multiple images at once, but this has never been implemented and the only function ever registered is implemented to make everything behave like a "global" command. Just do that unconditionally now. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 28 ++-------------------------- cmd.h | 2 -- qemu-io.c | 10 ---------- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/cmd.c b/cmd.c index 10a8688b2d..4e7579b982 100644 --- a/cmd.c +++ b/cmd.c @@ -34,7 +34,6 @@ cmdinfo_t *cmdtab; int ncmds; -static argsfunc_t args_func; static checkfunc_t check_func; static int ncmdline; static char **cmdline; @@ -127,22 +126,6 @@ void add_user_command(char *optarg) 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; -} - static void prep_fetchline(void *opaque) { int *fetchable = opaque; @@ -155,7 +138,7 @@ static char *get_prompt(void); void command_loop(void) { - int c, i, j = 0, done = 0, fetchable = 0, prompted = 0; + int c, i, done = 0, fetchable = 0, prompted = 0; char *input; char **v; const cmdinfo_t *ct; @@ -171,14 +154,7 @@ void command_loop(void) 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); - } - } + done = command(ct, c, v); } else { fprintf(stderr, _("command \"%s\" not found\n"), v[0]); } diff --git a/cmd.h b/cmd.h index b763b198c4..8e6f7538c4 100644 --- a/cmd.h +++ b/cmd.h @@ -41,12 +41,10 @@ extern int ncmds; void help_init(void); void quit_init(void); -typedef int (*argsfunc_t)(int index); typedef int (*checkfunc_t)(const cmdinfo_t *ci); void add_command(const cmdinfo_t *ci); void add_user_command(char *optarg); -void add_args_command(argsfunc_t af); void add_check_command(checkfunc_t cf); const cmdinfo_t *find_command(const char *cmd); diff --git a/qemu-io.c b/qemu-io.c index 5e6680b247..4288b8cc0f 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -1888,15 +1888,6 @@ static int open_f(int argc, char **argv) return openfile(argv[optind], flags, growable); } -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) { @@ -2043,7 +2034,6 @@ int main(int argc, char **argv) add_command(&wait_break_cmd); add_command(&abort_cmd); - add_args_command(init_args_command); add_check_command(init_check_command); /* open the device */ -- cgit v1.2.1 From 5e00984aef7c1c317e27c0e8acf66526513c770f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:27 +0200 Subject: cutils: Support 'P' and 'E' suffixes in strtosz() Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- include/qemu-common.h | 2 ++ monitor.c | 8 ++++---- qemu-img.c | 10 ++++++---- tests/qemu-iotests/049.out | 8 ++++---- util/cutils.c | 4 ++++ 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/qemu-common.h b/include/qemu-common.h index cb82ef3d42..d95ea1e147 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -191,6 +191,8 @@ int parse_uint_full(const char *s, unsigned long long *value, int base); * A-Z, as strtosz() will use qemu_toupper() on the given argument * prior to comparison. */ +#define STRTOSZ_DEFSUFFIX_EB 'E' +#define STRTOSZ_DEFSUFFIX_PB 'P' #define STRTOSZ_DEFSUFFIX_TB 'T' #define STRTOSZ_DEFSUFFIX_GB 'G' #define STRTOSZ_DEFSUFFIX_MB 'M' diff --git a/monitor.c b/monitor.c index eefc7f083f..9d279b81cf 100644 --- a/monitor.c +++ b/monitor.c @@ -93,10 +93,10 @@ * 'M' Non-negative target long (32 or 64 bit), in user mode the * value is multiplied by 2^20 (think Mebibyte) * 'o' octets (aka bytes) - * user mode accepts an optional T, t, G, g, M, m, K, k - * suffix, which multiplies the value by 2^40 for - * suffixes T and t, 2^30 for suffixes G and g, 2^20 for - * M and m, 2^10 for K and k + * user mode accepts an optional E, e, P, p, T, t, G, g, M, m, + * K, k suffix, which multiplies the value by 2^60 for suffixes E + * and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t, + * 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k * 'T' double * user mode accepts an optional ms, us, ns suffix, * which divides the value by 1e3, 1e6, 1e9, respectively diff --git a/qemu-img.c b/qemu-img.c index 82c7977353..e089c7860b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -85,8 +85,9 @@ static void help(void) " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" " 'directsync' and 'unsafe' (default for convert)\n" " 'size' is the disk image size in bytes. Optional suffixes\n" - " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" - " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" + " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" + " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" + " supported. 'b' is ignored.\n" " 'output_filename' is the destination disk image filename\n" " 'output_fmt' is the destination format\n" " 'options' is a comma separated list of format specific options in a\n" @@ -387,8 +388,9 @@ static int img_create(int argc, char **argv) error_report("Image size must be less than 8 EiB!"); } else { error_report("Invalid image size specified! You may use k, M, " - "G or T suffixes for "); - error_report("kilobytes, megabytes, gigabytes and terabytes."); + "G, T, P or E suffixes for "); + error_report("kilobytes, megabytes, gigabytes, terabytes, " + "petabytes and exabytes."); } return 1; } diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 72db13f8d2..d2f0efe16d 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -108,15 +108,15 @@ qemu-img: Formatting or formatting option not supported for file format 'qcow2' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte -qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for -qemu-img: kilobytes, megabytes, gigabytes and terabytes. +qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar -qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for -qemu-img: kilobytes, megabytes, gigabytes and terabytes. +qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for +qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img: Parameter 'size' expects a size diff --git a/util/cutils.c b/util/cutils.c index a1658197cf..8f28896843 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -267,6 +267,10 @@ static int64_t suffix_mul(char suffix, int64_t unit) return unit * unit * unit; case STRTOSZ_DEFSUFFIX_TB: return unit * unit * unit * unit; + case STRTOSZ_DEFSUFFIX_PB: + return unit * unit * unit * unit * unit; + case STRTOSZ_DEFSUFFIX_EB: + return unit * unit * unit * unit * unit * unit; } return -1; } -- cgit v1.2.1 From b6e356aa25c81d928e1c463292048d29cf25f04e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:28 +0200 Subject: qemu-io: Make cvtnum() a wrapper around strtosz_suffix() No reason to implement the same thing multiple times. A nice side effect is that fractional numbers like 0.5M can be used in qemu-io now. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 37 ------------------------------------- cmd.h | 1 - qemu-io.c | 6 ++++++ 3 files changed, 6 insertions(+), 38 deletions(-) diff --git a/cmd.c b/cmd.c index 4e7579b982..214c6f757d 100644 --- a/cmd.c +++ b/cmd.c @@ -344,43 +344,6 @@ doneline( #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 = qemu_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)) diff --git a/cmd.h b/cmd.h index 8e6f7538c4..4dcfe885a4 100644 --- a/cmd.h +++ b/cmd.h @@ -58,7 +58,6 @@ char **breakline(char *input, int *count); void doneline(char *input, char **vec); char *fetchline(void); -long long cvtnum(char *s); void cvtstr(double value, char *str, size_t sz); struct timeval tsub(struct timeval t1, struct timeval t2); diff --git a/qemu-io.c b/qemu-io.c index 4288b8cc0f..8a719a881c 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -29,6 +29,12 @@ static BlockDriverState *bs; static int misalign; +static int64_t cvtnum(const char *s) +{ + char *end; + return strtosz_suffix(s, &end, STRTOSZ_DEFSUFFIX_B); +} + /* * Parse the pattern argument to various sub-commands. * -- cgit v1.2.1 From cf49a6a00c19cabf4006d4f82bef26345043e7b5 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:29 +0200 Subject: qemu-io: Handle cvtnum() errors in 'alloc' Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- qemu-io.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qemu-io.c b/qemu-io.c index 8a719a881c..b4f56fc59f 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -1596,7 +1596,10 @@ static int alloc_f(int argc, char **argv) int ret; offset = cvtnum(argv[1]); - if (offset & 0x1ff) { + if (offset < 0) { + printf("non-numeric offset argument -- %s\n", argv[1]); + return 0; + } else if (offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", offset); return 0; @@ -1604,6 +1607,10 @@ static int alloc_f(int argc, char **argv) if (argc == 3) { nb_sectors = cvtnum(argv[2]); + if (nb_sectors < 0) { + printf("non-numeric length argument -- %s\n", argv[2]); + return 0; + } } else { nb_sectors = 1; } -- cgit v1.2.1 From 734c3b85cb72d264ad2b38a87f30304e05de2cb1 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:30 +0200 Subject: qemu-io: Don't use global bs in command implementations Pass in the BlockDriverState to the command handlers instead of using the global variable. This is an important step to make the commands usable outside of qemu-io. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 6 ++- cmd.h | 8 ++- qemu-io.c | 167 ++++++++++++++++++++++++++++++++++---------------------------- 3 files changed, 101 insertions(+), 80 deletions(-) diff --git a/cmd.c b/cmd.c index 214c6f757d..d501aabb0c 100644 --- a/cmd.c +++ b/cmd.c @@ -57,7 +57,7 @@ check_command( const cmdinfo_t *ci) { if (check_func) - return check_func(ci); + return check_func(qemuio_bs, ci); return 1; } @@ -103,7 +103,7 @@ command( return 0; } optind = 0; - return ct->cfunc(argc, argv); + return ct->cfunc(qemuio_bs, argc, argv); } const cmdinfo_t * @@ -452,6 +452,7 @@ static cmdinfo_t quit_cmd; /* ARGSUSED */ static int quit_f( + BlockDriverState *bs, int argc, char **argv) { @@ -490,6 +491,7 @@ help_all(void) static int help_f( + BlockDriverState *bs, int argc, char **argv) { diff --git a/cmd.h b/cmd.h index 4dcfe885a4..ccf6336e8e 100644 --- a/cmd.h +++ b/cmd.h @@ -17,9 +17,13 @@ #ifndef __COMMAND_H__ #define __COMMAND_H__ +#include "qemu-common.h" + #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ -typedef int (*cfunc_t)(int argc, char **argv); +extern BlockDriverState *qemuio_bs; + +typedef int (*cfunc_t)(BlockDriverState *bs, int argc, char **argv); typedef void (*helpfunc_t)(void); typedef struct cmdinfo { @@ -41,7 +45,7 @@ extern int ncmds; void help_init(void); void quit_init(void); -typedef int (*checkfunc_t)(const cmdinfo_t *ci); +typedef int (*checkfunc_t)(BlockDriverState *bs, const cmdinfo_t *ci); void add_command(const cmdinfo_t *ci); void add_user_command(char *optarg); diff --git a/qemu-io.c b/qemu-io.c index b4f56fc59f..39d7063816 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -25,8 +25,8 @@ #define CMD_NOFILE_OK 0x01 char *progname; -static BlockDriverState *bs; +BlockDriverState *qemuio_bs; static int misalign; static int64_t cvtnum(const char *s) @@ -63,7 +63,7 @@ static int parse_pattern(const char *arg) */ #define MISALIGN_OFFSET 16 -static void *qemu_io_alloc(size_t len, int pattern) +static void *qemu_io_alloc(BlockDriverState *bs, size_t len, int pattern) { void *buf; @@ -136,7 +136,8 @@ static void print_report(const char *op, struct timeval *t, int64_t offset, * vector matching it. */ static void * -create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) +create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov, + int pattern) { size_t *sizes = g_new0(size_t, nr_iov); size_t count = 0; @@ -172,7 +173,7 @@ create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) qemu_iovec_init(qiov, nr_iov); - buf = p = qemu_io_alloc(count, pattern); + buf = p = qemu_io_alloc(bs, count, pattern); for (i = 0; i < nr_iov; i++) { qemu_iovec_add(qiov, p, sizes[i]); @@ -184,7 +185,8 @@ fail: return buf; } -static int do_read(char *buf, int64_t offset, int count, int *total) +static int do_read(BlockDriverState *bs, char *buf, int64_t offset, int count, + int *total) { int ret; @@ -196,7 +198,8 @@ static int do_read(char *buf, int64_t offset, int count, int *total) return 1; } -static int do_write(char *buf, int64_t offset, int count, int *total) +static int do_write(BlockDriverState *bs, char *buf, int64_t offset, int count, + int *total) { int ret; @@ -208,7 +211,8 @@ static int do_write(char *buf, int64_t offset, int count, int *total) return 1; } -static int do_pread(char *buf, int64_t offset, int count, int *total) +static int do_pread(BlockDriverState *bs, char *buf, int64_t offset, int count, + int *total) { *total = bdrv_pread(bs, offset, (uint8_t *)buf, count); if (*total < 0) { @@ -217,7 +221,8 @@ static int do_pread(char *buf, int64_t offset, int count, int *total) return 1; } -static int do_pwrite(char *buf, int64_t offset, int count, int *total) +static int do_pwrite(BlockDriverState *bs, char *buf, int64_t offset, int count, + int *total) { *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count); if (*total < 0) { @@ -227,6 +232,7 @@ static int do_pwrite(char *buf, int64_t offset, int count, int *total) } typedef struct { + BlockDriverState *bs; int64_t offset; int count; int *total; @@ -238,7 +244,7 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque) { CoWriteZeroes *data = opaque; - data->ret = bdrv_co_write_zeroes(bs, data->offset / BDRV_SECTOR_SIZE, + data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE, data->count / BDRV_SECTOR_SIZE); data->done = true; if (data->ret < 0) { @@ -249,10 +255,12 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque) *data->total = data->count; } -static int do_co_write_zeroes(int64_t offset, int count, int *total) +static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, + int *total) { Coroutine *co; CoWriteZeroes data = { + .bs = bs, .offset = offset, .count = count, .total = total, @@ -271,7 +279,8 @@ static int do_co_write_zeroes(int64_t offset, int count, int *total) } } -static int do_write_compressed(char *buf, int64_t offset, int count, int *total) +static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) { int ret; @@ -283,7 +292,8 @@ static int do_write_compressed(char *buf, int64_t offset, int count, int *total) return 1; } -static int do_load_vmstate(char *buf, int64_t offset, int count, int *total) +static int do_load_vmstate(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) { *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); if (*total < 0) { @@ -292,7 +302,8 @@ static int do_load_vmstate(char *buf, int64_t offset, int count, int *total) return 1; } -static int do_save_vmstate(char *buf, int64_t offset, int count, int *total) +static int do_save_vmstate(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) { *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count); if (*total < 0) { @@ -307,7 +318,8 @@ static void aio_rw_done(void *opaque, int ret) *(int *)opaque = ret; } -static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) +static int do_aio_readv(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t offset, int *total) { int async_ret = NOT_DONE; @@ -321,7 +333,8 @@ static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) return async_ret < 0 ? async_ret : 1; } -static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) +static int do_aio_writev(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t offset, int *total) { int async_ret = NOT_DONE; @@ -350,7 +363,8 @@ static void multiwrite_cb(void *opaque, int ret) } } -static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) +static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs, + int num_reqs, int *total) { int i, ret; struct multiwrite_async_ret async_ret = { @@ -399,7 +413,7 @@ static void read_help(void) "\n"); } -static int read_f(int argc, char **argv); +static int read_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t read_cmd = { .name = "read", @@ -412,7 +426,7 @@ static const cmdinfo_t read_cmd = { .help = read_help, }; -static int read_f(int argc, char **argv) +static int read_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; @@ -518,15 +532,15 @@ static int read_f(int argc, char **argv) } } - buf = qemu_io_alloc(count, 0xab); + buf = qemu_io_alloc(bs, count, 0xab); gettimeofday(&t1, NULL); if (pflag) { - cnt = do_pread(buf, offset, count, &total); + cnt = do_pread(bs, buf, offset, count, &total); } else if (bflag) { - cnt = do_load_vmstate(buf, offset, count, &total); + cnt = do_load_vmstate(bs, buf, offset, count, &total); } else { - cnt = do_read(buf, offset, count, &total); + cnt = do_read(bs, buf, offset, count, &total); } gettimeofday(&t2, NULL); @@ -583,7 +597,7 @@ static void readv_help(void) "\n"); } -static int readv_f(int argc, char **argv); +static int readv_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t readv_cmd = { .name = "readv", @@ -595,7 +609,7 @@ static const cmdinfo_t readv_cmd = { .help = readv_help, }; -static int readv_f(int argc, char **argv) +static int readv_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0, vflag = 0; @@ -651,13 +665,13 @@ static int readv_f(int argc, char **argv) } nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab); + buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, 0xab); if (buf == NULL) { return 0; } gettimeofday(&t1, NULL); - cnt = do_aio_readv(&qiov, offset, &total); + cnt = do_aio_readv(bs, &qiov, offset, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -714,7 +728,7 @@ static void write_help(void) "\n"); } -static int write_f(int argc, char **argv); +static int write_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t write_cmd = { .name = "write", @@ -727,7 +741,7 @@ static const cmdinfo_t write_cmd = { .help = write_help, }; -static int write_f(int argc, char **argv) +static int write_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; @@ -814,20 +828,20 @@ static int write_f(int argc, char **argv) } if (!zflag) { - buf = qemu_io_alloc(count, pattern); + buf = qemu_io_alloc(bs, count, pattern); } gettimeofday(&t1, NULL); if (pflag) { - cnt = do_pwrite(buf, offset, count, &total); + cnt = do_pwrite(bs, buf, offset, count, &total); } else if (bflag) { - cnt = do_save_vmstate(buf, offset, count, &total); + cnt = do_save_vmstate(bs, buf, offset, count, &total); } else if (zflag) { - cnt = do_co_write_zeroes(offset, count, &total); + cnt = do_co_write_zeroes(bs, offset, count, &total); } else if (cflag) { - cnt = do_write_compressed(buf, offset, count, &total); + cnt = do_write_compressed(bs, buf, offset, count, &total); } else { - cnt = do_write(buf, offset, count, &total); + cnt = do_write(bs, buf, offset, count, &total); } gettimeofday(&t2, NULL); @@ -870,7 +884,7 @@ writev_help(void) "\n"); } -static int writev_f(int argc, char **argv); +static int writev_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t writev_cmd = { .name = "writev", @@ -882,7 +896,7 @@ static const cmdinfo_t writev_cmd = { .help = writev_help, }; -static int writev_f(int argc, char **argv) +static int writev_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -932,13 +946,13 @@ static int writev_f(int argc, char **argv) } nr_iov = argc - optind; - buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern); + buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, pattern); if (buf == NULL) { return 0; } gettimeofday(&t1, NULL); - cnt = do_aio_writev(&qiov, offset, &total); + cnt = do_aio_writev(bs, &qiov, offset, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -979,7 +993,7 @@ static void multiwrite_help(void) "\n"); } -static int multiwrite_f(int argc, char **argv); +static int multiwrite_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t multiwrite_cmd = { .name = "multiwrite", @@ -991,7 +1005,7 @@ static const cmdinfo_t multiwrite_cmd = { .help = multiwrite_help, }; -static int multiwrite_f(int argc, char **argv) +static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -1072,7 +1086,7 @@ static int multiwrite_f(int argc, char **argv) nr_iov = j - optind; /* Build request */ - buf[i] = create_iovec(&qiovs[i], &argv[optind], nr_iov, pattern); + buf[i] = create_iovec(bs, &qiovs[i], &argv[optind], nr_iov, pattern); if (buf[i] == NULL) { goto out; } @@ -1090,7 +1104,7 @@ static int multiwrite_f(int argc, char **argv) nr_reqs = i; gettimeofday(&t1, NULL); - cnt = do_aio_multiwrite(reqs, nr_reqs, &total); + cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total); gettimeofday(&t2, NULL); if (cnt < 0) { @@ -1218,7 +1232,7 @@ static void aio_read_help(void) "\n"); } -static int aio_read_f(int argc, char **argv); +static int aio_read_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t aio_read_cmd = { .name = "aio_read", @@ -1230,7 +1244,7 @@ static const cmdinfo_t aio_read_cmd = { .help = aio_read_help, }; -static int aio_read_f(int argc, char **argv) +static int aio_read_f(BlockDriverState *bs, int argc, char **argv) { int nr_iov, c; struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); @@ -1281,7 +1295,7 @@ static int aio_read_f(int argc, char **argv) } nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); + ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, 0xab); if (ctx->buf == NULL) { g_free(ctx); return 0; @@ -1313,7 +1327,7 @@ static void aio_write_help(void) "\n"); } -static int aio_write_f(int argc, char **argv); +static int aio_write_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t aio_write_cmd = { .name = "aio_write", @@ -1325,7 +1339,7 @@ static const cmdinfo_t aio_write_cmd = { .help = aio_write_help, }; -static int aio_write_f(int argc, char **argv) +static int aio_write_f(BlockDriverState *bs, int argc, char **argv) { int nr_iov, c; int pattern = 0xcd; @@ -1373,7 +1387,7 @@ static int aio_write_f(int argc, char **argv) } nr_iov = argc - optind; - ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); + ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, pattern); if (ctx->buf == NULL) { g_free(ctx); return 0; @@ -1385,7 +1399,7 @@ static int aio_write_f(int argc, char **argv) return 0; } -static int aio_flush_f(int argc, char **argv) +static int aio_flush_f(BlockDriverState *bs, int argc, char **argv) { bdrv_drain_all(); return 0; @@ -1397,7 +1411,7 @@ static const cmdinfo_t aio_flush_cmd = { .oneline = "completes all outstanding aio requests" }; -static int flush_f(int argc, char **argv) +static int flush_f(BlockDriverState *bs, int argc, char **argv) { bdrv_flush(bs); return 0; @@ -1410,7 +1424,7 @@ static const cmdinfo_t flush_cmd = { .oneline = "flush all in-core file state to disk", }; -static int truncate_f(int argc, char **argv) +static int truncate_f(BlockDriverState *bs, int argc, char **argv) { int64_t offset; int ret; @@ -1440,7 +1454,7 @@ static const cmdinfo_t truncate_cmd = { .oneline = "truncates the current file at the given offset", }; -static int length_f(int argc, char **argv) +static int length_f(BlockDriverState *bs, int argc, char **argv) { int64_t size; char s1[64]; @@ -1465,7 +1479,7 @@ static const cmdinfo_t length_cmd = { }; -static int info_f(int argc, char **argv) +static int info_f(BlockDriverState *bs, int argc, char **argv) { BlockDriverInfo bdi; char s1[64], s2[64]; @@ -1516,7 +1530,7 @@ static void discard_help(void) "\n"); } -static int discard_f(int argc, char **argv); +static int discard_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t discard_cmd = { .name = "discard", @@ -1529,7 +1543,7 @@ static const cmdinfo_t discard_cmd = { .help = discard_help, }; -static int discard_f(int argc, char **argv) +static int discard_f(BlockDriverState *bs, int argc, char **argv) { struct timeval t1, t2; int Cflag = 0, qflag = 0; @@ -1587,7 +1601,7 @@ out: return 0; } -static int alloc_f(int argc, char **argv) +static int alloc_f(BlockDriverState *bs, int argc, char **argv) { int64_t offset, sector_num; int nb_sectors, remaining; @@ -1649,7 +1663,8 @@ static const cmdinfo_t alloc_cmd = { }; -static int map_is_allocated(int64_t sector_num, int64_t nb_sectors, int64_t *pnum) +static int map_is_allocated(BlockDriverState *bs, int64_t sector_num, + int64_t nb_sectors, int64_t *pnum) { int num, num_checked; int ret, firstret; @@ -1679,7 +1694,7 @@ static int map_is_allocated(int64_t sector_num, int64_t nb_sectors, int64_t *pnu return firstret; } -static int map_f(int argc, char **argv) +static int map_f(BlockDriverState *bs, int argc, char **argv) { int64_t offset; int64_t nb_sectors; @@ -1692,7 +1707,7 @@ static int map_f(int argc, char **argv) nb_sectors = bs->total_sectors; do { - ret = map_is_allocated(offset, nb_sectors, &num); + ret = map_is_allocated(bs, offset, nb_sectors, &num); if (ret < 0) { error_report("Failed to get allocation status: %s", strerror(-ret)); return 0; @@ -1720,7 +1735,7 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; -static int break_f(int argc, char **argv) +static int break_f(BlockDriverState *bs, int argc, char **argv) { int ret; @@ -1742,7 +1757,7 @@ static const cmdinfo_t break_cmd = { "request as tag", }; -static int resume_f(int argc, char **argv) +static int resume_f(BlockDriverState *bs, int argc, char **argv) { int ret; @@ -1763,7 +1778,7 @@ static const cmdinfo_t resume_cmd = { .oneline = "resumes the request tagged as tag", }; -static int wait_break_f(int argc, char **argv) +static int wait_break_f(BlockDriverState *bs, int argc, char **argv) { while (!bdrv_debug_is_suspended(bs, argv[1])) { qemu_aio_wait(); @@ -1781,7 +1796,7 @@ static const cmdinfo_t wait_break_cmd = { .oneline = "waits for the suspension of a request", }; -static int abort_f(int argc, char **argv) +static int abort_f(BlockDriverState *bs, int argc, char **argv) { abort(); } @@ -1793,10 +1808,10 @@ static const cmdinfo_t abort_cmd = { .oneline = "simulate a program crash using abort(3)", }; -static int close_f(int argc, char **argv) +static int close_f(BlockDriverState *bs, int argc, char **argv) { bdrv_delete(bs); - bs = NULL; + qemuio_bs = NULL; return 0; } @@ -1809,23 +1824,23 @@ static const cmdinfo_t close_cmd = { static int openfile(char *name, int flags, int growable) { - if (bs) { + if (qemuio_bs) { fprintf(stderr, "file open already, try 'help close'\n"); return 1; } if (growable) { - if (bdrv_file_open(&bs, name, NULL, flags)) { + if (bdrv_file_open(&qemuio_bs, name, NULL, flags)) { fprintf(stderr, "%s: can't open device %s\n", progname, name); return 1; } } else { - bs = bdrv_new("hda"); + qemuio_bs = bdrv_new("hda"); - if (bdrv_open(bs, name, NULL, flags, NULL) < 0) { + if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) { fprintf(stderr, "%s: can't open device %s\n", progname, name); - bdrv_delete(bs); - bs = NULL; + bdrv_delete(qemuio_bs); + qemuio_bs = NULL; return 1; } } @@ -1850,7 +1865,7 @@ static void open_help(void) "\n"); } -static int open_f(int argc, char **argv); +static int open_f(BlockDriverState *bs, int argc, char **argv); static const cmdinfo_t open_cmd = { .name = "open", @@ -1864,7 +1879,7 @@ static const cmdinfo_t open_cmd = { .help = open_help, }; -static int open_f(int argc, char **argv) +static int open_f(BlockDriverState *bs, int argc, char **argv) { int flags = 0; int readonly = 0; @@ -1901,7 +1916,7 @@ static int open_f(int argc, char **argv) return openfile(argv[optind], flags, growable); } -static int init_check_command(const cmdinfo_t *ct) +static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct) { if (ct->flags & CMD_FLAG_GLOBAL) { return 1; @@ -2064,8 +2079,8 @@ int main(int argc, char **argv) */ bdrv_drain_all(); - if (bs) { - bdrv_delete(bs); + if (qemuio_bs) { + bdrv_delete(qemuio_bs); } return 0; } -- cgit v1.2.1 From 797ac58cb2093ab9192d8998a1fef85d87cc8661 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:31 +0200 Subject: qemu-io: Split off commands to qemu-io-cmds.c This is the implementation of all qemu-io commands that make sense to be called from the qemu monitor, i.e. everything except open, close and quit. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- Makefile | 2 +- qemu-io-cmds.c | 1835 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-io.c | 1817 +------------------------------------------------------ 3 files changed, 1838 insertions(+), 1816 deletions(-) create mode 100644 qemu-io-cmds.c diff --git a/Makefile b/Makefile index a96736b06b..cf932eb539 100644 --- a/Makefile +++ b/Makefile @@ -186,7 +186,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o qemu-io-cmds.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c new file mode 100644 index 0000000000..0a3817ae7a --- /dev/null +++ b/qemu-io-cmds.c @@ -0,0 +1,1835 @@ +/* + * 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 "qemu-common.h" +#include "block/block_int.h" +#include "cmd.h" + +#define CMD_NOFILE_OK 0x01 + +int qemuio_misalign; + +static int64_t cvtnum(const char *s) +{ + char *end; + return strtosz_suffix(s, &end, STRTOSZ_DEFSUFFIX_B); +} + +/* + * Parse the pattern argument to various sub-commands. + * + * Because the pattern is used as an argument to memset it must evaluate + * to an unsigned integer that fits into a single byte. + */ +static int parse_pattern(const char *arg) +{ + char *endptr = NULL; + long pattern; + + pattern = strtol(arg, &endptr, 0); + if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { + printf("%s is not a valid pattern byte\n", arg); + return -1; + } + + return pattern; +} + +/* + * 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(BlockDriverState *bs, size_t len, int pattern) +{ + void *buf; + + if (qemuio_misalign) { + len += MISALIGN_OFFSET; + } + buf = qemu_blockalign(bs, len); + memset(buf, pattern, len); + if (qemuio_misalign) { + buf += MISALIGN_OFFSET; + } + return buf; +} + +static void qemu_io_free(void *p) +{ + if (qemuio_misalign) { + p -= MISALIGN_OFFSET; + } + qemu_vfree(p); +} + +static void dump_buffer(const void *buffer, int64_t offset, int len) +{ + int i, j; + const uint8_t *p; + + for (i = 0, p = buffer; i < len; i += 16) { + const uint8_t *s = p; + + printf("%08" PRIx64 ": ", 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(*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 %" PRId64 "\n", + op, total, count, 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)); + } +} + +/* + * Parse multiple length statements for vectored I/O, and construct an I/O + * vector matching it. + */ +static void * +create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov, + int pattern) +{ + size_t *sizes = g_new0(size_t, nr_iov); + size_t count = 0; + void *buf = NULL; + void *p; + int i; + + for (i = 0; i < nr_iov; i++) { + char *arg = argv[i]; + int64_t len; + + len = cvtnum(arg); + if (len < 0) { + printf("non-numeric length argument -- %s\n", arg); + goto fail; + } + + /* should be SIZE_T_MAX, but that doesn't exist */ + if (len > INT_MAX) { + printf("too large length argument -- %s\n", arg); + goto fail; + } + + if (len & 0x1ff) { + printf("length argument %" PRId64 + " is not sector aligned\n", len); + goto fail; + } + + sizes[i] = len; + count += len; + } + + qemu_iovec_init(qiov, nr_iov); + + buf = p = qemu_io_alloc(bs, count, pattern); + + for (i = 0; i < nr_iov; i++) { + qemu_iovec_add(qiov, p, sizes[i]); + p += sizes[i]; + } + +fail: + g_free(sizes); + return buf; +} + +static int do_read(BlockDriverState *bs, 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(BlockDriverState *bs, 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(BlockDriverState *bs, 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(BlockDriverState *bs, 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; +} + +typedef struct { + BlockDriverState *bs; + int64_t offset; + int count; + int *total; + int ret; + bool done; +} CoWriteZeroes; + +static void coroutine_fn co_write_zeroes_entry(void *opaque) +{ + CoWriteZeroes *data = opaque; + + data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE, + data->count / BDRV_SECTOR_SIZE); + data->done = true; + if (data->ret < 0) { + *data->total = data->ret; + return; + } + + *data->total = data->count; +} + +static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, + int *total) +{ + Coroutine *co; + CoWriteZeroes data = { + .bs = bs, + .offset = offset, + .count = count, + .total = total, + .done = false, + }; + + co = qemu_coroutine_create(co_write_zeroes_entry); + qemu_coroutine_enter(co, &data); + while (!data.done) { + qemu_aio_wait(); + } + if (data.ret < 0) { + return data.ret; + } else { + return 1; + } +} + +static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) +{ + int ret; + + ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9); + if (ret < 0) { + return ret; + } + *total = count; + return 1; +} + +static int do_load_vmstate(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) +{ + *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); + if (*total < 0) { + return *total; + } + return 1; +} + +static int do_save_vmstate(BlockDriverState *bs, char *buf, int64_t offset, + int count, int *total) +{ + *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, 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(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t offset, int *total) +{ + int async_ret = NOT_DONE; + + bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + while (async_ret == NOT_DONE) { + main_loop_wait(false); + } + + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; +} + +static int do_aio_writev(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t offset, int *total) +{ + int async_ret = NOT_DONE; + + bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, + aio_rw_done, &async_ret); + while (async_ret == NOT_DONE) { + main_loop_wait(false); + } + + *total = qiov->size; + return async_ret < 0 ? async_ret : 1; +} + +struct multiwrite_async_ret { + int num_done; + int error; +}; + +static void multiwrite_cb(void *opaque, int ret) +{ + struct multiwrite_async_ret *async_ret = opaque; + + async_ret->num_done++; + if (ret < 0) { + async_ret->error = ret; + } +} + +static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs, + int num_reqs, int *total) +{ + int i, ret; + struct multiwrite_async_ret async_ret = { + .num_done = 0, + .error = 0, + }; + + *total = 0; + for (i = 0; i < num_reqs; i++) { + reqs[i].cb = multiwrite_cb; + reqs[i].opaque = &async_ret; + *total += reqs[i].qiov->size; + } + + ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); + if (ret < 0) { + return ret; + } + + while (async_ret.num_done < num_reqs) { + main_loop_wait(false); + } + + return async_ret.error < 0 ? async_ret.error : 1; +} + +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" +" -b, -- read from the VM state rather than the virtual disk\n" +" -C, -- report statistics in a machine parsable format\n" +" -l, -- length for pattern verification (only with -P)\n" +" -p, -- use bdrv_pread to read the file\n" +" -P, -- use a pattern to verify read data\n" +" -q, -- quiet mode, do not show I/O statistics\n" +" -s, -- start offset for pattern verification (only with -P)\n" +" -v, -- dump buffer to standard output\n" +"\n"); +} + +static int read_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t read_cmd = { + .name = "read", + .altname = "r", + .cfunc = read_f, + .argmin = 2, + .argmax = -1, + .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", + .oneline = "reads a number of bytes at a specified offset", + .help = read_help, +}; + +static int read_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; + int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; + int c, cnt; + char *buf; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0, pattern_offset = 0, pattern_count = 0; + + while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'l': + lflag = 1; + pattern_count = cvtnum(optarg); + if (pattern_count < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'p': + pflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 's': + sflag = 1; + pattern_offset = cvtnum(optarg); + if (pattern_offset < 0) { + printf("non-numeric length argument -- %s\n", optarg); + return 0; + } + break; + case 'v': + vflag = 1; + break; + default: + return command_usage(&read_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&read_cmd); + } + + if (bflag && pflag) { + printf("-b and -p cannot be specified at the same time\n"); + return 0; + } + + 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 && (lflag || sflag)) { + return command_usage(&read_cmd); + } + + if (!lflag) { + pattern_count = count - pattern_offset; + } + + if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { + printf("pattern verification range exceeds end of read data\n"); + return 0; + } + + if (!pflag) { + if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } + } + + buf = qemu_io_alloc(bs, count, 0xab); + + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pread(bs, buf, offset, count, &total); + } else if (bflag) { + cnt = do_load_vmstate(bs, buf, offset, count, &total); + } else { + cnt = do_read(bs, buf, offset, count, &total); + } + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("read failed: %s\n", strerror(-cnt)); + goto out; + } + + if (Pflag) { + void *cmp_buf = g_malloc(pattern_count); + memset(cmp_buf, pattern, pattern_count); + if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { + printf("Pattern verification failed at offset %" + PRId64 ", %d bytes\n", + offset + pattern_offset, pattern_count); + } + g_free(cmp_buf); + } + + if (qflag) { + goto out; + } + + 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); + +out: + qemu_io_free(buf); + + return 0; +} + +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" +" -P, -- use a pattern to verify read data\n" +" -v, -- dump buffer to standard output\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int readv_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t readv_cmd = { + .name = "readv", + .cfunc = readv_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "reads a number of bytes at a specified offset", + .help = readv_help, +}; + +static int readv_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0, vflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + QEMUIOVector qiov; + int pattern = 0; + int Pflag = 0; + + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + 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 %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + nr_iov = argc - optind; + buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, 0xab); + if (buf == NULL) { + return 0; + } + + gettimeofday(&t1, NULL); + cnt = do_aio_readv(bs, &qiov, offset, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("readv failed: %s\n", strerror(-cnt)); + goto out; + } + + if (Pflag) { + void *cmp_buf = g_malloc(qiov.size); + memset(cmp_buf, pattern, qiov.size); + if (memcmp(buf, cmp_buf, qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", offset, qiov.size); + } + g_free(cmp_buf); + } + + if (qflag) { + goto out; + } + + 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); + +out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); + return 0; +} + +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" +" -b, -- write to the VM state rather than the virtual disk\n" +" -c, -- write compressed data with bdrv_write_compressed\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, -- quiet mode, do not show I/O statistics\n" +" -z, -- write zeroes using bdrv_co_write_zeroes\n" +"\n"); +} + +static int write_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t write_cmd = { + .name = "write", + .altname = "w", + .cfunc = write_f, + .argmin = 2, + .argmax = -1, + .args = "[-bcCpqz] [-P pattern ] off len", + .oneline = "writes a number of bytes at a specified offset", + .help = write_help, +}; + +static int write_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; + int cflag = 0; + int c, cnt; + char *buf = NULL; + int64_t offset; + int count; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int pattern = 0xcd; + + while ((c = getopt(argc, argv, "bcCpP:qz")) != EOF) { + switch (c) { + case 'b': + bflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'C': + Cflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'P': + Pflag = 1; + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + case 'q': + qflag = 1; + break; + case 'z': + zflag = 1; + break; + default: + return command_usage(&write_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&write_cmd); + } + + if (bflag + pflag + zflag > 1) { + printf("-b, -p, or -z cannot be specified at the same time\n"); + return 0; + } + + if (zflag && Pflag) { + printf("-z and -P cannot be specified at the same time\n"); + return 0; + } + + 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 %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + if (count & 0x1ff) { + printf("count %d is not sector aligned\n", + count); + return 0; + } + } + + if (!zflag) { + buf = qemu_io_alloc(bs, count, pattern); + } + + gettimeofday(&t1, NULL); + if (pflag) { + cnt = do_pwrite(bs, buf, offset, count, &total); + } else if (bflag) { + cnt = do_save_vmstate(bs, buf, offset, count, &total); + } else if (zflag) { + cnt = do_co_write_zeroes(bs, offset, count, &total); + } else if (cflag) { + cnt = do_write_compressed(bs, buf, offset, count, &total); + } else { + cnt = do_write(bs, buf, offset, count, &total); + } + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("write failed: %s\n", strerror(-cnt)); + goto out; + } + + if (qflag) { + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, offset, count, total, cnt, Cflag); + +out: + if (!zflag) { + qemu_io_free(buf); + } + + return 0; +} + +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, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int writev_f(BlockDriverState *bs, int argc, char **argv); + +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 writev_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char *buf; + int64_t offset; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + 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 = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + 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 %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + nr_iov = argc - optind; + buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, pattern); + if (buf == NULL) { + return 0; + } + + gettimeofday(&t1, NULL); + cnt = do_aio_writev(bs, &qiov, offset, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("writev failed: %s\n", strerror(-cnt)); + goto out; + } + + if (qflag) { + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); +out: + qemu_iovec_destroy(&qiov); + qemu_io_free(buf); + return 0; +} + +static void multiwrite_help(void) +{ + printf( +"\n" +" writes a range of bytes from the given offset source from multiple buffers,\n" +" in a batch of requests that may be merged by qemu\n" +"\n" +" Example:\n" +" 'multiwrite 512 1k 1k ; 4k 1k'\n" +" writes 2 kB at 512 bytes and 1 kB at 4 kB 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). The pattern byte is increased\n" +" by one for each request contained in the multiwrite command.\n" +" -P, -- use different pattern to fill file\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int multiwrite_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t multiwrite_cmd = { + .name = "multiwrite", + .cfunc = multiwrite_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", + .oneline = "issues multiple write requests at once", + .help = multiwrite_help, +}; + +static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char **buf; + int64_t offset, first_offset = 0; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + int nr_reqs; + int pattern = 0xcd; + QEMUIOVector *qiovs; + int i; + BlockRequest *reqs; + + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + return 0; + } + break; + default: + return command_usage(&writev_cmd); + } + } + + if (optind > argc - 2) { + return command_usage(&writev_cmd); + } + + nr_reqs = 1; + for (i = optind; i < argc; i++) { + if (!strcmp(argv[i], ";")) { + nr_reqs++; + } + } + + reqs = g_malloc0(nr_reqs * sizeof(*reqs)); + buf = g_malloc0(nr_reqs * sizeof(*buf)); + qiovs = g_malloc(nr_reqs * sizeof(*qiovs)); + + for (i = 0; i < nr_reqs && optind < argc; i++) { + int j; + + /* Read the offset of the request */ + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric offset argument -- %s\n", argv[optind]); + goto out; + } + optind++; + + if (offset & 0x1ff) { + printf("offset %lld is not sector aligned\n", + (long long)offset); + goto out; + } + + if (i == 0) { + first_offset = offset; + } + + /* Read lengths for qiov entries */ + for (j = optind; j < argc; j++) { + if (!strcmp(argv[j], ";")) { + break; + } + } + + nr_iov = j - optind; + + /* Build request */ + buf[i] = create_iovec(bs, &qiovs[i], &argv[optind], nr_iov, pattern); + if (buf[i] == NULL) { + goto out; + } + + reqs[i].qiov = &qiovs[i]; + reqs[i].sector = offset >> 9; + reqs[i].nb_sectors = reqs[i].qiov->size >> 9; + + optind = j + 1; + + pattern++; + } + + /* If there were empty requests at the end, ignore them */ + nr_reqs = i; + + gettimeofday(&t1, NULL); + cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("aio_multiwrite failed: %s\n", strerror(-cnt)); + goto out; + } + + if (qflag) { + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); +out: + for (i = 0; i < nr_reqs; i++) { + qemu_io_free(buf[i]); + if (reqs[i].qiov != NULL) { + qemu_iovec_destroy(&qiovs[i]); + } + } + g_free(buf); + g_free(reqs); + g_free(qiovs); + return 0; +} + +struct aio_ctx { + QEMUIOVector qiov; + int64_t offset; + char *buf; + int qflag; + int vflag; + int Cflag; + int Pflag; + int pattern; + struct timeval t1; +}; + +static void aio_write_done(void *opaque, int ret) +{ + struct aio_ctx *ctx = opaque; + struct timeval t2; + + gettimeofday(&t2, NULL); + + + if (ret < 0) { + printf("aio_write failed: %s\n", strerror(-ret)); + goto out; + } + + if (ctx->qflag) { + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("wrote", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); +out: + qemu_io_free(ctx->buf); + qemu_iovec_destroy(&ctx->qiov); + g_free(ctx); +} + +static void aio_read_done(void *opaque, int ret) +{ + struct aio_ctx *ctx = opaque; + struct timeval t2; + + gettimeofday(&t2, NULL); + + if (ret < 0) { + printf("readv failed: %s\n", strerror(-ret)); + goto out; + } + + if (ctx->Pflag) { + void *cmp_buf = g_malloc(ctx->qiov.size); + + memset(cmp_buf, ctx->pattern, ctx->qiov.size); + if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size); + } + g_free(cmp_buf); + } + + if (ctx->qflag) { + goto out; + } + + if (ctx->vflag) { + dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("read", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); +out: + qemu_io_free(ctx->buf); + qemu_iovec_destroy(&ctx->qiov); + g_free(ctx); +} + +static void aio_read_help(void) +{ + printf( +"\n" +" asynchronously reads a range of bytes from the given offset\n" +"\n" +" Example:\n" +" 'aio_read -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" +" The read is performed asynchronously and the aio_flush command must be\n" +" used to ensure all outstanding aio requests have been completed.\n" +" -C, -- report statistics in a machine parsable format\n" +" -P, -- use a pattern to verify read data\n" +" -v, -- dump buffer to standard output\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int aio_read_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t aio_read_cmd = { + .name = "aio_read", + .cfunc = aio_read_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cqv] [-P pattern ] off len [len..]", + .oneline = "asynchronously reads a number of bytes", + .help = aio_read_help, +}; + +static int aio_read_f(BlockDriverState *bs, int argc, char **argv) +{ + int nr_iov, c; + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); + + while ((c = getopt(argc, argv, "CP:qv")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'P': + ctx->Pflag = 1; + ctx->pattern = parse_pattern(optarg); + if (ctx->pattern < 0) { + g_free(ctx); + return 0; + } + break; + case 'q': + ctx->qflag = 1; + break; + case 'v': + ctx->vflag = 1; + break; + default: + g_free(ctx); + return command_usage(&aio_read_cmd); + } + } + + if (optind > argc - 2) { + g_free(ctx); + return command_usage(&aio_read_cmd); + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + g_free(ctx); + return 0; + } + optind++; + + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + g_free(ctx); + return 0; + } + + nr_iov = argc - optind; + ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, 0xab); + if (ctx->buf == NULL) { + g_free(ctx); + return 0; + } + + gettimeofday(&ctx->t1, NULL); + bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_read_done, ctx); + return 0; +} + +static void aio_write_help(void) +{ + printf( +"\n" +" asynchronously writes a range of bytes from the given offset source\n" +" from multiple buffers\n" +"\n" +" Example:\n" +" 'aio_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" +" The write is performed asynchronously and the aio_flush command must be\n" +" used to ensure all outstanding aio requests have been completed.\n" +" -P, -- use different pattern to fill file\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int aio_write_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t aio_write_cmd = { + .name = "aio_write", + .cfunc = aio_write_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..]", + .oneline = "asynchronously writes a number of bytes", + .help = aio_write_help, +}; + +static int aio_write_f(BlockDriverState *bs, int argc, char **argv) +{ + int nr_iov, c; + int pattern = 0xcd; + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); + + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + ctx->Cflag = 1; + break; + case 'q': + ctx->qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) { + g_free(ctx); + return 0; + } + break; + default: + g_free(ctx); + return command_usage(&aio_write_cmd); + } + } + + if (optind > argc - 2) { + g_free(ctx); + return command_usage(&aio_write_cmd); + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + g_free(ctx); + return 0; + } + optind++; + + if (ctx->offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); + g_free(ctx); + return 0; + } + + nr_iov = argc - optind; + ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, pattern); + if (ctx->buf == NULL) { + g_free(ctx); + return 0; + } + + gettimeofday(&ctx->t1, NULL); + bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, + ctx->qiov.size >> 9, aio_write_done, ctx); + return 0; +} + +static int aio_flush_f(BlockDriverState *bs, int argc, char **argv) +{ + bdrv_drain_all(); + return 0; +} + +static const cmdinfo_t aio_flush_cmd = { + .name = "aio_flush", + .cfunc = aio_flush_f, + .oneline = "completes all outstanding aio requests" +}; + +static int flush_f(BlockDriverState *bs, 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(BlockDriverState *bs, 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\n", 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(BlockDriverState *bs, int argc, char **argv) +{ + int64_t size; + char s1[64]; + + size = bdrv_getlength(bs); + if (size < 0) { + printf("getlength: %s\n", 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(BlockDriverState *bs, 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 void discard_help(void) +{ + printf( +"\n" +" discards a range of bytes from the given offset\n" +"\n" +" Example:\n" +" 'discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n" +"\n" +" Discards a segment of the currently open file.\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int discard_f(BlockDriverState *bs, int argc, char **argv); + +static const cmdinfo_t discard_cmd = { + .name = "discard", + .altname = "d", + .cfunc = discard_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] off len", + .oneline = "discards a number of bytes at a specified offset", + .help = discard_help, +}; + +static int discard_f(BlockDriverState *bs, int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, ret; + int64_t offset; + int count; + + while ((c = getopt(argc, argv, "Cq")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&discard_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&discard_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; + } + + gettimeofday(&t1, NULL); + ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, + count >> BDRV_SECTOR_BITS); + gettimeofday(&t2, NULL); + + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + if (!qflag) { + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, count, count, 1, Cflag); + } + +out: + return 0; +} + +static int alloc_f(BlockDriverState *bs, int argc, char **argv) +{ + int64_t offset, sector_num; + int nb_sectors, remaining; + char s1[64]; + int num, sum_alloc; + int ret; + + offset = cvtnum(argv[1]); + if (offset < 0) { + printf("non-numeric offset argument -- %s\n", argv[1]); + return 0; + } else if (offset & 0x1ff) { + printf("offset %" PRId64 " is not sector aligned\n", + offset); + return 0; + } + + if (argc == 3) { + nb_sectors = cvtnum(argv[2]); + if (nb_sectors < 0) { + printf("non-numeric length argument -- %s\n", argv[2]); + return 0; + } + } else { + nb_sectors = 1; + } + + remaining = nb_sectors; + sum_alloc = 0; + sector_num = offset >> 9; + while (remaining) { + ret = bdrv_is_allocated(bs, sector_num, remaining, &num); + sector_num += num; + remaining -= num; + if (ret) { + sum_alloc += num; + } + if (num == 0) { + nb_sectors -= remaining; + remaining = 0; + } + } + + cvtstr(offset, s1, sizeof(s1)); + + printf("%d/%d sectors allocated at offset %s\n", + sum_alloc, 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 map_is_allocated(BlockDriverState *bs, int64_t sector_num, + int64_t nb_sectors, int64_t *pnum) +{ + int num, num_checked; + int ret, firstret; + + num_checked = MIN(nb_sectors, INT_MAX); + ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); + if (ret < 0) { + return ret; + } + + firstret = ret; + *pnum = num; + + while (nb_sectors > 0 && ret == firstret) { + sector_num += num; + nb_sectors -= num; + + num_checked = MIN(nb_sectors, INT_MAX); + ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); + if (ret == firstret) { + *pnum += num; + } else { + break; + } + } + + return firstret; +} + +static int map_f(BlockDriverState *bs, int argc, char **argv) +{ + int64_t offset; + int64_t nb_sectors; + char s1[64]; + int64_t num; + int ret; + const char *retstr; + + offset = 0; + nb_sectors = bs->total_sectors; + + do { + ret = map_is_allocated(bs, offset, nb_sectors, &num); + if (ret < 0) { + error_report("Failed to get allocation status: %s", strerror(-ret)); + return 0; + } + + retstr = ret ? " allocated" : "not allocated"; + cvtstr(offset << 9ULL, s1, sizeof(s1)); + printf("[% 24" PRId64 "] % 8" PRId64 "/% 8" PRId64 " sectors %s " + "at offset %s (%d)\n", + offset << 9ULL, num, nb_sectors, retstr, s1, ret); + + offset += num; + nb_sectors -= num; + } while (offset < bs->total_sectors); + + return 0; +} + +static const cmdinfo_t map_cmd = { + .name = "map", + .argmin = 0, + .argmax = 0, + .cfunc = map_f, + .args = "", + .oneline = "prints the allocated areas of a file", +}; + +static int break_f(BlockDriverState *bs, int argc, char **argv) +{ + int ret; + + ret = bdrv_debug_breakpoint(bs, argv[1], argv[2]); + if (ret < 0) { + printf("Could not set breakpoint: %s\n", strerror(-ret)); + } + + return 0; +} + +static const cmdinfo_t break_cmd = { + .name = "break", + .argmin = 2, + .argmax = 2, + .cfunc = break_f, + .args = "event tag", + .oneline = "sets a breakpoint on event and tags the stopped " + "request as tag", +}; + +static int resume_f(BlockDriverState *bs, int argc, char **argv) +{ + int ret; + + ret = bdrv_debug_resume(bs, argv[1]); + if (ret < 0) { + printf("Could not resume request: %s\n", strerror(-ret)); + } + + return 0; +} + +static const cmdinfo_t resume_cmd = { + .name = "resume", + .argmin = 1, + .argmax = 1, + .cfunc = resume_f, + .args = "tag", + .oneline = "resumes the request tagged as tag", +}; + +static int wait_break_f(BlockDriverState *bs, int argc, char **argv) +{ + while (!bdrv_debug_is_suspended(bs, argv[1])) { + qemu_aio_wait(); + } + + return 0; +} + +static const cmdinfo_t wait_break_cmd = { + .name = "wait_break", + .argmin = 1, + .argmax = 1, + .cfunc = wait_break_f, + .args = "tag", + .oneline = "waits for the suspension of a request", +}; + +static int abort_f(BlockDriverState *bs, int argc, char **argv) +{ + abort(); +} + +static const cmdinfo_t abort_cmd = { + .name = "abort", + .cfunc = abort_f, + .flags = CMD_NOFILE_OK, + .oneline = "simulate a program crash using abort(3)", +}; + +static int init_check_command(BlockDriverState *bs, 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 __attribute((constructor)) init_qemuio_commands(void) +{ + /* initialize commands */ + help_init(); + add_command(&read_cmd); + add_command(&readv_cmd); + add_command(&write_cmd); + add_command(&writev_cmd); + add_command(&multiwrite_cmd); + add_command(&aio_read_cmd); + add_command(&aio_write_cmd); + add_command(&aio_flush_cmd); + add_command(&flush_cmd); + add_command(&truncate_cmd); + add_command(&length_cmd); + add_command(&info_cmd); + add_command(&discard_cmd); + add_command(&alloc_cmd); + add_command(&map_cmd); + add_command(&break_cmd); + add_command(&resume_cmd); + add_command(&wait_break_cmd); + add_command(&abort_cmd); + + add_check_command(init_check_command); +} diff --git a/qemu-io.c b/qemu-io.c index 39d7063816..14eef2cc65 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -27,1786 +27,7 @@ char *progname; BlockDriverState *qemuio_bs; -static int misalign; - -static int64_t cvtnum(const char *s) -{ - char *end; - return strtosz_suffix(s, &end, STRTOSZ_DEFSUFFIX_B); -} - -/* - * Parse the pattern argument to various sub-commands. - * - * Because the pattern is used as an argument to memset it must evaluate - * to an unsigned integer that fits into a single byte. - */ -static int parse_pattern(const char *arg) -{ - char *endptr = NULL; - long pattern; - - pattern = strtol(arg, &endptr, 0); - if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') { - printf("%s is not a valid pattern byte\n", arg); - return -1; - } - - return pattern; -} - -/* - * 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(BlockDriverState *bs, size_t len, int pattern) -{ - void *buf; - - if (misalign) { - len += MISALIGN_OFFSET; - } - buf = qemu_blockalign(bs, 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(const void *buffer, int64_t offset, int len) -{ - int i, j; - const uint8_t *p; - - for (i = 0, p = buffer; i < len; i += 16) { - const uint8_t *s = p; - - printf("%08" PRIx64 ": ", 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(*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 %" PRId64 "\n", - op, total, count, 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)); - } -} - -/* - * Parse multiple length statements for vectored I/O, and construct an I/O - * vector matching it. - */ -static void * -create_iovec(BlockDriverState *bs, QEMUIOVector *qiov, char **argv, int nr_iov, - int pattern) -{ - size_t *sizes = g_new0(size_t, nr_iov); - size_t count = 0; - void *buf = NULL; - void *p; - int i; - - for (i = 0; i < nr_iov; i++) { - char *arg = argv[i]; - int64_t len; - - len = cvtnum(arg); - if (len < 0) { - printf("non-numeric length argument -- %s\n", arg); - goto fail; - } - - /* should be SIZE_T_MAX, but that doesn't exist */ - if (len > INT_MAX) { - printf("too large length argument -- %s\n", arg); - goto fail; - } - - if (len & 0x1ff) { - printf("length argument %" PRId64 - " is not sector aligned\n", len); - goto fail; - } - - sizes[i] = len; - count += len; - } - - qemu_iovec_init(qiov, nr_iov); - - buf = p = qemu_io_alloc(bs, count, pattern); - - for (i = 0; i < nr_iov; i++) { - qemu_iovec_add(qiov, p, sizes[i]); - p += sizes[i]; - } - -fail: - g_free(sizes); - return buf; -} - -static int do_read(BlockDriverState *bs, 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(BlockDriverState *bs, 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(BlockDriverState *bs, 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(BlockDriverState *bs, 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; -} - -typedef struct { - BlockDriverState *bs; - int64_t offset; - int count; - int *total; - int ret; - bool done; -} CoWriteZeroes; - -static void coroutine_fn co_write_zeroes_entry(void *opaque) -{ - CoWriteZeroes *data = opaque; - - data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE, - data->count / BDRV_SECTOR_SIZE); - data->done = true; - if (data->ret < 0) { - *data->total = data->ret; - return; - } - - *data->total = data->count; -} - -static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count, - int *total) -{ - Coroutine *co; - CoWriteZeroes data = { - .bs = bs, - .offset = offset, - .count = count, - .total = total, - .done = false, - }; - - co = qemu_coroutine_create(co_write_zeroes_entry); - qemu_coroutine_enter(co, &data); - while (!data.done) { - qemu_aio_wait(); - } - if (data.ret < 0) { - return data.ret; - } else { - return 1; - } -} - -static int do_write_compressed(BlockDriverState *bs, char *buf, int64_t offset, - int count, int *total) -{ - int ret; - - ret = bdrv_write_compressed(bs, offset >> 9, (uint8_t *)buf, count >> 9); - if (ret < 0) { - return ret; - } - *total = count; - return 1; -} - -static int do_load_vmstate(BlockDriverState *bs, char *buf, int64_t offset, - int count, int *total) -{ - *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); - if (*total < 0) { - return *total; - } - return 1; -} - -static int do_save_vmstate(BlockDriverState *bs, char *buf, int64_t offset, - int count, int *total) -{ - *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, 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(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t offset, int *total) -{ - int async_ret = NOT_DONE; - - bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - while (async_ret == NOT_DONE) { - main_loop_wait(false); - } - - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; -} - -static int do_aio_writev(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t offset, int *total) -{ - int async_ret = NOT_DONE; - - bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, - aio_rw_done, &async_ret); - while (async_ret == NOT_DONE) { - main_loop_wait(false); - } - - *total = qiov->size; - return async_ret < 0 ? async_ret : 1; -} - -struct multiwrite_async_ret { - int num_done; - int error; -}; - -static void multiwrite_cb(void *opaque, int ret) -{ - struct multiwrite_async_ret *async_ret = opaque; - - async_ret->num_done++; - if (ret < 0) { - async_ret->error = ret; - } -} - -static int do_aio_multiwrite(BlockDriverState *bs, BlockRequest* reqs, - int num_reqs, int *total) -{ - int i, ret; - struct multiwrite_async_ret async_ret = { - .num_done = 0, - .error = 0, - }; - - *total = 0; - for (i = 0; i < num_reqs; i++) { - reqs[i].cb = multiwrite_cb; - reqs[i].opaque = &async_ret; - *total += reqs[i].qiov->size; - } - - ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); - if (ret < 0) { - return ret; - } - - while (async_ret.num_done < num_reqs) { - main_loop_wait(false); - } - - return async_ret.error < 0 ? async_ret.error : 1; -} - -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" -" -b, -- read from the VM state rather than the virtual disk\n" -" -C, -- report statistics in a machine parsable format\n" -" -l, -- length for pattern verification (only with -P)\n" -" -p, -- use bdrv_pread to read the file\n" -" -P, -- use a pattern to verify read data\n" -" -q, -- quiet mode, do not show I/O statistics\n" -" -s, -- start offset for pattern verification (only with -P)\n" -" -v, -- dump buffer to standard output\n" -"\n"); -} - -static int read_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t read_cmd = { - .name = "read", - .altname = "r", - .cfunc = read_f, - .argmin = 2, - .argmax = -1, - .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len", - .oneline = "reads a number of bytes at a specified offset", - .help = read_help, -}; - -static int read_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, vflag = 0; - int Pflag = 0, sflag = 0, lflag = 0, bflag = 0; - int c, cnt; - char *buf; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0, pattern_offset = 0, pattern_count = 0; - - while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'l': - lflag = 1; - pattern_count = cvtnum(optarg); - if (pattern_count < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'p': - pflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - break; - case 'q': - qflag = 1; - break; - case 's': - sflag = 1; - pattern_offset = cvtnum(optarg); - if (pattern_offset < 0) { - printf("non-numeric length argument -- %s\n", optarg); - return 0; - } - break; - case 'v': - vflag = 1; - break; - default: - return command_usage(&read_cmd); - } - } - - if (optind != argc - 2) { - return command_usage(&read_cmd); - } - - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); - return 0; - } - - 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 && (lflag || sflag)) { - return command_usage(&read_cmd); - } - - if (!lflag) { - pattern_count = count - pattern_offset; - } - - if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { - printf("pattern verification range exceeds end of read data\n"); - return 0; - } - - if (!pflag) { - if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } - - buf = qemu_io_alloc(bs, count, 0xab); - - gettimeofday(&t1, NULL); - if (pflag) { - cnt = do_pread(bs, buf, offset, count, &total); - } else if (bflag) { - cnt = do_load_vmstate(bs, buf, offset, count, &total); - } else { - cnt = do_read(bs, buf, offset, count, &total); - } - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("read failed: %s\n", strerror(-cnt)); - goto out; - } - - if (Pflag) { - void *cmp_buf = g_malloc(pattern_count); - memset(cmp_buf, pattern, pattern_count); - if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { - printf("Pattern verification failed at offset %" - PRId64 ", %d bytes\n", - offset + pattern_offset, pattern_count); - } - g_free(cmp_buf); - } - - if (qflag) { - goto out; - } - - 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); - -out: - qemu_io_free(buf); - - return 0; -} - -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" -" -P, -- use a pattern to verify read data\n" -" -v, -- dump buffer to standard output\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int readv_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t readv_cmd = { - .name = "readv", - .cfunc = readv_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "reads a number of bytes at a specified offset", - .help = readv_help, -}; - -static int readv_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, qflag = 0, vflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - QEMUIOVector qiov; - int pattern = 0; - int Pflag = 0; - - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - 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 %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - nr_iov = argc - optind; - buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, 0xab); - if (buf == NULL) { - return 0; - } - - gettimeofday(&t1, NULL); - cnt = do_aio_readv(bs, &qiov, offset, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("readv failed: %s\n", strerror(-cnt)); - goto out; - } - - if (Pflag) { - void *cmp_buf = g_malloc(qiov.size); - memset(cmp_buf, pattern, qiov.size); - if (memcmp(buf, cmp_buf, qiov.size)) { - printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", offset, qiov.size); - } - g_free(cmp_buf); - } - - if (qflag) { - goto out; - } - - 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); - -out: - qemu_iovec_destroy(&qiov); - qemu_io_free(buf); - return 0; -} - -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" -" -b, -- write to the VM state rather than the virtual disk\n" -" -c, -- write compressed data with bdrv_write_compressed\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, -- quiet mode, do not show I/O statistics\n" -" -z, -- write zeroes using bdrv_co_write_zeroes\n" -"\n"); -} - -static int write_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t write_cmd = { - .name = "write", - .altname = "w", - .cfunc = write_f, - .argmin = 2, - .argmax = -1, - .args = "[-bcCpqz] [-P pattern ] off len", - .oneline = "writes a number of bytes at a specified offset", - .help = write_help, -}; - -static int write_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; - int cflag = 0; - int c, cnt; - char *buf = NULL; - int64_t offset; - int count; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int pattern = 0xcd; - - while ((c = getopt(argc, argv, "bcCpP:qz")) != EOF) { - switch (c) { - case 'b': - bflag = 1; - break; - case 'c': - cflag = 1; - break; - case 'C': - Cflag = 1; - break; - case 'p': - pflag = 1; - break; - case 'P': - Pflag = 1; - pattern = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - break; - case 'q': - qflag = 1; - break; - case 'z': - zflag = 1; - break; - default: - return command_usage(&write_cmd); - } - } - - if (optind != argc - 2) { - return command_usage(&write_cmd); - } - - if (bflag + pflag + zflag > 1) { - printf("-b, -p, or -z cannot be specified at the same time\n"); - return 0; - } - - if (zflag && Pflag) { - printf("-z and -P cannot be specified at the same time\n"); - return 0; - } - - 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 %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - if (count & 0x1ff) { - printf("count %d is not sector aligned\n", - count); - return 0; - } - } - - if (!zflag) { - buf = qemu_io_alloc(bs, count, pattern); - } - - gettimeofday(&t1, NULL); - if (pflag) { - cnt = do_pwrite(bs, buf, offset, count, &total); - } else if (bflag) { - cnt = do_save_vmstate(bs, buf, offset, count, &total); - } else if (zflag) { - cnt = do_co_write_zeroes(bs, offset, count, &total); - } else if (cflag) { - cnt = do_write_compressed(bs, buf, offset, count, &total); - } else { - cnt = do_write(bs, buf, offset, count, &total); - } - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("write failed: %s\n", strerror(-cnt)); - goto out; - } - - if (qflag) { - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, offset, count, total, cnt, Cflag); - -out: - if (!zflag) { - qemu_io_free(buf); - } - - return 0; -} - -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, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int writev_f(BlockDriverState *bs, int argc, char **argv); - -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 writev_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, cnt; - char *buf; - int64_t offset; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - 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 = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - 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 %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - nr_iov = argc - optind; - buf = create_iovec(bs, &qiov, &argv[optind], nr_iov, pattern); - if (buf == NULL) { - return 0; - } - - gettimeofday(&t1, NULL); - cnt = do_aio_writev(bs, &qiov, offset, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("writev failed: %s\n", strerror(-cnt)); - goto out; - } - - if (qflag) { - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); -out: - qemu_iovec_destroy(&qiov); - qemu_io_free(buf); - return 0; -} - -static void multiwrite_help(void) -{ - printf( -"\n" -" writes a range of bytes from the given offset source from multiple buffers,\n" -" in a batch of requests that may be merged by qemu\n" -"\n" -" Example:\n" -" 'multiwrite 512 1k 1k ; 4k 1k'\n" -" writes 2 kB at 512 bytes and 1 kB at 4 kB 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). The pattern byte is increased\n" -" by one for each request contained in the multiwrite command.\n" -" -P, -- use different pattern to fill file\n" -" -C, -- report statistics in a machine parsable format\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int multiwrite_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t multiwrite_cmd = { - .name = "multiwrite", - .cfunc = multiwrite_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", - .oneline = "issues multiple write requests at once", - .help = multiwrite_help, -}; - -static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, cnt; - char **buf; - int64_t offset, first_offset = 0; - /* Some compilers get confused and warn if this is not initialized. */ - int total = 0; - int nr_iov; - int nr_reqs; - int pattern = 0xcd; - QEMUIOVector *qiovs; - int i; - BlockRequest *reqs; - - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) { - return 0; - } - break; - default: - return command_usage(&writev_cmd); - } - } - - if (optind > argc - 2) { - return command_usage(&writev_cmd); - } - - nr_reqs = 1; - for (i = optind; i < argc; i++) { - if (!strcmp(argv[i], ";")) { - nr_reqs++; - } - } - - reqs = g_malloc0(nr_reqs * sizeof(*reqs)); - buf = g_malloc0(nr_reqs * sizeof(*buf)); - qiovs = g_malloc(nr_reqs * sizeof(*qiovs)); - - for (i = 0; i < nr_reqs && optind < argc; i++) { - int j; - - /* Read the offset of the request */ - offset = cvtnum(argv[optind]); - if (offset < 0) { - printf("non-numeric offset argument -- %s\n", argv[optind]); - goto out; - } - optind++; - - if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); - goto out; - } - - if (i == 0) { - first_offset = offset; - } - - /* Read lengths for qiov entries */ - for (j = optind; j < argc; j++) { - if (!strcmp(argv[j], ";")) { - break; - } - } - - nr_iov = j - optind; - - /* Build request */ - buf[i] = create_iovec(bs, &qiovs[i], &argv[optind], nr_iov, pattern); - if (buf[i] == NULL) { - goto out; - } - - reqs[i].qiov = &qiovs[i]; - reqs[i].sector = offset >> 9; - reqs[i].nb_sectors = reqs[i].qiov->size >> 9; - - optind = j + 1; - - pattern++; - } - - /* If there were empty requests at the end, ignore them */ - nr_reqs = i; - - gettimeofday(&t1, NULL); - cnt = do_aio_multiwrite(bs, reqs, nr_reqs, &total); - gettimeofday(&t2, NULL); - - if (cnt < 0) { - printf("aio_multiwrite failed: %s\n", strerror(-cnt)); - goto out; - } - - if (qflag) { - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, t1); - print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); -out: - for (i = 0; i < nr_reqs; i++) { - qemu_io_free(buf[i]); - if (reqs[i].qiov != NULL) { - qemu_iovec_destroy(&qiovs[i]); - } - } - g_free(buf); - g_free(reqs); - g_free(qiovs); - return 0; -} - -struct aio_ctx { - QEMUIOVector qiov; - int64_t offset; - char *buf; - int qflag; - int vflag; - int Cflag; - int Pflag; - int pattern; - struct timeval t1; -}; - -static void aio_write_done(void *opaque, int ret) -{ - struct aio_ctx *ctx = opaque; - struct timeval t2; - - gettimeofday(&t2, NULL); - - - if (ret < 0) { - printf("aio_write failed: %s\n", strerror(-ret)); - goto out; - } - - if (ctx->qflag) { - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("wrote", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); -out: - qemu_io_free(ctx->buf); - qemu_iovec_destroy(&ctx->qiov); - g_free(ctx); -} - -static void aio_read_done(void *opaque, int ret) -{ - struct aio_ctx *ctx = opaque; - struct timeval t2; - - gettimeofday(&t2, NULL); - - if (ret < 0) { - printf("readv failed: %s\n", strerror(-ret)); - goto out; - } - - if (ctx->Pflag) { - void *cmp_buf = g_malloc(ctx->qiov.size); - - memset(cmp_buf, ctx->pattern, ctx->qiov.size); - if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { - printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size); - } - g_free(cmp_buf); - } - - if (ctx->qflag) { - goto out; - } - - if (ctx->vflag) { - dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size); - } - - /* Finally, report back -- -C gives a parsable format */ - t2 = tsub(t2, ctx->t1); - print_report("read", &t2, ctx->offset, ctx->qiov.size, - ctx->qiov.size, 1, ctx->Cflag); -out: - qemu_io_free(ctx->buf); - qemu_iovec_destroy(&ctx->qiov); - g_free(ctx); -} - -static void aio_read_help(void) -{ - printf( -"\n" -" asynchronously reads a range of bytes from the given offset\n" -"\n" -" Example:\n" -" 'aio_read -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" -" The read is performed asynchronously and the aio_flush command must be\n" -" used to ensure all outstanding aio requests have been completed.\n" -" -C, -- report statistics in a machine parsable format\n" -" -P, -- use a pattern to verify read data\n" -" -v, -- dump buffer to standard output\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int aio_read_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t aio_read_cmd = { - .name = "aio_read", - .cfunc = aio_read_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cqv] [-P pattern ] off len [len..]", - .oneline = "asynchronously reads a number of bytes", - .help = aio_read_help, -}; - -static int aio_read_f(BlockDriverState *bs, int argc, char **argv) -{ - int nr_iov, c; - struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); - - while ((c = getopt(argc, argv, "CP:qv")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'P': - ctx->Pflag = 1; - ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) { - g_free(ctx); - return 0; - } - break; - case 'q': - ctx->qflag = 1; - break; - case 'v': - ctx->vflag = 1; - break; - default: - g_free(ctx); - return command_usage(&aio_read_cmd); - } - } - - if (optind > argc - 2) { - g_free(ctx); - return command_usage(&aio_read_cmd); - } - - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - g_free(ctx); - return 0; - } - optind++; - - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - g_free(ctx); - return 0; - } - - nr_iov = argc - optind; - ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, 0xab); - if (ctx->buf == NULL) { - g_free(ctx); - return 0; - } - - gettimeofday(&ctx->t1, NULL); - bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_read_done, ctx); - return 0; -} - -static void aio_write_help(void) -{ - printf( -"\n" -" asynchronously writes a range of bytes from the given offset source\n" -" from multiple buffers\n" -"\n" -" Example:\n" -" 'aio_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" -" The write is performed asynchronously and the aio_flush command must be\n" -" used to ensure all outstanding aio requests have been completed.\n" -" -P, -- use different pattern to fill file\n" -" -C, -- report statistics in a machine parsable format\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int aio_write_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t aio_write_cmd = { - .name = "aio_write", - .cfunc = aio_write_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] [-P pattern ] off len [len..]", - .oneline = "asynchronously writes a number of bytes", - .help = aio_write_help, -}; - -static int aio_write_f(BlockDriverState *bs, int argc, char **argv) -{ - int nr_iov, c; - int pattern = 0xcd; - struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); - - while ((c = getopt(argc, argv, "CqP:")) != EOF) { - switch (c) { - case 'C': - ctx->Cflag = 1; - break; - case 'q': - ctx->qflag = 1; - break; - case 'P': - pattern = parse_pattern(optarg); - if (pattern < 0) { - g_free(ctx); - return 0; - } - break; - default: - g_free(ctx); - return command_usage(&aio_write_cmd); - } - } - - if (optind > argc - 2) { - g_free(ctx); - return command_usage(&aio_write_cmd); - } - - ctx->offset = cvtnum(argv[optind]); - if (ctx->offset < 0) { - printf("non-numeric length argument -- %s\n", argv[optind]); - g_free(ctx); - return 0; - } - optind++; - - if (ctx->offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - ctx->offset); - g_free(ctx); - return 0; - } - - nr_iov = argc - optind; - ctx->buf = create_iovec(bs, &ctx->qiov, &argv[optind], nr_iov, pattern); - if (ctx->buf == NULL) { - g_free(ctx); - return 0; - } - - gettimeofday(&ctx->t1, NULL); - bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov, - ctx->qiov.size >> 9, aio_write_done, ctx); - return 0; -} - -static int aio_flush_f(BlockDriverState *bs, int argc, char **argv) -{ - bdrv_drain_all(); - return 0; -} - -static const cmdinfo_t aio_flush_cmd = { - .name = "aio_flush", - .cfunc = aio_flush_f, - .oneline = "completes all outstanding aio requests" -}; - -static int flush_f(BlockDriverState *bs, 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(BlockDriverState *bs, 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\n", 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(BlockDriverState *bs, int argc, char **argv) -{ - int64_t size; - char s1[64]; - - size = bdrv_getlength(bs); - if (size < 0) { - printf("getlength: %s\n", 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(BlockDriverState *bs, 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 void discard_help(void) -{ - printf( -"\n" -" discards a range of bytes from the given offset\n" -"\n" -" Example:\n" -" 'discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n" -"\n" -" Discards a segment of the currently open file.\n" -" -C, -- report statistics in a machine parsable format\n" -" -q, -- quiet mode, do not show I/O statistics\n" -"\n"); -} - -static int discard_f(BlockDriverState *bs, int argc, char **argv); - -static const cmdinfo_t discard_cmd = { - .name = "discard", - .altname = "d", - .cfunc = discard_f, - .argmin = 2, - .argmax = -1, - .args = "[-Cq] off len", - .oneline = "discards a number of bytes at a specified offset", - .help = discard_help, -}; - -static int discard_f(BlockDriverState *bs, int argc, char **argv) -{ - struct timeval t1, t2; - int Cflag = 0, qflag = 0; - int c, ret; - int64_t offset; - int count; - - while ((c = getopt(argc, argv, "Cq")) != EOF) { - switch (c) { - case 'C': - Cflag = 1; - break; - case 'q': - qflag = 1; - break; - default: - return command_usage(&discard_cmd); - } - } - - if (optind != argc - 2) { - return command_usage(&discard_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; - } - - gettimeofday(&t1, NULL); - ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, - count >> BDRV_SECTOR_BITS); - gettimeofday(&t2, NULL); - - if (ret < 0) { - printf("discard failed: %s\n", strerror(-ret)); - goto out; - } - - /* Finally, report back -- -C gives a parsable format */ - if (!qflag) { - t2 = tsub(t2, t1); - print_report("discard", &t2, offset, count, count, 1, Cflag); - } - -out: - return 0; -} - -static int alloc_f(BlockDriverState *bs, int argc, char **argv) -{ - int64_t offset, sector_num; - int nb_sectors, remaining; - char s1[64]; - int num, sum_alloc; - int ret; - - offset = cvtnum(argv[1]); - if (offset < 0) { - printf("non-numeric offset argument -- %s\n", argv[1]); - return 0; - } else if (offset & 0x1ff) { - printf("offset %" PRId64 " is not sector aligned\n", - offset); - return 0; - } - - if (argc == 3) { - nb_sectors = cvtnum(argv[2]); - if (nb_sectors < 0) { - printf("non-numeric length argument -- %s\n", argv[2]); - return 0; - } - } else { - nb_sectors = 1; - } - - remaining = nb_sectors; - sum_alloc = 0; - sector_num = offset >> 9; - while (remaining) { - ret = bdrv_is_allocated(bs, sector_num, remaining, &num); - sector_num += num; - remaining -= num; - if (ret) { - sum_alloc += num; - } - if (num == 0) { - nb_sectors -= remaining; - remaining = 0; - } - } - - cvtstr(offset, s1, sizeof(s1)); - - printf("%d/%d sectors allocated at offset %s\n", - sum_alloc, 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 map_is_allocated(BlockDriverState *bs, int64_t sector_num, - int64_t nb_sectors, int64_t *pnum) -{ - int num, num_checked; - int ret, firstret; - - num_checked = MIN(nb_sectors, INT_MAX); - ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); - if (ret < 0) { - return ret; - } - - firstret = ret; - *pnum = num; - - while (nb_sectors > 0 && ret == firstret) { - sector_num += num; - nb_sectors -= num; - - num_checked = MIN(nb_sectors, INT_MAX); - ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); - if (ret == firstret) { - *pnum += num; - } else { - break; - } - } - - return firstret; -} - -static int map_f(BlockDriverState *bs, int argc, char **argv) -{ - int64_t offset; - int64_t nb_sectors; - char s1[64]; - int64_t num; - int ret; - const char *retstr; - - offset = 0; - nb_sectors = bs->total_sectors; - - do { - ret = map_is_allocated(bs, offset, nb_sectors, &num); - if (ret < 0) { - error_report("Failed to get allocation status: %s", strerror(-ret)); - return 0; - } - - retstr = ret ? " allocated" : "not allocated"; - cvtstr(offset << 9ULL, s1, sizeof(s1)); - printf("[% 24" PRId64 "] % 8" PRId64 "/% 8" PRId64 " sectors %s " - "at offset %s (%d)\n", - offset << 9ULL, num, nb_sectors, retstr, s1, ret); - - offset += num; - nb_sectors -= num; - } while (offset < bs->total_sectors); - - return 0; -} - -static const cmdinfo_t map_cmd = { - .name = "map", - .argmin = 0, - .argmax = 0, - .cfunc = map_f, - .args = "", - .oneline = "prints the allocated areas of a file", -}; - -static int break_f(BlockDriverState *bs, int argc, char **argv) -{ - int ret; - - ret = bdrv_debug_breakpoint(bs, argv[1], argv[2]); - if (ret < 0) { - printf("Could not set breakpoint: %s\n", strerror(-ret)); - } - - return 0; -} - -static const cmdinfo_t break_cmd = { - .name = "break", - .argmin = 2, - .argmax = 2, - .cfunc = break_f, - .args = "event tag", - .oneline = "sets a breakpoint on event and tags the stopped " - "request as tag", -}; - -static int resume_f(BlockDriverState *bs, int argc, char **argv) -{ - int ret; - - ret = bdrv_debug_resume(bs, argv[1]); - if (ret < 0) { - printf("Could not resume request: %s\n", strerror(-ret)); - } - - return 0; -} - -static const cmdinfo_t resume_cmd = { - .name = "resume", - .argmin = 1, - .argmax = 1, - .cfunc = resume_f, - .args = "tag", - .oneline = "resumes the request tagged as tag", -}; - -static int wait_break_f(BlockDriverState *bs, int argc, char **argv) -{ - while (!bdrv_debug_is_suspended(bs, argv[1])) { - qemu_aio_wait(); - } - - return 0; -} - -static const cmdinfo_t wait_break_cmd = { - .name = "wait_break", - .argmin = 1, - .argmax = 1, - .cfunc = wait_break_f, - .args = "tag", - .oneline = "waits for the suspension of a request", -}; - -static int abort_f(BlockDriverState *bs, int argc, char **argv) -{ - abort(); -} - -static const cmdinfo_t abort_cmd = { - .name = "abort", - .cfunc = abort_f, - .flags = CMD_NOFILE_OK, - .oneline = "simulate a program crash using abort(3)", -}; +extern int qemuio_misalign; static int close_f(BlockDriverState *bs, int argc, char **argv) { @@ -1916,18 +137,6 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) return openfile(argv[optind], flags, growable); } -static int init_check_command(BlockDriverState *bs, 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( @@ -1998,7 +207,7 @@ int main(int argc, char **argv) readonly = 1; break; case 'm': - misalign = 1; + qemuio_misalign = 1; break; case 'g': growable = 1; @@ -2039,30 +248,8 @@ int main(int argc, char **argv) /* 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(&multiwrite_cmd); - add_command(&aio_read_cmd); - add_command(&aio_write_cmd); - add_command(&aio_flush_cmd); - add_command(&flush_cmd); - add_command(&truncate_cmd); - add_command(&length_cmd); - add_command(&info_cmd); - add_command(&discard_cmd); - add_command(&alloc_cmd); - add_command(&map_cmd); - add_command(&break_cmd); - add_command(&resume_cmd); - add_command(&wait_break_cmd); - add_command(&abort_cmd); - - add_check_command(init_check_command); /* open the device */ if (!readonly) { -- cgit v1.2.1 From dd5832967ac3fe96bd5bf9f199639176998ead69 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:32 +0200 Subject: qemu-io: Factor out qemuio_command It's duplicated code. Move it to qemu-io-cmds.c because it's not dependent on any static data of the qemu-io tool. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 43 +++++-------------------------------------- cmd.h | 3 ++- qemu-io-cmds.c | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/cmd.c b/cmd.c index d501aabb0c..7ae978f38c 100644 --- a/cmd.c +++ b/cmd.c @@ -138,28 +138,11 @@ static char *get_prompt(void); void command_loop(void) { - int c, i, done = 0, fetchable = 0, prompted = 0; + int i, done = 0, fetchable = 0, prompted = 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) { - done = command(ct, c, v); - } else { - fprintf(stderr, _("command \"%s\" not found\n"), v[0]); - } - } - doneline(input, v); + done = qemuio_command(cmdline[i]); } if (cmdline) { g_free(cmdline); @@ -179,20 +162,13 @@ void command_loop(void) if (!fetchable) { continue; } + input = fetchline(); if (input == 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); + done = qemuio_command(input); + free(input); prompted = 0; fetchable = 0; @@ -328,15 +304,6 @@ char **breakline(char *input, int *count) 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) diff --git a/cmd.h b/cmd.h index ccf6336e8e..d6764089cb 100644 --- a/cmd.h +++ b/cmd.h @@ -59,7 +59,6 @@ int command(const cmdinfo_t *ci, int argc, char **argv); /* from input.h */ char **breakline(char *input, int *count); -void doneline(char *input, char **vec); char *fetchline(void); void cvtstr(double value, char *str, size_t sz); @@ -77,4 +76,6 @@ void timestr(struct timeval *tv, char *str, size_t sz, int flags); extern char *progname; +bool qemuio_command(const char *cmd); + #endif /* __COMMAND_H__ */ diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 0a3817ae7a..8b1244601e 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1807,6 +1807,30 @@ static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct) return 1; } +bool qemuio_command(const char *cmd) +{ + char *input; + const cmdinfo_t *ct; + char **v; + int c; + bool done = false; + + input = g_strdup(cmd); + 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]); + } + } + g_free(input); + g_free(v); + + return done; +} + static void __attribute((constructor)) init_qemuio_commands(void) { /* initialize commands */ -- cgit v1.2.1 From f18a834a92f0b490cefeb71410f3f25b969d336f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:33 +0200 Subject: qemu-io: Move 'help' function No reason to treat it different from other commands. Move it to qemu-io-cmds.c, adapt the coding style and register it like any other command. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 79 ---------------------------------------------------------- cmd.h | 1 - qemu-io-cmds.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 81 deletions(-) diff --git a/cmd.c b/cmd.c index 7ae978f38c..2941ad35fb 100644 --- a/cmd.c +++ b/cmd.c @@ -439,82 +439,3 @@ quit_init(void) 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( - BlockDriverState *bs, - 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); -} diff --git a/cmd.h b/cmd.h index d6764089cb..89e7c6e8b2 100644 --- a/cmd.h +++ b/cmd.h @@ -42,7 +42,6 @@ typedef struct cmdinfo { extern cmdinfo_t *cmdtab; extern int ncmds; -void help_init(void); void quit_init(void); typedef int (*checkfunc_t)(BlockDriverState *bs, const cmdinfo_t *ci); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 8b1244601e..fa8d9a0c38 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1795,6 +1795,71 @@ static const cmdinfo_t abort_cmd = { .oneline = "simulate a program crash using abort(3)", }; +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); +} + +static void help_onecmd(const char *cmd, const cmdinfo_t *ct) +{ + help_oneline(cmd, ct); + if (ct->help) { + ct->help(); + } +} + +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(BlockDriverState *bs, 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 const cmdinfo_t help_cmd = { + .name = "help", + .altname = "?", + .cfunc = help_f, + .argmin = 0, + .argmax = 1, + .flags = CMD_FLAG_GLOBAL, + .args = "[command]", + .oneline = "help for one or all commands", +}; + static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct) { if (ct->flags & CMD_FLAG_GLOBAL) { @@ -1834,7 +1899,7 @@ bool qemuio_command(const char *cmd) static void __attribute((constructor)) init_qemuio_commands(void) { /* initialize commands */ - help_init(); + add_command(&help_cmd); add_command(&read_cmd); add_command(&readv_cmd); add_command(&write_cmd); -- cgit v1.2.1 From e681be7eca0143fe7259ce8233fe5dd8898d072f Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:34 +0200 Subject: qemu-io: Move 'quit' function This one only makes sense in the context of the qemu-io tool, so move it to qemu-io.c. Adapt coding style and register it like other commands. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 29 ----------------------------- cmd.h | 2 -- qemu-io.c | 17 ++++++++++++++++- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/cmd.c b/cmd.c index 2941ad35fb..8496e74d64 100644 --- a/cmd.c +++ b/cmd.c @@ -410,32 +410,3 @@ timestr( snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000)); } } - - -/* from libxcmd/quit.c */ - -static cmdinfo_t quit_cmd; - -/* ARGSUSED */ -static int -quit_f( - BlockDriverState *bs, - 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); -} diff --git a/cmd.h b/cmd.h index 89e7c6e8b2..5b6f61b9ad 100644 --- a/cmd.h +++ b/cmd.h @@ -42,8 +42,6 @@ typedef struct cmdinfo { extern cmdinfo_t *cmdtab; extern int ncmds; -void quit_init(void); - typedef int (*checkfunc_t)(BlockDriverState *bs, const cmdinfo_t *ci); void add_command(const cmdinfo_t *ci); diff --git a/qemu-io.c b/qemu-io.c index 14eef2cc65..8f6c57e1b2 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -137,6 +137,21 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) return openfile(argv[optind], flags, growable); } +static int quit_f(BlockDriverState *bs, int argc, char **argv) +{ + return 1; +} + +static const cmdinfo_t quit_cmd = { + .name = "quit", + .altname = "q", + .cfunc = quit_f, + .argmin = -1, + .argmax = -1, + .flags = CMD_FLAG_GLOBAL, + .oneline = "exit the program", +}; + static void usage(const char *name) { printf( @@ -247,7 +262,7 @@ int main(int argc, char **argv) bdrv_init(); /* initialize commands */ - quit_init(); + add_command(&quit_cmd); add_command(&open_cmd); add_command(&close_cmd); -- cgit v1.2.1 From a38ed811474e953371f848233208c2026c2d1195 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:35 +0200 Subject: qemu-io: Move qemu_strsep() to cutils.c Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 21 --------------------- include/qemu-common.h | 1 + util/cutils.c | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/cmd.c b/cmd.c index 8496e74d64..f6bf2c5f9a 100644 --- a/cmd.c +++ b/cmd.c @@ -255,27 +255,6 @@ fetchline(void) } #endif -static char *qemu_strsep(char **input, const char *delim) -{ - char *result = *input; - if (result != NULL) { - char *p; - - for (p = result; *p != '\0'; p++) { - if (strchr(delim, *p)) { - break; - } - } - if (*p == '\0') { - *input = NULL; - } else { - *p = '\0'; - *input = p + 1; - } - } - return result; -} - char **breakline(char *input, int *count) { int c = 0; diff --git a/include/qemu-common.h b/include/qemu-common.h index d95ea1e147..ed8b6e2005 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -174,6 +174,7 @@ char *pstrcat(char *buf, int buf_size, const char *s); int strstart(const char *str, const char *val, const char **ptr); int stristart(const char *str, const char *val, const char **ptr); int qemu_strnlen(const char *s, int max_len); +char *qemu_strsep(char **input, const char *delim); time_t mktimegm(struct tm *tm); int qemu_fls(int i); int qemu_fdatasync(int fd); diff --git a/util/cutils.c b/util/cutils.c index 8f28896843..0116fcde74 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -107,6 +107,27 @@ int qemu_strnlen(const char *s, int max_len) return i; } +char *qemu_strsep(char **input, const char *delim) +{ + char *result = *input; + if (result != NULL) { + char *p; + + for (p = result; *p != '\0'; p++) { + if (strchr(delim, *p)) { + break; + } + } + if (*p == '\0') { + *input = NULL; + } else { + *p = '\0'; + *input = p + 1; + } + } + return result; +} + time_t mktimegm(struct tm *tm) { time_t t; -- cgit v1.2.1 From c2cdf5c5892165cbe7d3567bff5930521bc52669 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:36 +0200 Subject: qemu-io: Move functions for registering and running commands Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- cmd.c | 113 --------------------------------- cmd.h | 11 +--- qemu-io-cmds.c | 192 ++++++++++++++++++++++++++++++++++++++++++--------------- qemu-io.c | 10 +-- 4 files changed, 148 insertions(+), 178 deletions(-) diff --git a/cmd.c b/cmd.c index f6bf2c5f9a..6616d6177e 100644 --- a/cmd.c +++ b/cmd.c @@ -31,94 +31,9 @@ /* from libxcmd/command.c */ -cmdinfo_t *cmdtab; -int ncmds; - -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 = g_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(qemuio_bs, 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(qemuio_bs, 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) { @@ -255,34 +170,6 @@ fetchline(void) } #endif -char **breakline(char *input, int *count) -{ - int c = 0; - char *p; - char **rval = calloc(sizeof(char *), 1); - char **tmp; - - while (rval && (p = qemu_strsep(&input, " ")) != NULL) { - if (!*p) { - continue; - } - c++; - tmp = realloc(rval, sizeof(*rval) * (c + 1)); - if (!tmp) { - free(rval); - rval = NULL; - c = 0; - break; - } else { - rval = tmp; - } - rval[c - 1] = p; - rval[c] = NULL; - } - *count = c; - return rval; -} - #define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) diff --git a/cmd.h b/cmd.h index 5b6f61b9ad..0d01a33e97 100644 --- a/cmd.h +++ b/cmd.h @@ -39,23 +39,16 @@ typedef struct cmdinfo { helpfunc_t help; } cmdinfo_t; -extern cmdinfo_t *cmdtab; -extern int ncmds; - typedef int (*checkfunc_t)(BlockDriverState *bs, const cmdinfo_t *ci); -void add_command(const cmdinfo_t *ci); +void qemuio_add_command(const cmdinfo_t *ci); void add_user_command(char *optarg); void add_check_command(checkfunc_t cf); -const cmdinfo_t *find_command(const char *cmd); - void command_loop(void); -int command_usage(const cmdinfo_t *ci); -int command(const cmdinfo_t *ci, int argc, char **argv); +int qemuio_command_usage(const cmdinfo_t *ci); /* from input.h */ -char **breakline(char *input, int *count); char *fetchline(void); void cvtstr(double value, char *str, size_t sz); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index fa8d9a0c38..8acc8666d0 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -16,6 +16,110 @@ int qemuio_misalign; +static cmdinfo_t *cmdtab; +static int ncmds; + +static int compare_cmdname(const void *a, const void *b) +{ + return strcmp(((const cmdinfo_t *)a)->name, + ((const cmdinfo_t *)b)->name); +} + +void qemuio_add_command(const cmdinfo_t *ci) +{ + cmdtab = g_realloc(cmdtab, ++ncmds * sizeof(*cmdtab)); + cmdtab[ncmds - 1] = *ci; + qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname); +} + +int qemuio_command_usage(const cmdinfo_t *ci) +{ + printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); + return 0; +} + +static int init_check_command(BlockDriverState *bs, 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 int command(const cmdinfo_t *ct, int argc, char **argv) +{ + char *cmd = argv[0]; + + if (!init_check_command(qemuio_bs, 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(qemuio_bs, argc, argv); +} + +static 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; +} + +static char **breakline(char *input, int *count) +{ + int c = 0; + char *p; + char **rval = g_malloc0(sizeof(char *)); + char **tmp; + + while (rval && (p = qemu_strsep(&input, " ")) != NULL) { + if (!*p) { + continue; + } + c++; + tmp = g_realloc(rval, sizeof(*rval) * (c + 1)); + if (!tmp) { + g_free(rval); + rval = NULL; + c = 0; + break; + } else { + rval = tmp; + } + rval[c - 1] = p; + rval[c] = NULL; + } + *count = c; + return rval; +} + static int64_t cvtnum(const char *s) { char *end; @@ -467,12 +571,12 @@ static int read_f(BlockDriverState *bs, int argc, char **argv) vflag = 1; break; default: - return command_usage(&read_cmd); + return qemuio_command_usage(&read_cmd); } } if (optind != argc - 2) { - return command_usage(&read_cmd); + return qemuio_command_usage(&read_cmd); } if (bflag && pflag) { @@ -494,7 +598,7 @@ static int read_f(BlockDriverState *bs, int argc, char **argv) } if (!Pflag && (lflag || sflag)) { - return command_usage(&read_cmd); + return qemuio_command_usage(&read_cmd); } if (!lflag) { @@ -629,12 +733,12 @@ static int readv_f(BlockDriverState *bs, int argc, char **argv) vflag = 1; break; default: - return command_usage(&readv_cmd); + return qemuio_command_usage(&readv_cmd); } } if (optind > argc - 2) { - return command_usage(&readv_cmd); + return qemuio_command_usage(&readv_cmd); } @@ -769,12 +873,12 @@ static int write_f(BlockDriverState *bs, int argc, char **argv) zflag = 1; break; default: - return command_usage(&write_cmd); + return qemuio_command_usage(&write_cmd); } } if (optind != argc - 2) { - return command_usage(&write_cmd); + return qemuio_command_usage(&write_cmd); } if (bflag + pflag + zflag > 1) { @@ -911,12 +1015,12 @@ static int writev_f(BlockDriverState *bs, int argc, char **argv) } break; default: - return command_usage(&writev_cmd); + return qemuio_command_usage(&writev_cmd); } } if (optind > argc - 2) { - return command_usage(&writev_cmd); + return qemuio_command_usage(&writev_cmd); } offset = cvtnum(argv[optind]); @@ -1023,12 +1127,12 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv) } break; default: - return command_usage(&writev_cmd); + return qemuio_command_usage(&writev_cmd); } } if (optind > argc - 2) { - return command_usage(&writev_cmd); + return qemuio_command_usage(&writev_cmd); } nr_reqs = 1; @@ -1257,13 +1361,13 @@ static int aio_read_f(BlockDriverState *bs, int argc, char **argv) break; default: g_free(ctx); - return command_usage(&aio_read_cmd); + return qemuio_command_usage(&aio_read_cmd); } } if (optind > argc - 2) { g_free(ctx); - return command_usage(&aio_read_cmd); + return qemuio_command_usage(&aio_read_cmd); } ctx->offset = cvtnum(argv[optind]); @@ -1349,13 +1453,13 @@ static int aio_write_f(BlockDriverState *bs, int argc, char **argv) break; default: g_free(ctx); - return command_usage(&aio_write_cmd); + return qemuio_command_usage(&aio_write_cmd); } } if (optind > argc - 2) { g_free(ctx); - return command_usage(&aio_write_cmd); + return qemuio_command_usage(&aio_write_cmd); } ctx->offset = cvtnum(argv[optind]); @@ -1547,12 +1651,12 @@ static int discard_f(BlockDriverState *bs, int argc, char **argv) qflag = 1; break; default: - return command_usage(&discard_cmd); + return qemuio_command_usage(&discard_cmd); } } if (optind != argc - 2) { - return command_usage(&discard_cmd); + return qemuio_command_usage(&discard_cmd); } offset = cvtnum(argv[optind]); @@ -1860,18 +1964,6 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -static int init_check_command(BlockDriverState *bs, 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; -} - bool qemuio_command(const char *cmd) { char *input; @@ -1899,26 +1991,24 @@ bool qemuio_command(const char *cmd) static void __attribute((constructor)) init_qemuio_commands(void) { /* initialize commands */ - add_command(&help_cmd); - add_command(&read_cmd); - add_command(&readv_cmd); - add_command(&write_cmd); - add_command(&writev_cmd); - add_command(&multiwrite_cmd); - add_command(&aio_read_cmd); - add_command(&aio_write_cmd); - add_command(&aio_flush_cmd); - add_command(&flush_cmd); - add_command(&truncate_cmd); - add_command(&length_cmd); - add_command(&info_cmd); - add_command(&discard_cmd); - add_command(&alloc_cmd); - add_command(&map_cmd); - add_command(&break_cmd); - add_command(&resume_cmd); - add_command(&wait_break_cmd); - add_command(&abort_cmd); - - add_check_command(init_check_command); + qemuio_add_command(&help_cmd); + qemuio_add_command(&read_cmd); + qemuio_add_command(&readv_cmd); + qemuio_add_command(&write_cmd); + qemuio_add_command(&writev_cmd); + qemuio_add_command(&multiwrite_cmd); + qemuio_add_command(&aio_read_cmd); + qemuio_add_command(&aio_write_cmd); + qemuio_add_command(&aio_flush_cmd); + qemuio_add_command(&flush_cmd); + qemuio_add_command(&truncate_cmd); + qemuio_add_command(&length_cmd); + qemuio_add_command(&info_cmd); + qemuio_add_command(&discard_cmd); + qemuio_add_command(&alloc_cmd); + qemuio_add_command(&map_cmd); + qemuio_add_command(&break_cmd); + qemuio_add_command(&resume_cmd); + qemuio_add_command(&wait_break_cmd); + qemuio_add_command(&abort_cmd); } diff --git a/qemu-io.c b/qemu-io.c index 8f6c57e1b2..3bf5aec0de 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -122,7 +122,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) growable = 1; break; default: - return command_usage(&open_cmd); + return qemuio_command_usage(&open_cmd); } } @@ -131,7 +131,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv) } if (optind != argc - 1) { - return command_usage(&open_cmd); + return qemuio_command_usage(&open_cmd); } return openfile(argv[optind], flags, growable); @@ -262,9 +262,9 @@ int main(int argc, char **argv) bdrv_init(); /* initialize commands */ - add_command(&quit_cmd); - add_command(&open_cmd); - add_command(&close_cmd); + qemuio_add_command(&quit_cmd); + qemuio_add_command(&open_cmd); + qemuio_add_command(&close_cmd); /* open the device */ if (!readonly) { -- cgit v1.2.1 From d1174f13e78e2f43f7ae33d59b62b0b94468c8db Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:37 +0200 Subject: qemu-io: Move command_loop() and friends Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- cmd.c | 139 -------------------------------------------------------------- cmd.h | 9 ---- qemu-io.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 148 deletions(-) diff --git a/cmd.c b/cmd.c index 6616d6177e..26d38a8b82 100644 --- a/cmd.c +++ b/cmd.c @@ -31,145 +31,6 @@ /* from libxcmd/command.c */ -static int ncmdline; -static char **cmdline; - - -void add_user_command(char *optarg) -{ - cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *)); - cmdline[ncmdline-1] = optarg; -} - -static void prep_fetchline(void *opaque) -{ - int *fetchable = opaque; - - qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); - *fetchable= 1; -} - -static char *get_prompt(void); - -void command_loop(void) -{ - int i, done = 0, fetchable = 0, prompted = 0; - char *input; - - for (i = 0; !done && i < ncmdline; i++) { - done = qemuio_command(cmdline[i]); - } - if (cmdline) { - g_free(cmdline); - return; - } - - while (!done) { - if (!prompted) { - printf("%s", get_prompt()); - fflush(stdout); - qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable); - prompted = 1; - } - - main_loop_wait(false); - - if (!fetchable) { - continue; - } - - input = fetchline(); - if (input == NULL) { - break; - } - done = qemuio_command(input); - free(input); - - prompted = 0; - fetchable = 0; - } - qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); -} - -/* from libxcmd/input.c */ - -#if defined(ENABLE_READLINE) -# include -# include -#elif defined(ENABLE_EDITLINE) -# include -#endif - -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; - 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 - #define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) diff --git a/cmd.h b/cmd.h index 0d01a33e97..da0c7cffe1 100644 --- a/cmd.h +++ b/cmd.h @@ -39,18 +39,11 @@ typedef struct cmdinfo { helpfunc_t help; } cmdinfo_t; -typedef int (*checkfunc_t)(BlockDriverState *bs, const cmdinfo_t *ci); - void qemuio_add_command(const cmdinfo_t *ci); -void add_user_command(char *optarg); -void add_check_command(checkfunc_t cf); -void command_loop(void); int qemuio_command_usage(const cmdinfo_t *ci); /* from input.h */ -char *fetchline(void); - void cvtstr(double value, char *str, size_t sz); struct timeval tsub(struct timeval t1, struct timeval t2); @@ -64,8 +57,6 @@ enum { void timestr(struct timeval *tv, char *str, size_t sz, int flags); -extern char *progname; - bool qemuio_command(const char *cmd); #endif /* __COMMAND_H__ */ diff --git a/qemu-io.c b/qemu-io.c index 3bf5aec0de..eec8cbc9f2 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -29,6 +29,10 @@ char *progname; BlockDriverState *qemuio_bs; extern int qemuio_misalign; +/* qemu-io commands passed using -c */ +static int ncmdline; +static char **cmdline; + static int close_f(BlockDriverState *bs, int argc, char **argv) { bdrv_delete(bs); @@ -174,6 +178,141 @@ static void usage(const char *name) } +#if defined(ENABLE_READLINE) +# include +# include +#elif defined(ENABLE_EDITLINE) +# include +#endif + +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) +static char *fetchline(void) +{ + char *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(); +} + +static 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 +static char *fetchline(void) +{ + char *p, *line = g_malloc(MAXREADLINESZ); + + if (!fgets(line, MAXREADLINESZ, stdin)) { + g_free(line); + return NULL; + } + + p = line + strlen(line); + if (p != line && p[-1] == '\n') { + p[-1] = '\0'; + } + + return line; +} +#endif + +static void prep_fetchline(void *opaque) +{ + int *fetchable = opaque; + + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); + *fetchable= 1; +} + +static void command_loop(void) +{ + int i, done = 0, fetchable = 0, prompted = 0; + char *input; + + for (i = 0; !done && i < ncmdline; i++) { + done = qemuio_command(cmdline[i]); + } + if (cmdline) { + g_free(cmdline); + return; + } + + while (!done) { + if (!prompted) { + printf("%s", get_prompt()); + fflush(stdout); + qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable); + prompted = 1; + } + + main_loop_wait(false); + + if (!fetchable) { + continue; + } + + input = fetchline(); + if (input == NULL) { + break; + } + done = qemuio_command(input); + g_free(input); + + prompted = 0; + fetchable = 0; + } + qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); +} + +static void add_user_command(char *optarg) +{ + cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *)); + cmdline[ncmdline-1] = optarg; +} + int main(int argc, char **argv) { int readonly = 0; -- cgit v1.2.1 From 0b613881ae8fc59359b3d91e666fea6c9b1e731b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:38 +0200 Subject: qemu-io: Move remaining helpers from cmd.c Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- Makefile | 2 +- cmd.c | 139 --------------------------------------------------------- cmd.h | 14 ------ qemu-io-cmds.c | 104 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 154 deletions(-) delete mode 100644 cmd.c diff --git a/Makefile b/Makefile index cf932eb539..87298e5be3 100644 --- a/Makefile +++ b/Makefile @@ -186,7 +186,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o qemu-io-cmds.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o qemu-io-cmds.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/cmd.c b/cmd.c deleted file mode 100644 index 26d38a8b82..0000000000 --- a/cmd.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "cmd.h" -#include "block/aio.h" -#include "qemu/main-loop.h" - -#define _(x) x /* not gettext support yet */ - -/* from libxcmd/command.c */ - -#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) - -#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) -{ - char *trim; - const char *suffix; - - if (value >= EXABYTES(1)) { - suffix = " EiB"; - snprintf(str, size - 4, "%.3f", TO_EXABYTES(value)); - } else if (value >= PETABYTES(1)) { - suffix = " PiB"; - snprintf(str, size - 4, "%.3f", TO_PETABYTES(value)); - } else if (value >= TERABYTES(1)) { - suffix = " TiB"; - snprintf(str, size - 4, "%.3f", TO_TERABYTES(value)); - } else if (value >= GIGABYTES(1)) { - suffix = " GiB"; - snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value)); - } else if (value >= MEGABYTES(1)) { - suffix = " MiB"; - snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value)); - } else if (value >= KILOBYTES(1)) { - suffix = " KiB"; - snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value)); - } else { - suffix = " bytes"; - snprintf(str, size - 6, "%f", value); - } - - trim = strstr(str, ".000"); - if (trim) { - strcpy(trim, suffix); - } else { - strcat(str, suffix); - } -} - -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)); - } -} diff --git a/cmd.h b/cmd.h index da0c7cffe1..9907795dc9 100644 --- a/cmd.h +++ b/cmd.h @@ -43,20 +43,6 @@ void qemuio_add_command(const cmdinfo_t *ci); int qemuio_command_usage(const cmdinfo_t *ci); -/* from input.h */ -void cvtstr(double value, char *str, size_t sz); - -struct timeval tsub(struct timeval t1, struct timeval t2); -double tdiv(double value, struct timeval tv); - -enum { - DEFAULT_TIME = 0x0, - TERSE_FIXED_TIME = 0x1, - VERBOSE_FIXED_TIME = 0x2 -}; - -void timestr(struct timeval *tv, char *str, size_t sz, int flags); - bool qemuio_command(const char *cmd); #endif /* __COMMAND_H__ */ diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 8acc8666d0..05ce342467 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -126,6 +126,110 @@ static int64_t cvtnum(const char *s) return strtosz_suffix(s, &end, STRTOSZ_DEFSUFFIX_B); } +#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) + +#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)) + +static void cvtstr(double value, char *str, size_t size) +{ + char *trim; + const char *suffix; + + if (value >= EXABYTES(1)) { + suffix = " EiB"; + snprintf(str, size - 4, "%.3f", TO_EXABYTES(value)); + } else if (value >= PETABYTES(1)) { + suffix = " PiB"; + snprintf(str, size - 4, "%.3f", TO_PETABYTES(value)); + } else if (value >= TERABYTES(1)) { + suffix = " TiB"; + snprintf(str, size - 4, "%.3f", TO_TERABYTES(value)); + } else if (value >= GIGABYTES(1)) { + suffix = " GiB"; + snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value)); + } else if (value >= MEGABYTES(1)) { + suffix = " MiB"; + snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value)); + } else if (value >= KILOBYTES(1)) { + suffix = " KiB"; + snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value)); + } else { + suffix = " bytes"; + snprintf(str, size - 6, "%f", value); + } + + trim = strstr(str, ".000"); + if (trim) { + strcpy(trim, suffix); + } else { + strcat(str, suffix); + } +} + + + +static 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; +} + +static 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) + +enum { + DEFAULT_TIME = 0x0, + TERSE_FIXED_TIME = 0x1, + VERBOSE_FIXED_TIME = 0x2, +}; + +static 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)); + } +} + /* * Parse the pattern argument to various sub-commands. * -- cgit v1.2.1 From 3d21994f9c511cb63220fef5abea164b83fbb997 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:39 +0200 Subject: qemu-io: Interface cleanup Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- cmd.h | 48 ------------------------------------------------ include/qemu-io.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ qemu-io-cmds.c | 14 +++++++------- qemu-io.c | 7 +++---- 4 files changed, 56 insertions(+), 59 deletions(-) delete mode 100644 cmd.h create mode 100644 include/qemu-io.h diff --git a/cmd.h b/cmd.h deleted file mode 100644 index 9907795dc9..0000000000 --- a/cmd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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, see . - */ -#ifndef __COMMAND_H__ -#define __COMMAND_H__ - -#include "qemu-common.h" - -#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ - -extern BlockDriverState *qemuio_bs; - -typedef int (*cfunc_t)(BlockDriverState *bs, 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; - -void qemuio_add_command(const cmdinfo_t *ci); - -int qemuio_command_usage(const cmdinfo_t *ci); - -bool qemuio_command(const char *cmd); - -#endif /* __COMMAND_H__ */ diff --git a/include/qemu-io.h b/include/qemu-io.h new file mode 100644 index 0000000000..a418b46a40 --- /dev/null +++ b/include/qemu-io.h @@ -0,0 +1,46 @@ +/* + * 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, see . + */ + +#ifndef QEMU_IO_H +#define QEMU_IO_H + +#include "qemu-common.h" + +#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ + +typedef int (*cfunc_t)(BlockDriverState *bs, 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; + +bool qemuio_command(BlockDriverState *bs, const char *cmd); + +void qemuio_add_command(const cmdinfo_t *ci); +int qemuio_command_usage(const cmdinfo_t *ci); + +#endif /* QEMU_IO_H */ diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 05ce342467..ffbcf31cfc 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -8,9 +8,8 @@ * See the COPYING file in the top-level directory. */ -#include "qemu-common.h" +#include "qemu-io.h" #include "block/block_int.h" -#include "cmd.h" #define CMD_NOFILE_OK 0x01 @@ -50,11 +49,12 @@ static int init_check_command(BlockDriverState *bs, const cmdinfo_t *ct) return 1; } -static int command(const cmdinfo_t *ct, int argc, char **argv) +static int command(BlockDriverState *bs, const cmdinfo_t *ct, int argc, + char **argv) { char *cmd = argv[0]; - if (!init_check_command(qemuio_bs, ct)) { + if (!init_check_command(bs, ct)) { return 0; } @@ -75,7 +75,7 @@ static int command(const cmdinfo_t *ct, int argc, char **argv) return 0; } optind = 0; - return ct->cfunc(qemuio_bs, argc, argv); + return ct->cfunc(bs, argc, argv); } static const cmdinfo_t *find_command(const char *cmd) @@ -2068,7 +2068,7 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -bool qemuio_command(const char *cmd) +bool qemuio_command(BlockDriverState *bs, const char *cmd) { char *input; const cmdinfo_t *ct; @@ -2081,7 +2081,7 @@ bool qemuio_command(const char *cmd) if (c) { ct = find_command(v[0]); if (ct) { - done = command(ct, c, v); + done = command(bs, ct, c, v); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); } diff --git a/qemu-io.c b/qemu-io.c index eec8cbc9f2..514edcb527 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -14,10 +14,9 @@ #include #include -#include "qemu-common.h" +#include "qemu-io.h" #include "qemu/main-loop.h" #include "block/block_int.h" -#include "cmd.h" #include "trace/control.h" #define VERSION "0.0.1" @@ -273,7 +272,7 @@ static void command_loop(void) char *input; for (i = 0; !done && i < ncmdline; i++) { - done = qemuio_command(cmdline[i]); + done = qemuio_command(qemuio_bs, cmdline[i]); } if (cmdline) { g_free(cmdline); @@ -298,7 +297,7 @@ static void command_loop(void) if (input == NULL) { break; } - done = qemuio_command(input); + done = qemuio_command(qemuio_bs, input); g_free(input); prompted = 0; -- cgit v1.2.1 From 02da386a2d7a020e80b0aed64769efa9dd42072a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:40 +0200 Subject: qemu-io: Use the qemu version for -V Always printing 0.0.1 and never updating the version number wasn't very useful. qemu-io is released with qemu, so using the same version number makes most sense. Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- qemu-io.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qemu-io.c b/qemu-io.c index 514edcb527..cb9def50e6 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -19,8 +19,6 @@ #include "block/block_int.h" #include "trace/control.h" -#define VERSION "0.0.1" - #define CMD_NOFILE_OK 0x01 char *progname; @@ -380,7 +378,7 @@ int main(int argc, char **argv) } break; case 'V': - printf("%s version %s\n", progname, VERSION); + printf("%s version %s\n", progname, QEMU_VERSION); exit(0); case 'h': usage(progname); -- cgit v1.2.1 From 587da2c39c9ace168f4d01fa446a54ae998a2553 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 14:19:41 +0200 Subject: Make qemu-io commands available in HMP It was decided to not make this command available in QMP in order to make clear that this is not supposed to be a stable API and should be used only for testing and debugging purposes. Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- Makefile | 2 +- Makefile.objs | 1 + hmp-commands.hx | 16 ++++++++++++++++ hmp.c | 18 ++++++++++++++++++ hmp.h | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 87298e5be3..9a77ae0e18 100644 --- a/Makefile +++ b/Makefile @@ -186,7 +186,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o qemu-io-cmds.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/Makefile.objs b/Makefile.objs index 286ce069b2..5b288ba42f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -13,6 +13,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ block-obj-y += qapi-types.o qapi-visit.o +block-obj-y += qemu-io-cmds.o block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o block-obj-y += qemu-coroutine-sleep.o diff --git a/hmp-commands.hx b/hmp-commands.hx index 4f5a3fd7d5..396691a5d6 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1550,6 +1550,22 @@ STEXI Removes the chardev @var{id}. +ETEXI + + { + .name = "qemu-io", + .args_type = "device:B,command:s", + .params = "[device] \"[command]\"", + .help = "run a qemu-io command on a block device", + .mhandler.cmd = hmp_qemu_io, + }, + +STEXI +@item qemu-io @var{device} @var{command} +@findex qemu-io + +Executes a qemu-io command on the given block device. + ETEXI { diff --git a/hmp.c b/hmp.c index 4fb76ec7a7..64e0baac16 100644 --- a/hmp.c +++ b/hmp.c @@ -22,6 +22,7 @@ #include "qemu/sockets.h" #include "monitor/monitor.h" #include "ui/console.h" +#include "qemu-io.h" static void hmp_handle_error(Monitor *mon, Error **errp) { @@ -1425,3 +1426,20 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict) qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); hmp_handle_error(mon, &local_err); } + +void hmp_qemu_io(Monitor *mon, const QDict *qdict) +{ + BlockDriverState *bs; + const char* device = qdict_get_str(qdict, "device"); + const char* command = qdict_get_str(qdict, "command"); + Error *err = NULL; + + bs = bdrv_find(device); + if (bs) { + qemuio_command(bs, command); + } else { + error_set(&err, QERR_DEVICE_NOT_FOUND, device); + } + + hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 95fe76e218..56d2e92aa6 100644 --- a/hmp.h +++ b/hmp.h @@ -85,5 +85,6 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict); +void hmp_qemu_io(Monitor *mon, const QDict *qdict); #endif -- cgit v1.2.1 From bf736fe34caba0688c9095c31b9d097ea15c1296 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 15:17:55 +0200 Subject: blkdebug: Add BLKDBG_FLUSH_TO_OS/DISK events Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- block.c | 8 ++++---- block/blkdebug.c | 3 +++ include/block/block.h | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 3f616de974..79ad33d0f9 100644 --- a/block.c +++ b/block.c @@ -3186,13 +3186,11 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) { - BlockDriver *drv = bs->drv; - - if (!drv || !drv->bdrv_debug_event) { + if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { return; } - drv->bdrv_debug_event(bs, event); + bs->drv->bdrv_debug_event(bs, event); } int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, @@ -4024,6 +4022,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) } /* Write back cached data to the OS even with cache=unsafe */ + BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS); if (bs->drv->bdrv_co_flush_to_os) { ret = bs->drv->bdrv_co_flush_to_os(bs); if (ret < 0) { @@ -4036,6 +4035,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) goto flush_parent; } + BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK); if (bs->drv->bdrv_co_flush_to_disk) { ret = bs->drv->bdrv_co_flush_to_disk(bs); } else if (bs->drv->bdrv_aio_flush) { diff --git a/block/blkdebug.c b/block/blkdebug.c index 71f99e4067..ccb627ad93 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -182,6 +182,9 @@ static const char *event_names[BLKDBG_EVENT_MAX] = { [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", [BLKDBG_CLUSTER_FREE] = "cluster_free", + + [BLKDBG_FLUSH_TO_OS] = "flush_to_os", + [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", }; static int get_event_by_name(const char *name, BlkDebugEvent *event) diff --git a/include/block/block.h b/include/block/block.h index dc5b388d87..2307f67b0e 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -424,6 +424,9 @@ typedef enum { BLKDBG_CLUSTER_ALLOC_BYTES, BLKDBG_CLUSTER_FREE, + BLKDBG_FLUSH_TO_OS, + BLKDBG_FLUSH_TO_DISK, + BLKDBG_EVENT_MAX, } BlkDebugEvent; -- cgit v1.2.1 From c27d565604038c1572b16dd1cd06e277e6ef02e2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 15:17:56 +0200 Subject: ide-test: Add enum value for DEV Get rid of the magic number. Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- tests/ide-test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ide-test.c b/tests/ide-test.c index 365e9959ff..1c31a2e0ba 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -64,6 +64,7 @@ enum { }; enum { + DEV = 0x10, LBA = 0x40, }; @@ -394,7 +395,7 @@ static void test_identify(void) /* Read in the IDENTIFY buffer and check registers */ data = inb(IDE_BASE + reg_device); - g_assert_cmpint(data & 0x10, ==, 0); + g_assert_cmpint(data & DEV, ==, 0); for (i = 0; i < 256; i++) { data = inb(IDE_BASE + reg_status); -- cgit v1.2.1 From f68ec8379e88502b4841a110c070e9b118d3151c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Wed, 5 Jun 2013 15:17:57 +0200 Subject: ide: Set BSY bit during FLUSH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation of the ATA FLUSH command invokes a flush at the block layer, which may on raw files on POSIX entail a synchronous fdatasync(). This may in some cases take so long that the SLES 11 SP1 guest driver reports I/O errors and filesystems get corrupted or remounted read-only. Avoid this by setting BUSY_STAT, so that the guest is made aware we are in the middle of an operation and no ATA commands are attempted to be processed concurrently. Addresses BNC#637297. Suggested-by: Gonglei (Arei) Signed-off-by: Andreas Färber Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- hw/ide/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index c7a80413be..9926d9202b 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -814,6 +814,7 @@ void ide_flush_cache(IDEState *s) return; } + s->status |= BUSY_STAT; bdrv_acct_start(s->bs, &s->acct, 0, BDRV_ACCT_FLUSH); bdrv_aio_flush(s->bs, ide_flush_cb, s); } -- cgit v1.2.1 From bd07684aacfb61668ae2c25b7dd00b64f3d7c7f3 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 5 Jun 2013 15:17:58 +0200 Subject: ide-test: Add FLUSH CACHE test case This checks in particular that BSY is set while the flush request is in flight. Signed-off-by: Kevin Wolf Signed-off-by: Stefan Hajnoczi --- tests/ide-test.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/ide-test.c b/tests/ide-test.c index 1c31a2e0ba..828e71a38c 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -77,6 +77,7 @@ enum { enum { CMD_READ_DMA = 0xc8, CMD_WRITE_DMA = 0xca, + CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, CMDF_ABORT = 0x100, @@ -424,6 +425,43 @@ static void test_identify(void) ide_test_quit(); } +static void test_flush(void) +{ + uint8_t data; + + ide_test_start( + "-vnc none " + "-drive file=blkdebug::%s,if=ide,cache=writeback", + tmp_path); + + /* Delay the completion of the flush request until we explicitly do it */ + qmp("{'execute':'human-monitor-command', 'arguments': { " + "'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); + + /* FLUSH CACHE command on device 0*/ + outb(IDE_BASE + reg_device, 0); + outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + + /* Check status while request is in flight*/ + data = inb(IDE_BASE + reg_status); + assert_bit_set(data, BSY | DRDY); + assert_bit_clear(data, DF | ERR | DRQ); + + /* Complete the command */ + qmp("{'execute':'human-monitor-command', 'arguments': { " + "'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }"); + + /* Check registers */ + data = inb(IDE_BASE + reg_device); + g_assert_cmpint(data & DEV, ==, 0); + + data = inb(IDE_BASE + reg_status); + assert_bit_set(data, DRDY); + assert_bit_clear(data, BSY | DF | ERR | DRQ); + + ide_test_quit(); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -454,6 +492,8 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); + qtest_add_func("/ide/flush", test_flush); + ret = g_test_run(); /* Cleanup */ -- cgit v1.2.1 From fb0ed4539c6f02fa9e5a3cf9df2549713451eeca Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Thu, 6 Jun 2013 12:27:57 +0800 Subject: block: add snapshot info query function bdrv_query_snapshot_info_list() This patch adds function bdrv_query_snapshot_info_list(), which will retrieve snapshot info of an image in qmp object format. The implementation is based on the code moved from qemu-img.c with modification to fit more for qmp based block layer API. Signed-off-by: Wenchao Xia Reviewed-by: Eric Blake Signed-off-by: Stefan Hajnoczi --- block/qapi.c | 55 +++++++++++++++++++++++++++++++++++++++------------- include/block/qapi.h | 4 +++- qemu-img.c | 5 ++++- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 794dbf8fe8..1ed56da50c 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -26,29 +26,56 @@ #include "block/block_int.h" #include "qmp-commands.h" -void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info) +/* + * Returns 0 on success, with *p_list either set to describe snapshot + * information, or NULL because there are no snapshots. Returns -errno on + * error, with *p_list untouched. + */ +int bdrv_query_snapshot_info_list(BlockDriverState *bs, + SnapshotInfoList **p_list, + Error **errp) { int i, sn_count; QEMUSnapshotInfo *sn_tab = NULL; - SnapshotInfoList *info_list, *cur_item = NULL; + SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; + SnapshotInfo *info; + sn_count = bdrv_snapshot_list(bs, &sn_tab); + if (sn_count < 0) { + const char *dev = bdrv_get_device_name(bs); + switch (sn_count) { + case -ENOMEDIUM: + error_setg(errp, "Device '%s' is not inserted", dev); + break; + case -ENOTSUP: + error_setg(errp, + "Device '%s' does not support internal snapshots", + dev); + break; + default: + error_setg_errno(errp, -sn_count, + "Can't list snapshots of device '%s'", dev); + break; + } + return sn_count; + } for (i = 0; i < sn_count; i++) { - info->has_snapshots = true; - info_list = g_new0(SnapshotInfoList, 1); + info = g_new0(SnapshotInfo, 1); + info->id = g_strdup(sn_tab[i].id_str); + info->name = g_strdup(sn_tab[i].name); + info->vm_state_size = sn_tab[i].vm_state_size; + info->date_sec = sn_tab[i].date_sec; + info->date_nsec = sn_tab[i].date_nsec; + info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; + info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; - info_list->value = g_new0(SnapshotInfo, 1); - info_list->value->id = g_strdup(sn_tab[i].id_str); - info_list->value->name = g_strdup(sn_tab[i].name); - info_list->value->vm_state_size = sn_tab[i].vm_state_size; - info_list->value->date_sec = sn_tab[i].date_sec; - info_list->value->date_nsec = sn_tab[i].date_nsec; - info_list->value->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; - info_list->value->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; + info_list = g_new0(SnapshotInfoList, 1); + info_list->value = info; /* XXX: waiting for the qapi to support qemu-queue.h types */ if (!cur_item) { - info->snapshots = cur_item = info_list; + head = cur_item = info_list; } else { cur_item->next = info_list; cur_item = info_list; @@ -57,6 +84,8 @@ void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info) } g_free(sn_tab); + *p_list = head; + return 0; } void bdrv_collect_image_info(BlockDriverState *bs, diff --git a/include/block/qapi.h b/include/block/qapi.h index e6e568da94..4f223d1ce4 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -29,7 +29,9 @@ #include "block/block.h" #include "block/snapshot.h" -void bdrv_collect_snapshots(BlockDriverState *bs , ImageInfo *info); +int bdrv_query_snapshot_info_list(BlockDriverState *bs, + SnapshotInfoList **p_list, + Error **errp); void bdrv_collect_image_info(BlockDriverState *bs, ImageInfo *info, const char *filename); diff --git a/qemu-img.c b/qemu-img.c index e089c7860b..e3d8fe3c77 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1667,7 +1667,10 @@ static ImageInfoList *collect_image_info_list(const char *filename, info = g_new0(ImageInfo, 1); bdrv_collect_image_info(bs, info, filename); - bdrv_collect_snapshots(bs, info); + bdrv_query_snapshot_info_list(bs, &info->snapshots, NULL); + if (info->snapshots) { + info->has_snapshots = true; + } elem = g_new0(ImageInfoList, 1); elem->value = info; -- cgit v1.2.1 From 43526ec8d1395fe4efbed15e9764b64641b95bcc Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Thu, 6 Jun 2013 12:27:58 +0800 Subject: block: add image info query function bdrv_query_image_info() This patch adds function bdrv_query_image_info(), which will retrieve image info in qmp object format. The implementation is based on the code moved from qemu-img.c, but uses block layer function to get snapshot info. Signed-off-by: Wenchao Xia Signed-off-by: Stefan Hajnoczi --- block/qapi.c | 43 +++++++++++++++++++++++++++++++++++++------ include/block/qapi.h | 6 +++--- qemu-img.c | 11 ++++++----- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 1ed56da50c..e9d8b7433f 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -88,18 +88,29 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, return 0; } -void bdrv_collect_image_info(BlockDriverState *bs, - ImageInfo *info, - const char *filename) +/** + * bdrv_query_image_info: + * @bs: block device to examine + * @p_info: location to store image information + * @errp: location to store error information + * + * @p_info will be set only on success. On error, store error in @errp. + */ +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + Error **errp) { uint64_t total_sectors; - char backing_filename[1024]; + const char *backing_filename; char backing_filename2[1024]; BlockDriverInfo bdi; + int ret; + Error *err = NULL; + ImageInfo *info = g_new0(ImageInfo, 1); bdrv_get_geometry(bs, &total_sectors); - info->filename = g_strdup(filename); + info->filename = g_strdup(bs->filename); info->format = g_strdup(bdrv_get_format_name(bs)); info->virtual_size = total_sectors * 512; info->actual_size = bdrv_get_allocated_file_size(bs); @@ -116,7 +127,7 @@ void bdrv_collect_image_info(BlockDriverState *bs, info->dirty_flag = bdi.is_dirty; info->has_dirty_flag = true; } - bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); + backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { info->backing_filename = g_strdup(backing_filename); info->has_backing_filename = true; @@ -134,6 +145,26 @@ void bdrv_collect_image_info(BlockDriverState *bs, info->has_backing_filename_format = true; } } + + ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); + switch (ret) { + case 0: + if (info->snapshots) { + info->has_snapshots = true; + } + break; + /* recoverable error */ + case -ENOMEDIUM: + case -ENOTSUP: + error_free(err); + break; + default: + error_propagate(errp, err); + qapi_free_ImageInfo(info); + return; + } + + *p_info = info; } BlockInfo *bdrv_query_info(BlockDriverState *bs) diff --git a/include/block/qapi.h b/include/block/qapi.h index 4f223d1ce4..ab1f48f6f7 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -32,9 +32,9 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, SnapshotInfoList **p_list, Error **errp); -void bdrv_collect_image_info(BlockDriverState *bs, - ImageInfo *info, - const char *filename); +void bdrv_query_image_info(BlockDriverState *bs, + ImageInfo **p_info, + Error **errp); BlockInfo *bdrv_query_info(BlockDriverState *s); BlockStats *bdrv_query_stats(const BlockDriverState *bs); diff --git a/qemu-img.c b/qemu-img.c index e3d8fe3c77..809b4f1c00 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1644,6 +1644,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, ImageInfoList *head = NULL; ImageInfoList **last = &head; GHashTable *filenames; + Error *err = NULL; filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); @@ -1665,11 +1666,11 @@ static ImageInfoList *collect_image_info_list(const char *filename, goto err; } - info = g_new0(ImageInfo, 1); - bdrv_collect_image_info(bs, info, filename); - bdrv_query_snapshot_info_list(bs, &info->snapshots, NULL); - if (info->snapshots) { - info->has_snapshots = true; + bdrv_query_image_info(bs, &info, &err); + if (error_is_set(&err)) { + error_report("%s", error_get_pretty(err)); + error_free(err); + goto err; } elem = g_new0(ImageInfoList, 1); -- cgit v1.2.1 From 553a7e871822d933beaefbd596f0e4eed1614373 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Thu, 6 Jun 2013 12:27:59 +0800 Subject: qmp: add ImageInfo in BlockDeviceInfo used by query-block Now image info will be retrieved as an embbed json object inside BlockDeviceInfo, backing chain info and all related internal snapshot info can be got in the enhanced recursive structure of ImageInfo. New recursive member *backing-image is added to reflect the backing chain status. Signed-off-by: Wenchao Xia Signed-off-by: Stefan Hajnoczi --- block/qapi.c | 50 ++++++++++++++++++++++++++++++++++--- include/block/qapi.h | 4 ++- qapi-schema.json | 10 ++++++-- qmp-commands.hx | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 8 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index e9d8b7433f..a4bc4113b7 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -94,6 +94,13 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, * @p_info: location to store image information * @errp: location to store error information * + * Store "flat" image information in @p_info. + * + * "Flat" means it does *not* query backing image information, + * i.e. (*pinfo)->has_backing_image will be set to false and + * (*pinfo)->backing_image to NULL even when the image does in fact have + * a backing image. + * * @p_info will be set only on success. On error, store error in @errp. */ void bdrv_query_image_info(BlockDriverState *bs, @@ -167,9 +174,15 @@ void bdrv_query_image_info(BlockDriverState *bs, *p_info = info; } -BlockInfo *bdrv_query_info(BlockDriverState *bs) +/* @p_info will be set only on success. */ +void bdrv_query_info(BlockDriverState *bs, + BlockInfo **p_info, + Error **errp) { BlockInfo *info = g_malloc0(sizeof(*info)); + BlockDriverState *bs0; + ImageInfo **p_image_info; + Error *local_err = NULL; info->device = g_strdup(bs->device_name); info->type = g_strdup("unknown"); info->locked = bdrv_dev_is_medium_locked(bs); @@ -223,8 +236,30 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs) info->inserted->iops_wr = bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE]; } + + bs0 = bs; + p_image_info = &info->inserted->image; + while (1) { + bdrv_query_image_info(bs0, p_image_info, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + goto err; + } + if (bs0->drv && bs0->backing_hd) { + bs0 = bs0->backing_hd; + (*p_image_info)->has_backing_image = true; + p_image_info = &((*p_image_info)->backing_image); + } else { + break; + } + } } - return info; + + *p_info = info; + return; + + err: + qapi_free_BlockInfo(info); } BlockStats *bdrv_query_stats(const BlockDriverState *bs) @@ -261,16 +296,25 @@ BlockInfoList *qmp_query_block(Error **errp) { BlockInfoList *head = NULL, **p_next = &head; BlockDriverState *bs = NULL; + Error *local_err = NULL; while ((bs = bdrv_next(bs))) { BlockInfoList *info = g_malloc0(sizeof(*info)); - info->value = bdrv_query_info(bs); + bdrv_query_info(bs, &info->value, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + goto err; + } *p_next = info; p_next = &info->next; } return head; + + err: + qapi_free_BlockInfoList(head); + return NULL; } BlockStatsList *qmp_query_blockstats(Error **errp) diff --git a/include/block/qapi.h b/include/block/qapi.h index ab1f48f6f7..0496cc9282 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -35,7 +35,9 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, void bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, Error **errp); -BlockInfo *bdrv_query_info(BlockDriverState *s); +void bdrv_query_info(BlockDriverState *bs, + BlockInfo **p_info, + Error **errp); BlockStats *bdrv_query_stats(const BlockDriverState *bs); void bdrv_snapshot_dump(fprintf_function func_fprintf, void *f, diff --git a/qapi-schema.json b/qapi-schema.json index ef1f657efb..5ad6894738 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -236,6 +236,8 @@ # # @snapshots: #optional list of VM snapshots # +# @backing-image: #optional info of the backing image (since 1.6) +# # Since: 1.3 # ## @@ -245,7 +247,8 @@ '*actual-size': 'int', 'virtual-size': 'int', '*cluster-size': 'int', '*encrypted': 'bool', '*backing-filename': 'str', '*full-backing-filename': 'str', - '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } } + '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], + '*backing-image': 'ImageInfo' } } ## # @ImageCheck: @@ -756,6 +759,8 @@ # # @iops_wr: write I/O operations per second is specified # +# @image: the info of image used (since: 1.6) +# # Since: 0.14.0 # # Notes: This interface is only found in @BlockInfo. @@ -765,7 +770,8 @@ '*backing_file': 'str', 'backing_file_depth': 'int', 'encrypted': 'bool', 'encryption_key_missing': 'bool', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', - 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int'} } + 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', + 'image': 'ImageInfo' } } ## # @BlockDeviceIoStatus: diff --git a/qmp-commands.hx b/qmp-commands.hx index ffd130edf6..8cea5e554c 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1704,6 +1704,47 @@ Each json-object contain the following: - "iops": limit total I/O operations per second (json-int) - "iops_rd": limit read operations per second (json-int) - "iops_wr": limit write operations per second (json-int) + - "image": the detail of the image, it is a json-object containing + the following: + - "filename": image file name (json-string) + - "format": image format (json-string) + - "virtual-size": image capacity in bytes (json-int) + - "dirty-flag": true if image is not cleanly closed, not present + means clean (json-bool, optional) + - "actual-size": actual size on disk in bytes of the image, not + present when image does not support thin + provision (json-int, optional) + - "cluster-size": size of a cluster in bytes, not present if image + format does not support it (json-int, optional) + - "encrypted": true if the image is encrypted, not present means + false or the image format does not support + encryption (json-bool, optional) + - "backing_file": backing file name, not present means no backing + file is used or the image format does not + support backing file chain + (json-string, optional) + - "full-backing-filename": full path of the backing file, not + present if it equals backing_file or no + backing file is used + (json-string, optional) + - "backing-filename-format": the format of the backing file, not + present means unknown or no backing + file (json-string, optional) + - "snapshots": the internal snapshot info, it is an optional list + of json-object containing the following: + - "id": unique snapshot id (json-string) + - "name": snapshot name (json-string) + - "vm-state-size": size of the VM state in bytes (json-int) + - "date-sec": UTC date of the snapshot in seconds (json-int) + - "date-nsec": fractional part in nanoseconds to be used with + date-sec(json-int) + - "vm-clock-sec": VM clock relative to boot in seconds + (json-int) + - "vm-clock-nsec": fractional part in nanoseconds to be used + with vm-clock-sec (json-int) + - "backing-image": the detail of the backing image, it is an + optional json-object only present when a + backing image present for this image - "io-status": I/O operation status, only present if the device supports it and the VM is configured to stop on errors. It's always reset @@ -1724,14 +1765,38 @@ Example: "ro":false, "drv":"qcow2", "encrypted":false, - "file":"disks/test.img", - "backing_file_depth":0, + "file":"disks/test.qcow2", + "backing_file_depth":1, "bps":1000000, "bps_rd":0, "bps_wr":0, "iops":1000000, "iops_rd":0, "iops_wr":0, + "image":{ + "filename":"disks/test.qcow2", + "format":"qcow2", + "virtual-size":2048000, + "backing_file":"base.qcow2", + "full-backing-filename":"disks/base.qcow2", + "backing-filename-format:"qcow2", + "snapshots":[ + { + "id": "1", + "name": "snapshot1", + "vm-state-size": 0, + "date-sec": 10000200, + "date-nsec": 12, + "vm-clock-sec": 206, + "vm-clock-nsec": 30 + } + ], + "backing-image":{ + "filename":"disks/base.qcow2", + "format":"qcow2", + "virtual-size":2048000 + } + } }, "type":"unknown" }, -- cgit v1.2.1 From bd093a365e8d1437f437a48ddca3ed08283b3090 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Thu, 6 Jun 2013 12:28:00 +0800 Subject: hmp: show ImageInfo in 'info block' Now human monitor can show image details, include internal snapshot and backing chain info for every block device. Signed-off-by: Wenchao Xia Signed-off-by: Stefan Hajnoczi --- hmp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hmp.c b/hmp.c index 64e0baac16..4fc3bfef74 100644 --- a/hmp.c +++ b/hmp.c @@ -22,6 +22,7 @@ #include "qemu/sockets.h" #include "monitor/monitor.h" #include "ui/console.h" +#include "block/qapi.h" #include "qemu-io.h" static void hmp_handle_error(Monitor *mon, Error **errp) @@ -278,6 +279,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) void hmp_info_block(Monitor *mon, const QDict *qdict) { BlockInfoList *block_list, *info; + ImageInfo *image_info; block_list = qmp_query_block(NULL); @@ -319,6 +321,18 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) info->value->inserted->iops, info->value->inserted->iops_rd, info->value->inserted->iops_wr); + + monitor_printf(mon, " images:\n"); + image_info = info->value->inserted->image; + while (1) { + bdrv_image_info_dump((fprintf_function)monitor_printf, mon, + image_info); + if (image_info->has_backing_image) { + image_info = image_info->backing_image; + } else { + break; + } + } } else { monitor_printf(mon, " [not inserted]"); } -- cgit v1.2.1 From e73fe2b46c38776288415ce7bc8ba3fcd23721c4 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Thu, 6 Jun 2013 12:28:01 +0800 Subject: hmp: add parameters device and -v for info block With these parameters, user can choose the information to be showed, to avoid message flood in the monitor. Signed-off-by: Wenchao Xia Signed-off-by: Stefan Hajnoczi --- hmp.c | 25 ++++++++++++++++--------- monitor.c | 7 ++++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/hmp.c b/hmp.c index 4fc3bfef74..494a9aa459 100644 --- a/hmp.c +++ b/hmp.c @@ -280,10 +280,15 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) { BlockInfoList *block_list, *info; ImageInfo *image_info; + const char *device = qdict_get_try_str(qdict, "device"); + bool verbose = qdict_get_try_bool(qdict, "verbose", 0); block_list = qmp_query_block(NULL); for (info = block_list; info; info = info->next) { + if (device && strcmp(device, info->value->device)) { + continue; + } monitor_printf(mon, "%s: removable=%d", info->value->device, info->value->removable); @@ -322,15 +327,17 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) info->value->inserted->iops_rd, info->value->inserted->iops_wr); - monitor_printf(mon, " images:\n"); - image_info = info->value->inserted->image; - while (1) { - bdrv_image_info_dump((fprintf_function)monitor_printf, mon, - image_info); - if (image_info->has_backing_image) { - image_info = image_info->backing_image; - } else { - break; + if (verbose) { + monitor_printf(mon, " images:\n"); + image_info = info->value->inserted->image; + while (1) { + bdrv_image_info_dump((fprintf_function)monitor_printf, + mon, image_info); + if (image_info->has_backing_image) { + image_info = image_info->backing_image; + } else { + break; + } } } } else { diff --git a/monitor.c b/monitor.c index 9d279b81cf..017411fcb0 100644 --- a/monitor.c +++ b/monitor.c @@ -2472,9 +2472,10 @@ static mon_cmd_t info_cmds[] = { }, { .name = "block", - .args_type = "", - .params = "", - .help = "show the block devices", + .args_type = "verbose:-v,device:B?", + .params = "[-v] [device]", + .help = "show info of one block device or all block devices " + "(and details of images with -v option)", .mhandler.cmd = hmp_info_block, }, { -- cgit v1.2.1