• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 19855289298

02 Dec 2025 10:24AM UTC coverage: 62.126% (-0.02%) from 62.149%
19855289298

Pull #967

github

web-flow
Merge e77928524 into d34e85b1c
Pull Request #967: Add support for network interface alternative names

70 of 91 new or added lines in 7 files covered. (76.92%)

21 existing lines in 6 files now uncovered.

8820 of 14197 relevant lines covered (62.13%)

0.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.76
/pkg/host/internal/sriov/sriov.go
1
package sriov
2

3
import (
4
        "errors"
5
        "fmt"
6
        "os"
7
        "path/filepath"
8
        "strconv"
9
        "strings"
10
        "syscall"
11
        "time"
12

13
        "github.com/jaypipes/ghw"
14
        "github.com/vishvananda/netlink"
15
        "k8s.io/apimachinery/pkg/util/wait"
16
        "sigs.k8s.io/controller-runtime/pkg/log"
17

18
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
19
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
20
        dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils"
21
        ghwPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/ghw"
22
        netlinkPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
23
        sriovnetPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/sriovnet"
24
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/store"
25
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
26
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
27
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
28
)
29

30
type interfaceToConfigure struct {
31
        iface       sriovnetworkv1.Interface
32
        ifaceStatus sriovnetworkv1.InterfaceExt
33
}
34

35
type sriov struct {
36
        utilsHelper      utils.CmdInterface
37
        kernelHelper     types.KernelInterface
38
        networkHelper    types.NetworkInterface
39
        udevHelper       types.UdevInterface
40
        vdpaHelper       types.VdpaInterface
41
        infinibandHelper types.InfinibandInterface
42
        netlinkLib       netlinkPkg.NetlinkLib
43
        dputilsLib       dputilsPkg.DPUtilsLib
44
        sriovnetLib      sriovnetPkg.SriovnetLib
45
        ghwLib           ghwPkg.GHWLib
46
        bridgeHelper     types.BridgeInterface
47
}
48

49
func New(utilsHelper utils.CmdInterface,
50
        kernelHelper types.KernelInterface,
51
        networkHelper types.NetworkInterface,
52
        udevHelper types.UdevInterface,
53
        vdpaHelper types.VdpaInterface,
54
        infinibandHelper types.InfinibandInterface,
55
        netlinkLib netlinkPkg.NetlinkLib,
56
        dputilsLib dputilsPkg.DPUtilsLib,
57
        sriovnetLib sriovnetPkg.SriovnetLib,
58
        ghwLib ghwPkg.GHWLib,
59
        bridgeHelper types.BridgeInterface) types.SriovInterface {
1✔
60
        return &sriov{utilsHelper: utilsHelper,
1✔
61
                kernelHelper:     kernelHelper,
1✔
62
                networkHelper:    networkHelper,
1✔
63
                udevHelper:       udevHelper,
1✔
64
                vdpaHelper:       vdpaHelper,
1✔
65
                infinibandHelper: infinibandHelper,
1✔
66
                netlinkLib:       netlinkLib,
1✔
67
                dputilsLib:       dputilsLib,
1✔
68
                sriovnetLib:      sriovnetLib,
1✔
69
                ghwLib:           ghwLib,
1✔
70
                bridgeHelper:     bridgeHelper,
1✔
71
        }
1✔
72
}
1✔
73

74
func (s *sriov) SetSriovNumVfs(pciAddr string, numVfs int) error {
1✔
75
        log.Log.V(2).Info("SetSriovNumVfs(): set NumVfs", "device", pciAddr, "numVfs", numVfs)
1✔
76
        numVfsFilePath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, consts.NumVfsFile)
1✔
77
        bs := []byte(strconv.Itoa(numVfs))
1✔
78
        err := os.WriteFile(numVfsFilePath, []byte("0"), os.ModeAppend)
1✔
79
        if err != nil {
2✔
80
                log.Log.Error(err, "SetSriovNumVfs(): fail to reset NumVfs file", "path", numVfsFilePath)
1✔
81
                return err
1✔
82
        }
1✔
83
        if numVfs == 0 {
2✔
84
                return nil
1✔
85
        }
1✔
86
        err = os.WriteFile(numVfsFilePath, bs, os.ModeAppend)
1✔
87
        if err != nil {
1✔
88
                log.Log.Error(err, "SetSriovNumVfs(): fail to set NumVfs file", "path", numVfsFilePath)
×
89
                return err
×
90
        }
×
91
        return nil
1✔
92
}
93

94
func (s *sriov) ResetSriovDevice(ifaceStatus sriovnetworkv1.InterfaceExt) error {
1✔
95
        log.Log.V(2).Info("ResetSriovDevice(): reset SRIOV device", "address", ifaceStatus.PciAddress)
1✔
96
        if ifaceStatus.LinkType == consts.LinkTypeETH {
2✔
97
                var mtu int
1✔
98
                eswitchMode := sriovnetworkv1.ESwithModeLegacy
1✔
99
                is := sriovnetworkv1.InitialState.GetInterfaceStateByPciAddress(ifaceStatus.PciAddress)
1✔
100
                if is != nil {
1✔
101
                        mtu = is.Mtu
×
102
                        eswitchMode = sriovnetworkv1.GetEswitchModeFromStatus(is)
×
103
                } else {
1✔
104
                        mtu = 1500
1✔
105
                }
1✔
106
                log.Log.V(2).Info("ResetSriovDevice(): reset mtu", "value", mtu)
1✔
107
                if err := s.networkHelper.SetNetdevMTU(ifaceStatus.PciAddress, mtu); err != nil {
1✔
108
                        return err
×
109
                }
×
110
                log.Log.V(2).Info("ResetSriovDevice(): reset eswitch mode and number of VFs", "mode", eswitchMode)
1✔
111
                if err := s.setEswitchModeAndNumVFs(ifaceStatus.PciAddress, eswitchMode, 0); err != nil {
1✔
112
                        return err
×
113
                }
×
114
        } else if ifaceStatus.LinkType == consts.LinkTypeIB {
×
115
                if err := s.SetSriovNumVfs(ifaceStatus.PciAddress, 0); err != nil {
×
116
                        return err
×
117
                }
×
118
                if err := s.networkHelper.SetNetdevMTU(ifaceStatus.PciAddress, 2048); err != nil {
×
119
                        return err
×
120
                }
×
121
        }
122
        return nil
1✔
123
}
124

125
func (s *sriov) getVfInfo(vfAddr string, pfName string, eswitchMode string, devices []*ghw.PCIDevice) sriovnetworkv1.VirtualFunction {
1✔
126
        driver, err := s.dputilsLib.GetDriverName(vfAddr)
1✔
127
        if err != nil {
1✔
128
                log.Log.Error(err, "getVfInfo(): unable to parse device driver", "device", vfAddr)
×
129
        }
×
130
        id, err := s.dputilsLib.GetVFID(vfAddr)
1✔
131
        if err != nil {
1✔
132
                log.Log.Error(err, "getVfInfo(): unable to get VF index", "device", vfAddr)
×
133
        }
×
134
        vf := sriovnetworkv1.VirtualFunction{
1✔
135
                PciAddress: vfAddr,
1✔
136
                Driver:     driver,
1✔
137
                VfID:       id,
1✔
138
                VdpaType:   s.vdpaHelper.DiscoverVDPAType(vfAddr),
1✔
139
        }
1✔
140

1✔
141
        if eswitchMode == sriovnetworkv1.ESwithModeSwitchDev {
2✔
142
                repName, err := s.sriovnetLib.GetVfRepresentor(pfName, id)
1✔
143
                if err != nil {
1✔
144
                        log.Log.Error(err, "getVfInfo(): failed to get VF representor name", "device", vfAddr)
×
145
                } else {
1✔
146
                        vf.RepresentorName = repName
1✔
147
                }
1✔
148
        }
149

150
        if name := s.networkHelper.TryGetInterfaceName(vfAddr); name != "" {
2✔
151
                link, err := s.netlinkLib.LinkByName(name)
1✔
152
                if err != nil {
1✔
153
                        log.Log.Error(err, "getVfInfo(): unable to get VF Link Object", "name", name, "device", vfAddr)
×
154
                } else {
1✔
155
                        vf.Name = name
1✔
156
                        vf.Mtu = link.Attrs().MTU
1✔
157
                        vf.Mac = link.Attrs().HardwareAddr.String()
1✔
158
                }
1✔
159
        }
160
        vf.GUID = s.networkHelper.GetNetDevNodeGUID(vfAddr)
1✔
161

1✔
162
        for _, device := range devices {
2✔
163
                if vfAddr == device.Address {
2✔
164
                        vf.Vendor = device.Vendor.ID
1✔
165
                        vf.DeviceID = device.Product.ID
1✔
166
                        break
1✔
167
                }
168
        }
169
        return vf
1✔
170
}
171

172
func (s *sriov) VFIsReady(pciAddr string) (netlink.Link, error) {
1✔
173
        log.Log.Info("VFIsReady()", "device", pciAddr)
1✔
174
        var err error
1✔
175
        var vfLink netlink.Link
1✔
176
        err = wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) {
2✔
177
                vfIndex, err := s.networkHelper.GetInterfaceIndex(pciAddr)
1✔
178
                if err != nil {
2✔
179
                        log.Log.Error(err, "VFIsReady(): invalid index number", "device", pciAddr)
1✔
180
                        return false, nil
1✔
181
                }
1✔
182
                vfLink, err = s.netlinkLib.LinkByIndex(vfIndex)
1✔
183
                if err != nil {
1✔
184
                        log.Log.Error(err, "VFIsReady(): unable to get VF link", "device", pciAddr)
×
185
                        return false, nil
×
186
                }
×
187
                return true, nil
1✔
188
        })
189
        if err != nil {
1✔
190
                return vfLink, err
×
191
        }
×
192
        return vfLink, nil
1✔
193
}
194

195
func (s *sriov) SetVfAdminMac(vfAddr string, pfLink, vfLink netlink.Link) error {
1✔
196
        log.Log.Info("SetVfAdminMac()", "vf", vfAddr)
1✔
197

1✔
198
        vfID, err := s.dputilsLib.GetVFID(vfAddr)
1✔
199
        if err != nil {
1✔
200
                log.Log.Error(err, "SetVfAdminMac(): unable to get VF id", "address", vfAddr)
×
201
                return err
×
202
        }
×
203

204
        if err := s.netlinkLib.LinkSetVfHardwareAddr(pfLink, vfID, vfLink.Attrs().HardwareAddr); err != nil {
1✔
205
                return err
×
206
        }
×
207

208
        return nil
1✔
209
}
210

211
func (s *sriov) DiscoverSriovDevices(storeManager store.ManagerInterface) ([]sriovnetworkv1.InterfaceExt, error) {
1✔
212
        log.Log.V(2).Info("DiscoverSriovDevices")
1✔
213
        pfList := []sriovnetworkv1.InterfaceExt{}
1✔
214

1✔
215
        pci, err := s.ghwLib.PCI()
1✔
216
        if err != nil {
1✔
217
                return nil, fmt.Errorf("DiscoverSriovDevices(): error getting PCI info: %v", err)
×
218
        }
×
219

220
        devices := pci.Devices
1✔
221
        if len(devices) == 0 {
1✔
222
                return nil, fmt.Errorf("DiscoverSriovDevices(): could not retrieve PCI devices")
×
223
        }
×
224

225
        for _, device := range devices {
2✔
226
                devClass, err := strconv.ParseInt(device.Class.ID, 16, 64)
1✔
227
                if err != nil {
1✔
228
                        log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device class, skipping",
×
229
                                "device", device)
×
230
                        continue
×
231
                }
232
                if devClass != consts.NetClass {
2✔
233
                        // Not network device
1✔
234
                        continue
1✔
235
                }
236

237
                // TODO: exclude devices used by host system
238

239
                if s.dputilsLib.IsSriovVF(device.Address) {
2✔
240
                        continue
1✔
241
                }
242

243
                if !vars.DevMode {
2✔
244
                        if !sriovnetworkv1.IsSupportedModel(device.Vendor.ID, device.Product.ID) {
2✔
245
                                log.Log.Info("DiscoverSriovDevices(): unsupported device", "device", device)
1✔
246
                                continue
1✔
247
                        }
248
                }
249

250
                driver, err := s.dputilsLib.GetDriverName(device.Address)
1✔
251
                if err != nil {
1✔
252
                        log.Log.Error(err, "DiscoverSriovDevices(): unable to parse device driver for device, skipping", "device", device)
×
253
                        continue
×
254
                }
255

256
                pfNetName := s.networkHelper.TryGetInterfaceName(device.Address)
1✔
257

1✔
258
                if pfNetName == "" {
1✔
259
                        log.Log.Error(err, "DiscoverSriovDevices(): unable to get device name for device, skipping", "device", device.Address)
×
260
                        continue
×
261
                }
262

263
                altNames, err := s.netlinkLib.GetAltNames(pfNetName)
1✔
264
                if err != nil {
1✔
NEW
265
                        log.Log.Error(err, "DiscoverSriovDevices(): unable to get alternative names for device, skipping", "device", device)
×
NEW
266
                        continue
×
267
                }
268

269
                link, err := s.netlinkLib.LinkByName(pfNetName)
1✔
270
                if err != nil {
1✔
271
                        log.Log.Error(err, "DiscoverSriovDevices(): unable to get Link for device, skipping", "device", device.Address)
×
272
                        continue
×
273
                }
274

275
                iface := sriovnetworkv1.InterfaceExt{
1✔
276
                        Name:           pfNetName,
1✔
277
                        PciAddress:     device.Address,
1✔
278
                        Driver:         driver,
1✔
279
                        Vendor:         device.Vendor.ID,
1✔
280
                        DeviceID:       device.Product.ID,
1✔
281
                        Mtu:            link.Attrs().MTU,
1✔
282
                        Mac:            link.Attrs().HardwareAddr.String(),
1✔
283
                        LinkType:       s.encapTypeToLinkType(link.Attrs().EncapType),
1✔
284
                        LinkSpeed:      s.networkHelper.GetNetDevLinkSpeed(pfNetName),
1✔
285
                        LinkAdminState: s.networkHelper.GetNetDevLinkAdminState(pfNetName),
1✔
286
                        AltNames:       altNames,
1✔
287
                }
1✔
288

1✔
289
                pfStatus, exist, err := storeManager.LoadPfsStatus(iface.PciAddress)
1✔
290
                if err != nil {
1✔
291
                        log.Log.Error(err, "DiscoverSriovDevices(): failed to load PF status from disk")
×
292
                } else {
1✔
293
                        if exist {
1✔
294
                                iface.ExternallyManaged = pfStatus.ExternallyManaged
×
295
                        }
×
296
                }
297

298
                if s.dputilsLib.IsSriovPF(device.Address) {
2✔
299
                        iface.TotalVfs = s.dputilsLib.GetSriovVFcapacity(device.Address)
1✔
300
                        iface.NumVfs = s.dputilsLib.GetVFconfigured(device.Address)
1✔
301
                        iface.EswitchMode = s.GetNicSriovMode(device.Address)
1✔
302
                        if s.dputilsLib.SriovConfigured(device.Address) {
2✔
303
                                vfs, err := s.dputilsLib.GetVFList(device.Address)
1✔
304
                                if err != nil {
1✔
305
                                        log.Log.Error(err, "DiscoverSriovDevices(): unable to parse VFs for device, skipping",
×
306
                                                "device", device)
×
307
                                        continue
×
308
                                }
309
                                for _, vf := range vfs {
2✔
310
                                        instance := s.getVfInfo(vf, pfNetName, iface.EswitchMode, devices)
1✔
311
                                        iface.VFs = append(iface.VFs, instance)
1✔
312
                                }
1✔
313
                        }
314
                }
315
                pfList = append(pfList, iface)
1✔
316
        }
317

318
        return pfList, nil
1✔
319
}
320

321
func (s *sriov) configSriovPFDevice(iface *sriovnetworkv1.Interface) error {
1✔
322
        log.Log.V(2).Info("configSriovPFDevice(): configure PF sriov device",
1✔
323
                "device", iface.PciAddress)
1✔
324
        totalVfs := s.dputilsLib.GetSriovVFcapacity(iface.PciAddress)
1✔
325
        if iface.NumVfs > totalVfs {
1✔
326
                err := fmt.Errorf("cannot config SRIOV device: NumVfs (%d) is larger than TotalVfs (%d)", iface.NumVfs, totalVfs)
×
327
                log.Log.Error(err, "configSriovPFDevice(): fail to set NumVfs for device", "device", iface.PciAddress)
×
328
                return err
×
329
        }
×
330
        if err := s.configureHWOptionsForSwitchdev(iface); err != nil {
1✔
331
                return err
×
332
        }
×
333
        // remove all UDEV rules for the PF before adding new rules to
334
        // make sure that rules are always in a consistent state, e.g. there is no
335
        // switchdev-related rules for PF in legacy mode
336
        if err := s.removeUdevRules(iface.PciAddress); err != nil {
1✔
337
                log.Log.Error(err, "configSriovPFDevice(): fail to remove udev rules", "device", iface.PciAddress)
×
338
                return err
×
339
        }
×
340
        err := s.addUdevRules(iface)
1✔
341
        if err != nil {
1✔
342
                log.Log.Error(err, "configSriovPFDevice(): fail to add udev rules", "device", iface.PciAddress)
×
343
                return err
×
344
        }
×
345
        err = s.createVFs(iface)
1✔
346
        if err != nil {
1✔
347
                log.Log.Error(err, "configSriovPFDevice(): fail to set NumVfs for device", "device", iface.PciAddress)
×
348
                return err
×
349
        }
×
350
        if err := s.addVfRepresentorUdevRule(iface); err != nil {
1✔
351
                log.Log.Error(err, "configSriovPFDevice(): fail to add VR representor udev rule", "device", iface.PciAddress)
×
352
                return err
×
353
        }
×
354
        // set PF mtu
355
        if iface.Mtu > 0 && iface.Mtu > s.networkHelper.GetNetdevMTU(iface.PciAddress) {
1✔
356
                err = s.networkHelper.SetNetdevMTU(iface.PciAddress, iface.Mtu)
×
357
                if err != nil {
×
358
                        log.Log.Error(err, "configSriovPFDevice(): fail to set mtu for PF", "device", iface.PciAddress)
×
359
                        return err
×
360
                }
×
361
        }
362
        return nil
1✔
363
}
364

365
func (s *sriov) configureHWOptionsForSwitchdev(iface *sriovnetworkv1.Interface) error {
1✔
366
        log.Log.V(2).Info("configureHWOptionsForSwitchdev(): configure HW options for device",
1✔
367
                "device", iface.PciAddress)
1✔
368
        if sriovnetworkv1.GetEswitchModeFromSpec(iface) != sriovnetworkv1.ESwithModeSwitchDev {
2✔
369
                // we need to configure HW options only for PFs for which switchdev is a target mode
1✔
370
                return nil
1✔
371
        }
1✔
372
        if err := s.networkHelper.EnableHwTcOffload(iface.Name); err != nil {
1✔
373
                return err
×
374
        }
×
375
        desiredFlowSteeringMode := "smfs"
1✔
376
        currentFlowSteeringMode, err := s.networkHelper.GetDevlinkDeviceParam(iface.PciAddress, "flow_steering_mode")
1✔
377
        if err != nil {
2✔
378
                if errors.Is(err, syscall.EINVAL) || errors.Is(err, syscall.ENODEV) {
2✔
379
                        log.Log.V(2).Info("configureHWOptionsForSwitchdev(): device has no flow_steering_mode parameter, skip",
1✔
380
                                "device", iface.PciAddress)
1✔
381
                        return nil
1✔
382
                }
1✔
383
                log.Log.Error(err, "configureHWOptionsForSwitchdev(): fail to read current flow steering mode for the device", "device", iface.PciAddress)
×
384
                return err
×
385
        }
386
        if currentFlowSteeringMode == "" {
2✔
387
                log.Log.V(2).Info("configureHWOptionsForSwitchdev(): can't detect current flow_steering_mode mode for the device, skip",
1✔
388
                        "device", iface.PciAddress)
1✔
389
                return nil
1✔
390
        }
1✔
391
        if currentFlowSteeringMode == desiredFlowSteeringMode {
2✔
392
                return nil
1✔
393
        }
1✔
394
        // flow steering mode can be changed only when NIC is in legacy mode
395
        if s.GetNicSriovMode(iface.PciAddress) != sriovnetworkv1.ESwithModeLegacy {
2✔
396
                err = s.setEswitchModeAndNumVFs(iface.PciAddress, sriovnetworkv1.ESwithModeLegacy, 0)
1✔
397
                if err != nil {
1✔
398
                        log.Log.Error(err, "falied to switch Eswitch mode to legacy and reset number of vfs to 0")
×
399
                        return err
×
400
                }
×
401
        }
402
        if err := s.networkHelper.SetDevlinkDeviceParam(iface.PciAddress, "flow_steering_mode", desiredFlowSteeringMode); err != nil {
1✔
403
                if errors.Is(err, syscall.ENOTSUP) {
×
404
                        log.Log.V(2).Info("configureHWOptionsForSwitchdev(): device doesn't support changing of flow_steering_mode, skip", "device", iface.PciAddress)
×
405
                        return nil
×
406
                }
×
407
                log.Log.Error(err, "configureHWOptionsForSwitchdev(): fail to configure flow steering mode for the device", "device", iface.PciAddress)
×
408
                return err
×
409
        }
410
        return nil
1✔
411
}
412

413
func (s *sriov) checkExternallyManagedPF(iface *sriovnetworkv1.Interface) error {
1✔
414
        log.Log.V(2).Info("checkExternallyManagedPF(): configure PF sriov device",
1✔
415
                "device", iface.PciAddress)
1✔
416
        currentNumVfs := s.dputilsLib.GetVFconfigured(iface.PciAddress)
1✔
417
        if iface.NumVfs > currentNumVfs {
2✔
418
                errMsg := fmt.Sprintf("checkExternallyManagedPF(): number of request virtual functions %d is not equal to configured virtual "+
1✔
419
                        "functions %d but the policy is configured as ExternallyManaged for device %s",
1✔
420
                        iface.NumVfs, currentNumVfs, iface.PciAddress)
1✔
421
                log.Log.Error(nil, errMsg)
1✔
422
                return fmt.Errorf(errMsg)
1✔
423
        }
1✔
424
        currentEswitchMode := s.GetNicSriovMode(iface.PciAddress)
1✔
425
        expectedEswitchMode := sriovnetworkv1.GetEswitchModeFromSpec(iface)
1✔
426
        if currentEswitchMode != expectedEswitchMode {
1✔
427
                errMsg := fmt.Sprintf("checkExternallyManagedPF(): requested ESwitchMode mode \"%s\" is not equal to configured \"%s\" "+
×
428
                        "but the policy is configured as ExternallyManaged for device %s", expectedEswitchMode, currentEswitchMode, iface.PciAddress)
×
429
                log.Log.Error(nil, errMsg)
×
430
                return fmt.Errorf(errMsg)
×
431
        }
×
432
        currentMtu := s.networkHelper.GetNetdevMTU(iface.PciAddress)
1✔
433
        if iface.Mtu > 0 && iface.Mtu > currentMtu {
2✔
434
                err := fmt.Errorf("checkExternallyManagedPF(): requested MTU(%d) is greater than configured MTU(%d) for device %s. cannot change MTU as policy is configured as ExternallyManaged",
1✔
435
                        iface.Mtu, currentMtu, iface.PciAddress)
1✔
436
                log.Log.Error(nil, err.Error())
1✔
437
                return err
1✔
438
        }
1✔
439
        return nil
×
440
}
441

442
func (s *sriov) configSriovVFDevices(iface *sriovnetworkv1.Interface) error {
1✔
443
        log.Log.V(2).Info("configSriovVFDevices(): configure PF sriov device",
1✔
444
                "device", iface.PciAddress)
1✔
445
        if iface.NumVfs > 0 {
2✔
446
                vfAddrs, err := s.dputilsLib.GetVFList(iface.PciAddress)
1✔
447
                if err != nil {
1✔
448
                        log.Log.Error(err, "configSriovVFDevices(): unable to parse VFs for device", "device", iface.PciAddress)
×
449
                }
×
450
                pfLink, err := s.netlinkLib.LinkByName(iface.Name)
1✔
451
                if err != nil {
1✔
452
                        log.Log.Error(err, "configSriovVFDevices(): unable to get PF link for device", "device", iface)
×
453
                        return err
×
454
                }
×
455

456
                for _, addr := range vfAddrs {
2✔
457
                        hasDriver, _ := s.kernelHelper.HasDriver(addr)
1✔
458
                        if !hasDriver {
2✔
459
                                if err := s.kernelHelper.BindDefaultDriver(addr); err != nil {
1✔
460
                                        log.Log.Error(err, "configSriovVFDevices(): fail to bind default driver for device", "device", addr)
×
461
                                        return err
×
462
                                }
×
463
                        }
464
                        var group *sriovnetworkv1.VfGroup
1✔
465

1✔
466
                        vfID, err := s.dputilsLib.GetVFID(addr)
1✔
467
                        if err != nil {
1✔
468
                                log.Log.Error(err, "configSriovVFDevices(): unable to get VF id", "device", iface.PciAddress)
×
469
                                return err
×
470
                        }
×
471

472
                        for i := range iface.VfGroups {
2✔
473
                                if sriovnetworkv1.IndexInRange(vfID, iface.VfGroups[i].VfRange) {
2✔
474
                                        group = &iface.VfGroups[i]
1✔
475
                                        break
1✔
476
                                }
477
                        }
478

479
                        // VF group not found.
480
                        if group == nil {
1✔
481
                                continue
×
482
                        }
483

484
                        // only set GUID and MAC for VF with default driver
485
                        // for userspace drivers like vfio we configure the vf mac using the kernel nic mac address
486
                        // before we switch to the userspace driver
487
                        if yes, d := s.kernelHelper.HasDriver(addr); yes && !sriovnetworkv1.StringInArray(d, vars.DpdkDrivers) {
2✔
488
                                // LinkType is an optional field. Let's fallback to current link type
1✔
489
                                // if nothing is specified in the SriovNodePolicy
1✔
490
                                linkType := iface.LinkType
1✔
491
                                if linkType == "" {
2✔
492
                                        linkType = s.GetLinkType(iface.Name)
1✔
493
                                }
1✔
494
                                if strings.EqualFold(linkType, consts.LinkTypeIB) {
2✔
495
                                        if err := s.infinibandHelper.ConfigureVfGUID(addr, iface.PciAddress, vfID, pfLink); err != nil {
1✔
496
                                                return err
×
497
                                        }
×
498
                                        if err := s.kernelHelper.Unbind(addr); err != nil {
1✔
499
                                                return err
×
500
                                        }
×
501
                                } else {
1✔
502
                                        vfLink, err := s.VFIsReady(addr)
1✔
503
                                        if err != nil {
1✔
504
                                                log.Log.Error(err, "configSriovVFDevices(): VF link is not ready", "address", addr)
×
505
                                                err = s.kernelHelper.RebindVfToDefaultDriver(addr)
×
506
                                                if err != nil {
×
507
                                                        log.Log.Error(err, "configSriovVFDevices(): failed to rebind VF", "address", addr)
×
508
                                                        return err
×
509
                                                }
×
510

511
                                                // Try to check the VF status again
512
                                                vfLink, err = s.VFIsReady(addr)
×
513
                                                if err != nil {
×
514
                                                        log.Log.Error(err, "configSriovVFDevices(): VF link is not ready", "address", addr)
×
515
                                                        return err
×
516
                                                }
×
517
                                        }
518
                                        if err = s.SetVfAdminMac(addr, pfLink, vfLink); err != nil {
1✔
519
                                                log.Log.Error(err, "configSriovVFDevices(): fail to configure VF admin mac", "device", addr)
×
520
                                                return err
×
521
                                        }
×
522
                                }
523
                        }
524

525
                        if err = s.kernelHelper.UnbindDriverIfNeeded(addr, group.IsRdma); err != nil {
1✔
526
                                return err
×
527
                        }
×
528
                        // we set eswitch mode before this point and if the desired mode (and current at this point)
529
                        // is legacy, then VDPA device is already automatically disappeared,
530
                        // so we don't need to check it
531
                        if sriovnetworkv1.GetEswitchModeFromSpec(iface) == sriovnetworkv1.ESwithModeSwitchDev && group.VdpaType == "" {
1✔
532
                                if err := s.vdpaHelper.DeleteVDPADevice(addr); err != nil {
×
533
                                        log.Log.Error(err, "configSriovVFDevices(): fail to delete VDPA device",
×
534
                                                "device", addr)
×
535
                                        return err
×
536
                                }
×
537
                        }
538
                        if !sriovnetworkv1.StringInArray(group.DeviceType, vars.DpdkDrivers) {
2✔
539
                                if err := s.kernelHelper.BindDefaultDriver(addr); err != nil {
1✔
540
                                        log.Log.Error(err, "configSriovVFDevices(): fail to bind default driver for device", "device", addr)
×
541
                                        return err
×
542
                                }
×
543
                                // only set MTU for VF with default driver
544
                                if group.Mtu > 0 {
2✔
545
                                        if err := s.networkHelper.SetNetdevMTU(addr, group.Mtu); err != nil {
1✔
546
                                                log.Log.Error(err, "configSriovVFDevices(): fail to set mtu for VF", "address", addr)
×
547
                                                return err
×
548
                                        }
×
549
                                }
550
                                if sriovnetworkv1.GetEswitchModeFromSpec(iface) == sriovnetworkv1.ESwithModeSwitchDev && group.VdpaType != "" {
2✔
551
                                        if err := s.vdpaHelper.CreateVDPADevice(addr, group.VdpaType); err != nil {
1✔
552
                                                log.Log.Error(err, "configSriovVFDevices(): fail to create VDPA device",
×
553
                                                        "vdpaType", group.VdpaType, "device", addr)
×
554
                                                return err
×
555
                                        }
×
556
                                }
557
                        } else {
1✔
558
                                if err := s.kernelHelper.BindDpdkDriver(addr, group.DeviceType); err != nil {
1✔
559
                                        log.Log.Error(err, "configSriovVFDevices(): fail to bind driver for device",
×
560
                                                "driver", group.DeviceType, "device", addr)
×
561
                                        return err
×
562
                                }
×
563
                        }
564
                }
565
        }
566
        return nil
1✔
567
}
568

569
func (s *sriov) configSriovDevice(iface *sriovnetworkv1.Interface, skipVFConfiguration bool) error {
1✔
570
        log.Log.V(2).Info("configSriovDevice(): configure sriov device",
1✔
571
                "device", iface.PciAddress, "config", iface, "skipVFConfiguration", skipVFConfiguration)
1✔
572
        if !iface.ExternallyManaged {
2✔
573
                if err := s.configSriovPFDevice(iface); err != nil {
1✔
574
                        return err
×
575
                }
×
576
        }
577
        if skipVFConfiguration {
2✔
578
                if iface.ExternallyManaged {
1✔
579
                        return nil
×
580
                }
×
581
                log.Log.V(2).Info("configSriovDevice(): skipVFConfiguration is true, unbind all VFs from drivers",
1✔
582
                        "device", iface.PciAddress)
1✔
583
                return s.unbindAllVFsOnPF(iface.PciAddress)
1✔
584
        }
585
        // we don't need to validate externally managed PFs when skipVFConfiguration is true.
586
        // The function usually called with skipVFConfiguration true when running in the systemd mode and configuration is
587
        // in pre phase. Externally managed PFs may not be configured at this stage yet (preConfig stage is executed before NetworkManager, netplan)
588

589
        if iface.ExternallyManaged {
2✔
590
                if err := s.checkExternallyManagedPF(iface); err != nil {
2✔
591
                        return err
1✔
592
                }
1✔
593
        }
594
        if err := s.configSriovVFDevices(iface); err != nil {
1✔
595
                return err
×
596
        }
×
597
        // Set PF link up
598
        pfLink, err := s.netlinkLib.LinkByName(iface.Name)
1✔
599
        if err != nil {
1✔
600
                return err
×
601
        }
×
602
        if !s.netlinkLib.IsLinkAdminStateUp(pfLink) {
2✔
603
                err = s.netlinkLib.LinkSetUp(pfLink)
1✔
604
                if err != nil {
1✔
605
                        return err
×
606
                }
×
607
        }
608
        return nil
1✔
609
}
610

611
func (s *sriov) ConfigSriovInterfaces(storeManager store.ManagerInterface,
612
        interfaces []sriovnetworkv1.Interface, ifaceStatuses []sriovnetworkv1.InterfaceExt, skipVFConfiguration bool) error {
1✔
613
        toBeConfigured, toBeResetted, err := s.getConfigureAndReset(storeManager, interfaces, ifaceStatuses)
1✔
614
        if err != nil {
1✔
615
                log.Log.Error(err, "cannot get a list of interfaces to configure")
×
616
                return fmt.Errorf("cannot get a list of interfaces to configure")
×
617
        }
×
618

619
        if vars.ParallelNicConfig {
2✔
620
                err = s.configSriovInterfacesInParallel(storeManager, toBeConfigured, skipVFConfiguration)
1✔
621
        } else {
2✔
622
                err = s.configSriovInterfaces(storeManager, toBeConfigured, skipVFConfiguration)
1✔
623
        }
1✔
624
        if err != nil {
2✔
625
                log.Log.Error(err, "cannot configure sriov interfaces")
1✔
626
                return fmt.Errorf("cannot configure sriov interfaces")
1✔
627
        }
1✔
628
        if sriovnetworkv1.ContainsSwitchdevInterface(interfaces) && len(toBeConfigured) > 0 {
2✔
629
                // for switchdev devices we create udev rule that renames VF representors
1✔
630
                // after VFs are created. Reload rules to update interfaces
1✔
631
                if err := s.udevHelper.LoadUdevRules(); err != nil {
1✔
632
                        log.Log.Error(err, "cannot reload udev rules")
×
633
                        return fmt.Errorf("failed to reload udev rules: %v", err)
×
634
                }
×
635
        }
636

637
        if vars.ParallelNicConfig {
2✔
638
                err = s.resetSriovInterfacesInParallel(storeManager, toBeResetted)
1✔
639
        } else {
2✔
640
                err = s.resetSriovInterfaces(storeManager, toBeResetted)
1✔
641
        }
1✔
642
        if err != nil {
1✔
643
                log.Log.Error(err, "cannot reset sriov interfaces")
×
644
                return fmt.Errorf("cannot reset sriov interfaces")
×
645
        }
×
646
        return nil
1✔
647
}
648

649
func (s *sriov) getConfigureAndReset(storeManager store.ManagerInterface, interfaces []sriovnetworkv1.Interface,
650
        ifaceStatuses []sriovnetworkv1.InterfaceExt) ([]interfaceToConfigure, []sriovnetworkv1.InterfaceExt, error) {
1✔
651
        toBeConfigured := []interfaceToConfigure{}
1✔
652
        toBeResetted := []sriovnetworkv1.InterfaceExt{}
1✔
653
        for _, ifaceStatus := range ifaceStatuses {
2✔
654
                configured := false
1✔
655
                for _, iface := range interfaces {
2✔
656
                        if iface.PciAddress == ifaceStatus.PciAddress {
2✔
657
                                configured = true
1✔
658
                                skip, err := skipSriovConfig(&iface, &ifaceStatus, storeManager)
1✔
659
                                if err != nil {
1✔
660
                                        log.Log.Error(err, "getConfigureAndReset(): failed to check interface")
×
661
                                        return nil, nil, err
×
662
                                }
×
663
                                if skip {
1✔
664
                                        break
×
665
                                }
666
                                iface := iface
1✔
667
                                ifaceStatus := ifaceStatus
1✔
668
                                toBeConfigured = append(toBeConfigured, interfaceToConfigure{iface: iface, ifaceStatus: ifaceStatus})
1✔
669
                        }
670
                }
671

672
                if !configured {
2✔
673
                        toBeResetted = append(toBeResetted, ifaceStatus)
1✔
674
                }
1✔
675
        }
676
        return toBeConfigured, toBeResetted, nil
1✔
677
}
678

679
func (s *sriov) configSriovInterfacesInParallel(storeManager store.ManagerInterface, interfaces []interfaceToConfigure, skipVFConfiguration bool) error {
1✔
680
        log.Log.V(2).Info("configSriovInterfacesInParallel(): start sriov configuration")
1✔
681

1✔
682
        var result error
1✔
683
        errChannel := make(chan error)
1✔
684
        interfacesToConfigure := 0
1✔
685
        for ifaceIndex, iface := range interfaces {
2✔
686
                interfacesToConfigure += 1
1✔
687
                go func(iface *interfaceToConfigure) {
2✔
688
                        var err error
1✔
689
                        if err = s.configSriovDevice(&iface.iface, skipVFConfiguration); err != nil {
1✔
690
                                log.Log.Error(err, "configSriovInterfacesInParallel(): fail to configure sriov interface. resetting interface.", "address", iface.iface.PciAddress)
×
691
                                if iface.iface.ExternallyManaged {
×
692
                                        log.Log.V(2).Info("configSriovInterfacesInParallel(): skipping device reset as the nic is marked as externally created")
×
693
                                } else {
×
694
                                        if resetErr := s.ResetSriovDevice(iface.ifaceStatus); resetErr != nil {
×
695
                                                log.Log.Error(resetErr, "configSriovInterfacesInParallel(): failed to reset on error SR-IOV interface")
×
696
                                                err = resetErr
×
697
                                        }
×
698
                                }
699
                        }
700
                        errChannel <- err
1✔
701
                }(&interfaces[ifaceIndex])
702
                // Save the PF status to the host
703
                err := storeManager.SaveLastPfAppliedStatus(&iface.iface)
1✔
704
                if err != nil {
1✔
705
                        log.Log.Error(err, "configSriovInterfacesInParallel(): failed to save PF applied config to host")
×
706
                        return err
×
707
                }
×
708
        }
709

710
        for i := 0; i < interfacesToConfigure; i++ {
2✔
711
                errMsg := <-errChannel
1✔
712
                result = errors.Join(result, errMsg)
1✔
713
        }
1✔
714
        if result != nil {
1✔
715
                log.Log.Error(result, "configSriovInterfacesInParallel(): fail to configure sriov interfaces")
×
716
                return result
×
717
        }
×
718
        log.Log.V(2).Info("configSriovInterfacesInParallel(): sriov configuration finished")
1✔
719
        return nil
1✔
720
}
721

722
func (s *sriov) resetSriovInterfacesInParallel(storeManager store.ManagerInterface, interfaces []sriovnetworkv1.InterfaceExt) error {
1✔
723
        var result error
1✔
724
        errChannel := make(chan error, len(interfaces))
1✔
725
        interfacesToReset := 0
1✔
726
        for ifaceIndex := range interfaces {
2✔
727
                interfacesToReset += 1
1✔
728
                go func(iface *sriovnetworkv1.InterfaceExt) {
2✔
729
                        var err error
1✔
730
                        if err = s.checkForConfigAndReset(*iface, storeManager); err != nil {
1✔
731
                                log.Log.Error(err, "resetSriovInterfacesInParallel(): fail to reset sriov interface. resetting interface.", "address", iface.PciAddress)
×
732
                        }
×
733
                        errChannel <- err
1✔
734
                }(&interfaces[ifaceIndex])
735
        }
736

737
        for i := 0; i < interfacesToReset; i++ {
2✔
738
                errMsg := <-errChannel
1✔
739
                result = errors.Join(result, errMsg)
1✔
740
        }
1✔
741
        if result != nil {
1✔
742
                log.Log.Error(result, "resetSriovInterfacesInParallel(): fail to reset sriov interface")
×
743
                return result
×
744
        }
×
745
        log.Log.V(2).Info("resetSriovInterfacesInParallel(): sriov reset finished")
1✔
746

1✔
747
        return nil
1✔
748
}
749

750
func (s *sriov) configSriovInterfaces(storeManager store.ManagerInterface, interfaces []interfaceToConfigure, skipVFConfiguration bool) error {
1✔
751
        log.Log.V(2).Info("configSriovInterfaces(): start sriov configuration")
1✔
752
        for _, iface := range interfaces {
2✔
753
                if err := s.configSriovDevice(&iface.iface, skipVFConfiguration); err != nil {
2✔
754
                        log.Log.Error(err, "configSriovInterfaces(): fail to configure sriov interface. resetting interface.", "address", iface.iface.PciAddress)
1✔
755
                        if iface.iface.ExternallyManaged {
2✔
756
                                log.Log.V(2).Info("configSriovInterfaces(): skipping device reset as the nic is marked as externally created")
1✔
757
                        } else {
1✔
758
                                if resetErr := s.ResetSriovDevice(iface.ifaceStatus); resetErr != nil {
×
759
                                        log.Log.Error(resetErr, "configSriovInterfaces(): failed to reset on error SR-IOV interface")
×
760
                                }
×
761
                        }
762
                        return err
1✔
763
                }
764

765
                // Save the PF status to the host
766
                err := storeManager.SaveLastPfAppliedStatus(&iface.iface)
1✔
767
                if err != nil {
1✔
768
                        log.Log.Error(err, "configSriovInterfaces(): failed to save PF applied config to host")
×
769
                        return err
×
770
                }
×
771
        }
772
        log.Log.V(2).Info("configSriovInterfaces(): sriov configuration finished")
1✔
773
        return nil
1✔
774
}
775

776
func (s *sriov) resetSriovInterfaces(storeManager store.ManagerInterface, interfaces []sriovnetworkv1.InterfaceExt) error {
1✔
777
        for _, iface := range interfaces {
2✔
778
                if err := s.checkForConfigAndReset(iface, storeManager); err != nil {
1✔
779
                        log.Log.Error(err, "resetSriovInterfaces(): failed to reset sriov interface. resetting interface.", "address", iface.PciAddress)
×
780
                        return err
×
781
                }
×
782
        }
783
        log.Log.V(2).Info("resetSriovInterfaces(): sriov reset finished")
1✔
784
        return nil
1✔
785
}
786

787
// / skipSriovConfig checks if we need to apply SR-IOV configuration specified specific interface
788
func skipSriovConfig(iface *sriovnetworkv1.Interface, ifaceStatus *sriovnetworkv1.InterfaceExt, storeManager store.ManagerInterface) (bool, error) {
1✔
789
        if !sriovnetworkv1.NeedToUpdateSriov(iface, ifaceStatus) {
1✔
790
                log.Log.V(2).Info("ConfigSriovInterfaces(): no need update interface", "address", iface.PciAddress)
×
791

×
792
                // Save the PF status to the host
×
793
                err := storeManager.SaveLastPfAppliedStatus(iface)
×
794
                if err != nil {
×
795
                        log.Log.Error(err, "ConfigSriovInterfaces(): failed to save PF applied status config to host")
×
796
                        return false, err
×
797
                }
×
798

799
                return true, nil
×
800
        }
801
        return false, nil
1✔
802
}
803

804
func (s *sriov) checkForConfigAndReset(ifaceStatus sriovnetworkv1.InterfaceExt, storeManager store.ManagerInterface) error {
1✔
805
        // load the PF info
1✔
806
        pfStatus, exist, err := storeManager.LoadPfsStatus(ifaceStatus.PciAddress)
1✔
807
        if err != nil {
1✔
808
                log.Log.Error(err, "checkForConfigAndReset(): failed to load info about PF status for device",
×
809
                        "address", ifaceStatus.PciAddress)
×
810
                return err
×
811
        }
×
812

813
        if !exist {
1✔
814
                log.Log.V(2).Info("checkForConfigAndReset(): PF name with pci address has VFs configured but they weren't created by the sriov operator. Skipping the device reset",
×
815
                        "pf-name", ifaceStatus.Name,
×
816
                        "address", ifaceStatus.PciAddress)
×
817
                return nil
×
818
        }
×
819

820
        if pfStatus.ExternallyManaged {
2✔
821
                log.Log.V(2).Info("checkForConfigAndReset(): PF name with pci address was externally created skipping the device reset",
1✔
822
                        "pf-name", ifaceStatus.Name,
1✔
823
                        "address", ifaceStatus.PciAddress)
1✔
824

1✔
825
                // remove pf status from host
1✔
826
                err = storeManager.RemovePfAppliedStatus(ifaceStatus.PciAddress)
1✔
827
                if err != nil {
1✔
828
                        return err
×
829
                }
×
830

831
                return nil
1✔
832
        }
833
        err = s.removeUdevRules(ifaceStatus.PciAddress)
1✔
834
        if err != nil {
1✔
835
                return err
×
836
        }
×
837

838
        if ifaceStatus.NumVfs > 0 {
2✔
839
                if err = s.ResetSriovDevice(ifaceStatus); err != nil {
1✔
840
                        return err
×
841
                }
×
842
        }
843

844
        // remove pf status from host
845
        err = storeManager.RemovePfAppliedStatus(ifaceStatus.PciAddress)
1✔
846
        if err != nil {
1✔
847
                return err
×
848
        }
×
849

850
        return nil
1✔
851
}
852

853
func (s *sriov) ConfigSriovDeviceVirtual(iface *sriovnetworkv1.Interface) error {
×
854
        log.Log.V(2).Info("ConfigSriovDeviceVirtual(): config interface", "address", iface.PciAddress, "config", iface)
×
855
        // Config VFs
×
856
        if iface.NumVfs > 0 {
×
857
                if iface.NumVfs > 1 {
×
858
                        log.Log.Error(nil, "ConfigSriovDeviceVirtual(): in a virtual environment, only one VF per interface",
×
859
                                "numVfs", iface.NumVfs)
×
860
                        return errors.New("NumVfs > 1")
×
861
                }
×
862
                if len(iface.VfGroups) != 1 {
×
863
                        log.Log.Error(nil, "ConfigSriovDeviceVirtual(): missing VFGroup")
×
864
                        return errors.New("NumVfs != 1")
×
865
                }
×
866
                addr := iface.PciAddress
×
867
                log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "address", addr)
×
868
                driver := ""
×
869
                vfID := 0
×
870
                for _, group := range iface.VfGroups {
×
871
                        log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "group", group)
×
872
                        if sriovnetworkv1.IndexInRange(vfID, group.VfRange) {
×
873
                                log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "indexInRange", vfID)
×
874
                                if sriovnetworkv1.StringInArray(group.DeviceType, vars.DpdkDrivers) {
×
875
                                        log.Log.V(2).Info("ConfigSriovDeviceVirtual()", "driver", group.DeviceType)
×
876
                                        driver = group.DeviceType
×
877
                                }
×
878
                                break
×
879
                        }
880
                }
881
                if driver == "" {
×
882
                        log.Log.V(2).Info("ConfigSriovDeviceVirtual(): bind default")
×
883
                        if err := s.kernelHelper.BindDefaultDriver(addr); err != nil {
×
884
                                log.Log.Error(err, "ConfigSriovDeviceVirtual(): fail to bind default driver", "device", addr)
×
885
                                return err
×
886
                        }
×
887
                } else {
×
888
                        log.Log.V(2).Info("ConfigSriovDeviceVirtual(): bind driver", "driver", driver)
×
889
                        if err := s.kernelHelper.BindDpdkDriver(addr, driver); err != nil {
×
890
                                log.Log.Error(err, "ConfigSriovDeviceVirtual(): fail to bind driver for device",
×
891
                                        "driver", driver, "device", addr)
×
892
                                return err
×
893
                        }
×
894
                }
895
        }
896
        return nil
×
897
}
898

899
func (s *sriov) GetNicSriovMode(pciAddress string) string {
1✔
900
        log.Log.V(2).Info("GetNicSriovMode()", "device", pciAddress)
1✔
901
        devLink, err := s.netlinkLib.DevLinkGetDeviceByName("pci", pciAddress)
1✔
902
        if err != nil {
2✔
903
                if !errors.Is(err, syscall.ENODEV) {
2✔
904
                        log.Log.Error(err, "GetNicSriovMode(): failed to get eswitch mode, assume legacy", "device", pciAddress)
1✔
905
                }
1✔
906
        }
907
        if devLink != nil && devLink.Attrs.Eswitch.Mode != "" {
2✔
908
                return devLink.Attrs.Eswitch.Mode
1✔
909
        }
1✔
910

911
        return sriovnetworkv1.ESwithModeLegacy
1✔
912
}
913

914
func (s *sriov) SetNicSriovMode(pciAddress string, mode string) error {
1✔
915
        log.Log.V(2).Info("SetNicSriovMode()", "device", pciAddress, "mode", mode)
1✔
916

1✔
917
        dev, err := s.netlinkLib.DevLinkGetDeviceByName("pci", pciAddress)
1✔
918
        if err != nil {
2✔
919
                return fmt.Errorf("can't get devlink device [%s] to set eSwitch to [%s]: %w", pciAddress, mode, err)
1✔
920
        }
1✔
921

922
        err = s.netlinkLib.DevLinkSetEswitchMode(dev, mode)
1✔
923
        if err != nil {
2✔
924
                return fmt.Errorf("can't set eSwitch mode to [%s] on device [%s]: %w", mode, pciAddress, err)
1✔
925
        }
1✔
926
        return nil
1✔
927
}
928

929
func (s *sriov) GetLinkType(name string) string {
1✔
930
        log.Log.V(2).Info("GetLinkType()", "name", name)
1✔
931
        link, err := s.netlinkLib.LinkByName(name)
1✔
932
        if err != nil {
1✔
933
                log.Log.Error(err, "GetLinkType(): failed to get link", "device", name)
×
934
                return ""
×
935
        }
×
936
        return s.encapTypeToLinkType(link.Attrs().EncapType)
1✔
937
}
938

939
func (s *sriov) encapTypeToLinkType(encapType string) string {
1✔
940
        if encapType == "ether" {
2✔
941
                return consts.LinkTypeETH
1✔
942
        } else if encapType == "infiniband" {
1✔
943
                return consts.LinkTypeIB
×
944
        }
×
945
        return ""
×
946
}
947

948
// create required udev rules for PF:
949
// * rule to disable NetworkManager for VFs - for all modes
950
// * rule to keep PF name after switching to switchdev mode - only for switchdev mode
951
func (s *sriov) addUdevRules(iface *sriovnetworkv1.Interface) error {
1✔
952
        log.Log.V(2).Info("addUdevRules(): add udev rules for device",
1✔
953
                "device", iface.PciAddress)
1✔
954
        if err := s.udevHelper.AddDisableNMUdevRule(iface.PciAddress); err != nil {
1✔
955
                return err
×
956
        }
×
957
        if sriovnetworkv1.GetEswitchModeFromSpec(iface) == sriovnetworkv1.ESwithModeSwitchDev {
2✔
958
                if err := s.udevHelper.AddPersistPFNameUdevRule(iface.PciAddress, iface.Name); err != nil {
1✔
959
                        return err
×
960
                }
×
961
        }
962
        return nil
1✔
963
}
964

965
// add switchdev-specific udev rule that renames representors.
966
// this rule relies on phys_port_name and phys_switch_id parameter which
967
// on old kernels can be read only after switching PF to switchdev mode.
968
// if PF doesn't expose phys_port_name and phys_switch_id, then rule creation will be skipped
969
func (s *sriov) addVfRepresentorUdevRule(iface *sriovnetworkv1.Interface) error {
1✔
970
        if sriovnetworkv1.GetEswitchModeFromSpec(iface) == sriovnetworkv1.ESwithModeSwitchDev {
2✔
971
                portName, err := s.networkHelper.GetPhysPortName(iface.Name)
1✔
972
                if err != nil {
1✔
973
                        log.Log.Error(err, "addVfRepresentorUdevRule(): WARNING: can't read phys_port_name for device, skip creation of UDEV rule")
×
974
                        return nil
×
975
                }
×
976
                switchID, err := s.networkHelper.GetPhysSwitchID(iface.Name)
1✔
977
                if err != nil {
1✔
978
                        log.Log.Error(err, "addVfRepresentorUdevRule(): WARNING: can't read phys_switch_id for device, skip creation of UDEV rule")
×
979
                        return nil
×
980
                }
×
981
                return s.udevHelper.AddVfRepresentorUdevRule(iface.PciAddress, iface.Name, switchID, portName)
1✔
982
        }
983
        return nil
1✔
984
}
985

986
// remove all udev rules for PF created by the operator
987
func (s *sriov) removeUdevRules(pciAddress string) error {
1✔
988
        log.Log.V(2).Info("removeUdevRules(): remove udev rules for device",
1✔
989
                "device", pciAddress)
1✔
990
        if err := s.udevHelper.RemoveDisableNMUdevRule(pciAddress); err != nil {
1✔
991
                return err
×
992
        }
×
993
        if err := s.udevHelper.RemoveVfRepresentorUdevRule(pciAddress); err != nil {
1✔
994
                return err
×
995
        }
×
996
        return s.udevHelper.RemovePersistPFNameUdevRule(pciAddress)
1✔
997
}
998

999
// create VFs on the PF
1000
func (s *sriov) createVFs(iface *sriovnetworkv1.Interface) error {
1✔
1001
        expectedEswitchMode := sriovnetworkv1.GetEswitchModeFromSpec(iface)
1✔
1002
        log.Log.V(2).Info("createVFs(): configure VFs for device",
1✔
1003
                "device", iface.PciAddress, "count", iface.NumVfs, "mode", expectedEswitchMode)
1✔
1004

1✔
1005
        if s.dputilsLib.GetVFconfigured(iface.PciAddress) == iface.NumVfs {
1✔
1006
                if s.GetNicSriovMode(iface.PciAddress) == expectedEswitchMode {
×
1007
                        log.Log.V(2).Info("createVFs(): device is already configured",
×
1008
                                "device", iface.PciAddress, "count", iface.NumVfs, "mode", expectedEswitchMode)
×
1009
                        return nil
×
1010
                }
×
1011
        }
1012
        return s.setEswitchModeAndNumVFs(iface.PciAddress, expectedEswitchMode, iface.NumVfs)
1✔
1013
}
1014

1015
type setEswitchModeAndNumVFsFn func(string, string, int) error
1016

1017
func (s *sriov) setEswitchModeAndNumVFs(pciAddr string, desiredEswitchMode string, numVFs int) error {
1✔
1018
        pfDriverName, err := s.dputilsLib.GetDriverName(pciAddr)
1✔
1019
        if err != nil {
1✔
1020
                return err
×
1021
        }
×
1022

1023
        log.Log.V(2).Info("setEswitchModeAndNumVFs(): configure VFs for device",
1✔
1024
                "device", pciAddr, "count", numVFs, "mode", desiredEswitchMode, "driver", pfDriverName)
1✔
1025

1✔
1026
        setEswitchModeAndNumVFsByDriverName := map[string]setEswitchModeAndNumVFsFn{
1✔
1027
                "ice":       s.setEswitchModeAndNumVFsIce,
1✔
1028
                "mlx5_core": s.setEswitchModeAndNumVFsMlx,
1✔
1029
        }
1✔
1030

1✔
1031
        fn, ok := setEswitchModeAndNumVFsByDriverName[pfDriverName]
1✔
1032
        if !ok {
1✔
1033
                log.Log.V(2).Info("setEswitchModeAndNumVFs(): driver not found in the support list. Using fallback implementation",
×
1034
                        "device", pciAddr, "driver", pfDriverName)
×
1035

×
1036
                // Fallback to mlx5 driver
×
1037
                fn = s.setEswitchModeAndNumVFsMlx
×
1038
        }
×
1039

1040
        return fn(pciAddr, desiredEswitchMode, numVFs)
1✔
1041
}
1042

1043
// setEswitchModeAndNumVFsMlx configures PF eSwitch and sriov_numvfs in the following order:
1044
// a. set eSwitchMode to legacy
1045
// b. set the desired number of Virtual Functions
1046
// c. unbind driver of all VFs
1047
// d. set eSwitchMode to `switchdev` if requested
1048
func (s *sriov) setEswitchModeAndNumVFsMlx(pciAddr string, desiredEswitchMode string, numVFs int) error {
1✔
1049
        log.Log.V(2).Info("setEswitchModeAndNumVFsMlx(): configure VFs for device",
1✔
1050
                "device", pciAddr, "count", numVFs, "mode", desiredEswitchMode)
1✔
1051

1✔
1052
        // always switch NIC to the legacy mode before creating VFs. This is required because some drivers
1✔
1053
        // may not support VF creation in the switchdev mode
1✔
1054
        if s.GetNicSriovMode(pciAddr) != sriovnetworkv1.ESwithModeLegacy {
2✔
1055
                // detach PF from the managed bridge before switching the mode,
1✔
1056
                // changing of eSwitch mode may fail if NIC is part of the bridge (has offloaded TC rules)
1✔
1057
                if err := s.detachPFFromBridge(pciAddr); err != nil {
1✔
1058
                        return err
×
1059
                }
×
1060
                if err := s.unbindAllVFsOnPF(pciAddr); err != nil {
1✔
1061
                        log.Log.Error(err, "setEswitchModeAndNumVFsMlx(): failed to unbind VFs", "device", pciAddr, "mode", desiredEswitchMode)
×
1062
                        return err
×
1063
                }
×
1064
                if err := s.SetNicSriovMode(pciAddr, sriovnetworkv1.ESwithModeLegacy); err != nil {
1✔
1065
                        return err
×
1066
                }
×
1067
        }
1068
        if err := s.SetSriovNumVfs(pciAddr, numVFs); err != nil {
1✔
1069
                return err
×
1070
        }
×
1071

1072
        if desiredEswitchMode == sriovnetworkv1.ESwithModeSwitchDev {
2✔
1073
                if err := s.unbindAllVFsOnPF(pciAddr); err != nil {
1✔
1074
                        log.Log.Error(err, "setEswitchModeAndNumVFsMlx(): failed to unbind VFs", "device", pciAddr, "mode", desiredEswitchMode)
×
1075
                        return err
×
1076
                }
×
1077
                if err := s.SetNicSriovMode(pciAddr, desiredEswitchMode); err != nil {
1✔
1078
                        return err
×
1079
                }
×
1080
        }
1081
        return nil
1✔
1082
}
1083

1084
// setEswitchModeAndNumVFsIce configures PF eSwitch and sriov_numvfs in the following order:
1085
// a. set eSwitchMode to the desired mode if needed
1086
// a1. set sriov_numvfs to 0 before updating the eSwitchMode
1087
// b. set sriov_numvfs to the desired number of VFs
1088
func (s *sriov) setEswitchModeAndNumVFsIce(pciAddr string, desiredEswitchMode string, numVFs int) error {
1✔
1089
        log.Log.V(2).Info("setEswitchModeAndNumVFsIce(): configure VFs for device",
1✔
1090
                "device", pciAddr, "count", numVFs, "mode", desiredEswitchMode)
1✔
1091

1✔
1092
        if s.GetNicSriovMode(pciAddr) != desiredEswitchMode {
2✔
1093
                if err := s.SetSriovNumVfs(pciAddr, 0); err != nil {
1✔
1094
                        return err
×
1095
                }
×
1096

1097
                if err := s.SetNicSriovMode(pciAddr, desiredEswitchMode); err != nil {
1✔
1098
                        return err
×
1099
                }
×
1100
        }
1101

1102
        if err := s.SetSriovNumVfs(pciAddr, numVFs); err != nil {
1✔
1103
                return err
×
1104
        }
×
1105

1106
        return nil
1✔
1107
}
1108

1109
// detach PF from the managed bridge
1110
func (s *sriov) detachPFFromBridge(pciAddr string) error {
1✔
1111
        log.Log.V(2).Info("detachPFFromBridge(): detach PF", "device", pciAddr)
1✔
1112
        if !vars.ManageSoftwareBridges {
2✔
1113
                return nil
1✔
1114
        }
1✔
1115
        if err := s.bridgeHelper.DetachInterfaceFromManagedBridge(pciAddr); err != nil {
×
1116
                log.Log.Error(err, "detachPFFromBridge(): failed to detach interface from the managed bridge", "device", pciAddr)
×
1117
                return err
×
1118
        }
×
1119
        return nil
×
1120
}
1121

1122
// retrieve all VFs for the PF and unbind them from a driver
1123
func (s *sriov) unbindAllVFsOnPF(addr string) error {
1✔
1124
        log.Log.V(2).Info("unbindAllVFsOnPF(): unbind all VFs on PF", "device", addr)
1✔
1125
        vfAddrs, err := s.dputilsLib.GetVFList(addr)
1✔
1126
        if err != nil {
1✔
1127
                return fmt.Errorf("failed to read VF list for pci[%s]: %w", addr, err)
×
1128
        }
×
1129
        for _, vfAddr := range vfAddrs {
2✔
1130
                if err := s.kernelHelper.Unbind(vfAddr); err != nil {
1✔
1131
                        return fmt.Errorf("failed to unbind VF from the driver PF[%s], PF[%s]: %w", addr, vfAddr, err)
×
1132
                }
×
1133
        }
1134
        return nil
1✔
1135
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc