numam-spdk/scripts/common.sh
Michal Berger 5a436f0c61 scripts/common: Cache vendor|device ids for reverse lookup
Change-Id: I69959547d1c71cf3b65f8eb0b786197650a6ece0
Signed-off-by: Michal Berger <michalx.berger@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/2225
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2020-05-12 08:12:09 +00:00

215 lines
5.4 KiB
Bash

# 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
}