您的位置:首页 > 其它

GlusteFS:自我修复(Selfheal)源码分析

2012-10-27 12:52 260 查看

1.概述

Glusterfs自我修复是基于事件触发模式,修复的项主要包括文件内容(data),元数据(metadata),项(entry)等。修复分为2种类型,文件内容的整个修复(full),和差异化修复(diff),文件修复时候,并不会整个文件copy,而是以块单位进行copy。具体修复流程大致为从一个数据完整的节点,将文件读取到客户端,然后再将文件写到数据不完整的节点。

数据修复示意图






文件内容,元数据,目录项的修复主要经历3个步骤:

1、 检查查询到的元数据信息,判断是否需要触发修复操作;

2、 通过一定规则,从逻辑卷的所有brick中选择出一个brick作为sources;

3、 以source为模版,对其他节点进行修复操作;

在上面已经提及到,glusterfs的修复是基于事件模型的,有2中事件:第一,当进入某个目录的时候,会触发进行数据完整性检查,进行数据的自我修复;第二,当要进行文件的读写前会进行lookup的操作,该lookup操作会触发自我修复,两者最后都是通过调用afr_self_heal触发修复操作。

打开目录,进行目录内的自我修复






文件lookup时修复



2.功能测试

场景:建立只有2个brick- test5,test6的冗余卷。

Test5文件夹的可扩展属性:

>>> xattr.listxattr("test5")

(u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.test', u'trusted.afr.v1-client-0', u'trusted.afr.v1-client-1')

第一次进入某个文件夹这时候在opendir的时候触发修复,即某个项时,该项下所有的项被修复,包括项和项的元数据;

日志:

0-v1-replicate-0: background meta-data entry self-heal completed on /liu/test

>> xattr.listxattr("liu")//liu为一个文件夹

元数据信息:

(u'security.selinux', u'trusted.gfid', u'trusted.afr.v1-client-0', u'trusted.afr.v1-client-1')

该项下文件也会创建,并且写了文件的一个基本元数据,但是文件为0字节,其他很多元数据没有修复:

元数

未修复前元数据信息:

>>> xattr.listxattr("missing")

(u'security.selinux', u'trusted.gfid')//在mknod时候添加扩展属性trusted.gfid

修复后元数据:

>>> xattr.listxattr("missing")

(u'security.selinux', u'trusted.gfid', u'trusted.afr.v1-client-0', u'trusted.afr.v1-client-1')

修复后日志:

0- v1-replicate-0: background meta-data data self-heal completed on /missing

注:由上红色字体可知,修复文件的时候修复了数据和元数据两者。

Ø Lookup在这样几种情况下修复:

l 如果是目录,则仅仅修复该目录下的所有项;

l 如果是文件,则修复该文件;

当某一个项或者文件被修复,其元数据包括attr和xattr都会被修复

如,test6下面的文件THANKS,py-compile本来被删除,1-9目录项本来被删除,当修复后,元数据会与test5一致:

-rwxr-xr-x. 1 root root 0 12?.31 16:42 py-compile /test6目录,0字节,只是进行了文件创建,还没有文件的完全修复

-rw-r--r--. 1 root root 0 12?.30 14:37 THANKS /test6目录

drwxr-xr-x. 2 root root 4096 1?. 4 15:45 1 /test6目录

-rwxr-xr-x. 1 root root 4142 12?.31 16:42 py-compile /test5目录

-rw-r--r--. 1 root root 90 12?.30 14:37 THANKS /test5目录

drwxr-xr-x. 3 root root 4096 1?. 4 15:45 1 /test5目录

在数据修复过程中,我们经常会通过lookup,或者fstat操作获得iatt结构体内参数值,这些参数一个对象buf进行组织,buf内的数据为:

$9 = {ia_ino = 1, ia_gfid = '\000' <repeats 15 times>, "\001", ia_dev = 2049, ia_type = IA_IFDIR, ia_prot = {suid = 0 '\000', sgid = 0 '\000', sticky = 0 '\000', owner = {read = 1 '\001', write = 1 '\001', exec = 1 '\001'}, group = {read = 1 '\001', write
= 0 '\000', exec = 1 '\001'}, other = {read = 1 '\001', write = 0 '\000', exec = 1 '\001'}}, ia_nlink = 2, ia_uid = 0, ia_gid = 0, ia_rdev = 0, ia_size = 4096, ia_blksize = 4096, ia_blocks = 16, ia_atime = 1326072998, ia_atime_nsec = 76224397, ia_mtime = 1326072014,
ia_mtime_nsec = 464975995, ia_ctime = 1326072259, ia_ctime_nsec = 344098899}

一般扩展属性中的有dict结构体组织:

$12 = {is_static = 0 '\000', hash_size = 1, count = 2, refcount = 0, members = 0x7fd6fc000950, members_list = 0x7fd6fc000a60, extra_free = 0x0, extra_stdfree = 0x0, lock = 1}

每对数据对的值:

(gdb) p *xattr->members[0]

$25 = {hash_next = 0x7fd6fc0009c0, prev = 0x0, next = 0x7fd6fc0009c0, value = 0x7fd6fc000a30,

key = 0x7fd6fc000c20 "trusted.afr.v1-client-1"}

结构体struct _data内的数据:

(gdb) p *xattr->members[0]->value

$26 = {is_static = 0 '\000', is_const = 0 '\000', is_stdalloc = 0 '\000', len = 12, vec = 0x0, data = 0x7fd6fc000a10 "", refcount = 1, lock = 1}

在读取目录的时候,返回的每个项entry内的数据:

$14 = {{list = {next = 0x1d3b3f0, prev = 0x1d3d240}, {next = 0x1d3b3f0, prev = 0x1d3d240}}, d_ino = 140273059024932,

d_off = 140273139813794, d_len = 333631280, d_type = 32767, d_stat = {ia_ino = 1,

ia_gfid = "0\317\342\023\377\177\000\000\030\274\206\342\223\177\000", ia_dev = 11, ia_type = IA_IFREG, ia_prot = {

suid = 0 '\000', sgid = 0 '\000', sticky = 0 '\000', owner = {read = 0 '\000', write = 0 '\000', exec = 0 '\000'}, group = {

read = 0 '\000', write = 0 '\000', exec = 0 '\000'}, other = {read = 0 '\000', write = 0 '\000', exec = 0 '\000'}},

ia_nlink = 0, ia_uid = 0, ia_gid = 0, ia_rdev = 0, ia_size = 0, ia_blksize = 0, ia_blocks = 0, ia_atime = 3791377312,

ia_atime_nsec = 32659, ia_mtime = 0, ia_mtime_nsec = 0, ia_ctime = 0, ia_ctime_nsec = 0},

d_name = 0x7fff13e2ce50 "\360\263\323\001"}

项列表参数:(gdb) p entries->list

$9 = {next = 0x1d3b3f0, prev = 0x1d3d240}

3.源码分析

3.1.修复过程分析

修复过程,大致可以分为4个过程:



1、 判断参数是否需要修复:

该过程主要是将从各个brick查询到的元数据休息进行对比,主要包括文件类型,文件大小,文件权限,扩展属性等参数进行对比,看这些对应信息是否一致,如果不一致则要标识需要修复,包括如下几个标识:

metadata_self_heal;//修复元数据,包括文件属性与扩展属性

data_self_heal;//修复文件内容,当对比副本大小不一致时触发

entry_self_heal;//修复目录项

gfid_self_heal;

missing_entry_self_heal;

2. 通过事务类型标记source节点,及其源的计算过程:

1)如果是元数据修复,且所有节点均为innocent,选择ia_uid(为什么选择最小ia_uid)最小的副本作为source;

2)如果存在wise副本,且该副本没有被其他副本指控,则标识该节点为wise;如果每个wise副本冲突,这种情况下没有wise节点副本存在,则发生脑裂,没有source节点可以选择返回EIO异常;如果存在没有冲突的wise副本,则这些wise副本均可作为source节点;
3)如果没有wise节点存在,从fool中选择指证其他节点数最大的节点作为source;

4)如果上面3条均没有找到相应的source,则从所有brick中找出运行的brick副本,这些均可以作为sources;

5)选择第一个sources,作为模版;

3.以source为模版,开始进行自动修复

3.2.阅读卷选择

1、 首先通过source选择方式,选择一批source放在source数组内;
2、 获得prev_read_child= local->read_child_index;
3、 获得config_read_child= priv->read_child;
4、 如果prev_read_child在source数组中存在,则将prev_read_child作为read_child;
5、 否则如果config_read_child在source数组中存在,则将config_read_child作为read_child;
6、 如果在source数组中,prev_read_child与config_read_child均不存在,则从success_child中选择第一个在source数组中的节点作为read_child
7、 将read_child记录到ctx中,供读等操作使用;

3.3.算法分析

官方描述:

a) "Full" algorithm – this algorithm copies the entire file data in order to heal the out-ofsync copy. This algorithm is used when a file has to be created from scratch on a

server.

b) "Diff" algorithm – this algorithm compares blocks present on both servers and copies

only those blocks that are different from the correct copy to the out-of-sync copy. This

algorithm is used when files have to be re-built partially.

The “Diff” algorithm is especially beneficial for situations such as running VM images,

where self-heal of a recovering replicated copy of the image will occur much faster because

only the changed blocks need to be synchronized.

在文件内容的修复中,glusterfs分为2种算法full,diff。系统默认为full算法,可以通过在卷配置文件改变其算法类型。

在算法部分,程序会首先选择一种算法:

struct afr_sh_algorithm afr_self_heal_algorithms[] = {

{.name = "full", .fn = afr_sh_algo_full},

{.name = "diff", .fn = afr_sh_algo_diff},

{0, 0},

};

然后进入该算法。

3.3.1.full算法分析

从字面意思我们已经知道,该算法会完全复制一分副本到其他节点上,复制的过程是同步的,该中算法主要用在某台机器文件丢失了而要整个文件恢复。

该部分的执行流程大致如下:

同时发起多个数据段的读写,读写数量受自我修复窗口大小影响,默认为16;

数据是先读再写,读取的相应的段写到需要修复的节点文件上相应的位置;

Full算法执行流程图



下图为full算法sh_full_loop_driver内部执行流程图:



3.3.2.diff算法分析

算法diff会比较2个服务器存储文件的数据块,对不相同的块进行修复操作,该中算法主要运用到文件需要部分修复的情况下。

Diff算法执行流程图






上图说明:

通过配置文件参数,决定选择算法afr_sh_algo_diff函数,进入该函数执行;

然后调用sh_diff_loop_driver函数,分为2个部分:1部分以窗口大小与文件大小为限制,改变读取偏移量大小和增加循环计数loop与loops_running;2部分通过一个while循环将这些窗口数遍历,遍历执行sh_diff_checksum;

当不是第一调用该函数,如某个段修复完毕后

if (_gf_false == is_first_call)

sh_priv->loops_running--;

判断窗口中进行的修复数是否满了,若没有满,向窗口中加入修复数:

while ((0 == sh->op_failed) &&(sh_priv->loops_running < priv->data_self_heal_window_size)

&& (sh_priv->offset < sh->file_size)) {

loop++;

gf_log (this->name, GF_LOG_TRACE,

"spawning a loop for offset %"PRId64,

sh_priv->offset);

sh_priv->offset += sh_priv->block_size;

sh_priv->loops_running++;

如果不是第一次调用,如果数据没有修复完毕,则每次向窗口中加一个修复检查的线程:

if (_gf_false == is_first_call)

break;



循环,对每个段发起checksum请求,每个段的起始点为offset

while (loop--) {

if (sh->op_failed) {

// op failed in other loop, stop spawning more loops

sh_diff_loop_driver (frame, this, _gf_false, NULL);

} else {

sh_diff_checksum (frame, this, offset);

offset += block_size;

}

}

如果所有段修复完毕,则执行如下操作

if (is_driver_done) {

sh_diff_loop_driver_done (frame, this);

}

在sh_diff_checksum内部,会去source节点与其他节点读取相应偏移量出的大小为block size的文件的md5值;

从源节点去获得某段的rchecksum

STACK_WIND_COOKIE (rw_frame, sh_diff_checksum_cbk,

(void *) (long) cookie,

priv->children[sh->source],

priv->children[sh->source]->fops->rchecksum,

sh->healing_fd,

offset, sh_priv->block_size);

。。。。。。。。

在从其他节点获得该段的rchecksum信息

STACK_WIND_COOKIE (rw_frame, sh_diff_checksum_cbk,

(void *) (long) cookie,

priv->children[i],

priv->children[i]->fops->rchecksum,

sh->healing_fd,

offset, sh_priv->block_size);

。。。。。。。。。

在sh_diff_checksum_cbk内部,当每个节点返回信息后,会将节点获得的每个节点的段md5值放到相应位置:

将每个子节点返回的md5信息strong_checksum放到内存中相应的位置:

memcpy (loop_state->checksum + child_index * MD5_DIGEST_LEN,

strong_checksum,

MD5_DIGEST_LEN);

当每个子节点都返回后,将source节点返回的信息与其他只节点返回的信息进行比较

if (call_count == 0) {

for (i = 0; i < priv->child_count; i++) {

if (sh->sources[i] || !sh_local->child_up[i])

continue;

if (memcmp (loop_state->checksum + (i * MD5_DIGEST_LEN),

loop_state->checksum + (sh->source * MD5_DIGEST_LEN),

MD5_DIGEST_LEN)) {

如果某个节点与source节点信息不一致,则该节点需要进行修复:

write_needed = loop_state->write_needed[i] = 1;

准备进行修复的读操作:

sh_diff_read (rw_frame, this, loop_index);

在函数sh_diff_read内部,到souce节点去读取相应块的信息,返回的函数为sh_diff_read_cbk;

在函数sh_diff_read_cbk内部:

将段写到需要修复段的子节点上去:

for (i = 0; i < priv->child_count; i++) {

if (loop_state->write_needed[i]) {

wcookie = __make_cookie (loop_index, i);

STACK_WIND_COOKIE (rw_frame, sh_diff_write_cbk,

(void *) (long) wcookie,

priv->children[i],

priv->children[i]->fops->writev,

sh->healing_fd, vector, count,

loop_state->offset, iobref);

疑问:某一个子节点写成功后,是有已经将loop_state->write_needed[i]设置为0,还是其他控制,还是根本就没有控制??

7)在函数sh_diff_write_cbk内部,首先判断该段写是否成功,如果每个需要修复的子节点都成功后,会调用sh_diff_loop_return,进入下一个修复循环

3.4.修复流程分析

3.4.1.项修复分析

项修复,其实就是文件系统中某个目录项的修复,包括每个子节点对应的目录下目录项及其元数据是一致的,大体流程如下:



具体调用流程如下:



3.4.2.文件修复分析

文件修复时候,默认block_size为65536字节。文件修复即将source上的文件复制相应的段到需要修复的节点上,是以文件为单位进行修复。文件进行修复前,其元数据已经修复完毕,其工作大致会经历如下几部:

1)当元数据修复完毕后,如果检查到该项是文件,则进入文件修复函数afr_self_heal_data;

2)然后执行open操作,打开该文件的所有副本,获得一个fd,打开操作为通过文件路径获得fd;

3)检查是否存在数据锁,如果锁不存在要将所有副本进行锁定,如果锁存在则跳过锁过程;

4)通过fd,对所有的副本执行fxattrop操作,获得每个副本的可扩展属性xattr,为后面的可扩展属性检查使用;

每个副本xattr获得后,进行赋值:

sh->xattr[child_index] = dict_ref (xattr);

5)通过fd,对所有副本执行fstat操作,获得每个副本的属性,即获得每个副本iatt结构体内参数的值,如文件的大小,类型等等,接下来就准备进行数据的修复阶段。

每个副本成功返回后的参数设置:

sh->buf[child_index] = *buf; //buf内存储了所有的iatt结构体内的参数

sh->child_success[sh->success_count] = child_index;//返回子节点的索引

sh->success_count++;

6)计算扩展属性中记录的与修复相关的值,并且将这些值赋值给pending_matrix(待定矩阵)

初始化一个待定矩阵,该待定矩阵每个元素均为0:

afr_init_pending_matrix (pending_matrix, child_count);

for (i = 0; i < child_count; i++) {

pending_raw = NULL;

for (j = 0; j < child_count; j++) {

从xattr[i]中查找是否有键pending_key[j],如果有,将其值赋值给pending_raw

ret = dict_get_ptr (xattr[i], pending_key[j],

&pending_raw);

如果从相应节点xattr内没有查找到相应的键,则标示该节点为ignorant节点:

if (ret != 0) {

该情况可能某个扩展属性被恶意删除,或者该份副本被直接删除了,所有扩展属性均不存在

ignorant_subvols[i] = 1;

continue;

}

memcpy (pending, pending_raw, sizeof(pending));

通过事务类型决定在数组pending中索引的位置,3个索引分别为0,1,2,分别对应data,metadata,entry

k = afr_index_for_transaction_type (type);

pending_matrix[i][j] = ntoh32 (pending[k]);

}

}

注:pending_key保存了冗余类型卷文件应该有的所有xattr内的键,如有2个子卷,名称分别为vl-client-0,vl-client-1,则pending_key中的2个xattr的键分别为:

Trusted.afr.v1-client-0,trusted.afr.vl-client-1

接下来标记ignorant无知卷为pending:

for (i = 0; i < child_count; i++) {

if (ignorant_subvols[i]) {

for (j = 0; j < child_count; j++) {

如果某个节点扩展属性存在,则这个节点指控该ignorant节点的值自动加一

if (!ignorant_subvols[j])

pending_matrix[j][i] += 1;

}

}

}

比如afr有2个子卷0,1。0为ignorant,1不为ignorant,则:

由 pending_matrix[j][i] += 1,即pending_matrix[1][0]=1,则意味着子卷1控诉0卷为待定卷,即可能需要修复的卷

7)当待定矩阵建立完毕后,调用函数afr_mark_sources来就要判断哪些子卷能够作为source节点,如果一个节点作为了source节点,则修复的时候可以以该节点为模版,修复其他非source节点:

首先将source数组清零:

for (i = 0; i < child_count; i++) {

sources[i] = 0;

}

然后判断子节点的字符类型,有3类:

1. AFR_NODE_INNOCENT(天真);2. AFR_NODE_FOOL(愚蠢);3. AFR_NODE_WISE(英明)

characters[i].type =

afr_find_child_character_type (pending_matrix[i], i,

child_count,

xlator_name);

如:pending_matrix[0][0]=1(控诉自己为fool); pending_matrix[0][1]=0; pending_matrix[1][0]=0; pending_matrix[1][1]=0(没有控诉自己为wise)

接下来计算所有的可以作为source节点的节点数,和具体的哪些节点:

如果事务类型为“修复元数据“且所有节点均为innocent类型,则标识uid最小的节点上的副本为source

if ((type == AFR_SELF_HEAL_METADATA)

&& afr_sh_all_nodes_innocent (characters, child_count)) {

nsources = afr_sh_mark_lowest_uid_as_source (bufs,

valid_children,

child_count,

sources);

goto out;

}

判断是否有wise类型节点存在

if (afr_sh_wise_nodes_exist (characters, child_count)) {

计算所有的wise节点,如果某节点为wise节点(characters[j].type == AFR_NODE_WISE),且没有其他节点指证它,则标识该节点是wise的(characters[i].wisdom = 1)

afr_sh_compute_wisdom (pending_matrix, characters, child_count);

如果所有的wise节点均冲突,则认为该节点为split-brain,所谓冲突,。如:

characters[i].type == AFR_NODE_WISE而character[i].wisdom==0不为1,这种请求下,将不会有source节点存在,客户端会报错,修复操作不再进行

if (afr_sh_wise_nodes_conflict (characters, child_count)) {

//wise节点均冲突

/* split-brain */

gf_log (this->name, GF_LOG_INFO,

"split-brain possible, no source detected");

if (flags)

*flags |= AFR_SPLIT_BRAIN;

如果有wise节点冲突,则source节点数赋值为-1

nsources = -1;

} else {

//获得所有没有冲突的wise节点放在数组nsources中

nsources = afr_sh_mark_wisest_as_sources (sources,

characters,

child_count);

}

if (flags) {

*flags |= AFR_ALL_FOOLS;

nsources = -1;

goto out;

}

如果所有节点均为fool类型,标识指证其他节点最多次的节点为source节点,将该节点索引放入数组nsources中

nsources = afr_mark_biggest_of_fools_as_source (sources,

pending_matrix,

characters,

child_count);

根据上面计算获得nsource的值,会有相应的计算来获得到底选择哪一个source作为本次修复的source节点:

1) 如果nsources = -1,最喜欢的节点存在,且最喜欢的节点没有报错,则选择最喜欢的节点,则将该节点加入source数组内:

sh->sources[priv->favorite_child] = 1;

nsources = afr_sh_source_count (sh->sources,priv->child_count);

2)如果仅仅nsource=--1,其他2个条件不满足,则不能进行修复;

3)然后遍历source数组,选择第一个满足的子卷作为本次修复的source节点:

source = afr_sh_select_source (sh->sources, priv->child_count);

4)然后遍历所有的source节点,将他们与选择为本次修复的source节点进行文件大小对比,如果不相等,则source[i]=0:

for (i = 0; i < priv->child_count; i++) {

if (i == source || sh->child_errno[i])

continue;

if (SIZE_DIFFERS (&sh->buf[i], &sh->buf[source]))

sh->sources[i] = 0;

}

即之前虽把该节点标记为source节点,但是副本大小不对,因此该副本仍然需要修复

8)选择完source后,就准备选择相应的修复算法diff或者full进行数据修复了。

9)修复完毕后,还要对初source节点外的其他节点副本进行修饰,就如果本来文件长度就为300M,而某一个副本大小为600M,则会去除后300M:

STACK_WIND_COOKIE (frame, afr_sh_data_trim_cbk,

(void *) (long) i,

priv->children[i],

priv->children[i]->fops->ftruncate,

sh->healing_fd, sh->file_size);

10)擦除xattr内的待定标识,包括entry,data,metadata的相关标识

STACK_WIND_COOKIE (frame, afr_sh_data_erase_pending_cbk,

(void *) (long) i,

priv->children[i],

priv->children[i]->fops->fxattrop,

sh->healing_fd,

GF_XATTROP_ADD_ARRAY, erase_xattr[i]);

11)修复完毕后,如果锁存在要解锁,如果已经解锁,则需要检查fd是否关闭,如果没有关闭则通过flush操作要关闭,且要更新所有节点相应副本的属性,如最后访问时间等等。

大致流程如下:



上图说明:

具体调用流程如下:



3.2.2.元数据修复分析

元数据修复会设计到文件的属性的修复和扩展属性的修复,会经历如下几个主要步骤:

1) 通过锁将相应的项进行锁定;

2) 通过afr_sh_common_lookup操作查询到相应项的buf,xattr;

3) 与文件修复一样,通过xattr判断获得source,如果nsources== -1,则不进行元数据修复;然后从所有的source中选择一个source作为修复的source;

4) 将其他的source与选择出来的source比较权限,用户等属性,如果不一致,这些source也需要被修复:

/* detect changes not visible through pending flags -- JIC */

for (i = 0; i < priv->child_count; i++) {

if (i == source || sh->child_errno[i])

continue;

if (PERMISSION_DIFFERS (&sh->buf[i], &sh->buf[source]))

sh->sources[i] = 0;

if (OWNERSHIP_DIFFERS (&sh->buf[i], &sh->buf[source]))

sh->sources[i] = 0;

}

5) 进入修复,通过path先从source节点获得xattr;

6) 然后用获得的xattr来修复其他节点的xattr和attr:

修复需要修复的节点的attr:

STACK_WIND_COOKIE (frame, afr_sh_metadata_setattr_cbk,

(void *) (long) i,

priv->children[i],

priv->children[i]->fops->setattr,

&local->loc, &stbuf, valid);

修复需要修复的节点的xattr:

STACK_WIND_COOKIE (frame, afr_sh_metadata_xattr_cbk,

(void *) (long) i,

priv->children[i],

priv->children[i]->fops->setxattr,

&local->loc, xattr, 0);

7)擦去metadata的pending标识,然后解锁。

大致流程如下:



具体调用流程如下:



3.5. 触发修复流程分析

所谓触发修复,就是glusterfs在什么样的应用场景下会触发修复事件发生,而不是如何进行数据修复本身。在该部分,我们会分析2个应用场景:opendir场景与lookup场景。

3.5.1.opendir场景

该场景会在所有子卷执行opendir_cbk后,检查该打开的目录项是否在上下文(ctx)中有过记录,如果没有记录,就会触发所有子节点相应的该目录项的检查(子节点必须大于一,不然不能进行比较),检查每个节点上目录结构是否一致,如果不一致则触发修复事件,如果一致则不会触发。其大致流程如下:



具体函数调用流程如下:



在该应用场景下,最重要的一个函数就是afr_examine_dir_readdir_cbk,该函数负责对读取目录后的信息计算entry_cksum,比较每个节点返回的cksum是否一致,只要发现有一个不一致,则触发自动修复事件。

op_ret==0,意味着该节点下没有读取到目录项,即可能该目录下没有目录项,或者目录项已经读取完毕,将转入out处处理,后面会提及out处的处理:

if (op_ret == 0) {

gf_log (this->name, GF_LOG_DEBUG,

"%s: no entries found in %s",

local->loc.path, priv->children[child_index]->name);

goto out;

}

遍历返回得到的目录项,计算其entry_cksum,然后通过与算法并入整个节点目录项的checksum:

list_for_each_entry_safe (entry, tmp, &entries->list, list) {

entry_cksum = gf_rsync_weak_checksum (entry->d_name,

strlen (entry->d_name));

local->cont.opendir.checksum[child_index] ^= entry_cksum;//弱校验

}

项偏移量,即下次从该目录下的哪一个目录项开始读取:

list_for_each_entry (entry, &entries->list, list) {

last_offset = entry->d_off;

}

读取更多的目录项,直到将目录项的目录项读取完毕:local->fd:某个目录;每次读取大小:131072;读取偏移量:last_offset:

STACK_WIND_COOKIE (frame, afr_examine_dir_readdir_cbk,

(void *) (long) child_index,

priv->children[child_index],

priv->children[child_index]->fops->readdir,

local->fd, 131072, last_offset);

当所有节点目录项读取完毕,checksum计算完毕,开始进行checksum的比较,只要有一个checksum不相等,触发自我修复事件,关键代码如下:

……………………………

if (call_count == 0) {

if (__checksums_differ (local->cont.opendir.checksum,

priv->child_count,

local->child_up)) {

sh->need_entry_self_heal = _gf_true;

………………………………….

触发修复事件

afr_self_heal (frame, this, local->fd->inode);

} else {

比较后均都相等,则opendir正常返回

afr_set_opendir_done (this, local->fd->inode);

AFR_STACK_UNWIND (opendir, frame, local->op_ret,

local->op_errno, local->fd);

3.5.2.lookup场景

在进入某一个项或者打开某个文件的时候等均会进行lookup操作,这时会触发是否进行自我修复,其大致流程如下:



具体调用流程如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: