diff options
author | Tom Rini <trini@konsulko.com> | 2023-04-25 14:22:40 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-04-25 14:22:40 -0400 |
commit | bad2618b8c5c7e18dcfb9ff6a7d108e389d20cf7 (patch) | |
tree | dd96eae340d934015017a673ef06127c49c7a645 | |
parent | 6a11fdf0536e02ac9cd4a3da0535a271c694715f (diff) | |
parent | b0a2fe148a70dc805f1e11702a08a7065418a8dd (diff) | |
download | u-boot-bad2618b8c5c7e18dcfb9ff6a7d108e389d20cf7.zip u-boot-bad2618b8c5c7e18dcfb9ff6a7d108e389d20cf7.tar.gz u-boot-bad2618b8c5c7e18dcfb9ff6a7d108e389d20cf7.tar.bz2 |
Merge branch '2023-04-25-use-bounce-buffers-for-VIRTIO_F_IOMMU_PLATFORM'
To quote the author:
These patches will use bounce buffers when VIRTIO_F_IOMMU_PLATFORM
feature is in a virtio device.
This feature can be tested with qemu with -device virtio-iommu-pci. So
that when a -device virtio-blk-pci with iommu_platform=true, it will
uses the bounce buffer instead.
-rw-r--r-- | drivers/virtio/virtio-uclass.c | 2 | ||||
-rw-r--r-- | drivers/virtio/virtio_pci_modern.c | 38 | ||||
-rw-r--r-- | drivers/virtio/virtio_ring.c | 101 | ||||
-rw-r--r-- | include/virtio_ring.h | 21 |
4 files changed, 125 insertions, 37 deletions
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index de9bc90..b3fb3db 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -336,7 +336,7 @@ static int virtio_uclass_child_pre_probe(struct udevice *vdev) /* Transport features always preserved to pass to finalize_features */ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) if ((device_features & (1ULL << i)) && - (i == VIRTIO_F_VERSION_1)) + (i == VIRTIO_F_VERSION_1 || i == VIRTIO_F_IOMMU_PLATFORM)) __virtio_set_bit(vdev->parent, i); debug("(%s) final negotiated features supported %016llx\n", diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index cfde400..3cdc2d2 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -218,25 +218,6 @@ static int virtio_pci_set_status(struct udevice *udev, u8 status) return 0; } -static int virtio_pci_reset(struct udevice *udev) -{ - struct virtio_pci_priv *priv = dev_get_priv(udev); - - /* 0 status means a reset */ - iowrite8(0, &priv->common->device_status); - - /* - * After writing 0 to device_status, the driver MUST wait for a read - * of device_status to return 0 before reinitializing the device. - * This will flush out the status write, and flush in device writes, - * including MSI-X interrupts, if any. - */ - while (ioread8(&priv->common->device_status)) - udelay(1000); - - return 0; -} - static int virtio_pci_get_features(struct udevice *udev, u64 *features) { struct virtio_pci_priv *priv = dev_get_priv(udev); @@ -363,6 +344,25 @@ static int virtio_pci_find_vqs(struct udevice *udev, unsigned int nvqs, return 0; } +static int virtio_pci_reset(struct udevice *udev) +{ + struct virtio_pci_priv *priv = dev_get_priv(udev); + + /* 0 status means a reset */ + iowrite8(0, &priv->common->device_status); + + /* + * After writing 0 to device_status, the driver MUST wait for a read + * of device_status to return 0 before reinitializing the device. + * This will flush out the status write, and flush in device writes, + * including MSI-X interrupts, if any. + */ + while (ioread8(&priv->common->device_status)) + udelay(1000); + + return virtio_pci_del_vqs(udev); +} + static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) { struct virtio_pci_priv *priv = dev_get_priv(udev); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index f71bab7..c9adcce 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -6,6 +6,7 @@ * virtio ring implementation */ +#include <bouncebuf.h> #include <common.h> #include <dm.h> #include <log.h> @@ -15,15 +16,63 @@ #include <virtio_ring.h> #include <linux/bug.h> #include <linux/compat.h> +#include <linux/kernel.h> + +static void *virtio_alloc_pages(struct udevice *vdev, u32 npages) +{ + return memalign(PAGE_SIZE, npages * PAGE_SIZE); +} + +static void virtio_free_pages(struct udevice *vdev, void *ptr, u32 npages) +{ + free(ptr); +} + +static int __bb_force_page_align(struct bounce_buffer *state) +{ + const ulong align_mask = PAGE_SIZE - 1; + + if ((ulong)state->user_buffer & align_mask) + return 0; + + if (state->len != state->len_aligned) + return 0; + + return 1; +} static unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i, struct virtio_sg *sg, u16 flags) { struct vring_desc_shadow *desc_shadow = &vq->vring_desc_shadow[i]; struct vring_desc *desc = &vq->vring.desc[i]; + void *addr; + + if (IS_ENABLED(CONFIG_BOUNCE_BUFFER) && vq->vring.bouncebufs) { + struct bounce_buffer *bb = &vq->vring.bouncebufs[i]; + unsigned int bbflags; + int ret; + + if (flags & VRING_DESC_F_WRITE) + bbflags = GEN_BB_WRITE; + else + bbflags = GEN_BB_READ; + + ret = bounce_buffer_start_extalign(bb, sg->addr, sg->length, + bbflags, PAGE_SIZE, + __bb_force_page_align); + if (ret) { + debug("%s: failed to allocate bounce buffer (length 0x%zx)\n", + vq->vdev->name, sg->length); + } + + addr = bb->bounce_buffer; + } else { + addr = sg->addr; + } /* Update the shadow descriptor. */ - desc_shadow->addr = (u64)(uintptr_t)sg->addr; + desc_shadow->addr = (u64)(uintptr_t)addr; desc_shadow->len = sg->length; desc_shadow->flags = flags; @@ -36,6 +85,19 @@ static unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i, return desc_shadow->next; } +static void virtqueue_detach_desc(struct virtqueue *vq, unsigned int idx) +{ + struct vring_desc *desc = &vq->vring.desc[idx]; + struct bounce_buffer *bb; + + if (!IS_ENABLED(CONFIG_BOUNCE_BUFFER) || !vq->vring.bouncebufs) + return; + + bb = &vq->vring.bouncebufs[idx]; + bounce_buffer_stop(bb); + desc->addr = cpu_to_virtio64(vq->vdev, (u64)(uintptr_t)bb->user_buffer); +} + int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[], unsigned int out_sgs, unsigned int in_sgs) { @@ -154,10 +216,12 @@ static void detach_buf(struct virtqueue *vq, unsigned int head) i = head; while (vq->vring_desc_shadow[i].flags & VRING_DESC_F_NEXT) { + virtqueue_detach_desc(vq, i); i = vq->vring_desc_shadow[i].next; vq->num_free++; } + virtqueue_detach_desc(vq, i); vq->vring_desc_shadow[i].next = vq->free_head; vq->free_head = head; @@ -271,8 +335,11 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num, unsigned int vring_align, struct udevice *udev) { + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + struct udevice *vdev = uc_priv->vdev; struct virtqueue *vq; void *queue = NULL; + struct bounce_buffer *bbs = NULL; struct vring vring; /* We assume num is a power of 2 */ @@ -283,7 +350,9 @@ 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)); + size_t sz = vring_size(num, vring_align); + + queue = virtio_alloc_pages(vdev, DIV_ROUND_UP(sz, PAGE_SIZE)); if (queue) break; } @@ -293,30 +362,44 @@ 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 = virtio_alloc_pages(vdev, 1); } if (!queue) return NULL; memset(queue, 0, vring_size(num, vring_align)); - vring_init(&vring, num, queue, vring_align); - vq = __vring_new_virtqueue(index, vring, udev); - if (!vq) { - free(queue); - return NULL; + if (virtio_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + bbs = calloc(num, sizeof(*bbs)); + if (!bbs) + goto err_free_queue; } + + vring_init(&vring, num, queue, vring_align, bbs); + + vq = __vring_new_virtqueue(index, vring, udev); + if (!vq) + goto err_free_bbs; + debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name, queue, vq, num); return vq; + +err_free_bbs: + free(bbs); +err_free_queue: + virtio_free_pages(vdev, queue, DIV_ROUND_UP(vring.size, PAGE_SIZE)); + return NULL; } void vring_del_virtqueue(struct virtqueue *vq) { - free(vq->vring.desc); + virtio_free_pages(vq->vdev, vq->vring.desc, + DIV_ROUND_UP(vq->vring.size, PAGE_SIZE)); free(vq->vring_desc_shadow); list_del(&vq->list); + free(vq->vring.bouncebufs); free(vq); } diff --git a/include/virtio_ring.h b/include/virtio_ring.h index c77c212..e8e9104 100644 --- a/include/virtio_ring.h +++ b/include/virtio_ring.h @@ -86,6 +86,8 @@ struct vring_used { struct vring { unsigned int num; + size_t size; + struct bounce_buffer *bouncebufs; struct vring_desc *desc; struct vring_avail *avail; struct vring_used *used; @@ -137,23 +139,26 @@ struct virtqueue { #define vring_used_event(vr) ((vr)->avail->ring[(vr)->num]) #define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->ring[(vr)->num]) +static inline unsigned int vring_size(unsigned int num, unsigned long align) +{ + return ((sizeof(struct vring_desc) * num + + sizeof(__virtio16) * (3 + num) + align - 1) & ~(align - 1)) + + sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num; +} + static inline void vring_init(struct vring *vr, unsigned int num, void *p, - unsigned long align) + unsigned long align, + struct bounce_buffer *bouncebufs) { vr->num = num; + vr->size = vring_size(num, align); + vr->bouncebufs = bouncebufs; vr->desc = p; vr->avail = p + num * sizeof(struct vring_desc); vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16) + align - 1) & ~(align - 1)); } -static inline unsigned int vring_size(unsigned int num, unsigned long align) -{ - return ((sizeof(struct vring_desc) * num + - sizeof(__virtio16) * (3 + num) + align - 1) & ~(align - 1)) + - sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num; -} - /* * The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX. * Assuming a given event_idx value from the other side, if we have just |