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>
@ -62,34 +62,19 @@
#define PP_POLICY_MASK 0x1F #define PP_POLICY_MASK 0x1F
/* Non HW constants */ /* Non HW constants */
#define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */ #define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */
#define RAPL_PRIMITIVE_DUMMY BIT(2) #define RAPL_PRIMITIVE_DUMMY BIT(2)
#define TIME_WINDOW_MAX_MSEC 40000 #define TIME_WINDOW_MAX_MSEC 40000
#define TIME_WINDOW_MIN_MSEC 250 #define TIME_WINDOW_MIN_MSEC 250
#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */ #define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */
enum unit_type { enum unit_type {
ARBITRARY_UNIT, /* no translation */ ARBITRARY_UNIT, /* no translation */
POWER_UNIT, POWER_UNIT,
ENERGY_UNIT, ENERGY_UNIT,
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)
@ -108,7 +93,7 @@ struct rapl_defaults {
int (*check_unit)(struct rapl_package *rp, int cpu); int (*check_unit)(struct rapl_package *rp, int cpu);
void (*set_floor_freq)(struct rapl_domain *rd, bool mode); void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
u64 (*compute_time_window)(struct rapl_package *rp, u64 val, u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
bool to_raw); bool to_raw);
unsigned int dram_domain_energy_unit; unsigned int dram_domain_energy_unit;
}; };
static struct rapl_defaults *rapl_defaults; static struct rapl_defaults *rapl_defaults;
@ -143,19 +128,20 @@ struct rapl_primitive_info {
static void rapl_init_domains(struct rapl_package *rp); static void rapl_init_domains(struct rapl_package *rp);
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);
static int rapl_write_data_raw(struct rapl_domain *rd, 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 */
static const char * const rapl_domain_names[] = { static const char *const rapl_domain_names[] = {
"package", "package",
"core", "core",
"uncore", "uncore",
@ -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;
@ -276,50 +249,49 @@ static int get_domain_enable(struct powercap_zone *power_zone, bool *mode)
static const struct powercap_zone_ops zone_ops[] = { static const struct powercap_zone_ops zone_ops[] = {
/* RAPL_DOMAIN_PACKAGE */ /* RAPL_DOMAIN_PACKAGE */
{ {
.get_energy_uj = get_energy_counter, .get_energy_uj = get_energy_counter,
.get_max_energy_range_uj = get_max_energy_counter, .get_max_energy_range_uj = get_max_energy_counter,
.release = release_zone, .release = release_zone,
.set_enable = set_domain_enable, .set_enable = set_domain_enable,
.get_enable = get_domain_enable, .get_enable = get_domain_enable,
}, },
/* RAPL_DOMAIN_PP0 */ /* RAPL_DOMAIN_PP0 */
{ {
.get_energy_uj = get_energy_counter, .get_energy_uj = get_energy_counter,
.get_max_energy_range_uj = get_max_energy_counter, .get_max_energy_range_uj = get_max_energy_counter,
.release = release_zone, .release = release_zone,
.set_enable = set_domain_enable, .set_enable = set_domain_enable,
.get_enable = get_domain_enable, .get_enable = get_domain_enable,
}, },
/* RAPL_DOMAIN_PP1 */ /* RAPL_DOMAIN_PP1 */
{ {
.get_energy_uj = get_energy_counter, .get_energy_uj = get_energy_counter,
.get_max_energy_range_uj = get_max_energy_counter, .get_max_energy_range_uj = get_max_energy_counter,
.release = release_zone, .release = release_zone,
.set_enable = set_domain_enable, .set_enable = set_domain_enable,
.get_enable = get_domain_enable, .get_enable = get_domain_enable,
}, },
/* RAPL_DOMAIN_DRAM */ /* RAPL_DOMAIN_DRAM */
{ {
.get_energy_uj = get_energy_counter, .get_energy_uj = get_energy_counter,
.get_max_energy_range_uj = get_max_energy_counter, .get_max_energy_range_uj = get_max_energy_counter,
.release = release_zone, .release = release_zone,
.set_enable = set_domain_enable, .set_enable = set_domain_enable,
.get_enable = get_domain_enable, .get_enable = get_domain_enable,
}, },
/* RAPL_DOMAIN_PLATFORM */ /* RAPL_DOMAIN_PLATFORM */
{ {
.get_energy_uj = get_energy_counter, .get_energy_uj = get_energy_counter,
.get_max_energy_range_uj = get_max_energy_counter, .get_max_energy_range_uj = get_max_energy_counter,
.release = release_zone, .release = release_zone,
.set_enable = set_domain_enable, .set_enable = set_domain_enable,
.get_enable = get_domain_enable, .get_enable = get_domain_enable,
}, },
}; };
/* /*
* 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)
@ -338,7 +310,7 @@ static int contraint_to_pl(struct rapl_domain *rd, int cid)
} }
static int set_power_limit(struct powercap_zone *power_zone, int cid, static int set_power_limit(struct powercap_zone *power_zone, int cid,
u64 power_limit) u64 power_limit)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
struct rapl_package *rp; struct rapl_package *rp;
@ -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;
} }
@ -380,7 +352,7 @@ set_exit:
} }
static int get_current_power_limit(struct powercap_zone *power_zone, int cid, static int get_current_power_limit(struct powercap_zone *power_zone, int cid,
u64 *data) u64 *data)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
u64 val; u64 val;
@ -419,7 +391,7 @@ get_exit:
} }
static int set_time_window(struct powercap_zone *power_zone, int cid, static int set_time_window(struct powercap_zone *power_zone, int cid,
u64 window) u64 window)
{ {
struct rapl_domain *rd; struct rapl_domain *rd;
int ret = 0; int ret = 0;
@ -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):
@ -580,7 +557,7 @@ static void rapl_init_domains(struct rapl_package *rp)
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->domain_energy_unit = rd->domain_energy_unit =
rapl_defaults->dram_domain_energy_unit; rapl_defaults->dram_domain_energy_unit;
if (rd->domain_energy_unit) if (rd->domain_energy_unit)
pr_info("DRAM domain energy unit %dpj\n", pr_info("DRAM domain energy unit %dpj\n",
rd->domain_energy_unit); rd->domain_energy_unit);
@ -594,7 +571,7 @@ static void rapl_init_domains(struct rapl_package *rp)
} }
static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type, static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type,
u64 value, int to_raw) u64 value, int to_raw)
{ {
u64 units = 1; u64 units = 1;
struct rapl_package *rp = rd->rp; struct rapl_package *rp = rd->rp;
@ -631,40 +608,40 @@ static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type,
static struct rapl_primitive_info rpi[] = { static struct rapl_primitive_info rpi[] = {
/* name, mask, shift, msr index, unit divisor */ /* name, mask, shift, msr index, unit divisor */
PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0, PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0,
RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0), RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0),
PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0, PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0,
RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32, PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32,
RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31, PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15, PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16, PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47, PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48, PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17, PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17,
RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49, PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49,
RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK, PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK,
0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32, PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32,
RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16, PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16,
RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48, PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48,
RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0), RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0, PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0,
RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0), RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0),
PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0, PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0,
RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0), RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0),
/* non-hardware */ /* non-hardware */
PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT, PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT,
RAPL_PRIMITIVE_DERIVED), RAPL_PRIMITIVE_DERIVED),
{NULL, 0, 0, 0}, {NULL, 0, 0, 0},
}; };
@ -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];
@ -699,7 +675,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
cpu = rd->rp->lead_cpu; cpu = rd->rp->lead_cpu;
/* special-case package domain, which uses a different bit*/ /* special-case package domain, which uses a different bit */
if (prim == FW_LOCK && rd->id == RAPL_DOMAIN_PACKAGE) { if (prim == FW_LOCK && rd->id == RAPL_DOMAIN_PACKAGE) {
rp->mask = POWER_PACKAGE_LOCK; rp->mask = POWER_PACKAGE_LOCK;
rp->shift = 63; rp->shift = 63;
@ -729,8 +705,8 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
/* Similar use of primitive info in the read counterpart */ /* Similar use of primitive info in the read counterpart */
static int rapl_write_data_raw(struct rapl_domain *rd, 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)
{ {
struct rapl_primitive_info *rp = &rpi[prim]; struct rapl_primitive_info *rp = &rpi[prim];
int cpu; int cpu;
@ -774,7 +750,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
ra.mask = ~0; ra.mask = ~0;
if (rp->priv->read_raw(cpu, &ra)) { if (rp->priv->read_raw(cpu, &ra)) {
pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n", pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
rp->priv->reg_unit, cpu); rp->priv->reg_unit, cpu);
return -ENODEV; return -ENODEV;
} }
@ -788,7 +764,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
rp->time_unit = 1000000 / (1 << value); rp->time_unit = 1000000 / (1 << value);
pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n", pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n",
rp->name, rp->energy_unit, rp->time_unit, rp->power_unit); rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
return 0; return 0;
} }
@ -802,7 +778,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
ra.mask = ~0; ra.mask = ~0;
if (rp->priv->read_raw(cpu, &ra)) { if (rp->priv->read_raw(cpu, &ra)) {
pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n", pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
rp->priv->reg_unit, cpu); rp->priv->reg_unit, cpu);
return -ENODEV; return -ENODEV;
} }
@ -816,7 +792,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
rp->time_unit = 1000000 / (1 << value); rp->time_unit = 1000000 / (1 << value);
pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n", pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n",
rp->name, rp->energy_unit, rp->time_unit, rp->power_unit); rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
return 0; return 0;
} }
@ -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
@ -920,9 +895,9 @@ static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
} }
static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value, static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
bool to_raw) bool to_raw)
{ {
u64 f, y; /* fraction and exp. used for time unit */ u64 f, y; /* fraction and exp. used for time unit */
/* /*
* Special processing based on 2^Y*(1+F/4), refer * Special processing based on 2^Y*(1+F/4), refer
@ -942,7 +917,7 @@ static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
} }
static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value, static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
bool to_raw) bool to_raw)
{ {
/* /*
* Atom time unit encoding is straight forward val * time_unit, * Atom time unit encoding is straight forward val * time_unit,
@ -950,8 +925,8 @@ 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;
} }
@ -999,43 +974,44 @@ static const struct rapl_defaults rapl_defaults_cht = {
}; };
static const struct x86_cpu_id rapl_ids[] __initconst = { static const struct x86_cpu_id rapl_ids[] __initconst = {
INTEL_CPU_FAM6(SANDYBRIDGE, rapl_defaults_core), INTEL_CPU_FAM6(SANDYBRIDGE, rapl_defaults_core),
INTEL_CPU_FAM6(SANDYBRIDGE_X, rapl_defaults_core), INTEL_CPU_FAM6(SANDYBRIDGE_X, rapl_defaults_core),
INTEL_CPU_FAM6(IVYBRIDGE, rapl_defaults_core), INTEL_CPU_FAM6(IVYBRIDGE, rapl_defaults_core),
INTEL_CPU_FAM6(IVYBRIDGE_X, rapl_defaults_core), INTEL_CPU_FAM6(IVYBRIDGE_X, rapl_defaults_core),
INTEL_CPU_FAM6(HASWELL_CORE, rapl_defaults_core), INTEL_CPU_FAM6(HASWELL_CORE, rapl_defaults_core),
INTEL_CPU_FAM6(HASWELL_ULT, rapl_defaults_core), INTEL_CPU_FAM6(HASWELL_ULT, rapl_defaults_core),
INTEL_CPU_FAM6(HASWELL_GT3E, rapl_defaults_core), INTEL_CPU_FAM6(HASWELL_GT3E, rapl_defaults_core),
INTEL_CPU_FAM6(HASWELL_X, rapl_defaults_hsw_server), INTEL_CPU_FAM6(HASWELL_X, rapl_defaults_hsw_server),
INTEL_CPU_FAM6(BROADWELL_CORE, rapl_defaults_core), INTEL_CPU_FAM6(BROADWELL_CORE, rapl_defaults_core),
INTEL_CPU_FAM6(BROADWELL_GT3E, rapl_defaults_core), INTEL_CPU_FAM6(BROADWELL_GT3E, rapl_defaults_core),
INTEL_CPU_FAM6(BROADWELL_XEON_D, rapl_defaults_core), INTEL_CPU_FAM6(BROADWELL_XEON_D, rapl_defaults_core),
INTEL_CPU_FAM6(BROADWELL_X, rapl_defaults_hsw_server), INTEL_CPU_FAM6(BROADWELL_X, rapl_defaults_hsw_server),
INTEL_CPU_FAM6(SKYLAKE_DESKTOP, rapl_defaults_core), INTEL_CPU_FAM6(SKYLAKE_DESKTOP, rapl_defaults_core),
INTEL_CPU_FAM6(SKYLAKE_MOBILE, rapl_defaults_core), INTEL_CPU_FAM6(SKYLAKE_MOBILE, rapl_defaults_core),
INTEL_CPU_FAM6(SKYLAKE_X, rapl_defaults_hsw_server), INTEL_CPU_FAM6(SKYLAKE_X, rapl_defaults_hsw_server),
INTEL_CPU_FAM6(KABYLAKE_MOBILE, rapl_defaults_core), INTEL_CPU_FAM6(KABYLAKE_MOBILE, rapl_defaults_core),
INTEL_CPU_FAM6(KABYLAKE_DESKTOP, rapl_defaults_core), INTEL_CPU_FAM6(KABYLAKE_DESKTOP, rapl_defaults_core),
INTEL_CPU_FAM6(CANNONLAKE_MOBILE, rapl_defaults_core), INTEL_CPU_FAM6(CANNONLAKE_MOBILE, rapl_defaults_core),
INTEL_CPU_FAM6(ICELAKE_MOBILE, rapl_defaults_core), INTEL_CPU_FAM6(ICELAKE_MOBILE, rapl_defaults_core),
INTEL_CPU_FAM6(ATOM_SILVERMONT, rapl_defaults_byt), INTEL_CPU_FAM6(ATOM_SILVERMONT, rapl_defaults_byt),
INTEL_CPU_FAM6(ATOM_AIRMONT, rapl_defaults_cht), INTEL_CPU_FAM6(ATOM_AIRMONT, rapl_defaults_cht),
INTEL_CPU_FAM6(ATOM_SILVERMONT_MID, rapl_defaults_tng), INTEL_CPU_FAM6(ATOM_SILVERMONT_MID, rapl_defaults_tng),
INTEL_CPU_FAM6(ATOM_AIRMONT_MID, rapl_defaults_ann), INTEL_CPU_FAM6(ATOM_AIRMONT_MID, rapl_defaults_ann),
INTEL_CPU_FAM6(ATOM_GOLDMONT, rapl_defaults_core), INTEL_CPU_FAM6(ATOM_GOLDMONT, rapl_defaults_core),
INTEL_CPU_FAM6(ATOM_GOLDMONT_PLUS, rapl_defaults_core), INTEL_CPU_FAM6(ATOM_GOLDMONT_PLUS, rapl_defaults_core),
INTEL_CPU_FAM6(ATOM_GOLDMONT_X, rapl_defaults_core), INTEL_CPU_FAM6(ATOM_GOLDMONT_X, rapl_defaults_core),
INTEL_CPU_FAM6(ATOM_TREMONT_X, rapl_defaults_core), INTEL_CPU_FAM6(ATOM_TREMONT_X, rapl_defaults_core),
INTEL_CPU_FAM6(XEON_PHI_KNL, rapl_defaults_hsw_server), INTEL_CPU_FAM6(XEON_PHI_KNL, rapl_defaults_hsw_server),
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 */
@ -1051,7 +1027,7 @@ static void rapl_update_domain_data(struct rapl_package *rp)
for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) { for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) {
if (!rapl_read_data_raw(&rp->domains[dmn], prim, if (!rapl_read_data_raw(&rp->domains[dmn], prim,
rpi[prim].unit, &val)) rpi[prim].unit, &val))
rp->domains[dmn].rdd.primitives[prim] = val; rp->domains[dmn].rdd.primitives[prim] = val;
} }
} }
@ -1066,20 +1042,18 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
/* Update the domain data of the new package */ /* Update the domain data of the new package */
rapl_update_domain_data(rp); rapl_update_domain_data(rp);
/* first we register package domain as the parent zone*/ /* first we register package domain as the parent zone */
for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
if (rd->id == RAPL_DOMAIN_PACKAGE) { if (rd->id == RAPL_DOMAIN_PACKAGE) {
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], &constraint_ops);
nr_pl,
&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",
rp->name); rp->name);
return PTR_ERR(power_zone); return PTR_ERR(power_zone);
} }
/* track parent zone in per package/socket data */ /* track parent zone in per package/socket data */
@ -1092,21 +1066,21 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
pr_err("no package domain found, unknown topology!\n"); pr_err("no package domain found, unknown topology!\n");
return -ENODEV; return -ENODEV;
} }
/* now register domains as children of the socket/package*/ /* now register domains as children of the socket/package */
for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
if (rd->id == RAPL_DOMAIN_PACKAGE) if (rd->id == RAPL_DOMAIN_PACKAGE)
continue; continue;
/* 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);
if (IS_ERR(power_zone)) { if (IS_ERR(power_zone)) {
pr_debug("failed to register power_zone, %s:%s\n", pr_debug("failed to register power_zone, %s:%s\n",
rp->name, rd->name); rp->name, rd->name);
ret = PTR_ERR(power_zone); ret = PTR_ERR(power_zone);
goto err_cleanup; goto err_cleanup;
} }
@ -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,15 +1150,17 @@ 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,
&priv->platform_rapl_domain->power_zone); &priv->platform_rapl_domain->power_zone);
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;
} }
@ -1258,7 +1237,7 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
pr_info("Found RAPL domain %s\n", rapl_domain_names[i]); pr_info("Found RAPL domain %s\n", rapl_domain_names[i]);
} }
} }
rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX); rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX);
if (!rp->nr_domains) { if (!rp->nr_domains) {
pr_debug("no valid rapl domains found in %s\n", rp->name); pr_debug("no valid rapl domains found in %s\n", rp->name);
return -ENODEV; return -ENODEV;
@ -1266,7 +1245,7 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name); pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name);
rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain), rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain),
GFP_KERNEL); GFP_KERNEL);
if (!rp->domains) if (!rp->domains)
return -ENOMEM; return -ENOMEM;
@ -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);
@ -1325,14 +1329,13 @@ static struct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv)
if (topology_max_die_per_package() > 1) if (topology_max_die_per_package() > 1)
snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH,
"package-%d-die-%d", c->phys_proc_id, c->cpu_die_id); "package-%d-die-%d", c->phys_proc_id, c->cpu_die_id);
else else
snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d", snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d",
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,17 +1369,15 @@ 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;
break; break;
@ -1440,15 +1403,13 @@ 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;
} }
} }
@ -1457,7 +1418,7 @@ static void power_limit_state_restore(void)
} }
static int rapl_pm_callback(struct notifier_block *nb, static int rapl_pm_callback(struct notifier_block *nb,
unsigned long mode, void *_unused) unsigned long mode, void *_unused)
{ {
switch (mode) { switch (mode) {
case PM_SUSPEND_PREPARE: case PM_SUSPEND_PREPARE:
@ -1474,101 +1435,35 @@ 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",
boot_cpu_data.x86, boot_cpu_data.x86_model); boot_cpu_data.x86, boot_cpu_data.x86_model);
return -ENODEV; return -ENODEV;
} }
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__ */