Cgroup Abstraction Layer

Android 10 and higher use a control group (cgroup) abstraction layer with task profiles, which developers can use to describe a set (or sets) of restrictions to apply to a thread or a process. The system then follows the prescribed actions of the task profiles to select one or more appropriate cgroups, through which restrictions are applied, and changes to the underlying cgroup feature set can be made without affecting higher software layers.

About cgroups

Cgroups provide a mechanism for aggregating and partitioning sets of tasks (which consist of processes, threads, and all their future children) into hierarchical groups with specialized behavior. Android uses cgroups to control and account for system resources such as CPU and memory usage and allocation, with support for Linux kernel cgroups v1 and cgroups v2.

Android 9 and lower

In Android 9 and lower, the init.rc initialization script contained the set of available cgroups, their mounting points, and versions. While these could be changed, the Android framework expected a specific set of cgroups to exist at specific locations with a specific version and subgroup hierarchy, based on the script. This limited the ability to choose the next cgroup version to use, or to change the cgroup hierarchy to use new features.

Android 10 and higher

Android 10 and higher use cgroups with task profiles:

  • Cgroup setup - developers describe the cgroups setup in their cgroups.json file to define sets of cgroups, and their mounting locations and attributes. All cgroups are mounted during the early-init stage of the initialization process.
  • Task profiles - these provide an abstraction that decouples the required functionality from the details of its implementation. The Android framework applies the task profiles as described in the task_profiles.json file to a process or thread using the SetTaskProfiles and SetProcessProfiles APIs. (These APIs are unique to Android 11 and higher.)

To provide backward compatibility, the legacy functions set_cpuset_policy, set_sched_policy, and get_sched_policy provide the same API and functionality, but their implementation has been modified to use task profiles. For new use cases AOSP recommends using new task profiles APIs instead of the legacy set_sched_policy function.

Cgroups description file

Cgroups are described in the cgroups.json file located under <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Each controller is described in a subsection and must have a minimum of the following:

  • Name, defined by the Controller field.
  • Mounting path, defined by the Path field.
  • Mode, UID (user ID), and GID (group ID) describing the owner and access modes for the files under this path (all optional).
  • Optional attribute, set to true to let the system ignore the mounting error caused by a cgroup controller that the kernel doesn’t support being mounted.

Example cgroups.json file

The example below shows descriptions for cgroup v1 (Cgroups) and cgroup v2 (Cgroups2) controllers with their respective paths.

{
  "Cgroups": [
    {
      "Controller": "cpu",
      "Path": "/dev/cpuctl",
      "Mode": "0755",
      "UID": "system",
      "GID": "system"
    },
    {
      "Controller": "memory",
      "Path": "/dev/memcg",
      "Mode": "0700",
      "Optional": true
    }
  ],
 "Cgroups2": {
   "Path": "/sys/fs/cgroup",
   "Mode": "0755",
   "UID": "system",
   "GID": "system",
   "Controllers": [
     {
       "Controller": "freezer",
       "Path": ".",
       "Mode": "0755",
       "UID": "system",
       "GID": "system"
     }
   ]
 }
}

This example file contains two sections, Cgroups (describing the cgroup v1 controllers) and Cgroups2 (describing the cgroup v2 controllers). All controllers in the cgroups v2 hierarchy are mounted at the same location. Therefore, the Cgroups2 section has its own Path, Mode, UID, and GID attributes to describe the location and attributes for the root of the hierarchy. The Path attribute for Controllers under Cgroups2 is relative to that root path. In Android 12 and higher you can define a cgroup controller that's specified with path and mode as "Optional" by setting it to true.

The cgroups.json file is parsed as part of the init process, during the early-init stage, and the cgroups are mounted at the specified locations. To later obtain the cgroup mounting locations, use the CgroupGetControllerPath API function.

Task profiles file

The task_profiles.json file is located under <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Use it to describe a specific set of actions to be applied to a process or a thread. A set of actions is associated with a profile name, which is used in SetTaskProfiles and SetProcessProfiles calls to invoke profile actions.

Example task_profiles.json file

{
  "Attributes": [
    {
      "Name": "MemSoftLimit",
      "Controller": "memory",
      "File": "memory.soft_limit_in_bytes"
    },
    {
      "Name": "MemSwappiness",
      "Controller": "memory",
      "File": "memory.swappiness"
    }
  ],
  "Profiles": [
    {
      "Name": "MaxPerformance",
      "Actions" : [
        {
          "Name" : "JoinCgroup",
          "Params" :
          {
            "Controller": "schedtune",
            "Path": "top-app"
          }
        }
      ]
    },
    {
      "Name": "TimerSlackHigh",
      "Actions" : [
        {
          "Name" : "SetTimerSlack",
          "Params" :
          {
            "Slack": "40000000"
          }
        }
      ]
    },
    {
      "Name": "LowMemoryUsage",
      "Actions" : [
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSoftLimit",
            "Value" : "16MB"
          }
        },
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSwappiness",
            "Value" : "150"

          }
        }
      ]
    }
  ]
  "AggregateProfiles": [
     {
       "Name": "SCHED_SP_DEFAULT",
       "Profiles": [ "TimerSlackHigh", "MaxPerformance" ]
     },
     {
       "Name": "SCHED_SP_BACKGROUND",
       "Profiles": [ "LowMemoryUsage" ]
     }
}

Assign names to specific cgroup files as entries in your Attributes list. Each entry contains the following:

  • Name field - specifies the name of the Attribute.
  • Controller field - references a cgroup controller from the cgroups.json file, by its name.
  • File field - names a specific file under this controller.

Attributes are references in task profile definitions. Outside of task profiles, use them only when the framework requires direct access to those files, and access can't be abstracted using task profiles. In all other cases, use task profiles; they provide better decoupling between required behavior and its implementation details.

The Profiles section contains task profile definitions with the following:

  • Name field - defines the profile name.
  • Actions section - lists a set of actions performed when the profile is applied. Each action has the following:

    • Name field specifying the action
    • Params section specifying a set of parameters for the action

Supported actions are listed in the table below.

Action Parameter Description
SetTimerSlack Slack Timer slack in ns
SetAttribute Name A name referencing an attribute from the Attributes section
Value A value to be written to the file represented by the named attribute
WriteFileFilePathpath to the file
Valuea value to be written to the file
JoinCgroup Controller A name of the cgroup controller from cgroups.json
Path A sub-group path in the cgroup controller’s hierarchy

Android 12 and higher feature an AggregateProfiles section that contains aggregate profiles, each of which is an alias for a set of one or more profiles. Aggregate profile definitions consist of the following:

  • Name field - specifies the name of the aggregate profile.
  • Profiles field - lists the names of the profiles included in the aggregate profile.

When an aggregate profile is applied, all the containing profiles are also automatically applied. Aggregate profiles can contain both individual profiles or other aggregate profiles, as long as there are no recursions (a profile that includes itself).

task_profiles init language command

A task_profiles command in the Android Init Language is available for Android 12 and higher to facilitate task profile activation for a specific process. It replaces the writepid command (deprecated in Android 12) that was used to migrate a process between cgroups. The task_profiles command provides flexibility for changing underlying implementations with no effect on upper layers. In the example below, these two commands effectively perform the same operation:

  • writepid /dev/cpuctl/top-app/tasks

    Deprecated in Android 12 - Was used to write the PID of the current task into the /dev/cpuctl/top-app/tasks file.

  • task_profiles MaxPerformance

    Joins the current process into the top-app group under "cpu" controller (cpuctl), which results in writing the PID of the process to dev/cpuctl/top-app/tasks.

Always use the task_profiles command to migrate tasks in cgroup hierarchies in Android 12 and higher. It accepts one or more parameters, representing the names of the profiles specified in the task_profiles.json file.

Per API-level task profiles

In Android 12 and higher, you can amend or override definitions in the default cgroups.json and task_profiles.json files, either basing your change on the Android API level, or making it from the vendor partition.

To override the definitions based on API level, the following files must be present on the device:

  • pro/system/etc/task_profiles/cgroups_<API level>.json

    Use this for cgroups specific to an API level.

  • /system/etc/task_profiles/task_profiles_<API level>.json

    Use this for profiles specific to an API level.

To override the definitions from the vendor partition, the following files must be present on the device:

  • /vendor/etc/cgroups.json
  • /vendor/etc/task_profiles.json

If an attribute or a profile definition in these files uses the same name as is in the default file, the file (API-level or vendor-level) definition overrides the previous definition. Note also that vendor-level definitions override API-level definitions. If the new definition has a new name, then the set of attributes or profiles is amended with the new definition.

The Android system loads the cgroup and task_profile files in this order:

  1. Default cgroups.json and task_profiles.json files.
  2. API level-specific files, if present.
  3. Vendor partition files, if present.

Changes to existing API

Android 10 and higher keeps the functions set_cpuset_policy, set_sched_policy, and get_sched_policy without changes to the API. However, Android 10 moves these functions into libprocessgroup, which now contains all cgroup-related functionality.

Although the cutils/sched_policy.h header still exists, to avoid breaking existing code ensure that new code includes a new processgroup/sched_policy.h header instead.

Modules that use any of these functions should add dependency on the libprocessgroup library into their makefile. If a module doesn't use any other libcutils functionality, drop the libcutils library dependency from the makefile.

Task profiles APIs

The private APIs in processgroup/processgroup.h are defined in the table below:

Type API and Definition
bool SetTaskProfiles(int tid, const std::vector& profiles)

Applies the task profiles specified in profiles to the thread specified by a thread ID (tid) using its tid parameter.

bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)

Applies the task profiles specified in profiles to the process specified by its user and process IDs using uid and pid parameters

bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)

Returns whether a cgroup controller specified by cgroup_name exists; if true, sets the path variable to the root of that cgroup

bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)

Returns whether a profile attribute specified by attr_name exists; if true, sets the path variable to the path of the file associated with that profile attribute.

bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)

Returns whether a profile attribute specified by attr_name exists; if true, sets the path variable to the path of the file associated with that profile attribute, and to the thread specified by its thread ID using the tid parameter.

bool UsePerAppMemcg()

Returns whether system is configured to use per-application memory cgroups.