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) {}