Linux多队列网卡的硬件的实现详解
栏目分类:服务器教程   发布日期:2017-03-20   浏览次数:次   发布:www.16jiaocheng.com

多队列网卡是一种技术,最初是用来解决网络IO QoS (quality of service)问题的。
多队列网卡是一种技术,最初是用来解决网络IO QoS (quality of service)问题的,后来随着网络IO的带宽的不断提升,单核CPU不能完全处满足网卡的需求,通过多队列网卡驱动的支持,将各个队列通过中断绑定到不同的核上,以满足网卡的需求。

  常见的有Intel的82575、82576,Boardcom的57711等,下面以公司的服务器使用较多的Intel 82575网卡为例,分析一下多队列网卡的硬件的实现以及linux内核软件的支持。

  1.多队列网卡硬件实现

  图1.1是Intel 82575硬件逻辑图,有四个硬件队列。当收到报文时,通过hash包头的SIP、Sport、DIP、Dport四元组,将一条流总是收到相同的队列。同时触发与该队列绑定的中断。

Linux多队列网卡的硬件的实现详解

  图1.1 82575硬件逻辑图

  2. 2.6.21以前网卡驱动实现

  kernel从2.6.21之前不支持多队列特性,一个网卡只能申请一个中断号,因此同一个时刻只有一个核在处理网卡收到的包。如图2.1,协议栈通过NAPI轮询收取各个硬件queue中的报文到图2.2的net_device数据结构中,通过QDisc队列将报文发送到网卡。

  图2.1 2.6.21之前内核协议栈

  图2.2 2.6.21之前net_device

  3. 2.6.21后网卡驱动实现

  2.6.21开始支持多队列特性,当网卡驱动加载时,通过获取的网卡型号,得到网卡的硬件queue的数量,并结合CPU核的数量,最终通过Sum=Min(网卡queue,CPU core)得出所要激活的网卡queue数量(Sum),并申请Sum个中断号,分配给激活的各个queue。

  如图3.1,当某个queue收到报文时,触发相应的中断,收到中断的核,将该任务加入到协议栈负责收包的该核的NET_RX_SOFTIRQ队列中(NET_RX_SOFTIRQ在每个核上都有一个实例),在NET_RX_SOFTIRQ中,调用NAPI的收包接口,将报文收到CPU中如图3.2的有多个netdev_queue的net_device数据结构中。

  这样,CPU的各个核可以并发的收包,就不会应为一个核不能满足需求,导致网络IO性能下降。

  图3.1 2.6.21之后内核协议栈

  图3.2 2.6.21之后net_device

  4.中断绑定

  当CPU可以平行收包时,就会出现不同的核收取了同一个queue的报文,这就会产生报文乱序的问题,解决方法是将一个queue的中断绑定到唯一的一个核上去,从而避免了乱序问题。同时如果网络流量大的时候,可以将软中断均匀的分散到各个核上,避免CPU成为瓶颈。

  图4.1 /proc/interrupts

  5.中断亲合纠正

  一些多队列网卡驱动实现的不是太好,在初始化后会出现图4.1中同一个队列的tx、rx中断绑定到不同核上的问题,这样数据在core0与core1之间流动,导致核间数据交互加大,cache命中率降低,降低了效率。

  图5.1 不合理中断绑定

  linux network子系统的负责人David Miller提供了一个脚本,首先检索/proc/interrupts文件中的信息,按照图4.1中eth0-rx-0($VEC)中的VEC得出中断MASK,并将MASK

  写入中断号53对应的smp_affinity中。由于eth-rx-0与eth-tx-0的VEC相同,实现同一个queue的tx与rx中断绑定到一个核上,如图4.3所示。

  图4.2 set_irq_affinity


  图4.3 合理的中断绑定

  set_irq_affinity脚本位于http://mirror.oa.com/tlinux/tools/set_irq_affinity.sh。

  6.多队列网卡识别

  #lspci -vvv

  Ethernet controller的条目内容,如果有MSI-X && Enable+ && TabSize > 1,则该网卡是多队列网卡,如图4.4所示。

  图4.4 lspci内容

  Message Signaled Interrupts(MSI)是PCI规范的一个实现,可以突破CPU 256条interrupt的限制,使每个设备具有多个中断线变成可能,多队列网卡驱动给每个queue申请了MSI。MSI-X是MSI数组,Enable+指使能,TabSize是数组大小。

  # setting up irq affinity according to /proc/interrupts

  # 2008-11-25 Robert Olsson

  # 2009-02-19 updated by Jesse Brandeburg

  #

  # > Dave Miller:

  # (To get consistent naming in /proc/interrups)

  # I would suggest that people use something like:

  # char buf[IFNAMSIZ+6];

  #

  # sprintf(buf, "%s-%s-%d",

  # netdev->name,

  # (RX_INTERRUPT ? "rx" : "tx"),

  # queue->index);

  #

  # Assuming a device with two RX and TX queues.

  # This script will assign:

  #

  # eth0-rx-0 CPU0

  # eth0-rx-1 CPU1

  # eth0-tx-0 CPU0

  # eth0-tx-1 CPU1

  #

  set_affinity()

  {

  MASK=$((1<<$VEC))

  printf "%s mask=%X for /proc/irq/%d/smp_affinity\n" $DEV $MASK $IRQ

  printf "%X" $MASK > /proc/irq/$IRQ/smp_affinity

  #echo $DEV mask=$MASK for /proc/irq/$IRQ/smp_affinity

  #echo $MASK > /proc/irq/$IRQ/smp_affinity

  }

  if [ "$1" = "" ] ; then

  echo "Description:"

  echo " This script attempts to bind each queue of a multi-queue NIC"

  echo " to the same numbered core, ie tx0|rx0 --> cpu0, tx1|rx1 --> cpu1"

  echo "usage:"

  echo " $0 eth0 [eth1 eth2 eth3]"

  fi

  # check for irqbalance running

  IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`

  if [ "$IRQBALANCE_ON" == "0" ] ; then

  echo " WARNING: irqbalance is running and will"

  echo " likely override this script's affinitization."

  echo " Please stop the irqbalance service and/or execute"

  echo " 'killall irqbalance'"

  fi

  #

  # Set up the desired devices.

  #

  for DEV in $*

  do

  for DIR in rx tx TxRx

  do

  MAX=`grep $DEV-$DIR /proc/interrupts | wc -l`

  if [ "$MAX" == "0" ] ; then

  MAX=`egrep -i "$DEV:.*$DIR" /proc/interrupts | wc -l`

  fi

  if [ "$MAX" == "0" ] ; then

  echo no $DIR vectors found on $DEV

  continue

  #exit 1

  fi

  for VEC in `seq 0 1 $MAX`

  do

  IRQ=`cat /proc/interrupts | grep -i $DEV-$DIR-$VEC"$" | cut -d: -f1 | sed "s/ //g"`

  if [ -n "$IRQ" ]; then

  set_affinity

  else

  IRQ=`cat /proc/interrupts | egrep -i $DEV:v$VEC-$DIR"$" | cut -d: -f1 | sed "s/ //g"`

  if [ -n "$IRQ" ]; then

  set_affinity

  fi

  fi

  done

  done

  done

相关热词: Linux多队列网卡

Copyright © 2002-2016 16教程网 版权所有     
电脑教程 Office教程 平面设计 PS教程 室内设计 网页设计