2 * Sharing QEMU block devices via vhost-user protocal
4 * Parts of the code based on nbd/server.c.
6 * Copyright (c) Coiby Xu <coiby.xu@gmail.com>.
7 * Copyright (c) 2020 Red Hat, Inc.
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.
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"
24 VHOST_USER_BLK_NUM_QUEUES_DEFAULT
= 1,
27 typedef struct VuBlkReq
{
33 /* vhost user block device */
37 VirtioBlkHandler handler
;
38 QIOChannelSocket
*sioc
;
39 struct virtio_blk_config blkcfg
;
42 static void vu_blk_req_complete(VuBlkReq
*req
, size_t in_len
)
44 VuDev
*vu_dev
= &req
->server
->vu_dev
;
46 vu_queue_push(vu_dev
, req
->vq
, &req
->elem
, in_len
);
47 vu_queue_notify(vu_dev
, req
->vq
);
52 /* Called with server refcount increased, must decrease before returning */
53 static void coroutine_fn
vu_blk_virtio_process_req(void *opaque
)
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
;
66 in_len
= virtio_blk_process_req(handler
, in_iov
, out_iov
,
70 vhost_user_server_unref(server
);
74 vu_blk_req_complete(req
, in_len
);
75 vhost_user_server_unref(server
);
78 static void vu_blk_process_vq(VuDev
*vu_dev
, int idx
)
80 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
81 VuVirtq
*vq
= vu_get_queue(vu_dev
, idx
);
86 req
= vu_queue_pop(vu_dev
, vq
, sizeof(VuBlkReq
));
95 qemu_coroutine_create(vu_blk_virtio_process_req
, req
);
97 vhost_user_server_ref(server
);
98 qemu_coroutine_enter(co
);
102 static void vu_blk_queue_set_started(VuDev
*vu_dev
, int idx
, bool started
)
108 vq
= vu_get_queue(vu_dev
, idx
);
109 vu_set_queue_handler(vu_dev
, vq
, started
? vu_blk_process_vq
: NULL
);
112 static uint64_t vu_blk_get_features(VuDev
*dev
)
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
;
131 if (!vexp
->handler
.writable
) {
132 features
|= 1ull << VIRTIO_BLK_F_RO
;
138 static uint64_t vu_blk_get_protocol_features(VuDev
*dev
)
140 return 1ull << VHOST_USER_PROTOCOL_F_CONFIG
;
144 vu_blk_get_config(VuDev
*vu_dev
, uint8_t *config
, uint32_t len
)
146 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
147 VuBlkExport
*vexp
= container_of(server
, VuBlkExport
, vu_server
);
149 if (len
> sizeof(struct virtio_blk_config
)) {
153 memcpy(config
, &vexp
->blkcfg
, len
);
158 vu_blk_set_config(VuDev
*vu_dev
, const uint8_t *data
,
159 uint32_t offset
, uint32_t size
, uint32_t flags
)
161 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
162 VuBlkExport
*vexp
= container_of(server
, VuBlkExport
, vu_server
);
165 /* don't support live migration */
166 if (flags
!= VHOST_SET_CONFIG_TYPE_MASTER
) {
170 if (offset
!= offsetof(struct virtio_blk_config
, wce
) ||
176 vexp
->blkcfg
.wce
= wce
;
177 blk_set_enable_write_cache(vexp
->export
.blk
, wce
);
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
185 * To avoid this issue, process VHOST_USER_NONE request ahead
186 * of vu_process_message.
189 static int vu_blk_process_msg(VuDev
*dev
, VhostUserMsg
*vmsg
, int *do_reply
)
191 if (vmsg
->request
== VHOST_USER_NONE
) {
192 dev
->panic(dev
, "disconnect");
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
,
207 static void blk_aio_attached(AioContext
*ctx
, void *opaque
)
209 VuBlkExport
*vexp
= opaque
;
211 vexp
->export
.ctx
= ctx
;
212 vhost_user_server_attach_aio_context(&vexp
->vu_server
, ctx
);
215 static void blk_aio_detach(void *opaque
)
217 VuBlkExport
*vexp
= opaque
;
219 vhost_user_server_detach_aio_context(&vexp
->vu_server
);
220 vexp
->export
.ctx
= NULL
;
224 vu_blk_initialize_config(BlockDriverState
*bs
,
225 struct virtio_blk_config
*config
,
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);
247 static void vu_blk_exp_request_shutdown(BlockExport
*exp
)
249 VuBlkExport
*vexp
= container_of(exp
, VuBlkExport
, export
);
251 vhost_user_server_stop(&vexp
->vu_server
);
254 static int vu_blk_exp_create(BlockExport
*exp
, BlockExportOptions
*opts
,
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
;
263 vexp
->blkcfg
.wce
= 0;
265 if (vu_opts
->has_logical_block_size
) {
266 logical_block_size
= vu_opts
->logical_block_size
;
268 logical_block_size
= VIRTIO_BLK_SECTOR_SIZE
;
270 check_block_size(exp
->id
, "logical-block-size", logical_block_size
,
273 error_propagate(errp
, local_err
);
277 if (vu_opts
->has_num_queues
) {
278 num_queues
= vu_opts
->num_queues
;
280 if (num_queues
== 0) {
281 error_setg(errp
, "num-queues must be greater than 0");
284 vexp
->handler
.blk
= exp
->blk
;
285 vexp
->handler
.serial
= g_strdup("vhost_user_blk");
286 vexp
->handler
.logical_block_size
= logical_block_size
;
287 vexp
->handler
.writable
= opts
->writable
;
289 vu_blk_initialize_config(blk_bs(exp
->blk
), &vexp
->blkcfg
,
290 logical_block_size
, num_queues
);
292 blk_add_aio_context_notifier(exp
->blk
, blk_aio_attached
, blk_aio_detach
,
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 g_free(vexp
->handler
.serial
);
300 return -EADDRNOTAVAIL
;
306 static void vu_blk_exp_delete(BlockExport
*exp
)
308 VuBlkExport
*vexp
= container_of(exp
, VuBlkExport
, export
);
310 blk_remove_aio_context_notifier(exp
->blk
, blk_aio_attached
, blk_aio_detach
,
312 g_free(vexp
->handler
.serial
);
315 const BlockExportDriver blk_exp_vhost_user_blk
= {
316 .type
= BLOCK_EXPORT_TYPE_VHOST_USER_BLK
,
317 .instance_size
= sizeof(VuBlkExport
),
318 .create
= vu_blk_exp_create
,
319 .delete = vu_blk_exp_delete
,
320 .request_shutdown
= vu_blk_exp_request_shutdown
,