Skip to content

Commit 84d61e5

Browse files
stefanhaRHkevmw
authored andcommitted
virtio: use defer_call() in virtio_irqfd_notify()
virtio-blk and virtio-scsi invoke virtio_irqfd_notify() to send Used Buffer Notifications from an IOThread. This involves an eventfd write(2) syscall. Calling this repeatedly when completing multiple I/O requests in a row is wasteful. Use the defer_call() API to batch together virtio_irqfd_notify() calls made during thread pool (aio=threads), Linux AIO (aio=native), and io_uring (aio=io_uring) completion processing. Behavior is unchanged for emulated devices that do not use defer_call_begin()/defer_call_end() since defer_call() immediately invokes the callback when called outside a defer_call_begin()/defer_call_end() region. fio rw=randread bs=4k iodepth=64 numjobs=8 IOPS increases by ~9% with a single IOThread and 8 vCPUs. iodepth=1 decreases by ~1% but this could be noise. Detailed performance data and configuration specifics are available here: https://gitlab.com/stefanha/virt-playbooks/-/tree/blk_io_plug-irqfd This duplicates the BH that virtio-blk uses for batching. The next commit will remove it. Reviewed-by: Eric Blake <[email protected]> Signed-off-by: Stefan Hajnoczi <[email protected]> Message-ID: <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Reviewed-by: Kevin Wolf <[email protected]> Signed-off-by: Kevin Wolf <[email protected]>
1 parent 433fcea commit 84d61e5

File tree

5 files changed

+28
-1
lines changed

5 files changed

+28
-1
lines changed

block/io_uring.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ static void luring_process_completions(LuringState *s)
125125
{
126126
struct io_uring_cqe *cqes;
127127
int total_bytes;
128+
129+
defer_call_begin();
130+
128131
/*
129132
* Request completion callbacks can run the nested event loop.
130133
* Schedule ourselves so the nested event loop will "see" remaining
@@ -217,7 +220,10 @@ static void luring_process_completions(LuringState *s)
217220
aio_co_wake(luringcb->co);
218221
}
219222
}
223+
220224
qemu_bh_cancel(s->completion_bh);
225+
226+
defer_call_end();
221227
}
222228

223229
static int ioq_submit(LuringState *s)

block/linux-aio.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ static void qemu_laio_process_completions(LinuxAioState *s)
205205
{
206206
struct io_event *events;
207207

208+
defer_call_begin();
209+
208210
/* Reschedule so nested event loops see currently pending completions */
209211
qemu_bh_schedule(s->completion_bh);
210212

@@ -231,6 +233,8 @@ static void qemu_laio_process_completions(LinuxAioState *s)
231233
* own `for` loop. If we are the last all counters dropped to zero. */
232234
s->event_max = 0;
233235
s->event_idx = 0;
236+
237+
defer_call_end();
234238
}
235239

236240
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)

hw/virtio/trace-events

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "
7373
virtqueue_flush(void *vq, unsigned int count) "vq %p count %u"
7474
virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u"
7575
virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p"
76+
virtio_notify_irqfd_deferred_fn(void *vdev, void *vq) "vdev %p vq %p"
7677
virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p"
7778
virtio_notify(void *vdev, void *vq) "vdev %p vq %p"
7879
virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u"

hw/virtio/virtio.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "qapi/error.h"
1616
#include "qapi/qapi-commands-virtio.h"
1717
#include "trace.h"
18+
#include "qemu/defer-call.h"
1819
#include "qemu/error-report.h"
1920
#include "qemu/log.h"
2021
#include "qemu/main-loop.h"
@@ -2445,6 +2446,16 @@ static bool virtio_should_notify(VirtIODevice *vdev, VirtQueue *vq)
24452446
}
24462447
}
24472448

2449+
/* Batch irqs while inside a defer_call_begin()/defer_call_end() section */
2450+
static void virtio_notify_irqfd_deferred_fn(void *opaque)
2451+
{
2452+
EventNotifier *notifier = opaque;
2453+
VirtQueue *vq = container_of(notifier, VirtQueue, guest_notifier);
2454+
2455+
trace_virtio_notify_irqfd_deferred_fn(vq->vdev, vq);
2456+
event_notifier_set(notifier);
2457+
}
2458+
24482459
void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq)
24492460
{
24502461
WITH_RCU_READ_LOCK_GUARD() {
@@ -2471,7 +2482,7 @@ void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq)
24712482
* to an atomic operation.
24722483
*/
24732484
virtio_set_isr(vq->vdev, 0x1);
2474-
event_notifier_set(&vq->guest_notifier);
2485+
defer_call(virtio_notify_irqfd_deferred_fn, &vq->guest_notifier);
24752486
}
24762487

24772488
static void virtio_irq(VirtQueue *vq)

util/thread-pool.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* GNU GPL, version 2 or (at your option) any later version.
1616
*/
1717
#include "qemu/osdep.h"
18+
#include "qemu/defer-call.h"
1819
#include "qemu/queue.h"
1920
#include "qemu/thread.h"
2021
#include "qemu/coroutine.h"
@@ -175,6 +176,8 @@ static void thread_pool_completion_bh(void *opaque)
175176
ThreadPool *pool = opaque;
176177
ThreadPoolElement *elem, *next;
177178

179+
defer_call_begin(); /* cb() may use defer_call() to coalesce work */
180+
178181
restart:
179182
QLIST_FOREACH_SAFE(elem, &pool->head, all, next) {
180183
if (elem->state != THREAD_DONE) {
@@ -208,6 +211,8 @@ static void thread_pool_completion_bh(void *opaque)
208211
qemu_aio_unref(elem);
209212
}
210213
}
214+
215+
defer_call_end();
211216
}
212217

213218
static void thread_pool_cancel(BlockAIOCB *acb)

0 commit comments

Comments
 (0)