Skip to content

Commit a9a9440

Browse files
virtuosoIngo Molnar
authored andcommitted
perf/x86/intel/bts: Fix confused ordering of PMU callbacks
The intel_bts driver is using a CPU-local 'started' variable to order callbacks and PMIs and make sure that AUX transactions don't get messed up. However, the ordering rules in regard to this variable is a complete mess, which recently resulted in perf_fuzzer-triggered warnings and panics. The general ordering rule that is patch is enforcing is that this cpu-local variable be set only when the cpu-local AUX transaction is active; consequently, this variable is to be checked before the AUX related bits can be touched. Reported-by: Vince Weaver <[email protected]> Signed-off-by: Alexander Shishkin <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Stephane Eranian <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent b79ccad commit a9a9440

File tree

1 file changed

+80
-24
lines changed
  • arch/x86/events/intel

1 file changed

+80
-24
lines changed

arch/x86/events/intel/bts.c

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@
3131
struct bts_ctx {
3232
struct perf_output_handle handle;
3333
struct debug_store ds_back;
34-
int started;
34+
int state;
35+
};
36+
37+
/* BTS context states: */
38+
enum {
39+
/* no ongoing AUX transactions */
40+
BTS_STATE_STOPPED = 0,
41+
/* AUX transaction is on, BTS tracing is disabled */
42+
BTS_STATE_INACTIVE,
43+
/* AUX transaction is on, BTS tracing is running */
44+
BTS_STATE_ACTIVE,
3545
};
3646

3747
static DEFINE_PER_CPU(struct bts_ctx, bts_ctx);
@@ -204,6 +214,15 @@ static void bts_update(struct bts_ctx *bts)
204214
static int
205215
bts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle);
206216

217+
/*
218+
* Ordering PMU callbacks wrt themselves and the PMI is done by means
219+
* of bts::state, which:
220+
* - is set when bts::handle::event is valid, that is, between
221+
* perf_aux_output_begin() and perf_aux_output_end();
222+
* - is zero otherwise;
223+
* - is ordered against bts::handle::event with a compiler barrier.
224+
*/
225+
207226
static void __bts_event_start(struct perf_event *event)
208227
{
209228
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
@@ -221,10 +240,13 @@ static void __bts_event_start(struct perf_event *event)
221240

222241
/*
223242
* local barrier to make sure that ds configuration made it
224-
* before we enable BTS
243+
* before we enable BTS and bts::state goes ACTIVE
225244
*/
226245
wmb();
227246

247+
/* INACTIVE/STOPPED -> ACTIVE */
248+
WRITE_ONCE(bts->state, BTS_STATE_ACTIVE);
249+
228250
intel_pmu_enable_bts(config);
229251

230252
}
@@ -251,9 +273,6 @@ static void bts_event_start(struct perf_event *event, int flags)
251273

252274
__bts_event_start(event);
253275

254-
/* PMI handler: this counter is running and likely generating PMIs */
255-
ACCESS_ONCE(bts->started) = 1;
256-
257276
return;
258277

259278
fail_end_stop:
@@ -263,30 +282,34 @@ static void bts_event_start(struct perf_event *event, int flags)
263282
event->hw.state = PERF_HES_STOPPED;
264283
}
265284

266-
static void __bts_event_stop(struct perf_event *event)
285+
static void __bts_event_stop(struct perf_event *event, int state)
267286
{
287+
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
288+
289+
/* ACTIVE -> INACTIVE(PMI)/STOPPED(->stop()) */
290+
WRITE_ONCE(bts->state, state);
291+
268292
/*
269293
* No extra synchronization is mandated by the documentation to have
270294
* BTS data stores globally visible.
271295
*/
272296
intel_pmu_disable_bts();
273-
274-
if (event->hw.state & PERF_HES_STOPPED)
275-
return;
276-
277-
ACCESS_ONCE(event->hw.state) |= PERF_HES_STOPPED;
278297
}
279298

280299
static void bts_event_stop(struct perf_event *event, int flags)
281300
{
282301
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
283302
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
284-
struct bts_buffer *buf = perf_get_aux(&bts->handle);
303+
struct bts_buffer *buf = NULL;
304+
int state = READ_ONCE(bts->state);
305+
306+
if (state == BTS_STATE_ACTIVE)
307+
__bts_event_stop(event, BTS_STATE_STOPPED);
285308

286-
/* PMI handler: don't restart this counter */
287-
ACCESS_ONCE(bts->started) = 0;
309+
if (state != BTS_STATE_STOPPED)
310+
buf = perf_get_aux(&bts->handle);
288311

289-
__bts_event_stop(event);
312+
event->hw.state |= PERF_HES_STOPPED;
290313

291314
if (flags & PERF_EF_UPDATE) {
292315
bts_update(bts);
@@ -296,6 +319,7 @@ static void bts_event_stop(struct perf_event *event, int flags)
296319
bts->handle.head =
297320
local_xchg(&buf->data_size,
298321
buf->nr_pages << PAGE_SHIFT);
322+
299323
perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0),
300324
!!local_xchg(&buf->lost, 0));
301325
}
@@ -310,17 +334,36 @@ static void bts_event_stop(struct perf_event *event, int flags)
310334
void intel_bts_enable_local(void)
311335
{
312336
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
337+
int state = READ_ONCE(bts->state);
338+
339+
/*
340+
* Here we transition from INACTIVE to ACTIVE;
341+
* if we instead are STOPPED from the interrupt handler,
342+
* stay that way. Can't be ACTIVE here though.
343+
*/
344+
if (WARN_ON_ONCE(state == BTS_STATE_ACTIVE))
345+
return;
346+
347+
if (state == BTS_STATE_STOPPED)
348+
return;
313349

314-
if (bts->handle.event && bts->started)
350+
if (bts->handle.event)
315351
__bts_event_start(bts->handle.event);
316352
}
317353

318354
void intel_bts_disable_local(void)
319355
{
320356
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
321357

358+
/*
359+
* Here we transition from ACTIVE to INACTIVE;
360+
* do nothing for STOPPED or INACTIVE.
361+
*/
362+
if (READ_ONCE(bts->state) != BTS_STATE_ACTIVE)
363+
return;
364+
322365
if (bts->handle.event)
323-
__bts_event_stop(bts->handle.event);
366+
__bts_event_stop(bts->handle.event, BTS_STATE_INACTIVE);
324367
}
325368

326369
static int
@@ -407,9 +450,13 @@ int intel_bts_interrupt(void)
407450
struct perf_event *event = bts->handle.event;
408451
struct bts_buffer *buf;
409452
s64 old_head;
410-
int err;
453+
int err = -ENOSPC;
411454

412-
if (!event || !bts->started)
455+
/*
456+
* this is wrapped in intel_bts_enable_local/intel_bts_disable_local,
457+
* so we can only be INACTIVE or STOPPED
458+
*/
459+
if (READ_ONCE(bts->state) == BTS_STATE_STOPPED)
413460
return 0;
414461

415462
buf = perf_get_aux(&bts->handle);
@@ -432,12 +479,21 @@ int intel_bts_interrupt(void)
432479
!!local_xchg(&buf->lost, 0));
433480

434481
buf = perf_aux_output_begin(&bts->handle, event);
435-
if (!buf)
436-
return 1;
482+
if (buf)
483+
err = bts_buffer_reset(buf, &bts->handle);
437484

438-
err = bts_buffer_reset(buf, &bts->handle);
439-
if (err)
440-
perf_aux_output_end(&bts->handle, 0, false);
485+
if (err) {
486+
WRITE_ONCE(bts->state, BTS_STATE_STOPPED);
487+
488+
if (buf) {
489+
/*
490+
* BTS_STATE_STOPPED should be visible before
491+
* cleared handle::event
492+
*/
493+
barrier();
494+
perf_aux_output_end(&bts->handle, 0, false);
495+
}
496+
}
441497

442498
return 1;
443499
}

0 commit comments

Comments
 (0)