Linux CDP(连续性数据保护)实现方案、技术细节介绍及相关源码文档下载

目录
[隐藏]

一、块级CDP(连续数据保护)

Block based CDP Module for Linux Kernel是Red Hat学术开源项目中一个基于块的连续数据保护模块,这个模块在数据块级别提供连续数据保护能力。连续数据保护是一种备份和恢复技术,它持续地捕获所有I/O请求,并且为在这些请求打上时间戳标志。它将数据变化以及时间戳保存下来,以便恢复到过去的任意时刻。因此,这个模块支持数据的任意时刻映像。
在Linux的CDP实现中,涉及三个设备:
  • >主机磁盘(Host Disk)设备;
  • CDP仓库(Repository)设备;
  • CDP元数据(Metadata)设备;
CDP代码对主机磁盘设备数据块的在各个时刻所作的写操作都被记录下来,被顺序保存到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指定。
1.1 CDP仓库设备
全局变量maddr保存了下一个I/O请求在CDP仓库设备上执行的地址(起始扇区编号)。maddr的初值被定义为宏START_METADATA(0)。
unsigned int maddr = START_METADATA;
当一个写请求到来时,对应数据被写到CDP仓库设备中,这时所作的操作如下:
l 将写入CDP仓库设备的数据块起始扇区编号设置为maddr;
l 根据要写入主机磁盘设备的数据块的扇区数目增加maddr。
这时,我们要将这里写入的CDP仓库设备的数据块编号记录下来以便构造对应的元数据。
1.2 CDP元数据设备
全局变量taddr保存了下一个I/O请求对应的元数据在CDP元数据设备中保存的地址(起始扇区编号)。 taddr的初值被定义为宏START_METADATA(0)。
unsigned int taddr = START_METADATA;
当一个写请求到来时,对应的元数据被记录在CDP元数据设备中。
为了简单起见,在元数据设备上,一个扇区(512字节)只保存一个元数据信息(只有32字节),这样浪费了大量的存储空间,但对元数据设备的处理却非常简单:
  • 将写入CDP元数据设备的元数据起始扇区编号设置为taddr,长度为1个扇区;
  • 将taddr增1。
1.3 请求处理过程
请求处理过程是从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,通过这个域我们可以从B0、B1、B2找到B
    struct bio *bios[3]; 如果IO为WRITE,这个指针数组分别指向B0、B1、B2,为何需要这个域?
    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都执行完成之后,进行如下的处理:
  1. 调用B的善后处理函数;
  2. 释放期间分配的数据结构;
  3. 向上层buffer cache返回成功/错误码。
另一个需要说明的是对B2的构造,这个bio结构需要处理的是元数据。时间戳已经在进入make_request时获得了保存,而对主机磁盘设备操作的起始扇区和长度从B中可以获得,对应的CDP仓库和CDP元数据的起始地址分别保存在全局变量maddr和taddr中。
1.4 数据恢复过程
我们可以将数据恢复到以前的任意时刻。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链表。
另一点要引起注意的是,上面的第2步操作和第3步操作采用tasklet实现,不知作何考虑?是否是因为它们在I/O完成方法中处理。
这个模块是如何使用的?
struct bio {
        sector_t             bi_sector;         /* associated sector on disk */
        struct bio           *bi_next;          /* list of requests */
        struct block_device *bi_bdev;          /* associated block device */
        unsigned long        bi_flags;          /* status and command flags */
        unsigned long        bi_rw;             /* read or write? */
        unsigned short       bi_vcnt;           /* number of bio_vecs off */
        unsigned short       bi_idx;            /* current index in bi_io_vec */
        unsigned short       bi_phys_segments; /* number of segments after coalescing */
        unsigned short       bi_hw_segments;    /* number of segments after remapping */
        unsigned int         bi_size;           /* I/O count */
        unsigned int         bi_hw_front_size; /* size of the first mergeable segment */
        unsigned int         bi_hw_back_size;   /* size of the last mergeable segment */
        unsigned int         bi_max_vecs;       /* maximum bio_vecs possible */
        struct bio_vec       *bi_io_vec;        /* bio_vec list */
        bio_end_io_t         *bi_end_io;        /* I/O completion method */
        atomic_t             bi_cnt;            /* usage counter */
        void                 *bi_private;       /* owner-private method */
        bio_destructor_t     *bi_destructor;    /* destructor method */
};
 
struct bio_vec {
        /* pointer to the physical page on which this buffer resides */
        struct page     *bv_page;
 
        /* the length in bytes of this buffer */
        unsigned int    bv_len;
 
        /* the byte offset within the page where the buffer resides */
        unsigned int    bv_offset;
};
 
1.5 块级CDP源码下载与安装
你可从这里获得Block Based CDP module for linux kernel的源码以及所有的相关安装配置文档:
Project Title
 Block based CDP Module for Linux Kernel                      Download
The CDP module, which lies below buffer cache, records every write request to the Host Disk (to which CDP is to be provided) and timestamp these IO events. It sends these write requests to both the Host Disk and CDP Repository Disk. Thus CDP Repository holds mirror copy of the data on Host Disk ensuring Physical Data Protection. The timestamps of the data blocks are stored on CDP Repository Disk. On data corruption or disk failure, the System Administrator specifies the time at which data is to be restored. Using the time stamped blocks on the CDP Repository disk we can rollback at the specified time and restore the data to obtain the Any Point In Time image. This ensures Logical Data Protection.
参考文献
1. Block Based CDP module for linux kernel, http://sourceforge.net/projects/q-phoenix;
2.张冬. 大话存储Ⅱ—存储系统架构与底层原理极限剖析[M], 2011:466-470

二、相关:linux设备管理

Linux是一种类Unix系统,Unix的一个基本特点是“一切皆为文件”,它抽象了设备的处理,将所有的硬件设备都像普通文件一样看待,也就是说硬件可以跟普通文件一样来打开、关闭和读写。
系统中的设备都用一个设备特殊文件代表,叫做设备文件,设备文件又分为Block(块)型设备文件、Character(字符)型设备文件和Socket(网络插件)型设备文件。Block设备文件常常指定哪些需要以块(如512字节)的方式写入的设备,比如IDE硬盘、SCSI硬盘、光驱等。
Character型设备文件常指定直接读写,没有缓冲区的设备,比如并口、虚拟控制台等。Socket(网络插件)型设备文件指定的是网络设备访问的BSD socket 接口。
#ls -l /dev/hda /dev/video0 /dev/log
brw-rw—- 1 root disk 3, 0 Sep 15 2003 /dev/hda
srw-rw-rw- 1 root root 0 Jun 3 16:55 /dev/log
crw——- 1 root root 81, 0 Sep 15 2003 /dev/video0
上面显示的是三种设备文件,注意它们最前面的字符,Block型设备为b,Character型设备为c,Socket设备为s。
由此可以看出,设备文件都放在/dev目录下,比如硬盘就是用/dev/hd*来表示,/dev/hda表示第一个IDE接口的主设备,/dev/hda1表示第一个硬盘上的第一个分区;而/dev/hdc 表示第二个IDE接口的主设备。可以使用下面命令:
#dd if=/dev/hda of=/root/a.img bs=446 count=1
把第一个硬盘上前446个字节的MBR信息导入到a.img文件中。
对于Block和Character型设备,使用主(Major)和辅(minor)设备编号来描述设备。主设备编号来表示某种驱动程序,同一个设备驱动程序模块所控制的所有设备都有一个共同的主设备编号,而辅设备编号用于区分该控制器下不同的设备,比如,/dev/hda1(block 3/1)、/dev/hda2(block 3/2 )和/dev/hda3( block3/3 )都代表着同一块硬盘的三个分区,他们的主设备号都是3,辅设备号分别为1、2、3。 这些设备特殊文件用mknod命令来创建:
# mknod harddisk b 3 0

我们就在当前位置创建出一个与 /dev/hda一样的、可以访问第一个IDE设备主硬盘的文件,文件名叫做harddisk。

使用下面命令可以查看设备编号:

#file /dev/hda/dev/hda: block special (3/0)
其中Block代表/dev/hda是系统的Block型(块型)设备文件,它的主设备编号为3,辅设备编号为0。
#ls -l /dev/hda /dev/hdb
brw-rw—- 1 root disk 3, 0 Sep 15 2003 /dev/hda
brw-rw—- 1 root disk 3, 64 Sep 15 2003 /dev/hdb

使用ls -l也可以看到设备编号,/dev/hdb代表第一个IDE接口的从设备(Slave)也是Block设备,编号为(3/64),还有另外一种设备文件是/dev/tty*。使用如下命令:

#echo "hello tty1" > /dev/tty1

将字符串“hello tty1”输出到/dev/tty1代表的第一个虚拟控制台上,此时按“Alt + F1”可以看到该字符出现在屏幕上,这个特殊的文件就代表着我们的第一虚拟控制台。

#file /dev/tty1/dev/tty1: character special (4/1)

由上可以看到,它的类型为Character 型(字符型)设备文件,主设备号为4,辅设备号为1。同样,/dev/tty2代表着第二个虚拟控制台,是Character设备,编号为 (4/2)。

点赞 (2)
  1. latis说道:

    不管怎么样,还是很感谢你的回复!
    有问题时,再向你请教。

    再说一声,谢了!

  2. latis说道:

    你能再详细介绍一下Linux CDP(连续性数据保护)实现方案吗?
    对于代码的结构还有编译和使用???

    很感谢
    [reply=任侠,2012-08-23 03:54 PM]这是一个内核驱动模块,请以内核模块编译的方式编译、安装使用;
    这个模块于2006年开发,使用的内核版本为2.6.11,最新版的内核更改太多,不能直接使用;
    代码只有1004行,请自行阅读分析理解,重在实现技术细节方法的参考[/reply]

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code