FILE LAYOUT

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







發佈了70 篇原創文章 · 獲贊 4 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章