qemu/hw/virtio/vhost-vsock-common.c
<<
>>
Prefs
   1/*
   2 * Parent class for vhost-vsock devices
   3 *
   4 * Copyright 2015-2020 Red Hat, Inc.
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or
   7 * (at your option) any later version.  See the COPYING file in the
   8 * top-level directory.
   9 */
  10
  11#include "qemu/osdep.h"
  12#include "standard-headers/linux/virtio_vsock.h"
  13#include "qapi/error.h"
  14#include "hw/virtio/virtio-access.h"
  15#include "qemu/error-report.h"
  16#include "hw/qdev-properties.h"
  17#include "hw/virtio/vhost-vsock.h"
  18#include "qemu/iov.h"
  19#include "monitor/monitor.h"
  20
  21const int feature_bits[] = {
  22    VIRTIO_VSOCK_F_SEQPACKET,
  23    VHOST_INVALID_FEATURE_BIT
  24};
  25
  26uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features,
  27                                         Error **errp)
  28{
  29    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  30
  31    if (vvc->seqpacket != ON_OFF_AUTO_OFF) {
  32        virtio_add_feature(&features, VIRTIO_VSOCK_F_SEQPACKET);
  33    }
  34
  35    features = vhost_get_features(&vvc->vhost_dev, feature_bits, features);
  36
  37    if (vvc->seqpacket == ON_OFF_AUTO_ON &&
  38        !virtio_has_feature(features, VIRTIO_VSOCK_F_SEQPACKET)) {
  39        error_setg(errp, "vhost-vsock backend doesn't support seqpacket");
  40    }
  41
  42    return features;
  43}
  44
  45int vhost_vsock_common_start(VirtIODevice *vdev)
  46{
  47    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  48    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  49    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  50    int ret;
  51    int i;
  52
  53    if (!k->set_guest_notifiers) {
  54        error_report("binding does not support guest notifiers");
  55        return -ENOSYS;
  56    }
  57
  58    ret = vhost_dev_enable_notifiers(&vvc->vhost_dev, vdev);
  59    if (ret < 0) {
  60        error_report("Error enabling host notifiers: %d", -ret);
  61        return ret;
  62    }
  63
  64    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, true);
  65    if (ret < 0) {
  66        error_report("Error binding guest notifier: %d", -ret);
  67        goto err_host_notifiers;
  68    }
  69
  70    vvc->vhost_dev.acked_features = vdev->guest_features;
  71    ret = vhost_dev_start(&vvc->vhost_dev, vdev);
  72    if (ret < 0) {
  73        error_report("Error starting vhost: %d", -ret);
  74        goto err_guest_notifiers;
  75    }
  76
  77    /*
  78     * guest_notifier_mask/pending not used yet, so just unmask
  79     * everything here.  virtio-pci will do the right thing by
  80     * enabling/disabling irqfd.
  81     */
  82    for (i = 0; i < vvc->vhost_dev.nvqs; i++) {
  83        vhost_virtqueue_mask(&vvc->vhost_dev, vdev, i, false);
  84    }
  85
  86    return 0;
  87
  88err_guest_notifiers:
  89    k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
  90err_host_notifiers:
  91    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
  92    return ret;
  93}
  94
  95void vhost_vsock_common_stop(VirtIODevice *vdev)
  96{
  97    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  98    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  99    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 100    int ret;
 101
 102    if (!k->set_guest_notifiers) {
 103        return;
 104    }
 105
 106    vhost_dev_stop(&vvc->vhost_dev, vdev);
 107
 108    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
 109    if (ret < 0) {
 110        error_report("vhost guest notifier cleanup failed: %d", ret);
 111        return;
 112    }
 113
 114    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
 115}
 116
 117
 118static void vhost_vsock_common_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 119{
 120    /* Do nothing */
 121}
 122
 123static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx,
 124                                            bool mask)
 125{
 126    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 127
 128    vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask);
 129}
 130
 131static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev,
 132                                               int idx)
 133{
 134    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 135
 136    return vhost_virtqueue_pending(&vvc->vhost_dev, idx);
 137}
 138
 139static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc)
 140{
 141    VirtQueueElement *elem;
 142    VirtQueue *vq = vvc->event_vq;
 143    struct virtio_vsock_event event = {
 144        .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET),
 145    };
 146
 147    elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
 148    if (!elem) {
 149        error_report("vhost-vsock missed transport reset event");
 150        return;
 151    }
 152
 153    if (elem->out_num) {
 154        error_report("invalid vhost-vsock event virtqueue element with "
 155                     "out buffers");
 156        goto out;
 157    }
 158
 159    if (iov_from_buf(elem->in_sg, elem->in_num, 0,
 160                     &event, sizeof(event)) != sizeof(event)) {
 161        error_report("vhost-vsock event virtqueue element is too short");
 162        goto out;
 163    }
 164
 165    virtqueue_push(vq, elem, sizeof(event));
 166    virtio_notify(VIRTIO_DEVICE(vvc), vq);
 167
 168out:
 169    g_free(elem);
 170}
 171
 172static void vhost_vsock_common_post_load_timer_cleanup(VHostVSockCommon *vvc)
 173{
 174    if (!vvc->post_load_timer) {
 175        return;
 176    }
 177
 178    timer_free(vvc->post_load_timer);
 179    vvc->post_load_timer = NULL;
 180}
 181
 182static void vhost_vsock_common_post_load_timer_cb(void *opaque)
 183{
 184    VHostVSockCommon *vvc = opaque;
 185
 186    vhost_vsock_common_post_load_timer_cleanup(vvc);
 187    vhost_vsock_common_send_transport_reset(vvc);
 188}
 189
 190int vhost_vsock_common_pre_save(void *opaque)
 191{
 192    VHostVSockCommon *vvc = opaque;
 193
 194    /*
 195     * At this point, backend must be stopped, otherwise
 196     * it might keep writing to memory.
 197     */
 198    assert(!vvc->vhost_dev.started);
 199
 200    return 0;
 201}
 202
 203int vhost_vsock_common_post_load(void *opaque, int version_id)
 204{
 205    VHostVSockCommon *vvc = opaque;
 206    VirtIODevice *vdev = VIRTIO_DEVICE(vvc);
 207
 208    if (virtio_queue_get_addr(vdev, 2)) {
 209        /*
 210         * Defer transport reset event to a vm clock timer so that virtqueue
 211         * changes happen after migration has completed.
 212         */
 213        assert(!vvc->post_load_timer);
 214        vvc->post_load_timer =
 215            timer_new_ns(QEMU_CLOCK_VIRTUAL,
 216                         vhost_vsock_common_post_load_timer_cb,
 217                         vvc);
 218        timer_mod(vvc->post_load_timer, 1);
 219    }
 220    return 0;
 221}
 222
 223void vhost_vsock_common_realize(VirtIODevice *vdev, const char *name)
 224{
 225    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 226
 227    virtio_init(vdev, name, VIRTIO_ID_VSOCK,
 228                sizeof(struct virtio_vsock_config));
 229
 230    /* Receive and transmit queues belong to vhost */
 231    vvc->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 232                                      vhost_vsock_common_handle_output);
 233    vvc->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 234                                       vhost_vsock_common_handle_output);
 235
 236    /* The event queue belongs to QEMU */
 237    vvc->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 238                                       vhost_vsock_common_handle_output);
 239
 240    vvc->vhost_dev.nvqs = ARRAY_SIZE(vvc->vhost_vqs);
 241    vvc->vhost_dev.vqs = vvc->vhost_vqs;
 242
 243    vvc->post_load_timer = NULL;
 244}
 245
 246void vhost_vsock_common_unrealize(VirtIODevice *vdev)
 247{
 248    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 249
 250    vhost_vsock_common_post_load_timer_cleanup(vvc);
 251
 252    virtio_delete_queue(vvc->recv_vq);
 253    virtio_delete_queue(vvc->trans_vq);
 254    virtio_delete_queue(vvc->event_vq);
 255    virtio_cleanup(vdev);
 256}
 257
 258static Property vhost_vsock_common_properties[] = {
 259    DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket,
 260                            ON_OFF_AUTO_AUTO),
 261    DEFINE_PROP_END_OF_LIST(),
 262};
 263
 264static void vhost_vsock_common_class_init(ObjectClass *klass, void *data)
 265{
 266    DeviceClass *dc = DEVICE_CLASS(klass);
 267    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 268
 269    device_class_set_props(dc, vhost_vsock_common_properties);
 270    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 271    vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask;
 272    vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending;
 273}
 274
 275static const TypeInfo vhost_vsock_common_info = {
 276    .name = TYPE_VHOST_VSOCK_COMMON,
 277    .parent = TYPE_VIRTIO_DEVICE,
 278    .instance_size = sizeof(VHostVSockCommon),
 279    .class_init = vhost_vsock_common_class_init,
 280    .abstract = true,
 281};
 282
 283static void vhost_vsock_common_register_types(void)
 284{
 285    type_register_static(&vhost_vsock_common_info);
 286}
 287
 288type_init(vhost_vsock_common_register_types)
 289