天天看點

Linux核心軟RPS實作網絡接收軟中斷的負載均衡分發例行的Linux軟中斷分發機制與問題基于RPS的軟中斷分發優化我對原生RPS代碼的修改NET RX軟中斷負載均衡總體架構CPU親和接力優化為什麼我不分析代碼實作例行牢騷與感歎

Linux的中斷分為上下兩半部,一般而言(事實确實也是如此),被中斷的CPU執行中斷處理函 數,并在在本CPU上觸發軟中斷(下半部),等硬中斷處理傳回後,軟中斷随即開中斷在本CPU運作,或者wake up本CPU上的軟中斷核心線程來處理在硬中斷中pending的軟中斷。

       換句話說,Linux和同一個中斷向量相關的中斷上半部和軟中斷都是在同一個CPU上執行的,這個可以通過raise_softirq這個接口看出來。這 種設計的邏輯是正确的,但是在某些不甚智能的硬體前提下,它工作得并不好。核心沒有辦法去控制軟中斷的分發,是以也就隻能對硬中斷的發射聽之任之。這個分 為兩類情況:

按照上述邏輯,如果系統存在多個CPU核心,那麼隻能由一個CPU處理軟中斷了,這顯然會造成系統負載在各個CPU間不均衡。

注 意”盲目“一詞。這個是和主機闆以及總線相關的,和中斷源關系并不大。是以具體中斷哪個CPU和中斷源的業務邏輯也無關聯,比如主機闆和中斷控制器并不是理會 網卡的資料包内容,更不會根據資料包的元資訊中斷不同的CPU...即,中斷源對中斷哪個CPU這件事可以控制的東西幾乎沒有。為什麼必須是中斷源呢?因 此隻有它知道自己的業務邏輯,這又是一個端到端的設計方案問題。

       是以,Linux關于軟中斷的排程,缺乏了一點可以控制的邏輯,少了一點靈活性,完全就是靠着硬體中斷源中斷的CPU來,而這方面,硬體中斷源由于被中斷控制器和總線與CPU隔離了,它們之間的配合并不好。是以,需要加一個軟中斷排程層來解決這個問題。

       本文描述的并不是針對以上問題的一個通用的方案,因為它隻是針對為網絡資料包處理的,并且RPS在被google的人設計之初,其設計是高度定制化的,目的很單一,就是提高Linux伺服器的性能。而我,将這個思路移植到了提高Linux路由器的性能上。

上半部:用于skb在不同的CPU間分發。

下半部:使用者skb的實際協定棧接收處理。

事實上,利用Linux 2.6.35以後加入的RPS的思想可能會有更好的做法,根本不用重新分割網絡接收軟中斷。它基于以下的事實:

如果網卡很高端,那麼它一定支援硬體多隊列特性以及多中斷vector,這樣的話,就可以直接綁定一個隊列的中斷到一個CPU核心,無需軟中斷重分發skb。

如 果網卡很低檔,比如它不支援多隊列,也不支援多個中斷vector,且無法對中斷進行負載均衡,那麼也無需讓軟中斷來分發,直接要驅動裡面分發豈不更好 (其實這樣做真的不好)?事實上,即便支援單一中斷vector的CPU間負載均衡,最好也要禁掉它,因為它會破壞CPU cache的親和力。

中斷中不能進行複雜耗時操作,不能由複雜計算。中斷處理函數是裝置相關的,一般不由架構來負責,而是由驅動程式自己負責。協定棧主架構隻維護一個接口集,而驅動程式可以調用接口集内的API。你能保證驅動的編寫人員可以正确利用RPS而不是誤用它嗎?

       正确的做法就是将這一切機制隐藏起來,外部僅僅提供一套配置,你(驅動編寫人員)可以開啟它,關閉它,至于它怎麼工作的,你不用關心。

       是以,最終的方案還是跟我最初的一樣,看來RPS也是這麼個思路。修改軟中斷路徑中NAPI poll回調!然而poll回調也是驅動維護的,是以就在資料包資料的公共路徑上挂接一個HOOK,來負責RPS的處理。

答案似乎很簡單,答案是:因為我們自己用軟體可以做得更好!而基于簡單硬體的單純且愚蠢的盲目中斷負載均衡可能會(幾乎一定會)弄巧成拙!

       這是為什麼?因為簡單低端網卡硬體不識别網絡流,即它隻能識别到這是一個資料包,而不能識别到資料包的元組資訊。如果一個資料流的第一個資料包被分發到了 CPU1,而第二個資料包分發到了CPU2,那麼對于流的公共資料,比如nf_conntrack中記錄的東西,CPU cache的使用率就會比較低,cache抖動會比較厲害。對于TCP流而言,可能還會因為TCP串行包并行處理的延遲不确定性導緻資料包亂序。是以最直 接的想法就是将屬于一個流的所有資料包分發了一個CPU上。

要知道,Linux的RPS特性是google人員引入的,他們的目标在于提升伺服器的處理效率。是以他們着重考慮了以下的資訊:

哪個CPU在為這個資料流提供服務;

哪個CPU被接收了該流資料包的網卡所中斷;

哪個CPU運作處理該流資料包的軟中斷。

理 想情況,為了達到CPU cache的高效利用,上面的三個CPU應該是同一個CPU。而原生RPS實作就是這個目的。當然,為了這個目的,核心中不得不維護一個”流表“,裡面記 錄了上面三類CPU資訊。這個流表并不是真正的基于元組的流表,而是僅僅記錄上述CPU資訊的表。

       而我的需求則不同,我側重資料轉發而不是本地處理。是以我的着重看的是:

其實我并不看中哪個CPU排程發送資料包,發送線程隻是從VOQ中排程一個skb,然後發送,它并不處理資料包,甚至都不會去通路資料包的内容(包括協定頭),是以cache的使用率方面并不是發送線程首要考慮的。

       是以相對于Linux作為伺服器時關注哪個CPU為資料包所在的流提供服務,Linux作為路由器時哪個CPU資料發送邏輯可以忽略(雖然它也可以通過設 置二級緩存接力[最後講]來優化一點)。Linux作為路由器,所有的資料一定要快,一定盡可能簡單,因為它沒有Linux作為伺服器運作時伺服器處理的 固有延遲-查詢資料庫,業務邏輯處理等,而這個服務處理的固有延遲相對網絡處理處理延遲而言,要大得多,是以作為伺服器而言,網絡協定棧處理并不是瓶頸。伺服器是什麼?伺服器是資料包的終點,在此,協定棧隻是一個入口,一個基礎設施。

       在作為路由器運作時,網絡協定棧處理延遲是唯一的延遲,是以要優化它!路由器是什麼?路由器不是資料包的終點,路由器是資料包不得不經過,但是要盡可能快速離開的地方!

       是以我并沒有直接采用RPS的原生做法,而是将hash計算簡化了,并且不再維護任何狀态資訊,隻是計算一個hash:

[my_hash隻要将資訊足夠平均地進行散列即可!]

僅此而已。于是get_rps_cpu中就可以僅有上面的一句即可。

       這裡有一個複雜性要考慮,如果收到一個IP分片,且不是第一個,那麼就取不到四層資訊,因為可能會将它們和片頭分發到不同的CPU處理,在IP層需要重組的時候,就會涉及到CPU之間的資料互訪和同步問題,這個問題目前暫不考慮。

本節給出一個總體的架構,網卡很低端,假設如下:

不支援多隊列;

不支援中斷負載均衡;

隻會中斷CPU0。

它的架構如下圖所示:

<a href="http://s3.51cto.com/wyfs02/M00/6F/26/wKiom1WTGuny9zreAATV4uw5WU8115.jpg" target="_blank"></a>

本 節稍微提一點關于輸出處理線程的事,由于輸出處理線程邏輯比較簡單,就是執行排程政策然後有網卡發送skb,它并不會頻繁touch資料包(請注意,由于 采用了VOQ,資料包在放入VOQ的時候,它的二層資訊就已經封裝好了,部分可以采用分散/聚集IO的方式,如果不支援,隻能memcpy了...),因 此CPU cache對它的意義沒有對接收已經協定棧處理線程的大。然而不管怎樣,它還是要touch這個skb一次的,為了發送它,并且它還要touch輸入網卡 或者自己的VOQ,是以CPU cache如果與之親和,勢必會更好。

       為了不讓流水線單獨的處理過長,造成延遲增加,我傾向于将輸出邏輯放在一個單獨的線程中,如果CPU核心夠用,我還是傾向于将其綁在一個核心上,最好不要綁在和輸入處理的核心同一個上。那麼綁在哪個或者哪些上好呢?

       我傾向于共享二級cache或者三級cache的CPU兩個核心分别負責網絡接收處理和網絡發送排程處理。這就形成了一種輸入輸出的本地接力。按照主機闆構造和一般的CPU核心封裝,可以用下圖所示的建議:

第一,基于這樣的事實,我并沒有完全使用RPS的原生實作,而是對它進行了一些修正,我并沒有進行複雜的hash運算,我放寬了一些限制,目的是使得計算更加迅速,無狀态的東西根本不需要維護!

第二,我發現我逐漸看不懂我以前寫的代碼分析了,同時也很難看明白大批批的代碼分析的書,我覺得很難找到對應的版本和更新檔,但是基本思想卻是完全一樣的。是以我比較傾向于整理出事件被處理的流程,而不是單純的去分析代碼。

聲明:本文是針對底端通用裝置的最後補償,如果有硬體結合的方案,自然要忽略本文的做法。

小 小生病發燒中,公司裡一大堆積壓的事情,老婆公司最近老是開會!我本将心向明月,奈何明月照溝渠!我晚上會幹什麼?很多人會覺得我很累,晚上一定會美美睡 上一覺!不!沒有,我晚上在通信,在DIY,在debug!在看書,在UNIX,在Cisco!在古羅馬,古希臘!因為這是我唯一屬于自己的時間!隻要外 面下着雨,越大越好,我就可以持續4天不睡覺不吃飯,中間補一頓簡單的,有水即可,保持嘴巴裡進入的有機物最小化!我想說,任何哪家公司的哥們兒跟我比加 班,敢死磕麼?包括華為的!

       這不是憤怒的宣洩,這是一種能力。我記得我在國小五年級發現我原來可以這樣。國中的時候經常這樣,有時是為了求解幾道超級難度的數學題,到了大學,這就成 了常事,學習也好,有時就是打遊戲看片,純粹的玩,跟女朋友聊天幾個小時,她困了睡覺,我等着,等着她醒來繼續。上班以後,換了N家公司,因為加班死磕過 好幾個,一通宵+一天半可否。記得在一家公司每周四例行通宵,這可把我興奮的,特别是大雨天!我讨厭正常的作息,我比較喜歡非周末集體通宵,然後在晚上工 作之後,第二天大家要麼調休,要麼昏昏欲睡,而我望着他們有種虐人的感覺,時間都去哪了,時間就在那,拿到需要門檻!我有能力通過非周末持續通宵把大家所 有人帶入一種惡性循環,但我不會那麼做,因為我是一個善良的人。是以我經常會說,加班是我的專利,而不是你們的,加班對你們而言是一種折磨,當然,沒事在 公司呆着避高峰,躲債躲老婆躲家務,賺報帳費的除外!

       昨晚通宵,外面下着雨!收獲:羅馬/埃特魯裡亞的關系;SONET/SDH成幀标準;本文。其間照顧發燒的小小。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1669593

繼續閱讀