qemu/block/export/vhost-user-blk-server.c
<<
>>
Prefs
   1/*
   2 * Sharing QEMU block devices via vhost-user protocal
   3 *
   4 * Parts of the code based on nbd/server.c.
   5 *
   6 * Copyright (c) Coiby Xu <coiby.xu@gmail.com>.
   7 * Copyright (c) 2020 Red Hat, Inc.
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or
  10 * later.  See the COPYING file in the top-level directory.
  11 */
  12#include "qemu/osdep.h"
  13#include "block/block.h"
  14#include "subprojects/libvhost-user/libvhost-user.h" /* only for the type definitions */
  15#include "standard-headers/linux/virtio_blk.h"
  16#include "qemu/vhost-user-server.h"
  17#include "vhost-user-blk-server.h"
  18#include "qapi/error.h"
  19#include "qom/object_interfaces.h"
  20#include "util/block-helpers.h"
  21#include "virtio-blk-handler.h"
  22
  23enum {
  24    VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
  25};
  26
  27typedef struct VuBlkReq {
  28    VuVirtqElement elem;
  29    VuServer *server;
  30    struct VuVirtq *vq;
  31} VuBlkReq;
  32
  33/* vhost user block device */
  34typedef struct {
  35    BlockExport export;
  36    VuServer vu_server;
  37    VirtioBlkHandler handler;
  38    QIOChannelSocket *sioc;
  39    struct virtio_blk_config blkcfg;
  40} VuBlkExport;
  41
  42static void vu_blk_req_complete(VuBlkReq *req, size_t in_len)
  43{
  44    VuDev *vu_dev = &req->server->vu_dev;
  45
  46    vu_queue_push(vu_dev, req->vq, &req->elem, in_len);
  47    vu_queue_notify(vu_dev, req->vq);
  48
  49    free(req);
  50}
  51
  52/* Called with server refcount increased, must decrease before returning */
  53static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
  54{
  55    VuBlkReq *req = opaque;
  56    VuServer *server = req->server;
  57    VuVirtqElement *elem = &req->elem;
  58    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
  59    VirtioBlkHandler *handler = &vexp->handler;
  60    struct iovec *in_iov = elem->in_sg;
  61    struct iovec *out_iov = elem->out_sg;
  62    unsigned in_num = elem->in_num;
  63    unsigned out_num = elem->out_num;
  64    int in_len;
  65
  66    in_len = virtio_blk_process_req(handler, in_iov, out_iov,
  67                                    in_num, out_num);
  68    if (in_len < 0) {
  69        free(req);
  70        vhost_user_server_unref(server);
  71        return;
  72    }
  73
  74    vu_blk_req_complete(req, in_len);
  75    vhost_user_server_unref(server);
  76}
  77
  78static void vu_blk_process_vq(VuDev *vu_dev, int idx)
  79{
  80    VuServer *server = container_of(vu_dev, VuServer, vu_dev);
  81    VuVirtq *vq = vu_get_queue(vu_dev, idx);
  82
  83    while (1) {
  84        VuBlkReq *req;
  85
  86        req = vu_queue_pop(vu_dev, vq, sizeof(VuBlkReq));
  87        if (!req) {
  88            break;
  89        }
  90
  91        req->server = server;
  92        req->vq = vq;
  93
  94        Coroutine *co =
  95            qemu_coroutine_create(vu_blk_virtio_process_req, req);
  96
  97        vhost_user_server_ref(server);
  98        qemu_coroutine_enter(co);
  99    }
 100}
 101
 102static void vu_blk_queue_set_started(VuDev *vu_dev, int idx, bool started)
 103{
 104    VuVirtq *vq;
 105
 106    assert(vu_dev);
 107
 108    vq = vu_get_queue(vu_dev, idx);
 109    vu_set_queue_handler(vu_dev, vq, started ? vu_blk_process_vq : NULL);
 110}
 111
 112static uint64_t vu_blk_get_features(VuDev *dev)
 113{
 114    uint64_t features;
 115    VuServer *server = container_of(dev, VuServer, vu_dev);
 116    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
 117    features = 1ull << VIRTIO_BLK_F_SIZE_MAX |
 118               1ull << VIRTIO_BLK_F_SEG_MAX |
 119               1ull << VIRTIO_BLK_F_TOPOLOGY |
 120               1ull << VIRTIO_BLK_F_BLK_SIZE |
 121               1ull << VIRTIO_BLK_F_FLUSH |
 122               1ull << VIRTIO_BLK_F_DISCARD |
 123               1ull << VIRTIO_BLK_F_WRITE_ZEROES |
 124               1ull << VIRTIO_BLK_F_CONFIG_WCE |
 125               1ull << VIRTIO_BLK_F_MQ |
 126               1ull << VIRTIO_F_VERSION_1 |
 127               1ull << VIRTIO_RING_F_INDIRECT_DESC |
 128               1ull << VIRTIO_RING_F_EVENT_IDX |
 129               1ull << VHOST_USER_F_PROTOCOL_FEATURES;
 130
 131    if (!vexp->handler.writable) {
 132        features |= 1ull << VIRTIO_BLK_F_RO;
 133    }
 134
 135    return features;
 136}
 137
 138static uint64_t vu_blk_get_protocol_features(VuDev *dev)
 139{
 140    return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
 141}
 142
 143static int
 144vu_blk_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
 145{
 146    VuServer *server = container_of(vu_dev, VuServer, vu_dev);
 147    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
 148
 149    if (len > sizeof(struct virtio_blk_config)) {
 150        return -1;
 151    }
 152
 153    memcpy(config, &vexp->blkcfg, len);
 154    return 0;
 155}
 156
 157static int
 158vu_blk_set_config(VuDev *vu_dev, const uint8_t *data,
 159                    uint32_t offset, uint32_t size, uint32_t flags)
 160{
 161    VuServer *server = container_of(vu_dev, VuServer, vu_dev);
 162    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
 163    uint8_t wce;
 164
 165    /* don't support live migration */
 166    if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
 167        return -EINVAL;
 168    }
 169
 170    if (offset != offsetof(struct virtio_blk_config, wce) ||
 171        size != 1) {
 172        return -EINVAL;
 173    }
 174
 175    wce = *data;
 176    vexp->blkcfg.wce = wce;
 177    blk_set_enable_write_cache(vexp->export.blk, wce);
 178    return 0;
 179}
 180
 181/*
 182 * When the client disconnects, it sends a VHOST_USER_NONE request
 183 * and vu_process_message will simple call exit which cause the VM
 184 * to exit abruptly.
 185 * To avoid this issue,  process VHOST_USER_NONE request ahead
 186 * of vu_process_message.
 187 *
 188 */
 189static int vu_blk_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
 190{
 191    if (vmsg->request == VHOST_USER_NONE) {
 192        dev->panic(dev, "disconnect");
 193        return true;
 194    }
 195    return false;
 196}
 197
 198static const VuDevIface vu_blk_iface = {
 199    .get_features          = vu_blk_get_features,
 200    .queue_set_started     = vu_blk_queue_set_started,
 201    .get_protocol_features = vu_blk_get_protocol_features,
 202    .get_config            = vu_blk_get_config,
 203    .set_config            = vu_blk_set_config,
 204    .process_msg           = vu_blk_process_msg,
 205};
 206
 207static void blk_aio_attached(AioContext *ctx, void *opaque)
 208{
 209    VuBlkExport *vexp = opaque;
 210
 211    vexp->export.ctx = ctx;
 212    vhost_user_server_attach_aio_context(&vexp->vu_server, ctx);
 213}
 214
 215static void blk_aio_detach(void *opaque)
 216{
 217    VuBlkExport *vexp = opaque;
 218
 219    vhost_user_server_detach_aio_context(&vexp->vu_server);
 220    vexp->export.ctx = NULL;
 221}
 222
 223static void
 224vu_blk_initialize_config(BlockDriverState *bs,
 225                         struct virtio_blk_config *config,
 226                         uint32_t blk_size,
 227                         uint16_t num_queues)
 228{
 229    config->capacity =
 230        cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS);
 231    config->blk_size = cpu_to_le32(blk_size);
 232    config->size_max = cpu_to_le32(0);
 233    config->seg_max = cpu_to_le32(128 - 2);
 234    config->min_io_size = cpu_to_le16(1);
 235    config->opt_io_size = cpu_to_le32(1);
 236    config->num_queues = cpu_to_le16(num_queues);
 237    config->max_discard_sectors =
 238        cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS);
 239    config->max_discard_seg = cpu_to_le32(1);
 240    config->discard_sector_alignment =
 241        cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
 242    config->max_write_zeroes_sectors
 243        = cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS);
 244    config->max_write_zeroes_seg = cpu_to_le32(1);
 245}
 246
 247static void vu_blk_exp_request_shutdown(BlockExport *exp)
 248{
 249    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
 250
 251    vhost_user_server_stop(&vexp->vu_server);
 252}
 253
 254static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
 255                             Error **errp)
 256{
 257    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
 258    BlockExportOptionsVhostUserBlk *vu_opts = &opts->u.vhost_user_blk;
 259    Error *local_err = NULL;
 260    uint64_t logical_block_size;
 261    uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT;
 262
 263    vexp->blkcfg.wce = 0;
 264
 265    if (vu_opts->has_logical_block_size) {
 266        logical_block_size = vu_opts->logical_block_size;
 267    } else {
 268        logical_block_size = VIRTIO_BLK_SECTOR_SIZE;
 269    }
 270    check_block_size(exp->id, "logical-block-size", logical_block_size,
 271                     &local_err);
 272    if (local_err) {
 273        error_propagate(errp, local_err);
 274        return -EINVAL;
 275    }
 276
 277    if (vu_opts->has_num_queues) {
 278        num_queues = vu_opts->num_queues;
 279    }
 280    if (num_queues == 0) {
 281        error_setg(errp, "num-queues must be greater than 0");
 282        return -EINVAL;
 283    }
 284    vexp->handler.blk = exp->blk;
 285    vexp->handler.serial = g_strdup("vhost_user_blk");
 286    vexp->handler.logical_block_size = logical_block_size;
 287    vexp->handler.writable = opts->writable;
 288
 289    vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg,
 290                             logical_block_size, num_queues);
 291
 292    blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
 293                                 vexp);
 294
 295    if (!vhost_user_server_start(&vexp->vu_server, vu_opts->addr, exp->ctx,
 296                                 num_queues, &vu_blk_iface, errp)) {
 297        blk_remove_aio_context_notifier(exp->blk, blk_aio_attached,
 298                                        blk_aio_detach, vexp);
 299        g_free(vexp->handler.serial);
 300        return -EADDRNOTAVAIL;
 301    }
 302
 303    return 0;
 304}
 305
 306static void vu_blk_exp_delete(BlockExport *exp)
 307{
 308    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
 309
 310    blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
 311                                    vexp);
 312    g_free(vexp->handler.serial);
 313}
 314
 315const BlockExportDriver blk_exp_vhost_user_blk = {
 316    .type               = BLOCK_EXPORT_TYPE_VHOST_USER_BLK,
 317    .instance_size      = sizeof(VuBlkExport),
 318    .create             = vu_blk_exp_create,
 319    .delete             = vu_blk_exp_delete,
 320    .request_shutdown   = vu_blk_exp_request_shutdown,
 321};
 322