The csky pmu counter may have different io width. When the counter is smaller then 64 bits and counter value is smaller than the old value, it will result to a extremely large delta value. So the sampled value should be extend to 64 bits to avoid this, the extension bits base on the count-width property from dts. Signed-off-by: Mao Han <han_mao@xxxxxxxxx> Cc: Guo Ren <guoren@xxxxxxxxxx> --- arch/csky/kernel/perf_event.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/csky/kernel/perf_event.c b/arch/csky/kernel/perf_event.c index c022acc..36f7f20 100644 --- a/arch/csky/kernel/perf_event.c +++ b/arch/csky/kernel/perf_event.c @@ -9,6 +9,7 @@ #include <linux/platform_device.h> #define CSKY_PMU_MAX_EVENTS 32 +#define DEFAULT_COUNT_WIDTH 48 #define HPCR "<0, 0x0>" /* PMU Control reg */ #define HPCNTENR "<0, 0x4>" /* Count Enable reg */ @@ -18,6 +19,7 @@ static void (*hw_raw_write_mapping[CSKY_PMU_MAX_EVENTS])(uint64_t val); struct csky_pmu_t { struct pmu pmu; + uint32_t count_width; uint32_t hpcr; } csky_pmu; @@ -806,7 +808,12 @@ static void csky_perf_event_update(struct perf_event *event, struct hw_perf_event *hwc) { uint64_t prev_raw_count = local64_read(&hwc->prev_count); - uint64_t new_raw_count = hw_raw_read_mapping[hwc->idx](); + /* + * Sign extend count value to 64bit, otherwise delta calculation + * would be incorrect when overflow occurs. + */ + uint64_t new_raw_count = sign_extend64( + hw_raw_read_mapping[hwc->idx](), csky_pmu.count_width); int64_t delta = new_raw_count - prev_raw_count; /* @@ -1045,6 +1052,11 @@ int csky_pmu_device_probe(struct platform_device *pdev, ret = init_fn(&csky_pmu); } + if (!of_property_read_u32(node, "count-width", + &csky_pmu.count_width)) { + csky_pmu.count_width = DEFAULT_COUNT_WIDTH; + } + if (ret) { pr_notice("[perf] failed to probe PMU!\n"); return ret; -- 2.7.4