qemu/hw/virtio/vhost-vsock.c
<<
>>
Prefs
   1/*
   2 * Virtio vsock device
   3 *
   4 * Copyright 2015 Red Hat, Inc.
   5 *
   6 * Authors:
   7 *  Stefan Hajnoczi <stefanha@redhat.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or
  10 * (at your option) any later version.  See the COPYING file in the
  11 * top-level directory.
  12 */
  13
  14#include "qemu/osdep.h"
  15#include <sys/ioctl.h>
  16#include "standard-headers/linux/virtio_vsock.h"
  17#include "qapi/error.h"
  18#include "hw/virtio/virtio-bus.h"
  19#include "hw/virtio/virtio-access.h"
  20#include "qemu/error-report.h"
  21#include "hw/virtio/vhost-vsock.h"
  22#include "qemu/iov.h"
  23#include "monitor/monitor.h"
  24
  25enum {
  26    VHOST_VSOCK_SAVEVM_VERSION = 0,
  27
  28    VHOST_VSOCK_QUEUE_SIZE = 128,
  29};
  30
  31static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
  32{
  33    VHostVSock *vsock = VHOST_VSOCK(vdev);
  34    struct virtio_vsock_config vsockcfg = {};
  35
  36    virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid);
  37    memcpy(config, &vsockcfg, sizeof(vsockcfg));
  38}
  39
  40static int vhost_vsock_set_guest_cid(VHostVSock *vsock)
  41{
  42    const VhostOps *vhost_ops = vsock->vhost_dev.vhost_ops;
  43    int ret;
  44
  45    if (!vhost_ops->vhost_vsock_set_guest_cid) {
  46        return -ENOSYS;
  47    }
  48
  49    ret = vhost_ops->vhost_vsock_set_guest_cid(&vsock->vhost_dev,
  50                                               vsock->conf.guest_cid);
  51    if (ret < 0) {
  52        return -errno;
  53    }
  54    return 0;
  55}
  56
  57static int vhost_vsock_set_running(VHostVSock *vsock, int start)
  58{
  59    const VhostOps *vhost_ops = vsock->vhost_dev.vhost_ops;
  60    int ret;
  61
  62    if (!vhost_ops->vhost_vsock_set_running) {
  63        return -ENOSYS;
  64    }
  65
  66    ret = vhost_ops->vhost_vsock_set_running(&vsock->vhost_dev, start);
  67    if (ret < 0) {
  68        return -errno;
  69    }
  70    return 0;
  71}
  72
  73static void vhost_vsock_start(VirtIODevice *vdev)
  74{
  75    VHostVSock *vsock = VHOST_VSOCK(vdev);
  76    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  77    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  78    int ret;
  79    int i;
  80
  81    if (!k->set_guest_notifiers) {
  82        error_report("binding does not support guest notifiers");
  83        return;
  84    }
  85
  86    ret = vhost_dev_enable_notifiers(&vsock->vhost_dev, vdev);
  87    if (ret < 0) {
  88        error_report("Error enabling host notifiers: %d", -ret);
  89        return;
  90    }
  91
  92    ret = k->set_guest_notifiers(qbus->parent, vsock->vhost_dev.nvqs, true);
  93    if (ret < 0) {
  94        error_report("Error binding guest notifier: %d", -ret);
  95        goto err_host_notifiers;
  96    }
  97
  98    vsock->vhost_dev.acked_features = vdev->guest_features;
  99    ret = vhost_dev_start(&vsock->vhost_dev, vdev);
 100    if (ret < 0) {
 101        error_report("Error starting vhost: %d", -ret);
 102        goto err_guest_notifiers;
 103    }
 104
 105    ret = vhost_vsock_set_running(vsock, 1);
 106    if (ret < 0) {
 107        error_report("Error starting vhost vsock: %d", -ret);
 108        goto err_dev_start;
 109    }
 110
 111    /* guest_notifier_mask/pending not used yet, so just unmask
 112     * everything here.  virtio-pci will do the right thing by
 113     * enabling/disabling irqfd.
 114     */
 115    for (i = 0; i < vsock->vhost_dev.nvqs; i++) {
 116        vhost_virtqueue_mask(&vsock->vhost_dev, vdev, i, false);
 117    }
 118
 119    return;
 120
 121err_dev_start:
 122    vhost_dev_stop(&vsock->vhost_dev, vdev);
 123err_guest_notifiers:
 124    k->set_guest_notifiers(qbus->parent, vsock->vhost_dev.nvqs, false);
 125err_host_notifiers:
 126    vhost_dev_disable_notifiers(&vsock->vhost_dev, vdev);
 127}
 128
 129static void vhost_vsock_stop(VirtIODevice *vdev)
 130{
 131    VHostVSock *vsock = VHOST_VSOCK(vdev);
 132    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 133    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 134    int ret;
 135
 136    if (!k->set_guest_notifiers) {
 137        return;
 138    }
 139
 140    ret = vhost_vsock_set_running(vsock, 0);
 141    if (ret < 0) {
 142        error_report("vhost vsock set running failed: %d", ret);
 143        return;
 144    }
 145
 146    vhost_dev_stop(&vsock->vhost_dev, vdev);
 147
 148    ret = k->set_guest_notifiers(qbus->parent, vsock->vhost_dev.nvqs, false);
 149    if (ret < 0) {
 150        error_report("vhost guest notifier cleanup failed: %d", ret);
 151        return;
 152    }
 153
 154    vhost_dev_disable_notifiers(&vsock->vhost_dev, vdev);
 155}
 156
 157static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
 158{
 159    VHostVSock *vsock = VHOST_VSOCK(vdev);
 160    bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
 161
 162    if (!vdev->vm_running) {
 163        should_start = false;
 164    }
 165
 166    if (vsock->vhost_dev.started == should_start) {
 167        return;
 168    }
 169
 170    if (should_start) {
 171        vhost_vsock_start(vdev);
 172    } else {
 173        vhost_vsock_stop(vdev);
 174    }
 175}
 176
 177static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
 178                                         uint64_t requested_features,
 179                                         Error **errp)
 180{
 181    /* No feature bits used yet */
 182    return requested_features;
 183}
 184
 185static void vhost_vsock_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 186{
 187    /* Do nothing */
 188}
 189
 190static void vhost_vsock_guest_notifier_mask(VirtIODevice *vdev, int idx,
 191                                            bool mask)
 192{
 193    VHostVSock *vsock = VHOST_VSOCK(vdev);
 194
 195    vhost_virtqueue_mask(&vsock->vhost_dev, vdev, idx, mask);
 196}
 197
 198static bool vhost_vsock_guest_notifier_pending(VirtIODevice *vdev, int idx)
 199{
 200    VHostVSock *vsock = VHOST_VSOCK(vdev);
 201
 202    return vhost_virtqueue_pending(&vsock->vhost_dev, idx);
 203}
 204
 205static void vhost_vsock_send_transport_reset(VHostVSock *vsock)
 206{
 207    VirtQueueElement *elem;
 208    VirtQueue *vq = vsock->event_vq;
 209    struct virtio_vsock_event event = {
 210        .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET),
 211    };
 212
 213    elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
 214    if (!elem) {
 215        error_report("vhost-vsock missed transport reset event");
 216        return;
 217    }
 218
 219    if (elem->out_num) {
 220        error_report("invalid vhost-vsock event virtqueue element with "
 221                     "out buffers");
 222        goto out;
 223    }
 224
 225    if (iov_from_buf(elem->in_sg, elem->in_num, 0,
 226                     &event, sizeof(event)) != sizeof(event)) {
 227        error_report("vhost-vsock event virtqueue element is too short");
 228        goto out;
 229    }
 230
 231    virtqueue_push(vq, elem, sizeof(event));
 232    virtio_notify(VIRTIO_DEVICE(vsock), vq);
 233
 234out:
 235    g_free(elem);
 236}
 237
 238static void vhost_vsock_post_load_timer_cleanup(VHostVSock *vsock)
 239{
 240    if (!vsock->post_load_timer) {
 241        return;
 242    }
 243
 244    timer_del(vsock->post_load_timer);
 245    timer_free(vsock->post_load_timer);
 246    vsock->post_load_timer = NULL;
 247}
 248
 249static void vhost_vsock_post_load_timer_cb(void *opaque)
 250{
 251    VHostVSock *vsock = opaque;
 252
 253    vhost_vsock_post_load_timer_cleanup(vsock);
 254    vhost_vsock_send_transport_reset(vsock);
 255}
 256
 257static int vhost_vsock_pre_save(void *opaque)
 258{
 259    VHostVSock *vsock = opaque;
 260
 261    /* At this point, backend must be stopped, otherwise
 262     * it might keep writing to memory. */
 263    assert(!vsock->vhost_dev.started);
 264
 265    return 0;
 266}
 267
 268static int vhost_vsock_post_load(void *opaque, int version_id)
 269{
 270    VHostVSock *vsock = opaque;
 271    VirtIODevice *vdev = VIRTIO_DEVICE(vsock);
 272
 273    if (virtio_queue_get_addr(vdev, 2)) {
 274        /* Defer transport reset event to a vm clock timer so that virtqueue
 275         * changes happen after migration has completed.
 276         */
 277        assert(!vsock->post_load_timer);
 278        vsock->post_load_timer =
 279            timer_new_ns(QEMU_CLOCK_VIRTUAL,
 280                         vhost_vsock_post_load_timer_cb,
 281                         vsock);
 282        timer_mod(vsock->post_load_timer, 1);
 283    }
 284    return 0;
 285}
 286
 287static const VMStateDescription vmstate_virtio_vhost_vsock = {
 288    .name = "virtio-vhost_vsock",
 289    .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
 290    .version_id = VHOST_VSOCK_SAVEVM_VERSION,
 291    .fields = (VMStateField[]) {
 292        VMSTATE_VIRTIO_DEVICE,
 293        VMSTATE_END_OF_LIST()
 294    },
 295    .pre_save = vhost_vsock_pre_save,
 296    .post_load = vhost_vsock_post_load,
 297};
 298
 299static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
 300{
 301    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 302    VHostVSock *vsock = VHOST_VSOCK(dev);
 303    int vhostfd;
 304    int ret;
 305
 306    /* Refuse to use reserved CID numbers */
 307    if (vsock->conf.guest_cid <= 2) {
 308        error_setg(errp, "guest-cid property must be greater than 2");
 309        return;
 310    }
 311
 312    if (vsock->conf.guest_cid > UINT32_MAX) {
 313        error_setg(errp, "guest-cid property must be a 32-bit number");
 314        return;
 315    }
 316
 317    if (vsock->conf.vhostfd) {
 318        vhostfd = monitor_fd_param(cur_mon, vsock->conf.vhostfd, errp);
 319        if (vhostfd == -1) {
 320            error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
 321            return;
 322        }
 323    } else {
 324        vhostfd = open("/dev/vhost-vsock", O_RDWR);
 325        if (vhostfd < 0) {
 326            error_setg_errno(errp, -errno,
 327                             "vhost-vsock: failed to open vhost device");
 328            return;
 329        }
 330    }
 331
 332    virtio_init(vdev, "vhost-vsock", VIRTIO_ID_VSOCK,
 333                sizeof(struct virtio_vsock_config));
 334
 335    /* Receive and transmit queues belong to vhost */
 336    virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output);
 337    virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output);
 338
 339    /* The event queue belongs to QEMU */
 340    vsock->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 341                                       vhost_vsock_handle_output);
 342
 343    vsock->vhost_dev.nvqs = ARRAY_SIZE(vsock->vhost_vqs);
 344    vsock->vhost_dev.vqs = vsock->vhost_vqs;
 345    ret = vhost_dev_init(&vsock->vhost_dev, (void *)(uintptr_t)vhostfd,
 346                         VHOST_BACKEND_TYPE_KERNEL, 0);
 347    if (ret < 0) {
 348        error_setg_errno(errp, -ret, "vhost-vsock: vhost_dev_init failed");
 349        goto err_virtio;
 350    }
 351
 352    ret = vhost_vsock_set_guest_cid(vsock);
 353    if (ret < 0) {
 354        error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid");
 355        goto err_vhost_dev;
 356    }
 357
 358    vsock->post_load_timer = NULL;
 359    return;
 360
 361err_vhost_dev:
 362    vhost_dev_cleanup(&vsock->vhost_dev);
 363err_virtio:
 364    virtio_cleanup(vdev);
 365    close(vhostfd);
 366    return;
 367}
 368
 369static void vhost_vsock_device_unrealize(DeviceState *dev, Error **errp)
 370{
 371    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 372    VHostVSock *vsock = VHOST_VSOCK(dev);
 373
 374    vhost_vsock_post_load_timer_cleanup(vsock);
 375
 376    /* This will stop vhost backend if appropriate. */
 377    vhost_vsock_set_status(vdev, 0);
 378
 379    vhost_dev_cleanup(&vsock->vhost_dev);
 380    virtio_cleanup(vdev);
 381}
 382
 383static Property vhost_vsock_properties[] = {
 384    DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0),
 385    DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd),
 386    DEFINE_PROP_END_OF_LIST(),
 387};
 388
 389static void vhost_vsock_class_init(ObjectClass *klass, void *data)
 390{
 391    DeviceClass *dc = DEVICE_CLASS(klass);
 392    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 393
 394    dc->props = vhost_vsock_properties;
 395    dc->vmsd = &vmstate_virtio_vhost_vsock;
 396    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 397    vdc->realize = vhost_vsock_device_realize;
 398    vdc->unrealize = vhost_vsock_device_unrealize;
 399    vdc->get_features = vhost_vsock_get_features;
 400    vdc->get_config = vhost_vsock_get_config;
 401    vdc->set_status = vhost_vsock_set_status;
 402    vdc->guest_notifier_mask = vhost_vsock_guest_notifier_mask;
 403    vdc->guest_notifier_pending = vhost_vsock_guest_notifier_pending;
 404}
 405
 406static const TypeInfo vhost_vsock_info = {
 407    .name = TYPE_VHOST_VSOCK,
 408    .parent = TYPE_VIRTIO_DEVICE,
 409    .instance_size = sizeof(VHostVSock),
 410    .class_init = vhost_vsock_class_init,
 411};
 412
 413static void vhost_vsock_register_types(void)
 414{
 415    type_register_static(&vhost_vsock_info);
 416}
 417
 418type_init(vhost_vsock_register_types)
 419