tests/eas/preliminary: Add initial EAS suitability check
This introduces a test suite that provides early-warnings that the
platform does not have the preliminary setup necessary to support EAS
diff --git a/tests/eas/preliminary.py b/tests/eas/preliminary.py
new file mode 100644
index 0000000..a295f48
--- /dev/null
+++ b/tests/eas/preliminary.py
@@ -0,0 +1,164 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# Copyright (C) 2016, ARM Limited and contributors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import json
+import time
+import re
+import pandas
+import StringIO
+
+from unittest import SkipTest
+
+from env import TestEnv
+from test import LisaTest
+
+"""
+Goal
+====
+
+Check that the configuration of a given device is suitable
+for running EAS.
+
+Detailed Description
+====================
+
+This test reads the kernel configuration and digs around in sysfs to
+check the following attributes are true:
+ * the minimum set of required config options are enabled
+ * all CPUs have access to the 'sched' CPUFreq governor
+ * energy aware scheduling is present and enabled
+
+Expected Behaviour
+==================
+
+All required config options are set, sched governor is present.
+
+"""
+
+TEST_CONF = {
+ 'modules': ['cpufreq'],
+ 'results_dir': 'PreliminaryTests',
+ 'tools': [
+ 'sysbench',
+ ]
+}
+
+class BasicCheckTest(LisaTest):
+ @classmethod
+ def setUpClass(cls):
+ cls.env = TestEnv(test_conf=TEST_CONF)
+ cls.target = cls.env.target
+
+class TestSchedGovernor(BasicCheckTest):
+ def test_sched_governor_available(self):
+ """
+ Check that the 'sched' or 'schedutil' cpufreq governor is available
+ """
+ fail_list = []
+ for cpu in self.target.list_online_cpus():
+ governors = self.target.cpufreq.list_governors(cpu)
+ if 'sched' not in governors and 'schedutil' not in governors:
+ fail_list.append(cpu)
+ msg = 'CPUs {} do not support sched[util] cpufreq governor'.format(
+ fail_list)
+ self.assertTrue(len(fail_list) == 0, msg=msg)
+
+class TestKernelConfig(BasicCheckTest):
+ def test_kernel_config(self):
+ """
+ Check that the kernel config has the basic requirements for EAS
+ """
+ kernel_config = self.target.config
+ if not kernel_config.text:
+ raise SkipTest('Kernel config not available on target')
+
+ # NB: We don't test for schedtune/schedutil, that's tested by
+ # TestSchedGovernor.
+ necessary_configs = [
+ # 'CONFIG_CPU_FREQ_STAT',
+ 'CONFIG_CGROUPS',
+ 'CONFIG_SMP',
+ 'CONFIG_SCHED_MC',
+ 'CONFIG_CPU_FREQ',
+ 'CONFIG_CPU_IDLE',
+ 'CONFIG_SCHED_DEBUG',
+ ]
+
+ fail_list = [c for c in necessary_configs
+ if not kernel_config.is_enabled(c)]
+
+ if len(fail_list):
+ message = 'Mising kernel configs: ' + ', '.join(fail_list)
+ self.assertTrue(len(fail_list) == 0, msg=message)
+
+class TestWorkThroughput(BasicCheckTest):
+ """
+ Check that compute throughput increases with CPU frequency
+
+ That is, check that cpufreq really works in that setting a higher
+ frequency provides greater CPU performance
+ """
+ def _run_sysbench_work(self, cpu, duration):
+ """
+ Run benchmark using 1 thread on a given CPU.
+
+ :param cpu: cpu to run the benchmark on
+ :type cpu: str
+ :param duration: length of time, in seconds to run the benchmark
+
+ :returns: float - performance score
+ """
+ args = '--test=cpu --num-threads=1 --max-time={} run'.format(duration)
+
+ sysbench = self.target.path.join(self.target.executables_directory,
+ 'sysbench')
+ bench_out = self.target.invoke(sysbench, args=args, on_cpus=[cpu])
+
+ match = re.search(r'(total number of events:\s*)([\d.]*)', bench_out)
+ return float(match.group(2))
+
+ def _check_work_throughput(self, cpu):
+ seconds = 1.0
+ margin = 0.2
+ frequencies = self.target.cpufreq.list_frequencies(cpu)
+ original_governor = self.target.cpufreq.get_governor(cpu)
+ original_freq = None
+ if original_governor == 'userspace':
+ original_freq = self.target.cpufreq.get_frequency(cpu)
+ # set userspace governor
+ self.target.cpufreq.set_governor(cpu, 'userspace')
+ # do each freq in turn
+ result = {}
+ for freq in frequencies:
+ self.target.cpufreq.set_frequency(cpu, freq)
+ result[freq] = self._run_sysbench_work(cpu, seconds)
+ # restore governor
+ self.target.cpufreq.set_governor(cpu, original_governor)
+ if original_freq:
+ self.target.cpufreq.set_frequency(cpu, original_freq)
+ # compare work throughput
+ return result[frequencies[0]] < result[frequencies[-1]]
+
+ def test_work_throughput(self):
+ failed_cpus = []
+ for cpulist in self.env.topology.get_level('cpu'):
+ cpu = cpulist[0]
+ if not self._check_work_throughput(cpu):
+ failed_cpus.append(cpu)
+ msg='Work done did not scale with CPU Freq on CPUs: {}'\
+ .format(failed_cpus)
+ self.assertFalse(len(failed_cpus), msg=msg)