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