摘要:數據中心網絡協議棧正在轉向硬件,以在低延遲和低CPU利用率的情況下實現100 Gbps甚至更高的數據速率。但是,NIC中網絡協議棧的硬連線方式扼殺了傳輸協議的創新。本文通過設計Tonic(一種用于傳輸邏輯的靈活硬件架構)來實現高速網卡中的可編程傳輸協議。在100Gbps的速率下,傳輸協議必須每隔幾納秒在NIC上僅使用每個流狀態的幾千比特生成一個數據段。通過識別跨不同傳輸協議的傳輸邏輯的通用模式,我們為傳輸邏輯設計了一個高效的硬件“模板”,該模板在使用簡單的API編程的同時可以滿足這些約束?;?a target="_blank">FPGA的原型系統實驗表明,Tonic能夠支持多種協議的傳輸邏輯,并能滿足100Gbps背靠背128字節數據包的時序要求。也就是說,每隔10 ns,我們的原型就會為下游DMA流水線的一千多個活動流中的一個生成一個數據段的地址,以便獲取和傳輸數據包。
1. 介紹
傳輸協議以及網絡協議棧的其余部分傳統上都在軟件中運行。盡管軟件網絡協議棧一直在努力提高其性能和效率,但為了跟上當今數據中心的應用程序,軟件網絡協議棧往往會消耗30-40%的CPU周期。
隨著數據中心轉移到100 Gbps以太網上,軟件網絡協議棧的CPU利用率變得越來越高。因此,多個供應商開發了完全在網卡(NIC)上運行的硬件網絡協議棧。但是,在這些NIC上僅實現了兩種主要的傳輸協議,它們都是硬連線方式并且只能由供應商修改。
RoCE. RoCE用于遠程直接內存訪問(RDMA),使用DCQCN[43]進行擁塞控制,并使用簡單的go-back-N策略進行可靠數據傳輸。
TCP. 一些供應商將他們選擇的TCP變體卸載到NIC,以便直接通過套接字API(TCP卸載引擎)使用或啟用RDMA(iWARP)。
然而,在過去幾十年中提出的用于可靠傳輸和擁塞控制的無數可能算法中,這些協議僅使用了一小部分。例如,最近的研究表明,低延遲數據中心網絡可以顯著受益于接收端驅動的傳輸協議,這并不是當今硬件堆棧的選擇。在微軟數據中心部署RoCE網卡的嘗試中,運營商需要修改數據傳輸算法以避免網絡中出現活鎖,但必須依賴NIC供應商進行更改。已經提出了其他算法來改進RoCE的簡單可靠傳輸算法。多年來,TCP在各種網絡中的優化列表證明了傳輸協議對可編程性的需求。
在本文中,我們研究如何實現硬件傳輸協議可編程化。即使NIC供應商開放了硬件編程接口,在高速硬件中實現傳輸協議也需要大量的專業知識、時間和精力。為了跟上100Gbps的速度,傳輸協議應該每隔幾納秒生成并傳輸一個數據包。它需要能夠處理超過1000個活動流,這在今天的數據中心服務器中是很普遍的。然而,NIC在其片上內存和計算資源的數量方面受到極大的限制。
我們認為,高速網卡上的傳輸協議可以通過編程實現,而不需要讓用戶接觸高速硬件編程的全部復雜性。我們的論點主要基于兩個因素:
首先,可編程傳輸邏輯是實現靈活硬件傳輸協議的關鍵。傳輸協議的實現完成了多種功能,例如連接管理、數據緩沖區管理和數據傳輸。然而,它的核心任務是決定傳輸哪些數據段(數據傳輸)和何時傳輸(擁塞控制),統稱為傳輸邏輯,這也是大多數創新的地方。因此,在高速NIC上實現可編程傳輸協議的關鍵是使用戶能夠修改傳輸邏輯。
其次,可以利用傳輸邏輯中的通用模式來創建可重用的高速硬件模塊。盡管它們在應用級API(例如,TCP的套接字和字節流抽象與RDMA的基于消息的謂詞API)以及連接和數據緩沖區管理方面存在差異,但傳輸協議有幾種共同的模式。例如,不同的傳輸協議使用不同的算法來檢測丟失的數據包。但是,一旦數據包被宣布丟失,可靠傳輸協議就會將其重傳而優先于發送一個新的數據段。另一個例子,在擁塞控制中,給定由控制環路確定的參數(例如,擁塞窗口和速率),只有幾種常見的方法來計算流在任何時候可以傳輸多少字節。這使我們能夠為硬件中的傳輸邏輯設計一個高效的“模板”,可以使用簡單的API對其編程。
基于這些觀點,我們設計并開發了Tonic,這是一種可編程的硬件架構,可以使用簡單的API來實現各種傳輸協議的傳輸邏輯,同時支持100Gbps的數據速率。每個時鐘周期,Tonic都會生成下一個數據段的地址進行傳輸。數據段由下游DMA流水線從內存中提取,并由硬件網絡協議棧的其余部分轉換為一個完整的數據包(圖1)。
我們認為Tonic將駐留在NIC上,取代傳輸協議硬件實現中的硬編碼傳輸邏輯(例如,未來的RDMA NIC和TCP卸載引擎)。Tonic為傳輸邏輯提供了一個統一的可編程架構,與不同傳輸協議的具體實現如何執行連接和數據緩沖區管理以及它們的應用層API無關。然而,我們將以Socket API為例,描述Tonic如何與傳輸層的其余部分交互(§2),以及如何將其集成到Linux內核中以與應用程序進行交互(§5)。
我們在約8000行的Verilog代碼中實現了Tonic原型,并在不到2 0 0行代碼中實現各種傳輸協議的傳輸邏輯,從而證明了Tonic的可編程性。我們還使用FPGA展示了Tonic滿足~100Mpps的時序,即支持100Gbps的背靠背128B數據包。也就是說,每隔10 ns,Tonic可以生成下游DMA流水線獲取和發送一個數據包所需的傳輸元數據。從生成到傳輸,單個段地址通過Tonic的延遲約為0.1μs,Tonic最多可支持2048個并發流。
2. onic作為傳輸邏輯
本節概述了Tonic如何適應傳輸層(§2.1),以及如何克服在高速NIC上實現傳輸邏輯的挑戰(§2.2)。
>2.1 Tonic如何適應傳輸層
位于應用程序和堆棧其余部分之間的傳輸層協議執行兩個主要功能:
連接管理:連接管理包括創建和配置端點(例如,TCP的套接字和RDMA的隊列對),并在開始時建立連接,在結束時關閉連接并釋放其資源。
數據傳輸:數據傳輸涉及以段流的形式可靠而高效地將數據從一個端點傳輸到另一個端點1。不同的傳輸協議為應用程序請求數據傳輸提供了不同的API:TCP提供了字節流的抽象,應用程序可以連續向該字節流追加數據,而在RDMA中,對隊列對的每個“send”調用都會創建單獨的工作請求,并被視為單獨的消息。此外,在不同的傳輸協議實現中,管理應用程序的數據緩沖區的具體情況也有所不同。無論如何,傳輸協議必須將未完成的數據以適合單個數據包的多個數據段形式傳輸到目的地。確定哪些字節構成下一個數據段以及何時傳輸它是通過數據傳輸和擁塞控制算法來完成的,我們統稱為傳輸邏輯并在Tonic中實現。
圖1顯示了Tonic如何適合硬件網絡協議棧的高級概述。為了將Tonic與連接管理和應用級API的細節分離,連接設置和拆卸需要在Tonic之外運行。Tonic依靠傳輸層的其余部分為每個已建立的連接提供唯一的標識符(流id),并使用這些ID顯式地添加和刪除連接。
對于發送端的數據傳輸,Tonic跟蹤未完成的字節數和特定于傳輸的元數據以實現傳輸邏輯,即在擁塞控制算法指定的時間為每個流生成下一個數據段的地址。因此,Tonic不需要存儲和/或處理實際的數據字節;它依靠傳輸層的其余部分來管理主機上的數據緩沖區,并在現有連接上有新的數據傳輸請求時通知它(有關詳細信息,請參閱§5)。
傳輸邏輯的接收端主要涉及生成控制信號,如確認,每個數據包的授權令牌或周期性擁塞通知數據包(CNP),而傳輸層的其余部分則管理接收數據緩沖區并將接收的數據傳輸給應用程序。雖然處理接收到的數據可能會相當復雜,但在接收端上生成控制信號通常比發送端上更簡單。因此,雖然我們主要關注發送端,但我們重用發送端的模塊來實現接收端,僅用于生成每個數據包的累積和選擇性ack,并以線速授予令牌。
>2.2 硬件設計挑戰
由于兩個主要限制,在NIC中以線速實現傳輸邏輯極具挑戰性:
時序限制。數據中心的數據包大小中值小于200字節。要使這些小數據包達到100Gbps,網卡必須每10 ns發送一個數據包。因此,每隔10 ns,傳輸邏輯應該確定接下來由哪個活動流來傳輸哪個數據段。為了做出該決定,它需要使用每個流的一些狀態(例如,已確認的數據段、重復的ack、速率/窗口大小等)。當各種傳輸事件發生時(例如,接收確認或超時),更新這些狀態。這些更新可能涉及不可忽略的硬件開銷操作,例如搜索位圖和數組。
為了在處理每個事件時有更多的時間,同時仍然每隔~10 ns確定下一個據段,我們可以設想將傳輸事件的處理流水線化到跨多個階段。當傳入事件來自不同的流時,因為它們更新不同的狀態使得流水線更容易處理。處理相同流的背靠背事件(例如,在接收確認的同時生成數據段)需要更新到相同的狀態,這使得在確保狀態一致性的同時流水線事件處理變得困難。因此,我們努力在10ns內處理每個傳輸事件,而不是快速合并下一個事件的狀態,以防它影響相同的流。
內存限制:一個典型的數據中心服務器有超過1000個并發活動流,其中包含數千字節的動態數據。由于NIC只有幾兆字節的高速內存,因此傳輸協議在NIC上的每個流只能存儲幾千字節的狀態。
Tonic的目標是滿足這些嚴格的時序和內存限制,同時通過簡單的API支持可編程性。為此,我們確定了各種協議中傳輸邏輯的通用模式,并將這些模式實現為可重用的固定功能模塊。這些模式允許我們針對時序和內存優化這些模塊,同時通過減少用戶必須指定的功能來簡化API編程。這些模式在表1中進行了總結,并將在下一節中詳細討論,在那里我們將描述Tonic的組件以及這些模式如何影響它們的設計。
3. Tonic架構
發送端的傳輸邏輯決定了每個流要傳輸哪些數據段(數據傳遞)和何時傳輸(擁塞控制)。從概念上講,擁塞控制算法執行credit management,即確定給定流一次可以傳輸多少字節。數據傳輸算法執行segment selection,即確定特定流應該傳輸哪個連續的字節序列。盡管術語“數據傳輸”和“擁塞控制”通常與基于TCP的傳輸協議相關聯,但Tonic為傳輸邏輯提供了一種通用的可編程架構,也可用于其他類型的傳輸協議,例如接收端驅動和基于RDMA的[8]傳輸協議。
Tonic利用data delivery和credit management之間天然的功能分離,將它們劃分為具有獨立狀態的兩個組件(圖2)。data delivery引擎處理與數據段的生成、跟蹤和傳輸相關的事件,而credit引擎處理與調整每個流的信用并為具有足夠信用的流發送段地址相關的事件。
以兩個引擎之間的輕量級協調為代價,這種劃分方式幫助Tonic在每個周期同時處理多個事件(例如,接收確認和段傳輸)的同時滿足其時序限制。這些事件必須讀取其相應流的當前狀態,對其進行更新,并將其寫回內存以便在下一周期中處理事件。然而,在每個周期中對內存的并發讀寫成本很高。與使用寬內存來服務所有傳輸事件不同,分區允許data delivery和credit engines使用更窄的內存來服務于與其特定功能相關的事件,從而滿足時間限制。
在本節中,我們將在§3.1中介紹引擎如何協調,在保持輸出鏈路利用率的同時,公平有效地從每個周期的幾千個流中挑選一個流進行分段傳輸。接下來,§3.2和§3.3描述了每個引擎中的固定功能和可編程事件處理模塊,以及它們的設計是如何從表1中的模式中獲得靈感的。我們在§3.4中介紹了Tonic在一個循環中接收到同一流的多個事件時解決沖突的解決方案,并在§3.5中介紹了它的編程接口。
>3.1 高效的流調度
在任何時候,如果它(1)有足夠的信用并且(2)有新的或丟失的數據段要發送時,一個流只能傳輸一個數據段。為了節省工作量,Tonic必須跟蹤符合傳輸條件的流集合(滿足上述兩個標準),并且在每個周期選擇要傳輸的流時僅在這些流中進行選擇。要有效地做到這一點很有挑戰性。我們有超過1000多個流,它們的狀態分布在兩個引擎上:只有信用引擎知道每個流有多少信用,只有數據傳輸引擎知道流的段的狀態,并且可以生成它的下一個段的地址。我們不能通過在每個循環中檢查兩個引擎的所有流的狀態來找到在該循環中符合傳輸條件的流。
相反,我們將段地址的生成與它們最終傳輸到DMA流水線的過程解耦。我們允許數據傳遞引擎為一個流生成最多N個段地址,而不必有足夠的信用將它們發送出去。在信用引擎中,我們為每個流保留一個大小為N的環形緩沖區來存儲這些未完成的段地址。當流有足夠的信用來發送一個段時,信用引擎從緩沖區退出隊列并輸出一個段地址,并向數據傳輸引擎發送信號以減少該流中未完成段的數量。
這解決了兩個引擎之間的分區狀態問題。數據傳輸引擎不需要跟蹤流的信用變化來生成段地址。僅當段地址從緩沖區出列時才需要通知它。此外,信用引擎不需要知道所有流段的確切狀態。如果流的環形緩沖區為空,則該流沒有要發送的段。否則,當流有足夠的信用時,已經有可以輸出的段地址。
不過,數據傳輸引擎不能簡單地在每個周期檢查所有流的狀態,以確定哪些流可以生成段。相反,我們在數據傳輸引擎中動態維護活動流集,即該流至少要有一個要生成的段且未完成段少于N個 (參見圖2中的紅色編號圓圈)。當創建一個流時,會將其添加到活動集中。每個周期,從集合中選擇并移除一個流以用于段生成(步驟1)。一旦被處理(步驟2),只有當它有更多的數據段要發送并且少于N個未完成的數據段時,它才會被插回集合中(步驟3)。否則,如果稍后接收到來自信用引擎的ack或信號激活了流程,則將其插入到集合中(步驟9)。此外,生成的段地址被轉發到信用引擎(步驟4),用于插入環形緩沖區(步驟5)。
類似地,信用引擎維護準備傳輸流的集合,即在其環形緩沖區中具有一個或多個段地址的流,并且有足夠的信用將至少一個段發送出去。每個周期,從集合中選擇一個流(步驟6),發送來自其環形緩沖區中的一個段地址(步驟7),減少其信用,并且如果它有更多的段地址和信用用于進一步傳輸,則將其插回集合中(步驟8)。它還向數據傳輸引擎發送關于傳輸的信號(步驟9),以減少該流中未完成段的數量。
為了公平起見,當從活動(或準備傳輸)集合中挑選流時,Tonic使用FIFO在集合中的流之間實現循環調度。循環調度的選擇不是最基本的;任何其他滿足我們的時序約束的調度器都可以取代FIFO來支持其他調度規則。
>3.2 靈活的段選擇
對于B字節的信用,一個流可以發送S = max(B,MSS)字節,其中MSS是最大段的大小。在傳輸協議中,數據傳輸算法使用確認來跟蹤數據每個字節的狀態(例如,已傳輸、丟失、正在傳輸和未傳輸),并使用該狀態來決定下一步傳輸哪個連續的S字節數據。
然而,在高速NIC中實現數據傳輸算法有兩個主要挑戰。首先,由于內存限制,NIC無法存儲每個字節的信息。其次,除了少數例外,這些算法是為軟件設計的,在軟件中,它們可以存儲并自由循環使用大量元數據來聚合信息。這種計算靈活性在這些算法中創造了顯著的多樣性。不過由于NIC硬件比軟件更受約束,因此我們并不打算支持所有的數據傳輸算法,而是在尋找能夠在各種算法中通用,同時也適合硬件實現的模式。
· 預先計算的固定段邊界
數據傳輸算法可以從數據流中的任何位置選擇要發送的下一個字節,并生成具有可變邊界的段。但是,由于NIC無法保持每個字節狀態,因此當流請求傳輸新數據時,Tonic要求將數據劃分為固定大小的段(通過內核模塊或驅動程序,參見§5)。這樣,數據傳輸算法可以使用分段信息來選擇下一段。
注意,可以根據每個流的吞吐量和延遲需求為每個流配置固定的段大小。對于基于消息的傳輸協議(例如RoCEv2),具有固定的段邊界自然適合;消息長度是已知的,可以從一開始就選擇最佳的段大小。對于具有字節流抽象的協議(例如TCP和NDP),固定段大小應在數據添加到流中時即時確定。對于高帶寬數據流,可以將其設置為MSS(如果使用TSO[18],則設置為更大)。對于偶爾生成小數據段的流,可以將段大小設置為較小的值,這取決于是在通知Tonic之前將多個小段合并為一個較大的段,還是立即傳輸小段(§5)。無論如何,為了避免在NIC上存儲每個字節的狀態,段大小應該在Tonic之外決定,并且不經常更改。
· 有限窗口中每小段狀態
不同于流的可用信用,如果一個新段距離第一個未確認的段超過K個段,數據傳輸算法通常不會傳輸該新段,以限制發送端和接收端需要保持的狀態2。但是,在具有10μs RTT的100 Gbps網絡中,K可以達到約128個段。幸運的是,我們觀察到對于大多數的數據傳輸算法來說存儲以下每段狀態就足夠了:(1)段是否已確認(存在選擇性確認)?(2) 如果沒有,它是丟失了還是還在傳輸中?(3) 如果丟失,是否已重新傳輸(避免冗余重傳)?
更具體地說,我們觀察到在沒有明確的否定確認的情況下,數據傳輸算法從肯定確認中為每個段積累丟失的證據,例如重復累積(例如,TCP NewReno[23])或選擇性ack(例如,RDMA和TCP SACK的IRN [16])。一旦某個段的累積證據超過閾值,該算法就可以自信地宣布該片段丟失。通常,段i的丟失證據也是每個未確認的段j(j
· 并發事件處理
對于每個流,有四個主要事件會影響其下一個段地址的生成。首先,接收到確認可以向前移動窗口并使流能夠生成更多的段,或者發送信號段丟失并觸發重傳。第二,沒有確認(即超時)也可能導致更多的段被標記為丟失并觸發重傳。第三,段地址的生成會增加流的未完成段的數量,如果超過N,則可以停用該流。第四,段地址傳輸(除credit引擎外)減少了未完成段的數量,可以使流生成更多的段地址。
Tonic的數據傳輸引擎有四個模塊來處理這四個事件(圖2)。每個周期,每個模塊從數據傳輸引擎中的內存中讀取其接收到處理事件的流狀態,并相應地更新流狀態。數據傳輸引擎中的流狀態包括一組固定的變量,用于跟蹤跨事件段的當前窗口的狀態,以及可編程組件中使用的用戶定義變量(表2)。作為固定狀態變量的一個示例,Tonic為每個流保留了一組固定的位圖(見§3.2.2):ack位圖跟蹤選擇性確認的段,marked-for-rtx跟蹤需要重傳的丟失段,rtx-cnt存儲有關其先前重新傳輸的信息。
以下段落簡要描述了每個事件處理模塊如何影響流的狀態,以及是否存在我們可以利用的通用模式,以固定功能的方式實現其全部或部分功能。
輸入。該模塊處理確認過程(和其他輸入數據包,見§3.3.3)。響應確認的狀態變量的一些更新在所有數據傳輸算法中都是類似的,并且不需要編程(例如,更新窗口邊界,并在ack位圖中標記選擇性確認的段,見§3.2.2),而依賴于確認作為信號的丟失檢測和恢復,不同算法之間的差異很大,必須由用戶進行編程(表1中的#4)。因此,輸入模塊被設計為兩級流水線:一個用于公共更新的固定功能階段,另一個用于丟失檢測和恢復的可編程階段。
這種兩階段設計的好處是常見的更新主要涉及位圖和數組(§3.2.2),而它們在硬件中被實現為環形緩沖區,并且跨元素修改的成本很高。例如,在所有數據傳輸算法中,如果一個輸入數據包累計確認了段A,并有選擇性地確認段S,則wnd-start將會被更新為max(wnd-start,A),acked[s]為1,并且所有位圖和數組的邊界將根據新的wnd-start進行更新。通過將這些更新轉移到一個固定的功能階段,我們可以(i)優化它們以滿足Tonic的時序和內存限制,(ii)為程序員提供一個專用階段,即一個獨立的周期,用于進行丟失檢測和恢復。在這個專門階段,程序員可以使用前一階段更新的狀態變量和內存中的其余變量來推斷段丟失(并執行§3.3.3中討論的其他用戶定義的計算)。
定期更新。數據傳輸引擎對活動流進行迭代,每次發送一個到該模塊,以檢查重傳計時器是否過期,并執行其他用戶定義的定期更新(§3.3.3)。因此,憑借其10 ns的時鐘周期,Tonic可以在其重傳計時器到期后的幾us內覆蓋每個流。該模塊必須是可編程的,因為重傳超時是用于檢測丟失的信號(表1中的#4)。與輸入模塊的可編程階段類似,程序員可以使用每個流的狀態變量來推斷段損失。
段生成。給定一個活動流及其變量,該模塊生成下一個段的地址并將其轉發給信用引擎。Tonic根據以下觀察結果(表1中的#3)可以將段地址生成作為一個固定的功能模塊來實現:盡管不同的可靠數據傳輸算法有不同的方法來推斷段丟失,但一旦檢測到丟失的段,在發送任何新的數據之前重新傳輸它是合乎邏輯的。因此,無論數據傳輸算法如何,選擇下一段的過程都是相同的,并且在Tonic中作為一個固定功能模塊來實現。因此,該模塊優先重發marked-for-rtx中丟失的段,而不是發送下一個新的段,即highest_sent+1,同時也增加了未完成段的數量。
數據段傳輸。該模塊為固定功能,在從信用引擎傳輸段地址時觸發。它減少了相應流中未完成段的數量。如果流由于環形緩沖區滿了而被停用,則會再次將其插入活動集中。
>3.3 靈活的信用管理
傳輸協議使用擁塞控制算法,通過控制流的傳輸速度來避免網絡過載。這些算法包括一個控制回路,該回路通過監測輸入控制數據包流(例如,確認和擁塞通知數據包(CNP))來估計網絡容量,并設置限制輸出數據包的參數。雖然控制回路在許多算法中有所不同,但基于參數的信用計算卻不盡相同。Tonic具有用于信用計算的高效固定功能模塊(§3.3.1和§3.3.2),并將參數調整歸入可編程模塊(§3.3.3)。
· 常用的信用計算模式
擁塞控制算法有多種方法來估計網絡容量。然而,它們通過三種主要方式(表1中的#5)對數據傳輸進行限制:
擁塞窗口??刂苹芈废拗屏艘粋€流從第一個未確認的字節開始,最多只能飛行W個字節。因此,如果字節i是第一個未確認的字節,則流不能發送超過i +W的字節。跟蹤飛行中的段以執行擁塞窗口可能會變得復雜(例如,存在選擇性確認的情況下)并在數據傳輸引擎中傳入模塊的固定功能階段實現。
速率??刂苹芈废拗屏鞯钠骄俾剩≧)和最大突發大?。―)。因此,如果流在最后一次傳輸的t0時間具有信用c0,則在時間t時的信用將為min(R?(t?t0)+c0,D)。正如我們在§4中所示,在嚴格的時序和內存約束下實現精確的單流速率限制是一項具有挑戰性的任務,而在Tonic中有一個優化的固定功能實現。
授權令牌??刂苹芈窂慕邮斩私邮樟钆撇⑵涮砑拥搅鞯男庞弥?,而不是估計網絡容量。因此,一個流的信用是接收的令牌總數減去傳輸的字節數,信用計算邏輯由簡單的加法組成。
由于大多數擁塞控制算法都使用這些算法3,因此我們優化了它們的實現,以滿足Tonic的時序和內存約束。擁塞窗口的計算主要受ack的影響。因此,擁塞窗口的計算和實施發生在數據傳輸引擎中。對于其他兩個信用計算方案,信用引擎處理與信用相關事件,用戶可以簡單地選擇在信用引擎中使用哪個方案。
· 信用計算的事件處理
從概念上講,有三個主要事件可以觸發流的信用計算,信用引擎有不同的模塊可以在每個周期內并發處理它們(圖2)。首先,當從數據傳輸引擎接收到一個段地址并且該段地址是流的環形緩沖區中的唯一地址時,流現在可以根據其信用(排隊模塊)進行傳輸或保持空閑。第二,當一個流傳輸一個段地址時,它的信用度必須降低,我們應該根據它更新的信用度和它的環形緩沖區(傳輸模塊)的占用情況來確定它是否有資格再次傳輸。第三,可以向流中添加信用的事件(例如,來自授權令牌和泄露速率限制),這是基于速率和基于令牌的信用計算之間的主要區別。
當使用授權令牌時,信用引擎需要兩個專用模塊來增加流的信用:一個用于處理來自接收端的輸入授權令牌,另一個用于增加超時重傳的信用。當基于速率時,信用引擎不需要任何額外的模塊來增加信用,因為速率為R字節/周期的流在每個周期內隱式地獲得R字節的信用,因此,我們可以提前計算它何時符合傳輸條件。
假設在周期T0中,傳輸模塊從流f中發送了一個段,并且正在判斷該流是否符合進一步發送的條件。假設f在環形緩沖區中有更多的段,但缺少L字節的信用。當T=L/R,傳輸模塊可以計算何時有足夠的信用,并為T周期設置一個計時器。當計時器到期時,f至少有一個段有足夠的信用,因此它可以直接插入ready-to-tx。當f到達ready-to-tx的頭部并在T1周期中再次由傳輸模塊處理時,傳輸模塊可以將f的信用增加(T1?T0)? R?S,其中S是在時間T1時傳輸的段的大小4。請注意,在使用速率時,信用引擎必須執行分割并維護單流計時器。我們將在§4中討論這些操作的硬件實現。
· 靈活的參數調整
擁塞控制算法通常有一個控制回路,該回路持續監測網絡并根據估計的網絡容量調整信用計算參數,即速率或窗口大小。參數調整要么由輸入的數據包(例如,確認和它們的信號,如TCP變體中的ECN或延遲以及Timely,以及DCQCN中的擁塞通知數據包(CNP))觸發,要么由周期性計時器和計數器觸發(TCP變體和字節計數器中的超時,以及DCQCN中的各種計時器),在某些情況下,也受到段丟失的觸發(TCP中重復確認后的窗口調整)。
針對這些觸發器, Tonic的用戶可以使用“Incoming”模塊的可編程平臺(該模塊可以查看所有輸入數據包)以及定時器和計數器的“Periodic Updates”模塊來指定參數調整邏輯。這兩個模塊都位于數據傳輸引擎中,可以訪問段狀態信息,以防參數調整需要段狀態(如掉線)。更新后的參數將轉發到信用引擎。
如§6.1.1所示。我們在Tonic中實現了幾種擁塞控制算法,它們的參數調整計算在我們的10 ns時鐘周期內完成。那些使用整數運算的算法不需要做任何修改。對于那些具有浮點運算的操作,例如DCQCN,我們使用整數運算將其近似為某個小數點。如果一個算法需要高精度和復雜的浮點運算來調整參數,而又不能在一個時鐘周期內實現[19],則可以將計算交給Tonic之外的浮點運算模塊。該模塊可以執行異步計算,并將輸出存儲在單獨的內存中,該內存通過“周期性更新”模塊合并到Tonic中。
>3.4 處理沖突事件
Tonic力求同時處理事件,以便對事件作出反應。因此,如果一個流在同一個周期中接收到多個事件,它允許事件處理模塊處理事件并更新流的狀態變量,并在將其寫回內存之前協調狀態(圖2中的合并模塊)。
根據定義,由于確認和重傳超時是互斥的。因此,如果在同一周期內接收到對同一流的確認和超時,則Tonic將丟棄超時。這大大簡化了合并邏輯,因為幾個變量(窗口大小和重傳計時器周期)僅由這兩個事件修改,因此永遠不會同時更新。我們可以使用簡單的、預定義的合并邏輯來解決剩余變量的并發更新。例如,段生成增加未完成段的數量,而段傳輸減少未完成段的數量;如果兩個事件同時影響同一個流,則數字不會更改。用戶定義的變量在Incoming或Periodic Updates模塊中更新,如果兩個更新發生在同一個周期中,我們依賴程序員來指定哪些更新的變量應該優先。
>3.5 Tonic的編程接口
為了在Tonic中實現新的傳輸邏輯,程序員只需指定(i)使用三種信用管理方案中的哪一種,(ii)響應確認和超時的丟失檢測和恢復邏輯,以及(iii)響應輸入數據包或周期計時器和計數器的擁塞控制參數調整。第一個用于為信用引擎選擇合適的模塊,最后兩個插入到數據傳輸引擎的相應可編程階段(圖2)。
為了指定Incoming模塊的可編程階段的邏輯,程序員需要編寫一個函數,該函數接收輸入數據包(ack或其他控制信號)、新確認的段數、用ack中的信息更新的ack位圖、wnd- start的新舊值(以防窗口因新的累積ack而向前移動),以及流的其余狀態變量(表2)作為輸入。在輸出中,它們可以在marked-for-rtx中標記要重傳的段范圍,更新擁塞控制參數,如窗口大小和速率,并重置重傳計時器。周期性更新模塊的編程接口等等。
在指定這些函數時,程序員可以使用整數算術運算(例如,帶有小寬度操作數是加減乘除)和有限的只讀位圖操作集(例如,索引查找),并在更新的ack位圖中查找第一個設置位(示例程序見附錄F)。請注意,數據傳輸引擎中的一個專用固定功能階段在收到ack時執行代價高昂的通用位圖更新(§3.2.3)。我們在§6.1.1中說明了可以使用此接口實現多種傳輸協議,并舉出了一些不能實現的示例。
4. 硬件實現
在本節中,我們描述了在Tonic嚴格的時序和內存約束下最難實現的Tonic組件的硬件設計。
高精度的單流速率限制。在T=L/R周期中,一個具有每周期速率為R字節和要發送L字節的流將具有足夠的信用用于傳輸。Tonic需要在信用引擎中執行此計算,但必須將R表示為一個整數,因為它無法進行浮點除法。這在速率限制精度和Tonic可以支持的速率范圍之間進行了權衡。如果R以字節/周期為單位,則我們不能支持低于1字節/周期或~1 Gbps的速率。如果我們用每千個周期的字節數表示R,我們可以支持低至1 Mbps的速率。然而,T=L/ R決定了從現在開始該流有多少個周期有資格傳輸,這導致較高帶寬流的速率一致性和精度較低。為了在不犧牲精度的情況下支持大范圍的速率,Tonic在不同的精度級別保留了流的多種表示形式,并在任何時刻挑選最精確的表示來計算T(詳情見附錄B)。
高效的位圖操作。Tonic使用高達128位的位圖來跟蹤每個流的段狀態。位圖以環形緩沖區的形式實現。頭指針對應于第一個未確認的段,并使用新的ack沿著緩沖區向前移動。為了有效地實現其輸出取決于位圖中所有位的值的操作,我們必須將緩沖區分成多層的小部分,并行處理它們,并連接結果。在Tonic中經常使用的一種這樣的操作是查找報頭之后的第一個設置位。環形緩沖區中報頭的移動使該操作的實現變得復雜,因為跟蹤每層中的報頭需要額外的處理,使得它很難在我們的10 ns目標內進行計算。相反,Tonic在輸入環形緩沖區上使用輕量級預處理,以完全避免各層中的報頭索引計算(詳細信息見附錄C)。
并行內存訪問。在每個周期中,數據傳輸引擎中的五個模塊(包括Incoming模塊的兩段)同時訪問其內存(§3.2.3)。然而,FPGA只有雙端口RAM存儲器,每個端口在每個周期都能進行讀或寫。構建具有更多并發讀、寫的存儲器需要在單獨的存儲器“分塊”中保存數據的多個副本,并跟蹤分塊中每個地址的最新數據5。為了避免支持五個并發讀寫,我們設法將每個流的狀態變量劃分為兩個組,每個組最多處理四個事件。因此,Tonic可以使用兩個具有四個讀寫端口的存儲器,而不是單個具有五個端口的存儲器,同時為所有處理模塊提供并發訪問。
5. 將Tonic集成到傳輸層
Tonic的傳輸邏輯有意與其他傳輸功能(如連接管理、應用級API和緩沖區管理)的具體實現相分離。本節提供一個示例,說明Tonic如何與Linux內核交互,以了解新連接、數據傳輸請求和連接終止6。創建套接字后,應用程序使用各種系統調用進行連接管理和數據傳輸。由于Tonic主要關注傳輸邏輯的發送端,因此我們只討論與傳輸層的發送端相關的系統調用和修改。
連接管理??蛻舳松系腸onnect()發起連接,服務器上的listen()和accept()監測并接受連接,close()終止連接。由于連接管理發生在Tonic之外,因此這些系統調用的內核實現保持不變。但是,一旦連接建立,內核會將其映射到[0,N)中的唯一flow id,其中N是Tonic支持的最大流數,并通過NIC驅動程序通知Tonic關于新連接。
具體地說,通過內核中連接的Transmission Control Block (TCB),通信終端的IP地址和端口與為連接選擇的flow id和固定段大小一起發送到Tonic。內核只需要跟蹤用于連接管理的TCB字段(例如,IP地址、端口和TCP FSM)、數據緩沖區的指針以及與接收器相關的字段。發送端上用于數據傳輸的字段(即snd.nxt、snd.una和snd.wnd)存儲在Tonic中并由Tonic處理。最后,在調用close()之后,內核使用該連接的flow id通知Tonic終止。
數據傳輸。send()將數據添加到連接的套接字緩沖區,該緩沖區存儲等待傳輸的未完成數據。Tonic為未完成的數據保留幾比特的每段狀態,并以段執行所有傳輸邏輯計算。因此,在Tonic開始傳輸之前,數據應該被劃分為同等大小的段(§3.2)。因此,對send()的修改主要涉及根據連接配置的段大小來確定套接字緩沖區中數據的段邊界,并決定何時將新段通知Tonic。具體來說,內核除了頭部和尾部外,還為每個連接的套接字緩沖區保留一個額外的指針,稱為tonic-tail。它指向已通知Tonic的最后一段。head和tonic-tail的更新被發送給Tonic,以便在生成下一段地址時從內存中獲取。
從一個空的套接字緩沖區開始,當應用程序調用send()時,數據被復制到套接字緩沖區,tail也相應地更新。假設連接配置的段大小為C,那么數據就會被分割成C大小的段。假設數據被分割成S個段和B(B
如果在時間T之后沒有足夠的數據用于C大小的段,內核需要通知Tonic“子段”(小于C的段)及其大小,并相應地更新Tonic-Tail。注意,Tonic要求除突發事件中的最后一個數據段外的所有數據段的大小相同,因為所有計算(包括窗口更新)都是以數據段為單位的。因此,在創建一個“子段”之后,如果應用程序有更多數據,Tonic只有在完成當前段的傳輸后才能開始傳輸。Tonic在成功傳輸最后一個“子段”后通知內核,此時head和tonic-Tail將會相等,內核繼續對套接字緩沖區中的剩余數據進行分區,并像以前一樣更新Tonic。注意,Tonic可以使用可配置的頻率周期地向內核轉發確認信息,使head向前移動,為套接字緩沖區的新數據騰出空間。
可以根據每個流的延遲和吞吐量特性為其配置C和T。對于高帶寬流,可以將C設置為MSS(如果使用TSO,則設置為更大)。對于零星產生小段的流,設置C和T不是那么直接,因為段不能在Tonic內合并。我們在附錄D中詳細討論了決定這些參數的權衡問題。
其他考慮事項。如§6所示,Tonic當前的設計支持2048個并發流,與數據中心[15,37]中觀察到的工作集以及文獻[20]中的其他硬件負載相匹配。如果一個主機的開放連接超過Tonic所能支持的數量,內核可以按照先到先得的原則將數據流的數據傳輸分流到Tonic,或者讓用戶在創建套接字時設置標志,并在Tonic耗盡新流的資源后回退到軟件。另外,現代基于FPGA的網卡有一個大的DRAM直接連接到FPGA[20]。DRAM可以用來存儲更多連接的狀態,并在它們激活和需要傳輸數據時將它們來回交換到Tonic的內存中。此外,為了提供對硬件傳輸邏輯性能的可見性,Tonic可以為內核提供一個接口,以便定期從NIC提取傳輸統計數據。
其他傳輸層。上面的設計是如何將Tonic集成到常用傳輸層的一個示例。但是,TCP、套接字和字節流并不適用于所有應用程序。事實上,一些具有高帶寬、低延遲流的數據中心應用程序開始使用RDMA及其基于消息的API來代替。Tonic也可以被集成到基于RDMA的傳輸中,我們將在附錄A中討論這一點。
6. 評估
為了評估Tonic,我們用Verilog實現了一個原型(約8K行代碼),并用C++實現了一個周期精確的硬件仿真器 (約2K行代碼) [11]。該仿真器與NS3網絡仿真器[4]集成在一起進行端到端的實驗。
要在Tonic的Verilog原型上實現傳輸協議,程序員只需要提供三個Verilog文件:(i) incoming.v,描述丟失檢測和恢復邏輯以及如何改變信用管理參數(即,速率或窗口) 以響應于輸入數據包;該代碼被插入到數據傳輸引擎中的輸入流水線的第二階段;(ii) periodic_updates.v,描述丟失檢測和恢復邏輯以響應超時,以及如何改變信用管理參數(即,速率或窗口)以響應周期性定時器和計數器,該代碼被插入到數據傳輸引擎中的周期性更新模塊中 (iii)user_configs.vh,其指定要使用三個信用計算方案中的哪一個以及用戶定義的狀態變量和其他參數的初始值,例如初始窗口大小、速率和信用。
我們從以下兩個方面對Tonic進行評估:
硬件設計(§6.1)。我們使用Tonic的Verilog原型來評估其硬件架構的可編程性和可拓展性。Tonic能否支持各種傳輸協議嗎?它是否減少了在NIC中實施傳輸協議的開發工作?Tonic能否支持具有多個變量的復雜用戶定義邏輯嗎?它能夠支持多少個單流段和并發流?
端到端行為(§6.2)。我們使用Tonic的周期性精確仿真器和NS3來比較Tonic的端到端行為和兩個協議的硬編碼實現:New Reno[23]和使用DCQCN的 RoCEv2 [43],對于單個流和多個流共享一個瓶頸鏈路。
>6.1 硬件設計
評估硬件設計效率有兩個主要指標:(i)資源利用率。FPGA由原語塊組成,這些原語塊可以通過配置和連接以實現Verilog程序:look-up tables (LUT)是主要的可重新配置邏輯塊,而block RAM(BRAM)用于實現存儲器。(ii)定時。在每個周期開始時,每個模塊的輸入被寫入一組輸入寄存器。在下一個周期開始之前,該模塊必須處理輸入并準備輸出寄存器的結果。Tonic 的meet timing必須是100 MHz,才能每10 ns傳輸一個段地址。也就是說,要實現100 Gbps,每個模塊中從輸入到輸出寄存器的每條路徑的處理延遲必須保持在10 ns以內。
我們使用這兩個指標來評估Tonic的可編程性和可擴展性。這些指標高度依賴于用于合成的特定目標。我們使用Kintex UltraScale+XCKU15P FPGA作為我們的目標,是因為該FPGA和其他具有類似功能的FPGA在今天的商業可編程NIC中都是bump-in-the-wire結構。這是一個保守的選擇,因為這些NIC是為10-40 Gbps以太網設計的。一塊100 Gbps的網卡可能會有一個功能更強大的FPGA。此外,我們將Tonic的所有組件綜合到FPGA上,作為一個獨立的原型進行評估。然而,考慮到固定功能模塊和可編程模塊之間有明確的接口,可以設想將固定功能組件作為ASIC實現以提高效率。除非另有說明,否則在所有實驗中,我們都將并發流的最大數量設置為1024,將最大窗口大小設置為128段7。
· 硬件可編程性
我們已經在Tonic中實現了六種協議的發送端傳輸邏輯,作為文獻中各種類型的段選擇和信用計算算法的代表。表3總結了固定功能模塊和用戶定義模塊的資源利用率,以及用于實現它們的代碼行和用戶定義狀態字節數。雖然我們對所有協議使用相同的單流狀態變量集(表2),但并非所有協議在處理傳輸事件時都使用所有變量。例如,位圖只被有選擇性ack的協議使用。因此,通過一些預處理,從Verilog設計中去掉不相關的變量和計算,甚至可以進一步降低資源利用率。
Reno和New Reno代表TCP變體,僅使用累積ack來實現可靠傳輸以及用于信用管理的擁塞窗口。Reno只能使用快速重傳來恢復窗口內的一次丟失,而New Reno使用部分確認來更有效地恢復同一窗口中的多個丟失。SACK受到RFC 6675的啟發,表示使用選擇性ack的TCP變體。我們的實現是每個ack至少有一個SACK塊,但可以擴展到更多。
NDP[24]代表接收器驅動的協議,最近提出用于低延遲數據中心網絡。它使用明確的NACK和超時進行丟失檢測,并授予令牌進行擁塞控制。RoCEv2 w/DCQCN[43]是一種廣泛使用的以太網RDMA傳輸協議,IRN[34]是一種最新的基于硬件的協議,用于改進ROCE的簡單數據傳輸算法。這兩者都使用速率限制器進行信用管理。
注意,如§3.2所述,并非所有數據傳輸算法都適用于硬件實現。例如,由于NIC上的內存限制,不可能在網卡上為每個數據包(新數據包和重傳輸數據包)保留時間戳。因此,嚴重依賴每包時間戳的傳輸協議(例如,QUIC[27])需要被修改以使用更少的時間戳工作,例如,對于要卸載到硬件的正在傳輸的段的子集。
從這些結果中可以得出三個重要結論:
① Tonic支持多種傳輸協議。
② 使用Tonic,上述每種協議的實現只需不到200行Verilog代碼,用戶自定義邏輯占用不到FPGA 0.6%的LUT。相比之下,Tonic的固定功能模塊可以在這些協議中重復使用,使用約8K行代碼實現,消耗了60倍的LUT。
③ 不同的信用管理方案具有不同的開銷。對于使用擁塞窗口的傳輸協議,窗口計算與數據傳輸引擎重疊,因此在數據傳輸引擎中實現(§3.3.1)。因此,他們的信用引擎使用的資源比其他公司少。與強制執行接收器生成的授權令牌相比,速率限制需要更多的單流狀態和更復雜的操作(§4),但需要更少的內存端口用于并發讀取和寫入(§3.3.2),總體上導致更低的BRAM和更高的LUT利用率。
· 硬件可擴展性
我們通過研究Tonic架構中的可變性來源(可編程模塊和各種參數)如何影響內存利用率和計時來評估Tonic的可擴展性。
可編程模塊中的用戶定義邏輯可以具有任意長的相關操作鏈,這可能會導致時序沖突。我們用不同數量的算術、邏輯和位圖運算為incoming.v(數據傳輸引擎中Incoming模塊的可編程階段)生成了70個隨機程序,并分析了在不違反10 ns時序的情況下相關操作鏈的長度。這些程序使用高達125B的狀態,最大依賴于65個邏輯電平(分別比表3中的基準協議多6倍和2倍)。每個邏輯電平代表幾個原始邏輯塊(LUT、MUX、DSP等)中的一個,它們鏈接在一起以實現Verilog程序中的路徑。
我們將這些程序插入Tonic,對其進行綜合,并分析邏輯級數與最大延遲路徑延遲之間的關系(表4)。邏輯級數不超過32的程序始終滿足時序要求,而邏輯級數超過43的程序則不符合時序要求。邏輯級數在32到42之間的,最大延遲路徑的等待時間約為10 ns。根據最大延遲路徑上原語的組合及其延遲,該區域中的程序可能會滿足時序要求。我們的基準協議在其最大延遲路徑上有13到29個邏輯級數,并且都滿足時序。因此,Tonic不僅支持我們的基準協議,而且還有空間支持未來更復雜的協議。
用戶定義的狀態變量增加了影響BRAM利用率的內存寬度。我們在SACK、IRN和NDP增加了額外的變量,以查看在不違反時序和耗盡BRAM的情況下內存的寬度,并針對三種信用管理方案中的每一種重復該實驗,因為它們具有不同的存儲器足跡(表4)。Tonic支持用戶自定義狀態的448B擁塞窗口信用管理,340B速率,256B授予令牌(表3中的協議使用小于30B)。
最大窗口大小確定存儲在數據傳輸引擎中的每個流位圖的大小,以跟蹤流的段狀態,從而影響內存利用率和位圖操作的復雜性,從而影響時序。Tonic可以支持256位的位圖(即跟蹤256段),使用它我們可以在高達30μs RTT的網絡中支持單個100Gbps流(表4)。
并發流的最大數量決定內存深度和用于流調度的FIFO大?。ā?.1)。因此,它會影響內存利用率和隊列操作,從而影響時序。Tonic可以在硬件上擴展到2048個并發流(表4),這與數據中心[15,37]和文獻[20]中的其他硬件卸載觀察到的活動流集的大小相匹配。
Tonic有更多的空間來支持未來的協議,這些協議比我們的基準協議更復雜,具有更多用戶定義的變量。它可以跟蹤每個流的256個段,支持2048個并發流。有了更強大的FPGA和更多的BRAM,Tonic可以支持更大的窗口和更多的流。
>6.2 端到端行為
為了檢查Tonic的端到端行為,并驗證不同協議中基于Tonic的傳輸邏輯實現的保真度,我們在C++中為Tonic開發了一個周期精確的硬件仿真器。我們將此仿真器與NS3一起使用,以顯示基于Tonic實現的NewReno和RoCEv2 w/DCQCN發送端與其硬編碼的NS3實現相匹配。請注意,這些仿真的目的是分析和驗證Tonic的端到端行為。Tonic支持100Gbps線路速率的能力已在§6.1中得到證明。因此,在我們的仿真中,我們使用10Gbps和40Gbps作為線速率,僅僅是為了使硬件仿真在幾秒鐘內的多個流在計算上易于處理。
· TCP New Reno
我們在Tonic中基于RFC6582實現了TCP New Reno,并使用NS3的本地網絡堆棧進行硬編碼實現。我們基于Tonic的實現與NS3中未修改的本地TCP接收器配合使用。在所有仿真中,主機通過10Gbps鏈路連接到一個交換機,RTT為10μs,緩沖區為5.5MB,最小重傳超時為200ms(Linux默認值),段大為1000B,并且在接收器上啟用延遲ack。
單流。我們啟動從一個主機到另一個主機的單一流,并在接收方的NIC上隨機丟棄數據包。圖3(a)和圖3(b)分別顯示了擁塞窗口和傳輸序列號的更新(重傳用大圓點標記)。Tonic在這兩種情況下的行為都與硬編碼的實現密切相關。這些細微的差異源于這樣一個事實:在NS3的網絡堆棧中,所有計算都在同一虛擬時間步長中進行,而在Tonic中,每個事件(輸入數據包、段地址生成等)都在100ns周期內處理(從10ns增加到10G線速率)。
多個流。兩個發送端各發送100個流到一個接收端,因此200個流在5秒鐘內共享一個瓶頸鏈路?;赥onic實現的200個流的平均吞吐量的CDF與硬編碼的實現密切相關(圖3(c))。我們觀察到重傳次數的分布也是類似的。在分析毫秒級的流吞吐量時,我們注意到硬編碼實現的變化比Tonic大,因為相對于NS3的堆棧,Tonic在同一主機上的流執行每包循環調度。
· RoCEv2 with DCQCN
我們用Tonic實現ROCE w/DCQCN,并使用作者在[44]中的NS3進行硬編碼實現。我們基于Tonic的實現與未經修改的硬編碼ROCE接收器一起工作。在所有仿真中,主機通過40Gbps鏈路連接到同一交換機,RTT為4μs,網段大小為1000B,我們使用中的默認DCQCN參數。
單流。Tonic是一種基于速率的算法,它使用CNP和周期性定時器和計數器進行擁塞控制,而不是TCP中的丟包。因此,為了觀察單個流的速率更新,我們從兩臺主機向同一接收器運行兩個流一秒鐘,以造成擁塞并跟蹤其中一個流的吞吐量變化,因為它們都收斂到相同的速率。Tonic的行為與硬編碼的實現非常匹配(圖4)。我們還運行了一個100Gbps的DCQCN流,其中包含128B背靠背數據包,并確認Tonic可以使100Gbps鏈路飽和。
多流。兩個發送方各向一個接收方發送100個流,因此200個流在一秒鐘內共享一條瓶頸鏈路。Tonic和硬編碼的實現都在同一主機上的流之間執行每數據包循環調度。結果,這兩種情況下的所有流最終的平均吞吐量為203±0.2 Mbps。此外,我們觀察到兩種情況下CNP的分布是匹配的。
7. 相關工作
Tonic是第一個能夠支持100Gbps的可編程硬件傳輸邏輯架構。在本節中,我們回顧最密切相關的先前工作。
商用硬件網絡協議棧。一些網卡的硬件網絡堆棧帶有硬連線傳輸協議。但是,它們只實現了兩種協議,要么是ROCE[8]要么是供應商選擇的TCP變體,并且只能由供應商修改。Tonic使程序員能夠以最小的努力在硬件中實現各種傳輸協議。由于缺乏對這些NIC體系結構的公開詳細描述,我們無法將我們的設計決策與他們的進行比較。
非商業性硬件傳輸協議。最近的工作探索了高速運行、占用內存少的硬件傳輸協議。Tonic通過使研究人員能夠以適度的開發努力實施新的協議來促進這一領域的創新。
加速網絡功能。一些學術和工業項目將終端主機虛擬交換和網絡功能卸載到FPGA,處理已經生成的數據包流。另一方面,Tonic通過一次跟蹤可能的幾百個數據段來實現NIC中的傳輸邏輯,以便在運行用戶定義的傳輸邏輯的同時以線速生成數據包,以確保高效可靠的傳輸。
附錄
附錄A 在RDMA中集成Tonic
遠程直接內存訪問(RDMA)使應用程序能夠直接訪問遠程端點上的內存,而不涉及CPU。為此,端點創建類似于連接的queue pair,并發布請求,稱為Work Queue Elements (WQEs),用于發送或接收彼此的內存數據。雖然RDMA起源于InfiniBand networks,但以太網上的RDMA在數據中心變得越來越普遍。在本節中,我們使用RDMA來指代以太網上的RDMA實現。
一旦創建了隊列對,RDMA NIC就可以將新的“連接”添加到Tonic,并在the sender side使用它來傳輸數據,以響應不同的WQE。每個WQE對應于一個單獨的消息傳輸,因此很好地滿足了Tonic在開始數據傳輸之前確定段邊界的需要。
例如,在RDMA寫入中,一個端點發布一個Request WQE以寫入另一個端點上的內存。在Request WQE中指定數據長度、發送方上的數據源地址和接收方上的數據接收器地址。因此,RDMA應用程序和Tonic之間的緩沖區可以決定段邊界,并通知Tonic要從發送器上讀取數據的段數和源存儲器地址。一旦Tonic生成下一個網段地址,RDMA網卡的其余部分就可以從發送方的內存中對其進行DMA,并添加合適的報頭。RDMA發送類似于RDMA寫入,不同之處在于它需要接收方上的接收WQE來指定來自發送方的數據應該寫入的接收方地址。所以,Tonic仍然可以在發送方以相同的方式使用。
另一個示例,在一個RDMA Read中,一個端點從另一個端點上的存儲器請求數據。因此,響應方端點應該向請求方端點傳輸數據。同樣,數據長度、響應方的數據源地址和請求方的數據接收器地址在WQE中指定。因此,緩沖區可以決定數據段邊界,并使用Tonic傳輸數據。
因此,Tonic可以集成到RDMA 網卡中,以取代數據傳輸發送方的硬編碼傳輸邏輯。事實上,我們的兩個基準協議ROCE w/DCQCN[43]和IRN[34]是為RDMA 網卡提出的。也就是說,這是假設在另一端有一個兼容的接收器來生成控制信號(例如,確認、擁塞通知等)。無論選擇在發送端的Tonic上實現哪種傳輸協議都需要這些信號。
雖然以太網上的RDMA的一些實現,如iWarp[7]處理失序(OOO)數據包并實現類似TCP/IP的確認,但其他的(即RoCE[8])假設一個無損網絡并具有更簡單的傳輸協議,不要求接收器處理OOO數據包和產生頻繁的控制信號。然而,隨著以太網上的RDMA在數據中心越來越普遍,在這些網卡中也正在實現在接收器上處理OOO數據包和生成各種控制信號的能力,以實現更有效的傳輸。
最后,Tonic在同一流中提供有序可靠的數據傳輸。因此,通過同一流發送的消息以相同的順序傳輸給接收方。然而,有時需要支持通信端點(例如,隊列對)的無序消息傳輸,例如,當消息彼此獨立時,或者當使用“非連接”的端點(例如,一個發送者和多個接收者)時,可以提高應用的性能。通過在Tonic中為同一通信端點創建多個流并同時使用它們,仍然可以使用Tonic支持無序消息傳輸。擴展Tonic以支持同一流中的無序消息傳輸是未來工作的一個有趣途徑。
附錄B 高精度單流速率限制
在信用引擎中使用速率時,如果一個速率為R字節/周期的流需要L字節的信用來傳輸一個段,則Tonic計算T=L/R作為該流將有足夠的信用來傳輸的時間。它設置一個定時器,該定時器在T周期內到期,并且在其到期時,將未準備好發送的流排隊以進行傳輸(§3.3.2)。由于Tonic無法在其時間限制內進行浮點除法,因此R必須表示為整數。
這在速率限制精度和Tonic可以支持的速率范圍之間進行了權衡。如果我們將R表示為每個周期的字節數,則可以計算流將具有足夠信用的確切周期,但不能支持低于每周期一個字節或約1Gbps的速率。如果我們用每千次周期的字節數來表示R,我們可以支持較低的速率(例如,1 Mbps),但T=L/R將確定從現在起該流可以有多少千次周期有資格進行傳輸。這導致較高帶寬流的速率一致性和精度較低。舉個具體的例子,對于一個20Gbps的流,R將是每千次周期25000字節。假設流有1500字節的數據段要傳輸。它將有足夠的信用在8個周期內完成,但是必須等待[1500/25000]=1000個周期來排隊等待傳輸。
Tonic沒有對R進行單一表示,而是對每個流保留多個變量R1,. . ., Rk,每個變量以不同的精確程度代表流的速率。由于擁塞控制環路根據網絡容量調整速率,Tonic可以有效地在R1、.。。。,Rk之間切換以選擇最精確的表示法來隨時計算T。這使得Tonic能夠在不犧牲速率限制精度的情況下支持各種的速率。
附錄C 高效的位圖操作
Tonic使用高達128位的位圖來跟蹤每個流的段窗口的狀態。位圖被實現為環形緩沖區,頭部指針對應于第一個未確認的段。隨著新的確認到來,頭部指針在環形中向前移動。為了有效地實現其輸出取決于位圖中所有位的值的操作,我們必須通過將環形緩沖區劃分為較小的部分,并行處理它們,并合并結果來對它們進行并行化。對于較大的環形緩沖器,這種分割運行的模式在多層中重復。由于每一層都依賴于前一層的輸入,我們必須將每一層的計算保持在最低限度,以保持在10 ns的目標范圍內。
其中一種操作是找到頭部之后的第一個設置位。此操作用于在marked-for-rtx位圖中查找下一個丟失的數據段進行重傳。環形緩沖區的頭部移動使該操作的實現變得復雜。假設我們有一個32位的環形緩沖區A32,第5和30位設置為1,頭部位于索引6。因此,find first(A32,6)=30。我們把環形緩沖區分成8個4位的部分,并將結果送入8位環形緩沖區A8,其中A8[i]=OR(A32[i:i+3])。因此,只設置了A8[1]和A8[7]。然而,由于A32[4:7]中的設置位在原環形緩沖區中的頭部之前,我們不能簡單地使用1作為A8的頭部索引,否則我們將錯誤地生成5而不是30作為最終結果。因此,我們需要額外的計算來找到正確的新頭部。對于具有多層這種分割運行模式的較大環形緩沖區,我們需要計算每一層的頭部。
相反,我們在輸入環形緩沖區上使用了輕量級的預處理,以完全避免頭部索引計算。更具體地說,使用A32作為輸入,我們計算出等于A32的A’32,只是從索引0到頭部(在我們的例子中是6)的所有位都被設置為0。從索引0開始,A’32中的第一個設置位始終比A32中的第一個設置位更接近原始頭部。因此,如果A’32有任何設置位,則find first(A32,6)等于find first(A’32,0),否則等于find first(A32,0)。這樣,與輸入的頭部索引H無關,我們總是可以從頭部索引固定為0的兩個子問題中求解find first(A,H)。
附錄D 使用Tonic通過Socket API確定流量的C和T
在§5中,我們提供了一個示例,說明如何將Tonic集成到Linux內核中,以便應用程序可以通過Socket API使用它。我們引入了兩個參數:(i)C,它是流的固定段大??;(ii)T,它是內核在向Tonic發送“子段”(小于C的段)之前等待來自應用程序的更多數據的持續時間。C和T可以根據每個流的延遲和吞吐量特性進行配置。對于高帶寬流,可以將C設置為MSS(如果更大,則可以使用TSO)。對于只偶爾生成數據段的流,正如我們下面討論的那樣,設置C和T并不是那么簡單。
在固定C的情況下,增加T會導致更多的小數據段在發送到Tonic進行傳輸之前被合并成C大小的數據段,但代價是延遲更高。C決定了Tonic生成的數據段的大小和子數據段的數量。回顧一下§5,當沒有足夠的數據在T內形成一個完整的C大小的段時,就會創建一個子段。Tonic需要除突發中的最后一個子段外的所有段的大小相等。因此,即使在子段被發送到Tonic進行傳輸之后,更多的數據被添加到套接字緩沖區,Tonic也必須成功地傳送所有先前的段,然后才能開始傳輸新的段。因此,最好是產生更大的分段但更少的子分段。在T固定的情況下,增加C會產生更大的段。然而,為了產生更少的子段,C應該被挑選出來,以便在大多數情況下突發中的數據可以被C整除。突發在時間上被T分隔。因此,T的選擇會影響C的選擇,反之亦然。
例如,如果應用程序周期性地生成128字節的請求,則C可以很容易地設置為128,T則基于周期性。作為另一個例子,對于一個周期性地生成大小不一的段的應用程序,將T設置為0并且將C設置為最大預期段大小,會導致Tonic傳輸由應用產生的數據段而不進行整合,從而潛在地創建許多子段。對于同一應用程序,將T設置為0,將C設置為最小預期段大小可能會導致Tonic生成許多小段,因為所有分段都將被分解為最小預期段大小。請注意,如果Tonic用于僅偶爾生成數據段的流,則這些權衡會變得更加明顯。對于高帶寬流,C可以設置為MSS(如果使用TSO則會更大),而T則取決于應用程序的流量模式和突發度。
審核編輯:劉清
評論
查看更多