Add vfio support to scripts.

cleanup.sh and unbind.sh have been combined into a single
setup.sh that takes one optional parameter (reset). If no
parameter is given, the script will automatically bind
all NVMe and IOAT devices to either uio_pci_generic
or vfio-pci, as appropriate based on IOMMU settings. If
the reset parameter is given, the devices will be bound back
to the appropriate kernel drivers.

Change-Id: I25db3234f1ecfb352a281e5093f4c1aa455152ae
Signed-off-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Ben Walker 2016-02-19 14:11:08 -07:00
parent c0a1cd3827
commit 55dc5f2131
6 changed files with 144 additions and 96 deletions

View File

@ -88,4 +88,4 @@ any NVMe and I/OAT devices must be unbound from the native kernel drivers.
SPDK includes scripts to automate this process on both Linux and FreeBSD. SPDK includes scripts to automate this process on both Linux and FreeBSD.
1) scripts/configure_hugepages.sh 1) scripts/configure_hugepages.sh
2) scripts/unbind.sh 2) scripts/setup.sh

View File

@ -10,7 +10,7 @@ if [ $EUID -ne 0 ]; then
exit 1 exit 1
fi fi
trap "process_core; $rootdir/scripts/cleanup.sh; exit 1" SIGINT SIGTERM EXIT trap "process_core; $rootdir/scripts/setup.sh reset; exit 1" SIGINT SIGTERM EXIT
timing_enter autotest timing_enter autotest
@ -38,7 +38,7 @@ timing_enter afterboot
./scripts/configure_hugepages.sh 1024 ./scripts/configure_hugepages.sh 1024
timing_exit afterboot timing_exit afterboot
./scripts/unbind.sh ./scripts/setup.sh
##################### #####################
# Unit Tests # Unit Tests
@ -52,7 +52,7 @@ time test/lib/ioat/ioat.sh
timing_exit lib timing_exit lib
./scripts/cleanup.sh ./scripts/setup.sh reset
./scripts/build_kmod.sh clean ./scripts/build_kmod.sh clean
timing_exit autotest timing_exit autotest

View File

@ -448,7 +448,8 @@ spdk_pci_device_has_non_uio_driver(struct spdk_pci_device *dev)
driver_begin = driver; driver_begin = driver;
} }
return strcmp(driver_begin, "uio_pci_generic") != 0; return (strcmp(driver_begin, "uio_pci_generic") != 0 &&
strcmp(driver_begin, "vfio-pci") != 0);
} }
#endif #endif

View File

@ -1,28 +0,0 @@
#!/usr/bin/env bash
set -e
function cleanup_linux() {
# detach pci devices from uio driver
grep -q "^uio_pci_generic" /proc/modules && rmmod uio_pci_generic
# bind NVMe devices to NVMe driver if no kernel device
if [ -d "/sys/bus/pci/drivers/nvme" ]; then
device=`find /sys/bus/pci/drivers/nvme -name "0000*" -print`
if [ -z "$device" ]; then
rmmod nvme
modprobe nvme
fi
fi
}
function cleanup_freebsd {
kldunload contigmem.ko || true
kldunload nic_uio.ko || true
}
if [ `uname` = Linux ]; then
cleanup_linux
else
cleanup_freebsd
fi

138
scripts/setup.sh Executable file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env bash
set -e
rootdir=$(readlink -f $(dirname $0))/..
function linux_iter_pci {
# Argument is the class code
# TODO: More specifically match against only class codes in the grep
# step.
lspci -mm -n | grep $1 | tr -d '"' | awk -F " " '{print "0000:"$1}'
}
function configure_linux {
driver_name=vfio-pci
if [ -z "$(ls /sys/kernel/iommu_groups)" ]; then
# No IOMMU. Use uio.
driver_name=uio_pci_generic
fi
# NVMe
modprobe $driver_name || true
for bdf in $(linux_iter_pci 0108); do
if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
# Unbind the device from whatever driver it is currently bound to
echo $bdf > "/sys/bus/pci/devices/$bdf/driver/unbind"
fi
# Bind this device to the new driver
ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
echo "Binding $bdf ($ven_dev_id) to $driver_name"
echo $ven_dev_id > "/sys/bus/pci/drivers/$driver_name/new_id"
done
# IOAT
TMP=`mktemp`
#collect all the device_id info of ioat devices.
grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/lib/ioat/ioat_pci.h \
| awk -F"x" '{print $2}' > $TMP
for dev_id in `cat $TMP`; do
# Abuse linux_iter_pci by giving it a device ID instead of a class code
for bdf in $(linux_iter_pci $dev_id); do
if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
# Unbind the device from whatever driver it is currently bound to
echo $bdf > "/sys/bus/pci/devices/$bdf/driver/unbind"
fi
# Bind this device to the new driver
ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
echo "Binding $bdf ($ven_dev_id) to $driver_name"
echo $ven_dev_id > "/sys/bus/pci/drivers/$driver_name/new_id"
done
done
rm $TMP
echo "1" > "/sys/bus/pci/rescan"
}
function reset_linux {
# NVMe
modprobe nvme || true
for bdf in $(linux_iter_pci 0108); do
ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
# Unregister this device from the driver it was bound to.
echo $ven_dev_id > "/sys/bus/pci/devices/$bdf/driver/remove_id"
# Unbind the device from whatever driver it is currently bound to
echo $bdf > "/sys/bus/pci/devices/$bdf/driver/unbind"
fi
# Bind this device back to the nvme driver
echo "Binding $bdf ($ven_dev_id) to nvme"
echo $bdf > "/sys/bus/pci/drivers/nvme/bind"
done
# IOAT
TMP=`mktemp`
#collect all the device_id info of ioat devices.
grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/lib/ioat/ioat_pci.h \
| awk -F"x" '{print $2}' > $TMP
modprobe ioatdma || true
for dev_id in `cat $TMP`; do
# Abuse linux_iter_pci by giving it a device ID instead of a class code
for bdf in $(linux_iter_pci $dev_id); do
ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
# Unregister this device from the driver it was bound to.
echo $ven_dev_id > "/sys/bus/pci/devices/$bdf/driver/remove_id"
# Unbind the device from whatever driver it is currently bound to
echo $bdf > "/sys/bus/pci/devices/$bdf/driver/unbind"
fi
# Bind this device back to the nvme driver
echo "Binding $bdf ($ven_dev_id) to ioatdma"
echo $bdf > "/sys/bus/pci/drivers/ioatdma/bind"
done
done
rm $TMP
echo "1" > "/sys/bus/pci/rescan"
}
function configure_freebsd {
TMP=`mktemp`
AWK_PROG="{if (count > 0) printf \",\"; printf \"%s:%s:%s\",\$2,\$3,\$4; count++}"
echo $AWK_PROG > $TMP
PCICONF=`pciconf -l | grep 'class=0x010802\|^ioat'`
BDFS=`echo $PCICONF | awk -F: -f $TMP`
kldunload nic_uio.ko || true
kenv hw.nic_uio.bdfs=$BDFS
kldload nic_uio.ko
rm $TMP
}
function reset_freebsd {
kldunload contigmem.ko || true
kldunload nic_uio.ko || true
}
mode=$1
if [ "$mode" == "" ]; then
mode="config"
fi
if [ `uname` = Linux ]; then
if [ "$mode" == "config" ]; then
configure_linux
elif [ "$mode" == "reset" ]; then
reset_linux
fi
else
if [ "$mode" == "config" ]; then
configure_freebsd
elif [ "$mode" == "reset" ]; then
reset_freebsd
fi
fi

View File

@ -1,63 +0,0 @@
#!/usr/bin/env bash
set -e
rootdir=$(readlink -f $(dirname $0))/..
function prep_nvme {
TMP=`mktemp`
# Get vendor_id:device_id by nvme's class_id and subcalss_id
lspci -n | awk -F " " '{if ($2 == "0108:") {print $3}}' \
| awk -F ":" '{print $1" "$2}' > $TMP
cat $TMP | while read device
do
echo $device > /sys/bus/pci/drivers/uio_pci_generic/new_id
done
rm $TMP
}
function prep_ioat {
TMP=`mktemp`
#collect all the device_id info of ioat devices.
grep "PCI_DEVICE_ID_INTEL_IOAT" $rootdir/lib/ioat/ioat_pci.h \
| awk -F"x" '{print $2}' > $TMP
vendor=8086
for device in `cat $TMP`
do
if lspci -n | grep "$vendor:$device"
then
echo $vendor $device > /sys/bus/pci/drivers/uio_pci_generic/new_id
fi
done
rm $TMP
}
function configure_linux {
rmmod nvme || true
rmmod uio_pci_generic || true
rmmod ioatdma || true
modprobe uio_pci_generic || true
prep_nvme
prep_ioat
}
function configure_freebsd {
TMP=`mktemp`
AWK_PROG="{if (count > 0) printf \",\"; printf \"%s:%s:%s\",\$2,\$3,\$4; count++}"
echo $AWK_PROG > $TMP
PCICONF=`pciconf -l | grep 'class=0x010802\|^ioat'`
BDFS=`echo $PCICONF | awk -F: -f $TMP`
kldunload nic_uio.ko || true
kenv hw.nic_uio.bdfs=$BDFS
kldload nic_uio.ko
rm $TMP
}
if [ `uname` = Linux ]; then
configure_linux
else
configure_freebsd
fi