一、什么是 handoff?
在 VPP 中,每个 worker thread 处理其本地的 input queue(例如设备接收到的包),而某些包可能需要由特定线程处理(比如为了保持同一 flow 的一致性),这时就需要将包从当前线程“交给”另一个线程——这就是 handoff。
简单理解为:
handoff = 数据包从当前线程交给另一个线程处理的过程
二、为什么需要 handoff?
多线程收包
多个 CPU core 收包时,不同的线程处理不同流量,但为了避免一个 flow 被多个线程处理造成状态同步开销,常常按 flow 做绑定。
保持流的一致性
比如:一个五元组为键的 TCP 连接要由同一个线程处理,确保 NAT/session 状态一致。
负载均衡或重新调度
在复杂处理逻辑中(如服务链、VNFs),包的后续处理可能需要在不同的 pipeline 上进行,跨线程更合理。
三、handoff 的应用场景
-
接口 RX queue 负载均衡
-
接收到的数据包根据 hash(如五元组)计算,分发给不同 worker。
-
-
节点之间线程迁移
-
某个 graph node 处理完后,发现需要将处理转给另一个线程,比如 NAT 后数据要交给 firewall,且 firewall 在线程2上。
-
-
多队列设备支持
-
高速网卡多个队列分发给多个线程处理时,handoff 可以将错误分发或需要特定线程处理的包重新调度。
-
四、handoff 的实现机制
在 VPP 中,handoff 本质上是通过以下机制实现:
1. Graph 节点机制
VPP 是一个基于 DAG(有向无环图)的 packet processing engine,每个节点可决定下一个节点,handoff 节点就是一个特殊的 node。
2. vlib_buffer_t->thread_index
包的元信息中可以记录目标线程。
3. vlib_buffer_enqueue_to_thread()
此函数用于将一组包发送到目标线程的 frame queue 中,是 handoff 的核心。
4. 调度机制
目标线程会从自己的 input node 拉取这些 packet frame,并继续处理。
五、示意流程图
(Thread 0) | +-----v------+ | RX Input | +------------+ | v +--------------------+ | classify/hash | <—— 计算目标线程 +--------------------+ | v +-------------------+ | handoff to T1 | ——> (Thread 1) +-------------------+ | v +---------------+ | graph nodes | +---------------+
六、代码例子(伪代码)
u32 next_thread = flow_hash % vlib_num_workers(); u32 *to_next = vlib_frame_vector_args(frame); u32 *thread_indices = ...; vlib_buffer_enqueue_to_thread(vm, node, buffer_indices, thread_indices, n_packets);
在 handoff node 中,可以根据 hash 得到目标线程,并将包 enqueue 到该线程。
七、相关配置命令(用于 interface 分发)
set interface rx-placement <interface> queue <queue-id> worker <worker-id>
或者:
set interface input node <interface> input <node-name> queue <queue-id> worker <worker-id>
用于控制不同接口队列上的包由哪个 worker 线程处理。
八、注意事项
-
Handoff 存在开销,需谨慎使用,频繁跨线程会影响性能。
-
应配合 flow 粘性(flow affinity)设计使用。
-
不同线程之间共享的数据结构需加锁或用 per-thread 数据结构。
总结
特性 | 描述 |
---|---|
功能 | 将 packet 从一个线程交给另一个线程处理 |
目的 | 实现负载均衡、线程绑定、功能分离等 |
实现 | 基于 vlib_buffer_enqueue_to_thread 和 input queues |
配置 | 可通过 CLI 设置接口的队列到线程映射 |
应用 | NAT、firewall、service chain、多队列设备等场景 |