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, ¤t ) ) {
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, ¤t ) ) {
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, ¤t ) ) { err = errno; break; }
736 0 : valid = iavf_pf_ntuple_rule_equal( &rule, ¤t );
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
|