主题
实验 19:用户态 datapath
实验 1 里 ovs-dpctl show 的第一行是:
text
system@ovs-system:system 表示内核 datapath。这一章换成另一种:netdev,也就是用户态 datapath。
两种 datapath
text
内核 datapath(system) 用户态 datapath(netdev)
┌──────────────────────┐ ┌──────────────────────┐
│ ovs-vswitchd │ │ ovs-vswitchd │
│ 完整流表 │ │ 完整流表 │
│ │ │ + 转发缓存 │
│ │ │ + 实际转发 │
└──────────┬───────────┘ └──────────┬───────────┘
upcall│装缓存 收发包 │
┌──────────┴───────────┐ ┌──────────┴───────────┐
│ 内核 openvswitch 模块 │ │ 网卡 │
│ 转发缓存 + 实际转发 │ │ DPDK / AF_XDP / │
└──────────┬───────────┘ │ AF_PACKET │
│ └──────────────────────┘
┌──────────┴───────────┐
│ 网卡 │
└──────────────────────┘system(内核) | netdev(用户态) | |
|---|---|---|
| 转发在哪 | 内核模块 | ovs-vswitchd 进程内 |
| 慢路径 | upcall 跨内核/用户态边界 | 同进程内调用,没有边界 |
| 收发包方式 | 内核网络栈 | DPDK / AF_XDP / AF_PACKET |
| CPU 占用 | 按需 | PMD 线程通常 100% 轮询 |
| 需要特殊网卡驱动 | 否 | DPDK 模式需要 |
| 极限性能 | 中 | 高(DPDK 可达线速) |
| 适合 | 通用场景 | NFV、高吞吐转发 |
用户态 datapath 的核心思路是绕过内核网络栈,配合 DPDK 用轮询代替中断,用大页内存减少 TLB miss,从而把小包转发性能提上去。代价是 CPU 常驻占用。
起环境
bash
cd labs/learn-ovs/16-userspace-dp
./start.sh先看这台机器支持哪些类型:
bash
docker exec clab-userspace-dp-ovs1 ovs-vsctl get Open_vSwitch . datapath_typestext
[netdev, system]这个字段实验 2 里见过,现在派上用场了。
建一个 netdev 网桥
只差一个字段:
bash
N="docker exec clab-userspace-dp-ovs1"
$N ovs-vsctl add-br br0 -- set Bridge br0 datapath_type=netdev
$N ovs-vsctl add-port br0 eth1
$N ovs-vsctl add-port br0 eth2
$N ip link set br0 updatapath_type 必须在建网桥时或之后立刻设
改一个已经在跑的网桥的 datapath_type,OVS 会销毁重建它的 datapath,所有流表和端口状态都会重置。生产环境上别这么干。
验证:
bash
docker exec clab-userspace-dp-h1 ping -c 3 192.168.10.2text
3 packets transmitted, 3 received, 0% packet loss通了,而且转发全程没有进过内核 openvswitch 模块。
证据:内核 datapath 是空的
bash
$N ovs-dpctl showtext
(没有任何输出)ovs-dpctl 直接和内核模块对话(实验 2 那张表),它看不到任何东西 —— 因为这个网桥的 datapath 压根不在内核里。
要看用户态 datapath,必须走 ovs-vswitchd:
bash
$N ovs-appctl dpctl/showtext
netdev@ovs-netdev:
lookups: hit:5 missed:7 lost:0
flows: 7
port 0: ovs-netdev (tap)
port 1: br0 (tap)
port 2: eth1
port 3: eth2这就是必须用 ovs-appctl dpctl/ 的另一个理由
实验 1 说过日常应该用 ovs-appctl dpctl/... 而不是 ovs-dpctl。用户态 datapath 下这不再是"建议",而是唯一可行的方式 —— ovs-dpctl 完全看不到 netdev datapath。
注意 port 0: ovs-netdev (tap) 和 port 1: br0 (tap):netdev 模式下 internal 端口是通过 tap 设备接回 Linux 协议栈的,而内核模式下它们是 internal 类型。
上层完全不变
这是最值得强调的一点。流表、trace、MAC 表 —— 所有命令和语法一模一样:
bash
$N ovs-ofctl dump-flows br0text
cookie=0x0, table=0, n_packets=12, priority=0 actions=NORMALbash
$N ovs-appctl ofproto/trace br0 "in_port=1,dl_src=<h1MAC>,dl_dst=<h2MAC>"text
bridge("br0")
-------------
0. priority 0
NORMAL
-> forwarding to learned port
Datapath actions: 3换 datapath 只影响最底层那一跳。 前面十八章学的东西全部适用。
用户态的缓存结构更复杂
bash
$N ovs-appctl dpctl/dump-flowstext
flow-dump from the main thread:
recirc_id(0),in_port(2),packet_type(ns=0,id=0),
eth(src=aa:c1:ab:ab:fb:f2,dst=ff:ff:ff:ff:ff:ff),
eth_type(0x0806),arp(...), actions:1,3格式和内核的 megaflow 基本一样,多了个 packet_type 字段。
但内部结构不同 —— 看 PMD 统计:
bash
$N ovs-appctl dpif-netdev/pmd-stats-showtext
main thread:
packets received: 27
packet recirculations: 0
avg. datapath passes per packet: 1.00
phwol hits: 0
mfex opt hits: 0
simple match hits: 0
emc hits: 0
smc hits: 0
megaflow hits: 16用户态 datapath 有四级缓存,从快到慢:
| 层级 | 全称 | 说明 |
|---|---|---|
phwol | Partial HW Offload | 网卡硬件已经算好了流标识 |
emc | Exact Match Cache | 精确匹配缓存,最快,容量小(默认 8192) |
smc | Signature Match Cache | 签名匹配,比 EMC 省内存 |
megaflow | —— | 带掩码的匹配,和内核 megaflow 同一个概念 |
一个包依次尝试这几级,全都不中才上到完整流表。
emc hits 占比是用户态转发的核心性能指标。 EMC 命中率低意味着流数量超过了缓存容量,性能会明显下降。
这里没有 PMD 线程
输出里是 main thread:,不是 pmd thread numa_id 0 core_id 2:。
因为我们没配 DPDK,也没设 pmd-cpu-mask。这种情况下转发由 ovs-vswitchd 主线程用 AF_PACKET 完成 —— 能跑通,但完全没有性能优势,反而比内核 datapath 慢(多了内核到用户态的拷贝)。
真正的性能来自下面这些配置。
走向真正的高性能
本实验只验证了机制。生产环境的 OVS-DPDK 需要:
一、初始化 DPDK
bash
ovs-vsctl set Open_vSwitch . other_config:dpdk-init=true
ovs-vsctl set Open_vSwitch . other_config:dpdk-socket-mem="1024,1024"需要提前配好大页内存(hugepages)并重启 ovs-vswitchd。
二、绑定 PMD 线程到专用 CPU
bash
ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x6PMD(Poll Mode Driver)线程忙轮询收包,会把绑定的核占到 100%。这不是异常,是设计如此 —— 轮询消除了中断延迟。
绑核要避开内核和其它业务用的核,并且注意 NUMA 亲和性:网卡和 PMD 线程应该在同一个 NUMA 节点。
三、用 DPDK 类型的端口
bash
ovs-vsctl add-port br0 dpdk0 \
-- set Interface dpdk0 type=dpdk options:dpdk-devargs=0000:01:00.0网卡要先用 dpdk-devbind 从内核驱动解绑,交给 vfio-pci。解绑之后内核就看不到这块网卡了,ip link 里会消失。
四、虚拟机接入用 vhost-user
bash
ovs-vsctl add-port br0 vhu0 \
-- set Interface vhu0 type=dpdkvhostuserclient \
options:vhost-server-path=/var/run/openvswitch/vhu0虚拟机通过共享内存直接和 OVS 交换报文,绕过宿主机内核。
AF_XDP:折中方案
不想上 DPDK 那套重型配置,可以用 AF_XDP:
bash
ovs-vsctl add-port br0 eth1 -- set Interface eth1 type=afxdp它利用内核的 XDP 钩子在协议栈之前处理包,性能不如 DPDK 但不需要解绑网卡驱动,部署轻量得多。Open_vSwitch.iface_types 里能看到 afxdp 和 afxdp-nonpmd。
什么时候该用
绝大多数场景用内核 datapath。 它够快、零配置、和 Linux 生态完全兼容。
考虑用户态 datapath 的信号:
- 小包(64 字节)转发能力成为瓶颈,需要百万 pps 以上
- NFV 场景,虚拟机之间要跑高吞吐流量
- 有专门的核可以拿来做转发(PMD 会占满)
- 团队有能力维护大页、绑核、NUMA 这些配置
用户态 datapath 不是免费的性能提升
配错了会比内核 datapath 更慢(就像本实验的 AF_PACKET 模式)。而且它引入了一整套新的运维复杂度:大页内存、CPU 隔离、NUMA 亲和、网卡驱动解绑。
先确认内核 datapath 真的成了瓶颈,再考虑迁移。第 6 部分会讲怎么测。
自动检查
bash
./verify.shtext
[通过] br0 的 datapath_type 是 netdev
[通过] 存在 netdev@ovs-netdev 这个 datapath
[通过] 内核 datapath 里没有 br0 的端口
[通过] 用户态转发正常:h1 能 ping 通 h2自测清单
system和netdev两种 datapath,转发分别发生在哪里?- 建 netdev 网桥需要改什么?
- 为什么
ovs-dpctl show看不到 netdev datapath?该用什么命令? - 用户态 datapath 有哪四级缓存?哪个指标最关键?
- 本实验里为什么没有 PMD 线程?这意味着什么?
- PMD 线程 CPU 占用 100% 是异常吗?
- 什么信号说明该考虑用户态 datapath?
常见问题
建了 netdev 网桥但端口报错
bash
ovs-vsctl show | grep -A3 errornetdev datapath 对端口类型更挑。普通 Linux 设备(veth、物理网卡)走 AF_PACKET 是支持的,但某些虚拟设备类型不支持。
ovs-dpctl 什么都不显示
预期行为。 netdev datapath 不在内核里,用:
bash
ovs-appctl dpctl/show
ovs-appctl dpctl/dump-flows性能比内核 datapath 还差
没配 DPDK 或 AF_XDP 时就是这样 —— AF_PACKET 模式多了一次内核到用户态的拷贝。
要性能必须配 DPDK(大页 + 绑核 + 网卡解绑)或至少 AF_XDP。
dpdk-init 设了但 ovs-vswitchd 起不来
看日志:
bash
tail -50 /var/log/openvswitch/ovs-vswitchd.log最常见的是大页内存没配够。检查:
bash
cat /proc/meminfo | grep -i hugePMD 线程把 CPU 占满了
这是设计如此。 PMD 忙轮询以消除中断延迟。用 pmd-cpu-mask 把它限制在专用核上,别让它和业务或内核抢核。
小结
- OVS 有两种 datapath:
system(内核模块) 和netdev(用户态) - 切换只需
set Bridge br0 datapath_type=netdev,上层流表、trace、命令全部不变 - netdev datapath 下
ovs-dpctl完全看不到,必须用ovs-appctl dpctl/... - 用户态有四级缓存:phwol → EMC → SMC → megaflow,
emc hits占比是核心性能指标 - 不配 DPDK/AF_XDP 时走 AF_PACKET,能跑通但比内核 datapath 更慢
- 真正的性能需要:DPDK 初始化 + 大页内存 + PMD 绑核 + 网卡驱动解绑
- PMD 线程占满 CPU 是设计如此(忙轮询消除中断延迟)
- AF_XDP 是轻量折中:不用解绑网卡,性能介于两者之间
- 绝大多数场景用内核 datapath;先确认它真成了瓶颈再迁移
下一章:OVN 是什么 —— 把前面所有零件组装成一个完整的虚拟网络系统。