X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-io.c;h=475a8bd0347a1c037d8b7c33b7beb672ce520dc7;hb=6eebf958abf3f3f701116d4524ef88bb9fd6e341;hp=ffa62fb46fb9ee50d0f02a61107ab6c111474f85;hpb=4a1cc6800a54b154a214e7ecf36c63c8c81f2074;p=qemu.git diff --git a/qemu-io.c b/qemu-io.c index ffa62fb46..475a8bd03 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -15,8 +15,10 @@ #include #include "qemu-common.h" -#include "block_int.h" +#include "qemu/main-loop.h" +#include "block/block_int.h" #include "cmd.h" +#include "trace/control.h" #define VERSION "0.0.1" @@ -130,7 +132,7 @@ static void print_report(const char *op, struct timeval *t, int64_t offset, static void * create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) { - size_t *sizes = calloc(nr_iov, sizeof(size_t)); + size_t *sizes = g_new0(size_t, nr_iov); size_t count = 0; void *buf = NULL; void *p; @@ -172,7 +174,7 @@ create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern) } fail: - free(sizes); + g_free(sizes); return buf; } @@ -218,6 +220,63 @@ static int do_pwrite(char *buf, int64_t offset, int count, int *total) return 1; } +typedef struct { + 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(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(int64_t offset, int count, int *total) +{ + Coroutine *co; + CoWriteZeroes data = { + .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(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(char *buf, int64_t offset, int count, int *total) { *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); @@ -249,7 +308,7 @@ static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -263,7 +322,7 @@ static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total) bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9, aio_rw_done, &async_ret); while (async_ret == NOT_DONE) { - qemu_aio_wait(); + main_loop_wait(false); } *total = qiov->size; @@ -306,7 +365,7 @@ static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) } while (async_ret.num_done < num_reqs) { - qemu_aio_wait(); + main_loop_wait(false); } return async_ret.error < 0 ? async_ret.error : 1; @@ -471,14 +530,14 @@ static int read_f(int argc, char **argv) } if (Pflag) { - void *cmp_buf = malloc(pattern_count); + 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); } - free(cmp_buf); + g_free(cmp_buf); } if (qflag) { @@ -601,13 +660,13 @@ static int readv_f(int argc, char **argv) } if (Pflag) { - void *cmp_buf = malloc(qiov.size); + 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); } - free(cmp_buf); + g_free(cmp_buf); } if (qflag) { @@ -623,6 +682,7 @@ static int readv_f(int argc, char **argv) print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); out: + qemu_iovec_destroy(&qiov); qemu_io_free(buf); return 0; } @@ -639,10 +699,12 @@ static void write_help(void) " 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"); } @@ -654,7 +716,7 @@ static const cmdinfo_t write_cmd = { .cfunc = write_f, .argmin = 2, .argmax = -1, - .args = "[-abCpq] [-P pattern ] off len", + .args = "[-bcCpqz] [-P pattern ] off len", .oneline = "writes a number of bytes at a specified offset", .help = write_help, }; @@ -662,20 +724,24 @@ static const cmdinfo_t write_cmd = { static int write_f(int argc, char **argv) { struct timeval t1, t2; - int Cflag = 0, pflag = 0, qflag = 0, bflag = 0; + int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; + int cflag = 0; int c, cnt; - char *buf; + 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, "bCpP:q")) != EOF) { + 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; @@ -683,6 +749,7 @@ static int write_f(int argc, char **argv) pflag = 1; break; case 'P': + Pflag = 1; pattern = parse_pattern(optarg); if (pattern < 0) { return 0; @@ -691,6 +758,9 @@ static int write_f(int argc, char **argv) case 'q': qflag = 1; break; + case 'z': + zflag = 1; + break; default: return command_usage(&write_cmd); } @@ -700,8 +770,13 @@ static int write_f(int argc, char **argv) return command_usage(&write_cmd); } - if (bflag && pflag) { - printf("-b and -p cannot be specified at the same time\n"); + 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; } @@ -732,13 +807,19 @@ static int write_f(int argc, char **argv) } } - buf = qemu_io_alloc(count, pattern); + if (!zflag) { + buf = qemu_io_alloc(count, pattern); + } gettimeofday(&t1, NULL); if (pflag) { cnt = do_pwrite(buf, offset, count, &total); } else if (bflag) { cnt = do_save_vmstate(buf, offset, count, &total); + } else if (zflag) { + cnt = do_co_write_zeroes(offset, count, &total); + } else if (cflag) { + cnt = do_write_compressed(buf, offset, count, &total); } else { cnt = do_write(buf, offset, count, &total); } @@ -758,7 +839,9 @@ static int write_f(int argc, char **argv) print_report("wrote", &t2, offset, count, total, cnt, Cflag); out: - qemu_io_free(buf); + if (!zflag) { + qemu_io_free(buf); + } return 0; } @@ -865,6 +948,7 @@ static int writev_f(int argc, char **argv) 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; } @@ -1063,7 +1147,8 @@ static void aio_write_done(void *opaque, int ret) ctx->qiov.size, 1, ctx->Cflag); out: qemu_io_free(ctx->buf); - free(ctx); + qemu_iovec_destroy(&ctx->qiov); + g_free(ctx); } static void aio_read_done(void *opaque, int ret) @@ -1079,14 +1164,14 @@ static void aio_read_done(void *opaque, int ret) } if (ctx->Pflag) { - void *cmp_buf = malloc(ctx->qiov.size); + 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); } - free(cmp_buf); + g_free(cmp_buf); } if (ctx->qflag) { @@ -1103,7 +1188,8 @@ static void aio_read_done(void *opaque, int ret) ctx->qiov.size, 1, ctx->Cflag); out: qemu_io_free(ctx->buf); - free(ctx); + qemu_iovec_destroy(&ctx->qiov); + g_free(ctx); } static void aio_read_help(void) @@ -1118,7 +1204,7 @@ static void aio_read_help(void) " 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" +" 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" @@ -1141,7 +1227,7 @@ static const cmdinfo_t aio_read_cmd = { static int aio_read_f(int argc, char **argv) { int nr_iov, c; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); while ((c = getopt(argc, argv, "CP:qv")) != EOF) { switch (c) { @@ -1152,7 +1238,7 @@ static int aio_read_f(int argc, char **argv) ctx->Pflag = 1; ctx->pattern = parse_pattern(optarg); if (ctx->pattern < 0) { - free(ctx); + g_free(ctx); return 0; } break; @@ -1163,20 +1249,20 @@ static int aio_read_f(int argc, char **argv) ctx->vflag = 1; break; default: - free(ctx); + g_free(ctx); return command_usage(&aio_read_cmd); } } if (optind > argc - 2) { - free(ctx); + 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]); - free(ctx); + g_free(ctx); return 0; } optind++; @@ -1184,14 +1270,14 @@ static int aio_read_f(int argc, char **argv) if (ctx->offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", ctx->offset); - free(ctx); + g_free(ctx); return 0; } nr_iov = argc - optind; ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab); if (ctx->buf == NULL) { - free(ctx); + g_free(ctx); return 0; } @@ -1214,7 +1300,7 @@ static void aio_write_help(void) " 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" +" 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" @@ -1237,7 +1323,7 @@ static int aio_write_f(int argc, char **argv) { int nr_iov, c; int pattern = 0xcd; - struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx)); + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); while ((c = getopt(argc, argv, "CqP:")) != EOF) { switch (c) { @@ -1250,25 +1336,25 @@ static int aio_write_f(int argc, char **argv) case 'P': pattern = parse_pattern(optarg); if (pattern < 0) { - free(ctx); + g_free(ctx); return 0; } break; default: - free(ctx); + g_free(ctx); return command_usage(&aio_write_cmd); } } if (optind > argc - 2) { - free(ctx); + 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]); - free(ctx); + g_free(ctx); return 0; } optind++; @@ -1276,14 +1362,14 @@ static int aio_write_f(int argc, char **argv) if (ctx->offset & 0x1ff) { printf("offset %" PRId64 " is not sector aligned\n", ctx->offset); - free(ctx); + g_free(ctx); return 0; } nr_iov = argc - optind; ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern); if (ctx->buf == NULL) { - free(ctx); + g_free(ctx); return 0; } @@ -1295,7 +1381,7 @@ static int aio_write_f(int argc, char **argv) static int aio_flush_f(int argc, char **argv) { - qemu_aio_flush(); + bdrv_drain_all(); return 0; } @@ -1497,7 +1583,7 @@ out: static int alloc_f(int argc, char **argv) { - int64_t offset; + int64_t offset, sector_num; int nb_sectors, remaining; char s1[64]; int num, sum_alloc; @@ -1518,12 +1604,18 @@ static int alloc_f(int argc, char **argv) remaining = nb_sectors; sum_alloc = 0; + sector_num = offset >> 9; while (remaining) { - ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num); + 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)); @@ -1579,6 +1671,78 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; +static int break_f(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(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(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(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 close_f(int argc, char **argv) { @@ -1602,14 +1766,14 @@ static int openfile(char *name, int flags, int growable) } if (growable) { - if (bdrv_file_open(&bs, name, flags)) { + if (bdrv_file_open(&bs, name, NULL, flags)) { fprintf(stderr, "%s: can't open device %s\n", progname, name); return 1; } } else { bs = bdrv_new("hda"); - if (bdrv_open(bs, name, flags, NULL) < 0) { + if (bdrv_open(bs, name, NULL, flags, NULL) < 0) { fprintf(stderr, "%s: can't open device %s\n", progname, name); bdrv_delete(bs); bs = NULL; @@ -1722,6 +1886,8 @@ static void usage(const char *name) " -g, --growable allow file to grow (only applies to protocols)\n" " -m, --misalign misalign allocations for O_DIRECT\n" " -k, --native-aio use kernel AIO implementation (on Linux only)\n" +" -t, --cache=MODE use the given cache mode for the image\n" +" -T, --trace FILE enable trace events listed in the given file\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\n", @@ -1733,7 +1899,7 @@ int main(int argc, char **argv) { int readonly = 0; int growable = 0; - const char *sopt = "hVc:rsnmgk"; + const char *sopt = "hVc:d:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1745,11 +1911,14 @@ int main(int argc, char **argv) { "misalign", 0, NULL, 'm' }, { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, + { "discard", 1, NULL, 'd' }, + { "cache", 1, NULL, 't' }, + { "trace", 1, NULL, 'T' }, { NULL, 0, NULL, 0 } }; int c; int opt_index = 0; - int flags = 0; + int flags = BDRV_O_UNMAP; progname = basename(argv[0]); @@ -1761,6 +1930,12 @@ int main(int argc, char **argv) case 'n': flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; break; + case 'd': + if (bdrv_parse_discard_flags(optarg, &flags) < 0) { + error_report("Invalid discard option: %s", optarg); + exit(1); + } + break; case 'c': add_user_command(optarg); break; @@ -1776,6 +1951,17 @@ int main(int argc, char **argv) case 'k': flags |= BDRV_O_NATIVE_AIO; break; + case 't': + if (bdrv_parse_cache_flags(optarg, &flags) < 0) { + error_report("Invalid cache option: %s", optarg); + exit(1); + } + break; + case 'T': + if (!trace_backend_init(optarg, NULL)) { + exit(1); /* error message will have been printed */ + } + break; case 'V': printf("%s version %s\n", progname, VERSION); exit(0); @@ -1793,6 +1979,7 @@ int main(int argc, char **argv) exit(1); } + qemu_init_main_loop(); bdrv_init(); /* initialize commands */ @@ -1815,6 +2002,10 @@ int main(int argc, char **argv) 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_args_command(init_args_command); add_check_command(init_check_command);