[dev][virtio][block] handle scatter gathered reads/writes

This commit is contained in:
Travis Geiselbrecht
2015-09-21 20:21:16 -07:00
parent 4f5c692005
commit c17ed4295b
2 changed files with 49 additions and 4 deletions

View File

@@ -140,7 +140,7 @@ status_t virtio_block_init(struct virtio_device *dev, uint32_t host_features)
// XXX check features bits and ack/nak them
/* allocate a virtio ring */
virtio_alloc_ring(dev, 0, 16);
virtio_alloc_ring(dev, 0, 256);
/* set our irq handler */
dev->irq_driver_callback = &virtio_block_irq_driver_callback;
@@ -208,6 +208,7 @@ ssize_t virtio_block_read_write(struct virtio_device *dev, void *buf, off_t offs
uint16_t i;
struct vring_desc *desc;
paddr_t pa;
vaddr_t va = (vaddr_t)buf;
LTRACEF("dev %p, buf %p, offset 0x%llx, len %zu\n", dev, buf, offset, len);
@@ -235,16 +236,58 @@ ssize_t virtio_block_read_write(struct virtio_device *dev, void *buf, off_t offs
/* set up the descriptor pointing to the buffer */
desc = virtio_desc_index_to_desc(dev, 0, desc->next);
#if WITH_KERNEL_VM
// XXX handle bufs that cross page boundaries
arch_mmu_query((vaddr_t)buf, &pa, NULL);
/* translate the first buffer */
arch_mmu_query(va, &pa, NULL);
desc->addr = (uint64_t)pa;
/* desc->len is filled in below */
#else
desc->addr = (uint64_t)(uintptr_t)buf;
#endif
desc->len = len;
#endif
desc->flags |= write ? 0 : VRING_DESC_F_WRITE; /* mark buffer as write-only if its a block read */
desc->flags |= VRING_DESC_F_NEXT;
#if WITH_KERNEL_VM
/* see if we need to add more descriptors due to scatter gather */
paddr_t next_pa = PAGE_ALIGN(pa + 1);
desc->len = MIN(next_pa - pa, len);
LTRACEF("first descriptor va 0x%lx desc->addr 0x%llx desc->len %zu\n", va, desc->addr, desc->len);
len -= desc->len;
while (len > 0) {
/* amount of source buffer handled by this iteration of the loop */
size_t len_tohandle = MIN(len, PAGE_SIZE);
/* translate the next page in the buffer */
va = PAGE_ALIGN(va + 1);
arch_mmu_query(va, &pa, NULL);
LTRACEF("va now 0x%lx, pa 0x%lx, next_pa 0x%lx, remaining len %zu\n", va, pa, next_pa, len);
/* is the new translated physical address contiguous to the last one? */
if (next_pa == pa) {
LTRACEF("extending last one by %zu bytes\n", len_tohandle);
desc->len += len_tohandle;
} else {
uint16_t next_i = virtio_alloc_desc(dev, 0);
struct vring_desc *next_desc = virtio_desc_index_to_desc(dev, 0, next_i);
DEBUG_ASSERT(next_desc);
LTRACEF("doesn't extend, need new desc, allocated desc %i (%p)\n", next_i, next_desc);
/* fill this descriptor in and put it after the last one but before the response descriptor */
next_desc->addr = (uint64_t)pa;
next_desc->len = len_tohandle;
next_desc->flags = write ? 0 : VRING_DESC_F_WRITE; /* mark buffer as write-only if its a block read */
next_desc->flags |= VRING_DESC_F_NEXT;
next_desc->next = desc->next;
desc->next = next_i;
desc = next_desc;
}
len -= len_tohandle;
next_pa += PAGE_SIZE;
}
#endif
/* set up the descriptor pointing to the response */
desc = virtio_desc_index_to_desc(dev, 0, desc->next);
desc->addr = bdev->blk_response_phys;

View File

@@ -23,6 +23,7 @@
#pragma once
#include <compiler.h>
#include <assert.h>
#include <list.h>
#include <sys/types.h>
#include <dev/virtio/virtio_ring.h>
@@ -71,6 +72,7 @@ struct vring_desc *virtio_alloc_desc_chain(struct virtio_device *dev, uint ring_
static inline struct vring_desc *virtio_desc_index_to_desc(struct virtio_device *dev, uint ring_index, uint16_t desc_index)
{
DEBUG_ASSERT(desc_index != 0xffff);
return &dev->ring[ring_index].desc[desc_index];
}