前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DPDK 报文调度/保序 终极解决方案 Event Dev 简介

DPDK 报文调度/保序 终极解决方案 Event Dev 简介

作者头像
Linux阅码场
发布2019-11-13 15:23:28
2K0
发布2019-11-13 15:23:28
举报
文章被收录于专栏:LINUX阅码场LINUX阅码场

1、背景介绍

网络报文的分发以及保序一直以来是让人头痛的问题, 为了完整的解释Event Dev的背景,我们可以从两个基本概念的定义开始:

  • 报文分发是指针对网络通信报文的某种特征(例如 5 tuples)对网络报文进行分类以及对这个分类进行对应的特定处理。这种分类通常称为是flow, 例如 src ip, src port, dest ip, dest port, 一致的报文被认为是一个flow, 还可以更进一步提取特征来加强判断同一flow的条件, 例如协议类型, tunnel id 等等。
  • 保序, 是指在包文处理过程中不改变原有的顺序, 大多数情况下可以弱化为在同一flow内的包文顺序不变。这里和协议倒不是非常相关。

首先最常见的包文分发技术是基于NIC 多队列的 Receive Side Scaling, NIC 首先根据算法对包文进行分类,然后尽可能平均的把不同flow放置到不同的接受队列中。RSS虽然解决了包文分发到多个cpu core的问题, 但是RSS并不了解哪个flow应该到哪个cpu core, 也无法通过配置解决, 同时RSS只能分发没有其它选择, 为了解决这个问题 Intel 引入了更进一步的方案 Flow Director, 通过Flow Director, NIC 不但可以分发包文还可以指定特定的flow到特定的cpu core,同时除了分发也可以选择drop 包文。要更加精准和灵活, 更多细节请参考 Flow Director Ref。

无论RSS/Flow Director 都可以分发/同一flow保序 但是很难满足更复杂的应用场景。在PIPELINE 处理模型中, 除了接受端分发以外, 每个节点处理后还会 二次, 三次, N次分发包文, 这下该怎么办 ?

如下图所示, 同一flow 4个包文开始分发, 1,3 进入一个路径, 2,4 进入一个路径, 因为没有任何锁来控制同步(性能考量) 所以在处理结束的时候包文的顺序以及打乱, 原因是在两个完全并行的路径上顺序是无法保证的, 最直接的想法就是引入一个reordering的功能节点, 当然代价是昂贵的, 同时在CPU core之间做包文的分发也是代价昂贵的操作。

02、CPU软件包调度

通过网卡来分发调度只能在接收端进行一次, 在更加复杂的PIPELINE模型中无法进一步进行调度, 所以非常直接的解决方案就是通过CPU 来对报文进行调度/保序, 一个高效的实现方案并不是显而易见的, 主要的困难在于两个方面:

  • 如何高效的分发
  • 如何高效的保序

DPDK的 组件 Packet Distributor 在这方面进行了有益的探索。

调度报文, 本质上就是CPU core之间的通信, X86的架构下 , CPU core 之间最高效的通信方式就是通过 L3 cache-line, 一个 cache-line 是64Byte, 也就是说 在一个cache-line中最多可以包含8个64bits指针,恰恰8个指针可以代表8个报文。Distributor Core 和 Worker Core 通过cache-line来通信, Distributor/Worker Core 通过首个报文的最低4位来判断读写的状态 (mbuf 默认都是 64Bytes 对齐, 所以低6位都是空余的可以用来表示状态), 如下图所示:

以下是状态位的定义(可左右滑动):

#define RTE_DISTRIB_NO_BUF 0 /**< empty flags: no buffer requested */

#define RTE_DISTRIB_GET_BUF (1) /**< worker requests a buffer, returns old */

#define RTE_DISTRIB_RETURN_BUF (2) /**< worker returns a buffer, no request */

其工作流程如下(引自dpdk.org), 不再赘述:

Distributor 作为纯软件报文分发确实做到了比较极致, 但是却没有完美的解决保序的要求, 依然需要引入 reorder buffer来重建报文顺序。同时如果worker core 是处理异步业务 Distributor core 也需要引入更加复杂的逻辑来应对。大家可以参考源代码: example/distributor/

03

终极形态

报文分发/保序的最终形态终于演化至Hardware实现 (i e基于CPU的软件方案在灵活性,性能上都遭遇了瓶颈)。当前的Hardware主要有两家厂商, NXP 和Cavium, 我们今天的讨论主要基于后者, 主要的原因是因为公开的资料比较全面。 Cavium eventdev HW 介绍

对于HW实现来说, 显而易见的是把分发/保序的工作分担给HW,从而节省CPU的计算资源。

核心的HW框架如下(引用自Cavium的资料):

  • 蓝色的队列/粉色的调度器都是基于HW实现
  • 每一个硬件的队列中包含若干的基于flow 划分的子队列, 每一个event会根据其相关flow最终插入到其中一个子队列
  • 在event enqueue阶段, event的发送源需要显式地指定队列号,亦即,该event应该进入那个队列
  • 之后调度器会根据 event 类型, event优先级, 队列类型等参数进行调度
  • 在event dequeue 阶段 每一个worker cpu core 会polling对应的port, 获取event, 处理结束后需要指定下一阶段的queue id 等信息再enqueue回对应队列
  • 有两个特殊的阶段是NIC RX/TX, RX 只有enqueue, 而TX 只有dequeue。理想情况下 RX/TX 可以做成inline 形式, 免去CPU 还需要polling NIC Ring

以下是简化的event定义, 前64位是 meta data, 后64位是mbuf的地址。Meta data中包含各种控制信息可以参考代码,这里不再赘述。(可左右滑动)

struct rte_event {

/** WORD0 */

union {

uint64_t event;

/** Event attributes for dequeue or enqueue operation */

};

/** WORD1 */

union {

struct rte_mbuf *mbuf;

/**< mbuf pointer if dequeued event is associated with mbuf */

};

};

报文调度主要有两种类型:

Atomic: 在特定时间点, 每一个flow 只有一个报文被调度到一个CPU进行处理, 从而保证处理过程中相关数据结构的原子性, 达到无锁处理的目标,可以简化应用设计.

Ordered: 在特定时间点, 每一个flow可以有多个报文被调度到多个CPU进行处理, 但是处理结束后报文的顺序没有变化.

下面结合EventDev HW 设计来描述一下一个报文的生命周期。首先来看下图:

  • 网卡收到报文,填写好flowid ,queue id从而enqueue到queue 0
  • 调度器HW根据入队的event flow id 和queue id将其分配至queue 0的flow0子队列
  • 调度器根据Queue0和 Dequeue port 0 的链接状态, 将其分发到port0 出队
  • CPU 0 从port0 取出event 进行相关处理, 业务逻辑代码根据配置信息将event 转发到queue1, 填写好queue id后, 从port0 入队
  • 调度器硬件根据queue id 这次将event分配到queue 1的flow0子队列
  • 调度器根据queue 1和 port1 链接的状态, 将其分发到port1出队
  • CPU 1 从 port1 取出event 进行加密处理, 之后依旧从por1入队到队列, 但是指定event 类型为TX
  • 调度器这次将根据事件类型将event分发至TX出队port, 从而发送

影响调度器行为的参数有:

  • flow id: event 所属的flow
  • queue id: event 入队时指定的队列id
  • event type: event的类型
  • queue type: 队列的分发属性
  • op type: event 是new/forward/release主要是指这个事件是新引入事件(例如RX),还是转发(node节点之间),还是释放(含异常处理)

结合下图可以更好的理解一下队列类型的影响:

Atomic 类型队列:

首先一个flow的event会保序进入queue 0的flow0 子队列, 调度器只会分发该flow的一个event到一个出队port, 在这个event被重新入队以前不会分发下一个event。

例如 e0 被分发到port0, 在e0被重新入队之前, e1,e2,e3都要等待。

Ordered 类型队列:

  • 首先一个flow的event会保序进入queue 0 的flow0 子队列, 调度器会同时分发e0, e1, e2, e3 到多个port上。例如 e3 被分发到port0, e0 被分发到port1, e1被分发到port2, e2被分发到port3。
  • 当e0,e1,e2,e3重新入队的时刻, HW会对其顺序重整。 保证对下一个阶段的处理是保序的。

在目录/driver/event/sw下, Intel 公司贡献了基于软件实现的eventdev-sw PMD。eventdev-sw PMD 和基于硬件实现的 eventdev PMD 在api 级别兼容, 功能上也基本一致。在/example/eventdev_pipeline_sw_pmd 目录下有基于eventdev-sw PMD的例子程序。这也是目前唯一upstream的 eventdev 例子程序, 非常有参考价值。

到这里, eventdev 的前世今生基本上已经讨论完毕。虽然event dev还是dpdk家族中相对新的组件, 但是我相信event dev最终会成为包处理业务的中流砥柱,不可或缺的一环。希望大家多提宝贵意见。

相关链接:

Receive Side Scaling (RSS):

https://docs.microsoft.com/en-us/windows-hardware/drivers/network/rss-with-hardware-queuing

Flow Director Ref:

https://www.intel.com/content/www/us/en/ethernet-products/converged-network-adapters/ethernet-flow-director.html

Cavium eventdev HW 介绍:

https://dpdksummit.com/Archive/pdf/2016USA/Day01-Session04-JerinJacob-DPDKUSASummit2016.pdf

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux阅码场 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档