天天看點

【ceph】cephfs的鎖 筆記CEPH caps簡介

目錄

​​CEPH MDS鎖實作介紹​​

​​鎖的作用​​

​​鎖的擷取​​

​​加鎖類型​​

​​鎖的種類和狀态機​​

​​鎖的狀态轉換​​

​​鎖的釋放​​

​​了解cephfs鎖必須要知道的事情​​

CEPH caps簡介

CAPS 和鎖的關系

caps由mds進行管理,其将中繼資料劃分為多個部分,每個部分都有專門的鎖(SimpleLock、ScatterLock、FileLock)來保護,mds通過這些鎖的狀态來确定caps可以怎麼樣配置設定。

mds内部維護了每個鎖的狀态機,其内容非常複雜,也是mds保證caps配置設定準确性和資料一緻性的關鍵。

CEPH MDS鎖實作介紹

鎖的作用

MDS中的鎖是為了保護log的正常寫入。每次對目錄樹進行操作前,需要先将目标path中涉及的節點加鎖,在記憶體中修改完目錄樹(修改方式類似于RCU,即生成一個新節點,push_back到 隊列 中)後,将新的目錄樹資訊(隻是此條path,不是整個目錄樹)記錄到MDS的journal對象中,journal對象落盤後再将 隊列 中的節點pop_front出來,至此,記憶體中的目錄樹已經能反映出之前的修改,加的鎖也在此時開始釋放,最後目前目錄樹的資訊更新到meta pool的dir對象中。

鎖的擷取

加鎖類型

加鎖的類型分三類:rdlock(讀)、wrlock(寫)、xlock(互斥)。每次對目錄樹進行操作前都要将path上的節點進行适當地加鎖。可從​

​src/mds/Server.cc​

​中觀察這一操作:

handle_client_xxx
      |-- rdlock_path_xlock_dentry或rdlock_path_pin_ref
      |-- mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)      

對于一個路徑進行操作時,最後一個dentry之前的dentry都要加rdlock,避免别人進行修改。xlock用于建立或者修改節點時,比如mkdir時需要對新的dentry加xlock,建立新檔案時需要對CInode::linklock(負責inode的nlink屬性)加xlock。rdlock和xlock符合通常認知:共享讀,互斥寫。

wrlock比較特殊,主要用在​

​CInode::filelock​

​​和​

​CInode::nestlock​

​​上,前者負責保護目前目錄的統計資訊​

​inode_t::dirstat​

​​,後者負責保護目前目錄的遞歸統計資訊​

​inode_t::rstat​

​;

由于一個目錄可以分成多個分片,甚至同一個分片也可以有多個副本分散于各個mds,為了允許對這些分片的統計資訊同時進行修改,引入了wrlock,這些分散的被修改的資訊将在後續的一個時間點上進行綜合,最終傳播到目錄樹的inode資訊中(見​

​CInode::preditry_journal_parents​

​​)。對于​

​CInode::versionlock​

​​和​

​CDentry::versionlock​

​也會加wrlock鎖,但由于是locallock sm,意義和simplelock的xlock一樣,隻是為了互斥寫。

鎖的種類和狀态機

一個inode中的資訊有很多種,每種由不同的鎖來保護,每個鎖的狀态變化由遵循特定的規則——狀态機。狀态機的定義在​

​src/mds/locks.c​

​中,共有四種:

  • simaplelock state machine
  • scatter_lock state machine
  • file_lock state machine
  • local_lock state machine

​CInode​

​​和​

​CDentry​

​中每種鎖使用的狀态機如下:

struct LockType {

  int type;

  const sm_t *sm;

  explicit LockType(int t) : type(t) {

    switch (type) {

    case CEPH_LOCK_DN:

    case CEPH_LOCK_IAUTH:

    case CEPH_LOCK_ILINK:

    case CEPH_LOCK_IXATTR:

    case CEPH_LOCK_ISNAP:

    case CEPH_LOCK_IFLOCK:

    case CEPH_LOCK_IPOLICY:

      sm = &sm_simplelock;

      break;

    case CEPH_LOCK_IDFT:

    case CEPH_LOCK_INEST:

      sm = &sm_scatterlock;

      break;

    case CEPH_LOCK_IFILE:

      sm = &sm_filelock;

      break;

    case CEPH_LOCK_DVERSION:

    case CEPH_LOCK_IVERSION:

      sm = &sm_locallock;

      break;

    default:

      sm = 0;

    }

  }

};

其中locallock sm最簡單,不做解釋;

絕大多數鎖使用simplelock sm,這些鎖隻需要“共享讀、互斥寫”功能;

目錄分片資訊和遞歸統計資訊則使用scatterlock sm,這種狀态機能提供“共享讀、共享寫”功能;

最複雜的是​

​CInode::filelock​

​使用的filelock sm,因為filelock既負責目錄統計資訊這種需要“共享讀、共享寫”的資料,也負責保護inode中的atime、mtime等需要“共享讀、互斥寫”的屬性。

​CInode​

​​種每種鎖負責保護的資料可由​

​CInode::encode_lock_state​

​推斷出來:

void CInode::encode_lock_state(int type, bufferlist& bl)
{
  ...
  switch (type) {
  case CEPH_LOCK_IAUTH:
    encode(inode.version, bl);
    encode(inode.ctime, bl);
    encode(inode.mode, bl);
    encode(inode.uid, bl);
    encode(inode.gid, bl);  
    break;
  case CEPH_LOCK_ILINK:
    encode(inode.version, bl);
    encode(inode.ctime, bl);
    encode(inode.nlink, bl);
    break;
  case CEPH_LOCK_IDFT:
    ...
    encode(dirfragtree, bl);
    ...
  case CEPH_LOCK_IFILE:
    if (is_auth()) {
      encode(inode.version, bl);
      encode(inode.ctime, bl);
      encode(inode.mtime, bl);
      encode(inode.atime, bl);
      encode(inode.time_warp_seq, bl);
      if (!is_dir()) {
    encode(inode.layout, bl, mdcache->mds->mdsmap->get_up_features());
    encode(inode.size, bl);
    encode(inode.truncate_seq, bl);
    encode(inode.truncate_size, bl);
    encode(inode.client_ranges, bl);
    encode(inode.inline_data, bl);
      }
    ...
  case CEPH_LOCK_INEST:
    ...      

鎖的狀态轉換

有了狀态機後就可根據預先定義的轉換規則判斷此次加鎖是否可行,不可行的情況下要對鎖的狀态進行适當轉換。鎖狀态的轉換有兩種驅動方式:

  • accquire_locks中根據目前sate判讀是否能加鎖,可以則直接變更鎖的目前狀态。
  • 按照狀态機中的sm_state_t::next訓示逐漸變換,這種方式一般有tick或者log flush回調等函數觸發。
隻有auth才有機會直接變更鎖的目前狀态,副本隻能向auth發消息請求加鎖

下圖展示了加xlock時的狀态變換,根據狀态機描述,如果目前無法加xlock,則對鎖進行一些轉換,如調用​

​Locker::simple_xlock()​

​​或​

​Locker::simple_lock()​

​,如果轉換過程無法順利進行(gather==true)則加鎖失敗。

【ceph】cephfs的鎖 筆記CEPH caps簡介

加xlock時的狀态轉換

鎖的釋放

請求失敗或完成後,​

​Locker::drop_locks()​

​負責鎖的釋放,其間會處理鎖的等待隊列,對鎖的狀态進行kick.

正常情況下要等到日志落盤後才會觸發釋放鎖的動作,如果設定了mds_early_reply = true則送出完log就會釋放rdlocks,但wrlock和xlock依然要等到log落盤後才釋放。

作者:宋新穎

連結:https://www.jianshu.com/p/dc16309519d1

了解cephfs鎖必須要知道的事情

1、本文讨論的鎖不是flock。flock是posix标準中對檔案某部分偏移加的鎖。

而這裡的鎖指的是MDS集中管理多用戶端對于檔案并發通路時,Dentry和Inode等資料結構控制的鎖。每個鎖内有parent變量記錄的就是該鎖相關的Dentry或者Inode。Cephfs采用和GPFS類似的并發更新元檔案的方式。同一份中繼資料分散多個MDS節點,但是有一個Auth節點,該節點負責

- 串行更新(serializing updates)

- 将中繼資料持久化到磁盤上(committing changes to disks)

- 保證緩存一緻性(consistency)

- 維護節點之間的一緻性(cache coherence)

多路并發的MDS節點會更新每個副本,但是會周期性地向AUTH發送目前節點最新的資料。

2、鎖的類型分為LocalLock、SimpleLock、ScatterLock、FileLock

無論何種類型的鎖,都有一個state變量記錄目前鎖的狀态。每個鎖都有相關的狀态機控制狀态轉移。

【ceph】cephfs的鎖 筆記CEPH caps簡介

​​​​​​

根據狀态機可以确定:

- 下一步鎖的狀态(next_state)

- 其他副本(Object比如Inode在多mds的其他mds也有副本叫replica)的狀态

- 允許誰可讀(can_read)、誰可加讀鎖(can_rdlock)、誰可加寫(can_wrlock)、誰可以加排他鎖(can_xlock)等。這裡的誰被抽象成為ANY、AUTH、XCL等。

ANY指的任何擁有Object副本的MDS,

AUTH(authority)指的是被授權Object的MDS,

XCL指的是被授權Object的MDS或者排他執行的用戶端。

- 對應的caps是什麼,包括擁有Object副本的MDS的此時應該使用的caps(針對不同的角色也分為4種)

複雜程度而言,可以根據sm_state_t結構體裡面定義的條數判斷,LocalLock最簡單,SimpleLock/ScatterLock相對複雜,FileLock最為複雜。

3、鎖的狀态有很多,都是以LOCK_開頭的(除了LOCK_AC_*之外,它代表鎖的某種行為)。

一般而言,

LOCK_<狀态>                     都是穩定狀态(标準是在狀态機其next_state為0,也有例外),比如LOCK_SYNC/LOCK_LOCK/LOCK_MIX等。

LOCK_<狀态1>_< 狀态2>    是中間狀态,比如LOCK_SYNC_LOCK就是從LOCK_SYNC狀态向LOCK_LOCK狀态轉移的中間狀态,日志中可以看到“sync->lock”就是這種狀态dump出來資訊。

4、每個Inode裡面有多個Lock,每個Lock對應2中介紹的4種類型的鎖。每種鎖關聯的是不同的檔案系統資源。

LocalLock  -  versionlock

SimpleLock用在nlink、atime、mtime等屬性的處理上。其特點是"共享讀、互斥寫"。

SimpleLock  - authlock  linklock  xattrlock(擴充屬性相關)  snaplock(快照相關)    flocklockpolicylock(layout相關)  

ScatterLock用在需要處理疊代的資料結構,比如目錄樹或者目錄下面的統計資訊,其特點是“共享讀、共享寫”

ScatterLock -  dirfragtreelock nestlock 

FileLock既用在處理atime、ctime等需要互斥操作的屬性上,也有需要共享寫的統計資訊上。

FileLock - filelock

5、Cap當中的s代表share,意味着用戶端擁有讀相關資訊的能力,比如删除inodes時mds會設定CEPH_CAP_LINK_SHARED,即Cap為Ls,用戶端讀到該标志位,會判斷inode的nlink資訊是否為0,0則代表删除用戶端執行針對删除的操作。x代表允許用戶端更新相關資訊的能力(獨占執行的能力)。

6、Cap和鎖的關系

用戶端是根據MDS賦予的cap确定相關行為。MDS則根據用戶端發來的請求和目前鎖的狀态,确定授予(grant)或者剝奪(revoke)用戶端的caps。

7、打算對一個inode或者dentry加鎖,需要先确定給其中的哪些鎖加什麼類型的操作。一般有rdlock、wrlock、remote_wrlock、xlock等。

rdlock  讀鎖操作,當一個資源加了讀鎖之後,不能再加獨占鎖對其修改。

wrlock  寫鎖操作

remote_wrlock  遠端寫鎖操作

xlock  獨占鎖操作,比如修改某項資源,一定要讓鎖執行該操作。

使用場景比如,

在建立快照的時候需要給執行目錄的inode的snap鎖加上獨占操作。

xlocks.insert(&diri->snaplock));

ls的時候會對父目錄至根目錄的dentry都加rdlock防止被修改。

rdlock.insert(&dn->lock)

日志裡面“isnap sync r=<NUM>”之類的輸出代表目前snap鎖處在sync狀态,鎖上有NUM個讀鎖操作(見SimpleLock.h SimpleLock::_print())。

8、Locker中*_start 表示對某個鎖執行某種類型的鎖操作。*_finish表示結束對某個鎖加某種類型的鎖操作。*_try代表嘗試去執行某種類型的鎖操作是否能夠成功。這裡的*代表rdlock、wrlock、remote_wrlock、xlock等。

9、Locker中有操作狀态的轉變函數,将任何中間狀态變為*_sync同步狀态,*_excl 獨占執行狀态、*_lock鎖狀态、*_xlock獨占鎖狀态。*可以是simple/scatter/file代表不同的鎖類型。比如Locker::file_xsyn函數會讓filelock進入xsyn狀态,XSYN狀态出現在如下場景中:

如果一個用戶端在執行ls –al的時候想要rdlock。此時如果一個EXCL的用戶端正在緩存寫,那麼MDS會讓鎖進入XSYN狀态,進入這個狀态寫會被停止,但是緩存不會刷到磁盤(提升效率)。  

10、MDS和其他MDS處理鎖互相發的是MLock請求。執行發送的函數是Locker::send_lock_message。内容是鎖和相關的轉換操作(LOCK_AC_*定義的行為)。

由auth的MDS發給replica的MDS有如下所示。replica的MDS收到之後會将其本地保留的鎖狀态,盡可能轉換為消息中定義的鎖狀态。

LOCK_AC_SYNC      //向SYNC狀态轉換

LOCK_AC_MIX          //向MIX狀态轉換

LOCK_AC_LOCK      //向LOCK狀态轉換

繼續閱讀