1
0
Fork 0

intel_rapl: abstract RAPL common code

Split intel_rapl.c to intel_rapl_common.c and intel_rapl_msr.c, where
intel_rapl_common.c contains the common code that can be used by both MSR
and MMIO interface.
intel_rapl_msr.c contains the implementation of RAPL MSR interface.

Reviewed-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
alistair/sunxi64-5.4-dsi
Zhang Rui 2019-07-10 21:44:30 +08:00 committed by Rafael J. Wysocki
parent 1193b1658d
commit 3382388d71
5 changed files with 389 additions and 320 deletions

View File

@ -16,14 +16,17 @@ menuconfig POWERCAP
if POWERCAP if POWERCAP
# Client driver configurations go here. # Client driver configurations go here.
config INTEL_RAPL_CORE
tristate
config INTEL_RAPL config INTEL_RAPL
tristate "Intel RAPL Support" tristate "Intel RAPL Support via MSR Interface"
depends on X86 && IOSF_MBI depends on X86 && IOSF_MBI
default n select INTEL_RAPL_CORE
---help--- ---help---
This enables support for the Intel Running Average Power Limit (RAPL) This enables support for the Intel Running Average Power Limit (RAPL)
technology which allows power limits to be enforced and monitored on technology via MSR interface, which allows power limits to be enforced
modern Intel processors (Sandy Bridge and later). and monitored on modern Intel processors (Sandy Bridge and later).
In RAPL, the platform level settings are divided into domains for In RAPL, the platform level settings are divided into domains for
fine grained control. These domains include processor package, DRAM fine grained control. These domains include processor package, DRAM

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_POWERCAP) += powercap_sys.o
obj-$(CONFIG_INTEL_RAPL) += intel_rapl.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o
obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o
obj-$(CONFIG_IDLE_INJECT) += idle_inject.o obj-$(CONFIG_IDLE_INJECT) += idle_inject.o

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Intel Running Average Power Limit (RAPL) Driver * Common code for Intel Running Average Power Limit (RAPL) support.
* Copyright (c) 2013, Intel Corporation. * Copyright (c) 2019, Intel Corporation.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -18,10 +18,10 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/powercap.h> #include <linux/powercap.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <asm/iosf_mbi.h>
#include <linux/intel_rapl.h> #include <linux/intel_rapl.h>
#include <asm/iosf_mbi.h> #include <linux/processor.h>
#include <asm/processor.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
@ -75,21 +75,6 @@ enum unit_type {
TIME_UNIT, TIME_UNIT,
}; };
/* private data for RAPL MSR Interface */
static struct rapl_if_priv rapl_msr_priv = {
.reg_unit = MSR_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO },
.regs[RAPL_DOMAIN_PP0] = {
MSR_PP0_POWER_LIMIT, MSR_PP0_ENERGY_STATUS, 0, MSR_PP0_POLICY, 0 },
.regs[RAPL_DOMAIN_PP1] = {
MSR_PP1_POWER_LIMIT, MSR_PP1_ENERGY_STATUS, 0, MSR_PP1_POLICY, 0 },
.regs[RAPL_DOMAIN_DRAM] = {
MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO },
.regs[RAPL_DOMAIN_PLATFORM] = {
MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
};
/* per domain data, some are optional */ /* per domain data, some are optional */
#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2) #define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
@ -149,9 +134,10 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim, enum rapl_primitives prim,
unsigned long long value); unsigned long long value);
static u64 rapl_unit_xlate(struct rapl_domain *rd, static u64 rapl_unit_xlate(struct rapl_domain *rd,
enum unit_type type, u64 value, enum unit_type type, u64 value, int to_raw);
int to_raw);
static void package_power_limit_irq_save(struct rapl_package *rp); static void package_power_limit_irq_save(struct rapl_package *rp);
static int rapl_init_core(void);
static void rapl_remove_core(void);
static LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */ static LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */
@ -163,21 +149,8 @@ static const char * const rapl_domain_names[] = {
"psys", "psys",
}; };
/* caller to ensure CPU hotplug lock is held */ static int get_energy_counter(struct powercap_zone *power_zone,
static struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_if_priv *priv) u64 *energy_raw)
{
int id = topology_logical_die_id(cpu);
struct rapl_package *rp;
list_for_each_entry(rp, &rapl_packages, plist) {
if (rp->id == id && rp->priv->control_type == priv->control_type)
return rp;
}
return NULL;
}
static int get_energy_counter(struct powercap_zone *power_zone, u64 *energy_raw)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
u64 energy_now; u64 energy_now;
@ -316,10 +289,9 @@ static const struct powercap_zone_ops zone_ops[] = {
}, },
}; };
/* /*
* Constraint index used by powercap can be different than power limit (PL) * Constraint index used by powercap can be different than power limit (PL)
* index in that some PLs maybe missing due to non-existant MSRs. So we * index in that some PLs maybe missing due to non-existent MSRs. So we
* need to convert here by finding the valid PLs only (name populated). * need to convert here by finding the valid PLs only (name populated).
*/ */
static int contraint_to_pl(struct rapl_domain *rd, int cid) static int contraint_to_pl(struct rapl_domain *rd, int cid)
@ -356,8 +328,8 @@ static int set_power_limit(struct powercap_zone *power_zone, int cid,
rp = rd->rp; rp = rd->rp;
if (rd->state & DOMAIN_STATE_BIOS_LOCKED) { if (rd->state & DOMAIN_STATE_BIOS_LOCKED) {
dev_warn(&power_zone->dev, "%s locked by BIOS, monitoring only\n", dev_warn(&power_zone->dev,
rd->name); "%s locked by BIOS, monitoring only\n", rd->name);
ret = -EACCES; ret = -EACCES;
goto set_exit; goto set_exit;
} }
@ -449,7 +421,8 @@ set_time_exit:
return ret; return ret;
} }
static int get_time_window(struct powercap_zone *power_zone, int cid, u64 *data) static int get_time_window(struct powercap_zone *power_zone, int cid,
u64 *data)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
u64 val; u64 val;
@ -484,7 +457,8 @@ get_time_exit:
return ret; return ret;
} }
static const char *get_constraint_name(struct powercap_zone *power_zone, int cid) static const char *get_constraint_name(struct powercap_zone *power_zone,
int cid)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
int id; int id;
@ -497,9 +471,7 @@ static const char *get_constraint_name(struct powercap_zone *power_zone, int cid
return NULL; return NULL;
} }
static int get_max_power(struct powercap_zone *power_zone, int id, u64 *data)
static int get_max_power(struct powercap_zone *power_zone, int id,
u64 *data)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
u64 val; u64 val;
@ -547,11 +519,16 @@ static void rapl_init_domains(struct rapl_package *rp)
for (i = 0; i < RAPL_DOMAIN_MAX; i++) { for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
unsigned int mask = rp->domain_map & (1 << i); unsigned int mask = rp->domain_map & (1 << i);
rd->regs[RAPL_DOMAIN_REG_LIMIT] = rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT]; rd->regs[RAPL_DOMAIN_REG_LIMIT] =
rd->regs[RAPL_DOMAIN_REG_STATUS] = rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS]; rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT];
rd->regs[RAPL_DOMAIN_REG_PERF] = rp->priv->regs[i][RAPL_DOMAIN_REG_PERF]; rd->regs[RAPL_DOMAIN_REG_STATUS] =
rd->regs[RAPL_DOMAIN_REG_POLICY] = rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY]; rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS];
rd->regs[RAPL_DOMAIN_REG_INFO] = rp->priv->regs[i][RAPL_DOMAIN_REG_INFO]; rd->regs[RAPL_DOMAIN_REG_PERF] =
rp->priv->regs[i][RAPL_DOMAIN_REG_PERF];
rd->regs[RAPL_DOMAIN_REG_POLICY] =
rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY];
rd->regs[RAPL_DOMAIN_REG_INFO] =
rp->priv->regs[i][RAPL_DOMAIN_REG_INFO];
switch (mask) { switch (mask) {
case BIT(RAPL_DOMAIN_PACKAGE): case BIT(RAPL_DOMAIN_PACKAGE):
@ -682,8 +659,7 @@ static struct rapl_primitive_info rpi[] = {
* 63-------------------------- 31--------------------------- 0 * 63-------------------------- 31--------------------------- 0
*/ */
static int rapl_read_data_raw(struct rapl_domain *rd, static int rapl_read_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim, enum rapl_primitives prim, bool xlate, u64 *data)
bool xlate, u64 *data)
{ {
u64 value; u64 value;
struct rapl_primitive_info *rp = &rpi[prim]; struct rapl_primitive_info *rp = &rpi[prim];
@ -836,7 +812,6 @@ static void power_limit_irq_save_cpu(void *info)
wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
} }
/* REVISIT: /* REVISIT:
* When package power limit is set artificially low by RAPL, LVT * When package power limit is set artificially low by RAPL, LVT
* thermal interrupt for package power limit should be ignored * thermal interrupt for package power limit should be ignored
@ -950,7 +925,7 @@ static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
*/ */
if (!to_raw) if (!to_raw)
return (value) ? value *= rp->time_unit : rp->time_unit; return (value) ? value *= rp->time_unit : rp->time_unit;
else
value = div64_u64(value, rp->time_unit); value = div64_u64(value, rp->time_unit);
return value; return value;
@ -1036,6 +1011,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
INTEL_CPU_FAM6(XEON_PHI_KNM, rapl_defaults_hsw_server), INTEL_CPU_FAM6(XEON_PHI_KNM, rapl_defaults_hsw_server),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, rapl_ids); MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
/* Read once for all raw primitive data for domains */ /* Read once for all raw primitive data for domains */
@ -1072,10 +1048,8 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
nr_pl = find_nr_power_limit(rd); nr_pl = find_nr_power_limit(rd);
pr_debug("register package domain %s\n", rp->name); pr_debug("register package domain %s\n", rp->name);
power_zone = powercap_register_zone(&rd->power_zone, power_zone = powercap_register_zone(&rd->power_zone,
rp->priv->control_type, rp->priv->control_type, rp->name,
rp->name, NULL, NULL, &zone_ops[rd->id], nr_pl,
&zone_ops[rd->id],
nr_pl,
&constraint_ops); &constraint_ops);
if (IS_ERR(power_zone)) { if (IS_ERR(power_zone)) {
pr_debug("failed to register power zone %s\n", pr_debug("failed to register power zone %s\n",
@ -1099,8 +1073,8 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
/* number of power limits per domain varies */ /* number of power limits per domain varies */
nr_pl = find_nr_power_limit(rd); nr_pl = find_nr_power_limit(rd);
power_zone = powercap_register_zone(&rd->power_zone, power_zone = powercap_register_zone(&rd->power_zone,
rp->priv->control_type, rd->name, rp->priv->control_type,
rp->power_zone, rd->name, rp->power_zone,
&zone_ops[rd->id], nr_pl, &zone_ops[rd->id], nr_pl,
&constraint_ops); &constraint_ops);
@ -1120,13 +1094,14 @@ err_cleanup:
*/ */
while (--rd >= rp->domains) { while (--rd >= rp->domains) {
pr_debug("unregister %s domain %s\n", rp->name, rd->name); pr_debug("unregister %s domain %s\n", rp->name, rd->name);
powercap_unregister_zone(rp->priv->control_type, &rd->power_zone); powercap_unregister_zone(rp->priv->control_type,
&rd->power_zone);
} }
return ret; return ret;
} }
static int __init rapl_add_platform_domain(struct rapl_if_priv *priv) int rapl_add_platform_domain(struct rapl_if_priv *priv)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
struct powercap_zone *power_zone; struct powercap_zone *power_zone;
@ -1151,8 +1126,10 @@ static int __init rapl_add_platform_domain(struct rapl_if_priv *priv)
rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM]; rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
rd->id = RAPL_DOMAIN_PLATFORM; rd->id = RAPL_DOMAIN_PLATFORM;
rd->regs[RAPL_DOMAIN_REG_LIMIT] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT]; rd->regs[RAPL_DOMAIN_REG_LIMIT] =
rd->regs[RAPL_DOMAIN_REG_STATUS] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS]; priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
rd->regs[RAPL_DOMAIN_REG_STATUS] =
priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
rd->rpl[0].prim_id = PL1_ENABLE; rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name; rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE; rd->rpl[1].prim_id = PL2_ENABLE;
@ -1173,8 +1150,9 @@ static int __init rapl_add_platform_domain(struct rapl_if_priv *priv)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rapl_add_platform_domain);
static void rapl_remove_platform_domain(struct rapl_if_priv *priv) void rapl_remove_platform_domain(struct rapl_if_priv *priv)
{ {
if (priv->platform_rapl_domain) { if (priv->platform_rapl_domain) {
powercap_unregister_zone(priv->control_type, powercap_unregister_zone(priv->control_type,
@ -1182,6 +1160,7 @@ static void rapl_remove_platform_domain(struct rapl_if_priv *priv)
kfree(priv->platform_rapl_domain); kfree(priv->platform_rapl_domain);
} }
} }
EXPORT_SYMBOL_GPL(rapl_remove_platform_domain);
static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp) static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
{ {
@ -1212,13 +1191,12 @@ static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
return 0; return 0;
} }
/* /*
* Check if power limits are available. Two cases when they are not available: * Check if power limits are available. Two cases when they are not available:
* 1. Locked by BIOS, in this case we still provide read-only access so that * 1. Locked by BIOS, in this case we still provide read-only access so that
* users can see what limit is set by the BIOS. * users can see what limit is set by the BIOS.
* 2. Some CPUs make some domains monitoring only which means PLx MSRs may not * 2. Some CPUs make some domains monitoring only which means PLx MSRs may not
* exist at all. In this case, we do not show the contraints in powercap. * exist at all. In this case, we do not show the constraints in powercap.
* *
* Called after domains are detected and initialized. * Called after domains are detected and initialized.
*/ */
@ -1235,9 +1213,10 @@ static void rapl_detect_powerlimit(struct rapl_domain *rd)
rd->state |= DOMAIN_STATE_BIOS_LOCKED; rd->state |= DOMAIN_STATE_BIOS_LOCKED;
} }
} }
/* check if power limit MSRs exists, otherwise domain is monitoring only */ /* check if power limit MSR exists, otherwise domain is monitoring only */
for (i = 0; i < NR_POWER_LIMITS; i++) { for (i = 0; i < NR_POWER_LIMITS; i++) {
int prim = rd->rpl[i].prim_id; int prim = rd->rpl[i].prim_id;
if (rapl_read_data_raw(rd, prim, false, &val64)) if (rapl_read_data_raw(rd, prim, false, &val64))
rd->rpl[i].name = NULL; rd->rpl[i].name = NULL;
} }
@ -1279,7 +1258,7 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
} }
/* called from CPU hotplug notifier, hotplug lock held */ /* called from CPU hotplug notifier, hotplug lock held */
static void rapl_remove_package(struct rapl_package *rp) void rapl_remove_package(struct rapl_package *rp)
{ {
struct rapl_domain *rd, *rd_package = NULL; struct rapl_domain *rd, *rd_package = NULL;
@ -1298,22 +1277,47 @@ static void rapl_remove_package(struct rapl_package *rp)
} }
pr_debug("remove package, undo power limit on %s: %s\n", pr_debug("remove package, undo power limit on %s: %s\n",
rp->name, rd->name); rp->name, rd->name);
powercap_unregister_zone(rp->priv->control_type, &rd->power_zone); powercap_unregister_zone(rp->priv->control_type,
&rd->power_zone);
} }
/* do parent zone last */ /* do parent zone last */
powercap_unregister_zone(rp->priv->control_type, &rd_package->power_zone); powercap_unregister_zone(rp->priv->control_type,
&rd_package->power_zone);
list_del(&rp->plist); list_del(&rp->plist);
if (list_empty(&rapl_packages))
rapl_remove_core();
kfree(rp); kfree(rp);
} }
EXPORT_SYMBOL_GPL(rapl_remove_package);
/* caller to ensure CPU hotplug lock is held */
struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_if_priv *priv)
{
int id = topology_logical_die_id(cpu);
struct rapl_package *rp;
list_for_each_entry(rp, &rapl_packages, plist) {
if (rp->id == id
&& rp->priv->control_type == priv->control_type)
return rp;
}
return NULL;
}
EXPORT_SYMBOL_GPL(rapl_find_package_domain);
/* called from CPU hotplug notifier, hotplug lock held */ /* called from CPU hotplug notifier, hotplug lock held */
static struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv) struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv)
{ {
int id = topology_logical_die_id(cpu); int id = topology_logical_die_id(cpu);
struct rapl_package *rp; struct rapl_package *rp;
struct cpuinfo_x86 *c = &cpu_data(cpu); struct cpuinfo_x86 *c = &cpu_data(cpu);
int ret; int ret;
ret = rapl_init_core();
if (ret)
return ERR_PTR(ret);
rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL); rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL);
if (!rp) if (!rp)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -1331,8 +1335,7 @@ static struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv)
c->phys_proc_id); c->phys_proc_id);
/* check if the package contains valid domains */ /* check if the package contains valid domains */
if (rapl_detect_domains(rp, cpu) || if (rapl_detect_domains(rp, cpu) || rapl_defaults->check_unit(rp, cpu)) {
rapl_defaults->check_unit(rp, cpu)) {
ret = -ENODEV; ret = -ENODEV;
goto err_free_package; goto err_free_package;
} }
@ -1348,45 +1351,7 @@ err_free_package:
kfree(rp); kfree(rp);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
EXPORT_SYMBOL_GPL(rapl_add_package);
/* Handles CPU hotplug on multi-socket systems.
* If a CPU goes online as the first CPU of the physical package
* we add the RAPL package to the system. Similarly, when the last
* CPU of the package is removed, we remove the RAPL package and its
* associated domains. Cooling devices are handled accordingly at
* per-domain level.
*/
static int rapl_cpu_online(unsigned int cpu)
{
struct rapl_package *rp;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp) {
rp = rapl_add_package(cpu, &rapl_msr_priv);
if (IS_ERR(rp))
return PTR_ERR(rp);
}
cpumask_set_cpu(cpu, &rp->cpumask);
return 0;
}
static int rapl_cpu_down_prep(unsigned int cpu)
{
struct rapl_package *rp;
int lead_cpu;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp)
return 0;
cpumask_clear_cpu(cpu, &rp->cpumask);
lead_cpu = cpumask_first(&rp->cpumask);
if (lead_cpu >= nr_cpu_ids)
rapl_remove_package(rp);
else if (rp->lead_cpu == cpu)
rp->lead_cpu = lead_cpu;
return 0;
}
static void power_limit_state_save(void) static void power_limit_state_save(void)
{ {
@ -1404,16 +1369,14 @@ static void power_limit_state_save(void)
switch (rd->rpl[i].prim_id) { switch (rd->rpl[i].prim_id) {
case PL1_ENABLE: case PL1_ENABLE:
ret = rapl_read_data_raw(rd, ret = rapl_read_data_raw(rd,
POWER_LIMIT1, POWER_LIMIT1, true,
true,
&rd->rpl[i].last_power_limit); &rd->rpl[i].last_power_limit);
if (ret) if (ret)
rd->rpl[i].last_power_limit = 0; rd->rpl[i].last_power_limit = 0;
break; break;
case PL2_ENABLE: case PL2_ENABLE:
ret = rapl_read_data_raw(rd, ret = rapl_read_data_raw(rd,
POWER_LIMIT2, POWER_LIMIT2, true,
true,
&rd->rpl[i].last_power_limit); &rd->rpl[i].last_power_limit);
if (ret) if (ret)
rd->rpl[i].last_power_limit = 0; rd->rpl[i].last_power_limit = 0;
@ -1440,14 +1403,12 @@ static void power_limit_state_restore(void)
switch (rd->rpl[i].prim_id) { switch (rd->rpl[i].prim_id) {
case PL1_ENABLE: case PL1_ENABLE:
if (rd->rpl[i].last_power_limit) if (rd->rpl[i].last_power_limit)
rapl_write_data_raw(rd, rapl_write_data_raw(rd, POWER_LIMIT1,
POWER_LIMIT1,
rd->rpl[i].last_power_limit); rd->rpl[i].last_power_limit);
break; break;
case PL2_ENABLE: case PL2_ENABLE:
if (rd->rpl[i].last_power_limit) if (rd->rpl[i].last_power_limit)
rapl_write_data_raw(rd, rapl_write_data_raw(rd, POWER_LIMIT2,
POWER_LIMIT2,
rd->rpl[i].last_power_limit); rd->rpl[i].last_power_limit);
break; break;
} }
@ -1474,48 +1435,14 @@ static struct notifier_block rapl_pm_notifier = {
.notifier_call = rapl_pm_callback, .notifier_call = rapl_pm_callback,
}; };
static int rapl_msr_read_raw(int cpu, struct reg_action *ra) static int rapl_init_core(void)
{
if (rdmsrl_safe_on_cpu(cpu, ra->reg, &ra->value)) {
pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg, cpu);
return -EIO;
}
ra->value &= ra->mask;
return 0;
}
static void rapl_msr_update_func(void *info)
{
struct reg_action *ra = info;
u64 val;
ra->err = rdmsrl_safe(ra->reg, &val);
if (ra->err)
return;
val &= ~ra->mask;
val |= ra->value;
ra->err = wrmsrl_safe(ra->reg, val);
}
static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
{
int ret;
ret = smp_call_function_single(cpu, rapl_msr_update_func, ra, 1);
if (WARN_ON_ONCE(ret))
return ret;
return ra->err;
}
static int __init rapl_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
int ret; int ret;
if (rapl_defaults)
return 0;
id = x86_match_cpu(rapl_ids); id = x86_match_cpu(rapl_ids);
if (!id) { if (!id) {
pr_err("driver does not support CPU family %d model %d\n", pr_err("driver does not support CPU family %d model %d\n",
@ -1526,49 +1453,17 @@ static int __init rapl_init(void)
rapl_defaults = (struct rapl_defaults *)id->driver_data; rapl_defaults = (struct rapl_defaults *)id->driver_data;
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
if (IS_ERR(rapl_msr_priv.control_type)) {
pr_debug("failed to register powercap control_type.\n");
return PTR_ERR(rapl_msr_priv.control_type);
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
goto err_unreg;
rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
rapl_add_platform_domain(&rapl_msr_priv);
ret = register_pm_notifier(&rapl_pm_notifier); ret = register_pm_notifier(&rapl_pm_notifier);
if (ret)
goto err_unreg_all;
return 0; return 0;
err_unreg_all:
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
err_unreg:
powercap_unregister_control_type(rapl_msr_priv.control_type);
return ret;
} }
static void __exit rapl_exit(void) static void rapl_remove_core(void)
{ {
unregister_pm_notifier(&rapl_pm_notifier); unregister_pm_notifier(&rapl_pm_notifier);
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online); rapl_defaults = NULL;
rapl_remove_platform_domain(&rapl_msr_priv);
powercap_unregister_control_type(rapl_msr_priv.control_type);
} }
module_init(rapl_init); MODULE_DESCRIPTION("Intel Runtime Average Power Limit (RAPL) common code");
module_exit(rapl_exit);
MODULE_DESCRIPTION("Driver for Intel RAPL (Running Average Power Limit)");
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>"); MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Running Average Power Limit (RAPL) Driver via MSR interface
* Copyright (c) 2019, Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/log2.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/cpu.h>
#include <linux/powercap.h>
#include <linux/suspend.h>
#include <linux/intel_rapl.h>
#include <linux/processor.h>
#include <asm/iosf_mbi.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
/* Local defines */
#define MSR_PLATFORM_POWER_LIMIT 0x0000065C
/* private data for RAPL MSR Interface */
static struct rapl_if_priv rapl_msr_priv = {
.reg_unit = MSR_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO },
.regs[RAPL_DOMAIN_PP0] = {
MSR_PP0_POWER_LIMIT, MSR_PP0_ENERGY_STATUS, 0, MSR_PP0_POLICY, 0 },
.regs[RAPL_DOMAIN_PP1] = {
MSR_PP1_POWER_LIMIT, MSR_PP1_ENERGY_STATUS, 0, MSR_PP1_POLICY, 0 },
.regs[RAPL_DOMAIN_DRAM] = {
MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO },
.regs[RAPL_DOMAIN_PLATFORM] = {
MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
};
/* Handles CPU hotplug on multi-socket systems.
* If a CPU goes online as the first CPU of the physical package
* we add the RAPL package to the system. Similarly, when the last
* CPU of the package is removed, we remove the RAPL package and its
* associated domains. Cooling devices are handled accordingly at
* per-domain level.
*/
static int rapl_cpu_online(unsigned int cpu)
{
struct rapl_package *rp;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp) {
rp = rapl_add_package(cpu, &rapl_msr_priv);
if (IS_ERR(rp))
return PTR_ERR(rp);
}
cpumask_set_cpu(cpu, &rp->cpumask);
return 0;
}
static int rapl_cpu_down_prep(unsigned int cpu)
{
struct rapl_package *rp;
int lead_cpu;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp)
return 0;
cpumask_clear_cpu(cpu, &rp->cpumask);
lead_cpu = cpumask_first(&rp->cpumask);
if (lead_cpu >= nr_cpu_ids)
rapl_remove_package(rp);
else if (rp->lead_cpu == cpu)
rp->lead_cpu = lead_cpu;
return 0;
}
static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
{
if (rdmsrl_safe_on_cpu(cpu, ra->reg, &ra->value)) {
pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg, cpu);
return -EIO;
}
ra->value &= ra->mask;
return 0;
}
static void rapl_msr_update_func(void *info)
{
struct reg_action *ra = info;
u64 val;
ra->err = rdmsrl_safe(ra->reg, &val);
if (ra->err)
return;
val &= ~ra->mask;
val |= ra->value;
ra->err = wrmsrl_safe(ra->reg, val);
}
static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
{
int ret;
ret = smp_call_function_single(cpu, rapl_msr_update_func, ra, 1);
if (WARN_ON_ONCE(ret))
return ret;
return ra->err;
}
static int __init rapl_msr_init(void)
{
int ret;
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
if (IS_ERR(rapl_msr_priv.control_type)) {
pr_debug("failed to register powercap control_type.\n");
return PTR_ERR(rapl_msr_priv.control_type);
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
goto out;
rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
rapl_add_platform_domain(&rapl_msr_priv);
return 0;
out:
if (ret)
powercap_unregister_control_type(rapl_msr_priv.control_type);
return ret;
}
static void __exit rapl_msr_exit(void)
{
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
rapl_remove_platform_domain(&rapl_msr_priv);
powercap_unregister_control_type(rapl_msr_priv.control_type);
}
module_init(rapl_msr_init);
module_exit(rapl_msr_exit);
MODULE_DESCRIPTION("Driver for Intel RAPL (Running Average Power Limit) control via MSR interface");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2");

View File

@ -142,4 +142,11 @@ struct rapl_package {
struct rapl_if_priv *priv; struct rapl_if_priv *priv;
}; };
struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_if_priv *priv);
struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv);
void rapl_remove_package(struct rapl_package *rp);
int rapl_add_platform_domain(struct rapl_if_priv *priv);
void rapl_remove_platform_domain(struct rapl_if_priv *priv);
#endif /* __INTEL_RAPL_H__ */ #endif /* __INTEL_RAPL_H__ */