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的計算結果如下