qemu

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

vhost-user-blk-server.c (9588B)


      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 
     23 enum {
     24     VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
     25 };
     26 
     27 typedef struct VuBlkReq {
     28     VuVirtqElement elem;
     29     VuServer *server;
     30     struct VuVirtq *vq;
     31 } VuBlkReq;
     32 
     33 /* vhost user block device */
     34 typedef struct {
     35     BlockExport export;
     36     VuServer vu_server;
     37     VirtioBlkHandler handler;
     38     QIOChannelSocket *sioc;
     39     struct virtio_blk_config blkcfg;
     40 } VuBlkExport;
     41 
     42 static 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 */
     53 static 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 
     78 static 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 
    102 static 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 
    112 static 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 
    138 static uint64_t vu_blk_get_protocol_features(VuDev *dev)
    139 {
    140     return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
    141 }
    142 
    143 static int
    144 vu_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 
    157 static int
    158 vu_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  */
    189 static 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 
    198 static 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 
    207 static 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 
    215 static 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 
    223 static void
    224 vu_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 
    247 static 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 
    254 static 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 
    306 static 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 
    315 const 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 };