之前面試時被問到描述下一個請求的完整流程,當時的結(jié)果很不理想坷衍,今天嘗試重新組織下寝优,記錄在這里。
這里有篇文章通俗易懂地描述了VFS層頁緩存在cephfs中會有哪些“坑”以及相應(yīng)策略枫耳。
mount后發(fā)生了什么乏矾?
ceph-fuse在不指定rootpath參數(shù)時,client端的root inode和mds的root一樣。如果指定了rootpath參數(shù)钻心,那么client端的root inode則是rootpath中的最后一個dentry的inode凄硼,不過從這個inode的往上一直到mds root inode的路徑上的所有inode也都會在client端存一份副本(下圖紅色圓圈節(jié)點),這些父節(jié)點是為了計算quota時使用捷沸,比如quota可能設(shè)置在了mds root inode上摊沉,這時client端如果沒有mds root inode則無法正確得到quota值,除此之外這些父節(jié)點inode別無他用痒给。
ceph-fuse接收到的請求一定是關(guān)于一個inode的说墨,這點可以通過Client::ll_xxx
函數(shù)定義看出。而且這個inode必須是已經(jīng)從mds獲取過的苍柏。舉例來說尼斧,剛mount完成時,client端的元數(shù)據(jù)信息只有root inode(及其父節(jié)點inode)试吁,這時FUSE是不會直接向client(即ceph-fuse)請求一個非root inode直接子節(jié)點的棺棵,這一點是由VFS+FUSE模塊保證的。
打開文件
通過open()
系統(tǒng)調(diào)用打開文件時熄捍,傳入?yún)?shù)是一個路徑烛恤,F(xiàn)USE模塊會從root節(jié)點一個dentry一個dentry地遍歷(通過Client::ll_lookup()
)路徑,確保每個節(jié)點對應(yīng)的inode都已經(jīng)存在client端治唤。如果在一個剛mount好的client中請求一個目錄深度很深的路徑棒动,在系統(tǒng)調(diào)用上看只是一個請求,但實際上在client和mds間會產(chǎn)生多次通信宾添,路徑越深通信往返次數(shù)越多。
打開文件是需要給open()
傳入flat_t
表示讀寫權(quán)限柜裸,這些系統(tǒng)flag在client端會先轉(zhuǎn)換成CEPH_FILE_MODE_xxx
缕陕,然后每個CEPH_FILE_MODE_xxx
對應(yīng)到一組CAP CEPH_CAP_xxx
,如果client已經(jīng)擁有所需CAP疙挺,則成功返回扛邑,否則向mds發(fā)起請求。
在只有一個client時铐然,client會作為loner擁有全部cap蔬崩,這種情況比較簡單,通常在一次請求內(nèi)就能完成搀暑。稍微復(fù)雜些的是多個client的情況沥阳。
當client2 向mds發(fā)送OP_OPEN后,MDS端由Server::handle_client_open()
來進行處理自点。處理過程主要是圖中的四步桐罕。
第一步加鎖是常規(guī)操作,為了防止父節(jié)點被刪除。
第二步issue_new_caps()
在mds端記錄client2 聲稱需要的cap功炮,通過eval()
驅(qū)動鎖的狀態(tài)進行轉(zhuǎn)換溅潜,因為有新的client加入,且兩個client都需要對文件進行寫操作薪伏,這時IFILE lock從之前的EXCL狀態(tài)向MIX轉(zhuǎn)換滚澜,在本例的情況下(client1成功打開文件后,client2發(fā)起打開請求)嫁怀,EXCL到MIX的狀態(tài)無法直接在MDS端完成设捐,因為client1在之前作為loner被授予了過多cap,這些cap要先收回眶掌,才能繼續(xù)向MIX轉(zhuǎn)換挡育。回收cap的是異步的朴爬,所以不會阻塞后續(xù)步驟即寒。
第三步check_inode_max_size()
是為了記錄client2 對文件可寫入的范圍,這個數(shù)據(jù)作為client range被記錄到日志中召噩,用于故障恢復(fù)時使用母赵。這一步需要對IFILE進行wrlock
,這次加寫鎖時不會有問題的具滴,因為根據(jù)狀態(tài)機凹嘲,EXCL->MIX狀態(tài)下是允許EXL角色進行wrlock
的,剛好client1作為loner是XCL角色构韵,因此加鎖不會失敗周蹭。等日志落盤后鎖會被釋放,這時再根據(jù)當前狀態(tài)判斷是否發(fā)送新的cap給各個client疲恢,如果這時對client1的revoke cap還未完成凶朗,那么不會有新的cap發(fā)送給client,因為鎖的狀態(tài)沒有改變显拳,需要繼續(xù)等對client1 cap的收回(當然這個等待是異步的)棚愤。整個第三步也不會阻塞后續(xù)步驟。
第四步將inode信息發(fā)回給client2杂数。只有inode信息宛畦,沒有cap,cap會在其他過程中單獨發(fā)給client2(當mds完成對client1 的cap revoke時)揍移。
讀寫文件
文件打開后在系統(tǒng)層只是擁有了文件句柄次和,在實際調(diào)用read()
或write()
前,文件的CAP可能還并沒有完全授予給client羊精,因此不論是Client::ll_read()
還是Client::ll_write()
都會先進行get_caps()
斯够,確保已經(jīng)有相應(yīng)cap囚玫,如果沒有,就等待(通常情況下并不會向MDS發(fā)請求读规,因為沒有cap說明之前的open()
調(diào)用還沒有真正結(jié)束抓督,open()
調(diào)用完成時需要有兩個結(jié)果:1.inode信息被發(fā)回 2.cap被授予)。
讀寫文件需要的cap包括:
cap | 用途 |
---|---|
CEPH_CAP_FILE_RD | 讀文件需要 |
CEPH_CAP_FILE_WR | 寫文件需要 |
CEPH_CAP_FILE_CACHE | 從本client的cache中讀文件需要 |
CEPH_CAP_FILE_CACHE | 從本client的cache中寫文件需要 |
FILE_RD
和FILE_WR
是必須讀寫文件時的CAP束亏。
FILE_CACHE
和FILE_BUFFER
對應(yīng)常規(guī)文件系統(tǒng)里的緩沖概念铃在,每個client都可以把文件內(nèi)容cache在自己的內(nèi)存中,如果使用內(nèi)存中的數(shù)據(jù)碍遍,必須要有對應(yīng)的這兩個cap定铜。
這四個cap在get_caps()
時會增加cap的引用計數(shù),在相應(yīng)的讀寫操作從RADOS返回后怕敬,引用計數(shù)被減小揣炕。當cap的引用計數(shù)不為0時,即使收到了mds的revoke cap請求东跪,client也是無法釋放cap的畸陡。