]> git.proxmox.com Git - mirror_qemu.git/blob - block/export/vhost-user-blk-server.c
Merge tag 'pull-qapi-2023-04-26' of https://repo.or.cz/qemu/armbru into staging
[mirror_qemu.git] / block / export / vhost-user-blk-server.c
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 };