31
31
struct bts_ctx {
32
32
struct perf_output_handle handle ;
33
33
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 ,
35
45
};
36
46
37
47
static DEFINE_PER_CPU (struct bts_ctx , bts_ctx ) ;
@@ -204,6 +214,15 @@ static void bts_update(struct bts_ctx *bts)
204
214
static int
205
215
bts_buffer_reset (struct bts_buffer * buf , struct perf_output_handle * handle );
206
216
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
+
207
226
static void __bts_event_start (struct perf_event * event )
208
227
{
209
228
struct bts_ctx * bts = this_cpu_ptr (& bts_ctx );
@@ -221,10 +240,13 @@ static void __bts_event_start(struct perf_event *event)
221
240
222
241
/*
223
242
* 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
225
244
*/
226
245
wmb ();
227
246
247
+ /* INACTIVE/STOPPED -> ACTIVE */
248
+ WRITE_ONCE (bts -> state , BTS_STATE_ACTIVE );
249
+
228
250
intel_pmu_enable_bts (config );
229
251
230
252
}
@@ -251,9 +273,6 @@ static void bts_event_start(struct perf_event *event, int flags)
251
273
252
274
__bts_event_start (event );
253
275
254
- /* PMI handler: this counter is running and likely generating PMIs */
255
- ACCESS_ONCE (bts -> started ) = 1 ;
256
-
257
276
return ;
258
277
259
278
fail_end_stop :
@@ -263,30 +282,34 @@ static void bts_event_start(struct perf_event *event, int flags)
263
282
event -> hw .state = PERF_HES_STOPPED ;
264
283
}
265
284
266
- static void __bts_event_stop (struct perf_event * event )
285
+ static void __bts_event_stop (struct perf_event * event , int state )
267
286
{
287
+ struct bts_ctx * bts = this_cpu_ptr (& bts_ctx );
288
+
289
+ /* ACTIVE -> INACTIVE(PMI)/STOPPED(->stop()) */
290
+ WRITE_ONCE (bts -> state , state );
291
+
268
292
/*
269
293
* No extra synchronization is mandated by the documentation to have
270
294
* BTS data stores globally visible.
271
295
*/
272
296
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 ;
278
297
}
279
298
280
299
static void bts_event_stop (struct perf_event * event , int flags )
281
300
{
282
301
struct cpu_hw_events * cpuc = this_cpu_ptr (& cpu_hw_events );
283
302
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 );
285
308
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 ) ;
288
311
289
- __bts_event_stop ( event ) ;
312
+ event -> hw . state |= PERF_HES_STOPPED ;
290
313
291
314
if (flags & PERF_EF_UPDATE ) {
292
315
bts_update (bts );
@@ -296,6 +319,7 @@ static void bts_event_stop(struct perf_event *event, int flags)
296
319
bts -> handle .head =
297
320
local_xchg (& buf -> data_size ,
298
321
buf -> nr_pages << PAGE_SHIFT );
322
+
299
323
perf_aux_output_end (& bts -> handle , local_xchg (& buf -> data_size , 0 ),
300
324
!!local_xchg (& buf -> lost , 0 ));
301
325
}
@@ -310,17 +334,36 @@ static void bts_event_stop(struct perf_event *event, int flags)
310
334
void intel_bts_enable_local (void )
311
335
{
312
336
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 ;
313
349
314
- if (bts -> handle .event && bts -> started )
350
+ if (bts -> handle .event )
315
351
__bts_event_start (bts -> handle .event );
316
352
}
317
353
318
354
void intel_bts_disable_local (void )
319
355
{
320
356
struct bts_ctx * bts = this_cpu_ptr (& bts_ctx );
321
357
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
+
322
365
if (bts -> handle .event )
323
- __bts_event_stop (bts -> handle .event );
366
+ __bts_event_stop (bts -> handle .event , BTS_STATE_INACTIVE );
324
367
}
325
368
326
369
static int
@@ -407,9 +450,13 @@ int intel_bts_interrupt(void)
407
450
struct perf_event * event = bts -> handle .event ;
408
451
struct bts_buffer * buf ;
409
452
s64 old_head ;
410
- int err ;
453
+ int err = - ENOSPC ;
411
454
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 )
413
460
return 0 ;
414
461
415
462
buf = perf_get_aux (& bts -> handle );
@@ -432,12 +479,21 @@ int intel_bts_interrupt(void)
432
479
!!local_xchg (& buf -> lost , 0 ));
433
480
434
481
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 ) ;
437
484
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
+ }
441
497
442
498
return 1 ;
443
499
}
0 commit comments