qemu-io: Add 'aio_discard' command
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> Message-ID: <20260427170520.101242-3-kwolf@redhat.com> Reviewed-by: Denis V. Lunev <den@openvz.org> Tested-by: Denis V. Lunev <den@openvz.org> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Kevin Wolf committed
Apr 27, 2026 at 19:05 UTC
7f8466e2ce620e3c6a6e2f32d616367174d4dbe9
1 file changed
+115
qemu-io-cmds.c
+115
@@ -2218,6 +2218,120 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
2218
return 0;
2219
}
2220
2221
+static void aio_discard_help(void)
2222
+{
2223
+ printf(
2224
+"\n"
2225
+" asynchronously discards a range of bytes from the given offset\n"
2226
+"\n"
2227
+" Example:\n"
2228
+" 'aio_discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n"
2229
+"\n"
2230
+" Discards a segment of the currently open file.\n"
2231
+" -C, -- report statistics in a machine parsable format\n"
2232
+" -q, -- quiet mode, do not show I/O statistics\n"
2233
+" The discard is performed asynchronously and the aio_flush command must be\n"
2234
+" used to ensure all outstanding aio requests have been completed.\n"
2235
+" Note that due to its asynchronous nature, this command will be\n"
2236
+" considered successful once the request is submitted, independently\n"
2237
+" of potential I/O errors.\n"
2238
+"\n");
2239
+}
2240
+
2241
+static int aio_discard_f(BlockBackend *blk, int argc, char **argv);
2242
+
2243
+static const cmdinfo_t aio_discard_cmd = {
2244
+ .name = "aio_discard",
2245
+ .cfunc = aio_discard_f,
2246
+ .perm = BLK_PERM_WRITE,
2247
+ .argmin = 2,
2248
+ .argmax = -1,
2249
+ .args = "[-Cq] off len",
2250
+ .oneline = "asynchronously discards a number of bytes",
2251
+ .help = aio_discard_help,
2252
+};
2253
+
2254
+static void aio_discard_done(void *opaque, int ret)
2255
+{
2256
+ struct aio_ctx *ctx = opaque;
2257
+ struct timespec t2;
2258
+
2259
+ clock_gettime(CLOCK_MONOTONIC, &t2);
2260
+
2261
+ if (ret < 0) {
2262
+ printf("aio_discard failed: %s\n", strerror(-ret));
2263
+ block_acct_failed(blk_get_stats(ctx->blk), &ctx->acct);
2264
+ goto out;
2265
+ }
2266
+
2267
+ block_acct_done(blk_get_stats(ctx->blk), &ctx->acct);
2268
+
2269
+ if (ctx->qflag) {
2270
+ goto out;
2271
+ }
2272
+
2273
+ /* Finally, report back -- -C gives a parsable format */
2274
+ t2 = tsub(t2, ctx->t1);
2275
+ print_report("discarded ", &t2, ctx->offset, ctx->qiov.size,
2276
+ ctx->qiov.size, 1, ctx->Cflag);
2277
+out:
2278
+ g_free(ctx);
2279
+}
2280
+
2281
+static int aio_discard_f(BlockBackend *blk, int argc, char **argv)
2282
+{
2283
+ int c, ret;
2284
+ int64_t count;
2285
+ struct aio_ctx *ctx = g_new0(struct aio_ctx, 1);
2286
+
2287
+ ctx->blk = blk;
2288
+
2289
+ while ((c = getopt(argc, argv, "Cq")) != -1) {
2290
+ switch (c) {
2291
+ case 'C':
2292
+ ctx->Cflag = true;
2293
+ break;
2294
+ case 'q':
2295
+ ctx->qflag = true;
2296
+ break;
2297
+ default:
2298
+ g_free(ctx);
2299
+ qemuio_command_usage(&aio_discard_cmd);
2300
+ return -EINVAL;
2301
+ }
2302
+ }
2303
+
2304
+ if (optind != argc - 2) {
2305
+ g_free(ctx);
2306
+ qemuio_command_usage(&aio_discard_cmd);
2307
+ return -EINVAL;
2308
+ }
2309
+
2310
+ ctx->offset = cvtnum(argv[optind]);
2311
+ if (ctx->offset < 0) {
2312
+ ret = ctx->offset;
2313
+ print_cvtnum_err(ret, argv[optind]);
2314
+ g_free(ctx);
2315
+ return ret;
2316
+ }
2317
+ optind++;
2318
+
2319
+ count = cvtnum(argv[optind]);
2320
+ if (count < 0) {
2321
+ print_cvtnum_err(count, argv[optind]);
2322
+ g_free(ctx);
2323
+ return count;
2324
+ }
2325
+
2326
+ clock_gettime(CLOCK_MONOTONIC, &ctx->t1);
2327
+ ctx->qiov.size = count;
2328
+ block_acct_start(blk_get_stats(blk), &ctx->acct, ctx->qiov.size,
2329
+ BLOCK_ACCT_UNMAP);
2330
+ blk_aio_pdiscard(blk, ctx->offset, count, aio_discard_done, ctx);
2331
+
2332
+ return 0;
2333
+}
2334
+
2335
static int alloc_f(BlockBackend *blk, int argc, char **argv)
2336
{
2337
BlockDriverState *bs = blk_bs(blk);
@@ -2800,6 +2914,7 @@ static void __attribute((constructor)) init_qemuio_commands(void)
2914
qemuio_add_command(&length_cmd);
2915
qemuio_add_command(&info_cmd);
2916
qemuio_add_command(&discard_cmd);
2917
+ qemuio_add_command(&aio_discard_cmd);
2918
qemuio_add_command(&alloc_cmd);
2919
qemuio_add_command(&map_cmd);
2920
qemuio_add_command(&reopen_cmd);