1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <sys/types.h> 20 21 #include <map> 22 #include <memory> 23 #include <mutex> 24 #include <optional> 25 #include <span> 26 #include <string> 27 #include <string_view> 28 #include <vector> 29 30 #include <android-base/unique_fd.h> 31 #include <cgroup_map.h> 32 33 class IProfileAttribute { 34 public: 35 virtual ~IProfileAttribute() = 0; 36 virtual void Reset(const CgroupControllerWrapper& controller, const std::string& file_name, 37 const std::string& file_v2_name) = 0; 38 virtual const CgroupControllerWrapper* controller() const = 0; 39 virtual const std::string& file_name() const = 0; 40 virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0; 41 virtual bool GetPathForTask(pid_t tid, std::string* path) const = 0; 42 virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0; 43 }; 44 45 class ProfileAttribute : public IProfileAttribute { 46 public: 47 // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is 48 // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for 49 // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies. ProfileAttribute(const CgroupControllerWrapper & controller,const std::string & file_name,const std::string & file_v2_name)50 ProfileAttribute(const CgroupControllerWrapper& controller, const std::string& file_name, 51 const std::string& file_v2_name) 52 : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {} 53 ~ProfileAttribute() = default; 54 controller()55 const CgroupControllerWrapper* controller() const override { return &controller_; } 56 const std::string& file_name() const override; 57 void Reset(const CgroupControllerWrapper& controller, const std::string& file_name, 58 const std::string& file_v2_name) override; 59 60 bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override; 61 bool GetPathForTask(pid_t tid, std::string* path) const override; 62 bool GetPathForUID(uid_t uid, std::string* path) const override; 63 64 private: 65 CgroupControllerWrapper controller_; 66 std::string file_name_; 67 std::string file_v2_name_; 68 }; 69 70 // Abstract profile element 71 class ProfileAction { 72 public: 73 enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT }; 74 ~ProfileAction()75 virtual ~ProfileAction() {} 76 77 virtual const char* Name() const = 0; 78 79 // Default implementations will fail ExecuteForProcess(uid_t,pid_t)80 virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; } ExecuteForTask(pid_t)81 virtual bool ExecuteForTask(pid_t) const { return false; } ExecuteForUID(uid_t)82 virtual bool ExecuteForUID(uid_t) const { return false; } 83 EnableResourceCaching(ResourceCacheType)84 virtual void EnableResourceCaching(ResourceCacheType) {} DropResourceCaching(ResourceCacheType)85 virtual void DropResourceCaching(ResourceCacheType) {} IsValidForProcess(uid_t,pid_t)86 virtual bool IsValidForProcess(uid_t, pid_t) const { return false; } IsValidForTask(pid_t)87 virtual bool IsValidForTask(pid_t) const { return false; } 88 89 protected: 90 enum CacheUseResult { SUCCESS, FAIL, UNUSED }; 91 }; 92 93 // Profile actions 94 class SetTimerSlackAction : public ProfileAction { 95 public: SetTimerSlackAction(unsigned long slack)96 SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {} 97 Name()98 const char* Name() const override { return "SetTimerSlack"; } 99 bool ExecuteForTask(pid_t tid) const override; IsValidForProcess(uid_t,pid_t)100 bool IsValidForProcess(uid_t, pid_t) const override { return true; } IsValidForTask(pid_t)101 bool IsValidForTask(pid_t) const override { return true; } 102 103 private: 104 unsigned long slack_; 105 }; 106 107 // Set attribute profile element 108 class SetAttributeAction : public ProfileAction { 109 public: SetAttributeAction(const IProfileAttribute * attribute,const std::string & value,bool optional)110 SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional) 111 : attribute_(attribute), value_(value), optional_(optional) {} 112 Name()113 const char* Name() const override { return "SetAttribute"; } 114 bool ExecuteForProcess(uid_t uid, pid_t pid) const override; 115 bool ExecuteForTask(pid_t tid) const override; 116 bool ExecuteForUID(uid_t uid) const override; 117 bool IsValidForProcess(uid_t uid, pid_t pid) const override; 118 bool IsValidForTask(pid_t tid) const override; 119 120 private: 121 const IProfileAttribute* attribute_; 122 std::string value_; 123 bool optional_; 124 125 bool WriteValueToFile(const std::string& path) const; 126 }; 127 128 // Set cgroup profile element 129 class SetCgroupAction : public ProfileAction { 130 public: 131 SetCgroupAction(const CgroupControllerWrapper& c, const std::string& p); 132 Name()133 const char* Name() const override { return "SetCgroup"; } 134 bool ExecuteForProcess(uid_t uid, pid_t pid) const override; 135 bool ExecuteForTask(pid_t tid) const override; 136 void EnableResourceCaching(ResourceCacheType cache_type) override; 137 void DropResourceCaching(ResourceCacheType cache_type) override; 138 bool IsValidForProcess(uid_t uid, pid_t pid) const override; 139 bool IsValidForTask(pid_t tid) const override; 140 controller()141 const CgroupControllerWrapper* controller() const { return &controller_; } 142 143 private: 144 CgroupControllerWrapper controller_; 145 std::string path_; 146 android::base::unique_fd fd_[ProfileAction::RCT_COUNT]; 147 mutable std::mutex fd_mutex_; 148 149 bool AddTidToCgroup(pid_t tid, int fd, ResourceCacheType cache_type) const; 150 CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const; 151 }; 152 153 // Write to file action 154 class WriteFileAction : public ProfileAction { 155 public: 156 WriteFileAction(const std::string& task_path, const std::string& proc_path, 157 const std::string& value, bool logfailures); 158 Name()159 const char* Name() const override { return "WriteFile"; } 160 bool ExecuteForProcess(uid_t uid, pid_t pid) const override; 161 bool ExecuteForTask(pid_t tid) const override; 162 void EnableResourceCaching(ResourceCacheType cache_type) override; 163 void DropResourceCaching(ResourceCacheType cache_type) override; 164 bool IsValidForProcess(uid_t uid, pid_t pid) const override; 165 bool IsValidForTask(pid_t tid) const override; 166 167 private: 168 std::string task_path_, proc_path_, value_; 169 bool logfailures_; 170 android::base::unique_fd fd_[ProfileAction::RCT_COUNT]; 171 mutable std::mutex fd_mutex_; 172 173 bool WriteValueToFile(const std::string& value, ResourceCacheType cache_type, uid_t uid, 174 pid_t pid, bool logfailures) const; 175 CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const; 176 }; 177 178 // Set scheduler policy action 179 class SetSchedulerPolicyAction : public ProfileAction { 180 public: SetSchedulerPolicyAction(int policy)181 SetSchedulerPolicyAction(int policy) 182 : policy_(policy) {} SetSchedulerPolicyAction(int policy,int priority_or_nice)183 SetSchedulerPolicyAction(int policy, int priority_or_nice) 184 : policy_(policy), priority_or_nice_(priority_or_nice) {} 185 Name()186 const char* Name() const override { return "SetSchedulerPolicy"; } 187 bool ExecuteForTask(pid_t tid) const override; 188 189 static bool isNormalPolicy(int policy); 190 static bool toPriority(int policy, int virtual_priority, int& priority_out); 191 192 private: 193 int policy_; 194 std::optional<int> priority_or_nice_; 195 }; 196 197 class TaskProfile { 198 public: TaskProfile(const std::string & name)199 TaskProfile(const std::string& name) : name_(name), res_cached_(false) {} 200 Name()201 const std::string& Name() const { return name_; } Add(std::unique_ptr<ProfileAction> e)202 void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); } 203 void MoveTo(TaskProfile* profile); 204 205 bool ExecuteForProcess(uid_t uid, pid_t pid) const; 206 bool ExecuteForTask(pid_t tid) const; 207 bool ExecuteForUID(uid_t uid) const; 208 void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type); 209 void DropResourceCaching(ProfileAction::ResourceCacheType cache_type); 210 bool IsValidForProcess(uid_t uid, pid_t pid) const; 211 bool IsValidForTask(pid_t tid) const; 212 213 private: 214 const std::string name_; 215 bool res_cached_; 216 std::vector<std::unique_ptr<ProfileAction>> elements_; 217 }; 218 219 // Set aggregate profile element 220 class ApplyProfileAction : public ProfileAction { 221 public: ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>> & profiles)222 ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles) 223 : profiles_(profiles) {} 224 Name()225 const char* Name() const override { return "ApplyProfileAction"; } 226 bool ExecuteForProcess(uid_t uid, pid_t pid) const override; 227 bool ExecuteForTask(pid_t tid) const override; 228 void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override; 229 void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override; 230 bool IsValidForProcess(uid_t uid, pid_t pid) const override; 231 bool IsValidForTask(pid_t tid) const override; 232 233 private: 234 std::vector<std::shared_ptr<TaskProfile>> profiles_; 235 }; 236 237 class TaskProfiles { 238 public: 239 // Should be used by all users 240 static TaskProfiles& GetInstance(); 241 242 TaskProfile* GetProfile(std::string_view name) const; 243 const IProfileAttribute* GetAttribute(std::string_view name) const; 244 void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const; 245 template <typename T> 246 bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles, bool use_fd_cache); 247 template <typename T> 248 bool SetTaskProfiles(pid_t tid, std::span<const T> profiles, bool use_fd_cache); 249 template <typename T> 250 bool SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache); 251 252 private: 253 TaskProfiles(); 254 255 bool Load(const CgroupMap& cg_map, const std::string& file_name); 256 257 std::map<std::string, std::shared_ptr<TaskProfile>, std::less<>> profiles_; 258 std::map<std::string, std::unique_ptr<IProfileAttribute>, std::less<>> attributes_; 259 }; 260 261 std::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid); 262 std::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid); 263