linux/arch/x86/kernel/cpu/aperfmperf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * x86 APERF/MPERF KHz calculation for
   4 * /sys/.../cpufreq/scaling_cur_freq
   5 *
   6 * Copyright (C) 2017 Intel Corp.
   7 * Author: Len Brown <len.brown@intel.com>
   8 */
   9
  10#include <linux/delay.h>
  11#include <linux/ktime.h>
  12#include <linux/math64.h>
  13#include <linux/percpu.h>
  14#include <linux/cpufreq.h>
  15#include <linux/smp.h>
  16#include <linux/sched/isolation.h>
  17
  18#include "cpu.h"
  19
  20struct aperfmperf_sample {
  21        unsigned int    khz;
  22        ktime_t time;
  23        u64     aperf;
  24        u64     mperf;
  25};
  26
  27static DEFINE_PER_CPU(struct aperfmperf_sample, samples);
  28
  29#define APERFMPERF_CACHE_THRESHOLD_MS   10
  30#define APERFMPERF_REFRESH_DELAY_MS     10
  31#define APERFMPERF_STALE_THRESHOLD_MS   1000
  32
  33/*
  34 * aperfmperf_snapshot_khz()
  35 * On the current CPU, snapshot APERF, MPERF, and jiffies
  36 * unless we already did it within 10ms
  37 * calculate kHz, save snapshot
  38 */
  39static void aperfmperf_snapshot_khz(void *dummy)
  40{
  41        u64 aperf, aperf_delta;
  42        u64 mperf, mperf_delta;
  43        struct aperfmperf_sample *s = this_cpu_ptr(&samples);
  44        unsigned long flags;
  45
  46        local_irq_save(flags);
  47        rdmsrl(MSR_IA32_APERF, aperf);
  48        rdmsrl(MSR_IA32_MPERF, mperf);
  49        local_irq_restore(flags);
  50
  51        aperf_delta = aperf - s->aperf;
  52        mperf_delta = mperf - s->mperf;
  53
  54        /*
  55         * There is no architectural guarantee that MPERF
  56         * increments faster than we can read it.
  57         */
  58        if (mperf_delta == 0)
  59                return;
  60
  61        s->time = ktime_get();
  62        s->aperf = aperf;
  63        s->mperf = mperf;
  64        s->khz = div64_u64((cpu_khz * aperf_delta), mperf_delta);
  65}
  66
  67static bool aperfmperf_snapshot_cpu(int cpu, ktime_t now, bool wait)
  68{
  69        s64 time_delta = ktime_ms_delta(now, per_cpu(samples.time, cpu));
  70
  71        /* Don't bother re-computing within the cache threshold time. */
  72        if (time_delta < APERFMPERF_CACHE_THRESHOLD_MS)
  73                return true;
  74
  75        smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, wait);
  76
  77        /* Return false if the previous iteration was too long ago. */
  78        return time_delta <= APERFMPERF_STALE_THRESHOLD_MS;
  79}
  80
  81unsigned int aperfmperf_get_khz(int cpu)
  82{
  83        if (!cpu_khz)
  84                return 0;
  85
  86        if (!boot_cpu_has(X86_FEATURE_APERFMPERF))
  87                return 0;
  88
  89        if (!housekeeping_cpu(cpu, HK_FLAG_MISC))
  90                return 0;
  91
  92        aperfmperf_snapshot_cpu(cpu, ktime_get(), true);
  93        return per_cpu(samples.khz, cpu);
  94}
  95
  96void arch_freq_prepare_all(void)
  97{
  98        ktime_t now = ktime_get();
  99        bool wait = false;
 100        int cpu;
 101
 102        if (!cpu_khz)
 103                return;
 104
 105        if (!boot_cpu_has(X86_FEATURE_APERFMPERF))
 106                return;
 107
 108        for_each_online_cpu(cpu) {
 109                if (!housekeeping_cpu(cpu, HK_FLAG_MISC))
 110                        continue;
 111                if (!aperfmperf_snapshot_cpu(cpu, now, false))
 112                        wait = true;
 113        }
 114
 115        if (wait)
 116                msleep(APERFMPERF_REFRESH_DELAY_MS);
 117}
 118
 119unsigned int arch_freq_get_on_cpu(int cpu)
 120{
 121        if (!cpu_khz)
 122                return 0;
 123
 124        if (!boot_cpu_has(X86_FEATURE_APERFMPERF))
 125                return 0;
 126
 127        if (!housekeeping_cpu(cpu, HK_FLAG_MISC))
 128                return 0;
 129
 130        if (aperfmperf_snapshot_cpu(cpu, ktime_get(), true))
 131                return per_cpu(samples.khz, cpu);
 132
 133        msleep(APERFMPERF_REFRESH_DELAY_MS);
 134        smp_call_function_single(cpu, aperfmperf_snapshot_khz, NULL, 1);
 135
 136        return per_cpu(samples.khz, cpu);
 137}
 138