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
  21int vhost_vsock_common_start(VirtIODevice *vdev)
  22{
  23    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  24    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  25    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  26    int ret;
  27    int i;
  28
  29    if (!k->set_guest_notifiers) {
  30        error_report("binding does not support guest notifiers");
  31        return -ENOSYS;
  32    }
  33
  34    ret = vhost_dev_enable_notifiers(&vvc->vhost_dev, vdev);
  35    if (ret < 0) {
  36        error_report("Error enabling host notifiers: %d", -ret);
  37        return ret;
  38    }
  39
  40    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, true);
  41    if (ret < 0) {
  42        error_report("Error binding guest notifier: %d", -ret);
  43        goto err_host_notifiers;
  44    }
  45
  46    vvc->vhost_dev.acked_features = vdev->guest_features;
  47    ret = vhost_dev_start(&vvc->vhost_dev, vdev);
  48    if (ret < 0) {
  49        error_report("Error starting vhost: %d", -ret);
  50        goto err_guest_notifiers;
  51    }
  52
  53    /*
  54     * guest_notifier_mask/pending not used yet, so just unmask
  55     * everything here.  virtio-pci will do the right thing by
  56     * enabling/disabling irqfd.
  57     */
  58    for (i = 0; i < vvc->vhost_dev.nvqs; i++) {
  59        vhost_virtqueue_mask(&vvc->vhost_dev, vdev, i, false);
  60    }
  61
  62    return 0;
  63
  64err_guest_notifiers:
  65    k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
  66err_host_notifiers:
  67    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
  68    return ret;
  69}
  70
  71void vhost_vsock_common_stop(VirtIODevice *vdev)
  72{
  73    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  74    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  75    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  76    int ret;
  77
  78    if (!k->set_guest_notifiers) {
  79        return;
  80    }
  81
  82    vhost_dev_stop(&vvc->vhost_dev, vdev);
  83
  84    ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false);
  85    if (ret < 0) {
  86        error_report("vhost guest notifier cleanup failed: %d", ret);
  87        return;
  88    }
  89
  90    vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev);
  91}
  92
  93
  94static void vhost_vsock_common_handle_output(VirtIODevice *vdev, VirtQueue *vq)
  95{
  96    /* Do nothing */
  97}
  98
  99static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx,
 100                                            bool mask)
 101{
 102    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 103
 104    vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask);
 105}
 106
 107static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev,
 108                                               int idx)
 109{
 110    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 111
 112    return vhost_virtqueue_pending(&vvc->vhost_dev, idx);
 113}
 114
 115static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc)
 116{
 117    VirtQueueElement *elem;
 118    VirtQueue *vq = vvc->event_vq;
 119    struct virtio_vsock_event event = {
 120        .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET),
 121    };
 122
 123    elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
 124    if (!elem) {
 125        error_report("vhost-vsock missed transport reset event");
 126        return;
 127    }
 128
 129    if (elem->out_num) {
 130        error_report("invalid vhost-vsock event virtqueue element with "
 131                     "out buffers");
 132        goto out;
 133    }
 134
 135    if (iov_from_buf(elem->in_sg, elem->in_num, 0,
 136                     &event, sizeof(event)) != sizeof(event)) {
 137        error_report("vhost-vsock event virtqueue element is too short");
 138        goto out;
 139    }
 140
 141    virtqueue_push(vq, elem, sizeof(event));
 142    virtio_notify(VIRTIO_DEVICE(vvc), vq);
 143
 144out:
 145    g_free(elem);
 146}
 147
 148static void vhost_vsock_common_post_load_timer_cleanup(VHostVSockCommon *vvc)
 149{
 150    if (!vvc->post_load_timer) {
 151        return;
 152    }
 153
 154    timer_free(vvc->post_load_timer);
 155    vvc->post_load_timer = NULL;
 156}
 157
 158static void vhost_vsock_common_post_load_timer_cb(void *opaque)
 159{
 160    VHostVSockCommon *vvc = opaque;
 161
 162    vhost_vsock_common_post_load_timer_cleanup(vvc);
 163    vhost_vsock_common_send_transport_reset(vvc);
 164}
 165
 166int vhost_vsock_common_pre_save(void *opaque)
 167{
 168    VHostVSockCommon *vvc = opaque;
 169
 170    /*
 171     * At this point, backend must be stopped, otherwise
 172     * it might keep writing to memory.
 173     */
 174    assert(!vvc->vhost_dev.started);
 175
 176    return 0;
 177}
 178
 179int vhost_vsock_common_post_load(void *opaque, int version_id)
 180{
 181    VHostVSockCommon *vvc = opaque;
 182    VirtIODevice *vdev = VIRTIO_DEVICE(vvc);
 183
 184    if (virtio_queue_get_addr(vdev, 2)) {
 185        /*
 186         * Defer transport reset event to a vm clock timer so that virtqueue
 187         * changes happen after migration has completed.
 188         */
 189        assert(!vvc->post_load_timer);
 190        vvc->post_load_timer =
 191            timer_new_ns(QEMU_CLOCK_VIRTUAL,
 192                         vhost_vsock_common_post_load_timer_cb,
 193                         vvc);
 194        timer_mod(vvc->post_load_timer, 1);
 195    }
 196    return 0;
 197}
 198
 199void vhost_vsock_common_realize(VirtIODevice *vdev, const char *name)
 200{
 201    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 202
 203    virtio_init(vdev, name, VIRTIO_ID_VSOCK,
 204                sizeof(struct virtio_vsock_config));
 205
 206    /* Receive and transmit queues belong to vhost */
 207    vvc->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 208                                      vhost_vsock_common_handle_output);
 209    vvc->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 210                                       vhost_vsock_common_handle_output);
 211
 212    /* The event queue belongs to QEMU */
 213    vvc->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 214                                       vhost_vsock_common_handle_output);
 215
 216    vvc->vhost_dev.nvqs = ARRAY_SIZE(vvc->vhost_vqs);
 217    vvc->vhost_dev.vqs = vvc->vhost_vqs;
 218
 219    vvc->post_load_timer = NULL;
 220}
 221
 222void vhost_vsock_common_unrealize(VirtIODevice *vdev)
 223{
 224    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 225
 226    vhost_vsock_common_post_load_timer_cleanup(vvc);
 227
 228    virtio_delete_queue(vvc->recv_vq);
 229    virtio_delete_queue(vvc->trans_vq);
 230    virtio_delete_queue(vvc->event_vq);
 231    virtio_cleanup(vdev);
 232}
 233
 234static void vhost_vsock_common_class_init(ObjectClass *klass, void *data)
 235{
 236    DeviceClass *dc = DEVICE_CLASS(klass);
 237    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 238
 239    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 240    vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask;
 241    vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending;
 242}
 243
 244static const TypeInfo vhost_vsock_common_info = {
 245    .name = TYPE_VHOST_VSOCK_COMMON,
 246    .parent = TYPE_VIRTIO_DEVICE,
 247    .instance_size = sizeof(VHostVSockCommon),
 248    .class_init = vhost_vsock_common_class_init,
 249    .abstract = true,
 250};
 251
 252static void vhost_vsock_common_register_types(void)
 253{
 254    type_register_static(&vhost_vsock_common_info);
 255}
 256
 257type_init(vhost_vsock_common_register_types)
 258