FILE LAYOUT
1 file layout中的基本概念 pNFS支持三种LAYOUT,分别为:block layout、object layout、file layout。这篇文章中讲解file layout的设计理念,不涉及Linux中的代码。首先介绍几个概念,这些概念见RFC5661第13章。Unit: Unit是固定长度的数据块,因为pNFS中可以包含多个DS,客户端可以向多个DS发送数据
1 file layout中的基本概念
pNFS支持三种LAYOUT,分别为:block layout、object layout、file layout。这篇文章中讲解file layout的设计理念,不涉及Linux中的代码。首先介绍几个概念,这些概念见RFC5661第13章。
Unit: Unit是固定长度的数据块,因为pNFS中可以包含多个DS,客户端可以向多个DS发送数据,以Unit为单位,每个Unit发送给不同的DS。
Pattern: 这是数据分发方式,比如可以将第一个Unit发送给DS1,第二个Unit发送给DS2。或者将第一个Unit发送给DS2,第二个Unit发送给DS1。Pattern表示这种分发规则。
Stripe: 这表示数据的一轮分发过程。客户端按照Pattern指定的方式分发数据。第一个Unit发送给DS1,第二个Unit发送给DS2。那么第三个Unit又该分发给DS1了,第四个Unit分发给DS2。第一个Unit和第二个Unit构成了一个Stripe,第三个Unit和第四个Unit构成了另一个Strip。
Stripe Count: 表示每个分发过程中传输的Unit数量,在前面这个例子中Stripe Count就等于2。
Stripe Width: 表示每轮数据分发过程中传输的数据总量。Stripe Width = Stripe Count * 每个Unit中包含的数据量。
RFC5661提供了DS组的概念,pNFS可以将若干个DS构成一个分组,客户端向其中每个DS传输数据是等价的。因此客户端在传输数据时可以从中随便挑选一个DS,如果某个DS不能工作了,可以挑选另外一个正常工作的DS,这种设计增强了系统的稳定性。另外,pNFS中Stripe Count的数量不必和DS组的数量一致,在一个Stripe中客户端可以向其中某个或某些DS组传输多个Unit。下面是RFC5661中举的一个例子:
pNFS中包含7个DS,依次用A--G表示,这7个DS分成了三组:{ A, B, C, D }, { E },{ F, G }。{A, B, C, D}编号为0,{E}编号为1,{F, G}编号为2。Stripe的定义如下:{ 2, 0, 1, 0 },按照这种规则客户端将Unit0发送给{ F, G },Unit1发送给{ A, B, C, D},Unit2发送给{E},Unit3发送给{ A, B, C, D}。从Unit4开始重复这个分发过程。在这个例子中DS的数量为7,DS组的数量为3,Stripe Count的值为4。
2 LAYOUTGET
客户端发起读写请求前需要先向MDS发起LAYOUTGET请求,这个请求的主要作用是获取文件的布局信息,也就是说这块数据保存在哪些DS中了。LAYOUTGET请求的应答报文包含下列信息
struct LAYOUTGET4resok {
bool logr_return_on_close;
stateid4 logr_stateid;
layout4 logr_layout<>;
};
logr_return_on_close:这是一个标志位,表示客户端关闭文件时是否需要交还layout。
logr_stateid:这是layout使用的stateid。
logr_layout:这个字段中包含了layout的布局信息,这个结构的定义如下:
struct layout4 {
offset4 lo_offset;
length4 lo_length;
layoutiomode4 lo_iomode;
layout_content4 lo_content;
};
lo_offset: layout在文件中的起始位置。
lo_length: layout中包含的数据量。
lo_iomode: 这是客户端请求的访问权限。
lo_content: 这是跟layout类型相关的数据结构,这个结构的定义如下:
struct layout_content4 {
layouttype4 loc_type;
opaque loc_body<>;
};
loc_type: 表示layout类型,取值为:LAYOUT4_NFSV4_1_FILES、LAYOUT4_OSD2_OBJECTS、LAYOUT4_BLOCK_VOLUME。
loc_body: 这是具体layout类型中定义的数据结构,如果loc_type的值为LAYOUT4_NFSV4_1_FILES,则这个字段的类型是nfsv4_1_file_layout4,定义如下:
struct nfsv4_1_file_layout4 { deviceid4 nfl_deviceid; nfl_util4 nfl_util; uint32_t nfl_first_stripe_index; offset4 nfl_pattern_offset; nfs_fh4 nfl_fh_list<>; };nfl_deviceid: 表示文件的存储位置。
nfl_first_stripe_index: 表示从哪个DS组开始使用。还是以前面的例子为例,Stripe的定义为{ 2, 0, 1, 0 }。如果nfl_first_stripe_index=2,那么Unit0会发送给{E},Unit1发送给{ A, B, C, D },Unit2发送给{F, G},Unit3发送给{ A, B, C, D }。
nfl_pattern_offset: 这个字段表示Pattern的起始位置,我们需要依靠这个字段计算将数据发送到哪个DS组中,后面会给出例子。这个值和lo_offset不一定相等。
nfl_fh_list: 这个字段中包含了一组文件句柄,客户端向DS发送数据时需要使用这组句柄,后面会进一步讲解。
nfl_util: 这个字段中包含了三个数据,首先看几个宏定义:
const NFL4_UFLG_MASK = 0x0000003F; const NFL4_UFLG_DENSE = 0x00000001; // 这个标志表示数据的分发方式,包含两种取值:DENSE和SPARSE,后面会详细介绍。 const NFL4_UFLG_COMMIT_THRU_MDS = 0x00000002; // 这个标志表示COMMIT请求提交给MSD还是提交给DS。 const NFL4_UFLG_STRIPE_UNIT_SIZE_MASK = 0xFFFFFFC0; // 这是Unit的长度位。因此nfl_util & NFL4_UFLG_DENSE表示数据分发方式,1表示DENSE方式,0表示SPARSE方式。
nfl_util & NFL4_UFLG_COMMIT_THRU_MDS表示COMMIT请求的提交方式,1表示提交给MDS,0表示提交给DS。
nfl_util & NFL4_UFLG_STRIPE_UNIT_SIZE_MASK表示Unit的长度。
3 GETDEVICEINFO
这个请求的作用是请求DS的IP地址,只有请求到IP地址后才能向相应的DS发起读写请求,应答报文包含下列数据:
struct GETDEVICEINFO4resok {
device_addr4 gdir_device_addr;
bitmap4 gdir_notification;
};
gdir_notification: 这是一个标志位,表示当deviceid发生变化时是否通知客户端。
gdir_device_addr: 这是若干个DS的地址,客户端可以向这些DS发送数据,这个数据结构定义如下:
struct device_addr4 {
layouttype4 da_layout_type;
opaque da_addr_body<>;
};
da_layout_type: 表示layout类型,取值为LAYOUT4_NFSV4_1_FILES、LAYOUT4_OSD2_OBJECTS、LAYOUT4_BLOCK_VOLUME。
da_addr_body: 包含了DS的地址,如果da_layout_type取值为LAYOUT4_NFSV4_1_FILES,则这个字段的类型是nfsv4_1_file_layout_ds_addr4,定义如下:
struct nfsv4_1_file_layout_ds_addr4 {
uint32_t nflda_stripe_indices<>;
multipath_list4 nflda_multipath_ds_list<>;
};
nflda_stripe_indices: 这就是stripe。
nflda_multipath_ds_list: 这是DS组的地址。
后面我们会距离说明这些字段的含义。
4.SPARSE
filelayout包含两种数据分发方式,分别是DENSE和SPARSE,首先讲解SPARSE方式。还是以前面的例子为例,为了计算客户端应该向哪个DS发送数据,首先需要计算发送的数据属于哪个Unit,计算方法是
relative_offset = file_offset - nfl_pattern_offset;
SUi = floor(relative_offset / stripe_unit_size);
其中file_offset表示要传输的数据在文件中的偏移位置,nfl_pattern_offset表示pattern在文件中的偏移位置,stripe_unit_size就是Unit中数据的长度。这样我们就计算出了数据在哪个Unit中。然后我们计算应该将这个Unit发送给哪个DS组,计算方法是
stripe_count = number of elements in nflda_stripe_indices;
j = (SUi + nfl_first_stripe_index) % stripe_count;
idx = nflda_stripe_indices[j];
nflda_stripe_indices是LAYOUTGET中返回的stripe的信息,其中元素的数量就是Stripe Count。这样就计算出了DS组,也就是说需要将这个Unit发送到编号为idx的DS组中。发送数据时还需要使用文件句柄,文件句柄也是LAYOUTGET请求中返回的,位于应答报文的nfl_fh_list字段。这是一组文件句柄,当采用SPARSE方式时,这组句柄的数量如下:
0个: 所有的DS都使用与MDS相同的句柄。
1个: 所有的DS使用同一个文件句柄,且这个句柄不一定与MDS使用的句柄相同。
n个: 这里n是DS组的数量,每个DS组使用单独的文件句柄,互相独立。如果一个DS组中有多个DS,则同一个DS组中所有的DS使用相同的文件句柄。
fh_count = number of elements in nfl_fh_list;
ds_count = number of elements in nflda_multipath_ds_list;
switch (fh_count) {
case ds_count:
fh = nfl_fh_list[idx];
break;
case 1:
fh = nfl_fh_list[0];
break;
case 0:
fh = filehandle returned by OPEN;
break;
default:
throw a fatal exception;
break;
}
address_list = nflda_multipath_ds_list[idx];
现在讲前面的例子,假设DS组信息如下:
nflda_multipath_ds_list<> = { A, B, C, D }, { E }, { F, G }
也就是说
nflda_multipath_ds_list[0] = { A, B, C, D }
nflda_multipath_ds_list[1] = { E }
nflda_multipath_ds_list[2] = { F, G }
假设Stripe信息如下:
nflda_stripe_indices<> = { 2, 0, 1, 0 }
假设Stripe初始索引值
nfl_first_stripe_index = 2
假设文件句柄信息如下:
nfl_fh_list = { 0x36, 0x87, 0x67 }
如果客户端传输第0个数据块SU0,现在计算DS的地址和传输数据时使用的文件句柄
因为 nfl_first_stripe_index = 2,所以
idx = nflda_stripe_indices[(0 + 2) % 4] = nflda_stripe_indices[2] = 1
nflda_multipath_ds_list[1] = { E }
nfl_fh_list[1] = { 0x87 }
前13个Unit的计算结果如下:
5 DENSE
DENSE是另外一种数据分发方式,当采用这种分发方式时,nfl_fh_list中文件句柄的数量必须等于Stripe Count。现在DS和文件句柄的计算方法如下:
stripe_count = number of elements in nflda_stripe_indices;
j = (SUi + nfl_first_stripe_index) % stripe_count;
idx = nflda_stripe_indices[j];
fh_count = number of elements in nfl_fh_list;
ds_count = number of elements in nflda_multipath_ds_list;
switch (fh_count) {
case stripe_count:
fh = nfl_fh_list[j];
break;
default:
throw a fatal exception;
break;
}
address_list = nflda_multipath_ds_list[idx];
还是前面的例子
nflda_multipath_ds_list<> = { A, B, C, D }, { E }, { F, G }
nflda_stripe_indices<> = { 2, 0, 1, 0 }
nfl_first_stripe_index = 2
nfl_fh_list = { 0x67, 0x37, 0x87, 0x36 }
按照上面的计算方法,如果客户端现在传输第一个数据块SU1
j = (1 + 2) % 4 = 3
idx = nflda_stripe_indices[j] = nflda_stripe_indices[3] = 0
nflda_multipath_ds_list[0] = { A, B, C, D }
nfl_fh_list[3] = { 0x36 }
因此最后的结果是{ 0x36, { A, B, C, D } }。然后客户端可以从{ A, B, C, D }中随便选择一个DS发送数据。
前13个Unit的计算结果如下
更多推荐
所有评论(0)