Skip to content

Commit

Permalink
x86, sched: Calculate frequency invariance for AMD systems
Browse files Browse the repository at this point in the history
This is the first pass in creating the ability to calculate the
frequency invariance on AMD systems. This approach uses the CPPC
highest performance and nominal performance values that range from
0 - 255 instead of a high and base frquency. This is because we do
not have the ability on AMD to get a highest frequency value.

On AMD systems the highest performance and nominal performance
vaues do correspond to the highest and base frequencies for the system
so using them should produce an appropriate ratio but some tweaking
is likely necessary.

Due to CPPC being initialized later in boot than when the frequency
invariant calculation is currently made, I had to create a callback
from the CPPC init code to do the calculation after we have CPPC
data.

Signed-off-by: Nathan Fontenot <nathan.fontenot@amd.com>
[ ggherdovich@suse.cz: cosmetic changes ]
Signed-off-by: Giovanni Gherdovich <ggherdovich@suse.cz>
  • Loading branch information
nfont authored and intel-lab-lkp committed Nov 10, 2020
1 parent 238c911 commit 5a6d809
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 8 deletions.
8 changes: 8 additions & 0 deletions arch/x86/include/asm/topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,12 @@ static inline void arch_set_max_freq_ratio(bool turbo_disabled)
}
#endif

#ifdef CONFIG_ACPI
void init_freq_invariance_cppc(void);
#else
static inline void init_freq_invariance_cppc(void)
{
}
#endif

#endif /* _ASM_X86_TOPOLOGY_H */
76 changes: 68 additions & 8 deletions arch/x86/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
#include <asm/hw_irq.h>
#include <asm/stackprotector.h>

#ifdef CONFIG_ACPI
#include <acpi/cppc_acpi.h>
#endif

/* representing HT siblings of each logical CPU */
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);
EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);
Expand Down Expand Up @@ -148,7 +152,7 @@ static inline void smpboot_restore_warm_reset_vector(void)
*((volatile u32 *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = 0;
}

static void init_freq_invariance(bool secondary);
static void init_freq_invariance(bool secondary, bool cppc_ready);

/*
* Report back to the Boot Processor during boot time or to the caller processor
Expand Down Expand Up @@ -186,7 +190,7 @@ static void smp_callin(void)
*/
set_cpu_sibling_map(raw_smp_processor_id());

init_freq_invariance(true);
init_freq_invariance(true, false);

/*
* Get our bogomips.
Expand Down Expand Up @@ -1340,7 +1344,7 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
set_sched_topology(x86_topology);

set_cpu_sibling_map(0);
init_freq_invariance(false);
init_freq_invariance(false, false);
smp_sanity_check();

switch (apic_intr_mode) {
Expand Down Expand Up @@ -2027,7 +2031,47 @@ static bool intel_set_max_freq_ratio(void)
return true;
}

static void init_counter_refs(void)
#ifdef CONFIG_ACPI
static bool amd_set_max_freq_ratio(void)
{
struct cppc_perf_caps perf_caps;
u64 highest_perf, nominal_perf;
u64 perf_ratio;
int rc;

rc = cppc_get_perf_caps(0, &perf_caps);
if (rc) {
pr_debug("Could not retrieve perf counters (%d)\n", rc);
return false;
}

highest_perf = perf_caps.highest_perf;
nominal_perf = perf_caps.nominal_perf;

if (!highest_perf || !nominal_perf) {
pr_debug("Could not retrieve highest or nominal performance\n");
return false;
}

perf_ratio = div_u64(highest_perf * SCHED_CAPACITY_SCALE, nominal_perf);
if (!perf_ratio) {
pr_debug("Non-zero highest/nominal perf values led to a 0 ratio\n");
return false;
}

arch_turbo_freq_ratio = perf_ratio;
arch_set_max_freq_ratio(false);

return true;
}
#else
static bool amd_set_max_freq_ratio(void)
{
return false;
}
#endif

static void init_counter_refs(void *arg)
{
u64 aperf, mperf;

Expand All @@ -2038,7 +2082,7 @@ static void init_counter_refs(void)
this_cpu_write(arch_prev_mperf, mperf);
}

static void init_freq_invariance(bool secondary)
static void init_freq_invariance(bool secondary, bool cppc_ready)
{
bool ret = false;

Expand All @@ -2047,22 +2091,38 @@ static void init_freq_invariance(bool secondary)

if (secondary) {
if (static_branch_likely(&arch_scale_freq_key)) {
init_counter_refs();
init_counter_refs(NULL);
}
return;
}

if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
ret = intel_set_max_freq_ratio();
else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
if (!cppc_ready) {
return;
}
ret = amd_set_max_freq_ratio();
}

if (ret) {
init_counter_refs();
init_counter_refs(NULL);
static_branch_enable(&arch_scale_freq_key);
} else {
pr_debug("Couldn't determine max cpu frequency, necessary for scale-invariant accounting.\n");
}
}

#ifdef CONFIG_ACPI
void init_freq_invariance_cppc(void)
{
init_freq_invariance(false, true);

if (static_branch_likely(&arch_scale_freq_key))
on_each_cpu(init_counter_refs, NULL, 0);
}
#endif

static void disable_freq_invariance_workfn(struct work_struct *work)
{
static_branch_disable(&arch_scale_freq_key);
Expand Down Expand Up @@ -2112,7 +2172,7 @@ void arch_scale_freq_tick(void)
schedule_work(&disable_freq_invariance_work);
}
#else
static inline void init_freq_invariance(bool secondary)
static inline void init_freq_invariance(bool secondary, bool cppc_ready)
{
}
#endif /* CONFIG_X86_64 */
5 changes: 5 additions & 0 deletions drivers/acpi/cppc_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <linux/ktime.h>
#include <linux/rwsem.h>
#include <linux/wait.h>
#include <linux/topology.h>

#include <acpi/cppc_acpi.h>

Expand Down Expand Up @@ -850,6 +851,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}

/* Only needed once, so call on CPU0 */
if (pr->id == 0)
init_freq_invariance_cppc();

kfree(output.pointer);
return 0;

Expand Down

0 comments on commit 5a6d809

Please sign in to comment.