9ae3a8
From de29d97cab7b97d776cbb1077079eff0bfc46186 Mon Sep 17 00:00:00 2001
9ae3a8
From: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Date: Tue, 29 Nov 2011 12:42:20 +0100
9ae3a8
Subject: [PATCH 10/37] raw: Probe required direct I/O alignment
9ae3a8
9ae3a8
Message-id: <1392117622-28812-11-git-send-email-kwolf@redhat.com>
9ae3a8
Patchwork-id: 57175
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH v2 10/37] raw: Probe required direct I/O alignment
9ae3a8
Bugzilla: 748906
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Max Reitz <mreitz@redhat.com>
9ae3a8
9ae3a8
From: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
Add a bs->request_alignment field that contains the required
9ae3a8
offset/length alignment for I/O requests and fill it in the raw block
9ae3a8
drivers. Use ioctls if possible, else see what alignment it takes for
9ae3a8
O_DIRECT to succeed.
9ae3a8
9ae3a8
While at it, also expose the memory alignment requirements, which may be
9ae3a8
(and in practice are) different from the disk alignment requirements.
9ae3a8
9ae3a8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9ae3a8
(cherry picked from commit c25f53b06eba1575d5d0e92a0132455c97825b83)
9ae3a8
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
---
9ae3a8
 block.c                   |   3 ++
9ae3a8
 block/raw-posix.c         | 102 ++++++++++++++++++++++++++++++++++++++--------
9ae3a8
 block/raw-win32.c         |  41 +++++++++++++++++++
9ae3a8
 include/block/block_int.h |   3 ++
9ae3a8
 4 files changed, 132 insertions(+), 17 deletions(-)
9ae3a8
---
9ae3a8
 block.c                   |    3 +
9ae3a8
 block/raw-posix.c         |  102 +++++++++++++++++++++++++++++++++++++-------
9ae3a8
 block/raw-win32.c         |   41 ++++++++++++++++++
9ae3a8
 include/block/block_int.h |    3 +
9ae3a8
 4 files changed, 132 insertions(+), 17 deletions(-)
9ae3a8
9ae3a8
diff --git a/block.c b/block.c
9ae3a8
index f2102bc..40a4a34 100644
9ae3a8
--- a/block.c
9ae3a8
+++ b/block.c
9ae3a8
@@ -780,6 +780,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
9ae3a8
 
9ae3a8
     bs->open_flags = flags;
9ae3a8
     bs->guest_block_size = 512;
9ae3a8
+    bs->request_alignment = 512;
9ae3a8
     bs->zero_beyond_eof = true;
9ae3a8
     open_flags = bdrv_open_flags(bs, flags);
9ae3a8
     bs->read_only = !(open_flags & BDRV_O_RDWR);
9ae3a8
@@ -845,6 +846,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
9ae3a8
     }
9ae3a8
 
9ae3a8
     bdrv_refresh_limits(bs);
9ae3a8
+    assert(bdrv_opt_mem_align(bs) != 0);
9ae3a8
+    assert(bs->request_alignment != 0);
9ae3a8
 
9ae3a8
 #ifndef _WIN32
9ae3a8
     if (bs->is_temporary) {
9ae3a8
diff --git a/block/raw-posix.c b/block/raw-posix.c
9ae3a8
index f410668..9ee5b8e 100644
9ae3a8
--- a/block/raw-posix.c
9ae3a8
+++ b/block/raw-posix.c
9ae3a8
@@ -127,6 +127,8 @@ typedef struct BDRVRawState {
9ae3a8
     int fd;
9ae3a8
     int type;
9ae3a8
     int open_flags;
9ae3a8
+    size_t buf_align;
9ae3a8
+
9ae3a8
 #if defined(__linux__)
9ae3a8
     /* linux floppy specific */
9ae3a8
     int64_t fd_open_time;
9ae3a8
@@ -213,6 +215,76 @@ static int raw_normalize_devicepath(const char **filename)
9ae3a8
 }
9ae3a8
 #endif
9ae3a8
 
9ae3a8
+static void raw_probe_alignment(BlockDriverState *bs)
9ae3a8
+{
9ae3a8
+    BDRVRawState *s = bs->opaque;
9ae3a8
+    char *buf;
9ae3a8
+    unsigned int sector_size;
9ae3a8
+
9ae3a8
+    /* For /dev/sg devices the alignment is not really used.
9ae3a8
+       With buffered I/O, we don't have any restrictions. */
9ae3a8
+    if (bs->sg || !(s->open_flags & O_DIRECT)) {
9ae3a8
+        bs->request_alignment = 1;
9ae3a8
+        s->buf_align = 1;
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* Try a few ioctls to get the right size */
9ae3a8
+    bs->request_alignment = 0;
9ae3a8
+    s->buf_align = 0;
9ae3a8
+
9ae3a8
+#ifdef BLKSSZGET
9ae3a8
+    if (ioctl(s->fd, BLKSSZGET, &sector_size) >= 0) {
9ae3a8
+        bs->request_alignment = sector_size;
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+#ifdef DKIOCGETBLOCKSIZE
9ae3a8
+    if (ioctl(s->fd, DKIOCGETBLOCKSIZE, &sector_size) >= 0) {
9ae3a8
+        bs->request_alignment = sector_size;
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+#ifdef DIOCGSECTORSIZE
9ae3a8
+    if (ioctl(s->fd, DIOCGSECTORSIZE, &sector_size) >= 0) {
9ae3a8
+        bs->request_alignment = sector_size;
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+#ifdef CONFIG_XFS
9ae3a8
+    if (s->is_xfs) {
9ae3a8
+        struct dioattr da;
9ae3a8
+        if (xfsctl(NULL, s->fd, XFS_IOC_DIOINFO, &da) >= 0) {
9ae3a8
+            bs->request_alignment = da.d_miniosz;
9ae3a8
+            /* The kernel returns wrong information for d_mem */
9ae3a8
+            /* s->buf_align = da.d_mem; */
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+#endif
9ae3a8
+
9ae3a8
+    /* If we could not get the sizes so far, we can only guess them */
9ae3a8
+    if (!s->buf_align) {
9ae3a8
+        size_t align;
9ae3a8
+        buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE);
9ae3a8
+        for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
9ae3a8
+            if (pread(s->fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) {
9ae3a8
+                s->buf_align = align;
9ae3a8
+                break;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+        qemu_vfree(buf);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (!bs->request_alignment) {
9ae3a8
+        size_t align;
9ae3a8
+        buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE);
9ae3a8
+        for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
9ae3a8
+            if (pread(s->fd, buf, align, 0) >= 0) {
9ae3a8
+                bs->request_alignment = align;
9ae3a8
+                break;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+        qemu_vfree(buf);
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
 static void raw_parse_flags(int bdrv_flags, int *open_flags)
9ae3a8
 {
9ae3a8
     assert(open_flags != NULL);
9ae3a8
@@ -464,7 +536,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
9ae3a8
     return ret;
9ae3a8
 }
9ae3a8
 
9ae3a8
-
9ae3a8
 static void raw_reopen_commit(BDRVReopenState *state)
9ae3a8
 {
9ae3a8
     BDRVRawReopenState *raw_s = state->opaque;
9ae3a8
@@ -500,23 +571,15 @@ static void raw_reopen_abort(BDRVReopenState *state)
9ae3a8
     state->opaque = NULL;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static int raw_refresh_limits(BlockDriverState *bs)
9ae3a8
+{
9ae3a8
+    BDRVRawState *s = bs->opaque;
9ae3a8
 
9ae3a8
-/* XXX: use host sector size if necessary with:
9ae3a8
-#ifdef DIOCGSECTORSIZE
9ae3a8
-        {
9ae3a8
-            unsigned int sectorsize = 512;
9ae3a8
-            if (!ioctl(fd, DIOCGSECTORSIZE, &sectorsize) &&
9ae3a8
-                sectorsize > bufsize)
9ae3a8
-                bufsize = sectorsize;
9ae3a8
-        }
9ae3a8
-#endif
9ae3a8
-#ifdef CONFIG_COCOA
9ae3a8
-        uint32_t blockSize = 512;
9ae3a8
-        if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
9ae3a8
-            bufsize = blockSize;
9ae3a8
-        }
9ae3a8
-#endif
9ae3a8
-*/
9ae3a8
+    raw_probe_alignment(bs);
9ae3a8
+    bs->bl.opt_mem_alignment = s->buf_align;
9ae3a8
+
9ae3a8
+    return 0;
9ae3a8
+}
9ae3a8
 
9ae3a8
 static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
9ae3a8
 {
9ae3a8
@@ -1363,6 +1426,7 @@ static BlockDriver bdrv_file = {
9ae3a8
     .bdrv_aio_writev = raw_aio_writev,
9ae3a8
     .bdrv_aio_flush = raw_aio_flush,
9ae3a8
     .bdrv_aio_discard = raw_aio_discard,
9ae3a8
+    .bdrv_refresh_limits = raw_refresh_limits,
9ae3a8
 
9ae3a8
     .bdrv_truncate = raw_truncate,
9ae3a8
     .bdrv_getlength = raw_getlength,
9ae3a8
@@ -1739,6 +1803,7 @@ static BlockDriver bdrv_host_device = {
9ae3a8
     .bdrv_aio_writev	= raw_aio_writev,
9ae3a8
     .bdrv_aio_flush	= raw_aio_flush,
9ae3a8
     .bdrv_aio_discard   = hdev_aio_discard,
9ae3a8
+    .bdrv_refresh_limits = raw_refresh_limits,
9ae3a8
 
9ae3a8
     .bdrv_truncate      = raw_truncate,
9ae3a8
     .bdrv_getlength	= raw_getlength,
9ae3a8
@@ -1869,6 +1934,7 @@ static BlockDriver bdrv_host_floppy = {
9ae3a8
     .bdrv_aio_readv     = raw_aio_readv,
9ae3a8
     .bdrv_aio_writev    = raw_aio_writev,
9ae3a8
     .bdrv_aio_flush	= raw_aio_flush,
9ae3a8
+    .bdrv_refresh_limits = raw_refresh_limits,
9ae3a8
 
9ae3a8
     .bdrv_truncate      = raw_truncate,
9ae3a8
     .bdrv_getlength      = raw_getlength,
9ae3a8
@@ -1978,6 +2044,7 @@ static BlockDriver bdrv_host_cdrom = {
9ae3a8
     .bdrv_aio_readv     = raw_aio_readv,
9ae3a8
     .bdrv_aio_writev    = raw_aio_writev,
9ae3a8
     .bdrv_aio_flush	= raw_aio_flush,
9ae3a8
+    .bdrv_refresh_limits = raw_refresh_limits,
9ae3a8
 
9ae3a8
     .bdrv_truncate      = raw_truncate,
9ae3a8
     .bdrv_getlength      = raw_getlength,
9ae3a8
@@ -2105,6 +2172,7 @@ static BlockDriver bdrv_host_cdrom = {
9ae3a8
     .bdrv_aio_readv     = raw_aio_readv,
9ae3a8
     .bdrv_aio_writev    = raw_aio_writev,
9ae3a8
     .bdrv_aio_flush	= raw_aio_flush,
9ae3a8
+    .bdrv_refresh_limits = raw_refresh_limits,
9ae3a8
 
9ae3a8
     .bdrv_truncate      = raw_truncate,
9ae3a8
     .bdrv_getlength      = raw_getlength,
9ae3a8
diff --git a/block/raw-win32.c b/block/raw-win32.c
9ae3a8
index 6ac3797..ac20370 100644
9ae3a8
--- a/block/raw-win32.c
9ae3a8
+++ b/block/raw-win32.c
9ae3a8
@@ -201,6 +201,35 @@ static int set_sparse(int fd)
9ae3a8
 				 NULL, 0, NULL, 0, &returned, NULL);
9ae3a8
 }
9ae3a8
 
9ae3a8
+static void raw_probe_alignment(BlockDriverState *bs)
9ae3a8
+{
9ae3a8
+    BDRVRawState *s = bs->opaque;
9ae3a8
+    DWORD sectorsPerCluster, freeClusters, totalClusters, count;
9ae3a8
+    DISK_GEOMETRY_EX dg;
9ae3a8
+    BOOL status;
9ae3a8
+
9ae3a8
+    if (s->type == FTYPE_CD) {
9ae3a8
+        bs->request_alignment = 2048;
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+    if (s->type == FTYPE_HARDDISK) {
9ae3a8
+        status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
9ae3a8
+                                 NULL, 0, &dg, sizeof(dg), &count, NULL);
9ae3a8
+        if (status != 0) {
9ae3a8
+            bs->request_alignment = dg.Geometry.BytesPerSector;
9ae3a8
+            return;
9ae3a8
+        }
9ae3a8
+        /* try GetDiskFreeSpace too */
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (s->drive_path[0]) {
9ae3a8
+        GetDiskFreeSpace(s->drive_path, &sectorsPerCluster,
9ae3a8
+                         &dg.Geometry.BytesPerSector,
9ae3a8
+                         &freeClusters, &totalClusters);
9ae3a8
+        bs->request_alignment = dg.Geometry.BytesPerSector;
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
 static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
9ae3a8
 {
9ae3a8
     assert(access_flags != NULL);
9ae3a8
@@ -268,6 +297,17 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
         }
9ae3a8
     }
9ae3a8
 
9ae3a8
+    if (filename[0] && filename[1] == ':') {
9ae3a8
+        snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]);
9ae3a8
+    } else if (filename[0] == '\\' && filename[1] == '\\') {
9ae3a8
+        s->drive_path[0] = 0;
9ae3a8
+    } else {
9ae3a8
+        /* Relative path.  */
9ae3a8
+        char buf[MAX_PATH];
9ae3a8
+        GetCurrentDirectory(MAX_PATH, buf);
9ae3a8
+        snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]);
9ae3a8
+    }
9ae3a8
+
9ae3a8
     s->hfile = CreateFile(filename, access_flags,
9ae3a8
                           FILE_SHARE_READ, NULL,
9ae3a8
                           OPEN_EXISTING, overlapped, NULL);
9ae3a8
@@ -293,6 +333,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
9ae3a8
         s->aio = aio;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    raw_probe_alignment(bs);
9ae3a8
     ret = 0;
9ae3a8
 fail:
9ae3a8
     qemu_opts_del(opts);
9ae3a8
diff --git a/include/block/block_int.h b/include/block/block_int.h
9ae3a8
index 0445e6b..e66bd5f 100644
9ae3a8
--- a/include/block/block_int.h
9ae3a8
+++ b/include/block/block_int.h
9ae3a8
@@ -307,6 +307,9 @@ struct BlockDriverState {
9ae3a8
     /* Whether produces zeros when read beyond eof */
9ae3a8
     bool zero_beyond_eof;
9ae3a8
 
9ae3a8
+    /* Alignment requirement for offset/length of I/O requests */
9ae3a8
+    unsigned int request_alignment;
9ae3a8
+
9ae3a8
     /* the block size for which the guest device expects atomicity */
9ae3a8
     int guest_block_size;
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8