xref: /spdk/scripts/setup.sh (revision 9889ab2dc80e40dae92dcef361d53dcba722043d)
1 #!/usr/bin/env bash
2 
3 set -e
4 
5 rootdir=$(readlink -f $(dirname $0))/..
6 source "$rootdir/scripts/common.sh"
7 
8 function 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).
66 function 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 
80 function 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 
88 function 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 
119 function 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 
133 function linux_hugetlbfs_mounts() {
134 	mount | grep ' type hugetlbfs ' | awk '{ print $3 }'
135 }
136 
137 function 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 
157 function 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 
170 function 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 
299 function 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 
355 function 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 
417 function 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 
513 function 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 
521 function 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 
631 function 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 
664 function 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 
680 function reset_freebsd {
681 	kldunload contigmem.ko || true
682 	kldunload nic_uio.ko || true
683 }
684 
685 mode=$1
686 
687 if [ -z "$mode" ]; then
688 	mode="config"
689 fi
690 
691 : ${HUGEMEM:=2048}
692 : ${PCI_WHITELIST:=""}
693 : ${PCI_BLACKLIST:=""}
694 
695 if [ -n "$NVME_WHITELIST" ]; then
696 	PCI_WHITELIST="$PCI_WHITELIST $NVME_WHITELIST"
697 fi
698 
699 if [ -n "$SKIP_PCI" ]; then
700 	PCI_WHITELIST="none"
701 fi
702 
703 if [ -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
708 fi
709 
710 if [ $(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
728 else
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
742 fi
743