sparc: Support HW cache events.

First supported chip for HW cache events is Ultra-IIIi.

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2009-09-26 20:42:10 -07:00
parent 0d9df2515d
commit 2ce4da2efc

View File

@ -68,8 +68,19 @@ struct perf_event_map {
#define PIC_LOWER 0x02
};
#define C(x) PERF_COUNT_HW_CACHE_##x
#define CACHE_OP_UNSUPPORTED 0xfffe
#define CACHE_OP_NONSENSE 0xffff
typedef struct perf_event_map cache_map_t
[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX];
struct sparc_pmu {
const struct perf_event_map *(*event_map)(int);
const cache_map_t *cache_map;
int max_events;
int upper_shift;
int lower_shift;
@ -92,8 +103,96 @@ static const struct perf_event_map *ultra3i_event_map(int event_id)
return &ultra3i_perfmon_event_map[event_id];
}
static const cache_map_t ultra3i_cache_map = {
[C(L1D)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
[C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = { 0x0a, PIC_LOWER },
[C(RESULT_MISS)] = { 0x0a, PIC_UPPER },
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
[C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
},
},
[C(L1I)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
[C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
},
[ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
[ C(RESULT_MISS) ] = { CACHE_OP_NONSENSE },
},
[ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
},
[C(LL)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { 0x0c, PIC_LOWER, },
[C(RESULT_MISS)] = { 0x0c, PIC_UPPER, },
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = { 0x0c, PIC_LOWER },
[C(RESULT_MISS)] = { 0x0c, PIC_UPPER },
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
[C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
},
},
[C(DTLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
[C(RESULT_MISS)] = { 0x12, PIC_UPPER, },
},
[ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
[ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
},
[C(ITLB)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
[C(RESULT_MISS)] = { 0x11, PIC_UPPER, },
},
[ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
[ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
},
[C(BPU)] = {
[C(OP_READ)] = {
[C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
[C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
},
[ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
[ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
[ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
},
},
};
static const struct sparc_pmu ultra3i_pmu = {
.event_map = ultra3i_event_map,
.cache_map = &ultra3i_cache_map,
.max_events = ARRAY_SIZE(ultra3i_perfmon_event_map),
.upper_shift = 11,
.lower_shift = 4,
@ -375,6 +474,37 @@ void perf_event_release_pmc(void)
}
}
static const struct perf_event_map *sparc_map_cache_event(u64 config)
{
unsigned int cache_type, cache_op, cache_result;
const struct perf_event_map *pmap;
if (!sparc_pmu->cache_map)
return ERR_PTR(-ENOENT);
cache_type = (config >> 0) & 0xff;
if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
return ERR_PTR(-EINVAL);
cache_op = (config >> 8) & 0xff;
if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
return ERR_PTR(-EINVAL);
cache_result = (config >> 16) & 0xff;
if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
return ERR_PTR(-EINVAL);
pmap = &((*sparc_pmu->cache_map)[cache_type][cache_op][cache_result]);
if (pmap->encoding == CACHE_OP_UNSUPPORTED)
return ERR_PTR(-ENOENT);
if (pmap->encoding == CACHE_OP_NONSENSE)
return ERR_PTR(-EINVAL);
return pmap;
}
static void hw_perf_event_destroy(struct perf_event *event)
{
perf_event_release_pmc();
@ -390,12 +520,17 @@ static int __hw_perf_event_init(struct perf_event *event)
if (atomic_read(&nmi_active) < 0)
return -ENODEV;
if (attr->type != PERF_TYPE_HARDWARE)
if (attr->type == PERF_TYPE_HARDWARE) {
if (attr->config >= sparc_pmu->max_events)
return -EINVAL;
pmap = sparc_pmu->event_map(attr->config);
} else if (attr->type == PERF_TYPE_HW_CACHE) {
pmap = sparc_map_cache_event(attr->config);
if (IS_ERR(pmap))
return PTR_ERR(pmap);
} else
return -EOPNOTSUPP;
if (attr->config >= sparc_pmu->max_events)
return -EINVAL;
perf_event_grab_pmc();
event->destroy = hw_perf_event_destroy;
@ -417,8 +552,6 @@ static int __hw_perf_event_init(struct perf_event *event)
atomic64_set(&hwc->period_left, hwc->sample_period);
}
pmap = sparc_pmu->event_map(attr->config);
enc = pmap->encoding;
if (pmap->pic_mask & PIC_UPPER) {
hwc->idx = PIC_UPPER_INDEX;