727 lines
18 KiB
C
727 lines
18 KiB
C
#include <linux/list.h>
|
|
#include <linux/perf_event.h>
|
|
#include <asm/sic_regs.h>
|
|
#include <linux/nodemask.h>
|
|
#include <asm/perf_event_uncore.h>
|
|
#include <linux/slab.h>
|
|
|
|
static struct e2k_uncore *e2k_uncore_ipcc;
|
|
static struct e2k_uncore *e2k_uncore_iocc;
|
|
static struct e2k_uncore *e2k_uncore_sic;
|
|
|
|
/*
|
|
* To add new monitor to uncore perf:
|
|
*
|
|
* 1) Implement 3 register access functions which can:
|
|
* - set current events count
|
|
* - get current events count
|
|
* - set config field of monitor
|
|
*
|
|
* 2) Fill struct e2k_uncore_reg_ops with this functions
|
|
* (for example ipcc_reg_ops)
|
|
*
|
|
* 3) Fill struct e2k_uncore_event_desc with event descriptions
|
|
* (for example sic_MCM1_events)
|
|
* Be careful, values from descriptions are passed by parser
|
|
* to perf_event_open() into attr.config field
|
|
*
|
|
* 4) Fill struct attribute
|
|
* (for example e2k_sic_MCM1_events_attrs)
|
|
*
|
|
* 5) Fill struct attribute_group
|
|
* (for example e2k_sic_MCM1_attr_group)
|
|
*
|
|
* 6) Fill struct e2k_uncore with
|
|
* Main fields:
|
|
* - pmu.event_init = e2k_uncore_event_init
|
|
* - pmu.add = e2k_uncore_add
|
|
* - pmu.del = e2k_uncore_del
|
|
* - pmu.start = e2k_uncore_start
|
|
* - pmu.stop = e2k_uncore_stop
|
|
* - pmu.read = e2k_uncore_read
|
|
* - pmu.reg_ops = ~struct from 1) step~
|
|
* - pmu.attr_groups = ~struct from 5) step~
|
|
* - .name = ~name~ (is used by sysfs)
|
|
*
|
|
* Optional fields:
|
|
* You can use other fields of e2k_uncore
|
|
* (.node, .idx_at_node) as you want
|
|
* (for example: allow reg access functions write
|
|
* directly into neccessary registers).
|
|
* If you want add another fields.
|
|
*
|
|
* 7) Create array of valid events terminated with -1 and fill
|
|
* pmu.valid_events with it. It is used for error check
|
|
* (for example iocc_valid_events).
|
|
*
|
|
* 8) Pass e2k_uncore.pmu and e2k_uncore.name to perf_pmu_register()
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* (void *) is used for flexibility
|
|
*/
|
|
static void get_ipcc_str_cnt(struct e2k_uncore *uncore, void *result)
|
|
{
|
|
e2k_ipcc_str_struct_t reg;
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IPCC_STR_reg = sic_get_ipcc_str(node, idx);
|
|
|
|
*((int *)result) = reg.E2K_IPCC_STR_ecnt;
|
|
}
|
|
|
|
static void set_ipcc_str_cfg(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_ipcc_str_struct_t reg;
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IPCC_STR_reg = sic_get_ipcc_str(node, idx);
|
|
|
|
reg.E2K_IPCC_STR_ecf = *((unsigned int *)val);
|
|
|
|
sic_set_ipcc_str(node, idx, reg.E2K_IPCC_STR_reg);
|
|
}
|
|
|
|
static void set_ipcc_str_cnt(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_ipcc_str_struct_t reg;
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IPCC_STR_reg = sic_get_ipcc_str(node, idx);
|
|
|
|
reg.E2K_IPCC_STR_ecnt = *((unsigned int *)val);
|
|
|
|
sic_set_ipcc_str(node, idx, reg.E2K_IPCC_STR_reg);
|
|
}
|
|
|
|
static struct e2k_uncore_reg_ops ipcc_reg_ops = {
|
|
.get_cnt = get_ipcc_str_cnt,
|
|
.set_cfg = set_ipcc_str_cfg,
|
|
.set_cnt = set_ipcc_str_cnt,
|
|
};
|
|
|
|
static void get_iocc_str_cnt(struct e2k_uncore *uncore, void *result)
|
|
{
|
|
e2k_io_str_struct_t reg;
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IO_STR_reg = sic_get_io_str(node, idx);
|
|
|
|
*((int *)result) = reg.E2K_IO_STR_rc;
|
|
}
|
|
|
|
static void set_iocc_str_cfg(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_io_str_struct_t reg;
|
|
int event_val = *((int *)val);
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IO_STR_reg = sic_get_ipcc_str(node, idx);
|
|
reg.E2K_IO_STR_reg &= ~E2K_IO_STR_EVENT_MASK;
|
|
reg.E2K_IO_STR_reg |= event_val << E2K_IO_STR_EVENT_SHIFT;
|
|
|
|
sic_set_io_str(node, idx, reg.E2K_IO_STR_reg);
|
|
}
|
|
|
|
static void set_iocc_str_cnt(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_io_str_struct_t reg;
|
|
int node = uncore->node;
|
|
int idx = uncore->idx_at_node;
|
|
|
|
reg.E2K_IO_STR_reg = sic_get_io_str(node, idx);
|
|
reg.E2K_IO_STR_rc = *((unsigned int *)val);
|
|
|
|
sic_set_io_str(node, idx, reg.E2K_IO_STR_reg);
|
|
}
|
|
|
|
static struct e2k_uncore_reg_ops iocc_reg_ops = {
|
|
.get_cnt = get_iocc_str_cnt,
|
|
.set_cfg = set_iocc_str_cfg,
|
|
.set_cnt = set_iocc_str_cnt,
|
|
};
|
|
|
|
enum {
|
|
MCM0,
|
|
MCM1,
|
|
};
|
|
|
|
static void get_sic_str_cnt(struct e2k_uncore *uncore, void *result)
|
|
{
|
|
e2k_sic_mar_lo_struct_t mar_lo_reg;
|
|
e2k_sic_mar_hi_struct_t mar_hi_reg;
|
|
|
|
int node = uncore->node;
|
|
int monitor_id = uncore->idx_at_node;
|
|
|
|
u64 _result;
|
|
|
|
switch (monitor_id) {
|
|
case MCM0:
|
|
mar_lo_reg.E2K_SIC_MAR_LO_reg =
|
|
sic_read_node_nbsr_reg(node, SIC_sic_mar0_lo);
|
|
mar_hi_reg.E2K_SIC_MAR_HI_reg =
|
|
sic_read_node_nbsr_reg(node, SIC_sic_mar0_hi);
|
|
break;
|
|
case MCM1:
|
|
mar_lo_reg.E2K_SIC_MAR_LO_reg =
|
|
sic_read_node_nbsr_reg(node, SIC_sic_mar1_lo);
|
|
mar_hi_reg.E2K_SIC_MAR_HI_reg =
|
|
sic_read_node_nbsr_reg(node, SIC_sic_mar1_hi);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_result = (((u64)mar_hi_reg.E2K_SIC_MAR_HI_reg) << 32) |
|
|
mar_lo_reg.E2K_SIC_MAR_LO_reg;
|
|
|
|
*((u64 *)result) = _result;
|
|
}
|
|
|
|
static void set_sic_str_cfg(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_sic_mcr_struct_t mcr_reg;
|
|
int node = uncore->node;
|
|
int monitor_id = uncore->idx_at_node;
|
|
u64 _val = *((u64 *)val);
|
|
|
|
mcr_reg.E2K_SIC_MCR_reg = sic_read_node_nbsr_reg(node, SIC_sic_mcr);
|
|
|
|
switch (monitor_id) {
|
|
case MCM0:
|
|
mcr_reg.E2K_SIC_MCR_v0 = 1;
|
|
mcr_reg.E2K_SIC_MCR_es0 = _val;
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mcr,
|
|
mcr_reg.E2K_SIC_MCR_reg);
|
|
case MCM1:
|
|
mcr_reg.E2K_SIC_MCR_v1 = 1;
|
|
mcr_reg.E2K_SIC_MCR_es1 = _val;
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mcr,
|
|
mcr_reg.E2K_SIC_MCR_reg);
|
|
}
|
|
|
|
}
|
|
|
|
static void set_sic_str_cnt(struct e2k_uncore *uncore, void *val)
|
|
{
|
|
e2k_sic_mar_lo_struct_t mar_lo_reg;
|
|
e2k_sic_mar_hi_struct_t mar_hi_reg;
|
|
|
|
int node = uncore->node;
|
|
int monitor_id = uncore->idx_at_node;
|
|
|
|
u64 _val = *((u64 *)val);
|
|
|
|
mar_lo_reg.E2K_SIC_MAR_LO_reg = _val;
|
|
mar_hi_reg.E2K_SIC_MAR_HI_reg = _val >> 32;
|
|
|
|
switch (monitor_id) {
|
|
case MCM0:
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mar0_lo,
|
|
mar_hi_reg.E2K_SIC_MAR_LO_reg);
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mar0_hi,
|
|
mar_hi_reg.E2K_SIC_MAR_HI_reg);
|
|
case MCM1:
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mar1_lo,
|
|
mar_hi_reg.E2K_SIC_MAR_LO_reg);
|
|
sic_write_node_nbsr_reg(node, SIC_sic_mar1_hi,
|
|
mar_hi_reg.E2K_SIC_MAR_HI_reg);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct e2k_uncore_reg_ops sic_reg_ops = {
|
|
.get_cnt = get_sic_str_cnt,
|
|
.set_cfg = set_sic_str_cfg,
|
|
.set_cnt = set_sic_str_cnt,
|
|
};
|
|
|
|
static struct e2k_uncore_event_desc ipcc_events[] = {
|
|
E2K_UNCORE_EVENT_DESC(phl_errors, "event=0x1"),
|
|
E2K_UNCORE_EVENT_DESC(retry_ops, "event=0x2"),
|
|
{ /*end: all zeroes */ },
|
|
};
|
|
|
|
static int ipcc_valid_events[] = { 1, 2, -1};
|
|
|
|
static struct attribute *e2k_ipcc_events_attrs[] = {
|
|
&ipcc_events[0].attr.attr,
|
|
&ipcc_events[1].attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group e2k_ipcc_events_group = {
|
|
.name = "events",
|
|
.attrs = e2k_ipcc_events_attrs,
|
|
};
|
|
|
|
PMU_FORMAT_ATTR(event, "config:0-5");
|
|
|
|
static struct attribute *e2k_uncore_format_attr[] = {
|
|
&format_attr_event.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group e2k_ipcc_format_group = {
|
|
.name = "format",
|
|
.attrs = e2k_uncore_format_attr,
|
|
};
|
|
|
|
static struct attribute_group *e2k_ipcc_attr_group[] = {
|
|
&e2k_ipcc_events_group,
|
|
&e2k_ipcc_format_group,
|
|
NULL,
|
|
};
|
|
|
|
static struct e2k_uncore_event_desc iocc_events[] = {
|
|
E2K_UNCORE_EVENT_DESC(busy, "event=0x1"),
|
|
E2K_UNCORE_EVENT_DESC(crc_err, "event=0x2"),
|
|
E2K_UNCORE_EVENT_DESC(time_out, "event=0x4"),
|
|
E2K_UNCORE_EVENT_DESC(cmn_rc, "event=0x7"),
|
|
{ /*end: all zeroes */ },
|
|
};
|
|
|
|
static int iocc_valid_events[] = { 1, 2, 4, 7, -1 };
|
|
|
|
static struct attribute *e2k_iocc_events_attrs[] = {
|
|
&iocc_events[0].attr.attr,
|
|
&iocc_events[1].attr.attr,
|
|
&iocc_events[2].attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group e2k_iocc_events_group = {
|
|
.name = "events",
|
|
.attrs = e2k_iocc_events_attrs,
|
|
};
|
|
|
|
static struct attribute_group e2k_iocc_format_group = {
|
|
.name = "format",
|
|
.attrs = e2k_uncore_format_attr,
|
|
};
|
|
|
|
static struct attribute_group *e2k_iocc_attr_group[] = {
|
|
&e2k_iocc_events_group,
|
|
&e2k_iocc_format_group,
|
|
NULL,
|
|
};
|
|
|
|
static struct e2k_uncore_event_desc sic_MCM0_events[] = {
|
|
E2K_UNCORE_EVENT_DESC(mc_read, "event=0x0"),
|
|
E2K_UNCORE_EVENT_DESC(mc_write_local, "event=0x1"),
|
|
E2K_UNCORE_EVENT_DESC(mc_read_local_cores, "event=0x2"),
|
|
E2K_UNCORE_EVENT_DESC(dir_cache_hit, "event=0x3"),
|
|
E2K_UNCORE_EVENT_DESC(dir_cache_read_hit, "event=0x4"),
|
|
E2K_UNCORE_EVENT_DESC(retry, "event=0x5"),
|
|
E2K_UNCORE_EVENT_DESC(retry2_rdbuf_full, "event=0x6"),
|
|
E2K_UNCORE_EVENT_DESC(retry2_wrbuf, "event=0x7"),
|
|
E2K_UNCORE_EVENT_DESC(retry2_dm, "event=0x8"),
|
|
{ /*end: all zeroes */ },
|
|
};
|
|
|
|
static int sic_MCM0_valid_events[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, -1 };
|
|
|
|
static struct attribute *e2k_sic_MCM0_events_attrs[] = {
|
|
&sic_MCM0_events[0].attr.attr,
|
|
&sic_MCM0_events[1].attr.attr,
|
|
&sic_MCM0_events[2].attr.attr,
|
|
&sic_MCM0_events[3].attr.attr,
|
|
&sic_MCM0_events[4].attr.attr,
|
|
&sic_MCM0_events[5].attr.attr,
|
|
&sic_MCM0_events[6].attr.attr,
|
|
&sic_MCM0_events[7].attr.attr,
|
|
&sic_MCM0_events[8].attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group e2k_sic_MCM0_events_group = {
|
|
.name = "events",
|
|
.attrs = e2k_sic_MCM0_events_attrs,
|
|
};
|
|
|
|
static struct attribute_group e2k_sic_MCM0_format_group = {
|
|
.name = "format",
|
|
.attrs = e2k_uncore_format_attr,
|
|
};
|
|
|
|
static struct attribute_group *e2k_sic_MCM0_attr_group[] = {
|
|
&e2k_sic_MCM0_events_group,
|
|
&e2k_sic_MCM0_format_group,
|
|
NULL,
|
|
};
|
|
|
|
static struct e2k_uncore_event_desc sic_MCM1_events[] = {
|
|
E2K_UNCORE_EVENT_DESC(mc_write, "event=0x0"),
|
|
E2K_UNCORE_EVENT_DESC(mc_read_local, "event=0x1"),
|
|
E2K_UNCORE_EVENT_DESC(mc_write_local_cores, "event=0x2"),
|
|
E2K_UNCORE_EVENT_DESC(dir_cache_miss, "event=0x3"),
|
|
E2K_UNCORE_EVENT_DESC(dir_cache_read_miss, "event=0x4"),
|
|
E2K_UNCORE_EVENT_DESC(retry1, "event=0x5"),
|
|
E2K_UNCORE_EVENT_DESC(retry2, "event=0x6"),
|
|
{ /*end: all zeroes */ },
|
|
};
|
|
|
|
static int sic_MCM1_valid_events[] = { 0, 1, 2, 3, 4, 5, 6, -1 };
|
|
|
|
static struct attribute *e2k_sic_MCM1_events_attrs[] = {
|
|
&sic_MCM1_events[0].attr.attr,
|
|
&sic_MCM1_events[1].attr.attr,
|
|
&sic_MCM1_events[2].attr.attr,
|
|
&sic_MCM1_events[3].attr.attr,
|
|
&sic_MCM1_events[4].attr.attr,
|
|
&sic_MCM1_events[5].attr.attr,
|
|
&sic_MCM1_events[6].attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group e2k_sic_MCM1_events_group = {
|
|
.name = "events",
|
|
.attrs = e2k_sic_MCM1_events_attrs,
|
|
};
|
|
|
|
static struct attribute_group e2k_sic_MCM1_format_group = {
|
|
.name = "format",
|
|
.attrs = e2k_uncore_format_attr,
|
|
};
|
|
|
|
static struct attribute_group *e2k_sic_MCM1_attr_group[] = {
|
|
&e2k_sic_MCM1_events_group,
|
|
&e2k_sic_MCM1_format_group,
|
|
NULL,
|
|
};
|
|
|
|
static struct e2k_uncore *event_to_e2k_uncore(struct perf_event *event)
|
|
{
|
|
return container_of(event->pmu, struct e2k_uncore, pmu);
|
|
}
|
|
|
|
static void e2k_uncore_start(struct perf_event *event, int flags)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
struct e2k_uncore *uncore = event_to_e2k_uncore(event);
|
|
|
|
if (flags & PERF_EF_RELOAD)
|
|
uncore->reg_ops->set_cnt(uncore, (void *)&hwc->prev_count);
|
|
|
|
hwc->state = 0;
|
|
|
|
uncore->reg_ops->set_cfg(uncore, (void *)&hwc->config);
|
|
perf_event_update_userpage(event);
|
|
}
|
|
|
|
static int is_valid_event(struct perf_event *event)
|
|
{
|
|
struct e2k_uncore *uncore;
|
|
int i;
|
|
|
|
uncore = event_to_e2k_uncore(event);
|
|
if (!uncore)
|
|
return -ENODEV;
|
|
|
|
for (i = 0; uncore->valid_events[i] != -1; i++) {
|
|
if (event->attr.config == uncore->valid_events[i])
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int e2k_uncore_event_init(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
if (event->attr.type != event->pmu->type)
|
|
return -ENOENT;
|
|
|
|
/* Only sampling events are supported */
|
|
if (event->attr.sample_period)
|
|
return -EINVAL;
|
|
|
|
/* IPCC and IOCC counters don't have usr/os/guest/host bits */
|
|
if (event->attr.exclude_user || event->attr.exclude_kernel ||
|
|
event->attr.exclude_host || event->attr.exclude_guest)
|
|
return -EINVAL;
|
|
|
|
hwc->config = event->attr.config;
|
|
hwc->idx = -1;
|
|
|
|
return is_valid_event(event);
|
|
}
|
|
|
|
static int e2k_uncore_add(struct perf_event *event, int flags)
|
|
{
|
|
int i;
|
|
struct e2k_uncore *uncore = event_to_e2k_uncore(event);
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
|
|
/* are we already assigned? */
|
|
if (hwc->idx != -1 && uncore->events[hwc->idx] == event)
|
|
goto out;
|
|
|
|
for (i = 0; i < uncore->num_counters; i++) {
|
|
if (uncore->events[i] == event) {
|
|
hwc->idx = i;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* if didn't find, take the first availible counter */
|
|
hwc->idx = -1;
|
|
for (i = 0; i < uncore->num_counters; i++) {
|
|
if (cmpxchg(&uncore->events[i], NULL, event) == NULL) {
|
|
hwc->idx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (hwc->idx == -1) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
|
|
|
|
if (flags & PERF_EF_START)
|
|
e2k_uncore_start(event, PERF_EF_RELOAD);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void e2k_uncore_read(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
struct e2k_uncore *uncore = event_to_e2k_uncore(event);
|
|
|
|
u64 prev, new;
|
|
s64 delta;
|
|
|
|
prev = local64_read(&hwc->prev_count);
|
|
uncore->reg_ops->get_cnt(uncore, &new);
|
|
delta = new - prev;
|
|
local64_add(delta, &event->count);
|
|
}
|
|
|
|
static void e2k_uncore_stop(struct perf_event *event, int flags)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
struct e2k_uncore *uncore = event_to_e2k_uncore(event);
|
|
|
|
uncore->reg_ops->set_cfg(uncore, &hwc->config);
|
|
hwc->state |= PERF_HES_STOPPED;
|
|
|
|
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
|
e2k_uncore_read(event);
|
|
hwc->state |= PERF_HES_UPTODATE;
|
|
}
|
|
}
|
|
|
|
static void e2k_uncore_del(struct perf_event *event, int flags)
|
|
{
|
|
int i;
|
|
struct e2k_uncore *uncore = event_to_e2k_uncore(event);
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
e2k_uncore_stop(event, PERF_EF_UPDATE);
|
|
|
|
for (i = 0; i < uncore->num_counters; i++) {
|
|
if (cmpxchg(&uncore->events[i], event, NULL) == event)
|
|
break;
|
|
}
|
|
|
|
hwc->idx = -1;
|
|
}
|
|
|
|
|
|
static int __init register_ipcc_pmus()
|
|
{
|
|
int i;
|
|
int num_counters = NUM_COUNTERS_IPCC;
|
|
|
|
e2k_uncore_ipcc = kzalloc(sizeof(struct e2k_uncore) *
|
|
num_counters, GFP_KERNEL);
|
|
if (!e2k_uncore_ipcc)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
e2k_uncore_ipcc[i].pmu.attr_groups =
|
|
(const struct attribute_group **)e2k_ipcc_attr_group;
|
|
e2k_uncore_ipcc[i].pmu.event_init = e2k_uncore_event_init,
|
|
e2k_uncore_ipcc[i].pmu.add = e2k_uncore_add,
|
|
e2k_uncore_ipcc[i].pmu.del = e2k_uncore_del,
|
|
e2k_uncore_ipcc[i].pmu.start = e2k_uncore_start,
|
|
e2k_uncore_ipcc[i].pmu.stop = e2k_uncore_stop,
|
|
e2k_uncore_ipcc[i].pmu.read = e2k_uncore_read,
|
|
|
|
e2k_uncore_ipcc[i].reg_ops = &ipcc_reg_ops;
|
|
e2k_uncore_ipcc[i].num_counters = 1;
|
|
|
|
e2k_uncore_ipcc[i].node = i / IPCC_STR_PER_NODE;
|
|
e2k_uncore_ipcc[i].idx_at_node = i % IPCC_STR_PER_NODE;
|
|
|
|
e2k_uncore_ipcc[i].valid_events = ipcc_valid_events;
|
|
|
|
sprintf(e2k_uncore_ipcc[i].name, "ipcc_%d_%d",
|
|
e2k_uncore_ipcc[i].node,
|
|
e2k_uncore_ipcc[i].idx_at_node);
|
|
}
|
|
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
perf_pmu_register(&e2k_uncore_ipcc[i].pmu,
|
|
e2k_uncore_ipcc[i].name, -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init register_iocc_pmus()
|
|
{
|
|
int i;
|
|
int num_counters = NUM_COUNTERS_IOCC;
|
|
|
|
e2k_uncore_iocc = kzalloc(sizeof(struct e2k_uncore) *
|
|
num_counters, GFP_KERNEL);
|
|
if (!e2k_uncore_iocc)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
e2k_uncore_iocc[i].pmu.attr_groups =
|
|
(const struct attribute_group **)e2k_iocc_attr_group;
|
|
e2k_uncore_iocc[i].pmu.event_init = e2k_uncore_event_init;
|
|
e2k_uncore_iocc[i].pmu.add = e2k_uncore_add;
|
|
e2k_uncore_iocc[i].pmu.del = e2k_uncore_del;
|
|
e2k_uncore_iocc[i].pmu.start = e2k_uncore_start;
|
|
e2k_uncore_iocc[i].pmu.stop = e2k_uncore_stop;
|
|
e2k_uncore_iocc[i].pmu.read = e2k_uncore_read;
|
|
|
|
e2k_uncore_iocc[i].reg_ops = &iocc_reg_ops;
|
|
e2k_uncore_iocc[i].num_counters = 1;
|
|
|
|
e2k_uncore_iocc[i].node = i / IOCC_STR_PER_NODE;
|
|
e2k_uncore_iocc[i].idx_at_node = i % IOCC_STR_PER_NODE;
|
|
|
|
e2k_uncore_iocc[i].valid_events = iocc_valid_events;
|
|
|
|
sprintf(e2k_uncore_iocc[i].name, "iocc_%d_%d",
|
|
e2k_uncore_iocc[i].node,
|
|
e2k_uncore_iocc[i].idx_at_node);
|
|
}
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
perf_pmu_register(&e2k_uncore_iocc[i].pmu,
|
|
e2k_uncore_iocc[i].name, -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init register_sic_pmus()
|
|
{
|
|
int i;
|
|
int num_counters = NUM_COUNTERS_SIC;
|
|
|
|
e2k_uncore_sic = kzalloc(sizeof(struct e2k_uncore) *
|
|
num_counters, GFP_KERNEL);
|
|
if (!e2k_uncore_sic)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
e2k_uncore_sic[i].pmu.event_init = e2k_uncore_event_init,
|
|
e2k_uncore_sic[i].pmu.add = e2k_uncore_add;
|
|
e2k_uncore_sic[i].pmu.del = e2k_uncore_del;
|
|
e2k_uncore_sic[i].pmu.start = e2k_uncore_start;
|
|
e2k_uncore_sic[i].pmu.stop = e2k_uncore_stop;
|
|
e2k_uncore_sic[i].pmu.read = e2k_uncore_read;
|
|
|
|
e2k_uncore_sic[i].reg_ops = &sic_reg_ops;
|
|
e2k_uncore_sic[i].num_counters = 1;
|
|
|
|
e2k_uncore_sic[i].node = i / SIC_STR_PER_NODE;
|
|
e2k_uncore_sic[i].idx_at_node = i % SIC_STR_PER_NODE;
|
|
|
|
switch (i % SIC_STR_PER_NODE) {
|
|
case 0:
|
|
e2k_uncore_sic[i].valid_events = sic_MCM0_valid_events;
|
|
e2k_uncore_sic[i].pmu.attr_groups = (const struct
|
|
attribute_group **)e2k_sic_MCM0_attr_group;
|
|
sprintf(e2k_uncore_sic[i].name, "sic_%d_MCM0",
|
|
e2k_uncore_sic[i].node);
|
|
break;
|
|
|
|
case 1:
|
|
e2k_uncore_sic[i].valid_events = sic_MCM1_valid_events;
|
|
e2k_uncore_sic[i].pmu.attr_groups = (const struct
|
|
attribute_group **)e2k_sic_MCM1_attr_group;
|
|
sprintf(e2k_uncore_sic[i].name, "sic_%d_MCM1",
|
|
e2k_uncore_sic[i].node);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_counters; i++) {
|
|
perf_pmu_register(&e2k_uncore_sic[i].pmu,
|
|
e2k_uncore_sic[i].name, -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __init e2k_uncore_pmu_exit(struct e2k_uncore *uncore,
|
|
int num_per_machine)
|
|
{
|
|
int i;
|
|
|
|
if (uncore) {
|
|
|
|
for (i = 0; i < num_per_machine; i++)
|
|
perf_pmu_unregister(&uncore[i].pmu);
|
|
|
|
kfree(uncore);
|
|
}
|
|
}
|
|
|
|
static int __init e2k_uncore_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (E2K_UNCORE_HAS_IPCC) {
|
|
ret = register_ipcc_pmus();
|
|
if (ret)
|
|
goto err_ipcc;
|
|
}
|
|
if (E2K_UNCORE_HAS_IOCC) {
|
|
ret = register_iocc_pmus();
|
|
if (ret)
|
|
goto err_iocc;
|
|
}
|
|
if (E2K_UNCORE_HAS_SIC) {
|
|
ret = register_sic_pmus();
|
|
if (ret)
|
|
goto err_sic;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_sic:
|
|
e2k_uncore_pmu_exit(e2k_uncore_sic, NUM_COUNTERS_SIC);
|
|
err_iocc:
|
|
e2k_uncore_pmu_exit(e2k_uncore_iocc, NUM_COUNTERS_IOCC);
|
|
err_ipcc:
|
|
e2k_uncore_pmu_exit(e2k_uncore_ipcc, NUM_COUNTERS_IPCC);
|
|
|
|
return ret;
|
|
}
|
|
device_initcall(e2k_uncore_init);
|