254 lines
9.0 KiB
Python
254 lines
9.0 KiB
Python
# 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')
|