xref: /qemu/block/export/vhost-user-blk-server.c (revision 8afc43ea)
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