多队列网卡介绍以及Suricata应用场景

一、基础

1.相关名词

IRQ

Interrupt Request,中断请求,从硬件层发出
作用:执行硬件中断的请求

SMP(Symmetrical Multi-Processing)

对称多处理器系统,是指在一个计算机上汇集了一组CPU,各CPU之间共享内存子系统以及总线结构(或者说是两个或多个同样的处理器通过一块共享内存彼此连接。)
作用:适用于多处理器计算机

APIC(Advanced Programmable Interrupt Controllers)

高级可编程中断控制器

松耦合多处理架构

最早的Linux SMP是松耦合多处理系统。这些系统是利用多个高速互连的单一系统构造的(如 10G 以太网、Fibre Channel 或 Infiniband)。构建松耦合多处理系统很容易,但是构建大型的多处理器网络可能占用相当大的空间并消耗很多电量。因为它们通常是利用普通硬件来构建的,所以包含的有些硬件不相关却要耗费很多电量和空间。更大的缺点在于通信结构。即使使用高速网络(如 10G 以太网),也存在系统可伸缩性的限制。

CMP

芯片多级处理。CMP一种紧密耦合多处理器,可以将它看作将松耦合架构缩小至芯片级。即在一个集成电路中,多个芯片、共享内存以及互连形成了一个紧密集成的多处理核心

 

2.中断的相关概念

Linux 内核对计算机上所有的设备进行管理,进行管理的方式是内核和设备之间的通信。解决通信的方式有两种:

  1. 轮询。轮询是指内核对设备状态进行周期性的查询
  2. 中断。中断是指在设备需要CPU的时候主动发起通信

从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如 8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断线。

  • 中断可以分为NMI(不可屏蔽中断)和INTR(可屏蔽中断)。

其中 NMI 是不可屏蔽中断,它通常用于电源掉电和物理存储器奇偶校验;INTR是可屏蔽中断,可以通过设置中断屏蔽位来进行中断屏蔽,它主要用于接受外部硬件的中断信号,这些信号由中断控制器传递给 CPU。

  • 常见的两种中断控制器:
    1.可编程中断控制器(PIC)8259A
    2.高级可编程中断控制器(APIC)

传统的 PIC(Programmable Interrupt Controller)是由两片 8259A 风格的外部芯片以“级联”的方式连接在一起。每个芯片可处理多达 8 个不同的 IRQ。因为从 PIC 的 INT 输出线连接到主 PIC 的 IRQ2 引脚,所以可用 IRQ 线的个数达到 15 个

二、多队列网卡CPU中断均衡

注意:本文全部是基于多核CPU环境而写,如果是单核CPU,没有任何意义

首先。我们要先判断当前系统环境是否支持多队列网卡,执行命令:

lspci -vvv  

Paste_Image.png

注意上图中红色部分。如果在Ethernet项中。含有[a0] MSI-X: Enable+ Count=9 Masked-语句,则说明当前系统环境是支持多队列网卡的,否则不支持。对于不支持多网卡队列的CPU均衡,将在下文分析。

或者使用

 ethtool  -l    ==》 查看网卡多队列情况 

 

1. Linux系统中断设置原理

为了在支持SMP的硬件上通过Linux使用SMP,需要适当的配置内核。
以网卡中断为例,如果流量大了,一个CPU处理会十分吃力,甚至崩溃;因为CPU处于忙碌状态,此时系统性能低,不能较快处理网卡接收的数据包。数据包堆积,网卡缓存溢出,导致丢包。这也是为什么负载高时会有网络丢包的原因了。但是服务器明明是多核CPU,为什么其他CPU没有参与处理了?原因是CPU没有做均衡时,默认是在CPU0上执行中断。虽然系统服务本身是有/etc/init.d/irqbalance 这个服务的作用就是用来做CPU均衡的,但是对于处理流量很大的服务器来说,这个服务的效果就微乎其微了,CPU均衡没有达到最优。
注意:如果要使用SMP(对称多处理系统),CPU需要内置APIC

中断绑定(CPU均衡)分为单队列网卡和多队列网卡两种情况。对于多队列网卡,开启SMP,如果只是开启SMP可能不会使CPU中断均衡达到最优。
可以同时开启SMP和RPS/RFS,使得CPU中断均衡达到最优(因为CPU核心可能会更多,但是网卡队列只有4-8个之类的,这个需要看具体机机型)。对于单队列网,只能开启RPS/RFS。

中断绑定——中断亲和力(IRQ Affinity)
维持亲和性是为了提高缓存效率

在 SMP 体系结构中,我们可以通过调用系统调用和一组相关的宏来设置 CPU 亲和力(CPU affinity),将一个或多个进程绑定到一个或多个处理器上运行。中断在这方面也毫不示弱,也具有相同的特性——中断亲和力。中断亲和力是指将一个或多个中断源绑定到特定的 CPU 核心上运行。
在 /proc/irq 目录中,对于已经注册中断处理程序的硬件设备,都会在该目录下存在一个以该中断号命名的目录,该目录下有一个 smp_affinity 文件(SMP 体系结构才有该文件),文件中的数据表示 CPU 位掩码,可以用来设置该IRQ与某个CPU的亲和力(默认值为 0xffffffff,表明把中断发送到所有的 CPU 上去处理),通过指定CPU 核心与某个中断的亲和性后,中断所对应的硬件设备发出的中断请求就都会给这个CPU核心处理。
如果中断控制器不支持 IRQ affinity,不能改变此默认值,同时也不能关闭所有的 CPU 位掩码,即不能设置成 0x0。
注意:SMP 绑定irq 到网卡 只对多队列网卡生效。其实也可以通过查看/proc/interrupt来查看系统是否支持多队列网卡——即,如果interrupt文件中含有ethN-xxx的就是多队列,如果只是ethN的就是单队列
**

2.相关目录文件以及实例

2.1中断相关文件

  • /proc/interrupts:该文件存放了每个I/O设备的对应中断号、每个CPU的中断数、中断类型【这边包含了所有的设备的中断以及中断对应的中断id】。

     

  • /proc/irq/:该目录下存放的是以IRQ号命名的目录,如/proc/irq/40/,表示中断号为40的相关信息

  • /proc/irq/[irq_num]/smp_affinity:该文件存放的是CPU位掩码(十六进制)。修改该文件中的值可以改变CPU和某中断的亲和性

  • /proc/irq/[irq_num]/smp_affinity_list:该文件存放的是CPU列表(十进制)。注意,CPU核心个数用表示编号从0开始,如cpu0,cpu1等

  • smp_affinity_list和smp_affinity任意更改一个文件都会生效,两个文件相互影响,只不过是表示方法不一致,但一般都是修改smp_affinity 文件

  • 以8核CUP为例,列出相关文件中如何表示CPU列表

 cup     二进制     smp_affinity_list      smp_affinity(十六进制)
cpu0     0001              0                                   1        
cpu1     0010              1                                   2
cpu2     0100              2                                   4
cpu3     1000              3                                   8
cpu4     010000            4                                   10
cpu5     0100000           5                                   40
......如上类推

在计算cpu亲和性时很容易混淆,我们先排除smp_affinity_list这项不看。只看二进制和和十六进制这两项。这里可以得出一个公式:
Python语法:

smp_affinity_value = hex(2**(N-1))

其中N代表的是CPU核心数。那么,为什么是"N-1"呢。原因很简单,因为对于多核服务器而言,cpu编号是从cpu0开始的。比如24核心的服务器,cpu编号为cpu0-cpu23

2.2 中断亲和性设置实例——以以太网卡的中断为例

  • 动态监控CPU中断情况,观察中断变化

watch -d -n 1 'cat /proc/interrupts'

  • 查看网卡中断相关信息

cat /proc/interrupts | grep -E "eth|CPU"

  • 网卡亲和性设置

修改proc/irq/irq_number/smp_affinity之前,先停掉irq自动调节服务,不然修改的值就会被覆盖。

/etc/init.d/irqbalance stop

通过查看网卡中断相关信息,得到网卡中断为19

[root@master ~]# cd /proc/irq/19
[root@master 19]# cat smp_affinity
00000000,00000000,00000000,00000001
[root@master 19]# cat smp_affinity_list 
0

修改值,将19号中断绑定在cpu2上:

[root@master 19]# echo 4 > smp_affinity
[root@master 19]# cat smp_affinity
00000000,00000000,00000000,00000004
[root@master 19]# cat smp_affinity_list 
2

如果是要将网卡中断绑定在cpu0和cpu2上怎么做了?请先参照上文中的CPU列表。cpu0和2的十六进制值分别为1,4。那么如果要同时绑定在cpu0和cpu2上,则十六进制值为5,如下:

[root@master 19]# echo 5 > smp_affinity
[root@master 19]# cat smp_affinity
00000000,00000000,00000000,00000005
[root@master 19]# cat smp_affinity_list 
0,2

再做一个连续绑定的例子,如绑定在cpu0,1,2上:

[root@master 19]# cat smp_affinity
00000000,00000000,00000000,00000007
[root@master 19]# cat smp_affinity_list 
0-2

从这里可以得出一个结论:绑定单个cpu只要写数字就行,如果是绑定多个cpu则用逗号隔开,如果是绑定连续CPU,则用-符号。

注意:写入smp_affinity中的必须是16进制(不带0x标识),更新这个文件后,smp_affinity_list也会更新,这个文件里面是10进制。

我们在计算CPU 核心编号时,是以二进制算的,但是文件中要存放的是十六进制(不带0x标识)。如CPU 0表示的是第一个核心,二进制为0001,十六进制为1。该算法可参考前述的CPU 列表对应关系。
再比如,f 十六进制是15,二进制就是1111,这个就是表示设备随机选择一个CPU执行中断。

三、使用taskset为系统进程PID设置CPU亲和性

  • 查看某个进程的CPU亲和性
# taskset -p 30011
pid 30011's current affinity mask: ff
  • 设置某个进程的CPU亲和性
 # taskset -p 1 30011
pid 30011's current affinity mask: ff
pid 30011's new affinity mask: 1
  • 使用-c选项可以将一个进程对应到多个CPU上去
# taskset -p -c 1,3 30011
pid 30011's current affinity list: 0
pid 30011's new affinity list: 1,3


# taskset -p -c 1-7 30011
pid 30011's current affinity list: 1,3
pid 30011's new affinity list: 1-7

四、多队列网卡中断绑定——CPU中断均衡脚本

eth_irq.py

#!/usr/bin/python

import re
from multiprocessing import cpu_count

dir = '/proc/irq'
interrupt = '/proc/interrupts'


class IRQ():
    def __init__(self):
        self.irq_num = []

        with open(interrupt, 'r') as f:
            for i in f:
                if re.search(r'eth|em', i):
                     #print re.split(r'\s*|:', i)[1]
                     self.irq_num.append(re.split(r'\s*|:', i)[1].split(':')[0])
                #if re.search('eth', i):
                #    print re.split(r'\s*|:', i)
                #    self.irq_num.append(re.split(r'\s|:', i)[2])
        print self.irq_num
        self.cpu_num = cpu_count()
        self.mask = [hex(2**i).split('0x')[1] for i in range(self.cpu_num)]
        self.set_affinity()


    def set_affinity(self):
        affinity_file = []
        for i in self.irq_num:
            affinity_file.append('%s/%s/smp_affinity' % (dir, i))
        #print affinity_file
        #print self.mask
        self.mask.extend(self.mask) 
        #print self.mask
        for i in range(len(self.mask)):
            print '%s %s' % (self.mask[i], affinity_file[i])
            with open(affinity_file[i], 'w') as f:
                f.write("%s" % self.mask[i])
            
           
if __name__ == '__main__':
    a = IRQ()
    print a.mask

这个脚本写的有点死,不够灵活,有时间再重写一下,做一个封装。

执行上面脚本运行前,可执行test.sh查看smp_affinity_list中的值的变化

test.sh

#!/bin/bash

irq=`grep 'eth' /proc/interrupts  | awk '{print $1}' | cut -d : -f 1`
for i in $irq
do
    num=`cat /proc/irq/$i/smp_affinity_list`
    echo /proc/irq/$i/smp_affinity_list"    "$num
done

前(其实之前已经做过均衡,我这里只是改了下)

/proc/irq/59/smp_affinity_list    2
/proc/irq/60/smp_affinity_list    3
/proc/irq/61/smp_affinity_list    4
/proc/irq/62/smp_affinity_list    5
/proc/irq/63/smp_affinity_list    6
/proc/irq/64/smp_affinity_list    7
/proc/irq/65/smp_affinity_list    0
/proc/irq/66/smp_affinity_list    1
/proc/irq/68/smp_affinity_list    2
/proc/irq/69/smp_affinity_list    3
/proc/irq/70/smp_affinity_list    4
/proc/irq/71/smp_affinity_list    5
/proc/irq/72/smp_affinity_list    6
/proc/irq/73/smp_affinity_list    7
/proc/irq/74/smp_affinity_list    0
/proc/irq/75/smp_affinity_list    1

/proc/irq/59/smp_affinity_list    0
/proc/irq/60/smp_affinity_list    1
/proc/irq/61/smp_affinity_list    2
/proc/irq/62/smp_affinity_list    3
/proc/irq/63/smp_affinity_list    4
/proc/irq/64/smp_affinity_list    5
/proc/irq/65/smp_affinity_list    6
/proc/irq/66/smp_affinity_list    7
/proc/irq/68/smp_affinity_list    0
/proc/irq/69/smp_affinity_list    1
/proc/irq/70/smp_affinity_list    2
/proc/irq/71/smp_affinity_list    3
/proc/irq/72/smp_affinity_list    4
/proc/irq/73/smp_affinity_list    5
/proc/irq/74/smp_affinity_list    6
/proc/irq/75/smp_affinity_list    7

五、单队列多网卡CPU中断均衡

使用RPS/RFS在软件层面模拟多队列网卡功能。RPS/RFS是谷歌工程师提交的内核补丁。意在处理多核CPU单队列网卡的情况。

RFS需要内核编译CONFIG_RPS选项,RFS才起作用。全局数据流表(rps_sock_flow_table)的总数可以通过下面的参数来设置:
/proc/sys/net/core/rps_sock_flow_entries

每个队列的数据流表总数可以通过下面的参数来设置:

/sys/class/net/[iface]/queues/rx-/rps_cpus
/sys/class/net/[iface]/queues/rx-/rps_flow_cnt
/proc/sys/net/core/rps_sock_flow_entries
  • /sys/class/net/[iface]/queues/rx-/rps_cpus
      该文件存放的是对应的CPU核心,如果值为f...则表示每个队列绑定到所有cpu核心上;如果值为1,2之类的,则表示为绑定对应的CPU核心。如:对于物理CPU个数为2,逻辑CPU为8核心的机器,具体计算方法是第一颗cpu是00000001,第二个cpu是00000010,第3个cpu是 00000100,依次类推,由于是所有的cpu都负担,所以所有的cpu数值相加,得到的数值为11111111,十六进制就刚好是ff。ff就表示绑定到所有CPU核心上。
  • /proc/sys/net/core/rps_sock_flow_entries
      该数值是根据网卡有多少个个通道计算得出的数据,例如8通道的网卡,那么1个网卡,每个通道设置4096的数值,8*4096就是/proc/sys/net/core/rps_sock_flow_entries 的数值,对于内存大的机器可以适当调大rps_flow_cnt

每个队列分别绑定到一个对应的CPU核心上

/sys/class/net/eth1/queues/rx-0/rps_cpus   1(这里的数据是十六进制,文件中为:000001)
/sys/class/net/eth1/queues/rx-1/rps_cpus   2
/sys/class/net/eth1/queues/rx-2/rps_cpus   4
/sys/class/net/eth1/queues/rx-3/rps_cpus   8
/sys/class/net/eth1/queues/rx-4/rps_cpus   10
/sys/class/net/eth1/queues/rx-5/rps_cpus   20
/sys/class/net/eth1/queues/rx-6/rps_cpus   40
/sys/class/net/eth1/queues/rx-7/rps_cpus   80

/sys/class/net/eth1/queues/rx-0/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-1/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-2/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-3/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-4/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-5/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-6/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-7/rps_flow_cnt 4096

/proc/sys/net/core/rps_sock_flow_entries 32768

每个队列绑定到所有CPU核心上

/sys/class/net/eth1/queues/rx-0/rps_cpus   ff
/sys/class/net/eth1/queues/rx-1/rps_cpus   ff
/sys/class/net/eth1/queues/rx-2/rps_cpus   ff
/sys/class/net/eth1/queues/rx-3/rps_cpus   ff
/sys/class/net/eth1/queues/rx-4/rps_cpus   ff
/sys/class/net/eth1/queues/rx-5/rps_cpus   ff
/sys/class/net/eth1/queues/rx-6/rps_cpus   ff
/sys/class/net/eth1/queues/rx-7/rps_cpus   ff

/sys/class/net/eth1/queues/rx-0/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-1/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-2/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-3/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-4/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-5/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-6/rps_flow_cnt 4096
/sys/class/net/eth1/queues/rx-7/rps_flow_cnt 4096

/proc/sys/net/core/rps_sock_flow_entries 32768

如果不开启rps功能,则rps_cpus 文件中的值设置为0

七、IRQ均衡脚本

写脚本时发现很多问题,如下面的两台主机,都是支持多队列网卡的,但是注意观察最后两列的区别

[root@master ~]# grep 'eth' /proc/interrupts 
  59:   61033490          0    3671863          0          0          0          0          0   PCI-MSI-edge      eth0-0
  60:   41459456    3308875          0    2914342          0          0          0          0   PCI-MSI-edge      eth0-1
  61:   54622748          0    3360309          0    4103929          0          0          0   PCI-MSI-edge      eth0-2
  62:  310768180          0          0   32393316          0   31050595          0          0   PCI-MSI-edge      eth0-3
  63:   47763053          0          0          0    3674309          0    3352638          0   PCI-MSI-edge      eth0-4
  64:   66969322          0          0          0          0    5011122          0    5180864   PCI-MSI-edge      eth0-5
  65:   50396675          0          0          0          0          0    3175900          0   PCI-MSI-edge      eth0-6
  66:   44104243    3138376          0          0          0          0          0    3091676   PCI-MSI-edge      eth0-7
  68: 3994017501          0  338732695          0          0          0          0          0   PCI-MSI-edge      eth1-0
  69: 2203747223  766400094          0  818107598          0          0          0          0   PCI-MSI-edge      eth1-1
  70: 3089604544          0  759400051          0  795843526          0          0          0   PCI-MSI-edge      eth1-2
  71: 1894677558          0          0  760811049          0  793309576          0          0   PCI-MSI-edge      eth1-3
  72:  805024305          0          0          0  723044628          0  759874105          0   PCI-MSI-edge      eth1-4
  73: 1582319475          0          0          0          0  721360118          0  781055635   PCI-MSI-edge      eth1-5
  74: 2854078786          0          0          0          0          0  709514558          0   PCI-MSI-edge      eth1-6
  75: 1699542056  779165245          0          0          0          0          0  717908550   PCI-MSI-edge      eth1-7
[root@minion ~]# grep 'eth' /proc/interrupts 
 90:          6          0          0          0          0          0          0          0       PCI-MSI-X  eth0
 98: 3178016492   23261814   26903212   24899595 2274387599  284769513 2180244656  385019532       PCI-MSI-X  eth0-rx-0
106:       1442  411400813   25969662    6659594 2117294006  142635319 2079125696  331603756       PCI-MSI-X  eth0-rx-1
114:       6626   20485044 2713744686    6831426 3331230251 4042587617  992902361 1019496640       PCI-MSI-X  eth0-rx-2
122:        771   38602882   23569683 1670873952 1400480565 1479682722  878554708 1266571848       PCI-MSI-X  eth0-rx-3
130:        676    4001308     121711  241193970 4054343807         10 2219979772 2992231001       PCI-MSI-X  eth0-tx-0
146:          8          0          0          0          0          0          0          0       PCI-MSI-X  eth1
154:       1208  304770485  506778587 3164891700    6749496 3992464600    7582534     610809       PCI-MSI-X  eth1-rx-0
162:      24012  923562439 1868789464  430527766    5200172    4789746  978408581     297773       PCI-MSI-X  eth1-rx-1
170:     179444  589446443  591027856  173978259    2152491    2082150    2069743 1894849209       PCI-MSI-X  eth1-rx-2
178: 2445302424 1728604344  843543689  232630412    4365207    4219576    4031133     502923       PCI-MSI-X  eth1-rx-3
186:      16761  325082928  712828704  249179575    2839276    2725962    2571980     255465       PCI-MSI-X  eth1-tx-0

 

八、Suricata中负载均衡处理逻辑

   这是我应用于suricata的一种处理方式,其他一些场景下应该也或多或少试用。

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tiny丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值