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