virtio: introduce virtqueue_alloc_element
Allocate the arrays for in_addr/out_addr/in_sg/out_sg outside the VirtQueueElement. For now, virtqueue_pop and vring_pop keep allocating a very large VirtQueueElement. Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
ab281c1781
commit
3724650db0
@ -403,8 +403,7 @@ void *vring_pop(VirtIODevice *vdev, Vring *vring, size_t sz)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(sz >= sizeof(VirtQueueElement));
|
elem = virtqueue_alloc_element(sz, VIRTQUEUE_MAX_SIZE, VIRTQUEUE_MAX_SIZE);
|
||||||
elem = g_malloc(sz);
|
|
||||||
|
|
||||||
/* Initialize elem so it can be safely unmapped */
|
/* Initialize elem so it can be safely unmapped */
|
||||||
elem->in_num = elem->out_num = 0;
|
elem->in_num = elem->out_num = 0;
|
||||||
|
@ -494,11 +494,30 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
|
|||||||
void virtqueue_map(VirtQueueElement *elem)
|
void virtqueue_map(VirtQueueElement *elem)
|
||||||
{
|
{
|
||||||
virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num,
|
virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num,
|
||||||
MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)),
|
VIRTQUEUE_MAX_SIZE, 1);
|
||||||
1);
|
|
||||||
virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num,
|
virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num,
|
||||||
MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)),
|
VIRTQUEUE_MAX_SIZE, 0);
|
||||||
0);
|
}
|
||||||
|
|
||||||
|
void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num)
|
||||||
|
{
|
||||||
|
VirtQueueElement *elem;
|
||||||
|
size_t in_addr_ofs = QEMU_ALIGN_UP(sz, __alignof__(elem->in_addr[0]));
|
||||||
|
size_t out_addr_ofs = in_addr_ofs + in_num * sizeof(elem->in_addr[0]);
|
||||||
|
size_t out_addr_end = out_addr_ofs + out_num * sizeof(elem->out_addr[0]);
|
||||||
|
size_t in_sg_ofs = QEMU_ALIGN_UP(out_addr_end, __alignof__(elem->in_sg[0]));
|
||||||
|
size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]);
|
||||||
|
size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]);
|
||||||
|
|
||||||
|
assert(sz >= sizeof(VirtQueueElement));
|
||||||
|
elem = g_malloc(out_sg_end);
|
||||||
|
elem->out_num = out_num;
|
||||||
|
elem->in_num = in_num;
|
||||||
|
elem->in_addr = (void *)elem + in_addr_ofs;
|
||||||
|
elem->out_addr = (void *)elem + out_addr_ofs;
|
||||||
|
elem->in_sg = (void *)elem + in_sg_ofs;
|
||||||
|
elem->out_sg = (void *)elem + out_sg_ofs;
|
||||||
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
||||||
@ -513,8 +532,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* When we start there are none of either input nor output. */
|
/* When we start there are none of either input nor output. */
|
||||||
assert(sz >= sizeof(VirtQueueElement));
|
elem = virtqueue_alloc_element(sz, VIRTQUEUE_MAX_SIZE, VIRTQUEUE_MAX_SIZE);
|
||||||
elem = g_malloc(sz);
|
|
||||||
elem->out_num = elem->in_num = 0;
|
elem->out_num = elem->in_num = 0;
|
||||||
|
|
||||||
max = vq->vring.num;
|
max = vq->vring.num;
|
||||||
@ -541,14 +559,14 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
struct iovec *sg;
|
struct iovec *sg;
|
||||||
|
|
||||||
if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
|
if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
|
||||||
if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) {
|
if (elem->in_num >= VIRTQUEUE_MAX_SIZE) {
|
||||||
error_report("Too many write descriptors in indirect table");
|
error_report("Too many write descriptors in indirect table");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i);
|
elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i);
|
||||||
sg = &elem->in_sg[elem->in_num++];
|
sg = &elem->in_sg[elem->in_num++];
|
||||||
} else {
|
} else {
|
||||||
if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) {
|
if (elem->out_num >= VIRTQUEUE_MAX_SIZE) {
|
||||||
error_report("Too many read descriptors in indirect table");
|
error_report("Too many read descriptors in indirect table");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -576,17 +594,87 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reading and writing a structure directly to QEMUFile is *awful*, but
|
||||||
|
* it is what QEMU has always done by mistake. We can change it sooner
|
||||||
|
* or later by bumping the version number of the affected vm states.
|
||||||
|
* In the meanwhile, since the in-memory layout of VirtQueueElement
|
||||||
|
* has changed, we need to marshal to and from the layout that was
|
||||||
|
* used before the change.
|
||||||
|
*/
|
||||||
|
typedef struct VirtQueueElementOld {
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int out_num;
|
||||||
|
unsigned int in_num;
|
||||||
|
hwaddr in_addr[VIRTQUEUE_MAX_SIZE];
|
||||||
|
hwaddr out_addr[VIRTQUEUE_MAX_SIZE];
|
||||||
|
struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
|
||||||
|
struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
|
||||||
|
} VirtQueueElementOld;
|
||||||
|
|
||||||
void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz)
|
void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz)
|
||||||
{
|
{
|
||||||
VirtQueueElement *elem = g_malloc(sz);
|
VirtQueueElement *elem;
|
||||||
qemu_get_buffer(f, (uint8_t *)elem, sizeof(VirtQueueElement));
|
VirtQueueElementOld data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld));
|
||||||
|
|
||||||
|
elem = virtqueue_alloc_element(sz, data.out_num, data.in_num);
|
||||||
|
elem->index = data.index;
|
||||||
|
|
||||||
|
for (i = 0; i < elem->in_num; i++) {
|
||||||
|
elem->in_addr[i] = data.in_addr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->out_num; i++) {
|
||||||
|
elem->out_addr[i] = data.out_addr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->in_num; i++) {
|
||||||
|
/* Base is overwritten by virtqueue_map. */
|
||||||
|
elem->in_sg[i].iov_base = 0;
|
||||||
|
elem->in_sg[i].iov_len = data.in_sg[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->out_num; i++) {
|
||||||
|
/* Base is overwritten by virtqueue_map. */
|
||||||
|
elem->out_sg[i].iov_base = 0;
|
||||||
|
elem->out_sg[i].iov_len = data.out_sg[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
virtqueue_map(elem);
|
virtqueue_map(elem);
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem)
|
void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem)
|
||||||
{
|
{
|
||||||
qemu_put_buffer(f, (uint8_t *)elem, sizeof(VirtQueueElement));
|
VirtQueueElementOld data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
data.index = elem->index;
|
||||||
|
data.in_num = elem->in_num;
|
||||||
|
data.out_num = elem->out_num;
|
||||||
|
|
||||||
|
for (i = 0; i < elem->in_num; i++) {
|
||||||
|
data.in_addr[i] = elem->in_addr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->out_num; i++) {
|
||||||
|
data.out_addr[i] = elem->out_addr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->in_num; i++) {
|
||||||
|
/* Base is overwritten by virtqueue_map when loading. Do not
|
||||||
|
* save it, as it would leak the QEMU address space layout. */
|
||||||
|
data.in_sg[i].iov_len = elem->in_sg[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < elem->out_num; i++) {
|
||||||
|
/* Do not save iov_base as above. */
|
||||||
|
data.out_sg[i].iov_len = elem->out_sg[i].iov_len;
|
||||||
|
}
|
||||||
|
qemu_put_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtio device */
|
/* virtio device */
|
||||||
|
@ -46,10 +46,10 @@ typedef struct VirtQueueElement
|
|||||||
unsigned int index;
|
unsigned int index;
|
||||||
unsigned int out_num;
|
unsigned int out_num;
|
||||||
unsigned int in_num;
|
unsigned int in_num;
|
||||||
hwaddr in_addr[VIRTQUEUE_MAX_SIZE];
|
hwaddr *in_addr;
|
||||||
hwaddr out_addr[VIRTQUEUE_MAX_SIZE];
|
hwaddr *out_addr;
|
||||||
struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
|
struct iovec *in_sg;
|
||||||
struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
|
struct iovec *out_sg;
|
||||||
} VirtQueueElement;
|
} VirtQueueElement;
|
||||||
|
|
||||||
#define VIRTIO_QUEUE_MAX 1024
|
#define VIRTIO_QUEUE_MAX 1024
|
||||||
@ -143,6 +143,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
|
|||||||
|
|
||||||
void virtio_del_queue(VirtIODevice *vdev, int n);
|
void virtio_del_queue(VirtIODevice *vdev, int n);
|
||||||
|
|
||||||
|
void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num);
|
||||||
void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
|
void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
|
||||||
unsigned int len);
|
unsigned int len);
|
||||||
void virtqueue_flush(VirtQueue *vq, unsigned int count);
|
void virtqueue_flush(VirtQueue *vq, unsigned int count);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user