该函数是 OvS(Open vSwitch) 中的一个静态内联函数,用于进行快速路径处理。具体分析如下:
参数说明:
- pmd:指向 dp_netdev_pmd_thread 结构体的指针。
- packets_:指向 dp_packet_batch 结构体的指针,表示待处理的数据包批量。
- keys:指向 netdev_flow_key 指针数组的指针,用于存储每个数据包对应的流表键。
- batches[]:packet_batch_per_flow 数组,表示每个流表规则匹配到的数据包批量。
- n_batches:size_t 类型的指针,表示 batches 数组中有多少个元素。
- in_port:ODP 端口号。
函数实现:
首先获取待处理数据包数量 cnt,并定义 PKT_ARRAY_SIZE 变量为 cnt 或 NETDEV_MAX_BURST(如果是 Checker 或 MSVC,则定义为 NETDEV_MAX_BURST)。然后声明一些变量和结构体指针:
struct dp_packet *packet; struct dpcls *cls; struct dpcls_rule *rules[PKT_ARRAY_SIZE]; struct dp_netdev *dp = pmd->dp; int upcall_ok_cnt = 0, upcall_fail_cnt = 0; int lookup_cnt = 0, add_lookup_cnt; bool any_miss;
接下来遍历所有待处理数据包,并计算出每个流表键 netdev_flow_key 的长度:
for (size_t i = 0; i < cnt; i++) {
/* Key length is needed in all the cases, hash computed on demand. */
keys[i]->len = netdev_flow_key_size(miniflow_n_values(&keys[i]->mf));
}
然后获取 in_port 对应的分类器 cls,如果 cls 存在,则根据匹配规则在 cls 中进行匹配。如果有 miss 的数据包,则判断是否能够成功获得读写锁:
if (OVS_LIKELY(cls)) {
any_miss = !dpcls_lookup(cls, (const struct netdev_flow_key **)keys,
rules, cnt, &lookup_cnt);
} else {
any_miss = true;
memset(rules, 0, sizeof(rules));
} if (OVS_UNLIKELY(any_miss) && !fat_rwlock_tryrdlock(&dp->upcall_rwlock)) {
如果成功获得读写锁,则遍历所有待处理数据包,分别进行处理。对于已经匹配到规则的数据包,直接跳过;对于没有匹配到规则的数据包,先尝试在缓存中查找该流表键是否已经存在,若存在则将其加入 lookup_cnt 中,并将对应的规则添加到 rules 数组中;否则调用 handle_packet_upcall 函数进行上报处理。
如果无法获得读写锁,则直接遍历所有待处理数据包并删除它们:
DP_PACKET_BATCH_FOREACH (i, packet, packets) {
if (OVS_UNLIKELY(!rules[i])) {
dp_packet_delete(packet);
upcall_fail_cnt++;
}
}
最后再次遍历所有待处理数据包,并根据匹配到的规则将它们添加到相应的 batches 中:
DP_PACKET_BATCH_FOREACH (i, packet, packets) {
struct dp_netdev_flow *flow;
if (OVS_UNLIKELY(!rules[i])) {
continue;
}
flow = dp_netdev_flow_cast(rules[i]);
uint32_t hash = dp_netdev_flow_hash(&flow->ufid);
smc_insert(pmd, keys[i], hash);
emc_probabilistic_insert(pmd, keys[i], flow);
dp_netdev_queue_batches(packet, flow,
miniflow_get_tcp_flags(&keys[i]->mf),
batches, n_batches);
}
最后更新性能计数器,并结束函数执行。