天天看點

阿裡核心月報:2017年05月

本文是2017年4月3日netconf會議報告。主要的議題是:移除ndo_select_queue()函數,對于refcount_t類型引入開關,tc重定向導緻核心陷入循環等。

ndo_select_queue()函數是大家首先讨論的,它被用來決定往網絡接口發送資料包的時間和方式。主要的争議是把它放在驅動裡面是否合适,alexander duyck說intel在考慮用ndo_select_queue()來說收/發包隊列的matching,不過目前也沒真正在用。

無線方面倒是重度使用該函數,不過是用來做traffic分流,比如讓語音資料避開一個best-effort服務的擁擠隊列。不過該函數也不是做這個事情的唯一選擇,在無線協定棧裡面基于fq_codel的流控機制也很有效。

接下來的問題就是怎麼把它移除了,很快我們就不會看到一個通用的ndo_select_queue()接口了。以後這個某個具體協定棧或者某個具體驅動操心的事情了。

refcount_t類型是kernel内部用來防止對象引用計數向上或者向下溢出的資料類型。大家的觀點是這樣的資料類型用于調試是可行的,但是不能預設enable,大家非常确信這樣的資料類型對網絡性能會有非常嚴重的影響,不過目前沒有資料來說明。

第二天關于kasan記憶體問題檢測的讨論中,eric也表達了類似refcount_t類型的觀點。這種檢測方法對一大批問題無能為力,還可能導緻協定棧突破10%記憶體使用限制,是以有人建議檢測方法的開啟關閉應該用可選擇參數控制。用于debug特定問題是ok的,但是一直開啟就影響到網絡性能。

關于可選擇參數大家通常都會預設關閉,以至于這種檢測方法實際不會發揮作用因為不會測試到選擇參數控制的路徑。eric解釋了一通google的測試,最後也承認某些路徑确實無法完全顧及到。

最後總結一下的話,就是謹慎的小夥伴會更喜歡這種提高核心可靠性的方法,不過搞網絡的朋友因為性能方面的考慮不能完全接受。

berg提出有時候使用者需要高性能擷取關心資料的方法,特别是無線應用裡面。雖然已經有相關的機制但是開銷實在太大。miller認為ebpf幹這個很合适。

hannes frederic sowa在會上提了個無傷大雅的問題,即核心如何處理vlan 0?理論上,vlan 0表示沒有vlan。但是核心現在處理vlan 0的方式取決于vlan子產品是否加載,以及vlan 0接口是否建立。是以,有時資料包中的vlan 0标記被直接丢棄,有時又沒有,出現語義不一緻。追溯源頭,vlan 0語義的破壞是由一個patch意外導緻,sowa計劃将恢複vlan 0的原始語義。

核心tc (traffic-control)子系統的維護者jamal hadi salim指出,利用tc redirect特性能夠使核心陷入無限循環。隻要将redirect的目的接口配置成源接口,就能很快重制這個問題。當然啦,隻要簡單的丢棄源接口和目的接口相同的資料包就可避免這種情況。更嚴重的是,當一個資料包從eth0發送到eth1, eth1又迅速的将這個包重定向到eth0。很明顯,隻有root使用者才有權限去刻意制造這種問題場景,管理者的錯,那又怪得了誰呢?

然而,當考慮到實體機上部署了容器的情況,事情就變得不那麼簡單了。一個非授信使用者可能在容器内部擁有root權限,并刻意構造這種問題場景,将會對運作在該伺服器上的其它容器構成dos攻擊。在特定條件下,還會導緻核心死鎖。

salim 發現,之是以會出現這個問題,是因為skb struct中用于标記資料包的兩個bits被砍掉了。這兩個bit是一種簡單的ttl機制,在每次循環中+1,當達到允許的最大限制時資料包被丢棄。正是這小小的ttl避免了死循環的出現。

目前的大趨勢是要精簡skb struct,至于是将這兩個bit重新召回,還是繼續忍受dos威脅,期待後續發展。

在一些場景下,使用者需要從核心中導出大量資料。salim提供了一個真實的tc讀取6m entries的案例。但是,目前的基于netlink機制的api一次隻能讀取20 entries,效率超級低。salim寫了個patch,提高一次讀資料的最大值到 8 x nlmsg_good_size.使性能提高一個數量級。顯然,這隻是一個臨時方案,社群還在尋找一個更通用完美的方案:從核心讀取大量統計資訊的機制,允許在讀取過程中,資料仍然在不斷變化。

本文是2017年4月4日netconf會議報告的第二部分。主要的議題是vrf上套接字的綁定,ebpf程式的辨別,ipv4/ipv6協定間的差別,資料中心硬體的變遷等等。

david ahern帶來的第一個問題是關于如何将套接字綁定到一個指定的接口上。目前有如下四種方法可以完成這項工作:

使用so_bindtodevice選項

使用ip_pktinfo選項

使用3.3版本核心引入的ip_unicast_if标記

使用ipv6範圍id字首

這裡存在的問題是有太多方式可以完成綁定套接字到指定接口這一工作,而對這些方法的修改勢必會破壞abi相容。同時這些方法之間也存在着沖突。比如一個使用者使用了某種方法設定了一個标記,但是這一方式會破壞其他使用者已經完成的工作,同時這一問題核心并不會向使用者态進行彙報。參加會議的開發者同意需要添加一個沖突通知機制來通知使用者這一問題。

接下來ahern提出了另一個問題,如何将套接字綁定到指定的vrf上。比如一個udp多點傳播套接字如何綁定到vrf上。

tom herbert說此前曾經讨論過通過擴充bind(2)系統調用的方式來解決這一問題。即通過bind(2)來将一個udp套接字綁定到一組離散的ip位址或者子網上。他認為這是一個很寬泛的問題,即如何提供更通用的接口。

ahern解釋了目前将套接字綁定到一個vrf從裝置上的方法。同時他提出了一個問題:核心接收到封包後如何選擇套接字。目前針對udp套接字的做法是進行打分。但這一方式并不通用。

david miller指出不同的功能域有不同的方法來完成上述工作。比如vrf層和netns層。此前miller不希望網絡代碼中充斥着各種netns關鍵字,因為在獲得靈活性的同時會帶來很大的性能開銷。他認為不要添加新的key而是要複用已有的架構。herbert則認為應該明确說明這一原因。最後大家的一緻意見是使用ip_unicast_if标記。目前該标記隻針對udp和raw套接字,随後将會被擴充到tcp。

ahern随後讨論的問題是關于bpf的。他舉了個例子,一個經典的bpf程式:對arp封包進行檢查。如果從過濾器讀取程式代碼,使用者會得到一系列難以解析的二進制資料。盡管核心可以輸出成類c風格,但是依然難以解析。

ahern希望對于一個經典bpf程式是否可以恢複到原始的普通文本輸出。比如将0x806替換成常量。即便對于ebpf程式的輸出也可以進行很多的改進。alexei starovoitov是bpf的維護者,他認為合理的做法是給ebpf程式傳回一些映射關系的資訊。更加複雜的資料則留待後續解決。

目前最重要的是debug工具的開發工作,即完成一個逆編譯器将指令重構為可以閱讀的代碼。随後是如何将資料傳回到使用者态。ahern解釋現在使用bpf(2)系統調用僅僅是将資料拷貝到不同的檔案描述符。starovoitov指出目前這樣的行為是比較合理的。

一個類似的問題是如何區分xdp程式(xdp也是通過bpf來編寫的)。miller解釋了使用者希望有一種方法來獲得系統中安裝了哪些xdp程式。目前僅僅隻有一個sha-1辨別符來辨別這些xdp程式,但是僅僅是内部使用的,并沒有暴露給使用者态。starovoitov提到目前有一個布爾值來辨別程式是否加載。

在上述問題的基礎上,如何擷取已經部署的bpf程式的代碼也成為了讨論的問題。但是目前并沒有特别的結論。

主要針對核心中 ipv4/v6 的處理差異提出讨論,看是否有某些層面合并的可能,比如:

loopback 裝置在路由表會産生不可達,vrf 的 loopback 接口目前并沒有做這樣的限制

anycast 路由在 vrf 上面被增加到錯誤的接口,以及jvm 實作自己的路由表

是否有可能合并 fib tree (v4/v6 的路由層核心資料結構),其中還針對是否 v4 應該支援源路由進行讨論

32-bit/64-bit/128-bit 位址優化的問題

譯:基本認同 “目前很多由于協定設計的差異,v4/v6 并不是完全相容,考慮到實作層面,資料結構基本無法合并,但是代碼邏輯在某種層面可以簡化和統一。”

主要是是否可以在驅動加載之前,為裝置傳遞某些選項,比如:有些裝置的 firmware 加載之前就需要預先根據某些配置來完成早期的初始化。 提到了 devlink , 但是實際上 pci bdf 未必能和裝置一一對應,包括是否讓驅動在某種狀态等待,或者一些特殊的腳本加載過程,總之 hack 的程度比較高。

譯:這裡面可能還要涉及到 bios 和早期 option rom 加載的問題,總之,目前并沒有統一的方法,我認為這個應該 hw design 和驅動編寫的時候應該充分考慮到這一點,給自己留好 fallback 的路線,這種事情還是别指望核心了。

主要是接口向後相容性問題經常被打破的問題,這個需要有全局觀,d.m. 應該會更加嚴格的控制這些 patch 的合并,包括是否實際驗證。總之這件事是被強調了,後面會嚴格控制。

譯:這裡并非技術本身,流程,還是流程。

主要是同一個網卡被多個主機共享,fb 已經有在自己的資料中心應用了。

會上提出了很多 concern, 主要包括安全性和穩定性,所謂通路範圍擴大化,當然給了黑客更多的機會,另外是否會引起故障擴大化。

另外,mellanox 和 broadcom 也都有類似的産品,還可以研究下 yosemite platform

譯:這些 host 網絡可以通過 pci-e 的通道互聯之後,某些網絡延遲可以做到更小,一些特殊的計算模型會更快,帶來的肯定就是穩定性和安全的問題,還是設計平衡。還有很多很多讨論了,大家有興趣去 netconf 現場聽聽吧。

2017lsfmm(linux storage filesystem, and memory-management summit)第二個全體會議日,fred knight向與會人員講解了過去一年中,存儲标準方面的進展。過去一年中,傳輸(如:光纖通信、以太網)和scsi協定都沒有太大變化,nvme标準則有一些更新。

在傳輸标準方面,32gb/128gb的光纖通信已經開始支援,64gb/256gb的支援也在開發中。t級别的光纖通信也在規劃中,knight說:如果帶寬可以達到t數量級,那就比較有趣了。同樣以太網也增加了新的帶寬支援(從2.5gb到tb級),同樣也有新的市場斬獲(比如:汽車)。nvme有了新的指令集,同時支援多種連接配接方式可選(如:pcee、基于以太的rdma、infiniband)。

對于scsi, 一個簡化的捆綁狀态指令(test bind)被添加。write atomic指令也被添加進來,這條指令允許寫操作要麼所有資料全部寫入,要麼什麼都不更改。另外,write scatter指令添加了了一個32位元組的變量。knight說:2016lsfmm中讨論的atomic與scatter的組合寫操作沒有後續更新。一些存儲廠商反對該操作,他們認為他們的了解與linux所需要的之間存在一些差距,是以不會對該特性有更多關注。

針對流id(stream ids)的write stream指令已經被添加到标準中。另外增加了background control指令,該指令允許對儲存設備中運作的背景任務(比如垃圾回收)進行一些控制操作。背景任務會對i/o的時間産生影響,是以使用者希望有能力通過改變參數對背景任務進行控制。

一些預定義的特性集合(例如:2010 basic,2016 basic,maintenance,provisioning)已經被确立,儲存設備廣告中可以利用這些名詞,同時主機也可以對這些特性的有效性進行檢查。knight說:新的特性集合也會陸續被添加,這将會是個持續的過程。ted ts'o問:是否裝置可以對外聲稱支援一些特性集合,但實作中支援了更多不屬于該特性集合中的指令?knight回答:裝置廠商可以這麼做。

knight說:過去一年中,scsi最大的一件事情是驅動器縮量(drive depopulation)。驅動器損壞與我們想象中的不一樣,它通常都是隻有一部分壞了。比如說:如果你有一個8tb的驅動器,該驅動器頭部的存儲空間無法工作,你可以簡單的将該驅動器前面的部分關閉,這樣你就會擁有一個6tb大小的可以正常工作的驅動器。

不管scsi還是ata,“損壞重用(repurposing)”都已經支援了,驅動器在縮減容量後可以繼續工作。對于已經存儲在驅動器中的資料,要麼全部丢失,要麼被映射到其他的邏輯塊位址(lba)中。get physical element status指令可以被用于确定哪些部分不能工作,新驅動器容量是多少。remove element and truncate指令被用于關閉不能工作的存儲空間。驅動器可以被重新格式化。如果其他存儲空間再次出現問題,上述流程可以繼續重複進行。

對于讀整個驅動器并嘗試進行資料修複已經在标準中支援了很長時間。有一些指令可以提供接下來的n個邏輯塊位址(lba)是否已經損壞的資訊,這些塊可以在接下來的進行中被跳過。這些特性使得在驅動器部分損壞的情況下,允許主機盡可能的修複更多資料。

對于驅動器縮量(depopulation),去年有一些“資料保留(data preserving)”模式的讨論。這是很複雜的,是以需要更多些時間。knight說:很多人都在問這個特性是否真的需要,是以這個特性也有可能被砍掉。

過去的一年nvme工作組是最忙的。面向光纖的官方規格書已經釋出。“清潔(sanitize)”指令也已經正式支援,該指令可以在将某個驅動器用于其他用途前,清除該驅動器。其他的機制,比如:加密擦除(crypto erase)和塊擦除(block erase),則會清除所有ssd,是以那些裝置不實作這些機制。

一個裝置的crash-dump機制稱之為“telemetry”,已經被添加進規格書中。流id(stream ids)也同樣被加入。一些對persistent reservation的支援也已經添加,但所添加的與scsi對應特性并不相容,這是所不希望的。一個相容版本正在制定中。

虛拟/仿真控制器是添加的另外一個特性。這個特性允許虛拟機認為他們可以擁有自己的專用nvme控制器。每個客戶虛拟機可以獲得一個專用的隊列,這些隊列是由hypervisor配置設定的。這就允許過個客戶虛拟機共享同一個實體nvme擴充卡。

nvme工作組每周的兩小時會議都有大約80人參加,并且t10(scsi)、t11(fibre channel)和t13(ata)委員會每個月都有三天的會議。有一些顧慮說這個組有做不完的事情。他說:nvme組在逐漸加速進度,而其他組則比幾年前更穩定一些。

mathew wilcox問knight如何評價在linux中,儲存設備彙報的所有不同的錯誤都被歸結為eio(i/o error)。knight笑着表示問題在于儲存設備彙報的各種類型的錯誤碼和媒體錯誤(譯者注:裝置本來就不應彙報那麼多不同種類的錯誤)。而martin petersen則指出,posix标準需要将這些錯誤映射為eio,因為應用無法了解其他錯誤的細節。

darrick j. wong是就職于oracle的xfs開發者。他的lsfmm session通常會讨論有大量圍繞xfs的讨論,本次也不例外,抛開各種雜七雜八的小話題不說,本次讨論的主線圍繞xfs reverse mapping和online fsck進行。

首先讓我們看看反向索引,xfs通過名為getfsmap的ioctl接口來實作反向索引查詢,具體來說就是給定一個extent,找出它的屬主到底為何(資料、中繼資料抑或空閑沒有配置設定)

而反向索引和online fsck這兩者之間其實有很強的依賴關系。一般實作一個online fsck時,核心仍然mount着檔案系統,随時随地可能修改它,為保證中繼資料的一緻性,online fsck的主體邏輯通常隻能由核心本身來執行,在執行的過程中與其他正常的檔案操作進行同步。而不是像平時offline fsck那樣純粹由一個使用者态工具來完成。darrick wong表示在onilne fsck的過程中,他不希望依靠盤上已有的中繼資料,因為理論上來說它們全都有風險,不完全可信。darrick wong的計劃是依靠反向索引來逐漸把中繼資料重建立立起來。同時他還可以通過getfsmap + direct io把所有的資料塊讀一遍看看會不會有錯誤。這裡唯一的漏洞在于當依賴反向索引工作的online fsck試圖重建反射索引自身時會出死鎖,即”自指悖論“。darrick wong的解決辦法是在這裡對online fsck和offline fsck取一折衷:當機所有的inodes操作,這使得online fsck頗像一個offline fsck,然後在重建的過程中慢慢逐一解凍它們。

darrick wong遇到的另一個問題是如何對付已經打開的fd。如果允許這些fd繼續操作,他們很可能會感覺到fsck帶來的不一緻。他能想到的一個辦法是摘掉這些檔案對應的page cache頁,把它們的inode_operations替換掉,讓它們基本上什麼都不做直接傳回錯誤。這樣處理過的inode除了被關閉進orphan list就沒别的用處了。

chris mason表示在facebook沒有這類online fsck的需求,我相信絕大多數網際網路公司或者雲計算公司都不會有,一般來說高可靠性應該優先通過replication來保證,單個結點auto-healthing的難度顯然遠大于replication,并且引入了如此之高的複雜度之後可靠性到底是提升了還是下降了都尚且存疑。況且就算這條路可行,在online fsck執行的過程中很可能檔案系統的各種性能都會下降,帶來較差的使用者體驗。在大型系統的設計中,我們更傾向于明确地搞清楚一個子部件的狀态,它要麼被标記成可用,就充分地完全地可用,要麼被标記成不可用,就不會有任務、請求、流量、容器或者虛拟機被排程上來,完全徹底地下線。一個半死不活正在修複自身的元件沒有人會喜歡。darrick wong的看法是這一類特性主要是針對那些甯可犧牲性能,也絕不能允許一個存儲服務,準确地說是一個存儲服務的結點不可用的系統準備的。

從我的角度來看,如果存在這樣一個通過期待本地檔案系統永遠不出錯來實作高可用的系統,那麼它可以被稱為“根本不具備高可用性”的系統,或者“錯誤設計的高可用系統”。

在2017 linux storage, filesystem, and memory management大會上,jeff layton說目前linux對writeback的錯誤處理有點混亂。他與其他參會者讨論,并提出了一種方案用于更好地處理writeback error。writeback是指先寫入緩存中,然後再落到檔案系統上。

最初是他在檢視ceph檔案系統時出現的enospc(out of space)錯誤。他發現pg_error(這個頁flag用于标記writeback出錯)會覆寫其他error 狀态,進而造成eio (io error)。這啟發了他去了解其他檔案系統是如何處理這種錯誤。最後他發現目前其他檔案系統對此并沒有一緻的解決方案。dmitry monakhov認為這是因為writeback的時機太晚了,而導緻enospc。但是layton說這個錯誤也會以其他方式觸發。

layton說,如果在writeback時出錯,這個錯誤應該在使用者态調用close()或fsync()時傳回告訴使用者。這些錯誤應該用struct address_space中的as_eio和as_enospc跟蹤。同時也被page level的pg_error跟蹤。

他也說,一個不小心的sync操作可能清除error flag,然而這些錯誤并沒有傳回到使用者态。pg_error也用來跟蹤和報告讀錯誤,是以讀寫混合可能會在flag報告前丢失。另外還有一個問題,當發生wirteback error時,page做了什麼。目前,page被留在了page cache中,并标記了clean和up-to-date,是以使用者态并不知道發生了錯誤。

是以,layton問到,遇到這種情況怎麼辦。james bottomley也問到,檔案系統想以什麼樣的細粒度來track error。layton講到address_space是個合适的level。jan kara指出pg_error本打算用于讀錯誤。但layton說到一些writeback的機制也用它。

layton建議,清理tree來去掉pg_error對于writeback errors。這樣的話就會透過tree來看writeback上的error會不會傳播到使用者空間,或者它們是否可能被錯誤地清除。ted ts'o 講到,可能有必要在不發生任何錯誤下writeback,因為這些錯誤不會傳回到使用者空間上。

bottomley講到,他更不願标記page clean,如果這些page還沒有寫入磁盤上的話。這些錯誤資訊有必要被跟蹤,以扇區為機關。是以block layer可以告訴檔案系統bio錯誤發生在哪裡。chris mason建議那些做“redoing the radix tree”的人可能想提供一種方法來存儲發生在檔案上的錯誤。這樣的話,錯誤就可以被報告出來,一旦報告,即可清除掉。

layton提出一個想法。可以在address_space結構上加上writeback error counter和writeback error code fields,同時在struct file上加上writeback error counter。當有writeback error發生時,address space的writeback error counter就會自增,并記錄下error code。當fsync()或close(),這些錯誤就會被報告出來 ,不過隻在file結構裡的wirteback error counter小于address space裡的couter。這樣的話,address_space裡的counter将會被拷貝到file結構中。

monakhov問到,為何需要counter,layton解釋到,這樣可以更高效處理multiple overlapping error,不管是在writeback出錯在file打開時還是在最後fasync()。ts'o 同意了這個說法。那些需要更多資訊的應用應該使用o_direct。而對于大多數應用程式,知道一個錯誤發生在檔案哪裡是必要的;所有的應用程式需要更好的粒度已經在使用o_direct。

layton's的方法将會出現一些false positive,但是不會出現false negatives。避免false positives可能是個比較大的目标,但提出的這個方法更簡單些,副作用也少。layton說到這種方法将會提高使用者空間的表現。

ts’o 指出現在處理enomem(out of memory)上有一些方法。如果writeback内部的error傳回的話,layton描述的一些問題也會發生。是以,一些檔案系統并不會傳回enomem錯誤,為了避免,他們不得不使用鎖。ts’o 已經有一些patch來推遲writeback,讓pages處于髒狀态,而不是獲得鎖,來阻止oom kills。

但是layton認為,首要的應該是想辦法來處理wirteback error。檔案系統現在避免這個問題,是因為他們現在處理不好。更好的處理temporaay error方法應該加進來。另一件需要做的事是當進行wirteback時,應該擁有更多的killable或可中斷的調用。

另外,laytony講到,當發生writeback error,page應該做哪些。目前,是将其置為clean。如果發生一個hard error(will never be retries),難道這個頁就無效了嗎?ts’o講到這些頁不能一直為髒狀态,因為如果有大量writeback error,系統将會oom。

整個kernel對于多隊列塊裝置的支援一直存在一個短闆,那就是面向多隊列裝置的io排程器。而4.12開發周期,同時引入了兩個新的支援多隊列的io排程器。

對于多隊裝置的支援,缺乏io排程器看上去是緻命的,但事實卻是,最初對于是否需要一個排程器并不明确。高端固态裝置并不存在旋轉延遲(rotational delay,指普通磁盤中,從磁盤上讀取資料的過程中,需要磁盤旋轉所産生的延遲)問題,是以固态裝置對操作的順序(ordering)并不敏感。但io排程即使對于最快速的裝置也是有價值的。排程器可以合并相鄰的塊通路,減少操作的次數,并且可以對不同操作進行優先級處理。是以雖然對引入一個針對多隊列裝置的io排程器期待了很久,但一直沒有相應的實作。

第一個支援多隊列io排程的是bfq(budget fair queing)排程。這個排程器對低速裝置有更好的io響應,适合用于手機等裝置中。

bfq是在cfq基礎上進行了改善,但将其合并仍然是一個漫長的過程。最主要的障礙就是:他是個傳統io排程器,并非面向多隊列排程的。塊子系統的開發者們有一個長期的目标,就是将所有驅動更改為多隊列模型。是以,合并一個對多隊列io排程提升有限的排程器,并不合适。

過去幾個月,bfq的開發者paolo valent将其代碼移植到了多隊列接口上。已知的問題都已經被解決。按照計劃,bfq将會出現在4.12中。

bfq是一個比較複雜的排程器,是為了提供好的互動響應而設計,尤其對于低速裝置。但對于io操作較快,吞吐量作為最主要的問題的情況下,這種複雜度變的沒有意義。固态裝置需要一個更為簡單的排程器。

kyber io排程器正是這個定位。它隻有1000行的代碼,比bfq簡單很多。io請求進入kyber中後,将被分為以讀為主的同步隊列和已寫為主的異步隊列。讀寫在應用中的行為特性使得讀的優先級高于寫,同時寫操作也不能一直處于饑餓狀态。

kyber排程器會将所有請求送到分發隊列中,然後測量每個請求的完成時間,根據完成時間調整隊列的限制,以達到配置所需要的延時。

kyber也已經被4.12的合并視窗接受。根據計劃,4.12核心将同時釋出兩個新的針對多隊列裝置的io排程器。使用者關注響應或者應用了低速裝置,可以選擇bfq。如果使用者對吞吐更敏感,則可以選擇kyber。

繼續閱讀