qemu/hw/block/vhost-user-blk.c
<<
>>
Prefs
   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/virtio/vhost.h"
  26#include "hw/virtio/vhost-user-blk.h"
  27#include "hw/virtio/virtio.h"
  28#include "hw/virtio/virtio-bus.h"
  29#include "hw/virtio/virtio-access.h"
  30#include "sysemu/sysemu.h"
  31#include "sysemu/runstate.h"
  32
  33static const int user_feature_bits[] = {
  34    VIRTIO_BLK_F_SIZE_MAX,
  35    VIRTIO_BLK_F_SEG_MAX,
  36    VIRTIO_BLK_F_GEOMETRY,
  37    VIRTIO_BLK_F_BLK_SIZE,
  38    VIRTIO_BLK_F_TOPOLOGY,
  39    VIRTIO_BLK_F_MQ,
  40    VIRTIO_BLK_F_RO,
  41    VIRTIO_BLK_F_FLUSH,
  42    VIRTIO_BLK_F_CONFIG_WCE,
  43    VIRTIO_BLK_F_DISCARD,
  44    VIRTIO_BLK_F_WRITE_ZEROES,
  45    VIRTIO_F_VERSION_1,
  46    VIRTIO_RING_F_INDIRECT_DESC,
  47    VIRTIO_RING_F_EVENT_IDX,
  48    VIRTIO_F_NOTIFY_ON_EMPTY,
  49    VHOST_INVALID_FEATURE_BIT
  50};
  51
  52static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
  53{
  54    VHostUserBlk *s = VHOST_USER_BLK(vdev);
  55
  56    memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
  57}
  58
  59static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
  60{
  61    VHostUserBlk *s = VHOST_USER_BLK(vdev);
  62    struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
  63    int ret;
  64
  65    if (blkcfg->wce == s->blkcfg.wce) {
  66        return;
  67    }
  68
  69    ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
  70                               offsetof(struct virtio_blk_config, wce),
  71                               sizeof(blkcfg->wce),
  72                               VHOST_SET_CONFIG_TYPE_MASTER);
  73    if (ret) {
  74        error_report("set device config space failed");
  75        return;
  76    }
  77
  78    s->blkcfg.wce = blkcfg->wce;
  79}
  80
  81static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
  82{
  83    int ret;
  84    struct virtio_blk_config blkcfg;
  85    VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
  86
  87    ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
  88                               sizeof(struct virtio_blk_config));
  89    if (ret < 0) {
  90        error_report("get config space failed");
  91        return -1;
  92    }
  93
  94    /* valid for resize only */
  95    if (blkcfg.capacity != s->blkcfg.capacity) {
  96        s->blkcfg.capacity = blkcfg.capacity;
  97        memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config));
  98        virtio_notify_config(dev->vdev);
  99    }
 100
 101    return 0;
 102}
 103
 104const VhostDevConfigOps blk_ops = {
 105    .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
 106};
 107
 108static int vhost_user_blk_start(VirtIODevice *vdev)
 109{
 110    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 111    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 112    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 113    int i, ret;
 114
 115    if (!k->set_guest_notifiers) {
 116        error_report("binding does not support guest notifiers");
 117        return -ENOSYS;
 118    }
 119
 120    ret = vhost_dev_enable_notifiers(&s->dev, vdev);
 121    if (ret < 0) {
 122        error_report("Error enabling host notifiers: %d", -ret);
 123        return ret;
 124    }
 125
 126    ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
 127    if (ret < 0) {
 128        error_report("Error binding guest notifier: %d", -ret);
 129        goto err_host_notifiers;
 130    }
 131
 132    s->dev.acked_features = vdev->guest_features;
 133
 134    ret = vhost_dev_prepare_inflight(&s->dev, vdev);
 135    if (ret < 0) {
 136        error_report("Error set inflight format: %d", -ret);
 137        goto err_guest_notifiers;
 138    }
 139
 140    if (!s->inflight->addr) {
 141        ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
 142        if (ret < 0) {
 143            error_report("Error get inflight: %d", -ret);
 144            goto err_guest_notifiers;
 145        }
 146    }
 147
 148    ret = vhost_dev_set_inflight(&s->dev, s->inflight);
 149    if (ret < 0) {
 150        error_report("Error set inflight: %d", -ret);
 151        goto err_guest_notifiers;
 152    }
 153
 154    ret = vhost_dev_start(&s->dev, vdev);
 155    if (ret < 0) {
 156        error_report("Error starting vhost: %d", -ret);
 157        goto err_guest_notifiers;
 158    }
 159    s->started_vu = true;
 160
 161    /* guest_notifier_mask/pending not used yet, so just unmask
 162     * everything here. virtio-pci will do the right thing by
 163     * enabling/disabling irqfd.
 164     */
 165    for (i = 0; i < s->dev.nvqs; i++) {
 166        vhost_virtqueue_mask(&s->dev, vdev, i, false);
 167    }
 168
 169    return ret;
 170
 171err_guest_notifiers:
 172    k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
 173err_host_notifiers:
 174    vhost_dev_disable_notifiers(&s->dev, vdev);
 175    return ret;
 176}
 177
 178static void vhost_user_blk_stop(VirtIODevice *vdev)
 179{
 180    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 181    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 182    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 183    int ret;
 184
 185    if (!s->started_vu) {
 186        return;
 187    }
 188    s->started_vu = false;
 189
 190    if (!k->set_guest_notifiers) {
 191        return;
 192    }
 193
 194    vhost_dev_stop(&s->dev, vdev);
 195
 196    ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
 197    if (ret < 0) {
 198        error_report("vhost guest notifier cleanup failed: %d", ret);
 199        return;
 200    }
 201
 202    vhost_dev_disable_notifiers(&s->dev, vdev);
 203}
 204
 205static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
 206{
 207    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 208    bool should_start = virtio_device_started(vdev, status);
 209    int ret;
 210
 211    if (!vdev->vm_running) {
 212        should_start = false;
 213    }
 214
 215    if (!s->connected) {
 216        return;
 217    }
 218
 219    if (s->dev.started == should_start) {
 220        return;
 221    }
 222
 223    if (should_start) {
 224        ret = vhost_user_blk_start(vdev);
 225        if (ret < 0) {
 226            error_report("vhost-user-blk: vhost start failed: %s",
 227                         strerror(-ret));
 228            qemu_chr_fe_disconnect(&s->chardev);
 229        }
 230    } else {
 231        vhost_user_blk_stop(vdev);
 232    }
 233
 234}
 235
 236static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
 237                                            uint64_t features,
 238                                            Error **errp)
 239{
 240    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 241
 242    /* Turn on pre-defined features */
 243    virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
 244    virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
 245    virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
 246    virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
 247    virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
 248    virtio_add_feature(&features, VIRTIO_BLK_F_RO);
 249    virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD);
 250    virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES);
 251
 252    if (s->config_wce) {
 253        virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
 254    }
 255    if (s->num_queues > 1) {
 256        virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
 257    }
 258
 259    return vhost_get_features(&s->dev, user_feature_bits, features);
 260}
 261
 262static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 263{
 264    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 265    int i, ret;
 266
 267    if (!vdev->start_on_kick) {
 268        return;
 269    }
 270
 271    if (!s->connected) {
 272        return;
 273    }
 274
 275    if (s->dev.started) {
 276        return;
 277    }
 278
 279    /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
 280     * vhost here instead of waiting for .set_status().
 281     */
 282    ret = vhost_user_blk_start(vdev);
 283    if (ret < 0) {
 284        error_report("vhost-user-blk: vhost start failed: %s",
 285                     strerror(-ret));
 286        qemu_chr_fe_disconnect(&s->chardev);
 287        return;
 288    }
 289
 290    /* Kick right away to begin processing requests already in vring */
 291    for (i = 0; i < s->dev.nvqs; i++) {
 292        VirtQueue *kick_vq = virtio_get_queue(vdev, i);
 293
 294        if (!virtio_queue_get_desc_addr(vdev, i)) {
 295            continue;
 296        }
 297        event_notifier_set(virtio_queue_get_host_notifier(kick_vq));
 298    }
 299}
 300
 301static void vhost_user_blk_reset(VirtIODevice *vdev)
 302{
 303    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 304
 305    vhost_dev_free_inflight(s->inflight);
 306}
 307
 308static int vhost_user_blk_connect(DeviceState *dev)
 309{
 310    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 311    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 312    int ret = 0;
 313
 314    if (s->connected) {
 315        return 0;
 316    }
 317    s->connected = true;
 318
 319    s->dev.nvqs = s->num_queues;
 320    s->dev.vqs = s->vhost_vqs;
 321    s->dev.vq_index = 0;
 322    s->dev.backend_features = 0;
 323
 324    vhost_dev_set_config_notifier(&s->dev, &blk_ops);
 325
 326    ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
 327    if (ret < 0) {
 328        error_report("vhost-user-blk: vhost initialization failed: %s",
 329                     strerror(-ret));
 330        return ret;
 331    }
 332
 333    /* restore vhost state */
 334    if (virtio_device_started(vdev, vdev->status)) {
 335        ret = vhost_user_blk_start(vdev);
 336        if (ret < 0) {
 337            error_report("vhost-user-blk: vhost start failed: %s",
 338                         strerror(-ret));
 339            return ret;
 340        }
 341    }
 342
 343    return 0;
 344}
 345
 346static void vhost_user_blk_disconnect(DeviceState *dev)
 347{
 348    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 349    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 350
 351    if (!s->connected) {
 352        return;
 353    }
 354    s->connected = false;
 355
 356    vhost_user_blk_stop(vdev);
 357
 358    vhost_dev_cleanup(&s->dev);
 359}
 360
 361static void vhost_user_blk_event(void *opaque, QEMUChrEvent event);
 362
 363static void vhost_user_blk_chr_closed_bh(void *opaque)
 364{
 365    DeviceState *dev = opaque;
 366    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 367    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 368
 369    vhost_user_blk_disconnect(dev);
 370    qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event,
 371            NULL, opaque, NULL, true);
 372}
 373
 374static void vhost_user_blk_event(void *opaque, QEMUChrEvent event)
 375{
 376    DeviceState *dev = opaque;
 377    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 378    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 379
 380    switch (event) {
 381    case CHR_EVENT_OPENED:
 382        if (vhost_user_blk_connect(dev) < 0) {
 383            qemu_chr_fe_disconnect(&s->chardev);
 384            return;
 385        }
 386        break;
 387    case CHR_EVENT_CLOSED:
 388        /*
 389         * A close event may happen during a read/write, but vhost
 390         * code assumes the vhost_dev remains setup, so delay the
 391         * stop & clear. There are two possible paths to hit this
 392         * disconnect event:
 393         * 1. When VM is in the RUN_STATE_PRELAUNCH state. The
 394         * vhost_user_blk_device_realize() is a caller.
 395         * 2. In tha main loop phase after VM start.
 396         *
 397         * For p2 the disconnect event will be delayed. We can't
 398         * do the same for p1, because we are not running the loop
 399         * at this moment. So just skip this step and perform
 400         * disconnect in the caller function.
 401         *
 402         * TODO: maybe it is a good idea to make the same fix
 403         * for other vhost-user devices.
 404         */
 405        if (runstate_is_running()) {
 406            AioContext *ctx = qemu_get_current_aio_context();
 407
 408            qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL, NULL,
 409                    NULL, NULL, false);
 410            aio_bh_schedule_oneshot(ctx, vhost_user_blk_chr_closed_bh, opaque);
 411        }
 412
 413        /*
 414         * Move vhost device to the stopped state. The vhost-user device
 415         * will be clean up and disconnected in BH. This can be useful in
 416         * the vhost migration code. If disconnect was caught there is an
 417         * option for the general vhost code to get the dev state without
 418         * knowing its type (in this case vhost-user).
 419         */
 420        s->dev.started = false;
 421        break;
 422    case CHR_EVENT_BREAK:
 423    case CHR_EVENT_MUX_IN:
 424    case CHR_EVENT_MUX_OUT:
 425        /* Ignore */
 426        break;
 427    }
 428}
 429
 430static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
 431{
 432    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 433    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 434    Error *err = NULL;
 435    int i, ret;
 436
 437    if (!s->chardev.chr) {
 438        error_setg(errp, "vhost-user-blk: chardev is mandatory");
 439        return;
 440    }
 441
 442    if (s->num_queues == VHOST_USER_BLK_AUTO_NUM_QUEUES) {
 443        s->num_queues = 1;
 444    }
 445    if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
 446        error_setg(errp, "vhost-user-blk: invalid number of IO queues");
 447        return;
 448    }
 449
 450    if (!s->queue_size) {
 451        error_setg(errp, "vhost-user-blk: queue size must be non-zero");
 452        return;
 453    }
 454
 455    if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
 456        return;
 457    }
 458
 459    virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
 460                sizeof(struct virtio_blk_config));
 461
 462    s->virtqs = g_new(VirtQueue *, s->num_queues);
 463    for (i = 0; i < s->num_queues; i++) {
 464        s->virtqs[i] = virtio_add_queue(vdev, s->queue_size,
 465                                        vhost_user_blk_handle_output);
 466    }
 467
 468    s->inflight = g_new0(struct vhost_inflight, 1);
 469    s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues);
 470    s->connected = false;
 471
 472    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, vhost_user_blk_event,
 473                             NULL, (void *)dev, NULL, true);
 474
 475reconnect:
 476    if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) {
 477        error_report_err(err);
 478        goto virtio_err;
 479    }
 480
 481    /* check whether vhost_user_blk_connect() failed or not */
 482    if (!s->connected) {
 483        goto reconnect;
 484    }
 485
 486    ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
 487                               sizeof(struct virtio_blk_config));
 488    if (ret < 0) {
 489        error_report("vhost-user-blk: get block config failed");
 490        goto reconnect;
 491    }
 492
 493    if (s->blkcfg.num_queues != s->num_queues) {
 494        s->blkcfg.num_queues = s->num_queues;
 495    }
 496
 497    return;
 498
 499virtio_err:
 500    g_free(s->vhost_vqs);
 501    s->vhost_vqs = NULL;
 502    g_free(s->inflight);
 503    s->inflight = NULL;
 504    for (i = 0; i < s->num_queues; i++) {
 505        virtio_delete_queue(s->virtqs[i]);
 506    }
 507    g_free(s->virtqs);
 508    virtio_cleanup(vdev);
 509    vhost_user_cleanup(&s->vhost_user);
 510}
 511
 512static void vhost_user_blk_device_unrealize(DeviceState *dev)
 513{
 514    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 515    VHostUserBlk *s = VHOST_USER_BLK(dev);
 516    int i;
 517
 518    virtio_set_status(vdev, 0);
 519    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, NULL,
 520                             NULL, NULL, NULL, false);
 521    vhost_dev_cleanup(&s->dev);
 522    vhost_dev_free_inflight(s->inflight);
 523    g_free(s->vhost_vqs);
 524    s->vhost_vqs = NULL;
 525    g_free(s->inflight);
 526    s->inflight = NULL;
 527
 528    for (i = 0; i < s->num_queues; i++) {
 529        virtio_delete_queue(s->virtqs[i]);
 530    }
 531    g_free(s->virtqs);
 532    virtio_cleanup(vdev);
 533    vhost_user_cleanup(&s->vhost_user);
 534}
 535
 536static void vhost_user_blk_instance_init(Object *obj)
 537{
 538    VHostUserBlk *s = VHOST_USER_BLK(obj);
 539
 540    device_add_bootindex_property(obj, &s->bootindex, "bootindex",
 541                                  "/disk@0,0", DEVICE(obj));
 542}
 543
 544static const VMStateDescription vmstate_vhost_user_blk = {
 545    .name = "vhost-user-blk",
 546    .minimum_version_id = 1,
 547    .version_id = 1,
 548    .fields = (VMStateField[]) {
 549        VMSTATE_VIRTIO_DEVICE,
 550        VMSTATE_END_OF_LIST()
 551    },
 552};
 553
 554static Property vhost_user_blk_properties[] = {
 555    DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
 556    DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues,
 557                       VHOST_USER_BLK_AUTO_NUM_QUEUES),
 558    DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
 559    DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true),
 560    DEFINE_PROP_END_OF_LIST(),
 561};
 562
 563static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
 564{
 565    DeviceClass *dc = DEVICE_CLASS(klass);
 566    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 567
 568    device_class_set_props(dc, vhost_user_blk_properties);
 569    dc->vmsd = &vmstate_vhost_user_blk;
 570    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 571    vdc->realize = vhost_user_blk_device_realize;
 572    vdc->unrealize = vhost_user_blk_device_unrealize;
 573    vdc->get_config = vhost_user_blk_update_config;
 574    vdc->set_config = vhost_user_blk_set_config;
 575    vdc->get_features = vhost_user_blk_get_features;
 576    vdc->set_status = vhost_user_blk_set_status;
 577    vdc->reset = vhost_user_blk_reset;
 578}
 579
 580static const TypeInfo vhost_user_blk_info = {
 581    .name = TYPE_VHOST_USER_BLK,
 582    .parent = TYPE_VIRTIO_DEVICE,
 583    .instance_size = sizeof(VHostUserBlk),
 584    .instance_init = vhost_user_blk_instance_init,
 585    .class_init = vhost_user_blk_class_init,
 586};
 587
 588static void virtio_register_types(void)
 589{
 590    type_register_static(&vhost_user_blk_info);
 591}
 592
 593type_init(virtio_register_types)
 594