qemu/hw/block/dataplane/virtio-blk.c
<<
>>
Prefs
   1/*
   2 * Dedicated thread for virtio-blk I/O processing
   3 *
   4 * Copyright 2012 IBM, Corp.
   5 * Copyright 2012 Red Hat, Inc. and/or its affiliates
   6 *
   7 * Authors:
   8 *   Stefan Hajnoczi <stefanha@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  11 * See the COPYING file in the top-level directory.
  12 *
  13 */
  14
  15#include "qemu/osdep.h"
  16#include "qapi/error.h"
  17#include "trace.h"
  18#include "qemu/iov.h"
  19#include "qemu/thread.h"
  20#include "qemu/error-report.h"
  21#include "hw/virtio/virtio-access.h"
  22#include "sysemu/block-backend.h"
  23#include "hw/virtio/virtio-blk.h"
  24#include "virtio-blk.h"
  25#include "block/aio.h"
  26#include "hw/virtio/virtio-bus.h"
  27#include "qom/object_interfaces.h"
  28
  29struct VirtIOBlockDataPlane {
  30    bool starting;
  31    bool stopping;
  32
  33    VirtIOBlkConf *conf;
  34    VirtIODevice *vdev;
  35    QEMUBH *bh;                     /* bh for guest notification */
  36    unsigned long *batch_notify_vqs;
  37
  38    /* Note that these EventNotifiers are assigned by value.  This is
  39     * fine as long as you do not call event_notifier_cleanup on them
  40     * (because you don't own the file descriptor or handle; you just
  41     * use it).
  42     */
  43    IOThread *iothread;
  44    AioContext *ctx;
  45};
  46
  47/* Raise an interrupt to signal guest, if necessary */
  48void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
  49{
  50    set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
  51    qemu_bh_schedule(s->bh);
  52}
  53
  54static void notify_guest_bh(void *opaque)
  55{
  56    VirtIOBlockDataPlane *s = opaque;
  57    unsigned nvqs = s->conf->num_queues;
  58    unsigned long bitmap[BITS_TO_LONGS(nvqs)];
  59    unsigned j;
  60
  61    memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap));
  62    memset(s->batch_notify_vqs, 0, sizeof(bitmap));
  63
  64    for (j = 0; j < nvqs; j += BITS_PER_LONG) {
  65        unsigned long bits = bitmap[j];
  66
  67        while (bits != 0) {
  68            unsigned i = j + ctzl(bits);
  69            VirtQueue *vq = virtio_get_queue(s->vdev, i);
  70
  71            if (virtio_should_notify(s->vdev, vq)) {
  72                event_notifier_set(virtio_queue_get_guest_notifier(vq));
  73            }
  74
  75            bits &= bits - 1; /* clear right-most bit */
  76        }
  77    }
  78}
  79
  80/* Context: QEMU global mutex held */
  81void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
  82                                  VirtIOBlockDataPlane **dataplane,
  83                                  Error **errp)
  84{
  85    VirtIOBlockDataPlane *s;
  86    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  87    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  88
  89    *dataplane = NULL;
  90
  91    if (!conf->iothread) {
  92        return;
  93    }
  94
  95    /* Don't try if transport does not support notifiers. */
  96    if (!k->set_guest_notifiers || !k->ioeventfd_started) {
  97        error_setg(errp,
  98                   "device is incompatible with dataplane "
  99                   "(transport does not support notifiers)");
 100        return;
 101    }
 102
 103    /* If dataplane is (re-)enabled while the guest is running there could be
 104     * block jobs that can conflict.
 105     */
 106    if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
 107        error_prepend(errp, "cannot start dataplane thread: ");
 108        return;
 109    }
 110
 111    s = g_new0(VirtIOBlockDataPlane, 1);
 112    s->vdev = vdev;
 113    s->conf = conf;
 114
 115    s->iothread = conf->iothread;
 116    object_ref(OBJECT(s->iothread));
 117    s->ctx = iothread_get_aio_context(s->iothread);
 118    s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
 119    s->batch_notify_vqs = bitmap_new(conf->num_queues);
 120
 121    *dataplane = s;
 122}
 123
 124/* Context: QEMU global mutex held */
 125void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
 126{
 127    if (!s) {
 128        return;
 129    }
 130
 131    virtio_blk_data_plane_stop(s);
 132    g_free(s->batch_notify_vqs);
 133    qemu_bh_delete(s->bh);
 134    object_unref(OBJECT(s->iothread));
 135    g_free(s);
 136}
 137
 138static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
 139                                                VirtQueue *vq)
 140{
 141    VirtIOBlock *s = (VirtIOBlock *)vdev;
 142
 143    assert(s->dataplane);
 144    assert(s->dataplane_started);
 145
 146    virtio_blk_handle_vq(s, vq);
 147}
 148
 149/* Context: QEMU global mutex held */
 150void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
 151{
 152    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
 153    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 154    VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
 155    unsigned i;
 156    unsigned nvqs = s->conf->num_queues;
 157    int r;
 158
 159    if (vblk->dataplane_started || s->starting) {
 160        return;
 161    }
 162
 163    s->starting = true;
 164
 165    /* Set up guest notifier (irq) */
 166    r = k->set_guest_notifiers(qbus->parent, nvqs, true);
 167    if (r != 0) {
 168        fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
 169                "ensure -enable-kvm is set\n", r);
 170        goto fail_guest_notifiers;
 171    }
 172
 173    /* Set up virtqueue notify */
 174    for (i = 0; i < nvqs; i++) {
 175        r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true);
 176        if (r != 0) {
 177            fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
 178            while (i--) {
 179                virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 180            }
 181            goto fail_guest_notifiers;
 182        }
 183    }
 184
 185    s->starting = false;
 186    vblk->dataplane_started = true;
 187    trace_virtio_blk_data_plane_start(s);
 188
 189    blk_set_aio_context(s->conf->conf.blk, s->ctx);
 190
 191    /* Kick right away to begin processing requests already in vring */
 192    for (i = 0; i < nvqs; i++) {
 193        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 194
 195        event_notifier_set(virtio_queue_get_host_notifier(vq));
 196    }
 197
 198    /* Get this show started by hooking up our callbacks */
 199    aio_context_acquire(s->ctx);
 200    for (i = 0; i < nvqs; i++) {
 201        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 202
 203        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx,
 204                virtio_blk_data_plane_handle_output);
 205    }
 206    aio_context_release(s->ctx);
 207    return;
 208
 209  fail_guest_notifiers:
 210    vblk->dataplane_disabled = true;
 211    s->starting = false;
 212    vblk->dataplane_started = true;
 213}
 214
 215/* Context: QEMU global mutex held */
 216void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
 217{
 218    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
 219    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 220    VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
 221    unsigned i;
 222    unsigned nvqs = s->conf->num_queues;
 223
 224    if (!vblk->dataplane_started || s->stopping) {
 225        return;
 226    }
 227
 228    /* Better luck next time. */
 229    if (vblk->dataplane_disabled) {
 230        vblk->dataplane_disabled = false;
 231        vblk->dataplane_started = false;
 232        return;
 233    }
 234    s->stopping = true;
 235    trace_virtio_blk_data_plane_stop(s);
 236
 237    aio_context_acquire(s->ctx);
 238
 239    /* Stop notifications for new requests from guest */
 240    for (i = 0; i < nvqs; i++) {
 241        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 242
 243        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
 244    }
 245
 246    /* Drain and switch bs back to the QEMU main loop */
 247    blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
 248
 249    aio_context_release(s->ctx);
 250
 251    for (i = 0; i < nvqs; i++) {
 252        virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 253    }
 254
 255    /* Clean up guest notifier (irq) */
 256    k->set_guest_notifiers(qbus->parent, nvqs, false);
 257
 258    vblk->dataplane_started = false;
 259    s->stopping = false;
 260}
 261