qemu/backends/vhost-user.c
<<
>>
Prefs
   1/*
   2 * QEMU vhost-user backend
   3 *
   4 * Copyright (C) 2018 Red Hat Inc
   5 *
   6 * Authors:
   7 *  Marc-André Lureau <marcandre.lureau@redhat.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13
  14#include "qemu/osdep.h"
  15#include "qapi/error.h"
  16#include "qapi/qmp/qerror.h"
  17#include "qemu/error-report.h"
  18#include "qom/object_interfaces.h"
  19#include "sysemu/vhost-user-backend.h"
  20#include "sysemu/kvm.h"
  21#include "io/channel-command.h"
  22#include "hw/virtio/virtio-bus.h"
  23
  24static bool
  25ioeventfd_enabled(void)
  26{
  27    return kvm_enabled() && kvm_eventfds_enabled();
  28}
  29
  30int
  31vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
  32                            unsigned nvqs, Error **errp)
  33{
  34    int ret;
  35
  36    assert(!b->vdev && vdev);
  37
  38    if (!ioeventfd_enabled()) {
  39        error_setg(errp, "vhost initialization failed: requires kvm");
  40        return -1;
  41    }
  42
  43    if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
  44        return -1;
  45    }
  46
  47    b->vdev = vdev;
  48    b->dev.nvqs = nvqs;
  49    b->dev.vqs = g_new0(struct vhost_virtqueue, nvqs);
  50
  51    ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
  52                         errp);
  53    if (ret < 0) {
  54        return -1;
  55    }
  56
  57    return 0;
  58}
  59
  60void
  61vhost_user_backend_start(VhostUserBackend *b)
  62{
  63    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
  64    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  65    int ret, i ;
  66
  67    if (b->started) {
  68        return;
  69    }
  70
  71    if (!k->set_guest_notifiers) {
  72        error_report("binding does not support guest notifiers");
  73        return;
  74    }
  75
  76    ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
  77    if (ret < 0) {
  78        return;
  79    }
  80
  81    ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
  82    if (ret < 0) {
  83        error_report("Error binding guest notifier");
  84        goto err_host_notifiers;
  85    }
  86
  87    b->dev.acked_features = b->vdev->guest_features;
  88    ret = vhost_dev_start(&b->dev, b->vdev);
  89    if (ret < 0) {
  90        error_report("Error start vhost dev");
  91        goto err_guest_notifiers;
  92    }
  93
  94    /* guest_notifier_mask/pending not used yet, so just unmask
  95     * everything here.  virtio-pci will do the right thing by
  96     * enabling/disabling irqfd.
  97     */
  98    for (i = 0; i < b->dev.nvqs; i++) {
  99        vhost_virtqueue_mask(&b->dev, b->vdev,
 100                             b->dev.vq_index + i, false);
 101    }
 102
 103    b->started = true;
 104    return;
 105
 106err_guest_notifiers:
 107    k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
 108err_host_notifiers:
 109    vhost_dev_disable_notifiers(&b->dev, b->vdev);
 110}
 111
 112void
 113vhost_user_backend_stop(VhostUserBackend *b)
 114{
 115    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
 116    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 117    int ret = 0;
 118
 119    if (!b->started) {
 120        return;
 121    }
 122
 123    vhost_dev_stop(&b->dev, b->vdev);
 124
 125    if (k->set_guest_notifiers) {
 126        ret = k->set_guest_notifiers(qbus->parent,
 127                                     b->dev.nvqs, false);
 128        if (ret < 0) {
 129            error_report("vhost guest notifier cleanup failed: %d", ret);
 130        }
 131    }
 132    assert(ret >= 0);
 133
 134    vhost_dev_disable_notifiers(&b->dev, b->vdev);
 135    b->started = false;
 136}
 137
 138static void set_chardev(Object *obj, const char *value, Error **errp)
 139{
 140    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
 141    Chardev *chr;
 142
 143    if (b->completed) {
 144        error_setg(errp, QERR_PERMISSION_DENIED);
 145        return;
 146    }
 147
 148    g_free(b->chr_name);
 149    b->chr_name = g_strdup(value);
 150
 151    chr = qemu_chr_find(b->chr_name);
 152    if (chr == NULL) {
 153        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
 154                  "Chardev '%s' not found", b->chr_name);
 155        return;
 156    }
 157
 158    if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
 159        return;
 160    }
 161
 162    b->completed = true;
 163    /* could call vhost_dev_init() so early message can be exchanged */
 164}
 165
 166static char *get_chardev(Object *obj, Error **errp)
 167{
 168    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
 169    Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
 170
 171    if (chr && chr->label) {
 172        return g_strdup(chr->label);
 173    }
 174
 175    return NULL;
 176}
 177
 178static void vhost_user_backend_class_init(ObjectClass *oc, void *data)
 179{
 180    object_class_property_add_str(oc, "chardev", get_chardev, set_chardev);
 181}
 182
 183static void vhost_user_backend_finalize(Object *obj)
 184{
 185    VhostUserBackend *b = VHOST_USER_BACKEND(obj);
 186
 187    g_free(b->dev.vqs);
 188    g_free(b->chr_name);
 189
 190    vhost_user_cleanup(&b->vhost_user);
 191    qemu_chr_fe_deinit(&b->chr, true);
 192}
 193
 194static const TypeInfo vhost_user_backend_info = {
 195    .name = TYPE_VHOST_USER_BACKEND,
 196    .parent = TYPE_OBJECT,
 197    .instance_size = sizeof(VhostUserBackend),
 198    .class_init = vhost_user_backend_class_init,
 199    .instance_finalize = vhost_user_backend_finalize,
 200};
 201
 202static void register_types(void)
 203{
 204    type_register_static(&vhost_user_backend_info);
 205}
 206
 207type_init(register_types);
 208