Chinaunix首页 | 论坛 | 博客
  • 博客访问: 11603698
  • 博文数量: 8065
  • 博客积分: 10002
  • 博客等级: 中将
  • 技术积分: 96708
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-16 17:06
文章分类

全部博文(8065)

文章存档

2008年(8065)

分类: 服务器与存储

2008-12-10 13:26:51

CDP是Linux内核中基于块的连续数据保护模块,这个模块在数据块级别提供连续数据保护能力。连续数据保护是一种备份和恢复技术,它持 续地捕获所有I/O请求,并且为在这些请求打上时间戳标志。它将数据变化以及时间戳保存下来,以便恢复到过去的任意时刻。因此,这个模块支持数据的任意时 刻映像。
在Linux CDP实现中,涉及三个设备:
主机磁盘(Host Disk)设备;
CDP仓库(Repository)设备;
CDP元数据(Metadata)设备;

    对主机磁盘设备数据块的在各个时刻所作的写操作都被记录下来,被顺序保存到CDP仓库设备中,同时对应的元数据也被保存在CDP元数据设备中。元数据包括以下信息:
    struct metadata {
    int hrs, min, sec;
    该数据块被写入主机磁盘设备的时间;

    unsigned int bisize;
    该数据块的以字节为单位的长度;
    sector_t cdp_sector; CDP
    仓库设备中对应数据块的起始扇区编号;
    sector_t host_sector;
    该数据块在主机磁盘设备中的起始扇区编号;
    };

    下图反映了主机磁盘设备和CDP仓库设备之间的关系。CDP仓库设备中按时间顺序保存了对主机磁盘设备的数据修改。A为主机磁盘设备上的一个扇区,该扇区在9:00和9:05分别进行了修改,它在CDP仓库设备中对应的扇区分别为A1和A2。
    下图反映了CDP仓库设备和CDP元数据设备之间的关系,它们以写入顺序一一对应。CDP仓库设备中的一个元数据对应CDP元数据设备中一个I/O请求,实际上可能是多个扇区。具体扇区数由元数据中的bisize指定,而起始扇区位置由cdp_sector指定。
    CDP仓库设备
    全局变量maddr保存了下一个I/O请求在CDP仓库设备上执行的地址(起始扇区编号)。maddr的初值被定义为宏START_METADATA(0)。
    unsigned int maddr = START_METADATA;
    当一个写请求到来时,对应数据被写到CDP仓库设备中,这时所作的操作如下:
    将写入CDP仓库设备的数据块起始扇区编号设置为maddr;
    根据要写入主机磁盘设备的数据块的扇区数目增加maddr。

      这时,我们要将这里写入的CDP仓库设备的数据块编号记录下来以便构造对应的元数据。
      CDP元数据设备
      全局变量taddr保存了下一个I/O请求对应的元数据在CDP元数据设备中保存的地址(起始扇区编号)。 taddr的初值被定义为宏START_METADATA(0)。
      unsigned int taddr = START_METADATA;
      当一个写请求到来时,对应的元数据被记录在CDP元数据设备中。
      为了简单起见,在元数据设备上,一个扇区(512字节)只保存一个元数据信息(只有32字节),这样浪费了大量的存储空间,但对元数据设备的处理却非常简单:
      将写入CDP元数据设备的元数据起始扇区编号设置为taddr,长度为1个扇区;
      将taddr增1。

        请求处理过程
        请求处理过程是从make_request函数开始的。考虑到读请求的处理的相似性,甚至更为简单,我们这里只分析对写请求的处理过程。我 们首先获得当前的系统时间。之后,写请求bio结构(为说明方便,我们记为B)被分为三个写请求bio结构(分别为B0、B1和B2)。这三个bio结构 的作用是:
        B0:将数据块写到主机磁盘设备;
        B1:将数据块写到CDP仓库设备;
        B2:将元数据写到CDP元数据设备。

          同其它块设备驱动程序的实现一样。我们从B克隆产生B0、B1和B2。然后重定向它们要处理的设备,即bi_bdev域。另外一个大的变动是重新设置了bi_end_io域,用于在I/O请求完成之后进行善后处理。
          为了处理善后,还将B0、B1和B2的bi_private指向同一个cdp_bio1结构。从这个结构,我们要能够回到对B的处理。
          struct cdp_bio {
          struct bio *master_bio;
          原来的bio,通过这个域我们可以从B0B1B2找到
          B
          struct bio *bios[3];
          如果IOWRITE,这个指针数组分别指向B0B1B2,为何需要这个域?

          atomic_t remaining;
          这里一个计数器,我们后面将解释。
          unsigned long state;
          I/O完成方法中使用
          };

          善后工作的主要目的是:在B0、B1和B2都执行完成后,回去执行B,为此,我们需要一个“have we finished”计数器,这就是原子整型变量remaining。在构造B0、B1、B2时分别递增,同时在B0、B1和B2的I/O完成方法中递减, 最后根据该值是否递减到0,来判断B0、B1和B2是否都已经执行完毕。为了防止B0在构造后,在B1和B2构造之前就执行到B0的I/O完成方法,从而 使得remaining变成0,这种错误情况。我们没有将remaining的初值设置为0,而是设为1。并在B0、B1、B2都构造完成执行递减一次。
          B0、B1、B2都执行完成之后,进行如下的处理:
          调用B的善后处理函数;
          释放期间分配的数据结构;
          向上层buffer cache返回成功/错误码。

            另一个需要说明的是对B2的构造,这个bio结构需要处理的是元数据。时间戳已经在进入make_request时获得了保存,而对主机磁盘设备操作的起始扇区和长度从B中可以获得,对应的CDP仓库和CDP元数据的起始地址分别保存在全局变量maddr和taddr中。
            数据恢复过程
            我们可以将数据恢复到以前的任意时刻。CDP实现代码中提供了一个blk_ioctl函数,用户空间以GET_TIME为参数调用该函数,将主机磁盘设备中的数据恢复到指定的时间点。恢复的过程分为两步:
            1. 顺序读取CDP元数据设备的所有扇区,构造一个从主机磁盘设备数据块到CDP仓库设备的(在这个时间点之前)更新数据块的映射。其结果保存在以mt_home为首的(映射表)链表中。
            这里需要构造taddr个对CDP元数据设备的读请求,每个请求读取一个扇区。在这些请求的I/O完成方法中,从读到的数据中构造元数据,并递减计数器count。
            如果元数据中的时间戳早于或等于指定的恢复时间点,则需要添加或修改mt_home链表的元数据结构。需要说明的是,这些项是以 host_sector为关键字索引的,因此添加或修改取决于前面是否出现对同一个host_sector的修改。我们以顺序方式读取的过程中,可以保证 host_sector(在指定的恢复时间点之前)的最新修改cdp_sector会出现在这个链表中。
            由于计数器count为taddr,如果它递减为0,说明CDP元数据设备中的所有数据均已读出并处理,这时我们可以继续往后面执行。
            2. 从CDP仓库设备中读取这些更新的数据块,构造以mt_bi_home为首的链表。
            同上面的处理类似,我们需要为mt_home链表中的每一项构造对CDP仓库设备的读请求,每个请求在CDP仓库设备的起始编号取决于 cdp_sector域,长度则根据bisize而定。这个请求读出的数据需要被写入到主机磁盘设备中,为此我们在读请求I/O完成函数中,构造一个对应 的往主机磁盘设备的写请求bio,该写请求的起始编号取决于host_sector域,长度根据bisize而定,而要写入的数据是刚刚从CDP仓库设备 中读出的数据。另外,在读请求I/O完成函数中,还要递减一个计数器,当该计数器递减到0时,说明我们已经全部处理了mt_home链表中的项,这时得到 一个以mr_bio_home为首,每项中都指向一个bio结构的链表。
            struct list_head mt_home; //BIO更新链表

            struct most_recent_blocks { //BIO
            更新表项
            struct bio *mrbio;
            struct list_head list;
            };

            3. 将mt_bi_home链表的数据块都恢复到主机磁盘设备中。
            这个操作相对比较简单,我们只需要在主机磁盘设备上执行mt_bi_home链表的每一个bio请求项即可。当然,我们要在这些请求项的I/O完成方法中做善后处理,即如果所有请求项都已经执行完毕,则释放mt_home链表和mt_bi_home链表。
            阅读(445) | 评论(0) | 转发(0) |
            给主人留下些什么吧!~~