????最近項目里面要求寫使用o_direct,但是測試時cephfs性能并不好。通過iostat查看底層磁盤利用率,實際上并不高。通過日志查看ceph-fuse的行為,發(fā)現(xiàn)其只進(jìn)行128K的寫。如何優(yōu)化?簡單但思路是將fuse發(fā)送給client的請求變得更大比如4M,這樣每次IO傳輸更多的數(shù)據(jù)效率會更高。
????但無論將上層業(yè)務(wù)下發(fā)的塊大小如何調(diào)整,這128K絲毫不會改變。通過ceph代碼分析發(fā)現(xiàn),其Client::ll_write并沒有限制單次寫的大小。調(diào)用ll_write來自fuse進(jìn)程,是否是fuse系統(tǒng)出現(xiàn)的限制導(dǎo)致業(yè)務(wù)層的數(shù)據(jù)被限制切分下發(fā)到ceph-fuse實際執(zhí)行的Client::ll_write里。
????筆者首先嘗試fuse提供的原生能力,即big_write和max_write。雖然fuse提供了big_write功能,但在實際使用中并沒有明顯的效果。分析如下在開啟O_DIRECT時,系統(tǒng)會調(diào)用fuse內(nèi)核注冊的fuse_direct_write_iter函數(shù)。該函數(shù)會在執(zhí)行時對該inode進(jìn)行加鎖,以保證不會有并發(fā)寫操作改文件。

????執(zhí)行關(guān)鍵是fuse_direct_io函數(shù),在fuse_iter_npages獲得的是最大pages數(shù)這里的定義為32,一次最多讀取4K*32即128K的數(shù)據(jù)(使用kmalloc分配)。

????當(dāng)IO的一個塊大小很大時,即count值可能很大,當(dāng)然nmax代表的max_writes值也可能很大,取最小值。假如設(shè)置4M的塊大小,max_writes為1M,那么按照1M從用戶pages中獲得數(shù)據(jù)。但是都會受限于req中最大的pages數(shù)量即32。

????big_write標(biāo)志位的出現(xiàn)讓能夠讓原先只填滿一個page(4K)便返回的情況,變成填滿多個page再返回,但pages多少受到FUSE_MAX_PAGES_PER_REQ限制。page選擇兩者中最小的值。max_write可變,但是FUSE_MAX_PAGES_PER_REQ的宏定義為32,也就是只要max_write超過128K(32*4K),數(shù)據(jù)都只能填滿32個頁,即128K。也就是內(nèi)核中存在一個每次請求最多能處理的頁數(shù)的限制,即每次/dev/fuse中讀取拷貝內(nèi)核空間的最大值。

????原生的big_write不能解決問題,筆者采用打算自行修改fuse相關(guān)的代碼解決這一問題。使用fuse文件系統(tǒng)需要圍繞著/dev/fuse這個內(nèi)核設(shè)備做文章,這個設(shè)備類似于應(yīng)用了生產(chǎn)者和消費者的模型。筆者發(fā)現(xiàn)需要修改除了以上fuse內(nèi)核部分的限制之外libfuse中的還有一部分需要修改具體如下: ? ? ??
????對于libfuse而言,其規(guī)定每個channel的最大緩沖區(qū)量,即每一次可以從該channel中獲得多大的數(shù)據(jù)。代碼位置在lib/fuse_kern_chan.c 中。原先設(shè)定為:
????#define MIN_BUFSIZE 0x20100
????ceph-fuse采用動態(tài)加載libfuse的方式,因此只要修改代碼編譯,并替換動態(tài)庫即可。
????果然,在將每次請求量增大之后,性能有了非常明顯的提升。發(fā)散一下:只要使用fuse的文件系統(tǒng)均會遇到同樣的問題,可以作為參考。