qemu

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

vhost-user-blk.c (17275B)


      1 /*
      2  * vhost-user-blk host device
      3  *
      4  * Copyright(C) 2017 Intel Corporation.
      5  *
      6  * Authors:
      7  *  Changpeng Liu <changpeng.liu@intel.com>
      8  *
      9  * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by:
     10  * Felipe Franciosi <felipe@nutanix.com>
     11  * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
     12  * Nicholas Bellinger <nab@risingtidesystems.com>
     13  *
     14  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
     15  * See the COPYING.LIB file in the top-level directory.
     16  *
     17  */
     18 
     19 #include "qemu/osdep.h"
     20 #include "qapi/error.h"
     21 #include "qemu/error-report.h"
     22 #include "qemu/cutils.h"
     23 #include "hw/qdev-core.h"
     24 #include "hw/qdev-properties.h"
     25 #include "hw/qdev-properties-system.h"
     26 #include "hw/virtio/virtio-blk-common.h"
     27 #include "hw/virtio/vhost.h"
     28 #include "hw/virtio/vhost-user-blk.h"
     29 #include "hw/virtio/virtio.h"
     30 #include "hw/virtio/virtio-bus.h"
     31 #include "hw/virtio/virtio-access.h"
     32 #include "sysemu/sysemu.h"
     33 #include "sysemu/runstate.h"
     34 
     35 #define REALIZE_CONNECTION_RETRIES 3
     36 
     37 static const int user_feature_bits[] = {
     38     VIRTIO_BLK_F_SIZE_MAX,
     39     VIRTIO_BLK_F_SEG_MAX,
     40     VIRTIO_BLK_F_GEOMETRY,
     41     VIRTIO_BLK_F_BLK_SIZE,
     42     VIRTIO_BLK_F_TOPOLOGY,
     43     VIRTIO_BLK_F_MQ,
     44     VIRTIO_BLK_F_RO,
     45     VIRTIO_BLK_F_FLUSH,
     46     VIRTIO_BLK_F_CONFIG_WCE,
     47     VIRTIO_BLK_F_DISCARD,
     48     VIRTIO_BLK_F_WRITE_ZEROES,
     49     VIRTIO_F_VERSION_1,
     50     VIRTIO_RING_F_INDIRECT_DESC,
     51     VIRTIO_RING_F_EVENT_IDX,
     52     VIRTIO_F_NOTIFY_ON_EMPTY,
     53     VIRTIO_F_RING_PACKED,
     54     VIRTIO_F_IOMMU_PLATFORM,
     55     VIRTIO_F_RING_RESET,
     56     VHOST_INVALID_FEATURE_BIT
     57 };
     58 
     59 static void vhost_user_blk_event(void *opaque, QEMUChrEvent event);
     60 
     61 static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
     62 {
     63     VHostUserBlk *s = VHOST_USER_BLK(vdev);
     64 
     65     /* Our num_queues overrides the device backend */
     66     virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues);
     67 
     68     memcpy(config, &s->blkcfg, vdev->config_len);
     69 }
     70 
     71 static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
     72 {
     73     VHostUserBlk *s = VHOST_USER_BLK(vdev);
     74     struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
     75     int ret;
     76 
     77     if (blkcfg->wce == s->blkcfg.wce) {
     78         return;
     79     }
     80 
     81     ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
     82                                offsetof(struct virtio_blk_config, wce),
     83                                sizeof(blkcfg->wce),
     84                                VHOST_SET_CONFIG_TYPE_MASTER);
     85     if (ret) {
     86         error_report("set device config space failed");
     87         return;
     88     }
     89 
     90     s->blkcfg.wce = blkcfg->wce;
     91 }
     92 
     93 static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
     94 {
     95     int ret;
     96     struct virtio_blk_config blkcfg;
     97     VirtIODevice *vdev = dev->vdev;
     98     VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
     99     Error *local_err = NULL;
    100 
    101     if (!dev->started) {
    102         return 0;
    103     }
    104 
    105     ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
    106                                vdev->config_len, &local_err);
    107     if (ret < 0) {
    108         error_report_err(local_err);
    109         return ret;
    110     }
    111 
    112     /* valid for resize only */
    113     if (blkcfg.capacity != s->blkcfg.capacity) {
    114         s->blkcfg.capacity = blkcfg.capacity;
    115         memcpy(dev->vdev->config, &s->blkcfg, vdev->config_len);
    116         virtio_notify_config(dev->vdev);
    117     }
    118 
    119     return 0;
    120 }
    121 
    122 const VhostDevConfigOps blk_ops = {
    123     .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
    124 };
    125 
    126 static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
    127 {
    128     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    129     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
    130     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    131     int i, ret;
    132 
    133     if (!k->set_guest_notifiers) {
    134         error_setg(errp, "binding does not support guest notifiers");
    135         return -ENOSYS;
    136     }
    137 
    138     ret = vhost_dev_enable_notifiers(&s->dev, vdev);
    139     if (ret < 0) {
    140         error_setg_errno(errp, -ret, "Error enabling host notifiers");
    141         return ret;
    142     }
    143 
    144     ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
    145     if (ret < 0) {
    146         error_setg_errno(errp, -ret, "Error binding guest notifier");
    147         goto err_host_notifiers;
    148     }
    149 
    150     s->dev.acked_features = vdev->guest_features;
    151 
    152     ret = vhost_dev_prepare_inflight(&s->dev, vdev);
    153     if (ret < 0) {
    154         error_setg_errno(errp, -ret, "Error setting inflight format");
    155         goto err_guest_notifiers;
    156     }
    157 
    158     if (!s->inflight->addr) {
    159         ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
    160         if (ret < 0) {
    161             error_setg_errno(errp, -ret, "Error getting inflight");
    162             goto err_guest_notifiers;
    163         }
    164     }
    165 
    166     ret = vhost_dev_set_inflight(&s->dev, s->inflight);
    167     if (ret < 0) {
    168         error_setg_errno(errp, -ret, "Error setting inflight");
    169         goto err_guest_notifiers;
    170     }
    171 
    172     /* guest_notifier_mask/pending not used yet, so just unmask
    173      * everything here. virtio-pci will do the right thing by
    174      * enabling/disabling irqfd.
    175      */
    176     for (i = 0; i < s->dev.nvqs; i++) {
    177         vhost_virtqueue_mask(&s->dev, vdev, i, false);
    178     }
    179 
    180     s->dev.vq_index_end = s->dev.nvqs;
    181     ret = vhost_dev_start(&s->dev, vdev, true);
    182     if (ret < 0) {
    183         error_setg_errno(errp, -ret, "Error starting vhost");
    184         goto err_guest_notifiers;
    185     }
    186     s->started_vu = true;
    187 
    188     return ret;
    189 
    190 err_guest_notifiers:
    191     for (i = 0; i < s->dev.nvqs; i++) {
    192         vhost_virtqueue_mask(&s->dev, vdev, i, true);
    193     }
    194     k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
    195 err_host_notifiers:
    196     vhost_dev_disable_notifiers(&s->dev, vdev);
    197     return ret;
    198 }
    199 
    200 static void vhost_user_blk_stop(VirtIODevice *vdev)
    201 {
    202     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    203     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
    204     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
    205     int ret;
    206 
    207     if (!s->started_vu) {
    208         return;
    209     }
    210     s->started_vu = false;
    211 
    212     if (!k->set_guest_notifiers) {
    213         return;
    214     }
    215 
    216     vhost_dev_stop(&s->dev, vdev, true);
    217 
    218     ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
    219     if (ret < 0) {
    220         error_report("vhost guest notifier cleanup failed: %d", ret);
    221         return;
    222     }
    223 
    224     vhost_dev_disable_notifiers(&s->dev, vdev);
    225 }
    226 
    227 static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
    228 {
    229     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    230     bool should_start = virtio_device_should_start(vdev, status);
    231     Error *local_err = NULL;
    232     int ret;
    233 
    234     if (!s->connected) {
    235         return;
    236     }
    237 
    238     if (vhost_dev_is_started(&s->dev) == should_start) {
    239         return;
    240     }
    241 
    242     if (should_start) {
    243         ret = vhost_user_blk_start(vdev, &local_err);
    244         if (ret < 0) {
    245             error_reportf_err(local_err, "vhost-user-blk: vhost start failed: ");
    246             qemu_chr_fe_disconnect(&s->chardev);
    247         }
    248     } else {
    249         vhost_user_blk_stop(vdev);
    250     }
    251 
    252 }
    253 
    254 static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
    255                                             uint64_t features,
    256                                             Error **errp)
    257 {
    258     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    259 
    260     /* Turn on pre-defined features */
    261     virtio_add_feature(&features, VIRTIO_BLK_F_SIZE_MAX);
    262     virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
    263     virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
    264     virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
    265     virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
    266     virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
    267     virtio_add_feature(&features, VIRTIO_BLK_F_RO);
    268 
    269     if (s->num_queues > 1) {
    270         virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
    271     }
    272 
    273     return vhost_get_features(&s->dev, user_feature_bits, features);
    274 }
    275 
    276 static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
    277 {
    278     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    279     Error *local_err = NULL;
    280     int i, ret;
    281 
    282     if (!vdev->start_on_kick) {
    283         return;
    284     }
    285 
    286     if (!s->connected) {
    287         return;
    288     }
    289 
    290     if (vhost_dev_is_started(&s->dev)) {
    291         return;
    292     }
    293 
    294     /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
    295      * vhost here instead of waiting for .set_status().
    296      */
    297     ret = vhost_user_blk_start(vdev, &local_err);
    298     if (ret < 0) {
    299         error_reportf_err(local_err, "vhost-user-blk: vhost start failed: ");
    300         qemu_chr_fe_disconnect(&s->chardev);
    301         return;
    302     }
    303 
    304     /* Kick right away to begin processing requests already in vring */
    305     for (i = 0; i < s->dev.nvqs; i++) {
    306         VirtQueue *kick_vq = virtio_get_queue(vdev, i);
    307 
    308         if (!virtio_queue_get_desc_addr(vdev, i)) {
    309             continue;
    310         }
    311         event_notifier_set(virtio_queue_get_host_notifier(kick_vq));
    312     }
    313 }
    314 
    315 static void vhost_user_blk_reset(VirtIODevice *vdev)
    316 {
    317     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    318 
    319     vhost_dev_free_inflight(s->inflight);
    320 }
    321 
    322 static int vhost_user_blk_connect(DeviceState *dev, Error **errp)
    323 {
    324     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    325     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    326     int ret = 0;
    327 
    328     if (s->connected) {
    329         return 0;
    330     }
    331     s->connected = true;
    332 
    333     s->dev.num_queues = s->num_queues;
    334     s->dev.nvqs = s->num_queues;
    335     s->dev.vqs = s->vhost_vqs;
    336     s->dev.vq_index = 0;
    337     s->dev.backend_features = 0;
    338 
    339     vhost_dev_set_config_notifier(&s->dev, &blk_ops);
    340 
    341     s->vhost_user.supports_config = true;
    342     ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
    343                          errp);
    344     if (ret < 0) {
    345         return ret;
    346     }
    347 
    348     /* restore vhost state */
    349     if (virtio_device_started(vdev, vdev->status)) {
    350         ret = vhost_user_blk_start(vdev, errp);
    351         if (ret < 0) {
    352             return ret;
    353         }
    354     }
    355 
    356     return 0;
    357 }
    358 
    359 static void vhost_user_blk_disconnect(DeviceState *dev)
    360 {
    361     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    362     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    363 
    364     if (!s->connected) {
    365         return;
    366     }
    367     s->connected = false;
    368 
    369     vhost_user_blk_stop(vdev);
    370 
    371     vhost_dev_cleanup(&s->dev);
    372 
    373     /* Re-instate the event handler for new connections */
    374     qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event,
    375                              NULL, dev, NULL, true);
    376 }
    377 
    378 static void vhost_user_blk_event(void *opaque, QEMUChrEvent event)
    379 {
    380     DeviceState *dev = opaque;
    381     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    382     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    383     Error *local_err = NULL;
    384 
    385     switch (event) {
    386     case CHR_EVENT_OPENED:
    387         if (vhost_user_blk_connect(dev, &local_err) < 0) {
    388             error_report_err(local_err);
    389             qemu_chr_fe_disconnect(&s->chardev);
    390             return;
    391         }
    392         break;
    393     case CHR_EVENT_CLOSED:
    394         /* defer close until later to avoid circular close */
    395         vhost_user_async_close(dev, &s->chardev, &s->dev,
    396                                vhost_user_blk_disconnect);
    397         break;
    398     case CHR_EVENT_BREAK:
    399     case CHR_EVENT_MUX_IN:
    400     case CHR_EVENT_MUX_OUT:
    401         /* Ignore */
    402         break;
    403     }
    404 }
    405 
    406 static int vhost_user_blk_realize_connect(VHostUserBlk *s, Error **errp)
    407 {
    408     DeviceState *dev = &s->parent_obj.parent_obj;
    409     int ret;
    410 
    411     s->connected = false;
    412 
    413     ret = qemu_chr_fe_wait_connected(&s->chardev, errp);
    414     if (ret < 0) {
    415         return ret;
    416     }
    417 
    418     ret = vhost_user_blk_connect(dev, errp);
    419     if (ret < 0) {
    420         qemu_chr_fe_disconnect(&s->chardev);
    421         return ret;
    422     }
    423     assert(s->connected);
    424 
    425     ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
    426                                s->parent_obj.config_len, errp);
    427     if (ret < 0) {
    428         qemu_chr_fe_disconnect(&s->chardev);
    429         vhost_dev_cleanup(&s->dev);
    430         return ret;
    431     }
    432 
    433     return 0;
    434 }
    435 
    436 static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
    437 {
    438     ERRP_GUARD();
    439     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    440     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    441     size_t config_size;
    442     int retries;
    443     int i, ret;
    444 
    445     if (!s->chardev.chr) {
    446         error_setg(errp, "chardev is mandatory");
    447         return;
    448     }
    449 
    450     if (s->num_queues == VHOST_USER_BLK_AUTO_NUM_QUEUES) {
    451         s->num_queues = 1;
    452     }
    453     if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
    454         error_setg(errp, "invalid number of IO queues");
    455         return;
    456     }
    457 
    458     if (!s->queue_size) {
    459         error_setg(errp, "queue size must be non-zero");
    460         return;
    461     }
    462     if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
    463         error_setg(errp, "queue size must not exceed %d",
    464                    VIRTQUEUE_MAX_SIZE);
    465         return;
    466     }
    467 
    468     if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
    469         return;
    470     }
    471 
    472     config_size = virtio_get_config_size(&virtio_blk_cfg_size_params,
    473                                          vdev->host_features);
    474     virtio_init(vdev, VIRTIO_ID_BLOCK, config_size);
    475 
    476     s->virtqs = g_new(VirtQueue *, s->num_queues);
    477     for (i = 0; i < s->num_queues; i++) {
    478         s->virtqs[i] = virtio_add_queue(vdev, s->queue_size,
    479                                         vhost_user_blk_handle_output);
    480     }
    481 
    482     s->inflight = g_new0(struct vhost_inflight, 1);
    483     s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
    484 
    485     retries = REALIZE_CONNECTION_RETRIES;
    486     assert(!*errp);
    487     do {
    488         if (*errp) {
    489             error_prepend(errp, "Reconnecting after error: ");
    490             error_report_err(*errp);
    491             *errp = NULL;
    492         }
    493         ret = vhost_user_blk_realize_connect(s, errp);
    494     } while (ret < 0 && retries--);
    495 
    496     if (ret < 0) {
    497         goto virtio_err;
    498     }
    499 
    500     /* we're fully initialized, now we can operate, so add the handler */
    501     qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL,
    502                              vhost_user_blk_event, NULL, (void *)dev,
    503                              NULL, true);
    504     return;
    505 
    506 virtio_err:
    507     g_free(s->vhost_vqs);
    508     s->vhost_vqs = NULL;
    509     g_free(s->inflight);
    510     s->inflight = NULL;
    511     for (i = 0; i < s->num_queues; i++) {
    512         virtio_delete_queue(s->virtqs[i]);
    513     }
    514     g_free(s->virtqs);
    515     virtio_cleanup(vdev);
    516     vhost_user_cleanup(&s->vhost_user);
    517 }
    518 
    519 static void vhost_user_blk_device_unrealize(DeviceState *dev)
    520 {
    521     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
    522     VHostUserBlk *s = VHOST_USER_BLK(dev);
    523     int i;
    524 
    525     virtio_set_status(vdev, 0);
    526     qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, NULL,
    527                              NULL, NULL, NULL, false);
    528     vhost_dev_cleanup(&s->dev);
    529     vhost_dev_free_inflight(s->inflight);
    530     g_free(s->vhost_vqs);
    531     s->vhost_vqs = NULL;
    532     g_free(s->inflight);
    533     s->inflight = NULL;
    534 
    535     for (i = 0; i < s->num_queues; i++) {
    536         virtio_delete_queue(s->virtqs[i]);
    537     }
    538     g_free(s->virtqs);
    539     virtio_cleanup(vdev);
    540     vhost_user_cleanup(&s->vhost_user);
    541 }
    542 
    543 static void vhost_user_blk_instance_init(Object *obj)
    544 {
    545     VHostUserBlk *s = VHOST_USER_BLK(obj);
    546 
    547     device_add_bootindex_property(obj, &s->bootindex, "bootindex",
    548                                   "/disk@0,0", DEVICE(obj));
    549 }
    550 
    551 static struct vhost_dev *vhost_user_blk_get_vhost(VirtIODevice *vdev)
    552 {
    553     VHostUserBlk *s = VHOST_USER_BLK(vdev);
    554     return &s->dev;
    555 }
    556 
    557 static const VMStateDescription vmstate_vhost_user_blk = {
    558     .name = "vhost-user-blk",
    559     .minimum_version_id = 1,
    560     .version_id = 1,
    561     .fields = (VMStateField[]) {
    562         VMSTATE_VIRTIO_DEVICE,
    563         VMSTATE_END_OF_LIST()
    564     },
    565 };
    566 
    567 static Property vhost_user_blk_properties[] = {
    568     DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
    569     DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
    570                        VHOST_USER_BLK_AUTO_NUM_QUEUES),
    571     DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
    572     DEFINE_PROP_BIT64("config-wce", VHostUserBlk, parent_obj.host_features,
    573                       VIRTIO_BLK_F_CONFIG_WCE, true),
    574     DEFINE_PROP_BIT64("discard", VHostUserBlk, parent_obj.host_features,
    575                       VIRTIO_BLK_F_DISCARD, true),
    576     DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features,
    577                       VIRTIO_BLK_F_WRITE_ZEROES, true),
    578     DEFINE_PROP_END_OF_LIST(),
    579 };
    580 
    581 static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
    582 {
    583     DeviceClass *dc = DEVICE_CLASS(klass);
    584     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
    585 
    586     device_class_set_props(dc, vhost_user_blk_properties);
    587     dc->vmsd = &vmstate_vhost_user_blk;
    588     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
    589     vdc->realize = vhost_user_blk_device_realize;
    590     vdc->unrealize = vhost_user_blk_device_unrealize;
    591     vdc->get_config = vhost_user_blk_update_config;
    592     vdc->set_config = vhost_user_blk_set_config;
    593     vdc->get_features = vhost_user_blk_get_features;
    594     vdc->set_status = vhost_user_blk_set_status;
    595     vdc->reset = vhost_user_blk_reset;
    596     vdc->get_vhost = vhost_user_blk_get_vhost;
    597 }
    598 
    599 static const TypeInfo vhost_user_blk_info = {
    600     .name = TYPE_VHOST_USER_BLK,
    601     .parent = TYPE_VIRTIO_DEVICE,
    602     .instance_size = sizeof(VHostUserBlk),
    603     .instance_init = vhost_user_blk_instance_init,
    604     .class_init = vhost_user_blk_class_init,
    605 };
    606 
    607 static void virtio_register_types(void)
    608 {
    609     type_register_static(&vhost_user_blk_info);
    610 }
    611 
    612 type_init(virtio_register_types)