forked from yandex-cloud/yc-libvhost-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblockdev.c
166 lines (132 loc) · 4.89 KB
/
blockdev.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <inttypes.h>
#include <stdint.h>
#include "vhost/blockdev.h"
#include "server_internal.h"
#include "vdev.h"
#include "logging.h"
#include "bio.h"
#include "virtio/virtio_blk.h"
struct vhd_bdev {
/* Base vdev */
struct vhd_vdev vdev;
/* VM-facing interface type */
struct virtio_blk_dev vblk;
LIST_ENTRY(vhd_bdev) blockdevs;
};
static LIST_HEAD(, vhd_bdev) g_bdev_list = LIST_HEAD_INITIALIZER(g_bdev_list);
#define VHD_BLOCKDEV_FROM_VDEV(ptr) containerof(ptr, struct vhd_bdev, vdev)
/*////////////////////////////////////////////////////////////////////////////*/
static uint64_t vblk_get_features(struct vhd_vdev *vdev)
{
struct vhd_bdev *dev = VHD_BLOCKDEV_FROM_VDEV(vdev);
return virtio_blk_get_features(&dev->vblk);
}
static int vblk_set_features(struct vhd_vdev *vdev, uint64_t features)
{
return 0;
}
/* vhost_get_config assumes that config is less than VHOST_USER_CONFIG_SPACE_MAX */
VHD_STATIC_ASSERT(sizeof(struct virtio_blk_config) <= VHOST_USER_CONFIG_SPACE_MAX);
static size_t vblk_get_config(struct vhd_vdev *vdev, void *cfgbuf,
size_t bufsize, size_t offset)
{
struct vhd_bdev *dev = VHD_BLOCKDEV_FROM_VDEV(vdev);
return virtio_blk_get_config(&dev->vblk, cfgbuf, bufsize, offset);
}
static int vblk_dispatch(struct vhd_vdev *vdev, struct vhd_vring *vring)
{
struct vhd_bdev *dev = VHD_BLOCKDEV_FROM_VDEV(vdev);
return virtio_blk_dispatch_requests(&dev->vblk, &vring->vq);
}
static void vblk_free(struct vhd_vdev *vdev)
{
struct vhd_bdev *bdev = VHD_BLOCKDEV_FROM_VDEV(vdev);
LIST_REMOVE(bdev, blockdevs);
virtio_blk_destroy_dev(&bdev->vblk);
vhd_free(bdev);
}
static const struct vhd_vdev_type g_virtio_blk_vdev_type = {
.desc = "virtio-blk",
.get_features = vblk_get_features,
.set_features = vblk_set_features,
.get_config = vblk_get_config,
.dispatch_requests = vblk_dispatch,
.free = vblk_free,
};
struct set_total_blocks {
struct vhd_vdev *vdev;
uint64_t total_blocks;
};
static void set_total_blocks_entry(struct vhd_work *work, void *opaque)
{
struct set_total_blocks *stb = opaque;
struct vhd_bdev *dev = VHD_BLOCKDEV_FROM_VDEV(stb->vdev);
virtio_blk_set_total_blocks(&dev->vblk, stb->total_blocks);
vhd_complete_work(work, 0);
}
void vhd_blockdev_set_total_blocks(struct vhd_vdev *vdev, uint64_t total_blocks)
{
struct set_total_blocks stb = {
.vdev = vdev,
.total_blocks = total_blocks,
};
VHD_OBJ_INFO(vdev, "Set total blocks %" PRIu64, total_blocks);
/*
* Modify virtio config in g_vhost_evloop, to not interfere with .get_config
*
* We don't need vdev_submit_work_and_wait() logic here, as setting
* total_blocks in config is unrelated stopping process, so it should not be
* a problem intersect with wdev_stop_work work.
*/
int ret = vhd_submit_ctl_work_and_wait(set_total_blocks_entry, &stb);
VHD_VERIFY(ret == 0);
}
static bool blockdev_validate_features(const struct vhd_bdev_info *bdev)
{
const uint64_t valid_features = VHD_BDEV_F_READONLY |
VHD_BDEV_F_DISCARD |
VHD_BDEV_F_WRITE_ZEROES;
return (bdev->features & valid_features) == bdev->features;
}
struct vhd_vdev *vhd_register_blockdev(const struct vhd_bdev_info *bdev,
struct vhd_request_queue **rqs,
int num_rqs, void *priv)
{
int res;
if (!bdev->total_blocks || !bdev->block_size) {
VHD_LOG_ERROR("Zero blockdev capacity %" PRIu64 " * %" PRIu32,
bdev->total_blocks, bdev->block_size);
return NULL;
}
if ((bdev->block_size & (bdev->block_size - 1)) ||
bdev->block_size % VHD_SECTOR_SIZE) {
VHD_LOG_ERROR("Block size %" PRIu32 " is not"
" a power of two multiple of sector size (%llu)",
bdev->block_size, VHD_SECTOR_SIZE);
return NULL;
}
if (!blockdev_validate_features(bdev)) {
VHD_LOG_ERROR("Invalid blockdev features %" PRIu64, bdev->features);
return NULL;
}
struct vhd_bdev *dev = vhd_zalloc(sizeof(*dev));
virtio_blk_init_dev(&dev->vblk, bdev);
res = vhd_vdev_init_server(&dev->vdev, bdev->socket_path,
&g_virtio_blk_vdev_type,
bdev->num_queues, rqs, num_rqs, priv,
bdev->map_cb, bdev->unmap_cb);
if (res != 0) {
goto error_out;
}
LIST_INSERT_HEAD(&g_bdev_list, dev, blockdevs);
return &dev->vdev;
error_out:
virtio_blk_destroy_dev(&dev->vblk);
vhd_free(dev);
return NULL;
}
void vhd_unregister_blockdev(struct vhd_vdev *vdev,
void (*unregister_complete)(void *), void *arg)
{
vhd_vdev_stop_server(vdev, unregister_complete, arg);
}