FILE LAYOUT

来源:互联网 发布:战舰世界 本森数据 编辑:程序博客网 时间:2024/06/05 19:57

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 }

因此,最后结果是 { 0x87, { E } }。

前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的计算结果如下