[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when
From: |
Dr. David Alan Gilbert |
Subject: |
Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap |
Date: |
Fri, 8 Dec 2017 20:45:31 +0000 |
User-agent: |
Mutt/1.9.1 (2017-09-22) |
* Igor Mammedov (address@hidden) wrote:
> vhost_verify_ring_mappings() were used to verify that
> rings are still accessible and related memory hasn't
> been moved after flatview is updated.
I think I've rolled the equivalent into my v2 I've just
posted; please have a look.
Dave
> It were doing checks by mapping ring's GPA+len and
> checking that HVA hasn't changed with new memory map.
> To avoid maybe expensive mapping call, we were
> identifying address range that changed and were doing
> mapping only if ring were in changed range.
>
> However it's no neccessary to perform ringi's GPA
> mapping as we already have it's current HVA and all
> we need is to verify that ring's GPA translates to
> the same HVA in updated flatview.
>
> That could be done during time when we are building
> vhost memmap where vhost_update_mem_cb() already maps
> every RAM MemoryRegionSection to get section HVA. This
> fact can be used to check that ring belongs to the same
> MemoryRegion in the same place, like this:
>
> hva_ring_offset = GPA(ring) - GPA(MemoryRegionSection)
> ring_hva == HVA(MemoryRegionSection) + hva_ring_offset
>
> Doing this would allow us to drop ~100LOC of code which
> figures out changed memory range and avoid calling not
> needed reverse vhost_memory_map(is_write=true) which is
> overkill for the task at hand.
>
> Signed-off-by: Igor Mammedov <address@hidden>
> ---
> PS:
> should be applied on top of David's series
>
> ---
> include/hw/virtio/vhost.h | 2 -
> hw/virtio/vhost.c | 195
> ++++++++++++++--------------------------------
> 2 files changed, 57 insertions(+), 140 deletions(-)
>
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index 467dc77..fc4af5c 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -68,8 +68,6 @@ struct vhost_dev {
> uint64_t log_size;
> Error *migration_blocker;
> bool memory_changed;
> - hwaddr mem_changed_start_addr;
> - hwaddr mem_changed_end_addr;
> const VhostOps *vhost_ops;
> void *opaque;
> struct vhost_log *log;
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 5b9a7d7..026bac3 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -296,35 +296,43 @@ static void vhost_memory_unmap(struct vhost_dev *dev,
> void *buffer,
> }
> }
>
> -static int vhost_verify_ring_part_mapping(struct vhost_dev *dev,
> - void *part,
> - uint64_t part_addr,
> - uint64_t part_size,
> - uint64_t start_addr,
> - uint64_t size)
> +static int vhost_verify_ring_part_mapping(void *ring_hva,
> + uint64_t ring_gpa,
> + uint64_t ring_size,
> + void *reg_hva,
> + uint64_t reg_gpa,
> + uint64_t reg_size)
> {
> - hwaddr l;
> - void *p;
> - int r = 0;
> + uint64_t hva_ring_offset;
> + uint64_t ring_last = range_get_last(ring_gpa, ring_size);
> + uint64_t reg_last = range_get_last(reg_gpa, reg_size);
>
> - if (!ranges_overlap(start_addr, size, part_addr, part_size)) {
> + /* start check from the end so that the rest of checks
> + * are done when whole ring is in merged sections range
> + */
> + if (ring_last < reg_last || ring_gpa > reg_last) {
> return 0;
> }
> - l = part_size;
> - p = vhost_memory_map(dev, part_addr, &l, 1);
> - if (!p || l != part_size) {
> - r = -ENOMEM;
> +
> + /* check that whole ring's is mapped */
> + if (range_get_last(ring_gpa, ring_size) >
> + range_get_last(reg_gpa, reg_size)) {
> + return -EBUSY;
> }
> - if (p != part) {
> - r = -EBUSY;
> +
> + /* check that ring's MemoryRegion wasn't replaced */
> + hva_ring_offset = ring_gpa - reg_gpa;
> + if (ring_hva != reg_hva + hva_ring_offset) {
> + return -ENOMEM;
> }
> - vhost_memory_unmap(dev, p, l, 0, 0);
> - return r;
> +
> + return 0;
> }
>
> static int vhost_verify_ring_mappings(struct vhost_dev *dev,
> - uint64_t start_addr,
> - uint64_t size)
> + void *reg_hva,
> + uint64_t reg_gpa,
> + uint64_t reg_size)
> {
> int i, j;
> int r = 0;
> @@ -338,23 +346,26 @@ static int vhost_verify_ring_mappings(struct vhost_dev
> *dev,
> struct vhost_virtqueue *vq = dev->vqs + i;
>
> j = 0;
> - r = vhost_verify_ring_part_mapping(dev, vq->desc, vq->desc_phys,
> - vq->desc_size, start_addr, size);
> - if (!r) {
> + r = vhost_verify_ring_part_mapping(
> + vq->desc, vq->desc_phys, vq->desc_size,
> + reg_hva, reg_gpa, reg_size);
> + if (r) {
> break;
> }
>
> j++;
> - r = vhost_verify_ring_part_mapping(dev, vq->avail, vq->avail_phys,
> - vq->avail_size, start_addr, size);
> - if (!r) {
> + r = vhost_verify_ring_part_mapping(
> + vq->desc, vq->desc_phys, vq->desc_size,
> + reg_hva, reg_gpa, reg_size);
> + if (r) {
> break;
> }
>
> j++;
> - r = vhost_verify_ring_part_mapping(dev, vq->used, vq->used_phys,
> - vq->used_size, start_addr, size);
> - if (!r) {
> + r = vhost_verify_ring_part_mapping(
> + vq->desc, vq->desc_phys, vq->desc_size,
> + reg_hva, reg_gpa, reg_size);
> + if (r) {
> break;
> }
> }
> @@ -384,24 +395,18 @@ static bool vhost_section(MemoryRegionSection *section)
> return result;
> }
>
> -static void vhost_begin(MemoryListener *listener)
> -{
> - struct vhost_dev *dev = container_of(listener, struct vhost_dev,
> - memory_listener);
> - dev->mem_changed_end_addr = 0;
> - dev->mem_changed_start_addr = -1;
> -}
> -
> struct vhost_update_mem_tmp {
> struct vhost_dev *dev;
> uint32_t nregions;
> struct vhost_memory_region *regions;
> };
>
> +
> /* Called for each MRS from vhost_update_mem */
> static int vhost_update_mem_cb(MemoryRegionSection *mrs, void *opaque)
> {
> struct vhost_update_mem_tmp *vtmp = opaque;
> + struct vhost_dev *dev = vtmp->dev;
> struct vhost_memory_region *cur_vmr;
> bool need_add = true;
> uint64_t mrs_size;
> @@ -416,8 +421,7 @@ static int vhost_update_mem_cb(MemoryRegionSection *mrs,
> void *opaque)
> mrs_host = (uintptr_t)memory_region_get_ram_ptr(mrs->mr) +
> mrs->offset_within_region;
>
> - trace_vhost_update_mem_cb(mrs->mr->name, mrs_gpa, mrs_size,
> - (uint64_t)mrs_host);
> + trace_vhost_update_mem_cb(mrs->mr->name, mrs_gpa, mrs_size, mrs_host);
>
> if (vtmp->nregions) {
> /* Since we already have at least one region, lets see if
> @@ -459,85 +463,19 @@ static int vhost_update_mem_cb(MemoryRegionSection
> *mrs, void *opaque)
> cur_vmr->flags_padding = 0;
> }
>
> - return 0;
> -}
> -
> -static bool vhost_update_compare_list(struct vhost_dev *dev,
> - struct vhost_update_mem_tmp *vtmp,
> - hwaddr *change_start, hwaddr
> *change_end)
> -{
> - uint32_t oldi, newi;
> - *change_start = 0;
> - *change_end = 0;
> -
> - for (newi = 0, oldi = 0; newi < vtmp->nregions; newi++) {
> - struct vhost_memory_region *newr = &vtmp->regions[newi];
> - hwaddr newr_last = range_get_last(newr->guest_phys_addr,
> - newr->memory_size);
> - trace_vhost_update_compare_list_loopn(newi, oldi,
> - newr->guest_phys_addr,
> - newr->memory_size);
> - bool whole_change = true;
> - while (oldi < dev->mem->nregions) {
> - struct vhost_memory_region *oldr = &dev->mem->regions[oldi];
> - hwaddr oldr_last = range_get_last(oldr->guest_phys_addr,
> - oldr->memory_size);
> - trace_vhost_update_compare_list_loopo(newi, oldi,
> - oldr->guest_phys_addr,
> - oldr->memory_size);
> - if (newr->guest_phys_addr == oldr->guest_phys_addr &&
> - newr->memory_size == oldr->memory_size) {
> - /* Match in GPA and size, but it could be different
> - * in host address or flags
> - */
> - whole_change = newr->userspace_addr != oldr->userspace_addr
> ||
> - newr->flags_padding != oldr->flags_padding;
> - oldi++;
> - break; /* inner old loop */
> - }
> - /* There's a difference - need to figure out what */
> - if (oldr_last < newr->guest_phys_addr) {
> - /* There used to be a region before us that's gone */
> - *change_start = MIN(*change_start, oldr->guest_phys_addr);
> - *change_end = MAX(*change_end, oldr_last);
> - oldi++;
> - continue; /* inner old loop */
> - }
> - if (oldr->guest_phys_addr > newr_last) {
> - /* We've passed all the old mappings that could have
> overlapped
> - * this one
> - */
> - break; /* Inner old loop */
> - }
> - /* Overlap case */
> - *change_start = MIN(*change_start,
> - MIN(oldr->guest_phys_addr,
> - newr->guest_phys_addr));
> - *change_end = MAX(*change_end,
> - MAX(oldr_last, newr_last));
> - whole_change = false;
> - /* There might be more old mappings that overlap */
> - oldi++;
> - }
> - if (whole_change) {
> - /* No old region to compare against, this must be a change */
> - *change_start = MIN(*change_start, newr->guest_phys_addr);
> - *change_end = MAX(*change_end, newr_last);
> - }
> + if (dev->started &&
> + vhost_verify_ring_mappings(dev, (void *)mrs_host, mrs_gpa,
> mrs_size)) {
> + abort();
> }
>
> - return *change_start || *change_end;
> + return 0;
> }
>
> static int vhost_update_mem(struct vhost_dev *dev)
> {
> int res;
> - struct vhost_update_mem_tmp vtmp;
> - hwaddr change_start, change_end;
> - bool need_update;
> - vtmp.regions = 0;
> - vtmp.nregions = 0;
> - vtmp.dev = dev;
> + size_t mem_size;
> + struct vhost_update_mem_tmp vtmp = {.dev = dev};
>
> res = address_space_iterate(&address_space_memory,
> vhost_update_mem_cb, &vtmp);
> @@ -545,24 +483,17 @@ static int vhost_update_mem(struct vhost_dev *dev)
> goto out;
> }
>
> - need_update = vhost_update_compare_list(dev, &vtmp,
> - &change_start, &change_end);
> - trace_vhost_update_mem_comparison(need_update,
> - (uint64_t)change_start,
> - (uint64_t)change_end);
> - if (need_update) {
> - /* Update the main regions list from our tmp */
> - size_t mem_size = offsetof(struct vhost_memory, regions) +
> - (vtmp.nregions + 1) * sizeof dev->mem->regions[0];
> + mem_size = offsetof(struct vhost_memory, regions) +
> + (vtmp.nregions + 1) * sizeof dev->mem->regions[0];
>
> + if(vtmp.nregions != dev->mem->nregions ||
> + memcmp(vtmp.regions, dev->mem->regions, mem_size)) {
> + /* Update the main regions list from our tmp */
> dev->mem = g_realloc(dev->mem, mem_size);
> dev->mem->nregions = vtmp.nregions;
> memcpy(dev->mem->regions, vtmp.regions,
> vtmp.nregions * sizeof dev->mem->regions[0]);
> used_memslots = vtmp.nregions;
> -
> - dev->mem_changed_start_addr = change_start;
> - dev->mem_changed_end_addr = change_end;
> }
>
> out:
> @@ -574,8 +505,6 @@ static void vhost_commit(MemoryListener *listener)
> {
> struct vhost_dev *dev = container_of(listener, struct vhost_dev,
> memory_listener);
> - hwaddr start_addr = 0;
> - ram_addr_t size = 0;
> uint64_t log_size;
> int r;
>
> @@ -585,22 +514,11 @@ static void vhost_commit(MemoryListener *listener)
> if (!dev->started) {
> return;
> }
> - if (dev->mem_changed_start_addr > dev->mem_changed_end_addr) {
> - return;
> - }
>
> if (vhost_update_mem(dev)) {
> return;
> }
>
> - if (dev->started) {
> - start_addr = dev->mem_changed_start_addr;
> - size = dev->mem_changed_end_addr - dev->mem_changed_start_addr + 1;
> -
> - r = vhost_verify_ring_mappings(dev, start_addr, size);
> - assert(r >= 0);
> - }
> -
> if (!dev->log_enabled) {
> r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
> if (r < 0) {
> @@ -1235,7 +1153,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
> hdev->features = features;
>
> hdev->memory_listener = (MemoryListener) {
> - .begin = vhost_begin,
> .commit = vhost_commit,
> .region_add = vhost_region_add,
> .region_del = vhost_region_del,
> @@ -1458,7 +1375,6 @@ int vhost_dev_start(struct vhost_dev *hdev,
> VirtIODevice *vdev)
> /* should only be called after backend is connected */
> assert(hdev->vhost_ops);
>
> - hdev->started = true;
> hdev->vdev = vdev;
>
> r = vhost_dev_set_features(hdev, hdev->log_enabled);
> @@ -1469,6 +1385,9 @@ int vhost_dev_start(struct vhost_dev *hdev,
> VirtIODevice *vdev)
> if (vhost_update_mem(hdev)) {
> goto fail_mem;
> }
> +
> + hdev->started = true;
> +
> if (vhost_dev_has_iommu(hdev)) {
> memory_listener_register(&hdev->iommu_listener, vdev->dma_as);
> }
> --
> 2.7.4
>
--
Dr. David Alan Gilbert / address@hidden / Manchester, UK
- [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Igor Mammedov, 2017/12/01
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Dr. David Alan Gilbert, 2017/12/07
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Igor Mammedov, 2017/12/08
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Dr. David Alan Gilbert, 2017/12/08
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Igor Mammedov, 2017/12/11
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Dr. David Alan Gilbert, 2017/12/11
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Igor Mammedov, 2017/12/11
- Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap, Dr. David Alan Gilbert, 2017/12/11
Re: [Qemu-devel] [RFC] vhost: check if ring mapping is still valid when building memmap,
Dr. David Alan Gilbert <=