Skip to content

Commit 6d937e0

Browse files
sandip4nIngo Molnar
authored andcommitted
perf/x86/amd/uncore: Use hrtimer for handling overflows
Uncore counters do not provide mechanisms like interrupts to report overflows and the accumulated user-visible count is incorrect if there is more than one overflow between two successive read requests for the same event because the value of prev_count goes out-of-date for calculating the correct delta. To avoid this, start a hrtimer to periodically initiate a pmu->read() of the active counters for keeping prev_count up-to-date. It should be noted that the hrtimer duration should be lesser than the shortest time it takes for a counter to overflow for this approach to be effective. Signed-off-by: Sandipan Das <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Acked-by: Peter Zijlstra <[email protected]> Link: https://lore.kernel.org/r/8ecf5fe20452da1cd19cf3ff4954d3e7c5137468.1744906694.git.sandipan.das@amd.com
1 parent 05c9b0c commit 6d937e0

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

arch/x86/events/amd/uncore.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define NUM_COUNTERS_NB 4
2222
#define NUM_COUNTERS_L2 4
2323
#define NUM_COUNTERS_L3 6
24+
#define NUM_COUNTERS_MAX 64
2425

2526
#define RDPMC_BASE_NB 6
2627
#define RDPMC_BASE_LLC 10
@@ -38,6 +39,10 @@ struct amd_uncore_ctx {
3839
int refcnt;
3940
int cpu;
4041
struct perf_event **events;
42+
unsigned long active_mask[BITS_TO_LONGS(NUM_COUNTERS_MAX)];
43+
int nr_active;
44+
struct hrtimer hrtimer;
45+
u64 hrtimer_duration;
4146
};
4247

4348
struct amd_uncore_pmu {
@@ -87,6 +92,42 @@ static struct amd_uncore_pmu *event_to_amd_uncore_pmu(struct perf_event *event)
8792
return container_of(event->pmu, struct amd_uncore_pmu, pmu);
8893
}
8994

95+
static enum hrtimer_restart amd_uncore_hrtimer(struct hrtimer *hrtimer)
96+
{
97+
struct amd_uncore_ctx *ctx;
98+
struct perf_event *event;
99+
int bit;
100+
101+
ctx = container_of(hrtimer, struct amd_uncore_ctx, hrtimer);
102+
103+
if (!ctx->nr_active || ctx->cpu != smp_processor_id())
104+
return HRTIMER_NORESTART;
105+
106+
for_each_set_bit(bit, ctx->active_mask, NUM_COUNTERS_MAX) {
107+
event = ctx->events[bit];
108+
event->pmu->read(event);
109+
}
110+
111+
hrtimer_forward_now(hrtimer, ns_to_ktime(ctx->hrtimer_duration));
112+
return HRTIMER_RESTART;
113+
}
114+
115+
static void amd_uncore_start_hrtimer(struct amd_uncore_ctx *ctx)
116+
{
117+
hrtimer_start(&ctx->hrtimer, ns_to_ktime(ctx->hrtimer_duration),
118+
HRTIMER_MODE_REL_PINNED_HARD);
119+
}
120+
121+
static void amd_uncore_cancel_hrtimer(struct amd_uncore_ctx *ctx)
122+
{
123+
hrtimer_cancel(&ctx->hrtimer);
124+
}
125+
126+
static void amd_uncore_init_hrtimer(struct amd_uncore_ctx *ctx)
127+
{
128+
hrtimer_setup(&ctx->hrtimer, amd_uncore_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
129+
}
130+
90131
static void amd_uncore_read(struct perf_event *event)
91132
{
92133
struct hw_perf_event *hwc = &event->hw;
@@ -117,18 +158,26 @@ static void amd_uncore_read(struct perf_event *event)
117158

118159
static void amd_uncore_start(struct perf_event *event, int flags)
119160
{
161+
struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
162+
struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
120163
struct hw_perf_event *hwc = &event->hw;
121164

165+
if (!ctx->nr_active++)
166+
amd_uncore_start_hrtimer(ctx);
167+
122168
if (flags & PERF_EF_RELOAD)
123169
wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count));
124170

125171
hwc->state = 0;
172+
__set_bit(hwc->idx, ctx->active_mask);
126173
wrmsrl(hwc->config_base, (hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE));
127174
perf_event_update_userpage(event);
128175
}
129176

130177
static void amd_uncore_stop(struct perf_event *event, int flags)
131178
{
179+
struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
180+
struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
132181
struct hw_perf_event *hwc = &event->hw;
133182

134183
wrmsrl(hwc->config_base, hwc->config);
@@ -138,6 +187,11 @@ static void amd_uncore_stop(struct perf_event *event, int flags)
138187
event->pmu->read(event);
139188
hwc->state |= PERF_HES_UPTODATE;
140189
}
190+
191+
if (!--ctx->nr_active)
192+
amd_uncore_cancel_hrtimer(ctx);
193+
194+
__clear_bit(hwc->idx, ctx->active_mask);
141195
}
142196

143197
static int amd_uncore_add(struct perf_event *event, int flags)
@@ -490,6 +544,9 @@ static int amd_uncore_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
490544
goto fail;
491545
}
492546

547+
amd_uncore_init_hrtimer(curr);
548+
curr->hrtimer_duration = 60LL * NSEC_PER_SEC;
549+
493550
cpumask_set_cpu(cpu, &pmu->active_mask);
494551
}
495552

@@ -879,12 +936,18 @@ static int amd_uncore_umc_event_init(struct perf_event *event)
879936

880937
static void amd_uncore_umc_start(struct perf_event *event, int flags)
881938
{
939+
struct amd_uncore_pmu *pmu = event_to_amd_uncore_pmu(event);
940+
struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
882941
struct hw_perf_event *hwc = &event->hw;
883942

943+
if (!ctx->nr_active++)
944+
amd_uncore_start_hrtimer(ctx);
945+
884946
if (flags & PERF_EF_RELOAD)
885947
wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count));
886948

887949
hwc->state = 0;
950+
__set_bit(hwc->idx, ctx->active_mask);
888951
wrmsrl(hwc->config_base, (hwc->config | AMD64_PERFMON_V2_ENABLE_UMC));
889952
perf_event_update_userpage(event);
890953
}

0 commit comments

Comments
 (0)