If qemu couldn't find out what O_DIRECT alignment to use with a given file, it would run into assert(bdrv_opt_mem_align(bs) != 0); in block.c and confuse users. This adds a more descriptive error message for such cases.
Signed-off-by: Kevin Wolf <kw...@redhat.com> --- block/raw-posix.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/block/raw-posix.c b/block/raw-posix.c index ef497b2..8e9758e 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -221,7 +221,7 @@ static int raw_normalize_devicepath(const char **filename) } #endif -static void raw_probe_alignment(BlockDriverState *bs) +static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) { BDRVRawState *s = bs->opaque; char *buf; @@ -240,24 +240,24 @@ static void raw_probe_alignment(BlockDriverState *bs) s->buf_align = 0; #ifdef BLKSSZGET - if (ioctl(s->fd, BLKSSZGET, §or_size) >= 0) { + if (ioctl(fd, BLKSSZGET, §or_size) >= 0) { bs->request_alignment = sector_size; } #endif #ifdef DKIOCGETBLOCKSIZE - if (ioctl(s->fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { + if (ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) >= 0) { bs->request_alignment = sector_size; } #endif #ifdef DIOCGSECTORSIZE - if (ioctl(s->fd, DIOCGSECTORSIZE, §or_size) >= 0) { + if (ioctl(fd, DIOCGSECTORSIZE, §or_size) >= 0) { bs->request_alignment = sector_size; } #endif #ifdef CONFIG_XFS if (s->is_xfs) { struct dioattr da; - if (xfsctl(NULL, s->fd, XFS_IOC_DIOINFO, &da) >= 0) { + if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, &da) >= 0) { bs->request_alignment = da.d_miniosz; /* The kernel returns wrong information for d_mem */ /* s->buf_align = da.d_mem; */ @@ -270,7 +270,7 @@ static void raw_probe_alignment(BlockDriverState *bs) size_t align; buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(s->fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) { + if (pread(fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) { s->buf_align = align; break; } @@ -282,13 +282,18 @@ static void raw_probe_alignment(BlockDriverState *bs) size_t align; buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE); for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) { - if (pread(s->fd, buf, align, 0) >= 0) { + if (pread(fd, buf, align, 0) >= 0) { bs->request_alignment = align; break; } } qemu_vfree(buf); } + + if (!s->buf_align || !bs->request_alignment) { + error_setg(errp, "Could not find working O_DIRECT alignment. " + "Try cache.direct=off."); + } } static void raw_parse_flags(int bdrv_flags, int *open_flags) @@ -505,6 +510,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, BDRVRawState *s; BDRVRawReopenState *raw_s; int ret = 0; + Error *local_err = NULL; assert(state != NULL); assert(state->bs != NULL); @@ -577,6 +583,19 @@ static int raw_reopen_prepare(BDRVReopenState *state, ret = -1; } } + + /* Fail already reopen_prepare() if we can't get a working O_DIRECT + * alignment with the new fd. */ + if (raw_s->fd != -1) { + raw_probe_alignment(state->bs, raw_s->fd, &local_err); + if (local_err) { + qemu_close(raw_s->fd); + raw_s->fd = -1; + error_propagate(errp, local_err); + ret = -EINVAL; + } + } + return ret; } @@ -619,7 +638,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVRawState *s = bs->opaque; - raw_probe_alignment(bs); + raw_probe_alignment(bs, s->fd, errp); bs->bl.opt_mem_alignment = s->buf_align; } -- 1.8.3.1