blob: 4838b4ce7df0ab48728da457be7623a3ebaca0e8 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "login_manager/scheduler_util.h"
#include <algorithm>
#include <map>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_piece.h>
#include <base/strings/string_util.h>
namespace {
// Size of the prefix 'cpu'.
constexpr size_t kCpuPrefixSize = 3;
constexpr char kCpuBusDir[] = "/sys/bus/cpu/devices";
constexpr const char* kPerfAttributeFiles[] = {
"cpu_capacity", "cpufreq/cpuinfo_max_freq", "acpi_cppc/highest_perf"};
constexpr char kCpusetNonUrgentDir[] =
"/sys/fs/cgroup/cpuset/chrome/non-urgent";
} // namespace
namespace login_manager {
std::vector<std::string> GetSmallCoreCpuIdsFromAttr(
const base::FilePath& cpu_bus_dir, base::StringPiece attribute) {
base::FilePath cpu0_attr_file = cpu_bus_dir.Append("cpu0").Append(attribute);
if (!base::PathExists(cpu0_attr_file))
return {};
// Gets attribute values through traversing the attribute of each cpu, and
// stores them into a map.
base::FileEnumerator enumerator(cpu_bus_dir, false /*recursive*/,
base::FileEnumerator::DIRECTORIES);
std::map<int, std::vector<std::string>> attr_to_cpu_ids_map;
for (base::FilePath subdir = enumerator.Next(); !subdir.empty();
subdir = enumerator.Next()) {
std::string item_str;
if (base::ReadFileToString(subdir.Append(attribute), &item_str)) {
std::string subdir_name = subdir.BaseName().value();
DCHECK_GT(subdir_name.size(), kCpuPrefixSize);
int item = atoi(item_str.c_str());
if (item <= 0) {
LOG(ERROR) << "Invalid value read from " << subdir_name
<< " attribute file!";
continue;
}
std::string cpu_id = subdir_name.substr(kCpuPrefixSize);
attr_to_cpu_ids_map[item].emplace_back(cpu_id);
}
}
// If the number of attribute value is 1, the cpu arch is not hybrid, the
// small core cpu id list is empty.
if (attr_to_cpu_ids_map.size() <= 1)
return {};
auto it = attr_to_cpu_ids_map.begin();
std::vector<std::string> small_cpu_ids = it->second;
// If the map has more than 2 attribute values, we consider the cpus with two
// smallest capacities / freqs as small cores.
if (attr_to_cpu_ids_map.size() > 2) {
++it;
small_cpu_ids.insert(small_cpu_ids.end(), it->second.begin(),
it->second.end());
}
std::sort(small_cpu_ids.begin(), small_cpu_ids.end());
return small_cpu_ids;
}
std::vector<std::string> CalculateSmallCoreCpusIfHybrid(
const base::FilePath& cpu_bus_dir) {
// sysfs attributes are probed in order they are appear in
// #kPerfAttributeFiles
for (const auto& perf_attr : kPerfAttributeFiles) {
if (std::vector<std::string> small_cpu_ids =
GetSmallCoreCpuIdsFromAttr(cpu_bus_dir, perf_attr);
!small_cpu_ids.empty()) {
return small_cpu_ids;
}
}
return {};
}
bool ConfigureNonUrgentCpuset(brillo::CrosConfigInterface* cros_config) {
base::FilePath nonurgent_path(kCpusetNonUrgentDir);
if (!base::PathExists(nonurgent_path)) {
LOG(WARNING) << "The path of non-urgent cpuset doesn't exist!";
return false;
}
std::string cpuset_conf;
// Writes cpuset-nonurgent to non-urgent cpuset if it's specified in
// cros_config.
if (cros_config &&
cros_config->GetString("/scheduler-tune", "cpuset-nonurgent",
&cpuset_conf) &&
!cpuset_conf.empty()) {
if (!base::WriteFile(nonurgent_path.Append("cpus"), cpuset_conf)) {
LOG(ERROR) << "Error writing non urgent cpuset!";
return false;
}
LOG(INFO) << "Non-urgent cpuset is " << cpuset_conf << " from cros_config";
return true;
}
// Use all small cores as non-urgent cpuset, if cpuset-nonurgent isn't
// specified in cros_config.
std::vector<std::string> ecpu_ids =
CalculateSmallCoreCpusIfHybrid(base::FilePath(kCpuBusDir));
if (ecpu_ids.empty())
return false;
std::string ecpu_mask = base::JoinString(ecpu_ids, ",");
LOG(INFO) << "The board has hybrid arch cpu, the non-urgent cpuset is "
<< ecpu_mask << ".";
if (!base::WriteFile(nonurgent_path.Append("cpus"), ecpu_mask)) {
LOG(ERROR) << "Error writing mask of small cores to non urgent cpuset!";
return false;
}
return true;
}
} // namespace login_manager