core_scheduling.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chromeos/system/core_scheduling.h"
  5. #include <errno.h>
  6. #include <sys/prctl.h>
  7. #include <set>
  8. #include <string>
  9. #include "base/feature_list.h"
  10. #include "base/files/file_util.h"
  11. #include "base/logging.h"
  12. #include "base/metrics/field_trial_params.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/strings/stringprintf.h"
  15. #include "base/system/sys_info.h"
  16. #include "base/threading/thread_restrictions.h"
  17. // Downstream core scheduling interface for 4.19, 5.4 kernels.
  18. // TODO(b/152605392): Remove once those kernel versions are obsolete.
  19. #ifndef PR_SET_CORE_SCHED
  20. #define PR_SET_CORE_SCHED 0x200
  21. #endif
  22. // Upstream interface for core scheduling.
  23. #ifndef PR_SCHED_CORE
  24. #define PR_SCHED_CORE 62
  25. #define PR_SCHED_CORE_GET 0
  26. #define PR_SCHED_CORE_CREATE 1
  27. #define PR_SCHED_CORE_SHARE_TO 2
  28. #define PR_SCHED_CORE_SHARE_FROM 3
  29. #define PR_SCHED_CORE_MAX 4
  30. #endif
  31. #ifndef PID_T_MAX
  32. #define PID_T_MAX (1 << 30)
  33. #endif
  34. enum pid_type { PIDTYPE_PID = 0, PIDTYPE_TGID, PIDTYPE_PGID };
  35. namespace chromeos {
  36. namespace system {
  37. namespace {
  38. const base::Feature kCoreScheduling{"CoreSchedulingEnabled",
  39. base::FEATURE_ENABLED_BY_DEFAULT};
  40. }
  41. void EnableCoreSchedulingIfAvailable() {
  42. if (!base::FeatureList::IsEnabled(kCoreScheduling)) {
  43. return;
  44. }
  45. // prctl(2) will return EINVAL for unknown functions. We're tolerant to this
  46. // and will log an error message for non EINVAL errnos.
  47. if (prctl(PR_SET_CORE_SCHED, 1) == -1 &&
  48. prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, 0, PIDTYPE_PID, 0) == -1) {
  49. PLOG_IF(WARNING, errno != EINVAL) << "Unable to set core scheduling";
  50. }
  51. }
  52. bool IsCoreSchedulingAvailable() {
  53. static const bool kernel_support = []() {
  54. // Test for kernel 4.19, 5.4 downstream support.
  55. // Pass bad param `prctl(0x200, 2)`. If it is supported, we will get ERANGE
  56. // rather than EINVAL.
  57. if (prctl(PR_SET_CORE_SCHED, 2) == -1 && errno == ERANGE) {
  58. VLOG(1) << "Core scheduling legacy interface supported";
  59. return true;
  60. }
  61. // Test for kernel 5.10 upstream support.
  62. // Pass bad param pid=PID_T_MAX. If it is supported, we will get ENODEV
  63. // (supported by not enabled) or ESRCH (bad param).
  64. int res =
  65. prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, PID_T_MAX, PIDTYPE_PID, 0);
  66. int saved_errno = errno;
  67. if (res == -1 && saved_errno == ENODEV) {
  68. VLOG(1) << "Core scheduling supported, but not enabled "
  69. "(likely because SMT is not enabled)";
  70. return false;
  71. }
  72. if (res == -1 && saved_errno == ESRCH) {
  73. VLOG(1) << "Core scheduling supported and enabled";
  74. return true;
  75. }
  76. VLOG(1) << "Core scheduling not supported in kernel";
  77. return false;
  78. }();
  79. if (!kernel_support) {
  80. return false;
  81. }
  82. static const bool has_vulns = []() {
  83. // Reading from sysfs doesn't block.
  84. base::ScopedAllowBlocking scoped_allow_blocking;
  85. base::FilePath sysfs_vulns("/sys/devices/system/cpu/vulnerabilities");
  86. std::string buf;
  87. for (const std::string& s : {"l1tf", "mds"}) {
  88. base::FilePath vuln = sysfs_vulns.Append(s);
  89. if (!base::ReadFileToString(vuln, &buf)) {
  90. LOG(ERROR) << "Could not read " << vuln;
  91. continue;
  92. }
  93. base::TrimWhitespaceASCII(buf, base::TRIM_ALL, &buf);
  94. if (buf != "Not affected") {
  95. VLOG(1) << "Core scheduling available: " << vuln << "=" << buf;
  96. return true;
  97. }
  98. }
  99. VLOG(1) << "Core scheduling not required";
  100. return false;
  101. }();
  102. return has_vulns;
  103. }
  104. int NumberOfPhysicalCores() {
  105. // cat /sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list |\
  106. // sort -u | wc -l
  107. static const int num_physical_cores = []() {
  108. // Reading from sysfs doesn't block.
  109. base::ScopedAllowBlocking scoped_allow_blocking;
  110. std::set<std::string> lists;
  111. std::string buf;
  112. for (int i = 0; i < base::SysInfo::NumberOfProcessors(); ++i) {
  113. base::FilePath list(base::StringPrintf(
  114. "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i));
  115. if (!base::ReadFileToString(list, &buf)) {
  116. LOG(ERROR) << "Could not read " << list;
  117. } else {
  118. lists.insert(buf);
  119. }
  120. }
  121. return lists.size() > 0 ? lists.size() : 1;
  122. }();
  123. return num_physical_cores;
  124. }
  125. } // namespace system
  126. } // namespace chromeos