软中断相关知识讲解以及优化方法

中断简介

X86体系结构的计算机采用中断机制来协同处理器与其他设备的工作。当一个设备需要与处理器通信时,就会向处理器发出一个中断信号。例如敲击键盘时,键盘就会产生一个中断,通知操作系统有键被按下。在机器启动的时候,系统就已经识别了所有设备,并且也把相应的中断处理器加载到中断表中。所有的Linux操作系统都是基于中断驱动的,当我们在键盘上按下一个按键时,键盘就会对CPU说,一个键已经被按下。在这种情况下,键盘的IRQ线路中的电压就会发生一次变化,而这种电压的变化就是来自设备的请求,就相当于说这个设备有一个请求需要处理。

中断其实就是由硬件或软件所发送的一种称为IRQ(中断请求)的信号。中断允许让设备,如键盘,串口卡,并口等设备表明它们需要CPU。一旦CPU接收了中断请求,CPU就会暂时停止执行正在运行的程序,并且调用一个称为中断处理器或中断服务程序(interrupt service routine)的特定程序

中断服务程序或中断处理器可以在中断向量表中找到,而这个中断向量表位于内存中的固定地址中。中断被CPU处理后,就会恢复执行之前被中断的程序,整个流程如下图所示。

由于中断会频繁发生,因此要求中断处理程序执行要快速。为了实现快速执行,必须要将一些繁重且不非常紧急的任务从中断处理程序中剥离出来,这一部分Linux中称为下半部,有三种方法处理下半部——软中断、tasklet和工作队列

以网卡为例,当网卡收到数据包时会产生中断,通知内核有新数据包,然后内核调用中断处理程序进行响应,把数据包从网卡缓存拷贝到内存,因为网卡缓存大小有限,如果不及时拷出数据,后续数据包将会因为缓存溢出被丢弃,因此这一工作需要立即完成,这就是硬中断。剩下的处理和操作数据包的工作就会交给软中断。高负载的网卡是软中断产生的大户,很容易形成瓶颈。

为了解决中断处理程序执行过长和中断丢失的问题,Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部:

  • 上半部用来快速处理中断,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作。这就是硬中断,特点是快速执行

  • 下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行。这是软中断,特点是延迟执行

硬中断和软中断的区别

硬中断

1)硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。

2)处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU(也有一种特殊的情况,就是在大型主机上是有硬件通道的,它可以在没有主CPU的支持下,可以同时处理多个中断。)。

3)硬中断可以直接中断CPU。它会引起内核中相关的代码被触发。对于那些需要花费一些时间去处理的进程,中断代码本身也可以被其他的硬中断中断。

4)对于时钟中断,内核调度代码会将当前正在运行的进程挂起,从而让其他的进程来运行。它的存在是为了让调度代码(或称为调度器)可以调度多任务。

软中断

软中断简介

1)软中断的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。

2)通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。

3)软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。

4)软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。

查看 /proc/softirqs

第一,要注意软中断的类型,也就是这个界面中第一列的内容。从第一列你可以看到,软中断包括了 10 个类别,分别对应不同的工作类型。比如 NET_RX 表示网络接收中断,而 NET_TX 表示网络发送中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linjx ~]# watch -d "cat /proc/softirqs |awk 'NR==1{print \"head\",\$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8};NR>1{print \$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8,\$9}' |column -t"
Every 2.0s: cat /proc/softirqs |awk 'NR==1{print "head",$1,$2,$3,$4,$5,$6,$7,$8};NR>1{print $1,$2,$3,$4,$5,$6,$7,$8,$9}... Fri Jul 31 16:02:52 2020

head CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
HI: 0 1 0 0 0 0 0 0
TIMER: 2004179863 120805548 3648624983 3286795439 1285954916 874999229 1723943939 2245741976
NET_TX: 1195 1264 1291 1275 1507 1447 279 322
NET_RX: 3661090579 2737644990 1435209796 1028014121 3757347966 3301748530 273368144 1303326963
BLOCK: 7334310 0 0 0 0 0 0 0
BLOCK_IOPOLL: 0 0 0 0 0 0 0 0
TASKLET: 2408609 2383987 2185339 2216423 2478076 2581088 551758 582002
SCHED: 903843879 1924840488 1813449807 1799790537 1710208764 1753204772 1769987583 1777085914
HRTIMER: 0 0 0 0 0 0 0 0
RCU: 3618243577 3653248436 3249174196 3156861515 2620158866 68619621 2435534711 2690357339

第二,要注意同一种软中断在不同 CPU 上的分布情况,也就是同一行的内容。正常情况下,同一种中断在不同 CPU 上的累积次数应该差不多。比如这个界面中,NET_RX 在 CPU0 和 CPU1 上的中断次数基本是同一个数量级,相差不大。

软中断实际上是以内核线程的方式运行的,每个 CPU 都对应一个软中断内核线程,这个软中断内核线程就叫做ksoftirqd/CPU编号。

1
2
[root@linjx ~]# ps aux |grep softirq |grep -v grep
root 6 0.0 0.0 0 0 ? S Dec07 0:02 [ksoftirqd/0]

网卡多队列

简介

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

查看

查看网卡是否支持多队列有2个方法,一种是使用lspci -vvv来查看,另一种是使用ethtool -l查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@localhost ~]# lspci -vvv 2>/dev/null|grep Eth -A16 |grep MSI-X
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-
Capabilities: [70] MSI-X: Enable+ Count=10 Masked-

[root@localhost ~]# ethtool -l eth0
Channel parameters for eth0:
Pre-set maximums:
RX: 0
TX: 0
Other: 1
Combined: 8 # 表示最多支持设置8个队列
Current hardware settings:
RX: 0
TX: 0
Other: 1
Combined: 8 #表示当前生效的是8个队列

可以使用ethtool -L eth0 combined 2设置eth0当前使用2个队列

网卡相关其他参数的查看方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 查看网卡驱动
[root@localhost 148]# ethtool -i eth0
driver: igb
version: 5.3.0-k
firmware-version: 1.63, 0x800009fa
bus-info: 0000:01:00.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: no

# 查看网卡队列大小
[root@localhost 148]# ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Current hardware settings:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256

# 查看TSO/GSO
[root@localhost 148]# ethtool -k eth0 |grep offload
tcp-segmentation-offload: on
udp-fragmentation-offload: off [fixed]
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off [fixed]
rx-vlan-offload: on
tx-vlan-offload: on

设置

smp_affinity设置

smp_affinity叫做 中断亲和力,要设置的话 ,得先查出网卡的中断号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost 148]# grep eth0 /proc/interrupts | grep -o "^ *[0-9]*" | xargs -i echo {}
147
148
149
150
151
152
153
154
155
[root@localhost 148]# cat /proc/irq/147/smp_affinity
000000,00000080
[root@localhost 148]# cat /proc/irq/147/smp_affinity_list
7
[root@localhost 148]# for n in `cat /proc/interrupts |grep eth0- |awk '{print $1}' |sed 's/://'`;do cpu=`cat /proc/irq/$n/smp_affinity_list`;echo -e "/proc/irq/$n/smp_affinity_list\t$cpu";done
/proc/irq/65/smp_affinity_list 15
/proc/irq/66/smp_affinity_list 16
/proc/irq/67/smp_affinity_list 17
/proc/irq/68/smp_affinity_list 18
/proc/irq/69/smp_affinity_list 19
/proc/irq/70/smp_affinity_list 20
/proc/irq/71/smp_affinity_list 21
/proc/irq/72/smp_affinity_list 22

可以看出中断号为147绑定到了CPU7上面了。其中smp_affinity是16进制表示;而smp_affinity_list是10进制的。

二者的转化关系如下:

1
2
3
4
5
6
7
8
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
......如上类推

可以使用python的hex函数求出对应的16进制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> for i in range(1,11):print(i-1,hex(2**(i-1)))
...
0 0x1
1 0x2
2 0x4
3 0x8
4 0x10
5 0x20
6 0x40
7 0x80
8 0x100
9 0x200
# 也可以使用shell来处理
[root@localhost]# id=9;echo $((10**(id/4) * 2**(id%4)))
200

接下来就可以进行设置了。先停止irq的自动调节服务/etc/init.d/irqbalance stop,然后如下设置:

1
2
3
4
5
6
[root@localhost 148]# echo 7 >smp_affinity;cat smp_affinity smp_affinity_list
000000,00000007
0-2 # 绑定CPU0 1 2,这边的7是16进制相加的结果
[root@localhost 148]# echo 401 >smp_affinity;cat smp_affinity smp_affinity_list
000000,00000401
0,10 # 绑定CPU0 10

从这里可以得出一个结论:绑定单个cpu只要写数字就行,如果是绑定多个cpu则用逗号隔开,如果是绑定连续CPU,则用-符号。注意:写入smp_affinity中的必须是16进制(不带0x标识),更新这个文件后,smp_affinity_list也会更新,这个文件里面是10进制。

taskset设置方法

使用taskset即可设置了。taskset -c 1,2,3 /etc/init.d/mysql start或者taskset -cp 1,2,3 2345把CPU#1 #2 #3分配给PID为2345的进程

附nic.sh脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/bin/bash

TESTED_driverS=("igb" "ixgbe" "bnx2" "tg3")
TESTED_modelS=(
# igb
"Intel Corporation 82576 Gigabit Network Connection (rev 01)"
"Intel Corporation I350 Gigabit Network Connection (rev 01)"
# ixgbe
"Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01)"
"Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)"
# tg3
"Broadcom Corporation NetXtreme BCM5720 Gigabit Ethernet PCIe"
"Broadcom Corporation NetXtreme BCM5761 Gigabit Ethernet PCIe (rev 10)"
# bnx2
"Broadcom Corporation NetXtreme II BCM5708 Gigabit Ethernet (rev 12)"
"Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)"
)
RPS_SOCK_FLOW_ENTRIES=32768

if tput colors > /dev/null; then
RED='\e[0;31m'
YELLOW='\e[1;33m'
GREEN='\e[0;32m'
NC='\e[0m'
else
RED=''
YELLOW=''
NC=''
fi

function err_msg() {
local msg=$1
echo -e "$RED[Error] $msg $NC"
exit 1
}

function warn_msg() {
local msg=$1
echo -e "$YELLOW[Warning] $msg $NC"
}

function info_msg() {
local msg=$1
echo -e "$msg $NC"
}

function print_usage() {
echo "Usage: $0 -i <interface>"
exit 0
}

function hardware_queues() {
local driver=$1
local intrs=$2
case $driver in
igb|ixgbe|bnx2)
echo $intrs;;
tg3)
echo $((intrs-1));;
esac
}

function intr_pattern() {
local iface=$1
local driver=$2
case $driver in
igb|ixgbe)
echo $iface-TxRx;;
bnx2|tg3)
echo $iface;;
esac
}

function intr_list() {
local iface=$1
local driver=$2

case $driver in
igb|ixgbe|bnx2|tg3)
intr_pattern $iface $driver | xargs -i grep {} /proc/interrupts | grep -o "^ *[0-9]*" | xargs -i echo {};;
esac
}

# 10进制转16进制
function cpuid_to_mask() {
local id=$1
echo $((10**(id/4) * 2**(id%4)))
}



#if [[ ! "$UID" = 0 ]]; then
# err_msg "This script must be run as ROOT"
#fi

#while getopts i:h option
#do
# case "$option" in
# i)
# nic=$OPTARG;;
# h|\?)
# print_usage;;
# esac
#done
#
#if [[ -z $nic ]]; then
# print_usage
#fi

function configure_nic() {
nic=$1

# Sanity checks on the interface given
if ! ifconfig $nic > /dev/null 2>&1; then
warn_msg "$nic not available... please double check"
return
fi
if ! ip link show $nic | grep UP > /dev/null 2>&1; then
warn_msg "$nic not up... please double check"
return
fi

# Check the driver of the interface
local driver=$(ethtool -i $nic 2>/dev/null | grep driver | egrep -o "[a-zA-Z0-9_]+$")
if [[ -z $driver ]] || [[ ! -n "${TESTED_driverS[$driver]}" ]]; then
warn_msg "We have not tested on $nic (driver: $driver) and not sure how to configure it yet. Consider choosing another?"
return
fi

local driver_version=$(ethtool -i eth0 2>/dev/null | grep ^version | egrep -o "[a-zA-Z0-9_.]+$")
local bus=$(ethtool -i $nic 2>/dev/null | grep bus-info | egrep -o "[0-9a-f]+:[0-9a-f]+\.[0-9a-f]+")
local model=$(lspci | grep $bus | sed "s/$bus //g" | sed "s/Ethernet controller: //g")
local intrs=$(grep $nic- /proc/interrupts | wc -l)
local cores=$(grep processor /proc/cpuinfo | wc -l)
local tx_queues=$(ls /sys/class/net/$nic/queues | grep tx | wc -l)
local rx_queues=$(ls /sys/class/net/$nic/queues | grep rx | wc -l)
local hw_queues=$(hardware_queues $driver $intrs)

info_msg "Configuring $nic..."
info_msg " Bus info: $bus"
info_msg " Model: $model"
info_msg " Driver: $driver-$driver_version"
info_msg " Number of..."
info_msg " CPU cores: $cores"
info_msg " software Tx queues: $tx_queues"
info_msg " software Rx queues: $rx_queues"
info_msg " hardware queues: $hw_queues"

if [[ $hw_queues -lt 0 ]] || [[ $cores -eq 1 ]]; then
err_msg "DONE!! setting irq balance is not necessary"
fi

# Allow 3000 interrupts at most per second 设置每秒中断数上限,防止中断风暴。两个Rx中断间隔设置成至少333us,约3000个中断每秒。
ethtool -C $nic rx-usecs 333 > /dev/null 2>&1
info_msg " Interrupt throttle rate is set to 3000"

# Enlarge open file limits
if [ `ulimit -n` -le 1024 ]; then
warn_msg "Max open file limit is possibly too small to affect performancet."
fi

# Disable irqbalance
if ps aux | grep irqbalance | grep -v grep > /dev/null 2>&1; then
info_msg "Disable irqbalance..."
# XXX Do we have a more moderate way to do this?
killall irqbalance > /dev/null 2>&1
fi

# Use XPS to set affinities of Tx queues
# Note: This is only done when we have more Tx queues than cores.
if [[ $tx_queues -ge $cores ]]; then
for i in $(seq 0 $((cores-1))); do
cpuid_to_mask $((i%cores)) | xargs -i echo {} > /sys/class/net/$nic/queues/tx-$i/xps_cpus
done
info_msg " XPS enabled"
fi

# Enable RPS/RFS if number of cores and hardware cores are not equal
if [[ ! $hw_queues == $cores ]]; then
sysctl -w net.core.rps_sock_flow_entries=$RPS_SOCK_FLOW_ENTRIES
for i in /sys/class/net/$nic/queues/rx-*; do
printf "%x\n" $((2**cores-1)) | xargs -i echo {} > $i/rps_cpus;
printf "%d\n" $(($RPS_SOCK_FLOW_ENTRIES/$hw_queues)) | xargs -i echo {} > $i/rps_flow_cnt;
done
info_msg " RPS/RFS enabled"
else
sysctl -w net.core.rps_sock_flow_entries=0
for i in /sys/class/net/$nic/queues/rx-*; do
echo 0 > $i/rps_cpus;
echo 0 > $i/rps_flow_cnt;
done
info_msg " RPS/RFS disabled"
fi

# Set interrupt affinities
i=0
intr_list $nic $driver | while read irq; do
cpuid_to_mask $((i%cores)) | xargs -i echo {} > /proc/irq/$irq/smp_affinity
i=$((i+1))
done
}


INTERFACES=$(ls /sys/class/net/ | grep eth)
for NIC in $INTERFACES;do
configure_nic $NIC
done

或者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
[root@localhost ~]# cat /usr/local/bin/set_irqbalance_for_intel.sh
#!/bin/bash
#Version_1.0.0:2019/11/22 1、针对i40e网卡i40e,关闭自适应adaptive功能 2、megasas多队列网卡设置亲和性 3、统一网卡队列的绑定方式(之前包括两种输入方式)
#Version_1.0.1:2019/12/18 1、ixgbe网卡绑定方式更改为1616绑定

network_eth=`/sbin/route -n | grep '^0.0.0.0' |awk '{print $NF}'|grep -E "eth"|head -n1`
network_bond=`/sbin/route -n | grep '^0.0.0.0' |awk '{print $NF}'|grep -E "bond"|head -n1`
driver="ixgbe"
ip=`/sbin/ifconfig |grep "inet addr" |grep -v "127.0.0.1" |awk -F "[: ]+" '{print $4}' |sed -n '1p'`

irq_cpu(){
cpunum=`cat /proc/cpuinfo | grep processor | wc -l`
system=`/usr/sbin/dmidecode | grep "Product Name" |grep -E 'S2600JF|S2400LP'`
cpu_id=`cat /proc/cpuinfo |grep "physical id"|awk '{print $NF}'|sort |uniq |wc -l`
count=`/usr/bin/expr ${bcount} \* 16`
if [[ $count -ge ${cpunum} ]]; then
count=0
fi

/sbin/service irqbalance status |grep "running"
if [ $? -eq 0 ];then
if [ `echo "$irqnum" | wc -l` != 1 ];then
/sbin/chkconfig irqbalance off
/sbin/service irqbalance stop
fi
fi

### set irq balance for Multiple queue network card
if (([ `echo "$irqnum" | wc -l` != 1 ] && [ `echo "$irqnum" | wc -l` -le 32 ]) || [ "$cpunum" -le 32 ]) && ([ ! -z "$system" ] && [ $cpu_id -ge 2 ]);then
cat /proc/cpuinfo |grep "processor" > /tmp/cpu1.txt
cat /proc/cpuinfo |grep "physical id" > /tmp/cpu2.txt
/usr/bin/paste /tmp/cpu1.txt /tmp/cpu2.txt > /tmp/cpu.txt
cpu_num=`cat /tmp/cpu.txt |awk '$NF==0{print $3}' |wc -l`
cpunum=${cpu_num}
count=1
for n in $irqnum; do
cpu_value=`cat /tmp/cpu.txt |awk '$NF==0{print $3}' |awk -v a=${count} 'NR==a{print $1}'`
affinity=`echo 2 | awk '{print $1**"'${cpu_value}'"}'`
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;${affinity}" | bc` >/proc/irq/$n/smp_affinity
if [ $count -eq ${cpunum} ];then
count=0
fi
count=`/usr/bin/expr $count + 1`
done
else
#first=`echo -n $irqnum|awk '{print $1}'`
for n in $irqnum; do
affinity=`echo 2 | awk '{print $1**"'$count'"}'`
if [ $count -le 31 ]; then
echo $n
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;${affinity}" | bc` >/proc/irq/$n/smp_affinity
elif [ $count -gt 31 ] && [ $count -le 63 ]; then
echo $n
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;${affinity}" | bc |sed 's/00000000//g' |awk '{print $1 "," "00000000"}' ` >/proc/irq/$n/smp_affinity
elif [ $count -gt 63 ] && [ $count -le 95 ]; then
echo $n
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;${affinity}" | bc |sed 's/0000000000000000//g' |awk '{print $1 "," "00000000" "," "00000000"}' ` >/proc/irq/$n/smp_affinity
elif [ $count -gt 95 ] && [ $count -le 127 ]; then
echo $n
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;${affinity}" | bc |sed 's/000000000000000000000000//g' |awk '{print $1 "," "00000000" "," "00000000" "," "00000000"}' ` >/proc/irq/$n/smp_affinity

else
echo ff >/proc/irq/$n/smp_affinity
echo `echo "obase=16;1" | bc` >/proc/irq/$n/smp_affinity
count=0
fi
count=`/usr/bin/expr $count + 1`
if [ $count -eq $cpunum ]; then
count=0
fi
done
fi
ERROR
}

ERROR(){
rm -rf /tmp/irqsmp.txt /tmp/irqsmplist.txt
if [ `echo "$irqnum" |wc -l` -ge "$cpunum" ]; then
num="$cpunum"
elif [[ ! -z "$irqnum" ]]; then
num=`echo "$irqnum" |wc -l`
fi

for line in $irqnum; do
cat /proc/irq/${line}/smp_affinity_list >> /tmp/irqsmp.txt
cat /proc/irq/${line}/smp_affinity >> /tmp/irqsmplist.txt
done

if ( cat /tmp/irqsmplist.txt |grep "ff" ) || ( [[ `cat /tmp/irqsmp.txt |sort -u | wc -l` -ne "$num" ]] ); then
/usr/bin/msalarm -n public_Hwfault -p "set_irqbalance_failed" -h "$ip"
fi

# rm -rf /tmp/irqsmp.txt
}

set_rps(){
eth_num=`cat /proc/interrupts | grep -E "$1-|$1\["| wc -l`
RXNUM=`cat /proc/cpuinfo | grep processor | wc -l`
# NUM=$RXNUM
NUM=`ls /sys/class/net/$1/queues/ |grep "rx-"|awk -F - '{print $NF}' |wc -l`

let FNUM=$RXNUM/4;
let SOCK_FLOW_ENTRIES=$FNUM*4096
if [ $FNUM -le 8 ]
then
FSTR=''
for ((j=0; j<$FNUM;j++))
do
FSTR=${FSTR}'f'
done
elif [ $FNUM -le 16 ]
then
FSTR=',ffffffff'
FNUM=$[$FNUM-8]
for ((j=0; j<$FNUM;j++))
do
FSTR='f'${FSTR}
done
elif [ $FNUM -le 24 ]
then
FSTR=',ffffffff,ffffffff'
FNUM=$[$FNUM-16]
for ((j=0; j<$FNUM;j++))
do
FSTR='f'${FSTR}
done
elif [ $FNUM -le 32 ]
then
FSTR=',ffffffff,ffffffff,ffffffff'
FNUM=$[$FNUM-24]
for ((j=0; j<$FNUM;j++))
do
FSTR='f'${FSTR}
done
fi

# echo 98304 > /proc/sys/net/core/rps_sock_flow_entries
if [ $eth_num -le 2 ];then
let NUM=`ls /sys/class/net/$1/queues/ |grep "rx-"|awk -F - '{print $NF}'|head -n1`
echo ${FSTR} > /sys/class/net/$1/queues/rx-$NUM/rps_cpus
echo 512 > /sys/class/net/$1/queues/rx-$NUM/rps_flow_cnt
else
for ((z=0; z<$NUM; z++))
do
echo ${FSTR} > /sys/class/net/$1/queues/rx-$z/rps_cpus
echo 512 > /sys/class/net/$1/queues/rx-$z/rps_flow_cnt
done
fi
}

set_combined(){
if [[ `/usr/sbin/dmidecode -s system-product-name |grep -E "OpenStack|VMware Virtual|KVM"` ]] && [[ `/usr/sbin/ethtool -l $1 |grep Combined |sort -u |wc -l` -gt 1 ]]; then
comvalue=`/usr/sbin/ethtool -l $1 |grep Combined |awk 'NR==1{print $2}'`
/usr/sbin/ethtool -L $1 combined $comvalue
fi
}
set_adaptive(){
if ( /sbin/ethtool -i $1 | grep "driver" |grep "i40e" ) && ( /sbin/ethtool -c $1 |grep "Adaptive" |grep "on" ); then
/sbin/ethtool -C $1 adaptive-rx off
/sbin/ethtool -C $1 adaptive-tx off
fi
}

if [ $network_eth ];then
bcount=0
check_vm=`cat /proc/interrupts | grep "virtio0-"`
if [ $? -eq 0 ];then
##is vm
irqnum=`cat /proc/interrupts | grep -E "virtio0-input" | awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
irqnum=`cat /proc/interrupts | grep -E "virtio0-output" | awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
elif [ `cat /proc/interrupts | grep "virtio1-"` ]; then
irqnum=`cat /proc/interrupts | grep -E "virtio1-input" | awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
irqnum=`cat /proc/interrupts | grep -E "virtio1-output" | awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
else
irqnum=`cat /proc/interrupts | grep -E "$network_eth-|$network_eth\[" | awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
if [ `cat /proc/interrupts | grep -E "$network_eth-|$network_eth\["|wc -l` -le 2 ];then
set_rps $network_eth
fi
fi

os=`uname -r | grep 3.10`
if [ $? -eq 0 ];then
# ixgbe,set rps
check_ixgbe=`/sbin/ethtool -i $network_eth | grep driver | awk -F ":" '{print $2}'`
if [ $check_ixgbe"x" == $driver"x" ];then
set_rps $network_eth
fi
fi
set_combined $network_eth
set_adaptive $network_eth
elif [ $network_bond ];then
bond_eth=`cat /proc/net/bonding/bond* | grep "Slave Interface" |awk '{print $NF}'`
/bin/true >/tmp/bond
bcount=0
for i in $bond_eth
do
check_ixgbe=`/sbin/ethtool -i $i | grep driver | awk -F ":" '{print $2}'`
if [ $check_ixgbe"x" == $driver"x" ] && [[ `/bin/uname -r |grep -w "3.10"` ]]; then
irqnum=`cat /proc/interrupts | grep -E "$i-|$i\["| awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
bcount=`/usr/bin/expr ${bcount} + 1`
set_rps $i
else
cat /proc/interrupts | grep -E "$i-|$i\["| awk -F : '{print $1}' | awk '{print $1}' >>/tmp/bond
fi

if [ `cat /proc/interrupts | grep -E "$i-|$i\["| awk -F : '{print $1}' | awk '{print $1}'|wc -l` -le 2 ];then
set_rps $i
fi
done
bcount=0
irqnum=`cat /tmp/bond`
if [[ ! -z ${irqnum} ]];then
irq_cpu
fi
for combond in ${bond_eth}; do
set_combined $combond
set_adaptive $combond
done
fi

irqtype(){
if [[ `cat /proc/interrupts | grep "$1" |sort -u |wc -l` -ge 2 ]]; then
irqnum=`cat /proc/interrupts | grep "$1" |awk -F : '{print $1}' | awk '{print $1}'`
irq_cpu
fi
}

irqtype mpt3sas0
irqtype hpsa0
irqtype megasas
irqtype aacraid
irqtype mlx4

if [[ `cat /proc/interrupts | grep "megasas" |sort -u |wc -l` -eq 1 ]]; then
disklist=`df | grep cache | awk '{print $1}' | awk -F/ '{print $3}' | sed "s/[0-9]//g"`
IFS_OLD=$IFS
IFS=$'\n'
for x in $disklist
do
echo 2 > /sys/block/${x}/queue/rq_affinity
done
IFS=$IFS_OLD
fi

if ( `/bin/ls /sys/devices/system/cpu/cpu0/cpufreq > /dev/null 2>&1` ); then
check_value=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor`
if [ $check_value != "performance" ];then
for per in `/bin/ls /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor`;do echo performance > ${per}; done
fi
fi

参考资料

0%