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 = "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 return -EADDRNOTAVAIL; 300 } 301 302 return 0; 303 } 304 305 static void vu_blk_exp_delete(BlockExport *exp) 306 { 307 VuBlkExport *vexp = container_of(exp, VuBlkExport, export); 308 309 blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, 310 vexp); 311 } 312 313 const BlockExportDriver blk_exp_vhost_user_blk = { 314 .type = BLOCK_EXPORT_TYPE_VHOST_USER_BLK, 315 .instance_size = sizeof(VuBlkExport), 316 .create = vu_blk_exp_create, 317 .delete = vu_blk_exp_delete, 318 .request_shutdown = vu_blk_exp_request_shutdown, 319 }; 320