天天看點

關于Linux cpu中斷問題什麼是中斷?什麼是RPS/RFS如何檢視網卡的隊列數某壓測調整案例:

作者:牧原

什麼是中斷?

當一個硬體(如磁盤控制器或者以太網卡), 需要打斷CPU的工作時, 它就觸發一個中斷. 該中斷通知CPU發生了某些事情并且CPU應該放下目前的工作去處理這個事情. 為了防止多個設定發送相同的中斷, Linux設計了一套中斷請求系統, 使得計算機系統中的每個裝置被配置設定了各自的中斷号, 以確定它的中斷請求的唯一性.

從2.4 核心開始, Linux改進了配置設定特定中斷到指定的處理器(或處理器組)的功能. 這被稱為SMP IRQ affinity, 它可以控制系統如何響應各種硬體事件. 允許你限制或者重新配置設定伺服器的工作負載, 進而讓伺服器更有效的工作.

以網卡中斷為例,在沒有設定SMP IRQ affinity時, 所有網卡中斷都關聯到CPU0, 這導緻了CPU0負載過高,而無法有效快速的處理網絡資料包,導緻了瓶頸。

通過SMP IRQ affinity, 把網卡多個中斷配置設定到多個CPU上,可以分散CPU壓力,提高資料處理速度。但是smp_affinity要求網卡支援多隊列,如果網卡支援多隊列則設定才有作用,網卡有多隊列,才會有多個中斷号,這樣就可以把不同的中斷号配置設定到不同CPU上,這樣中斷号就能相對均勻的配置設定到不同的CPU上。

而單隊列的網卡可以通過RPS/RFS來模拟多隊列的情況,但是該效果并不如網卡本身多隊列+開啟RPSRFS 來的有效

什麼是RPS/RFS

RPS(Receive Packet Steering)主要是把軟中斷的負載均衡到各個cpu,簡單來說,是網卡驅動對每個流生成一個hash辨別,這個HASH值得計算可以通過四元組來計算(SIP,SPORT,DIP,DPORT),然後由中斷處理的地方根據這個hash辨別配置設定到相應的CPU上去,這樣就可以比較充分的發揮多核的能力了。通俗點來說就是在軟體層面模拟實作硬體的多隊列網卡功能,如果網卡本身支援多隊列功能的話RPS就不會有任何的作用。該功能主要針對單隊列網卡多CPU環境,如網卡支援多隊列則可使用SMP irq affinity直接綁定硬中斷。

關于Linux cpu中斷問題什麼是中斷?什麼是RPS/RFS如何檢視網卡的隊列數某壓測調整案例:

圖1 隻有RPS的情況下(來源網絡)

由于RPS隻是單純把資料包均衡到不同的cpu,這個時候如果應用程式所在的cpu和軟中斷處理的cpu不是同一個,此時對于cpu cache的影響會很大,那麼RFS(Receive flow steering)確定應用程式處理的cpu跟軟中斷處理的cpu是同一個,這樣就充分利用cpu的cache,這兩個更新檔往往都是一起設定,來達到最好的優化效果, 主要是針對單隊列網卡多CPU環境。

關于Linux cpu中斷問題什麼是中斷?什麼是RPS/RFS如何檢視網卡的隊列數某壓測調整案例:

圖2:同時開啟RPS/RFS後(來源網絡)

rps_flow_cnt,rps_sock_flow_entries,參數的值會被進位到最近的2的幂次方值,對于單隊列裝置,單隊列的rps_flow_cnt值被配置成與 rps_sock_flow_entries相同。

RFS依靠RPS的機制插入資料包到指定CPU的backlog隊列,并喚醒那個CPU來執行

預設情況下,開啟irqbalance是足夠用的,但是對于一些對網絡性能要求比較高的場景,手動綁定中斷磨合是比較好的選擇

開啟irqbalance,會存在一些問題,比如:
a) 有時候計算出來的值不合理,導緻CPU使用還是不均衡。
b) 在系統比較空閑IRQ處于 Power-save mode 時,irqbalance 會将中斷集中配置設定給第一個 CPU,
以保證其它空閑 CPU 的睡眠時間,降低能耗。如果壓力突然上升,可能會由于調整的滞後性帶來性能問題。
c) 進行中斷的CPU總是會變,導緻了更多的context switch。
d)也存在一些情況,啟動了irqbalance,但是并沒有生效,沒有真正去設定進行中斷的cpu。           

如何檢視網卡的隊列數

1,Combined代表隊列個數,說明我的測試機有4個隊列

# ethtool -l eth0
Channel parameters for eth0:
Pre-set maximums:
RX:     0
TX:     0
Other:      0
Combined:   4
Current hardware settings:
RX:     0
TX:     0
Other:      0
Combined:   4           

2,以ecs centos7.6為例,系統進行中斷的記錄在/proc/interrupts檔案裡面,預設這個檔案記錄比較多,影響檢視,同時如果cpu核心也非常多的話,對于閱讀的影響非常大

# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
  0:        141          0          0          0   IO-APIC-edge      timer
  1:         10          0          0          0   IO-APIC-edge      i8042
  4:        807          0          0          0   IO-APIC-edge      serial
  6:          3          0          0          0   IO-APIC-edge      floppy
  8:          0          0          0          0   IO-APIC-edge      rtc0
  9:          0          0          0          0   IO-APIC-fasteoi   acpi
 10:          0          0          0          0   IO-APIC-fasteoi   virtio3
 11:         22          0          0          0   IO-APIC-fasteoi   uhci_hcd:usb1
 12:         15          0          0          0   IO-APIC-edge      i8042
 14:          0          0          0          0   IO-APIC-edge      ata_piix
 15:          0          0          0          0   IO-APIC-edge      ata_piix
 24:          0          0          0          0   PCI-MSI-edge      virtio1-config
 25:       4522          0          0       4911   PCI-MSI-edge      virtio1-req.0
 26:          0          0          0          0   PCI-MSI-edge      virtio2-config
 27:       1913          0          0          0   PCI-MSI-edge      virtio2-input.0
 28:          3        834          0          0   PCI-MSI-edge      virtio2-output.0
 29:          2          0       1557          0   PCI-MSI-edge      virtio2-input.1
 30:          2          0          0        187   PCI-MSI-edge      virtio2-output.1
 31:          0          0          0          0   PCI-MSI-edge      virtio0-config
 32:       1960          0          0          0   PCI-MSI-edge      virtio2-input.2
 33:          2        798          0          0   PCI-MSI-edge      virtio2-output.2
 34:         30          0          0          0   PCI-MSI-edge      virtio0-virtqueues
 35:          3          0        272          0   PCI-MSI-edge      virtio2-input.3
 36:          2          0          0        106   PCI-MSI-edge      virtio2-output.3
input0說明是cpu1(0号CPU)處理的網絡中斷            
阿裡雲ecs網絡中斷,如果是多個中斷的話,還有input.1 input.2 input.3這種形式
......
PIW:          0          0          0          0   Posted-interrupt wakeup event           

3,如果ecs的cpu核心非常多,那這個檔案看起來就會比較費勁了,可使用下面的指令檢視進行中斷的核心

使用下面這個指令,即可将阿裡雲ecs進行中斷的cpu找出來了(下面這個示範是8c 4個隊列)
# for i in $(egrep "\-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
5
7
1
3
處理一下sar拷貝用
# for i in $(egrep "\-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done |tr -s '\n' ','
5,7,1,3,
#sar -P 5,7,1,3 1 每秒重新整理一次cpu 序号為5,7,1,3核心的cpu使用率
# sar -P ALL 1 每秒重新整理所有核心,用于少量CPU核心的監控,這樣我們就可以知道處理慢的原因是不是因為隊列不夠導緻的了
Linux 3.10.0-957.5.1.el7.x86_64 (iZwz98aynkjcxvtra0f375Z)   05/26/2020  _x86_64_    (4 CPU)
05:10:06 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:10:07 PM     all      5.63      0.00      3.58      1.02      0.00     89.77
05:10:07 PM       0      6.12      0.00      3.06      1.02      0.00     89.80
05:10:07 PM       1      5.10      0.00      5.10      0.00      0.00     89.80
05:10:07 PM       2      5.10      0.00      3.06      2.04      0.00     89.80
05:10:07 PM       3      5.10      0.00      4.08      1.02      0.00     89.80
05:10:07 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
05:10:08 PM     all      8.78      0.00     15.01      0.69      0.00     75.52
05:10:08 PM       0     10.00      0.00     16.36      0.91      0.00     72.73
05:10:08 PM       1      4.81      0.00     13.46      1.92      0.00     79.81
05:10:08 PM       2     10.91      0.00     15.45      0.91      0.00     72.73
05:10:08 PM       3      9.09      0.00     14.55      0.00      0.00     76.36
sar 小技巧
列印idle小于10的核心
sar -P 1,3,5,7 1 |tail -n+3|awk '$NF<10 {print $0}'
看所有核心是否有單核打滿的把1357換成ALL即可
sar -P ALL 1 |tail -n+3|awk '$NF<10 {print $0}'
再貼一個4c8g規格的配置(ecs.c6.xlarge ),
可以看到4c也給了四個隊列,但是預設設定的是在cpu0 和 2上進行中斷
# grep -i "input" /proc/interrupts
 27:       1932          0          0          0   PCI-MSI-edge      virtio2-input.0
 29:          2          0       1627          0   PCI-MSI-edge      virtio2-input.1
 32:       1974          0          0          0   PCI-MSI-edge      virtio2-input.2
 35:          3          0        284          0   PCI-MSI-edge      virtio2-input.3
# for i in $(egrep "\-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
1
3
1
3
原因是cpu是超線程的,“每個vCPU綁定到一個實體CPU超線程”,是以即使是4個隊列預設也在2個cpu核心上
# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    2
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1           

4,關閉IRQbalance

# service irqbalance status
Redirecting to /bin/systemctl status irqbalance.service
● irqbalance.service - irqbalance daemon
   Loaded: loaded (/usr/lib/systemd/system/irqbalance.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Wed 2020-05-27 14:39:28 CST; 2s ago
  Process: 1832 ExecStart=/usr/sbin/irqbalance --foreground $IRQBALANCE_ARGS (code=exited, status=0/SUCCESS)
 Main PID: 1832 (code=exited, status=0/SUCCESS)
May 27 14:11:40 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Started irqbalance daemon.
May 27 14:39:28 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Stopping irqbalance daemon...
May 27 14:39:28 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Stopped irqbalance daemon.           

5,手動設定RPS

5.1 手動設定之前我們需要先了解下面的檔案(IRQ_number就是前面grep input拿到的序号)

進入/proc/irq/${IRQ_number}/,關注兩個檔案:smp_affinity和smp_affinity_list

smp_affinity是bitmask+16進制,

smp_affinity_list:這個檔案更好了解,采用的是10進制,可讀性高

改這兩個任意一個檔案,另一個檔案會同步更改。

為了友善了解,咱們直接看十進制的檔案smp_affinity_list即可

如果這一步沒看明白,注意前面的 /proc/interrupts的輸出
# for i in $(egrep "\-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
1
3
1
3
手動設定進行中斷的CPU号碼可以直接echo修改,下面就是将序号27的中斷放到cpu0上處理,一般建議可以把cpu0空出來
# echo 0 >> /proc/irq/27/smp_affinity_list
# cat /proc/irq/27/smp_affinity_list
0
關于bitmask
“f” 是十六進制的值對應的 二進制是”1111”(可以了解為4c的配置設定為f的話,所有的cpu參與進行中斷)
二進制中的每個位代表了伺服器上的每個CPU. 一個簡單的demo
  CPU序号  二進制  十六進制
   CPU 0   0001    1
   CPU 1   0010    2
   CPU 2   0100    4
   CPU 3   1000    8           

5.2 需要對每塊網卡每個隊列分别進行設定。如對eth0的0号隊列設定:

echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus

這裡的設定方式和中斷親和力設定的方法是類似的。采用的是掩碼的方式,但是這裡通常要将所有的CPU設定進入,如:

4core,f

8core,ff

16core,ffff

32core,ffffffff

預設在0号cpu上

# cat /sys/class/net/eth0/queues/rx-0/rps_cpus
0
# echo f >>/sys/class/net/eth0/queues/rx-0/rps_cpus
# cat /sys/class/net/eth0/queues/rx-0/rps_cpus
f           

6,設定RFS的方式

需要設定兩個地方:

6.1, 全局表:rps_sock_flow_table的條目數量。通過一個核心參數控制:

# sysctl -a |grep net.core.rps_sock_flow_entries
net.core.rps_sock_flow_entries = 0
# sysctl -w net.core.rps_sock_flow_entries=1024
net.core.rps_sock_flow_entries = 1024           

6.2,每個網卡隊列hash表的條目數:

#  cat /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
0
# echo 256 >> /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
#  cat /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
256           

需要啟動RFS,兩者都需要設定。

建議機器上所有的網卡隊列設定的rps_flow_cnt相加應該小于或者等于rps_sock_flow_entries。因為是4個隊列,是以每個隊列設定256,可以根據實際情況增大

某壓測調整案例:

背景:iperf3 5個線程 1個1g打流量,抖動,如 4.6--5.2 波動較大,檢視中斷不均衡,做如下設定

1,設定RPS ,32c 16隊列 設定ffffffff

2, RFS 65536 /16 = 4096

3, smp_affinity_list 1,3,5,7,9....31

依然不均衡,考慮到RFS 命中cache的問題,用戶端增加到16線程,中斷處理趨于穩定,但是流量依然有波動

4,關閉TSO後流量在7.9x--8.0x之間波動,趨于穩定

繼續閱讀