vhost: defer setting up new mem table

There is an issue when QEMU sets new memory table just after guest OS
starts booting. Then, if guest OS tries to issue any I/O to device (e.g.
using BIOS INT13h - EDD) it will get stuck because previous addresses of
mmaped memory might change.

To fix this issue, defer using the new mem table until after we receive
the first SET_VRING_ADDR message. SET_VRING_ADDR will be sent by QEMU
when guest OS virtio (e.g. virtio-scsi) driver starts initialization.
At this point it is safe to invalidate the old mem tables because there
will be no more outstanding IO at this point.

Change-Id: I24772be87a8b6c8781868b9b7773317761499748
Signed-off-by: Jim Harris <james.r.harris@intel.com>
Signed-off-by: Dariusz Stojaczyk <dariuszx.stojaczyk@intel.com>
This commit is contained in:
Dariusz Stojaczyk 2017-05-08 09:42:51 -07:00 committed by Jim Harris
parent fab374a7c6
commit 8457b98cf2
2 changed files with 37 additions and 2 deletions

View File

@ -47,6 +47,7 @@
#include <rte_ether.h>
#include "rte_vhost.h"
#include "vhost_user.h"
/* Used to indicate that the device is running on a data core */
#define VIRTIO_DEV_RUNNING 1
@ -196,6 +197,9 @@ struct virtio_net {
uint32_t nr_guest_pages;
uint32_t max_guest_pages;
struct guest_page *guest_pages;
int has_new_mem_table;
struct VhostUserMemory mem_table;
int mem_table_fds[VHOST_MEMORY_MAX_NREGIONS];
} __rte_cache_aligned;

View File

@ -323,6 +323,8 @@ qva_to_vva(struct virtio_net *dev, uint64_t qva)
return 0;
}
static int vhost_setup_mem_table(struct virtio_net *dev);
/*
* The virtio device sends us the desc, used and avail ring addresses.
* This function then converts these to our address space.
@ -332,6 +334,12 @@ vhost_user_set_vring_addr(struct virtio_net *dev, struct vhost_vring_addr *addr)
{
struct vhost_virtqueue *vq;
if (dev->has_new_mem_table) {
vhost_setup_mem_table(dev);
dev->has_new_mem_table = 0;
}
if (dev->mem == NULL)
return -1;
@ -499,7 +507,30 @@ dump_guest_pages(struct virtio_net *dev)
static int
vhost_user_set_mem_table(struct virtio_net *dev, struct VhostUserMsg *pmsg)
{
struct VhostUserMemory memory = pmsg->payload.memory;
uint32_t i;
if (dev->has_new_mem_table) {
/*
* The previous mem table was not consumed, so close the
* file descriptors from that mem table before copying
* the new one.
*/
for (i = 0; i < dev->mem_table.nregions; i++) {
close(dev->mem_table_fds[i]);
}
}
memcpy(&dev->mem_table, &pmsg->payload.memory, sizeof(dev->mem_table));
memcpy(dev->mem_table_fds, pmsg->fds, sizeof(dev->mem_table_fds));
dev->has_new_mem_table = 1;
return 0;
}
static int
vhost_setup_mem_table(struct virtio_net *dev)
{
struct VhostUserMemory memory = dev->mem_table;
struct rte_vhost_mem_region *reg;
void *mmap_addr;
uint64_t mmap_size;
@ -532,7 +563,7 @@ vhost_user_set_mem_table(struct virtio_net *dev, struct VhostUserMsg *pmsg)
dev->mem->nregions = memory.nregions;
for (i = 0; i < memory.nregions; i++) {
fd = pmsg->fds[i];
fd = dev->mem_table_fds[i];
reg = &dev->mem->regions[i];
reg->guest_phys_addr = memory.regions[i].guest_phys_addr;