[PATCH 2/4] qemu-io: Add 'aio_discard' command

Kevin Wolf posted 4 patches 2 months, 2 weeks ago
Maintainers: John Snow <jsnow@redhat.com>, Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>, Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>
[PATCH 2/4] qemu-io: Add 'aio_discard' command
Posted by Kevin Wolf 2 months, 2 weeks ago
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 <kwolf@redhat.com>
---
 qemu-io-cmds.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 13e03301624..0f5baa70f94 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -2218,6 +2218,118 @@ 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:
+            qemuio_command_usage(&aio_discard_cmd);
+            return -EINVAL;
+        }
+    }
+
+    if (optind != argc - 2) {
+        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 +2912,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.53.0
Re: [PATCH 2/4] qemu-io: Add 'aio_discard' command
Posted by Denis V. Lunev 2 months, 2 weeks ago
On 4/27/26 19:05, Kevin Wolf wrote:
> 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 <kwolf@redhat.com>
> ---
>  qemu-io-cmds.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 113 insertions(+)
>
> diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
> index 13e03301624..0f5baa70f94 100644
> --- a/qemu-io-cmds.c
> +++ b/qemu-io-cmds.c
> @@ -2218,6 +2218,118 @@ 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:
> +            qemuio_command_usage(&aio_discard_cmd);
leaked ctx

> +            return -EINVAL;
> +        }
> +    }
> +
> +    if (optind != argc - 2) {
> +        qemuio_command_usage(&aio_discard_cmd);
leaked ctx

> +        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 +2912,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);