qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-blk.c (10461B)


      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 
     29 struct 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 */
     49 void 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 
     59 static 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 / BITS_PER_LONG];
     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 */
     84 bool 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 */
    139 void 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 
    157 /* Context: QEMU global mutex held */
    158 int virtio_blk_data_plane_start(VirtIODevice *vdev)
    159 {
    160     VirtIOBlock *vblk = VIRTIO_BLK(vdev);
    161     VirtIOBlockDataPlane *s = vblk->dataplane;
    162     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk)));
    163     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    164     AioContext *old_context;
    165     unsigned i;
    166     unsigned nvqs = s->conf->num_queues;
    167     Error *local_err = NULL;
    168     int r;
    169 
    170     if (vblk->dataplane_started || s->starting) {
    171         return 0;
    172     }
    173 
    174     s->starting = true;
    175 
    176     if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
    177         s->batch_notifications = true;
    178     } else {
    179         s->batch_notifications = false;
    180     }
    181 
    182     /* Set up guest notifier (irq) */
    183     r = k->set_guest_notifiers(qbus->parent, nvqs, true);
    184     if (r != 0) {
    185         error_report("virtio-blk failed to set guest notifier (%d), "
    186                      "ensure -accel kvm is set.", r);
    187         goto fail_guest_notifiers;
    188     }
    189 
    190     /*
    191      * Batch all the host notifiers in a single transaction to avoid
    192      * quadratic time complexity in address_space_update_ioeventfds().
    193      */
    194     memory_region_transaction_begin();
    195 
    196     /* Set up virtqueue notify */
    197     for (i = 0; i < nvqs; i++) {
    198         r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true);
    199         if (r != 0) {
    200             int j = i;
    201 
    202             fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
    203             while (i--) {
    204                 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
    205             }
    206 
    207             /*
    208              * The transaction expects the ioeventfds to be open when it
    209              * commits. Do it now, before the cleanup loop.
    210              */
    211             memory_region_transaction_commit();
    212 
    213             while (j--) {
    214                 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), j);
    215             }
    216             goto fail_host_notifiers;
    217         }
    218     }
    219 
    220     memory_region_transaction_commit();
    221 
    222     /*
    223      * These fields are visible to the IOThread so we rely on implicit barriers
    224      * in aio_context_acquire() on the write side and aio_notify_accept() on
    225      * the read side.
    226      */
    227     s->starting = false;
    228     vblk->dataplane_started = true;
    229     trace_virtio_blk_data_plane_start(s);
    230 
    231     old_context = blk_get_aio_context(s->conf->conf.blk);
    232     aio_context_acquire(old_context);
    233     r = blk_set_aio_context(s->conf->conf.blk, s->ctx, &local_err);
    234     aio_context_release(old_context);
    235     if (r < 0) {
    236         error_report_err(local_err);
    237         goto fail_aio_context;
    238     }
    239 
    240     /* Process queued requests before the ones in vring */
    241     virtio_blk_process_queued_requests(vblk, false);
    242 
    243     /* Kick right away to begin processing requests already in vring */
    244     for (i = 0; i < nvqs; i++) {
    245         VirtQueue *vq = virtio_get_queue(s->vdev, i);
    246 
    247         event_notifier_set(virtio_queue_get_host_notifier(vq));
    248     }
    249 
    250     /* Get this show started by hooking up our callbacks */
    251     aio_context_acquire(s->ctx);
    252     for (i = 0; i < nvqs; i++) {
    253         VirtQueue *vq = virtio_get_queue(s->vdev, i);
    254 
    255         virtio_queue_aio_attach_host_notifier(vq, s->ctx);
    256     }
    257     aio_context_release(s->ctx);
    258     return 0;
    259 
    260   fail_aio_context:
    261     memory_region_transaction_begin();
    262 
    263     for (i = 0; i < nvqs; i++) {
    264         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
    265     }
    266 
    267     memory_region_transaction_commit();
    268 
    269     for (i = 0; i < nvqs; i++) {
    270         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
    271     }
    272   fail_host_notifiers:
    273     k->set_guest_notifiers(qbus->parent, nvqs, false);
    274   fail_guest_notifiers:
    275     /*
    276      * If we failed to set up the guest notifiers queued requests will be
    277      * processed on the main context.
    278      */
    279     virtio_blk_process_queued_requests(vblk, false);
    280     vblk->dataplane_disabled = true;
    281     s->starting = false;
    282     vblk->dataplane_started = true;
    283     return -ENOSYS;
    284 }
    285 
    286 /* Stop notifications for new requests from guest.
    287  *
    288  * Context: BH in IOThread
    289  */
    290 static void virtio_blk_data_plane_stop_bh(void *opaque)
    291 {
    292     VirtIOBlockDataPlane *s = opaque;
    293     unsigned i;
    294 
    295     for (i = 0; i < s->conf->num_queues; i++) {
    296         VirtQueue *vq = virtio_get_queue(s->vdev, i);
    297 
    298         virtio_queue_aio_detach_host_notifier(vq, s->ctx);
    299     }
    300 }
    301 
    302 /* Context: QEMU global mutex held */
    303 void virtio_blk_data_plane_stop(VirtIODevice *vdev)
    304 {
    305     VirtIOBlock *vblk = VIRTIO_BLK(vdev);
    306     VirtIOBlockDataPlane *s = vblk->dataplane;
    307     BusState *qbus = qdev_get_parent_bus(DEVICE(vblk));
    308     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    309     unsigned i;
    310     unsigned nvqs = s->conf->num_queues;
    311 
    312     if (!vblk->dataplane_started || s->stopping) {
    313         return;
    314     }
    315 
    316     /* Better luck next time. */
    317     if (vblk->dataplane_disabled) {
    318         vblk->dataplane_disabled = false;
    319         vblk->dataplane_started = false;
    320         return;
    321     }
    322     s->stopping = true;
    323     trace_virtio_blk_data_plane_stop(s);
    324 
    325     aio_context_acquire(s->ctx);
    326     aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s);
    327 
    328     /* Drain and try to switch bs back to the QEMU main loop. If other users
    329      * keep the BlockBackend in the iothread, that's ok */
    330     blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context(), NULL);
    331 
    332     aio_context_release(s->ctx);
    333 
    334     /*
    335      * Batch all the host notifiers in a single transaction to avoid
    336      * quadratic time complexity in address_space_update_ioeventfds().
    337      */
    338     memory_region_transaction_begin();
    339 
    340     for (i = 0; i < nvqs; i++) {
    341         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
    342     }
    343 
    344     /*
    345      * The transaction expects the ioeventfds to be open when it
    346      * commits. Do it now, before the cleanup loop.
    347      */
    348     memory_region_transaction_commit();
    349 
    350     for (i = 0; i < nvqs; i++) {
    351         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
    352     }
    353 
    354     qemu_bh_cancel(s->bh);
    355     notify_guest_bh(s); /* final chance to notify guest */
    356 
    357     /* Clean up guest notifier (irq) */
    358     k->set_guest_notifiers(qbus->parent, nvqs, false);
    359 
    360     vblk->dataplane_started = false;
    361     s->stopping = false;
    362 }