mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-09-04 12:41:37 +00:00
kvm,virtio: add scatter-gather support v2
The size of the virtqueue(16) was smaller than scatter-gather(128) which hypervisor tells the guest. Under some circumstances, if the guest uses scatter-gather which larger than 16 - 2, kernel panics. That is why the scatter-gather support v1 breaks Cyrill's kernel boot. Two descriptors are used as the header and status descriptors. The remaining descriptors can be used as the real disk data descriptors. So DISK_SEG_MAX should be VIRTIO_BLK_QUEUE_SIZE - 2. VIRTIO_BLK_QUEUE_SIZE is 128 and DISK_SEG_MAX is 126 in this patch. Tested-by: Cyrill Gorcunov <gorcunov@gmail.com> Signed-off-by: Asias He <asias.hejun@gmail.com> Signed-off-by: Pekka Enberg <penberg@kernel.org>
This commit is contained in:
+74
-44
@@ -18,7 +18,7 @@
|
||||
|
||||
#define NUM_VIRT_QUEUES 1
|
||||
|
||||
#define VIRTIO_BLK_QUEUE_SIZE 16
|
||||
#define VIRTIO_BLK_QUEUE_SIZE 128
|
||||
|
||||
struct device {
|
||||
struct virtio_blk_config blk_config;
|
||||
@@ -36,10 +36,12 @@ struct device {
|
||||
#define DISK_CYLINDERS 1024
|
||||
#define DISK_HEADS 64
|
||||
#define DISK_SECTORS 32
|
||||
#define DISK_SEG_MAX 126
|
||||
|
||||
static struct device device = {
|
||||
.blk_config = (struct virtio_blk_config) {
|
||||
.capacity = DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
|
||||
.seg_max = DISK_SEG_MAX,
|
||||
/* VIRTIO_BLK_F_GEOMETRY */
|
||||
.geometry = {
|
||||
.cylinders = DISK_CYLINDERS,
|
||||
@@ -54,7 +56,7 @@ static struct device device = {
|
||||
* node kernel will compute disk geometry by own, the
|
||||
* same applies to VIRTIO_BLK_F_BLK_SIZE
|
||||
*/
|
||||
.host_features = 0,
|
||||
.host_features = (1UL << VIRTIO_BLK_F_SEG_MAX),
|
||||
};
|
||||
|
||||
static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
|
||||
@@ -107,73 +109,101 @@ 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)
|
||||
static bool blk_virtio_request(struct kvm *self, struct virt_queue *queue)
|
||||
{
|
||||
struct vring_used_elem *used_elem;
|
||||
struct virtio_blk_outhdr *req;
|
||||
uint16_t desc_block_last;
|
||||
struct vring_desc *desc;
|
||||
uint16_t desc_status;
|
||||
uint16_t desc_block;
|
||||
uint32_t block_len;
|
||||
uint16_t desc_ndx;
|
||||
uint32_t block_cnt;
|
||||
uint16_t desc_hdr;
|
||||
uint8_t *status;
|
||||
void *block;
|
||||
int err;
|
||||
int err_cnt;
|
||||
|
||||
desc_ndx = virt_queue__pop(queue);
|
||||
/* header */
|
||||
desc_hdr = virt_queue__pop(queue);
|
||||
|
||||
if (desc_ndx >= queue->vring.num) {
|
||||
if (desc_hdr >= queue->vring.num) {
|
||||
warning("fatal I/O error");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* header */
|
||||
desc = virt_queue__get_desc(queue, desc_ndx);
|
||||
desc = virt_queue__get_desc(queue, desc_hdr);
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
req = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
/* block */
|
||||
desc = virt_queue__get_desc(queue, desc->next);
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
block = guest_flat_to_host(self, desc->addr);
|
||||
block_len = desc->len;
|
||||
|
||||
/* status */
|
||||
desc = virt_queue__get_desc(queue, desc->next);
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
desc_status = desc_hdr;
|
||||
|
||||
do {
|
||||
desc_block_last = desc_status;
|
||||
desc_status = virt_queue__get_desc(queue, desc_status)->next;
|
||||
|
||||
if (desc_status >= queue->vring.num) {
|
||||
warning("fatal I/O error");
|
||||
return false;
|
||||
}
|
||||
|
||||
desc = virt_queue__get_desc(queue, desc_status);
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
} while (desc->flags & VRING_DESC_F_NEXT);
|
||||
|
||||
status = guest_flat_to_host(self, desc->addr);
|
||||
|
||||
switch (req->type) {
|
||||
case VIRTIO_BLK_T_IN: {
|
||||
int err;
|
||||
/* block */
|
||||
desc_block = desc_hdr;
|
||||
block_cnt = 0;
|
||||
err_cnt = 0;
|
||||
|
||||
err = disk_image__read_sector(self->disk_image, req->sector, block, block_len);
|
||||
if (err)
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
else
|
||||
*status = VIRTIO_BLK_S_OK;
|
||||
break;
|
||||
}
|
||||
case VIRTIO_BLK_T_OUT: {
|
||||
int err;
|
||||
do {
|
||||
desc_block = virt_queue__get_desc(queue, desc_block)->next;
|
||||
|
||||
desc = virt_queue__get_desc(queue, desc_block);
|
||||
assert(!(desc->flags & VRING_DESC_F_INDIRECT));
|
||||
|
||||
block = guest_flat_to_host(self, desc->addr);
|
||||
block_len = desc->len;
|
||||
|
||||
switch (req->type) {
|
||||
case VIRTIO_BLK_T_IN:
|
||||
err = disk_image__read_sector(self->disk_image, req->sector, block, block_len);
|
||||
break;
|
||||
case VIRTIO_BLK_T_OUT:
|
||||
err = disk_image__write_sector(self->disk_image, req->sector, block, block_len);
|
||||
break;
|
||||
default:
|
||||
warning("request type %d", req->type);
|
||||
err = -1;
|
||||
}
|
||||
|
||||
err = disk_image__write_sector(self->disk_image, req->sector, block, block_len);
|
||||
if (err)
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
else
|
||||
*status = VIRTIO_BLK_S_OK;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warning("request type %d", req->type);
|
||||
*status = VIRTIO_BLK_S_IOERR;
|
||||
break;
|
||||
}
|
||||
err_cnt++;
|
||||
|
||||
req->sector += block_len >> SECTOR_SHIFT;
|
||||
block_cnt += block_len;
|
||||
|
||||
if (desc_block == desc_block_last)
|
||||
break;
|
||||
|
||||
if (desc_block >= queue->vring.num) {
|
||||
warning("fatal I/O error");
|
||||
return false;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
*status = err_cnt ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
|
||||
|
||||
used_elem = virt_queue__get_used_elem(queue);
|
||||
|
||||
used_elem->id = desc_ndx;
|
||||
used_elem->len = 3;
|
||||
used_elem->id = desc_hdr;
|
||||
used_elem->len = block_cnt;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -214,7 +244,7 @@ static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size
|
||||
queue = &device.virt_queues[queue_index];
|
||||
|
||||
while (queue->vring.avail->idx != queue->last_avail_idx) {
|
||||
if (!blk_virtio_read(self, queue))
|
||||
if (!blk_virtio_request(self, queue))
|
||||
return false;
|
||||
}
|
||||
kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
|
||||
|
||||
Reference in New Issue
Block a user