blob: 13ccc5b56b1d9206de5f9d40a28795e88e0c4dab [file] [log] [blame]
# 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')