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