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(&params, 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, &params);
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(&params, 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, &params);
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 *);
};


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新办的宝卡支付宝注册过怎么办 办支付宝后银行卡被盗刷怎么办 微信扫二维码送平衡群发了怎么办车 拼多多没收到货点了确认收货怎么办 拼多多收货地址填错了怎么办 京东订单申请退款已发货怎么办 手机播放声音的地方进水了怎么办 手机出声音的地方进水了怎么办 手机听声音的地方进水了怎么办 装修公司我只要基装她不肯怎么办 雄迈未来家庭忘记账号和密码怎么办 地方文件和国家政策有冲突怎么办? 网上贷款放款成功没有到账怎么办 我来贷放款中不到账怎么办 手持身份证被别人网贷了怎么办 身份证被别人做了网贷怎么办 身份证丢了被别人网贷了怎么办 户口身份证被注销了网贷怎么办 首付交了贷款办不下来怎么办 付首付后贷款没有批下来怎么办 交了首付贷款没有办下来怎么办 买房交了首付贷款没批下来怎么办 交完首付办不下来贷款怎么办 房子交了首付贷款办不下来怎么办 浦发银行办理房贷流水不够怎么办 房贷审批过程中突然换工作怎么办 建行快贷结清后无额度怎么办 建设银行快贷逾期2个月怎么办 全职速卖通一个月没订单要怎么办 买家点的好评写的差评怎么办 手机流量把话费扣没了怎么办 街电忘记还怎么办3天了 街电1个星期没还怎么办 苹果手机设置密码忘记问题了怎么办 买房付了首付后无法办按揭怎么办 苹果x导入通讯录不显示名字怎么办 苹果切换了id通讯录没有了怎么办 换了id通讯录记录没有了怎么办 4s升级系统通讯录没了怎么办 手机接听声音小怎么办但插耳机正常 苹果6p手机听筒声音小怎么办