Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a890fa01 authored by T.J. Mercier's avatar T.J. Mercier
Browse files

libprocessgroup: Look for memcg v2 MaxActivationDepth override

Init may write a file to /metadata containing a server override for
memcg v2 MaxActivationDepth for devices in an experiment population.
Use this value instead of the value from cgroups.json if the file
exists.

This will be reverted after the memcg v2 experiment is complete, and
before the final 25Q2 TOT snap. The final MaxActivationDepth value will
be commited to cgroups.json on AOSP. go/android-memcgv2-exp

Bug: 384577842
Test: setprop persist.device_config.mglru_native.lru_gen_config all
Test: grep memory /sys/fs/cgroup/*/cgroup.subtree_control
Test: reboot, then grep memory /sys/fs/cgroup/*/cgroup.subtree_control (again)
Test: Also tested with corp credentials and server flags in teamfood/droidfood.
Ignore-AOSP-First: 25Q2 Beta Experiment only go/android-memcgv2-exp
Change-Id: I960fe503bebc020867bb0c71be4e1bac282a1c0d
parent c56279e5
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -287,6 +287,9 @@ cc_binary {
        "make_f2fs",
        "mke2fs",
        "sload_f2fs",

        // TODO: Revert after go/android-memcgv2-exp b/386797433
        "memcgv2_activation_depth",
    ],
}

@@ -685,3 +688,10 @@ phony {
        default: ["init_first_stage"],
    }),
}

// TODO: Revert after go/android-memcgv2-exp b/386797433
sh_binary {
    name: "memcgv2_activation_depth",
    src: "memcgv2_activation_depth.sh",
    filename_from_src: true,
}
+86 −0
Original line number Diff line number Diff line
#!/bin/sh

# This script adjusts overrides of the memcg v2 MaxActivationDepth value at runtime.
# The override value needs to be accessible starting very early in the Android boot, where aconfig
# flags and system properties do not work. A file on /metadata is used instead.

# The kernel allows this to be as high as 65535, but our Android hierarchy is never that deep.
MAX_ALLOWED_DEPTH=5

# Store overridden MaxActivationDepths here for libprocessgroup to find them
OVERRIDE_FILE_PATH="/metadata/libprocessgroup/memcg_v2_max_activation_depth"

if [ "$#" -ne 1 ]
then
    echo "Usage: $0 <memcg v2 MaxActivationDepth value>"
    exit 99
fi

max_activation_depth=$1

if [[ $max_activation_depth != +([0-9]) ]]
then
    echo "MaxActivationDepth value must be a positive integer: $max_activation_depth"
    exit 98
fi

if [ $max_activation_depth -lt 0 ]
then
    echo "Negative MaxActivationDepth is invalid: $max_activation_depth"
    exit 97
fi

if [ $max_activation_depth -gt $MAX_ALLOWED_DEPTH ]
then
    echo "MaxActivationDepth is too large: $max_activation_depth"
    exit 96
fi

grep memory /sys/fs/cgroup/cgroup.controllers
if [ $? -ne 0 ]
then
    echo "memcg v2 is not available on this device!"
    exit 95
fi

current_activation_depth=$(cat $OVERRIDE_FILE_PATH)
if [ $? -ne 0 ]
then
    # Find the default activation depth in the absence of any properties / overrides.
    #
    # To do this 100% correctly requires JSON parsing which we don't really want to do here.
    # We know that this will be called only for Pixel (for a limited-duration experiment), and that
    # Pixel does not override cgroups.json, therefore we can assume that the system cgroups.json has
    # only a single MaxActivationDepth entry which corresponds to the v2 memory controller. So we
    # can just grep for the default value.
    default_activation_depth=$(grep MaxActivationDepth /system/etc/cgroups.json | tr -dc '0-9')
    if [ $? -ne 0 -o $default_activation_depth -gt $MAX_ALLOWED_DEPTH ]
    then
        # If MaxActivationDepth is not present, libprocessgroup does not limit how deep it will activate
        default_activation_depth=$MAX_ALLOWED_DEPTH
    fi
    current_activation_depth=$default_activation_depth
fi

# libprocessgroup will pick this up for all future cgroup creations, including on the next boot
echo $max_activation_depth > $OVERRIDE_FILE_PATH

if [ $max_activation_depth -lt $current_activation_depth ]
then
    # We can deactivate memcgs which are deeper than the new depth value, however that would leave
    # behind zombie memcgs which would ruin the metrics produced from this device. The only way to
    # eliminate those zombies is to remove the entire cgroup, which we cannot do without killing
    # all the contained processes. So the only real option we have is to reboot here.
    # This should only happen once at the end of the experiment.
    echo "Rebooting due to decreased memcg v2 MaxActivationDepth"
    reboot
elif [ $max_activation_depth -gt $current_activation_depth ]
then
    for d in $(seq $max_activation_depth)
    do
        for f in $(find /sys/fs/cgroup/ -mindepth $d -maxdepth $d -name cgroup.subtree_control)
        do
            echo "+memory" > $f
        done
    done
fi
+40 −1
Original line number Diff line number Diff line
@@ -18,15 +18,19 @@

#include <algorithm>
#include <iterator>
#include <mutex>
#include <optional>
#include <string_view>

#include <mntent.h>
#include <unistd.h>

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <json/reader.h>
#include <json/value.h>

@@ -174,6 +178,38 @@ static std::optional<std::map<MountDir, MountOpts>> ReadCgroupV1Mounts() {
    return mounts;
}

// Keep the override file open to reduce open syscalls, but read it every time.
// Note that memcgv2_activation_depth.sh can race with us here.
std::optional<unsigned int> ReadMaxActivationDepthMetadataOverride() {
    static const char* OVERRIDE_FILE_PATH =
        "/metadata/libprocessgroup/memcg_v2_max_activation_depth";
    static int override_fd = open(OVERRIDE_FILE_PATH, O_RDONLY | O_CLOEXEC);
    static std::mutex mtx;

    std::unique_lock lock(mtx);
    if (override_fd < 0) {
        override_fd = open(OVERRIDE_FILE_PATH, O_RDONLY | O_CLOEXEC);
        if (override_fd < 0) return std::nullopt;
    }

    std::string depth_str;
    const bool ret = android::base::ReadFdToString(override_fd, &depth_str);
    lseek(override_fd, 0, SEEK_SET);
    lock.unlock();

    if (!ret) {
        PLOG(ERROR) << "Failed to read max activation depth override";
        return std::nullopt;
    }

    unsigned int depth;
    if (!android::base::ParseUint(android::base::Trim(depth_str), &depth)) {
        PLOG(ERROR) << "Failed to convert max activation depth override (" << depth_str << ')';
        return std::nullopt;
    }
    return depth;
}

}  // anonymous namespace


@@ -235,7 +271,10 @@ bool ReadDescriptors(CgroupDescriptorMap* descriptors) {
bool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors) {
    for (const auto& [name, descriptor] : descriptors) {
        const uint32_t flags = descriptor.controller()->flags();
        const uint32_t max_activation_depth = descriptor.controller()->max_activation_depth();
        uint32_t max_activation_depth;
        std::optional<unsigned int> metadataMaxDepth = ReadMaxActivationDepthMetadataOverride();
        if (metadataMaxDepth) max_activation_depth = *metadataMaxDepth;
        else max_activation_depth = descriptor.controller()->max_activation_depth();
        const unsigned int depth = GetCgroupDepth(descriptor.controller()->path(), path);

        if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {
+9 −0
Original line number Diff line number Diff line
@@ -614,6 +614,9 @@ on post-fs

    mkdir /metadata/staged-install 0770 root system

    # TODO: Revert after go/android-memcgv2-exp b/386797433
    mkdir /metadata/libprocessgroup 0770 root system

on late-fs
    # Ensure that tracefs has the correct permissions.
    # This does not work correctly if it is called in post-fs.
@@ -1318,6 +1321,9 @@ on init && property:ro.debuggable=1
# Multi-Gen LRU Experiment
on property:persist.device_config.mglru_native.lru_gen_config=none
  write /sys/kernel/mm/lru_gen/enabled 0
  # Memcg v2 Experiment
  # TODO: Revert after go/android-memcgv2-exp b/386797433
  exec - system system -- /system/bin/memcgv2_activation_depth.sh 0
on property:persist.device_config.mglru_native.lru_gen_config=core
  write /sys/kernel/mm/lru_gen/enabled 1
on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
@@ -1326,6 +1332,9 @@ on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_y
  write /sys/kernel/mm/lru_gen/enabled 5
on property:persist.device_config.mglru_native.lru_gen_config=all
  write /sys/kernel/mm/lru_gen/enabled 7
  # Memcg v2 Experiment
  # TODO: Revert after go/android-memcgv2-exp b/386797433
  exec - system system -- /system/bin/memcgv2_activation_depth.sh 3

# Allow other processes to run `snapshotctl` through `init`. This requires
# `set_prop` permission on `snapshotctl_prop`.