描述了unikraft的块设备及其功能
数据结构
各结构体之间的关联
uk_blkdev
1 | struct uk_blkdev { |
- submit_one:通过将信息放入virtio buffer,向host提交事件请求。只是一个接口,具体实现由
virtio_blk.c
文件中的virtio_blkdev_submit_request
函数完成 - finish_reqs:host完成请求,并通过virtqueue_vring将请求出队,返还给uk_blkdev。只是一个接口,具体实现由
virtio_blk.c
文件中的virtio_blkdev_complete_reqs
函数完成 - _data:目前块设备的具体数据信息
- capabilities:块设备的容量
- dev_ops:块设备可实现的操作,只定义接口,具体实现由virtio_blk完成
- _queue[CONFIG_LIBUKBLKDEV_MAXNBQUEUES]:块设备队列,具体结构在
virtio_blk.c
文件中 - _list:UK_TAILQ_ENTRY(环状双链表表项),用于将blkdev链接至uk_blkdev_list
uk_blkdev_data
1
2
3
4
5
6
7
8
9
10
11
12
13struct uk_blkdev_data {
/* Device id identifier */
const uint16_t id;
/* Device state */
enum uk_blkdev_state state;
/* Event handler for each queue */
struct uk_blkdev_event_handler
queue_handler[CONFIG_LIBUKBLKDEV_MAXNBQUEUES];
/* Name of device*/
const char *drv_name;
/* Allocator */
struct uk_alloc *a;
}; - id:块设备的id号,id号根据系统创建顺序进行分配,从0开始。
- state:设备状态信息,枚举类型
- queue_handler[CONFIG_LIBUKBLKDEV_MAXNBQUEUES]:块设备的任务管理器
uk_blkdev_event_handler
用来实现用户的线程任务1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21struct uk_blkdev_event_handler {
/* Callback */
uk_blkdev_queue_event_t callback;
/* Parameter for callback */
void *cookie;
/* Semaphore to trigger events. */
struct uk_semaphore events;
/* Reference to blk device. */
struct uk_blkdev *dev;
/* Queue id which caused event. */
uint16_t queue_id;
/* Dispatcher thread. */
struct uk_thread *dispatcher;
/* Reference to thread name. */
char *dispatcher_name;
/* Scheduler for dispatcher. */
struct uk_sched *dispatcher_s;
};uk_blkdev_cap
1
2
3
4
5
6
7
8
9
10
11
12struct uk_blkdev_cap {
/* Number of sectors */
__sector sectors;
/* Sector size */
size_t ssize;
/* Access mode (O_RDONLY, O_RDWR, O_WRONLY) */
int mode;
/* Max nb of supported sectors for an op */
__sector max_sectors_per_req;
/* Alignment (number of bytes) for data used in future requests */
uint16_t ioalign;
}; - sectors:每个块设备扇区的数量
- ssize:每个扇区的大小
- mode:扇区的使用模式
- max_sectors_per_req:每次请求可以分配的最大数量的扇区
- ioalign:请求的字节对齐方式
uk_blkdev_ops
具体实现在1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22struct uk_blkdev_ops {
// virtio_blk_device的max_vqueue_pairs传入dev_info->max_queues
uk_blkdev_get_info_t get_info;
// 为virtio_blk_device创建uk_blkdev_queue空间,并conf配置
uk_blkdev_configure_t dev_configure;
// 根据virtio_blkdev_queue->qs[queue_id]对应的uk_blkdev_queue,获取nb_min、nb_max、nb_is_power_of_two存入qinfo
uk_blkdev_queue_get_info_t queue_get_info;
// 在vbdev->qs[queue_id]建立uk_blkdev_queue的成员,并返回queue
uk_blkdev_queue_configure_t queue_configure;
// virtio_blkdev配置状态更新完成
uk_blkdev_start_t dev_start;
// 停止virtio_blkdev
uk_blkdev_stop_t dev_stop;
// 对uk_blkdev_queue进行中断使能
uk_blkdev_queue_intr_enable_t queue_intr_enable;
// 禁止uk_blkdev_queue过程进行中断
uk_blkdev_queue_intr_disable_t queue_intr_disable;
// 释放uk_blkdev_queue的空间
uk_blkdev_queue_unconfigure_t queue_unconfigure;
// 释放virtio_blk_device的uk_blkdev_queue空间
uk_blkdev_unconfigure_t dev_unconfigure;
};vitrio_blk.c
中1
2
3
4
5
6
7
8
9
10
11
12static const struct uk_blkdev_ops virtio_blkdev_ops = {
.get_info = virtio_blkdev_get_info,
.dev_configure = virtio_blkdev_configure,
.queue_get_info = virtio_blkdev_queue_info_get,
.queue_configure = virtio_blkdev_queue_setup,
.queue_intr_enable = virtio_blkdev_queue_intr_enable,
.dev_start = virtio_blkdev_start,
.dev_stop = virtio_blkdev_stop,
.queue_intr_disable = virtio_blkdev_queue_intr_disable,
.queue_unconfigure = virtio_blkdev_queue_release,
.dev_unconfigure = virtio_blkdev_unconfigure,
};操作
对virtio层的抽象操作
以下函数均是基于uk_blkdev_ops的成员操作实现的外层抽象以下是基于块设备的submit_one、finish_reqs操作实现的外层抽象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 得到块设备支持的队列的最大数量
int uk_blkdev_get_info(struct uk_blkdev *dev, struct uk_blkdev_info *dev_info)
// 为块设备所在的virtio块设备创建块设备队列空间,并conf配置
int uk_blkdev_configure(struct uk_blkdev *dev, const struct uk_blkdev_conf *conf)
// 获取块设备的第queue_id个队列描述符的最大数量、最小数量、2的幂后的数量存入qinfo
int uk_blkdev_queue_get_info(struct uk_blkdev *dev, uint16_t queue_id,
struct uk_blkdev_queue_info *q_info)
// 创建事件管理+构建块设备的第queue_id个块设备队列
int uk_blkdev_queue_configure(struct uk_blkdev *dev, uint16_t queue_id,
uint16_t nb_desc,
const struct uk_blkdev_queue_conf *queue_conf)
// virtio块设备状态更新完成,可以开始任务
int uk_blkdev_start(struct uk_blkdev *dev)
// 停用dev块设备,不能再向host发送任何请求
int uk_blkdev_stop(struct uk_blkdev *dev)
// 释放块设备中的一个队列
int uk_blkdev_queue_unconfigure(struct uk_blkdev *dev, uint16_t queue_id)
// 释放virtio块设备的块队列队头成员空间
int uk_blkdev_unconfigure(struct uk_blkdev *dev)1
2
3
4
5
6// 通过将信息放入virtio buffer,向host提交事件请求
int uk_blkdev_queue_submit_one(struct uk_blkdev *dev, uint16_t queue_id,
struct uk_blkreq *req)
// host完成事件请求
int uk_blkdev_queue_finish_reqs(struct uk_blkdev *dev,
uint16_t queue_id)事件管理结构的创建及销毁
创建一个event handler,并传给块设备
如果有线程的话,在event_handler中顺便创建线程销毁事件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
48static int _create_event_handler(uk_blkdev_queue_event_t callback,
void *cookie,
struct uk_blkdev *dev, uint16_t queue_id,
struct uk_sched *s,
struct uk_blkdev_event_handler *event_handler)
{
UK_ASSERT(event_handler);
UK_ASSERT(callback || (!callback && !cookie));
event_handler->callback = callback;
event_handler->cookie = cookie;
/* If we do not have a callback, we do not need a thread */
if (!callback)
return 0;
event_handler->dev = dev;
event_handler->queue_id = queue_id;
uk_semaphore_init(&event_handler->events, 0);
event_handler->dispatcher_s = s;
/* Create a name for the dispatcher thread.
* In case of errors, we just continue without a name
*/
if (asprintf(&event_handler->dispatcher_name,
"blkdev%"PRIu16"-q%"PRIu16"]",
dev->_data->id, queue_id) < 0) {
event_handler->dispatcher_name = NULL;
}
/* Create thread */
event_handler->dispatcher = uk_sched_thread_create(
event_handler->dispatcher_s,
event_handler->dispatcher_name, NULL,
_dispatcher, (void *)event_handler);
// 线程没有创建成功,则释放空间,并报错
if (event_handler->dispatcher == NULL) {
if (event_handler->dispatcher_name) {
free(event_handler->dispatcher);
event_handler->dispatcher = NULL;
}
return -ENOMEM;
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21static void _destroy_event_handler(struct uk_blkdev_event_handler *h
__maybe_unused)
{
UK_ASSERT(h);
if (h->dispatcher) {
uk_semaphore_up(&h->events); // 信号量增加
UK_ASSERT(h->dispatcher_s);
uk_thread_kill(h->dispatcher);
uk_thread_wait(h->dispatcher);
h->dispatcher = NULL;
}
// 释放线程命名空间
if (h->dispatcher_name) {
free(h->dispatcher_name);
h->dispatcher_name = NULL;
}
}
注册块设备
在virtio驱动创建块设备时使用
1.分配dev->_data空间
2.将块设备插入到块设备链表尾部
3.系统块设备数量+1
1 | int uk_blkdev_drv_register(struct uk_blkdev *dev, struct uk_alloc *a, |
注销块设备
1.释放dev->_data空间
2.将块设备从块设备链表中移除
3.系统块设备数量-1
1 | void uk_blkdev_drv_unregister(struct uk_blkdev *dev) |