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