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 "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"
25 VHOST_USER_BLK_NUM_QUEUES_DEFAULT
= 1,
28 typedef struct VuBlkReq
{
34 /* vhost user block device */
38 VirtioBlkHandler handler
;
39 QIOChannelSocket
*sioc
;
40 struct virtio_blk_config blkcfg
;
43 static void vu_blk_req_complete(VuBlkReq
*req
, size_t in_len
)
45 VuDev
*vu_dev
= &req
->server
->vu_dev
;
47 vu_queue_push(vu_dev
, req
->vq
, &req
->elem
, in_len
);
48 vu_queue_notify(vu_dev
, req
->vq
);
53 /* Called with server refcount increased, must decrease before returning */
54 static void coroutine_fn
vu_blk_virtio_process_req(void *opaque
)
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
;
67 in_len
= virtio_blk_process_req(handler
, in_iov
, out_iov
,
71 vhost_user_server_unref(server
);
75 vu_blk_req_complete(req
, in_len
);
76 vhost_user_server_unref(server
);
79 static void vu_blk_process_vq(VuDev
*vu_dev
, int idx
)
81 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
82 VuVirtq
*vq
= vu_get_queue(vu_dev
, idx
);
87 req
= vu_queue_pop(vu_dev
, vq
, sizeof(VuBlkReq
));
96 qemu_coroutine_create(vu_blk_virtio_process_req
, req
);
98 vhost_user_server_ref(server
);
99 qemu_coroutine_enter(co
);
103 static void vu_blk_queue_set_started(VuDev
*vu_dev
, int idx
, bool started
)
109 vq
= vu_get_queue(vu_dev
, idx
);
110 vu_set_queue_handler(vu_dev
, vq
, started
? vu_blk_process_vq
: NULL
);
113 static uint64_t vu_blk_get_features(VuDev
*dev
)
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
;
132 if (!vexp
->handler
.writable
) {
133 features
|= 1ull << VIRTIO_BLK_F_RO
;
139 static uint64_t vu_blk_get_protocol_features(VuDev
*dev
)
141 return 1ull << VHOST_USER_PROTOCOL_F_CONFIG
;
145 vu_blk_get_config(VuDev
*vu_dev
, uint8_t *config
, uint32_t len
)
147 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
148 VuBlkExport
*vexp
= container_of(server
, VuBlkExport
, vu_server
);
150 if (len
> sizeof(struct virtio_blk_config
)) {
154 memcpy(config
, &vexp
->blkcfg
, len
);
159 vu_blk_set_config(VuDev
*vu_dev
, const uint8_t *data
,
160 uint32_t offset
, uint32_t size
, uint32_t flags
)
162 VuServer
*server
= container_of(vu_dev
, VuServer
, vu_dev
);
163 VuBlkExport
*vexp
= container_of(server
, VuBlkExport
, vu_server
);
166 /* don't support live migration */
167 if (flags
!= VHOST_SET_CONFIG_TYPE_MASTER
) {
171 if (offset
!= offsetof(struct virtio_blk_config
, wce
) ||
177 vexp
->blkcfg
.wce
= wce
;
178 blk_set_enable_write_cache(vexp
->export
.blk
, wce
);
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
186 * To avoid this issue, process VHOST_USER_NONE request ahead
187 * of vu_process_message.
190 static int vu_blk_process_msg(VuDev
*dev
, VhostUserMsg
*vmsg
, int *do_reply
)
192 if (vmsg
->request
== VHOST_USER_NONE
) {
193 dev
->panic(dev
, "disconnect");
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
,
208 static void blk_aio_attached(AioContext
*ctx
, void *opaque
)
210 VuBlkExport
*vexp
= opaque
;
212 vexp
->export
.ctx
= ctx
;
213 vhost_user_server_attach_aio_context(&vexp
->vu_server
, ctx
);
216 static void blk_aio_detach(void *opaque
)
218 VuBlkExport
*vexp
= opaque
;
220 vhost_user_server_detach_aio_context(&vexp
->vu_server
);
221 vexp
->export
.ctx
= NULL
;
225 vu_blk_initialize_config(BlockDriverState
*bs
,
226 struct virtio_blk_config
*config
,
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);
248 static void vu_blk_exp_request_shutdown(BlockExport
*exp
)
250 VuBlkExport
*vexp
= container_of(exp
, VuBlkExport
, export
);
252 vhost_user_server_stop(&vexp
->vu_server
);
255 static void vu_blk_exp_resize(void *opaque
)
257 VuBlkExport
*vexp
= opaque
;
258 BlockDriverState
*bs
= blk_bs(vexp
->handler
.blk
);
259 int64_t new_size
= bdrv_getlength(bs
);
262 error_printf("Failed to get length of block node '%s'",
263 bdrv_get_node_name(bs
));
267 vexp
->blkcfg
.capacity
= cpu_to_le64(new_size
>> VIRTIO_BLK_SECTOR_BITS
);
269 vu_config_change_msg(&vexp
->vu_server
.vu_dev
);
272 static const BlockDevOps vu_blk_dev_ops
= {
273 .resize_cb
= vu_blk_exp_resize
,
276 static int vu_blk_exp_create(BlockExport
*exp
, BlockExportOptions
*opts
,
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
;
285 vexp
->blkcfg
.wce
= 0;
287 if (vu_opts
->has_logical_block_size
) {
288 logical_block_size
= vu_opts
->logical_block_size
;
290 logical_block_size
= VIRTIO_BLK_SECTOR_SIZE
;
292 check_block_size(exp
->id
, "logical-block-size", logical_block_size
,
295 error_propagate(errp
, local_err
);
299 if (vu_opts
->has_num_queues
) {
300 num_queues
= vu_opts
->num_queues
;
302 if (num_queues
== 0) {
303 error_setg(errp
, "num-queues must be greater than 0");
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
;
311 vu_blk_initialize_config(blk_bs(exp
->blk
), &vexp
->blkcfg
,
312 logical_block_size
, num_queues
);
314 blk_add_aio_context_notifier(exp
->blk
, blk_aio_attached
, blk_aio_detach
,
317 blk_set_dev_ops(exp
->blk
, &vu_blk_dev_ops
, vexp
);
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
;
330 static void vu_blk_exp_delete(BlockExport
*exp
)
332 VuBlkExport
*vexp
= container_of(exp
, VuBlkExport
, export
);
334 blk_remove_aio_context_notifier(exp
->blk
, blk_aio_attached
, blk_aio_detach
,
336 g_free(vexp
->handler
.serial
);
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
,