hyperv: synic: only setup ack notifier if there's a callback
[qemu/ar7.git] / contrib / vhost-user-blk / vhost-user-blk.c
blob571f114a5678ce7c2d7686e68d0db16ad072094a
1 /*
2 * vhost-user-blk sample application
4 * Copyright (c) 2017 Intel Corporation. All rights reserved.
6 * Author:
7 * Changpeng Liu <changpeng.liu@intel.com>
9 * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
10 * implementation by:
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"
23 #include <glib.h>
25 struct virtio_blk_inhdr {
26 unsigned char status;
29 /* vhost user block device */
30 typedef struct VubDev {
31 VugDev parent;
32 int blk_fd;
33 struct virtio_blk_config blkcfg;
34 bool enable_ro;
35 char *blk_name;
36 GMainLoop *loop;
37 } VubDev;
39 typedef struct VubReq {
40 VuVirtqElement *elem;
41 int64_t sector_num;
42 size_t size;
43 struct virtio_blk_inhdr *in;
44 struct virtio_blk_outhdr *out;
45 VubDev *vdev_blk;
46 struct VuVirtq *vq;
47 } VubReq;
49 /* refer util/iov.c */
50 static size_t vub_iov_size(const struct iovec *iov,
51 const unsigned int iov_cnt)
53 size_t len;
54 unsigned int i;
56 len = 0;
57 for (i = 0; i < iov_cnt; i++) {
58 len += iov[i].iov_len;
60 return len;
63 static void vub_panic_cb(VuDev *vu_dev, const char *buf)
65 VugDev *gdev;
66 VubDev *vdev_blk;
68 assert(vu_dev);
70 gdev = container_of(vu_dev, VugDev, parent);
71 vdev_blk = container_of(gdev, VubDev, parent);
72 if (buf) {
73 g_warning("vu_panic: %s", buf);
76 g_main_loop_quit(vdev_blk->loop);
79 static void vub_req_complete(VubReq *req)
81 VugDev *gdev = &req->vdev_blk->parent;
82 VuDev *vu_dev = &gdev->parent;
84 /* IO size with 1 extra status byte */
85 vu_queue_push(vu_dev, req->vq, req->elem,
86 req->size + 1);
87 vu_queue_notify(vu_dev, req->vq);
89 if (req->elem) {
90 free(req->elem);
93 g_free(req);
96 static int vub_open(const char *file_name, bool wce)
98 int fd;
99 int flags = O_RDWR;
101 if (!wce) {
102 flags |= O_DIRECT;
105 fd = open(file_name, flags);
106 if (fd < 0) {
107 fprintf(stderr, "Cannot open file %s, %s\n", file_name,
108 strerror(errno));
109 return -1;
112 return fd;
115 static ssize_t
116 vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt)
118 VubDev *vdev_blk = req->vdev_blk;
119 ssize_t rc;
121 if (!iovcnt) {
122 fprintf(stderr, "Invalid Read IOV count\n");
123 return -1;
126 req->size = vub_iov_size(iov, iovcnt);
127 rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
128 if (rc < 0) {
129 fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
130 vdev_blk->blk_name, req->sector_num, req->size,
131 strerror(errno));
132 return -1;
135 return rc;
138 static ssize_t
139 vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt)
141 VubDev *vdev_blk = req->vdev_blk;
142 ssize_t rc;
144 if (!iovcnt) {
145 fprintf(stderr, "Invalid Write IOV count\n");
146 return -1;
149 req->size = vub_iov_size(iov, iovcnt);
150 rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
151 if (rc < 0) {
152 fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
153 vdev_blk->blk_name, req->sector_num, req->size,
154 strerror(errno));
155 return -1;
158 return rc;
161 static void
162 vub_flush(VubReq *req)
164 VubDev *vdev_blk = req->vdev_blk;
166 fdatasync(vdev_blk->blk_fd);
169 static int vub_virtio_process_req(VubDev *vdev_blk,
170 VuVirtq *vq)
172 VugDev *gdev = &vdev_blk->parent;
173 VuDev *vu_dev = &gdev->parent;
174 VuVirtqElement *elem;
175 uint32_t type;
176 unsigned in_num;
177 unsigned out_num;
178 VubReq *req;
180 elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement) + sizeof(VubReq));
181 if (!elem) {
182 return -1;
185 /* refer to hw/block/virtio_blk.c */
186 if (elem->out_num < 1 || elem->in_num < 1) {
187 fprintf(stderr, "virtio-blk request missing headers\n");
188 free(elem);
189 return -1;
192 req = g_new0(VubReq, 1);
193 req->vdev_blk = vdev_blk;
194 req->vq = vq;
195 req->elem = elem;
197 in_num = elem->in_num;
198 out_num = elem->out_num;
200 /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
201 if (elem->out_sg[0].iov_len < sizeof(struct virtio_blk_outhdr)) {
202 fprintf(stderr, "Invalid outhdr size\n");
203 goto err;
205 req->out = (struct virtio_blk_outhdr *)elem->out_sg[0].iov_base;
206 out_num--;
208 if (elem->in_sg[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
209 fprintf(stderr, "Invalid inhdr size\n");
210 goto err;
212 req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base;
213 in_num--;
215 type = le32toh(req->out->type);
216 switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
217 case VIRTIO_BLK_T_IN: {
218 ssize_t ret = 0;
219 bool is_write = type & VIRTIO_BLK_T_OUT;
220 req->sector_num = le64toh(req->out->sector);
221 if (is_write) {
222 ret = vub_writev(req, &elem->out_sg[1], out_num);
223 } else {
224 ret = vub_readv(req, &elem->in_sg[0], in_num);
226 if (ret >= 0) {
227 req->in->status = VIRTIO_BLK_S_OK;
228 } else {
229 req->in->status = VIRTIO_BLK_S_IOERR;
231 vub_req_complete(req);
232 break;
234 case VIRTIO_BLK_T_FLUSH: {
235 vub_flush(req);
236 req->in->status = VIRTIO_BLK_S_OK;
237 vub_req_complete(req);
238 break;
240 case VIRTIO_BLK_T_GET_ID: {
241 size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num),
242 VIRTIO_BLK_ID_BYTES);
243 snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
244 req->in->status = VIRTIO_BLK_S_OK;
245 req->size = elem->in_sg[0].iov_len;
246 vub_req_complete(req);
247 break;
249 default: {
250 req->in->status = VIRTIO_BLK_S_UNSUPP;
251 vub_req_complete(req);
252 break;
256 return 0;
258 err:
259 free(elem);
260 g_free(req);
261 return -1;
264 static void vub_process_vq(VuDev *vu_dev, int idx)
266 VugDev *gdev;
267 VubDev *vdev_blk;
268 VuVirtq *vq;
269 int ret;
271 if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
272 fprintf(stderr, "VQ Index out of range: %d\n", idx);
273 vub_panic_cb(vu_dev, NULL);
274 return;
277 gdev = container_of(vu_dev, VugDev, parent);
278 vdev_blk = container_of(gdev, VubDev, parent);
279 assert(vdev_blk);
281 vq = vu_get_queue(vu_dev, idx);
282 assert(vq);
284 while (1) {
285 ret = vub_virtio_process_req(vdev_blk, vq);
286 if (ret) {
287 break;
292 static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
294 VuVirtq *vq;
296 assert(vu_dev);
298 vq = vu_get_queue(vu_dev, idx);
299 vu_set_queue_handler(vu_dev, vq, started ? vub_process_vq : NULL);
302 static uint64_t
303 vub_get_features(VuDev *dev)
305 uint64_t features;
306 VugDev *gdev;
307 VubDev *vdev_blk;
309 gdev = container_of(dev, VugDev, parent);
310 vdev_blk = container_of(gdev, VubDev, parent);
312 features = 1ull << VIRTIO_BLK_F_SIZE_MAX |
313 1ull << VIRTIO_BLK_F_SEG_MAX |
314 1ull << VIRTIO_BLK_F_TOPOLOGY |
315 1ull << VIRTIO_BLK_F_BLK_SIZE |
316 1ull << VIRTIO_BLK_F_FLUSH |
317 1ull << VIRTIO_BLK_F_CONFIG_WCE |
318 1ull << VIRTIO_F_VERSION_1 |
319 1ull << VHOST_USER_F_PROTOCOL_FEATURES;
321 if (vdev_blk->enable_ro) {
322 features |= 1ull << VIRTIO_BLK_F_RO;
325 return features;
328 static uint64_t
329 vub_get_protocol_features(VuDev *dev)
331 return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
334 static int
335 vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
337 VugDev *gdev;
338 VubDev *vdev_blk;
340 gdev = container_of(vu_dev, VugDev, parent);
341 vdev_blk = container_of(gdev, VubDev, parent);
342 memcpy(config, &vdev_blk->blkcfg, len);
344 return 0;
347 static int
348 vub_set_config(VuDev *vu_dev, const uint8_t *data,
349 uint32_t offset, uint32_t size, uint32_t flags)
351 VugDev *gdev;
352 VubDev *vdev_blk;
353 uint8_t wce;
354 int fd;
356 /* don't support live migration */
357 if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
358 return -1;
361 gdev = container_of(vu_dev, VugDev, parent);
362 vdev_blk = container_of(gdev, VubDev, parent);
364 if (offset != offsetof(struct virtio_blk_config, wce) ||
365 size != 1) {
366 return -1;
369 wce = *data;
370 if (wce == vdev_blk->blkcfg.wce) {
371 /* Do nothing as same with old configuration */
372 return 0;
375 vdev_blk->blkcfg.wce = wce;
376 fprintf(stdout, "Write Cache Policy Changed\n");
377 if (vdev_blk->blk_fd >= 0) {
378 close(vdev_blk->blk_fd);
379 vdev_blk->blk_fd = -1;
382 fd = vub_open(vdev_blk->blk_name, wce);
383 if (fd < 0) {
384 fprintf(stderr, "Error to open block device %s\n", vdev_blk->blk_name);
385 vdev_blk->blk_fd = -1;
386 return -1;
388 vdev_blk->blk_fd = fd;
390 return 0;
393 static const VuDevIface vub_iface = {
394 .get_features = vub_get_features,
395 .queue_set_started = vub_queue_set_started,
396 .get_protocol_features = vub_get_protocol_features,
397 .get_config = vub_get_config,
398 .set_config = vub_set_config,
401 static int unix_sock_new(char *unix_fn)
403 int sock;
404 struct sockaddr_un un;
405 size_t len;
407 assert(unix_fn);
409 sock = socket(AF_UNIX, SOCK_STREAM, 0);
410 if (sock <= 0) {
411 perror("socket");
412 return -1;
415 un.sun_family = AF_UNIX;
416 (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
417 len = sizeof(un.sun_family) + strlen(un.sun_path);
419 (void)unlink(unix_fn);
420 if (bind(sock, (struct sockaddr *)&un, len) < 0) {
421 perror("bind");
422 goto fail;
425 if (listen(sock, 1) < 0) {
426 perror("listen");
427 goto fail;
430 return sock;
432 fail:
433 (void)close(sock);
435 return -1;
438 static void vub_free(struct VubDev *vdev_blk)
440 if (!vdev_blk) {
441 return;
444 g_main_loop_unref(vdev_blk->loop);
445 if (vdev_blk->blk_fd >= 0) {
446 close(vdev_blk->blk_fd);
448 g_free(vdev_blk);
451 static uint32_t
452 vub_get_blocksize(int fd)
454 uint32_t blocksize = 512;
456 #if defined(__linux__) && defined(BLKSSZGET)
457 if (ioctl(fd, BLKSSZGET, &blocksize) == 0) {
458 return blocklen;
460 #endif
462 return blocksize;
465 static void
466 vub_initialize_config(int fd, struct virtio_blk_config *config)
468 off64_t capacity;
470 capacity = lseek64(fd, 0, SEEK_END);
471 config->capacity = capacity >> 9;
472 config->blk_size = vub_get_blocksize(fd);
473 config->size_max = 65536;
474 config->seg_max = 128 - 2;
475 config->min_io_size = 1;
476 config->opt_io_size = 1;
477 config->num_queues = 1;
480 static VubDev *
481 vub_new(char *blk_file)
483 VubDev *vdev_blk;
485 vdev_blk = g_new0(VubDev, 1);
486 vdev_blk->loop = g_main_loop_new(NULL, FALSE);
487 vdev_blk->blk_fd = vub_open(blk_file, 0);
488 if (vdev_blk->blk_fd < 0) {
489 fprintf(stderr, "Error to open block device %s\n", blk_file);
490 vub_free(vdev_blk);
491 return NULL;
493 vdev_blk->enable_ro = false;
494 vdev_blk->blkcfg.wce = 0;
495 vdev_blk->blk_name = blk_file;
497 /* fill virtio_blk_config with block parameters */
498 vub_initialize_config(vdev_blk->blk_fd, &vdev_blk->blkcfg);
500 return vdev_blk;
503 int main(int argc, char **argv)
505 int opt;
506 char *unix_socket = NULL;
507 char *blk_file = NULL;
508 bool enable_ro = false;
509 int lsock = -1, csock = -1;
510 VubDev *vdev_blk = NULL;
512 while ((opt = getopt(argc, argv, "b:rs:h")) != -1) {
513 switch (opt) {
514 case 'b':
515 blk_file = g_strdup(optarg);
516 break;
517 case 's':
518 unix_socket = g_strdup(optarg);
519 break;
520 case 'r':
521 enable_ro = true;
522 break;
523 case 'h':
524 default:
525 printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
526 " | -r Enable read-only ] | [ -h ]\n", argv[0]);
527 return 0;
531 if (!unix_socket || !blk_file) {
532 printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
533 " | -r Enable read-only ] | [ -h ]\n", argv[0]);
534 return -1;
537 lsock = unix_sock_new(unix_socket);
538 if (lsock < 0) {
539 goto err;
542 csock = accept(lsock, (void *)0, (void *)0);
543 if (csock < 0) {
544 fprintf(stderr, "Accept error %s\n", strerror(errno));
545 goto err;
548 vdev_blk = vub_new(blk_file);
549 if (!vdev_blk) {
550 goto err;
552 if (enable_ro) {
553 vdev_blk->enable_ro = true;
556 vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
558 g_main_loop_run(vdev_blk->loop);
560 vug_deinit(&vdev_blk->parent);
562 err:
563 vub_free(vdev_blk);
564 if (csock >= 0) {
565 close(csock);
567 if (lsock >= 0) {
568 close(lsock);
570 g_free(unix_socket);
571 g_free(blk_file);
573 return 0;