mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-11-29 14:41:26 +01: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
@ -336,7 +336,7 @@ static int virtio_uclass_child_pre_probe(struct udevice *vdev)
|
|||||||
/* Transport features always preserved to pass to finalize_features */
|
/* Transport features always preserved to pass to finalize_features */
|
||||||
for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
|
for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
|
||||||
if ((device_features & (1ULL << 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);
|
__virtio_set_bit(vdev->parent, i);
|
||||||
|
|
||||||
debug("(%s) final negotiated features supported %016llx\n",
|
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;
|
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)
|
static int virtio_pci_get_features(struct udevice *udev, u64 *features)
|
||||||
{
|
{
|
||||||
struct virtio_pci_priv *priv = dev_get_priv(udev);
|
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;
|
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)
|
static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
|
||||||
{
|
{
|
||||||
struct virtio_pci_priv *priv = dev_get_priv(udev);
|
struct virtio_pci_priv *priv = dev_get_priv(udev);
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
* virtio ring implementation
|
* virtio ring implementation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <bouncebuf.h>
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
@ -15,15 +16,63 @@
|
|||||||
#include <virtio_ring.h>
|
#include <virtio_ring.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/compat.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,
|
static unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i,
|
||||||
struct virtio_sg *sg, u16 flags)
|
struct virtio_sg *sg, u16 flags)
|
||||||
{
|
{
|
||||||
struct vring_desc_shadow *desc_shadow = &vq->vring_desc_shadow[i];
|
struct vring_desc_shadow *desc_shadow = &vq->vring_desc_shadow[i];
|
||||||
struct vring_desc *desc = &vq->vring.desc[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. */
|
/* 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->len = sg->length;
|
||||||
desc_shadow->flags = flags;
|
desc_shadow->flags = flags;
|
||||||
|
|
||||||
@ -36,6 +85,19 @@ static unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i,
|
|||||||
return desc_shadow->next;
|
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[],
|
int virtqueue_add(struct virtqueue *vq, struct virtio_sg *sgs[],
|
||||||
unsigned int out_sgs, unsigned int in_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;
|
i = head;
|
||||||
|
|
||||||
while (vq->vring_desc_shadow[i].flags & VRING_DESC_F_NEXT) {
|
while (vq->vring_desc_shadow[i].flags & VRING_DESC_F_NEXT) {
|
||||||
|
virtqueue_detach_desc(vq, i);
|
||||||
i = vq->vring_desc_shadow[i].next;
|
i = vq->vring_desc_shadow[i].next;
|
||||||
vq->num_free++;
|
vq->num_free++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtqueue_detach_desc(vq, i);
|
||||||
vq->vring_desc_shadow[i].next = vq->free_head;
|
vq->vring_desc_shadow[i].next = vq->free_head;
|
||||||
vq->free_head = head;
|
vq->free_head = head;
|
||||||
|
|
||||||
@ -271,8 +335,11 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
|
|||||||
unsigned int vring_align,
|
unsigned int vring_align,
|
||||||
struct udevice *udev)
|
struct udevice *udev)
|
||||||
{
|
{
|
||||||
|
struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
|
||||||
|
struct udevice *vdev = uc_priv->vdev;
|
||||||
struct virtqueue *vq;
|
struct virtqueue *vq;
|
||||||
void *queue = NULL;
|
void *queue = NULL;
|
||||||
|
struct bounce_buffer *bbs = NULL;
|
||||||
struct vring vring;
|
struct vring vring;
|
||||||
|
|
||||||
/* We assume num is a power of 2 */
|
/* 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 */
|
/* TODO: allocate each queue chunk individually */
|
||||||
for (; num && vring_size(num, vring_align) > PAGE_SIZE; num /= 2) {
|
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)
|
if (queue)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -293,30 +362,44 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
|
|||||||
|
|
||||||
if (!queue) {
|
if (!queue) {
|
||||||
/* Try to get a single page. You are my only hope! */
|
/* 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)
|
if (!queue)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memset(queue, 0, vring_size(num, vring_align));
|
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);
|
vq = __vring_new_virtqueue(index, vring, udev);
|
||||||
if (!vq) {
|
if (!vq)
|
||||||
free(queue);
|
goto err_free_bbs;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
|
debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
|
||||||
queue, vq, num);
|
queue, vq, num);
|
||||||
|
|
||||||
return vq;
|
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)
|
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);
|
free(vq->vring_desc_shadow);
|
||||||
list_del(&vq->list);
|
list_del(&vq->list);
|
||||||
|
free(vq->vring.bouncebufs);
|
||||||
free(vq);
|
free(vq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,8 @@ struct vring_used {
|
|||||||
|
|
||||||
struct vring {
|
struct vring {
|
||||||
unsigned int num;
|
unsigned int num;
|
||||||
|
size_t size;
|
||||||
|
struct bounce_buffer *bouncebufs;
|
||||||
struct vring_desc *desc;
|
struct vring_desc *desc;
|
||||||
struct vring_avail *avail;
|
struct vring_avail *avail;
|
||||||
struct vring_used *used;
|
struct vring_used *used;
|
||||||
@ -137,16 +139,6 @@ struct virtqueue {
|
|||||||
#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
|
#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
|
||||||
#define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->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)
|
static inline unsigned int vring_size(unsigned int num, unsigned long align)
|
||||||
{
|
{
|
||||||
return ((sizeof(struct vring_desc) * num +
|
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;
|
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.
|
* 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
|
* Assuming a given event_idx value from the other side, if we have just
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user