LCOV - code coverage report
Current view: top level - app/shared/commands/configure - iavf.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 32 796 4.0 %
Date: 2026-11-08 04:25:05 Functions: 2 58 3.4 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : 
       3             : #include "configure.h"
       4             : #include "fd_ethtool_ioctl.h"
       5             : #include "../../../platform/fd_file_util.h"
       6             : #include "../../../platform/fd_sys_util.h"
       7             : #include "../../../../disco/net/iavf/fd_iavf.h"
       8             : #include "../../../../waltz/ip/fd_netlink1.h"
       9             : #include "../../../../util/net/fd_eth.h"
      10             : 
      11             : #include <dirent.h>
      12             : #include <errno.h>
      13             : #include <fcntl.h>
      14             : #include <linux/capability.h>
      15             : #include <linux/ethtool.h>
      16             : #include <linux/rtnetlink.h>
      17             : #include <linux/sockios.h>
      18             : #include <sys/ioctl.h>
      19             : #include <net/if.h>
      20             : #include <stdlib.h>
      21             : #include <sys/stat.h>
      22             : #include <sys/socket.h>
      23             : #include <sys/time.h>
      24             : #include <unistd.h>
      25             : 
      26           0 : #define NAME "iavf"
      27             : 
      28           0 : #define IAVF_SYSFS_ROOT "/sys"
      29             : 
      30             : /* iavf_vf_policy holds the PF's rtnetlink settings for one VF. Missing
      31             :    attributes leave the corresponding *_valid fields clear */
      32             : struct iavf_vf_policy {
      33             :   uchar mac[ 6 ];
      34             :   int   mac_valid;
      35             :   int   spoofchk;
      36             :   int   spoofchk_valid;
      37             :   int   trust;
      38             :   int   trust_valid;
      39             :   int   link_state_auto;
      40             :   int   link_state_valid;
      41             : };
      42             : typedef struct iavf_vf_policy iavf_vf_policy_t;
      43             : 
      44             : static int
      45           0 : enabled( fd_config_t const * config ) {
      46           0 :   return 0==strcmp( config->net.provider, "iavf" );
      47           0 : }
      48             : 
      49             : static void
      50             : init_perm( fd_cap_chk_t *      chk,
      51           0 :            fd_config_t const * config FD_PARAM_UNUSED ) {
      52           0 :   fd_cap_chk_root( chk, NAME, "configure Intel SR-IOV VFs and PF filters" );
      53           0 :   if( !fd_iavf_vfio_avail() ) {
      54           0 :     fd_cap_chk_cap( chk, NAME, CAP_SYS_MODULE, "run modprobe to load the vfio-pci kernel module" );
      55           0 :   }
      56           0 : }
      57             : 
      58             : static void
      59             : fini_perm( fd_cap_chk_t *      chk,
      60           0 :            fd_config_t const * config FD_PARAM_UNUSED ) {
      61           0 :   fd_cap_chk_root( chk, NAME, "configure Intel SR-IOV VFs and PF filters" );
      62           0 : }
      63             : 
      64             : static int
      65             : iavf_netdev_mac_get( char const * interface,
      66           0 :                      uchar        mac[ 6 ] ) {
      67           0 :   char path[ PATH_MAX ];
      68           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/class/net/%s/address", interface ) );
      69             : 
      70           0 :   char  text[ 32 ];
      71           0 :   ulong text_sz;
      72           0 :   if( FD_UNLIKELY( NULL==fd_file_util_read_cstr( path, text, sizeof(text), &text_sz ) ) ) return -1;
      73           0 :   if( 0!=text_sz && text[ text_sz-1UL ]=='\n' ) text[ --text_sz ] = '\0';
      74           0 :   if( FD_UNLIKELY( strlen( text )!=17UL || !fd_cstr_to_mac_addr( text, mac ) ) ) {
      75           0 :     errno = EBADMSG;
      76           0 :     return -1;
      77           0 :   }
      78           0 :   if( FD_UNLIKELY( !(mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]) || fd_eth_mac_is_mcast( mac ) ) ) {
      79           0 :     errno = EBADMSG;
      80           0 :     return -1;
      81           0 :   }
      82           0 :   return 0;
      83           0 : }
      84             : 
      85             : static int
      86             : iavf_pf_pci_addr_get( char const * pf_if,
      87           0 :                       char         pf_pci[ FD_IAVF_PCI_ADDR_SZ ] ) {
      88           0 :   char path[ PATH_MAX ];
      89           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/class/net/%s/device", pf_if ) );
      90           0 :   char resolved[ PATH_MAX ];
      91           0 :   if( FD_UNLIKELY( NULL==realpath( path, resolved ) ) ) return -1;
      92           0 :   char const * pci = strrchr( resolved, '/' )+1;
      93           0 :   if( FD_UNLIKELY( strlen( pci )!=FD_IAVF_PCI_ADDR_SZ-1UL ) ) {
      94           0 :     errno = ENODEV;
      95           0 :     return -1;
      96           0 :   }
      97           0 :   fd_cstr_ncpy( pf_pci, pci, FD_IAVF_PCI_ADDR_SZ );
      98           0 :   return 0;
      99           0 : }
     100             : 
     101             : static int
     102             : iavf_pci_driver_name_get( char const * pci,
     103           0 :                           char         driver[ FD_IAVF_DRIVER_NAME_MAX ] ) {
     104           0 :   char path[ PATH_MAX ];
     105           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/bus/pci/devices/%s/driver", pci ) );
     106           0 :   char resolved[ PATH_MAX ];
     107           0 :   if( FD_UNLIKELY( NULL==realpath( path, resolved ) ) ) return -1;
     108           0 :   char const * name = strrchr( resolved, '/' )+1;
     109           0 :   if( FD_UNLIKELY( strlen( name )>=FD_IAVF_DRIVER_NAME_MAX ) ) {
     110           0 :     errno = ENAMETOOLONG;
     111           0 :     return -1;
     112           0 :   }
     113           0 :   fd_cstr_ncpy( driver, name, FD_IAVF_DRIVER_NAME_MAX );
     114           0 :   return 0;
     115           0 : }
     116             : 
     117             : static int
     118             : iavf_pf_sriov_numvfs_get( char const * pf_if,
     119           0 :                           uint *       numvfs ) {
     120           0 :   if( FD_UNLIKELY( -1==fd_iavf_numvfs_get( pf_if, numvfs ) ) ) {
     121           0 :     int err = errno;
     122           0 :     char path[ PATH_MAX ];
     123           0 :     FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/class/net/%s/device/sriov_numvfs", pf_if ) );
     124           0 :     FD_LOG_WARNING(( "reading %s failed (%i-%s)", path, err, fd_io_strerror( err ) ));
     125           0 :     if( err==ENOENT ) {
     126           0 :       FD_LOG_WARNING(( "Cannot create VFs on PF %s because sriov_numvfs is missing. "
     127           0 :                        "The NIC's Virtualization Mode is likely not set to SR-IOV, so select SR-IOV in the NIC firmware settings "
     128           0 :                        "and reboot the machine", pf_if ));
     129           0 :       char const * guide = fd_iavf_sriov_bios_guide( 1 );
     130           0 :       if( NULL!=guide ) FD_LOG_WARNING(( "%s", guide ));
     131           0 :     }
     132           0 :     errno = err;
     133           0 :     return -1;
     134           0 :   }
     135           0 :   return 0;
     136           0 : }
     137             : 
     138             : static int
     139             : iavf_pf_sriov_numvfs_set( char const * pf_if,
     140           0 :                           uint         numvfs ) {
     141           0 :   char path[ PATH_MAX ];
     142           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/class/net/%s/device/sriov_numvfs", pf_if ) );
     143           0 :   if( FD_UNLIKELY( -1==fd_file_util_write_uint( path, numvfs ) ) ) {
     144           0 :     int err = errno;
     145           0 :     if( 0!=numvfs && err==ENOMEM ) {
     146           0 :       FD_LOG_WARNING(( "Enabling VFs on PF %s failed (%i-%s).\n"
     147           0 :                        "  Linux can return this error for insufficient PCIe resources or a kernel\n"
     148           0 :                        "  memory allocation failure",
     149           0 :                        pf_if, err, fd_io_strerror( err ) ));
     150           0 :     }
     151           0 :     errno = err;
     152           0 :     return -1;
     153           0 :   }
     154           0 :   return 0;
     155           0 : }
     156             : 
     157             : static int
     158             : iavf_vf_iommu_group_get( char const * vf_pci,
     159           0 :                          char         group[ 32 ] ) {
     160           0 :   char path[ PATH_MAX ];
     161           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/bus/pci/devices/%s/iommu_group", vf_pci ) );
     162           0 :   char resolved[ PATH_MAX ];
     163           0 :   if( FD_UNLIKELY( NULL==realpath( path, resolved ) ) ) return -1;
     164           0 :   char const * name    = strrchr( resolved, '/' )+1;
     165           0 :   ulong        name_sz = strlen( name );
     166           0 :   if( FD_UNLIKELY( 0==name_sz || name_sz>=32UL ) ) {
     167           0 :     errno = EBADMSG;
     168           0 :     return -1;
     169           0 :   }
     170           0 :   for( ulong i=0UL; i<name_sz; i++ ) {
     171           0 :     if( FD_UNLIKELY( name[i]<'0' || name[i]>'9' ) ) {
     172           0 :       errno = EBADMSG;
     173           0 :       return -1;
     174           0 :     }
     175           0 :   }
     176           0 :   fd_cstr_ncpy( group, name, 32UL );
     177           0 :   return 0;
     178           0 : }
     179             : 
     180             : /* iavf_vf_vfio_group_user_pid_find scans /proc/<pid>/fd for the VF's
     181             :    /dev/vfio/<group> descriptor. Returns 1 with *user_pid set when found,
     182             :    0 if no descriptor is found, or -1 with errno set on failure */
     183             : static int
     184             : iavf_vf_vfio_group_user_pid_find( char const * vf_pci,
     185           0 :                                   uint *       user_pid ) {
     186           0 :   char group[ 32 ];
     187           0 :   if( FD_UNLIKELY( -1==iavf_vf_iommu_group_get( vf_pci, group ) ) ) return -1;
     188           0 :   char vfio_path[ 64 ];
     189           0 :   FD_TEST( fd_cstr_printf_check( vfio_path, sizeof(vfio_path), NULL, "/dev/vfio/%s", group ) );
     190             : 
     191           0 :   DIR * proc = opendir( "/proc" );
     192           0 :   if( FD_UNLIKELY( NULL==proc ) ) return -1;
     193           0 :   int err = 0;
     194           0 :   for(;;) {
     195           0 :     errno                   = 0;
     196           0 :     struct dirent * process = readdir( proc );
     197           0 :     if( NULL==process ) {
     198           0 :       err = errno;
     199           0 :       break;
     200           0 :     }
     201           0 :     char const * p = process->d_name;
     202           0 :     if( !*p ) continue;
     203           0 :     for( ; *p>='0' && *p<='9'; p++ ) {}
     204           0 :     if( *p ) continue;
     205             : 
     206           0 :     char fd_dir_path[ PATH_MAX ];
     207           0 :     FD_TEST( fd_cstr_printf_check( fd_dir_path, sizeof(fd_dir_path), NULL, "/proc/%s/fd", process->d_name ) );
     208           0 :     DIR * fd_dir = opendir( fd_dir_path );
     209           0 :     if( NULL==fd_dir ) {
     210           0 :       if( errno==ENOENT ) continue;
     211           0 :       err = errno;
     212           0 :       break;
     213           0 :     }
     214           0 :     for(;;) {
     215           0 :       errno                    = 0;
     216           0 :       struct dirent * fd_entry = readdir( fd_dir );
     217           0 :       if( NULL==fd_entry ) {
     218           0 :         if( 0!=errno ) err = errno;
     219           0 :         break;
     220           0 :       }
     221           0 :       if( fd_entry->d_name[0]=='.' ) continue;
     222           0 :       char fd_path[ PATH_MAX ];
     223           0 :       FD_TEST( fd_cstr_printf_check( fd_path, sizeof(fd_path), NULL, "%s/%s", fd_dir_path, fd_entry->d_name ) );
     224           0 :       char target[ PATH_MAX ];
     225           0 :       long target_sz = readlink( fd_path, target, sizeof(target)-1UL );
     226           0 :       if( -1==target_sz ) {
     227           0 :         if( errno==ENOENT ) continue;
     228           0 :         err = errno;
     229           0 :         break;
     230           0 :       }
     231           0 :       target[ target_sz ] = '\0';
     232           0 :       if( 0!=strcmp( target, vfio_path ) ) continue;
     233           0 :       ulong pid = strtoul( process->d_name, NULL, 10 );
     234           0 :       if( FD_UNLIKELY( pid>UINT_MAX ) ) {
     235           0 :         err = ERANGE;
     236           0 :         break;
     237           0 :       }
     238           0 :       *user_pid = (uint)pid;
     239           0 :       if( FD_UNLIKELY( -1==closedir( fd_dir ) && 0==err ) ) err = errno;
     240           0 :       if( FD_UNLIKELY( -1==closedir( proc ) && 0==err ) ) err = errno;
     241           0 :       if( 0!=err ) {
     242           0 :         errno = err;
     243           0 :         return -1;
     244           0 :       }
     245           0 :       return 1;
     246           0 :     }
     247           0 :     if( FD_UNLIKELY( -1==closedir( fd_dir ) && 0==err ) ) err = errno;
     248           0 :     if( 0!=err ) break;
     249           0 :   }
     250           0 :   if( FD_UNLIKELY( -1==closedir( proc ) && 0==err ) ) err = errno;
     251           0 :   if( 0!=err ) {
     252           0 :     errno = err;
     253           0 :     return -1;
     254           0 :   }
     255           0 :   return 0;
     256           0 : }
     257             : 
     258             : /* iavf_rtnetlink_request returns 0 on success or a positive errno on failure */
     259             : static int
     260             : iavf_rtnetlink_request( struct nlmsghdr * request,
     261             :                         ushort            reply_type,
     262             :                         uchar *           buf,
     263           0 :                         ulong             buf_sz ) {
     264           0 :   fd_netlink_t nl __attribute__((cleanup(fd_netlink_fini)));
     265           0 :   if( FD_UNLIKELY( NULL==fd_netlink_init( &nl, 0U ) ) ) return errno;
     266             : 
     267           0 :   struct timeval timeout = { .tv_sec=5L };
     268           0 :   if( FD_UNLIKELY( -1==setsockopt( nl.fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout) ) ) ) return errno;
     269             : 
     270           0 :   request->nlmsg_seq = ++nl.seq;
     271           0 :   long sent = sendto( nl.fd, request, request->nlmsg_len, 0, NULL, 0 );
     272           0 :   if( FD_UNLIKELY( -1==sent ) ) return errno;
     273           0 :   if( FD_UNLIKELY( sent!=(long)request->nlmsg_len ) ) return EIO;
     274             : 
     275           0 :   fd_netlink_iter_t iter;
     276           0 :   fd_netlink_iter_init( &iter, &nl, buf, buf_sz );
     277             : 
     278             :   /* Distinguish a receive timeout from a driver returning EAGAIN */
     279           0 :   if( FD_UNLIKELY( iter.err==EAGAIN ) ) return ETIMEDOUT;
     280           0 :   if( FD_UNLIKELY( 0!=iter.err ) ) return iter.err;
     281           0 :   if( FD_UNLIKELY( fd_netlink_iter_done( &iter ) ) ) return EPROTO;
     282             : 
     283           0 :   struct nlmsghdr const * reply = fd_netlink_iter_msg( &iter );
     284           0 :   if( FD_UNLIKELY( reply->nlmsg_seq!=nl.seq ) ) return EPROTO;
     285             : 
     286           0 :   if( reply->nlmsg_type==NLMSG_ERROR ) {
     287           0 :     if( FD_UNLIKELY( reply->nlmsg_len<NLMSG_LENGTH( sizeof(struct nlmsgerr) ) ) ) return EPROTO;
     288             :     /* NLMSG_ERROR carries zero for an ACK or a negative Linux errno */
     289           0 :     struct nlmsgerr const * ack = NLMSG_DATA( reply );
     290           0 :     if( FD_UNLIKELY( ack->error>0 || ack->error<-4095 ) ) return EPROTO;
     291           0 :     if( 0!=ack->error ) return -ack->error;
     292           0 :   }
     293             : 
     294           0 :   if( FD_UNLIKELY( reply->nlmsg_type!=reply_type ) ) return EPROTO;
     295           0 :   return 0;
     296           0 : }
     297             : 
     298             : static int
     299             : iavf_vf_policy_attr_set( uint         if_idx,
     300             :                          ushort       attr,
     301             :                          void const * value,
     302           0 :                          uint         value_sz ) {
     303           0 :   struct {
     304           0 :     struct nlmsghdr  nlh;
     305           0 :     struct ifinfomsg ifi;
     306           0 :     struct rtattr    list;
     307           0 :     struct rtattr    vf;
     308           0 :     struct rtattr    setting;
     309           0 :     uchar            value[ sizeof(struct ifla_vf_mac) ];
     310           0 :   } request = {
     311           0 :     .nlh     = { .nlmsg_type=RTM_SETLINK, .nlmsg_flags=NLM_F_REQUEST | NLM_F_ACK },
     312           0 :     .ifi     = { .ifi_family=AF_UNSPEC, .ifi_index=(int)if_idx },
     313           0 :     .list    = { .rta_type=IFLA_VFINFO_LIST },
     314           0 :     .vf      = { .rta_type=IFLA_VF_INFO },
     315           0 :     .setting = { .rta_type=attr }
     316           0 :   };
     317           0 :   FD_TEST( value_sz<=sizeof(request.value) && 0==(value_sz & 3U) );
     318           0 :   request.setting.rta_len = (ushort)RTA_LENGTH( value_sz );
     319           0 :   request.vf.rta_len      = (ushort)RTA_LENGTH( request.setting.rta_len );
     320           0 :   request.list.rta_len    = (ushort)RTA_LENGTH( request.vf.rta_len );
     321           0 :   request.nlh.nlmsg_len   = NLMSG_LENGTH( sizeof(request.ifi)+request.list.rta_len );
     322           0 :   fd_memcpy( request.value, value, value_sz );
     323             : 
     324           0 :   uchar buf[ 4096 ] __attribute__((aligned(NLMSG_ALIGNTO)));
     325           0 :   int err = iavf_rtnetlink_request( &request.nlh, NLMSG_ERROR, buf, sizeof(buf) );
     326           0 :   if( 0!=err ) { errno = err; return -1; }
     327           0 :   return 0;
     328           0 : }
     329             : 
     330             : /* iavf_vf_policy_parse extracts the MAC address, spoof checking, trust and
     331             :    link state from one IFLA_VF_INFO entry matching vf_idx */
     332             : static int
     333             : iavf_vf_policy_parse( struct rtattr const * vf_info,
     334             :                       uint                  vf_idx,
     335           0 :                       iavf_vf_policy_t *    policy ) {
     336           0 :   iavf_vf_policy_t entry     = {0};
     337           0 :   uint             entry_idx = UINT_MAX;
     338             : 
     339           0 :   struct rtattr const * attr      = RTA_DATA( vf_info );
     340           0 :   long                  remaining = (long)RTA_PAYLOAD( vf_info );
     341           0 :   for( ; RTA_OK( attr, remaining ); attr=RTA_NEXT( attr, remaining ) ) {
     342           0 :     int type = attr->rta_type & NLA_TYPE_MASK;
     343             : 
     344           0 :     if( type==IFLA_VF_MAC ) {
     345           0 :       if( FD_UNLIKELY( RTA_PAYLOAD( attr )!=sizeof(struct ifla_vf_mac) ) ) return EPROTO;
     346           0 :       struct ifla_vf_mac const * vf_mac = RTA_DATA( attr );
     347           0 :       entry_idx = vf_mac->vf;
     348           0 :       fd_memcpy( entry.mac, vf_mac->mac, sizeof(entry.mac) );
     349           0 :       int mac_nonzero = entry.mac[0] | entry.mac[1] | entry.mac[2] | entry.mac[3] | entry.mac[4] | entry.mac[5];
     350           0 :       entry.mac_valid = !!mac_nonzero && !fd_eth_mac_is_mcast( entry.mac );
     351             : 
     352           0 :     } else if( type==IFLA_VF_SPOOFCHK ) {
     353           0 :       if( FD_UNLIKELY( RTA_PAYLOAD( attr )!=sizeof(struct ifla_vf_spoofchk) ) ) return EPROTO;
     354           0 :       struct ifla_vf_spoofchk const * vf_spoofchk = RTA_DATA( attr );
     355           0 :       if( vf_spoofchk->vf!=vf_idx ) continue;
     356             : 
     357           0 :       entry.spoofchk       = !!vf_spoofchk->setting;
     358             :       /* Linux uses UINT_MAX when a driver does not report this setting */
     359           0 :       entry.spoofchk_valid = vf_spoofchk->setting!=UINT_MAX;
     360             : 
     361           0 :     } else if( type==IFLA_VF_TRUST ) {
     362           0 :       if( FD_UNLIKELY( RTA_PAYLOAD( attr )!=sizeof(struct ifla_vf_trust) ) ) return EPROTO;
     363           0 :       struct ifla_vf_trust const * vf_trust = RTA_DATA( attr );
     364           0 :       if( vf_trust->vf!=vf_idx ) continue;
     365             : 
     366           0 :       entry.trust       = !!vf_trust->setting;
     367           0 :       entry.trust_valid = vf_trust->setting!=UINT_MAX;
     368             : 
     369           0 :     } else if( type==IFLA_VF_LINK_STATE ) {
     370           0 :       if( FD_UNLIKELY( RTA_PAYLOAD( attr )!=sizeof(struct ifla_vf_link_state) ) ) return EPROTO;
     371           0 :       struct ifla_vf_link_state const * vf_link_state = RTA_DATA( attr );
     372           0 :       if( vf_link_state->vf!=vf_idx ) continue;
     373             : 
     374           0 :       entry.link_state_auto  = vf_link_state->link_state==IFLA_VF_LINK_STATE_AUTO;
     375           0 :       entry.link_state_valid = 1;
     376           0 :     }
     377           0 :   }
     378             : 
     379           0 :   if( FD_UNLIKELY( 0!=remaining ) ) return EPROTO;
     380           0 :   if( entry_idx!=vf_idx ) return ENODATA;
     381           0 :   *policy = entry;
     382           0 :   return 0;
     383           0 : }
     384             : 
     385             : /* iavf_vf_policy_get reads vf_idx's settings through RTM_GETLINK on pf_if.
     386             :    The PF can report these settings when the VF is bound to vfio-pci and
     387             :    has no Linux network interface */
     388             : static int
     389             : iavf_vf_policy_get( char const *       pf_if,
     390             :                     uint               vf_idx,
     391           0 :                     iavf_vf_policy_t * policy ) {
     392           0 :   uint if_idx = if_nametoindex( pf_if );
     393           0 :   if( FD_UNLIKELY( 0==if_idx ) ) return -1;
     394             : 
     395           0 :   struct {
     396           0 :     struct nlmsghdr  nlh;
     397           0 :     struct ifinfomsg ifi;
     398           0 :     struct rtattr    ext_mask;
     399           0 :     uint             ext_mask_value;
     400           0 :   } request = {
     401           0 :     .nlh = {
     402           0 :       .nlmsg_len   = sizeof(request),
     403           0 :       .nlmsg_type  = RTM_GETLINK,
     404           0 :       .nlmsg_flags = NLM_F_REQUEST
     405           0 :     },
     406           0 :     .ifi            = { .ifi_family=AF_UNSPEC, .ifi_index=(int)if_idx },
     407           0 :     .ext_mask       = { .rta_type=IFLA_EXT_MASK, .rta_len=RTA_LENGTH( sizeof(uint) ) },
     408           0 :     .ext_mask_value = RTEXT_FILTER_VF
     409           0 :   };
     410             : 
     411             :   /* RTM_GETLINK returns every VF on the PF in one reply */
     412           0 :   uchar buf[ 131072 ] __attribute__((aligned(NLMSG_ALIGNTO)));
     413           0 :   int err = iavf_rtnetlink_request( &request.nlh, RTM_NEWLINK, buf, sizeof(buf) );
     414           0 :   if( FD_UNLIKELY( 0!=err ) ) goto done;
     415             : 
     416           0 :   struct nlmsghdr const * reply = fd_type_pun_const( buf );
     417           0 :   struct ifinfomsg const * ifi = NLMSG_DATA( reply );
     418           0 :   if( FD_UNLIKELY( reply->nlmsg_len<NLMSG_LENGTH( sizeof(*ifi) ) || ifi->ifi_index!=(int)if_idx ) ) {
     419           0 :     err = EPROTO;
     420           0 :     goto done;
     421           0 :   }
     422             : 
     423           0 :   int vf_found = 0;
     424           0 :   struct rtattr const * attr           = IFLA_RTA( ifi );
     425           0 :   long                  attr_remaining = (long)IFLA_PAYLOAD( reply );
     426           0 :   for( ; RTA_OK( attr, attr_remaining ); attr=RTA_NEXT( attr, attr_remaining ) ) {
     427           0 :     if( (attr->rta_type & NLA_TYPE_MASK)!=IFLA_VFINFO_LIST ) continue;
     428             : 
     429           0 :     struct rtattr const * vf_info      = RTA_DATA( attr );
     430           0 :     long                  vf_remaining = (long)RTA_PAYLOAD( attr );
     431           0 :     for( ; RTA_OK( vf_info, vf_remaining ); vf_info=RTA_NEXT( vf_info, vf_remaining ) ) {
     432           0 :       if( (vf_info->rta_type & NLA_TYPE_MASK)!=IFLA_VF_INFO ) continue;
     433             : 
     434           0 :       err = iavf_vf_policy_parse( vf_info, vf_idx, policy );
     435           0 :       if( err==ENODATA ) continue;
     436           0 :       if( FD_UNLIKELY( 0!=err ) ) goto done;
     437           0 :       vf_found = 1;
     438           0 :     }
     439           0 :     if( FD_UNLIKELY( 0!=vf_remaining ) ) {
     440           0 :       err = EPROTO;
     441           0 :       goto done;
     442           0 :     }
     443           0 :   }
     444             : 
     445           0 :   if( FD_UNLIKELY( 0!=attr_remaining ) ) err = EPROTO;
     446           0 :   else if( !vf_found ) err = ENODATA;
     447           0 :   else err = 0;
     448           0 : done:
     449           0 :   if( FD_UNLIKELY( 0!=err ) ) {
     450           0 :     errno = err;
     451           0 :     return -1;
     452           0 :   }
     453           0 :   return 0;
     454           0 : }
     455             : 
     456             : static void
     457             : iavf_vf_policy_configure( char const * pf_if,
     458             :                           uint         vf_idx,
     459           0 :                           uchar const  mac[ 6 ] ) {
     460           0 :   iavf_vf_policy_t policy;
     461           0 :   if( -1==iavf_vf_policy_get( pf_if, vf_idx, &policy ) ) {
     462           0 :     FD_LOG_ERR(( "Reading settings for VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) ));
     463           0 :   }
     464           0 :   uint if_idx = if_nametoindex( pf_if );
     465           0 :   if( 0==if_idx ) {
     466           0 :     FD_LOG_ERR(( "Looking up the interface index for PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     467           0 :   }
     468           0 :   struct ifla_vf_mac        vf_mac   = { .vf=vf_idx };
     469           0 :   struct ifla_vf_spoofchk   spoofchk = { .vf=vf_idx, .setting=0U };
     470           0 :   struct ifla_vf_trust      trust    = { .vf=vf_idx, .setting=0U };
     471           0 :   struct ifla_vf_link_state link     = { .vf=vf_idx, .link_state=IFLA_VF_LINK_STATE_AUTO };
     472           0 :   fd_memcpy( vf_mac.mac, mac, 6UL );
     473             :   /* i40e_ndo_set_vf_mac resets the VF even when the MAC address is unchanged */
     474           0 :   if( (!policy.mac_valid || 0!=memcmp( policy.mac, mac, 6UL )) &&
     475           0 :       -1==iavf_vf_policy_attr_set( if_idx, IFLA_VF_MAC, &vf_mac, sizeof(vf_mac) ) ) {
     476           0 :     FD_LOG_ERR(( "Setting the MAC address of VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) ));
     477           0 :   }
     478           0 :   if( (!policy.spoofchk_valid || policy.spoofchk) &&
     479           0 :       -1==iavf_vf_policy_attr_set( if_idx, IFLA_VF_SPOOFCHK, &spoofchk, sizeof(spoofchk) ) ) {
     480           0 :     FD_LOG_ERR(( "Disabling spoof checking for VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) ));
     481           0 :   }
     482           0 :   if( (!policy.trust_valid || policy.trust) &&
     483           0 :       -1==iavf_vf_policy_attr_set( if_idx, IFLA_VF_TRUST, &trust, sizeof(trust) ) ) {
     484           0 :     FD_LOG_ERR(( "Disabling trust for VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) ));
     485           0 :   }
     486             :   /* IFLA_VF_LINK_STATE_AUTO reports the physical link's up/down state */
     487           0 :   if( (!policy.link_state_valid || !policy.link_state_auto) &&
     488           0 :       -1==iavf_vf_policy_attr_set( if_idx, IFLA_VF_LINK_STATE, &link, sizeof(link) ) ) {
     489           0 :     FD_LOG_ERR(( "Setting automatic link state for VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) ));
     490           0 :   }
     491           0 : }
     492             : 
     493             : static int
     494           0 : iavf_vf_driver_unbind( char const * vf_pci ) {
     495           0 :   char path[ PATH_MAX ];
     496           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/bus/pci/devices/%s/driver/unbind", vf_pci ) );
     497           0 :   return fd_file_util_write_cstr( path, vf_pci );
     498           0 : }
     499             : 
     500             : static int
     501             : iavf_vf_driver_override_set( char const * vf_pci,
     502           0 :                              char const * driver ) {
     503           0 :   char path[ PATH_MAX ];
     504           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, IAVF_SYSFS_ROOT "/bus/pci/devices/%s/driver_override", vf_pci ) );
     505           0 :   return fd_file_util_write_cstr( path, driver );
     506           0 : }
     507             : 
     508             : static void
     509           0 : iavf_vf_vfio_pci_bind( char const * vf_pci ) {
     510           0 :   struct stat st;
     511           0 :   if( FD_UNLIKELY( -1==stat( IAVF_SYSFS_ROOT "/bus/pci/drivers/vfio-pci", &st ) ) ) {
     512           0 :     FD_LOG_ERR(( "Checking /sys/bus/pci/drivers/vfio-pci failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     513           0 :   }
     514             : 
     515             :   /* Set driver_override before unbinding so subsequent PCI driver matching
     516             :      accepts only vfio-pci for this VF. The override itself does not bind it */
     517           0 :   if( FD_UNLIKELY( -1==iavf_vf_driver_override_set( vf_pci, "vfio-pci" ) ) ) {
     518           0 :     FD_LOG_ERR(( "Setting driver_override to vfio-pci for VF %s failed (%i-%s)", vf_pci, errno, fd_io_strerror( errno ) ));
     519           0 :   }
     520           0 :   char driver[ FD_IAVF_DRIVER_NAME_MAX ];
     521           0 :   int has_driver = 0==iavf_pci_driver_name_get( vf_pci, driver );
     522           0 :   if( !has_driver && errno!=ENOENT ) {
     523           0 :     FD_LOG_ERR(( "Reading the driver for VF %s failed (%i-%s)", vf_pci, errno, fd_io_strerror( errno ) ));
     524           0 :   }
     525           0 :   if( has_driver && 0==strcmp( driver, "vfio-pci" ) ) return;
     526           0 :   if( has_driver && -1==iavf_vf_driver_unbind( vf_pci ) && errno!=ENOENT && errno!=ENODEV ) {
     527           0 :     FD_LOG_ERR(( "Unbinding VF %s from %s failed (%i-%s)", vf_pci, driver, errno, fd_io_strerror( errno ) ));
     528           0 :   }
     529             :   /* Probe explicitly, including when sriov_drivers_autoprobe is 0 on the PF */
     530           0 :   int err = -1==fd_file_util_write_cstr( IAVF_SYSFS_ROOT "/bus/pci/drivers_probe", vf_pci ) ? errno : 0;
     531           0 :   has_driver = 0==iavf_pci_driver_name_get( vf_pci, driver );
     532           0 :   if( !has_driver && errno!=ENOENT ) {
     533           0 :     FD_LOG_ERR(( "Reading the driver for VF %s after probing failed (%i-%s)", vf_pci, errno, fd_io_strerror( errno ) ));
     534           0 :   }
     535           0 :   if( has_driver && 0==strcmp( driver, "vfio-pci" ) ) return;
     536           0 :   if( 0!=err ) {
     537           0 :     FD_LOG_ERR(( "Probing VF %s through /sys/bus/pci/drivers_probe failed (%i-%s)", vf_pci, err, fd_io_strerror( err ) ));
     538           0 :   }
     539           0 :   FD_LOG_ERR(( "VF %s did not bind to vfio-pci after probing (driver '%s')", vf_pci, has_driver ? driver : "none" ));
     540           0 : }
     541             : 
     542             : static int
     543             : iavf_pf_ntuple_ioctl( fd_ethtool_ioctl_t *   ioc,
     544           0 :                       struct ethtool_rxnfc * request ) {
     545           0 :   ioc->ifr.ifr_data = (void *)request;
     546           0 :   return ioctl( ioc->fd, SIOCETHTOOL, &ioc->ifr );
     547           0 : }
     548             : 
     549             : static int
     550             : iavf_pf_ntuple_rule_get( fd_ethtool_ioctl_t *          ioc,
     551             :                          uint                          location,
     552           0 :                          struct ethtool_rx_flow_spec * rule ) {
     553           0 :   struct ethtool_rxnfc request = { .cmd=ETHTOOL_GRXCLSRULE, .fs={ .location=location } };
     554           0 :   if( -1==iavf_pf_ntuple_ioctl( ioc, &request ) ) return -1;
     555           0 :   *rule = request.fs;
     556           0 :   return 0;
     557           0 : }
     558             : 
     559             : static int
     560             : iavf_pf_ntuple_rule_equal( struct ethtool_rx_flow_spec const * a,
     561          15 :                            struct ethtool_rx_flow_spec const * b ) {
     562             :   /* i40e can fill unused IPv6 destination mask bytes with 0xff when reading
     563             :      back UDP/IPv4 ntuple rules. Ignore these bytes when comparing rules */
     564          15 :   struct ethtool_tcpip4_spec const * a_hdr  = &a->h_u.udp_ip4_spec;
     565          15 :   struct ethtool_tcpip4_spec const * b_hdr  = &b->h_u.udp_ip4_spec;
     566          15 :   struct ethtool_tcpip4_spec const * a_mask = &a->m_u.udp_ip4_spec;
     567          15 :   struct ethtool_tcpip4_spec const * b_mask = &b->m_u.udp_ip4_spec;
     568             : 
     569          15 :   int same_type_action_location = a->flow_type  ==UDP_V4_FLOW &&
     570          15 :                                   b->flow_type  ==UDP_V4_FLOW &&
     571          15 :                                   a->ring_cookie==b->ring_cookie &&
     572          15 :                                   a->location   ==b->location;
     573          15 :   int same_udp_ip4_fields       = a_hdr->ip4src==b_hdr->ip4src &&
     574          15 :                                   a_hdr->ip4dst==b_hdr->ip4dst &&
     575          15 :                                   a_hdr->psrc  ==b_hdr->psrc   &&
     576          15 :                                   a_hdr->pdst  ==b_hdr->pdst   &&
     577          15 :                                   a_hdr->tos   ==b_hdr->tos;
     578          15 :   int same_udp_ip4_masks        = a_mask->ip4src==b_mask->ip4src &&
     579          15 :                                   a_mask->ip4dst==b_mask->ip4dst &&
     580          15 :                                   a_mask->psrc  ==b_mask->psrc   &&
     581          15 :                                   a_mask->pdst  ==b_mask->pdst   &&
     582          15 :                                   a_mask->tos   ==b_mask->tos;
     583          15 :   int same_flow_extensions      = 0==memcmp( &a->h_ext, &b->h_ext, sizeof(a->h_ext) ) &&
     584          15 :                                   0==memcmp( &a->m_ext, &b->m_ext, sizeof(a->m_ext) );
     585          15 :   return same_type_action_location && same_udp_ip4_fields && same_udp_ip4_masks && same_flow_extensions;
     586          15 : }
     587             : 
     588             : /* iavf_pf_ntuple_drop_rule_build returns a drop rule for the given UDP/IPv4
     589             :    destination port. The PF drops suppress kernel delivery, while fd_iavf_init
     590             :    installs the VF receive MAC filter separately. */
     591             : static struct ethtool_rx_flow_spec
     592             : iavf_pf_ntuple_drop_rule_build( fd_config_t const * config,
     593             :                                 ushort              port,
     594           3 :                                 uint                location ) {
     595           3 :   return (struct ethtool_rx_flow_spec) {
     596           3 :     .flow_type   = UDP_V4_FLOW,
     597           3 :     .h_u         = { .udp_ip4_spec={ .ip4dst=config->net.bind_address_parsed, .pdst=fd_ushort_bswap( port ) } },
     598           3 :     .m_u         = { .udp_ip4_spec={ .ip4dst=0!=config->net.bind_address_parsed ? UINT_MAX : 0U, .pdst=USHRT_MAX } },
     599           3 :     .ring_cookie = RX_CLS_FLOW_DISC,
     600           3 :     .location    = location
     601           3 :   };
     602           3 : }
     603             : 
     604             : static void
     605             : iavf_pf_ntuple_drops_install( char const *        pf_if,
     606           0 :                               fd_config_t const * config ) {
     607           0 :   fd_ethtool_ioctl_t ioc;
     608           0 :   if( NULL==fd_ethtool_ioctl_init( &ioc, pf_if ) ) {
     609           0 :     FD_LOG_ERR(( "Opening PF %s for ntuple rule installation failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     610           0 :   }
     611           0 :   int err = fd_ethtool_ioctl_feature_set( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, 1 );
     612           0 :   if( 0!=err ) {
     613           0 :     FD_LOG_ERR(( "Enabling ntuple filtering on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) ));
     614           0 :   }
     615             :   /* Delete every PF ntuple rule */
     616           0 :   err = fd_ethtool_ioctl_ntuple_clear( &ioc );
     617           0 :   if( 0!=err ) {
     618           0 :     FD_LOG_ERR(( "Clearing ntuple rules on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) ));
     619           0 :   }
     620             : 
     621           0 :   struct ethtool_rxnfc count = { .cmd=ETHTOOL_GRXCLSRLCNT };
     622           0 :   if( -1==iavf_pf_ntuple_ioctl( &ioc, &count ) ) {
     623           0 :     FD_LOG_ERR(( "Reading the ntuple rule capacity on PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     624           0 :   }
     625           0 :   uint capacity = (uint)(count.data & ~((ulong)RX_CLS_LOC_SPECIAL));
     626           0 :   if( 0==capacity || capacity>65536U ) {
     627           0 :     FD_LOG_ERR(( "PF %s reports an unsupported ntuple rule capacity of %u", pf_if, capacity ));
     628           0 :   }
     629           0 :   ushort ports[ 32 ];
     630           0 :   uint port_cnt = configure_udp_ports( config, ports );
     631           0 :   if( port_cnt>capacity ) {
     632           0 :     FD_LOG_ERR(( "PF %s has space for %u ntuple rules but IAVF requires %u UDP drop rules", pf_if, capacity, port_cnt ));
     633           0 :   }
     634             : 
     635             :   /* Install PF UDP drop rules at ntuple locations capacity-1U-i.
     636             :      iavf_pf_config_check uses the same locations to verify each port's rule */
     637           0 :   for( uint i=0U; i<port_cnt; i++ ) {
     638           0 :     struct ethtool_rx_flow_spec rule = iavf_pf_ntuple_drop_rule_build( config, ports[i], capacity-1U-i );
     639           0 :     struct ethtool_rxnfc insert = { .cmd=ETHTOOL_SRXCLSRLINS, .fs=rule };
     640           0 :     if( -1==iavf_pf_ntuple_ioctl( &ioc, &insert ) ) {
     641           0 :       FD_LOG_ERR(( "Installing UDP drop rule %u for port %hu on PF %s failed (%i-%s)", rule.location, ports[i], pf_if, errno, fd_io_strerror( errno ) ));
     642           0 :     }
     643           0 :     struct ethtool_rx_flow_spec current;
     644           0 :     if( -1==iavf_pf_ntuple_rule_get( &ioc, rule.location, &current ) ) {
     645           0 :       FD_LOG_ERR(( "Reading UDP drop rule %u for port %hu on PF %s after installation failed (%i-%s)", rule.location, ports[i], pf_if, errno, fd_io_strerror( errno ) ));
     646           0 :     }
     647           0 :     if( !iavf_pf_ntuple_rule_equal( &rule, &current ) ) {
     648           0 :       FD_LOG_ERR(( "UDP drop rule %u for port %hu on PF %s does not match the required rule after installation", rule.location, ports[i], pf_if ));
     649           0 :     }
     650           0 :   }
     651           0 :   fd_ethtool_ioctl_fini( &ioc );
     652           0 : }
     653             : 
     654             : static configure_result_t
     655             : iavf_pf_config_check( fd_config_t const * config,
     656             :                       char const *        pf_if,
     657           0 :                       int                 check_type ) {
     658           0 :   uint numvfs;
     659           0 :   if( FD_UNLIKELY( -1==iavf_pf_sriov_numvfs_get( pf_if, &numvfs ) ) ) {
     660           0 :     FD_LOG_ERR(( "Cannot read the enabled VF count for PF %s (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     661           0 :   }
     662           0 :   int fini_check = check_type==FD_CONFIGURE_CHECK_TYPE_FINI_PERM ||
     663           0 :                    check_type==FD_CONFIGURE_CHECK_TYPE_PRE_FINI  ||
     664           0 :                    check_type==FD_CONFIGURE_CHECK_TYPE_POST_FINI;
     665           0 :   if( !fini_check && FD_UNLIKELY( -1==fd_iavf_ari_check( pf_if ) ) ) {
     666           0 :     int err = errno;
     667           0 :     if( err==EOPNOTSUPP ) {
     668           0 :       FD_LOG_ERR(( "iavf provider requires PCIe ARI forwarding, but on PF %s ARI is disabled. "
     669           0 :                    "Please either enable ARI on the device or set [net.provider] to \"auto\"", pf_if ));
     670           0 :     }
     671           0 :     FD_LOG_ERR(( "Reading ARI forwarding status for PF %s from /sys/class/net/%s/device/ari_enabled failed (%i-%s)",
     672           0 :                  pf_if, pf_if, err, fd_io_strerror( err ) ));
     673           0 :   }
     674             :   /* A PF's only VF is removed by configure fini. Report it before checking
     675             :      ntuple rules so missing rules cannot cause fini to skip VF removal */
     676           0 :   if( fini_check && numvfs==1U ) {
     677           0 :     PARTIALLY_CONFIGURED( "PF %s has one VF to remove", pf_if );
     678           0 :   }
     679           0 :   if( !fini_check && 0==numvfs ) {
     680           0 :     if( FD_UNLIKELY( -1==fd_iavf_sriov_resources_check( pf_if ) ) ) {
     681           0 :       int err = errno;
     682           0 :       if( err==ENOSPC ) {
     683           0 :         FD_LOG_ERR(( "Cannot create VF 0 on PF %s. The VF's PCI bus number is outside the parent bridge's assigned range", pf_if ));
     684           0 :       }
     685           0 :       if( err==ENOMEM ) {
     686           0 :         FD_LOG_ERR(( "The system has not reserved PCIe memory for VFs on PF %s.\n\n"
     687           0 :                      "  %s", pf_if, fd_iavf_sriov_bios_guide( 0 ) ));
     688           0 :       }
     689           0 :       FD_LOG_ERR(( "Checking VF PCIe resources for PF %s failed (%i-%s). "
     690           0 :                    "Check that the PCI sysfs files under /sys/class/net/%s/device are accessible.",
     691           0 :                    pf_if, err, fd_io_strerror( err ), pf_if ));
     692           0 :     }
     693           0 :     NOT_CONFIGURED( "PF %s has no VFs", pf_if );
     694           0 :   }
     695           0 :   fd_ethtool_ioctl_t ioc;
     696           0 :   if( NULL==fd_ethtool_ioctl_init( &ioc, pf_if ) ) {
     697           0 :     PARTIALLY_CONFIGURED( "Opening PF %s for ntuple rule lookup failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) );
     698           0 :   }
     699           0 :   if( !fini_check ) {
     700           0 :     int ntuple_enabled = 0;
     701           0 :     int err = fd_ethtool_ioctl_feature_test( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, &ntuple_enabled );
     702           0 :     if( 0!=err || !ntuple_enabled ) {
     703           0 :       fd_ethtool_ioctl_fini( &ioc );
     704           0 :       if( 0!=err ) {
     705           0 :         PARTIALLY_CONFIGURED( "Reading ntuple filtering support on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) );
     706           0 :       }
     707           0 :       NOT_CONFIGURED( "PF %s ntuple filtering disabled", pf_if );
     708           0 :     }
     709           0 :   }
     710             : 
     711             :   /* With sriov_numvfs==0, fini still runs if the PF has any ntuple rules */
     712           0 :   struct ethtool_rxnfc count = { .cmd=ETHTOOL_GRXCLSRLCNT };
     713           0 :   int err = -1==iavf_pf_ntuple_ioctl( &ioc, &count ) ? errno : 0;
     714           0 :   if( 0!=err || fini_check ) {
     715           0 :     fd_ethtool_ioctl_fini( &ioc );
     716           0 :     if( 0!=err && !(fini_check && err==EOPNOTSUPP) ) {
     717           0 :       PARTIALLY_CONFIGURED( "Reading the ntuple rule count on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) );
     718           0 :     }
     719           0 :     if( 0==err && 0!=count.rule_cnt ) {
     720           0 :       PARTIALLY_CONFIGURED( "PF %s has %u ntuple rules to remove", pf_if, count.rule_cnt );
     721           0 :     }
     722           0 :     NOT_CONFIGURED( "PF %s has %u VFs and no ntuple rules to remove", pf_if, numvfs );
     723           0 :   }
     724           0 :   ushort ports[ 32 ];
     725           0 :   uint port_cnt = configure_udp_ports( config, ports );
     726           0 :   if( 0==count.rule_cnt && 0!=port_cnt ) {
     727           0 :     fd_ethtool_ioctl_fini( &ioc );
     728           0 :     NOT_CONFIGURED( "PF %s UDP drop rules missing", pf_if );
     729           0 :   }
     730           0 :   uint capacity = (uint)(count.data & ~((ulong)RX_CLS_LOC_SPECIAL));
     731           0 :   int valid = 0!=capacity && capacity<=65536U && port_cnt<=capacity && count.rule_cnt==port_cnt;
     732           0 :   for( uint i=0U; valid && i<port_cnt; i++ ) {
     733           0 :     struct ethtool_rx_flow_spec rule = iavf_pf_ntuple_drop_rule_build( config, ports[i], capacity-1U-i );
     734           0 :     struct ethtool_rx_flow_spec current;
     735           0 :     if( -1==iavf_pf_ntuple_rule_get( &ioc, rule.location, &current ) ) { err = errno; break; }
     736           0 :     valid = iavf_pf_ntuple_rule_equal( &rule, &current );
     737           0 :   }
     738           0 :   fd_ethtool_ioctl_fini( &ioc );
     739           0 :   if( 0!=err ) {
     740           0 :     PARTIALLY_CONFIGURED( "Reading a UDP drop rule on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) );
     741           0 :   }
     742           0 :   if( !valid ) {
     743           0 :     PARTIALLY_CONFIGURED( "PF %s does not have the %u matching UDP drop rules required by IAVF", pf_if, port_cnt );
     744           0 :   }
     745             : 
     746           0 :   char pf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     747           0 :   char vf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     748           0 :   uint vf_idx;
     749           0 :   fd_iavf_pci_info_t pci;
     750           0 :   if( -1==iavf_pf_pci_addr_get( pf_if, pf_pci ) ) {
     751           0 :     PARTIALLY_CONFIGURED( "Reading the PCI address of PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) );
     752           0 :   }
     753           0 :   if( -1==fd_iavf_get_last_vf( pf_if, &vf_idx, vf_pci, 1 ) ) {
     754           0 :     PARTIALLY_CONFIGURED( "Finding a VF bound to vfio-pci on PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) );
     755           0 :   }
     756           0 :   if( -1==fd_iavf_pci_probe( &pci, vf_pci ) ) {
     757           0 :     PARTIALLY_CONFIGURED( "Reading PCI information for VF %s on PF %s failed (%i-%s)", vf_pci, pf_if, errno, fd_io_strerror( errno ) );
     758           0 :   }
     759           0 :   if( 0!=strcmp( pci.pf_pci_addr, pf_pci ) ) {
     760           0 :     PARTIALLY_CONFIGURED( "VF %s belongs to PF %s but IAVF requires PF %s (%s)", vf_pci, pci.pf_pci_addr, pf_if, pf_pci );
     761           0 :   }
     762           0 :   if( 0!=strcmp( pci.driver, "vfio-pci" ) ) {
     763           0 :     PARTIALLY_CONFIGURED( "VF %u on PF %s uses driver '%s' but IAVF requires vfio-pci", vf_idx, pf_if, pci.driver );
     764           0 :   }
     765           0 :   uchar mac[ 6 ];
     766           0 :   iavf_vf_policy_t policy;
     767           0 :   if( -1==iavf_netdev_mac_get( config->net.interface, mac ) ) {
     768           0 :     PARTIALLY_CONFIGURED( "Reading the MAC address of interface %s failed (%i-%s)", config->net.interface, errno, fd_io_strerror( errno ) );
     769           0 :   }
     770           0 :   if( -1==iavf_vf_policy_get( pf_if, vf_idx, &policy ) ) {
     771           0 :     PARTIALLY_CONFIGURED( "Reading settings for VF %u on PF %s failed (%i-%s)", vf_idx, pf_if, errno, fd_io_strerror( errno ) );
     772           0 :   }
     773           0 :   if( !policy.mac_valid ) {
     774           0 :     PARTIALLY_CONFIGURED( "PF %s did not report a valid unicast MAC address for VF %u", pf_if, vf_idx );
     775           0 :   }
     776           0 :   if( !policy.spoofchk_valid ) {
     777           0 :     PARTIALLY_CONFIGURED( "PF %s did not report spoof checking for VF %u", pf_if, vf_idx );
     778           0 :   }
     779           0 :   if( !policy.trust_valid ) {
     780           0 :     PARTIALLY_CONFIGURED( "PF %s did not report trust for VF %u", pf_if, vf_idx );
     781           0 :   }
     782           0 :   if( !policy.link_state_valid ) {
     783           0 :     PARTIALLY_CONFIGURED( "PF %s did not report link state for VF %u", pf_if, vf_idx );
     784           0 :   }
     785           0 :   if( 0!=memcmp( policy.mac, mac, 6UL ) ) {
     786           0 :     PARTIALLY_CONFIGURED( "VF %u on PF %s requires MAC " FD_ETH_MAC_FMT, vf_idx, pf_if, FD_ETH_MAC_FMT_ARGS( mac ) );
     787           0 :   }
     788           0 :   if( policy.spoofchk ) {
     789           0 :     PARTIALLY_CONFIGURED( "VF %u on PF %s requires spoof checking disabled", vf_idx, pf_if );
     790           0 :   }
     791           0 :   if( policy.trust ) {
     792           0 :     PARTIALLY_CONFIGURED( "VF %u on PF %s requires trust disabled", vf_idx, pf_if );
     793           0 :   }
     794           0 :   if( !policy.link_state_auto ) {
     795           0 :     PARTIALLY_CONFIGURED( "VF %u on PF %s requires automatic link state", vf_idx, pf_if );
     796           0 :   }
     797           0 :   CONFIGURE_OK();
     798           0 : }
     799             : 
     800             : static int
     801           0 : iavf_vf_vfio_group_idle_check( char const * vf_pci ) {
     802           0 :   uint user_pid;
     803           0 :   int in_use = iavf_vf_vfio_group_user_pid_find( vf_pci, &user_pid );
     804           0 :   if( -1==in_use ) return -1;
     805           0 :   if( in_use ) {
     806           0 :     FD_LOG_WARNING(( "VF %s is in use by PID %u", vf_pci, user_pid ));
     807           0 :     errno = EBUSY;
     808           0 :     return -1;
     809           0 :   }
     810           0 :   return 0;
     811           0 : }
     812             : 
     813             : static void
     814             : iavf_pf_configure( char const *        pf_if,
     815             :                    uchar const         mac[ 6 ],
     816           0 :                    fd_config_t const * config ) {
     817             :   /* Linux requires disabling all VFs before changing an existing nonzero
     818             :      sriov_numvfs to a different nonzero count. Reuse the highest index VF
     819             :      bound to vfio-pci when VFs exist, otherwise create VF 0 */
     820           0 :   uint numvfs;
     821           0 :   if( -1==iavf_pf_sriov_numvfs_get( pf_if, &numvfs ) ) {
     822           0 :     FD_LOG_ERR(( "Reading the enabled VF count for PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     823           0 :   }
     824           0 :   if( 0==numvfs && -1==iavf_pf_sriov_numvfs_set( pf_if, 1U ) ) {
     825           0 :     int err = errno;
     826           0 :     FD_LOG_ERR(( "Creating VF 0 on PF %s through /sys/class/net/%s/device/sriov_numvfs failed (%i-%s)%s",
     827           0 :                  pf_if, pf_if, err, fd_io_strerror( err ),
     828           0 :                  err==ENOMEM ? "" : ".\n  The PF driver may have logged why VF creation failed, so check dmesg for details" ));
     829           0 :   }
     830           0 :   char pf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     831           0 :   char vf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     832           0 :   uint vf_idx;
     833           0 :   fd_iavf_pci_info_t pci;
     834           0 :   if( -1==iavf_pf_pci_addr_get( pf_if, pf_pci ) ) {
     835           0 :     FD_LOG_ERR(( "Reading the PCI address of PF %s failed (%i-%s)", pf_if, errno, fd_io_strerror( errno ) ));
     836           0 :   }
     837           0 :   if( -1==fd_iavf_get_last_vf( pf_if, &vf_idx, vf_pci, numvfs>0U ) ) {
     838           0 :     int err = errno;
     839           0 :     if( numvfs && err==ENOENT ) {
     840           0 :       FD_LOG_ERR(( "No VF on PF %s is bound to vfio-pci. Bind an unused VF to vfio-pci before running configure init iavf", pf_if ));
     841           0 :     }
     842           0 :     FD_LOG_ERR(( "Finding a VF on PF %s failed (%i-%s)", pf_if, err, fd_io_strerror( err ) ));
     843           0 :   }
     844           0 :   if( -1==fd_iavf_pci_probe( &pci, vf_pci ) ) {
     845           0 :     FD_LOG_ERR(( "Reading PCI information for VF %s on PF %s failed (%i-%s)", vf_pci, pf_if, errno, fd_io_strerror( errno ) ));
     846           0 :   }
     847           0 :   if( 0!=strcmp( pci.pf_pci_addr, pf_pci ) ) {
     848           0 :     FD_LOG_ERR(( "VF %s belongs to PF %s but IAVF requires PF %s (%s)", vf_pci, pci.pf_pci_addr, pf_if, pf_pci ));
     849           0 :   }
     850           0 :   if( 0!=numvfs && 0!=strcmp( pci.driver, "vfio-pci" ) ) {
     851           0 :     FD_LOG_ERR(( "Existing VF %u (%s) on PF %s uses driver '%s'. "
     852           0 :                  "Please bind it to vfio-pci explicitly if it is intended for Firedancer, or set [net.provider] to \"auto\"",
     853           0 :                  vf_idx, vf_pci, pf_if, pci.driver[0] ? pci.driver : "none" ));
     854           0 :   }
     855           0 :   if( -1==iavf_vf_vfio_group_idle_check( vf_pci ) ) {
     856           0 :     FD_LOG_ERR(( "VF %s on PF %s is unavailable for configuration (%i-%s)", vf_pci, pf_if, errno, fd_io_strerror( errno ) ));
     857           0 :   }
     858           0 :   if( 0==numvfs ) iavf_vf_vfio_pci_bind( vf_pci );
     859           0 :   iavf_vf_policy_configure( pf_if, vf_idx, mac );
     860           0 :   iavf_pf_ntuple_drops_install( pf_if, config );
     861           0 : }
     862             : 
     863             : static void
     864           0 : init( fd_config_t const * config ) {
     865           0 :   if( !fd_iavf_vfio_avail() ) {
     866           0 :     FD_LOG_NOTICE(( "%sRUN: `modprobe vfio-pci`%s", fd_log_style_dim(), fd_log_style_normal() ));
     867           0 :     if( FD_UNLIKELY( -1==fd_sys_util_modprobe( "vfio-pci", 0 ) ) ) {
     868           0 :       FD_LOG_ERR(( "failed to load vfio-pci kernel module. Run `sudo modprobe vfio-pci` and retry" ));
     869           0 :     }
     870           0 :     if( FD_UNLIKELY( !fd_iavf_vfio_avail() ) ) {
     871           0 :       FD_LOG_ERR(( "vfio-pci driver still unavailable after trying to load its kernel module" ));
     872           0 :     }
     873           0 :   }
     874           0 :   char const * diagnostic;
     875           0 :   if( FD_UNLIKELY( -1==fd_iavf_vfio_api_check( &diagnostic ) ) ) {
     876           0 :     FD_LOG_ERR(( "%s (%i-%s)", diagnostic, errno, fd_io_strerror( errno ) ));
     877           0 :   }
     878           0 :   char members[ FD_IAVF_MEMBER_MAX ][ IFNAMSIZ ];
     879           0 :   ulong member_cnt;
     880           0 :   if( -1==fd_iavf_member_interfaces( config->net.interface, members, &member_cnt ) ) {
     881           0 :     FD_LOG_ERR(( "Discovering PFs for interface %s failed (%i-%s)", config->net.interface, errno, fd_io_strerror( errno ) ));
     882           0 :   }
     883             :   /* Resolve every PF's iommu_group and check VF bus numbers and BAR
     884             :      allocations on PFs with sriov_numvfs==0 before creating any VF or
     885             :      replacing any PF ntuple rules */
     886           0 :   int member_configured[ FD_IAVF_MEMBER_MAX ];
     887           0 :   for( ulong i=0UL; i<member_cnt; i++ ) {
     888           0 :     char const * diagnostic;
     889           0 :     if( FD_UNLIKELY( -1==fd_iavf_iommu_check( members[i], &diagnostic ) ) ) {
     890           0 :       int err = errno;
     891           0 :       FD_LOG_ERR(( "%s for PF %s.\n"
     892           0 :                    "  Resolving /sys/class/net/%s/device/iommu_group failed (%i-%s).\n\n"
     893           0 :                    "  %s",
     894           0 :                    err==ENOENT ? "IOMMU is not enabled" : "IOMMU check failed",
     895           0 :                    members[i], members[i], err, fd_io_strerror( err ), diagnostic ));
     896           0 :     }
     897           0 :     member_configured[i] = iavf_pf_config_check( config, members[i], FD_CONFIGURE_CHECK_TYPE_PRE_INIT ).result==CONFIGURE_OK;
     898           0 :   }
     899             :   /* Assign the bond MAC to the selected VF on each PF when using a bond */
     900           0 :   uchar mac[ 6 ];
     901           0 :   if( -1==iavf_netdev_mac_get( config->net.interface, mac ) ) {
     902           0 :     FD_LOG_ERR(( "Reading the MAC address of interface %s failed (%i-%s)", config->net.interface, errno, fd_io_strerror( errno ) ));
     903           0 :   }
     904           0 :   for( ulong i=0UL; i<member_cnt; i++ ) {
     905           0 :     if( member_configured[i] ) continue;
     906           0 :     iavf_pf_configure( members[i], mac, config );
     907           0 :   }
     908           0 : }
     909             : 
     910             : static int
     911             : fini( fd_config_t const * config,
     912           0 :       int                 pre_init ) {
     913             :   /* configure init passes pre_init=1 when undoing a partially configured
     914             :      stage. Leave existing VFs for init to reuse. The configure fini command
     915             :      passes pre_init=0 and runs the PF ntuple and VF removal below */
     916           0 :   if( pre_init ) return 0;
     917           0 :   char members[ FD_IAVF_MEMBER_MAX ][ IFNAMSIZ ];
     918           0 :   ulong member_cnt;
     919           0 :   if( -1==fd_iavf_member_interfaces( config->net.interface, members, &member_cnt ) ) {
     920           0 :     FD_LOG_ERR(( "Discovering PFs for interface %s failed (%i-%s)", config->net.interface, errno, fd_io_strerror( errno ) ));
     921           0 :   }
     922           0 :   int changed = 0;
     923           0 :   for( ulong i=0UL; i<member_cnt; i++ ) {
     924           0 :     uint numvfs;
     925           0 :     if( -1==iavf_pf_sriov_numvfs_get( members[i], &numvfs ) ) {
     926           0 :       FD_LOG_ERR(( "Reading the enabled VF count for PF %s failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     927           0 :     }
     928             :     /* If the sole VF has an open VFIO group descriptor, abort before deleting
     929             :        this PF's ntuple rules */
     930           0 :     if( numvfs==1U ) {
     931           0 :       char vf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     932           0 :       if( -1==fd_iavf_get_last_vf( members[i], NULL, vf_pci, 0 ) ) {
     933           0 :         FD_LOG_ERR(( "Finding the highest-index VF on PF %s failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     934           0 :       }
     935           0 :       if( -1==iavf_vf_vfio_group_idle_check( vf_pci ) ) {
     936           0 :         FD_LOG_ERR(( "VF %s on PF %s is unavailable for removal (%i-%s)", vf_pci, members[i], errno, fd_io_strerror( errno ) ));
     937           0 :       }
     938           0 :     }
     939           0 :     fd_ethtool_ioctl_t ioc;
     940           0 :     if( NULL==fd_ethtool_ioctl_init( &ioc, members[i] ) ) {
     941           0 :       FD_LOG_ERR(( "Opening PF %s for ntuple rule cleanup failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     942           0 :     }
     943           0 :     struct ethtool_rxnfc count = { .cmd=ETHTOOL_GRXCLSRLCNT };
     944           0 :     if( -1==iavf_pf_ntuple_ioctl( &ioc, &count ) && errno!=EOPNOTSUPP ) {
     945           0 :       FD_LOG_ERR(( "Reading the ntuple rule count on PF %s before cleanup failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     946           0 :     }
     947           0 :     int err = fd_ethtool_ioctl_ntuple_clear( &ioc );
     948           0 :     fd_ethtool_ioctl_fini( &ioc );
     949           0 :     if( 0!=err ) {
     950           0 :       FD_LOG_ERR(( "Clearing ntuple rules on PF %s failed (%i-%s)", members[i], err, fd_io_strerror( err ) ));
     951           0 :     }
     952           0 :     changed |= 0!=count.rule_cnt;
     953             : 
     954             :     /* sriov_numvfs=0 removes every VF on the PF. Reread the num and check
     955             :        VFIO group descriptors again, and only write zero for a single-VF PF */
     956           0 :     if( -1==iavf_pf_sriov_numvfs_get( members[i], &numvfs ) ) {
     957           0 :       FD_LOG_ERR(( "Reading the enabled VF count for PF %s failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     958           0 :     }
     959           0 :     if( numvfs==1U ) {
     960           0 :       char vf_pci[ FD_IAVF_PCI_ADDR_SZ ];
     961           0 :       if( -1==fd_iavf_get_last_vf( members[i], NULL, vf_pci, 0 ) ) {
     962           0 :         FD_LOG_ERR(( "Finding the highest-index VF on PF %s failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     963           0 :       }
     964           0 :       if( -1==iavf_vf_vfio_group_idle_check( vf_pci ) ) {
     965           0 :         FD_LOG_ERR(( "VF %s on PF %s is unavailable for removal (%i-%s)", vf_pci, members[i], errno, fd_io_strerror( errno ) ));
     966           0 :       }
     967           0 :       if( -1==iavf_pf_sriov_numvfs_set( members[i], 0U ) ) {
     968           0 :         FD_LOG_ERR(( "Removing VF 0 from PF %s failed (%i-%s)", members[i], errno, fd_io_strerror( errno ) ));
     969           0 :       }
     970           0 :       changed = 1;
     971           0 :     }
     972           0 :   }
     973           0 :   return changed;
     974           0 : }
     975             : 
     976             : static configure_result_t
     977             : check( fd_config_t const * config,
     978           0 :        int                 check_type ) {
     979           0 :   char members[ FD_IAVF_MEMBER_MAX ][ IFNAMSIZ ];
     980           0 :   ulong member_cnt;
     981           0 :   if( -1==fd_iavf_member_interfaces( config->net.interface, members, &member_cnt ) ) {
     982           0 :     PARTIALLY_CONFIGURED( "Discovering PFs for interface %s failed (%i-%s)", config->net.interface, errno, fd_io_strerror( errno ) );
     983           0 :   }
     984           0 :   ulong missing_cnt = 0UL;
     985           0 :   configure_result_t first = { .result=CONFIGURE_OK };
     986           0 :   for( ulong i=0UL; i<member_cnt; i++ ) {
     987           0 :     configure_result_t result = iavf_pf_config_check( config, members[i], check_type );
     988           0 :     missing_cnt += (ulong)(result.result==CONFIGURE_NOT_CONFIGURED);
     989           0 :     if( first.result==CONFIGURE_OK && result.result!=CONFIGURE_OK ) first = result;
     990           0 :   }
     991           0 :   if( check_type!=FD_CONFIGURE_CHECK_TYPE_FINI_PERM &&
     992           0 :       check_type!=FD_CONFIGURE_CHECK_TYPE_PRE_FINI  &&
     993           0 :       check_type!=FD_CONFIGURE_CHECK_TYPE_POST_FINI ) {
     994           0 :     if( !fd_iavf_vfio_avail() ) NOT_CONFIGURED( "vfio-pci driver unavailable" );
     995           0 :     char const * diagnostic;
     996           0 :     if( -1==fd_iavf_vfio_api_check( &diagnostic ) ) {
     997           0 :       NOT_CONFIGURED( "%s (%i-%s)", diagnostic, errno, fd_io_strerror( errno ) );
     998           0 :     }
     999           0 :   }
    1000           0 :   if( first.result==CONFIGURE_OK ) CONFIGURE_OK();
    1001             :   /* configure_stage runs fini before init for PARTIALLY_CONFIGURED results.
    1002             :      Return NOT_CONFIGURED so init can configure VF settings and
    1003             :      PF ntuple rules without deleting and recreating VFs */
    1004           0 :   if( check_type==FD_CONFIGURE_CHECK_TYPE_PRE_INIT ) {
    1005           0 :     NOT_CONFIGURED( "%s", first.message );
    1006           0 :   }
    1007           0 :   if( missing_cnt==member_cnt || first.result==CONFIGURE_PARTIALLY_CONFIGURED ) return first;
    1008           0 :   PARTIALLY_CONFIGURED( "IAVF PFs are not all configured" );
    1009           0 : }
    1010             : 
    1011             : configure_stage_t fd_cfg_stage_iavf = {
    1012             :   .name      = NAME,
    1013             :   .enabled   = enabled,
    1014             :   .init_perm = init_perm,
    1015             :   .fini_perm = fini_perm,
    1016             :   .init      = init,
    1017             :   .fini      = fini,
    1018             :   .check     = check,
    1019             : };
    1020             : 
    1021             : #undef NAME

Generated by: LCOV version 1.14