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

k8snetworkplumbingwg / sriov-network-operator / 10058808149

23 Jul 2024 12:22PM UTC coverage: 43.964% (+0.6%) from 43.351%
10058808149

Pull #659

github

web-flow
Merge f199eb95f into 588abb449
Pull Request #659: Configure IB VFs' GUIDs using a statically provided GUID pool

212 of 280 new or added lines in 13 files covered. (75.71%)

2 existing lines in 1 file now uncovered.

6526 of 14844 relevant lines covered (43.96%)

0.48 hits per line

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

55.95
/pkg/host/internal/network/network.go
1
package network
2

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

13
        "github.com/cenkalti/backoff"
14
        "github.com/vishvananda/netlink/nl"
15
        "sigs.k8s.io/controller-runtime/pkg/log"
16

17
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
18
        dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils"
19
        ethtoolPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/ethtool"
20
        netlinkPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
21
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
22
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
23
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
24
)
25

26
type network struct {
27
        utilsHelper utils.CmdInterface
28
        dputilsLib  dputilsPkg.DPUtilsLib
29
        netlinkLib  netlinkPkg.NetlinkLib
30
        ethtoolLib  ethtoolPkg.EthtoolLib
31
}
32

33
func New(utilsHelper utils.CmdInterface, dputilsLib dputilsPkg.DPUtilsLib, netlinkLib netlinkPkg.NetlinkLib, ethtoolLib ethtoolPkg.EthtoolLib) types.NetworkInterface {
1✔
34
        return &network{
1✔
35
                utilsHelper: utilsHelper,
1✔
36
                dputilsLib:  dputilsLib,
1✔
37
                netlinkLib:  netlinkLib,
1✔
38
                ethtoolLib:  ethtoolLib,
1✔
39
        }
1✔
40
}
1✔
41

42
// TryToGetVirtualInterfaceName get the interface name of a virtio interface
43
func (n *network) TryToGetVirtualInterfaceName(pciAddr string) string {
×
44
        log.Log.Info("TryToGetVirtualInterfaceName() get interface name for device", "device", pciAddr)
×
45

×
46
        // To support different driver that is not virtio-pci like mlx
×
47
        name := n.TryGetInterfaceName(pciAddr)
×
48
        if name != "" {
×
49
                return name
×
50
        }
×
51

52
        netDir, err := filepath.Glob(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "virtio*", "net"))
×
53
        if err != nil || len(netDir) < 1 {
×
54
                return ""
×
55
        }
×
56

57
        fInfos, err := os.ReadDir(netDir[0])
×
58
        if err != nil {
×
59
                log.Log.Error(err, "TryToGetVirtualInterfaceName(): failed to read net directory", "dir", netDir[0])
×
60
                return ""
×
61
        }
×
62

63
        names := make([]string, 0)
×
64
        for _, f := range fInfos {
×
65
                names = append(names, f.Name())
×
66
        }
×
67

68
        if len(names) < 1 {
×
69
                return ""
×
70
        }
×
71

72
        return names[0]
×
73
}
74

75
func (n *network) TryGetInterfaceName(pciAddr string) string {
1✔
76
        names, err := n.dputilsLib.GetNetNames(pciAddr)
1✔
77
        if err != nil || len(names) < 1 {
1✔
78
                log.Log.Error(err, "TryGetInterfaceName(): failed to get interface name")
×
79
                return ""
×
80
        }
×
81
        netDevName := names[0]
1✔
82

1✔
83
        // Switchdev PF and their VFs representors are existing under the same PCI address since kernel 5.8
1✔
84
        // if device is switchdev then return PF name
1✔
85
        for _, name := range names {
2✔
86
                if !n.IsSwitchdev(name) {
2✔
87
                        continue
1✔
88
                }
89
                // Try to get the phys port name, if not exists then fallback to check without it
90
                // phys_port_name should be in formant p<port-num> e.g p0,p1,p2 ...etc.
91
                if physPortName, err := n.GetPhysPortName(name); err == nil {
×
92
                        if !vars.PfPhysPortNameRe.MatchString(physPortName) {
×
93
                                continue
×
94
                        }
95
                }
96
                return name
×
97
        }
98

99
        log.Log.V(2).Info("TryGetInterfaceName()", "name", netDevName)
1✔
100
        return netDevName
1✔
101
}
102

103
// GetInterfaceIndex returns network interface index base on pci address or error if occurred
104
func (n *network) GetInterfaceIndex(pciAddr string) (int, error) {
1✔
105
        ifName := n.TryGetInterfaceName(pciAddr)
1✔
106
        if ifName == "" {
1✔
107
                return -1, fmt.Errorf("failed to get interface name")
×
108
        }
×
109

110
        // read the ifindex file from the interface folder
111
        indexFile := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "net", ifName, "ifindex")
1✔
112
        ifIndex, err := os.ReadFile(indexFile)
1✔
113
        if err != nil {
2✔
114
                log.Log.Error(err, "GetInterfaceIndex(): failed to read ifindex file", "indexFile", indexFile)
1✔
115
                return -1, err
1✔
116
        }
1✔
117

118
        intIfIndex, err := strconv.Atoi(strings.TrimSpace(string(ifIndex)))
1✔
119
        if err != nil {
1✔
120
                log.Log.Error(err, "GetInterfaceIndex(): failed to parse ifindex file content", "ifIndex", string(ifIndex))
×
121
                return -1, err
×
122
        }
×
123
        return intIfIndex, nil
1✔
124
}
125

126
func (n *network) GetPhysSwitchID(name string) (string, error) {
1✔
127
        swIDFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_switch_id")
1✔
128
        physSwitchID, err := os.ReadFile(swIDFile)
1✔
129
        if err != nil {
1✔
130
                return "", err
×
131
        }
×
132
        if physSwitchID != nil {
2✔
133
                return strings.TrimSpace(string(physSwitchID)), nil
1✔
134
        }
1✔
135
        return "", nil
×
136
}
137

138
func (n *network) GetPhysPortName(name string) (string, error) {
×
139
        devicePortNameFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_port_name")
×
140
        physPortName, err := os.ReadFile(devicePortNameFile)
×
141
        if err != nil {
×
142
                return "", err
×
143
        }
×
144
        if physPortName != nil {
×
145
                return strings.TrimSpace(string(physPortName)), nil
×
146
        }
×
147
        return "", nil
×
148
}
149

150
func (n *network) IsSwitchdev(name string) bool {
1✔
151
        switchID, err := n.GetPhysSwitchID(name)
1✔
152
        if err != nil || switchID == "" {
2✔
153
                return false
1✔
154
        }
1✔
155

156
        return true
×
157
}
158

159
func (n *network) GetNetdevMTU(pciAddr string) int {
×
160
        log.Log.V(2).Info("GetNetdevMTU(): get MTU", "device", pciAddr)
×
161
        ifaceName := n.TryGetInterfaceName(pciAddr)
×
162
        if ifaceName == "" {
×
163
                return 0
×
164
        }
×
165

166
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
167
        if err != nil {
×
168
                log.Log.Error(err, "GetNetdevMTU(): fail to get Link ", "device", ifaceName)
×
169
                return 0
×
170
        }
×
171

172
        return link.Attrs().MTU
×
173
}
174

175
func (n *network) SetNetdevMTU(pciAddr string, mtu int) error {
×
176
        log.Log.V(2).Info("SetNetdevMTU(): set MTU", "device", pciAddr, "mtu", mtu)
×
177
        if mtu <= 0 {
×
178
                log.Log.V(2).Info("SetNetdevMTU(): refusing to set MTU", "mtu", mtu)
×
179
                return nil
×
180
        }
×
181
        b := backoff.NewConstantBackOff(1 * time.Second)
×
182
        err := backoff.Retry(func() error {
×
183
                ifaceName := n.TryGetInterfaceName(pciAddr)
×
184
                if ifaceName == "" {
×
185
                        log.Log.Error(nil, "SetNetdevMTU(): fail to get interface name", "device", pciAddr)
×
186
                        return fmt.Errorf("failed to get netdevice for device %s", pciAddr)
×
187
                }
×
188

189
                link, err := n.netlinkLib.LinkByName(ifaceName)
×
190
                if err != nil {
×
191
                        log.Log.Error(err, "SetNetdevMTU(): fail to get Link ", "device", ifaceName)
×
192
                        return err
×
193
                }
×
194
                return n.netlinkLib.LinkSetMTU(link, mtu)
×
195
        }, backoff.WithMaxRetries(b, 10))
196

197
        if err != nil {
×
198
                log.Log.Error(err, "SetNetdevMTU(): fail to set mtu after retrying")
×
199
                return err
×
200
        }
×
201
        return nil
×
202
}
203

204
// GetNetDevMac returns network device MAC address or empty string if address cannot be
205
// retrieved.
206
func (n *network) GetNetDevMac(ifaceName string) string {
×
207
        log.Log.V(2).Info("GetNetDevMac(): get Mac", "device", ifaceName)
×
208
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
209
        if err != nil {
×
210
                log.Log.Error(err, "GetNetDevMac(): failed to get Link", "device", ifaceName)
×
211
                return ""
×
212
        }
×
213
        return link.Attrs().HardwareAddr.String()
×
214
}
215

216
// GetNetDevNodeGUID returns the network interface node GUID if device is RDMA capable otherwise returns empty string
217
func (n *network) GetNetDevNodeGUID(pciAddr string) string {
1✔
218
        if len(pciAddr) == 0 {
2✔
219
                return ""
1✔
220
        }
1✔
221

222
        rdmaDevicesPath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "infiniband")
1✔
223
        rdmaDevices, err := os.ReadDir(rdmaDevicesPath)
1✔
224
        if err != nil {
2✔
225
                if !errors.Is(err, fs.ErrNotExist) {
1✔
226
                        log.Log.Error(err, "GetNetDevNodeGUID(): failed to read RDMA related directory", "pciAddr", pciAddr)
×
227
                }
×
228
                return ""
1✔
229
        }
230

231
        if len(rdmaDevices) != 1 {
2✔
232
                log.Log.Error(err, "GetNetDevNodeGUID(): expected just one RDMA device", "pciAddr", pciAddr, "numOfDevices", len(rdmaDevices))
1✔
233
                return ""
1✔
234
        }
1✔
235

236
        rdmaLink, err := n.netlinkLib.RdmaLinkByName(rdmaDevices[0].Name())
1✔
237
        if err != nil {
2✔
238
                log.Log.Error(err, "GetNetDevNodeGUID(): failed to get RDMA link", "pciAddr", pciAddr)
1✔
239
                return ""
1✔
240
        }
1✔
241

242
        return rdmaLink.Attrs.NodeGuid
1✔
243
}
244

245
func (n *network) GetNetDevLinkSpeed(ifaceName string) string {
×
246
        log.Log.V(2).Info("GetNetDevLinkSpeed(): get LinkSpeed", "device", ifaceName)
×
247
        speedFilePath := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, ifaceName, "speed")
×
248
        data, err := os.ReadFile(speedFilePath)
×
249
        if err != nil {
×
250
                log.Log.Error(err, "GetNetDevLinkSpeed(): fail to read Link Speed file", "path", speedFilePath)
×
251
                return ""
×
252
        }
×
253

254
        return fmt.Sprintf("%s Mb/s", strings.TrimSpace(string(data)))
×
255
}
256

257
// GetDevlinkDeviceParam returns devlink parameter for the device as a string, if the parameter has multiple values
258
// then the function will return only first one from the list.
259
func (n *network) GetDevlinkDeviceParam(pciAddr, paramName string) (string, error) {
1✔
260
        funcLog := log.Log.WithValues("device", pciAddr, "param", paramName)
1✔
261
        funcLog.V(2).Info("GetDevlinkDeviceParam(): get device parameter")
1✔
262
        param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
1✔
263
        if err != nil {
2✔
264
                funcLog.Error(err, "GetDevlinkDeviceParam(): fail to get devlink device param")
1✔
265
                return "", err
1✔
266
        }
1✔
267
        if len(param.Values) == 0 {
1✔
268
                err = fmt.Errorf("param %s has no value", paramName)
×
269
                funcLog.Error(err, "GetDevlinkDeviceParam(): error")
×
270
                return "", err
×
271
        }
×
272
        var value string
1✔
273
        switch param.Type {
1✔
274
        case nl.DEVLINK_PARAM_TYPE_U8, nl.DEVLINK_PARAM_TYPE_U16, nl.DEVLINK_PARAM_TYPE_U32:
1✔
275
                var valData uint64
1✔
276
                switch v := param.Values[0].Data.(type) {
1✔
277
                case uint8:
1✔
278
                        valData = uint64(v)
1✔
279
                case uint16:
1✔
280
                        valData = uint64(v)
1✔
281
                case uint32:
1✔
282
                        valData = uint64(v)
1✔
283
                default:
×
284
                        return "", fmt.Errorf("unexpected uint type type")
×
285
                }
286
                value = strconv.FormatUint(valData, 10)
1✔
287

288
        case nl.DEVLINK_PARAM_TYPE_STRING:
1✔
289
                value = param.Values[0].Data.(string)
1✔
290
        case nl.DEVLINK_PARAM_TYPE_BOOL:
1✔
291
                value = strconv.FormatBool(param.Values[0].Data.(bool))
1✔
292
        default:
×
293
                return "", fmt.Errorf("unknown value type: %d", param.Type)
×
294
        }
295
        funcLog.V(2).Info("GetDevlinkDeviceParam(): result", "value", value)
1✔
296
        return value, nil
1✔
297
}
298

299
// SetDevlinkDeviceParam set devlink parameter for the device, accepts paramName and value
300
// as a string. Automatically set CMODE for the parameter and converts the value to the right
301
// type before submitting it.
302
func (n *network) SetDevlinkDeviceParam(pciAddr, paramName, value string) error {
1✔
303
        funcLog := log.Log.WithValues("device", pciAddr, "param", paramName, "value", value)
1✔
304
        funcLog.V(2).Info("SetDevlinkDeviceParam(): set device parameter")
1✔
305
        param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
1✔
306
        if err != nil {
2✔
307
                funcLog.Error(err, "SetDevlinkDeviceParam(): can't get existing param data")
1✔
308
                return err
1✔
309
        }
1✔
310
        if len(param.Values) == 0 {
1✔
311
                err = fmt.Errorf("param %s has no value", paramName)
×
312
                funcLog.Error(err, "SetDevlinkDeviceParam(): error")
×
313
                return err
×
314
        }
×
315
        targetCMOD := param.Values[0].CMODE
1✔
316
        var typedValue interface{}
1✔
317
        var v uint64
1✔
318
        switch param.Type {
1✔
319
        case nl.DEVLINK_PARAM_TYPE_U8:
1✔
320
                v, err = strconv.ParseUint(value, 10, 8)
1✔
321
                typedValue = uint8(v)
1✔
322
        case nl.DEVLINK_PARAM_TYPE_U16:
1✔
323
                v, err = strconv.ParseUint(value, 10, 16)
1✔
324
                typedValue = uint16(v)
1✔
325
        case nl.DEVLINK_PARAM_TYPE_U32:
1✔
326
                v, err = strconv.ParseUint(value, 10, 32)
1✔
327
                typedValue = uint32(v)
1✔
328
        case nl.DEVLINK_PARAM_TYPE_STRING:
1✔
329
                err = nil
1✔
330
                typedValue = value
1✔
331
        case nl.DEVLINK_PARAM_TYPE_BOOL:
1✔
332
                typedValue, err = strconv.ParseBool(value)
1✔
333
        default:
×
334
                return fmt.Errorf("parameter has unknown value type: %d", param.Type)
×
335
        }
336
        if err != nil {
2✔
337
                err = fmt.Errorf("failed to convert value %s to the required type: %T, devlink paramType is: %d", value, typedValue, param.Type)
1✔
338
                funcLog.Error(err, "SetDevlinkDeviceParam(): error")
1✔
339
                return err
1✔
340
        }
1✔
341
        if err := n.netlinkLib.DevlinkSetDeviceParam(consts.BusPci, pciAddr, paramName, targetCMOD, typedValue); err != nil {
2✔
342
                funcLog.Error(err, "SetDevlinkDeviceParam(): failed to set parameter")
1✔
343
                return err
1✔
344
        }
1✔
345
        return nil
1✔
346
}
347

348
// EnableHwTcOffload makes sure that hw-tc-offload feature is enabled if device supports it
349
func (n *network) EnableHwTcOffload(ifaceName string) error {
1✔
350
        log.Log.V(2).Info("EnableHwTcOffload(): enable offloading", "device", ifaceName)
1✔
351
        hwTcOffloadFeatureName := "hw-tc-offload"
1✔
352

1✔
353
        knownFeatures, err := n.ethtoolLib.FeatureNames(ifaceName)
1✔
354
        if err != nil {
2✔
355
                log.Log.Error(err, "EnableHwTcOffload(): can't list supported features", "device", ifaceName)
1✔
356
                return err
1✔
357
        }
1✔
358
        if _, isKnown := knownFeatures[hwTcOffloadFeatureName]; !isKnown {
2✔
359
                log.Log.V(0).Info("EnableHwTcOffload(): can't enable feature, feature is not supported", "device", ifaceName)
1✔
360
                return nil
1✔
361
        }
1✔
362
        currentFeaturesState, err := n.ethtoolLib.Features(ifaceName)
1✔
363
        if err != nil {
2✔
364
                log.Log.Error(err, "EnableHwTcOffload(): can't read features state for device", "device", ifaceName)
1✔
365
                return err
1✔
366
        }
1✔
367
        if currentFeaturesState[hwTcOffloadFeatureName] {
2✔
368
                log.Log.V(2).Info("EnableHwTcOffload(): already enabled", "device", ifaceName)
1✔
369
                return nil
1✔
370
        }
1✔
371
        if err := n.ethtoolLib.Change(ifaceName, map[string]bool{hwTcOffloadFeatureName: true}); err != nil {
2✔
372
                log.Log.Error(err, "EnableHwTcOffload(): can't set feature for device", "device", ifaceName)
1✔
373
                return err
1✔
374
        }
1✔
375
        updatedFeaturesState, err := n.ethtoolLib.Features(ifaceName)
1✔
376
        if err != nil {
2✔
377
                log.Log.Error(err, "EnableHwTcOffload(): can't read features state for device", "device", ifaceName)
1✔
378
                return err
1✔
379
        }
1✔
380
        if updatedFeaturesState[hwTcOffloadFeatureName] {
2✔
381
                log.Log.V(2).Info("EnableHwTcOffload(): feature enabled", "device", ifaceName)
1✔
382
                return nil
1✔
383
        }
1✔
384
        log.Log.V(0).Info("EnableHwTcOffload(): feature is still disabled, not supported by device", "device", ifaceName)
1✔
385
        return nil
1✔
386
}
387

388
// GetNetDevLinkAdminState returns the admin state of the interface.
389
func (n *network) GetNetDevLinkAdminState(ifaceName string) string {
×
390
        log.Log.V(2).Info("GetNetDevLinkAdminState(): get LinkAdminState", "device", ifaceName)
×
391
        if len(ifaceName) == 0 {
×
392
                return ""
×
393
        }
×
394

395
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
396
        if err != nil {
×
397
                log.Log.Error(err, "GetNetDevLinkAdminState(): failed to get link", "device", ifaceName)
×
398
                return ""
×
399
        }
×
400

401
        if n.netlinkLib.IsLinkAdminStateUp(link) {
×
402
                return consts.LinkAdminStateUp
×
403
        }
×
404

405
        return consts.LinkAdminStateDown
×
406
}
407

408
// GetPciAddressFromInterfaceName parses sysfs to get pci address of an interface by name
409
func (n *network) GetPciAddressFromInterfaceName(interfaceName string) (string, error) {
1✔
410
        log.Log.V(2).Info("GetPciAddressFromInterfaceName(): get pci address", "interface", interfaceName)
1✔
411
        sysfsPath := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, interfaceName, "device")
1✔
412

1✔
413
        pciDevDir, err := os.Readlink(sysfsPath)
1✔
414

1✔
415
        if err != nil {
1✔
NEW
416
                log.Log.Error(err, "GetPciAddressFromInterfaceName(): failed to get pci device dir", "interface", interfaceName)
×
NEW
417
                return "", err
×
NEW
418
        }
×
419

420
        pciAddress := filepath.Base(pciDevDir)
1✔
421
        log.Log.V(2).Info("GetPciAddressFromInterfaceName(): result", "interface", interfaceName, "pci address", pciAddress)
1✔
422
        return pciAddress, nil
1✔
423
}
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