github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/hack/vf-netns-switcher.sh (about) 1 #!/bin/bash 2 3 conf_file="" 4 5 declare -a netnses 6 7 declare -A pfs 8 declare -A pcis 9 declare -A pf_port_names 10 declare -A pf_switch_ids 11 12 TIMEOUT="${TIMEOUT:-2}" 13 POLL_INTERVAL="${POLL_INTERVAL:-1}" 14 15 while test $# -gt 0; do 16 case "$1" in 17 18 --netns | -n) 19 input=$2 20 local_netns=$(echo $input | cut -s -d ':' -f 1) 21 local_pfs=$(echo $input | cut -s -d ':' -f 2) 22 input="" 23 24 if [[ -z "$local_netns" ]];then 25 echo "Error: flag --netns specified but netns is empty, please \ 26 provide it in the form --netns <netns>:<pf1>,<pf2> !" 27 echo "Exiting!" 28 exit 1 29 fi 30 31 if [[ -z "$local_pfs" ]];then 32 echo "Error: flag --netns specified but pfs is empty, please \ 33 provide it in the form --netns <netns>:<pf1>,<pf2> !" 34 echo "Exiting!" 35 exit 1 36 fi 37 38 netnses+=("$local_netns") 39 40 pfs["$local_netns"]="$(echo $local_pfs | tr , " ")" 41 42 local_netns="" 43 local_pfs="" 44 45 shift 46 shift 47 ;; 48 49 --conf-file | -c) 50 conf_file=$2 51 if [[ ! -f "$conf_file" ]];then 52 echo "Error: flag --conf-file specified but file $conf_file \ 53 not found!" 54 exit 1 55 fi 56 57 shift 58 shift 59 ;; 60 61 --help | -h) 62 echo " 63 vf-netns-switcher.sh --netns <netns>:<pf1>,<pf2> [--conf-file <>]: 64 65 --netns | -n The netns and its interfaces to switch the interfaces PFs and VFs to. \ 66 It must be of the form <netns>:<pf1>,<pf2>. This flag can be repeated to specify more netnses. 67 68 --conf-file | -c A file to read confs from, this will override cli flags. Conf file should be of the form: 69 [ 70 { 71 "netns": <netns1>, 72 "pfs": [ 73 "pf1", 74 "pf2" 75 ] 76 }, 77 { 78 "netns": <netns2>, 79 "pfs": [ 80 "pf3", 81 "pf4" 82 ] 83 } 84 ] 85 86 " 87 exit 0 88 ;; 89 90 *) 91 echo "Error: invalid option: $1!" 92 echo "Exiting..." 93 exit 1 94 esac 95 done 96 97 return_interfaces_to_default_namespace(){ 98 for netns in ${netnses[@]};do 99 for pf in ${pfs[$netns]};do 100 return_interface_to_default_namespace "${netns}" "${pf}" 101 done 102 done 103 104 exit 105 } 106 107 return_interface_to_default_namespace(){ 108 local netns="$1" 109 local interface="$2" 110 111 local reset_interface_netns="${interface}-reset" 112 113 local status=0 114 115 ip netns add "${reset_interface_netns}" 116 sleep 1 117 118 if ! ip netns exec "${netns}" ip link set "${interface}" netns "${reset_interface_netns}";then 119 echo "ERROR: unable to switch interface ${interface} from netns ${netns} to netns ${reset_interface_netns}!" 120 status=1 121 fi 122 123 ip netns del "${reset_interface_netns}" 124 125 ip link set "${interface}" up 126 127 return ${status} 128 } 129 130 get_pcis_from_pfs(){ 131 local worker_netns="$1" 132 shift 133 local interfaces="$@" 134 for interface in $interfaces; do 135 pcis["$interface"]="$(get_pci_from_net_name "$interface" "$worker_netns")" 136 done 137 } 138 139 get_pci_from_net_name(){ 140 local interface_name=$1 141 local worker_netns="$2" 142 143 if [[ -z "$(ip l show $interface_name)" ]];then 144 if [[ -n "$(docker exec -t ${worker_netns} ip l show $interface_name)" ]];then 145 ip netns exec ${worker_netns} bash -c "basename \$(readlink /sys/class/net/${interface_name}/device)" 146 return 0 147 fi 148 echo "" 149 return 1 150 fi 151 152 basename $(readlink /sys/class/net/${interface_name}/device) 153 return 0 154 } 155 156 netns_create(){ 157 local worker_netns="$1" 158 159 if [[ ! -e /var/run/netns/$worker_netns ]];then 160 local pid="$(docker inspect -f '{{.State.Pid}}' $worker_netns)" 161 162 if [[ -z "$pid" ]];then 163 return 1 164 fi 165 166 mkdir -p /var/run/netns/ 167 rm -rf /var/run/netns/$worker_netns 168 ln -sf /proc/$pid/ns/net "/var/run/netns/$worker_netns" 169 170 if [[ -z "$(ip netns | grep $worker_netns)" ]];then 171 return 1 172 fi 173 fi 174 return 0 175 } 176 177 switch_pfs(){ 178 local worker_netns="$1" 179 shift 180 local interfaces="$@" 181 182 echo "Switching \"$interfaces\" into $worker_netns ..." 183 184 for pf in $interfaces;do 185 switch_pf "$pf" "$worker_netns" 186 done 187 } 188 189 switch_pf(){ 190 local pf_name="$1" 191 local worker_netns="$2" 192 193 if [[ -z "$(ip netns | grep ${worker_netns})" ]];then 194 echo "Error: Namespace $worker_netns not found!" 195 return 1 196 fi 197 198 if [[ -z "$(ip l show ${pf_name})" ]];then 199 if [[ -z "$(docker exec -t ${worker_netns} ip l show ${pf_name})" ]];then 200 echo "Error: Interface $pf_name not found..." 201 return 1 202 fi 203 204 echo "PF ${pf_name} already in namespace $worker_netns!" 205 else 206 if ! ip l set dev $pf_name netns $worker_netns;then 207 echo "Error: unable to set $pf_name namespace to $worker_netns!" 208 return 1 209 fi 210 fi 211 212 if ! docker exec -t ${worker_netns} ip l set $pf_name up;then 213 echo "Error: unable to set $pf_name to up!" 214 return 1 215 fi 216 217 return 0 218 219 } 220 221 switch_vf(){ 222 local vf_name="$1" 223 local worker_netns="$2" 224 225 if [[ -z "$(ip l show $vf_name)" ]];then 226 return 1 227 fi 228 229 if ip link set "$vf_name" netns "$worker_netns"; then 230 if timeout "$TIMEOUT"s bash -c "until ip netns exec $worker_netns ip link show $vf_name > /dev/null; do sleep $POLL_INTERVAL; done"; then 231 return 0 232 else 233 return 1 234 fi 235 else 236 return 1 237 fi 238 } 239 240 switch_netns_vfs(){ 241 local worker_netns="$1" 242 243 for pf in ${pfs["$worker_netns"]};do 244 echo "Switching interface $pf vfs into $worker_netns...." 245 switch_interface_vfs "$pf" "$worker_netns" "${pcis[$pf]}" 246 done 247 } 248 249 get_pf_switch_dev_info(){ 250 local worker_netns="$1" 251 shift 252 local interfaces="$@" 253 for interface in $interfaces; do 254 interface_pci_address="${pcis[$interface]}" 255 if grep -q 'switchdev' <(devlink dev eswitch show pci/$interface_pci_address ); then 256 continue 257 fi 258 pf_port_names["$interface"]="$(cat /sys/class/net/${interface}/phys_port_name)" 259 pf_switch_ids["$interface"]="$(cat /sys/class/net/${interface}/phys_switch_id)" 260 done 261 } 262 263 switch_netns_vf_representors(){ 264 local worker_netns="$1" 265 for pf in ${pfs["$worker_netns"]};do 266 echo "Switching pf $pf vf representors into $worker_netns ..." 267 switch_interface_vf_representors "$pf" "$worker_netns" 268 done 269 } 270 271 switch_interface_vf_representors(){ 272 local pf_name="$1" 273 local worker_netns=$2 274 275 if [[ -z "${pf_switch_ids[$pf_name]}" ]] || [[ -z ${pf_port_names[$pf_name]:1} ]];then 276 echo "$pf_name does not have pf_switch_id or pf_port_name, assuming not switchdev..." 277 return 0 278 fi 279 280 for interface in $(ls /sys/class/net);do 281 phys_switch_id=$(cat /sys/class/net/$interface/phys_switch_id) 282 if [[ "$phys_switch_id" != "${pf_switch_ids[$pf_name]}" ]]; then 283 continue 284 fi 285 phys_port_name=$(cat /sys/class/net/$interface/phys_port_name) 286 phys_port_name_pf_index=${phys_port_name%vf*} 287 phys_port_name_pf_index=${phys_port_name_pf_index#pf} 288 if [[ "$phys_port_name_pf_index" != "${pf_port_names[$pf_name]:1}" ]]; then 289 continue 290 fi 291 echo "Switching VF representor $interface of PF $pf_name to netns $worker_netns" 292 switch_vf $interface $worker_netns 293 done 294 } 295 296 switch_interface_vfs(){ 297 local pf_name="$1" 298 local worker_netns="$2" 299 local pci="$3" 300 301 vfs_list=$(ls /sys/bus/pci/devices/$pci | grep virtfn) 302 303 if [[ -z "${vfs_list}" ]];then 304 echo "Warning: No VFs found for interface $pf_name!!" 305 return 0 306 fi 307 308 for vf in $vfs_list;do 309 local vf_interface="$(ls /sys/bus/pci/devices/$pci/$vf/net)" 310 311 if [[ -n "$vf_interface" ]];then 312 echo "Switching $vf_interface to namespace $worker_netns..." 313 sleep 2 314 if ! switch_vf "$vf_interface" "$worker_netns";then 315 echo "Error: could not switch $vf_interface to namespace $worker_netns!" 316 else 317 echo "Successfully switched $vf_interface to namespace $worker_netns" 318 fi 319 fi 320 done 321 } 322 323 read_confs(){ 324 local conf_file="$1" 325 326 let number_of_netns=$(jq length "${conf_file}")-1 327 328 for index in $(seq 0 $number_of_netns);do 329 netnses+=("$(jq -r .[${index}].netns $conf_file)") 330 let number_of_pfs=$(jq .[$index].pfs $conf_file | jq length)-1 331 for pf_index in $(seq 0 $number_of_pfs);do 332 pfs[${netnses[-1]}]+="$(jq -r .[$index].pfs[$pf_index] $conf_file) " 333 done 334 done 335 } 336 337 variables_check(){ 338 local status=0 339 340 check_empty_var "netnses" 341 let status=$status+$? 342 check_empty_var "pfs" 343 let status=$status+$? 344 345 return $status 346 } 347 348 check_empty_var(){ 349 local var_name="$1" 350 351 if [[ -z "${!var_name[@]}" ]];then 352 echo "Error: $var_name is empty..." 353 return 1 354 fi 355 356 return 0 357 } 358 359 main(){ 360 trap return_interfaces_to_default_namespace INT EXIT TERM 361 362 while true;do 363 for netns in ${netnses[@]};do 364 switch_pfs "$netns" "${pfs[$netns]}" 365 sleep 2 366 switch_netns_vfs "$netns" 367 sleep 2 368 switch_netns_vf_representors "$netns" 369 done 370 sleep $TIMEOUT 371 done 372 } 373 374 if [[ -n "$conf_file" ]];then 375 unset netnses 376 unset pfs 377 378 declare -a netnses 379 declare -A pfs 380 381 read_confs "$conf_file" 382 fi 383 384 variables_check 385 let status=$? 386 if [[ "$status" != "0" ]];then 387 echo "Error: empty var..." 388 exit $status 389 fi 390 391 for netns in ${netnses[@]};do 392 netns_create "$netns" 393 let status=$status+$? 394 if [[ "$status" != "0" ]];then 395 echo "Error: failed to create netns..." 396 exit $status 397 fi 398 done 399 400 for netns in ${netnses[@]};do 401 get_pcis_from_pfs "$netns" "${pfs[$netns]}" 402 get_pf_switch_dev_info "$netns" "${pfs[$netns]}" 403 done 404 405 if [[ "${#pcis[@]}" == "0" ]];then 406 echo "Error: could not get pci addresses of interfaces ${pfs[@]}!!" 407 exit 1 408 fi 409 410 main