]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
drivers/virtio: Use DMA mapping with ukvmem
authorMarc Rittinghaus <marc.rittinghaus@unikraft.io>
Thu, 2 Feb 2023 19:03:37 +0000 (20:03 +0100)
committerUnikraft <monkey@unikraft.io>
Fri, 3 Feb 2023 17:23:15 +0000 (17:23 +0000)
The current implementation uses the heap to allocate memory for the
virtio queues. This is problematic when used in conjunction with
on-demand paging for the heap. It can cause pages of the virtio
queue to be non-contiguous in physical memory. Some pages may
not even mapped to physical memory, yet. Since the hypervisor
does not care about the virtual mapping but only gets the physical
start address of the virtio queue (i.e., the physical address of the
first page), it will write on any physical page in the range of the
virtio queue (i.e., start physical address + size of queue). However,
these physical pages may belong to arbitrary other mappings. And
even if they belong to the same virtqueue, they might not be mapped
in the same order as in the virtual space. This is especially hard to
debug, because the guest is not performing the corrupting memory
writes.

To fix this, we request contiguous memory from the frame allocator
and map this in the same order in the virtual address space using
uk_vma_map_dma().

Signed-off-by: Marc Rittinghaus <marc.rittinghaus@unikraft.io>
Reviewed-by: Marco Schlumpp <marco@unikraft.io>
Approved-by: Michalis Pappas <michalis.pappas@opensynergy.com>
Tested-by: Unikraft CI <monkey@unikraft.io>
GitHub-Closes: #603

plat/drivers/virtio/virtio_ring.c

index 855ade531c0875a27e22540374a4f42d0eb818ee..2eaae8dfdeb86936eeb2658d359a9c94d745b6f3 100644 (file)
 #include <uk/plat/io.h>
 #include <virtio/virtio_ring.h>
 #include <virtio/virtqueue.h>
+#ifdef CONFIG_LIBUKVMEM
+#include <uk/arch/paging.h>
+#include <uk/plat/paging.h>
+#include <uk/vmem.h>
+#include <uk/falloc.h>
+#endif /* CONFIG_LIBUKVMEM */
 
 #define VIRTQUEUE_MAX_SIZE  32768
 #define to_virtqueue_vring(vq)                 \
@@ -411,12 +417,31 @@ struct virtqueue *virtqueue_create(__u16 queue_id, __u16 nr_descs, __u16 align,
        vrq->vring_mem = NULL;
 
        ring_size = vring_size(nr_descs, align);
+#ifdef CONFIG_LIBUKVMEM
+       struct uk_pagetable *pt = ukplat_pt_get_active();
+       __paddr_t paddr = __PADDR_ANY;
+       __vaddr_t vaddr = __VADDR_ANY;
+
+       ring_size = PAGE_ALIGN_UP(ring_size);
+
+       rc = pt->fa->falloc(pt->fa, &paddr, ring_size >> PAGE_SHIFT, 0);
+       if (unlikely(rc))
+               goto err_freevq;
+
+       rc = uk_vma_map_dma(uk_vas_get_active(), &vaddr, ring_size,
+                           PAGE_ATTR_PROT_RW, UK_VMA_MAP_POPULATE,
+                           "virtqueue", paddr);
+       if (unlikely(rc))
+               goto err_freevq;
+
+       vrq->vring_mem = (void *)vaddr;
+#else /* CONFIG_LIBUKVMEM */
        if (uk_posix_memalign(a, &vrq->vring_mem,
                              __PAGE_SIZE, ring_size) != 0) {
-               uk_pr_err("Allocation of vring failed\n");
                rc = -ENOMEM;
                goto err_freevq;
        }
+#endif /* !CONFIG_LIBUKVMEM */
        memset(vrq->vring_mem, 0, ring_size);
        virtqueue_vring_init(vrq, nr_descs, align);
 
@@ -428,6 +453,7 @@ struct virtqueue *virtqueue_create(__u16 queue_id, __u16 nr_descs, __u16 align,
        return vq;
 
 err_freevq:
+       uk_pr_err("Allocation of vring failed\n");
        uk_free(a, vrq);
 err_exit:
        return ERR2PTR(rc);