The U-Boot code assumes 1:1 phys2virt mapping and either disabled caches or a lot of luck. Use the DMA API to get appropriate addresses for DMA and use coherent/streaming DMA mappings where appropriate. This is required for proper operation on MIPS. Signed-off-by: Ahmad Fatoum <ahmad@xxxxxx> --- drivers/virtio/virtio_ring.c | 98 +++++++++++++++++++++++++++++++----- include/linux/virtio_ring.h | 2 + 2 files changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index cac3362e7251..68180fe37da0 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -22,12 +22,45 @@ #define vq_info(vq, fmt, ...) \ dev_info(&vq->vdev->dev, fmt, ##__VA_ARGS__) +static inline struct device_d *vring_dma_dev(const struct virtqueue *vq) +{ + return vq->vdev->dev.parent; +} + +/* Map one sg entry. */ +static dma_addr_t vring_map_one_sg(struct virtqueue *vq, + struct virtio_sg *sg, + enum dma_data_direction direction) +{ + return dma_map_single(vring_dma_dev(vq), sg->addr, sg->length, direction); +} + +static int vring_mapping_error(struct virtqueue *vq, + dma_addr_t addr) +{ + return dma_mapping_error(vring_dma_dev(vq), addr); +} + +static void vring_unmap_one(struct virtqueue *vq, + struct vring_desc *desc) +{ + u16 flags; + + flags = virtio16_to_cpu(vq->vdev, desc->flags); + + dma_unmap_single(vring_dma_dev(vq), + virtio64_to_cpu(vq->vdev, desc->addr), + virtio32_to_cpu(vq->vdev, desc->len), + (flags & VRING_DESC_F_WRITE) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], unsigned int out_sgs, unsigned int in_sgs) { struct vring_desc *desc; unsigned int total_sg = out_sgs + in_sgs; - unsigned int i, n, avail, descs_used, uninitialized_var(prev); + unsigned int i, err_idx, n, avail, descs_used, uninitialized_var(prev); int head; WARN_ON(total_sg == 0); @@ -53,9 +86,13 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], for (n = 0; n < out_sgs; n++) { struct virtio_sg *sg = sgs[n]; + dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_TO_DEVICE); + if (vring_mapping_error(vq, addr)) + goto unmap_release; + desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT); - desc[i].addr = cpu_to_virtio64(vq->vdev, (u64)(size_t)sg->addr); + desc[i].addr = cpu_to_virtio64(vq->vdev, addr); desc[i].len = cpu_to_virtio32(vq->vdev, sg->length); prev = i; @@ -63,11 +100,13 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], } for (; n < (out_sgs + in_sgs); n++) { struct virtio_sg *sg = sgs[n]; + dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE); + if (vring_mapping_error(vq, addr)) + goto unmap_release; desc[i].flags = cpu_to_virtio16(vq->vdev, VRING_DESC_F_NEXT | VRING_DESC_F_WRITE); - desc[i].addr = cpu_to_virtio64(vq->vdev, - (u64)(uintptr_t)sg->addr); + desc[i].addr = cpu_to_virtio64(vq->vdev, addr); desc[i].len = cpu_to_virtio32(vq->vdev, sg->length); prev = i; @@ -106,6 +145,19 @@ int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], virtqueue_kick(vq); return 0; + +unmap_release: + err_idx = i; + + for (n = 0; n < total_sg; n++) { + if (i == err_idx) + break; + vring_unmap_one(vq, &desc[i]); + i = virtio16_to_cpu(vq->vdev, desc[i].next); + } + + return -ENOMEM; + } static bool virtqueue_kick_prepare(struct virtqueue *vq) @@ -149,10 +201,12 @@ static void detach_buf(struct virtqueue *vq, unsigned int head) i = head; while (vq->vring.desc[i].flags & nextflag) { + vring_unmap_one(vq, &vq->vring.desc[i]); i = virtio16_to_cpu(vq->vdev, vq->vring.desc[i].next); vq->num_free++; } + vring_unmap_one(vq, &vq->vring.desc[i]); vq->vring.desc[i].next = cpu_to_virtio16(vq->vdev, vq->free_head); vq->free_head = head; @@ -225,6 +279,8 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->avail_flags_shadow = 0; vq->avail_idx_shadow = 0; vq->num_added = 0; + vq->queue_dma_addr = 0; + vq->queue_size_in_bytes = 0; list_add_tail(&vq->list, &vdev->vqs); vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX); @@ -243,12 +299,24 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, return vq; } +static void *vring_alloc_queue(size_t size, dma_addr_t *dma_handle) +{ + return dma_alloc_coherent(size, dma_handle); +} + +static void vring_free_queue(size_t size, void *queue, dma_addr_t dma_handle) +{ + dma_free_coherent(queue, dma_handle, size); +} + struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, unsigned int vring_align, struct virtio_device *vdev) { struct virtqueue *vq; void *queue = NULL; + dma_addr_t dma_addr; + size_t queue_size_in_bytes; struct vring vring; /* We assume num is a power of 2 */ @@ -259,7 +327,7 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, /* TODO: allocate each queue chunk individually */ for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) { - queue = memalign(PAGE_SIZE, vring_size(num, vring_align)); + queue = vring_alloc_queue(vring_size(num, vring_align), &dma_addr); if (queue) break; } @@ -269,27 +337,31 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, if (!queue) { /* Try to get a single page. You are my only hope! */ - queue = memalign(PAGE_SIZE, vring_size(num, vring_align)); + queue = vring_alloc_queue(vring_size(num, vring_align), &dma_addr); } if (!queue) return NULL; - memset(queue, 0, vring_size(num, vring_align)); + queue_size_in_bytes = vring_size(num, vring_align); vring_init(&vring, num, queue, vring_align); vq = __vring_new_virtqueue(index, vring, vdev); if (!vq) { - free(queue); + vring_free_queue(queue_size_in_bytes, queue, dma_addr); return NULL; } - vq_debug(vq, "created vring @ %p for vq with num %u\n", queue, num); + vq_debug(vq, "created vring @ (virt=%p, phys=%pad) for vq with num %u\n", + queue, &dma_addr, num); + + vq->queue_dma_addr = dma_addr; + vq->queue_size_in_bytes = queue_size_in_bytes; return vq; } void vring_del_virtqueue(struct virtqueue *vq) { - free(vq->vring.desc); + vring_free_queue(vq->queue_size_in_bytes, vq->vring.desc, vq->queue_dma_addr); list_del(&vq->list); free(vq); } @@ -301,18 +373,18 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *vq) dma_addr_t virtqueue_get_desc_addr(struct virtqueue *vq) { - return (dma_addr_t)vq->vring.desc; + return vq->queue_dma_addr; } dma_addr_t virtqueue_get_avail_addr(struct virtqueue *vq) { - return (dma_addr_t)vq->vring.desc + + return vq->queue_dma_addr + ((char *)vq->vring.avail - (char *)vq->vring.desc); } dma_addr_t virtqueue_get_used_addr(struct virtqueue *vq) { - return (dma_addr_t)vq->vring.desc + + return vq->queue_dma_addr + ((char *)vq->vring.used - (char *)vq->vring.desc); } diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index 3c11592b09e4..c349af90ce50 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -108,6 +108,8 @@ struct virtqueue { u16 last_used_idx; u16 avail_flags_shadow; u16 avail_idx_shadow; + dma_addr_t queue_dma_addr; + size_t queue_size_in_bytes; }; /* -- 2.30.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox