tools/power/turbostat: Fix turbostat for AMD Zen CPUs
[linux-2.6-microblaze.git] / tools / power / x86 / turbostat / turbostat.c
index a7c4f07..b5f4ec2 100644 (file)
 #include <sys/capability.h>
 #include <errno.h>
 #include <math.h>
+#include <linux/perf_event.h>
+#include <asm/unistd.h>
 
 char *proc_stat = "/proc/stat";
 FILE *outf;
 int *fd_percpu;
+int *fd_instr_count_percpu;
 struct timeval interval_tv = {5, 0};
 struct timespec interval_ts = {5, 0};
 unsigned int num_iterations;
@@ -75,6 +78,7 @@ char *output_buffer, *outp;
 unsigned int do_rapl;
 unsigned int do_dts;
 unsigned int do_ptm;
+unsigned int do_ipc;
 unsigned long long  gfx_cur_rc6_ms;
 unsigned long long cpuidle_cur_cpu_lpi_us;
 unsigned long long cpuidle_cur_sys_lpi_us;
@@ -173,6 +177,7 @@ struct thread_data {
        unsigned long long aperf;
        unsigned long long mperf;
        unsigned long long c1;
+       unsigned long long instr_count;
        unsigned long long  irq_count;
        unsigned int smi_count;
        unsigned int cpu_id;
@@ -297,7 +302,10 @@ int idx_to_offset(int idx)
 
        switch (idx) {
        case IDX_PKG_ENERGY:
-               offset = MSR_PKG_ENERGY_STATUS;
+               if (do_rapl & RAPL_AMD_F17H)
+                       offset = MSR_PKG_ENERGY_STAT;
+               else
+                       offset = MSR_PKG_ENERGY_STATUS;
                break;
        case IDX_DRAM_ENERGY:
                offset = MSR_DRAM_ENERGY_STATUS;
@@ -326,6 +334,7 @@ int offset_to_idx(int offset)
 
        switch (offset) {
        case MSR_PKG_ENERGY_STATUS:
+       case MSR_PKG_ENERGY_STAT:
                idx = IDX_PKG_ENERGY;
                break;
        case MSR_DRAM_ENERGY_STATUS:
@@ -353,7 +362,7 @@ int idx_valid(int idx)
 {
        switch (idx) {
        case IDX_PKG_ENERGY:
-               return do_rapl & RAPL_PKG;
+               return do_rapl & (RAPL_PKG | RAPL_AMD_F17H);
        case IDX_DRAM_ENERGY:
                return do_rapl & RAPL_DRAM;
        case IDX_PP0_ENERGY:
@@ -490,6 +499,39 @@ int get_msr_fd(int cpu)
        return fd;
 }
 
+static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags)
+{
+       return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+}
+
+static int perf_instr_count_open(int cpu_num)
+{
+       struct perf_event_attr pea;
+       int fd;
+
+       memset(&pea, 0, sizeof(struct perf_event_attr));
+       pea.type = PERF_TYPE_HARDWARE;
+       pea.size = sizeof(struct perf_event_attr);
+       pea.config = PERF_COUNT_HW_INSTRUCTIONS;
+
+       /* counter for cpu_num, including user + kernel and all processes */
+       fd = perf_event_open(&pea, -1, cpu_num, -1, 0);
+       if (fd == -1) 
+               err(-1, "cpu%d: perf instruction counter\n", cpu_num);
+
+       return fd;
+}
+
+int get_instr_count_fd(int cpu)
+{
+       if (fd_instr_count_percpu[cpu])
+               return fd_instr_count_percpu[cpu];
+
+       fd_instr_count_percpu[cpu] = perf_instr_count_open(cpu);
+
+       return fd_instr_count_percpu[cpu];
+}
+
 int get_msr(int cpu, off_t offset, unsigned long long *msr)
 {
        ssize_t retval;
@@ -561,6 +603,7 @@ struct msr_counter bic[] = {
        { 0x0, "X2APIC" },
        { 0x0, "Die" },
        { 0x0, "GFXAMHz" },
+       { 0x0, "IPC" },
 };
 
 #define MAX_BIC (sizeof(bic) / sizeof(struct msr_counter))
@@ -616,6 +659,7 @@ struct msr_counter bic[] = {
 #define        BIC_X2APIC      (1ULL << 49)
 #define        BIC_Die         (1ULL << 50)
 #define        BIC_GFXACTMHz   (1ULL << 51)
+#define        BIC_IPC         (1ULL << 52)
 
 #define BIC_DISABLED_BY_DEFAULT        (BIC_USEC | BIC_TOD | BIC_APIC | BIC_X2APIC)
 
@@ -627,6 +671,7 @@ unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_sysfs | BIC_APIC | BIC
 #define ENABLE_BIC(COUNTER_NAME) (bic_enabled |= COUNTER_NAME)
 #define BIC_PRESENT(COUNTER_BIT) (bic_present |= COUNTER_BIT)
 #define BIC_NOT_PRESENT(COUNTER_BIT) (bic_present &= ~COUNTER_BIT)
+#define BIC_IS_ENABLED(COUNTER_BIT) (bic_enabled & COUNTER_BIT)
 
 
 #define MAX_DEFERRED 16
@@ -764,6 +809,9 @@ void print_header(char *delim)
        if (DO_BIC(BIC_TSC_MHz))
                outp += sprintf(outp, "%sTSC_MHz", (printed++ ? delim : ""));
 
+       if (DO_BIC(BIC_IPC))
+               outp += sprintf(outp, "%sIPC", (printed++ ? delim : ""));
+
        if (DO_BIC(BIC_IRQ)) {
                if (sums_need_wide_columns)
                        outp += sprintf(outp, "%s     IRQ", (printed++ ? delim : ""));
@@ -926,6 +974,9 @@ int dump_counters(struct thread_data *t, struct core_data *c,
                outp += sprintf(outp, "mperf: %016llX\n", t->mperf);
                outp += sprintf(outp, "c1: %016llX\n", t->c1);
 
+               if (DO_BIC(BIC_IPC))
+                       outp += sprintf(outp, "IPC: %lld\n", t->instr_count);
+
                if (DO_BIC(BIC_IRQ))
                        outp += sprintf(outp, "IRQ: %lld\n", t->irq_count);
                if (DO_BIC(BIC_SMI))
@@ -1105,6 +1156,9 @@ int format_counters(struct thread_data *t, struct core_data *c,
        if (DO_BIC(BIC_TSC_MHz))
                outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), 1.0 * t->tsc/units/interval_float);
 
+       if (DO_BIC(BIC_IPC))
+               outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 1.0 * t->instr_count / t->aperf);
+
        /* IRQ */
        if (DO_BIC(BIC_IRQ)) {
                if (sums_need_wide_columns)
@@ -1482,6 +1536,9 @@ delta_thread(struct thread_data *new, struct thread_data *old,
                old->mperf = 1; /* divide by 0 protection */
        }
 
+       if (DO_BIC(BIC_IPC))
+               old->instr_count = new->instr_count - old->instr_count;
+
        if (DO_BIC(BIC_IRQ))
                old->irq_count = new->irq_count - old->irq_count;
 
@@ -1536,6 +1593,8 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
        t->mperf = 0;
        t->c1 = 0;
 
+       t->instr_count = 0;
+
        t->irq_count = 0;
        t->smi_count = 0;
 
@@ -1611,6 +1670,8 @@ int sum_counters(struct thread_data *t, struct core_data *c,
        average.threads.mperf += t->mperf;
        average.threads.c1 += t->c1;
 
+       average.threads.instr_count += t->instr_count;
+
        average.threads.irq_count += t->irq_count;
        average.threads.smi_count += t->smi_count;
 
@@ -1707,6 +1768,7 @@ void compute_average(struct thread_data *t, struct core_data *c,
        average.threads.tsc /= topo.num_cpus;
        average.threads.aperf /= topo.num_cpus;
        average.threads.mperf /= topo.num_cpus;
+       average.threads.instr_count /= topo.num_cpus;
        average.threads.c1 /= topo.num_cpus;
 
        if (average.threads.irq_count > 9999999)
@@ -1989,6 +2051,10 @@ retry:
                t->mperf = t->mperf * aperf_mperf_multiplier;
        }
 
+       if (DO_BIC(BIC_IPC))
+               if (read(get_instr_count_fd(cpu), &t->instr_count, sizeof(long long)) != sizeof(long long))
+                       return -4;
+
        if (DO_BIC(BIC_IRQ))
                t->irq_count = irqs_per_cpu[cpu];
        if (DO_BIC(BIC_SMI)) {
@@ -4210,6 +4276,7 @@ rapl_dram_energy_units_probe(int  model, double rapl_energy_units)
        switch (model) {
        case INTEL_FAM6_HASWELL_X:      /* HSX */
        case INTEL_FAM6_BROADWELL_X:    /* BDX */
+       case INTEL_FAM6_SKYLAKE_X:      /* SKX */
        case INTEL_FAM6_XEON_PHI_KNL:   /* KNL */
                return (rapl_dram_energy_units = 15.3 / 1000000);
        default:
@@ -4817,33 +4884,12 @@ double discover_bclk(unsigned int family, unsigned int model)
  * below this value, including the Digital Thermal Sensor (DTS),
  * Package Thermal Management Sensor (PTM), and thermal event thresholds.
  */
-int read_tcc_activation_temp()
+int set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p)
 {
        unsigned long long msr;
-       unsigned int tcc, target_c, offset_c;
-
-       /* Temperature Target MSR is Nehalem and newer only */
-       if (!do_nhm_platform_info)
-               return 0;
-
-       if (get_msr(base_cpu, MSR_IA32_TEMPERATURE_TARGET, &msr))
-               return 0;
-
-       target_c = (msr >> 16) & 0xFF;
-
-       offset_c = (msr >> 24) & 0xF;
-
-       tcc = target_c - offset_c;
-
-       if (!quiet)
-               fprintf(outf, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C) (%d default - %d offset)\n",
-                       base_cpu, msr, tcc, target_c, offset_c);
-
-       return tcc;
-}
+       unsigned int target_c_local;
+       int cpu;
 
-int set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p)
-{
        /* tcc_activation_temp is used only for dts or ptm */
        if (!(do_dts || do_ptm))
                return 0;
@@ -4852,18 +4898,43 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk
        if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
                return 0;
 
+       cpu = t->cpu_id;
+       if (cpu_migrate(cpu)) {
+               fprintf(outf, "Could not migrate to CPU %d\n", cpu);
+               return -1;
+       }
+
        if (tcc_activation_temp_override != 0) {
                tcc_activation_temp = tcc_activation_temp_override;
-               fprintf(outf, "Using cmdline TCC Target (%d C)\n", tcc_activation_temp);
+               fprintf(outf, "cpu%d: Using cmdline TCC Target (%d C)\n",
+                       cpu, tcc_activation_temp);
                return 0;
        }
 
-       tcc_activation_temp = read_tcc_activation_temp();
-       if (tcc_activation_temp)
-               return 0;
+       /* Temperature Target MSR is Nehalem and newer only */
+       if (!do_nhm_platform_info)
+               goto guess;
+
+       if (get_msr(base_cpu, MSR_IA32_TEMPERATURE_TARGET, &msr))
+               goto guess;
+
+       target_c_local = (msr >> 16) & 0xFF;
 
+       if (!quiet)
+               fprintf(outf, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n",
+                       cpu, msr, target_c_local);
+
+       if (!target_c_local)
+               goto guess;
+
+       tcc_activation_temp = target_c_local;
+
+       return 0;
+
+guess:
        tcc_activation_temp = TJMAX_DEFAULT;
-       fprintf(outf, "Guessing tjMax %d C, Please use -T to specify\n", tcc_activation_temp);
+       fprintf(outf, "cpu%d: Guessing tjMax %d C, Please use -T to specify\n",
+               cpu, tcc_activation_temp);
 
        return 0;
 }
@@ -4994,12 +5065,14 @@ unsigned int intel_model_duplicates(unsigned int model)
        case INTEL_FAM6_ROCKETLAKE:
        case INTEL_FAM6_LAKEFIELD:
        case INTEL_FAM6_ALDERLAKE:
+       case INTEL_FAM6_ALDERLAKE_L:
                return INTEL_FAM6_CANNONLAKE_L;
 
        case INTEL_FAM6_ATOM_TREMONT_L:
                return INTEL_FAM6_ATOM_TREMONT;
 
        case INTEL_FAM6_ICELAKE_X:
+       case INTEL_FAM6_ICELAKE_D:
        case INTEL_FAM6_SAPPHIRERAPIDS_X:
                return INTEL_FAM6_SKYLAKE_X;
        }
@@ -5031,11 +5104,32 @@ void print_dev_latency(void)
        close(fd);
 }
 
+
+/*
+ * Linux-perf manages the the HW instructions-retired counter
+ * by enabling when requested, and hiding rollover
+ */
+void linux_perf_init(void)
+{
+       if (!BIC_IS_ENABLED(BIC_IPC))
+               return;
+
+       if (access("/proc/sys/kernel/perf_event_paranoid", F_OK))
+               return;
+
+       fd_instr_count_percpu = calloc(topo.max_cpu_num + 1, sizeof(int));
+       if (fd_instr_count_percpu == NULL)
+               err(-1, "calloc fd_instr_count_percpu");
+
+       BIC_PRESENT(BIC_IPC);
+}
+
 void process_cpuid()
 {
        unsigned int eax, ebx, ecx, edx;
        unsigned int fms, family, model, stepping, ecx_flags, edx_flags;
        unsigned int has_turbo;
+       unsigned long long ucode_patch = 0;
 
        eax = ebx = ecx = edx = 0;
 
@@ -5049,8 +5143,8 @@ void process_cpuid()
                hygon_genuine = 1;
 
        if (!quiet)
-               fprintf(outf, "CPUID(0): %.4s%.4s%.4s ",
-                       (char *)&ebx, (char *)&edx, (char *)&ecx);
+               fprintf(outf, "CPUID(0): %.4s%.4s%.4s 0x%x CPUID levels\n",
+                       (char *)&ebx, (char *)&edx, (char *)&ecx, max_level);
 
        __cpuid(1, fms, ebx, ecx, edx);
        family = (fms >> 8) & 0xf;
@@ -5063,6 +5157,9 @@ void process_cpuid()
        ecx_flags = ecx;
        edx_flags = edx;
 
+       if (get_msr(sched_getcpu(), MSR_IA32_UCODE_REV, &ucode_patch))
+               warnx("get_msr(UCODE)\n");
+
        /*
         * check max extended function levels of CPUID.
         * This is needed to check for invariant TSC.
@@ -5072,8 +5169,9 @@ void process_cpuid()
        __cpuid(0x80000000, max_extended_level, ebx, ecx, edx);
 
        if (!quiet) {
-               fprintf(outf, "0x%x CPUID levels; 0x%x xlevels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
-                       max_level, max_extended_level, family, model, stepping, family, model, stepping);
+               fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d) microcode 0x%x\n",
+                       family, model, stepping, family, model, stepping, (unsigned int)((ucode_patch >> 32) & 0xFFFFFFFF));
+               fprintf(outf, "CPUID(0x80000000): max_extended_levels: 0x%x\n", max_extended_level);
                fprintf(outf, "CPUID(1): %s %s %s %s %s %s %s %s %s %s\n",
                        ecx_flags & (1 << 0) ? "SSE3" : "-",
                        ecx_flags & (1 << 3) ? "MONITOR" : "-",
@@ -5642,6 +5740,7 @@ void turbostat_init()
        check_dev_msr();
        check_permissions();
        process_cpuid();
+       linux_perf_init();
 
 
        if (!quiet)
@@ -5739,7 +5838,7 @@ int get_and_dump_counters(void)
 }
 
 void print_version() {
-       fprintf(outf, "turbostat version 20.09.30"
+       fprintf(outf, "turbostat version 21.03.12"
                " - Len Brown <lenb@kernel.org>\n");
 }
 
@@ -6087,6 +6186,7 @@ void cmdline(int argc, char **argv)
                {"debug",       no_argument,            0, 'd'},        /* internal, not documented */
                {"enable",      required_argument,      0, 'e'},
                {"interval",    required_argument,      0, 'i'},
+               {"IPC", no_argument,                    0, 'I'},
                {"num_iterations",      required_argument,      0, 'n'},
                {"help",        no_argument,            0, 'h'},
                {"hide",        required_argument,      0, 'H'},        // meh, -h taken by --help