Testing interactions between multiple requests that include discard requests require that qemu-io can do the discard asynchronously, like it already does for reads and writes. To this effect, add an 'aio_discard' command.
Signed-off-by: Kevin Wolf <[email protected]> Message-ID: <[email protected]> Reviewed-by: Denis V. Lunev <[email protected]> Tested-by: Denis V. Lunev <[email protected]> Signed-off-by: Kevin Wolf <[email protected]> --- qemu-io-cmds.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index f6d077908f2..de4c1966fea 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -2218,6 +2218,120 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) return 0; } +static void aio_discard_help(void) +{ + printf( +"\n" +" asynchronously discards a range of bytes from the given offset\n" +"\n" +" Example:\n" +" 'aio_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" +" The discard is performed asynchronously and the aio_flush command must be\n" +" used to ensure all outstanding aio requests have been completed.\n" +" Note that due to its asynchronous nature, this command will be\n" +" considered successful once the request is submitted, independently\n" +" of potential I/O errors.\n" +"\n"); +} + +static int aio_discard_f(BlockBackend *blk, int argc, char **argv); + +static const cmdinfo_t aio_discard_cmd = { + .name = "aio_discard", + .cfunc = aio_discard_f, + .perm = BLK_PERM_WRITE, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] off len", + .oneline = "asynchronously discards a number of bytes", + .help = aio_discard_help, +}; + +static void aio_discard_done(void *opaque, int ret) +{ + struct aio_ctx *ctx = opaque; + struct timespec t2; + + clock_gettime(CLOCK_MONOTONIC, &t2); + + if (ret < 0) { + printf("aio_discard failed: %s\n", strerror(-ret)); + block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct); + goto out; + } + + block_acct_done(blk_get_stats(ctx->blk), &ctx->acct); + + if (ctx->qflag) { + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, ctx->t1); + print_report("discarded ", &t2, ctx->offset, ctx->qiov.size, + ctx->qiov.size, 1, ctx->Cflag); +out: + g_free(ctx); +} + +static int aio_discard_f(BlockBackend *blk, int argc, char **argv) +{ + int c, ret; + int64_t count; + struct aio_ctx *ctx = g_new0(struct aio_ctx, 1); + + ctx->blk = blk; + + while ((c = getopt(argc, argv, "Cq")) != -1) { + switch (c) { + case 'C': + ctx->Cflag = true; + break; + case 'q': + ctx->qflag = true; + break; + default: + g_free(ctx); + qemuio_command_usage(&aio_discard_cmd); + return -EINVAL; + } + } + + if (optind != argc - 2) { + g_free(ctx); + qemuio_command_usage(&aio_discard_cmd); + return -EINVAL; + } + + ctx->offset = cvtnum(argv[optind]); + if (ctx->offset < 0) { + ret = ctx->offset; + print_cvtnum_err(ret, argv[optind]); + g_free(ctx); + return ret; + } + optind++; + + count = cvtnum(argv[optind]); + if (count < 0) { + print_cvtnum_err(count, argv[optind]); + g_free(ctx); + return count; + } + + clock_gettime(CLOCK_MONOTONIC, &ctx->t1); + ctx->qiov.size = count; + block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size, + BLOCK_ACCT_UNMAP); + blk_aio_pdiscard(blk, ctx->offset, count, aio_discard_done, ctx); + + return 0; +} + static int alloc_f(BlockBackend *blk, int argc, char **argv) { BlockDriverState *bs = blk_bs(blk); @@ -2800,6 +2914,7 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&length_cmd); qemuio_add_command(&info_cmd); qemuio_add_command(&discard_cmd); + qemuio_add_command(&aio_discard_cmd); qemuio_add_command(&alloc_cmd); qemuio_add_command(&map_cmd); qemuio_add_command(&reopen_cmd); -- 2.54.0
