You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
5.3 KiB
202 lines
5.3 KiB
/*
|
|
* Copyright (c) 2013-2014 Oracle and/or its affiliates. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it would be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Author: Alexey Kodanev <alexey.kodanev@oracle.com>
|
|
*
|
|
* Check support for disabling dynamic overclocking in acpi_cpufreq driver.
|
|
* Required Linux 3.7+.
|
|
*
|
|
* The test compares time spent on sum calculation with/without
|
|
* boost-disable bit. If boost is enabled we can get a slightly shorter
|
|
* time period. Measure elapsed time instead of sysfs cpuinfo_cur_freq value,
|
|
* because after the upstream commit 8673b83bf2f013379453b4779047bf3c6ae387e4,
|
|
* current cpu frequency became target cpu frequency.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <sched.h>
|
|
#include <time.h>
|
|
|
|
#include "test.h"
|
|
#include "lapi/posix_clocks.h"
|
|
#include "safe_macros.h"
|
|
|
|
char *TCID = "cpufreq_boost";
|
|
|
|
#define SYSFS_CPU_DIR "/sys/devices/system/cpu/"
|
|
|
|
struct cpufreq_driver_info {
|
|
char *name;
|
|
int off;
|
|
char *on_str;
|
|
char *off_str;
|
|
char *file;
|
|
};
|
|
static const struct cpufreq_driver_info cdrv[] = {
|
|
{ "acpi_cpufreq", 0, "1", "0", SYSFS_CPU_DIR "cpufreq/boost" },
|
|
{ "intel_pstate", 1, "0", "1", SYSFS_CPU_DIR "intel_pstate/no_turbo" },
|
|
};
|
|
static int id = -1;
|
|
|
|
static int boost_value;
|
|
|
|
const char governor[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_governor";
|
|
static char governor_name[16];
|
|
|
|
const char maxspeed[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_max_freq";
|
|
|
|
static void check_set_turbo(char *file, char *off)
|
|
{
|
|
int fd;
|
|
|
|
fd = SAFE_OPEN(NULL, file, O_WRONLY);
|
|
|
|
/* We try to skip test when getting EPERM. */
|
|
if (write(fd, off, 1) == -1 && errno == EPERM) {
|
|
SAFE_CLOSE(NULL, fd);
|
|
tst_brkm(TCONF, NULL,
|
|
"Turbo is disabled by BIOS or unavailable on processor");
|
|
}
|
|
|
|
SAFE_CLOSE(NULL, fd);
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
FILE_PRINTF(cdrv[id].file, "%d", boost_value);
|
|
|
|
if (governor[0] != '\0')
|
|
FILE_PRINTF(governor, "%s", governor_name);
|
|
}
|
|
|
|
static void setup(void)
|
|
{
|
|
int fd;
|
|
unsigned int i;
|
|
tst_require_root();
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cdrv); ++i) {
|
|
fd = open(cdrv[i].file, O_RDWR);
|
|
if (fd == -1)
|
|
continue;
|
|
|
|
id = i;
|
|
close(fd);
|
|
break;
|
|
}
|
|
|
|
if (id == -1)
|
|
tst_brkm(TCONF, NULL, "overclock not supported");
|
|
|
|
tst_resm(TINFO, "found '%s' driver, sysfs knob '%s'",
|
|
cdrv[id].name, cdrv[id].file);
|
|
|
|
tst_sig(FORK, DEF_HANDLER, cleanup);
|
|
|
|
SAFE_FILE_SCANF(NULL, cdrv[i].file, "%d", &boost_value);
|
|
|
|
/* For intel_pstate, we cannot write data into intel_pstate/no_turbo
|
|
* and return EPERM if turbo is disabled by BIOS or unavailable on
|
|
* processor. We should check this case by writing original data.
|
|
*/
|
|
if (!strcmp(cdrv[i].name, "intel_pstate") && boost_value == cdrv[i].off)
|
|
check_set_turbo(cdrv[i].file, cdrv[i].off_str);
|
|
|
|
/* change cpu0 scaling governor */
|
|
SAFE_FILE_SCANF(NULL, governor, "%s", governor_name);
|
|
SAFE_FILE_PRINTF(cleanup, governor, "%s", "performance");
|
|
|
|
/* use only cpu0 */
|
|
cpu_set_t set;
|
|
CPU_ZERO(&set);
|
|
CPU_SET(0, &set);
|
|
if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0)
|
|
tst_brkm(TBROK | TERRNO, cleanup, "failed to set CPU0");
|
|
|
|
struct sched_param params;
|
|
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
if (sched_setscheduler(getpid(), SCHED_FIFO, ¶ms)) {
|
|
tst_resm(TWARN | TERRNO,
|
|
"failed to set FIFO sched with max priority");
|
|
}
|
|
}
|
|
|
|
static int load_cpu(int max_freq_khz)
|
|
{
|
|
int sum = 0, i = 0, total_time_ms;
|
|
struct timespec tv_start, tv_end;
|
|
|
|
const int max_sum = max_freq_khz / 1000;
|
|
const int units = 1000000; /* Mhz */
|
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &tv_start);
|
|
|
|
do {
|
|
for (i = 0; i < units; ++i)
|
|
asm ("" : : : "memory");
|
|
} while (++sum < max_sum);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &tv_end);
|
|
|
|
total_time_ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000 +
|
|
(tv_end.tv_nsec - tv_start.tv_nsec) / 1000000;
|
|
|
|
if (!total_time_ms)
|
|
tst_brkm(TBROK, cleanup, "time period is 0");
|
|
|
|
tst_resm(TINFO, "elapsed time is %d ms", total_time_ms);
|
|
|
|
return total_time_ms;
|
|
}
|
|
|
|
static void test_run(void)
|
|
{
|
|
int boost_time, boost_off_time, max_freq_khz;
|
|
SAFE_FILE_SCANF(cleanup, maxspeed, "%d", &max_freq_khz);
|
|
tst_resm(TINFO, "maximum speed is %d KHz", max_freq_khz);
|
|
|
|
/* Enable boost */
|
|
if (boost_value == cdrv[id].off)
|
|
SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].on_str);
|
|
tst_resm(TINFO, "load CPU0 with boost enabled");
|
|
boost_time = load_cpu(max_freq_khz);
|
|
|
|
/* Disable boost */
|
|
SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].off_str);
|
|
tst_resm(TINFO, "load CPU0 with boost disabled");
|
|
boost_off_time = load_cpu(max_freq_khz);
|
|
|
|
boost_off_time *= .98;
|
|
|
|
tst_resm((boost_time < boost_off_time) ? TPASS : TFAIL,
|
|
"compare time spent with and without boost (-2%%)");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
tst_parse_opts(argc, argv, NULL, NULL);
|
|
|
|
setup();
|
|
|
|
test_run();
|
|
|
|
cleanup();
|
|
|
|
tst_exit();
|
|
}
|