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/main-loop.h"
  20#include "qemu/thread.h"
  21#include "qemu/error-report.h"
  22#include "hw/virtio/virtio-access.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    bool batch_notifications;
  38
  39    /* Note that these EventNotifiers are assigned by value.  This is
  40     * fine as long as you do not call event_notifier_cleanup on them
  41     * (because you don't own the file descriptor or handle; you just
  42     * use it).
  43     */
  44    IOThread *iothread;
  45    AioContext *ctx;
  46};
  47
  48/* Raise an interrupt to signal guest, if necessary */
  49void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
  50{
  51    if (s->batch_notifications) {
  52        set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
  53        qemu_bh_schedule(s->bh);
  54    } else {
  55        virtio_notify_irqfd(s->vdev, vq);
  56    }
  57}
  58
  59static void notify_guest_bh(void *opaque)
  60{
  61    VirtIOBlockDataPlane *s = opaque;
  62    unsigned nvqs = s->conf->num_queues;
  63    unsigned long bitmap[BITS_TO_LONGS(nvqs)];
  64    unsigned j;
  65
  66    memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap));
  67    memset(s->batch_notify_vqs, 0, sizeof(bitmap));
  68
  69    for (j = 0; j < nvqs; j += BITS_PER_LONG) {
  70        unsigned long bits = bitmap[j];
  71
  72        while (bits != 0) {
  73            unsigned i = j + ctzl(bits);
  74            VirtQueue *vq = virtio_get_queue(s->vdev, i);
  75
  76            virtio_notify_irqfd(s->vdev, vq);
  77
  78            bits &= bits - 1; /* clear right-most bit */
  79        }
  80    }
  81}
  82
  83/* Context: QEMU global mutex held */
  84bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
  85                                  VirtIOBlockDataPlane **dataplane,
  86                                  Error **errp)
  87{
  88    VirtIOBlockDataPlane *s;
  89    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
  90    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
  91
  92    *dataplane = NULL;
  93
  94    if (conf->iothread) {
  95        if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
  96            error_setg(errp,
  97                       "device is incompatible with iothread "
  98                       "(transport does not support notifiers)");
  99            return false;
 100        }
 101        if (!virtio_device_ioeventfd_enabled(vdev)) {
 102            error_setg(errp, "ioeventfd is required for iothread");
 103            return false;
 104        }
 105
 106        /* If dataplane is (re-)enabled while the guest is running there could
 107         * be block jobs that can conflict.
 108         */
 109        if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
 110            error_prepend(errp, "cannot start virtio-blk dataplane: ");
 111            return false;
 112        }
 113    }
 114    /* Don't try if transport does not support notifiers. */
 115    if (!virtio_device_ioeventfd_enabled(vdev)) {
 116        return false;
 117    }
 118
 119    s = g_new0(VirtIOBlockDataPlane, 1);
 120    s->vdev = vdev;
 121    s->conf = conf;
 122
 123    if (conf->iothread) {
 124        s->iothread = conf->iothread;
 125        object_ref(OBJECT(s->iothread));
 126        s->ctx = iothread_get_aio_context(s->iothread);
 127    } else {
 128        s->ctx = qemu_get_aio_context();
 129    }
 130    s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
 131    s->batch_notify_vqs = bitmap_new(conf->num_queues);
 132
 133    *dataplane = s;
 134
 135    return true;
 136}
 137
 138/* Context: QEMU global mutex held */
 139void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
 140{
 141    VirtIOBlock *vblk;
 142
 143    if (!s) {
 144        return;
 145    }
 146
 147    vblk = VIRTIO_BLK(s->vdev);
 148    assert(!vblk->dataplane_started);
 149    g_free(s->batch_notify_vqs);
 150    qemu_bh_delete(s->bh);
 151    if (s->iothread) {
 152        object_unref(OBJECT(s->iothread));
 153    }
 154    g_free(s);
 155}
 156
 157static bool virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
 158                                                VirtQueue *vq)
 159{
 160    VirtIOBlock *s = (VirtIOBlock *)vdev;
 161
 162    assert(s->dataplane);
 163    assert(s->dataplane_started);
 164
 165    return virtio_blk_handle_vq(s, vq);
 166}
 167
 168/* Context: QEMU global mutex held */
 169int virtio_blk_data_plane_start(VirtIODevice *vdev)
 170{
 171    VirtIOBlock *vblk = VIRTIO_BLK(vdev);
 172    VirtIOBlockDataPlane *s = vblk->dataplane;
 173    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk)));
 174    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 175    unsigned i;
 176    unsigned nvqs = s->conf->num_queues;
 177    Error *local_err = NULL;
 178    int r;
 179
 180    if (vblk->dataplane_started || s->starting) {
 181        return 0;
 182    }
 183
 184    s->starting = true;
 185
 186    if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
 187        s->batch_notifications = true;
 188    } else {
 189        s->batch_notifications = false;
 190    }
 191
 192    /* Set up guest notifier (irq) */
 193    r = k->set_guest_notifiers(qbus->parent, nvqs, true);
 194    if (r != 0) {
 195        error_report("virtio-blk failed to set guest notifier (%d), "
 196                     "ensure -accel kvm is set.", r);
 197        goto fail_guest_notifiers;
 198    }
 199
 200    /* Set up virtqueue notify */
 201    for (i = 0; i < nvqs; i++) {
 202        r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true);
 203        if (r != 0) {
 204            fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
 205            while (i--) {
 206                virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 207                virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
 208            }
 209            goto fail_guest_notifiers;
 210        }
 211    }
 212
 213    s->starting = false;
 214    vblk->dataplane_started = true;
 215    trace_virtio_blk_data_plane_start(s);
 216
 217    r = blk_set_aio_context(s->conf->conf.blk, s->ctx, &local_err);
 218    if (r < 0) {
 219        error_report_err(local_err);
 220        goto fail_guest_notifiers;
 221    }
 222
 223    /* Kick right away to begin processing requests already in vring */
 224    for (i = 0; i < nvqs; i++) {
 225        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 226
 227        event_notifier_set(virtio_queue_get_host_notifier(vq));
 228    }
 229
 230    /* Get this show started by hooking up our callbacks */
 231    aio_context_acquire(s->ctx);
 232    for (i = 0; i < nvqs; i++) {
 233        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 234
 235        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx,
 236                virtio_blk_data_plane_handle_output);
 237    }
 238    aio_context_release(s->ctx);
 239    return 0;
 240
 241  fail_guest_notifiers:
 242    vblk->dataplane_disabled = true;
 243    s->starting = false;
 244    vblk->dataplane_started = true;
 245    return -ENOSYS;
 246}
 247
 248/* Stop notifications for new requests from guest.
 249 *
 250 * Context: BH in IOThread
 251 */
 252static void virtio_blk_data_plane_stop_bh(void *opaque)
 253{
 254    VirtIOBlockDataPlane *s = opaque;
 255    unsigned i;
 256
 257    for (i = 0; i < s->conf->num_queues; i++) {
 258        VirtQueue *vq = virtio_get_queue(s->vdev, i);
 259
 260        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
 261    }
 262}
 263
 264/* Context: QEMU global mutex held */
 265void virtio_blk_data_plane_stop(VirtIODevice *vdev)
 266{
 267    VirtIOBlock *vblk = VIRTIO_BLK(vdev);
 268    VirtIOBlockDataPlane *s = vblk->dataplane;
 269    BusState *qbus = qdev_get_parent_bus(DEVICE(vblk));
 270    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 271    unsigned i;
 272    unsigned nvqs = s->conf->num_queues;
 273
 274    if (!vblk->dataplane_started || s->stopping) {
 275        return;
 276    }
 277
 278    /* Better luck next time. */
 279    if (vblk->dataplane_disabled) {
 280        vblk->dataplane_disabled = false;
 281        vblk->dataplane_started = false;
 282        return;
 283    }
 284    s->stopping = true;
 285    trace_virtio_blk_data_plane_stop(s);
 286
 287    aio_context_acquire(s->ctx);
 288    aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s);
 289
 290    /* Drain and try to switch bs back to the QEMU main loop. If other users
 291     * keep the BlockBackend in the iothread, that's ok */
 292    blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context(), NULL);
 293
 294    aio_context_release(s->ctx);
 295
 296    for (i = 0; i < nvqs; i++) {
 297        virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
 298        virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
 299    }
 300
 301    qemu_bh_cancel(s->bh);
 302    notify_guest_bh(s); /* final chance to notify guest */
 303
 304    /* Clean up guest notifier (irq) */
 305    k->set_guest_notifiers(qbus->parent, nvqs, false);
 306
 307    vblk->dataplane_started = false;
 308    s->stopping = false;
 309}
 310