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