Home | History | Annotate | Download | only in performanced
      1 #include "performance_service.h"
      2 
      3 #include <sched.h>
      4 #include <sys/prctl.h>
      5 #include <unistd.h>
      6 
      7 #include <pdx/default_transport/service_endpoint.h>
      8 #include <pdx/rpc/argument_encoder.h>
      9 #include <pdx/rpc/message_buffer.h>
     10 #include <pdx/rpc/remote_method.h>
     11 #include <private/android_filesystem_config.h>
     12 #include <private/dvr/performance_rpc.h>
     13 #include <private/dvr/trusted_uids.h>
     14 
     15 #include "task.h"
     16 
     17 // This prctl is only available in Android kernels.
     18 #define PR_SET_TIMERSLACK_PID 41
     19 
     20 using android::dvr::IsTrustedUid;
     21 using android::dvr::Task;
     22 using android::pdx::ErrorStatus;
     23 using android::pdx::Message;
     24 using android::pdx::Status;
     25 using android::pdx::default_transport::Endpoint;
     26 using android::pdx::rpc::DispatchRemoteMethod;
     27 
     28 namespace {
     29 
     30 const char kCpuSetBasePath[] = "/dev/cpuset";
     31 
     32 const char kRootCpuSet[] = "/";
     33 
     34 constexpr unsigned long kTimerSlackForegroundNs = 50000;
     35 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
     36 
     37 // Expands the given parameter pack expression using an initializer list to
     38 // guarantee ordering and a comma expression to guarantee even void expressions
     39 // are valid elements of the initializer list.
     40 #define EXPAND_PACK(...) \
     41   std::initializer_list<int> { (__VA_ARGS__, 0)... }
     42 
     43 // Returns true if the sender's euid matches any of the uids in |UIDs|.
     44 template <uid_t... UIDs>
     45 struct UserId {
     46   static bool Check(const Message& sender, const Task&) {
     47     const uid_t uid = sender.GetEffectiveUserId();
     48     bool allow = false;
     49     EXPAND_PACK(allow |= (uid == UIDs));
     50     return allow;
     51   }
     52 };
     53 
     54 // Returns true if the sender's egid matches any of the gids in |GIDs|.
     55 template <gid_t... GIDs>
     56 struct GroupId {
     57   static bool Check(const Message& sender, const Task&) {
     58     const gid_t gid = sender.GetEffectiveGroupId();
     59     bool allow = false;
     60     EXPAND_PACK(allow |= (gid == GIDs));
     61     return allow;
     62   }
     63 };
     64 
     65 // Returns true if the sender's euid is trusted according to VR manager service.
     66 struct Trusted {
     67   static bool Check(const Message& sender, const Task&) {
     68     return IsTrustedUid(sender.GetEffectiveUserId());
     69   }
     70 };
     71 
     72 // Returns returns true if the task belongs to the sending process.
     73 struct SameProcess {
     74   static bool Check(const Message& sender, const Task& task) {
     75     return sender.GetProcessId() == task.thread_group_id();
     76   }
     77 };
     78 
     79 // Returns true if any of the checks in |Allows| pass, false otherwise.
     80 template <typename... Allows>
     81 struct CheckOr {
     82   static bool Check(const Message& sender, const Task& task) {
     83     bool allow = false;
     84     EXPAND_PACK(allow |= Allows::Check(sender, task));
     85     return allow;
     86   }
     87 };
     88 
     89 // Returns true if all of the checks in |Allows| pass, false otherwise.
     90 template <typename... Allows>
     91 struct CheckAnd {
     92   static bool Check(const Message& sender, const Task& task) {
     93     bool allow = true;
     94     EXPAND_PACK(allow &= Allows::Check(sender, task));
     95     return allow;
     96   }
     97 };
     98 
     99 }  // anonymous namespace
    100 
    101 namespace android {
    102 namespace dvr {
    103 
    104 PerformanceService::PerformanceService()
    105     : BASE("PerformanceService",
    106            Endpoint::Create(PerformanceRPC::kClientPath)) {
    107   cpuset_.Load(kCpuSetBasePath);
    108 
    109   Task task(getpid());
    110   ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
    111         task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
    112 
    113   // Errors here are checked in IsInitialized().
    114   sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
    115   sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
    116 
    117   const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
    118   const int fifo_low = sched_fifo_min_priority_;
    119   const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
    120 
    121   // TODO(eieio): Make this configurable on the command line or config file.
    122   cpuset_.MoveUnboundTasks("/kernel");
    123 
    124   // TODO(eieio): Replace this witha device-specific config file. This is just a
    125   // hack for now to put some form of permission logic in place while a longer
    126   // term solution is developed.
    127   using AllowRootSystem =
    128       CheckAnd<SameProcess,
    129                CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>;
    130   using AllowRootSystemGraphics =
    131       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
    132                                     GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
    133   using AllowRootSystemAudio =
    134       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
    135                                     GroupId<AID_SYSTEM, AID_AUDIO>>>;
    136   using AllowRootSystemTrusted =
    137       CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>;
    138 
    139   partition_permission_check_ = AllowRootSystemTrusted::Check;
    140 
    141   // Setup the scheduler classes.
    142   // TODO(eieio): Replace this with a device-specific config file.
    143   scheduler_policies_ = {
    144       {"audio:low",
    145        {.timer_slack = kTimerSlackForegroundNs,
    146         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    147         .priority = fifo_medium,
    148         .permission_check = AllowRootSystemAudio::Check}},
    149       {"audio:high",
    150        {.timer_slack = kTimerSlackForegroundNs,
    151         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    152         .priority = fifo_medium + 3,
    153         .permission_check = AllowRootSystemAudio::Check}},
    154       {"graphics",
    155        {.timer_slack = kTimerSlackForegroundNs,
    156         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    157         .priority = fifo_medium,
    158         .permission_check = AllowRootSystemGraphics::Check}},
    159       {"graphics:low",
    160        {.timer_slack = kTimerSlackForegroundNs,
    161         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    162         .priority = fifo_medium,
    163         .permission_check = AllowRootSystemGraphics::Check}},
    164       {"graphics:high",
    165        {.timer_slack = kTimerSlackForegroundNs,
    166         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    167         .priority = fifo_medium + 2,
    168         .permission_check = AllowRootSystemGraphics::Check}},
    169       {"sensors",
    170        {.timer_slack = kTimerSlackForegroundNs,
    171         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    172         .priority = fifo_low,
    173         .permission_check = AllowRootSystem::Check}},
    174       {"sensors:low",
    175        {.timer_slack = kTimerSlackForegroundNs,
    176         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    177         .priority = fifo_low,
    178         .permission_check = AllowRootSystem::Check}},
    179       {"sensors:high",
    180        {.timer_slack = kTimerSlackForegroundNs,
    181         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    182         .priority = fifo_low + 1,
    183         .permission_check = AllowRootSystem::Check}},
    184       {"vr:system:arp",
    185        {.timer_slack = kTimerSlackForegroundNs,
    186         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    187         .priority = fifo_medium + 2,
    188         .permission_check = AllowRootSystemTrusted::Check,
    189         "/system/performance"}},
    190       {"vr:app:render",
    191        {.timer_slack = kTimerSlackForegroundNs,
    192         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
    193         .priority = fifo_medium + 1,
    194         .permission_check = AllowRootSystemTrusted::Check,
    195         "/application/performance"}},
    196       {"normal",
    197        {.timer_slack = kTimerSlackForegroundNs,
    198         .scheduler_policy = SCHED_NORMAL,
    199         .priority = 0}},
    200       {"foreground",
    201        {.timer_slack = kTimerSlackForegroundNs,
    202         .scheduler_policy = SCHED_NORMAL,
    203         .priority = 0}},
    204       {"background",
    205        {.timer_slack = kTimerSlackBackgroundNs,
    206         .scheduler_policy = SCHED_BATCH,
    207         .priority = 0}},
    208       {"batch",
    209        {.timer_slack = kTimerSlackBackgroundNs,
    210         .scheduler_policy = SCHED_BATCH,
    211         .priority = 0}},
    212   };
    213 }
    214 
    215 bool PerformanceService::IsInitialized() const {
    216   return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
    217          sched_fifo_max_priority_ >= 0;
    218 }
    219 
    220 std::string PerformanceService::DumpState(size_t /*max_length*/) {
    221   return cpuset_.DumpState();
    222 }
    223 
    224 Status<void> PerformanceService::OnSetSchedulerPolicy(
    225     Message& message, pid_t task_id, const std::string& scheduler_policy) {
    226   ALOGI(
    227       "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
    228       "scheduler_policy=%s",
    229       task_id, scheduler_policy.c_str());
    230 
    231   Task task(task_id);
    232   if (!task) {
    233     ALOGE(
    234         "PerformanceService::OnSetSchedulerPolicy: Unable to access /proc/%d "
    235         "to gather task information.",
    236         task_id);
    237     return ErrorStatus(EINVAL);
    238   }
    239 
    240   auto search = scheduler_policies_.find(scheduler_policy);
    241   if (search != scheduler_policies_.end()) {
    242     auto config = search->second;
    243 
    244     // Make sure the sending process is allowed to make the requested change to
    245     // this task.
    246     if (!config.IsAllowed(message, task))
    247       return ErrorStatus(EINVAL);
    248 
    249     // Get the thread group's cpu set. Policies that do not specify a cpuset
    250     // should default to this cpuset.
    251     std::string thread_group_cpuset;
    252     Task thread_group{task.thread_group_id()};
    253     if (thread_group) {
    254       thread_group_cpuset = thread_group.GetCpuSetPath();
    255     } else {
    256       ALOGE(
    257           "PerformanceService::OnSetSchedulerPolicy: Failed to get thread "
    258           "group tgid=%d for task_id=%d",
    259           task.thread_group_id(), task_id);
    260       thread_group_cpuset = kRootCpuSet;
    261     }
    262 
    263     std::string target_cpuset;
    264     if (config.cpuset.empty()) {
    265       target_cpuset = thread_group_cpuset;
    266     } else {
    267       target_cpuset = config.cpuset;
    268     }
    269     ALOGI("PerformanceService::OnSetSchedulerPolicy: Using cpuset=%s",
    270           target_cpuset.c_str());
    271 
    272     auto target_set = cpuset_.Lookup(target_cpuset);
    273     if (target_set) {
    274       auto attach_status = target_set->AttachTask(task_id);
    275       ALOGW_IF(!attach_status,
    276                "PerformanceService::OnSetSchedulerPolicy: Failed to attach "
    277                "task=%d to cpuset=%s: %s",
    278                task_id, target_cpuset.c_str(),
    279                attach_status.GetErrorMessage().c_str());
    280     } else {
    281       ALOGW(
    282           "PerformanceService::OnSetSchedulerPolicy: Failed to lookup "
    283           "cpuset=%s",
    284           target_cpuset.c_str());
    285     }
    286 
    287     struct sched_param param;
    288     param.sched_priority = config.priority;
    289 
    290     sched_setscheduler(task_id, config.scheduler_policy, &param);
    291     prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
    292     return {};
    293   } else {
    294     ALOGE(
    295         "PerformanceService::OnSetSchedulerPolicy: Invalid scheduler_policy=%s "
    296         "requested by task=%d.",
    297         scheduler_policy.c_str(), task_id);
    298     return ErrorStatus(EINVAL);
    299   }
    300 }
    301 
    302 Status<void> PerformanceService::OnSetCpuPartition(
    303     Message& message, pid_t task_id, const std::string& partition) {
    304   Task task(task_id);
    305   if (!task || task.thread_group_id() != message.GetProcessId())
    306     return ErrorStatus(EINVAL);
    307 
    308   // Temporary permission check.
    309   // TODO(eieio): Replace this with a configuration file.
    310   if (partition_permission_check_ &&
    311       !partition_permission_check_(message, task)) {
    312     return ErrorStatus(EINVAL);
    313   }
    314 
    315   auto target_set = cpuset_.Lookup(partition);
    316   if (!target_set)
    317     return ErrorStatus(ENOENT);
    318 
    319   auto attach_status = target_set->AttachTask(task_id);
    320   if (!attach_status)
    321     return attach_status;
    322 
    323   return {};
    324 }
    325 
    326 Status<void> PerformanceService::OnSetSchedulerClass(
    327     Message& message, pid_t task_id, const std::string& scheduler_class) {
    328   Task task(task_id);
    329   if (!task)
    330     return ErrorStatus(EINVAL);
    331 
    332   auto search = scheduler_policies_.find(scheduler_class);
    333   if (search != scheduler_policies_.end()) {
    334     auto config = search->second;
    335 
    336     // Make sure the sending process is allowed to make the requested change to
    337     // this task.
    338     if (!config.IsAllowed(message, task))
    339       return ErrorStatus(EINVAL);
    340 
    341     struct sched_param param;
    342     param.sched_priority = config.priority;
    343 
    344     sched_setscheduler(task_id, config.scheduler_policy, &param);
    345     prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
    346     ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
    347           task_id, scheduler_class.c_str());
    348     return {};
    349   } else {
    350     ALOGE(
    351         "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
    352         "by task=%d.",
    353         scheduler_class.c_str(), task_id);
    354     return ErrorStatus(EINVAL);
    355   }
    356 }
    357 
    358 Status<std::string> PerformanceService::OnGetCpuPartition(Message& message,
    359                                                           pid_t task_id) {
    360   // Make sure the task id is valid and belongs to the sending process.
    361   Task task(task_id);
    362   if (!task || task.thread_group_id() != message.GetProcessId())
    363     return ErrorStatus(EINVAL);
    364 
    365   return task.GetCpuSetPath();
    366 }
    367 
    368 Status<void> PerformanceService::HandleMessage(Message& message) {
    369   ALOGD_IF(TRACE, "PerformanceService::HandleMessage: op=%d", message.GetOp());
    370   switch (message.GetOp()) {
    371     case PerformanceRPC::SetSchedulerPolicy::Opcode:
    372       DispatchRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
    373           *this, &PerformanceService::OnSetSchedulerPolicy, message);
    374       return {};
    375 
    376     case PerformanceRPC::SetCpuPartition::Opcode:
    377       DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>(
    378           *this, &PerformanceService::OnSetCpuPartition, message);
    379       return {};
    380 
    381     case PerformanceRPC::SetSchedulerClass::Opcode:
    382       DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
    383           *this, &PerformanceService::OnSetSchedulerClass, message);
    384       return {};
    385 
    386     case PerformanceRPC::GetCpuPartition::Opcode:
    387       DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
    388           *this, &PerformanceService::OnGetCpuPartition, message);
    389       return {};
    390 
    391     default:
    392       return Service::HandleMessage(message);
    393   }
    394 }
    395 
    396 }  // namespace dvr
    397 }  // namespace android
    398