summaryrefslogtreecommitdiffstats
path: root/src/spdk/scripts/common.sh
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/scripts/common.sh')
-rw-r--r--src/spdk/scripts/common.sh239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/spdk/scripts/common.sh b/src/spdk/scripts/common.sh
new file mode 100644
index 000000000..a70cfba0d
--- /dev/null
+++ b/src/spdk/scripts/common.sh
@@ -0,0 +1,239 @@
+# Common shell utility functions
+
+# Check if PCI device is on PCI_WHITELIST and not on PCI_BLACKLIST
+# Env:
+# if PCI_WHITELIST is empty assume device is whitelistened
+# if PCI_BLACKLIST is empty assume device is NOT blacklistened
+# Params:
+# $1 - PCI BDF
+function pci_can_use() {
+ local i
+
+ # The '\ ' part is important
+ if [[ " $PCI_BLACKLIST " =~ \ $1\ ]]; then
+ return 1
+ fi
+
+ if [[ -z "$PCI_WHITELIST" ]]; then
+ #no whitelist specified, bind all devices
+ return 0
+ fi
+
+ for i in $PCI_WHITELIST; do
+ if [ "$i" == "$1" ]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+cache_pci_init() {
+ local -gA pci_bus_cache
+ local -gA pci_ids_vendor
+ local -gA pci_ids_device
+
+ [[ -z ${pci_bus_cache[*]} || $CMD == reset ]] || return 1
+
+ pci_bus_cache=()
+ pci_bus_ids_vendor=()
+ pci_bus_ids_device=()
+}
+
+cache_pci() {
+ local pci=$1 class=$2 vendor=$3 device=$4
+
+ if [[ -n $class ]]; then
+ class=0x${class/0x/}
+ pci_bus_cache["$class"]="${pci_bus_cache["$class"]:+${pci_bus_cache["$class"]} }$pci"
+ fi
+ if [[ -n $vendor && -n $device ]]; then
+ vendor=0x${vendor/0x/} device=0x${device/0x/}
+ pci_bus_cache["$vendor"]="${pci_bus_cache["$vendor"]:+${pci_bus_cache["$vendor"]} }$pci"
+ pci_bus_cache["$device"]="${pci_bus_cache["$device"]:+${pci_bus_cache["$device"]} }$pci"
+ pci_bus_cache["$vendor:$device"]="${pci_bus_cache["$vendor:$device"]:+${pci_bus_cache["$vendor:$device"]} }$pci"
+
+ pci_ids_vendor["$pci"]=$vendor
+ pci_ids_device["$pci"]=$device
+ fi
+}
+
+cache_pci_bus_sysfs() {
+ [[ -e /sys/bus/pci/devices ]] || return 1
+
+ cache_pci_init || return 0
+
+ local pci
+ local class vendor device
+
+ for pci in /sys/bus/pci/devices/*; do
+ class=$(< "$pci/class") vendor=$(< "$pci/vendor") device=$(< "$pci/device")
+ cache_pci "${pci##*/}" "$class" "$vendor" "$device"
+ done
+}
+
+cache_pci_bus_lspci() {
+ hash lspci 2> /dev/null || return 1
+
+ cache_pci_init || return 0
+
+ local dev
+ while read -ra dev; do
+ dev=("${dev[@]//\"/}")
+ # lspci splits ls byte of the class (prog. interface) into a separate
+ # field if it's != 0. Look for it and normalize the value to fit with
+ # what kernel exposes under sysfs.
+ if [[ ${dev[*]} =~ -p([0-9]+) ]]; then
+ dev[1]+=${BASH_REMATCH[1]}
+ else
+ dev[1]+=00
+ fi
+ # pci class vendor device
+ cache_pci "${dev[@]::4}"
+ done < <(lspci -Dnmm)
+}
+
+cache_pci_bus_pciconf() {
+ hash pciconf 2> /dev/null || return 1
+
+ cache_pci_init || return 0
+
+ local class vd vendor device
+ local pci domain bus device function
+
+ while read -r pci class _ vd _; do
+ IFS=":" read -r domain bus device function _ <<< "${pci##*pci}"
+ pci=$(printf '%04x:%02x:%02x:%x' \
+ "$domain" "$bus" "$device" "$function")
+ class=$(printf '0x%06x' $((class)))
+ vendor=$(printf '0x%04x' $((vd & 0xffff)))
+ device=$(printf '0x%04x' $(((vd >> 16) & 0xffff)))
+
+ cache_pci "$pci" "$class" "$vendor" "$device"
+ done < <(pciconf -l)
+}
+
+cache_pci_bus() {
+ case "$(uname -s)" in
+ Linux) cache_pci_bus_lspci || cache_pci_bus_sysfs ;;
+ FreeBSD) cache_pci_bus_pciconf ;;
+ esac
+}
+
+iter_all_pci_sysfs() {
+ cache_pci_bus_sysfs || return 1
+
+ # default to class of the nvme devices
+ local find=${1:-0x010802} findx=$2
+ local pci pcis
+
+ [[ -n ${pci_bus_cache["$find"]} ]] || return 0
+ read -ra pcis <<< "${pci_bus_cache["$find"]}"
+
+ if ((findx)); then
+ printf '%s\n' "${pcis[@]::findx}"
+ else
+ printf '%s\n' "${pcis[@]}"
+ fi
+}
+
+# This function will ignore PCI PCI_WHITELIST and PCI_BLACKLIST
+function iter_all_pci_class_code() {
+ local class
+ local subclass
+ local progif
+ class="$(printf %02x $((0x$1)))"
+ subclass="$(printf %02x $((0x$2)))"
+ progif="$(printf %02x $((0x$3)))"
+
+ if hash lspci &> /dev/null; then
+ if [ "$progif" != "00" ]; then
+ lspci -mm -n -D \
+ | grep -i -- "-p${progif}" \
+ | awk -v cc="\"${class}${subclass}\"" -F " " \
+ '{if (cc ~ $2) print $1}' | tr -d '"'
+ else
+ lspci -mm -n -D \
+ | awk -v cc="\"${class}${subclass}\"" -F " " \
+ '{if (cc ~ $2) print $1}' | tr -d '"'
+ fi
+ elif hash pciconf &> /dev/null; then
+ local addr=($(pciconf -l | grep -i "class=0x${class}${subclass}${progif}" \
+ | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' '))
+ printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]}
+ elif iter_all_pci_sysfs "$(printf '0x%06x' $((0x$progif | 0x$subclass << 8 | 0x$class << 16)))"; then
+ :
+ else
+ echo "Missing PCI enumeration utility" >&2
+ exit 1
+ fi
+}
+
+# This function will ignore PCI PCI_WHITELIST and PCI_BLACKLIST
+function iter_all_pci_dev_id() {
+ local ven_id
+ local dev_id
+ ven_id="$(printf %04x $((0x$1)))"
+ dev_id="$(printf %04x $((0x$2)))"
+
+ if hash lspci &> /dev/null; then
+ lspci -mm -n -D | awk -v ven="\"$ven_id\"" -v dev="\"${dev_id}\"" -F " " \
+ '{if (ven ~ $3 && dev ~ $4) print $1}' | tr -d '"'
+ elif hash pciconf &> /dev/null; then
+ local addr=($(pciconf -l | grep -i "chip=0x${dev_id}${ven_id}" \
+ | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' '))
+ printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]}
+ elif iter_all_pci_sysfs "0x$ven_id:0x$dev_id"; then
+ :
+ else
+ echo "Missing PCI enumeration utility" >&2
+ exit 1
+ fi
+}
+
+function iter_pci_dev_id() {
+ local bdf=""
+
+ for bdf in $(iter_all_pci_dev_id "$@"); do
+ if pci_can_use "$bdf"; then
+ echo "$bdf"
+ fi
+ done
+}
+
+# This function will filter out PCI devices using PCI_WHITELIST and PCI_BLACKLIST
+# See function pci_can_use()
+function iter_pci_class_code() {
+ local bdf=""
+
+ for bdf in $(iter_all_pci_class_code "$@"); do
+ if pci_can_use "$bdf"; then
+ echo "$bdf"
+ fi
+ done
+}
+
+function nvme_in_userspace() {
+ # Check used drivers. If it's not vfio-pci or uio-pci-generic
+ # then most likely PCI_WHITELIST option was used for setup.sh
+ # and we do not want to use that disk.
+
+ local bdf bdfs
+ local nvmes
+
+ if [[ -n ${pci_bus_cache["0x010802"]} ]]; then
+ nvmes=(${pci_bus_cache["0x010802"]})
+ else
+ nvmes=($(iter_pci_class_code 01 08 02))
+ fi
+
+ for bdf in "${nvmes[@]}"; do
+ if [[ -e /sys/bus/pci/drivers/nvme/$bdf ]] \
+ || [[ $(uname -s) == FreeBSD && $(pciconf -l "pci$bdf") == nvme* ]]; then
+ continue
+ fi
+ bdfs+=("$bdf")
+ done
+ ((${#bdfs[@]})) || return 1
+ printf '%s\n' "${bdfs[@]}"
+}