blob: 57978c1edc640c9ea1fe90541e98e0f7559300ac [file] [log] [blame]
/* Copyright (c) 2016-7 Google Inc.
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Usage: turbo [reset]
*
* This will print the turbo ratio since the last reset for each core.
*
* x86 only (TODO) */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <parlib/sysinfo.h>
#include <parlib/arch/arch.h>
#include <ros/arch/msr-index.h>
#include <argp.h>
static const char doc[] = "turbo -- control for turbo mode";
static const char args_doc[] = "";
static struct argp_option options[] = {
{"enable", 'e', 0, 0, "Enable turbo mode"},
{"disable", 'd', 0, 0, "Disable turbo mode"},
{"status", 's', 0, 0, "Print status of turbo mode"},
{0, 0, 0, 0, ""},
{"ratio", 'r', 0, 0, "Print the experienced turbo ratio"},
{"zero", 'z', 0, 0, "Zero the turbo ratio"},
{NULL, 'h', 0, OPTION_HIDDEN, NULL},
{ 0 }
};
#define PROG_CMD_ENABLE 1
#define PROG_CMD_DISABLE 2
#define PROG_CMD_STATUS 3
#define PROG_CMD_PRINT_RATIO 4
#define PROG_CMD_ZERO_RATIO 5
struct prog_opts {
int cmd;
};
static int num_cores;
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct prog_opts *p_opts = state->input;
switch (key) {
case 'e':
if (p_opts->cmd) {
fprintf(stderr, "Too many commands; one allowed.\n\n");
argp_usage(state);
}
p_opts->cmd = PROG_CMD_ENABLE;
break;
case 'd':
if (p_opts->cmd) {
fprintf(stderr, "Too many commands; one allowed.\n\n");
argp_usage(state);
}
p_opts->cmd = PROG_CMD_DISABLE;
break;
case 's':
if (p_opts->cmd) {
fprintf(stderr, "Too many commands; one allowed.\n\n");
argp_usage(state);
}
p_opts->cmd = PROG_CMD_STATUS;
break;
case 'r':
if (p_opts->cmd) {
fprintf(stderr, "Too many commands; one allowed.\n\n");
argp_usage(state);
}
p_opts->cmd = PROG_CMD_PRINT_RATIO;
break;
case 'z':
if (p_opts->cmd) {
fprintf(stderr, "Too many commands; one allowed.\n\n");
argp_usage(state);
}
p_opts->cmd = PROG_CMD_ZERO_RATIO;
break;
case ARGP_KEY_ARG:
fprintf(stderr, "Extra arguments\n");
argp_usage(state);
break;
case ARGP_KEY_END:
if (!p_opts->cmd)
argp_usage(state);
break;
case 'h':
argp_state_help(state, stderr, ARGP_HELP_LONG);
break;
default:
return ARGP_ERR_UNKNOWN;
};
return 0;
}
static struct argp argp = {options, parse_opt, args_doc, doc};
static int get_msr_fd(void)
{
int fd;
fd = open("#arch/msr", O_RDWR);
if (fd < 0) {
perror("open");
exit(-1);
}
return fd;
}
static int set_turbo_mode(bool enable)
{
size_t buf_sz;
ssize_t ret;
int fd;
uint64_t *buf;
uint64_t msr_val;
fd = get_msr_fd();
buf_sz = num_cores * sizeof(uint64_t);
buf = malloc(buf_sz);
assert(buf);
ret = pread(fd, buf, buf_sz, MSR_IA32_PERF_CTL);
if (ret < 0) {
perror("pread MSR_PERF_CTL");
exit(-1);
}
/* The assumption here is that all cores have the same MSR value.
* Changing this would require changing the wrmsr kernel interface. */
msr_val = buf[0];
if (enable)
msr_val &= ~(1ULL << 32);
else
msr_val |= 1ULL << 32;
ret = pwrite(fd, &msr_val, sizeof(msr_val), MSR_IA32_PERF_CTL);
if (ret < 0) {
perror("pwrite MSR_PERF_CTL");
exit(-1);
}
printf("%s turbo mode for all cores\n",
enable ? "Enabled" : "Disabled");
free(buf);
close(fd);
return 0;
}
static int print_turbo_status(void)
{
size_t buf_sz;
ssize_t ret;
int fd;
uint64_t *buf;
uint64_t msr_val;
fd = get_msr_fd();
buf_sz = num_cores * sizeof(uint64_t);
buf = malloc(buf_sz);
assert(buf);
ret = pread(fd, buf, buf_sz, MSR_IA32_PERF_CTL);
if (ret < 0) {
perror("pread MSR_PERF_CTL");
exit(-1);
}
/* The assumption here is that all cores have the same MSR value.
* Changing this would require changing the wrmsr kernel interface. */
msr_val = buf[0];
printf("Turbo mode is %s for all cores\n", msr_val & (1ULL << 32) ?
"disabled" : "enabled");
free(buf);
close(fd);
return 0;
}
static void check_for_ratio_msrs(void)
{
uint32_t ecx;
parlib_cpuid(0x6, 0, NULL, NULL, &ecx, NULL);
if (!(ecx & (1 << 0))) {
fprintf(stderr, "cpuid says no MPERF and APERF\n");
exit(-1);
}
}
static int print_turbo_ratio(void)
{
size_t buf_sz;
ssize_t ret;
int fd;
uint64_t *mperf_buf, *aperf_buf;
check_for_ratio_msrs();
fd = get_msr_fd();
buf_sz = num_cores * sizeof(uint64_t);
mperf_buf = malloc(buf_sz);
aperf_buf = malloc(buf_sz);
assert(mperf_buf && aperf_buf);
/* ideally these reads happen with no interference/interrupts in between
*/
ret = pread(fd, mperf_buf, buf_sz, MSR_IA32_MPERF);
if (ret < 0) {
perror("pread MSR_MPERF");
exit(-1);
}
ret = pread(fd, aperf_buf, buf_sz, MSR_IA32_APERF);
if (ret < 0) {
perror("pread MSR_APERF");
exit(-1);
}
for (int i = 0; i < num_cores; i++)
printf("Core %3d: %4f%\n", i,
100.0 * aperf_buf[i] / mperf_buf[i]);
free(mperf_buf);
free(aperf_buf);
close(fd);
return 0;
}
static int zero_turbo_ratio(void)
{
uint64_t reset_val = 0;
ssize_t ret;
int fd;
check_for_ratio_msrs();
fd = get_msr_fd();
ret = pwrite(fd, &reset_val, sizeof(reset_val), MSR_IA32_MPERF);
if (ret < 0) {
perror("pwrite MSR_MPERF");
exit(-1);
}
ret = pwrite(fd, &reset_val, sizeof(reset_val), MSR_IA32_APERF);
if (ret < 0) {
perror("pwrite MSR_APERF");
exit(-1);
}
close(fd);
return 0;
}
int main(int argc, char **argv)
{
struct prog_opts popts = {0};
argp_parse(&argp, argc, argv, 0, 0, &popts);
/* TODO: could use a core list or something in the future (like perf) */
num_cores = get_num_pcores();
switch (popts.cmd) {
case PROG_CMD_ENABLE:
return set_turbo_mode(TRUE);
case PROG_CMD_DISABLE:
return set_turbo_mode(FALSE);
case PROG_CMD_STATUS:
return print_turbo_status();
case PROG_CMD_PRINT_RATIO:
return print_turbo_ratio();
case PROG_CMD_ZERO_RATIO:
return zero_turbo_ratio();
default:
fprintf(stderr, "Unhandled cmd (argp should catch this)!\n");
return -1;
};
}