1# Lint as: python2, python3 2# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import glob 7import logging 8import os 9import time 10from autotest_lib.client.bin import test 11from autotest_lib.client.common_lib import error, utils 12 13SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable' 14SYSFS_INTEL_PSTATE_PATH = '/sys/devices/system/cpu/intel_pstate' 15 16 17class power_CPUFreq(test.test): 18 version = 1 19 20 def initialize(self): 21 cpufreq_path = '/sys/devices/system/cpu/cpu*/cpufreq' 22 23 dirs = glob.glob(cpufreq_path) 24 if not dirs: 25 raise error.TestFail('cpufreq not supported') 26 27 self._cpufreq_dirs = dirs 28 self._cpus = [cpufreq(dirname) for dirname in dirs] 29 for cpu in self._cpus: 30 cpu.save_state() 31 cpu.disable_constraints() 32 33 # Store the setting if the system has CPUQuiet feature 34 if os.path.exists(SYSFS_CPUQUIET_ENABLE): 35 self.is_cpuquiet_enabled = utils.read_file(SYSFS_CPUQUIET_ENABLE) 36 utils.write_one_line(SYSFS_CPUQUIET_ENABLE, '0') 37 38 def run_once(self): 39 # TODO(crbug.com/485276) Revisit this exception once we've refactored 40 # test to account for intel_pstate cpufreq driver 41 if os.path.exists(SYSFS_INTEL_PSTATE_PATH): 42 raise error.TestNAError('Test does NOT support intel_pstate driver') 43 44 keyvals = {} 45 try: 46 # First attempt to set all frequencies on each core before going 47 # on to the next core. 48 self.test_cores_in_series() 49 # Record that it was the first test that passed. 50 keyvals['test_cores_in_series'] = 1 51 except error.TestFail as exception: 52 if str(exception) == 'Unable to set frequency': 53 # If test_cores_in_series fails, try to set each frequency for 54 # all cores before moving on to the next frequency. 55 logging.debug('trying to set freq in parallel') 56 self.test_cores_in_parallel() 57 # Record that it was the second test that passed. 58 keyvals['test_cores_in_parallel'] = 1 59 else: 60 raise exception 61 62 self.write_perf_keyval(keyvals) 63 64 def test_cores_in_series(self): 65 for cpu in self._cpus: 66 if 'userspace' not in cpu.get_available_governors(): 67 raise error.TestError('userspace governor not supported') 68 69 available_frequencies = cpu.get_available_frequencies() 70 if len(available_frequencies) == 1: 71 raise error.TestFail('Not enough frequencies supported!') 72 73 # set cpufreq governor to userspace 74 cpu.set_governor('userspace') 75 76 # cycle through all available frequencies 77 for freq in available_frequencies: 78 cpu.set_frequency(freq) 79 if not self.compare_freqs(freq, cpu): 80 raise error.TestFail('Unable to set frequency') 81 82 def test_cores_in_parallel(self): 83 cpus = self._cpus 84 cpu0 = self._cpus[0] 85 86 # Use the first CPU's frequencies for all CPUs. Assume that they are 87 # the same. 88 available_frequencies = cpu0.get_available_frequencies() 89 if len(available_frequencies) == 1: 90 raise error.TestFail('Not enough frequencies supported!') 91 92 for cpu in cpus: 93 this_frequencies = cpu.get_available_frequencies() 94 if set(available_frequencies) != set(this_frequencies): 95 raise error.TestError( 96 "Can't fallback to parallel test: %s / %s differ: %r / %r" % 97 (cpu, cpu0, available_frequencies, this_frequencies)) 98 99 if 'userspace' not in cpu.get_available_governors(): 100 raise error.TestError('userspace governor not supported') 101 102 # set cpufreq governor to userspace 103 cpu.set_governor('userspace') 104 105 # cycle through all available frequencies 106 for freq in available_frequencies: 107 for cpu in cpus: 108 cpu.set_frequency(freq) 109 for cpu in cpus: 110 if not self.compare_freqs(freq, cpu): 111 raise error.TestFail('Unable to set frequency') 112 113 def compare_freqs(self, set_freq, cpu): 114 # crbug.com/848309 : older kernels have race between setting 115 # governor and access to setting frequency so add a retry 116 try: 117 freq = cpu.get_current_frequency() 118 except IOError: 119 logging.warning('Frequency getting failed. Retrying once.') 120 time.sleep(.1) 121 freq = cpu.get_current_frequency() 122 123 logging.debug('frequency set:%d vs actual:%d', 124 set_freq, freq) 125 126 # setting freq to a particular frequency isn't reliable so just test 127 # that driver allows setting & getting. 128 if cpu.get_driver() == 'acpi-cpufreq': 129 return True 130 131 return set_freq == freq 132 133 def cleanup(self): 134 if self._cpus: 135 for cpu in self._cpus: 136 # restore cpufreq state 137 cpu.restore_state() 138 139 # Restore the original setting if system has CPUQuiet feature 140 if os.path.exists(SYSFS_CPUQUIET_ENABLE): 141 utils.open_write_close(SYSFS_CPUQUIET_ENABLE, 142 self.is_cpuquiet_enabled) 143 144 145class cpufreq(object): 146 147 def __init__(self, path): 148 self.__base_path = path 149 self.__save_files_list = [ 150 'scaling_max_freq', 'scaling_min_freq', 'scaling_governor' 151 ] 152 self._freqs = None 153 # disable boost to limit how much freq can vary in userspace mode 154 if self.get_driver() == 'acpi-cpufreq': 155 self.disable_boost() 156 157 def __del__(self): 158 if self.get_driver() == 'acpi-cpufreq': 159 self.enable_boost() 160 161 def __str__(self): 162 return os.path.basename(os.path.dirname(self.__base_path)) 163 164 def __write_file(self, file_name, data): 165 path = os.path.join(self.__base_path, file_name) 166 try: 167 utils.open_write_close(path, data) 168 except IOError as e: 169 logging.warning('write of %s failed: %s', path, str(e)) 170 171 def __read_file(self, file_name): 172 path = os.path.join(self.__base_path, file_name) 173 f = open(path, 'r') 174 data = f.read() 175 f.close() 176 return data 177 178 def save_state(self): 179 logging.info('saving state:') 180 for fname in self.__save_files_list: 181 data = self.__read_file(fname) 182 setattr(self, fname, data) 183 logging.info(fname + ': ' + data) 184 185 def restore_state(self): 186 logging.info('restoring state:') 187 for fname in self.__save_files_list: 188 # Sometimes a newline gets appended to a data string and it throws 189 # an error when being written to a sysfs file. Call strip() to 190 # eliminateextra whitespace characters so it can be written cleanly 191 # to the file. 192 data = getattr(self, fname).strip() 193 logging.info(fname + ': ' + data) 194 self.__write_file(fname, data) 195 196 def disable_constraints(self): 197 logging.info('disabling min/max constraints:') 198 self.__write_file('scaling_min_freq', str(self.get_min_frequency())) 199 self.__write_file('scaling_max_freq', str(self.get_max_frequency())) 200 201 def get_available_governors(self): 202 governors = self.__read_file('scaling_available_governors') 203 logging.info('available governors: %s', governors) 204 return governors.split() 205 206 def get_current_governor(self): 207 governor = self.__read_file('scaling_governor') 208 logging.info('current governor: %s', governor) 209 return governor.split()[0] 210 211 def get_driver(self): 212 driver = self.__read_file('scaling_driver') 213 logging.info('current driver: %s', driver) 214 return driver.split()[0] 215 216 def set_governor(self, governor): 217 logging.info('setting governor to %s', governor) 218 self.__write_file('scaling_governor', governor) 219 220 def get_available_frequencies(self): 221 if self._freqs: 222 return self._freqs 223 frequencies = self.__read_file('scaling_available_frequencies') 224 logging.info('available frequencies: %s', frequencies) 225 self._freqs = [int(i) for i in frequencies.split()] 226 return self._freqs 227 228 def get_current_frequency(self): 229 freq = int(self.__read_file('scaling_cur_freq')) 230 logging.info('current frequency: %s', freq) 231 return freq 232 233 def get_min_frequency(self): 234 freq = int(self.__read_file('cpuinfo_min_freq')) 235 logging.info('min frequency: %s', freq) 236 return freq 237 238 def get_max_frequency(self): 239 freq = int(self.__read_file('cpuinfo_max_freq')) 240 logging.info('max frequency: %s', freq) 241 return freq 242 243 def set_frequency(self, frequency): 244 logging.info('setting frequency to %d', frequency) 245 self.__write_file('scaling_setspeed', str(frequency)) 246 247 def disable_boost(self): 248 """Disable boost. 249 250 Note, boost is NOT a per-cpu parameter, 251 /sys/device/system/cpu/cpufreq/boost 252 253 So code below would unnecessarily disable it per-cpu but that should not 254 cause any issues. 255 """ 256 logging.debug('Disable boost') 257 self.__write_file('../../cpufreq/boost', '0') 258 259 def enable_boost(self): 260 """Enable boost. 261 262 Note, boost is NOT a per-cpu parameter, 263 /sys/device/system/cpu/cpufreq/boost 264 265 So code below would unnecessarily enable it per-cpu but that should not 266 cause any issues. 267 """ 268 logging.debug('Enable boost') 269 self.__write_file('../../cpufreq/boost', '1') 270