tapdisk vbd
重新回到 tapdisk2 的进程,这里我们重点研究下 tapdisk-vbd.c 中的重要函数
tapdisk2 进程初始化会调用 tapdisk2_open_device 打开通过配置项目指定的 vbd 设备,e.g. vhd:xxxxx:xxxxxx
tapdisk_vbd_initialize 生成一个 td_vbd_t 结构
tapdisk_server_get_vbd 获取这个新的 td_vbd_t 结构
tapdisk_vbd_parse_stack 分析 tapdisk 配置内容
tapdisk_vbd_open 打开相应的设备,包括
- tapdisk_vbd_open_stack 调用 __tapdisk_vbd_open_vdi,该函数生成一个 td_image_t 结构的镜像,以及对应的 td_driver_t
- tapdisk_vbd_map_device 首先打开 blktapXX 的字符设备,然后mmap BLKTAP_MMAP_REGION_SIZE 大小的内存。最后调用一个ioctl(ring->fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE) 告诉设备。ring->mem 第一个page,用来作为 IO环的shared page,之后的MMAP_PAGES 大小,即 32*11个页用来放IO请求对应的页框
- tapdisk_vbd_register_event_watches 在 vbd->ring.fd 上注册一个 SCHEDULER_POLL_READ_FD 事件,其中回调函数为 tapdisk_vbd_ring_event
- tapdisk_vbd_ring_event 原型如下
static void
tapdisk_vbd_ring_event(event_id_t id, char mode, void *private)
{
td_vbd_t *vbd;
vbd = (td_vbd_t *)private;
tapdisk_vbd_pull_ring_requests(vbd);
tapdisk_vbd_issue_requests(vbd);
/* vbd may be destroyed after this call */
tapdisk_vbd_check_ring_message(vbd);
}
tapdisk_vbd_pull_ring_requests 从IO环里把 IO 请求拿出来,然后调用 tapdisk_vbd_move_request 把请求加入到 vbd->new_requests 队列里。
tapdisk_vbd_issue_requests 首先会再次尝试上一次失败的请求,即调用 tapdisk_vbd_reissue_failed_requests,之后尝试这一次新的请求,调用 tapdisk_vbd_issue_new_requests。最终都会走到 tapdisk_vbd_issue_request 中。
tapdisk_vbd_issue_request 对于这个 blkif_request_t 请求的每一个 segment 首先把 vbd_request_t 封装成一个 td_request_t 结构的 IO 请求,然后调用具体driver 指向的 td_queue_write / td_queue_read
对于 td_queue_read / td_queue_write 的每一个请求,其 callback 函数为 tapdisk_vbd_complete_td_request,最终是根据请求是否成功执行,把其放到 vbd->failed_requests 或者 vbd->completed_requests 队列中。
最后提一个函数 tapdisk_vbd_kick,这个是在收到了 IO 的 response 之后,把结果写到 IO 环里面,再调一个 ioctl(ring->fd, BLKTAP_IOCTL_KICK_FE, 0) 让后端设备知道请求已经返回了。把response 写回到 ring 的工作是由 vbd 的callback函数 tapdisk_vbd_callback 完成的。其中 tapdisk_vbd_callback 最终调用的 tapdisk_vbd_write_response_to_ring