@@ -88,7 +88,8 @@ ssize_t disk_image__read(struct disk_image *disk, u64 sector, const struct iovec
int iovcount, void *param);
ssize_t disk_image__write(struct disk_image *disk, u64 sector, const struct iovec *iov,
int iovcount, void *param);
-ssize_t disk_image__get_serial(struct disk_image *disk, void *buffer, ssize_t *len);
+ssize_t disk_image__get_serial(struct disk_image *disk, struct iovec *iov,
+ int iovcount, ssize_t len);
struct disk_image *raw_image__probe(int fd, struct stat *st, bool readonly);
struct disk_image *blkdev__probe(const char *filename, int flags, struct stat *st);
@@ -2,6 +2,7 @@
#include "kvm/qcow.h"
#include "kvm/virtio-blk.h"
#include "kvm/kvm.h"
+#include "kvm/iovec.h"
#include <linux/err.h>
#include <poll.h>
@@ -304,20 +305,33 @@ ssize_t disk_image__write(struct disk_image *disk, u64 sector,
return total;
}
-ssize_t disk_image__get_serial(struct disk_image *disk, void *buffer, ssize_t *len)
+ssize_t disk_image__get_serial(struct disk_image *disk, struct iovec *iov,
+ int iovcount, ssize_t len)
{
struct stat st;
+ void *buf;
int r;
r = fstat(disk->fd, &st);
if (r)
return r;
- *len = snprintf(buffer, *len, "%llu%llu%llu",
- (unsigned long long)st.st_dev,
- (unsigned long long)st.st_rdev,
- (unsigned long long)st.st_ino);
- return *len;
+ buf = malloc(len);
+ if (!buf)
+ return -ENOMEM;
+
+ len = snprintf(buf, len, "%llu%llu%llu",
+ (unsigned long long)st.st_dev,
+ (unsigned long long)st.st_rdev,
+ (unsigned long long)st.st_ino);
+ if (len < 0 || (size_t)len > iov_size(iov, iovcount)) {
+ free(buf);
+ return -ENOMEM;
+ }
+
+ memcpy_toiovec(iov, buf, len);
+ free(buf);
+ return len;
}
void disk_image__set_callback(struct disk_image *disk,
@@ -2,6 +2,7 @@
#include "kvm/virtio-pci-dev.h"
#include "kvm/disk-image.h"
+#include "kvm/iovec.h"
#include "kvm/mutex.h"
#include "kvm/util.h"
#include "kvm/kvm.h"
@@ -33,6 +34,7 @@ struct blk_dev_req {
struct blk_dev *bdev;
struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
u16 out, in, head;
+ u8 *status;
struct kvm *kvm;
};
@@ -66,7 +68,7 @@ void virtio_blk_complete(void *param, long len)
u8 *status;
/* status */
- status = req->iov[req->out + req->in - 1].iov_base;
+ status = req->status;
*status = (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
mutex_lock(&bdev->mutex);
@@ -79,46 +81,60 @@ void virtio_blk_complete(void *param, long len)
static void virtio_blk_do_io_request(struct kvm *kvm, struct virt_queue *vq, struct blk_dev_req *req)
{
- struct virtio_blk_outhdr *req_hdr;
- ssize_t block_cnt;
+ struct virtio_blk_outhdr req_hdr;
+ size_t iovcount, last_iov;
struct blk_dev *bdev;
struct iovec *iov;
- u16 out, in;
+ ssize_t len;
u32 type;
u64 sector;
- block_cnt = -1;
bdev = req->bdev;
iov = req->iov;
- out = req->out;
- in = req->in;
- req_hdr = iov[0].iov_base;
- type = virtio_guest_to_host_u32(vq, req_hdr->type);
- sector = virtio_guest_to_host_u64(vq, req_hdr->sector);
+ iovcount = req->out;
+ len = memcpy_fromiovec_safe(&req_hdr, &iov, sizeof(req_hdr), &iovcount);
+ if (len) {
+ pr_warning("Failed to get header");
+ return;
+ }
+
+ type = virtio_guest_to_host_u32(vq, req_hdr.type);
+ sector = virtio_guest_to_host_u64(vq, req_hdr.sector);
+
+ iovcount += req->in;
+ if (!iov_size(iov, iovcount)) {
+ pr_warning("Invalid IOV");
+ return;
+ }
+
+ /* Extract status byte from iovec */
+ last_iov = iovcount - 1;
+ while (!iov[last_iov].iov_len)
+ last_iov--;
+ iov[last_iov].iov_len--;
+ req->status = iov[last_iov].iov_base + iov[last_iov].iov_len;
+ if (!iov[last_iov].iov_len)
+ iovcount--;
switch (type) {
case VIRTIO_BLK_T_IN:
- block_cnt = disk_image__read(bdev->disk, sector,
- iov + 1, in + out - 2, req);
+ disk_image__read(bdev->disk, sector, iov, iovcount, req);
break;
case VIRTIO_BLK_T_OUT:
- block_cnt = disk_image__write(bdev->disk, sector,
- iov + 1, in + out - 2, req);
+ disk_image__write(bdev->disk, sector, iov, iovcount, req);
break;
case VIRTIO_BLK_T_FLUSH:
- block_cnt = disk_image__flush(bdev->disk);
- virtio_blk_complete(req, block_cnt);
+ len = disk_image__flush(bdev->disk);
+ virtio_blk_complete(req, len);
break;
case VIRTIO_BLK_T_GET_ID:
- block_cnt = VIRTIO_BLK_ID_BYTES;
- disk_image__get_serial(bdev->disk,
- (iov + 1)->iov_base, &block_cnt);
- virtio_blk_complete(req, block_cnt);
+ len = disk_image__get_serial(bdev->disk, iov, iovcount,
+ VIRTIO_BLK_ID_BYTES);
+ virtio_blk_complete(req, len);
break;
default:
pr_warning("request type %d", type);
- block_cnt = -1;
break;
}
}
@@ -161,6 +177,7 @@ static u32 get_host_features(struct kvm *kvm, void *dev)
| 1UL << VIRTIO_BLK_F_FLUSH
| 1UL << VIRTIO_RING_F_EVENT_IDX
| 1UL << VIRTIO_RING_F_INDIRECT_DESC
+ | 1UL << VIRTIO_F_ANY_LAYOUT
| (bdev->disk->readonly ? 1UL << VIRTIO_BLK_F_RO : 0);
}
The current virtio-block implementation assumes that buffers have a specific layout (5.2.6.4 "Legacy Interface: Framing Requirements"). Modern virtio removes this layout constraint, so we have to be careful when reading buffers. Note that since the Linux driver uses the same layout as the legacy transport, arbitrary layouts were not actually tested. Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> --- include/kvm/disk-image.h | 3 +- disk/core.c | 26 ++++++++++++++---- virtio/blk.c | 59 ++++++++++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 28 deletions(-)