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 "standard-headers/linux/virtio_vsock.h"
  16#include "qapi/error.h"
  17#include "hw/virtio/virtio-access.h"
  18#include "qemu/error-report.h"
  19#include "qemu/sockets.h"
  20#include "hw/qdev-properties.h"
  21#include "hw/virtio/vhost-vsock.h"
  22#include "monitor/monitor.h"
  23
  24const int feature_bits[] = {
  25    VIRTIO_VSOCK_F_SEQPACKET,
  26    VHOST_INVALID_FEATURE_BIT
  27};
  28
  29static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
  30{
  31    VHostVSock *vsock = VHOST_VSOCK(vdev);
  32    struct virtio_vsock_config vsockcfg = {};
  33
  34    virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid);
  35    memcpy(config, &vsockcfg, sizeof(vsockcfg));
  36}
  37
  38static int vhost_vsock_set_guest_cid(VirtIODevice *vdev)
  39{
  40    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  41    VHostVSock *vsock = VHOST_VSOCK(vdev);
  42    const VhostOps *vhost_ops = vvc->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(&vvc->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(VirtIODevice *vdev, int start)
  58{
  59    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  60    const VhostOps *vhost_ops = vvc->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(&vvc->vhost_dev, start);
  68    if (ret < 0) {
  69        return -errno;
  70    }
  71    return 0;
  72}
  73
  74
  75static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
  76{
  77    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
  78    bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
  79    int ret;
  80
  81    if (!vdev->vm_running) {
  82        should_start = false;
  83    }
  84
  85    if (vvc->vhost_dev.started == should_start) {
  86        return;
  87    }
  88
  89    if (should_start) {
  90        ret = vhost_vsock_common_start(vdev);
  91        if (ret < 0) {
  92            return;
  93        }
  94
  95        ret = vhost_vsock_set_running(vdev, 1);
  96        if (ret < 0) {
  97            vhost_vsock_common_stop(vdev);
  98            error_report("Error starting vhost vsock: %d", -ret);
  99            return;
 100        }
 101    } else {
 102        ret = vhost_vsock_set_running(vdev, 0);
 103        if (ret < 0) {
 104            error_report("vhost vsock set running failed: %d", ret);
 105            return;
 106        }
 107
 108        vhost_vsock_common_stop(vdev);
 109    }
 110}
 111
 112static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
 113                                         uint64_t requested_features,
 114                                         Error **errp)
 115{
 116    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 117
 118    virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET);
 119    return vhost_get_features(&vvc->vhost_dev, feature_bits,
 120                                requested_features);
 121}
 122
 123static const VMStateDescription vmstate_virtio_vhost_vsock = {
 124    .name = "virtio-vhost_vsock",
 125    .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
 126    .version_id = VHOST_VSOCK_SAVEVM_VERSION,
 127    .fields = (VMStateField[]) {
 128        VMSTATE_VIRTIO_DEVICE,
 129        VMSTATE_END_OF_LIST()
 130    },
 131    .pre_save = vhost_vsock_common_pre_save,
 132    .post_load = vhost_vsock_common_post_load,
 133};
 134
 135static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
 136{
 137    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
 138    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 139    VHostVSock *vsock = VHOST_VSOCK(dev);
 140    int vhostfd;
 141    int ret;
 142
 143    /* Refuse to use reserved CID numbers */
 144    if (vsock->conf.guest_cid <= 2) {
 145        error_setg(errp, "guest-cid property must be greater than 2");
 146        return;
 147    }
 148
 149    if (vsock->conf.guest_cid > UINT32_MAX) {
 150        error_setg(errp, "guest-cid property must be a 32-bit number");
 151        return;
 152    }
 153
 154    if (vsock->conf.vhostfd) {
 155        vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
 156        if (vhostfd == -1) {
 157            error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
 158            return;
 159        }
 160
 161        ret = qemu_try_set_nonblock(vhostfd);
 162        if (ret < 0) {
 163            error_setg_errno(errp, -ret,
 164                             "vhost-vsock: unable to set non-blocking mode");
 165            return;
 166        }
 167    } else {
 168        vhostfd = open("/dev/vhost-vsock", O_RDWR);
 169        if (vhostfd < 0) {
 170            error_setg_errno(errp, errno,
 171                             "vhost-vsock: failed to open vhost device");
 172            return;
 173        }
 174
 175        qemu_set_nonblock(vhostfd);
 176    }
 177
 178    vhost_vsock_common_realize(vdev, "vhost-vsock");
 179
 180    ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
 181                         VHOST_BACKEND_TYPE_KERNEL, 0, errp);
 182    if (ret < 0) {
 183        goto err_virtio;
 184    }
 185
 186    ret = vhost_vsock_set_guest_cid(vdev);
 187    if (ret < 0) {
 188        error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid");
 189        goto err_vhost_dev;
 190    }
 191
 192    return;
 193
 194err_vhost_dev:
 195    vhost_dev_cleanup(&vvc->vhost_dev);
 196    /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */
 197    vhostfd = -1;
 198err_virtio:
 199    vhost_vsock_common_unrealize(vdev);
 200    if (vhostfd >= 0) {
 201        close(vhostfd);
 202    }
 203    return;
 204}
 205
 206static void vhost_vsock_device_unrealize(DeviceState *dev)
 207{
 208    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
 209    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 210
 211    /* This will stop vhost backend if appropriate. */
 212    vhost_vsock_set_status(vdev, 0);
 213
 214    vhost_dev_cleanup(&vvc->vhost_dev);
 215    vhost_vsock_common_unrealize(vdev);
 216}
 217
 218static Property vhost_vsock_properties[] = {
 219    DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0),
 220    DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd),
 221    DEFINE_PROP_END_OF_LIST(),
 222};
 223
 224static void vhost_vsock_class_init(ObjectClass *klass, void *data)
 225{
 226    DeviceClass *dc = DEVICE_CLASS(klass);
 227    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 228
 229    device_class_set_props(dc, vhost_vsock_properties);
 230    dc->vmsd = &vmstate_virtio_vhost_vsock;
 231    vdc->realize = vhost_vsock_device_realize;
 232    vdc->unrealize = vhost_vsock_device_unrealize;
 233    vdc->get_features = vhost_vsock_get_features;
 234    vdc->get_config = vhost_vsock_get_config;
 235    vdc->set_status = vhost_vsock_set_status;
 236}
 237
 238static const TypeInfo vhost_vsock_info = {
 239    .name = TYPE_VHOST_VSOCK,
 240    .parent = TYPE_VHOST_VSOCK_COMMON,
 241    .instance_size = sizeof(VHostVSock),
 242    .class_init = vhost_vsock_class_init,
 243};
 244
 245static void vhost_vsock_register_types(void)
 246{
 247    type_register_static(&vhost_vsock_info);
 248}
 249
 250type_init(vhost_vsock_register_types)
 251