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
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# extended toeplitz test: test rxhash plus, optionally, either (1) rss mapping
# from rxhash to rx queue ('-rss') or (2) rps mapping from rxhash to cpu
# ('-rps <rps_map>')
#
# irq-pattern-prefix can be derived from /sys/kernel/irq/*/action,
# which is a driver-specific encoding.
#
# invoke as ./toeplitz.sh (-i <iface>) -u|-t -4|-6 \
# [(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)]
source setup_loopback.sh
readonly SERVER_IP4="192.168.1.200/24"
readonly SERVER_IP6="fda8::1/64"
readonly SERVER_MAC="aa:00:00:00:00:02"
readonly CLIENT_IP4="192.168.1.100/24"
readonly CLIENT_IP6="fda8::2/64"
readonly CLIENT_MAC="aa:00:00:00:00:01"
PORT=8000
KEY="$(</proc/sys/net/core/netdev_rss_key)"
TEST_RSS=false
RPS_MAP=""
PROTO_FLAG=""
IP_FLAG=""
DEV="eth0"
# Return the number of rxqs among which RSS is configured to spread packets.
# This is determined by reading the RSS indirection table using ethtool.
get_rss_cfg_num_rxqs() {
echo $(ethtool -x "${DEV}" |
grep -E [[:space:]]+[0-9]+:[[:space:]]+ |
cut -d: -f2- |
awk '{$1=$1};1' |
tr ' ' '\n' |
sort -u |
wc -l)
}
# Return a list of the receive irq handler cpus.
# The list is ordered by the irqs, so first rxq-0 cpu, then rxq-1 cpu, etc.
# Reads /sys/kernel/irq/ in order, so algorithm depends on
# irq_{rxq-0} < irq_{rxq-1}, etc.
get_rx_irq_cpus() {
CPUS=""
# sort so that irq 2 is read before irq 10
SORTED_IRQS=$(for i in /sys/kernel/irq/*; do echo $i; done | sort -V)
# Consider only as many queues as RSS actually uses. We assume that
# if RSS_CFG_NUM_RXQS=N, then RSS uses rxqs 0-(N-1).
RSS_CFG_NUM_RXQS=$(get_rss_cfg_num_rxqs)
RXQ_COUNT=0
for i in ${SORTED_IRQS}
do
[[ "${RXQ_COUNT}" -lt "${RSS_CFG_NUM_RXQS}" ]] || break
# lookup relevant IRQs by action name
[[ -e "$i/actions" ]] || continue
cat "$i/actions" | grep -q "${IRQ_PATTERN}" || continue
irqname=$(<"$i/actions")
# does the IRQ get called
irqcount=$(cat "$i/per_cpu_count" | tr -d '0,')
[[ -n "${irqcount}" ]] || continue
# lookup CPU
irq=$(basename "$i")
cpu=$(cat "/proc/irq/$irq/smp_affinity_list")
if [[ -z "${CPUS}" ]]; then
CPUS="${cpu}"
else
CPUS="${CPUS},${cpu}"
fi
RXQ_COUNT=$((RXQ_COUNT+1))
done
echo "${CPUS}"
}
get_disable_rfs_cmd() {
echo "echo 0 > /proc/sys/net/core/rps_sock_flow_entries;"
}
get_set_rps_bitmaps_cmd() {
CMD=""
for i in /sys/class/net/${DEV}/queues/rx-*/rps_cpus
do
CMD="${CMD} echo $1 > ${i};"
done
echo "${CMD}"
}
get_disable_rps_cmd() {
echo "$(get_set_rps_bitmaps_cmd 0)"
}
die() {
echo "$1"
exit 1
}
check_nic_rxhash_enabled() {
local -r pattern="receive-hashing:\ on"
ethtool -k "${DEV}" | grep -q "${pattern}" || die "rxhash must be enabled"
}
parse_opts() {
local prog=$0
shift 1
while [[ "$1" =~ "-" ]]; do
if [[ "$1" = "-irq_prefix" ]]; then
shift
IRQ_PATTERN="^$1-[0-9]*$"
elif [[ "$1" = "-u" || "$1" = "-t" ]]; then
PROTO_FLAG="$1"
elif [[ "$1" = "-4" ]]; then
IP_FLAG="$1"
SERVER_IP="${SERVER_IP4}"
CLIENT_IP="${CLIENT_IP4}"
elif [[ "$1" = "-6" ]]; then
IP_FLAG="$1"
SERVER_IP="${SERVER_IP6}"
CLIENT_IP="${CLIENT_IP6}"
elif [[ "$1" = "-rss" ]]; then
TEST_RSS=true
elif [[ "$1" = "-rps" ]]; then
shift
RPS_MAP="$1"
elif [[ "$1" = "-i" ]]; then
shift
DEV="$1"
else
die "Usage: ${prog} (-i <iface>) -u|-t -4|-6 \
[(-rss -irq_prefix <irq-pattern-prefix>)|(-rps <rps_map>)]"
fi
shift
done
}
setup() {
setup_loopback_environment "${DEV}"
# Set up server_ns namespace and client_ns namespace
setup_macvlan_ns "${DEV}" server_ns server \
"${SERVER_MAC}" "${SERVER_IP}"
setup_macvlan_ns "${DEV}" client_ns client \
"${CLIENT_MAC}" "${CLIENT_IP}"
}
cleanup() {
cleanup_macvlan_ns server_ns server client_ns client
cleanup_loopback "${DEV}"
}
parse_opts $0 $@
setup
trap cleanup EXIT
check_nic_rxhash_enabled
# Actual test starts here
if [[ "${TEST_RSS}" = true ]]; then
# RPS/RFS must be disabled because they move packets between cpus,
# which breaks the PACKET_FANOUT_CPU identification of RSS decisions.
eval "$(get_disable_rfs_cmd) $(get_disable_rps_cmd)" \
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \
-C "$(get_rx_irq_cpus)" -s -v &
elif [[ ! -z "${RPS_MAP}" ]]; then
eval "$(get_disable_rfs_cmd) $(get_set_rps_bitmaps_cmd ${RPS_MAP})" \
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 \
-r "0x${RPS_MAP}" -s -v &
else
ip netns exec server_ns ./toeplitz "${IP_FLAG}" "${PROTO_FLAG}" \
-d "${PORT}" -i "${DEV}" -k "${KEY}" -T 1000 -s -v &
fi
server_pid=$!
ip netns exec client_ns ./toeplitz_client.sh "${PROTO_FLAG}" \
"${IP_FLAG}" "${SERVER_IP%%/*}" "${PORT}" &
client_pid=$!
wait "${server_pid}"
exit_code=$?
kill -9 "${client_pid}"
if [[ "${exit_code}" -eq 0 ]]; then
echo "Test Succeeded!"
fi
exit "${exit_code}"
|