From a103630b5c9d187a3fa085585da58c4a6745e7bc Mon Sep 17 00:00:00 2001 From: "T.J. Mercier" Date: Tue, 7 Nov 2023 14:36:46 +0000 Subject: [PATCH] libprocessgroup: Use cgroup.kill By using cgroup.kill we don't need to read cgroup.procs at all for SIGKILLs, which is more efficient and should help reduce CPU contention and cgroup lock contention. Fallback to cgroup.procs if we encounter an error trying to use cgroup.kill, but if cgroup.kill fails it's likely that cgroup.procs will too. Bug: 239829790 Test: atest StagedRollbackTest:com.android.tests.rollback.host.StagedRollbackTest#testNativeWatchdogTriggersRebootlessApexRollback Change-Id: I9da67efd00af49b7b4b502fb742c1095d5c7b9e9 --- libprocessgroup/processgroup.cpp | 54 +++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index 7e27d7544..3209adf16 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp @@ -56,6 +56,7 @@ using android::base::WriteStringToFile; using namespace std::chrono_literals; #define PROCESSGROUP_CGROUP_PROCS_FILE "cgroup.procs" +#define PROCESSGROUP_CGROUP_KILL_FILE "cgroup.kill" #define PROCESSGROUP_CGROUP_EVENTS_FILE "cgroup.events" bool CgroupsAvailable() { @@ -77,6 +78,29 @@ bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) return true; } +static std::string ConvertUidToPath(const char* cgroup, uid_t uid) { + return StringPrintf("%s/uid_%u", cgroup, uid); +} + +static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) { + return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid); +} + +static bool CgroupKillAvailable() { + static std::once_flag f; + static bool cgroup_kill_available = false; + std::call_once(f, []() { + std::string cg_kill; + CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cg_kill); + // cgroup.kill is not on the root cgroup, so check a non-root cgroup that should always + // exist + cg_kill = ConvertUidToPath(cg_kill.c_str(), AID_ROOT) + '/' + PROCESSGROUP_CGROUP_KILL_FILE; + cgroup_kill_available = access(cg_kill.c_str(), F_OK) == 0; + }); + + return cgroup_kill_available; +} + static bool CgroupGetMemcgAppsPath(std::string* path) { CgroupController controller = CgroupMap::GetInstance().FindController("memory"); @@ -208,14 +232,6 @@ bool SetUserProfiles(uid_t uid, const std::vector& profiles) { false); } -static std::string ConvertUidToPath(const char* cgroup, uid_t uid) { - return StringPrintf("%s/uid_%u", cgroup, uid); -} - -static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) { - return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid); -} - static int RemoveCgroup(const char* cgroup, uid_t uid, int pid) { auto path = ConvertUidPidToPath(cgroup, uid, pid); int ret = TEMP_FAILURE_RETRY(rmdir(path.c_str())); @@ -362,6 +378,28 @@ bool sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) { CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path); cgroup_v2_path = ConvertUidPidToPath(hierarchy_root_path.c_str(), uid, initialPid); + if (signal == SIGKILL && CgroupKillAvailable()) { + LOG(VERBOSE) << "Using " << PROCESSGROUP_CGROUP_KILL_FILE << " to SIGKILL " + << cgroup_v2_path; + + // We need to kill the process group in addition to the cgroup. For normal apps they + // should completely overlap, but system_server kills depend on process group kills to + // take down apps which are in their own cgroups and not individually targeted. + if (kill(-initialPid, signal) == -1 && errno != ESRCH) { + PLOG(WARNING) << "kill(" << -initialPid << ", " << signal << ") failed"; + } + + const std::string killfilepath = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_KILL_FILE; + if (WriteStringToFile("1", killfilepath)) { + return true; + } else { + PLOG(ERROR) << "Failed to write 1 to " << killfilepath; + // Fallback to cgroup.procs below + } + } + + // Since cgroup.kill only sends SIGKILLs, we read cgroup.procs to find each process to + // signal individually. This is more costly than using cgroup.kill for SIGKILLs. LOG(VERBOSE) << "Using " << PROCESSGROUP_CGROUP_PROCS_FILE << " to signal (" << signal << ") " << cgroup_v2_path;