1/* 2 * Copyright (c) 2013-2014 Oracle and/or its affiliates. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it would be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write the Free Software Foundation, 16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 * 18 * Author: Alexey Kodanev <alexey.kodanev@oracle.com> 19 * 20 * Check support for disabling dynamic overclocking in acpi_cpufreq driver. 21 * Required Linux 3.7+. 22 * 23 * The test compares time spent on sum calculation with/without 24 * boost-disable bit. If boost is enabled we can get a slightly shorter 25 * time period. Measure elapsed time instead of sysfs cpuinfo_cur_freq value, 26 * because after the upstream commit 8673b83bf2f013379453b4779047bf3c6ae387e4, 27 * current cpu frequency became target cpu frequency. 28 */ 29 30#define _GNU_SOURCE 31#include <sched.h> 32#include <time.h> 33 34#include "test.h" 35#include "lapi/posix_clocks.h" 36#include "safe_macros.h" 37 38char *TCID = "cpufreq_boost"; 39 40#define SYSFS_CPU_DIR "/sys/devices/system/cpu/" 41 42struct cpufreq_driver_info { 43 char *name; 44 int off; 45 char *on_str; 46 char *off_str; 47 char *file; 48}; 49static const struct cpufreq_driver_info cdrv[] = { 50 { "acpi_cpufreq", 0, "1", "0", SYSFS_CPU_DIR "cpufreq/boost" }, 51 { "intel_pstate", 1, "0", "1", SYSFS_CPU_DIR "intel_pstate/no_turbo" }, 52}; 53static int id = -1; 54 55static int boost_value; 56 57const char governor[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_governor"; 58static char governor_name[16]; 59 60const char maxspeed[] = SYSFS_CPU_DIR "cpu0/cpufreq/scaling_max_freq"; 61 62static void cleanup(void) 63{ 64 FILE_PRINTF(cdrv[id].file, "%d", boost_value); 65 66 if (governor[0] != '\0') 67 FILE_PRINTF(governor, "%s", governor_name); 68} 69 70static void setup(void) 71{ 72 int fd; 73 unsigned int i; 74 tst_require_root(); 75 76 for (i = 0; i < ARRAY_SIZE(cdrv); ++i) { 77 fd = open(cdrv[i].file, O_RDWR); 78 if (fd == -1) 79 continue; 80 81 id = i; 82 close(fd); 83 break; 84 } 85 86 if (id == -1) 87 tst_brkm(TCONF, NULL, "overclock not supported"); 88 89 tst_resm(TINFO, "found '%s' driver, sysfs knob '%s'", 90 cdrv[id].name, cdrv[id].file); 91 92 tst_sig(FORK, DEF_HANDLER, cleanup); 93 94 SAFE_FILE_SCANF(NULL, cdrv[i].file, "%d", &boost_value); 95 96 /* change cpu0 scaling governor */ 97 SAFE_FILE_SCANF(NULL, governor, "%s", governor_name); 98 SAFE_FILE_PRINTF(cleanup, governor, "%s", "performance"); 99 100 /* use only cpu0 */ 101 cpu_set_t set; 102 CPU_ZERO(&set); 103 CPU_SET(0, &set); 104 if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0) 105 tst_brkm(TBROK | TERRNO, cleanup, "failed to set CPU0"); 106 107 struct sched_param params; 108 params.sched_priority = sched_get_priority_max(SCHED_FIFO); 109 if (sched_setscheduler(getpid(), SCHED_FIFO, ¶ms)) { 110 tst_resm(TWARN | TERRNO, 111 "failed to set FIFO sched with max priority"); 112 } 113} 114 115static int load_cpu(int max_freq_khz) 116{ 117 int sum = 0, i = 0, total_time_ms; 118 struct timespec tv_start, tv_end; 119 120 const int max_sum = max_freq_khz / 1000; 121 const int units = 1000000; /* Mhz */ 122 123 clock_gettime(CLOCK_MONOTONIC_RAW, &tv_start); 124 125 do { 126 for (i = 0; i < units; ++i) 127 asm ("" : : : "memory"); 128 } while (++sum < max_sum); 129 130 clock_gettime(CLOCK_MONOTONIC_RAW, &tv_end); 131 132 total_time_ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + 133 (tv_end.tv_nsec - tv_start.tv_nsec) / 1000000; 134 135 if (!total_time_ms) 136 tst_brkm(TBROK, cleanup, "time period is 0"); 137 138 tst_resm(TINFO, "elapsed time is %d ms", total_time_ms); 139 140 return total_time_ms; 141} 142 143static void test_run(void) 144{ 145 int boost_time, boost_off_time, max_freq_khz; 146 SAFE_FILE_SCANF(cleanup, maxspeed, "%d", &max_freq_khz); 147 tst_resm(TINFO, "maximum speed is %d KHz", max_freq_khz); 148 149 /* Enable boost */ 150 if (boost_value == cdrv[id].off) 151 SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].on_str); 152 tst_resm(TINFO, "load CPU0 with boost enabled"); 153 boost_time = load_cpu(max_freq_khz); 154 155 /* Disable boost */ 156 SAFE_FILE_PRINTF(cleanup, cdrv[id].file, "%s", cdrv[id].off_str); 157 tst_resm(TINFO, "load CPU0 with boost disabled"); 158 boost_off_time = load_cpu(max_freq_khz); 159 160 boost_off_time *= .98; 161 162 tst_resm((boost_time < boost_off_time) ? TPASS : TFAIL, 163 "compare time spent with and without boost (-2%%)"); 164} 165 166int main(int argc, char *argv[]) 167{ 168 tst_parse_opts(argc, argv, NULL, NULL); 169 170 setup(); 171 172 test_run(); 173 174 cleanup(); 175 176 tst_exit(); 177} 178