diff --git a/internal/runtimehandlerhooks/high_performance_hooks_linux.go b/internal/runtimehandlerhooks/high_performance_hooks_linux.go index 14a7148ed05..f3f1fbc558a 100644 --- a/internal/runtimehandlerhooks/high_performance_hooks_linux.go +++ b/internal/runtimehandlerhooks/high_performance_hooks_linux.go @@ -61,12 +61,54 @@ const ( SharedCPUsEnvVar = "OPENSHIFT_SHARED_CPUS" ) +// ServiceManager interface for managing system services. +type ServiceManager interface { + IsServiceEnabled(serviceName string) bool + RestartService(serviceName string) error +} + +// CommandRunner interface for running external commands. +type CommandRunner interface { + LookPath(file string) (string, error) + RunCommand(name string, env []string, arg ...string) error +} + +// Default implementations. +type defaultServiceManager struct{} + +func (d *defaultServiceManager) IsServiceEnabled(serviceName string) bool { + return isServiceEnabled(serviceName) +} + +func (d *defaultServiceManager) RestartService(serviceName string) error { + return restartService(serviceName) +} + +type defaultCommandRunner struct{} + +func (d *defaultCommandRunner) LookPath(file string) (string, error) { + return exec.LookPath(file) +} + +func (d *defaultCommandRunner) RunCommand(name string, env []string, arg ...string) error { + cmd := cmdrunner.Command(name, arg...) + if len(env) > 0 { + cmd.Env = env + } + + return cmd.Run() +} + +var ( + serviceManager ServiceManager = &defaultServiceManager{} + commandRunner CommandRunner = &defaultCommandRunner{} +) + // HighPerformanceHooks used to run additional hooks that will configure a system for the latency sensitive workloads. type HighPerformanceHooks struct { irqBalanceConfigFile string cpusetLock sync.Mutex - irqSMPAffinityFileLock sync.Mutex - irqBalanceConfigFileLock sync.Mutex + updateIRQSMPAffinityLock sync.Mutex sharedCPUs string irqSMPAffinityFile string } @@ -197,13 +239,6 @@ func (h *HighPerformanceHooks) PreStop(ctx context.Context, c *oci.Container, s return nil } - // enable the IRQ smp balancing for the container CPUs - if shouldIRQLoadBalancingBeDisabled(ctx, s.Annotations()) { - if err := h.setIRQLoadBalancing(ctx, c, true); err != nil { - return fmt.Errorf("set IRQ load balancing: %w", err) - } - } - // disable the CPU load balancing for the container CPUs if shouldCPULoadBalancingBeDisabled(ctx, s.Annotations()) { podManager, containerManagers, err := libctrManagersForPodAndContainerCgroup(c, s.CgroupParent()) @@ -240,13 +275,23 @@ func (h *HighPerformanceHooks) PreStop(ctx context.Context, c *oci.Container, s } // If CPU load balancing is enabled, then *all* containers must run this PostStop hook. -func (*HighPerformanceHooks) PostStop(ctx context.Context, c *oci.Container, s *sandbox.Sandbox) error { +func (h *HighPerformanceHooks) PostStop(ctx context.Context, c *oci.Container, s *sandbox.Sandbox) error { + cSpec := c.Spec() + if shouldRunHooks(ctx, c.ID(), &cSpec, s) { + // enable the IRQ smp balancing for the container CPUs + if shouldIRQLoadBalancingBeDisabled(ctx, s.Annotations()) { + if err := h.setIRQLoadBalancing(ctx, c, true); err != nil { + return fmt.Errorf("set IRQ load balancing: %w", err) + } + } + } + // We could check if `!cpuLoadBalancingAllowed()` here, but it requires access to the config, which would be // odd to plumb. Instead, always assume if they're using a HighPerformanceHook, they have CPULoadBalanceDisabled // annotation allowed. - h := &DefaultCPULoadBalanceHooks{} + dh := &DefaultCPULoadBalanceHooks{} - return h.PostStop(ctx, c, s) + return dh.PostStop(ctx, c, s) } func shouldCPULoadBalancingBeDisabled(ctx context.Context, annotations fields.Set) bool { @@ -572,6 +617,16 @@ func disableCPULoadBalancingV1(containerManagers []cgroups.Manager) error { return nil } +// setIRQLoadBalancing configures interrupt load balancing for container CPUs by updating +// the IRQ SMP affinity mask and IRQ balance service configuration. It then handles IRQ balance restart. +// When enable is false (= container added), removes container CPUs from interrupt handling to reduce latency; +// when true (= container removed), restores them. +// The entire function after reading IRQ SMP affinity must be wrapped inside a single lock to avoid race conditions. +// The reason for this is that once we read from the SMP IRQ affinity file, we have to calculate new masks and +// write those masks to /proc/irq/default_smp_affinity and /etc/sysconfig/irqbalance. +// We also must restart irqbalance or run irqbalance --oneshot within the same lock. +// Without this lock, 2 threads could read from the file and calculate the new mask but overwrite the +// results of each other, or start irbalance --oneshot with different values. func (h *HighPerformanceHooks) setIRQLoadBalancing(ctx context.Context, c *oci.Container, enable bool) error { lspec := c.Spec().Linux if lspec == nil || @@ -581,69 +636,111 @@ func (h *HighPerformanceHooks) setIRQLoadBalancing(ctx context.Context, c *oci.C return fmt.Errorf("find container %s CPUs", c.ID()) } - newIRQBalanceSetting, err := h.updateNewIRQSMPAffinityMask(lspec.Resources.CPU.Cpus, enable) + h.updateIRQSMPAffinityLock.Lock() + defer h.updateIRQSMPAffinityLock.Unlock() + + newIRQBalanceSetting, err := h.updateNewIRQSMPAffinityMask(ctx, c.ID(), c.Name(), lspec.Resources.CPU.Cpus, enable) if err != nil { return err } + // Now, restart the irqbalance service or run irqbalance --oneshot command. + // On failure, this will log errors but will not return them, as it is not critical for the pod to start. + if !h.handleIRQBalanceRestart(ctx, c.Name()) { + h.handleIRQBalanceOneShot(ctx, c.Name(), newIRQBalanceSetting) + } - isIrqConfigExists := fileExists(h.irqBalanceConfigFile) + return nil +} - if isIrqConfigExists { - if err := h.updateIrqBalanceConfigFile(newIRQBalanceSetting); err != nil { - return err - } +// handleIRQBalanceRestart handles the restart of the irqbalance service. +func (h *HighPerformanceHooks) handleIRQBalanceRestart(ctx context.Context, cName string) bool { + // If the irqbalance service is enabled, restart it and return. + // systemd's StartLimitBurst might cause issues here when container restarts occur in very + // quick succession and the parameter must be reconfigured for this to work correctly. + // See: + // https://github.com/cri-o/cri-o/pull/8834/commits/b96928dcbb7956e0ebde42238e88955831411216 + if !serviceManager.IsServiceEnabled(irqBalancedName) || !fileExists(h.irqBalanceConfigFile) { + return false } - if !isServiceEnabled(irqBalancedName) || !isIrqConfigExists { - if _, err := exec.LookPath(irqBalancedName); err != nil { - // irqbalance is not installed, skip the rest; pod should still start, so return nil instead - log.Warnf(ctx, "Irqbalance binary not found: %v", err) + log.Debugf(ctx, "Container %q restarting irqbalance service", cName) - return nil - } - // run irqbalance in daemon mode, so this won't cause delay - cmd := cmdrunner.Command(irqBalancedName, "--oneshot") - additionalEnv := irqBalanceBannedCpus + "=" + newIRQBalanceSetting - cmd.Env = append(os.Environ(), additionalEnv) + if err := serviceManager.RestartService(irqBalancedName); err != nil { + log.Warnf(ctx, "Irqbalance service restart failed: %v", err) - return cmd.Run() + return false } - if err := restartIrqBalanceService(); err != nil { - log.Warnf(ctx, "Irqbalance service restart failed: %v", err) + return true +} + +// handleIRQBalanceOneShot runs irqbalance --oneshot command. +func (h *HighPerformanceHooks) handleIRQBalanceOneShot(ctx context.Context, cName, newIRQBalanceSetting string) { + irqBalanceFullPath, err := commandRunner.LookPath(irqBalancedName) + if err != nil { + // irqbalance is not installed, skip the rest; pod should still start, so return nil instead. + log.Warnf(ctx, "Irqbalance binary not found: %v", err) + + return } - return nil -} + env := fmt.Sprintf("%s=%s", irqBalanceBannedCpus, newIRQBalanceSetting) + log.Debugf(ctx, "Container %q running '%s %s %s'", cName, env, irqBalanceFullPath, "--oneshot") -func (h *HighPerformanceHooks) updateNewIRQSMPAffinityMask(cpus string, enable bool) (string, error) { - h.irqSMPAffinityFileLock.Lock() - defer h.irqSMPAffinityFileLock.Unlock() + if err := commandRunner.RunCommand( + irqBalanceFullPath, + []string{env}, + "--oneshot", + ); err != nil { + log.Warnf(ctx, "Container %q failed to run '%s %s %s', err: %q", + cName, env, irqBalanceFullPath, "--oneshot", err) + } +} +// updateNewIRQSMPAffinityMask updates SMP IRQ affinity and IRQ balance configuration files. +func (h *HighPerformanceHooks) updateNewIRQSMPAffinityMask(ctx context.Context, cID, cName, cpus string, enable bool) (newIRQBalanceSetting string, err error) { content, err := os.ReadFile(h.irqSMPAffinityFile) if err != nil { return "", err } - currentIRQSMPSetting := strings.TrimSpace(string(content)) + originalIRQSMPSetting := strings.TrimSpace(string(content)) - newIRQSMPSetting, newIRQBalanceSetting, err := calcIRQSMPAffinityMask(cpus, currentIRQSMPSetting, enable) + newIRQSMPSetting, newIRQBalanceSetting, err := calcIRQSMPAffinityMask(cpus, originalIRQSMPSetting, enable) if err != nil { return "", err } + log.Debugf(ctx, "Container %q (%q) enable %t cpus %q set %q: %q -> %q; %q: %q", + cID, cName, enable, cpus, + h.irqSMPAffinityFile, originalIRQSMPSetting, newIRQSMPSetting, + h.irqBalanceConfigFile, newIRQBalanceSetting, + ) + if err := os.WriteFile(h.irqSMPAffinityFile, []byte(newIRQSMPSetting), 0o644); err != nil { return "", err } - return newIRQBalanceSetting, nil -} + // Rollback IRQ SMP affinity file to maintain consistency if something goes wrong. + defer func() { + if err != nil { + if rollbackErr := os.WriteFile(h.irqSMPAffinityFile, []byte(originalIRQSMPSetting), 0o644); rollbackErr != nil { + log.Errorf(ctx, "Failed to rollback IRQ SMP affinity file after config update failure: err: %q, rollback err: %q", + err, rollbackErr) + } + } + }() + + // Nothing else to do if irq balance config file does not exist. + if !fileExists(h.irqBalanceConfigFile) { + return newIRQBalanceSetting, nil + } -func (h *HighPerformanceHooks) updateIrqBalanceConfigFile(newIRQBalanceSetting string) error { - h.irqBalanceConfigFileLock.Lock() - defer h.irqBalanceConfigFileLock.Unlock() + if err := updateIrqBalanceConfigFile(h.irqBalanceConfigFile, newIRQBalanceSetting); err != nil { + return "", err + } - return updateIrqBalanceConfigFile(h.irqBalanceConfigFile, newIRQBalanceSetting) + return newIRQBalanceSetting, nil } func setCPUQuota(podManager cgroups.Manager, containerManagers []cgroups.Manager) error { @@ -1047,8 +1144,8 @@ func RestoreIrqBalanceConfig(ctx context.Context, irqBalanceConfigFile, irqBanne return err } - if isServiceEnabled(irqBalancedName) { - if err := restartIrqBalanceService(); err != nil { + if serviceManager.IsServiceEnabled(irqBalancedName) { + if err := serviceManager.RestartService(irqBalancedName); err != nil { log.Warnf(ctx, "Irqbalance service restart failed: %v", err) } } diff --git a/internal/runtimehandlerhooks/high_performance_hooks_test.go b/internal/runtimehandlerhooks/high_performance_hooks_test.go index 9fb089f2ef1..868a8765f91 100644 --- a/internal/runtimehandlerhooks/high_performance_hooks_test.go +++ b/internal/runtimehandlerhooks/high_performance_hooks_test.go @@ -2,6 +2,8 @@ package runtimehandlerhooks import ( "context" + "errors" + "fmt" "os" "path/filepath" "strconv" @@ -37,6 +39,66 @@ const ( governorUserspace = "userspace" ) +type mockServiceManager struct { + isServiceEnabled map[string]bool + restartService map[string]error + history []string +} + +func (m *mockServiceManager) IsServiceEnabled(serviceName string) bool { + m.history = append(m.history, "systemctl is-enabled "+serviceName) + if m.isServiceEnabled == nil { + return false + } + + return m.isServiceEnabled[serviceName] +} + +func (m *mockServiceManager) RestartService(serviceName string) error { + m.history = append(m.history, "systemctl restart "+serviceName) + if m.restartService == nil { + return errors.New("service not found") + } + + if _, ok := m.restartService[serviceName]; !ok { + return errors.New("service not found") + } + + return m.restartService[serviceName] +} + +type mockCommandRunner struct { + lookPath map[string]struct { + path string + err error + } + history []string +} + +func (m *mockCommandRunner) LookPath(file string) (string, error) { + m.history = append(m.history, "which "+file) + if m.lookPath == nil { + return "", errors.New("path not found") + } + + if _, ok := m.lookPath[file]; !ok { + return "", errors.New("path not found") + } + + return m.lookPath[file].path, m.lookPath[file].err +} + +func (m *mockCommandRunner) RunCommand(name string, env []string, arg ...string) error { + m.history = append(m.history, fmt.Sprintf( + "%s %s %s", + strings.Join(env, " "), + name, + strings.Join(arg, " "), + )) + + return nil +} + // The actual test suite. var _ = Describe("high_performance_hooks", func() { container, err := oci.NewContainer("containerID", "", "", "", @@ -551,9 +613,12 @@ var _ = Describe("high_performance_hooks", func() { }) Describe("restoreIrqBalanceConfig", func() { + var mockSvcMgr *mockServiceManager + irqSmpAffinityFile := filepath.Join(fixturesDir, "irq_smp_affinity") irqBalanceConfigFile := filepath.Join(fixturesDir, "irqbalance") irqBannedCPUConfigFile := filepath.Join(fixturesDir, "orig_irq_banned_cpus") + verifyRestoreIrqBalanceConfig := func(expectedOrigBannedCPUs, expectedBannedCPUs string) { err = RestoreIrqBalanceConfig(context.TODO(), irqBalanceConfigFile, irqBannedCPUConfigFile, irqSmpAffinityFile) ExpectWithOffset(1, err).ToNot(HaveOccurred()) @@ -579,6 +644,21 @@ var _ = Describe("high_performance_hooks", func() { bannedCPUs, err := retrieveIrqBannedCPUMasks(irqBalanceConfigFile) Expect(err).ToNot(HaveOccurred()) Expect(bannedCPUs).To(Equal("0000ffff,ffffcfcc")) + + mockSvcMgr = &mockServiceManager{ + isServiceEnabled: map[string]bool{ + "irqbalance": true, + }, + restartService: map[string]error{ + "irqbalance": nil, + }, + history: []string{}, + } + serviceManager = mockSvcMgr + }) + + JustAfterEach(func() { + serviceManager = &defaultServiceManager{} }) Context("when banned cpu config file doesn't exist", func() { @@ -589,6 +669,8 @@ var _ = Describe("high_performance_hooks", func() { It("should set banned cpu config file from irq balance config", func() { verifyRestoreIrqBalanceConfig("0000ffff,ffffcfcc", "0000ffff,ffffcfcc") + Expect(mockSvcMgr.history).NotTo(ContainElement("systemctl is-enabled irqbalance")) + Expect(mockSvcMgr.history).NotTo(ContainElement("systemctl restart irqbalance")) }) }) @@ -602,10 +684,189 @@ var _ = Describe("high_performance_hooks", func() { It("should restore irq balance config with content from banned cpu config file", func() { verifyRestoreIrqBalanceConfig("00000000,00000000", "00000000,00000000") + Expect(mockSvcMgr.history).To(ContainElement("systemctl is-enabled irqbalance")) + Expect(mockSvcMgr.history).To(ContainElement("systemctl restart irqbalance")) }) }) }) + Describe("handleIRQBalanceRestart", func() { + irqBalanceConfigFile := filepath.Join(fixturesDir, "irqbalance") + + h := &HighPerformanceHooks{ + irqBalanceConfigFile: irqBalanceConfigFile, + } + + type parameters struct { + isServiceEnabled bool + irqBalanceFileExists bool + restartServiceSucceeds bool + pathLookupError bool + calculatedIRQBalanceMask string + } + + DescribeTable("handleIRQBalanceRestart scenarios", + func(p parameters, serviceMgrHistory, cmdRunnerHistory []string) { + defer func() { + // Reset global mocks. + serviceManager = &defaultServiceManager{} + commandRunner = &defaultCommandRunner{} + }() + + // Setup mocks according to parameters and irqbalance config file. + mockSvcMgr := &mockServiceManager{ + isServiceEnabled: map[string]bool{ + "irqbalance": p.isServiceEnabled, + }, + history: []string{}, + } + mockCmdRunner := &mockCommandRunner{ + history: []string{}, + } + + if p.restartServiceSucceeds { + mockSvcMgr.restartService = map[string]error{ + "irqbalance": nil, + } + } else { + mockSvcMgr.restartService = map[string]error{ + "irqbalance": errors.New("restart failed"), + } + } + + if p.pathLookupError { + mockCmdRunner.lookPath = map[string]struct { + path string + err error + }{ + "irqbalance": {path: "", err: errors.New("not found")}, + } + } else { + mockCmdRunner.lookPath = map[string]struct { + path string + err error + }{ + "irqbalance": {path: "/usr/bin/irqbalance", err: nil}, + } + } + if p.irqBalanceFileExists { + err = os.WriteFile(irqBalanceConfigFile, []byte(""), 0o644) + Expect(err).ToNot(HaveOccurred()) + err = updateIrqBalanceConfigFile(irqBalanceConfigFile, p.calculatedIRQBalanceMask) + Expect(err).ToNot(HaveOccurred()) + } + serviceManager = mockSvcMgr + commandRunner = mockCmdRunner + + // Execute application logic. + if !h.handleIRQBalanceRestart(context.TODO(), "container-name") { + h.handleIRQBalanceOneShot(context.TODO(), "container-name", p.calculatedIRQBalanceMask) + } + + // Verify behavior based on scenario. + Expect(mockSvcMgr.history).To(Equal(serviceMgrHistory)) + Expect(mockCmdRunner.history).To(Equal(cmdRunnerHistory)) + }, + Entry("irqbalance is enabled and succeeds", + parameters{isServiceEnabled: true, irqBalanceFileExists: true, restartServiceSucceeds: true, pathLookupError: false, calculatedIRQBalanceMask: "ffff,ffff"}, + []string{ + "systemctl is-enabled irqbalance", + "systemctl restart irqbalance", + }, + []string{}), + Entry("irqbalance is enabled but irqbalance file does not exist", + parameters{isServiceEnabled: true, irqBalanceFileExists: false, restartServiceSucceeds: false, pathLookupError: false, calculatedIRQBalanceMask: "ffff,ffff"}, + []string{ + "systemctl is-enabled irqbalance", + }, + []string{ + "which irqbalance", + "IRQBALANCE_BANNED_CPUS=ffff,ffff /usr/bin/irqbalance --oneshot", + }), + Entry("irqbalance is enabled and fails but oneshot works", + parameters{isServiceEnabled: true, irqBalanceFileExists: true, restartServiceSucceeds: false, pathLookupError: false, calculatedIRQBalanceMask: "ffff,ffff"}, + []string{ + "systemctl is-enabled irqbalance", + "systemctl restart irqbalance", + }, + []string{ + "which irqbalance", + "IRQBALANCE_BANNED_CPUS=ffff,ffff /usr/bin/irqbalance --oneshot", + }), + Entry("irqbalance is disabled but irqBalance file exists", + parameters{isServiceEnabled: false, irqBalanceFileExists: true, restartServiceSucceeds: false, pathLookupError: false, calculatedIRQBalanceMask: "ffff,ffff"}, + []string{ + "systemctl is-enabled irqbalance", + }, + []string{ + "which irqbalance", + "IRQBALANCE_BANNED_CPUS=ffff,ffff /usr/bin/irqbalance --oneshot", + }), + Entry("irqbalance is disabled, oneshot lookup fails", + parameters{isServiceEnabled: false, irqBalanceFileExists: true, restartServiceSucceeds: false, pathLookupError: true, calculatedIRQBalanceMask: "ffff,ffff"}, + []string{ + "systemctl is-enabled irqbalance", + }, + []string{ + "which irqbalance", + }), + ) + }) + + Describe("updateNewIRQSMPAffinityMask rollback", func() { + irqBalanceConfigFile := filepath.Join(fixturesDir, "irqbalance") + irqSMPAffinityFile := filepath.Join(fixturesDir, "irqsmpaffinity") + + h := &HighPerformanceHooks{ + irqSMPAffinityFile: irqSMPAffinityFile, + irqBalanceConfigFile: irqBalanceConfigFile, + } + + type parameters struct { + irqBalanceFileRO bool + originalIRQSMPAffinityMask string + expectedIRQSMPAffinityMask string + } + + DescribeTable("test rollback", + func(p parameters) { + err := os.WriteFile(irqSMPAffinityFile, []byte(p.originalIRQSMPAffinityMask), 0o644) + Expect(err).ToNot(HaveOccurred()) + + if p.irqBalanceFileRO { + err = os.Symlink("/proc/version", irqBalanceConfigFile) + Expect(err).ToNot(HaveOccurred()) + } else { + err = os.WriteFile(irqBalanceConfigFile, []byte(""), 0o644) + Expect(err).ToNot(HaveOccurred()) + } + + _, err = h.updateNewIRQSMPAffinityMask(context.TODO(), "cID", "CName", "2-3", false) + if p.irqBalanceFileRO { + Expect(err).To(HaveOccurred()) + } else { + Expect(err).NotTo(HaveOccurred()) + } + + writtenMask, err := os.ReadFile(irqSMPAffinityFile) + Expect(err).NotTo(HaveOccurred()) + Expect(string(writtenMask)).To(Equal(p.expectedIRQSMPAffinityMask)) + }, + Entry("writing IRQ balance file fails", + parameters{ + irqBalanceFileRO: true, + originalIRQSMPAffinityMask: "ffffffff", + expectedIRQSMPAffinityMask: "ffffffff", + }), + Entry("writing IRQ balance file succeeds", + parameters{ + irqBalanceFileRO: false, + originalIRQSMPAffinityMask: "ffffffff", + expectedIRQSMPAffinityMask: "fffffff3", + }), + ) + }) + Describe("convertAnnotationToLatency", func() { verifyConvertAnnotationToLatency := func(annotation string, expected string, expect_error bool) { latency, err := convertAnnotationToLatency(annotation) @@ -774,16 +1035,25 @@ var _ = Describe("high_performance_hooks", func() { var cfg *config.Config var hooksRetriever *HooksRetriever + formatIRQBalanceBannedCPUs := func(v string) string { + return fmt.Sprintf("%s=%q", irqBalanceBannedCpus, v) + } + irqSmpAffinityFile := filepath.Join(fixturesDir, "irq_smp_affinity") irqBalanceConfigFile := filepath.Join(fixturesDir, "irqbalance") flags = "0000,0000ffff" + bannedCPUFlags = "ffffffff,ffff0000" ctx := context.Background() - verifySetIRQLoadBalancing := func(expected string) { + verifySetIRQLoadBalancing := func(expectedIrqSmp, expectedIrqBalance string) { content, err := os.ReadFile(irqSmpAffinityFile) Expect(err).ToNot(HaveOccurred()) - Expect(strings.Trim(string(content), "\n")).To(Equal(expected)) + Expect(strings.Trim(string(content), "\n")).To(Equal(expectedIrqSmp)) + + content, err = os.ReadFile(irqBalanceConfigFile) + Expect(err).ToNot(HaveOccurred()) + Expect(strings.Trim(string(content), "\n")).To(Equal(formatIRQBalanceBannedCPUs(expectedIrqBalance))) } createContainer := func(cpus string) (*oci.Container, error) { @@ -821,6 +1091,8 @@ var _ = Describe("high_performance_hooks", func() { // create tests affinity file err = os.WriteFile(irqSmpAffinityFile, []byte(flags), 0o644) Expect(err).ToNot(HaveOccurred()) + err = os.WriteFile(irqBalanceConfigFile, []byte(formatIRQBalanceBannedCPUs(bannedCPUFlags)), 0o644) + Expect(err).ToNot(HaveOccurred()) sbox := sandbox.NewBuilder() createdAt := time.Now() @@ -878,6 +1150,7 @@ var _ = Describe("high_performance_hooks", func() { Expect(hooks).NotTo(BeNil()) if hph, ok := hooks.(*HighPerformanceHooks); ok { hph.irqSMPAffinityFile = irqSmpAffinityFile + hph.irqBalanceConfigFile = irqBalanceConfigFile } var wg sync.WaitGroup for cpu := range 16 { @@ -891,7 +1164,7 @@ var _ = Describe("high_performance_hooks", func() { }() } wg.Wait() - verifySetIRQLoadBalancing("00000000,00000000") + verifySetIRQLoadBalancing("00000000,00000000", "ffffffff,ffffffff") }) }) @@ -918,6 +1191,7 @@ var _ = Describe("high_performance_hooks", func() { hph, ok := hooks.(*HighPerformanceHooks) Expect(ok).To(BeTrue()) hph.irqSMPAffinityFile = irqSmpAffinityFile + hph.irqBalanceConfigFile = irqBalanceConfigFile var wg sync.WaitGroup for cpu := range 16 { @@ -931,7 +1205,7 @@ var _ = Describe("high_performance_hooks", func() { }() } wg.Wait() - verifySetIRQLoadBalancing(flags) + verifySetIRQLoadBalancing(flags, bannedCPUFlags) }) }) @@ -959,6 +1233,7 @@ var _ = Describe("high_performance_hooks", func() { Expect(hooks).NotTo(BeNil()) if hph, ok := hooks.(*HighPerformanceHooks); ok { hph.irqSMPAffinityFile = irqSmpAffinityFile + hph.irqBalanceConfigFile = irqBalanceConfigFile } var wg sync.WaitGroup for cpu := range 16 { @@ -972,7 +1247,7 @@ var _ = Describe("high_performance_hooks", func() { }() } wg.Wait() - verifySetIRQLoadBalancing("00000000,00000000") + verifySetIRQLoadBalancing("00000000,00000000", "ffffffff,ffffffff") }) }) @@ -1023,6 +1298,7 @@ var _ = Describe("high_performance_hooks", func() { Expect(hooks).NotTo(BeNil()) if hph, ok := hooks.(*HighPerformanceHooks); ok { hph.irqSMPAffinityFile = irqSmpAffinityFile + hph.irqBalanceConfigFile = irqBalanceConfigFile } var wg sync.WaitGroup for cpu := range 16 { @@ -1036,7 +1312,7 @@ var _ = Describe("high_performance_hooks", func() { }() } wg.Wait() - verifySetIRQLoadBalancing("00000000,00000000") + verifySetIRQLoadBalancing("00000000,00000000", "ffffffff,ffffffff") }) }) @@ -1084,7 +1360,7 @@ var _ = Describe("high_performance_hooks", func() { }() } wg.Wait() - verifySetIRQLoadBalancing(flags) + verifySetIRQLoadBalancing(flags, bannedCPUFlags) }) }) }) diff --git a/internal/runtimehandlerhooks/runtime_handler_hooks_linux.go b/internal/runtimehandlerhooks/runtime_handler_hooks_linux.go index facd0347858..f0d791b038f 100644 --- a/internal/runtimehandlerhooks/runtime_handler_hooks_linux.go +++ b/internal/runtimehandlerhooks/runtime_handler_hooks_linux.go @@ -46,8 +46,7 @@ func (hr *HooksRetriever) Get(runtimeName string, sandboxAnnotations map[string] hr.highPerformanceHooks = &HighPerformanceHooks{ irqBalanceConfigFile: hr.config.IrqBalanceConfigFile, cpusetLock: sync.Mutex{}, - irqSMPAffinityFileLock: sync.Mutex{}, - irqBalanceConfigFileLock: sync.Mutex{}, + updateIRQSMPAffinityLock: sync.Mutex{}, sharedCPUs: hr.config.SharedCPUSet, irqSMPAffinityFile: IrqSmpAffinityProcFile, } diff --git a/internal/runtimehandlerhooks/utils_linux.go b/internal/runtimehandlerhooks/utils_linux.go index e93931a15c1..b7ab84ae2d9 100644 --- a/internal/runtimehandlerhooks/utils_linux.go +++ b/internal/runtimehandlerhooks/utils_linux.go @@ -155,8 +155,8 @@ func calcIRQSMPAffinityMask(cpus, current string, set bool) (cpuMask, bannedCPUM return maskStringWithComma, invertedMaskStringWithComma, nil } -func restartIrqBalanceService() error { - return cmdrunner.Command("systemctl", "restart", "irqbalance").Run() +func restartService(serviceName string) error { + return cmdrunner.Command("systemctl", "restart", serviceName).Run() } func isServiceEnabled(serviceName string) bool {