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