Created
June 25, 2020 15:33
-
-
Save mzpqnxow/762eee200a70a1356a511813986e239c to your computer and use it in GitHub Desktop.
Shell script for setting up DPDK fail-safe PMD on Azure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Copyright (c) 2017 6WIND S.A. | |
version=20171003 | |
[ "$0" != "bash" ] && self=$(readlink -f "$0") | |
[ "$self" ] && _self=${self##*/} | |
netvsc_id="{f8615163-df3e-46c5-913f-f2d2f965ed0e}" | |
# Setup display variables | |
TPUT=$(tput longname 2>/dev/null || true) # returns '' if $TERM is not known | |
if [ "$TPUT" ]; then | |
BLACK=$(tput op || true) | |
RED=$(tput setaf 1 || true) | |
# GREEN=$(tput setaf 2 || true) | |
YELLOW=$(tput setaf 3 || true) | |
BLUE=$(tput setaf 6 || true) | |
BOLD=$(tput bold || true) | |
NORMAL=$(tput sgr0 || true) | |
fi | |
if [ "$1" = "-v" -o "$1" = "--version" ]; then | |
printf 'version: %s\n' $version | |
exit 0 | |
fi | |
[ "$1" = "-h" ] || \ | |
[ "$1" = "help" ] || \ | |
[ "$1" = "--help" ] && printf '%s\n' "Usage: | |
$_self [vdev|with <net1[ net2 ...]>|preferred|fallback|help] [MAC] | |
${BOLD}${YELLOW}$_self [vdev] <[MAC1[ MAC2 ...]]>${NORMAL} | |
Generates a vdev parameter for each VF-NetVSC pair found on the system. | |
If MAC is specified, restrict results to those matching such MAC address. | |
If the VF is not currently plugged-in and no bonding has been configured, | |
then MAC must be provided or using a specific NetVSC device using the | |
${BOLD}with${NORMAL} command instead. | |
You can use it as follows: | |
${BOLD}path/to/app \$(${BLUE}$self${BLACK})${NORMAL} | |
${BOLD}${YELLOW}$_self with <net1[ net2 ...]>${NORMAL} | |
Use the specified netdevices as fallback for fail-safe pairs. | |
One fail-safe instance will be created per netdevice. | |
Each such netdevice will seek a VF matching its MAC address, be it currently | |
plugged-in or not. | |
You can use it as follows: | |
${BOLD}path/to/app \$(${BLUE}$self ${RED}with eth1 eth2${BLACK})${NORMAL} | |
${BOLD}${YELLOW}$_self preferred [MAC]${NORMAL} | |
Generates a device definition for the preferred device. | |
The preferred device is detected following those rules: | |
* Detect any PCI device currently plugged in that could be part of a VF-NetVSC pair. | |
This PCI device has to share a MAC address with another non-PCI device having | |
the class_id \"${RED}$netvsc_id${NORMAL}\". | |
If a MAC address is specified, restrict results to those using such MAC address. | |
* If no such device is detected and if a MAC address is specified, any | |
other PCI device using this MAC address is then used instead. This allows | |
forcing the use of arbitrary PCI device. | |
This option is usually used within the ${BOLD}exec${NORMAL} device type of the fail-safe. | |
${BOLD}${YELLOW}$_self fallback [MAC]${NORMAL} | |
Generates a device definition for the fallback device. | |
The fallback device is a TAP device capturing the traffic from a NetVSC device. | |
It is detected using the following rules: | |
* Detect any VF-NetVSC pairs by listing all PCI device sharing a MAC address with | |
another device having the class_id \"${RED}$netvsc_id${NORMAL}\". | |
* If the VF is not currently plugged-in, detect any NetVSC device being | |
part of a bonding configuration. | |
* If a MAC address is specified, restrict results to those using such MAC address. | |
* If no such NetVSC device could be detected and a MAC address is specified, generate | |
a dummy TAP PMD command, then detect any possible non-PCI device on the system using | |
this MAC address, and use it as a remote. | |
This option is usually used within the ${BOLD}exec${NORMAL} device type of the fail-safe. | |
" | |
# printf, but to stderr | |
err_printf () { | |
(>&2 printf %s "${BOLD}${RED}" && printf "$@" && printf %s "${NORMAL}") | |
} | |
# Print the MAC of a netdev | |
# $1: netdev name | |
get_mac () { | |
local dev="$1" | |
local file="/sys/class/net/${dev}/address" | |
[ "$dev" ] && [ -f "$file" ] && cat "$file" | |
} | |
# Returns true if the device has the specified MAC | |
# $1: netdev name | |
# $2: MAC address | |
is_mac () { | |
local net="$1" | |
local mac="$2" | |
[ "$net" ] || return 1 | |
[ -f "/sys/class/net/${net}/address" ] && \ | |
[ "$(get_mac "$net")" = "$mac" ] && \ | |
return 0 | |
return 1 | |
} | |
# Returns true if the device is a NetVSC device. | |
# $1: netdev name | |
is_netvsc () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
[ -f "/sys/class/net/${net}/device/class_id" ] && \ | |
[ "$(cat "/sys/class/net/${net}/device/class_id")" = "$netvsc_id" ] && \ | |
return 0 | |
return 1 | |
} | |
# Returns true if the device is a bonding device. | |
# $1: netdev name | |
is_bond () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
[ -d "/sys/class/net/${net}/bonding" ] || return 1 | |
return 0 | |
} | |
get_parent_bond () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
for dev in /sys/class/net/*; do | |
dev="${dev##*/}" | |
[ -f "/sys/class/net/${dev}/bonding/slaves" ] || continue | |
for slave in $(cat "/sys/class/net/${dev}/bonding/slaves"); do | |
[ "$slave" = "$net" ] && echo "$dev" | |
done | |
done | |
return 0 | |
} | |
# Returns true if the device is a PCI device. | |
# $1: netdev name | |
is_pci () { | |
local net="$1" | |
local pci_addr | |
[ "$net" ] || return 1 | |
[ -L "/sys/class/net/${net}/device" ] || return 1 | |
pci_addr="$(readlink -f "/sys/class/net/${net}/device")" | |
pci_addr="${pci_addr##*/}" | |
[ -L "/sys/bus/pci/devices/${pci_addr}" ] && \ | |
return 0 | |
return 1 | |
} | |
# Returns true if the device is a Mellanox device | |
# $1: netdev name | |
is_mlx () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
[ -f "/sys/class/net/${net}/device/vendor" ] && \ | |
[ "$(cat "/sys/class/net/${net}/device/vendor")" = "0x15b3" ] && \ | |
return 0 | |
return 1 | |
} | |
# Returns true if the device is a ConnectX-3 device. | |
# $1: netdev name | |
is_cx3 () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
[ -f "/sys/class/net/${net}/device/vendor" ] || return 1 | |
[ "$(cat "/sys/class/net/${net}/device/device")" = "0x1003" ] || \ | |
[ "$(cat "/sys/class/net/${net}/device/device")" = "0x1004" ] || \ | |
[ "$(cat "/sys/class/net/${net}/device/device")" = "0x1007" ] && \ | |
return 0 | |
return 1 | |
} | |
# Returns true if the device is a VF in a valid VF-NetVSC pair | |
# $1: netdev name | |
is_paired_vf () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
is_pci "$net" || return 1 | |
for other in /sys/class/net/*; do | |
other="${other##*/}" | |
[ "$other" = "$net" ] && continue | |
is_pci "$other" && continue | |
[ "$(get_mac "$net")" = "$(get_mac "$other")" ] && \ | |
return 0 | |
done | |
return 1 | |
} | |
# Returns true if the device is a NetVSC in a valid VF-NetVSC pair | |
# $1: netdev name | |
is_paired_netvsc () { | |
local net="$1" | |
[ "$net" ] || return 1 | |
is_netvsc "$net" || return 1 | |
for other in /sys/class/net/*; do | |
other="${other##*/}" | |
[ "$other" = "$net" ] && continue | |
is_netvsc "$other" && continue | |
[ "$(get_mac "$net")" = "$(get_mac "$other")" ] && \ | |
return 0 | |
done | |
return 1 | |
} | |
# Yield functions template | |
yield_='yield_SUBST () { | |
local mac="$1" | |
for net in /sys/class/net/*; do | |
net="${net##*/}" | |
if is_SUBST "$net" "$mac"; then | |
if [ "$mac" ]; then | |
[ "$mac" = "$(get_mac "$net")" ] && \ | |
printf '\''%s\n'\'' "$net" | |
else | |
printf '\''%s\n'\'' "$net" | |
fi | |
fi | |
done | |
}' | |
# Declare a new yield_* function | |
# $1: type of device to detect (netvsc, pci, mlx, cx3) | |
declare_yield () { | |
local pattern="$1" | |
[ "$pattern" ] || return 1 | |
printf '%s' "$yield_" |sed "s:SUBST:${pattern}:g" | |
} | |
eval "$(declare_yield mac)" | |
eval "$(declare_yield netvsc)" | |
eval "$(declare_yield pci)" | |
eval "$(declare_yield mlx)" | |
eval "$(declare_yield cx3)" | |
eval "$(declare_yield paired_vf)" | |
eval "$(declare_yield paired_netvsc)" | |
# Prints a primary device description. | |
#[$1]: MAC address to restrict results to. | |
preferred () { | |
local mac="$1" | |
local devargs | |
local count=0 | |
for dev in $(yield_paired_vf "$mac"); do | |
devargs="$(readlink -f "/sys/class/net/${dev}/device")" | |
devargs="${devargs##*/}" | |
is_cx3 "$dev" && devargs="$devargs,port=$(cat "/sys/class/net/${dev}/dev_port")" | |
[ "$devargs" ] && printf '%s\n' "$devargs" | |
count=$((count+1)) | |
done | |
if [ "$mac" ] && [ "$count" = 0 ]; then | |
for dev in $(yield_mac "$mac"); do | |
is_pci "$dev" || continue | |
devargs="$(readlink -f "/sys/class/net/${dev}/device")" | |
devargs="${devargs##*/}" | |
is_cx3 "$dev" && devargs="$devargs,port=$(cat "/sys/class/net/${dev}/dev_port")" | |
[ "$devargs" ] && printf '%s\n' "$devargs" | |
done | |
fi | |
} | |
# Prints a secondary device description. | |
#[$1]: MAC address to match | |
#[$2]: starting tap index | |
fallback () { | |
local mac="$1" | |
local count="$2" | |
local devargs | |
local remote | |
local name | |
[ "$count" ] || count=0 | |
local pairs=$(yield_paired_netvsc "$mac") | |
[ -z $(yield_mlx "$mac") ] && pairs=$(yield_netvsc "$mac") | |
for dev in "$pairs"; do | |
mac="$(get_mac "$dev")" | |
name="$count" | |
is_netvsc "$dev" && remote="$dev" | |
bond="$(get_parent_bond "$dev")" | |
[ "$bond" = "" ] || remote="$bond" | |
[ "$remote" ] && name="$remote" | |
printf "net_tap_%s,iface=tap_%s" "$name" "$name" | |
[ "$remote" ] && printf ",remote=%s" "$remote" | |
printf "\n" | |
count=$((count+1)) | |
done | |
if [ "$count" = 0 ]; then | |
name="$count" | |
if [ "$mac" ]; then | |
for dev in $(yield_mac "$mac"); do | |
is_pci "$dev" && continue | |
remote="$dev" | |
break | |
done | |
fi | |
[ "$remote" ] && name="$remote" | |
printf "net_tap_%s,iface=tap_%s" "$name" "$name" | |
[ "$remote" ] && printf ",remote=%s" "$remote" | |
printf "\n" | |
fi | |
} | |
# Prints one DPDK fail-safe PMD description. | |
vdev_dev () { | |
local count="$1" | |
local mac="$2" | |
printf "%s=net_failsafe%d,mac=%s,exec(%s),exec(%s) " \ | |
"--vdev" "$count" "$mac" \ | |
"${self},preferred,${mac}" \ | |
"${self},fallback,${mac},${count}" | |
} | |
# Prints one DPDK fail-safe PMD command line for each | |
# netdevice name supplied as parameter. | |
# $@: netdevices names | |
with () { | |
local count=0 | |
local mac | |
for net in "$@"; do | |
#is_netvsc "$net" || err_printf "\n%s is not a NetVSC device\n" "$net" | |
mac="$(get_mac "$net")" | |
printf "%s " "$(vdev_dev "$count" "$mac")" | |
count=$((count+1)) | |
done | |
printf "\n" | |
} | |
# Prints one or more DPDK fail-safe PMD command lines. | |
#[$1]: target MAC address. If none, generates one fail-safe per existing | |
# NetVSC pairs | |
vdev () { | |
local it=0 | |
for mac in "$@"; do | |
local pairs=$(yield_paired_netvsc "$mac") | |
[ -z $(yield_mlx "$mac") ] && pairs=$(yield_netvsc "$mac") | |
[ -z "$pairs" ] && continue | |
for dev in "$pairs"; do | |
mac="$(get_mac "$dev")" | |
vdev_dev "$it" "$mac" | |
it=$((it+1)) | |
done | |
done | |
printf " -w 0000:00:00.0 " | |
printf "\n" | |
} | |
[ "$0" != "bash" ] || return | |
if [ "$1" = "preferred" ]; then | |
shift | |
preferred "$@" | |
elif [ "$1" = "fallback" ]; then | |
shift | |
fallback "$@" | |
elif [ "$1" = "with" ]; then | |
shift | |
with "$@" | |
elif [ "$1" = "vdev" ]; then | |
shift | |
vdev "$@" | |
elif [ "$1" != "help" ]; then | |
vdev "$@" | |
fi | |
#exit $? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment