diff options
Diffstat (limited to 'src/spdk/scripts/common.sh')
-rw-r--r-- | src/spdk/scripts/common.sh | 239 |
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[@]}" +} |