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