• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import glob
6import logging
7import os
8import time
9from autotest_lib.client.bin import test
10from autotest_lib.client.common_lib import error, utils
11
12SYSFS_CPUQUIET_ENABLE = '/sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable'
13SYSFS_INTEL_PSTATE_PATH = '/sys/devices/system/cpu/intel_pstate'
14
15
16class 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
137class 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