12a2359b8SXie Yongji /* 22a2359b8SXie Yongji * Export QEMU block device via VDUSE 32a2359b8SXie Yongji * 42a2359b8SXie Yongji * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved. 52a2359b8SXie Yongji * 62a2359b8SXie Yongji * Author: 72a2359b8SXie Yongji * Xie Yongji <xieyongji@bytedance.com> 82a2359b8SXie Yongji * 92a2359b8SXie Yongji * This work is licensed under the terms of the GNU GPL, version 2 or 102a2359b8SXie Yongji * later. See the COPYING file in the top-level directory. 112a2359b8SXie Yongji */ 122a2359b8SXie Yongji 13*2ca10faeSMarkus Armbruster #include "qemu/osdep.h" 142a2359b8SXie Yongji #include <sys/eventfd.h> 152a2359b8SXie Yongji 162a2359b8SXie Yongji #include "qapi/error.h" 172a2359b8SXie Yongji #include "block/export.h" 182a2359b8SXie Yongji #include "qemu/error-report.h" 192a2359b8SXie Yongji #include "util/block-helpers.h" 202a2359b8SXie Yongji #include "subprojects/libvduse/libvduse.h" 212a2359b8SXie Yongji #include "virtio-blk-handler.h" 222a2359b8SXie Yongji 232a2359b8SXie Yongji #include "standard-headers/linux/virtio_blk.h" 242a2359b8SXie Yongji 252a2359b8SXie Yongji #define VDUSE_DEFAULT_NUM_QUEUE 1 262a2359b8SXie Yongji #define VDUSE_DEFAULT_QUEUE_SIZE 256 272a2359b8SXie Yongji 282a2359b8SXie Yongji typedef struct VduseBlkExport { 292a2359b8SXie Yongji BlockExport export; 302a2359b8SXie Yongji VirtioBlkHandler handler; 312a2359b8SXie Yongji VduseDev *dev; 322a2359b8SXie Yongji uint16_t num_queues; 33d043e2dbSXie Yongji char *recon_file; 342a2359b8SXie Yongji unsigned int inflight; 352a2359b8SXie Yongji } VduseBlkExport; 362a2359b8SXie Yongji 372a2359b8SXie Yongji typedef struct VduseBlkReq { 382a2359b8SXie Yongji VduseVirtqElement elem; 392a2359b8SXie Yongji VduseVirtq *vq; 402a2359b8SXie Yongji } VduseBlkReq; 412a2359b8SXie Yongji 422a2359b8SXie Yongji static void vduse_blk_inflight_inc(VduseBlkExport *vblk_exp) 432a2359b8SXie Yongji { 442a2359b8SXie Yongji vblk_exp->inflight++; 452a2359b8SXie Yongji } 462a2359b8SXie Yongji 472a2359b8SXie Yongji static void vduse_blk_inflight_dec(VduseBlkExport *vblk_exp) 482a2359b8SXie Yongji { 492a2359b8SXie Yongji if (--vblk_exp->inflight == 0) { 502a2359b8SXie Yongji aio_wait_kick(); 512a2359b8SXie Yongji } 522a2359b8SXie Yongji } 532a2359b8SXie Yongji 542a2359b8SXie Yongji static void vduse_blk_req_complete(VduseBlkReq *req, size_t in_len) 552a2359b8SXie Yongji { 562a2359b8SXie Yongji vduse_queue_push(req->vq, &req->elem, in_len); 572a2359b8SXie Yongji vduse_queue_notify(req->vq); 582a2359b8SXie Yongji 592a2359b8SXie Yongji free(req); 602a2359b8SXie Yongji } 612a2359b8SXie Yongji 622a2359b8SXie Yongji static void coroutine_fn vduse_blk_virtio_process_req(void *opaque) 632a2359b8SXie Yongji { 642a2359b8SXie Yongji VduseBlkReq *req = opaque; 652a2359b8SXie Yongji VduseVirtq *vq = req->vq; 662a2359b8SXie Yongji VduseDev *dev = vduse_queue_get_dev(vq); 672a2359b8SXie Yongji VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); 682a2359b8SXie Yongji VirtioBlkHandler *handler = &vblk_exp->handler; 692a2359b8SXie Yongji VduseVirtqElement *elem = &req->elem; 702a2359b8SXie Yongji struct iovec *in_iov = elem->in_sg; 712a2359b8SXie Yongji struct iovec *out_iov = elem->out_sg; 722a2359b8SXie Yongji unsigned in_num = elem->in_num; 732a2359b8SXie Yongji unsigned out_num = elem->out_num; 742a2359b8SXie Yongji int in_len; 752a2359b8SXie Yongji 762a2359b8SXie Yongji in_len = virtio_blk_process_req(handler, in_iov, 772a2359b8SXie Yongji out_iov, in_num, out_num); 782a2359b8SXie Yongji if (in_len < 0) { 792a2359b8SXie Yongji free(req); 802a2359b8SXie Yongji return; 812a2359b8SXie Yongji } 822a2359b8SXie Yongji 832a2359b8SXie Yongji vduse_blk_req_complete(req, in_len); 842a2359b8SXie Yongji vduse_blk_inflight_dec(vblk_exp); 852a2359b8SXie Yongji } 862a2359b8SXie Yongji 872a2359b8SXie Yongji static void vduse_blk_vq_handler(VduseDev *dev, VduseVirtq *vq) 882a2359b8SXie Yongji { 892a2359b8SXie Yongji VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); 902a2359b8SXie Yongji 912a2359b8SXie Yongji while (1) { 922a2359b8SXie Yongji VduseBlkReq *req; 932a2359b8SXie Yongji 942a2359b8SXie Yongji req = vduse_queue_pop(vq, sizeof(VduseBlkReq)); 952a2359b8SXie Yongji if (!req) { 962a2359b8SXie Yongji break; 972a2359b8SXie Yongji } 982a2359b8SXie Yongji req->vq = vq; 992a2359b8SXie Yongji 1002a2359b8SXie Yongji Coroutine *co = 1012a2359b8SXie Yongji qemu_coroutine_create(vduse_blk_virtio_process_req, req); 1022a2359b8SXie Yongji 1032a2359b8SXie Yongji vduse_blk_inflight_inc(vblk_exp); 1042a2359b8SXie Yongji qemu_coroutine_enter(co); 1052a2359b8SXie Yongji } 1062a2359b8SXie Yongji } 1072a2359b8SXie Yongji 1082a2359b8SXie Yongji static void on_vduse_vq_kick(void *opaque) 1092a2359b8SXie Yongji { 1102a2359b8SXie Yongji VduseVirtq *vq = opaque; 1112a2359b8SXie Yongji VduseDev *dev = vduse_queue_get_dev(vq); 1122a2359b8SXie Yongji int fd = vduse_queue_get_fd(vq); 1132a2359b8SXie Yongji eventfd_t kick_data; 1142a2359b8SXie Yongji 1152a2359b8SXie Yongji if (eventfd_read(fd, &kick_data) == -1) { 1162a2359b8SXie Yongji error_report("failed to read data from eventfd"); 1172a2359b8SXie Yongji return; 1182a2359b8SXie Yongji } 1192a2359b8SXie Yongji 1202a2359b8SXie Yongji vduse_blk_vq_handler(dev, vq); 1212a2359b8SXie Yongji } 1222a2359b8SXie Yongji 1232a2359b8SXie Yongji static void vduse_blk_enable_queue(VduseDev *dev, VduseVirtq *vq) 1242a2359b8SXie Yongji { 1252a2359b8SXie Yongji VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); 1262a2359b8SXie Yongji 1272a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), 1282a2359b8SXie Yongji true, on_vduse_vq_kick, NULL, NULL, NULL, vq); 129d043e2dbSXie Yongji /* Make sure we don't miss any kick afer reconnecting */ 130d043e2dbSXie Yongji eventfd_write(vduse_queue_get_fd(vq), 1); 1312a2359b8SXie Yongji } 1322a2359b8SXie Yongji 1332a2359b8SXie Yongji static void vduse_blk_disable_queue(VduseDev *dev, VduseVirtq *vq) 1342a2359b8SXie Yongji { 1352a2359b8SXie Yongji VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev); 1362a2359b8SXie Yongji 1372a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), 1382a2359b8SXie Yongji true, NULL, NULL, NULL, NULL, NULL); 1392a2359b8SXie Yongji } 1402a2359b8SXie Yongji 1412a2359b8SXie Yongji static const VduseOps vduse_blk_ops = { 1422a2359b8SXie Yongji .enable_queue = vduse_blk_enable_queue, 1432a2359b8SXie Yongji .disable_queue = vduse_blk_disable_queue, 1442a2359b8SXie Yongji }; 1452a2359b8SXie Yongji 1462a2359b8SXie Yongji static void on_vduse_dev_kick(void *opaque) 1472a2359b8SXie Yongji { 1482a2359b8SXie Yongji VduseDev *dev = opaque; 1492a2359b8SXie Yongji 1502a2359b8SXie Yongji vduse_dev_handler(dev); 1512a2359b8SXie Yongji } 1522a2359b8SXie Yongji 1532a2359b8SXie Yongji static void vduse_blk_attach_ctx(VduseBlkExport *vblk_exp, AioContext *ctx) 1542a2359b8SXie Yongji { 1552a2359b8SXie Yongji int i; 1562a2359b8SXie Yongji 1572a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), 1582a2359b8SXie Yongji true, on_vduse_dev_kick, NULL, NULL, NULL, 1592a2359b8SXie Yongji vblk_exp->dev); 1602a2359b8SXie Yongji 1612a2359b8SXie Yongji for (i = 0; i < vblk_exp->num_queues; i++) { 1622a2359b8SXie Yongji VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); 1632a2359b8SXie Yongji int fd = vduse_queue_get_fd(vq); 1642a2359b8SXie Yongji 1652a2359b8SXie Yongji if (fd < 0) { 1662a2359b8SXie Yongji continue; 1672a2359b8SXie Yongji } 1682a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, fd, true, 1692a2359b8SXie Yongji on_vduse_vq_kick, NULL, NULL, NULL, vq); 1702a2359b8SXie Yongji } 1712a2359b8SXie Yongji } 1722a2359b8SXie Yongji 1732a2359b8SXie Yongji static void vduse_blk_detach_ctx(VduseBlkExport *vblk_exp) 1742a2359b8SXie Yongji { 1752a2359b8SXie Yongji int i; 1762a2359b8SXie Yongji 1772a2359b8SXie Yongji for (i = 0; i < vblk_exp->num_queues; i++) { 1782a2359b8SXie Yongji VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); 1792a2359b8SXie Yongji int fd = vduse_queue_get_fd(vq); 1802a2359b8SXie Yongji 1812a2359b8SXie Yongji if (fd < 0) { 1822a2359b8SXie Yongji continue; 1832a2359b8SXie Yongji } 1842a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, fd, 1852a2359b8SXie Yongji true, NULL, NULL, NULL, NULL, NULL); 1862a2359b8SXie Yongji } 1872a2359b8SXie Yongji aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), 1882a2359b8SXie Yongji true, NULL, NULL, NULL, NULL, NULL); 1892a2359b8SXie Yongji 1902a2359b8SXie Yongji AIO_WAIT_WHILE(vblk_exp->export.ctx, vblk_exp->inflight > 0); 1912a2359b8SXie Yongji } 1922a2359b8SXie Yongji 1932a2359b8SXie Yongji 1942a2359b8SXie Yongji static void blk_aio_attached(AioContext *ctx, void *opaque) 1952a2359b8SXie Yongji { 1962a2359b8SXie Yongji VduseBlkExport *vblk_exp = opaque; 1972a2359b8SXie Yongji 1982a2359b8SXie Yongji vblk_exp->export.ctx = ctx; 1992a2359b8SXie Yongji vduse_blk_attach_ctx(vblk_exp, ctx); 2002a2359b8SXie Yongji } 2012a2359b8SXie Yongji 2022a2359b8SXie Yongji static void blk_aio_detach(void *opaque) 2032a2359b8SXie Yongji { 2042a2359b8SXie Yongji VduseBlkExport *vblk_exp = opaque; 2052a2359b8SXie Yongji 2062a2359b8SXie Yongji vduse_blk_detach_ctx(vblk_exp); 2072a2359b8SXie Yongji vblk_exp->export.ctx = NULL; 2082a2359b8SXie Yongji } 2092a2359b8SXie Yongji 2109e4dea67SXie Yongji static void vduse_blk_resize(void *opaque) 2119e4dea67SXie Yongji { 2129e4dea67SXie Yongji BlockExport *exp = opaque; 2139e4dea67SXie Yongji VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); 2149e4dea67SXie Yongji struct virtio_blk_config config; 2159e4dea67SXie Yongji 2169e4dea67SXie Yongji config.capacity = 2179e4dea67SXie Yongji cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); 2189e4dea67SXie Yongji vduse_dev_update_config(vblk_exp->dev, sizeof(config.capacity), 2199e4dea67SXie Yongji offsetof(struct virtio_blk_config, capacity), 2209e4dea67SXie Yongji (char *)&config.capacity); 2219e4dea67SXie Yongji } 2229e4dea67SXie Yongji 2239e4dea67SXie Yongji static const BlockDevOps vduse_block_ops = { 2249e4dea67SXie Yongji .resize_cb = vduse_blk_resize, 2259e4dea67SXie Yongji }; 2269e4dea67SXie Yongji 2272a2359b8SXie Yongji static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, 2282a2359b8SXie Yongji Error **errp) 2292a2359b8SXie Yongji { 2302a2359b8SXie Yongji VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); 2312a2359b8SXie Yongji BlockExportOptionsVduseBlk *vblk_opts = &opts->u.vduse_blk; 2322a2359b8SXie Yongji uint64_t logical_block_size = VIRTIO_BLK_SECTOR_SIZE; 2332a2359b8SXie Yongji uint16_t num_queues = VDUSE_DEFAULT_NUM_QUEUE; 2342a2359b8SXie Yongji uint16_t queue_size = VDUSE_DEFAULT_QUEUE_SIZE; 2352a2359b8SXie Yongji Error *local_err = NULL; 2362a2359b8SXie Yongji struct virtio_blk_config config = { 0 }; 2372a2359b8SXie Yongji uint64_t features; 2380862a087SXie Yongji int i, ret; 2392a2359b8SXie Yongji 2402a2359b8SXie Yongji if (vblk_opts->has_num_queues) { 2412a2359b8SXie Yongji num_queues = vblk_opts->num_queues; 2422a2359b8SXie Yongji if (num_queues == 0) { 2432a2359b8SXie Yongji error_setg(errp, "num-queues must be greater than 0"); 2442a2359b8SXie Yongji return -EINVAL; 2452a2359b8SXie Yongji } 2462a2359b8SXie Yongji } 2472a2359b8SXie Yongji 2482a2359b8SXie Yongji if (vblk_opts->has_queue_size) { 2492a2359b8SXie Yongji queue_size = vblk_opts->queue_size; 2502a2359b8SXie Yongji if (queue_size <= 2 || !is_power_of_2(queue_size) || 2512a2359b8SXie Yongji queue_size > VIRTQUEUE_MAX_SIZE) { 2522a2359b8SXie Yongji error_setg(errp, "queue-size is invalid"); 2532a2359b8SXie Yongji return -EINVAL; 2542a2359b8SXie Yongji } 2552a2359b8SXie Yongji } 2562a2359b8SXie Yongji 2572a2359b8SXie Yongji if (vblk_opts->has_logical_block_size) { 2582a2359b8SXie Yongji logical_block_size = vblk_opts->logical_block_size; 2592a2359b8SXie Yongji check_block_size(exp->id, "logical-block-size", logical_block_size, 2602a2359b8SXie Yongji &local_err); 2612a2359b8SXie Yongji if (local_err) { 2622a2359b8SXie Yongji error_propagate(errp, local_err); 2632a2359b8SXie Yongji return -EINVAL; 2642a2359b8SXie Yongji } 2652a2359b8SXie Yongji } 2662a2359b8SXie Yongji vblk_exp->num_queues = num_queues; 2672a2359b8SXie Yongji vblk_exp->handler.blk = exp->blk; 26854fde4ffSMarkus Armbruster vblk_exp->handler.serial = g_strdup(vblk_opts->serial ?: ""); 2692a2359b8SXie Yongji vblk_exp->handler.logical_block_size = logical_block_size; 2702a2359b8SXie Yongji vblk_exp->handler.writable = opts->writable; 2712a2359b8SXie Yongji 2722a2359b8SXie Yongji config.capacity = 2732a2359b8SXie Yongji cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS); 2742a2359b8SXie Yongji config.seg_max = cpu_to_le32(queue_size - 2); 2752a2359b8SXie Yongji config.min_io_size = cpu_to_le16(1); 2762a2359b8SXie Yongji config.opt_io_size = cpu_to_le32(1); 2772a2359b8SXie Yongji config.num_queues = cpu_to_le16(num_queues); 2782a2359b8SXie Yongji config.blk_size = cpu_to_le32(logical_block_size); 2792a2359b8SXie Yongji config.max_discard_sectors = cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS); 2802a2359b8SXie Yongji config.max_discard_seg = cpu_to_le32(1); 2812a2359b8SXie Yongji config.discard_sector_alignment = 2822a2359b8SXie Yongji cpu_to_le32(logical_block_size >> VIRTIO_BLK_SECTOR_BITS); 2832a2359b8SXie Yongji config.max_write_zeroes_sectors = 2842a2359b8SXie Yongji cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS); 2852a2359b8SXie Yongji config.max_write_zeroes_seg = cpu_to_le32(1); 2862a2359b8SXie Yongji 2872a2359b8SXie Yongji features = vduse_get_virtio_features() | 2882a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_SEG_MAX) | 2892a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_TOPOLOGY) | 2902a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_BLK_SIZE) | 2912a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_FLUSH) | 2922a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_DISCARD) | 2932a2359b8SXie Yongji (1ULL << VIRTIO_BLK_F_WRITE_ZEROES); 2942a2359b8SXie Yongji 2952a2359b8SXie Yongji if (num_queues > 1) { 2962a2359b8SXie Yongji features |= 1ULL << VIRTIO_BLK_F_MQ; 2972a2359b8SXie Yongji } 2982a2359b8SXie Yongji if (!opts->writable) { 2992a2359b8SXie Yongji features |= 1ULL << VIRTIO_BLK_F_RO; 3002a2359b8SXie Yongji } 3012a2359b8SXie Yongji 302779d82e1SXie Yongji vblk_exp->dev = vduse_dev_create(vblk_opts->name, VIRTIO_ID_BLOCK, 0, 3032a2359b8SXie Yongji features, num_queues, 3042a2359b8SXie Yongji sizeof(struct virtio_blk_config), 3052a2359b8SXie Yongji (char *)&config, &vduse_blk_ops, 3062a2359b8SXie Yongji vblk_exp); 3072a2359b8SXie Yongji if (!vblk_exp->dev) { 3082a2359b8SXie Yongji error_setg(errp, "failed to create vduse device"); 3090862a087SXie Yongji ret = -ENOMEM; 3100862a087SXie Yongji goto err_dev; 3112a2359b8SXie Yongji } 3122a2359b8SXie Yongji 313d043e2dbSXie Yongji vblk_exp->recon_file = g_strdup_printf("%s/vduse-blk-%s", 314779d82e1SXie Yongji g_get_tmp_dir(), vblk_opts->name); 315d043e2dbSXie Yongji if (vduse_set_reconnect_log_file(vblk_exp->dev, vblk_exp->recon_file)) { 316d043e2dbSXie Yongji error_setg(errp, "failed to set reconnect log file"); 3170862a087SXie Yongji ret = -EINVAL; 3180862a087SXie Yongji goto err; 319d043e2dbSXie Yongji } 320d043e2dbSXie Yongji 3212a2359b8SXie Yongji for (i = 0; i < num_queues; i++) { 3222a2359b8SXie Yongji vduse_dev_setup_queue(vblk_exp->dev, i, queue_size); 3232a2359b8SXie Yongji } 3242a2359b8SXie Yongji 3252a2359b8SXie Yongji aio_set_fd_handler(exp->ctx, vduse_dev_get_fd(vblk_exp->dev), true, 3262a2359b8SXie Yongji on_vduse_dev_kick, NULL, NULL, NULL, vblk_exp->dev); 3272a2359b8SXie Yongji 3282a2359b8SXie Yongji blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, 3292a2359b8SXie Yongji vblk_exp); 3302a2359b8SXie Yongji 3319e4dea67SXie Yongji blk_set_dev_ops(exp->blk, &vduse_block_ops, exp); 3329e4dea67SXie Yongji 3332a2359b8SXie Yongji return 0; 3340862a087SXie Yongji err: 3350862a087SXie Yongji vduse_dev_destroy(vblk_exp->dev); 3360862a087SXie Yongji g_free(vblk_exp->recon_file); 3370862a087SXie Yongji err_dev: 3380862a087SXie Yongji g_free(vblk_exp->handler.serial); 3390862a087SXie Yongji return ret; 3402a2359b8SXie Yongji } 3412a2359b8SXie Yongji 3422a2359b8SXie Yongji static void vduse_blk_exp_delete(BlockExport *exp) 3432a2359b8SXie Yongji { 3442a2359b8SXie Yongji VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); 345d043e2dbSXie Yongji int ret; 3462a2359b8SXie Yongji 3472a2359b8SXie Yongji blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, 3482a2359b8SXie Yongji vblk_exp); 3499e4dea67SXie Yongji blk_set_dev_ops(exp->blk, NULL, NULL); 350d043e2dbSXie Yongji ret = vduse_dev_destroy(vblk_exp->dev); 351d043e2dbSXie Yongji if (ret != -EBUSY) { 352d043e2dbSXie Yongji unlink(vblk_exp->recon_file); 353d043e2dbSXie Yongji } 354d043e2dbSXie Yongji g_free(vblk_exp->recon_file); 3550862a087SXie Yongji g_free(vblk_exp->handler.serial); 3562a2359b8SXie Yongji } 3572a2359b8SXie Yongji 3582a2359b8SXie Yongji static void vduse_blk_exp_request_shutdown(BlockExport *exp) 3592a2359b8SXie Yongji { 3602a2359b8SXie Yongji VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export); 3612a2359b8SXie Yongji 3622a2359b8SXie Yongji aio_context_acquire(vblk_exp->export.ctx); 3632a2359b8SXie Yongji vduse_blk_detach_ctx(vblk_exp); 3642a2359b8SXie Yongji aio_context_acquire(vblk_exp->export.ctx); 3652a2359b8SXie Yongji } 3662a2359b8SXie Yongji 3672a2359b8SXie Yongji const BlockExportDriver blk_exp_vduse_blk = { 3682a2359b8SXie Yongji .type = BLOCK_EXPORT_TYPE_VDUSE_BLK, 3692a2359b8SXie Yongji .instance_size = sizeof(VduseBlkExport), 3702a2359b8SXie Yongji .create = vduse_blk_exp_create, 3712a2359b8SXie Yongji .delete = vduse_blk_exp_delete, 3722a2359b8SXie Yongji .request_shutdown = vduse_blk_exp_request_shutdown, 3732a2359b8SXie Yongji }; 374