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