2 * vhost-user-blk sample application
4 * Copyright (c) 2017 Intel Corporation. All rights reserved.
7 * Changpeng Liu <changpeng.liu@intel.com>
9 * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
11 * Felipe Franciosi <felipe@nutanix.com>
12 * Anthony Liguori <aliguori@us.ibm.com>
14 * This work is licensed under the terms of the GNU GPL, version 2 only.
15 * See the COPYING file in the top-level directory.
18 #include "qemu/osdep.h"
19 #include "standard-headers/linux/virtio_blk.h"
20 #include "contrib/libvhost-user/libvhost-user-glib.h"
21 #include "contrib/libvhost-user/libvhost-user.h"
24 struct virtio_blk_inhdr
{
28 /* vhost user block device */
29 typedef struct VubDev
{
32 struct virtio_blk_config blkcfg
;
38 typedef struct VubReq
{
42 struct virtio_blk_inhdr
*in
;
43 struct virtio_blk_outhdr
*out
;
48 /* refer util/iov.c */
49 static size_t vub_iov_size(const struct iovec
*iov
,
50 const unsigned int iov_cnt
)
56 for (i
= 0; i
< iov_cnt
; i
++) {
57 len
+= iov
[i
].iov_len
;
62 static void vub_panic_cb(VuDev
*vu_dev
, const char *buf
)
69 gdev
= container_of(vu_dev
, VugDev
, parent
);
70 vdev_blk
= container_of(gdev
, VubDev
, parent
);
72 g_warning("vu_panic: %s", buf
);
75 g_main_loop_quit(vdev_blk
->loop
);
78 static void vub_req_complete(VubReq
*req
)
80 VugDev
*gdev
= &req
->vdev_blk
->parent
;
81 VuDev
*vu_dev
= &gdev
->parent
;
83 /* IO size with 1 extra status byte */
84 vu_queue_push(vu_dev
, req
->vq
, req
->elem
,
86 vu_queue_notify(vu_dev
, req
->vq
);
95 static int vub_open(const char *file_name
, bool wce
)
104 fd
= open(file_name
, flags
);
106 fprintf(stderr
, "Cannot open file %s, %s\n", file_name
,
115 vub_readv(VubReq
*req
, struct iovec
*iov
, uint32_t iovcnt
)
117 VubDev
*vdev_blk
= req
->vdev_blk
;
121 fprintf(stderr
, "Invalid Read IOV count\n");
125 req
->size
= vub_iov_size(iov
, iovcnt
);
126 rc
= preadv(vdev_blk
->blk_fd
, iov
, iovcnt
, req
->sector_num
* 512);
128 fprintf(stderr
, "%s, Sector %"PRIu64
", Size %lu failed with %s\n",
129 vdev_blk
->blk_name
, req
->sector_num
, req
->size
,
138 vub_writev(VubReq
*req
, struct iovec
*iov
, uint32_t iovcnt
)
140 VubDev
*vdev_blk
= req
->vdev_blk
;
144 fprintf(stderr
, "Invalid Write IOV count\n");
148 req
->size
= vub_iov_size(iov
, iovcnt
);
149 rc
= pwritev(vdev_blk
->blk_fd
, iov
, iovcnt
, req
->sector_num
* 512);
151 fprintf(stderr
, "%s, Sector %"PRIu64
", Size %lu failed with %s\n",
152 vdev_blk
->blk_name
, req
->sector_num
, req
->size
,
161 vub_flush(VubReq
*req
)
163 VubDev
*vdev_blk
= req
->vdev_blk
;
165 fdatasync(vdev_blk
->blk_fd
);
168 static int vub_virtio_process_req(VubDev
*vdev_blk
,
171 VugDev
*gdev
= &vdev_blk
->parent
;
172 VuDev
*vu_dev
= &gdev
->parent
;
173 VuVirtqElement
*elem
;
179 elem
= vu_queue_pop(vu_dev
, vq
, sizeof(VuVirtqElement
) + sizeof(VubReq
));
184 /* refer to hw/block/virtio_blk.c */
185 if (elem
->out_num
< 1 || elem
->in_num
< 1) {
186 fprintf(stderr
, "virtio-blk request missing headers\n");
191 req
= g_new0(VubReq
, 1);
192 req
->vdev_blk
= vdev_blk
;
196 in_num
= elem
->in_num
;
197 out_num
= elem
->out_num
;
199 /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
200 if (elem
->out_sg
[0].iov_len
< sizeof(struct virtio_blk_outhdr
)) {
201 fprintf(stderr
, "Invalid outhdr size\n");
204 req
->out
= (struct virtio_blk_outhdr
*)elem
->out_sg
[0].iov_base
;
207 if (elem
->in_sg
[in_num
- 1].iov_len
< sizeof(struct virtio_blk_inhdr
)) {
208 fprintf(stderr
, "Invalid inhdr size\n");
211 req
->in
= (struct virtio_blk_inhdr
*)elem
->in_sg
[in_num
- 1].iov_base
;
214 type
= le32toh(req
->out
->type
);
215 switch (type
& ~(VIRTIO_BLK_T_OUT
| VIRTIO_BLK_T_BARRIER
)) {
216 case VIRTIO_BLK_T_IN
: {
218 bool is_write
= type
& VIRTIO_BLK_T_OUT
;
219 req
->sector_num
= le64toh(req
->out
->sector
);
221 ret
= vub_writev(req
, &elem
->out_sg
[1], out_num
);
223 ret
= vub_readv(req
, &elem
->in_sg
[0], in_num
);
226 req
->in
->status
= VIRTIO_BLK_S_OK
;
228 req
->in
->status
= VIRTIO_BLK_S_IOERR
;
230 vub_req_complete(req
);
233 case VIRTIO_BLK_T_FLUSH
: {
235 req
->in
->status
= VIRTIO_BLK_S_OK
;
236 vub_req_complete(req
);
239 case VIRTIO_BLK_T_GET_ID
: {
240 size_t size
= MIN(vub_iov_size(&elem
->in_sg
[0], in_num
),
241 VIRTIO_BLK_ID_BYTES
);
242 snprintf(elem
->in_sg
[0].iov_base
, size
, "%s", "vhost_user_blk");
243 req
->in
->status
= VIRTIO_BLK_S_OK
;
244 req
->size
= elem
->in_sg
[0].iov_len
;
245 vub_req_complete(req
);
249 req
->in
->status
= VIRTIO_BLK_S_UNSUPP
;
250 vub_req_complete(req
);
263 static void vub_process_vq(VuDev
*vu_dev
, int idx
)
270 if ((idx
< 0) || (idx
>= VHOST_MAX_NR_VIRTQUEUE
)) {
271 fprintf(stderr
, "VQ Index out of range: %d\n", idx
);
272 vub_panic_cb(vu_dev
, NULL
);
276 gdev
= container_of(vu_dev
, VugDev
, parent
);
277 vdev_blk
= container_of(gdev
, VubDev
, parent
);
280 vq
= vu_get_queue(vu_dev
, idx
);
284 ret
= vub_virtio_process_req(vdev_blk
, vq
);
291 static void vub_queue_set_started(VuDev
*vu_dev
, int idx
, bool started
)
297 vq
= vu_get_queue(vu_dev
, idx
);
298 vu_set_queue_handler(vu_dev
, vq
, started
? vub_process_vq
: NULL
);
302 vub_get_features(VuDev
*dev
)
308 gdev
= container_of(dev
, VugDev
, parent
);
309 vdev_blk
= container_of(gdev
, VubDev
, parent
);
311 features
= 1ull << VIRTIO_BLK_F_SIZE_MAX
|
312 1ull << VIRTIO_BLK_F_SEG_MAX
|
313 1ull << VIRTIO_BLK_F_TOPOLOGY
|
314 1ull << VIRTIO_BLK_F_BLK_SIZE
|
315 1ull << VIRTIO_BLK_F_FLUSH
|
316 1ull << VIRTIO_BLK_F_CONFIG_WCE
|
317 1ull << VIRTIO_F_VERSION_1
|
318 1ull << VHOST_USER_F_PROTOCOL_FEATURES
;
320 if (vdev_blk
->enable_ro
) {
321 features
|= 1ull << VIRTIO_BLK_F_RO
;
328 vub_get_protocol_features(VuDev
*dev
)
330 return 1ull << VHOST_USER_PROTOCOL_F_CONFIG
;
334 vub_get_config(VuDev
*vu_dev
, uint8_t *config
, uint32_t len
)
339 gdev
= container_of(vu_dev
, VugDev
, parent
);
340 vdev_blk
= container_of(gdev
, VubDev
, parent
);
341 memcpy(config
, &vdev_blk
->blkcfg
, len
);
347 vub_set_config(VuDev
*vu_dev
, const uint8_t *data
,
348 uint32_t offset
, uint32_t size
, uint32_t flags
)
355 /* don't support live migration */
356 if (flags
!= VHOST_SET_CONFIG_TYPE_MASTER
) {
360 gdev
= container_of(vu_dev
, VugDev
, parent
);
361 vdev_blk
= container_of(gdev
, VubDev
, parent
);
363 if (offset
!= offsetof(struct virtio_blk_config
, wce
) ||
369 if (wce
== vdev_blk
->blkcfg
.wce
) {
370 /* Do nothing as same with old configuration */
374 vdev_blk
->blkcfg
.wce
= wce
;
375 fprintf(stdout
, "Write Cache Policy Changed\n");
376 if (vdev_blk
->blk_fd
>= 0) {
377 close(vdev_blk
->blk_fd
);
378 vdev_blk
->blk_fd
= -1;
381 fd
= vub_open(vdev_blk
->blk_name
, wce
);
383 fprintf(stderr
, "Error to open block device %s\n", vdev_blk
->blk_name
);
384 vdev_blk
->blk_fd
= -1;
387 vdev_blk
->blk_fd
= fd
;
392 static const VuDevIface vub_iface
= {
393 .get_features
= vub_get_features
,
394 .queue_set_started
= vub_queue_set_started
,
395 .get_protocol_features
= vub_get_protocol_features
,
396 .get_config
= vub_get_config
,
397 .set_config
= vub_set_config
,
400 static int unix_sock_new(char *unix_fn
)
403 struct sockaddr_un un
;
408 sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
414 un
.sun_family
= AF_UNIX
;
415 (void)snprintf(un
.sun_path
, sizeof(un
.sun_path
), "%s", unix_fn
);
416 len
= sizeof(un
.sun_family
) + strlen(un
.sun_path
);
418 (void)unlink(unix_fn
);
419 if (bind(sock
, (struct sockaddr
*)&un
, len
) < 0) {
424 if (listen(sock
, 1) < 0) {
437 static void vub_free(struct VubDev
*vdev_blk
)
443 g_main_loop_unref(vdev_blk
->loop
);
444 if (vdev_blk
->blk_fd
>= 0) {
445 close(vdev_blk
->blk_fd
);
451 vub_get_blocksize(int fd
)
453 uint32_t blocksize
= 512;
455 #if defined(__linux__) && defined(BLKSSZGET)
456 if (ioctl(fd
, BLKSSZGET
, &blocksize
) == 0) {
465 vub_initialize_config(int fd
, struct virtio_blk_config
*config
)
469 capacity
= lseek64(fd
, 0, SEEK_END
);
470 config
->capacity
= capacity
>> 9;
471 config
->blk_size
= vub_get_blocksize(fd
);
472 config
->size_max
= 65536;
473 config
->seg_max
= 128 - 2;
474 config
->min_io_size
= 1;
475 config
->opt_io_size
= 1;
476 config
->num_queues
= 1;
480 vub_new(char *blk_file
)
484 vdev_blk
= g_new0(VubDev
, 1);
485 vdev_blk
->loop
= g_main_loop_new(NULL
, FALSE
);
486 vdev_blk
->blk_fd
= vub_open(blk_file
, 0);
487 if (vdev_blk
->blk_fd
< 0) {
488 fprintf(stderr
, "Error to open block device %s\n", blk_file
);
492 vdev_blk
->enable_ro
= false;
493 vdev_blk
->blkcfg
.wce
= 0;
494 vdev_blk
->blk_name
= blk_file
;
496 /* fill virtio_blk_config with block parameters */
497 vub_initialize_config(vdev_blk
->blk_fd
, &vdev_blk
->blkcfg
);
502 int main(int argc
, char **argv
)
505 char *unix_socket
= NULL
;
506 char *blk_file
= NULL
;
507 bool enable_ro
= false;
508 int lsock
= -1, csock
= -1;
509 VubDev
*vdev_blk
= NULL
;
511 while ((opt
= getopt(argc
, argv
, "b:rs:h")) != -1) {
514 blk_file
= g_strdup(optarg
);
517 unix_socket
= g_strdup(optarg
);
524 printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
525 " | -r Enable read-only ] | [ -h ]\n", argv
[0]);
530 if (!unix_socket
|| !blk_file
) {
531 printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
532 " | -r Enable read-only ] | [ -h ]\n", argv
[0]);
536 lsock
= unix_sock_new(unix_socket
);
541 csock
= accept(lsock
, (void *)0, (void *)0);
543 fprintf(stderr
, "Accept error %s\n", strerror(errno
));
547 vdev_blk
= vub_new(blk_file
);
552 vdev_blk
->enable_ro
= true;
555 vug_init(&vdev_blk
->parent
, csock
, vub_panic_cb
, &vub_iface
);
557 g_main_loop_run(vdev_blk
->loop
);
559 vug_deinit(&vdev_blk
->parent
);