| /* 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; |
| }; |
| } |