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