Commit 04b7a152 authored by Greg Kurz's avatar Greg Kurz Committed by Michael S. Tsirkin
Browse files

vhost: set vring endianness for legacy virtio



Legacy virtio is native endian: if the guest and host endianness differ,
we have to tell vhost so it can swap bytes where appropriate. This is
done through a vhost ring ioctl.

Signed-off-by: default avatarGreg Kurz <gkurz@linux.vnet.ibm.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 41d283bd
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
#include "hw/hw.h"
#include "qemu/atomic.h"
#include "qemu/range.h"
#include "qemu/error-report.h"
#include <linux/vhost.h>
#include "exec/address-spaces.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "migration/migration.h"

static struct vhost_log *vhost_log;
@@ -686,6 +688,27 @@ static void vhost_log_stop(MemoryListener *listener,
    /* FIXME: implement */
}

static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
                                                   bool is_big_endian,
                                                   int vhost_vq_index)
{
    struct vhost_vring_state s = {
        .index = vhost_vq_index,
        .num = is_big_endian
    };

    if (!dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN, &s)) {
        return 0;
    }

    if (errno == ENOTTY) {
        error_report("vhost does not support cross-endian");
        return -ENOSYS;
    }

    return -errno;
}

static int vhost_virtqueue_start(struct vhost_dev *dev,
                                struct VirtIODevice *vdev,
                                struct vhost_virtqueue *vq,
@@ -716,6 +739,16 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
        return -errno;
    }

    if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
        virtio_legacy_is_cross_endian(vdev)) {
        r = vhost_virtqueue_set_vring_endian_legacy(dev,
                                                    virtio_is_big_endian(vdev),
                                                    vhost_vq_index);
        if (r) {
            return -errno;
        }
    }

    s = l = virtio_queue_get_desc_size(vdev, idx);
    a = virtio_queue_get_desc_addr(vdev, idx);
    vq->desc = cpu_physical_memory_map(a, &l, 0);
@@ -786,8 +819,9 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
                                    struct vhost_virtqueue *vq,
                                    unsigned idx)
{
    int vhost_vq_index = idx - dev->vq_index;
    struct vhost_vring_state state = {
        .index = idx - dev->vq_index
        .index = vhost_vq_index,
    };
    int r;
    assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
@@ -798,6 +832,20 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
    }
    virtio_queue_set_last_avail_idx(vdev, idx, state.num);
    virtio_queue_invalidate_signalled_used(vdev, idx);

    /* In the cross-endian case, we need to reset the vring endianness to
     * native as legacy devices expect so by default.
     */
    if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
        virtio_legacy_is_cross_endian(vdev)) {
        r = vhost_virtqueue_set_vring_endian_legacy(dev,
                                                    !virtio_is_big_endian(vdev),
                                                    vhost_vq_index);
        if (r < 0) {
            error_report("failed to reset vring endianness");
        }
    }

    assert (r >= 0);
    cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
                              0, virtio_queue_get_ring_size(vdev, idx));