qemu/hw/virtio/vhost-user-fs.c
<<
>>
Prefs
   1/*
   2 * Vhost-user filesystem virtio device
   3 *
   4 * Copyright 2018-2019 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_fs.h"
  17#include "qapi/error.h"
  18#include "hw/qdev-properties.h"
  19#include "hw/virtio/virtio-bus.h"
  20#include "hw/virtio/virtio-access.h"
  21#include "qemu/error-report.h"
  22#include "hw/virtio/vhost-user-fs.h"
  23#include "monitor/monitor.h"
  24
  25static void vuf_get_config(VirtIODevice *vdev, uint8_t *config)
  26{
  27    VHostUserFS *fs = VHOST_USER_FS(vdev);
  28    struct virtio_fs_config fscfg = {};
  29
  30    memcpy((char *)fscfg.tag, fs->conf.tag,
  31           MIN(strlen(fs->conf.tag) + 1, sizeof(fscfg.tag)));
  32
  33    virtio_stl_p(vdev, &fscfg.num_request_queues, fs->conf.num_request_queues);
  34
  35    memcpy(config, &fscfg, sizeof(fscfg));
  36}
  37
  38static void vuf_start(VirtIODevice *vdev)
  39{
  40    VHostUserFS *fs = VHOST_USER_FS(vdev);
  41    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  42    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  43    int ret;
  44    int i;
  45
  46    if (!k->set_guest_notifiers) {
  47        error_report("binding does not support guest notifiers");
  48        return;
  49    }
  50
  51    ret = vhost_dev_enable_notifiers(&fs->vhost_dev, vdev);
  52    if (ret < 0) {
  53        error_report("Error enabling host notifiers: %d", -ret);
  54        return;
  55    }
  56
  57    ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, true);
  58    if (ret < 0) {
  59        error_report("Error binding guest notifier: %d", -ret);
  60        goto err_host_notifiers;
  61    }
  62
  63    fs->vhost_dev.acked_features = vdev->guest_features;
  64    ret = vhost_dev_start(&fs->vhost_dev, vdev);
  65    if (ret < 0) {
  66        error_report("Error starting vhost: %d", -ret);
  67        goto err_guest_notifiers;
  68    }
  69
  70    /*
  71     * guest_notifier_mask/pending not used yet, so just unmask
  72     * everything here.  virtio-pci will do the right thing by
  73     * enabling/disabling irqfd.
  74     */
  75    for (i = 0; i < fs->vhost_dev.nvqs; i++) {
  76        vhost_virtqueue_mask(&fs->vhost_dev, vdev, i, false);
  77    }
  78
  79    return;
  80
  81err_guest_notifiers:
  82    k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
  83err_host_notifiers:
  84    vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
  85}
  86
  87static void vuf_stop(VirtIODevice *vdev)
  88{
  89    VHostUserFS *fs = VHOST_USER_FS(vdev);
  90    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  91    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  92    int ret;
  93
  94    if (!k->set_guest_notifiers) {
  95        return;
  96    }
  97
  98    vhost_dev_stop(&fs->vhost_dev, vdev);
  99
 100    ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false);
 101    if (ret < 0) {
 102        error_report("vhost guest notifier cleanup failed: %d", ret);
 103        return;
 104    }
 105
 106    vhost_dev_disable_notifiers(&fs->vhost_dev, vdev);
 107}
 108
 109static void vuf_set_status(VirtIODevice *vdev, uint8_t status)
 110{
 111    VHostUserFS *fs = VHOST_USER_FS(vdev);
 112    bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
 113
 114    if (!vdev->vm_running) {
 115        should_start = false;
 116    }
 117
 118    if (fs->vhost_dev.started == should_start) {
 119        return;
 120    }
 121
 122    if (should_start) {
 123        vuf_start(vdev);
 124    } else {
 125        vuf_stop(vdev);
 126    }
 127}
 128
 129static uint64_t vuf_get_features(VirtIODevice *vdev,
 130                                      uint64_t requested_features,
 131                                      Error **errp)
 132{
 133    /* No feature bits used yet */
 134    return requested_features;
 135}
 136
 137static void vuf_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 138{
 139    /*
 140     * Not normally called; it's the daemon that handles the queue;
 141     * however virtio's cleanup path can call this.
 142     */
 143}
 144
 145static void vuf_guest_notifier_mask(VirtIODevice *vdev, int idx,
 146                                            bool mask)
 147{
 148    VHostUserFS *fs = VHOST_USER_FS(vdev);
 149
 150    vhost_virtqueue_mask(&fs->vhost_dev, vdev, idx, mask);
 151}
 152
 153static bool vuf_guest_notifier_pending(VirtIODevice *vdev, int idx)
 154{
 155    VHostUserFS *fs = VHOST_USER_FS(vdev);
 156
 157    return vhost_virtqueue_pending(&fs->vhost_dev, idx);
 158}
 159
 160static void vuf_device_realize(DeviceState *dev, Error **errp)
 161{
 162    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 163    VHostUserFS *fs = VHOST_USER_FS(dev);
 164    unsigned int i;
 165    size_t len;
 166    int ret;
 167
 168    if (!fs->conf.chardev.chr) {
 169        error_setg(errp, "missing chardev");
 170        return;
 171    }
 172
 173    if (!fs->conf.tag) {
 174        error_setg(errp, "missing tag property");
 175        return;
 176    }
 177    len = strlen(fs->conf.tag);
 178    if (len == 0) {
 179        error_setg(errp, "tag property cannot be empty");
 180        return;
 181    }
 182    if (len > sizeof_field(struct virtio_fs_config, tag)) {
 183        error_setg(errp, "tag property must be %zu bytes or less",
 184                   sizeof_field(struct virtio_fs_config, tag));
 185        return;
 186    }
 187
 188    if (fs->conf.num_request_queues == 0) {
 189        error_setg(errp, "num-request-queues property must be larger than 0");
 190        return;
 191    }
 192
 193    if (!is_power_of_2(fs->conf.queue_size)) {
 194        error_setg(errp, "queue-size property must be a power of 2");
 195        return;
 196    }
 197
 198    if (fs->conf.queue_size > VIRTQUEUE_MAX_SIZE) {
 199        error_setg(errp, "queue-size property must be %u or smaller",
 200                   VIRTQUEUE_MAX_SIZE);
 201        return;
 202    }
 203
 204    if (!vhost_user_init(&fs->vhost_user, &fs->conf.chardev, errp)) {
 205        return;
 206    }
 207
 208    virtio_init(vdev, "vhost-user-fs", VIRTIO_ID_FS,
 209                sizeof(struct virtio_fs_config));
 210
 211    /* Hiprio queue */
 212    virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
 213
 214    /* Request queues */
 215    for (i = 0; i < fs->conf.num_request_queues; i++) {
 216        virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output);
 217    }
 218
 219    /* 1 high prio queue, plus the number configured */
 220    fs->vhost_dev.nvqs = 1 + fs->conf.num_request_queues;
 221    fs->vhost_dev.vqs = g_new0(struct vhost_virtqueue, fs->vhost_dev.nvqs);
 222    ret = vhost_dev_init(&fs->vhost_dev, &fs->vhost_user,
 223                         VHOST_BACKEND_TYPE_USER, 0);
 224    if (ret < 0) {
 225        error_setg_errno(errp, -ret, "vhost_dev_init failed");
 226        goto err_virtio;
 227    }
 228
 229    return;
 230
 231err_virtio:
 232    vhost_user_cleanup(&fs->vhost_user);
 233    virtio_cleanup(vdev);
 234    g_free(fs->vhost_dev.vqs);
 235    return;
 236}
 237
 238static void vuf_device_unrealize(DeviceState *dev, Error **errp)
 239{
 240    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 241    VHostUserFS *fs = VHOST_USER_FS(dev);
 242
 243    /* This will stop vhost backend if appropriate. */
 244    vuf_set_status(vdev, 0);
 245
 246    vhost_dev_cleanup(&fs->vhost_dev);
 247
 248    vhost_user_cleanup(&fs->vhost_user);
 249
 250    virtio_cleanup(vdev);
 251    g_free(fs->vhost_dev.vqs);
 252    fs->vhost_dev.vqs = NULL;
 253}
 254
 255static const VMStateDescription vuf_vmstate = {
 256    .name = "vhost-user-fs",
 257    .unmigratable = 1,
 258};
 259
 260static Property vuf_properties[] = {
 261    DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
 262    DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
 263    DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
 264                       conf.num_request_queues, 1),
 265    DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
 266    DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd),
 267    DEFINE_PROP_END_OF_LIST(),
 268};
 269
 270static void vuf_class_init(ObjectClass *klass, void *data)
 271{
 272    DeviceClass *dc = DEVICE_CLASS(klass);
 273    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 274
 275    dc->props = vuf_properties;
 276    dc->vmsd = &vuf_vmstate;
 277    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 278    vdc->realize = vuf_device_realize;
 279    vdc->unrealize = vuf_device_unrealize;
 280    vdc->get_features = vuf_get_features;
 281    vdc->get_config = vuf_get_config;
 282    vdc->set_status = vuf_set_status;
 283    vdc->guest_notifier_mask = vuf_guest_notifier_mask;
 284    vdc->guest_notifier_pending = vuf_guest_notifier_pending;
 285}
 286
 287static const TypeInfo vuf_info = {
 288    .name = TYPE_VHOST_USER_FS,
 289    .parent = TYPE_VIRTIO_DEVICE,
 290    .instance_size = sizeof(VHostUserFS),
 291    .class_init = vuf_class_init,
 292};
 293
 294static void vuf_register_types(void)
 295{
 296    type_register_static(&vuf_info);
 297}
 298
 299type_init(vuf_register_types)
 300