2013-11-12 23:58:50 +07:00
|
|
|
/*
|
|
|
|
* perf_event_intel_rapl.c: support Intel RAPL energy consumption counters
|
|
|
|
* Copyright (C) 2013 Google, Inc., Stephane Eranian
|
|
|
|
*
|
|
|
|
* Intel RAPL interface is specified in the IA-32 Manual Vol3b
|
|
|
|
* section 14.7.1 (September 2013)
|
|
|
|
*
|
|
|
|
* RAPL provides more controls than just reporting energy consumption
|
|
|
|
* however here we only expose the 3 energy consumption free running
|
|
|
|
* counters (pp0, pkg, dram).
|
|
|
|
*
|
|
|
|
* Each of those counters increments in a power unit defined by the
|
|
|
|
* RAPL_POWER_UNIT MSR. On SandyBridge, this unit is 1/(2^16) Joules
|
|
|
|
* but it can vary.
|
|
|
|
*
|
|
|
|
* Counter to rapl events mappings:
|
|
|
|
*
|
|
|
|
* pp0 counter: consumption of all physical cores (power plane 0)
|
|
|
|
* event: rapl_energy_cores
|
|
|
|
* perf code: 0x1
|
|
|
|
*
|
|
|
|
* pkg counter: consumption of the whole processor package
|
|
|
|
* event: rapl_energy_pkg
|
|
|
|
* perf code: 0x2
|
|
|
|
*
|
|
|
|
* dram counter: consumption of the dram domain (servers only)
|
|
|
|
* event: rapl_energy_dram
|
|
|
|
* perf code: 0x3
|
|
|
|
*
|
2014-01-08 17:15:53 +07:00
|
|
|
* dram counter: consumption of the builtin-gpu domain (client only)
|
|
|
|
* event: rapl_energy_gpu
|
|
|
|
* perf code: 0x4
|
|
|
|
*
|
2013-11-12 23:58:50 +07:00
|
|
|
* We manage those counters as free running (read-only). They may be
|
|
|
|
* use simultaneously by other tools, such as turbostat.
|
|
|
|
*
|
|
|
|
* The events only support system-wide mode counting. There is no
|
|
|
|
* sampling support because it does not make sense and is not
|
|
|
|
* supported by the RAPL hardware.
|
|
|
|
*
|
|
|
|
* Because we want to avoid floating-point operations in the kernel,
|
|
|
|
* the events are all reported in fixed point arithmetic (32.32).
|
|
|
|
* Tools must adjust the counts to convert them to Watts using
|
|
|
|
* the duration of the measurement. Tools may use a function such as
|
|
|
|
* ldexp(raw_count, -32);
|
|
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/perf_event.h>
|
|
|
|
#include <asm/cpu_device_id.h>
|
2016-02-10 16:55:23 +07:00
|
|
|
#include "../perf_event.h"
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* RAPL energy status counters
|
|
|
|
*/
|
|
|
|
#define RAPL_IDX_PP0_NRG_STAT 0 /* all cores */
|
|
|
|
#define INTEL_RAPL_PP0 0x1 /* pseudo-encoding */
|
|
|
|
#define RAPL_IDX_PKG_NRG_STAT 1 /* entire package */
|
|
|
|
#define INTEL_RAPL_PKG 0x2 /* pseudo-encoding */
|
|
|
|
#define RAPL_IDX_RAM_NRG_STAT 2 /* DRAM */
|
|
|
|
#define INTEL_RAPL_RAM 0x3 /* pseudo-encoding */
|
2014-04-02 11:49:55 +07:00
|
|
|
#define RAPL_IDX_PP1_NRG_STAT 3 /* gpu */
|
2014-01-08 17:15:53 +07:00
|
|
|
#define INTEL_RAPL_PP1 0x4 /* pseudo-encoding */
|
2013-11-12 23:58:50 +07:00
|
|
|
|
2015-03-27 04:28:45 +07:00
|
|
|
#define NR_RAPL_DOMAINS 0x4
|
2015-12-01 00:48:42 +07:00
|
|
|
static const char *const rapl_domain_names[NR_RAPL_DOMAINS] __initconst = {
|
2015-03-27 04:28:45 +07:00
|
|
|
"pp0-core",
|
|
|
|
"package",
|
|
|
|
"dram",
|
|
|
|
"pp1-gpu",
|
|
|
|
};
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
/* Clients have PP0, PKG */
|
|
|
|
#define RAPL_IDX_CLN (1<<RAPL_IDX_PP0_NRG_STAT|\
|
2014-01-08 17:15:53 +07:00
|
|
|
1<<RAPL_IDX_PKG_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_PP1_NRG_STAT)
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
/* Servers have PP0, PKG, RAM */
|
|
|
|
#define RAPL_IDX_SRV (1<<RAPL_IDX_PP0_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_PKG_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_RAM_NRG_STAT)
|
|
|
|
|
2014-04-02 11:49:55 +07:00
|
|
|
/* Servers have PP0, PKG, RAM, PP1 */
|
|
|
|
#define RAPL_IDX_HSW (1<<RAPL_IDX_PP0_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_PKG_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_RAM_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_PP1_NRG_STAT)
|
|
|
|
|
2015-05-27 01:47:39 +07:00
|
|
|
/* Knights Landing has PKG, RAM */
|
|
|
|
#define RAPL_IDX_KNL (1<<RAPL_IDX_PKG_NRG_STAT|\
|
|
|
|
1<<RAPL_IDX_RAM_NRG_STAT)
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
/*
|
|
|
|
* event code: LSB 8 bits, passed in attr->config
|
|
|
|
* any other bit is reserved
|
|
|
|
*/
|
|
|
|
#define RAPL_EVENT_MASK 0xFFULL
|
|
|
|
|
|
|
|
#define DEFINE_RAPL_FORMAT_ATTR(_var, _name, _format) \
|
|
|
|
static ssize_t __rapl_##_var##_show(struct kobject *kobj, \
|
|
|
|
struct kobj_attribute *attr, \
|
|
|
|
char *page) \
|
|
|
|
{ \
|
|
|
|
BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
|
|
|
|
return sprintf(page, _format "\n"); \
|
|
|
|
} \
|
|
|
|
static struct kobj_attribute format_attr_##_var = \
|
|
|
|
__ATTR(_name, 0444, __rapl_##_var##_show, NULL)
|
|
|
|
|
|
|
|
#define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */
|
|
|
|
|
2015-12-04 17:07:41 +07:00
|
|
|
#define RAPL_EVENT_ATTR_STR(_name, v, str) \
|
|
|
|
static struct perf_pmu_events_attr event_attr_##v = { \
|
|
|
|
.attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \
|
|
|
|
.id = 0, \
|
|
|
|
.event_str = str, \
|
2015-01-14 05:59:53 +07:00
|
|
|
};
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
struct rapl_pmu {
|
|
|
|
spinlock_t lock;
|
|
|
|
int n_active; /* number of active events */
|
|
|
|
struct list_head active_list;
|
|
|
|
struct pmu *pmu; /* pointer to rapl_pmu_class */
|
2013-11-12 23:58:51 +07:00
|
|
|
ktime_t timer_interval; /* in ktime_t unit */
|
|
|
|
struct hrtimer hrtimer;
|
2013-11-12 23:58:50 +07:00
|
|
|
};
|
|
|
|
|
2015-03-27 04:28:45 +07:00
|
|
|
static int rapl_hw_unit[NR_RAPL_DOMAINS] __read_mostly; /* 1/2^hw_unit Joule */
|
2013-11-12 23:58:50 +07:00
|
|
|
static struct pmu rapl_pmu_class;
|
|
|
|
static cpumask_t rapl_cpu_mask;
|
|
|
|
static int rapl_cntr_mask;
|
|
|
|
|
|
|
|
static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu);
|
|
|
|
static DEFINE_PER_CPU(struct rapl_pmu *, rapl_pmu_to_free);
|
|
|
|
|
|
|
|
static inline u64 rapl_read_counter(struct perf_event *event)
|
|
|
|
{
|
|
|
|
u64 raw;
|
|
|
|
rdmsrl(event->hw.event_base, raw);
|
|
|
|
return raw;
|
|
|
|
}
|
|
|
|
|
2015-03-27 04:28:45 +07:00
|
|
|
static inline u64 rapl_scale(u64 v, int cfg)
|
2013-11-12 23:58:50 +07:00
|
|
|
{
|
2015-03-27 04:28:45 +07:00
|
|
|
if (cfg > NR_RAPL_DOMAINS) {
|
|
|
|
pr_warn("invalid domain %d, failed to scale data\n", cfg);
|
|
|
|
return v;
|
|
|
|
}
|
2013-11-12 23:58:50 +07:00
|
|
|
/*
|
|
|
|
* scale delta to smallest unit (1/2^32)
|
|
|
|
* users must then scale back: count * 1/(1e9*2^32) to get Joules
|
|
|
|
* or use ldexp(count, -32).
|
|
|
|
* Watts = Joules/Time delta
|
|
|
|
*/
|
2015-03-27 04:28:45 +07:00
|
|
|
return v << (32 - rapl_hw_unit[cfg - 1]);
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static u64 rapl_event_update(struct perf_event *event)
|
|
|
|
{
|
|
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
u64 prev_raw_count, new_raw_count;
|
|
|
|
s64 delta, sdelta;
|
|
|
|
int shift = RAPL_CNTR_WIDTH;
|
|
|
|
|
|
|
|
again:
|
|
|
|
prev_raw_count = local64_read(&hwc->prev_count);
|
|
|
|
rdmsrl(event->hw.event_base, new_raw_count);
|
|
|
|
|
|
|
|
if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
|
|
|
|
new_raw_count) != prev_raw_count) {
|
|
|
|
cpu_relax();
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we have the new raw value and have updated the prev
|
|
|
|
* timestamp already. We can now calculate the elapsed delta
|
|
|
|
* (event-)time and add that to the generic event.
|
|
|
|
*
|
|
|
|
* Careful, not all hw sign-extends above the physical width
|
|
|
|
* of the count.
|
|
|
|
*/
|
|
|
|
delta = (new_raw_count << shift) - (prev_raw_count << shift);
|
|
|
|
delta >>= shift;
|
|
|
|
|
2015-03-27 04:28:45 +07:00
|
|
|
sdelta = rapl_scale(delta, event->hw.config);
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
local64_add(sdelta, &event->count);
|
|
|
|
|
|
|
|
return new_raw_count;
|
|
|
|
}
|
|
|
|
|
2013-11-12 23:58:51 +07:00
|
|
|
static void rapl_start_hrtimer(struct rapl_pmu *pmu)
|
|
|
|
{
|
2015-04-15 04:09:00 +07:00
|
|
|
hrtimer_start(&pmu->hrtimer, pmu->timer_interval,
|
|
|
|
HRTIMER_MODE_REL_PINNED);
|
2013-11-12 23:58:51 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_stop_hrtimer(struct rapl_pmu *pmu)
|
|
|
|
{
|
|
|
|
hrtimer_cancel(&pmu->hrtimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer)
|
|
|
|
{
|
x86: Replace __get_cpu_var uses
__get_cpu_var() is used for multiple purposes in the kernel source. One of
them is address calculation via the form &__get_cpu_var(x). This calculates
the address for the instance of the percpu variable of the current processor
based on an offset.
Other use cases are for storing and retrieving data from the current
processors percpu area. __get_cpu_var() can be used as an lvalue when
writing data or on the right side of an assignment.
__get_cpu_var() is defined as :
#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
__get_cpu_var() always only does an address determination. However, store
and retrieve operations could use a segment prefix (or global register on
other platforms) to avoid the address calculation.
this_cpu_write() and this_cpu_read() can directly take an offset into a
percpu area and use optimized assembly code to read and write per cpu
variables.
This patch converts __get_cpu_var into either an explicit address
calculation using this_cpu_ptr() or into a use of this_cpu operations that
use the offset. Thereby address calculations are avoided and less registers
are used when code is generated.
Transformations done to __get_cpu_var()
1. Determine the address of the percpu instance of the current processor.
DEFINE_PER_CPU(int, y);
int *x = &__get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(&y);
2. Same as #1 but this time an array structure is involved.
DEFINE_PER_CPU(int, y[20]);
int *x = __get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(y);
3. Retrieve the content of the current processors instance of a per cpu
variable.
DEFINE_PER_CPU(int, y);
int x = __get_cpu_var(y)
Converts to
int x = __this_cpu_read(y);
4. Retrieve the content of a percpu struct
DEFINE_PER_CPU(struct mystruct, y);
struct mystruct x = __get_cpu_var(y);
Converts to
memcpy(&x, this_cpu_ptr(&y), sizeof(x));
5. Assignment to a per cpu variable
DEFINE_PER_CPU(int, y)
__get_cpu_var(y) = x;
Converts to
__this_cpu_write(y, x);
6. Increment/Decrement etc of a per cpu variable
DEFINE_PER_CPU(int, y);
__get_cpu_var(y)++
Converts to
__this_cpu_inc(y)
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2014-08-18 00:30:40 +07:00
|
|
|
struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu);
|
2013-11-12 23:58:51 +07:00
|
|
|
struct perf_event *event;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!pmu->n_active)
|
|
|
|
return HRTIMER_NORESTART;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&pmu->lock, flags);
|
|
|
|
|
|
|
|
list_for_each_entry(event, &pmu->active_list, active_entry) {
|
|
|
|
rapl_event_update(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&pmu->lock, flags);
|
|
|
|
|
|
|
|
hrtimer_forward_now(hrtimer, pmu->timer_interval);
|
|
|
|
|
|
|
|
return HRTIMER_RESTART;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_hrtimer_init(struct rapl_pmu *pmu)
|
|
|
|
{
|
|
|
|
struct hrtimer *hr = &pmu->hrtimer;
|
|
|
|
|
|
|
|
hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
|
|
hr->function = rapl_hrtimer_handle;
|
|
|
|
}
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
static void __rapl_pmu_event_start(struct rapl_pmu *pmu,
|
|
|
|
struct perf_event *event)
|
|
|
|
{
|
|
|
|
if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
event->hw.state = 0;
|
|
|
|
|
|
|
|
list_add_tail(&event->active_entry, &pmu->active_list);
|
|
|
|
|
|
|
|
local64_set(&event->hw.prev_count, rapl_read_counter(event));
|
|
|
|
|
|
|
|
pmu->n_active++;
|
2013-11-12 23:58:51 +07:00
|
|
|
if (pmu->n_active == 1)
|
|
|
|
rapl_start_hrtimer(pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_pmu_event_start(struct perf_event *event, int mode)
|
|
|
|
{
|
x86: Replace __get_cpu_var uses
__get_cpu_var() is used for multiple purposes in the kernel source. One of
them is address calculation via the form &__get_cpu_var(x). This calculates
the address for the instance of the percpu variable of the current processor
based on an offset.
Other use cases are for storing and retrieving data from the current
processors percpu area. __get_cpu_var() can be used as an lvalue when
writing data or on the right side of an assignment.
__get_cpu_var() is defined as :
#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
__get_cpu_var() always only does an address determination. However, store
and retrieve operations could use a segment prefix (or global register on
other platforms) to avoid the address calculation.
this_cpu_write() and this_cpu_read() can directly take an offset into a
percpu area and use optimized assembly code to read and write per cpu
variables.
This patch converts __get_cpu_var into either an explicit address
calculation using this_cpu_ptr() or into a use of this_cpu operations that
use the offset. Thereby address calculations are avoided and less registers
are used when code is generated.
Transformations done to __get_cpu_var()
1. Determine the address of the percpu instance of the current processor.
DEFINE_PER_CPU(int, y);
int *x = &__get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(&y);
2. Same as #1 but this time an array structure is involved.
DEFINE_PER_CPU(int, y[20]);
int *x = __get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(y);
3. Retrieve the content of the current processors instance of a per cpu
variable.
DEFINE_PER_CPU(int, y);
int x = __get_cpu_var(y)
Converts to
int x = __this_cpu_read(y);
4. Retrieve the content of a percpu struct
DEFINE_PER_CPU(struct mystruct, y);
struct mystruct x = __get_cpu_var(y);
Converts to
memcpy(&x, this_cpu_ptr(&y), sizeof(x));
5. Assignment to a per cpu variable
DEFINE_PER_CPU(int, y)
__get_cpu_var(y) = x;
Converts to
__this_cpu_write(y, x);
6. Increment/Decrement etc of a per cpu variable
DEFINE_PER_CPU(int, y);
__get_cpu_var(y)++
Converts to
__this_cpu_inc(y)
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2014-08-18 00:30:40 +07:00
|
|
|
struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&pmu->lock, flags);
|
|
|
|
__rapl_pmu_event_start(pmu, event);
|
|
|
|
spin_unlock_irqrestore(&pmu->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_pmu_event_stop(struct perf_event *event, int mode)
|
|
|
|
{
|
x86: Replace __get_cpu_var uses
__get_cpu_var() is used for multiple purposes in the kernel source. One of
them is address calculation via the form &__get_cpu_var(x). This calculates
the address for the instance of the percpu variable of the current processor
based on an offset.
Other use cases are for storing and retrieving data from the current
processors percpu area. __get_cpu_var() can be used as an lvalue when
writing data or on the right side of an assignment.
__get_cpu_var() is defined as :
#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
__get_cpu_var() always only does an address determination. However, store
and retrieve operations could use a segment prefix (or global register on
other platforms) to avoid the address calculation.
this_cpu_write() and this_cpu_read() can directly take an offset into a
percpu area and use optimized assembly code to read and write per cpu
variables.
This patch converts __get_cpu_var into either an explicit address
calculation using this_cpu_ptr() or into a use of this_cpu operations that
use the offset. Thereby address calculations are avoided and less registers
are used when code is generated.
Transformations done to __get_cpu_var()
1. Determine the address of the percpu instance of the current processor.
DEFINE_PER_CPU(int, y);
int *x = &__get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(&y);
2. Same as #1 but this time an array structure is involved.
DEFINE_PER_CPU(int, y[20]);
int *x = __get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(y);
3. Retrieve the content of the current processors instance of a per cpu
variable.
DEFINE_PER_CPU(int, y);
int x = __get_cpu_var(y)
Converts to
int x = __this_cpu_read(y);
4. Retrieve the content of a percpu struct
DEFINE_PER_CPU(struct mystruct, y);
struct mystruct x = __get_cpu_var(y);
Converts to
memcpy(&x, this_cpu_ptr(&y), sizeof(x));
5. Assignment to a per cpu variable
DEFINE_PER_CPU(int, y)
__get_cpu_var(y) = x;
Converts to
__this_cpu_write(y, x);
6. Increment/Decrement etc of a per cpu variable
DEFINE_PER_CPU(int, y);
__get_cpu_var(y)++
Converts to
__this_cpu_inc(y)
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2014-08-18 00:30:40 +07:00
|
|
|
struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&pmu->lock, flags);
|
|
|
|
|
|
|
|
/* mark event as deactivated and stopped */
|
|
|
|
if (!(hwc->state & PERF_HES_STOPPED)) {
|
|
|
|
WARN_ON_ONCE(pmu->n_active <= 0);
|
|
|
|
pmu->n_active--;
|
2013-11-12 23:58:51 +07:00
|
|
|
if (pmu->n_active == 0)
|
|
|
|
rapl_stop_hrtimer(pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
list_del(&event->active_entry);
|
|
|
|
|
|
|
|
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
|
|
|
|
hwc->state |= PERF_HES_STOPPED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if update of sw counter is necessary */
|
|
|
|
if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
|
|
|
/*
|
|
|
|
* Drain the remaining delta count out of a event
|
|
|
|
* that we are disabling:
|
|
|
|
*/
|
|
|
|
rapl_event_update(event);
|
|
|
|
hwc->state |= PERF_HES_UPTODATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&pmu->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_pmu_event_add(struct perf_event *event, int mode)
|
|
|
|
{
|
x86: Replace __get_cpu_var uses
__get_cpu_var() is used for multiple purposes in the kernel source. One of
them is address calculation via the form &__get_cpu_var(x). This calculates
the address for the instance of the percpu variable of the current processor
based on an offset.
Other use cases are for storing and retrieving data from the current
processors percpu area. __get_cpu_var() can be used as an lvalue when
writing data or on the right side of an assignment.
__get_cpu_var() is defined as :
#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
__get_cpu_var() always only does an address determination. However, store
and retrieve operations could use a segment prefix (or global register on
other platforms) to avoid the address calculation.
this_cpu_write() and this_cpu_read() can directly take an offset into a
percpu area and use optimized assembly code to read and write per cpu
variables.
This patch converts __get_cpu_var into either an explicit address
calculation using this_cpu_ptr() or into a use of this_cpu operations that
use the offset. Thereby address calculations are avoided and less registers
are used when code is generated.
Transformations done to __get_cpu_var()
1. Determine the address of the percpu instance of the current processor.
DEFINE_PER_CPU(int, y);
int *x = &__get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(&y);
2. Same as #1 but this time an array structure is involved.
DEFINE_PER_CPU(int, y[20]);
int *x = __get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(y);
3. Retrieve the content of the current processors instance of a per cpu
variable.
DEFINE_PER_CPU(int, y);
int x = __get_cpu_var(y)
Converts to
int x = __this_cpu_read(y);
4. Retrieve the content of a percpu struct
DEFINE_PER_CPU(struct mystruct, y);
struct mystruct x = __get_cpu_var(y);
Converts to
memcpy(&x, this_cpu_ptr(&y), sizeof(x));
5. Assignment to a per cpu variable
DEFINE_PER_CPU(int, y)
__get_cpu_var(y) = x;
Converts to
__this_cpu_write(y, x);
6. Increment/Decrement etc of a per cpu variable
DEFINE_PER_CPU(int, y);
__get_cpu_var(y)++
Converts to
__this_cpu_inc(y)
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2014-08-18 00:30:40 +07:00
|
|
|
struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&pmu->lock, flags);
|
|
|
|
|
|
|
|
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
|
|
|
|
|
|
|
|
if (mode & PERF_EF_START)
|
|
|
|
__rapl_pmu_event_start(pmu, event);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&pmu->lock, flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_pmu_event_del(struct perf_event *event, int flags)
|
|
|
|
{
|
|
|
|
rapl_pmu_event_stop(event, PERF_EF_UPDATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_pmu_event_init(struct perf_event *event)
|
|
|
|
{
|
|
|
|
u64 cfg = event->attr.config & RAPL_EVENT_MASK;
|
|
|
|
int bit, msr, ret = 0;
|
|
|
|
|
|
|
|
/* only look at RAPL events */
|
|
|
|
if (event->attr.type != rapl_pmu_class.type)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
/* check only supported bits are set */
|
|
|
|
if (event->attr.config & ~RAPL_EVENT_MASK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check event is known (determines counter)
|
|
|
|
*/
|
|
|
|
switch (cfg) {
|
|
|
|
case INTEL_RAPL_PP0:
|
|
|
|
bit = RAPL_IDX_PP0_NRG_STAT;
|
|
|
|
msr = MSR_PP0_ENERGY_STATUS;
|
|
|
|
break;
|
|
|
|
case INTEL_RAPL_PKG:
|
|
|
|
bit = RAPL_IDX_PKG_NRG_STAT;
|
|
|
|
msr = MSR_PKG_ENERGY_STATUS;
|
|
|
|
break;
|
|
|
|
case INTEL_RAPL_RAM:
|
|
|
|
bit = RAPL_IDX_RAM_NRG_STAT;
|
|
|
|
msr = MSR_DRAM_ENERGY_STATUS;
|
|
|
|
break;
|
2014-01-08 17:15:53 +07:00
|
|
|
case INTEL_RAPL_PP1:
|
|
|
|
bit = RAPL_IDX_PP1_NRG_STAT;
|
|
|
|
msr = MSR_PP1_ENERGY_STATUS;
|
|
|
|
break;
|
2013-11-12 23:58:50 +07:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* check event supported */
|
|
|
|
if (!(rapl_cntr_mask & (1 << bit)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* unsupported modes and filters */
|
|
|
|
if (event->attr.exclude_user ||
|
|
|
|
event->attr.exclude_kernel ||
|
|
|
|
event->attr.exclude_hv ||
|
|
|
|
event->attr.exclude_idle ||
|
|
|
|
event->attr.exclude_host ||
|
|
|
|
event->attr.exclude_guest ||
|
|
|
|
event->attr.sample_period) /* no sampling */
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* must be done before validate_group */
|
|
|
|
event->hw.event_base = msr;
|
|
|
|
event->hw.config = cfg;
|
|
|
|
event->hw.idx = bit;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_pmu_event_read(struct perf_event *event)
|
|
|
|
{
|
|
|
|
rapl_event_update(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t rapl_get_attr_cpumask(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
2014-09-30 20:48:22 +07:00
|
|
|
return cpumap_print_to_pagebuf(true, buf, &rapl_cpu_mask);
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL);
|
|
|
|
|
|
|
|
static struct attribute *rapl_pmu_attrs[] = {
|
|
|
|
&dev_attr_cpumask.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute_group rapl_pmu_attr_group = {
|
|
|
|
.attrs = rapl_pmu_attrs,
|
|
|
|
};
|
|
|
|
|
2015-01-14 05:59:53 +07:00
|
|
|
RAPL_EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-pkg , rapl_pkg, "event=0x02");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-ram , rapl_ram, "event=0x03");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-gpu , rapl_gpu, "event=0x04");
|
2013-11-12 23:58:50 +07:00
|
|
|
|
2015-01-14 05:59:53 +07:00
|
|
|
RAPL_EVENT_ATTR_STR(energy-cores.unit, rapl_cores_unit, "Joules");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-pkg.unit , rapl_pkg_unit, "Joules");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-ram.unit , rapl_ram_unit, "Joules");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-gpu.unit , rapl_gpu_unit, "Joules");
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we compute in 0.23 nJ increments regardless of MSR
|
|
|
|
*/
|
2015-01-14 05:59:53 +07:00
|
|
|
RAPL_EVENT_ATTR_STR(energy-cores.scale, rapl_cores_scale, "2.3283064365386962890625e-10");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-pkg.scale, rapl_pkg_scale, "2.3283064365386962890625e-10");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-ram.scale, rapl_ram_scale, "2.3283064365386962890625e-10");
|
|
|
|
RAPL_EVENT_ATTR_STR(energy-gpu.scale, rapl_gpu_scale, "2.3283064365386962890625e-10");
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
static struct attribute *rapl_events_srv_attr[] = {
|
|
|
|
EVENT_PTR(rapl_cores),
|
|
|
|
EVENT_PTR(rapl_pkg),
|
|
|
|
EVENT_PTR(rapl_ram),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_unit),
|
|
|
|
EVENT_PTR(rapl_pkg_unit),
|
|
|
|
EVENT_PTR(rapl_ram_unit),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_scale),
|
|
|
|
EVENT_PTR(rapl_pkg_scale),
|
|
|
|
EVENT_PTR(rapl_ram_scale),
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute *rapl_events_cln_attr[] = {
|
|
|
|
EVENT_PTR(rapl_cores),
|
|
|
|
EVENT_PTR(rapl_pkg),
|
2014-01-08 17:15:53 +07:00
|
|
|
EVENT_PTR(rapl_gpu),
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_unit),
|
|
|
|
EVENT_PTR(rapl_pkg_unit),
|
2014-01-08 17:15:53 +07:00
|
|
|
EVENT_PTR(rapl_gpu_unit),
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_scale),
|
|
|
|
EVENT_PTR(rapl_pkg_scale),
|
2014-01-08 17:15:53 +07:00
|
|
|
EVENT_PTR(rapl_gpu_scale),
|
2013-11-12 23:58:50 +07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2014-04-02 11:49:55 +07:00
|
|
|
static struct attribute *rapl_events_hsw_attr[] = {
|
|
|
|
EVENT_PTR(rapl_cores),
|
|
|
|
EVENT_PTR(rapl_pkg),
|
|
|
|
EVENT_PTR(rapl_gpu),
|
|
|
|
EVENT_PTR(rapl_ram),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_unit),
|
|
|
|
EVENT_PTR(rapl_pkg_unit),
|
|
|
|
EVENT_PTR(rapl_gpu_unit),
|
|
|
|
EVENT_PTR(rapl_ram_unit),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_cores_scale),
|
|
|
|
EVENT_PTR(rapl_pkg_scale),
|
|
|
|
EVENT_PTR(rapl_gpu_scale),
|
|
|
|
EVENT_PTR(rapl_ram_scale),
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2015-05-27 01:47:39 +07:00
|
|
|
static struct attribute *rapl_events_knl_attr[] = {
|
|
|
|
EVENT_PTR(rapl_pkg),
|
|
|
|
EVENT_PTR(rapl_ram),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_pkg_unit),
|
|
|
|
EVENT_PTR(rapl_ram_unit),
|
|
|
|
|
|
|
|
EVENT_PTR(rapl_pkg_scale),
|
|
|
|
EVENT_PTR(rapl_ram_scale),
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
static struct attribute_group rapl_pmu_events_group = {
|
|
|
|
.name = "events",
|
|
|
|
.attrs = NULL, /* patched at runtime */
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_RAPL_FORMAT_ATTR(event, event, "config:0-7");
|
|
|
|
static struct attribute *rapl_formats_attr[] = {
|
|
|
|
&format_attr_event.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute_group rapl_pmu_format_group = {
|
|
|
|
.name = "format",
|
|
|
|
.attrs = rapl_formats_attr,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct attribute_group *rapl_attr_groups[] = {
|
|
|
|
&rapl_pmu_attr_group,
|
|
|
|
&rapl_pmu_format_group,
|
|
|
|
&rapl_pmu_events_group,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct pmu rapl_pmu_class = {
|
|
|
|
.attr_groups = rapl_attr_groups,
|
|
|
|
.task_ctx_nr = perf_invalid_context, /* system-wide only */
|
|
|
|
.event_init = rapl_pmu_event_init,
|
|
|
|
.add = rapl_pmu_event_add, /* must have */
|
|
|
|
.del = rapl_pmu_event_del, /* must have */
|
|
|
|
.start = rapl_pmu_event_start,
|
|
|
|
.stop = rapl_pmu_event_stop,
|
|
|
|
.read = rapl_pmu_event_read,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void rapl_cpu_exit(int cpu)
|
|
|
|
{
|
|
|
|
struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
|
|
|
|
int i, phys_id = topology_physical_package_id(cpu);
|
|
|
|
int target = -1;
|
|
|
|
|
|
|
|
/* find a new cpu on same package */
|
|
|
|
for_each_online_cpu(i) {
|
|
|
|
if (i == cpu)
|
|
|
|
continue;
|
|
|
|
if (phys_id == topology_physical_package_id(i)) {
|
|
|
|
target = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* clear cpu from cpumask
|
|
|
|
* if was set in cpumask and still some cpu on package,
|
|
|
|
* then move to new cpu
|
|
|
|
*/
|
|
|
|
if (cpumask_test_and_clear_cpu(cpu, &rapl_cpu_mask) && target >= 0)
|
|
|
|
cpumask_set_cpu(target, &rapl_cpu_mask);
|
|
|
|
|
|
|
|
WARN_ON(cpumask_empty(&rapl_cpu_mask));
|
|
|
|
/*
|
|
|
|
* migrate events and context to new cpu
|
|
|
|
*/
|
|
|
|
if (target >= 0)
|
|
|
|
perf_pmu_migrate_context(pmu->pmu, cpu, target);
|
2013-11-12 23:58:51 +07:00
|
|
|
|
|
|
|
/* cancel overflow polling timer for CPU */
|
|
|
|
rapl_stop_hrtimer(pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_cpu_init(int cpu)
|
|
|
|
{
|
|
|
|
int i, phys_id = topology_physical_package_id(cpu);
|
|
|
|
|
|
|
|
/* check if phys_is is already covered */
|
|
|
|
for_each_cpu(i, &rapl_cpu_mask) {
|
|
|
|
if (phys_id == topology_physical_package_id(i))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* was not found, so add it */
|
|
|
|
cpumask_set_cpu(cpu, &rapl_cpu_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_cpu_prepare(int cpu)
|
|
|
|
{
|
|
|
|
struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
|
|
|
|
int phys_id = topology_physical_package_id(cpu);
|
2013-11-12 23:58:51 +07:00
|
|
|
u64 ms;
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
if (pmu)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (phys_id < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
|
|
|
|
if (!pmu)
|
|
|
|
return -1;
|
|
|
|
spin_lock_init(&pmu->lock);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&pmu->active_list);
|
|
|
|
|
|
|
|
pmu->pmu = &rapl_pmu_class;
|
|
|
|
|
2013-11-12 23:58:51 +07:00
|
|
|
/*
|
|
|
|
* use reference of 200W for scaling the timeout
|
|
|
|
* to avoid missing counter overflows.
|
|
|
|
* 200W = 200 Joules/sec
|
|
|
|
* divide interval by 2 to avoid lockstep (2 * 100)
|
|
|
|
* if hw unit is 32, then we use 2 ms 1/200/2
|
|
|
|
*/
|
2015-03-27 04:28:45 +07:00
|
|
|
if (rapl_hw_unit[0] < 32)
|
|
|
|
ms = (1000 / (2 * 100)) * (1ULL << (32 - rapl_hw_unit[0] - 1));
|
2013-11-12 23:58:51 +07:00
|
|
|
else
|
|
|
|
ms = 2;
|
|
|
|
|
|
|
|
pmu->timer_interval = ms_to_ktime(ms);
|
|
|
|
|
|
|
|
rapl_hrtimer_init(pmu);
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
/* set RAPL pmu for this cpu for now */
|
|
|
|
per_cpu(rapl_pmu, cpu) = pmu;
|
|
|
|
per_cpu(rapl_pmu_to_free, cpu) = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rapl_cpu_kfree(int cpu)
|
|
|
|
{
|
|
|
|
struct rapl_pmu *pmu = per_cpu(rapl_pmu_to_free, cpu);
|
|
|
|
|
|
|
|
kfree(pmu);
|
|
|
|
|
|
|
|
per_cpu(rapl_pmu_to_free, cpu) = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_cpu_dying(int cpu)
|
|
|
|
{
|
|
|
|
struct rapl_pmu *pmu = per_cpu(rapl_pmu, cpu);
|
|
|
|
|
|
|
|
if (!pmu)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
per_cpu(rapl_pmu, cpu) = NULL;
|
|
|
|
|
|
|
|
per_cpu(rapl_pmu_to_free, cpu) = pmu;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_cpu_notifier(struct notifier_block *self,
|
|
|
|
unsigned long action, void *hcpu)
|
|
|
|
{
|
|
|
|
unsigned int cpu = (long)hcpu;
|
|
|
|
|
|
|
|
switch (action & ~CPU_TASKS_FROZEN) {
|
|
|
|
case CPU_UP_PREPARE:
|
|
|
|
rapl_cpu_prepare(cpu);
|
|
|
|
break;
|
|
|
|
case CPU_STARTING:
|
|
|
|
rapl_cpu_init(cpu);
|
|
|
|
break;
|
|
|
|
case CPU_UP_CANCELED:
|
|
|
|
case CPU_DYING:
|
|
|
|
rapl_cpu_dying(cpu);
|
|
|
|
break;
|
|
|
|
case CPU_ONLINE:
|
|
|
|
case CPU_DEAD:
|
|
|
|
rapl_cpu_kfree(cpu);
|
|
|
|
break;
|
|
|
|
case CPU_DOWN_PREPARE:
|
|
|
|
rapl_cpu_exit(cpu);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
2016-02-23 05:19:22 +07:00
|
|
|
static __init void rapl_hsw_server_quirk(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* DRAM domain on HSW server has fixed energy unit which can be
|
|
|
|
* different than the unit from power unit MSR.
|
|
|
|
* "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, V2
|
|
|
|
* of 2. Datasheet, September 2014, Reference Number: 330784-001 "
|
|
|
|
*/
|
|
|
|
rapl_hw_unit[RAPL_IDX_RAM_NRG_STAT] = 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rapl_check_hw_unit(void (*quirk)(void))
|
2015-03-27 04:28:45 +07:00
|
|
|
{
|
|
|
|
u64 msr_rapl_power_unit_bits;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* protect rdmsrl() to handle virtualization */
|
|
|
|
if (rdmsrl_safe(MSR_RAPL_POWER_UNIT, &msr_rapl_power_unit_bits))
|
|
|
|
return -1;
|
|
|
|
for (i = 0; i < NR_RAPL_DOMAINS; i++)
|
|
|
|
rapl_hw_unit[i] = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
|
|
|
|
|
2016-02-23 05:19:22 +07:00
|
|
|
/* Apply cpu model quirk */
|
|
|
|
if (quirk)
|
|
|
|
quirk();
|
2015-03-27 04:28:45 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-23 05:19:21 +07:00
|
|
|
static void __init cleanup_rapl_pmus(void)
|
|
|
|
{
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_online_cpu(cpu)
|
|
|
|
kfree(per_cpu(rapl_pmu, cpu));
|
|
|
|
}
|
|
|
|
|
2013-11-12 23:58:50 +07:00
|
|
|
static const struct x86_cpu_id rapl_cpu_match[] = {
|
|
|
|
[0] = { .vendor = X86_VENDOR_INTEL, .family = 6 },
|
|
|
|
[1] = {},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init rapl_pmu_init(void)
|
|
|
|
{
|
2016-02-23 05:19:22 +07:00
|
|
|
void (*quirk)(void) = NULL;
|
2013-11-12 23:58:50 +07:00
|
|
|
struct rapl_pmu *pmu;
|
|
|
|
int cpu, ret;
|
2015-03-27 04:28:45 +07:00
|
|
|
int i;
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* check for Intel processor family 6
|
|
|
|
*/
|
|
|
|
if (!x86_match_cpu(rapl_cpu_match))
|
2016-02-23 05:19:21 +07:00
|
|
|
return -ENODEV;
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
/* check supported CPU */
|
|
|
|
switch (boot_cpu_data.x86_model) {
|
|
|
|
case 42: /* Sandy Bridge */
|
|
|
|
case 58: /* Ivy Bridge */
|
|
|
|
rapl_cntr_mask = RAPL_IDX_CLN;
|
|
|
|
rapl_pmu_events_group.attrs = rapl_events_cln_attr;
|
|
|
|
break;
|
2015-03-27 04:28:45 +07:00
|
|
|
case 63: /* Haswell-Server */
|
2016-02-23 05:19:22 +07:00
|
|
|
quirk = rapl_hsw_server_quirk;
|
2015-03-27 04:28:45 +07:00
|
|
|
rapl_cntr_mask = RAPL_IDX_SRV;
|
|
|
|
rapl_pmu_events_group.attrs = rapl_events_srv_attr;
|
|
|
|
break;
|
2014-04-02 11:49:55 +07:00
|
|
|
case 60: /* Haswell */
|
|
|
|
case 69: /* Haswell-Celeron */
|
2015-04-23 14:07:09 +07:00
|
|
|
case 61: /* Broadwell */
|
2014-04-02 11:49:55 +07:00
|
|
|
rapl_cntr_mask = RAPL_IDX_HSW;
|
|
|
|
rapl_pmu_events_group.attrs = rapl_events_hsw_attr;
|
|
|
|
break;
|
2013-11-12 23:58:50 +07:00
|
|
|
case 45: /* Sandy Bridge-EP */
|
|
|
|
case 62: /* IvyTown */
|
|
|
|
rapl_cntr_mask = RAPL_IDX_SRV;
|
|
|
|
rapl_pmu_events_group.attrs = rapl_events_srv_attr;
|
|
|
|
break;
|
2015-05-27 01:47:39 +07:00
|
|
|
case 87: /* Knights Landing */
|
2016-02-23 05:19:22 +07:00
|
|
|
quirk = rapl_hsw_server_quirk;
|
2015-05-27 01:47:39 +07:00
|
|
|
rapl_cntr_mask = RAPL_IDX_KNL;
|
|
|
|
rapl_pmu_events_group.attrs = rapl_events_knl_attr;
|
2016-02-23 05:19:20 +07:00
|
|
|
break;
|
2013-11-12 23:58:50 +07:00
|
|
|
default:
|
|
|
|
/* unsupported */
|
2016-02-23 05:19:21 +07:00
|
|
|
return -ENODEV;
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
2016-02-23 05:19:21 +07:00
|
|
|
|
2016-02-23 05:19:22 +07:00
|
|
|
ret = rapl_check_hw_unit(quirk);
|
2015-03-27 04:28:45 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2014-03-11 03:38:09 +07:00
|
|
|
|
|
|
|
cpu_notifier_register_begin();
|
2013-11-12 23:58:50 +07:00
|
|
|
|
|
|
|
for_each_online_cpu(cpu) {
|
2014-03-14 02:36:26 +07:00
|
|
|
ret = rapl_cpu_prepare(cpu);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2013-11-12 23:58:50 +07:00
|
|
|
rapl_cpu_init(cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = perf_pmu_register(&rapl_pmu_class, "power", -1);
|
|
|
|
if (WARN_ON(ret)) {
|
|
|
|
pr_info("RAPL PMU detected, registration failed (%d), RAPL PMU disabled\n", ret);
|
2016-02-23 05:19:21 +07:00
|
|
|
goto out;
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
|
2016-02-23 05:19:21 +07:00
|
|
|
__perf_cpu_notifier(rapl_cpu_notifier);
|
|
|
|
|
x86: Replace __get_cpu_var uses
__get_cpu_var() is used for multiple purposes in the kernel source. One of
them is address calculation via the form &__get_cpu_var(x). This calculates
the address for the instance of the percpu variable of the current processor
based on an offset.
Other use cases are for storing and retrieving data from the current
processors percpu area. __get_cpu_var() can be used as an lvalue when
writing data or on the right side of an assignment.
__get_cpu_var() is defined as :
#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
__get_cpu_var() always only does an address determination. However, store
and retrieve operations could use a segment prefix (or global register on
other platforms) to avoid the address calculation.
this_cpu_write() and this_cpu_read() can directly take an offset into a
percpu area and use optimized assembly code to read and write per cpu
variables.
This patch converts __get_cpu_var into either an explicit address
calculation using this_cpu_ptr() or into a use of this_cpu operations that
use the offset. Thereby address calculations are avoided and less registers
are used when code is generated.
Transformations done to __get_cpu_var()
1. Determine the address of the percpu instance of the current processor.
DEFINE_PER_CPU(int, y);
int *x = &__get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(&y);
2. Same as #1 but this time an array structure is involved.
DEFINE_PER_CPU(int, y[20]);
int *x = __get_cpu_var(y);
Converts to
int *x = this_cpu_ptr(y);
3. Retrieve the content of the current processors instance of a per cpu
variable.
DEFINE_PER_CPU(int, y);
int x = __get_cpu_var(y)
Converts to
int x = __this_cpu_read(y);
4. Retrieve the content of a percpu struct
DEFINE_PER_CPU(struct mystruct, y);
struct mystruct x = __get_cpu_var(y);
Converts to
memcpy(&x, this_cpu_ptr(&y), sizeof(x));
5. Assignment to a per cpu variable
DEFINE_PER_CPU(int, y)
__get_cpu_var(y) = x;
Converts to
__this_cpu_write(y, x);
6. Increment/Decrement etc of a per cpu variable
DEFINE_PER_CPU(int, y);
__get_cpu_var(y)++
Converts to
__this_cpu_inc(y)
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Christoph Lameter <cl@linux.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
2014-08-18 00:30:40 +07:00
|
|
|
pmu = __this_cpu_read(rapl_pmu);
|
2013-11-12 23:58:50 +07:00
|
|
|
|
2015-03-27 04:28:45 +07:00
|
|
|
pr_info("RAPL PMU detected,"
|
2013-11-12 23:58:50 +07:00
|
|
|
" API unit is 2^-32 Joules,"
|
2013-11-12 23:58:51 +07:00
|
|
|
" %d fixed counters"
|
|
|
|
" %llu ms ovfl timer\n",
|
|
|
|
hweight32(rapl_cntr_mask),
|
|
|
|
ktime_to_ms(pmu->timer_interval));
|
2015-03-27 04:28:45 +07:00
|
|
|
for (i = 0; i < NR_RAPL_DOMAINS; i++) {
|
|
|
|
if (rapl_cntr_mask & (1 << i)) {
|
|
|
|
pr_info("hw unit of domain %s 2^-%d Joules\n",
|
|
|
|
rapl_domain_names[i], rapl_hw_unit[i]);
|
|
|
|
}
|
|
|
|
}
|
2013-11-12 23:58:50 +07:00
|
|
|
|
2016-02-23 05:19:21 +07:00
|
|
|
cpu_notifier_register_done();
|
2013-11-12 23:58:50 +07:00
|
|
|
return 0;
|
2016-02-23 05:19:21 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
cleanup_rapl_pmus();
|
|
|
|
cpu_notifier_register_done();
|
|
|
|
return ret;
|
2013-11-12 23:58:50 +07:00
|
|
|
}
|
|
|
|
device_initcall(rapl_pmu_init);
|