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

virtio-scsi-dataplane.c (8056B)


      1/*
      2 * Virtio SCSI dataplane
      3 *
      4 * Copyright Red Hat, Inc. 2014
      5 *
      6 * Authors:
      7 *   Fam Zheng <famz@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 "hw/virtio/virtio-scsi.h"
     17#include "qemu/error-report.h"
     18#include "sysemu/block-backend.h"
     19#include "hw/scsi/scsi.h"
     20#include "scsi/constants.h"
     21#include "hw/virtio/virtio-bus.h"
     22#include "hw/virtio/virtio-access.h"
     23
     24/* Context: QEMU global mutex held */
     25void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
     26{
     27    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     28    VirtIODevice *vdev = VIRTIO_DEVICE(s);
     29    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
     30    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
     31
     32    if (vs->conf.iothread) {
     33        if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
     34            error_setg(errp,
     35                       "device is incompatible with iothread "
     36                       "(transport does not support notifiers)");
     37            return;
     38        }
     39        if (!virtio_device_ioeventfd_enabled(vdev)) {
     40            error_setg(errp, "ioeventfd is required for iothread");
     41            return;
     42        }
     43        s->ctx = iothread_get_aio_context(vs->conf.iothread);
     44    } else {
     45        if (!virtio_device_ioeventfd_enabled(vdev)) {
     46            return;
     47        }
     48        s->ctx = qemu_get_aio_context();
     49    }
     50}
     51
     52static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev,
     53                                              VirtQueue *vq)
     54{
     55    bool progress = false;
     56    VirtIOSCSI *s = VIRTIO_SCSI(vdev);
     57
     58    virtio_scsi_acquire(s);
     59    if (!s->dataplane_fenced) {
     60        assert(s->ctx && s->dataplane_started);
     61        progress = virtio_scsi_handle_cmd_vq(s, vq);
     62    }
     63    virtio_scsi_release(s);
     64    return progress;
     65}
     66
     67static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev,
     68                                               VirtQueue *vq)
     69{
     70    bool progress = false;
     71    VirtIOSCSI *s = VIRTIO_SCSI(vdev);
     72
     73    virtio_scsi_acquire(s);
     74    if (!s->dataplane_fenced) {
     75        assert(s->ctx && s->dataplane_started);
     76        progress = virtio_scsi_handle_ctrl_vq(s, vq);
     77    }
     78    virtio_scsi_release(s);
     79    return progress;
     80}
     81
     82static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev,
     83                                                VirtQueue *vq)
     84{
     85    bool progress = false;
     86    VirtIOSCSI *s = VIRTIO_SCSI(vdev);
     87
     88    virtio_scsi_acquire(s);
     89    if (!s->dataplane_fenced) {
     90        assert(s->ctx && s->dataplane_started);
     91        progress = virtio_scsi_handle_event_vq(s, vq);
     92    }
     93    virtio_scsi_release(s);
     94    return progress;
     95}
     96
     97static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
     98{
     99    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
    100    int rc;
    101
    102    /* Set up virtqueue notify */
    103    rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true);
    104    if (rc != 0) {
    105        fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
    106                rc);
    107        s->dataplane_fenced = true;
    108        return rc;
    109    }
    110
    111    return 0;
    112}
    113
    114/* Context: BH in IOThread */
    115static void virtio_scsi_dataplane_stop_bh(void *opaque)
    116{
    117    VirtIOSCSI *s = opaque;
    118    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
    119    int i;
    120
    121    virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL);
    122    virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL);
    123    for (i = 0; i < vs->conf.num_queues; i++) {
    124        virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL);
    125    }
    126}
    127
    128/* Context: QEMU global mutex held */
    129int virtio_scsi_dataplane_start(VirtIODevice *vdev)
    130{
    131    int i;
    132    int rc;
    133    int vq_init_count = 0;
    134    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
    135    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    136    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
    137    VirtIOSCSI *s = VIRTIO_SCSI(vdev);
    138
    139    if (s->dataplane_started ||
    140        s->dataplane_starting ||
    141        s->dataplane_fenced) {
    142        return 0;
    143    }
    144
    145    s->dataplane_starting = true;
    146
    147    /* Set up guest notifier (irq) */
    148    rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
    149    if (rc != 0) {
    150        error_report("virtio-scsi: Failed to set guest notifiers (%d), "
    151                     "ensure -accel kvm is set.", rc);
    152        goto fail_guest_notifiers;
    153    }
    154
    155    /*
    156     * Batch all the host notifiers in a single transaction to avoid
    157     * quadratic time complexity in address_space_update_ioeventfds().
    158     */
    159    memory_region_transaction_begin();
    160
    161    rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0);
    162    if (rc != 0) {
    163        goto fail_host_notifiers;
    164    }
    165
    166    vq_init_count++;
    167    rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1);
    168    if (rc != 0) {
    169        goto fail_host_notifiers;
    170    }
    171
    172    vq_init_count++;
    173
    174    for (i = 0; i < vs->conf.num_queues; i++) {
    175        rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2);
    176        if (rc) {
    177            goto fail_host_notifiers;
    178        }
    179        vq_init_count++;
    180    }
    181
    182    memory_region_transaction_commit();
    183
    184    aio_context_acquire(s->ctx);
    185    virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx,
    186                                            virtio_scsi_data_plane_handle_ctrl);
    187    virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx,
    188                                           virtio_scsi_data_plane_handle_event);
    189
    190    for (i = 0; i < vs->conf.num_queues; i++) {
    191        virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx,
    192                                             virtio_scsi_data_plane_handle_cmd);
    193    }
    194
    195    s->dataplane_starting = false;
    196    s->dataplane_started = true;
    197    aio_context_release(s->ctx);
    198    return 0;
    199
    200fail_host_notifiers:
    201    for (i = 0; i < vq_init_count; i++) {
    202        virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
    203    }
    204
    205    /*
    206     * The transaction expects the ioeventfds to be open when it
    207     * commits. Do it now, before the cleanup loop.
    208     */
    209    memory_region_transaction_commit();
    210
    211    for (i = 0; i < vq_init_count; i++) {
    212        virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
    213    }
    214    k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
    215fail_guest_notifiers:
    216    s->dataplane_fenced = true;
    217    s->dataplane_starting = false;
    218    s->dataplane_started = true;
    219    return -ENOSYS;
    220}
    221
    222/* Context: QEMU global mutex held */
    223void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
    224{
    225    BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
    226    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    227    VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
    228    VirtIOSCSI *s = VIRTIO_SCSI(vdev);
    229    int i;
    230
    231    if (!s->dataplane_started || s->dataplane_stopping) {
    232        return;
    233    }
    234
    235    /* Better luck next time. */
    236    if (s->dataplane_fenced) {
    237        s->dataplane_fenced = false;
    238        s->dataplane_started = false;
    239        return;
    240    }
    241    s->dataplane_stopping = true;
    242
    243    aio_context_acquire(s->ctx);
    244    aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
    245    aio_context_release(s->ctx);
    246
    247    blk_drain_all(); /* ensure there are no in-flight requests */
    248
    249    /*
    250     * Batch all the host notifiers in a single transaction to avoid
    251     * quadratic time complexity in address_space_update_ioeventfds().
    252     */
    253    memory_region_transaction_begin();
    254
    255    for (i = 0; i < vs->conf.num_queues + 2; i++) {
    256        virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
    257    }
    258
    259    /*
    260     * The transaction expects the ioeventfds to be open when it
    261     * commits. Do it now, before the cleanup loop.
    262     */
    263    memory_region_transaction_commit();
    264
    265    for (i = 0; i < vs->conf.num_queues + 2; i++) {
    266        virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
    267    }
    268
    269    /* Clean up guest notifier (irq) */
    270    k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
    271    s->dataplane_stopping = false;
    272    s->dataplane_started = false;
    273}