From 3984611601e9cc8c41b996e8e6dc543b383e0922 Mon Sep 17 00:00:00 2001 From: "T.J. Mercier" Date: Wed, 9 Oct 2024 22:40:26 +0000 Subject: [PATCH] libprocessgroup: Add SetSchedulerPolicy Action Add a new profile action that allows users to set a scheduler policy and priority for a thread when a profile with the action is applied. This can be used with the POSIX policies specified in the sched_setscheduler man page. A priority value is required for real-time policies. A nice value may optionally be provided for normal policies. If a normal policy is used and a nice value is provided, it will be applied with setpriority() after the scheduler policy is applied. The range of nice values that can be used in the task profiles JSON in this case is [-20, 19]. If a real-time policy is used, the range of priorities that can be used in the task profiles JSON is [1, 99] which will be mapped onto [sched_get_priority_min(), sched_get_priority_max()] at runtime for the the provided policy. Here are some examples: Use lowest-priority with FIFO policy: "Actions": [ { "Name": "SetSchedulerPolicy", "Params": { "Policy": "SCHED_FIFO", "Priority": 1 } } ] Use highest-priority (lowest nice) with standard NORMAL scheduler: "Actions": [ { "Name": "SetSchedulerPolicy", "Params": { "Policy": "SCHED_OTHER", "Nice": -20 } } ] Bug: 368072932 BYPASS_INCLUSIVE_LANGUAGE_REASON=false positive Change-Id: I502ceb3913016e9f4e6b6322cc0eee905c9a5c9a --- libprocessgroup/task_profiles.cpp | 120 +++++++++++++++++++++++++++++- libprocessgroup/task_profiles.h | 20 +++++ 2 files changed, 136 insertions(+), 4 deletions(-) diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp index 7f33d4a40..110ef5267 100644 --- a/libprocessgroup/task_profiles.cpp +++ b/libprocessgroup/task_profiles.cpp @@ -17,11 +17,16 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "libprocessgroup" +#include + +#include +#include + #include #include +#include +#include #include -#include -#include #include #include @@ -30,13 +35,13 @@ #include #include +#include + #include #include #include -#include - using android::base::GetThreadId; using android::base::GetUintProperty; using android::base::StringPrintf; @@ -649,6 +654,57 @@ bool WriteFileAction::IsValidForTask(int) const { return access(task_path_.c_str(), W_OK) == 0; } +bool SetSchedulerPolicyAction::isNormalPolicy(int policy) { + return policy == SCHED_OTHER || policy == SCHED_BATCH || policy == SCHED_IDLE; +} + +bool SetSchedulerPolicyAction::toPriority(int policy, int virtual_priority, int& priority_out) { + constexpr int VIRTUAL_PRIORITY_MIN = 1; + constexpr int VIRTUAL_PRIORITY_MAX = 99; + + if (virtual_priority < VIRTUAL_PRIORITY_MIN || virtual_priority > VIRTUAL_PRIORITY_MAX) { + LOG(WARNING) << "SetSchedulerPolicy: invalid priority (" << virtual_priority + << ") for policy (" << policy << ")"; + return false; + } + + const int min = sched_get_priority_min(policy); + if (min == -1) { + PLOG(ERROR) << "SetSchedulerPolicy: Cannot get min sched priority for policy " << policy; + return false; + } + + const int max = sched_get_priority_max(policy); + if (max == -1) { + PLOG(ERROR) << "SetSchedulerPolicy: Cannot get max sched priority for policy " << policy; + return false; + } + + priority_out = min + (virtual_priority - VIRTUAL_PRIORITY_MIN) * (max - min) / + (VIRTUAL_PRIORITY_MAX - VIRTUAL_PRIORITY_MIN); + + return true; +} + +bool SetSchedulerPolicyAction::ExecuteForTask(pid_t tid) const { + struct sched_param param = {}; + param.sched_priority = isNormalPolicy(policy_) ? 0 : *priority_or_nice_; + if (sched_setscheduler(tid, policy_, ¶m) == -1) { + PLOG(WARNING) << "SetSchedulerPolicy: Failed to apply scheduler policy (" << policy_ + << ") with priority (" << *priority_or_nice_ << ") to tid " << tid; + return false; + } + + if (isNormalPolicy(policy_) && priority_or_nice_ && + setpriority(PRIO_PROCESS, tid, *priority_or_nice_) == -1) { + PLOG(WARNING) << "SetSchedulerPolicy: Failed to apply nice (" << *priority_or_nice_ + << ") to tid " << tid; + return false; + } + + return true; +} + bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { for (const auto& profile : profiles_) { profile->ExecuteForProcess(uid, pid); @@ -936,6 +992,62 @@ bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) { LOG(WARNING) << "WriteFile: invalid parameter: " << "empty value"; } + } else if (action_name == "SetSchedulerPolicy") { + const std::map POLICY_MAP = { + {"SCHED_OTHER", SCHED_OTHER}, + {"SCHED_BATCH", SCHED_BATCH}, + {"SCHED_IDLE", SCHED_IDLE}, + {"SCHED_FIFO", SCHED_FIFO}, + {"SCHED_RR", SCHED_RR}, + }; + const std::string policy_str = params_val["Policy"].asString(); + + const auto it = POLICY_MAP.find(policy_str); + if (it == POLICY_MAP.end()) { + LOG(WARNING) << "SetSchedulerPolicy: invalid policy " << policy_str; + continue; + } + + const int policy = it->second; + + if (SetSchedulerPolicyAction::isNormalPolicy(policy)) { + if (params_val.isMember("Priority")) { + LOG(WARNING) << "SetSchedulerPolicy: Normal policies (" << policy_str + << ") use Nice values, not Priority values"; + } + + if (params_val.isMember("Nice")) { + // If present, this optional value will be passed in an additional syscall + // to setpriority(), since the sched_priority value must be 0 for calls to + // sched_setscheduler() with "normal" policies. + const int nice = params_val["Nice"].asInt(); + + const int LINUX_MIN_NICE = -20; + const int LINUX_MAX_NICE = 19; + if (nice < LINUX_MIN_NICE || nice > LINUX_MAX_NICE) { + LOG(WARNING) << "SetSchedulerPolicy: Provided nice (" << nice + << ") appears out of range."; + } + profile->Add(std::make_unique(policy, nice)); + } else { + profile->Add(std::make_unique(policy)); + } + } else { + if (params_val.isMember("Nice")) { + LOG(WARNING) << "SetSchedulerPolicy: Real-time policies (" << policy_str + << ") use Priority values, not Nice values"; + } + + // This is a "virtual priority" as described by `man 2 sched_get_priority_min` + // that will be mapped onto the following range for the provided policy: + // [sched_get_priority_min(), sched_get_priority_max()] + const int virtual_priority = params_val["Priority"].asInt(); + + int priority; + if (SetSchedulerPolicyAction::toPriority(policy, virtual_priority, priority)) { + profile->Add(std::make_unique(policy, priority)); + } + } } else { LOG(WARNING) << "Unknown profile action: " << action_name; } diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h index e52ce38fe..ea948b58e 100644 --- a/libprocessgroup/task_profiles.h +++ b/libprocessgroup/task_profiles.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,25 @@ class WriteFileAction : public ProfileAction { CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const; }; +// Set scheduler policy action +class SetSchedulerPolicyAction : public ProfileAction { + public: + SetSchedulerPolicyAction(int policy) + : policy_(policy) {} + SetSchedulerPolicyAction(int policy, int priority_or_nice) + : policy_(policy), priority_or_nice_(priority_or_nice) {} + + const char* Name() const override { return "SetSchedulerPolicy"; } + bool ExecuteForTask(pid_t tid) const override; + + static bool isNormalPolicy(int policy); + static bool toPriority(int policy, int virtual_priority, int& priority_out); + + private: + int policy_; + std::optional priority_or_nice_; +}; + class TaskProfile { public: TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}