mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
virtio: Allocate virtqueue in page-size units
In preparation for explicit bouncing of virtqueue pages for devices
advertising the VIRTIO_F_IOMMU_PLATFORM feature, introduce a couple
of wrappers around virtqueue allocation and freeing operations,
ensuring that buffers are handled in terms of page-size units.
Signed-off-by: Will Deacon <willdeacon@google.com>
[ Paul: pick from the Android tree. Rebase to the upstream ]
Signed-off-by: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
Link: b4bb5227d4
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
@@ -15,6 +15,17 @@
|
||||
#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 unsigned int virtqueue_attach_desc(struct virtqueue *vq, unsigned int i,
|
||||
struct virtio_sg *sg, u16 flags)
|
||||
@@ -271,6 +282,8 @@ 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 vring vring;
|
||||
@@ -283,7 +296,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,7 +308,7 @@ 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;
|
||||
@@ -303,7 +318,7 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
|
||||
|
||||
vq = __vring_new_virtqueue(index, vring, udev);
|
||||
if (!vq) {
|
||||
free(queue);
|
||||
virtio_free_pages(vdev, queue, DIV_ROUND_UP(vring.size, PAGE_SIZE));
|
||||
return NULL;
|
||||
}
|
||||
debug("(%s): created vring @ %p for vq @ %p with num %u\n", udev->name,
|
||||
@@ -314,7 +329,8 @@ struct virtqueue *vring_create_virtqueue(unsigned int index, unsigned int num,
|
||||
|
||||
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);
|
||||
|
||||
@@ -86,6 +86,7 @@ struct vring_used {
|
||||
|
||||
struct vring {
|
||||
unsigned int num;
|
||||
size_t size;
|
||||
struct vring_desc *desc;
|
||||
struct vring_avail *avail;
|
||||
struct vring_used *used;
|
||||
@@ -137,16 +138,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 +145,17 @@ 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)
|
||||
{
|
||||
vr->num = num;
|
||||
vr->size = vring_size(num, align);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user