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