1# Common shell utility functions 2 3# Check if PCI device is in PCI_ALLOWED and not in PCI_BLOCKED 4# Env: 5# if PCI_ALLOWED is empty assume device is allowed 6# if PCI_BLOCKED is empty assume device is NOT blocked 7# Params: 8# $1 - PCI BDF 9function pci_can_use() { 10 local i 11 12 # The '\ ' part is important 13 if [[ " $PCI_BLOCKED " =~ \ $1\ ]]; then 14 return 1 15 fi 16 17 if [[ -z "$PCI_ALLOWED" ]]; then 18 #no allow list specified, bind all devices 19 return 0 20 fi 21 22 for i in $PCI_ALLOWED; do 23 if [ "$i" == "$1" ]; then 24 return 0 25 fi 26 done 27 28 return 1 29} 30 31resolve_mod() { 32 local mod=$1 aliases=() 33 34 if aliases=($(modprobe -R "$mod")); then 35 echo "${aliases[0]}" 36 else 37 echo "unknown" 38 fi 2> /dev/null 39} 40 41cache_pci_init() { 42 local -gA pci_bus_cache 43 local -gA pci_ids_vendor 44 local -gA pci_ids_device 45 local -gA pci_bus_driver 46 local -gA pci_mod_driver 47 local -gA pci_mod_resolved 48 49 [[ -z ${pci_bus_cache[*]} || $CMD == reset ]] || return 1 50 51 pci_bus_cache=() 52 pci_bus_ids_vendor=() 53 pci_bus_ids_device=() 54 pci_bus_driver=() 55 pci_mod_driver=() 56 pci_mod_resolved=() 57} 58 59cache_pci() { 60 local pci=$1 class=$2 vendor=$3 device=$4 driver=$5 mod=$6 61 62 if [[ -n $class ]]; then 63 class=0x${class/0x/} 64 pci_bus_cache["$class"]="${pci_bus_cache["$class"]:+${pci_bus_cache["$class"]} }$pci" 65 fi 66 if [[ -n $vendor && -n $device ]]; then 67 vendor=0x${vendor/0x/} device=0x${device/0x/} 68 pci_bus_cache["$vendor:$device"]="${pci_bus_cache["$vendor:$device"]:+${pci_bus_cache["$vendor:$device"]} }$pci" 69 70 pci_ids_vendor["$pci"]=$vendor 71 pci_ids_device["$pci"]=$device 72 fi 73 if [[ -n $driver ]]; then 74 pci_bus_driver["$pci"]=$driver 75 fi 76 if [[ -n $mod ]]; then 77 pci_mod_driver["$pci"]=$mod 78 pci_mod_resolved["$pci"]=$(resolve_mod "$mod") 79 fi 80} 81 82cache_pci_bus_sysfs() { 83 [[ -e /sys/bus/pci/devices ]] || return 1 84 85 cache_pci_init || return 0 86 87 local pci 88 local class vendor device driver mod 89 90 for pci in /sys/bus/pci/devices/*; do 91 class=$(< "$pci/class") vendor=$(< "$pci/vendor") device=$(< "$pci/device") driver="" mod="" 92 if [[ -e $pci/driver ]]; then 93 driver=$(readlink -f "$pci/driver") 94 driver=${driver##*/} 95 else 96 driver=unbound 97 fi 98 if [[ -e $pci/modalias ]]; then 99 mod=$(< "$pci/modalias") 100 fi 101 cache_pci "${pci##*/}" "$class" "$vendor" "$device" "$driver" "$mod" 102 done 103} 104 105cache_pci_bus_lspci() { 106 hash lspci 2> /dev/null || return 1 107 108 cache_pci_init || return 0 109 110 local dev 111 while read -ra dev; do 112 dev=("${dev[@]//\"/}") 113 # lspci splits ls byte of the class (prog. interface) into a separate 114 # field if it's != 0. Look for it and normalize the value to fit with 115 # what kernel exposes under sysfs. 116 if [[ ${dev[*]} =~ -p([0-9]+) ]]; then 117 dev[1]+=${BASH_REMATCH[1]} 118 else 119 dev[1]+=00 120 fi 121 # pci class vendor device 122 cache_pci "${dev[@]::4}" 123 done < <(lspci -Dnmm) 124} 125 126cache_pci_bus_pciconf() { 127 hash pciconf 2> /dev/null || return 1 128 129 cache_pci_init || return 0 130 131 local class vd vendor device 132 local pci domain bus device function 133 134 while read -r pci class _ vd _; do 135 IFS=":" read -r domain bus device function _ <<< "${pci##*pci}" 136 pci=$(printf '%04x:%02x:%02x:%x' \ 137 "$domain" "$bus" "$device" "$function") 138 class=$(printf '0x%06x' $((class))) 139 vendor=$(printf '0x%04x' $((vd & 0xffff))) 140 device=$(printf '0x%04x' $(((vd >> 16) & 0xffff))) 141 142 cache_pci "$pci" "$class" "$vendor" "$device" 143 done < <(pciconf -l) 144} 145 146cache_pci_bus() { 147 case "$(uname -s)" in 148 Linux) cache_pci_bus_lspci || cache_pci_bus_sysfs ;; 149 FreeBSD) cache_pci_bus_pciconf ;; 150 esac 151} 152 153iter_all_pci_sysfs() { 154 cache_pci_bus_sysfs || return 1 155 156 # default to class of the nvme devices 157 local find=${1:-0x010802} findx=$2 158 local pci pcis 159 160 [[ -n ${pci_bus_cache["$find"]} ]] || return 0 161 read -ra pcis <<< "${pci_bus_cache["$find"]}" 162 163 if ((findx)); then 164 printf '%s\n' "${pcis[@]::findx}" 165 else 166 printf '%s\n' "${pcis[@]}" 167 fi 168} 169 170# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 171function iter_all_pci_class_code() { 172 local class 173 local subclass 174 local progif 175 class="$(printf %02x $((0x$1)))" 176 subclass="$(printf %02x $((0x$2)))" 177 progif="$(printf %02x $((0x$3)))" 178 179 if hash lspci &> /dev/null; then 180 if [ "$progif" != "00" ]; then 181 lspci -mm -n -D \ 182 | grep -i -- "-p${progif}" \ 183 | awk -v cc="\"${class}${subclass}\"" -F " " \ 184 '{if (cc ~ $2) print $1}' | tr -d '"' 185 else 186 lspci -mm -n -D \ 187 | awk -v cc="\"${class}${subclass}\"" -F " " \ 188 '{if (cc ~ $2) print $1}' | tr -d '"' 189 fi 190 elif hash pciconf &> /dev/null; then 191 local addr=($(pciconf -l | grep -i "class=0x${class}${subclass}${progif}" \ 192 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 193 printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]} 194 elif iter_all_pci_sysfs "$(printf '0x%06x' $((0x$progif | 0x$subclass << 8 | 0x$class << 16)))"; then 195 : 196 else 197 echo "Missing PCI enumeration utility" >&2 198 exit 1 199 fi 200} 201 202# This function will ignore PCI PCI_ALLOWED and PCI_BLOCKED 203function iter_all_pci_dev_id() { 204 local ven_id 205 local dev_id 206 ven_id="$(printf %04x $((0x$1)))" 207 dev_id="$(printf %04x $((0x$2)))" 208 209 if hash lspci &> /dev/null; then 210 lspci -mm -n -D | awk -v ven="\"$ven_id\"" -v dev="\"${dev_id}\"" -F " " \ 211 '{if (ven ~ $3 && dev ~ $4) print $1}' | tr -d '"' 212 elif hash pciconf &> /dev/null; then 213 local addr=($(pciconf -l | grep -i "chip=0x${dev_id}${ven_id}" \ 214 | cut -d$'\t' -f1 | sed -e 's/^[a-zA-Z0-9_]*@pci//g' | tr ':' ' ')) 215 printf "%04x:%02x:%02x:%x\n" ${addr[0]} ${addr[1]} ${addr[2]} ${addr[3]} 216 elif iter_all_pci_sysfs "0x$ven_id:0x$dev_id"; then 217 : 218 else 219 echo "Missing PCI enumeration utility" >&2 220 exit 1 221 fi 222} 223 224function iter_pci_dev_id() { 225 local bdf="" 226 227 for bdf in $(iter_all_pci_dev_id "$@"); do 228 if pci_can_use "$bdf"; then 229 echo "$bdf" 230 fi 231 done 232} 233 234# This function will filter out PCI devices using PCI_ALLOWED and PCI_BLOCKED 235# See function pci_can_use() 236function iter_pci_class_code() { 237 local bdf="" 238 239 for bdf in $(iter_all_pci_class_code "$@"); do 240 if pci_can_use "$bdf"; then 241 echo "$bdf" 242 fi 243 done 244} 245 246function nvme_in_userspace() { 247 # Check used drivers. If it's not vfio-pci or uio-pci-generic 248 # then most likely PCI_ALLOWED option was used for setup.sh 249 # and we do not want to use that disk. 250 251 local bdf bdfs 252 local nvmes 253 254 if [[ -n ${pci_bus_cache["0x010802"]} ]]; then 255 nvmes=(${pci_bus_cache["0x010802"]}) 256 else 257 nvmes=($(iter_pci_class_code 01 08 02)) 258 fi 259 260 for bdf in "${nvmes[@]}"; do 261 if [[ -e /sys/bus/pci/drivers/nvme/$bdf ]] \ 262 || [[ $(uname -s) == FreeBSD && $(pciconf -l "pci${bdf/./:}") == nvme* ]]; then 263 continue 264 fi 265 bdfs+=("$bdf") 266 done 267 ((${#bdfs[@]})) || return 1 268 printf '%s\n' "${bdfs[@]}" 269} 270 271cmp_versions() { 272 local ver1 ver1_l 273 local ver2 ver2_l 274 275 IFS=".-:" read -ra ver1 <<< "$1" 276 IFS=".-:" read -ra ver2 <<< "$3" 277 local op=$2 278 279 ver1_l=${#ver1[@]} 280 ver2_l=${#ver2[@]} 281 282 local lt=0 gt=0 eq=0 v 283 case "$op" in 284 "<") : $((eq = gt = 1)) ;; 285 ">") : $((eq = lt = 1)) ;; 286 "<=") : $((gt = 1)) ;; 287 ">=") : $((lt = 1)) ;; 288 "==") : $((lt = gt = 1)) ;; 289 esac 290 291 decimal() ( 292 local d=${1,,} 293 if [[ $d =~ ^[0-9]+$ ]]; then 294 echo $((10#$d)) 295 elif [[ $d =~ ^0x || $d =~ ^[a-f0-9]+$ ]]; then 296 d=${d/0x/} 297 echo $((0x$d)) 298 else 299 echo 0 300 fi 301 ) 302 303 for ((v = 0; v < (ver1_l > ver2_l ? ver1_l : ver2_l); v++)); do 304 ver1[v]=$(decimal "${ver1[v]}") 305 ver2[v]=$(decimal "${ver2[v]}") 306 ((ver1[v] > ver2[v])) && return "$gt" 307 ((ver1[v] < ver2[v])) && return "$lt" 308 done 309 [[ ${ver1[*]} == "${ver2[*]}" ]] && return "$eq" 310} 311 312lt() { cmp_versions "$1" "<" "$2"; } 313gt() { cmp_versions "$1" ">" "$2"; } 314le() { cmp_versions "$1" "<=" "$2"; } 315ge() { cmp_versions "$1" ">=" "$2"; } 316eq() { cmp_versions "$1" "==" "$2"; } 317neq() { ! eq "$1" "$2"; } 318 319block_in_use() { 320 local block=$1 data pt 321 # Skip devices that are in use - simple blkid it to see if 322 # there's any metadata (pt, fs, etc.) present on the drive. 323 # FIXME: Special case to ignore atari as a potential false 324 # positive: 325 # https://github.com/spdk/spdk/issues/2079 326 # Devices with SPDK's GPT part type are not considered to 327 # be in use. 328 329 if "$rootdir/scripts/spdk-gpt.py" "$block"; then 330 return 1 331 fi 332 333 data=$(blkid "/dev/${block##*/}") || data=none 334 335 if [[ $data == none ]]; then 336 return 1 337 fi 338 339 pt=$(blkid -s PTTYPE -o value "/dev/${block##*/}") || pt=none 340 341 if [[ $pt == none || $pt == atari ]]; then 342 return 1 343 fi 344 345 # Devices used in SPDK tests always create GPT partitions 346 # with label containing SPDK_TEST string. Such devices were 347 # part of the tests before, so are not considered in use. 348 if [[ $pt == gpt ]] && parted "/dev/${block##*/}" -ms print | grep -q "SPDK_TEST"; then 349 return 1 350 fi 351 352 return 0 353} 354 355get_spdk_gpt() { 356 local spdk_guid 357 358 [[ -e $rootdir/module/bdev/gpt/gpt.h ]] || return 1 359 360 IFS="()" read -r _ spdk_guid _ < <(grep SPDK_GPT_PART_TYPE_GUID "$rootdir/module/bdev/gpt/gpt.h") 361 spdk_guid=${spdk_guid//, /-} spdk_guid=${spdk_guid//0x/} 362 363 echo "$spdk_guid" 364} 365 366if [[ -e "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" ]]; then 367 # Adjust uname to report the operating system as WSL, Msys or Cygwin 368 # and the kernel name as Windows. Define kill() to invoke the SIGTERM 369 # handler before causing a hard stop with TerminateProcess. 370 source "$CONFIG_WPDK_DIR/bin/wpdk_common.sh" 371fi 372