天天看點

CephFS MDS内部鎖介紹

轉載:https://www.jianshu.com/p/dc16309519d1

鎖的作用

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)則加鎖失敗。

CephFS MDS内部鎖介紹

image

鎖的釋放

請求失敗或完成後,

Locker::drop_locks()

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

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

MDS鎖類型

/*
 * metadata lock types.
 *  - these are bitmasks.. we can compose them
 *  - they also define the lock ordering by the MDS
 *  - a few of these are internal to the mds
 */
#define CEPH_LOCK_DVERSION    1
#define CEPH_LOCK_DN          2
#define CEPH_LOCK_IVERSION    16    /* mds internal */
#define CEPH_LOCK_ISNAP       32
#define CEPH_LOCK_IFILE       64
#define CEPH_LOCK_IAUTH       128
#define CEPH_LOCK_ILINK       256
#define CEPH_LOCK_IDFT        512   /* dir frag tree */
#define CEPH_LOCK_INEST       1024  /* mds internal */
#define CEPH_LOCK_IXATTR      2048
#define CEPH_LOCK_IFLOCK      4096  /* advisory file locks */
#define CEPH_LOCK_INO         8192  /* immutable inode bits; not a lock */
#define CEPH_LOCK_IPOLICY     16384 /* policy lock on dirs. MDS internal */      
// -- lock types --
// see CEPH_LOCK_*

inline const char *get_lock_type_name(int t) {
  switch (t) {
  case CEPH_LOCK_DN: return "dn";
  case CEPH_LOCK_DVERSION: return "dversion";
  case CEPH_LOCK_IVERSION: return "iversion";
  case CEPH_LOCK_IFILE: return "ifile";
  case CEPH_LOCK_IAUTH: return "iauth";
  case CEPH_LOCK_ILINK: return "ilink";
  case CEPH_LOCK_IDFT: return "idft";
  case CEPH_LOCK_INEST: return "inest";
  case CEPH_LOCK_IXATTR: return "ixattr";
  case CEPH_LOCK_ISNAP: return "isnap";
  case CEPH_LOCK_INO: return "ino";
  case CEPH_LOCK_IFLOCK: return "iflock";
  case CEPH_LOCK_IPOLICY: return "ipolicy";
  default: ceph_abort(); return 0;
  }
}      
  bool acquire_locks(MDRequestRef& mdr,
             set<SimpleLock*> &rdlocks,
             set<SimpleLock*> &wrlocks,
             set<SimpleLock*> &xlocks,
             map<SimpleLock*,mds_rank_t> *remote_wrlocks=NULL,
             CInode *auth_pin_freeze=NULL,
             bool auth_pin_nonblock=false);      

todo