雲計算

關於Linux cpu中斷問題

作者:牧原

什麼是中斷?

當一個硬件(如磁盤控制器或者以太網卡), 需要打斷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直接綁定硬中斷。
image.png

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

由於RPS只是單純把數據包均衡到不同的cpu,這個時候如果應用程序所在的cpu和軟中斷處理的cpu不是同一個,此時對於cpu cache的影響會很大,那麼RFS(Receive flow steering)確保應用程序處理的cpu跟軟中斷處理的cpu是同一個,這樣就充分利用cpu的cache,這兩個補丁往往都是一起設置,來達到最好的優化效果, 主要是針對單隊列網卡多CPU環境。
image.png

圖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之間波動,趨於穩定

Leave a Reply

Your email address will not be published. Required fields are marked *