cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

vhost-user-fs.c (9232B)


      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, errp);
    239    if (ret < 0) {
    240        goto err_virtio;
    241    }
    242
    243    return;
    244
    245err_virtio:
    246    vhost_user_cleanup(&fs->vhost_user);
    247    virtio_delete_queue(fs->hiprio_vq);
    248    for (i = 0; i < fs->conf.num_request_queues; i++) {
    249        virtio_delete_queue(fs->req_vqs[i]);
    250    }
    251    g_free(fs->req_vqs);
    252    virtio_cleanup(vdev);
    253    g_free(fs->vhost_dev.vqs);
    254    return;
    255}
    256
    257static void vuf_device_unrealize(DeviceState *dev)
    258{
    259    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    260    VHostUserFS *fs = VHOST_USER_FS(dev);
    261    int i;
    262
    263    /* This will stop vhost backend if appropriate. */
    264    vuf_set_status(vdev, 0);
    265
    266    vhost_dev_cleanup(&fs->vhost_dev);
    267
    268    vhost_user_cleanup(&fs->vhost_user);
    269
    270    virtio_delete_queue(fs->hiprio_vq);
    271    for (i = 0; i < fs->conf.num_request_queues; i++) {
    272        virtio_delete_queue(fs->req_vqs[i]);
    273    }
    274    g_free(fs->req_vqs);
    275    virtio_cleanup(vdev);
    276    g_free(fs->vhost_dev.vqs);
    277    fs->vhost_dev.vqs = NULL;
    278}
    279
    280static const VMStateDescription vuf_vmstate = {
    281    .name = "vhost-user-fs",
    282    .unmigratable = 1,
    283};
    284
    285static Property vuf_properties[] = {
    286    DEFINE_PROP_CHR("chardev", VHostUserFS, conf.chardev),
    287    DEFINE_PROP_STRING("tag", VHostUserFS, conf.tag),
    288    DEFINE_PROP_UINT16("num-request-queues", VHostUserFS,
    289                       conf.num_request_queues, 1),
    290    DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128),
    291    DEFINE_PROP_END_OF_LIST(),
    292};
    293
    294static void vuf_instance_init(Object *obj)
    295{
    296    VHostUserFS *fs = VHOST_USER_FS(obj);
    297
    298    device_add_bootindex_property(obj, &fs->bootindex, "bootindex",
    299                                  "/filesystem@0", DEVICE(obj));
    300}
    301
    302static void vuf_class_init(ObjectClass *klass, void *data)
    303{
    304    DeviceClass *dc = DEVICE_CLASS(klass);
    305    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
    306
    307    device_class_set_props(dc, vuf_properties);
    308    dc->vmsd = &vuf_vmstate;
    309    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
    310    vdc->realize = vuf_device_realize;
    311    vdc->unrealize = vuf_device_unrealize;
    312    vdc->get_features = vuf_get_features;
    313    vdc->get_config = vuf_get_config;
    314    vdc->set_status = vuf_set_status;
    315    vdc->guest_notifier_mask = vuf_guest_notifier_mask;
    316    vdc->guest_notifier_pending = vuf_guest_notifier_pending;
    317}
    318
    319static const TypeInfo vuf_info = {
    320    .name = TYPE_VHOST_USER_FS,
    321    .parent = TYPE_VIRTIO_DEVICE,
    322    .instance_size = sizeof(VHostUserFS),
    323    .instance_init = vuf_instance_init,
    324    .class_init = vuf_class_init,
    325};
    326
    327static void vuf_register_types(void)
    328{
    329    type_register_static(&vuf_info);
    330}
    331
    332type_init(vuf_register_types)