免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 14339 | 回复: 8
打印 上一主题 下一主题

linux协议栈之xps特性分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-12-13 02:08 |只看该作者 |倒序浏览
本帖最后由 simohayha_cu 于 2010-12-13 02:23 编辑

xps全称是Transmit Packet Steering,是rfs/rps的作者Tom Herbert提交的又一个patch,预计会在2.6.37进入内核。

patch的详细信息可以看这里:
http://amailbox.org/mailarchive/linux-netdev/2010/8/23/6283586

这个patch主要是针对多队列的网卡发送时的优化,当发送一个数据包的时候,它会根据cpu来选择对应的队列,而这个cpu  map可以通过sysctl来设置:
  1. /sys/class/net/eth<n>/queues/tx-<n>/xps_cpus
复制代码
这里xps_cpus是一个cpu掩码,表示当前队列对应的cpu。

而xps主要就是提高多对列下的数据包发送吞吐量,具体来说就是提高了发送数据的局部性。按照作者的benchmark,能够提高20%.

原理很简单,就是根据当前skb对应的hash值(如果当前socket有hash,那么就使用当前socket的)来散列到xps_cpus这个掩码所设置的cpu上,也就是cpu和队列是一个1对1,或者1对多的关系,这样一个队列只可能对应一个cpu,从而提高了传输结构的局部性。

没有xps之前的做法是这样的,当前的cpu根据一个skb的4元组hash来选择队列发送数据,也就是说cpu和队列是一个多对多的关系,而这样自然就会导致传输结构的cache line bouncing。

这里还有一个引起cache line bouncing的原因,不过这段看不太懂:
Also when sending from one CPU to a queue whose
transmit interrupt is on a CPU in another cache domain cause more
cache line bouncing with transmit completion


接下来来看代码,我这里看得代码是net-next分支,这个分支已经将xps合并进去了。

先来看相关的数据结构,首先是xps_map,这个数据结构保存了对应的cpu掩码对应的发送队列,其中queues队列就保存了发送对列.这里一个xps_map有可能会映射到多个队列。
  1. struct xps_map {
  2. //队列长度
  3.         unsigned int len;
  4.         unsigned int alloc_len;
  5.         struct rcu_head rcu;
  6. //对应的队列序列号数组
  7.         u16 queues[0];
  8. };
复制代码
而下面这个结构保存了设备的所有的cpu map,比如一个设备 16个队列,然后这里这个设备的xps_dev_maps就会保存这16个队列的xps map(sysctl中设置的xps_map),而每个就是一个xps_map结构。
  1. struct xps_dev_maps {
  2. //rcu锁
  3.         struct rcu_head rcu;
  4. //所有对列的cpu map数组
  5.         struct xps_map __rcu *cpu_map[0];
  6. };
复制代码
然后就是net_device结构增加了一个xps_dev_maps的域来保存这个设备所有的cpu map。
  1. struct net_device {
  2. ................................
  3. #ifdef CONFIG_XPS
  4. //保存当前设备的所有xps map.
  5.         struct xps_dev_maps __rcu *xps_maps;
  6. #endif
  7. ..........................
  8. }
复制代码
我们知道内核发送数据包从ip层到驱动层是通过调用dev_queue_xmit,而在dev_queue_xmit中会调用dev_pick_tx来选择一个队列,这里这个patch就是修改这个函数,我们接下来就来看这个函数。

先来分析下这个函数的主要流程,首先,如果设备只有一个队列,那么就选择这唯一的队列。
  1. if (dev->real_num_tx_queues == 1)
  2.                 queue_index = 0;
复制代码
然后如果设备设置了回调函数ndo_select_queue,则调用ndo_select_queue来选择队列号,这里要注意,当编写驱动时,如果设置了回调函数ndo_select_queue,此时如果需要xps特性,则最好通过get_xps_queue来取得队列号。
  1. else if (ops->ndo_select_queue) {
  2.                 queue_index = ops->ndo_select_queue(dev, skb);
  3.                 queue_index = dev_cap_txqueue(dev, queue_index);
  4. }
复制代码
然后进入主要的处理流程,首先从skb从属的sk中取得缓存的队列索引,如果有缓存,则直接返回这个索引,否则开始计算索引,这里就通过调用xps patch最重要的一个函数get_xps_queue来计算queue_index.
  1. static struct netdev_queue *dev_pick_tx(struct net_device *dev,
  2.                                         struct sk_buff *skb)
  3. {
  4. ....................................
  5. else {
  6.                 struct sock *sk = skb->sk;
  7.                 queue_index = sk_tx_queue_get(sk);

  8.                 if (queue_index < 0 || skb->ooo_okay ||
  9.                     queue_index >= dev->real_num_tx_queues) {
  10.                         int old_index = queue_index;
  11. //开始计算队列索引
  12.                         queue_index = get_xps_queue(dev, skb);
  13.                         if (queue_index < 0)
  14. //调用老的计算方法来计算queue index.
  15.                                 queue_index = skb_tx_hash(dev, skb);
  16. ......................................................
  17.                 }
  18.         }
  19. //存储队列索引
  20.         skb_set_queue_mapping(skb, queue_index);
  21. //返回对应的queue
  22.         return netdev_get_tx_queue(dev, queue_index);
  23. }
复制代码
接下来我们来看get_xps_queue,这个函数是这个patch的核心,它的流程也很简单,就是通过当前的cpu id获得对应的xps_maps,然后如果当前的cpu和队列是1:1对应则返回对应的队列id,否则计算skb的hash值,根据这个hash来得到在xps_maps 中的queue的位置,从而返回queue id.
  1. static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
  2. {
  3. #ifdef CONFIG_XPS
  4.         struct xps_dev_maps *dev_maps;
  5.         struct xps_map *map;
  6.         int queue_index = -1;

  7.         rcu_read_lock();
  8.         dev_maps = rcu_dereference(dev->xps_maps);
  9.         if (dev_maps) {
  10. //根据cpu id得到当前cpu对应的队列集合
  11.                 map = rcu_dereference(
  12.                     dev_maps->cpu_map[raw_smp_processor_id()]);
  13.                 if (map) {
  14. //如果队列集合长度为1,则说明是1:1对应
  15.                         if (map->len == 1)
  16.                                 queue_index = map->queues[0];
  17.                         else {
  18. //否则开始计算hash值,接下来和老的计算hash方法一致。
  19.                                 u32 hash;
  20. //如果sk_hash存在,则取得sk_hash(这个hash,在我们rps和rfs的时候计算过的,也就是四元组的hash值)
  21.                                 if (skb->sk && skb->sk->sk_hash)
  22.                                         hash = skb->sk->sk_hash;
  23.                                 else
  24. //否则开始重新计算
  25.                                         hash = (__force u16) skb->protocol ^
  26.                                             skb->rxhash;
  27.                                 hash = jhash_1word(hash, hashrnd);
  28. //根据hash值来选择对应的队列
  29.                                 queue_index = map->queues[
  30.                                     ((u64)hash * map->len) >> 32];
  31.                         }
  32.                         if (unlikely(queue_index >= dev->real_num_tx_queues))
  33.                                 queue_index = -1;
  34.                 }
  35.         }
  36.         rcu_read_unlock();

  37.         return queue_index;
  38. #else
  39.         return -1;
  40. #endif
  41. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2010-12-13 09:32 |只看该作者
多谢分享,楼主测试过patch之后性能提升多少吗?

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
3 [报告]
发表于 2010-12-13 10:06 |只看该作者
感谢分享。RFS/RPS 还没来接深入研究呢,XPS就出来了。
XPS能够完全兼容 RPS么。

论坛徽章:
0
4 [报告]
发表于 2010-12-13 10:55 |只看该作者
回复 3# Godbach
XPS能够完全兼容 RPS么
XPS 和 RPS完全是不同的东西,XPS 是发数据, RPS 是收数据

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
5 [报告]
发表于 2010-12-13 11:04 |只看该作者
回复 4# raintung
晕。一看到多队列,就想到接收上了。

论坛徽章:
0
6 [报告]
发表于 2010-12-13 13:52 |只看该作者
貌似发送的开销本来就比接收小很多吧

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
7 [报告]
发表于 2010-12-13 16:10 |只看该作者
顶一下,这两个patch很给力

论坛徽章:
0
8 [报告]
发表于 2010-12-14 15:25 |只看该作者
看了一下Herbert的原文,并没有得出楼主的结论:
“一个队列只可能对应一个CPU“

原文是这么说的:
Each transmit queue can be associated with a number of CPUs which will
used the queue to send packets.  This is configured as a CPU mask on a
per queue basis in:

/sys/class/net/eth<n>/queues/tx-<n>/xps_cpus

显然,楼主的结论是错的。

txq发送完后会给某核一个中断,由CPU释放skb。中断绑定与XPS两码事,结果是:
CPU(n)发送skb,交给txq(m),中断却给了CPU(p)。CPU(n)和CPU(p)都要碰skb,自然造成所谓的“cache line bouncing"。

谈到“cache line bouncing",就要谈cache snooping。CPU通过cache snooping维护cache一致性。
http://www.tektalk.org/2010/12/0 ... %e5%8d%8f%e8%ae%ae/

CPU(n)和CPU(p)的故事可不止“cache line bouncing",NUMA呢?

楼主提到的局部化的确重要,局部化做的好,性能与核数会呈线性关系。

论坛徽章:
0
9 [报告]
发表于 2010-12-14 16:46 |只看该作者
看了一下Herbert的原文,并没有得出楼主的结论:
“一个队列只可能对应一个CPU“

原文是这么说的:
Ea ...
picktracy 发表于 2010-12-14 15:25



我这里说的有问题,应该是最好能够配置为一个队列对应一个cpu。而且这个cpu最好能够是这个队列的中断的亲缘cpu。这样就能够解决这个cache line bouncing。不过这样也只能针对多队列的网卡。
   

这里有个同学说的 eric dumazet 提的一个patch 能够解决单队列情况下的cache line bouncing(这个patch 在tom这个帖子里面也有提及),不过patch被拒了:

http://www.pagefault.info/?p=168#comment-52
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP