mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-10-04 15:58:29 +00:00
kvm: Fix virtio block device support some more
This patch fixes the following issues in virtio block device support: - Handle all requests in the virtqueue before signaling an interrupt. - Initialize ->used vring elements properly. - Handle non-read requests by injecting an I/O error to the guest. Signed-off-by: Pekka Enberg <penberg@kernel.org>
This commit is contained in:
committed by
Will Deacon
parent
93d18b7223
commit
4155ba8cda
+65
-43
@@ -9,6 +9,9 @@
|
||||
#include "kvm/kvm.h"
|
||||
#include "kvm/pci.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define VIRTIO_BLK_IRQ 14
|
||||
|
||||
#define NUM_VIRT_QUEUES 1
|
||||
@@ -110,6 +113,65 @@ static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool blk_virtio_read(struct kvm *self, struct virt_queue *queue)
|
||||
{
|
||||
struct vring_used_elem *used_elem;
|
||||
struct virtio_blk_outhdr *req;
|
||||
struct vring_desc *desc;
|
||||
uint16_t desc_ndx;
|
||||
uint32_t dst_len;
|
||||
uint8_t *status;
|
||||
void *dst;
|
||||
int err;
|
||||
|
||||
desc_ndx = queue->vring.avail->ring[queue->last_avail_idx++ % queue->vring.num];
|
||||
|
||||
if (desc_ndx >= queue->vring.num) {
|
||||
warning("fatal I/O error");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* header */
|
||||
desc = &queue->vring.desc[desc_ndx];
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
req = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
/* block */
|
||||
desc = &queue->vring.desc[desc->next];
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
dst = guest_flat_to_host(self, desc->addr);
|
||||
dst_len = desc->len;
|
||||
|
||||
/* status */
|
||||
desc = &queue->vring.desc[desc->next];
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
status = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
info("reading sector %" PRIu64 " (%d bytes)", req->sector, dst_len);
|
||||
|
||||
if (req->type == VIRTIO_BLK_T_IN) {
|
||||
err = disk_image__read_sector(self->disk_image, req->sector, dst, dst_len);
|
||||
|
||||
if (err)
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
else
|
||||
*status = VIRTIO_BLK_S_OK;
|
||||
} else {
|
||||
warning("request type %d", req->type);
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
}
|
||||
|
||||
used_elem = &queue->vring.used->ring[queue->vring.used->idx++ % queue->vring.num];
|
||||
|
||||
used_elem->id = desc_ndx;
|
||||
used_elem->len = 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
{
|
||||
unsigned long offset;
|
||||
@@ -138,57 +200,17 @@ static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size
|
||||
device.queue_selector = ioport__read16(data);
|
||||
break;
|
||||
case VIRTIO_PCI_QUEUE_NOTIFY: {
|
||||
struct virtio_blk_outhdr *req;
|
||||
struct virt_queue *queue;
|
||||
struct vring_desc *desc;
|
||||
uint16_t queue_index;
|
||||
uint16_t desc_ndx;
|
||||
uint32_t dst_len;
|
||||
uint8_t *status;
|
||||
void *dst;
|
||||
int err;
|
||||
|
||||
queue_index = ioport__read16(data);
|
||||
|
||||
queue = &device.virt_queues[queue_index];
|
||||
|
||||
if (queue->vring.avail->idx == queue->last_avail_idx) {
|
||||
warning("vring is empty");
|
||||
break;
|
||||
while (queue->vring.avail->idx != queue->last_avail_idx) {
|
||||
if (!blk_virtio_read(self, queue))
|
||||
return false;
|
||||
}
|
||||
|
||||
desc_ndx = queue->vring.avail->ring[queue->last_avail_idx++ % queue->vring.num];
|
||||
|
||||
if (desc_ndx >= queue->vring.num) {
|
||||
warning("fatal I/O error");
|
||||
break;
|
||||
}
|
||||
|
||||
/* header */
|
||||
desc = &queue->vring.desc[desc_ndx];
|
||||
|
||||
req = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
/* block */
|
||||
desc = &queue->vring.desc[desc->next];
|
||||
|
||||
dst = guest_flat_to_host(self, desc->addr);
|
||||
dst_len = desc->len;
|
||||
|
||||
/* status */
|
||||
desc = &queue->vring.desc[desc->next];
|
||||
|
||||
status = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
err = disk_image__read_sector(self->disk_image, req->sector, dst, dst_len);
|
||||
|
||||
if (err)
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
else
|
||||
*status = VIRTIO_BLK_S_OK;
|
||||
|
||||
queue->vring.used->idx++;
|
||||
|
||||
kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
|
||||
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user