阿里内核月报2014年4月
Ideas for supporting shingled magnetic recording (SMR)
在2014年LSF&MM峰會(huì)上,Dave Chinner和Ted Ts'o一起主持了一個(gè)跨越2天而占用2個(gè)時(shí)間段的會(huì)議。 這個(gè)會(huì)議的主題是,是文件系統(tǒng)還是塊設(shè)備層才是支持SMR設(shè)備的正確接口。最后,它的討論范圍 有點(diǎn)超出這個(gè)主題。
Zone信息接口和緩存
Ts'o一開始就描敘了他正在處理的一個(gè)SMR設(shè)備返回的Zone信息kernel級(jí)別的C接口。SMR設(shè)備將會(huì) 報(bào)告驅(qū)動(dòng)器中存在的Zones, 它們的特性(大小,僅串行,。。。)和對(duì)于每個(gè)僅串行Zone寫指針 的位置。Ts'o的想法是以緊湊的形式在內(nèi)核中緩存這些信息,這樣就不需要向設(shè)備發(fā)送多個(gè)"report zones"命令。代替地,舉例說,感興趣的內(nèi)核子系統(tǒng)能查詢Zones大小和它的寫指針的位置。
Ts'o說用戶接口是ioctl(),但James Bottomley認(rèn)為sysfs接口更有意義。Chinner擔(dān)心sysfs將會(huì)有 成千的條目,Ric Wheeler提醒一個(gè)跟定的設(shè)備可能實(shí)際上有上萬的Zones.
Ts'o說他正在用的數(shù)據(jù)結(jié)構(gòu)假定Zones大部分都被成組成大小相同的Zone區(qū)域。這個(gè)接口將支持其他 設(shè)備布局。Zach Brown想知道內(nèi)核為什么需要緩存這些信息,既然這也許要求探尋SCSI總線,尋找 重置寫指針命令。沒有人認(rèn)為探尋總線是可靠的,但一些人認(rèn)為不允許訪問裸SCSI貌似合理的。 Bottomley潑冷水Scsi sg層bypass Ts'o的緩存。
一個(gè)關(guān)于如何處理主機(jī)管理的設(shè)備的問題出現(xiàn)了(主機(jī)必須確保所有寫串行Zones都是串行的)。Ts'o 說他在主機(jī)感知的設(shè)備中看到了可怕的一秒延遲(主機(jī)能犯錯(cuò),轉(zhuǎn)換層將重映射非串行寫,它能導(dǎo)致 垃圾回收和可怕的延遲),這意味著用戶將想Linux支持主機(jī)管里的行為。那將避免了主機(jī)感知的設(shè)備 上出現(xiàn)的延遲。
但是,正如Chinner指出的,在用戶空間有一些不能改變的固定布局。舉例說,mkfs零化分區(qū)末端, 并且SMR設(shè)備必須能與之工作。他是高度懷疑主機(jī)管理的設(shè)備將與Linux完全工作。今天Linux上沒有什么 能運(yùn)行在主機(jī)管里的SMR設(shè)備上。但那些設(shè)備將可能生產(chǎn)起來更便宜,因此它們將是可用的,并且用戶向 支持它們。就主機(jī)管理的設(shè)備vs主機(jī)感知的設(shè)備問題輪詢了屋內(nèi)設(shè)備制造者們,最后也沒有太大的結(jié)論。
Ts'o建議使用設(shè)備映射在內(nèi)核中創(chuàng)建轉(zhuǎn)換層來支持主機(jī)管理的設(shè)備?!拔覀冃迯?fù)缺陷比供應(yīng)商推出新的 固件更快”。但正如Chris Mason指出的,任何新的設(shè)備映射層對(duì)用戶可用都不會(huì)超過3年,但未來有需求 支持2種SMR設(shè)備。第一個(gè)會(huì)議在這就超時(shí)了,沒有太多實(shí)際的結(jié)論。
當(dāng)Ts'o再一次撿起這個(gè)話題的時(shí)候,他向前邁出了更多。在許多場景下,塊設(shè)備正在做更多的事情。舉例 來說,SMR和帶有dm-thin的瘦provisioning.文件系統(tǒng)為基本的旋轉(zhuǎn)型驅(qū)動(dòng)器所做的優(yōu)化布局在其他場景 下是不明智的。對(duì)于SSD驅(qū)動(dòng)器,轉(zhuǎn)換層和驅(qū)動(dòng)器是如此的快以致文件系統(tǒng)不需要關(guān)心轉(zhuǎn)化層和驅(qū)動(dòng)器固件 中所做的。對(duì)于SMR和其他場景,那也許不是真的,因此有必要重新思考一下文件系統(tǒng)層。
將文件系統(tǒng)一分為二
這是Chinner關(guān)于文件系統(tǒng)思考的關(guān)鍵。他提醒到他已經(jīng)開始寫一些東西,并且對(duì)其他的建議和想法是開放的, 但他想到一些關(guān)于他思考的反饋。一個(gè)文件系統(tǒng)真正是由2個(gè)層組成:名字空間層和塊分配層。Linux文件 系統(tǒng)已經(jīng)做了許多工作來為旋轉(zhuǎn)型驅(qū)動(dòng)器優(yōu)化塊分配,但有其他類型的設(shè)備,SMR和永久內(nèi)存,它們的優(yōu)化就 有些落后了。
因此,為了優(yōu)化各種類型設(shè)備的塊分配,從文件系統(tǒng)名字空間處理中分拆出塊分配是由意義的。名字空間部分 將保持不變,所有的塊分配部分移入一個(gè)智能塊設(shè)備中,它能知道底層設(shè)備的特性并能相應(yīng)地分配塊。
名字空間層希望一組分配是連續(xù)的,但塊分配層可能基于它的知識(shí)改寫這些決定。假如它正在SMR設(shè)備上寫塊, 意識(shí)到它不能在連續(xù)的位置上寫數(shù)據(jù),它將返回附近的塊。對(duì)于旋轉(zhuǎn)型媒介,它將返回連續(xù)塊,但對(duì)于永久 內(nèi)存,我們不關(guān)心,因此,它僅返回一些便利的塊。任何不支持COW的文件系統(tǒng)都不能為SMR做優(yōu)化,因?yàn)槟悴?能在串行Zones中改寫數(shù)據(jù)。那意味著需要向ext4和XFS中添加COW功能。
但將文件系統(tǒng)一分為二意味著盤上格式能改變。名字空間層所關(guān)心的是它上面的元數(shù)據(jù)是一致的。但Ts'o拋出 了一個(gè)問題:它與存在15年之久的基于對(duì)象的存儲(chǔ)有怎樣的不同?
Chinner說他不準(zhǔn)備將像文件和inodes等東西移到塊分配層。那將僅是分配和回收塊的層。他問,為什么在每 個(gè)文件系統(tǒng)中為不同種設(shè)備優(yōu)化塊分配?
Chinner的想法和基于對(duì)象的存儲(chǔ)另一個(gè)不同是元數(shù)據(jù)與文件系統(tǒng)呆在一塊兒,不像基于對(duì)象的存儲(chǔ)那樣將它 們移到設(shè)備上。Chinner說他將不尋求分配一個(gè)能連接屬性的對(duì)象,僅創(chuàng)建為特定設(shè)備優(yōu)化的分配器。一旦那 被實(shí)現(xiàn)了,在多個(gè)文件系統(tǒng)間共享分配器是有意義的。
Mason提醒到Chinner描述的東西非常像FusionIO DirectFS文件系統(tǒng)。Chinner說他并不驚奇。他尋找但沒有 發(fā)現(xiàn)太多關(guān)于DirectFS的文檔和過去其他人提到這些想法。它不必是新的,但他正在試著將它作為解決現(xiàn)存 的問題的一種方法。
Bottomley問如何得到一些我們能測試的東西。Chinner原以為需要半年的時(shí)間,但目前看來在它能工作之前, 還有許多工作要做。他問“我們應(yīng)該采用這種方法?”,Wheeler認(rèn)為它值得期待;它避免了重復(fù),并且利用 新設(shè)備的優(yōu)點(diǎn)。其他人也都表示樂觀,但他們希望Chinner當(dāng)他做的時(shí)候時(shí)刻記住基于對(duì)象的存儲(chǔ)為什么不 工作的原因。Chinner認(rèn)為大約在6到12個(gè)月后一個(gè)POC將會(huì)出現(xiàn)。
Data integrity user-space interfaces
今年LSF/MM 2014上,Darrick Wong和Zach Brown主持了一個(gè)關(guān)于數(shù)據(jù)完整性(DIF)用戶態(tài)接口的討論。這一用戶態(tài)接口的相關(guān)代碼Darrick已經(jīng)發(fā)補(bǔ)丁提交到社區(qū)了。這一接口使用DIF規(guī)范來為相關(guān)的塊數(shù)據(jù)添加必要的校驗(yàn)和,以方便用戶檢查數(shù)據(jù)的損壞。
在去年的LSF/MM峰會(huì)上,Darrick也曾討論過DIF/DIX相關(guān)的話題,但是當(dāng)時(shí)沒有任何代碼給出。今年,他編寫了一套相關(guān)的接口來實(shí)現(xiàn)此功能。James Bottomley認(rèn)為提供這樣的借口給用戶會(huì)讓用戶過多的了解系統(tǒng)內(nèi)部的信息,這樣做過于復(fù)雜了。而Martin Petersen則認(rèn)為沒必要暴露底層借口給用,而是用庫函數(shù)來封裝相應(yīng)的接口來方便用戶的使用。
Ted Ts'o指出目前的借口需要擴(kuò)展aio中的iocb數(shù)據(jù)接口,后面他指出了他不太喜歡這樣做的原因。而Zach則認(rèn)為如果用戶需要將自己的數(shù)據(jù)放入iocb中,則必須要對(duì)這個(gè)接口做必要的修改。但這一工作需要不同用戶之間的協(xié)調(diào)。Kent Overstreet傾向于定義一個(gè)新的系統(tǒng)調(diào)用來完成這一工作,而不是使用已有的aio相關(guān)接口。但是Darrick則更傾向于使用已有接口。
Ted指出他對(duì)擴(kuò)展iocb結(jié)構(gòu)有顧慮的原因是google內(nèi)部實(shí)現(xiàn)了一個(gè)自己的I/O調(diào)度器,而這個(gè)調(diào)度器也對(duì)iocb結(jié)構(gòu)進(jìn)行了修改。所以對(duì)iocb結(jié)構(gòu)的修改會(huì)造成google內(nèi)核代碼rebase時(shí)候的麻煩。而同時(shí),Ted指出google也沒有打算把他們I/O調(diào)度器的相關(guān)代碼貢獻(xiàn)給社區(qū)的計(jì)劃。Darrick說能不能在iocb中添加一個(gè)標(biāo)志為來標(biāo)識(shí)具體哪些成員被使用??磥磉@塊還需要更多的協(xié)調(diào)工作。不過Ted說原則上他不反對(duì)這個(gè)用戶態(tài)接口。
Copy offload
2014年的LSFMM峰會(huì)上Martin和Zach介紹了copy offload的進(jìn)展,這個(gè)特性主要是在服務(wù)器的CPU和網(wǎng)絡(luò)不參與的條件下直接拷貝數(shù)據(jù)。Hannes和Doug同時(shí)也和大家同步了copy offload的一些額外的option。
Martin和Zach的演講題目是 "Copy Offload: Are We There Yet?"(看到這個(gè)題目,我只能呵呵了,每年都炒,每年都沒啥進(jìn)展)。Martin想和大家說“是的,謝謝”,但是似乎大家還是一如既往的很感興趣,所以Martin給大家介紹了一下他做的一些工作,比如基于Hanne3s的VPD(vital product data)重寫模塊,現(xiàn)在接口已經(jīng)很簡潔,只包含了源和目標(biāo)設(shè)備以及各自的便宜量,當(dāng)然還有拷貝的大小(以block為單位)。內(nèi)部的實(shí)現(xiàn)目前是使用了SCSI XCOPY命令,因?yàn)檫@個(gè)目前基本被所有的大廠商支持。
如果底層存儲(chǔ)設(shè)備支持的很好的話,copy offload可以輕而易舉得拷貝大量數(shù)據(jù),于是乎有人問samba的支持如何。Zach的回應(yīng)是需要一個(gè)新的接口,這個(gè)接口會(huì)使用文件描述符以及文件范圍來操作。但是由于copy offload這個(gè)操作可能會(huì)導(dǎo)致你的拷貝部分成功,所以用戶態(tài)程序需要處理這個(gè)問題。
Error handling
Hannes Reinecke在今年的LSFMM上帶來了兩個(gè)和塊設(shè)備層錯(cuò)誤處理相關(guān)的話題。第一個(gè)是分區(qū)掃描錯(cuò)誤,第二個(gè)是SCSI錯(cuò)誤處理路徑的徹底重構(gòu)。
Reinecke已經(jīng)添加了許多更加細(xì)致的錯(cuò)誤碼,用于幫助診斷問題。這期間他碰到一個(gè)問題,EMC的驅(qū)動(dòng)在分區(qū)掃描至磁盤末尾時(shí)返回ENOSPC。而他更希望看到的是和其他七個(gè)內(nèi)核分區(qū)掃描一樣的返回ENXIO。因此Reinecke在SCSI代碼里面將該錯(cuò)誤重新映射為EXNIO。否則的話這段代碼處理會(huì)有問題,因?yàn)镋NOSPC指的是達(dá)到了上限。
Al Viro提出了他的擔(dān)憂,重新映射后的錯(cuò)誤碼將會(huì)擴(kuò)散到用戶態(tài),給很多工具帶來很多困惑。Reinecke讓他確信重新映射后的錯(cuò)誤碼僅會(huì)傳播到塊設(shè)備層。能夠區(qū)分實(shí)際的IO錯(cuò)誤和掃描到磁盤末尾,將使得分區(qū)掃描器能夠在探測到IO錯(cuò)誤之后停止掃描。
在另外一個(gè)討論上,Reinecke給出一個(gè)關(guān)于從SCSI錯(cuò)誤在各個(gè)level恢復(fù)(LUN, target, bus, ...)的提議。他認(rèn)為部分層面直接reset沒有任何意義,而是應(yīng)該看具體探測到的錯(cuò)誤進(jìn)行判斷。例如,如果target是不可達(dá)的,對(duì)LUN進(jìn)行reset,或者對(duì)bus都是無意義的。而應(yīng)該嘗試重新傳輸一次,假如再失敗,那么將需要進(jìn)行機(jī)器重啟。這才是一個(gè)命令超時(shí)或者返回出錯(cuò)所應(yīng)該走的路徑。
針對(duì)這個(gè),底下聽眾有大量的抱怨,主要是此時(shí)并不是必須要重啟。當(dāng)其中一個(gè)LUN或者target出現(xiàn)錯(cuò)誤,其他LUN的傳輸鏈路將被破壞,即使他們處理IO正常。部分問題是源于LUN的reset命令不會(huì)time out,Reinecke說到。
但是Roland Dreier注意到一個(gè)丟失的IO可能導(dǎo)致整個(gè)存儲(chǔ)陣列需要reset,這將會(huì)花費(fèi)一分多鐘處理。此外,一旦進(jìn)入錯(cuò)誤處理路徑,所有到該機(jī)器的有問題IO都會(huì)停止等待。在一些大存儲(chǔ)陣列里面,丟失一個(gè)包可能導(dǎo)致很長一段時(shí)間沒有任何IO。Reinecke爭論到命令會(huì)重試,但是承認(rèn)一個(gè)嚴(yán)重的錯(cuò)誤確實(shí)是會(huì)導(dǎo)致這種情況發(fā)生。
當(dāng)然,讓事情復(fù)雜的進(jìn)一步還有,存儲(chǔ)廠商對(duì)不同的錯(cuò)誤都各自進(jìn)行不同的處理。對(duì)一個(gè)廠商的錯(cuò)誤恢復(fù)步驟可能和另外一個(gè)廠商的相同,也可能不同。最終,似乎大家都同意,Reinecke的修改將會(huì)使得事情比現(xiàn)在更好,是正確道路上往前的一步。
A revoke() update and more
在今年的LSF/MM峰會(huì)上Al Viro介紹了系統(tǒng)調(diào)用revoke()的最新進(jìn)展。revoke()的作用就是將一個(gè)文件名對(duì)應(yīng)的所有文件句柄都關(guān)閉,這樣調(diào)用進(jìn)程就知道他能夠獨(dú)享這個(gè)文件名對(duì)應(yīng)的文件或者設(shè)備,在這個(gè)topic中Al順便介紹了一下他對(duì)read()和write()各個(gè)變種的統(tǒng)一工作。
Viro開始的時(shí)候提到revoke()是他這個(gè)session里面最沒啥意思的,因?yàn)榇a基本已經(jīng)ok,實(shí)現(xiàn)也很簡單,文件在打開的時(shí)候如果聲明是可以revoke的話就會(huì)增加一個(gè)引用計(jì)數(shù),這樣如果revoke()被調(diào)用了,他就會(huì)阻塞等待直到所有打開的文件句柄都關(guān)閉(也就是說引用計(jì)數(shù)變成0),同時(shí)保證后面的任何open都會(huì)失敗。
目前在procfs以及sysfs里面已經(jīng)有些類似的邏輯,這些等到revoke合并以后就會(huì)被干掉。實(shí)現(xiàn)revoke的一個(gè)重要前提是它不能對(duì)正常的路徑有任何的性能損耗,因?yàn)榇蟛糠謶?yīng)用都不會(huì)使用這個(gè)特性,目前來看poll()和mmap()還有一些問題。Viro在做這個(gè)的同時(shí)注意到內(nèi)核里面很多代碼都有bug,囧。比如如果一個(gè)debugfs里面的一個(gè)文件在打開以后被刪除了,對(duì)這個(gè)打開句柄的任何read/write操作都會(huì)使內(nèi)核掛掉(真的,假的,我了個(gè)去)。動(dòng)態(tài)debugfs就是一個(gè)杯具,所以Viro希望revoke()在debugfs里面能夠工作的ok。
接下來Viro提到了他對(duì)read/readv/splice_read以及write/writev/splice_write代碼的統(tǒng)一工作。目前看read/readv以及write/writev已經(jīng)合并得差不多了,而splice*目前看很糟糕。在理想情況下,這些變種應(yīng)該在上層路徑上保持一致,一直到處理函數(shù)才有各自的區(qū)別。但現(xiàn)實(shí)情況是他們對(duì)數(shù)據(jù)有不同的視圖,splice*系列的函數(shù)的數(shù)據(jù)是放置在page里面的,而read/write則是放置在iovec里面,也許需要?jiǎng)?chuàng)建一個(gè)新的數(shù)據(jù)結(jié)構(gòu)來包裝這些。
另一個(gè)問題是iov_iter。iov_shorten()會(huì)嘗試重新計(jì)算iovec里面對(duì)應(yīng)的網(wǎng)絡(luò)包的個(gè)數(shù),所以如果存在short read/write,iovec就會(huì)被修改。更糟糕的是,iovec的修改是和協(xié)議相關(guān)的,這個(gè)對(duì)用戶非常不友好。事實(shí)上,CIFS的一個(gè)哥們火上澆油說CIFS每次都是拷貝一份iovec因?yàn)樗膊恢赖讓訒?huì)對(duì)iovec做什么樣的修改。Viro最后總結(jié)說iovec就應(yīng)該和協(xié)議無關(guān),所以他正在干掉iov_shorten以及其他會(huì)縮小iovec的地方,這個(gè)可能最終會(huì)導(dǎo)致sendpage()被干掉。
Thin provisioning
Thin provisioning
(Eric Sandeen、Luká? Czerner、Mike Snitzer和Dmitry Monakhov在2014年的LSFMM上討論thin provisioning的問題。Thin provisioning是什么意思想必不用多說,但如何翻譯倒不太一致,正式文件中居然是。。。自動(dòng)精簡配置??好吧,還是叫超賣更順口些?,F(xiàn)在的linux的標(biāo)準(zhǔn)用法已經(jīng)是在dm層上用dm-thin target來實(shí)現(xiàn)thin provisioning了,目前dm-thin也已經(jīng)相當(dāng)穩(wěn)定,大家今天討論的主要是一些性能問題)
Ted Ts'o:開會(huì)
Snitzer: dm-thin現(xiàn)在的塊分配算法對(duì)上層應(yīng)用是透明的,基本就是寫時(shí)按需分配,這樣的話如果我在dm-thin數(shù)據(jù)卷上有多個(gè)volume,每個(gè)volume上邊都有進(jìn)程在順序?qū)?#xff0c;它們?cè)偃プx時(shí)就不是順序的了。我想像CFQ那樣,給每個(gè)volume準(zhǔn)備一個(gè)待提交的struct bio的有序列表,然后讓各個(gè)volume輪流去寫盤,每次寫一大片,這樣讀的時(shí)候這一大片還是順序讀。我不會(huì)把這東西搞得像電梯算法那么復(fù)雜,主要是為了提高些locality。另外,XFS和ext4能不能向我暴露出來allocation group的邊界在哪?我可以把這當(dāng)成一個(gè)hint,這樣寫IO跨越邊界時(shí)我就知道一個(gè)大塊寫已經(jīng)結(jié)束了。
Joel Becker、Dave Chinner: 你要這個(gè)干啥?你用邏輯塊號(hào)就行了。你真正想要的只是一個(gè)hint,別去關(guān)心它到底是不是allocation group的邊界,這是文件系統(tǒng)的內(nèi)部細(xì)節(jié)。
Ted Ts'o:總之,文件系統(tǒng)應(yīng)該提供一個(gè)抽象的hint,用來給dm-thin判斷l(xiāng)ocality
Ted Ts'o:下一個(gè)議題~~, dm-thin必須知道哪些塊被釋放了,不然很快就入不敷出了,我看用DISCARD命令就不錯(cuò)。
Roland Dreier: 多數(shù)線上系統(tǒng)管理員都把這東西關(guān)掉了。
Martin Petersen: 另外TRIM命令的支持也一直不太好
某人:估計(jì)目前唯一可靠的辦法是用獨(dú)立的工具做離線trim。
Snitzer:提醒大家注意一下,我們關(guān)心的不是那些硬件設(shè)備對(duì)DISCARD命令的真實(shí)支持情況到底怎么樣,它可能支持,可能不支持,眼下咱們沒能力關(guān)心,咱們關(guān)心的僅僅是把這個(gè)命令傳到dm-thin這一層,讓dm-thin知道哪塊空間被釋放了就足夠了,甚至直接約定不往下傳給設(shè)備也行。mount時(shí)加個(gè)-o discard就足夠了。
Ted Ts'o:下一個(gè)議題~~, fallocate()是用來預(yù)分配空間的,不過目前這命令只是在文件系統(tǒng)層上有效,根本影響不到塊設(shè)備層。這樣在dm-thin上造成的結(jié)果就是有可能用戶事先調(diào)用了fallocate()分配出來了空間,然后實(shí)際寫的時(shí)候遇到-ENOSPC --- 因?yàn)閐m-thin那邊沒有空間了,這完全違反了fallocate()的語義。要是不把fallocate()的塊分配這事交給塊設(shè)備層,這事情解決不了。
Dave Chinner: 不可能這么做,違反分層了。
Eric Sandeen、 Dave Chinner:提醒大家一下,文件系統(tǒng)沒空間后的行為很不一樣,XFS的話,如果數(shù)據(jù)已經(jīng)寫到redolog里了,它就會(huì)持續(xù)重試往數(shù)據(jù)盤里寫,ext4和btrfs就不會(huì)這樣。一般來說,用戶程序?qū)?ENOSPC的處理都不太好,持續(xù)地返回-ENOSPC說不定會(huì)讓用戶程序徹底瘋掉。
Dmitry Monakhov:我建議文件系統(tǒng)應(yīng)該有一個(gè)標(biāo)準(zhǔn)化的途徑向用戶報(bào)告各種事件,包括像磁盤沒有空間了這種錯(cuò)誤。比如從VFS層發(fā)個(gè)uevent上去,前幾天我提這個(gè)建議的時(shí)候,大家都表示同意,不過沒人知道該從哪開始著手做,或者什么時(shí)候能搞定。
Ted Ts'o:。。。散會(huì)
(就像LSMM的大多數(shù)議程一樣,開完會(huì)后開發(fā)者們還是什么也定不下來,但不管怎么說---總比不開會(huì)強(qiáng)。)
Block multi-queue status
Nic Bellinger在2014 LSFMM上主持討論了block multi-queue的現(xiàn)狀。blkmq在3.13的時(shí)候被merge到了內(nèi)核,但是只支持virtio_block驅(qū)動(dòng)且基本上可以工作,雖然到現(xiàn)在又有些變動(dòng),但是整體架構(gòu)上是OK的。Micron的mtip32xx SSD驅(qū)動(dòng)的轉(zhuǎn)換也基本完成了,目前的驅(qū)動(dòng)是單隊(duì)列且共享tags,轉(zhuǎn)換后是有八條隊(duì)列,IOPS可以達(dá)到180w,和未修改的驅(qū)動(dòng)一樣。它在2-socket系統(tǒng)上表現(xiàn)良好,但是在4-socket機(jī)器上有所下降。
這其中的一個(gè)問題是因?yàn)槿鄙賢ags。有人說他們替換掉了per-cpu ida后就消除了tags問題。而Matthew Wilcox也提出了tag的另一個(gè)問題:linux上的實(shí)現(xiàn)是每個(gè)LUN上唯一,但是規(guī)范上只要求每target唯一。James Bottomley也說規(guī)范允許16bit的tags,而不是目前用的8bit。
Bellinger繼續(xù)介紹了他和Hellwig在給SCSI添加多隊(duì)列的工作。從2008年起就開始有人指出SCSI core在小隨機(jī)IO性能方面的問題,這大部分是由于鎖的cache-line bounce,這使得這類IO的IOPS只能達(dá)到250K。使用多SCSI host后可以使性能達(dá)到100w IOPS,但是會(huì)有1/3的CPU耗在spinlock爭搶。因此Bellinger使用blkmq來預(yù)分配SCSI command,sense buffer, 保護(hù)信息和request,他的初級(jí)實(shí)現(xiàn)不包含錯(cuò)誤處理,出現(xiàn)的任何錯(cuò)誤都會(huì)造成oops,但是可以達(dá)到170w IOPS。Hellwig實(shí)現(xiàn)了錯(cuò)誤處理并做了merge的計(jì)劃,但是他倆在是否將faster IOPS作為默認(rèn)模式上還沒有達(dá)成一致,這也會(huì)讓合并有些困難。另外,Bellinger認(rèn)為驅(qū)動(dòng)的轉(zhuǎn)換相對(duì)比較容易,而Bottomley認(rèn)為在消除驅(qū)動(dòng)的鎖方面仍有較多工作要做。
Large-sector drives
雖然Linux kernel對(duì)4k sector的設(shè)備的支持才[剛剛出來 http://lwn.net/Articles/377895/], 2014年LSF/MM峰會(huì)上Ric Wheeler又開始招呼大家開始討論sector尺寸大于4k的設(shè)備了。Ric首先給設(shè)備制造商拋出一個(gè)問題:當(dāng)時(shí)他們選擇4k sector size的原因是啥,是因?yàn)樗麄冏栽傅倪€是因?yàn)槭艿絻?nèi)核的限制。這個(gè)問題廠商們并沒有正面回答,不過他們的一致意見是最好在sector size上能夠有更多的靈活性,比如可以支持64k或者128k這樣的。
Dave Chinner提到了目前內(nèi)核需要修改的地方,目前看似乎文件系統(tǒng)以及處理分區(qū)的代碼需要修改。他還提到目前page cache代碼的假設(shè)都是能夠基于page大小做IO,Jan Kara也提醒大家目前頁面回收算法也是基于上面這個(gè)假設(shè),所以如果頁面大小(4K)小于sector大小,可能比較麻煩。Dave提到一種解決方案是用類似IRIX中的chunk方案,這玩意可以處理多內(nèi)存頁的buffer,但是他不缺定是否靠譜。另一條路子就是允許小于sector尺寸的讀寫,但是這個(gè)就不可避免的會(huì)出現(xiàn)讀-修改-寫回這種場景,性能就杯具了。
最后Ric總結(jié)說目前設(shè)備制造商還沒有開始推動(dòng)大sector尺寸的設(shè)備支持,所以內(nèi)核社區(qū)還有時(shí)間,但是內(nèi)核社區(qū)的一個(gè)基本原則就是先要搞定Sector尺寸大于page尺寸的情況,后面應(yīng)該就差不多了。
Direct I/O status
Kent Overstreet今年在LSF/MM峰會(huì)上主持討論了Direct I/O目前的狀態(tài)。Direct I/O的作用是允許用戶跨國系統(tǒng)page cache直接訪問磁盤上的數(shù)據(jù)。
Kent首先提到他的biovec的相關(guān)工作已經(jīng)被社區(qū)合并了。這些工作之后就可以很容易的拆分一個(gè)bio結(jié)構(gòu)。剩下的一個(gè)前提準(zhǔn)備是make_request()函數(shù)。該函數(shù)需要能夠處理任意大小的bio。
在完成了所有上述準(zhǔn)備工作之后,內(nèi)核就可以拋棄buffer_head從而直接使用bio來進(jìn)行DIO操作了。這樣代碼的復(fù)雜度就可以有效地降低。同時(shí),DIO代碼路徑上也不再需要通過buffer_head來獲取文件系統(tǒng)元數(shù)據(jù)信息。同時(shí),由于make_request()函數(shù)可以處理任意大小的bio,buffered I/O路徑也可以進(jìn)行相應(yīng)的優(yōu)化,文件系統(tǒng)不再需要處理bio拆分這樣無效率的工作了。
FedFS, NFS, Samba, and user-space file servers
雖然LSFMM分了三個(gè)tracks討論,但多數(shù)話題都無法將存儲(chǔ)和文件系統(tǒng)分開討論,恐怕只有Chuck Lever和Jim Lieb牽頭的討論是個(gè)例外,他們主要提及了用戶空間文件服務(wù)器,尤其是FedFS和用戶空間NFS。
Lever首先從介紹FedFS是什么東東開始:FedFS(Federated Filesystem)可以讓管理員把幾個(gè)完全不同的文件服務(wù)器導(dǎo)出的文件系統(tǒng)合并成一棵文件系統(tǒng)樹呈現(xiàn)給用戶。FedFS通過一種稱為referrals的概念表示“底層”文件服務(wù)器上的文件或者目錄信息。因此,FedFS文件系統(tǒng)實(shí)際上是樹狀組織一個(gè)referrals集合。
物理保存referrals信息的對(duì)象稱為“junctions”。不同文件系統(tǒng)實(shí)現(xiàn)junctions的方式并不一樣:Samba使用符號(hào)連接保存訪問數(shù)據(jù)所需要的元信息,而FedFS則使用帶有特殊擴(kuò)展屬性的空目錄表示。雖然去年會(huì)議上對(duì)統(tǒng)一junctions格式的意向達(dá)到了一致,但Samba和FedFS依然各行其是。
接下來就是怎么統(tǒng)一的討論了:NFS的開發(fā)者覺得空目錄方法不好,這個(gè)設(shè)計(jì)類似于Windows的reparse points概念。有人提出如果能引入一種新inode,或者至少引入一個(gè)新標(biāo)志位就完美了,但Tso反對(duì)說,這意味著需要修改所有文件系統(tǒng)才能支持它(作為文件系統(tǒng)小白,我表示個(gè)人不能理解為什么要修改所有文件系統(tǒng)?!)。同樣,符號(hào)連接方法也沒有得到親們的好評(píng):Layton解釋了為什么不能用符號(hào)連接,而Trond Myklebust則清楚地認(rèn)為符號(hào)連接的主意是too ugly和hacky,并且也限制了referral的信息只能保存在一個(gè)頁面上,所以如果要在一個(gè)referral上支持多個(gè)協(xié)議恐怕很困難。Myklebust總結(jié)道,符號(hào)連接,Samba自己玩玩尚可,推而廣之還是不要了??上У氖?#xff0c;究竟怎么統(tǒng)一格式還沒有定論。
后面開始小話題滿天飛了,我摘要一下:
- 用戶空間文件服務(wù)器需要內(nèi)核協(xié)助的地方蠻多的,其中之一是file-private locks,Layton已經(jīng)提供了一個(gè)patch,Lieb希望它可以進(jìn)入3.15合并窗口。然后,就是讓glibc支持這種新型鎖。
- inotify事件的過濾機(jī)制,例如,Ganesha文件系統(tǒng)服務(wù)器需要監(jiān)聽底層文件系統(tǒng)的事件,但不需要其自身的事件,這用于Ganesha的緩存管理機(jī)制。同樣也有補(bǔ)丁了,等待合并中。
- Ganesha開發(fā)者希望可以用更快的方法處理user credentials,現(xiàn)在每次操作都需要數(shù)次(概念上需要7次)系統(tǒng)調(diào)用。最后的結(jié)果是大家認(rèn)為Ganesha需要一種user credentials緩存,而Tso覺得他有個(gè)招兒可以解決問題,轉(zhuǎn)私下交流了。
- readdir()的增強(qiáng)版提議再次提上日程,看起來依然無望。
- 最后也是一個(gè)沒有結(jié)論的話題:目前有兩種ACL:POSIX ACL和NFS ACL,各有一批使用者,兩者的語義也不完全相同。AI Viro認(rèn)為同者支持兩者過于復(fù)雜,而雖然NFS ACL的語義更豐富,但大家對(duì)NFS ACL是否能夠模擬POSIX ACL也沒有定論。所以,也就沒法確定內(nèi)核應(yīng)該支持哪個(gè)了。
Lots of new perf features
在2014年的Linux Foundation Collaboration Summit大會(huì)上,Red Hat的Jiri Olsa和LG的Namhyung Kim為我們介紹了Perf的一些新特性。
Olsa的介紹如下:
1)Perf利用了libtraceevent庫來分析Tracepoint的格式。
Libtracepoint由Steven Rostedt開發(fā),是ftrace前端的一部分。我們知道,每個(gè)kernel的Tracepoint在它的format文件里面都有格式信息,用以描述該Tracepoint輸出信息的格式。Libraceevent可以幫助用戶空間的程序更方便地獲得這些格式信息。
2)對(duì)Intel處理器新特性的支持
2.1) 支持了RAPL(Running Average Power Limit 詳見注[1])
在RAPL的支持下,管理能能夠設(shè)置并監(jiān)控系統(tǒng)中各種硬件域(Hardware Domain,見注[1])的能耗上限。基于RAPl,Stephane Eranian做了相應(yīng)的支持,可以對(duì)不同的硬件域進(jìn)行能耗相關(guān)的采樣和分析:如所有的物理Core,CPU芯片,DRAM或者內(nèi)建的GPU。2.2) 訪存行為的Profiling
對(duì)內(nèi)存訪問行為的Profiling是很多開發(fā)者的共性需求,以往,perf在這方面做的并不好,主要因?yàn)镃PU沒有提供相關(guān)的接口。目前,在最新的Intel CPU的支持下,我們能夠觀察到對(duì)內(nèi)存的加載與存儲(chǔ)操作。而且這些事件還附帶了豐富的信息:指令地址、數(shù)據(jù)地址、訪問目標(biāo)(L1 Cache、本地RAM、遠(yuǎn)端Cache…)、TLB的訪問狀況(命中、丟失…)等。同時(shí),我們還能拿到每次訪問的開銷(weight,即此次訪存操作消耗了多少個(gè)CPU cycles)。不過,這個(gè)開銷的數(shù)值在現(xiàn)階段還不是很精確。但在未來的CPU中,訪存開銷的精度會(huì)得到持續(xù)改善。
Perf僅能夠記錄內(nèi)存訪問信息,并不去嘗試分析它。另外一個(gè)工具“c2c(cache to cache)”能夠監(jiān)測到CPU間共享的cache line。在指定地址之后,c2c能夠報(bào)告每一條cache line的狀態(tài),比如:訪問類型(加載 or 存儲(chǔ))、訪問偏移以及觸發(fā)訪問的指令。有了這些信息,開發(fā)者便能夠?qū)γ織lcache line都了如指掌。
3) 對(duì)調(diào)用鏈功能的改善
在利用ftrace進(jìn)行profiling時(shí),大家都希望能看到相關(guān)的函數(shù)調(diào)用鏈。目前,我們使用了frame pointer實(shí)現(xiàn)了這個(gè)功能。如果frame pointer未被使用時(shí)(GCC往往會(huì)將該寄存器優(yōu)化為普通寄存器),則利用libdw,在DWARF調(diào)試信息的支持下,unwind應(yīng)用程序的stack。Perf已經(jīng)能夠記錄用戶棧與相關(guān)的寄存器,而libdw可以完成后續(xù)的工作。Libdw比libunwind要快很多。[扁鵲下一步也將考慮使用libunwind替換掉libunwind]。
另外一種快速生成調(diào)用鏈的方法是使用LBR(Last Branches Record)信息。LBR機(jī)制在能夠存儲(chǔ)被采到的那條指令所觸發(fā)的分支列表。同時(shí)包括分支的源地址與目標(biāo)地址。利用這些信息,也能夠獲得調(diào)用鏈。
4) 未來工作
4.1) 添加對(duì)CTF(Common Trace Format)數(shù)據(jù)格式的支持。
CTF格式當(dāng)前被LTTng(Linux Trace toolkit next generation)用于數(shù)據(jù)存儲(chǔ)格式。4.2) 將perf record多線程化。
4.3) 事件的相互觸發(fā)機(jī)制
允許一個(gè)事件打開或關(guān)閉另外一些事件,從而減少被記錄的數(shù)據(jù)量。原始代碼由Frédéric Weisbecker開發(fā),但目前尚沒有合適的用戶接口。
4.4) 事件組
目前,perf需要在每個(gè)CPU上為每個(gè)打開的事件申請(qǐng)一個(gè)文件描述符。如果CPU很多,打開的事件數(shù)也很多。那么文件描述符很可能不夠用。目前,已經(jīng)有計(jì)劃使得每個(gè)“事件組”共享同一個(gè)文件描述符。當(dāng)然,大家僅僅討論過這個(gè)想法,還沒有代碼。
4.5) Perf代碼的通用化
Perf 的一些核心代碼被移到了tools/lib中,其它的工具也可以使用這些代碼了。
4.6) 測試用例
Perf現(xiàn)在共有26個(gè)測試用例。每個(gè)commit都需要跑一遍這些測試。測試集還會(huì)日益豐富。
Namhyung Kim的介紹如下:
1) Perf report 的--children參數(shù)
展示完整的callchain,以及callchain中的每個(gè)函數(shù)對(duì)測試結(jié)果的貢獻(xiàn)。在perf report的輸出結(jié)果中,增加了“children”與“self”列,用以展示Callchain中每個(gè)函數(shù)的貢獻(xiàn)。
2)--field參數(shù)
用以指定需要展示的信息(dso,thread comm等等)。該參數(shù)可以與“--sort”配合使用。
3)Ftrace的支持
在perf中支持ftrace是一個(gè)新特性。目前,僅僅支持function與function_graph。該特性利用了libtraceevents的kbuffer API來訪問事件。為ftrace事件提供類似perf的行為是未來的目標(biāo)。
我們可以這樣使用perf的ftrace功能:
perf ftrace record
perf ftrace report 或 perf ftrace show
4)在uprobe中增加了DWARF的支持
Perf目前能夠利用符號(hào)名與行號(hào)動(dòng)態(tài)設(shè)置tracepoint。該工作由Masami Hiramatsu貢獻(xiàn),并已經(jīng)被收入upstream了。通過“--line”與“--var”便可以利用Binary中的debug信息創(chuàng)建uprobe檢查點(diǎn)。之后,perf可以對(duì)這些進(jìn)行采樣。
5)SDT(Statically Defined Tracepoints)的支持
SDT類似于內(nèi)核的Tracepoint,面向應(yīng)用程序。在支持SDT后,perf能夠訪問SDT并對(duì)SDT進(jìn)行采樣。目前能夠給出應(yīng)用中支持的SDT的列表,并利用uprobe訪問它們。不久之后,我們可以像訪問perf的事件那樣來訪問SDT。
注: [1] RAPL: Running Average Power Limit。IntelCPU自Sandy Bridge之后引入的新接口。通過該接口,軟件能夠監(jiān)測、控制芯片的能耗,并可以獲得能耗相關(guān)的通知。RAPL為了實(shí)現(xiàn)細(xì)粒度的能耗控制,將硬件劃分成了不同的域,如:芯片、DRAM控制器、CPU core、Graphic uncore等。
Ref:http://lwn.net/Articles/545745/
Sealed files
通過共享內(nèi)存來進(jìn)行進(jìn)程間通信的常用方法。這個(gè)方式簡單高效,但有一個(gè)陷阱:所有交互進(jìn)程必須相互信任。也就是操作共享空間的進(jìn)程必須假設(shè)對(duì)方不會(huì)做壞事,比如在你的讀的時(shí)候偷偷修改了內(nèi)容還告訴你沒有修改,在不恰當(dāng)?shù)臅r(shí)機(jī)把空間的后端文件大小變了導(dǎo)致進(jìn)程的讀寫操作觸發(fā)致命的信號(hào)中止程序等。當(dāng)然現(xiàn)實(shí)中,我們可以通過拷貝共享空間的數(shù)據(jù)到其它地方來避免這些問題。但這樣做十分低效和笨重。
開發(fā)者最近一直在討論內(nèi)核層面上的解決方案。三月份David Herrmann同學(xué)提交了file sealing補(bǔ)丁,保證一方在操作共享空間的時(shí)候?qū)Ψ讲粫?huì)做壞事。
這個(gè)方案通過fcntl()操作共享空間的文件描述符引入了三個(gè)標(biāo)志位
如果這三個(gè)標(biāo)志同時(shí)被設(shè)置,這個(gè)共享空間就變成了不可變(immutable)的空間。同時(shí)這些標(biāo)志不能作用于有可寫映射的空間,除非通過munmap()來把可寫的映射去掉。一旦空間被密封后,一方可以通過調(diào)用fcntl,傳入SHMEM_GET_SEALS來獲取對(duì)方設(shè)置的標(biāo)志。這將避免一些潛在的安全和未知因素。
目前文件密封(file seal)操作只能作用于shmfs,通過修改write(), truncate(), mmap()路徑上的代碼實(shí)現(xiàn)。
有人指出引入了文件密封機(jī)制也會(huì)引入安全問題。比如說通過頻繁的去密封一個(gè)普通文件形成拒絕服務(wù)攻擊。目前文件密封機(jī)制只做作用于shmfs,所以不會(huì)給普通文件帶來這個(gè)問題。
如果不想掛載shmfs來顯示的操作文件,這個(gè)補(bǔ)丁引入了一個(gè)新的系統(tǒng)調(diào)用:
int memfd_create(const char *name, u64 size, u64 flags);
這個(gè)系統(tǒng)調(diào)用將返回一個(gè)支持文件密封的句柄,然后用戶可以通過mmap()映射到共享空間。
大多數(shù)開發(fā)者都覺得這套補(bǔ)丁不錯(cuò),但是也有一些人覺得實(shí)現(xiàn)和語義有點(diǎn)問題。Linus大神不滿意所有人都能去密封文件,不管是不是自己創(chuàng)建的。建議只有創(chuàng)建者才能密封文件。David同學(xué)沒有反對(duì)這個(gè)建議,說如果這樣做還能簡化實(shí)現(xiàn)。他覺得還可以添加一個(gè)標(biāo)志(MFD_ALLOW_SEALING)來控制是否允許別人密封。
Ted Ts'o提到文件密封可以擴(kuò)大到更大的范圍,應(yīng)該做vfs層中去,然后所有的文件系統(tǒng)都可以支持這個(gè)操作。David同學(xué)回答說,到目前為止他沒有見到任何通用文件系統(tǒng)對(duì)這個(gè)特性的需求用例。這個(gè)問題的討論以沒有結(jié)果而告終。
目前這個(gè)補(bǔ)丁已經(jīng)有了一些潛在用戶,kdbus和顯卡已經(jīng)Android系統(tǒng)都有可能用到,所以這個(gè)機(jī)制被合并的前景很好。
Avoiding memory-allocation deadlocks
避免申請(qǐng)內(nèi)存死鎖 有句諺語"要掙錢必須先花錢", 盡管這個(gè)悖論很容易通過創(chuàng)業(yè)貸款和堅(jiān)持收支平衡的原則來解決。 一個(gè)相似的邏輯適用于管理像linux這樣的操作系統(tǒng)的內(nèi)存: 有時(shí)候,你為了釋放內(nèi)存,需要先申請(qǐng)內(nèi)存。 這里,同樣需要堅(jiān)持原則, 盡管不小心的結(jié)果不是破產(chǎn),而是死鎖。
作為linux開發(fā)歷程的一個(gè)縮影,內(nèi)核如何保持其收支的平衡是很有趣的,并有助于理解如何去處理未來發(fā)生的死鎖。 讓我們從介紹1998年早期的Linux 2.1.80pre3里的__GFP_IO作為一個(gè)美好起點(diǎn)。
__GFP_IO in 2.1.80pre3
內(nèi)核里的任何內(nèi)存申請(qǐng)都包含一個(gè)gfp_t的參數(shù),這是一組標(biāo)志用來指導(dǎo)get_free_page()函數(shù)如何去定位一個(gè)空閑頁。 從2.1.80pre3,這個(gè)參數(shù)的類型從一組簡單的枚舉類型,改變?yōu)闃?biāo)志位。每個(gè)標(biāo)志里的含義跟以前的一致,但是 這是他們第一次被明確的標(biāo)示。
—GFP_IO 是這些新標(biāo)志中的一個(gè)。當(dāng)它被設(shè)置時(shí), get_free_pages() 可以調(diào)用shm_swap() 把一些頁寫到交換分區(qū)。 如果shm_swap()為了完成寫操作,需要申請(qǐng)一些buffer_head結(jié)構(gòu)體,要注意不能再設(shè)置—GFP_IO標(biāo)志了。 否則,很容易就發(fā)生死循環(huán),進(jìn)而很快耗光棧,并導(dǎo)致內(nèi)核崩潰。
今天,內(nèi)核里的—GFP_IO是2.1.80內(nèi)核引入的,原先的—GFP_IO在 2.1.116內(nèi)核已被刪除了。盡管名字一樣,但是他們是不同的標(biāo)志了。
PF_MEMALLOC in 2.1.116
很久以前(1998年8月),我們還沒有像今天這樣好的修改日志,所以需要一個(gè)操作系統(tǒng)考古學(xué)家去猜測之前哪些修改的原因。 我們確認(rèn)之前get_free_page()使用的(每請(qǐng)求一個(gè)的)—GFP_IO已經(jīng)沒有了,并出現(xiàn)了一個(gè)新的(每進(jìn)程一個(gè)的)標(biāo)志PF_MEMALLOC, 來取代它避免遞歸的作用。這次改動(dòng)的一個(gè)明顯的好處是它更專注于解決一個(gè)問題:遞歸是per-process的問題,因此一個(gè)per-process標(biāo)志更合適。 之前,很多申請(qǐng)內(nèi)存的地方會(huì)不必要的避免使用—GFP_IO?,F(xiàn)在,這些地方不用再擔(dān)心這個(gè)遞歸的問題了。
下面的代碼注釋強(qiáng)調(diào)了內(nèi)存分配的一個(gè)重要方面:
*"PF_MEMALLOC" 標(biāo)志使我們避免遞歸
* 如果我們做換出的時(shí)候工作時(shí),需要更多的內(nèi)存,我們只需要返回"success",來告訴內(nèi)存分配器接受申請(qǐng)。
當(dāng)條件滿足時(shí), get_free_page()就會(huì)從空閑鏈里面拿出一頁并盡快返回這一頁。當(dāng)條件不滿足時(shí),不會(huì)僅僅釋放一頁來滿足當(dāng)前請(qǐng)求, 而是多釋放一些頁,以節(jié)省下次的時(shí)間。這樣,就相當(dāng)于重新補(bǔ)充了創(chuàng)業(yè)貸款。PF_MEMALLOC 的一個(gè)特別的結(jié)果是內(nèi)存分配器不需要 特別努力地去得到大量的頁。他盡量根據(jù)它已有的去設(shè)法應(yīng)對(duì)。
這意味著帶有PF_MEMALLOC的進(jìn)程有可能適用最后僅有的很少的空閑內(nèi)存,相比之下,其他的進(jìn)程則必須換出并釋放大量內(nèi)存后才能夠使用。 在當(dāng)前內(nèi)核里PF_MEMALLOC的這種特性還有并且正式些。在內(nèi)存分配器里有個(gè)“水位線”的概念,如果空閑的內(nèi)存總數(shù)低于特定的水位線, 分配器就會(huì)盡量釋放更多的內(nèi)存,而不是返回現(xiàn)有的內(nèi)存。跟—GFP能夠選擇不同的(最小,低,高)水位線不同,PF_MEMALLOC會(huì)忽略所有的水位線, 只要有可用的內(nèi)存,它就會(huì)返回這些內(nèi)存。
PF_MEMALLOC實(shí)際上相當(dāng)于說“現(xiàn)在要停止省錢,開始消費(fèi),否則我們將會(huì)沒有產(chǎn)品可賣”。結(jié)果現(xiàn)在PF_MEMALLOC被廣泛的使用, 而不僅僅是為了避免遞歸(盡管他有這個(gè)作用)。像nbd,網(wǎng)絡(luò)塊設(shè)備,iscsi_tcp和MMC卡控制器用到的幾個(gè)線程都設(shè)置了PF_MEMALLOC, 任何調(diào)用他們?nèi)懗鲆豁搩?nèi)存(以釋放它)的時(shí)候,他們肯定能夠得到內(nèi)存。
與此相比,在2.6.33,MTD驅(qū)動(dòng)(它管理NANDflash并對(duì)MMC卡驅(qū)動(dòng)有類似的作用)停止使用PF_MEMALLOC標(biāo)志, 并在注釋里說是這是一種不正確的用法。關(guān)于內(nèi)核里其他使用的地方是否合理的問題已經(jīng)超出了我們這篇文章要討論的深度了。
__GFP_IO in 2.2.0pre6
當(dāng)—GFP_IO再次出現(xiàn)在內(nèi)核時(shí),跟最初的目的相比,它有一個(gè)類似的目的,但是是為了一個(gè)很重要的不同的原因。為了理解那個(gè)原因, 很有必要看看代碼里的一個(gè)注釋:
/* 如果我們不能夠做IO,不要深入到換出的東西里,*/
這里的關(guān)注還要涉及到遞歸,但也會(huì)涉及到鎖,如per-inode mutex, page lock和其他各種鎖。 為了寫出一頁而調(diào)用到文件系統(tǒng)里可能需要鎖。如果分配內(nèi)存時(shí),某一個(gè)鎖被阻住了,那么避免任何可能調(diào)用到文件系統(tǒng)并取得相同鎖 就很重要。這些情況下,代碼千萬不要使用—GFP_IO;其他情況下,參數(shù)里包含這個(gè)標(biāo)志是相當(dāng)安全的。
PF_MEMALLOC避免了get_free_page()又遞歸調(diào)用了get_free_page(), 而—GFP_IO更通用,并且避免了 一個(gè)持有鎖的進(jìn)程通過get_free_page()調(diào)用到其他也需要這個(gè)鎖的函數(shù)。這里的風(fēng)險(xiǎn)不是像PF_MEMALLOC那樣的耗光棧, 而是死鎖.
考慮到先前—GFP_IO 的經(jīng)驗(yàn)并不成功,有人可能疑惑為啥使用一個(gè)GFP標(biāo)志而不是一個(gè)進(jìn)程標(biāo)志, 進(jìn)程標(biāo)志可以明確地說“我持有一個(gè)文件系統(tǒng)的鎖”。 像許多軟件設(shè)計(jì)一樣,這可能“在那時(shí)看來是個(gè)好主意”
__GFP_FS in 2.4.5.8
這個(gè)標(biāo)志最初出現(xiàn)在2.4.5.1里,名字是—GFP_BUFFER ,但直到2.4.5.8里被命名為—GFP_FS前,沒有真正正常工作。 顯然在原始設(shè)計(jì)里有個(gè) thinko, 這要求不僅改變一些代碼,還有改了一個(gè)新的名字。
實(shí)際上—GFP_FS從—GFP_IO里分走了一部分功能,所以過去是一個(gè)標(biāo)志,現(xiàn)在又兩個(gè)標(biāo)志。 只有三種組合可能是正確的:全沒有,全有,新的可能僅—GFP_IO被設(shè)置。 這將允許我們已經(jīng)準(zhǔn)備的buffers被寫出,但會(huì)禁止調(diào)用到文件系統(tǒng)里去準(zhǔn)備那些buffers。 I/O行為被允許,文件系統(tǒng)行為被禁止。
推測,—GFP_IO先前有這么寬泛的功能,對(duì)性能有影響,在一些可能有I/O行為的地方也被排除(禁止使用)了。 重新提煉規(guī)則,增加一個(gè)新的標(biāo)志位,從而更加靈活,對(duì)性能影響更小。
PF_FSTRANS in 2.5.36
在2002年底XFS被合并進(jìn)內(nèi)核時(shí),這個(gè)新的進(jìn)程標(biāo)志出現(xiàn)。 它被用來指明一個(gè)文件系統(tǒng)事務(wù)正在被準(zhǔn)備,也就是所有的寫文件系統(tǒng)都會(huì)被阻塞,直到這個(gè)事務(wù)被處理完畢。 這個(gè)標(biāo)志的作用是,當(dāng)申請(qǐng)內(nèi)存時(shí),并設(shè)置了PF_FSTRANS時(shí),—GFP_FS 就會(huì)被剔出,至少XFS的代碼產(chǎn)生的請(qǐng)求是這樣的。 其他代碼里的請(qǐng)求不會(huì)受影響,但是當(dāng)這個(gè)標(biāo)志設(shè)置時(shí),其他代碼里的內(nèi)存申請(qǐng)很少被調(diào)到。
在2.1.116內(nèi)核里,由于用的地方少,而且修改也很容易理解,刪除一個(gè)像—GFP_IO 的標(biāo)志位是很簡單的事情。 在2.5.26,這樣的操作就困難的多了。 3.6rc1稍后的版本,NFS開始使用PF_FSTRANS 標(biāo)志。PF_FSTRANS 不是被使用在事務(wù)里,而是被NFS用在創(chuàng)建并傳輸一個(gè)RPC請(qǐng)求到網(wǎng)絡(luò)上 所以,現(xiàn)在名字有一點(diǎn)不太確切。這個(gè)標(biāo)志在NFS中的作用,不是用來清除—GFP_FS標(biāo)志,而是避免在nfs_release_page里發(fā)送一個(gè)COMMIT請(qǐng)求, 這在沒有設(shè)置—GFP_FS標(biāo)志是應(yīng)該避免的。這樣的使用方法跟XFS的使用方法有很大的不同。也許把它重命名為PF_MEMALLOC_NOFS,并刪除GFP_FS, 從而使這個(gè)標(biāo)志具有全局的屬性的想法,是個(gè)不錯(cuò)的主意。
set_gfp_allowed_mask() in 2.6.34
首次出現(xiàn)在2.6.31,但在2.6.34里變得更加有意思。 gfp_allowed_mask 是一個(gè)全局的變量,它包含了一系列的GFP標(biāo)志。特別是—GFP_FS,—GFP_IO, and—GFP_WAIT (這個(gè)標(biāo)志位允許get_free_page()函數(shù)等待其他進(jìn)程釋放更多的內(nèi)存)有時(shí)候會(huì)通過這種機(jī)制失效。 它能夠影響更多的進(jìn)程并使更多的標(biāo)志失效,這有點(diǎn)像PF_FSTRANS。
gfp_allowed_mask能夠在啟動(dòng)的早期階段給kmalloc()函數(shù)更多的支持。在啟動(dòng)的早期階段,中斷時(shí)被禁止的。任何試圖通過—GFP_WAIT或者其他標(biāo)志去申請(qǐng)內(nèi)存,都會(huì)觸發(fā)來自lockdep checker的一個(gè)警告信息。在啟動(dòng)階段,如果內(nèi)存如此緊張,以至于讓 分配者不需要要去等待,那將是讓人感到很吃驚的。 所以gfp_allowed_mask 在初始化的時(shí)候去除了前面提到的三個(gè)標(biāo)志, 并在啟動(dòng)完成后,把他們?cè)偌踊貋怼?/p>
我們這些年得到的一個(gè)經(jīng)驗(yàn)是啟動(dòng)不是像我們想得那么特殊:無論是掛起和恢復(fù),還是硬件的熱拔插。 在2.6.34里這個(gè)掩碼被擴(kuò)展以覆蓋掛起和恢復(fù)。
對(duì)于內(nèi)存分配引起的死鎖,刮起比啟動(dòng)更有代表性。在啟動(dòng)階段有大量的空閑內(nèi)存,而刮起時(shí)則不是這樣,可能碰巧我們也很缺乏內(nèi)存。 那么就不是彈出個(gè)警告信息,而是真正的死鎖。掛起和重啟是順序化的進(jìn)程,如設(shè)備順序進(jìn)入休眠,而反向的順序醒來。 所以對(duì)塊設(shè)備來說,盡量避免使用—GFP_IO是遠(yuǎn)遠(yuǎn)不夠的。一些寫請(qǐng)求設(shè)計(jì)到的塊設(shè)備,可能在順序操作中已經(jīng)休眠,因而 在這個(gè)請(qǐng)求完成前,不可能恢復(fù)過來。
使用一個(gè)系統(tǒng)級(jí)別的設(shè)置來禁止這些標(biāo)志位,這看起來有些小題大做。因?yàn)?#xff0c;只要考慮那些順序刮起所涉及的進(jìn)程就足夠了。 但這是一個(gè)簡單可靠的修復(fù)辦法。不會(huì)影響正在運(yùn)行的系統(tǒng)。
PF_MEMALLOC_NOIO in 3.9-rc1
就像掛起和恢復(fù)告訴我們,啟動(dòng)不是一個(gè)很特殊的場景。進(jìn)而,運(yùn)行時(shí)的電源管理搞所我們,刮起也不是很特殊的場景。 如何一個(gè)塊設(shè)備為了節(jié)省電源,在系統(tǒng)運(yùn)行期間掛起,顯然直到他或者他依賴的設(shè)備(如USB控制器,PCI總線)恢復(fù)過來前, 任何將一個(gè)臟頁寫出的請(qǐng)求都不會(huì)得到處理。所以任何一個(gè)這樣的設(shè)備執(zhí)行帶有—GFP_IO的內(nèi)存請(qǐng)求都是不安全的。
當(dāng)一個(gè)設(shè)備刮起或者恢復(fù)的時(shí)候,我們使用set_gfp_allowed_mask()來確保這一點(diǎn)。但是如果有多個(gè)這樣的設(shè)備同時(shí) 掛起或者恢復(fù),我們很難處理什么時(shí)候恢復(fù)正確的mask。所以有個(gè)patch引入了一個(gè)進(jìn)程的標(biāo)志位,就像PF_FSTRANS標(biāo)志一樣, 只禁止了—GFP_IO標(biāo)志位而不是—GFP_FS標(biāo)志位。在設(shè)置標(biāo)志時(shí),先記錄老的值,等完成時(shí),再恢復(fù)回去。每個(gè)設(shè)備增加了一個(gè) memalloc_noio標(biāo)志,來指明什么時(shí)候去設(shè)置這個(gè)標(biāo)志。他還可以被傳遞到設(shè)備樹里的父節(jié)點(diǎn)。當(dāng)一個(gè)帶有memalloc_noio的設(shè)備進(jìn)入 電源管理的代碼的時(shí)候,PF_MEMALLOC_NOIO就會(huì)被設(shè)置。
盡管啟動(dòng)的早期階段和設(shè)備的掛起和恢復(fù)大多是單線程(或者指定的線程),設(shè)置在這些線程上設(shè)置PF_MEMALLOC_NOIO和PF_FSTRANS 標(biāo)志位能夠替代set_gfp_allowed_mask。 但是,這樣的變化沒有明顯的好處,而且能否正常工作并安全并不是很明確,所以目前還是保持不變。
Patterns that emerge
節(jié)下來有兩個(gè)地方要做進(jìn)一步的修改: 1。如何進(jìn)一步避免“避免遞歸”。最初是個(gè)枚舉類型的值,然后是—GFP_IO,之后PF_MEMALLOC。下一步是第二版的—GFP_IO.最后會(huì)分成 兩個(gè)獨(dú)立的標(biāo)志位。 2。一個(gè)GFP標(biāo)志位是不夠的, 不是一個(gè)單獨(dú)的申請(qǐng)要被控制,而是給定進(jìn)程的所有內(nèi)存申請(qǐng)都要被控制。 我們使用一個(gè)進(jìn)程標(biāo)志來禁止—GFP_WAIT或者一個(gè)每進(jìn)程gfp_allowed_mask只是一個(gè)時(shí)間問題嗎?
在3.6rc1,增加了一個(gè)新的標(biāo)志—GFP_MEMALLOC,以支持swap-over-NFS。 這個(gè)標(biāo)志在忽略低水位線并訪問保留內(nèi)存方面跟PF_MEMALLOC類似。 這個(gè)標(biāo)志位和每socket的sk_allocation mask, 使得某些tcp sock(nfs使用他們進(jìn)行swap over)能夠訪問保留內(nèi)存,從而保證swap-out成功。 顯然,GFP標(biāo)志和進(jìn)程標(biāo)志,以及每設(shè)備和每socket標(biāo)志都要改。
這篇文章沒完,下周還要結(jié)合具體配置進(jìn)行講解。
Ktap or BPF?
過去幾年內(nèi)核一直在考慮增加一個(gè)動(dòng)態(tài)的跟蹤機(jī)制. ktap和bpf是近兩年出現(xiàn)的兩種內(nèi)核動(dòng)態(tài)跟蹤的機(jī)制。 這里的動(dòng)態(tài)跟蹤機(jī)制,我個(gè)人的理解就是把報(bào)文過濾的虛擬機(jī)從用戶態(tài)轉(zhuǎn)移到內(nèi)核態(tài)去做, 這樣可以提高效率。例如,減少不必要的報(bào)文copy(內(nèi)核態(tài)到用戶態(tài))。
BFP就是"Berkeley packet filter"。在各種報(bào)文分類的場合,它被廣泛的使用,如tcpdump就支持這種格式。 2011年,BPF采用了一種新的編譯器從而大大提高了速度。3.15內(nèi)核也采用了這個(gè)特性。 一個(gè)重寫的BPF引擎可以為用戶空間提供一樣的虛擬指令集,在內(nèi)核里這些指令還可以被轉(zhuǎn)換為一種更 接近硬件指令的格式。跟舊的格式相比,新的格式有很多優(yōu)勢,包括使用10個(gè)而不是2個(gè)64位的寄存器, 更高效的跳轉(zhuǎn)指令,一種允許從BPF程序里調(diào)用內(nèi)核函數(shù)的機(jī)制。 顯然,在BPF爭取成為內(nèi)核動(dòng)態(tài)跟蹤設(shè)施的虛擬引擎時(shí),這些附加的能力進(jìn)一步增強(qiáng)了它的優(yōu)勢。
目前,如果ktap要想被內(nèi)核接受,必須先和BPF虛擬引擎融合。 KTAP的作者Jovi Zhangwei(這個(gè)兄弟目前在華為)也表達(dá)了做出這樣改變的愿望, 但同時(shí)他也指出了一些BPF的需要解決的缺陷, BPF不支持一些KTAP需要的特性,如訪問全局變量,timer-limited looping等。 Jovi反復(fù)的抱怨BPF的跟蹤機(jī)制,它是基于“把腳本附加到一個(gè)特定的跟蹤點(diǎn)上”, 而Jovi需要一個(gè)更加彈性的機(jī)制"把一個(gè)腳本附加到多個(gè)跟蹤點(diǎn)上“。 只要兩個(gè)作者通力合作,這些都不是很困難的事情。但是兩個(gè)人溝通出現(xiàn)的問題,彼此誤解。 目前也沒有其它人加入進(jìn)來,事情就被擱在這兒了。
Scripting languages
Ktap是基于lua語言的,lua的一些屬性在設(shè)置動(dòng)態(tài)跟蹤時(shí)很有用,如associative arrays。 然而,還有一些人,更喜歡一種C風(fēng)格的語言。理由是內(nèi)核使用的C語言,開發(fā)者對(duì)C風(fēng)格語言更容易接受。 BPF一開始使用一種受限的C語言版本,Alexei已經(jīng)提供把GCC和LLVM(?)轉(zhuǎn)換成BPF虛擬引擎需要的后端。
而Jovi并不贊同上述觀點(diǎn),他認(rèn)為ktap工作起來更簡單。他舉了個(gè)例子:
void dropmon(struct bpf_context *ctx) {void *loc;uint64_t *drop_cnt;loc = (void *)ctx->arg2;drop_cnt = bpf_table_lookup(ctx, 0, &loc);if (drop_cnt) {__sync_fetch_and_add(drop_cnt, 1);} else {uint64_t init = 0;bpf_table_update(ctx, 0, &loc, &init);}}而KTAP只需要:
var s ={}trace skb:kfree_skb {s[arg2] += 1}ALexei承認(rèn)KTAP語法使用起來更簡潔。同時(shí), 他還建議一旦確定使用哪個(gè)虛擬引擎, 用戶空間要有一些腳本語言的支持。 現(xiàn)在情況是,ktap里有一些有意思的函數(shù),作者希望能有個(gè)開發(fā)者能夠作些工作, 以便于把這些代碼合入到內(nèi)核中。
Changing the default shared memory limits
@泰來
Linux內(nèi)核的System V共享內(nèi)存從Unix年代開始就有一個(gè)固定的限制值。雖然用戶可調(diào)大這個(gè)值,但是隨著硬件的飛速發(fā)展,應(yīng)用程序期待越來越大的內(nèi)存,這個(gè)值顯然太小了。現(xiàn)在除了調(diào)大這個(gè)默認(rèn)值,開發(fā)者開始討論我們是否需要這個(gè)限制了。但是為了兼容老的程序和避免一些不可預(yù)料的后果,簡單粗暴的去掉這個(gè)限制也不可能,所以當(dāng)大家都覺得這個(gè)值太小了的時(shí)候,修復(fù)確是一件不容易的事情。
System V風(fēng)格的共享內(nèi)存廣泛用于進(jìn)程間的通信,比如在多進(jìn)程的數(shù)據(jù)庫中就有廣泛的應(yīng)用。對(duì)于空間大小系統(tǒng)有兩種限制,一種是每個(gè)空間可以允許的最大值(SHMMAX),一種是所有共享空間加起來的最大值(SHMALL)。在Linux系統(tǒng)中,SHMMAX默認(rèn)是32MB,SHALL的計(jì)算公式是:
#define SHMALL (SHMMAX/getpagesize()*(SHMMNI/16))其中SHMMNI是系統(tǒng)支持最大的空間個(gè)數(shù)--默認(rèn)是4096個(gè)。SHAMAX和SHMALL可以通過sysctl調(diào)節(jié)。實(shí)際是系統(tǒng)還有一個(gè)宏SHMMNI,表示一個(gè)空間的最小值,默認(rèn)是1個(gè)字節(jié),這個(gè)值不能被修改。
近年來管理員一上來就調(diào)大SHMMAX幾乎成了標(biāo)配的動(dòng)作,因?yàn)?2MB確實(shí)太小了。很多文章就建議跑一些受歡迎的程序之前,需要慣例性的調(diào)大這個(gè)值。
所以,在一些開發(fā)者當(dāng)中認(rèn)為調(diào)大這個(gè)值是當(dāng)務(wù)之急。在3月31號(hào)的時(shí)候,Davidlohr Bueso就提交了一個(gè)補(bǔ)丁,把這個(gè)值修改成128MB。當(dāng)然,128只是一個(gè)隨機(jī)值,背后沒有任何意義可言。
鼎鼎大名的Andrew Morton隨后指出,這個(gè)補(bǔ)丁沒有解決真正的問題。這個(gè)值還是需要很多用戶去手動(dòng)的調(diào)大默認(rèn)值。他呼吁大家想得更遠(yuǎn)一點(diǎn):如何才能永遠(yuǎn)的把這個(gè)問題徹底根治掉?
一個(gè)辦法就是更本不提供SHMMAX這個(gè)值給用戶,但是有人指出有時(shí)候管理員確實(shí)想設(shè)置一些限制來避免內(nèi)存被濫用。Motohiro Kosaki建議看是否可以把默認(rèn)值設(shè)置為0,然后用來表示無最大限制。但是后面Spraul指出,有用例把SHMALL設(shè)置為0來表示徹底關(guān)掉共享空間這個(gè)機(jī)制。所以用0來表示無限制,不能完全走通。
后來Spraul同學(xué)搞了一個(gè)自己的版本,就是把SHMALL和SHMMAX設(shè)置為ULONG_MAX。有人指出這其實(shí)又引入了潛在的問題:如果有用戶通過N + 1的方式來設(shè)置這兩個(gè)值,那么+1后這兩個(gè)值直接溢出了,完全變成了相反的效果。后面針對(duì)這個(gè)方式,做了一些小的改進(jìn),但都不如人意。
默認(rèn)把限制去掉,有人覺得SHM空間會(huì)有機(jī)會(huì)耗光內(nèi)存。如果這個(gè)發(fā)生,將是災(zāi)難性的,因?yàn)镺OM機(jī)制無法釋放SHM占用的內(nèi)存。解決方式是開啟shm_rmid_forced選項(xiàng)(也就是讓強(qiáng)制SHM空間和一個(gè)進(jìn)程聯(lián)系在一起,所以可以通過殺死進(jìn)程來釋放內(nèi)存)或者選擇手動(dòng)設(shè)置限制(這不是又回到了原點(diǎn)么?這個(gè)補(bǔ)丁的目的就是避免用戶的手工設(shè)置)
即使是回到了原點(diǎn),刪除古老的32MB限制也算得上一個(gè)不小的進(jìn)步。
Loopback NFS: theory and practice
Linux NFS開發(fā)者一直以來就知道將一個(gè)NFS文件系統(tǒng)掛載到導(dǎo)出它的相同主機(jī)上,將會(huì)導(dǎo)致死鎖(有時(shí)候被稱作回環(huán)或者本地NFS掛載)。除了10年前發(fā)布的一個(gè)補(bǔ)丁外,有很少的努力來解決這個(gè)場景的問題因?yàn)闆]有可信的用例出現(xiàn)。NFS實(shí)現(xiàn)的測試肯定能從回環(huán)掛載中受益;這很可能觸發(fā)上面提到的補(bǔ)丁。打上這個(gè)補(bǔ)丁,其余的死鎖的觸發(fā)需要做出一些努力,因此對(duì)測試者的建議本質(zhì)上是“小心,你應(yīng)該是安全的”。
對(duì)于其他的測試用例,似乎使用“bind”掛載提供了與回環(huán)NFS掛載類似的效果。簡言之:假如當(dāng)你使用回環(huán)NFS掛載受傷,你不要簡單的那么做。然而,最近一個(gè)可信的測試用例出現(xiàn)了,激發(fā)了關(guān)于這個(gè)問題更多的思考。它導(dǎo)致作者寫了一篇關(guān)于文件系統(tǒng)與內(nèi)存管理間交互的教育性文章,并且產(chǎn)生了近期發(fā)布的補(bǔ)丁集(取代之前的嘗試)來消除大部分,甚至所有的死鎖。
一個(gè)簡單的集群文件系統(tǒng)
那個(gè)用例涉及使用NFS作為一個(gè)高可用性集群中的文件系統(tǒng),在那,所有的主機(jī)擁有共享的對(duì)存儲(chǔ)訪問權(quán)限。對(duì)于集群中所有能平等地訪問存儲(chǔ)的節(jié)點(diǎn)來說,你需要某種集群文件系統(tǒng),像OCFS2, Ceph, 或者GlusterFS.假如集群不需要特定高級(jí)的吞吐量并且假如系統(tǒng)管理員偏愛繼續(xù)使用已有技術(shù),NFS能提供一個(gè)簡單的臨時(shí)替代方案。
理論遇到了實(shí)踐
這個(gè)場景觸發(fā)的死鎖通常涉及一系列的事件像:1)NFS服務(wù)器試著分配內(nèi)存,2)內(nèi)存分配器試著通過把一些內(nèi)存頁經(jīng)過NFS客戶端寫到文件系統(tǒng),3)NFS客戶端等待NFS服務(wù)器有些進(jìn)展。我的假設(shè)是這個(gè)死鎖是不可避免的因?yàn)橥粋€(gè)內(nèi)存管理器正試著服務(wù)2個(gè)不同但競爭的用戶: NFS客戶端和NFS服務(wù)器。
一種可能的修復(fù)方法可能是在一個(gè)虛擬機(jī)中運(yùn)行NFS服務(wù)器,并且給它以一個(gè)固定且被鎖住內(nèi)存分配,這樣將沒有任何競爭。這將工作,但對(duì)于我們的管理員,它很難是一個(gè)簡單解決方案,很可能將會(huì)帶來為最佳性能而計(jì)算VM大小的挑戰(zhàn)。
似乎對(duì)__GFP_FS和PF_FSTRANS標(biāo)識(shí)的操作應(yīng)該能解決死鎖問題。假如我們把nfsd看作是NFS文件系統(tǒng)的底層,死鎖涉及到分配內(nèi)存的文件系統(tǒng)的底層,并且觸發(fā)回寫到相同的文件系統(tǒng)。這準(zhǔn)確是__GFP_FS被設(shè)計(jì)來阻止的死鎖。實(shí)際上,在所有nfsd線程中設(shè)置PF_FSTRANS標(biāo)識(shí)的確修復(fù)之前很容易就碰到的死鎖。
使用__GFP_FS框架,要么直接,要么通過PF_FSTRANS, 最終證明作為回環(huán)NFS掛載問題的解決方案,要么是不充分,要么是不可信賴的。
一個(gè)關(guān)鍵的補(bǔ)丁
Mel Gorman在Linux 3.2中提交了一個(gè)對(duì)于這個(gè)問題很關(guān)鍵的補(bǔ)丁。這個(gè)補(bǔ)丁集改變了內(nèi)存回收和文件系統(tǒng)回寫間的交互。
在3.2之前,內(nèi)存回收通常發(fā)起它能發(fā)現(xiàn)的文件臟頁的回寫。在頁能被釋放前,明顯需要寫這個(gè)臟頁的內(nèi)存到永久存儲(chǔ)上,因此似乎當(dāng)尋找可釋放頁時(shí)做這些是有意義的。不幸的是,它有一些嚴(yán)重的負(fù)面影響。
一個(gè)負(fù)面影響是被使用的內(nèi)存棧空間的數(shù)目。第二個(gè)是頁可能以不可測順序被寫出。
因此,Linux 3.2中從直接回收中刪除了回寫,把它留給kswapd或者各種文件系統(tǒng)回寫線程。
等待回寫
在這個(gè)補(bǔ)丁被應(yīng)用后,直接回收將不再回寫臟的文件頁,這個(gè)延遲將也不再發(fā)生。此時(shí),假如所有看到的臟頁正在等待擁塞的設(shè)備,我們將得到一個(gè)明顯的小延遲。這個(gè)導(dǎo)致了回環(huán)NFS掛載一些問題。與Linux 3.2之前存在的隱含延遲相反,清除__GFP_FS標(biāo)識(shí)不能避免這個(gè)延遲。這就是為什么使用__GFP_FS或者PF_FSTRANS標(biāo)識(shí)不夠充分的原因。
從歷史中學(xué)習(xí)
這個(gè)問題是類似于被介紹中提到的那個(gè)補(bǔ)丁修復(fù)的10前就出現(xiàn)的問題。在那個(gè)場景中,問題是正在弄臟頁面的進(jìn)程將會(huì)慢下來直到一定量的臟頁被寫出。當(dāng)這發(fā)生的時(shí)候,nfsd最終被阻塞直到它寫出一些頁面,因此導(dǎo)致死鎖。在我們當(dāng)前的場景中,延遲發(fā)生是當(dāng)回收內(nèi)存的時(shí)候而不是弄臟內(nèi)存,并且延遲有100ms的上限,除此之外,它是一個(gè)類似的問題。
解決方案是添加一個(gè)每進(jìn)程標(biāo)識(shí) PF_LESS_THROTTLE, 它僅為nfsd線程設(shè)置。在這門限的時(shí)候,進(jìn)程將會(huì)慢下來,這個(gè)標(biāo)識(shí)將增加門限,因此解決了死鎖。在那個(gè)補(bǔ)丁中,我們能看到2個(gè)重要的想法:使用一個(gè)每進(jìn)程標(biāo)識(shí),和不要完全刪除限流,僅增大門限來避免死鎖。當(dāng)弄臟頁面的時(shí)候nfsd根本不限流,它將導(dǎo)致其他問題。
一個(gè)去,一個(gè)留
隨著在控制下的livelock,不僅對(duì)于回環(huán)NFS掛載,而且同樣潛在的對(duì)于loop塊設(shè)備,我們僅需要處理一個(gè)遺留的死鎖。當(dāng)我們發(fā)現(xiàn)第一個(gè)問題的時(shí)候,實(shí)際要求的改變是相當(dāng)小的。下周將發(fā)出的對(duì)這個(gè)改變的理解和解釋將是更加本質(zhì)的。
總結(jié)
以上是生活随笔為你收集整理的阿里内核月报2014年4月的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET把DataTable转成X
- 下一篇: IE 市场份额暴跌,Edge 能否守住微