1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "cgroup_action.h"
16 #include <algorithm>              // for replace
17 #include <vector>                 // for vector
18 #include <climits>                // for PATH_MAX
19 #include <memory>                 // for unique_ptr
20 #include <cstdlib>                // for realpath
21 #include <cstring>                // for strlen
22 #include <utility>                // for pair
23 #include "cgroup_controller.h"    // for CgroupController
24 #include "cgroup_map.h"           // for CgroupMap
25 #include "config_policy_utils.h"  // for GetOneCfgFile
26 #include "nlohmann/json.hpp"           // for Value
27 #include "process_group_log.h"    // for PGCGS_LOGI, PGCGS_LOGE
28 #include "process_group_util.h"   // for ReadFileToString
29 #include "sched_policy.h"         // for SchedPolicy, SP_UPPER_LIMIT, SP_DEF...
30 
31 namespace OHOS {
32 namespace ResourceSchedule {
33 namespace CgroupSetting {
34 namespace {
35     static constexpr const char* CGROUP_SETTING_CONFIG_FILE = "etc/cgroup_sched/cgroup_action_config.json";
36 
37     static const std::string STR_SP_DEFAULT = "sp_default";
38     static const std::string STR_SP_BACKGROUND = "sp_background";
39     static const std::string STR_SP_FOREGROUND = "sp_foreground";
40     static const std::string STR_SP_SYSTEM_BACKGROUND = "sp_system_background";
41     static const std::string STR_SP_TOP_APP = "sp_top_app";
42 
43     static const std::string ABBR_SP_DEFAULT = "df";
44     static const std::string ABBR_SP_BACKGROUND = "bg";
45     static const std::string ABBR_SP_FOREGROUND = "fg";
46     static const std::string ABBR_SP_SYSTEM_BACKGROUND = "sy";
47     static const std::string ABBR_SP_TOP_APP = "ta";
48 }
49 
GetInstance()50 CgroupAction& CgroupAction::GetInstance()
51 {
52     static CgroupAction instance;
53     return instance;
54 }
55 
CgroupAction()56 CgroupAction::CgroupAction()
57 {
58     AddSchedPolicyDeclaration(SP_DEFAULT, STR_SP_DEFAULT, ABBR_SP_DEFAULT);
59     AddSchedPolicyDeclaration(SP_BACKGROUND, STR_SP_BACKGROUND, ABBR_SP_BACKGROUND);
60     AddSchedPolicyDeclaration(SP_FOREGROUND, STR_SP_FOREGROUND, ABBR_SP_FOREGROUND);
61     AddSchedPolicyDeclaration(SP_SYSTEM_BACKGROUND, STR_SP_SYSTEM_BACKGROUND, ABBR_SP_SYSTEM_BACKGROUND);
62     AddSchedPolicyDeclaration(SP_TOP_APP, STR_SP_TOP_APP, ABBR_SP_TOP_APP);
63 }
64 
AddSchedPolicyDeclaration(const SchedPolicy policy,const std::string & fullName,const std::string & abbrName)65 void CgroupAction::AddSchedPolicyDeclaration(const SchedPolicy policy,
66     const std::string& fullName, const std::string& abbrName)
67 {
68     std::lock_guard<std::mutex> lock(mutex_);
69     if (!allowToAdd_) {
70         PGCGS_LOGI("%{public}s not allowed: %{public}u, %{public}s, %{public}s",
71             __func__, policy, fullName.c_str(), abbrName.c_str());
72         return;
73     }
74     if (policy >= SP_UPPER_LIMIT) {
75         PGCGS_LOGI("%{public}s out of range: %{public}u, %{public}s, %{public}s",
76             __func__, policy, fullName.c_str(), abbrName.c_str());
77         return;
78     }
79     if (fullName.empty() || abbrName.empty()) {
80         return;
81     }
82     if (fullNames_.find(policy) != fullNames_.end()) {
83         return;
84     }
85     if (std::any_of(fullNames_.begin(), fullNames_.end(),
86         [ &fullName ] (const auto& kv) { return kv.second == fullName; })) {
87         return;
88     }
89     PGCGS_LOGI("%{public}s add sched policy: %{public}u, %{public}s, %{public}s",
90         __func__, policy, fullName.c_str(), abbrName.c_str());
91     fullNames_[policy] = fullName;
92     abbrNames_[policy] = abbrName;
93 }
94 
GetSchedPolicyList()95 std::vector<SchedPolicy> CgroupAction::GetSchedPolicyList()
96 {
97     std::lock_guard<std::mutex> lock(mutex_);
98     std::vector<SchedPolicy> policyList;
99     std::transform(fullNames_.begin(), fullNames_.end(), std::back_inserter(policyList),
100         [] (const auto& kv) { return kv.first; });
101     return policyList;
102 }
103 
IsSchedPolicyValid(SchedPolicy policy)104 bool CgroupAction::IsSchedPolicyValid(SchedPolicy policy)
105 {
106     std::lock_guard<std::mutex> lock(mutex_);
107     return fullNames_.find(policy) != fullNames_.end();
108 }
109 
GetSchedPolicyFullName(SchedPolicy policy)110 const char* CgroupAction::GetSchedPolicyFullName(SchedPolicy policy)
111 {
112     std::lock_guard<std::mutex> lock(mutex_);
113     if (fullNames_.find(policy) != fullNames_.end()) {
114         return fullNames_[policy].c_str();
115     }
116     return "error";
117 }
118 
GetSchedPolicyAbbrName(SchedPolicy policy)119 const char* CgroupAction::GetSchedPolicyAbbrName(SchedPolicy policy)
120 {
121     std::lock_guard<std::mutex> lock(mutex_);
122     if (abbrNames_.find(policy) != abbrNames_.end()) {
123         return abbrNames_[policy].c_str();
124     }
125     return "error";
126 }
127 
SetThreadSchedPolicy(int tid,SchedPolicy policy)128 bool CgroupAction::SetThreadSchedPolicy(int tid, SchedPolicy policy)
129 {
130     if (!IsSchedPolicyValid(policy)) {
131         return false;
132     }
133     if (!IsEnabled()) {
134         return false;
135     }
136     return CgroupMap::GetInstance().SetThreadSchedPolicy(tid, policy, false);
137 }
138 
SetThreadGroupSchedPolicy(int tid,SchedPolicy policy)139 bool CgroupAction::SetThreadGroupSchedPolicy(int tid, SchedPolicy policy)
140 {
141     if (!IsSchedPolicyValid(policy)) {
142         return false;
143     }
144     if (!IsEnabled()) {
145         return false;
146     }
147     return CgroupMap::GetInstance().SetThreadSchedPolicy(tid, policy, true);
148 }
149 
LoadConfigFile()150 bool CgroupAction::LoadConfigFile()
151 {
152     PGCGS_LOGI("%{public}s CgroupAction::LoadConfigFile loading config file", __func__);
153     nlohmann::json jsonObjRoot;
154     if (!ParseConfigFileToJsonObj(jsonObjRoot)) {
155         return false;
156     }
157     return CgroupMap::GetInstance().LoadConfigFromJsonObj(jsonObjRoot);
158 }
159 
IsEnabled()160 bool CgroupAction::IsEnabled()
161 {
162     {
163         std::lock_guard<std::mutex> lock(mutex_);
164         allowToAdd_ = false;
165     }
166     static bool enable = LoadConfigFile();
167     return enable;
168 }
169 
GetSchedPolicy(int tid,SchedPolicy * policy)170 int CgroupAction::GetSchedPolicy(int tid, SchedPolicy* policy)
171 {
172     if (!IsEnabled()) {
173         *policy = SP_UPPER_LIMIT;
174         return -1;
175     }
176     std::string subgroup;
177     std::vector<CgroupController *> controllers;
178     bool getTaskGroup = false;
179     CgroupMap& instance = CgroupMap::GetInstance();
180     if (instance.FindAllEnableCgroupControllers(controllers)) {
181         for (const auto controller : controllers) {
182             if (controller->GetTaskGroup(tid, subgroup)) {
183                 getTaskGroup = true;
184                 break;
185             }
186         }
187     }
188     if (!getTaskGroup) {
189         *policy = SP_UPPER_LIMIT;
190         return -1;
191     }
192     if (subgroup.empty()) {
193         *policy = SP_DEFAULT;
194         return 0;
195     }
196 
197     replace(subgroup.begin(), subgroup.end(), '-', '_');
198     subgroup = "sp_" + subgroup;
199     return GetSchedPolicyByName(subgroup, policy);
200 }
201 
GetSchedPolicyByName(const std::string & name,SchedPolicy * policy)202 int CgroupAction::GetSchedPolicyByName(const std::string& name, SchedPolicy* policy)
203 {
204     const auto& result = std::find_if(fullNames_.begin(), fullNames_.end(),
205         [ &name ] (const auto& kv) { return kv.second == name; });
206     if (result != fullNames_.end()) {
207         *policy = result->first;
208         return 0;
209     }
210     *policy = SP_UPPER_LIMIT;
211     return -1;
212 }
213 
ParseConfigFileToJsonObj(nlohmann::json & jsonObjRoot)214 bool CgroupAction::ParseConfigFileToJsonObj(nlohmann::json& jsonObjRoot)
215 {
216     char buf[PATH_MAX + 1];
217     char* configFilePath = GetOneCfgFile(CGROUP_SETTING_CONFIG_FILE, buf, PATH_MAX + 1);
218     char tmpPath[PATH_MAX + 1] = {0};
219     if (!configFilePath || strlen(configFilePath) == 0 || strlen(configFilePath) > PATH_MAX ||
220         !realpath(configFilePath, tmpPath)) {
221         PGCGS_LOGE("%{public}s: read %{public}s failed", __func__, CGROUP_SETTING_CONFIG_FILE);
222         return false;
223     }
224     std::string realConfigFile(tmpPath);
225     std::string jsonString;
226     if (!ReadFileToString(realConfigFile, jsonString)) {
227         PGCGS_LOGE("%{public}s: read config file failed", __func__);
228         return false;
229     }
230 
231     if (jsonString.empty()) {
232         return false;
233     }
234     jsonObjRoot = nlohmann::json::parse(jsonString, nullptr, false);
235     if (jsonObjRoot.is_discarded()) {
236         PGCGS_LOGE("%{public}s: json obj parse failed, jsonString=%{public}s", __func__, jsonString.c_str());
237         return false;
238     }
239     return true;
240 }
241 } // namespace CgroupSetting
242 } // namespace ResourceSchedule
243 } // namespace OHOS
244