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 "qom/object.h"
  24#include "hw/qdev-core.h"
  25#include "hw/qdev-properties.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    memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
  58}
  59
  60static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
  61{
  62    VHostUserBlk *s = VHOST_USER_BLK(vdev);
  63    struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
  64    int ret;
  65
  66    if (blkcfg->wce == s->blkcfg.wce) {
  67        return;
  68    }
  69
  70    ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
  71                               offsetof(struct virtio_blk_config, wce),
  72                               sizeof(blkcfg->wce),
  73                               VHOST_SET_CONFIG_TYPE_MASTER);
  74    if (ret) {
  75        error_report("set device config space failed");
  76        return;
  77    }
  78
  79    s->blkcfg.wce = blkcfg->wce;
  80}
  81
  82static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
  83{
  84    int ret;
  85    struct virtio_blk_config blkcfg;
  86    VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
  87
  88    ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
  89                               sizeof(struct virtio_blk_config));
  90    if (ret < 0) {
  91        error_report("get config space failed");
  92        return -1;
  93    }
  94
  95    /* valid for resize only */
  96    if (blkcfg.capacity != s->blkcfg.capacity) {
  97        s->blkcfg.capacity = blkcfg.capacity;
  98        memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config));
  99        virtio_notify_config(dev->vdev);
 100    }
 101
 102    return 0;
 103}
 104
 105const VhostDevConfigOps blk_ops = {
 106    .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
 107};
 108
 109static int vhost_user_blk_start(VirtIODevice *vdev)
 110{
 111    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 112    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 113    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 114    int i, ret;
 115
 116    if (!k->set_guest_notifiers) {
 117        error_report("binding does not support guest notifiers");
 118        return -ENOSYS;
 119    }
 120
 121    ret = vhost_dev_enable_notifiers(&s->dev, vdev);
 122    if (ret < 0) {
 123        error_report("Error enabling host notifiers: %d", -ret);
 124        return ret;
 125    }
 126
 127    ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
 128    if (ret < 0) {
 129        error_report("Error binding guest notifier: %d", -ret);
 130        goto err_host_notifiers;
 131    }
 132
 133    s->dev.acked_features = vdev->guest_features;
 134
 135    if (!s->inflight->addr) {
 136        ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight);
 137        if (ret < 0) {
 138            error_report("Error get inflight: %d", -ret);
 139            goto err_guest_notifiers;
 140        }
 141    }
 142
 143    ret = vhost_dev_set_inflight(&s->dev, s->inflight);
 144    if (ret < 0) {
 145        error_report("Error set inflight: %d", -ret);
 146        goto err_guest_notifiers;
 147    }
 148
 149    ret = vhost_dev_start(&s->dev, vdev);
 150    if (ret < 0) {
 151        error_report("Error starting vhost: %d", -ret);
 152        goto err_guest_notifiers;
 153    }
 154
 155    /* guest_notifier_mask/pending not used yet, so just unmask
 156     * everything here. virtio-pci will do the right thing by
 157     * enabling/disabling irqfd.
 158     */
 159    for (i = 0; i < s->dev.nvqs; i++) {
 160        vhost_virtqueue_mask(&s->dev, vdev, i, false);
 161    }
 162
 163    return ret;
 164
 165err_guest_notifiers:
 166    k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
 167err_host_notifiers:
 168    vhost_dev_disable_notifiers(&s->dev, vdev);
 169    return ret;
 170}
 171
 172static void vhost_user_blk_stop(VirtIODevice *vdev)
 173{
 174    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 175    BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
 176    VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
 177    int ret;
 178
 179    if (!k->set_guest_notifiers) {
 180        return;
 181    }
 182
 183    vhost_dev_stop(&s->dev, vdev);
 184
 185    ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
 186    if (ret < 0) {
 187        error_report("vhost guest notifier cleanup failed: %d", ret);
 188        return;
 189    }
 190
 191    vhost_dev_disable_notifiers(&s->dev, vdev);
 192}
 193
 194static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
 195{
 196    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 197    bool should_start = virtio_device_started(vdev, status);
 198    int ret;
 199
 200    if (!vdev->vm_running) {
 201        should_start = false;
 202    }
 203
 204    if (!s->connected) {
 205        return;
 206    }
 207
 208    if (s->dev.started == should_start) {
 209        return;
 210    }
 211
 212    if (should_start) {
 213        ret = vhost_user_blk_start(vdev);
 214        if (ret < 0) {
 215            error_report("vhost-user-blk: vhost start failed: %s",
 216                         strerror(-ret));
 217            qemu_chr_fe_disconnect(&s->chardev);
 218        }
 219    } else {
 220        vhost_user_blk_stop(vdev);
 221    }
 222
 223}
 224
 225static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
 226                                            uint64_t features,
 227                                            Error **errp)
 228{
 229    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 230
 231    /* Turn on pre-defined features */
 232    virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
 233    virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
 234    virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
 235    virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
 236    virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
 237    virtio_add_feature(&features, VIRTIO_BLK_F_RO);
 238    virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD);
 239    virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES);
 240
 241    if (s->config_wce) {
 242        virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
 243    }
 244    if (s->num_queues > 1) {
 245        virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
 246    }
 247
 248    return vhost_get_features(&s->dev, user_feature_bits, features);
 249}
 250
 251static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 252{
 253    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 254    int i, ret;
 255
 256    if (!vdev->start_on_kick) {
 257        return;
 258    }
 259
 260    if (!s->connected) {
 261        return;
 262    }
 263
 264    if (s->dev.started) {
 265        return;
 266    }
 267
 268    /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
 269     * vhost here instead of waiting for .set_status().
 270     */
 271    ret = vhost_user_blk_start(vdev);
 272    if (ret < 0) {
 273        error_report("vhost-user-blk: vhost start failed: %s",
 274                     strerror(-ret));
 275        qemu_chr_fe_disconnect(&s->chardev);
 276        return;
 277    }
 278
 279    /* Kick right away to begin processing requests already in vring */
 280    for (i = 0; i < s->dev.nvqs; i++) {
 281        VirtQueue *kick_vq = virtio_get_queue(vdev, i);
 282
 283        if (!virtio_queue_get_desc_addr(vdev, i)) {
 284            continue;
 285        }
 286        event_notifier_set(virtio_queue_get_host_notifier(kick_vq));
 287    }
 288}
 289
 290static void vhost_user_blk_reset(VirtIODevice *vdev)
 291{
 292    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 293
 294    vhost_dev_free_inflight(s->inflight);
 295}
 296
 297static int vhost_user_blk_connect(DeviceState *dev)
 298{
 299    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 300    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 301    int ret = 0;
 302
 303    if (s->connected) {
 304        return 0;
 305    }
 306    s->connected = true;
 307
 308    s->dev.nvqs = s->num_queues;
 309    s->dev.vqs = s->vqs;
 310    s->dev.vq_index = 0;
 311    s->dev.backend_features = 0;
 312
 313    vhost_dev_set_config_notifier(&s->dev, &blk_ops);
 314
 315    ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
 316    if (ret < 0) {
 317        error_report("vhost-user-blk: vhost initialization failed: %s",
 318                     strerror(-ret));
 319        return ret;
 320    }
 321
 322    /* restore vhost state */
 323    if (virtio_device_started(vdev, vdev->status)) {
 324        ret = vhost_user_blk_start(vdev);
 325        if (ret < 0) {
 326            error_report("vhost-user-blk: vhost start failed: %s",
 327                         strerror(-ret));
 328            return ret;
 329        }
 330    }
 331
 332    return 0;
 333}
 334
 335static void vhost_user_blk_disconnect(DeviceState *dev)
 336{
 337    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 338    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 339
 340    if (!s->connected) {
 341        return;
 342    }
 343    s->connected = false;
 344
 345    if (s->dev.started) {
 346        vhost_user_blk_stop(vdev);
 347    }
 348
 349    vhost_dev_cleanup(&s->dev);
 350}
 351
 352static gboolean vhost_user_blk_watch(GIOChannel *chan, GIOCondition cond,
 353                                     void *opaque)
 354{
 355    DeviceState *dev = opaque;
 356    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 357    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 358
 359    qemu_chr_fe_disconnect(&s->chardev);
 360
 361    return true;
 362}
 363
 364static void vhost_user_blk_event(void *opaque, int event)
 365{
 366    DeviceState *dev = opaque;
 367    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 368    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 369
 370    switch (event) {
 371    case CHR_EVENT_OPENED:
 372        if (vhost_user_blk_connect(dev) < 0) {
 373            qemu_chr_fe_disconnect(&s->chardev);
 374            return;
 375        }
 376        s->watch = qemu_chr_fe_add_watch(&s->chardev, G_IO_HUP,
 377                                         vhost_user_blk_watch, dev);
 378        break;
 379    case CHR_EVENT_CLOSED:
 380        vhost_user_blk_disconnect(dev);
 381        if (s->watch) {
 382            g_source_remove(s->watch);
 383            s->watch = 0;
 384        }
 385        break;
 386    }
 387}
 388
 389static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
 390{
 391    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 392    VHostUserBlk *s = VHOST_USER_BLK(vdev);
 393    Error *err = NULL;
 394    int i, ret;
 395
 396    if (!s->chardev.chr) {
 397        error_setg(errp, "vhost-user-blk: chardev is mandatory");
 398        return;
 399    }
 400
 401    if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
 402        error_setg(errp, "vhost-user-blk: invalid number of IO queues");
 403        return;
 404    }
 405
 406    if (!s->queue_size) {
 407        error_setg(errp, "vhost-user-blk: queue size must be non-zero");
 408        return;
 409    }
 410
 411    if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
 412        return;
 413    }
 414
 415    virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
 416                sizeof(struct virtio_blk_config));
 417
 418    for (i = 0; i < s->num_queues; i++) {
 419        virtio_add_queue(vdev, s->queue_size,
 420                         vhost_user_blk_handle_output);
 421    }
 422
 423    s->inflight = g_new0(struct vhost_inflight, 1);
 424    s->vqs = g_new0(struct vhost_virtqueue, s->num_queues);
 425    s->watch = 0;
 426    s->connected = false;
 427
 428    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, vhost_user_blk_event,
 429                             NULL, (void *)dev, NULL, true);
 430
 431reconnect:
 432    if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) {
 433        error_report_err(err);
 434        goto virtio_err;
 435    }
 436
 437    /* check whether vhost_user_blk_connect() failed or not */
 438    if (!s->connected) {
 439        goto reconnect;
 440    }
 441
 442    ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
 443                               sizeof(struct virtio_blk_config));
 444    if (ret < 0) {
 445        error_report("vhost-user-blk: get block config failed");
 446        goto reconnect;
 447    }
 448
 449    if (s->blkcfg.num_queues != s->num_queues) {
 450        s->blkcfg.num_queues = s->num_queues;
 451    }
 452
 453    return;
 454
 455virtio_err:
 456    g_free(s->vqs);
 457    g_free(s->inflight);
 458    for (i = 0; i < s->num_queues; i++) {
 459        virtio_del_queue(vdev, i);
 460    }
 461    virtio_cleanup(vdev);
 462    vhost_user_cleanup(&s->vhost_user);
 463}
 464
 465static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
 466{
 467    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 468    VHostUserBlk *s = VHOST_USER_BLK(dev);
 469    int i;
 470
 471    virtio_set_status(vdev, 0);
 472    qemu_chr_fe_set_handlers(&s->chardev,  NULL, NULL, NULL,
 473                             NULL, NULL, NULL, false);
 474    vhost_dev_cleanup(&s->dev);
 475    vhost_dev_free_inflight(s->inflight);
 476    g_free(s->vqs);
 477    g_free(s->inflight);
 478
 479    for (i = 0; i < s->num_queues; i++) {
 480        virtio_del_queue(vdev, i);
 481    }
 482    virtio_cleanup(vdev);
 483    vhost_user_cleanup(&s->vhost_user);
 484}
 485
 486static void vhost_user_blk_instance_init(Object *obj)
 487{
 488    VHostUserBlk *s = VHOST_USER_BLK(obj);
 489
 490    device_add_bootindex_property(obj, &s->bootindex, "bootindex",
 491                                  "/disk@0,0", DEVICE(obj), NULL);
 492}
 493
 494static const VMStateDescription vmstate_vhost_user_blk = {
 495    .name = "vhost-user-blk",
 496    .minimum_version_id = 1,
 497    .version_id = 1,
 498    .fields = (VMStateField[]) {
 499        VMSTATE_VIRTIO_DEVICE,
 500        VMSTATE_END_OF_LIST()
 501    },
 502};
 503
 504static Property vhost_user_blk_properties[] = {
 505    DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
 506    DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1),
 507    DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
 508    DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true),
 509    DEFINE_PROP_END_OF_LIST(),
 510};
 511
 512static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
 513{
 514    DeviceClass *dc = DEVICE_CLASS(klass);
 515    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 516
 517    dc->props = vhost_user_blk_properties;
 518    dc->vmsd = &vmstate_vhost_user_blk;
 519    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 520    vdc->realize = vhost_user_blk_device_realize;
 521    vdc->unrealize = vhost_user_blk_device_unrealize;
 522    vdc->get_config = vhost_user_blk_update_config;
 523    vdc->set_config = vhost_user_blk_set_config;
 524    vdc->get_features = vhost_user_blk_get_features;
 525    vdc->set_status = vhost_user_blk_set_status;
 526    vdc->reset = vhost_user_blk_reset;
 527}
 528
 529static const TypeInfo vhost_user_blk_info = {
 530    .name = TYPE_VHOST_USER_BLK,
 531    .parent = TYPE_VIRTIO_DEVICE,
 532    .instance_size = sizeof(VHostUserBlk),
 533    .instance_init = vhost_user_blk_instance_init,
 534    .class_init = vhost_user_blk_class_init,
 535};
 536
 537static void virtio_register_types(void)
 538{
 539    type_register_static(&vhost_user_blk_info);
 540}
 541
 542type_init(virtio_register_types)
 543