@@ -19,6 +19,7 @@ package sriov
19
19
20
20
import (
21
21
"fmt"
22
+ "net"
22
23
"os"
23
24
"path/filepath"
24
25
@@ -32,7 +33,8 @@ import (
32
33
33
34
var (
34
35
// SysBusPci is sysfs pci device directory
35
- SysBusPci = "/sys/bus/pci/devices"
36
+ SysBusPci = "/sys/bus/pci/devices"
37
+ UserspaceDrivers = []string {"vfio-pci" , "uio_pci_generic" , "igb_uio" }
36
38
)
37
39
38
40
// GetVFLinkName retrives interface name for given pci address
@@ -66,6 +68,27 @@ func IsOvsHardwareOffloadEnabled(deviceID string) bool {
66
68
return deviceID != ""
67
69
}
68
70
71
+ // HasUserspaceDriver checks if a device is attached to userspace driver
72
+ // This method is copied from https://github.com/k8snetworkplumbingwg/sriov-cni/blob/8af83a33b2cac8e2df0bd6276b76658eb7c790ab/pkg/utils/utils.go#L222
73
+ func HasUserspaceDriver (pciAddr string ) (bool , error ) {
74
+ driverLink := filepath .Join (SysBusPci , pciAddr , "driver" )
75
+ driverPath , err := filepath .EvalSymlinks (driverLink )
76
+ if err != nil {
77
+ return false , err
78
+ }
79
+ driverStat , err := os .Stat (driverPath )
80
+ if err != nil {
81
+ return false , err
82
+ }
83
+ driverName := driverStat .Name ()
84
+ for _ , drv := range UserspaceDrivers {
85
+ if driverName == drv {
86
+ return true , nil
87
+ }
88
+ }
89
+ return false , nil
90
+ }
91
+
69
92
// GetBridgeUplinkNameByDeviceID tries to automatically resolve uplink interface name
70
93
// for provided VF deviceID by following the sequence:
71
94
// VF pci address > PF pci address > Bond (optional, if PF is part of a bond)
@@ -159,48 +182,33 @@ func GetNetRepresentor(deviceID string) (string, error) {
159
182
return rep , nil
160
183
}
161
184
162
- // SetupSriovInterface moves smartVF into container namespace, rename it with ifName and also returns host interface with VF's representor device
163
- func SetupSriovInterface (contNetns ns.NetNS , containerID , ifName string , mtu int , deviceID string ) (* current.Interface , * current.Interface , error ) {
164
- hostIface := & current.Interface {}
165
- contIface := & current.Interface {}
166
-
185
+ // setupKernelSriovContIface moves smartVF into container namespace,
186
+ // configures the smartVF and also fills in the contIface fields
187
+ func setupKernelSriovContIface (contNetns ns.NetNS , contIface * current.Interface , deviceID string , pfLink netlink.Link , vfIdx int , ifName string , hwaddr net.HardwareAddr , mtu int ) error {
167
188
// get smart VF netdevice from PCI
168
189
vfNetdevices , err := sriovnet .GetNetDevicesFromPci (deviceID )
169
190
if err != nil {
170
- return nil , nil , err
191
+ return err
171
192
}
172
193
173
194
// Make sure we have 1 netdevice per pci address
174
195
if len (vfNetdevices ) != 1 {
175
- return nil , nil , fmt .Errorf ("failed to get one netdevice interface per %s" , deviceID )
196
+ return fmt .Errorf ("failed to get one netdevice interface per %s" , deviceID )
176
197
}
177
198
vfNetdevice := vfNetdevices [0 ]
178
199
179
- // network representor device for smartvf
180
- rep , err := GetNetRepresentor (deviceID )
181
- if err != nil {
182
- return nil , nil , err
183
- }
184
-
185
- hostIface .Name = rep
186
-
187
- link , err := netlink .LinkByName (hostIface .Name )
188
- if err != nil {
189
- return nil , nil , err
190
- }
191
- hostIface .Mac = link .Attrs ().HardwareAddr .String ()
192
-
193
- // set MTU on smart VF representor
194
- if mtu != 0 {
195
- if err = netlink .LinkSetMTU (link , mtu ); err != nil {
196
- return nil , nil , fmt .Errorf ("failed to set MTU on %s: %v" , hostIface .Name , err )
200
+ // if MAC address is provided, set it to the VF by using PF netlink
201
+ // which is accessible in the host namespace, not in the container namespace
202
+ if hwaddr != nil {
203
+ if err := netlink .LinkSetVfHardwareAddr (pfLink , vfIdx , hwaddr ); err != nil {
204
+ return err
197
205
}
198
206
}
199
207
200
208
// Move smart VF to Container namespace
201
209
err = moveIfToNetns (vfNetdevice , contNetns )
202
210
if err != nil {
203
- return nil , nil , err
211
+ return err
204
212
}
205
213
206
214
err = contNetns .Do (func (hostNS ns.NetNS ) error {
@@ -209,10 +217,20 @@ func SetupSriovInterface(contNetns ns.NetNS, containerID, ifName string, mtu int
209
217
if err != nil {
210
218
return err
211
219
}
212
- link , err = netlink .LinkByName (contIface .Name )
220
+ link , err : = netlink .LinkByName (contIface .Name )
213
221
if err != nil {
214
222
return err
215
223
}
224
+ // if MAC address is provided, set it to the kernel VF netdevice
225
+ // otherwise, read the MAC address from the kernel VF netdevice
226
+ if hwaddr != nil {
227
+ if err = netlink .LinkSetHardwareAddr (link , hwaddr ); err != nil {
228
+ return err
229
+ }
230
+ contIface .Mac = hwaddr .String ()
231
+ } else {
232
+ contIface .Mac = link .Attrs ().HardwareAddr .String ()
233
+ }
216
234
if mtu != 0 {
217
235
if err = netlink .LinkSetMTU (link , mtu ); err != nil {
218
236
return err
@@ -223,13 +241,101 @@ func SetupSriovInterface(contNetns ns.NetNS, containerID, ifName string, mtu int
223
241
return err
224
242
}
225
243
contIface .Sandbox = contNetns .Path ()
226
- contIface .Mac = link .Attrs ().HardwareAddr .String ()
227
244
228
245
return nil
229
246
})
247
+ if err != nil {
248
+ return err
249
+ }
250
+
251
+ return nil
252
+ }
253
+
254
+ // setupUserspaceSriovContIface configures smartVF via PF netlink and fills in the contIface fields
255
+ func setupUserspaceSriovContIface (contNetns ns.NetNS , contIface * current.Interface , pfLink netlink.Link , vfIdx int , ifName string , hwaddr net.HardwareAddr ) error {
256
+ contIface .Name = ifName
257
+ contIface .Sandbox = contNetns .Path ()
258
+
259
+ // if MAC address is provided, set it to the VF by using PF netlink
260
+ if hwaddr != nil {
261
+ if err := netlink .LinkSetVfHardwareAddr (pfLink , vfIdx , hwaddr ); err != nil {
262
+ return err
263
+ }
264
+ contIface .Mac = hwaddr .String ()
265
+ } else {
266
+ vfInfo := pfLink .Attrs ().Vfs [vfIdx ]
267
+ contIface .Mac = vfInfo .Mac .String ()
268
+ }
269
+
270
+ return nil
271
+ }
272
+
273
+ // SetupSriovInterface configures smartVF and returns VF's representor device as host interface and VF's netdevice as container interface
274
+ func SetupSriovInterface (contNetns ns.NetNS , containerID , ifName , mac string , mtu int , deviceID string , userspaceMode bool ) (* current.Interface , * current.Interface , error ) {
275
+ hostIface := & current.Interface {}
276
+ contIface := & current.Interface {}
277
+
278
+ // network representor device for smartvf
279
+ rep , err := GetNetRepresentor (deviceID )
280
+ if err != nil {
281
+ return nil , nil , err
282
+ }
283
+
284
+ hostIface .Name = rep
285
+
286
+ link , err := netlink .LinkByName (hostIface .Name )
287
+ if err != nil {
288
+ return nil , nil , err
289
+ }
290
+ hostIface .Mac = link .Attrs ().HardwareAddr .String ()
291
+
292
+ // get PF netlink and VF index from PCI address
293
+ pfIface , err := sriovnet .GetUplinkRepresentor (deviceID )
230
294
if err != nil {
231
295
return nil , nil , err
232
296
}
297
+ pfLink , err := netlink .LinkByName (pfIface )
298
+ if err != nil {
299
+ return nil , nil , err
300
+ }
301
+ vfIdx , err := sriovnet .GetVfIndexByPciAddress (deviceID )
302
+ if err != nil {
303
+ return nil , nil , err
304
+ }
305
+
306
+ // make sure PF netlink and VF index are valid
307
+ if len (pfLink .Attrs ().Vfs ) < vfIdx || pfLink .Attrs ().Vfs [vfIdx ].ID != vfIdx {
308
+ return nil , nil , fmt .Errorf ("failed to get vf info from %s at index %d with Vfs %v" , pfIface , vfIdx , pfLink .Attrs ().Vfs )
309
+ }
310
+
311
+ // parse MAC address if provided from args as described
312
+ // in the CNI spec (https://github.com/containernetworking/cni/blob/main/CONVENTIONS.md)
313
+ var hwaddr net.HardwareAddr
314
+ if mac != "" {
315
+ hwaddr , err = net .ParseMAC (mac )
316
+ if err != nil {
317
+ return nil , nil , fmt .Errorf ("failed to parse MAC address %q: %v" , mac , err )
318
+ }
319
+ }
320
+
321
+ // set MTU on smart VF representor
322
+ if mtu != 0 {
323
+ if err = netlink .LinkSetMTU (link , mtu ); err != nil {
324
+ return nil , nil , fmt .Errorf ("failed to set MTU on %s: %v" , hostIface .Name , err )
325
+ }
326
+ }
327
+
328
+ if ! userspaceMode {
329
+ // configure the smart VF netdevice directly in the container namespace
330
+ if err = setupKernelSriovContIface (contNetns , contIface , deviceID , pfLink , vfIdx , ifName , hwaddr , mtu ); err != nil {
331
+ return nil , nil , err
332
+ }
333
+ } else {
334
+ // configure the smart VF netdevice via PF netlink
335
+ if err = setupUserspaceSriovContIface (contNetns , contIface , pfLink , vfIdx , ifName , hwaddr ); err != nil {
336
+ return nil , nil , err
337
+ }
338
+ }
233
339
234
340
return hostIface , contIface , nil
235
341
}
0 commit comments