目錄
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)則加鎖失敗。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiATN381dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM2ITM0gjZ4YTMhFGMwYjYyYzXwMTMyIDM1IzLcZDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
加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變量記錄目前鎖的狀态。每個鎖都有相關的狀态機控制狀态轉移。
根據狀态機可以确定:
- 下一步鎖的狀态(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狀态轉換