diff --git a/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2.java b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2.java index be3cf5ed61..27f2c3ec3a 100644 --- a/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2.java +++ b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2.java @@ -5,8 +5,12 @@ package com.aws.greengrass.util.platforms.unix.linux; +import com.aws.greengrass.util.Utils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -20,11 +24,12 @@ public enum CgroupV2 { private static final String CGROUP_ROOT = "/sys/fs/cgroup"; private static final String GG_NAMESPACE = "greengrass"; - private static final String CGROUP_MEMORY_LIMITS = "memory.limit_in_bytes"; - private static final String CPU_CFS_PERIOD_US = "cpu.cfs_period_us"; - private static final String CPU_CFS_QUOTA_US = "cpu.cfs_quota_us"; + private static final String CPU_MAX = "cpu.max"; + private static final String MEMORY_MAX = "memory.max"; private static final String CGROUP_PROCS = "cgroup.procs"; + private static final String CGROUP_SUBTREE_CONTROL = "cgroup.subtree_control"; private static final String FREEZER_STATE_FILE = "freezer.state"; + private static final String CGROUP_FREEZE = "cgroup.freeze"; private final String osString; CgroupV2(String str) { @@ -43,31 +48,35 @@ public Path getSubsystemRootPath() { return Paths.get(CGROUP_ROOT); } + public Path getRootSubTreeControlPath() { + return getSubsystemRootPath().resolve(CGROUP_SUBTREE_CONTROL); + } + public Path getSubsystemGGPath() { return getSubsystemRootPath().resolve(GG_NAMESPACE); } - public Path getSubsystemComponentPath(String componentName) { - return getSubsystemGGPath().resolve(componentName); + public Path getGGSubTreeControlPath() { + return getSubsystemGGPath().resolve(CGROUP_SUBTREE_CONTROL); } - public Path getComponentMemoryLimitPath(String componentName) { - return getSubsystemComponentPath(componentName).resolve(CGROUP_MEMORY_LIMITS); + public Path getSubsystemComponentPath(String componentName) { + return getSubsystemGGPath().resolve(componentName); } - public Path getComponentCpuPeriodPath(String componentName) { - return getSubsystemComponentPath(componentName).resolve(CPU_CFS_PERIOD_US); + public Path getComponentCpuMaxPath(String componentName) { + return getSubsystemComponentPath(componentName).resolve(CPU_MAX); } - public Path getComponentCpuQuotaPath(String componentName) { - return getSubsystemComponentPath(componentName).resolve(CPU_CFS_QUOTA_US); + public Path getComponentMemoryMaxPath(String componentName) { + return getSubsystemComponentPath(componentName).resolve(MEMORY_MAX); } public Path getCgroupProcsPath(String componentName) { return getSubsystemComponentPath(componentName).resolve(CGROUP_PROCS); } - - public Path getCgroupFreezerStateFilePath(String componentName) { - return getSubsystemComponentPath(componentName).resolve(FREEZER_STATE_FILE); + + public Path getCgroupFreezePath(String componentName) { + return getSubsystemComponentPath(componentName).resolve(CGROUP_FREEZE); } } diff --git a/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2FreezerState.java b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2FreezerState.java new file mode 100644 index 0000000000..09ebdb79b1 --- /dev/null +++ b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/CgroupV2FreezerState.java @@ -0,0 +1,26 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.aws.greengrass.util.platforms.unix.linux; + +public enum CgroupV2FreezerState { + THAWED(0), + FROZEN(1); + + private int index; + + CgroupV2FreezerState(int index) { + this.index = index; + } + + /** + * Get the index value associated with this CgroupV2FreezerState. + * + * @return the integer index value associated with this CgroupV2FreezerState. + */ + public int getIndex() { + return index; + } +} diff --git a/src/main/java/com/aws/greengrass/util/platforms/unix/linux/LinuxSystemResourceControllerV2.java b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/LinuxSystemResourceControllerV2.java index e3ea77f6f3..f3231f61cf 100644 --- a/src/main/java/com/aws/greengrass/util/platforms/unix/linux/LinuxSystemResourceControllerV2.java +++ b/src/main/java/com/aws/greengrass/util/platforms/unix/linux/LinuxSystemResourceControllerV2.java @@ -11,6 +11,7 @@ import com.aws.greengrass.util.Coerce; import com.aws.greengrass.util.Utils; import com.aws.greengrass.util.platforms.SystemResourceController; +import org.apache.commons.lang3.StringUtils; import org.zeroturnaround.process.PidUtil; import java.io.IOException; @@ -38,7 +39,9 @@ public class LinuxSystemResourceControllerV2 implements SystemResourceController private static final String COMPONENT_NAME = "componentName"; private static final String MEMORY_KEY = "memory"; private static final String CPUS_KEY = "cpus"; + private static final String UNICODE_SPACE = "\\040"; + private static final String CGROUP_SUBTREE_CONTROL_CONTENT = "+cpuset +cpu +io +memory +pids"; private static final List RESOURCE_LIMIT_CGROUPS = Arrays.asList(CgroupV2.Memory, CgroupV2.CPU); private final CopyOnWriteArrayList usedCgroups = new CopyOnWriteArrayList<>(); @@ -71,9 +74,10 @@ public void updateResourceLimits(GreengrassService component, Map 0) { String memoryLimit = Long.toString(memoryLimitInKB * ONE_KB); - Files.write(CgroupV2.Memory.getComponentMemoryLimitPath(component.getServiceName()), + Files.write(CgroupV2.Memory.getComponentMemoryMaxPath(component.getServiceName()), memoryLimit.getBytes(StandardCharsets.UTF_8)); } else { logger.atWarn().kv(COMPONENT_NAME, component.getServiceName()).kv(MEMORY_KEY, memoryLimitInKB) @@ -85,14 +89,26 @@ public void updateResourceLimits(GreengrassService component, Map 0) { byte[] content = Files.readAllBytes( - CgroupV2.CPU.getComponentCpuPeriodPath(component.getServiceName())); - int cpuPeriodUs = Integer.parseInt(new String(content, StandardCharsets.UTF_8).trim()); - - int cpuQuotaUs = (int) (cpuPeriodUs * cpu); - String cpuQuotaUsStr = Integer.toString(cpuQuotaUs); + CgroupV2.CPU.getComponentCpuMaxPath(component.getServiceName())); + String cpuMaxContent = new String(content, StandardCharsets.UTF_8).trim(); + String[] cpuMaxContentArr = cpuMaxContent.split(" "); + String cpuMaxStr = "max"; + String cpuPeriodStr = "100000"; + + if (cpuMaxContentArr.length >= 2) { + cpuMaxStr = cpuMaxContentArr[0]; + cpuPeriodStr = cpuMaxContentArr[1]; + + if (!StringUtils.isEmpty(cpuPeriodStr)) { + int period = Integer.parseInt(cpuPeriodStr.trim()); + int max = (int) (period * cpu); + cpuMaxStr = Integer.toString(max); + } + } - Files.write(CgroupV2.CPU.getComponentCpuQuotaPath(component.getServiceName()), - cpuQuotaUsStr.getBytes(StandardCharsets.UTF_8)); + String latestCpuMaxContent = String.format("%s %s", cpuMaxStr, cpuPeriodStr); + Files.write(CgroupV2.CPU.getComponentCpuMaxPath(component.getServiceName()), + latestCpuMaxContent.getBytes(StandardCharsets.UTF_8)); } else { logger.atWarn().kv(COMPONENT_NAME, component.getServiceName()).kv(CPUS_KEY, cpu) .log("The provided cpu limit is invalid"); @@ -151,10 +167,6 @@ public void pauseComponentProcesses(GreengrassService component, List p addComponentProcessToCgroup(component.getServiceName(), process, CgroupV2.Freezer); } - if (LinuxSystemResourceControllerV2.CgroupFreezerState.FROZEN.equals( - currentFreezerCgroupState(component.getServiceName()))) { - return; - } Files.write(freezerCgroupStateFile(component.getServiceName()), LinuxSystemResourceControllerV2.CgroupFreezerState.FROZEN.toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); @@ -162,10 +174,6 @@ public void pauseComponentProcesses(GreengrassService component, List p @Override public void resumeComponentProcesses(GreengrassService component) throws IOException { - if (LinuxSystemResourceControllerV2.CgroupFreezerState.THAWED.equals( - currentFreezerCgroupState(component.getServiceName()))) { - return; - } Files.write(freezerCgroupStateFile(component.getServiceName()), LinuxSystemResourceControllerV2.CgroupFreezerState.THAWED.toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); @@ -213,10 +221,10 @@ private void handleErrorAddingPidToCgroup(IOException e, String component) { // Check the exception message here to avoid the exception stacktrace failing the tests. if (e.getMessage() != null && e.getMessage().contains("No such process")) { logger.atWarn().kv(COMPONENT_NAME, component) - .log("Failed to add pid to the cgroup because the process doesn't exist anymore"); + .log("Failed to add pid to the cgroupv2 because the process doesn't exist anymore"); } else { logger.atError().setCause(e).kv(COMPONENT_NAME, component) - .log("Failed to add pid to the cgroup"); + .log("Failed to add pid to the cgroupv2"); } } @@ -251,16 +259,18 @@ private void initializeCgroup(GreengrassService component, CgroupV2 cgroup) thro if (!mounts.contains(CgroupV2.getRootPath().toString())) { platform.runCmd(CgroupV2.rootMountCmd(), o -> { }, "Failed to mount cgroup2 root"); - Files.createDirectory(cgroup.getSubsystemRootPath()); + Utils.createPaths(cgroup.getSubsystemRootPath()); } - if (!Files.exists(cgroup.getSubsystemGGPath())) { - Files.createDirectory(cgroup.getSubsystemGGPath()); - } + //Enable controllers for root group + Files.write(cgroup.getRootSubTreeControlPath(), + CGROUP_SUBTREE_CONTROL_CONTENT.getBytes(StandardCharsets.UTF_8)); - if (!Files.exists(cgroup.getSubsystemComponentPath(component.getServiceName()))) { - Files.createDirectory(cgroup.getSubsystemComponentPath(component.getServiceName())); - } + Utils.createPaths(cgroup.getSubsystemGGPath()); + //Enable controllers for gg group + Files.write(cgroup.getGGSubTreeControlPath(), + CGROUP_SUBTREE_CONTROL_CONTENT.getBytes(StandardCharsets.UTF_8)); + Utils.createPaths(cgroup.getSubsystemComponentPath(component.getServiceName())); usedCgroups.add(cgroup); } @@ -271,7 +281,7 @@ private Set pidsInComponentCgroup(CgroupV2 cgroup, String component) th } private Path freezerCgroupStateFile(String component) { - return Cgroup.Freezer.getCgroupFreezerStateFilePath(component); + return CgroupV2.Freezer.getCgroupFreezePath(component); } private LinuxSystemResourceControllerV2.CgroupFreezerState currentFreezerCgroupState(String component)