xref: /spdk/scripts/setup.sh (revision 296e7fba0335252f8c81c63a6c45d34ac054bc75)
1#!/usr/bin/env bash
2
3set -e
4
5rootdir=$(readlink -f $(dirname $0))/..
6source "$rootdir/scripts/common.sh"
7
8function usage()
9{
10	if [ $(uname) = Linux ]; then
11		options="[config|reset|status|cleanup|help]"
12	else
13		options="[config|reset|help]"
14	fi
15
16	[[ -n $2 ]] && ( echo "$2"; echo ""; )
17	echo "Helper script for allocating hugepages and binding NVMe, I/OAT, VMD and Virtio devices"
18	echo "to a generic VFIO kernel driver. If VFIO is not available on the system, this script"
19	echo "will fall back to UIO. NVMe and Virtio devices with active mountpoints will be ignored."
20	echo "All hugepage operations use default hugepage size on the system (hugepagesz)."
21	echo "Usage: $(basename $1) $options"
22	echo
23	echo "$options - as following:"
24	echo "config            Default mode. Allocate hugepages and bind PCI devices."
25	if [ $(uname) = Linux ]; then
26		echo "cleanup            Remove any orphaned files that can be left in the system after SPDK application exit"
27	fi
28	echo "reset             Rebind PCI devices back to their original drivers."
29	echo "                  Also cleanup any leftover spdk files/resources."
30	echo "                  Hugepage memory size will remain unchanged."
31	if [ $(uname) = Linux ]; then
32		echo "status            Print status of all SPDK-compatible devices on the system."
33	fi
34	echo "help              Print this help message."
35	echo
36	echo "The following environment variables can be specified."
37	echo "HUGEMEM           Size of hugepage memory to allocate (in MB). 2048 by default."
38	echo "                  For NUMA systems, the hugepages will be evenly distributed"
39	echo "                  between CPU nodes"
40	echo "NRHUGE            Number of hugepages to allocate. This variable overwrites HUGEMEM."
41	echo "HUGENODE          Specific NUMA node to allocate hugepages on. To allocate"
42	echo "                  hugepages on multiple nodes run this script multiple times -"
43	echo "                  once for each node."
44	echo "PCI_WHITELIST"
45	echo "PCI_BLACKLIST     Whitespace separated list of PCI devices (NVMe, I/OAT, VMD, Virtio)."
46	echo "                  Each device must be specified as a full PCI address."
47	echo "                  E.g. PCI_WHITELIST=\"0000:01:00.0 0000:02:00.0\""
48	echo "                  To blacklist all PCI devices use a non-valid address."
49	echo "                  E.g. PCI_WHITELIST=\"none\""
50	echo "                  If PCI_WHITELIST and PCI_BLACKLIST are empty or unset, all PCI devices"
51	echo "                  will be bound."
52	echo "                  Each device in PCI_BLACKLIST will be ignored (driver won't be changed)."
53	echo "                  PCI_BLACKLIST has precedence over PCI_WHITELIST."
54	echo "TARGET_USER       User that will own hugepage mountpoint directory and vfio groups."
55	echo "                  By default the current user will be used."
56	echo "DRIVER_OVERRIDE   Disable automatic vfio-pci/uio_pci_generic selection and forcefully"
57	echo "                  bind devices to the given driver."
58	echo "                  E.g. DRIVER_OVERRIDE=uio_pci_generic or DRIVER_OVERRIDE=/home/public/dpdk/build/kmod/igb_uio.ko"
59	exit 0
60}
61
62# In monolithic kernels the lsmod won't work. So
63# back that with a /sys/modules. We also check
64# /sys/bus/pci/drivers/ as neither lsmod nor /sys/modules might
65# contain needed info (like in Fedora-like OS).
66function check_for_driver {
67	if lsmod | grep -q ${1//-/_}; then
68		return 1
69	fi
70
71	if [[ -d /sys/module/${1} || \
72			-d /sys/module/${1//-/_} || \
73			-d /sys/bus/pci/drivers/${1} || \
74			-d /sys/bus/pci/drivers/${1//-/_} ]]; then
75		return 2
76	fi
77	return 0
78}
79
80function pci_dev_echo() {
81	local bdf="$1"
82	local vendor="$(cat /sys/bus/pci/devices/$bdf/vendor)"
83	local device="$(cat /sys/bus/pci/devices/$bdf/device)"
84	shift
85	echo "$bdf (${vendor#0x} ${device#0x}): $*"
86}
87
88function linux_bind_driver() {
89	bdf="$1"
90	driver_name="$2"
91	old_driver_name="no driver"
92	ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
93
94	if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
95		old_driver_name=$(basename $(readlink /sys/bus/pci/devices/$bdf/driver))
96
97		if [ "$driver_name" = "$old_driver_name" ]; then
98			pci_dev_echo "$bdf" "Already using the $old_driver_name driver"
99			return 0
100		fi
101
102		echo "$ven_dev_id" > "/sys/bus/pci/devices/$bdf/driver/remove_id" 2> /dev/null || true
103		echo "$bdf" > "/sys/bus/pci/devices/$bdf/driver/unbind"
104	fi
105
106	pci_dev_echo "$bdf" "$old_driver_name -> $driver_name"
107
108	echo "$ven_dev_id" > "/sys/bus/pci/drivers/$driver_name/new_id" 2> /dev/null || true
109	echo "$bdf" > "/sys/bus/pci/drivers/$driver_name/bind" 2> /dev/null || true
110
111	iommu_group=$(basename $(readlink -f /sys/bus/pci/devices/$bdf/iommu_group))
112	if [ -e "/dev/vfio/$iommu_group" ]; then
113		if [ -n "$TARGET_USER" ]; then
114			chown "$TARGET_USER" "/dev/vfio/$iommu_group"
115		fi
116	fi
117}
118
119function linux_unbind_driver() {
120	local bdf="$1"
121	local ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
122	local old_driver_name="no driver"
123
124	if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
125		old_driver_name=$(basename $(readlink /sys/bus/pci/devices/$bdf/driver))
126		echo "$ven_dev_id" > "/sys/bus/pci/devices/$bdf/driver/remove_id" 2> /dev/null || true
127		echo "$bdf" > "/sys/bus/pci/devices/$bdf/driver/unbind"
128	fi
129
130	pci_dev_echo "$bdf" "$old_driver_name -> no driver"
131}
132
133function linux_hugetlbfs_mounts() {
134	mount | grep ' type hugetlbfs ' | awk '{ print $3 }'
135}
136
137function get_nvme_name_from_bdf {
138	local blknames=()
139
140	set +e
141	nvme_devs=$(lsblk -d --output NAME | grep "^nvme")
142	set -e
143	for dev in $nvme_devs; do
144		link_name=$(readlink /sys/block/$dev/device/device) || true
145		if [ -z "$link_name" ]; then
146			link_name=$(readlink /sys/block/$dev/device)
147		fi
148		link_bdf=$(basename "$link_name")
149		if [ "$link_bdf" = "$1" ]; then
150			blknames+=($dev)
151		fi
152	done
153
154	printf '%s\n' "${blknames[@]}"
155}
156
157function get_virtio_names_from_bdf {
158	blk_devs=$(lsblk --nodeps --output NAME)
159	virtio_names=''
160
161	for dev in $blk_devs; do
162		if readlink "/sys/block/$dev" | grep -q "$1"; then
163			virtio_names="$virtio_names $dev"
164		fi
165	done
166
167	eval "$2='$virtio_names'"
168}
169
170function configure_linux_pci {
171	local driver_path=""
172	driver_name=""
173	if [[ -n "${DRIVER_OVERRIDE}" ]]; then
174		driver_path="${DRIVER_OVERRIDE%/*}"
175		driver_name="${DRIVER_OVERRIDE##*/}"
176		# path = name -> there is no path
177		if [[ "$driver_path" = "$driver_name" ]]; then
178			driver_path=""
179		fi
180	elif [[ -n "$(ls /sys/kernel/iommu_groups)" || \
181	     (-e /sys/module/vfio/parameters/enable_unsafe_noiommu_mode && \
182	     "$(cat /sys/module/vfio/parameters/enable_unsafe_noiommu_mode)" == "Y") ]]; then
183		driver_name=vfio-pci
184	elif modinfo uio_pci_generic >/dev/null 2>&1; then
185		driver_name=uio_pci_generic
186	elif [[ -r "$rootdir/dpdk/build/kmod/igb_uio.ko" ]]; then
187		driver_path="$rootdir/dpdk/build/kmod/igb_uio.ko"
188		driver_name="igb_uio"
189		modprobe uio
190		echo "WARNING: uio_pci_generic not detected - using $driver_name"
191	else
192		echo "No valid drivers found [vfio-pci, uio_pci_generic, igb_uio]. Please either enable the vfio-pci or uio_pci_generic"
193		echo "kernel modules, or have SPDK build the igb_uio driver by running ./configure --with-igb-uio-driver and recompiling."
194		return 1
195	fi
196
197	# modprobe assumes the directory of the module. If the user passes in a path, we should use insmod
198	if [[ -n "$driver_path" ]]; then
199		insmod $driver_path || true
200	else
201		modprobe $driver_name
202	fi
203
204	# NVMe
205	for bdf in $(iter_all_pci_class_code 01 08 02); do
206		blknames=()
207		if ! pci_can_use $bdf; then
208			pci_dev_echo "$bdf" "Skipping un-whitelisted NVMe controller at $bdf"
209			continue
210		fi
211
212		mount=false
213		for blkname in $(get_nvme_name_from_bdf $bdf); do
214			mountpoints=$(lsblk /dev/$blkname --output MOUNTPOINT -n | wc -w)
215			if [ "$mountpoints" != "0" ]; then
216				mount=true
217				blknames+=($blkname)
218			fi
219		done
220
221		if ! $mount; then
222			linux_bind_driver "$bdf" "$driver_name"
223		else
224			for name in ${blknames[@]}; do
225				pci_dev_echo "$bdf" "Active mountpoints on /dev/$name, so not binding PCI dev"
226			done
227		fi
228	done
229
230	# IOAT
231	TMP=$(mktemp)
232	#collect all the device_id info of ioat devices.
233	grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/include/spdk/pci_ids.h \
234	| awk -F"x" '{print $2}' > $TMP
235
236	while IFS= read -r dev_id
237	do
238		for bdf in $(iter_all_pci_dev_id 8086 $dev_id); do
239			if ! pci_can_use $bdf; then
240				pci_dev_echo "$bdf" "Skipping un-whitelisted I/OAT device"
241				continue
242			fi
243
244			linux_bind_driver "$bdf" "$driver_name"
245		done
246	done < $TMP
247	rm $TMP
248
249	# virtio
250	TMP=$(mktemp)
251	#collect all the device_id info of virtio devices.
252	grep "PCI_DEVICE_ID_VIRTIO" $rootdir/include/spdk/pci_ids.h \
253	| awk -F"x" '{print $2}' > $TMP
254
255	while IFS= read -r dev_id
256	do
257		for bdf in $(iter_all_pci_dev_id 1af4 $dev_id); do
258			if ! pci_can_use $bdf; then
259				pci_dev_echo "$bdf" "Skipping un-whitelisted Virtio device at $bdf"
260				continue
261			fi
262			blknames=()
263			get_virtio_names_from_bdf "$bdf" blknames
264			for blkname in $blknames; do
265				if [ "$(lsblk /dev/$blkname --output MOUNTPOINT -n | wc -w)" != "0" ]; then
266					pci_dev_echo "$bdf" "Active mountpoints on /dev/$blkname, so not binding"
267					continue 2
268				fi
269			done
270
271			linux_bind_driver "$bdf" "$driver_name"
272		done
273	done < $TMP
274	rm $TMP
275
276	# VMD
277	TMP=$(mktemp)
278	#collect all the device_id info of vmd devices.
279	grep "PCI_DEVICE_ID_INTEL_VMD" $rootdir/include/spdk/pci_ids.h \
280	| awk -F"x" '{print $2}' > $TMP
281
282	while IFS= read -r dev_id
283	do
284		for bdf in $(iter_pci_dev_id 8086 $dev_id); do
285			if [[ -z "$PCI_WHITELIST" ]] || ! pci_can_use $bdf; then
286				echo "Skipping un-whitelisted VMD device at $bdf"
287				continue
288			fi
289
290			linux_bind_driver "$bdf" "$driver_name"
291                        echo " VMD generic kdrv: " "$bdf" "$driver_name"
292		done
293	done < $TMP
294	rm $TMP
295
296	echo "1" > "/sys/bus/pci/rescan"
297}
298
299function cleanup_linux {
300	shopt -s extglob nullglob
301	dirs_to_clean=""
302	dirs_to_clean="$(echo {/var/run,/tmp}/dpdk/spdk{,_pid}+([0-9])) "
303	if [[ -d $XDG_RUNTIME_DIR && $XDG_RUNTIME_DIR != *" "* ]]; then
304		dirs_to_clean+="$(readlink -e assert_not_empty $XDG_RUNTIME_DIR/dpdk/spdk{,_pid}+([0-9]) || true) "
305	fi
306
307	files_to_clean=""
308	for dir in $dirs_to_clean; do
309		files_to_clean+="$(echo $dir/*) "
310	done
311	shopt -u extglob nullglob
312
313	files_to_clean+="$(ls -1 /dev/shm/* | \
314	grep -E '(spdk_tgt|iscsi|vhost|nvmf|rocksdb|bdevio|bdevperf|vhost_fuzz|nvme_fuzz)_trace|spdk_iscsi_conns' || true) "
315	files_to_clean="$(readlink -e assert_not_empty $files_to_clean || true)"
316	if [[ -z "$files_to_clean" ]]; then
317		echo "Clean"
318		return 0;
319	fi
320
321	shopt -s extglob
322	for fd_dir in $(echo /proc/+([0-9])); do
323		opened_files+="$(readlink -e assert_not_empty $fd_dir/fd/* || true)"
324	done
325	shopt -u extglob
326
327	if [[ -z "$opened_files" ]]; then
328		echo "Can't get list of opened files!"
329		exit 1
330	fi
331
332	echo 'Cleaning'
333	for f in $files_to_clean; do
334		if ! echo "$opened_files" | grep -E -q "^$f\$"; then
335			echo "Removing:    $f"
336			rm $f
337		else
338			echo "Still open: $f"
339		fi
340	done
341
342	for dir in $dirs_to_clean; do
343	if ! echo "$opened_files" | grep -E -q "^$dir\$"; then
344		echo "Removing:    $dir"
345		rmdir $dir
346	else
347		echo "Still open: $dir"
348	fi
349	done
350	echo "Clean"
351
352	unset dirs_to_clean files_to_clean opened_files
353}
354
355function configure_linux {
356	configure_linux_pci
357	hugetlbfs_mounts=$(linux_hugetlbfs_mounts)
358
359	if [ -z "$hugetlbfs_mounts" ]; then
360		hugetlbfs_mounts=/mnt/huge
361		echo "Mounting hugetlbfs at $hugetlbfs_mounts"
362		mkdir -p "$hugetlbfs_mounts"
363		mount -t hugetlbfs nodev "$hugetlbfs_mounts"
364	fi
365
366	if [ -z "$HUGENODE" ]; then
367		hugepages_target="/proc/sys/vm/nr_hugepages"
368	else
369		hugepages_target="/sys/devices/system/node/node${HUGENODE}/hugepages/hugepages-${HUGEPGSZ}kB/nr_hugepages"
370	fi
371
372	echo "$NRHUGE" > "$hugepages_target"
373	allocated_hugepages=$(cat $hugepages_target)
374	if [ "$allocated_hugepages" -lt "$NRHUGE" ]; then
375		echo ""
376		echo "## ERROR: requested $NRHUGE hugepages but only $allocated_hugepages could be allocated."
377		echo "## Memory might be heavily fragmented. Please try flushing the system cache, or reboot the machine."
378		exit 1
379	fi
380
381	if [ "$driver_name" = "vfio-pci" ]; then
382		if [ -n "$TARGET_USER" ]; then
383			for mount in $hugetlbfs_mounts; do
384				chown "$TARGET_USER" "$mount"
385				chmod g+w "$mount"
386			done
387		fi
388
389		MEMLOCK_AMNT=$(ulimit -l)
390		if [ "$MEMLOCK_AMNT" != "unlimited" ] ; then
391			MEMLOCK_MB=$(( $MEMLOCK_AMNT / 1024 ))
392			echo ""
393			echo "Current user memlock limit: ${MEMLOCK_MB} MB"
394			echo ""
395			echo "This is the maximum amount of memory you will be"
396			echo "able to use with DPDK and VFIO if run as current user."
397			echo -n "To change this, please adjust limits.conf memlock "
398			echo "limit for current user."
399
400			if [ $MEMLOCK_AMNT -lt 65536 ] ; then
401				echo ""
402				echo "## WARNING: memlock limit is less than 64MB"
403				echo -n "## DPDK with VFIO may not be able to initialize "
404				echo "if run as current user."
405			fi
406		fi
407	fi
408
409	if [ ! -f /dev/cpu/0/msr ]; then
410		# Some distros build msr as a module.  Make sure it's loaded to ensure
411		#  DPDK can easily figure out the TSC rate rather than relying on 100ms
412		#  sleeps.
413		modprobe msr || true
414	fi
415}
416
417function reset_linux_pci {
418	# NVMe
419	set +e
420	check_for_driver nvme
421	driver_loaded=$?
422	set -e
423	for bdf in $(iter_all_pci_class_code 01 08 02); do
424		if ! pci_can_use $bdf; then
425			pci_dev_echo "$bdf" "Skipping un-whitelisted NVMe controller $blkname"
426			continue
427		fi
428		if [ $driver_loaded -ne 0 ]; then
429			linux_bind_driver "$bdf" nvme
430		else
431			linux_unbind_driver "$bdf"
432		fi
433	done
434
435	# IOAT
436	TMP=$(mktemp)
437	#collect all the device_id info of ioat devices.
438	grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/include/spdk/pci_ids.h \
439	| awk -F"x" '{print $2}' > $TMP
440
441	set +e
442	check_for_driver ioatdma
443	driver_loaded=$?
444	set -e
445	while IFS= read -r dev_id
446	do
447		for bdf in $(iter_all_pci_dev_id 8086 $dev_id); do
448			if ! pci_can_use $bdf; then
449				pci_dev_echo "$bdf" "Skipping un-whitelisted I/OAT device"
450				continue
451			fi
452			if [ $driver_loaded -ne 0 ]; then
453				linux_bind_driver "$bdf" ioatdma
454			else
455				linux_unbind_driver "$bdf"
456			fi
457		done
458	done < $TMP
459	rm $TMP
460
461	# virtio
462	TMP=$(mktemp)
463	#collect all the device_id info of virtio devices.
464	grep "PCI_DEVICE_ID_VIRTIO" $rootdir/include/spdk/pci_ids.h \
465	| awk -F"x" '{print $2}' > $TMP
466
467	# TODO: check if virtio-pci is loaded first and just unbind if it is not loaded
468	# Requires some more investigation - for example, some kernels do not seem to have
469	#  virtio-pci but just virtio_scsi instead.  Also need to make sure we get the
470	#  underscore vs. dash right in the virtio_scsi name.
471	modprobe virtio-pci || true
472	while IFS= read -r dev_id
473	do
474		for bdf in $(iter_all_pci_dev_id 1af4 $dev_id); do
475			if ! pci_can_use $bdf; then
476				pci_dev_echo "$bdf" "Skipping un-whitelisted Virtio device at"
477				continue
478			fi
479			linux_bind_driver "$bdf" virtio-pci
480		done
481	done < $TMP
482	rm $TMP
483
484	# VMD
485	TMP=$(mktemp)
486	#collect all the device_id info of vmd devices.
487	grep "PCI_DEVICE_ID_INTEL_VMD" $rootdir/include/spdk/pci_ids.h \
488	| awk -F"x" '{print $2}' > $TMP
489
490	set +e
491	check_for_driver vmd
492	driver_loaded=$?
493	set -e
494	while IFS= read -r dev_id
495	do
496		for bdf in $(iter_pci_dev_id 8086 $dev_id); do
497			if ! pci_can_use $bdf; then
498				echo "Skipping un-whitelisted VMD device at $bdf"
499				continue
500			fi
501			if [ $driver_loaded -ne 0 ]; then
502				linux_bind_driver "$bdf" vmd
503			else
504				linux_unbind_driver "$bdf"
505			fi
506		done
507	done < $TMP
508	rm $TMP
509
510	echo "1" > "/sys/bus/pci/rescan"
511}
512
513function reset_linux {
514	reset_linux_pci
515	for mount in $(linux_hugetlbfs_mounts); do
516		rm -f "$mount"/spdk*map_*
517	done
518	rm -f /run/.spdk*
519}
520
521function status_linux {
522	echo "Hugepages"
523	printf "%-6s %10s %8s / %6s\n" "node" "hugesize"  "free" "total"
524
525	numa_nodes=0
526	shopt -s nullglob
527	for path in /sys/devices/system/node/node?/hugepages/hugepages-*/; do
528		numa_nodes=$((numa_nodes + 1))
529		free_pages=$(cat $path/free_hugepages)
530		all_pages=$(cat $path/nr_hugepages)
531
532		[[ $path =~ (node[0-9]+)/hugepages/hugepages-([0-9]+kB) ]]
533
534		node=${BASH_REMATCH[1]}
535		huge_size=${BASH_REMATCH[2]}
536
537		printf "%-6s %10s %8s / %6s\n" $node $huge_size $free_pages $all_pages
538	done
539	shopt -u nullglob
540
541	# fall back to system-wide hugepages
542	if [ "$numa_nodes" = "0" ]; then
543		free_pages=$(grep HugePages_Free /proc/meminfo | awk '{ print $2 }')
544		all_pages=$(grep HugePages_Total /proc/meminfo | awk '{ print $2 }')
545		node="-"
546		huge_size="$HUGEPGSZ"
547
548		printf "%-6s %10s %8s / %6s\n" $node $huge_size $free_pages $all_pages
549	fi
550
551	echo ""
552	echo "NVMe devices"
553
554	echo -e "BDF\t\tVendor\tDevice\tNUMA\tDriver\t\tDevice name"
555	for bdf in $(iter_all_pci_class_code 01 08 02); do
556		driver=$(grep DRIVER /sys/bus/pci/devices/$bdf/uevent |awk -F"=" '{print $2}')
557		if [ "$numa_nodes" = "0" ]; then
558			node="-"
559		else
560			node=$(cat /sys/bus/pci/devices/$bdf/numa_node)
561		fi
562		device=$(cat /sys/bus/pci/devices/$bdf/device)
563		vendor=$(cat /sys/bus/pci/devices/$bdf/vendor)
564		if [ "$driver" = "nvme" ] && [ -d /sys/bus/pci/devices/$bdf/nvme ]; then
565			name="\t"$(ls /sys/bus/pci/devices/$bdf/nvme);
566		else
567			name="-";
568		fi
569		echo -e "$bdf\t${vendor#0x}\t${device#0x}\t$node\t${driver:--}\t\t$name";
570	done
571
572	echo ""
573	echo "I/OAT DMA"
574
575	#collect all the device_id info of ioat devices.
576	TMP=$(grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/include/spdk/pci_ids.h \
577	| awk -F"x" '{print $2}')
578	echo -e "BDF\t\tVendor\tDevice\tNUMA\tDriver"
579	for dev_id in $TMP; do
580		for bdf in $(iter_all_pci_dev_id 8086 $dev_id); do
581			driver=$(grep DRIVER /sys/bus/pci/devices/$bdf/uevent |awk -F"=" '{print $2}')
582			if [ "$numa_nodes" = "0" ]; then
583				node="-"
584			else
585				node=$(cat /sys/bus/pci/devices/$bdf/numa_node)
586			fi
587			device=$(cat /sys/bus/pci/devices/$bdf/device)
588			vendor=$(cat /sys/bus/pci/devices/$bdf/vendor)
589			echo -e "$bdf\t${vendor#0x}\t${device#0x}\t$node\t${driver:--}"
590		done
591	done
592
593	echo ""
594	echo "virtio"
595
596	#collect all the device_id info of virtio devices.
597	TMP=$(grep "PCI_DEVICE_ID_VIRTIO" $rootdir/include/spdk/pci_ids.h \
598	| awk -F"x" '{print $2}')
599	echo -e "BDF\t\tVendor\tDevice\tNUMA\tDriver\t\tDevice name"
600	for dev_id in $TMP; do
601		for bdf in $(iter_all_pci_dev_id 1af4 $dev_id); do
602			driver=$(grep DRIVER /sys/bus/pci/devices/$bdf/uevent |awk -F"=" '{print $2}')
603			if [ "$numa_nodes" = "0" ]; then
604				node="-"
605			else
606				node=$(cat /sys/bus/pci/devices/$bdf/numa_node)
607			fi
608			device=$(cat /sys/bus/pci/devices/$bdf/device)
609			vendor=$(cat /sys/bus/pci/devices/$bdf/vendor)
610			blknames=()
611			get_virtio_names_from_bdf "$bdf" blknames
612			echo -e "$bdf\t${vendor#0x}\t${device#0x}\t$node\t\t${driver:--}\t\t$blknames"
613		done
614	done
615
616	echo "VMD"
617
618	#collect all the device_id info of vmd devices.
619	TMP=$(grep "PCI_DEVICE_ID_INTEL_VMD" $rootdir/include/spdk/pci_ids.h \
620	| awk -F"x" '{print $2}')
621	echo -e "BDF\t\tNuma Node\tDriver Name"
622	for dev_id in $TMP; do
623		for bdf in $(iter_pci_dev_id 8086 $dev_id); do
624			driver=$(grep DRIVER /sys/bus/pci/devices/$bdf/uevent |awk -F"=" '{print $2}')
625			node=$(cat /sys/bus/pci/devices/$bdf/numa_node);
626			echo -e "$bdf\t$node\t\t$driver"
627		done
628	done
629}
630
631function configure_freebsd_pci {
632	TMP=$(mktemp)
633
634	# NVMe
635	GREP_STR="class=0x010802"
636
637	# IOAT
638	grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/include/spdk/pci_ids.h \
639	| awk -F"x" '{print $2}' > $TMP
640	while IFS= read -r dev_id
641	do
642		GREP_STR="${GREP_STR}\|chip=0x${dev_id}8086"
643	done < $TMP
644
645	# VMD
646	grep "PCI_DEVICE_ID_INTEL_VMD" $rootdir/include/spdk/pci_ids.h \
647	| awk -F"x" '{print $2}' > $TMP
648	while IFS= read -r dev_id
649	do
650		GREP_STR="${GREP_STR}\|chip=0x${dev_id}8086"
651	done < $TMP
652
653	AWK_PROG="{if (count > 0) printf \",\"; printf \"%s:%s:%s\",\$2,\$3,\$4; count++}"
654	echo $AWK_PROG > $TMP
655
656	BDFS=$(pciconf -l | grep "${GREP_STR}" | awk -F: -f $TMP)
657
658	kldunload nic_uio.ko || true
659	kenv hw.nic_uio.bdfs=$BDFS
660	kldload nic_uio.ko
661	rm $TMP
662}
663
664function configure_freebsd {
665	configure_freebsd_pci
666	# If contigmem is already loaded but the HUGEMEM specified doesn't match the
667	#  previous value, unload contigmem so that we can reload with the new value.
668	if kldstat -q -m contigmem; then
669		if [ $(kenv hw.contigmem.num_buffers) -ne "$((HUGEMEM / 256))" ]; then
670			kldunload contigmem.ko
671		fi
672	fi
673	if ! kldstat -q -m contigmem; then
674		kenv hw.contigmem.num_buffers=$((HUGEMEM / 256))
675		kenv hw.contigmem.buffer_size=$((256 * 1024 * 1024))
676		kldload contigmem.ko
677	fi
678}
679
680function reset_freebsd {
681	kldunload contigmem.ko || true
682	kldunload nic_uio.ko || true
683}
684
685mode=$1
686
687if [ -z "$mode" ]; then
688	mode="config"
689fi
690
691: ${HUGEMEM:=2048}
692: ${PCI_WHITELIST:=""}
693: ${PCI_BLACKLIST:=""}
694
695if [ -n "$NVME_WHITELIST" ]; then
696	PCI_WHITELIST="$PCI_WHITELIST $NVME_WHITELIST"
697fi
698
699if [ -n "$SKIP_PCI" ]; then
700	PCI_WHITELIST="none"
701fi
702
703if [ -z "$TARGET_USER" ]; then
704	TARGET_USER="$SUDO_USER"
705	if [ -z "$TARGET_USER" ]; then
706		TARGET_USER=$(logname 2>/dev/null) || true
707	fi
708fi
709
710if [ $(uname) = Linux ]; then
711	HUGEPGSZ=$(( $(grep Hugepagesize /proc/meminfo | cut -d : -f 2 | tr -dc '0-9') ))
712	HUGEPGSZ_MB=$(( $HUGEPGSZ / 1024 ))
713	: ${NRHUGE=$(( (HUGEMEM + HUGEPGSZ_MB - 1) / HUGEPGSZ_MB ))}
714
715	if [ "$mode" == "config" ]; then
716		configure_linux
717	elif [ "$mode" == "cleanup" ]; then
718		cleanup_linux
719	elif [ "$mode" == "reset" ]; then
720		reset_linux
721	elif [ "$mode" == "status" ]; then
722		status_linux
723	elif [ "$mode" == "help" ]; then
724		usage $0
725	else
726		usage $0 "Invalid argument '$mode'"
727	fi
728else
729	if [ "$mode" == "config" ]; then
730		configure_freebsd
731	elif [ "$mode" == "reset" ]; then
732		reset_freebsd
733	elif [ "$mode" == "cleanup" ]; then
734		echo "setup.sh cleanup function not yet supported on $(uname)"
735	elif [ "$mode" == "status" ]; then
736		echo "setup.sh status function not yet supported on $(uname)"
737	elif [ "$mode" == "help" ]; then
738		usage $0
739	else
740		usage $0 "Invalid argument '$mode'"
741	fi
742fi
743