xref: /spdk/test/nvmf/common.sh (revision c6c1234de9e0015e670dd0b51bf6ce39ee0e07bd)
1#  SPDX-License-Identifier: BSD-3-Clause
2#  Copyright (C) 2016 Intel Corporation
3#  Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES
4#  All rights reserved.
5#
6
7[[ $(uname -s) == FreeBSD ]] && return 0
8
9NVMF_PORT=4420
10NVMF_SECOND_PORT=4421
11NVMF_THIRD_PORT=4422
12NVMF_IP_PREFIX="192.168.100"
13NVMF_IP_LEAST_ADDR=8
14NVMF_TCP_IP_ADDRESS="127.0.0.1"
15NVMF_TRANSPORT_OPTS=""
16NVMF_SERIAL=SPDKISFASTANDAWESOME
17NVME_HOSTNQN=$(nvme gen-hostnqn)
18NVME_HOSTID=${NVME_HOSTNQN##*:}
19NVME_HOST=("--hostnqn=$NVME_HOSTNQN" "--hostid=$NVME_HOSTID")
20NVME_CONNECT="nvme connect"
21NET_TYPE=${NET_TYPE:-phy-fallback}
22NVME_SUBNQN=nqn.2016-06.io.spdk:testnqn
23
24function build_nvmf_app_args() {
25	if [ $SPDK_RUN_NON_ROOT -eq 1 ]; then
26		# We assume that test script is started from sudo
27		NVMF_APP=(sudo -E -u $SUDO_USER "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" "${NVMF_APP[@]}")
28	fi
29	NVMF_APP+=(-i "$NVMF_APP_SHM_ID" -e 0xFFFF)
30
31	NVMF_APP+=("${NO_HUGE[@]}")
32
33	if [ -n "$SPDK_HUGE_DIR" ]; then
34		NVMF_APP+=(--huge-dir "$SPDK_HUGE_DIR")
35	elif [ $SPDK_RUN_NON_ROOT -eq 1 ]; then
36		echo "In non-root test mode you have to set SPDK_HUGE_DIR variable." >&2
37		echo "For example:" >&2
38		echo "sudo mkdir /mnt/spdk_hugetlbfs" >&2
39		echo "sudo chown ${SUDO_USER}: /mnt/spdk_hugetlbfs" >&2
40		echo "export SPDK_HUGE_DIR=/mnt/spdk_hugetlbfs" >&2
41		return 1
42	fi
43}
44
45source "$rootdir/scripts/common.sh"
46
47: ${NVMF_APP_SHM_ID="0"}
48export NVMF_APP_SHM_ID
49build_nvmf_app_args
50
51have_pci_nics=0
52
53function rxe_cfg() {
54	"$rootdir/scripts/rxe_cfg_small.sh" "$@"
55}
56
57function load_ib_rdma_modules() {
58	if [ $(uname) != Linux ]; then
59		return 0
60	fi
61
62	modprobe ib_cm
63	modprobe ib_core
64	modprobe ib_umad
65	modprobe ib_uverbs
66	modprobe iw_cm
67	modprobe rdma_cm
68	modprobe rdma_ucm
69}
70
71function allocate_nic_ips() {
72	((count = NVMF_IP_LEAST_ADDR))
73	for nic_name in $(get_rdma_if_list); do
74		ip="$(get_ip_address $nic_name)"
75		if [[ -z $ip ]]; then
76			ip addr add $NVMF_IP_PREFIX.$count/24 dev $nic_name
77			ip link set $nic_name up
78			((count = count + 1))
79		fi
80		# dump configuration for debug log
81		ip addr show $nic_name
82	done
83}
84
85function get_available_rdma_ips() {
86	for nic_name in $(get_rdma_if_list); do
87		get_ip_address $nic_name
88	done
89}
90
91function get_rdma_if_list() {
92	local net_dev rxe_net_dev rxe_net_devs
93
94	mapfile -t rxe_net_devs < <(rxe_cfg rxe-net)
95
96	if ((${#net_devs[@]} == 0)); then
97		return 1
98	fi
99
100	# Pick only these devices which were found during gather_supported_nvmf_pci_devs() run
101	for net_dev in "${net_devs[@]}"; do
102		for rxe_net_dev in "${rxe_net_devs[@]}"; do
103			if [[ $net_dev == "$rxe_net_dev" ]]; then
104				echo "$net_dev"
105				continue 2
106			fi
107		done
108	done
109}
110
111function get_ip_address() {
112	interface=$1
113	ip -o -4 addr show $interface | awk '{print $4}' | cut -d"/" -f1
114}
115
116function nvmfcleanup() {
117	sync
118
119	if [ "$TEST_TRANSPORT" == "tcp" ] || [ "$TEST_TRANSPORT" == "rdma" ]; then
120		set +e
121		for i in {1..20}; do
122			modprobe -v -r nvme-$TEST_TRANSPORT
123			if modprobe -v -r nvme-fabrics; then
124				set -e
125				return 0
126			fi
127			sleep 1
128		done
129		set -e
130
131		# So far unable to remove the kernel modules. Try
132		# one more time and let it fail.
133		# Allow the transport module to fail for now. See Jim's comment
134		# about the nvme-tcp module below.
135		modprobe -v -r nvme-$TEST_TRANSPORT || true
136		modprobe -v -r nvme-fabrics
137	fi
138}
139
140function nvmf_veth_init() {
141	NVMF_FIRST_INITIATOR_IP=10.0.0.1
142	NVMF_SECOND_INITIATOR_IP=10.0.0.2
143	NVMF_FIRST_TARGET_IP=10.0.0.3
144	NVMF_SECOND_TARGET_IP=10.0.0.4
145	NVMF_INITIATOR_IP=$NVMF_FIRST_INITIATOR_IP
146	NVMF_BRIDGE="nvmf_br"
147	NVMF_INITIATOR_INTERFACE="nvmf_init_if"
148	NVMF_INITIATOR_INTERFACE2="nvmf_init_if2"
149	NVMF_INITIATOR_BRIDGE="nvmf_init_br"
150	NVMF_INITIATOR_BRIDGE2="nvmf_init_br2"
151	NVMF_TARGET_NAMESPACE="nvmf_tgt_ns_spdk"
152	NVMF_TARGET_NS_CMD=(ip netns exec "$NVMF_TARGET_NAMESPACE")
153	NVMF_TARGET_INTERFACE="nvmf_tgt_if"
154	NVMF_TARGET_INTERFACE2="nvmf_tgt_if2"
155	NVMF_TARGET_BRIDGE="nvmf_tgt_br"
156	NVMF_TARGET_BRIDGE2="nvmf_tgt_br2"
157
158	ip link set $NVMF_INITIATOR_BRIDGE nomaster || true
159	ip link set $NVMF_INITIATOR_BRIDGE2 nomaster || true
160	ip link set $NVMF_TARGET_BRIDGE nomaster || true
161	ip link set $NVMF_TARGET_BRIDGE2 nomaster || true
162	ip link set $NVMF_INITIATOR_BRIDGE down || true
163	ip link set $NVMF_INITIATOR_BRIDGE2 down || true
164	ip link set $NVMF_TARGET_BRIDGE down || true
165	ip link set $NVMF_TARGET_BRIDGE2 down || true
166	ip link delete $NVMF_BRIDGE type bridge || true
167	ip link delete $NVMF_INITIATOR_INTERFACE || true
168	ip link delete $NVMF_INITIATOR_INTERFACE2 || true
169	"${NVMF_TARGET_NS_CMD[@]}" ip link delete $NVMF_TARGET_INTERFACE || true
170	"${NVMF_TARGET_NS_CMD[@]}" ip link delete $NVMF_TARGET_INTERFACE2 || true
171
172	# Create network namespace
173	ip netns add $NVMF_TARGET_NAMESPACE
174
175	# Create veth (Virtual ethernet) interface pairs
176	ip link add $NVMF_INITIATOR_INTERFACE type veth peer name $NVMF_INITIATOR_BRIDGE
177	ip link add $NVMF_INITIATOR_INTERFACE2 type veth peer name $NVMF_INITIATOR_BRIDGE2
178	ip link add $NVMF_TARGET_INTERFACE type veth peer name $NVMF_TARGET_BRIDGE
179	ip link add $NVMF_TARGET_INTERFACE2 type veth peer name $NVMF_TARGET_BRIDGE2
180
181	# Associate veth interface pairs with network namespace
182	ip link set $NVMF_TARGET_INTERFACE netns $NVMF_TARGET_NAMESPACE
183	ip link set $NVMF_TARGET_INTERFACE2 netns $NVMF_TARGET_NAMESPACE
184
185	# Allocate IP addresses
186	ip addr add $NVMF_FIRST_INITIATOR_IP/24 dev $NVMF_INITIATOR_INTERFACE
187	ip addr add $NVMF_SECOND_INITIATOR_IP/24 dev $NVMF_INITIATOR_INTERFACE2
188	"${NVMF_TARGET_NS_CMD[@]}" ip addr add $NVMF_FIRST_TARGET_IP/24 dev $NVMF_TARGET_INTERFACE
189	"${NVMF_TARGET_NS_CMD[@]}" ip addr add $NVMF_SECOND_TARGET_IP/24 dev $NVMF_TARGET_INTERFACE2
190
191	# Link up veth interfaces
192	ip link set $NVMF_INITIATOR_INTERFACE up
193	ip link set $NVMF_INITIATOR_INTERFACE2 up
194	ip link set $NVMF_INITIATOR_BRIDGE up
195	ip link set $NVMF_INITIATOR_BRIDGE2 up
196	ip link set $NVMF_TARGET_BRIDGE up
197	ip link set $NVMF_TARGET_BRIDGE2 up
198	"${NVMF_TARGET_NS_CMD[@]}" ip link set $NVMF_TARGET_INTERFACE up
199	"${NVMF_TARGET_NS_CMD[@]}" ip link set $NVMF_TARGET_INTERFACE2 up
200	"${NVMF_TARGET_NS_CMD[@]}" ip link set lo up
201
202	# Create a bridge
203	ip link add $NVMF_BRIDGE type bridge
204	ip link set $NVMF_BRIDGE up
205
206	# Add veth interfaces to the bridge
207	ip link set $NVMF_INITIATOR_BRIDGE master $NVMF_BRIDGE
208	ip link set $NVMF_INITIATOR_BRIDGE2 master $NVMF_BRIDGE
209	ip link set $NVMF_TARGET_BRIDGE master $NVMF_BRIDGE
210	ip link set $NVMF_TARGET_BRIDGE2 master $NVMF_BRIDGE
211
212	# Accept connections from veth interface
213	ipts -I INPUT 1 -i $NVMF_INITIATOR_INTERFACE -p tcp --dport $NVMF_PORT -j ACCEPT
214	ipts -I INPUT 1 -i $NVMF_INITIATOR_INTERFACE2 -p tcp --dport $NVMF_PORT -j ACCEPT
215	ipts -A FORWARD -i $NVMF_BRIDGE -o $NVMF_BRIDGE -j ACCEPT
216
217	# Verify connectivity
218	ping -c 1 $NVMF_FIRST_TARGET_IP
219	ping -c 1 $NVMF_SECOND_TARGET_IP
220	"${NVMF_TARGET_NS_CMD[@]}" ping -c 1 $NVMF_FIRST_INITIATOR_IP
221	"${NVMF_TARGET_NS_CMD[@]}" ping -c 1 $NVMF_SECOND_INITIATOR_IP
222
223	NVMF_APP=("${NVMF_TARGET_NS_CMD[@]}" "${NVMF_APP[@]}")
224}
225
226function nvmf_veth_fini() {
227	# Cleanup bridge, veth interfaces, and network namespace
228	# Note: removing one veth removes the pair
229	ip link set $NVMF_INITIATOR_BRIDGE nomaster
230	ip link set $NVMF_INITIATOR_BRIDGE2 nomaster
231	ip link set $NVMF_TARGET_BRIDGE nomaster
232	ip link set $NVMF_TARGET_BRIDGE2 nomaster
233	ip link set $NVMF_INITIATOR_BRIDGE down
234	ip link set $NVMF_INITIATOR_BRIDGE2 down
235	ip link set $NVMF_TARGET_BRIDGE down
236	ip link set $NVMF_TARGET_BRIDGE2 down
237	ip link delete $NVMF_BRIDGE type bridge
238	ip link delete $NVMF_INITIATOR_INTERFACE
239	ip link delete $NVMF_INITIATOR_INTERFACE2
240	"${NVMF_TARGET_NS_CMD[@]}" ip link delete $NVMF_TARGET_INTERFACE
241	"${NVMF_TARGET_NS_CMD[@]}" ip link delete $NVMF_TARGET_INTERFACE2
242	remove_spdk_ns
243}
244
245function nvmf_tcp_init() {
246	NVMF_FIRST_INITIATOR_IP=10.0.0.1
247	NVMF_FIRST_TARGET_IP=10.0.0.2
248	NVMF_INITIATOR_IP=$NVMF_FIRST_INITIATOR_IP
249	TCP_INTERFACE_LIST=("${net_devs[@]}")
250
251	# We need two net devs at minimum
252	((${#TCP_INTERFACE_LIST[@]} > 1))
253
254	NVMF_TARGET_INTERFACE=${TCP_INTERFACE_LIST[0]}
255	NVMF_INITIATOR_INTERFACE=${TCP_INTERFACE_LIST[1]}
256
257	# Skip case nvmf_multipath in nvmf_tcp_init(), it will be covered by nvmf_veth_init().
258	NVMF_SECOND_TARGET_IP=""
259	NVMF_SECOND_INITIATOR_IP=""
260
261	NVMF_TARGET_NAMESPACE="${NVMF_TARGET_INTERFACE}_ns_spdk"
262	NVMF_TARGET_NS_CMD=(ip netns exec "$NVMF_TARGET_NAMESPACE")
263	ip -4 addr flush $NVMF_TARGET_INTERFACE || true
264	ip -4 addr flush $NVMF_INITIATOR_INTERFACE || true
265
266	# Create network namespace
267	ip netns add $NVMF_TARGET_NAMESPACE
268
269	# Associate phy interface pairs with network namespace
270	ip link set $NVMF_TARGET_INTERFACE netns $NVMF_TARGET_NAMESPACE
271
272	# Allocate IP addresses
273	ip addr add $NVMF_INITIATOR_IP/24 dev $NVMF_INITIATOR_INTERFACE
274	"${NVMF_TARGET_NS_CMD[@]}" ip addr add $NVMF_FIRST_TARGET_IP/24 dev $NVMF_TARGET_INTERFACE
275
276	# Link up phy interfaces
277	ip link set $NVMF_INITIATOR_INTERFACE up
278
279	"${NVMF_TARGET_NS_CMD[@]}" ip link set $NVMF_TARGET_INTERFACE up
280	"${NVMF_TARGET_NS_CMD[@]}" ip link set lo up
281
282	# Accept connections from phy interface
283	ipts -I INPUT 1 -i $NVMF_INITIATOR_INTERFACE -p tcp --dport $NVMF_PORT -j ACCEPT
284
285	# Verify connectivity
286	ping -c 1 $NVMF_FIRST_TARGET_IP
287	"${NVMF_TARGET_NS_CMD[@]}" ping -c 1 $NVMF_INITIATOR_IP
288
289	NVMF_APP=("${NVMF_TARGET_NS_CMD[@]}" "${NVMF_APP[@]}")
290}
291
292function nvmf_tcp_fini() {
293	iptr
294	if [[ "$NVMF_TARGET_NAMESPACE" == "nvmf_tgt_ns_spdk" ]]; then
295		nvmf_veth_fini
296		return 0
297	fi
298	remove_spdk_ns
299	ip -4 addr flush $NVMF_INITIATOR_INTERFACE || :
300}
301
302function gather_supported_nvmf_pci_devs() {
303	# Go through the entire pci bus and gather all ethernet controllers we support for the nvmf tests.
304	# Focus on the hardware that's currently being tested by the CI.
305	xtrace_disable
306	cache_pci_bus_sysfs
307	xtrace_restore
308
309	local intel=0x8086 mellanox=0x15b3 pci net_dev
310
311	local -a pci_devs=()
312	local -a pci_net_devs=()
313	local -A pci_drivers=()
314
315	local -ga net_devs=()
316	local -ga e810=()
317	local -ga x722=()
318	local -ga mlx=()
319
320	# E810-XXV
321	e810+=(${pci_bus_cache["$intel:0x1592"]})
322	e810+=(${pci_bus_cache["$intel:0x159b"]})
323	# X722 10G
324	x722+=(${pci_bus_cache["$intel:0x37d2"]})
325	# BlueField 3
326	mlx+=(${pci_bus_cache["$mellanox:0xa2dc"]})
327	# ConnectX-7
328	mlx+=(${pci_bus_cache["$mellanox:0x1021"]})
329	# BlueField 2
330	mlx+=(${pci_bus_cache["$mellanox:0xa2d6"]})
331	# ConnectX-6 Dx
332	mlx+=(${pci_bus_cache["$mellanox:0x101d"]})
333	# ConnectX-5
334	mlx+=(${pci_bus_cache["$mellanox:0x1017"]})
335	mlx+=(${pci_bus_cache["$mellanox:0x1019"]})
336	# ConnectX-4
337	mlx+=(${pci_bus_cache["$mellanox:0x1015"]})
338	mlx+=(${pci_bus_cache["$mellanox:0x1013"]})
339
340	pci_devs+=("${e810[@]}")
341	if [[ $TEST_TRANSPORT == rdma ]]; then
342		pci_devs+=("${x722[@]}")
343		pci_devs+=("${mlx[@]}")
344	fi
345
346	# Try to respect what CI wants to test and override pci_devs[]
347	if [[ $SPDK_TEST_NVMF_NICS == mlx5 ]]; then
348		pci_devs=("${mlx[@]}")
349	elif [[ $SPDK_TEST_NVMF_NICS == e810 ]]; then
350		pci_devs=("${e810[@]}")
351	elif [[ $SPDK_TEST_NVMF_NICS == x722 ]]; then
352		pci_devs=("${x722[@]}")
353	fi
354
355	if ((${#pci_devs[@]} == 0)); then
356		return 1
357	fi
358
359	# Load proper kernel modules if necessary
360	for pci in "${pci_devs[@]}"; do
361		echo "Found $pci (${pci_ids_vendor["$pci"]} - ${pci_ids_device["$pci"]})"
362		if [[ ${pci_mod_resolved["$pci"]} == unknown ]]; then
363			echo "Unresolved modalias for $pci (${pci_mod_driver["$pci"]}). Driver not installed|builtin?"
364			continue
365		fi
366		if [[ ${pci_bus_driver["$pci"]} == unbound ]]; then
367			echo "$pci not bound, needs ${pci_mod_resolved["$pci"]}"
368			pci_drivers["${pci_mod_resolved["$pci"]}"]=1
369		fi
370		if [[ ${pci_ids_device["$pci"]} == "0x1017" ]] \
371			|| [[ ${pci_ids_device["$pci"]} == "0x1019" ]] \
372			|| [[ $TEST_TRANSPORT == rdma ]]; then
373			# Reduce maximum number of queues when connecting with
374			# ConnectX-5 NICs. When using host systems with nproc > 64
375			# connecting with default options (where default equals to
376			# number of host online CPUs) creating all IO queues
377			# takes too much time and results in keep-alive timeout.
378			# See:
379			# https://github.com/spdk/spdk/issues/2772
380			# 0x1017 - MT27800 Family ConnectX-5
381			# 0x1019 - MT28800 Family ConnectX-5 Ex
382			NVME_CONNECT="nvme connect -i 15"
383		fi
384	done
385
386	if ((${#pci_drivers[@]} > 0)); then
387		echo "Loading kernel modules: ${!pci_drivers[*]}"
388		modprobe -a "${!pci_drivers[@]}"
389	fi
390
391	# E810 cards also need irdma driver to be around.
392	if [[ $SPDK_TEST_NVMF_NICS == e810 && $TEST_TRANSPORT == rdma ]]; then
393		if [[ -e /sys/module/irdma/parameters/roce_ena ]]; then
394			# Our tests don't play well with iWARP protocol. Make sure we use RoCEv2 instead.
395			(($(< /sys/module/irdma/parameters/roce_ena) != 1)) && modprobe -r irdma
396		fi
397		modinfo irdma && modprobe irdma roce_ena=1
398	fi > /dev/null
399
400	# All devices detected, kernel modules loaded. Now look under net class to see if there
401	# are any net devices bound to the controllers.
402	for pci in "${pci_devs[@]}"; do
403		pci_net_devs=("/sys/bus/pci/devices/$pci/net/"*)
404
405		# Check if available devices are in proper operational state. If not, remove them from the main list.
406		# This check is valid for TCP only since for RDMA we use infiniband which don't rely on actual UP
407		# state of the device.
408		if [[ $TEST_TRANSPORT == tcp ]]; then
409			for net_dev in "${!pci_net_devs[@]}"; do
410				[[ $(< "${pci_net_devs[net_dev]}/operstate") == up ]] || unset -v "pci_net_devs[net_dev]"
411			done
412		fi
413
414		if ((${#pci_net_devs[@]} == 0)); then
415			echo "No operational net devices associated with $pci"
416			continue
417		fi
418
419		pci_net_devs=("${pci_net_devs[@]##*/}")
420		echo "Found net devices under $pci: ${pci_net_devs[*]}"
421		net_devs+=("${pci_net_devs[@]}")
422	done
423
424	if ((${#net_devs[@]} == 0)); then
425		return 1
426	fi
427}
428
429prepare_net_devs() {
430	local -g is_hw=no
431
432	remove_spdk_ns
433
434	[[ $NET_TYPE != virt ]] && gather_supported_nvmf_pci_devs && is_hw=yes
435
436	if [[ $is_hw == yes ]]; then
437		if [[ $TEST_TRANSPORT == tcp ]]; then
438			nvmf_tcp_init
439		elif [[ $TEST_TRANSPORT == rdma ]]; then
440			rdma_device_init
441		fi
442		return 0
443	elif [[ $NET_TYPE == phy ]]; then
444		echo "ERROR: No supported devices were found, cannot run the $TEST_TRANSPORT test"
445		return 1
446	elif [[ $NET_TYPE == phy-fallback ]]; then
447		echo "WARNING: No supported devices were found, fallback requested for $TEST_TRANSPORT test"
448	fi
449
450	# NET_TYPE == virt or phy-fallback
451	if [[ $TEST_TRANSPORT == tcp ]]; then
452		nvmf_veth_init
453		return 0
454	fi
455
456	echo "ERROR: virt and fallback setup is not supported for $TEST_TRANSPORT"
457	return 1
458}
459
460function nvmftestinit() {
461	if [ -z $TEST_TRANSPORT ]; then
462		echo "transport not specified - use --transport= to specify"
463		return 1
464	fi
465
466	trap 'nvmftestfini' SIGINT SIGTERM EXIT
467
468	prepare_net_devs
469
470	if [ "$TEST_MODE" == "iso" ]; then
471		$rootdir/scripts/setup.sh
472	fi
473
474	NVMF_TRANSPORT_OPTS="-t $TEST_TRANSPORT"
475	if [[ "$TEST_TRANSPORT" == "rdma" ]]; then
476		RDMA_IP_LIST=$(get_available_rdma_ips)
477		NVMF_FIRST_TARGET_IP=$(echo "$RDMA_IP_LIST" | head -n 1)
478		NVMF_SECOND_TARGET_IP=$(echo "$RDMA_IP_LIST" | tail -n +2 | head -n 1)
479		if [ -z $NVMF_FIRST_TARGET_IP ]; then
480			echo "no RDMA NIC for nvmf test"
481			exit 1
482		fi
483		NVMF_TRANSPORT_OPTS="$NVMF_TRANSPORT_OPTS --num-shared-buffers 1024"
484	elif [[ "$TEST_TRANSPORT" == "tcp" ]]; then
485		NVMF_TRANSPORT_OPTS="$NVMF_TRANSPORT_OPTS -o"
486	fi
487
488	if [ "$TEST_TRANSPORT" == "tcp" ] || [ "$TEST_TRANSPORT" == "rdma" ]; then
489		# currently we run the host/perf test for TCP even on systems without kernel nvme-tcp
490		#  support; that's fine since the host/perf test uses the SPDK initiator
491		# maybe later we will enforce modprobe to succeed once we have systems in the test pool
492		#  with nvme-tcp kernel support - but until then let this pass so we can still run the
493		#  host/perf test with the tcp transport
494		modprobe nvme-$TEST_TRANSPORT || true
495	fi
496}
497
498function nvmfappstart() {
499	timing_enter start_nvmf_tgt
500	"${NVMF_APP[@]}" "$@" &
501	nvmfpid=$!
502	waitforlisten $nvmfpid
503	timing_exit start_nvmf_tgt
504	trap 'process_shm --id $NVMF_APP_SHM_ID || :; nvmftestfini' SIGINT SIGTERM EXIT
505}
506
507function nvmftestfini() {
508	nvmfcleanup || :
509	if [ -n "$nvmfpid" ]; then
510		killprocess $nvmfpid
511	fi
512	if [ "$TEST_MODE" == "iso" ]; then
513		$rootdir/scripts/setup.sh reset
514	fi
515	if [[ "$TEST_TRANSPORT" == "tcp" ]]; then
516		nvmf_tcp_fini
517	fi
518}
519
520function rdma_device_init() {
521	load_ib_rdma_modules
522	allocate_nic_ips
523}
524
525function nvme_connect() {
526	local init_count
527	init_count=$(nvme list | wc -l)
528
529	if ! nvme connect "$@"; then return $?; fi
530
531	for i in $(seq 1 10); do
532		if [ $(nvme list | wc -l) -gt $init_count ]; then
533			return 0
534		else
535			sleep 1s
536		fi
537	done
538	return 1
539}
540
541function get_nvme_devs() {
542	local dev _
543
544	while read -r dev _; do
545		if [[ $dev == /dev/nvme* ]]; then
546			echo "$dev"
547		fi
548	done < <(nvme list)
549}
550
551function gen_nvmf_target_json() {
552	local subsystem config=()
553
554	for subsystem in "${@:-1}"; do
555		config+=(
556			"$(
557				cat <<- EOF
558					{
559					  "params": {
560					    "name": "Nvme$subsystem",
561					    "trtype": "$TEST_TRANSPORT",
562					    "traddr": "$NVMF_FIRST_TARGET_IP",
563					    "adrfam": "ipv4",
564					    "trsvcid": "$NVMF_PORT",
565					    "subnqn": "nqn.2016-06.io.spdk:cnode$subsystem",
566					    "hostnqn": "nqn.2016-06.io.spdk:host$subsystem",
567					    "hdgst": ${hdgst:-false},
568					    "ddgst": ${ddgst:-false}
569					  },
570					  "method": "bdev_nvme_attach_controller"
571					}
572				EOF
573			)"
574		)
575	done
576	jq . <<- JSON
577		{
578		  "subsystems": [
579		    {
580		      "subsystem": "bdev",
581		      "config": [
582			{
583			  "method": "bdev_nvme_set_options",
584			  "params": {
585				"action_on_timeout": "none",
586				"timeout_us": 0,
587				"transport_retry_count": 4,
588				"arbitration_burst": 0,
589				"low_priority_weight": 0,
590				"medium_priority_weight": 0,
591				"high_priority_weight": 0,
592				"nvme_adminq_poll_period_us": 10000,
593				"keep_alive_timeout_ms" : 10000,
594				"nvme_ioq_poll_period_us": 0,
595				"io_queue_requests": 0,
596				"delay_cmd_submit": true
597			  }
598			},
599		        $(
600			IFS=","
601			printf '%s\n' "${config[*]}"
602		),
603			{
604			  "method": "bdev_wait_for_examine"
605			}
606		      ]
607		    }
608		  ]
609		}
610	JSON
611}
612
613function _remove_spdk_ns() {
614	local ns {ns,mn,an}_net_devs
615	while read -r ns _; do
616		[[ $ns == *_spdk ]] || continue
617		# Gather all devs from the target $ns namespace. We want to differentiate
618		# between veth and physical links and gather just the latter. To do so,
619		# we simply compare ifindex to iflink - as per kernel docs, these should
620		# be always equal for the physical links. For veth devices, since they are
621		# paired, iflink should point at an actual bridge, hence being different
622		# from its own ifindex.
623		ns_net_devs=($(
624			ip netns exec "$ns" bash <<- 'IN_NS'
625				shopt -s extglob nullglob
626				for dev in /sys/class/net/!(lo|bond*); do
627					(($(< "$dev/ifindex") == $(< "$dev/iflink"))) || continue
628					echo "${dev##*/}"
629				done
630			IN_NS
631		))
632		# Gather all the net devs from the main ns
633		mn_net_devs=($(basename -a /sys/class/net/!(lo|bond*)))
634		# Merge these two to have a list for comparison
635		an_net_devs=($(printf '%s\n' "${ns_net_devs[@]}" "${mn_net_devs[@]}" | sort))
636
637		ip netns delete "$ns"
638
639		# Check if our list matches against the main ns after $ns got deleted
640		while [[ ${an_net_devs[*]} != "${mn_net_devs[*]}" ]]; do
641			mn_net_devs=($(basename -a /sys/class/net/!(lo|bond*)))
642			sleep 1s
643		done
644	done < <(ip netns list)
645}
646
647remove_spdk_ns() {
648	xtrace_disable_per_cmd _remove_spdk_ns
649}
650
651configure_kernel_target() {
652	local kernel_name=$1 kernel_target_ip=$2
653	# Keep it global in scope for easier cleanup
654	nvmet=/sys/kernel/config/nvmet
655	kernel_subsystem=$nvmet/subsystems/$kernel_name
656	kernel_namespace=$kernel_subsystem/namespaces/1
657	kernel_port=$nvmet/ports/1
658
659	local block nvme
660
661	if [[ ! -e /sys/module/nvmet ]]; then
662		modprobe nvmet
663	fi
664
665	[[ -e $nvmet ]]
666
667	"$rootdir/scripts/setup.sh" reset
668
669	# Find nvme with an active ns device
670	for block in /sys/block/nvme*; do
671		[[ -e $block ]] || continue
672		is_block_zoned "${block##*/}" && continue
673		block_in_use "${block##*/}" || nvme="/dev/${block##*/}"
674	done
675
676	[[ -b $nvme ]]
677
678	mkdir "$kernel_subsystem"
679	mkdir "$kernel_namespace"
680	mkdir "$kernel_port"
681
682	# It allows only %llx value and for some reason kernel swaps the byte order
683	# so setting the serial is not very useful here
684	# "$kernel_subsystem/attr_serial"
685	echo "SPDK-$kernel_name" > "$kernel_subsystem/attr_model"
686
687	echo 1 > "$kernel_subsystem/attr_allow_any_host"
688	echo "$nvme" > "$kernel_namespace/device_path"
689	echo 1 > "$kernel_namespace/enable"
690
691	echo "$kernel_target_ip" > "$kernel_port/addr_traddr"
692	echo "$TEST_TRANSPORT" > "$kernel_port/addr_trtype"
693	echo "$NVMF_PORT" > "$kernel_port/addr_trsvcid"
694	echo ipv4 > "$kernel_port/addr_adrfam"
695
696	# Enable the listener by linking the port to previously created subsystem
697	ln -s "$kernel_subsystem" "$kernel_port/subsystems/"
698
699	# Check if target is available
700	nvme discover "${NVME_HOST[@]}" -a "$kernel_target_ip" -t "$TEST_TRANSPORT" -s "$NVMF_PORT"
701}
702
703clean_kernel_target() {
704	[[ -e $kernel_subsystem ]] || return 0
705
706	echo 0 > "$kernel_namespace/enable"
707
708	rm -f "$kernel_port/subsystems/${kernel_subsystem##*/}"
709	rmdir "$kernel_namespace"
710	rmdir "$kernel_port"
711	rmdir "$kernel_subsystem"
712
713	modules=(/sys/module/nvmet/holders/*)
714
715	modprobe -r "${modules[@]##*/}" nvmet
716
717	# Get back all nvmes to userspace
718	"$rootdir/scripts/setup.sh"
719}
720
721format_key() {
722	local prefix key digest
723
724	prefix="$1" key="$2" digest="$3"
725	python - <<- EOF
726		import base64, zlib
727
728		crc = zlib.crc32(b"$key").to_bytes(4, byteorder="little")
729		b64 = base64.b64encode(b"$key" + crc).decode("utf-8")
730		print("$prefix:{:02x}:{}:".format($digest, b64), end="")
731	EOF
732}
733
734format_interchange_psk() {
735	format_key "NVMeTLSkey-1" "$1" "$2"
736}
737
738format_dhchap_key() {
739	format_key "DHHC-1" "$1" "$2"
740}
741
742gen_dhchap_key() {
743	local digest len file key
744	local -A digests=([null]=0 [sha256]=1 [sha384]=2 [sha512]=3)
745
746	digest="$1" len=$2
747	key=$(xxd -p -c0 -l $((len / 2)) /dev/urandom)
748	file=$(mktemp -t "spdk.key-$1.XXX")
749	format_dhchap_key "$key" "${digests[$1]}" > "$file"
750	chmod 0600 "$file"
751
752	echo "$file"
753}
754
755get_main_ns_ip() {
756	# Determine which ip to use based on nvmftestinit() setup. For tcp we pick
757	# interface which resides in the main net namespace and which is visible
758	# to nvmet under tcp setup. $NVMF_FIRST_TARGET_IP is solely for rdma use.
759	# FIXME: This requires proper unification of the networking setup across
760	# different transports.
761	local ip
762	local -A ip_candidates=()
763
764	ip_candidates["rdma"]=NVMF_FIRST_TARGET_IP
765	ip_candidates["tcp"]=NVMF_INITIATOR_IP
766
767	[[ -z $TEST_TRANSPORT || -z ${ip_candidates["$TEST_TRANSPORT"]} ]] && return 1
768	ip=${ip_candidates["$TEST_TRANSPORT"]}
769
770	if [[ -z ${!ip} ]]; then
771		echo "$ip not set, call nvmftestinit() first" >&2
772		return 1
773	fi
774
775	echo "${!ip}"
776}
777
778uuid2nguid() {
779	tr -d - <<< "${1^^}"
780}
781
782ipts() { iptables "$@" -m comment --comment "SPDK_NVMF:$*"; }
783iptr() { iptables-save | grep -v SPDK_NVMF | iptables-restore; }
784