TAS: TCP Acceleration as an OS Service

本篇文章主要解决RPC场景下的TCP开销太大的问题。解决方法是从内核中提取出TCP最小功能,以用户态形式提供TCP fast-path。将原有功能(连接建立/销毁、拥塞控制和超时重传)留在slow-path(原来的协议栈)里。

TAS的好处

  • 性能
  • 可扩展性:scale TAS independent of the applications using it

TAS还是workload propotional的,可以根据网络负载动态调整CPU核,还能和应用程序共享同一个CPU核。

和NetKernel不同:Netkernel的目标是accelerate provider-driven network stack evolution by enabling new network protocol enhancements to be made available to tenant VMs transparently and simultaneously。

Background

这节主要总结了现有的网络协议栈架构和TCP协议栈的性能开销,具体内容很厉害。

网络协议栈架构

  • Monolithic, in-kernel
  • Kernel bypass
  • Protected kernel bypass
  • NIC offload
  • Dedicated CPUs
  • Dedicated fast path(TAS)

TCP协议栈开销,主要比较三种的CPU cycles、Instructions、CPU、Retiring(cycles)、Frontend Bound(blocked fetching instructions)、Backend Bound(fetching data)、Bad Speculation。

  • Linux
  • IX
  • TAS

一些结论

  1. 通过在与应用程序相同的处理器上以特权模式运行,它们引起了系统调用的开销,并污染了应用程序共享的L1、L2和转换缓冲区。
  2. 它们把每个连接的状态分散到几条cache line上,造成错误的共享,降低了缓存效率;
  3. 它们在机器的所有处理器核心上共享状态,导致缓存一致性和locking overhead;
  4. 它们为每个数据包执行整个TCP状态机,导致代码中有许多分支,不能有效利用批处理和预取机会。

Design

  • Efficiency:高性能
  • Connection scalability:支持超大量连接
  • Performance predictability:
  • Policy compliance:TAS必须能够执行诸如带宽限制、内存隔离、防火墙和拥塞控制等政策。
  • Workload proportionality:

TAS由三个部分组成:

  • Fast path
  • Slow path
  • 不被信任的per-application per-application user-space stack

所有的组件都通过一系列的共享内存队列连接,并对高速缓存有效的消息传递进行了优化。 Fast path负责处理普通情况下的数据包交换。它将收到的有效数据包的有效载荷直接存入用户空间的内存中。 在发送路径上,它根据slow path动态配置的每个连接速率或窗口限制,从用户内存中获取并封装有效负载。 用户级TCP堆栈为应用程序提供标准的POSIX API。 应用程序不需要修改,只需要(动态地)重新链接。 对于连接的建立和拆除,每个用户级栈都与slow path进行交互。

Fast path

Fast path处理协议头,发送TCP ack,执行网络拥塞策略,并执行payload segmentation。 此外,fast path还必须检测异常情况,如失序到达、connection control事件和未知或不寻常的数据包(如不寻常的IP和TCP头选项)。

  • Common-case receive
    • 假定数据包按照顺序到达
    • 丢弃所有的网络header,直接将有效payload插入user-level、per-flow、循环的receive payload buffer,应用程序通过轮询(epoll)接收通知,并用recv等套接字API操作
    • 在存入有序数据包的payload后,fast path自动生成一个ACK数据包,并将其传送给发送方以更新其TCP窗口
  • Common-case send
    • 通过appending data to a flow's per-flow circular transmit buffer(看后文,是bucket)来进行发送
    • fast path根据rate limit、 send window size、receiver TCP window来drain these buckets
  • Transmit payload buffer space reclamation:任何已经发送的有效载荷都会留在发送缓冲区中,直到被接收方确认
  • per-flow state:表3列举了TAS需要的per-flow state
  • Exceptions:处理两种异常情况:
    1. 当处理传入的ACK时,快速路径计算重复数,并在三个重复数之后触发快速恢复,方法是重新设置发送者状态,就像这些段没有被发送一样。快速路径也会增加每个流量的重传计数器,以通知慢速路径降低流量的速率限制。
    2. 快速路径跟踪接收缓冲区中的一个间隔的失序数据(从oo_start开始,长度为oo_len)。如果同一区间的失序段适合于接收缓冲区,快速路径就会接受它们。在这种情况下,快速路径将有效载荷写到接收缓冲区的相应位置。当一个内序段填补了现有流和间隔之间的空白时,快速路径通知用户级堆栈,就像一个大段到达一样,并重置其失序状态。其他失序的到达被丢弃,快速路径产生一个确认,指定下一个预期的序列号,以触发快速重传。

Slow path

慢速路径实现了所有的政策决定和管理机制。

  • 拥塞控制:同时支持基于速率和窗口的拥塞控制。
    • fast path在每个控制间隔(默认每2个RTTs)为每个流运行一个控制循环迭代。
    • slow path读取fast path记录的per-flow信息,通过共享内存更新速率/窗口
  • 连接控制:连接控制是复杂的。它包括端口分配、TCP选项的协商、维护ARP表和IP路由。
  • 超时重传
  • TCP stack管理:为了将新的用户态TCP协议栈与TAS联系起来,必须通过一个特殊的系统调用通知slow path。如果请求被批准,slow path会创建一对初始的上下文队列,用户空间协议栈会使用这些队列来创建连接缓冲区等。

User-space TCP stack

用户空间的TCP协议栈为应用程序提供了编程接口。 默认的接口是POSIX套接字,所以应用程序可以保持不被修改,但是每个应用程序的修改和扩展是可能的,因为接口在用户态。 例如,TAS也提供了一个类似于IX网络API的低级别的API。

上下文管理。 用户态协议栈负责定义和分配上下文。

Workload Proportionality

TAS在专用的处理器内核上执行协议处理,所需的内核数量取决于工作负载。 因此,TAS必须动态地调整用于处理的处理器核的数量,使之与当前的系统负载相称。 TAS通过三个独立的机制来实现这一点。

在fast path上,使用hardware and software packet steering引导数据包到相关的CPU。 在slow path上,监测CPU的利用率,并根据需要调整steering table。 最后,当一段时间内没有收到数据包时(实现中为10毫秒),fast path会阻塞线程。 这些CPU可以通过内核通知(eventfd)被唤醒。 这就要求我们在扩大/缩小规模事件中仔细处理队列之间的数据包重新分配。

Fast path。 初始化时,TAS为配置的最大CPU数创建线程,并为所有CPU分配网卡队列和应用队列。 由于有通知的自适应轮询,没有收到任何数据包的核心会自动阻塞并被取消调度。

我们设计了数据路径来处理到达错误的TAS核心的数据包,无论是来自网卡还是应用,都有一个每个连接的自旋锁保护连接状态。 这就避免了在添加或删除CPU时需要昂贵的协调和耗尽队列。 相反,我们只是异步地更新网卡和应用程序的packet steering,将数据包路由到或离开特定的CPU。 我们eagerly地更新NIC的RSS重定向表以引导传入的数据包,并lazily更新应用程序传出的数据包的路由。这使我们能够在规模扩大/缩小事件中保持robust。

Slow path。 slow path负责通过监测快速路径的CPU利用率来决定何时增加或删除CPU。 如果它检测到总的来说超过1.25个CPU是空闲的,它就开始移除一个CPU。 另一方面,如果总体上少于0.2个内核CPU,它就增加一个内核。具体的阈值是配置参数。