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