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