专栏首页专注网络研发nfconntrack全局锁的优化

nfconntrack全局锁的优化

nfconntrack是netfilter中的重要模块,很多netfilter的功能都依赖于这个模块,如NAT等。而利用linux来构建的网络设备,可以说,其80%的功能都依赖于nfconntrack实现的会话管理。所以,会话管理的性能优劣会对网络设备的性能产生直接的影响。

在网络设备中,对性能影响比较显著的就是锁的使用。可以说,网络设备的性能优化,很大程度上都是对于锁的优化。

nfconntrack中一个最重要的锁,就是全局的nf_conntrack_lock。这个锁的作用是保护全局会话表。当CPU尝试用当前数据包skb进行会话匹配,或者准备插入新的会话时,都需要对nf_conntrack_lock进行上锁。试想,在大流量的环境中,每个经过设备的数据包都要进行会话匹配,在多核的情况下,对这个锁的竞争是非常激烈的。

这是commiter做这个优化时,使用perf对内核进行的profile,可以清楚的看到对这个锁的spin_lock占用了大量的CPU,是性能的主要瓶颈。 —— 笔者以前从事的就是网络设备的开发工作,之前一直没有想到nfconntrack居然使用了这么长时间的一个全局锁。对于设备厂商来说,这个是早就应该进行的优化。

当确定nf_conntrack_lock全局锁为性能瓶颈时,我们应该怎样优化呢?这个问题可以一般化为,如何优化一个锁?最理想的情况,就是去掉这个锁。实现这个目的,一般可以使用空间换时间,或者使用无锁算法。对于会话表来说,锁是充分且必要的。因为必须要保证会话在会话表中的唯一性,查询和插入必须是原子的,不可中断的。因此必须使用锁来保证安全。第二条路,就是减小锁的粒度。这个也是这个优化的主要思想。

netfilter的会话,被分为两个部分,一个是original tuple,另外一个是reply tuple,两个tuple分别要插入两个不同的hash表中,用于两个方向上的匹配。现在要减小锁的粒度,最直观的想法是,将以前的全局锁,变成基于桶的锁,一个hash桶就使用一个锁。这个想法,从思路上是没有问题的,但是对于会话表来说,其桶的个数一般很大。

通过上面的命令,可以得到linux默认的nfconntrack的桶的个数。对于网络设备来说,有时候还要加大桶的个数。如果每个桶一个锁的话,会消耗掉不少内存。

这个commit采用了一个折中的办法。每个锁负责保障多个桶的安全,这样就不需要一个桶对应一个锁了,这样既提高了并发性,又没有增加太多内存消耗。

其定义了一个nf_contrack_locks数组,用户可以根据自己的环境,定义不同的CONNTRACK_LOCKS大小。

当使用多个锁来保护会话表后,就容易引入deadlock问题。试想,如果有两把锁A和B,分别用于保护不同的hash表的桶的集合。假设有两个tuple,它们被插入到两个不同的hash表的桶中。其中original tuple所在的桶,由锁A保护,reply tuple所在的桶,由锁B保护。如果两个方向的数据包同时到达两个不同的CPU1和2,则CPU1已经获得了锁A,并尝试获得锁B,而CPU已经获得锁B,并尝试获得锁A。这时,两个CPU都拿到一个锁,并尝试获得对方的锁,但肯定又无法拿到,于是这两个CPU就死锁了,系统也就hang住了。这个问题是在多锁环境下,经常遇到的问题。解决的方法就是:一定要保证上锁的顺序,并保持一致。

下面看看这个commit是如何解决这个问题的。

  1. 创建了一个新的函数nf_conntrack_double_lock用于封装上锁动作,保证所有代码使用一致的策略。
  2. 对于两个锁,每次上锁的时候,都是先获取索引较小的锁,再获取索引较大的锁。
  3. 判断两个锁的索引是否一致,避免对同一个锁进行两次上锁的操作。

通过上面3个技巧,就保证了新的nf_conntrack_locks,不会产生死锁。

对于nf_conntrack_lock全局锁优化的commit是93bb0ceb75be,感兴趣的同学可以自己阅读这个patch。没记错的话,这个commit是在3.18内核版本中引入的。—— 真没想到,这么晚才有人优化这个锁:(

PS:后面会继续分析对nfcontrack中锁的优化。

本文分享自微信公众号 - LinuxerPub(LinuxerPub),作者:glinuxer

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-07-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux内核那些事之连接跟踪

    连接跟踪(也叫会话管理)是状态防火墙关键核心,也是很多网元设备必不可少的一部分。各厂商的实现原理基本雷同,只是根据各自的业务进行修改和优化。其中,还有不少厂商干...

    glinuxer
  • TCP接收窗口的实现(一)

    TCP首部中的Window字段,表示当前套接字的接收窗口,即目前可以接收的数据大小,对端不会发送超过接收窗口大小的数据。如果在三次握手时,两端都支持Window...

    glinuxer
  • 系统编程语言Rust特点介绍(1)

    最近在学习Rust语言,感觉这门语言有点意思,因此写一篇文章分享。我不会去介绍Rust的基本语法,什么变量声明,if..else..,循环等等。这些东西,文档介...

    glinuxer
  • C++运算符重载形式——成员函数or友元函数

    运算符重载是C++多态的重要实现手段之一。通过运算符重载对运算符功能进行特殊定制,使其支持特定类型对象的运算,执行特定的功能,增强C++的扩展功能。

    Dabelv
  • openstack集群访问外部服务出现访问失败

    openstack私有云中的容器服务A(部署在openshift上)需要通过http访问阿里云中的B服务,中间需要经过openstack的nat网关,以及阿里云...

    charlieroro
  • Kubernetes v1.8 对 GPU 的支持

    在人工智能和深度学习领域,算法工程师对 GPU 的使用越来越多,因此用户也希望通过 Kubernetes 来为模型训练任务申请 GPU 资源。

    runzhliu
  • 物联网知识科普(1)-定义和发展历程

    “物联网”(英文Internet of Things简称IOT)指的通过射频识别(RFID)(RFID+互联网)、红外感应器、全球定位系统、激光扫描器、气体感应...

    物流IT圈
  • 关于版本号:alpha、beta、rc、stable

    但是对于版本怎么定义,规则如何确定,却是千差万别。具体应用,可以结合自己目前的实际情况命名;

    随心助手
  • GeForce RTX 30:AV1解码迎来视频内容的新时代

    原文 / https://www.nvidia.com/en-us/geforce/news/rtx-30-series-av1-decoding

    LiveVideoStack
  • mongodb 部署 安装 使用 记录

    最近使用tikv作为nosql存储,发现有很多坑,很多工具都不是很完善;不想折腾;于是换成了mongodb,mongodb部署记录如下,进行备忘:

    xuyaowen

扫码关注云+社区

领取腾讯云代金券