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