Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d211903f authored by Corey Tabaka's avatar Corey Tabaka Committed by android-build-merger
Browse files

Merge "Add cpuset switching to scheduler policy mechanism." into oc-mr1-dev

am: 1c6fdd5e

Change-Id: I0e1c35816de46929a1e25c2046bf70d9b2c5ab14
parents f7a6a461 1c6fdd5e
Loading
Loading
Loading
Loading
+84 −14
Original line number Original line Diff line number Diff line
@@ -22,13 +22,15 @@ using android::dvr::Task;
using android::pdx::ErrorStatus;
using android::pdx::ErrorStatus;
using android::pdx::Message;
using android::pdx::Message;
using android::pdx::Status;
using android::pdx::Status;
using android::pdx::rpc::DispatchRemoteMethod;
using android::pdx::default_transport::Endpoint;
using android::pdx::default_transport::Endpoint;
using android::pdx::rpc::DispatchRemoteMethod;


namespace {
namespace {


const char kCpuSetBasePath[] = "/dev/cpuset";
const char kCpuSetBasePath[] = "/dev/cpuset";


const char kRootCpuSet[] = "/";

constexpr unsigned long kTimerSlackForegroundNs = 50000;
constexpr unsigned long kTimerSlackForegroundNs = 50000;
constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
constexpr unsigned long kTimerSlackBackgroundNs = 40000000;


@@ -123,22 +125,22 @@ PerformanceService::PerformanceService()
  // hack for now to put some form of permission logic in place while a longer
  // hack for now to put some form of permission logic in place while a longer
  // term solution is developed.
  // term solution is developed.
  using AllowRootSystem =
  using AllowRootSystem =
      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM>,
      CheckAnd<SameProcess,
                                    GroupId<AID_SYSTEM>>>;
               CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>;
  using AllowRootSystemGraphics =
  using AllowRootSystemGraphics =
      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
                                    GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
                                    GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
  using AllowRootSystemAudio =
  using AllowRootSystemAudio =
      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
                                    GroupId<AID_SYSTEM, AID_AUDIO>>>;
                                    GroupId<AID_SYSTEM, AID_AUDIO>>>;
  using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>,
  using AllowRootSystemTrusted =
                                        GroupId<AID_SYSTEM>>;
      CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>;


  partition_permission_check_ = AllowRootSystemTrusted::Check;
  partition_permission_check_ = AllowRootSystemTrusted::Check;


  // Setup the scheduler classes.
  // Setup the scheduler classes.
  // TODO(eieio): Replace this with a device-specific config file.
  // TODO(eieio): Replace this with a device-specific config file.
  scheduler_classes_ = {
  scheduler_policies_ = {
      {"audio:low",
      {"audio:low",
       {.timer_slack = kTimerSlackForegroundNs,
       {.timer_slack = kTimerSlackForegroundNs,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
@@ -183,12 +185,14 @@ PerformanceService::PerformanceService()
       {.timer_slack = kTimerSlackForegroundNs,
       {.timer_slack = kTimerSlackForegroundNs,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
        .priority = fifo_medium + 2,
        .priority = fifo_medium + 2,
        .permission_check = AllowRootSystemTrusted::Check}},
        .permission_check = AllowRootSystemTrusted::Check,
        "/system/performance"}},
      {"vr:app:render",
      {"vr:app:render",
       {.timer_slack = kTimerSlackForegroundNs,
       {.timer_slack = kTimerSlackForegroundNs,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
        .priority = fifo_medium + 1,
        .priority = fifo_medium + 1,
        .permission_check = AllowRootSystemTrusted::Check}},
        .permission_check = AllowRootSystemTrusted::Check,
        "/application/performance"}},
      {"normal",
      {"normal",
       {.timer_slack = kTimerSlackForegroundNs,
       {.timer_slack = kTimerSlackForegroundNs,
        .scheduler_policy = SCHED_NORMAL,
        .scheduler_policy = SCHED_NORMAL,
@@ -219,14 +223,80 @@ std::string PerformanceService::DumpState(size_t /*max_length*/) {


Status<void> PerformanceService::OnSetSchedulerPolicy(
Status<void> PerformanceService::OnSetSchedulerPolicy(
    Message& message, pid_t task_id, const std::string& scheduler_policy) {
    Message& message, pid_t task_id, const std::string& scheduler_policy) {
  // Forward to scheduler class handler for now. In the future this method will
  // subsume the others by unifying both scheduler class and cpu partiton into a
  // single policy concept.
  ALOGI(
  ALOGI(
      "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
      "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
      "scheduler_policy=%s",
      "scheduler_policy=%s",
      task_id, scheduler_policy.c_str());
      task_id, scheduler_policy.c_str());
  return OnSetSchedulerClass(message, task_id, scheduler_policy);

  Task task(task_id);
  if (!task) {
    ALOGE(
        "PerformanceService::OnSetSchedulerPolicy: Unable to access /proc/%d "
        "to gather task information.",
        task_id);
    return ErrorStatus(EINVAL);
  }

  auto search = scheduler_policies_.find(scheduler_policy);
  if (search != scheduler_policies_.end()) {
    auto config = search->second;

    // Make sure the sending process is allowed to make the requested change to
    // this task.
    if (!config.IsAllowed(message, task))
      return ErrorStatus(EINVAL);

    // Get the thread group's cpu set. Policies that do not specify a cpuset
    // should default to this cpuset.
    std::string thread_group_cpuset;
    Task thread_group{task.thread_group_id()};
    if (thread_group) {
      thread_group_cpuset = thread_group.GetCpuSetPath();
    } else {
      ALOGE(
          "PerformanceService::OnSetSchedulerPolicy: Failed to get thread "
          "group tgid=%d for task_id=%d",
          task.thread_group_id(), task_id);
      thread_group_cpuset = kRootCpuSet;
    }

    std::string target_cpuset;
    if (config.cpuset.empty()) {
      target_cpuset = thread_group_cpuset;
    } else {
      target_cpuset = config.cpuset;
    }
    ALOGI("PerformanceService::OnSetSchedulerPolicy: Using cpuset=%s",
          target_cpuset.c_str());

    auto target_set = cpuset_.Lookup(target_cpuset);
    if (target_set) {
      auto attach_status = target_set->AttachTask(task_id);
      ALOGW_IF(!attach_status,
               "PerformanceService::OnSetSchedulerPolicy: Failed to attach "
               "task=%d to cpuset=%s: %s",
               task_id, target_cpuset.c_str(),
               attach_status.GetErrorMessage().c_str());
    } else {
      ALOGW(
          "PerformanceService::OnSetSchedulerPolicy: Failed to lookup "
          "cpuset=%s",
          target_cpuset.c_str());
    }

    struct sched_param param;
    param.sched_priority = config.priority;

    sched_setscheduler(task_id, config.scheduler_policy, &param);
    prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
    return {};
  } else {
    ALOGE(
        "PerformanceService::OnSetSchedulerPolicy: Invalid scheduler_policy=%s "
        "requested by task=%d.",
        scheduler_policy.c_str(), task_id);
    return ErrorStatus(EINVAL);
  }
}
}


Status<void> PerformanceService::OnSetCpuPartition(
Status<void> PerformanceService::OnSetCpuPartition(
@@ -259,8 +329,8 @@ Status<void> PerformanceService::OnSetSchedulerClass(
  if (!task)
  if (!task)
    return ErrorStatus(EINVAL);
    return ErrorStatus(EINVAL);


  auto search = scheduler_classes_.find(scheduler_class);
  auto search = scheduler_policies_.find(scheduler_class);
  if (search != scheduler_classes_.end()) {
  if (search != scheduler_policies_.end()) {
    auto config = search->second;
    auto config = search->second;


    // Make sure the sending process is allowed to make the requested change to
    // Make sure the sending process is allowed to make the requested change to
+3 −3
Original line number Original line Diff line number Diff line
@@ -44,13 +44,13 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> {
  int sched_fifo_min_priority_;
  int sched_fifo_min_priority_;
  int sched_fifo_max_priority_;
  int sched_fifo_max_priority_;


  // Scheduler class config type.
  struct SchedulerPolicyConfig {
  struct SchedulerClassConfig {
    unsigned long timer_slack;
    unsigned long timer_slack;
    int scheduler_policy;
    int scheduler_policy;
    int priority;
    int priority;
    std::function<bool(const pdx::Message& message, const Task& task)>
    std::function<bool(const pdx::Message& message, const Task& task)>
        permission_check;
        permission_check;
    std::string cpuset;


    // Check the permisison of the given task to use this scheduler class. If a
    // Check the permisison of the given task to use this scheduler class. If a
    // permission check function is not set then operations are only allowed on
    // permission check function is not set then operations are only allowed on
@@ -65,7 +65,7 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> {
    }
    }
  };
  };


  std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_;
  std::unordered_map<std::string, SchedulerPolicyConfig> scheduler_policies_;


  std::function<bool(const pdx::Message& message, const Task& task)>
  std::function<bool(const pdx::Message& message, const Task& task)>
      partition_permission_check_;
      partition_permission_check_;
+93 −13
Original line number Original line Diff line number Diff line
#include <errno.h>
#include <errno.h>
#include <sched.h>
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>


#include <condition_variable>
#include <condition_variable>
#include <cstdlib>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <mutex>
#include <sstream>
#include <thread>
#include <thread>
#include <utility>


#include <android-base/unique_fd.h>
#include <dvr/performance_client_api.h>
#include <dvr/performance_client_api.h>
#include <gtest/gtest.h>
#include <gtest/gtest.h>
#include <private/android_filesystem_config.h>
#include <private/android_filesystem_config.h>


#include "stdio_filebuf.h"
#include "string_trim.h"
#include "unique_file.h"

using android::dvr::Trim;
using android::dvr::UniqueFile;
using android::dvr::stdio_filebuf;

namespace {
namespace {


const char kTrustedUidEnvironmentVariable[] = "GTEST_TRUSTED_UID";
const char kTrustedUidEnvironmentVariable[] = "GTEST_TRUSTED_UID";


const char kProcBase[] = "/proc";

std::pair<UniqueFile, int> OpenTaskFile(pid_t task_id,
                                        const std::string& name) {
  std::ostringstream stream;
  stream << kProcBase << "/" << task_id << "/" << name;

  UniqueFile file{fopen(stream.str().c_str(), "r")};
  const int error = file ? 0 : errno;
  return {std::move(file), error};
}

std::string GetTaskCpuSet(pid_t task_id) {
  int error;
  UniqueFile file;

  std::tie(file, error) = OpenTaskFile(task_id, "cpuset");
  if (!file)
    return std::string("errno:") + strerror(error);

  stdio_filebuf<char> filebuf(file.get());
  std::istream file_stream(&filebuf);

  std::string line;
  std::getline(file_stream, line);
  return Trim(line);
}

}  // anonymous namespace
}  // anonymous namespace


TEST(DISABLED_PerformanceTest, SetCpuPartition) {
TEST(PerformanceTest, SetCpuPartition) {
  int error;
  int error;


  // Test setting the the partition for the current task.
  // Test setting the the partition for the current task.
@@ -59,13 +100,6 @@ TEST(DISABLED_PerformanceTest, SetCpuPartition) {
  }
  }
  thread.join();
  thread.join();


  // Test setting the partition for a task that isn't valid using
  // the task id of the thread that we just joined. Technically the
  // id could wrap around by the time we get here, but this is
  // extremely unlikely.
  error = dvrSetCpuPartition(task_id, "/application");
  EXPECT_EQ(-EINVAL, error);

  // Test setting the partition for a task that doesn't belong to us.
  // Test setting the partition for a task that doesn't belong to us.
  error = dvrSetCpuPartition(1, "/application");
  error = dvrSetCpuPartition(1, "/application");
  EXPECT_EQ(-EINVAL, error);
  EXPECT_EQ(-EINVAL, error);
@@ -73,6 +107,10 @@ TEST(DISABLED_PerformanceTest, SetCpuPartition) {
  // Test setting the partition to one that doesn't exist.
  // Test setting the partition to one that doesn't exist.
  error = dvrSetCpuPartition(0, "/foobar");
  error = dvrSetCpuPartition(0, "/foobar");
  EXPECT_EQ(-ENOENT, error);
  EXPECT_EQ(-ENOENT, error);

  // Set the test back to the root partition.
  error = dvrSetCpuPartition(0, "/");
  EXPECT_EQ(0, error);
}
}


TEST(PerformanceTest, SetSchedulerClass) {
TEST(PerformanceTest, SetSchedulerClass) {
@@ -96,8 +134,6 @@ TEST(PerformanceTest, SetSchedulerClass) {
  EXPECT_EQ(-EINVAL, error);
  EXPECT_EQ(-EINVAL, error);
}
}


// This API mirrors SetSchedulerClass for now. Replace with with a more specific
// test once the policy API is fully implemented.
TEST(PerformanceTest, SetSchedulerPolicy) {
TEST(PerformanceTest, SetSchedulerPolicy) {
  int error;
  int error;


@@ -115,6 +151,50 @@ TEST(PerformanceTest, SetSchedulerPolicy) {


  error = dvrSetSchedulerPolicy(0, "foobar");
  error = dvrSetSchedulerPolicy(0, "foobar");
  EXPECT_EQ(-EINVAL, error);
  EXPECT_EQ(-EINVAL, error);

  // Set the test back to the root partition.
  error = dvrSetCpuPartition(0, "/");
  EXPECT_EQ(0, error);

  const std::string original_cpuset = GetTaskCpuSet(gettid());
  EXPECT_EQ("/", original_cpuset);

  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
  EXPECT_EQ(0, error);
  EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));

  const std::string new_cpuset = GetTaskCpuSet(gettid());
  EXPECT_NE(original_cpuset, new_cpuset);

  // The cpuset for the thread group is now new_cpuset. Scheduler profiles that
  // do not specify a cpuset should not change the cpuset of a thread, except to
  // restore it to the thread group cpuset.
  std::string thread_original_cpuset;
  std::string thread_new_cpuset;
  std::string thread_final_cpuset;

  std::thread thread{
      [&thread_original_cpuset, &thread_new_cpuset, &thread_final_cpuset]() {
        thread_original_cpuset = GetTaskCpuSet(gettid());

        int error = dvrSetSchedulerPolicy(0, "vr:app:render");
        EXPECT_EQ(0, error);

        thread_new_cpuset = GetTaskCpuSet(gettid());

        error = dvrSetSchedulerPolicy(0, "normal");
        EXPECT_EQ(0, error);

        thread_final_cpuset = GetTaskCpuSet(gettid());
      }};
  thread.join();

  EXPECT_EQ(new_cpuset, thread_original_cpuset);
  EXPECT_NE(new_cpuset, thread_new_cpuset);
  EXPECT_EQ(new_cpuset, thread_final_cpuset);

  error = dvrSetCpuPartition(0, original_cpuset.c_str());
  EXPECT_EQ(0, error);
}
}


TEST(PerformanceTest, SchedulerClassResetOnFork) {
TEST(PerformanceTest, SchedulerClassResetOnFork) {
@@ -424,11 +504,11 @@ TEST(PerformanceTest, Permissions) {
    error = dvrSetSchedulerPolicy(0, "audio:high");
    error = dvrSetSchedulerPolicy(0, "audio:high");
    EXPECT_EQ(-EINVAL, error);
    EXPECT_EQ(-EINVAL, error);
    error = dvrSetSchedulerPolicy(0, "graphics");
    error = dvrSetSchedulerPolicy(0, "graphics");
    EXPECT_EQ(0, error);
    EXPECT_EQ(-EINVAL, error);
    error = dvrSetSchedulerPolicy(0, "graphics:low");
    error = dvrSetSchedulerPolicy(0, "graphics:low");
    EXPECT_EQ(0, error);
    EXPECT_EQ(-EINVAL, error);
    error = dvrSetSchedulerPolicy(0, "graphics:high");
    error = dvrSetSchedulerPolicy(0, "graphics:high");
    EXPECT_EQ(0, error);
    EXPECT_EQ(-EINVAL, error);
    error = dvrSetSchedulerPolicy(0, "sensors");
    error = dvrSetSchedulerPolicy(0, "sensors");
    EXPECT_EQ(-EINVAL, error);
    EXPECT_EQ(-EINVAL, error);
    error = dvrSetSchedulerPolicy(0, "sensors:low");
    error = dvrSetSchedulerPolicy(0, "sensors:low");
+11 −8
Original line number Original line Diff line number Diff line
@@ -48,16 +48,19 @@ Task::Task(pid_t task_id)
      thread_count_(0),
      thread_count_(0),
      cpus_allowed_mask_(0) {
      cpus_allowed_mask_(0) {
  task_fd_ = OpenTaskDirectory(task_id_);
  task_fd_ = OpenTaskDirectory(task_id_);
  ALOGE_IF(task_fd_.get() < 0,
  const int error = errno;
  ALOGE_IF(task_fd_.get() < 0 && error != EACCES,
           "Task::Task: Failed to open task directory for task_id=%d: %s",
           "Task::Task: Failed to open task directory for task_id=%d: %s",
           task_id, strerror(errno));
           task_id, strerror(error));


  if (IsValid()) {
    ReadStatusFields();
    ReadStatusFields();

    ALOGD_IF(TRACE,
  ALOGD_IF(TRACE, "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x",
             "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x",
             task_id_, name_.c_str(), thread_group_id_, parent_process_id_,
             task_id_, name_.c_str(), thread_group_id_, parent_process_id_,
             cpus_allowed_mask_);
             cpus_allowed_mask_);
  }
  }
}


base::unique_fd Task::OpenTaskFile(const std::string& name) const {
base::unique_fd Task::OpenTaskFile(const std::string& name) const {
  const std::string relative_path = "./" + name;
  const std::string relative_path = "./" + name;