mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 23:47:24 +00:00
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.
This commit is contained in:
commit
bad2618b8c
4 changed files with 127 additions and 39 deletions
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
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) {
|
||||
free(queue);
|
||||
return NULL;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,16 +139,6 @@ 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 void vring_init(struct vring *vr, unsigned int num, void *p,
|
||||
unsigned long align)
|
||||
{
|
||||
vr->num = num;
|
||||
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 +
|
||||
|
@ -154,6 +146,19 @@ static inline unsigned int vring_size(unsigned int num, unsigned long align)
|
|||
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,
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
Loading…
Reference in a new issue