Bounce any read or write request with a buffer not aligned, a sector
number not aligned or an offset not aligned according to the sector size
reported by blkfront.
Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
---
diff --git a/block-vbd.c b/block-vbd.c
index 0e70ef2..89ecf44 100644
--- a/block-vbd.c
+++ b/block-vbd.c
@@ -88,8 +88,12 @@ static int vbd_open(BlockDriverState *bs, const char
*filename, int flags)
if (!s->dev)
return -EIO;
- if (SECTOR_SIZE % s->info.sector_size) {
- printf("sector size is %d, we only support sector sizes that divide
%d\n", s->info.sector_size, SECTOR_SIZE);
+ if (s->info.sector_size % SECTOR_SIZE) {
+ printf("sector size is %d, we only support sector sizes that are
multiple of %d\n", s->info.sector_size, SECTOR_SIZE);
+ return -EIO;
+ }
+ if (PAGE_SIZE % s->info.sector_size) {
+ printf("sector size is %d, we only support sector sizes that divide
%llu\n", s->info.sector_size, PAGE_SIZE);
return -EIO;
}
@@ -101,8 +105,16 @@ static int vbd_open(BlockDriverState *bs, const char
*filename, int flags)
return 0;
}
+struct vbd_align {
+ uint8_t *src;
+ uint8_t *dst;
+ int64_t offset;
+ int bytes;
+};
+
typedef struct VbdAIOCB {
BlockDriverAIOCB common;
+ struct vbd_align align;
struct blkfront_aiocb aiocb;
} VbdAIOCB;
@@ -147,31 +159,77 @@ static void vbd_do_aio(struct blkfront_aiocb *aiocbp, int
ret) {
blkfront_aio(aiocbp, aiocbp->is_write);
return;
}
+ if (acb->align.bytes)
+ memcpy(acb->align.dst, acb->align.src + acb->align.offset,
acb->align.bytes);
+ if (acb->align.src)
+ qemu_free(acb->align.src);
acb->common.cb(acb->common.opaque, ret);
qemu_aio_release(acb);
}
+static int vbd_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
int nb_sectors);
+
static VbdAIOCB *vbd_aio_setup(BlockDriverState *bs,
int64_t sector_num, uint8_t *buf, int nb_sectors,
BlockDriverCompletionFunc *cb, uint8_t is_write, void *opaque)
{
BDRVVbdState *s = bs->opaque;
VbdAIOCB *acb;
+ int64_t sector_num_aligned = sector_num;
+ int nb_sectors_aligned = nb_sectors;
+ uint8_t *buf_aligned = buf;
+ int sector_alignment_offset = 0;
+ int misalign = 0;
acb = qemu_aio_get(bs, cb, opaque);
if (!acb)
return NULL;
+
+ memset(&acb->align, 0x00, sizeof(struct vbd_align));
+ /* non-sector-aligned location */
+ if ((sector_num * SECTOR_SIZE) & (s->info.sector_size - 1)) {
+ sector_num_aligned = sector_num & (~((s->info.sector_size >> 9) - 1));
+ sector_alignment_offset = sector_num - sector_num_aligned;
+ misalign = 1;
+ }
+ /* non-sector-sized amounts */
+ if ((nb_sectors * SECTOR_SIZE) & (s->info.sector_size - 1)) {
+ nb_sectors_aligned = (nb_sectors + (s->info.sector_size / SECTOR_SIZE
- 1)) & (~((s->info.sector_size >> 9) - 1));
+ misalign = 1;
+ }
+ /* non-sector-aligned buffer */
+ if (misalign || ((uintptr_t)buf & (s->info.sector_size - 1))) {
+ int sm = s->info.sector_size / SECTOR_SIZE;
+ nb_sectors_aligned += sector_alignment_offset;
+ buf_aligned = qemu_memalign(s->info.sector_size, nb_sectors_aligned *
SECTOR_SIZE);
+ if (is_write) {
+ if (sector_alignment_offset > 0)
+ vbd_read(bs, sector_num_aligned, buf_aligned, sm);
+ if (nb_sectors_aligned != nb_sectors)
+ vbd_read(bs, sector_num_aligned + nb_sectors_aligned - sm,
+ buf_aligned + (nb_sectors_aligned - sm) * SECTOR_SIZE,
+ sm);
+ memcpy(buf_aligned + (sector_alignment_offset * SECTOR_SIZE), buf,
nb_sectors * SECTOR_SIZE);
+ acb->align.src = buf_aligned;
+ } else {
+ acb->align.src = buf_aligned;
+ acb->align.dst = buf;
+ acb->align.offset = sector_alignment_offset * SECTOR_SIZE;
+ acb->align.bytes = nb_sectors * SECTOR_SIZE;
+ }
+ }
+
acb->aiocb.aio_dev = s->dev;
- acb->aiocb.aio_buf = buf;
- acb->aiocb.aio_offset = sector_num * SECTOR_SIZE;
+ acb->aiocb.aio_buf = buf_aligned;
+ acb->aiocb.aio_offset = sector_num_aligned * SECTOR_SIZE;
if (nb_sectors <= IDE_DMA_BUF_SECTORS)
- acb->aiocb.aio_nbytes = nb_sectors * SECTOR_SIZE;
+ acb->aiocb.aio_nbytes = nb_sectors_aligned * SECTOR_SIZE;
else
acb->aiocb.aio_nbytes = IDE_DMA_BUF_BYTES;
acb->aiocb.aio_cb = vbd_do_aio;
- acb->aiocb.total_bytes = nb_sectors * SECTOR_SIZE;
+ acb->aiocb.total_bytes = nb_sectors_aligned * SECTOR_SIZE;
acb->aiocb.is_write = is_write;
acb->aiocb.data = acb;
@@ -226,41 +284,13 @@ static int vbd_aligned_io(BlockDriverState *bs,
static int vbd_read(BlockDriverState *bs,
int64_t sector_num, uint8_t *buf, int nb_sectors)
{
- uint8_t *iobuf;
- int ret;
- /* page alignment would be a bit better, but that's still fine compared to
- * copying */
- if (!((uintptr_t)buf & (SECTOR_SIZE-1)))
- return vbd_aligned_io(bs, sector_num, buf, nb_sectors, 0);
- iobuf = qemu_memalign(PAGE_SIZE, nb_sectors * SECTOR_SIZE);
- ret = vbd_aligned_io(bs, sector_num, iobuf, nb_sectors, 0);
- memcpy(buf, iobuf, nb_sectors * SECTOR_SIZE);
- free(iobuf);
- if (ret < 0)
- return ret;
- else if (ret != nb_sectors * SECTOR_SIZE)
- return -EINVAL;
- else
- return 0;
+ return vbd_aligned_io(bs, sector_num, buf, nb_sectors, 0);
}
static int vbd_write(BlockDriverState *bs,
int64_t sector_num, const uint8_t *buf, int nb_sectors)
{
- uint8_t *iobuf;
- int ret;
- if (!((uintptr_t)buf & (SECTOR_SIZE-1)))
- return vbd_aligned_io(bs, sector_num, (uint8_t*) buf, nb_sectors, 1);
- iobuf = qemu_memalign(PAGE_SIZE, nb_sectors * SECTOR_SIZE);
- memcpy(iobuf, buf, nb_sectors * SECTOR_SIZE);
- ret = vbd_aligned_io(bs, sector_num, iobuf, nb_sectors, 1);
- free(iobuf);
- if (ret < 0)
- return ret;
- else if (ret != nb_sectors * SECTOR_SIZE)
- return -EINVAL;
- else
- return 0;
+ return vbd_aligned_io(bs, sector_num, (uint8_t*) buf, nb_sectors, 1);
}
static void vbd_aio_cancel(BlockDriverAIOCB *blockacb)
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|