OVS中的流缓存设计

发布时间 2023-04-18 14:25:17作者: salami_china

文档下载链接:https://www.mesalab.cn/download?id=1169&url=f%2Farticle%2Fdetail%3Fid%3D320

Microflow

在2007年,当在Linux上开始开发将成为Open vSwitch的代码时,只有内核数据包转发才能切实实 现良好的性能,因此最初的实现将所有OpenFlow处理都放入了内核模块中。 该模块从NIC或VM接收到 一个数据包,该数据包通过OpenFlow表进行分类(具有标准的OpenFlow匹配和操作),并在必要时对 其进行修改,最后将其发送到另一个端口。由于在内核中开发和更新内核模块相对困难,因此该方法很 快变得不切实际。

Open vSwitch采取的解决方案是将内核模块重新实现为microflow,其中单个缓存条目与 OpenFlow支持的所有数据包头字段精确匹配。 通过将内核模块实现为简单的哈希表而不是复杂的通用 数据包分类器,从而支持任意字段和掩码,从而实现了根本简化。 在这种设计中,缓存条目是非常细粒 度的,并且与大多数单个传输连接的数据包都匹配。但是对于单个传输连接,即使是网络路径的更改以 及IP TTL字段的更改也会导致未命中,并转移数据包发送到用户空间,用户空间将参考实际的OpenFlow 流表来决定如何转发它。 这意味着影响性能的关键指标是流建立时间,即内核将缓存“未命中”报告给用 户空间以及用户空间进行回复所花费的时间。

在多个Open vSwitch版本上,都采用了多种技术来减少microflow缓存的流建立时间。 例如,通过 减少设置给定microflow所需的平均系统调用次数批量处理流的方法,总共可将性能提高约24%。 最终,我们还将流设置负载分配到了多个用户空间线程上,从而受益于多个CPU内核,等等。

尽管microflow缓存适用于大多数流量模式,但面对大量短连接时,其性能会严重下降。 在这种情 况下,许多数据包会丢失高速缓存,不仅必须跨越内核-用户空间边界,而且还必须执行一系列昂贵的数 据包分类。 尽管批处理和多线程可以减轻这种压力,但它们不足以完全支持这类工作负载。这要求 Open vSwitch重新考虑流缓存设计。

 

Megaflow

为了避免这种性能极度恶化的情况,Open vSwitch 引入了 MegaFlow。和 MicroFlow 的精确匹配 不同,MegaFlow 可以做到模糊匹配,一个条目可以匹配一组数据包。它的实现和用户态的 TSS 类似, 但是在组织上有所不同。

一是没有优先级,这样可以快速返回无需遍历所有的哈希表;

二是 MegaFlow 中不像用户态中大量 table 组成了 pipeline,只通过一个 table 来进行匹配。

我们来看下面一条OpenFlow流表规则。

priority=200,ip,nw_dst=10.0.0.0/16 actions=output:1

它的作用就是匹配所有目的IP是10.0.0.0/16的网络包,从网卡1送出。对于下面的传输层连接,对应 的action都是一样的。

11.0.0.2:5742 -> 10.0.0.10:3306
11.0.0.2:5743 -> 10.0.0.10:3306
11.0.0.2:5744 -> 10.0.0.11:3306
11.0.0.3:5742 -> 10.0.0.10:3306

但是对应于microflow cache来说,就要有4条cache,需要上送4次ovs-vswitchd。但是实际上,如 果在kernel datapath如果有下面一条cache,只匹配目的IP地址:

ip,nw_dst=10.0.0.0/16 actions=output:1

那么数以亿计的传输层连接,可以在OVS内核模块,仅通过这条cache,完成转发。因为只有一条 cache,所以也只有一次上送ovs-vswitchd。这样能大大减少内核态向用户态上送的次数,从而提升网 络性能。这样一条非精确匹配的cache,被OpenVSwitch称为megaflow。

MegaFlow Cache 性能最关键的就是看如何能实现更好的泛化能力,即每个条目都能匹配尽可能多 的数据包,减少用户态和内核态之间进行交互的次数。同时需要尽可能降低哈希查询的次数,在尽可能 少的表里得到预期的结果。

# Megaflow 需要多次 hash table
在 OVS 中,Megaflow 需要查询多次 hash table 是因为 Megaflow 是由多个字段组成,每个字段都需要在相应的 hash table 中查找匹配的流表项。例如,Megaflow 可能包含源 MAC 地址、目的 MAC 地址、源 IP 地址、目的 IP 地址、协议类型等字段,每个字段都需要在相应的 hash table 中进行查询,才能找到匹配的流表项。
此外,OVS 中的 hash table 是基于哈希函数实现的,哈希函数可能会出现碰撞,导致多个流表项映射到同一个桶中。在这种情况下,需要对该桶中的所有流表项进行线性搜索,以找到与 Megaflow 匹配的流表项。因此,Megaflow 需要查询多次 hash table,以确保找到所有与 Megaflow 匹配的流表项。
# Microflow 需要一次 hash table
在 OVS 中,Microflow 只需要查询一次 hash table 是因为 Microflow 是由固定数量的字段组成,包括源 MAC 地址、目的 MAC 地址、协议类型、源端口号和目的端口号。这些字段被组合成一个 5-tuple,可以通过哈希函数转换为一个哈希值,然后用该哈希值在 OVS 的 flow table 中查找匹配的流表项。由于 Microflow 的字段数量是固定的,且哈希函数能够有效地将 5-tuple 映射到 flow table 中的桶中,因此只需要查询一次 hash table 就能够找到与 Microflow 匹配的流表项。

 

Microflow vs Megaflow

Microflow的优点是,只有一个Hash table,一旦缓存建立,内核中只需要查找一次Hash table即 可完成转发

Microflow的缺点是,每个不同的网络连接都需要一次内核空间上送用户空间,这有点多。

Megaflow的优点是,合并了拥有相同的datapath action的网络连接所对应的缓存,对于所有这些网络连接,只需要一 次内核空间上送用户空间

Megaflow的缺点是,缓存建立以后,要查多次Hash table。如果每个Hash table命中的概 率一样,那么平均需要查找 (n+1)/2 次hash table,最坏要查n个hash table。

 

现行方案

OVS内核模块采用Microflow+Megaflow的两级cache模式。Microflow作为第一级缓存,还是匹配 所有OpenFlow可能的匹配的值,但是这时对应的Action不再是Datapath action,而是送到某个 Megaflow的Hash table。当网络包送到kernel datapath时,会先在Microflow cache中查找,如果找到 了,那么再接着送到相应的Megaflow hash table继续查找;如果没有找到,网络包会在kernel datapath的Megaflow cache中继续查找,如果找到了,会增加Microflow cache的记录,将后继类似的 包直接指向相应的Megaflow Hash table;如果在Megaflow还是没有找到,那么就要上送ovsvswitchd,通过OpenFlow pipeline生成Megaflow cache。

这样不论Megaflow cache有多少个hash table,对于长连接来说,只需要查找两次hash table即可 完成转发,长连接的性能得到了保证。对于短连接来说,因为Megaflow cache的存在,也不用频繁的上 送ovs-vswitchd,只需要在OVS内核模块多查几次hash table就行,短连接的性能也得到了保证。