content: Enforce CPU affinity on Android

As a part of the experiment to run all execution on little cores
we want to make sure that the cpu affinity doesn't change at runtime,
e.g. when Chrome goes back from background. To do this, we introduce
an interface in content that allows to enforce affinity by checking
it on a regular basis (every 100 tasks) and setting it back when it
changes.

Change-Id: I766e94af42861870abe3a0079dd335a23462d481
Bug: 1111789
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2418673
Commit-Queue: Mikhail Khokhlov <[email protected]>
Reviewed-by: Robert Sesek <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Avi Drissman <[email protected]>
Reviewed-by: Eric Seckler <[email protected]>
Cr-Commit-Position: refs/heads/master@{#812027}
diff --git a/base/cpu_affinity_posix.cc b/base/cpu_affinity_posix.cc
index 35e02c64..cb033d8c 100644
--- a/base/cpu_affinity_posix.cc
+++ b/base/cpu_affinity_posix.cc
@@ -11,17 +11,29 @@
 
 namespace base {
 
-bool SetThreadCpuAffinityMode(PlatformThreadId thread_id,
-                              CpuAffinityMode affinity) {
+namespace {
+
+const cpu_set_t& AllCores() {
   static const cpu_set_t kAllCores = []() {
     cpu_set_t set;
-    memset(&set, 0xff, sizeof(set));
+    CPU_ZERO(&set);
+    std::vector<CPU::CoreType> core_types = CPU::GuessCoreTypes();
+    if (core_types.empty()) {
+      memset(&set, 0xff, sizeof(set));
+    } else {
+      for (size_t index = 0; index < core_types.size(); index++)
+        CPU_SET(index, &set);
+    }
     return set;
   }();
+  return kAllCores;
+}
+
+const cpu_set_t& LittleCores() {
   static const cpu_set_t kLittleCores = []() {
     std::vector<CPU::CoreType> core_types = CPU::GuessCoreTypes();
     if (core_types.empty())
-      return kAllCores;
+      return AllCores();
 
     cpu_set_t set;
     CPU_ZERO(&set);
@@ -32,7 +44,7 @@
         case CPU::CoreType::kSymmetric:
           // In the presence of an unknown core type or symmetric architecture,
           // fall back to allowing all cores.
-          return kAllCores;
+          return AllCores();
         case CPU::CoreType::kBigLittle_Little:
         case CPU::CoreType::kBigLittleBigger_Little:
           CPU_SET(core_index, &set);
@@ -45,18 +57,51 @@
     }
     return set;
   }();
+  return kLittleCores;
+}
 
+}  // anonymous namespace
+
+bool HasBigCpuCores() {
+  static const bool kHasBigCores = []() {
+    std::vector<CPU::CoreType> core_types = CPU::GuessCoreTypes();
+    if (core_types.empty())
+      return false;
+    for (CPU::CoreType core_type : core_types) {
+      switch (core_type) {
+        case CPU::CoreType::kUnknown:
+        case CPU::CoreType::kOther:
+        case CPU::CoreType::kSymmetric:
+          return false;
+        case CPU::CoreType::kBigLittle_Little:
+        case CPU::CoreType::kBigLittleBigger_Little:
+        case CPU::CoreType::kBigLittle_Big:
+        case CPU::CoreType::kBigLittleBigger_Big:
+        case CPU::CoreType::kBigLittleBigger_Bigger:
+          return true;
+      }
+    }
+    return false;
+  }();
+  return kHasBigCores;
+}
+
+bool SetThreadCpuAffinityMode(PlatformThreadId thread_id,
+                              CpuAffinityMode affinity) {
   int result = 0;
   switch (affinity) {
-    case CpuAffinityMode::kDefault:
-      result = sched_setaffinity(thread_id, sizeof(kAllCores), &kAllCores);
+    case CpuAffinityMode::kDefault: {
+      const cpu_set_t& all_cores = AllCores();
+      result = sched_setaffinity(thread_id, sizeof(all_cores), &all_cores);
       break;
-    case CpuAffinityMode::kLittleCoresOnly:
+    }
+    case CpuAffinityMode::kLittleCoresOnly: {
+      const cpu_set_t& little_cores = LittleCores();
       result =
-          sched_setaffinity(thread_id, sizeof(kLittleCores), &kLittleCores);
+          sched_setaffinity(thread_id, sizeof(little_cores), &little_cores);
       break;
+    }
   }
-
   return result == 0;
 }
 
@@ -75,4 +120,16 @@
   return any_threads && result;
 }
 
+Optional<CpuAffinityMode> CurrentThreadCpuAffinityMode() {
+  if (HasBigCpuCores()) {
+    cpu_set_t set;
+    sched_getaffinity(PlatformThread::CurrentId(), sizeof(set), &set);
+    if (CPU_EQUAL(&set, &AllCores()))
+      return CpuAffinityMode::kDefault;
+    if (CPU_EQUAL(&set, &LittleCores()))
+      return CpuAffinityMode::kLittleCoresOnly;
+  }
+  return nullopt;
+}
+
 }  // namespace base
diff --git a/base/cpu_affinity_posix.h b/base/cpu_affinity_posix.h
index 938a64c..5434a67 100644
--- a/base/cpu_affinity_posix.h
+++ b/base/cpu_affinity_posix.h
@@ -28,6 +28,15 @@
 BASE_EXPORT bool SetProcessCpuAffinityMode(ProcessHandle process_handle,
                                            CpuAffinityMode affinity);
 
+// Return true if the current architecture has big or bigger cores.
+BASE_EXPORT bool HasBigCpuCores();
+
+// For architectures with big cores, return the affinity mode that matches
+// the CPU affinity of the current thread. If no affinity mode exactly matches,
+// or if the architecture doesn't have different types of cores,
+// return nullopt.
+BASE_EXPORT base::Optional<CpuAffinityMode> CurrentThreadCpuAffinityMode();
+
 }  // namespace base
 
 #endif  // BASE_CPU_AFFINITY_POSIX_H_