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
This commit is contained in:
T.J. Mercier 2023-11-07 14:36:46 +00:00
parent 4928b6ef57
commit a103630b5c

View file

@ -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<std::string>& 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;