xen块设备体系结构(7)
来源:互联网 发布:鸟哥linux官网 编辑:程序博客网 时间:2024/05/21 17:17
tapdisk
恭喜,我们又回到tapdisk2进程上面来。我们已经知道 tapdevXXX 的块设备的读写,最终要落到实际的物理磁盘上,是要经过blktapXXX 字符设备,以及 tapdisk2进程的。我们首先来看 tapdisk2 进程是如何获取IO request,以及把 IO response 返回给 后端驱动的。
tapdisk2 进程是从 tapdisk2.c 的main函数开始的,main函数最后调用一个fork之后,父进程等待子进程通过一个 channel 管道返回成功信息之后退出。 而子进程则会调用 tapdisk2_create_device(params) 其中params 一般为 -n 传进来的虚拟设备参数 e.g. vhd:12345.vhd
tapdisk2_create_device函数前面的文章有过提及,这里我们最关心的是 tapdisk2_open_device 这个调用:
static int tapdisk2_open_device(int type, const char *path, const char *name)
{
int err;
td_vbd_t *vbd;
image_t image;
char *devname;
struct blktap2_params params;
err = tapdisk_vbd_initialize(-1, -1, TAPDISK2_VBD);
if (err)
return err;
vbd = tapdisk_server_get_vbd(TAPDISK2_VBD);
if (!vbd) {
err = -ENODEV;
CHILD_ERR(err, "couldn't find vbd\n");
return err;
}
err = asprintf(&devname, "%s%d", BLKTAP2_RING_DEVICE, handle.minor);
if (err == -1) {
err = -ENOMEM;
CHILD_ERR(err, "couldn't allocate ring\n");
return err;
}
err = tapdisk_vbd_parse_stack(vbd, name);
if (err) {
CHILD_ERR(err, "vbd_parse_stack failed: %d\n", err);
return err;
}
/* TODO: clean this up */
err = tapdisk_vbd_open(vbd, path, type,
TAPDISK_STORAGE_TYPE_DEFAULT,
devname, 0);
free(devname);
if (err) {
CHILD_ERR(err, "vbd open failed: %d\n", err);
return err;
}
memset(¶ms, 0, sizeof(params));
tapdisk_vbd_get_image_info(vbd, &image);
params.capacity = image.size;
params.sector_size = image.secsize;
snprintf(params.name, sizeof(params.name) - 1, "%s", name);
err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, ¶ms);
if (err) {
err = -errno;
CHILD_ERR(err, "create device failed: %d\n", err);
return err;
}
return 0;
}
对于上文的参数,传入的type为vhd,path为 12345.vhd,name为 vhd:12345.vhd
tapdisk_vbd_initialize 通过uuid,在当前tapdisk2进程管理的所有vbd设备中查找相应的 td_vbd_t 结构,如果查到说明设备已存在,退出。否则构造一个 td_vbd_t,注意这里的 td_vbd_t -> callback,其原型是
static void tapdisk_vbd_callback(void *arg, blkif_response_t *rsp)
{
td_vbd_t *vbd = (td_vbd_t *)arg;
tapdisk_vbd_write_response_to_ring(vbd, rsp);
}
在一系列初始化结束之后,把 td_vbd_t 结构加入到 tapdisk server 中
每一个tapdisk2进程启动,都会对应增加一个blktapXXX设备,一个tapdevXXX设备,此时全局变量handle.minor 就记录了设备的次设备号。
tapdisk_vbd_parse_stack 也是进行parse工作,根据参数,比如 vhd 还是 aio ,在disk_info_t 里找出对应的 disk_type ,并把信息记录在 td_vbd_driver_info 里。
tapdisk_vbd_open 调用如下:
int tapdisk_vbd_open(td_vbd_t *vbd, const char *name, uint16_t type,
uint16_t storage, const char *ring, td_flag_t flags)
{
int err;
err = tapdisk_vbd_open_stack(vbd, storage, flags);
if (err)
goto out;
err = tapdisk_vbd_map_device(vbd, ring);
if (err)
goto out;
err = tapdisk_vbd_register_event_watches(vbd);
if (err)
goto out;
return 0;
out:
tapdisk_vbd_close_vdi(vbd);
tapdisk_vbd_unmap_device(vbd);
tapdisk_vbd_unregister_events(vbd);
free(vbd->name);
vbd->name = NULL;
return err;
}
tapdisk_vbd_map_device 首先打开 blktapXXX 字符设备,并存在 td_vbd_t -> ring.fd 里,其中td_ring 结构代表了 blktapXXX 设备。之后 tapdisk2 进程和 blktapXXX 设备通过共享内存进行通信,先调用mmap 创建一块共享内存,其大小为 BLKTAP_MMAP_REGION_SIZE 。
BLKTAP_MMAP_REGION_SIZE = BLKTAP_RING_PAGES + MMAP_PAGES
BLKTAP_RING_PAGES 为 1, 这个page代表一个IO共享环,数据结构可以参考ring.h
MMAP_PAGES 大小为 32*11,代表实际的IO请求的页。一个共享环最多可以有 MAX_PENDING_REQS 个request,每个request最多11个segment,也就是11个page
接下来进行一系列初始化,ring->vstart 为实际IO请求所在的内存空间起始。代码如下:
static int tapdisk_vbd_map_device(td_vbd_t *vbd, const char *devname)
{
int err, psize;
td_ring_t *ring;
ring = &vbd->ring;
psize = getpagesize();
ring->fd = open(devname, O_RDWR);
if (ring->fd == -1) {
err = -errno;
EPRINTF("failed to open %s: %d\n", devname, err);
goto fail;
}
ring->mem = mmap(0, psize * BLKTAP_MMAP_REGION_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED, ring->fd, 0);
if (ring->mem == MAP_FAILED) {
err = -errno;
EPRINTF("failed to mmap %s: %d\n", devname, err);
goto fail;
}
ring->sring = (blkif_sring_t *)((unsigned long)ring->mem);
BACK_RING_INIT(&ring->fe_ring, ring->sring, psize);
ring->vstart =
(unsigned long)ring->mem + (BLKTAP_RING_PAGES * psize);
ioctl(ring->fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE);
return 0;
fail:
if (ring->mem && ring->mem != MAP_FAILED)
munmap(ring->mem, psize * BLKTAP_MMAP_REGION_SIZE);
if (ring->fd != -1)
close(ring->fd);
ring->fd = -1;
ring->mem = NULL;
return err;
}
最后调用的是 tapdisk_vbd_register_event_watches,里面调用了 tapdisk_server_register_event 在字符设备的 fd 上注册了事件 SCHEDULER_POLL_READ_FD,其 callback 函数为 tapdisk_vbd_ring_event , 而 tapdisk_vbd_ring_event 主要做如下的事情:
1. 当有读事件到来时,调用 tapdisk_vbd_pull_ring_requests ,从IO环里把 blkif_request_t 取出,每次取出上次req_cons 到 这次 req_prod这一段所有的request,放入 vbd->new_requests
2. 接着调用 tapdisk_vbd_issue_requests 该函数会尝试重新issue之前 fail 的请求,然后尝试 issue 新的请求,whatever,最终还是落到 tapdisk_vbd_issue_request 里面。
tapdisk_vbd_issue_request 会对request 里面每一个segment,最终去调用 td_queue_write 或者 td_queue_read。
td_queue_write/td_queue_read 会基于具体image,比如 vhd 或者 img,调用相应的 driver 执行真正的操作,具体的操作请参考
struct tap_disk {
const char *disk_type;
td_flag_t flags;
int private_data_size;
int (*td_open) (td_driver_t *, const char *, td_flag_t);
int (*td_close) (td_driver_t *);
int (*td_get_parent_id) (td_driver_t *, td_disk_id_t *);
int (*td_validate_parent) (td_driver_t *, td_driver_t *, td_flag_t);
void (*td_queue_read) (td_driver_t *, td_request_t);
void (*td_queue_write) (td_driver_t *, td_request_t);
void (*td_debug) (td_driver_t *);
};
tapdisk
恭喜,我们又回到tapdisk2进程上面来。我们已经知道 tapdevXXX 的块设备的读写,最终要落到实际的物理磁盘上,是要经过blktapXXX 字符设备,以及 tapdisk2进程的。我们首先来看 tapdisk2 进程是如何获取IO request,以及把 IO response 返回给 后端驱动的。
tapdisk2 进程是从 tapdisk2.c 的main函数开始的,main函数最后调用一个fork之后,父进程等待子进程通过一个 channel 管道返回成功信息之后退出。 而子进程则会调用 tapdisk2_create_device(params) 其中params 一般为 -n 传进来的虚拟设备参数 e.g. vhd:12345.vhd
tapdisk2_create_device函数前面的文章有过提及,这里我们最关心的是 tapdisk2_open_device 这个调用:
static int tapdisk2_open_device(int type, const char *path, const char *name)
{
int err;
td_vbd_t *vbd;
image_t image;
char *devname;
struct blktap2_params params;
err = tapdisk_vbd_initialize(-1, -1, TAPDISK2_VBD);
if (err)
return err;
vbd = tapdisk_server_get_vbd(TAPDISK2_VBD);
if (!vbd) {
err = -ENODEV;
CHILD_ERR(err, "couldn't find vbd\n");
return err;
}
err = asprintf(&devname, "%s%d", BLKTAP2_RING_DEVICE, handle.minor);
if (err == -1) {
err = -ENOMEM;
CHILD_ERR(err, "couldn't allocate ring\n");
return err;
}
err = tapdisk_vbd_parse_stack(vbd, name);
if (err) {
CHILD_ERR(err, "vbd_parse_stack failed: %d\n", err);
return err;
}
/* TODO: clean this up */
err = tapdisk_vbd_open(vbd, path, type,
TAPDISK_STORAGE_TYPE_DEFAULT,
devname, 0);
free(devname);
if (err) {
CHILD_ERR(err, "vbd open failed: %d\n", err);
return err;
}
memset(¶ms, 0, sizeof(params));
tapdisk_vbd_get_image_info(vbd, &image);
params.capacity = image.size;
params.sector_size = image.secsize;
snprintf(params.name, sizeof(params.name) - 1, "%s", name);
err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, ¶ms);
if (err) {
err = -errno;
CHILD_ERR(err, "create device failed: %d\n", err);
return err;
}
return 0;
}
对于上文的参数,传入的type为vhd,path为 12345.vhd,name为 vhd:12345.vhd
tapdisk_vbd_initialize 通过uuid,在当前tapdisk2进程管理的所有vbd设备中查找相应的 td_vbd_t 结构,如果查到说明设备已存在,退出。否则构造一个 td_vbd_t,注意这里的 td_vbd_t -> callback,其原型是
static void tapdisk_vbd_callback(void *arg, blkif_response_t *rsp)
{
td_vbd_t *vbd = (td_vbd_t *)arg;
tapdisk_vbd_write_response_to_ring(vbd, rsp);
}
在一系列初始化结束之后,把 td_vbd_t 结构加入到 tapdisk server 中
每一个tapdisk2进程启动,都会对应增加一个blktapXXX设备,一个tapdevXXX设备,此时全局变量handle.minor 就记录了设备的次设备号。
tapdisk_vbd_parse_stack 也是进行parse工作,根据参数,比如 vhd 还是 aio ,在disk_info_t 里找出对应的 disk_type ,并把信息记录在 td_vbd_driver_info 里。
tapdisk_vbd_open 调用如下:
int tapdisk_vbd_open(td_vbd_t *vbd, const char *name, uint16_t type,
uint16_t storage, const char *ring, td_flag_t flags)
{
int err;
err = tapdisk_vbd_open_stack(vbd, storage, flags);
if (err)
goto out;
err = tapdisk_vbd_map_device(vbd, ring);
if (err)
goto out;
err = tapdisk_vbd_register_event_watches(vbd);
if (err)
goto out;
return 0;
out:
tapdisk_vbd_close_vdi(vbd);
tapdisk_vbd_unmap_device(vbd);
tapdisk_vbd_unregister_events(vbd);
free(vbd->name);
vbd->name = NULL;
return err;
}
tapdisk_vbd_map_device 首先打开 blktapXXX 字符设备,并存在 td_vbd_t -> ring.fd 里,其中td_ring 结构代表了 blktapXXX 设备。之后 tapdisk2 进程和 blktapXXX 设备通过共享内存进行通信,先调用mmap 创建一块共享内存,其大小为 BLKTAP_MMAP_REGION_SIZE 。
BLKTAP_MMAP_REGION_SIZE = BLKTAP_RING_PAGES + MMAP_PAGES
BLKTAP_RING_PAGES 为 1, 这个page代表一个IO共享环,数据结构可以参考ring.h
MMAP_PAGES 大小为 32*11,代表实际的IO请求的页。一个共享环最多可以有 MAX_PENDING_REQS 个request,每个request最多11个segment,也就是11个page
接下来进行一系列初始化,ring->vstart 为实际IO请求所在的内存空间起始。代码如下:
static int tapdisk_vbd_map_device(td_vbd_t *vbd, const char *devname)
{
int err, psize;
td_ring_t *ring;
ring = &vbd->ring;
psize = getpagesize();
ring->fd = open(devname, O_RDWR);
if (ring->fd == -1) {
err = -errno;
EPRINTF("failed to open %s: %d\n", devname, err);
goto fail;
}
ring->mem = mmap(0, psize * BLKTAP_MMAP_REGION_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED, ring->fd, 0);
if (ring->mem == MAP_FAILED) {
err = -errno;
EPRINTF("failed to mmap %s: %d\n", devname, err);
goto fail;
}
ring->sring = (blkif_sring_t *)((unsigned long)ring->mem);
BACK_RING_INIT(&ring->fe_ring, ring->sring, psize);
ring->vstart =
(unsigned long)ring->mem + (BLKTAP_RING_PAGES * psize);
ioctl(ring->fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE);
return 0;
fail:
if (ring->mem && ring->mem != MAP_FAILED)
munmap(ring->mem, psize * BLKTAP_MMAP_REGION_SIZE);
if (ring->fd != -1)
close(ring->fd);
ring->fd = -1;
ring->mem = NULL;
return err;
}
最后调用的是 tapdisk_vbd_register_event_watches,里面调用了 tapdisk_server_register_event 在字符设备的 fd 上注册了事件 SCHEDULER_POLL_READ_FD,其 callback 函数为 tapdisk_vbd_ring_event , 而 tapdisk_vbd_ring_event 主要做如下的事情:
1. 当有读事件到来时,调用 tapdisk_vbd_pull_ring_requests ,从IO环里把 blkif_request_t 取出,每次取出上次req_cons 到 这次 req_prod这一段所有的request,放入 vbd->new_requests
2. 接着调用 tapdisk_vbd_issue_requests 该函数会尝试重新issue之前 fail 的请求,然后尝试 issue 新的请求,whatever,最终还是落到 tapdisk_vbd_issue_request 里面。
tapdisk_vbd_issue_request 会对request 里面每一个segment,最终去调用 td_queue_write 或者 td_queue_read。
td_queue_write/td_queue_read 会基于具体image,比如 vhd 或者 img,调用相应的 driver 执行真正的操作,具体的操作请参考
struct tap_disk {
const char *disk_type;
td_flag_t flags;
int private_data_size;
int (*td_open) (td_driver_t *, const char *, td_flag_t);
int (*td_close) (td_driver_t *);
int (*td_get_parent_id) (td_driver_t *, td_disk_id_t *);
int (*td_validate_parent) (td_driver_t *, td_driver_t *, td_flag_t);
void (*td_queue_read) (td_driver_t *, td_request_t);
void (*td_queue_write) (td_driver_t *, td_request_t);
void (*td_debug) (td_driver_t *);
};
- xen块设备体系结构(7)
- xen块设备体系结构(1)
- xen块设备体系结构(2)
- xen块设备体系结构(4)
- xen块设备体系结构(5)
- xen块设备体系结构(6)
- xen块设备体系结构 - tapdisk2 (1)
- xen块设备体系结构 - tapdisk2 (2)
- xen块设备体系结构 - tapdisk2 (3)
- xen块设备体系结构 - tapdisk2 (4)
- 块设备体系结构分析
- xen支持的块设备
- XEN体系结构
- xen体系结构
- xen的体系结构
- linux块设备7
- Xen的设备驱动
- Xen 的设备
- MFC点点滴滴——CStdioFile,CFile类
- PostgreSQL 9.1终于正式发布了
- tentsqlserver中用bcp 来导入导出表数据
- java double相加出现的怪事
- TFS自动编译项目文件的Target修改
- xen块设备体系结构(7)
- android进程间通信:使用AIDL
- php一句话免杀
- 推荐一个好的学习资料网!
- 在SQL Server上该做的和不该做的
- 移动智能终端与移动互联网的发展走向预估
- 初学PHP的18个基础例程
- linux课上学习笔记
- 当前线程不在单线程单元中,因此无法实例化 ActiveX 控件的解决方案