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

k8snetworkplumbingwg / sriov-network-operator / 8467233545

28 Mar 2024 12:26PM UTC coverage: 38.344% (+0.7%) from 37.675%
8467233545

push

github

web-flow
Merge pull request #643 from ykulazhenkov/pr-turn-on-switchdev

[switchdev 9/9] Enable new switchdev implementation

189 of 289 new or added lines in 9 files covered. (65.4%)

26 existing lines in 8 files now uncovered.

4798 of 12513 relevant lines covered (38.34%)

0.42 hits per line

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

44.49
/pkg/plugins/generic/generic_plugin.go
1
package generic
2

3
import (
4
        "bytes"
5
        "errors"
6
        "os/exec"
7
        "reflect"
8
        "strconv"
9
        "strings"
10
        "syscall"
11

12
        "sigs.k8s.io/controller-runtime/pkg/log"
13

14
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
15
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
16
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper"
17
        plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins"
18
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
19
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
20
)
21

22
var PluginName = "generic"
23

24
// driver id
25
const (
26
        Vfio = iota
27
        VirtioVdpa
28
        VhostVdpa
29
)
30

31
// driver name
32
const (
33
        vfioPciDriver    = "vfio_pci"
34
        virtioVdpaDriver = "virtio_vdpa"
35
        vhostVdpaDriver  = "vhost_vdpa"
36
)
37

38
// function type for determining if a given driver has to be loaded in the kernel
39
type needDriver func(state *sriovnetworkv1.SriovNetworkNodeState, driverState *DriverState) bool
40

41
type DriverState struct {
42
        DriverName     string
43
        DeviceType     string
44
        VdpaType       string
45
        NeedDriverFunc needDriver
46
        DriverLoaded   bool
47
}
48

49
type DriverStateMapType map[uint]*DriverState
50

51
type GenericPlugin struct {
52
        PluginName          string
53
        SpecVersion         string
54
        DesireState         *sriovnetworkv1.SriovNetworkNodeState
55
        LastState           *sriovnetworkv1.SriovNetworkNodeState
56
        DriverStateMap      DriverStateMapType
57
        DesiredKernelArgs   map[string]bool
58
        helpers             helper.HostHelpersInterface
59
        skipVFConfiguration bool
60
}
61

62
type Option = func(c *genericPluginOptions)
63

64
// WithSkipVFConfiguration configures generic plugin to skip configuration of the VFs.
65
// In this case PFs will be configured and VFs are created only, VF configuration phase is skipped.
66
// VFs on the PF (if the PF is not ExternallyManaged) will have no driver after the plugin execution completes.
67
func WithSkipVFConfiguration() Option {
1✔
68
        return func(c *genericPluginOptions) {
1✔
69
                c.skipVFConfiguration = true
×
70
        }
×
71
}
72

73
type genericPluginOptions struct {
74
        skipVFConfiguration bool
75
}
76

77
const scriptsPath = "bindata/scripts/enable-kargs.sh"
78

79
// Initialize our plugin and set up initial values
80
func NewGenericPlugin(helpers helper.HostHelpersInterface, options ...Option) (plugin.VendorPlugin, error) {
1✔
81
        cfg := &genericPluginOptions{}
1✔
82
        for _, o := range options {
1✔
83
                o(cfg)
×
84
        }
×
85
        driverStateMap := make(map[uint]*DriverState)
1✔
86
        driverStateMap[Vfio] = &DriverState{
1✔
87
                DriverName:     vfioPciDriver,
1✔
88
                DeviceType:     consts.DeviceTypeVfioPci,
1✔
89
                VdpaType:       "",
1✔
90
                NeedDriverFunc: needDriverCheckDeviceType,
1✔
91
                DriverLoaded:   false,
1✔
92
        }
1✔
93
        driverStateMap[VirtioVdpa] = &DriverState{
1✔
94
                DriverName:     virtioVdpaDriver,
1✔
95
                DeviceType:     consts.DeviceTypeNetDevice,
1✔
96
                VdpaType:       consts.VdpaTypeVirtio,
1✔
97
                NeedDriverFunc: needDriverCheckVdpaType,
1✔
98
                DriverLoaded:   false,
1✔
99
        }
1✔
100
        driverStateMap[VhostVdpa] = &DriverState{
1✔
101
                DriverName:     vhostVdpaDriver,
1✔
102
                DeviceType:     consts.DeviceTypeNetDevice,
1✔
103
                VdpaType:       consts.VdpaTypeVhost,
1✔
104
                NeedDriverFunc: needDriverCheckVdpaType,
1✔
105
                DriverLoaded:   false,
1✔
106
        }
1✔
107
        return &GenericPlugin{
1✔
108
                PluginName:          PluginName,
1✔
109
                SpecVersion:         "1.0",
1✔
110
                DriverStateMap:      driverStateMap,
1✔
111
                DesiredKernelArgs:   make(map[string]bool),
1✔
112
                helpers:             helpers,
1✔
113
                skipVFConfiguration: cfg.skipVFConfiguration,
1✔
114
        }, nil
1✔
115
}
116

117
// Name returns the name of the plugin
118
func (p *GenericPlugin) Name() string {
1✔
119
        return p.PluginName
1✔
120
}
1✔
121

122
// Spec returns the version of the spec expected by the plugin
123
func (p *GenericPlugin) Spec() string {
×
124
        return p.SpecVersion
×
125
}
×
126

127
// OnNodeStateChange Invoked when SriovNetworkNodeState CR is created or updated, return if need drain and/or reboot node
128
func (p *GenericPlugin) OnNodeStateChange(new *sriovnetworkv1.SriovNetworkNodeState) (needDrain bool, needReboot bool, err error) {
1✔
129
        log.Log.Info("generic plugin OnNodeStateChange()")
1✔
130
        p.DesireState = new
1✔
131

1✔
132
        needDrain = p.needDrainNode(new.Spec.Interfaces, new.Status.Interfaces)
1✔
133
        needReboot, err = p.needRebootNode(new)
1✔
134
        if err != nil {
1✔
135
                return needDrain, needReboot, err
×
136
        }
×
137

138
        if needReboot {
1✔
139
                needDrain = true
×
140
        }
×
141
        return
1✔
142
}
143

144
func (p *GenericPlugin) syncDriverState() error {
×
145
        for _, driverState := range p.DriverStateMap {
×
146
                if !driverState.DriverLoaded && driverState.NeedDriverFunc(p.DesireState, driverState) {
×
147
                        log.Log.V(2).Info("loading driver", "name", driverState.DriverName)
×
148
                        if err := p.helpers.LoadKernelModule(driverState.DriverName); err != nil {
×
149
                                log.Log.Error(err, "generic plugin syncDriverState(): fail to load kmod", "name", driverState.DriverName)
×
150
                                return err
×
151
                        }
×
152
                        driverState.DriverLoaded = true
×
153
                }
154
        }
155
        return nil
×
156
}
157

158
// Apply config change
159
func (p *GenericPlugin) Apply() error {
×
160
        log.Log.Info("generic plugin Apply()", "desiredState", p.DesireState.Spec)
×
161

×
162
        if p.LastState != nil {
×
163
                log.Log.Info("generic plugin Apply()", "lastState", p.LastState.Spec)
×
164
                if reflect.DeepEqual(p.LastState.Spec.Interfaces, p.DesireState.Spec.Interfaces) {
×
165
                        log.Log.Info("generic plugin Apply(): desired and latest state are the same, nothing to apply")
×
166
                        return nil
×
167
                }
×
168
        }
169

170
        if err := p.syncDriverState(); err != nil {
×
171
                return err
×
172
        }
×
173

174
        // When calling from systemd do not try to chroot
175
        if !vars.UsingSystemdMode {
×
176
                exit, err := p.helpers.Chroot(consts.Host)
×
177
                if err != nil {
×
178
                        return err
×
179
                }
×
180
                defer exit()
×
181
        }
182

183
        if err := p.helpers.ConfigSriovInterfaces(p.helpers, p.DesireState.Spec.Interfaces,
×
NEW
184
                p.DesireState.Status.Interfaces, p.skipVFConfiguration); err != nil {
×
185
                // Catch the "cannot allocate memory" error and try to use PCI realloc
×
186
                if errors.Is(err, syscall.ENOMEM) {
×
187
                        p.addToDesiredKernelArgs(consts.KernelArgPciRealloc)
×
188
                }
×
189
                return err
×
190
        }
191
        p.LastState = &sriovnetworkv1.SriovNetworkNodeState{}
×
192
        *p.LastState = *p.DesireState
×
193
        return nil
×
194
}
195

196
func needDriverCheckDeviceType(state *sriovnetworkv1.SriovNetworkNodeState, driverState *DriverState) bool {
1✔
197
        for _, iface := range state.Spec.Interfaces {
2✔
198
                for i := range iface.VfGroups {
2✔
199
                        if iface.VfGroups[i].DeviceType == driverState.DeviceType {
2✔
200
                                return true
1✔
201
                        }
1✔
202
                }
203
        }
204
        return false
1✔
205
}
206

207
func needDriverCheckVdpaType(state *sriovnetworkv1.SriovNetworkNodeState, driverState *DriverState) bool {
1✔
208
        for _, iface := range state.Spec.Interfaces {
2✔
209
                for i := range iface.VfGroups {
2✔
210
                        if iface.VfGroups[i].VdpaType == driverState.VdpaType {
2✔
211
                                return true
1✔
212
                        }
1✔
213
                }
214
        }
215
        return false
1✔
216
}
217

218
// setKernelArg Tries to add the kernel args via ostree or grubby.
219
func setKernelArg(karg string) (bool, error) {
×
220
        log.Log.Info("generic plugin setKernelArg()")
×
221
        var stdout, stderr bytes.Buffer
×
222
        cmd := exec.Command("/bin/sh", scriptsPath, karg)
×
223
        cmd.Stdout = &stdout
×
224
        cmd.Stderr = &stderr
×
225

×
226
        if err := cmd.Run(); err != nil {
×
227
                // if grubby is not there log and assume kernel args are set correctly.
×
228
                if utils.IsCommandNotFound(err) {
×
229
                        log.Log.Error(err, "generic plugin setKernelArg(): grubby or ostree command not found. Please ensure that kernel arg are set",
×
230
                                "kargs", karg)
×
231
                        return false, nil
×
232
                }
×
233
                log.Log.Error(err, "generic plugin setKernelArg(): fail to enable kernel arg", "karg", karg)
×
234
                return false, err
×
235
        }
236

237
        i, err := strconv.Atoi(strings.TrimSpace(stdout.String()))
×
238
        if err == nil {
×
239
                if i > 0 {
×
240
                        log.Log.Info("generic plugin setKernelArg(): need to reboot node for kernel arg", "karg", karg)
×
241
                        return true, nil
×
242
                }
×
243
        }
244
        return false, err
×
245
}
246

247
// addToDesiredKernelArgs Should be called to queue a kernel arg to be added to the node.
248
func (p *GenericPlugin) addToDesiredKernelArgs(karg string) {
×
249
        if _, ok := p.DesiredKernelArgs[karg]; !ok {
×
250
                log.Log.Info("generic plugin addToDesiredKernelArgs(): Adding to desired kernel arg", "karg", karg)
×
251
                p.DesiredKernelArgs[karg] = false
×
252
        }
×
253
}
254

255
// syncDesiredKernelArgs Should be called to set all the kernel arguments. Returns bool if node update is needed.
256
func (p *GenericPlugin) syncDesiredKernelArgs() (bool, error) {
1✔
257
        needReboot := false
1✔
258
        if len(p.DesiredKernelArgs) == 0 {
2✔
259
                return false, nil
1✔
260
        }
1✔
261
        kargs, err := p.helpers.GetCurrentKernelArgs()
×
262
        if err != nil {
×
263
                return false, err
×
264
        }
×
265
        for desiredKarg, attempted := range p.DesiredKernelArgs {
×
266
                set := p.helpers.IsKernelArgsSet(kargs, desiredKarg)
×
267
                if !set {
×
268
                        if attempted {
×
269
                                log.Log.V(2).Info("generic plugin syncDesiredKernelArgs(): previously attempted to set kernel arg",
×
270
                                        "karg", desiredKarg)
×
271
                        }
×
272
                        // There is a case when we try to set the kernel argument here, the daemon could decide to not reboot because
273
                        // the daemon encountered a potentially one-time error. However we always want to make sure that the kernel
274
                        // argument is set once the daemon goes through node state sync again.
275
                        update, err := setKernelArg(desiredKarg)
×
276
                        if err != nil {
×
277
                                log.Log.Error(err, "generic plugin syncDesiredKernelArgs(): fail to set kernel arg", "karg", desiredKarg)
×
278
                                return false, err
×
279
                        }
×
280
                        if update {
×
281
                                needReboot = true
×
282
                                log.Log.V(2).Info("generic plugin syncDesiredKernelArgs(): need reboot for setting kernel arg", "karg", desiredKarg)
×
283
                        }
×
284
                        p.DesiredKernelArgs[desiredKarg] = true
×
285
                }
286
        }
287
        return needReboot, nil
×
288
}
289

290
func (p *GenericPlugin) needDrainNode(desired sriovnetworkv1.Interfaces, current sriovnetworkv1.InterfaceExts) (needDrain bool) {
1✔
291
        log.Log.V(2).Info("generic plugin needDrainNode()", "current", current, "desired", desired)
1✔
292

1✔
293
        needDrain = false
1✔
294
        for _, ifaceStatus := range current {
2✔
295
                configured := false
1✔
296
                for _, iface := range desired {
2✔
297
                        if iface.PciAddress == ifaceStatus.PciAddress {
2✔
298
                                configured = true
1✔
299
                                if ifaceStatus.NumVfs == 0 {
1✔
300
                                        log.Log.V(2).Info("generic plugin needDrainNode(): no need drain, for PCI address, current NumVfs is 0",
×
301
                                                "address", iface.PciAddress)
×
302
                                        break
×
303
                                }
304
                                if sriovnetworkv1.NeedToUpdateSriov(&iface, &ifaceStatus) {
2✔
305
                                        log.Log.V(2).Info("generic plugin needDrainNode(): need drain, for PCI address request update",
1✔
306
                                                "address", iface.PciAddress)
1✔
307
                                        needDrain = true
1✔
308
                                        return
1✔
309
                                }
1✔
310
                                log.Log.V(2).Info("generic plugin needDrainNode(): no need drain,for PCI address",
1✔
311
                                        "address", iface.PciAddress, "expected-vfs", iface.NumVfs, "current-vfs", ifaceStatus.NumVfs)
1✔
312
                        }
313
                }
314
                if !configured && ifaceStatus.NumVfs > 0 {
1✔
315
                        // load the PF info
×
316
                        pfStatus, exist, err := p.helpers.LoadPfsStatus(ifaceStatus.PciAddress)
×
317
                        if err != nil {
×
318
                                log.Log.Error(err, "generic plugin needDrainNode(): failed to load info about PF status for pci device",
×
319
                                        "address", ifaceStatus.PciAddress)
×
320
                                continue
×
321
                        }
322

323
                        if !exist {
×
324
                                log.Log.Info("generic plugin needDrainNode(): PF name with pci address has VFs configured but they weren't created by the sriov operator. Skipping drain",
×
325
                                        "name", ifaceStatus.Name,
×
326
                                        "address", ifaceStatus.PciAddress)
×
327
                                continue
×
328
                        }
329

330
                        if pfStatus.ExternallyManaged {
×
331
                                log.Log.Info("generic plugin needDrainNode()(): PF name with pci address was externally created. Skipping drain",
×
332
                                        "name", ifaceStatus.Name,
×
333
                                        "address", ifaceStatus.PciAddress)
×
334
                                continue
×
335
                        }
336

337
                        log.Log.V(2).Info("generic plugin needDrainNode(): need drain since interface needs to be reset",
×
338
                                "interface", ifaceStatus)
×
339
                        needDrain = true
×
340
                        return
×
341
                }
342
        }
343
        return
1✔
344
}
345

346
func (p *GenericPlugin) addVfioDesiredKernelArg(state *sriovnetworkv1.SriovNetworkNodeState) {
1✔
347
        driverState := p.DriverStateMap[Vfio]
1✔
348
        if !driverState.DriverLoaded && driverState.NeedDriverFunc(state, driverState) {
1✔
349
                p.addToDesiredKernelArgs(consts.KernelArgIntelIommu)
×
350
                p.addToDesiredKernelArgs(consts.KernelArgIommuPt)
×
351
        }
×
352
}
353

354
func (p *GenericPlugin) needRebootNode(state *sriovnetworkv1.SriovNetworkNodeState) (needReboot bool, err error) {
1✔
355
        needReboot = false
1✔
356
        p.addVfioDesiredKernelArg(state)
1✔
357

1✔
358
        updateNode, err := p.syncDesiredKernelArgs()
1✔
359
        if err != nil {
1✔
360
                log.Log.Error(err, "generic plugin needRebootNode(): failed to set the desired kernel arguments")
×
361
                return false, err
×
362
        }
×
363
        if updateNode {
1✔
364
                log.Log.V(2).Info("generic plugin needRebootNode(): need reboot for updating kernel arguments")
×
365
                needReboot = true
×
366
        }
×
367
        return needReboot, nil
1✔
368
}
369

370
// ////////////// for testing purposes only ///////////////////////
371
func (p *GenericPlugin) getDriverStateMap() DriverStateMapType {
1✔
372
        return p.DriverStateMap
1✔
373
}
1✔
374

375
func (p *GenericPlugin) loadDriverForTests(state *sriovnetworkv1.SriovNetworkNodeState) {
1✔
376
        for _, driverState := range p.DriverStateMap {
2✔
377
                if !driverState.DriverLoaded && driverState.NeedDriverFunc(state, driverState) {
2✔
378
                        driverState.DriverLoaded = true
1✔
379
                }
1✔
380
        }
381
}
382

383
//////////////////////////////////////////////////////////////////
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