[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC] virtio: optimize access to guest physical memor
From: |
Vincenzo Maffione |
Subject: |
[Qemu-devel] [PATCH RFC] virtio: optimize access to guest physical memory |
Date: |
Mon, 14 Dec 2015 15:51:19 +0100 |
Accessing a location of guest physical memory involves a translation
from guest physical addresses to host virtual address. This patch
removes unnecessary accesses by virtio implementation, so that at
high processing rates the bottleneck goes from ~1Mtts to ~2Mtts.
Signed-off-by: Vincenzo Maffione <address@hidden>
---
hw/virtio/virtio.c | 118 +++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 88 insertions(+), 30 deletions(-)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 1edef59..1f853d6 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -21,6 +21,7 @@
#include "hw/virtio/virtio-bus.h"
#include "migration/migration.h"
#include "hw/virtio/virtio-access.h"
+#include "exec/address-spaces.h"
/*
* The alignment to use between consumer and producer parts of vring.
@@ -71,6 +72,8 @@ struct VirtQueue
{
VRing vring;
uint16_t last_avail_idx;
+ uint16_t last_fetched_avail_idx;
+ uint16_t last_used_idx;
/* Last used index value we have signalled on */
uint16_t signalled_used;
@@ -138,6 +141,15 @@ static inline uint16_t vring_desc_next(VirtIODevice *vdev,
hwaddr desc_pa,
return virtio_lduw_phys(vdev, pa);
}
+static inline void
+vring_desc_fetch(VirtIODevice *vdev, hwaddr desc_pa, int i, VRingDesc *desc)
+{
+ hwaddr pa;
+ pa = desc_pa + sizeof(VRingDesc) * i;
+ address_space_rw(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)desc, sizeof(VRingDesc), false);
+}
+
static inline uint16_t vring_avail_flags(VirtQueue *vq)
{
hwaddr pa;
@@ -178,6 +190,15 @@ static inline void vring_used_ring_len(VirtQueue *vq, int
i, uint32_t val)
virtio_stl_phys(vq->vdev, pa, val);
}
+static inline void
+vring_used_writeback(VirtQueue *vq, int i, VRingUsedElem *ue)
+{
+ hwaddr pa;
+ pa = vq->vring.used + offsetof(VRingUsed, ring[i]);
+ address_space_rw(&address_space_memory, pa, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)ue, sizeof(VRingUsedElem), true);
+}
+
static uint16_t vring_used_idx(VirtQueue *vq)
{
hwaddr pa;
@@ -190,6 +211,7 @@ static inline void vring_used_idx_set(VirtQueue *vq,
uint16_t val)
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, idx);
virtio_stw_phys(vq->vdev, pa, val);
+ vq->last_used_idx = val;
}
static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
@@ -277,15 +299,17 @@ void virtqueue_discard(VirtQueue *vq, const
VirtQueueElement *elem,
void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int len, unsigned int idx)
{
+ VRingUsedElem ue;
+
trace_virtqueue_fill(vq, elem, len, idx);
virtqueue_unmap_sg(vq, elem, len);
- idx = (idx + vring_used_idx(vq)) % vq->vring.num;
+ idx = (idx + vq->last_used_idx) % vq->vring.num;
- /* Get a pointer to the next entry in the used ring. */
- vring_used_ring_id(vq, idx, elem->index);
- vring_used_ring_len(vq, idx, len);
+ ue.id = elem->index;
+ ue.len = len;
+ vring_used_writeback(vq, idx, &ue);
}
void virtqueue_flush(VirtQueue *vq, unsigned int count)
@@ -294,7 +318,7 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count)
/* Make sure buffer is written before we update index. */
smp_wmb();
trace_virtqueue_flush(vq, count);
- old = vring_used_idx(vq);
+ old = vq->last_used_idx;
new = old + count;
vring_used_idx_set(vq, new);
vq->inuse -= count;
@@ -328,6 +352,18 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int
idx)
return num_heads;
}
+static bool virtqueue_empty(VirtQueue *vq)
+{
+ if (vq->last_avail_idx == vq->last_fetched_avail_idx) {
+ vq->last_fetched_avail_idx = vring_avail_idx(vq);
+ /* On success, callers read a descriptor at vq->last_avail_idx.
+ * Make sure descriptor read does not bypass avail index read. */
+ smp_rmb();
+ }
+
+ return vq->last_avail_idx == vq->last_fetched_avail_idx;
+}
+
static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx)
{
unsigned int head;
@@ -345,18 +381,18 @@ static unsigned int virtqueue_get_head(VirtQueue *vq,
unsigned int idx)
return head;
}
-static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa,
- unsigned int i, unsigned int max)
+static unsigned virtqueue_next_desc(VirtIODevice *vdev, VRingDesc *desc,
+ unsigned int max)
{
unsigned int next;
/* If this descriptor says it doesn't chain, we're done. */
- if (!(vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_NEXT)) {
+ if (!(desc->flags & VRING_DESC_F_NEXT)) {
return max;
}
/* Check they're not leading us off end of descriptors. */
- next = vring_desc_next(vdev, desc_pa, i);
+ next = desc->next;
/* Make sure compiler knows to grab that: we don't want it changing! */
smp_wmb();
@@ -382,15 +418,17 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned
int *in_bytes,
VirtIODevice *vdev = vq->vdev;
unsigned int max, num_bufs, indirect = 0;
hwaddr desc_pa;
+ VRingDesc desc;
int i;
max = vq->vring.num;
num_bufs = total_bufs;
i = virtqueue_get_head(vq, idx++);
desc_pa = vq->vring.desc;
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
@@ -403,27 +441,34 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned
int *in_bytes,
/* loop over the indirect descriptor table */
indirect = 1;
- max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(vdev, desc_pa, i);
+ max = desc.len / sizeof(VRingDesc);
+ desc_pa = desc.addr;
num_bufs = i = 0;
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
}
- do {
+ for (;;) {
/* If we've got too many, that implies a descriptor loop. */
if (++num_bufs > max) {
error_report("Looped descriptor");
exit(1);
}
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
- in_total += vring_desc_len(vdev, desc_pa, i);
+ if (desc.flags & VRING_DESC_F_WRITE) {
+ in_total += desc.len;
} else {
- out_total += vring_desc_len(vdev, desc_pa, i);
+ out_total += desc.len;
}
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
goto done;
}
- } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
+
+ i = virtqueue_next_desc(vdev, &desc, max);
+ if (i == max) {
+ break;
+ }
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
+ }
if (!indirect)
total_bufs = num_bufs;
@@ -506,9 +551,11 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
unsigned int i, head, max;
hwaddr desc_pa = vq->vring.desc;
VirtIODevice *vdev = vq->vdev;
+ VRingDesc desc;
- if (!virtqueue_num_heads(vq, vq->last_avail_idx))
+ if (virtqueue_empty(vq)) {
return 0;
+ }
/* When we start there are none of either input nor output. */
elem->out_num = elem->in_num = 0;
@@ -520,46 +567,55 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
vring_set_avail_event(vq, vq->last_avail_idx);
}
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
+
+ if (desc.flags & VRING_DESC_F_INDIRECT) {
+ if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
/* loop over the indirect descriptor table */
- max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(vdev, desc_pa, i);
+ max = desc.len / sizeof(VRingDesc);
+ desc_pa = desc.addr;
i = 0;
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
}
/* Collect all the descriptors */
- do {
+ for (;;) {
struct iovec *sg;
- if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
+ if (desc.flags & VRING_DESC_F_WRITE) {
if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) {
error_report("Too many write descriptors in indirect table");
exit(1);
}
- elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i);
+ elem->in_addr[elem->in_num] = desc.addr;
sg = &elem->in_sg[elem->in_num++];
} else {
if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) {
error_report("Too many read descriptors in indirect table");
exit(1);
}
- elem->out_addr[elem->out_num] = vring_desc_addr(vdev, desc_pa, i);
+ elem->out_addr[elem->out_num] = desc.addr;
sg = &elem->out_sg[elem->out_num++];
}
- sg->iov_len = vring_desc_len(vdev, desc_pa, i);
+ sg->iov_len = desc.len;
/* If we've got too many, that implies a descriptor loop. */
if ((elem->in_num + elem->out_num) > max) {
error_report("Looped descriptor");
exit(1);
}
- } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
+
+ i = virtqueue_next_desc(vdev, &desc, max);
+ if (i == max) {
+ break;
+ }
+ vring_desc_fetch(vdev, desc_pa, i, &desc);
+ }
/* Now map what we have collected */
virtqueue_map(elem);
@@ -673,6 +729,8 @@ void virtio_reset(void *opaque)
vdev->vq[i].vring.avail = 0;
vdev->vq[i].vring.used = 0;
vdev->vq[i].last_avail_idx = 0;
+ vdev->vq[i].last_fetched_avail_idx = 0;
+ vdev->vq[i].last_used_idx = 0;
virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
vdev->vq[i].signalled_used = 0;
vdev->vq[i].signalled_used_valid = false;
@@ -1052,7 +1110,7 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue
*vq)
v = vq->signalled_used_valid;
vq->signalled_used_valid = true;
old = vq->signalled_used;
- new = vq->signalled_used = vring_used_idx(vq);
+ new = vq->signalled_used = vq->last_used_idx;
return !v || vring_need_event(vring_get_used_event(vq), new, old);
}
--
2.6.3