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 <sys/ioctl.h>
  15#include "qemu/osdep.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 void 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
 266static int vhost_vsock_post_load(void *opaque, int version_id)
 267{
 268    VHostVSock *vsock = opaque;
 269    VirtIODevice *vdev = VIRTIO_DEVICE(vsock);
 270
 271    if (virtio_queue_get_addr(vdev, 2)) {
 272        /* Defer transport reset event to a vm clock timer so that virtqueue
 273         * changes happen after migration has completed.
 274         */
 275        assert(!vsock->post_load_timer);
 276        vsock->post_load_timer =
 277            timer_new_ns(QEMU_CLOCK_VIRTUAL,
 278                         vhost_vsock_post_load_timer_cb,
 279                         vsock);
 280        timer_mod(vsock->post_load_timer, 1);
 281    }
 282    return 0;
 283}
 284
 285static const VMStateDescription vmstate_virtio_vhost_vsock = {
 286    .name = "virtio-vhost_vsock",
 287    .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
 288    .version_id = VHOST_VSOCK_SAVEVM_VERSION,
 289    .fields = (VMStateField[]) {
 290        VMSTATE_VIRTIO_DEVICE,
 291        VMSTATE_END_OF_LIST()
 292    },
 293    .pre_save = vhost_vsock_pre_save,
 294    .post_load = vhost_vsock_post_load,
 295};
 296
 297static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
 298{
 299    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 300    VHostVSock *vsock = VHOST_VSOCK(dev);
 301    int vhostfd;
 302    int ret;
 303
 304    /* Refuse to use reserved CID numbers */
 305    if (vsock->conf.guest_cid <= 2) {
 306        error_setg(errp, "guest-cid property must be greater than 2");
 307        return;
 308    }
 309
 310    if (vsock->conf.guest_cid > UINT32_MAX) {
 311        error_setg(errp, "guest-cid property must be a 32-bit number");
 312        return;
 313    }
 314
 315    if (vsock->conf.vhostfd) {
 316        vhostfd = monitor_fd_param(cur_mon, vsock->conf.vhostfd, errp);
 317        if (vhostfd == -1) {
 318            error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
 319            return;
 320        }
 321    } else {
 322        vhostfd = open("/dev/vhost-vsock", O_RDWR);
 323        if (vhostfd < 0) {
 324            error_setg_errno(errp, -errno,
 325                             "vhost-vsock: failed to open vhost device");
 326            return;
 327        }
 328    }
 329
 330    virtio_init(vdev, "vhost-vsock", VIRTIO_ID_VSOCK,
 331                sizeof(struct virtio_vsock_config));
 332
 333    /* Receive and transmit queues belong to vhost */
 334    virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output);
 335    virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output);
 336
 337    /* The event queue belongs to QEMU */
 338    vsock->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE,
 339                                       vhost_vsock_handle_output);
 340
 341    vsock->vhost_dev.nvqs = ARRAY_SIZE(vsock->vhost_vqs);
 342    vsock->vhost_dev.vqs = vsock->vhost_vqs;
 343    ret = vhost_dev_init(&vsock->vhost_dev, (void *)(uintptr_t)vhostfd,
 344                         VHOST_BACKEND_TYPE_KERNEL, 0);
 345    if (ret < 0) {
 346        error_setg_errno(errp, -ret, "vhost-vsock: vhost_dev_init failed");
 347        goto err_virtio;
 348    }
 349
 350    ret = vhost_vsock_set_guest_cid(vsock);
 351    if (ret < 0) {
 352        error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid");
 353        goto err_vhost_dev;
 354    }
 355
 356    vsock->post_load_timer = NULL;
 357    return;
 358
 359err_vhost_dev:
 360    vhost_dev_cleanup(&vsock->vhost_dev);
 361err_virtio:
 362    virtio_cleanup(vdev);
 363    close(vhostfd);
 364    return;
 365}
 366
 367static void vhost_vsock_device_unrealize(DeviceState *dev, Error **errp)
 368{
 369    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 370    VHostVSock *vsock = VHOST_VSOCK(dev);
 371
 372    vhost_vsock_post_load_timer_cleanup(vsock);
 373
 374    /* This will stop vhost backend if appropriate. */
 375    vhost_vsock_set_status(vdev, 0);
 376
 377    vhost_dev_cleanup(&vsock->vhost_dev);
 378    virtio_cleanup(vdev);
 379}
 380
 381static Property vhost_vsock_properties[] = {
 382    DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0),
 383    DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd),
 384    DEFINE_PROP_END_OF_LIST(),
 385};
 386
 387static void vhost_vsock_class_init(ObjectClass *klass, void *data)
 388{
 389    DeviceClass *dc = DEVICE_CLASS(klass);
 390    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 391
 392    dc->props = vhost_vsock_properties;
 393    dc->vmsd = &vmstate_virtio_vhost_vsock;
 394    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 395    vdc->realize = vhost_vsock_device_realize;
 396    vdc->unrealize = vhost_vsock_device_unrealize;
 397    vdc->get_features = vhost_vsock_get_features;
 398    vdc->get_config = vhost_vsock_get_config;
 399    vdc->set_status = vhost_vsock_set_status;
 400    vdc->guest_notifier_mask = vhost_vsock_guest_notifier_mask;
 401    vdc->guest_notifier_pending = vhost_vsock_guest_notifier_pending;
 402}
 403
 404static const TypeInfo vhost_vsock_info = {
 405    .name = TYPE_VHOST_VSOCK,
 406    .parent = TYPE_VIRTIO_DEVICE,
 407    .instance_size = sizeof(VHostVSock),
 408    .class_init = vhost_vsock_class_init,
 409};
 410
 411static void vhost_vsock_register_types(void)
 412{
 413    type_register_static(&vhost_vsock_info);
 414}
 415
 416type_init(vhost_vsock_register_types)
 417