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            virtio_notify_irqfd(s->vdev, vq);
  72
  73            bits &= bits - 1; /* clear right-most bit */
  74        }
  75    }
  76}
  77
  78/* Context: QEMU global mutex held */
  79void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
  80                                  VirtIOBlockDataPlane **dataplane,
  81                                  Error **errp)
  82{
  83    VirtIOBlockDataPlane *s;
  84    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  85    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  86
  87    *dataplane = NULL;
  88
  89    if (conf->iothread) {
  90        if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
  91            error_setg(errp,
  92                       "device is incompatible with iothread "
  93                       "(transport does not support notifiers)");
  94            return;
  95        }
  96        if (!virtio_device_ioeventfd_enabled(vdev)) {
  97            error_setg(errp, "ioeventfd is required for iothread");
  98            return;
  99        }
 100
 101        /* If dataplane is (re-)enabled while the guest is running there could
 102         * be block jobs that can conflict.
 103         */
 104        if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
 105            error_prepend(errp, "cannot start virtio-blk dataplane: ");
 106            return;
 107        }
 108    }
 109    /* Don't try if transport does not support notifiers. */
 110    if (!virtio_device_ioeventfd_enabled(vdev)) {
 111        return;
 112    }
 113
 114    s = g_new0(VirtIOBlockDataPlane, 1);
 115    s->vdev = vdev;
 116    s->conf = conf;
 117
 118    if (conf->iothread) {
 119        s->iothread = conf->iothread;
 120        object_ref(OBJECT(s->iothread));
 121        s->ctx = iothread_get_aio_context(s->iothread);
 122    } else {
 123        s->ctx = qemu_get_aio_context();
 124    }
 125    s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
 126    s->batch_notify_vqs = bitmap_new(conf->num_queues);
 127
 128    *dataplane = s;
 129}
 130
 131/* Context: QEMU global mutex held */
 132void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
 133{
 134    VirtIOBlock *vblk;
 135
 136    if (!s) {
 137        return;
 138    }
 139
 140    vblk = VIRTIO_BLK(s->vdev);
 141    assert(!vblk->dataplane_started);
 142    g_free(s->batch_notify_vqs);
 143    qemu_bh_delete(s->bh);
 144    if (s->iothread) {
 145        object_unref(OBJECT(s->iothread));
 146    }
 147    g_free(s);
 148}
 149
 150static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
 151                                                VirtQueue *vq)
 152{
 153    VirtIOBlock *s = (VirtIOBlock *)vdev;
 154
 155    assert(s->dataplane);
 156    assert(s->dataplane_started);
 157
 158    virtio_blk_handle_vq(s, vq);
 159}
 160
 161/* Context: QEMU global mutex held */
 162int virtio_blk_data_plane_start(VirtIODevice *vdev)
 163{
 164    VirtIOBlock *vblk = VIRTIO_BLK(vdev);
 165    VirtIOBlockDataPlane *s = vblk->dataplane;
 166    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk)));
 167    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 168    unsigned i;
 169    unsigned nvqs = s->conf->num_queues;
 170    int r;
 171
 172    if (vblk->dataplane_started || s->starting) {
 173        return 0;
 174    }
 175
 176    s->starting = true;
 177
 178    /* Set up guest notifier (irq) */
 179    r = k->set_guest_notifiers(qbus->parent, nvqs, true);
 180    if (r != 0) {
 181        fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
 182                "ensure -enable-kvm is set\n", r);
 183        goto fail_guest_notifiers;
 184    }
 185
 186    /* Set up virtqueue notify */
 187    for (i = 0; i < nvqs; i++) {
 188        r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true);
 189        if (r != 0) {
 190            fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
 191            while (i--) {
 192                virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 193            }
 194            goto fail_guest_notifiers;
 195        }
 196    }
 197
 198    s->starting = false;
 199    vblk->dataplane_started = true;
 200    trace_virtio_blk_data_plane_start(s);
 201
 202    blk_set_aio_context(s->conf->conf.blk, s->ctx);
 203
 204    /* Kick right away to begin processing requests already in vring */
 205    for (i = 0; i < nvqs; i++) {
 206        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 207
 208        event_notifier_set(virtio_queue_get_host_notifier(vq));
 209    }
 210
 211    /* Get this show started by hooking up our callbacks */
 212    aio_context_acquire(s->ctx);
 213    for (i = 0; i < nvqs; i++) {
 214        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 215
 216        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx,
 217                virtio_blk_data_plane_handle_output);
 218    }
 219    aio_context_release(s->ctx);
 220    return 0;
 221
 222  fail_guest_notifiers:
 223    vblk->dataplane_disabled = true;
 224    s->starting = false;
 225    vblk->dataplane_started = true;
 226    return -ENOSYS;
 227}
 228
 229/* Context: QEMU global mutex held */
 230void virtio_blk_data_plane_stop(VirtIODevice *vdev)
 231{
 232    VirtIOBlock *vblk = VIRTIO_BLK(vdev);
 233    VirtIOBlockDataPlane *s = vblk->dataplane;
 234    BusState *qbus = qdev_get_parent_bus(DEVICE(vblk));
 235    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 236    unsigned i;
 237    unsigned nvqs = s->conf->num_queues;
 238
 239    if (!vblk->dataplane_started || s->stopping) {
 240        return;
 241    }
 242
 243    /* Better luck next time. */
 244    if (vblk->dataplane_disabled) {
 245        vblk->dataplane_disabled = false;
 246        vblk->dataplane_started = false;
 247        return;
 248    }
 249    s->stopping = true;
 250    trace_virtio_blk_data_plane_stop(s);
 251
 252    aio_context_acquire(s->ctx);
 253
 254    /* Stop notifications for new requests from guest */
 255    for (i = 0; i < nvqs; i++) {
 256        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 257
 258        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
 259    }
 260
 261    /* Drain and switch bs back to the QEMU main loop */
 262    blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
 263
 264    aio_context_release(s->ctx);
 265
 266    for (i = 0; i < nvqs; i++) {
 267        virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 268    }
 269
 270    /* Clean up guest notifier (irq) */
 271    k->set_guest_notifiers(qbus->parent, nvqs, false);
 272
 273    vblk->dataplane_started = false;
 274    s->stopping = false;
 275}
 276