1 /*
2 * Copyright 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "cpu_info.h" // NOLINT
17
18 #include <unistd.h>
19
20 #include <cerrno>
21 #include <climits>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25
26 namespace gav1_jni {
27 namespace {
28
29 // Note: The code in this file needs to use the 'long' type because it is the
30 // return type of the Standard C Library function strtol(). The linter warnings
31 // are suppressed with NOLINT comments since they are integers at runtime.
32
33 // Returns the number of online processor cores.
GetNumberOfProcessorsOnline()34 int GetNumberOfProcessorsOnline() {
35 // See https://developer.android.com/ndk/guides/cpu-features.
36 long num_cpus = sysconf(_SC_NPROCESSORS_ONLN); // NOLINT
37 if (num_cpus < 0) {
38 return 0;
39 }
40 // It is safe to cast num_cpus to int. sysconf(_SC_NPROCESSORS_ONLN) returns
41 // the return value of get_nprocs(), which is an int.
42 return static_cast<int>(num_cpus);
43 }
44
45 } // namespace
46
47 // These CPUs support heterogeneous multiprocessing.
48 #if defined(__arm__) || defined(__aarch64__)
49
50 // A helper function used by GetNumberOfPerformanceCoresOnline().
51 //
52 // Returns the cpuinfo_max_freq value (in kHz) of the given CPU. Returns 0 on
53 // failure.
GetCpuinfoMaxFreq(int cpu_index)54 long GetCpuinfoMaxFreq(int cpu_index) { // NOLINT
55 char buffer[128];
56 const int rv = snprintf(
57 buffer, sizeof(buffer),
58 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu_index);
59 if (rv < 0 || rv >= sizeof(buffer)) {
60 return 0;
61 }
62 FILE* file = fopen(buffer, "r");
63 if (file == nullptr) {
64 return 0;
65 }
66 char* const str = fgets(buffer, sizeof(buffer), file);
67 fclose(file);
68 if (str == nullptr) {
69 return 0;
70 }
71 const long freq = strtol(str, nullptr, 10); // NOLINT
72 if (freq <= 0 || freq == LONG_MAX) {
73 return 0;
74 }
75 return freq;
76 }
77
78 // Returns the number of performance CPU cores that are online. The number of
79 // efficiency CPU cores is subtracted from the total number of CPU cores. Uses
80 // cpuinfo_max_freq to determine whether a CPU is a performance core or an
81 // efficiency core.
82 //
83 // This function is not perfect. For example, the Snapdragon 632 SoC used in
84 // Motorola Moto G7 has performance and efficiency cores with the same
85 // cpuinfo_max_freq but different cpuinfo_min_freq. This function fails to
86 // differentiate the two kinds of cores and reports all the cores as
87 // performance cores.
GetNumberOfPerformanceCoresOnline()88 int GetNumberOfPerformanceCoresOnline() {
89 // Get the online CPU list. Some examples of the online CPU list are:
90 // "0-7"
91 // "0"
92 // "0-1,2,3,4-7"
93 FILE* file = fopen("/sys/devices/system/cpu/online", "r");
94 if (file == nullptr) {
95 return 0;
96 }
97 char online[512];
98 char* const str = fgets(online, sizeof(online), file);
99 fclose(file);
100 file = nullptr;
101 if (str == nullptr) {
102 return 0;
103 }
104
105 // Count the number of the slowest CPUs. Some SoCs such as Snapdragon 855
106 // have performance cores with different max frequencies, so only the slowest
107 // CPUs are efficiency cores. If we count the number of the fastest CPUs, we
108 // will fail to count the second fastest performance cores.
109 long slowest_cpu_freq = LONG_MAX; // NOLINT
110 int num_slowest_cpus = 0;
111 int num_cpus = 0;
112 const char* cp = online;
113 int range_begin = -1;
114 while (true) {
115 char* str_end;
116 const int cpu = static_cast<int>(strtol(cp, &str_end, 10)); // NOLINT
117 if (str_end == cp) {
118 break;
119 }
120 cp = str_end;
121 if (*cp == '-') {
122 range_begin = cpu;
123 } else {
124 if (range_begin == -1) {
125 range_begin = cpu;
126 }
127
128 num_cpus += cpu - range_begin + 1;
129 for (int i = range_begin; i <= cpu; ++i) {
130 const long freq = GetCpuinfoMaxFreq(i); // NOLINT
131 if (freq <= 0) {
132 return 0;
133 }
134 if (freq < slowest_cpu_freq) {
135 slowest_cpu_freq = freq;
136 num_slowest_cpus = 0;
137 }
138 if (freq == slowest_cpu_freq) {
139 ++num_slowest_cpus;
140 }
141 }
142
143 range_begin = -1;
144 }
145 if (*cp == '\0') {
146 break;
147 }
148 ++cp;
149 }
150
151 // If there are faster CPU cores than the slowest CPU cores, exclude the
152 // slowest CPU cores.
153 if (num_slowest_cpus < num_cpus) {
154 num_cpus -= num_slowest_cpus;
155 }
156 return num_cpus;
157 }
158
159 #else
160
161 // Assume symmetric multiprocessing.
GetNumberOfPerformanceCoresOnline()162 int GetNumberOfPerformanceCoresOnline() {
163 return GetNumberOfProcessorsOnline();
164 }
165
166 #endif
167
168 } // namespace gav1_jni
169