linux-aio: add IO_CMD_FDSYNC command support

Libaio defines IO_CMD_FDSYNC command to sync all outstanding
asynchronous I/O operations, by flushing out file data to the
disk storage. Enable linux-aio to submit such aio request.

When using aio=native without fdsync() support, QEMU creates
pthreads, and destroying these pthreads results in TLB flushes.
In a real-time guest environment, TLB flushes cause a latency
spike. This patch helps to avoid such spikes.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Prasad Pandit <pjp@fedoraproject.org>
Message-ID: <20240425070412.37248-1-ppandit@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Prasad Pandit 2024-04-25 12:34:12 +05:30 committed by Kevin Wolf
parent 10b1e09ed3
commit 24687abf23
3 changed files with 30 additions and 1 deletions

View File

@ -159,6 +159,7 @@ typedef struct BDRVRawState {
bool has_discard:1; bool has_discard:1;
bool has_write_zeroes:1; bool has_write_zeroes:1;
bool use_linux_aio:1; bool use_linux_aio:1;
bool has_laio_fdsync:1;
bool use_linux_io_uring:1; bool use_linux_io_uring:1;
int page_cache_inconsistent; /* errno from fdatasync failure */ int page_cache_inconsistent; /* errno from fdatasync failure */
bool has_fallocate; bool has_fallocate;
@ -718,6 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
if (s->use_linux_aio) {
s->has_laio_fdsync = laio_has_fdsync(s->fd);
}
#else #else
if (s->use_linux_aio) { if (s->use_linux_aio) {
error_setg(errp, "aio=native was specified, but is not supported " error_setg(errp, "aio=native was specified, but is not supported "
@ -2598,6 +2602,11 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
if (raw_check_linux_io_uring(s)) { if (raw_check_linux_io_uring(s)) {
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH);
} }
#endif
#ifdef CONFIG_LINUX_AIO
if (s->has_laio_fdsync && raw_check_linux_aio(s)) {
return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
}
#endif #endif
return raw_thread_pool_submit(handle_aiocb_flush, &acb); return raw_thread_pool_submit(handle_aiocb_flush, &acb);
} }

View File

@ -384,6 +384,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
case QEMU_AIO_READ: case QEMU_AIO_READ:
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
break; break;
case QEMU_AIO_FLUSH:
io_prep_fdsync(iocbs, fd);
break;
/* Currently Linux kernel does not support other operations */ /* Currently Linux kernel does not support other operations */
default: default:
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
@ -412,7 +415,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
AioContext *ctx = qemu_get_current_aio_context(); AioContext *ctx = qemu_get_current_aio_context();
struct qemu_laiocb laiocb = { struct qemu_laiocb laiocb = {
.co = qemu_coroutine_self(), .co = qemu_coroutine_self(),
.nbytes = qiov->size, .nbytes = qiov ? qiov->size : 0,
.ctx = aio_get_linux_aio(ctx), .ctx = aio_get_linux_aio(ctx),
.ret = -EINPROGRESS, .ret = -EINPROGRESS,
.is_read = (type == QEMU_AIO_READ), .is_read = (type == QEMU_AIO_READ),
@ -486,3 +489,19 @@ void laio_cleanup(LinuxAioState *s)
} }
g_free(s); g_free(s);
} }
bool laio_has_fdsync(int fd)
{
struct iocb cb;
struct iocb *cbs[] = {&cb, NULL};
io_context_t ctx = 0;
io_setup(1, &ctx);
/* check if host kernel supports IO_CMD_FDSYNC */
io_prep_fdsync(&cb, fd);
int ret = io_submit(ctx, 1, cbs);
io_destroy(ctx);
return (ret == -EINVAL) ? false : true;
}

View File

@ -60,6 +60,7 @@ void laio_cleanup(LinuxAioState *s);
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
int type, uint64_t dev_max_batch); int type, uint64_t dev_max_batch);
bool laio_has_fdsync(int);
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context);
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context);
#endif #endif