# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import glob import logging import os import time from autotest_lib.client.bin import test from autotest_lib.client.common_lib import error, utils SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable' SYSFS_INTEL_PSTATE_PATH = '/sys/devices/system/cpu/intel_pstate' class power_CPUFreq(test.test): version = 1 def initialize(self): cpufreq_path = '/sys/devices/system/cpu/cpu*/cpufreq' dirs = glob.glob(cpufreq_path) if not dirs: raise error.TestFail('cpufreq not supported') self._cpufreq_dirs = dirs self._cpus = [cpufreq(dirname) for dirname in dirs] for cpu in self._cpus: cpu.save_state() # Store the setting if the system has CPUQuiet feature if os.path.exists(SYSFS_CPUQUIET_ENABLE): self.is_cpuquiet_enabled = utils.read_file(SYSFS_CPUQUIET_ENABLE) utils.write_one_line(SYSFS_CPUQUIET_ENABLE, '0') def run_once(self): # TODO(crbug.com/485276) Revisit this exception once we've refactored # test to account for intel_pstate cpufreq driver if os.path.exists(SYSFS_INTEL_PSTATE_PATH): raise error.TestNAError('Test does NOT support intel_pstate driver') keyvals = {} try: # First attempt to set all frequencies on each core before going # on to the next core. self.test_cores_in_series() # Record that it was the first test that passed. keyvals['test_cores_in_series'] = 1 except error.TestFail as exception: if str(exception) == 'Unable to set frequency': # If test_cores_in_series fails, try to set each frequency for # all cores before moving on to the next frequency. logging.debug('trying to set freq in parallel') self.test_cores_in_parallel() # Record that it was the second test that passed. keyvals['test_cores_in_parallel'] = 1 else: raise exception self.write_perf_keyval(keyvals) def test_cores_in_series(self): for cpu in self._cpus: if 'userspace' not in cpu.get_available_governors(): raise error.TestError('userspace governor not supported') available_frequencies = cpu.get_available_frequencies() if len(available_frequencies) == 1: raise error.TestFail('Not enough frequencies supported!') # set cpufreq governor to userspace cpu.set_governor('userspace') # cycle through all available frequencies for freq in available_frequencies: cpu.set_frequency(freq) if not self.compare_freqs(freq, cpu): raise error.TestFail('Unable to set frequency') def test_cores_in_parallel(self): cpus = self._cpus cpu0 = self._cpus[0] # Use the first CPU's frequencies for all CPUs. Assume that they are # the same. available_frequencies = cpu0.get_available_frequencies() if len(available_frequencies) == 1: raise error.TestFail('Not enough frequencies supported!') for cpu in cpus: if 'userspace' not in cpu.get_available_governors(): raise error.TestError('userspace governor not supported') # set cpufreq governor to userspace cpu.set_governor('userspace') # cycle through all available frequencies for freq in available_frequencies: for cpu in cpus: cpu.set_frequency(freq) for cpu in cpus: if not self.compare_freqs(freq, cpu): raise error.TestFail('Unable to set frequency') def compare_freqs(self, set_freq, cpu): # crbug.com/848309 : older kernels have race between setting # governor and access to setting frequency so add a retry try: freq = cpu.get_current_frequency() except IOError: logging.warn('Frequency getting failed. Retrying once.') time.sleep(.1) freq = cpu.get_current_frequency() logging.debug('frequency set:%d vs actual:%d', set_freq, freq) # setting freq to a particular frequency isn't reliable so just test # that driver allows setting & getting. if cpu.get_driver() == 'acpi-cpufreq': return True return set_freq == freq def cleanup(self): if self._cpus: for cpu in self._cpus: # restore cpufreq state cpu.restore_state() # Restore the original setting if system has CPUQuiet feature if os.path.exists(SYSFS_CPUQUIET_ENABLE): utils.open_write_close(SYSFS_CPUQUIET_ENABLE, self.is_cpuquiet_enabled) class cpufreq(object): def __init__(self, path): self.__base_path = path self.__save_files_list = [ 'scaling_max_freq', 'scaling_min_freq', 'scaling_governor' ] self._freqs = None # disable boost to limit how much freq can vary in userspace mode if self.get_driver() == 'acpi-cpufreq': self.disable_boost() def __del__(self): if self.get_driver() == 'acpi-cpufreq': self.enable_boost() def __write_file(self, file_name, data): path = os.path.join(self.__base_path, file_name) try: utils.open_write_close(path, data) except IOError as e: logging.warn('write of %s failed: %s', path, str(e)) def __read_file(self, file_name): path = os.path.join(self.__base_path, file_name) f = open(path, 'r') data = f.read() f.close() return data def save_state(self): logging.info('saving state:') for fname in self.__save_files_list: data = self.__read_file(fname) setattr(self, fname, data) logging.info(fname + ': ' + data) def restore_state(self): logging.info('restoring state:') for fname in self.__save_files_list: # Sometimes a newline gets appended to a data string and it throws # an error when being written to a sysfs file. Call strip() to # eliminateextra whitespace characters so it can be written cleanly # to the file. data = getattr(self, fname).strip() logging.info(fname + ': ' + data) self.__write_file(fname, data) def get_available_governors(self): governors = self.__read_file('scaling_available_governors') logging.info('available governors: %s', governors) return governors.split() def get_current_governor(self): governor = self.__read_file('scaling_governor') logging.info('current governor: %s', governor) return governor.split()[0] def get_driver(self): driver = self.__read_file('scaling_driver') logging.info('current driver: %s', driver) return driver.split()[0] def set_governor(self, governor): logging.info('setting governor to %s', governor) self.__write_file('scaling_governor', governor) def get_available_frequencies(self): if self._freqs: return self._freqs frequencies = self.__read_file('scaling_available_frequencies') logging.info('available frequencies: %s', frequencies) self._freqs = [int(i) for i in frequencies.split()] return self._freqs def get_current_frequency(self): freq = int(self.__read_file('scaling_cur_freq')) logging.info('current frequency: %s', freq) return freq def set_frequency(self, frequency): logging.info('setting frequency to %d', frequency) if frequency >= self.get_current_frequency(): file_list = [ 'scaling_max_freq', 'scaling_min_freq', 'scaling_setspeed' ] else: file_list = [ 'scaling_min_freq', 'scaling_max_freq', 'scaling_setspeed' ] for fname in file_list: self.__write_file(fname, str(frequency)) def disable_boost(self): """Disable boost. Note, boost is NOT a per-cpu parameter, /sys/device/system/cpu/cpufreq/boost So code below would unnecessarily disable it per-cpu but that should not cause any issues. """ logging.debug('Disable boost') self.__write_file('../../cpufreq/boost', '0') def enable_boost(self): """Enable boost. Note, boost is NOT a per-cpu parameter, /sys/device/system/cpu/cpufreq/boost So code below would unnecessarily enable it per-cpu but that should not cause any issues. """ logging.debug('Enable boost') self.__write_file('../../cpufreq/boost', '1')