1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
/* virtio-pci.c - virtio ring management
*
* (c) Copyright 2008 Bull S.A.S.
*
* Author: Laurent Vivier <Laurent.Vivier@bull.net>
*
* some parts from Linux Virtio Ring
*
* Copyright Rusty Russell IBM Corporation 2007
*
* Adopted for Seabios: Gleb Natapov <gleb@redhat.com>
*
* This work is licensed under the terms of the GNU LGPLv3
* See the COPYING file in the top-level directory.
*
*
*/
#include "output.h" // panic
#include "virtio-ring.h"
#include "virtio-pci.h"
#define BUG() do { \
panic("BUG: failure at %d/%s()!\n", __LINE__, __func__); \
} while (0)
#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
/*
* vring_more_used
*
* is there some used buffers ?
*
*/
int vring_more_used(struct vring_virtqueue *vq)
{
struct vring_used *used = vq->vring.used;
int more = vq->last_used_idx != used->idx;
/* Make sure ring reads are done after idx read above. */
smp_rmb();
return more;
}
/*
* vring_free
*
* put at the begin of the free list the current desc[head]
*/
void vring_detach(struct vring_virtqueue *vq, unsigned int head)
{
struct vring *vr = &vq->vring;
struct vring_desc *desc = GET_LOWFLAT(vr->desc);
unsigned int i;
/* find end of given descriptor */
i = head;
while (desc[i].flags & VRING_DESC_F_NEXT)
i = desc[i].next;
/* link it with free list and point to it */
desc[i].next = vq->free_head;
vq->free_head = head;
}
/*
* vring_get_buf
*
* get a buffer from the used list
*
*/
int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len)
{
struct vring *vr = &vq->vring;
struct vring_used_elem *elem;
struct vring_used *used = vq->vring.used;
u32 id;
int ret;
// BUG_ON(!vring_more_used(vq));
elem = &used->ring[vq->last_used_idx % vr->num];
id = elem->id;
if (len != NULL)
*len = elem->len;
ret = vq->vdata[id];
vring_detach(vq, id);
vq->last_used_idx = vq->last_used_idx + 1;
return ret;
}
void vring_add_buf(struct vring_virtqueue *vq,
struct vring_list list[],
unsigned int out, unsigned int in,
int index, int num_added)
{
struct vring *vr = &vq->vring;
int i, av, head, prev;
struct vring_desc *desc = vr->desc;
struct vring_avail *avail = vr->avail;
BUG_ON(out + in == 0);
prev = 0;
head = vq->free_head;
for (i = head; out; i = desc[i].next, out--) {
desc[i].flags = VRING_DESC_F_NEXT;
desc[i].addr = (u64)virt_to_phys(list->addr);
desc[i].len = list->length;
prev = i;
list++;
}
for ( ; in; i = desc[i].next, in--) {
desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
desc[i].addr = (u64)virt_to_phys(list->addr);
desc[i].len = list->length;
prev = i;
list++;
}
desc[prev].flags = desc[prev].flags & ~VRING_DESC_F_NEXT;
vq->free_head = i;
vq->vdata[head] = index;
av = (avail->idx + num_added) % vr->num;
avail->ring[av] = head;
}
void vring_kick(struct vp_device *vp, struct vring_virtqueue *vq, int num_added)
{
struct vring *vr = &vq->vring;
struct vring_avail *avail = vr->avail;
/* Make sure idx update is done after ring write. */
smp_wmb();
avail->idx = avail->idx + num_added;
vp_notify(vp, vq);
}
|