前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RPS与RFS实现分析

RPS与RFS实现分析

作者头像
glinuxer
发布2019-04-10 14:59:49
2.5K0
发布2019-04-10 14:59:49
举报
文章被收录于专栏:专注网络研发专注网络研发

RPS和RFS是google贡献的两个补丁,在2.6.35版本中,正式被合并入了内核。这两个补丁总体来说,并不算复杂,实际上很多网络设备厂商早已在自己的产品中,有了类似的应用。但这个涉及到厂商的主营业务,所以不会做任何开源。

前几天在画RPS和RFS的流程图时,发现关于RPS和RFS的实现分析并不多,大部分都是设置和性能对比。所以,今天就炒炒冷饭,聊聊RPS与RFS的实现。

下面是较为完整的RPS&RFS的流程图。

因为微信会压缩图片,看不清楚的同学还是请访问https://raw.githubusercontent.com/gfreewind/kernel_skb_path/master/kernel_skb_path.jpg。

首先,我们要看为什么要有RPS和RFS?在没有它们的年代,网卡触发了接收中断之后,由一个CPU进行处理硬中断,接着在软中断里将数据包向上传递给协议栈。也就是说,哪个CPU响应了网卡中断,就由哪个CPU对该数据包进行全部的处理工作。在单核的时代,这样是没有问题的,但随着多核时代的到来,这就要求硬件必须较为均衡的将中断分发给不同的CPU。这对硬件提出了要求,莫说普通的单队列网卡,就是多队列网卡,也经常碰到在某些主板上,多个队列的中断都被发给一个CPU处理。这时,就需要在软件上做进一步均衡,也就是RPS的目的。

RPS全称为Receive Packet Steering,以TCP报文为例,RPS根据三层协议的IP报文的源地址和目的地址,四层协议的源端口和目的端口,进行hash运算后,确定由哪个CPU处理该报文。这样就弥补了硬件的不足,即使硬中断负载不均,通过RPS再处理,尽力保证各CPU的负载均衡。

而RFS是在RPS基础上更进一步,其全称是Receive Flow Steering,顾名思义RPS只针对数据包,没有对会话做任何考虑,而RFS则尽力保证同一会话仍然由“上次”的CPU处理,这样可以保证cache的热度,提高cache的命中率。

RPS的设置,通过/sys/class/net/eth0/queues/rx-0/rps_cpus设置CPU掩码,每一位对应一个CPU ID,RPS会将数据包在这几个CPU之间进行分发。RFS则略为复杂,既要通过/proc/sys/net/core/rps_sock_flow_entries设置全局的表项大小,也要通过/sys/class/net/eth0/queues/rx-0/rps_flow_cnt设置网卡接收队列的表项大小。

下面将从源码角度,分析RPS和RFS的实现。get_rps_cpu是RPS和RFS处理的入口函数。本文不对其做全面的分析,只对几个重要的关键点做一些分享。

1. 数据包的hash运算

目前很多网卡支持了receive-hashing,即由硬件对数据包做hash运算,驱动会调用skb_set_hash设置结果。这时skb的l4_hash就被设置为true,然后直接返回skb->hash结果。但有时会遇到硬件hash结果有问题,即hash结果比较集中,不分散,从而导致RPS的效果不好。这时就需要关闭网卡的receive-hashing功能。至于软件如何根据数据包计算hash,有兴趣的同学自行研究__skb_get_hash的实现即可。—— BTW,由于支持的协议越来越多,这部分代码也越来越复杂。(再吹个牛,PPTP的RPS支持,是我贡献的:))

2. RFS的全局表项匹配

要看懂这段代码,就要理解RFS的表项ents的结构。

每个ent,由2部分组成。前半部分,是表项对应的CPU,后半部分是hash结果。因此((ident ^ hash) & ~rps_cpu_mask)实际上就是用来匹配后面的hash ident结果。如果匹配,其值为0,如果不匹配,即没有对应RFS表项,则直接使用RPS获取对应的CPU。

3. RFS切换CPU的条件

next_cpu是RFS计算得来的本次sbk需要由哪个cpu处理,tcpu保存的是该flow上次由哪个cpu处理。当两者不一致时,即要切换处理flow的cpu。切换的条件如下:

1) tcpu != next_cpu —— 这个无需多言:)

2) tcp >= nr_cpu_ids,即没有设置处理该flow的cpu;

3)!cpu_online(tcpu),即tcpu已经不在线;

4)per_cpu(softnet_data, tcpu).input_queue_head - rflow->last_qtail >= 0,这个稍微隐晦些。input_queue_head是tcpu当前处理数据包的序号,而rflow->last_qtail记录的是上次该flow在tcpu上待处理的skb的序号。前者大于等于后者时,意味着在tcpu上,属于该flow的skb数据包已经被tcpu处理完毕。之所以要有这个条件,就是为了保证RFS在切换CPU时,也要保证flow的数据包不会被乱序处理。

当切换条件不满足时,RFS仍然会选择上次的cpu,即tcpu。

4. cpu记录RFS表项

既然RFS要保证局部性,所以在CPU开始处理该flow的数据包时,就要记录RFS表项,如在tcp_v4_do_rcv中,调用sock_rps_record_flow_hash来记录是当前CPU处理了该flow。

好了,通过流程图和几个关键点的分析,我相信大家对RPS和RFS已经比较清晰了。

PS:阅读内核代码,除了可以了解OS内部运行机制,还可以锻炼对大型软件的掌控,提高自己对复杂工程的分析能力。

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

本文分享自 LinuxerPub 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档