Skip to content

Commit

Permalink
Merge branch '2023-04-25-use-bounce-buffers-for-VIRTIO_F_IOMMU_PLATFORM'
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
trini committed Apr 25, 2023
2 parents 6a11fdf + b0a2fe1 commit bad2618
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 37 deletions.
2 changes: 1 addition & 1 deletion drivers/virtio/virtio-uclass.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
38 changes: 19 additions & 19 deletions drivers/virtio/virtio_pci_modern.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
101 changes: 92 additions & 9 deletions drivers/virtio/virtio_ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* virtio ring implementation
*/

#include <bouncebuf.h>
#include <common.h>
#include <dm.h>
#include <log.h>
Expand All @@ -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;

Expand All @@ -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)
{
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand All @@ -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;
}
Expand All @@ -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);
}

Expand Down
21 changes: 13 additions & 8 deletions include/virtio_ring.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit bad2618

Please sign in to comment.