diff options
Diffstat (limited to 'block-raw-posix.c')
-rw-r--r-- | block-raw-posix.c | 119 |
1 files changed, 113 insertions, 6 deletions
diff --git a/block-raw-posix.c b/block-raw-posix.c index 6b0009e564..0ea365f1c8 100644 --- a/block-raw-posix.c +++ b/block-raw-posix.c @@ -77,10 +77,10 @@ typedef struct BDRVRawState { int fd; int type; + int open_flags; unsigned int lseek_err_cnt; #if defined(__linux__) /* linux floppy specific */ - int fd_open_flags; int64_t fd_open_time; int64_t fd_error_time; int fd_got_error; @@ -111,6 +111,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) open_flags |= O_DIRECT; #endif + s->open_flags = open_flags; s->type = FTYPE_FILE; fd = open(filename, open_flags, 0644); @@ -141,7 +142,14 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) #endif */ -static int raw_pread(BlockDriverState *bs, int64_t offset, +/* + * offset and count are in bytes, but must be multiples of 512 for files + * opened with O_DIRECT. buf must be aligned to 512 bytes then. + * + * This function may be called without alignment if the caller ensures + * that O_DIRECT is not in effect. + */ +static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, uint8_t *buf, int count) { BDRVRawState *s = bs->opaque; @@ -194,7 +202,14 @@ label__raw_read__success: return ret; } -static int raw_pwrite(BlockDriverState *bs, int64_t offset, +/* + * offset and count are in bytes, but must be multiples of 512 for files + * opened with O_DIRECT. buf must be aligned to 512 bytes then. + * + * This function may be called without alignment if the caller ensures + * that O_DIRECT is not in effect. + */ +static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, const uint8_t *buf, int count) { BDRVRawState *s = bs->opaque; @@ -230,6 +245,67 @@ label__raw_write__success: return ret; } + +#ifdef O_DIRECT +/* + * offset and count are in bytes and possibly not aligned. For files opened + * with O_DIRECT, necessary alignments are ensured before calling + * raw_pread_aligned to do the actual read. + */ +static int raw_pread(BlockDriverState *bs, int64_t offset, + uint8_t *buf, int count) +{ + BDRVRawState *s = bs->opaque; + + if (unlikely((s->open_flags & O_DIRECT) && + (offset % 512 || count % 512 || (uintptr_t) buf % 512))) { + + int ret; + + // Temporarily disable O_DIRECT for unaligned access + fcntl(s->fd, F_SETFL, s->open_flags & ~O_DIRECT); + ret = raw_pread_aligned(bs, offset, buf, count); + fcntl(s->fd, F_SETFL, s->open_flags); + + return ret; + + } else { + return raw_pread_aligned(bs, offset, buf, count); + } +} + +/* + * offset and count are in bytes and possibly not aligned. For files opened + * with O_DIRECT, necessary alignments are ensured before calling + * raw_pwrite_aligned to do the actual write. + */ +static int raw_pwrite(BlockDriverState *bs, int64_t offset, + const uint8_t *buf, int count) +{ + BDRVRawState *s = bs->opaque; + + if (unlikely((s->open_flags & O_DIRECT) && + (offset % 512 || count % 512 || (uintptr_t) buf % 512))) { + + int ret; + + // Temporarily disable O_DIRECT for unaligned access + fcntl(s->fd, F_SETFL, s->open_flags & ~O_DIRECT); + ret = raw_pwrite_aligned(bs, offset, buf, count); + fcntl(s->fd, F_SETFL, s->open_flags); + + return ret; + } else { + return raw_pwrite_aligned(bs, offset, buf, count); + } +} + +#else +#define raw_pread raw_pread_aligned +#define raw_pwrite raw_pwrite_aligned +#endif + + /***********************************************************/ /* Unix AIO using POSIX AIO */ @@ -402,10 +478,26 @@ static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { RawAIOCB *acb; + BDRVRawState *s = bs->opaque; + + /* + * If O_DIRECT is used and the buffer is not aligned fall back + * to synchronous IO. + */ + if (unlikely((s->open_flags & O_DIRECT) && ((uintptr_t) buf % 512))) { + int ret; + + acb = qemu_aio_get(bs, cb, opaque); + ret = raw_pread(bs, 512 * sector_num, buf, 512 * nb_sectors); + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return &acb->common; + } acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque); if (!acb) return NULL; + if (aio_read(&acb->aiocb) < 0) { qemu_aio_release(acb); return NULL; @@ -418,6 +510,21 @@ static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { RawAIOCB *acb; + BDRVRawState *s = bs->opaque; + + /* + * If O_DIRECT is used and the buffer is not aligned fall back + * to synchronous IO. + */ + if (unlikely((s->open_flags & O_DIRECT) && ((uintptr_t) buf % 512))) { + int ret; + + acb = qemu_aio_get(bs, cb, opaque); + ret = raw_pwrite(bs, 512 * sector_num, buf, 512 * nb_sectors); + acb->common.cb(acb->common.opaque, ret); + qemu_aio_release(acb); + return &acb->common; + } acb = raw_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque); if (!acb) @@ -679,7 +786,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) s->type = FTYPE_CD; } else if (strstart(filename, "/dev/fd", NULL)) { s->type = FTYPE_FD; - s->fd_open_flags = open_flags; + s->open_flags = open_flags; /* open will not fail even if no floppy is inserted */ open_flags |= O_NONBLOCK; } else if (strstart(filename, "/dev/sg", NULL)) { @@ -734,7 +841,7 @@ static int fd_open(BlockDriverState *bs) #endif return -EIO; } - s->fd = open(bs->filename, s->fd_open_flags); + s->fd = open(bs->filename, s->open_flags); if (s->fd < 0) { s->fd_error_time = qemu_get_clock(rt_clock); s->fd_got_error = 1; @@ -831,7 +938,7 @@ static int raw_eject(BlockDriverState *bs, int eject_flag) close(s->fd); s->fd = -1; } - fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK); + fd = open(bs->filename, s->open_flags | O_NONBLOCK); if (fd >= 0) { if (ioctl(fd, FDEJECT, 0) < 0) perror("FDEJECT"); |