@文章来源于《深入浅出DPDK》
深入浅出DPDK学习笔记——网卡性能优化
DPDK的轮询模式
DPDK采用了轮询或者混杂中断的模式来进行收包和发包,此前主流运行在操作系统内核态的网卡驱动程序基本都是基于异步中断处理模式。
异步中断模式
当有包进入网卡收包队列后,网卡会产生硬件(MSIX/MSI/INTX)中断,进而触发CPU中断,进入中断服务程序,在中断服务程序(包含下半部)来完成收包的处理。当然为了改善包处理性能,也可以在中断处理过程中加入轮询,来避免过多的中断响应次数。总体而言,基于异步中断信号模式的收包,是不断地在做中断处理,上下文切换,每次处理这种开销是固定的,累加带来的负荷显而易见。在CPU比I/O速率高很多时,这个负荷可以被相对忽略,问题不大,但如果连接的是高速网卡且I/O频繁,大量数据进出系统,开销累加就被充分放大。中断是异步方式,因此CPU无需阻塞等待,有效利用率较高,特别是在收包吞吐率比较低或者没有包进入收包队列的时候,CPU可以用于其他任务处理 。
当有包需要发送出去的时候,基于异步中断信号的驱动程序会准备好要发送的包,配置好发送队列的各个描述符。在包被真正发送完成 时,网卡同样会产生硬件中断信号,进而触发CPU中断,进入中断服务 程序,来完成发包后的处理,例如释放缓存等。与收包一样,发送过程 也会包含不断地做中断处理,上下文切换,每次中断都带来CPU开销;同上,CPU有效利用率高,特别是在发包吞吐率比较低或者完全没有发 包的情况。
轮询模式
DPDK起初的纯轮询模式是指收发包完全不使用任何中断,集中所有运算资源用于报文处理。但这不是意味着DPDK不可以支持任何中断。根据应用场景需要,中断可以被支持,最典型的就是链路层状态发生变化的中断触发与处理。
DPDK纯轮询模式是指收发包完全不使用中断处理的高吞吐率的方式。DPDK所有的收发包有关的中断在物理端口初始化的时候都会关闭,也就是说,CPU这边在任何时候都不会收到收包或者发包成功的中 断信号,也不需要任何收发包有关的中断处理。DPDK到底是怎么知道 有包进入到网卡,完成收包?到底怎么准备发包,知道哪些包已经成功 经由网卡发送出去呢?
前面已经详细介绍了收发包的全部过程,任何包进入到网卡,网卡硬件会进行必要的检查、计算、解析和过滤等,最终包会进入物理端口的某一个队列。前面已经介绍了物理端口上的每一个收包队列,都会有 一个对应的由收包描述符组成的软件队列来进行硬件和软件的交互,以 达到收包的目的。DPDK的轮询驱动程序负责初始化好每一个收包描述符,其中就包含把包缓冲内存块的物理地址填充到收包描述符对应的位置,以及把对应的收包成功标志复位。然后驱动程序修改相应的队列管理寄存器来通知网卡硬件队列里面 的哪些位置的描述符是可以有硬件把收到的包填充进来的。网卡硬件会把收到的包一一填充到对应的收包描述符表示的缓冲内存块里面,同时把必要的信息填充到收包描述符里面,其中最重要的就是标记好收包成功标志。当一个收包描述符所代表的缓冲内存块大小不够存放一个完整 的包时,这时候就可能需要两个甚至多个收包描述符来处理一个包。
每一个收包队列,DPDK都会有一个对应的软件线程负责轮询里面 的收包描述符的收包成功的标志。一旦发现某一个收包描述符的收包成 功标志被硬件置位了,就意味着有一个包已经进入到网卡,并且网卡已经存储到描述符对应的缓冲内存块里面,这时候驱动程序会解析相应的 收包描述符,提取各种有用的信息,然后填充对应的缓冲内存块头部。 然后把收包缓冲内存块存放到收包函数提供的数组里面,同时分配好一个新的缓冲内存块给这个描述符,以便下一次收包。
每一个发包队列,DPDK都会有一个对应的软件线程负责设置需要 发送出去的包,DPDK的驱动程序负责提取发包缓冲内存块的有效信 息,例如包长、地址、校验和信息、VLAN配置信息等。DPDK的轮询 驱动程序根据内存缓存块中的包的内容来负责初始化好每一个发包描述 符,驱动程序会把每个包翻译成为一个或者多个发包描述符里能够理解 的内容,然后写入发包描述符。其中最关键的有两个,一个就是标识完 整的包结束的标志EOP(End Of Packet),另外一个就是请求报告发送状态RS(Report Status)。由于一个包可能存放在一个或者多个内存缓 冲块里面,需要一个或者多个发包描述符来表示一个等待发送的包, EOP就是驱动程序用来通知网卡硬件一个完整的包结束的标志。每当驱 动程序设置好相应的发包描述符,硬件就可以开始根据发包描述符的内 容来发包,那么驱动程序可能会需要知道什么时候发包完成,然后回收 占用的发包描述符和内存缓冲块。基于效率和性能上的考虑,驱动程序 可能不需要每一个发包描述符都报告发送结果,RS就是用来由驱动程 序来告诉网卡硬件什么时候需要报告发送结果的一个标志。不同的硬件 会有不同的机制,有的网卡硬件要求每一个包都要报告发送结果,有的 网卡硬件要求相隔几个包或者发包描述符再报告发送结果,而且可以由 驱动程序来设置具体的位置。
发包的轮询就是轮询发包结束的硬件标志位。DPDK驱动程序根据 需要发送的包的信息和内容,设置好相应的发包描述符,包含设置对应 的RS标志,然后会在发包线程里不断查询发包是否结束。只有设置了 RS标志的发包描述符,网卡硬件才会在发包完成时以写回的形式告诉 发包结束。不同的网卡可能会有不同的写回方式,比如基于描述符的写 回,比如基于头部的写回,等等。当驱动程序发现写回标志,意味着包 已经发送完成,就释放对应的发包描述符和对应的内存缓冲块,这时候 就全部完成了包的发送过程。
混合中断轮询模式
由于实际网络应用中可能存在的潮汐效应,在某些时间段网络数据流量可能很低,甚至完全没有需要处理的包,这样就会出现在高速端口下低负荷运行的场景,而完全轮询的方式会让处理器一直全速运行,明 显浪费处理能力和不节能。因此在DPDK R2.1和R2.2陆续添加了收包中 断与轮询的混合模式的支持,类似NAPI的思路,用户可以根据实际应 用场景来选择完全轮询模式,或者混合中断轮询模式。而且,完全由用户来制定中断和轮询的切换策略,比如什么时候开始进入中断休眠等待收包,中断唤醒后轮询多长时间,等等。
图7-1所示为DPDK的例子程序l3fwd-power,使用了DPDK支持的中 断加轮询的混合模式。应用程序开始就是轮询收包,这时候收包中断是 关闭的。但是当连续多次收到的包的个数为零的时候,应用程序定义了 一个简单的策略来决定是否以及什么时候让对应的收包线程进入休眠模
式,并且在休眠之前使能收包中断。休眠之后对应的核的运算能力就被 释放出来,完全可以用于其他任何运算,或者干脆进入省电模式,取决 于内核怎么调度。当后续有任何包收到的时候,会产生一个收包中断, 并且最终唤醒对应的应用程序收包线程。线程被唤醒后,就会关闭收包 中断,再次轮询收包。当然,应用程序完全可以根据不同的需要来定义 不同的策略来让收包线程休眠或者唤醒收包线程。
DPDK的混合中断轮询机制是基于UIO或VFIO来实现其收包中断通 知与处理流程的。如果是基于VFIO的实现,该中断机制是可以支持队 列级别的,即一个接收队列对应一个中断号,这是因为VFIO支持多 MSI-X中断号。但如果是基于UIO的实现,该中断机制就只支持一个中 断号,所有的队列共享一个中断号。
当然混合中断轮询模式相比完全轮询模式,会在包处理性能和时延 方面有一定的牺牲,比如由于需要把DPDK工作线程从睡眠状态唤醒并 运行,这样会引起中断触发后的第一个接收报文的时延增加。由于时延的增加,需要适当调整Mbuf队列的大小,以避免当大量报文同时到达 时可能发生的丢包现象。在应用场景下如何更高效地利用处理器的计算 能力,用户需要根据实际应用场景来做出最合适的选择。