@@ -418,6 +418,9 @@ static void pt_config_start(struct perf_event *event)
418418 struct pt * pt = this_cpu_ptr (& pt_ctx );
419419 u64 ctl = event -> hw .aux_config ;
420420
421+ if (READ_ONCE (event -> hw .aux_paused ))
422+ return ;
423+
421424 ctl |= RTIT_CTL_TRACEEN ;
422425 if (READ_ONCE (pt -> vmx_on ))
423426 perf_aux_output_flag (& pt -> handle , PERF_AUX_FLAG_PARTIAL );
@@ -534,7 +537,24 @@ static void pt_config(struct perf_event *event)
534537 reg |= (event -> attr .config & PT_CONFIG_MASK );
535538
536539 event -> hw .aux_config = reg ;
540+
541+ /*
542+ * Allow resume before starting so as not to overwrite a value set by a
543+ * PMI.
544+ */
545+ barrier ();
546+ WRITE_ONCE (pt -> resume_allowed , 1 );
547+ /* Configuration is complete, it is now OK to handle an NMI */
548+ barrier ();
549+ WRITE_ONCE (pt -> handle_nmi , 1 );
550+ barrier ();
537551 pt_config_start (event );
552+ barrier ();
553+ /*
554+ * Allow pause after starting so its pt_config_stop() doesn't race with
555+ * pt_config_start().
556+ */
557+ WRITE_ONCE (pt -> pause_allowed , 1 );
538558}
539559
540560static void pt_config_stop (struct perf_event * event )
@@ -1516,6 +1536,7 @@ void intel_pt_interrupt(void)
15161536 buf = perf_aux_output_begin (& pt -> handle , event );
15171537 if (!buf ) {
15181538 event -> hw .state = PERF_HES_STOPPED ;
1539+ WRITE_ONCE (pt -> resume_allowed , 0 );
15191540 return ;
15201541 }
15211542
@@ -1524,6 +1545,7 @@ void intel_pt_interrupt(void)
15241545 ret = pt_buffer_reset_markers (buf , & pt -> handle );
15251546 if (ret ) {
15261547 perf_aux_output_end (& pt -> handle , 0 );
1548+ WRITE_ONCE (pt -> resume_allowed , 0 );
15271549 return ;
15281550 }
15291551
@@ -1578,6 +1600,26 @@ static void pt_event_start(struct perf_event *event, int mode)
15781600 struct pt * pt = this_cpu_ptr (& pt_ctx );
15791601 struct pt_buffer * buf ;
15801602
1603+ if (mode & PERF_EF_RESUME ) {
1604+ if (READ_ONCE (pt -> resume_allowed )) {
1605+ u64 status ;
1606+
1607+ /*
1608+ * Only if the trace is not active and the error and
1609+ * stopped bits are clear, is it safe to start, but a
1610+ * PMI might have just cleared these, so resume_allowed
1611+ * must be checked again also.
1612+ */
1613+ rdmsrl (MSR_IA32_RTIT_STATUS , status );
1614+ if (!(status & (RTIT_STATUS_TRIGGEREN |
1615+ RTIT_STATUS_ERROR |
1616+ RTIT_STATUS_STOPPED )) &&
1617+ READ_ONCE (pt -> resume_allowed ))
1618+ pt_config_start (event );
1619+ }
1620+ return ;
1621+ }
1622+
15811623 buf = perf_aux_output_begin (& pt -> handle , event );
15821624 if (!buf )
15831625 goto fail_stop ;
@@ -1588,7 +1630,6 @@ static void pt_event_start(struct perf_event *event, int mode)
15881630 goto fail_end_stop ;
15891631 }
15901632
1591- WRITE_ONCE (pt -> handle_nmi , 1 );
15921633 hwc -> state = 0 ;
15931634
15941635 pt_config_buffer (buf );
@@ -1606,13 +1647,28 @@ static void pt_event_stop(struct perf_event *event, int mode)
16061647{
16071648 struct pt * pt = this_cpu_ptr (& pt_ctx );
16081649
1650+ if (mode & PERF_EF_PAUSE ) {
1651+ if (READ_ONCE (pt -> pause_allowed ))
1652+ pt_config_stop (event );
1653+ return ;
1654+ }
1655+
16091656 /*
16101657 * Protect against the PMI racing with disabling wrmsr,
16111658 * see comment in intel_pt_interrupt().
16121659 */
16131660 WRITE_ONCE (pt -> handle_nmi , 0 );
16141661 barrier ();
16151662
1663+ /*
1664+ * Prevent a resume from attempting to restart tracing, or a pause
1665+ * during a subsequent start. Do this after clearing handle_nmi so that
1666+ * pt_event_snapshot_aux() will not re-allow them.
1667+ */
1668+ WRITE_ONCE (pt -> pause_allowed , 0 );
1669+ WRITE_ONCE (pt -> resume_allowed , 0 );
1670+ barrier ();
1671+
16161672 pt_config_stop (event );
16171673
16181674 if (event -> hw .state == PERF_HES_STOPPED )
@@ -1662,6 +1718,10 @@ static long pt_event_snapshot_aux(struct perf_event *event,
16621718 if (WARN_ON_ONCE (!buf -> snapshot ))
16631719 return 0 ;
16641720
1721+ /* Prevent pause/resume from attempting to start/stop tracing */
1722+ WRITE_ONCE (pt -> pause_allowed , 0 );
1723+ WRITE_ONCE (pt -> resume_allowed , 0 );
1724+ barrier ();
16651725 /*
16661726 * There is no PT interrupt in this mode, so stop the trace and it will
16671727 * remain stopped while the buffer is copied.
@@ -1681,8 +1741,13 @@ static long pt_event_snapshot_aux(struct perf_event *event,
16811741 * Here, handle_nmi tells us if the tracing was on.
16821742 * If the tracing was on, restart it.
16831743 */
1684- if (READ_ONCE (pt -> handle_nmi ))
1744+ if (READ_ONCE (pt -> handle_nmi )) {
1745+ WRITE_ONCE (pt -> resume_allowed , 1 );
1746+ barrier ();
16851747 pt_config_start (event );
1748+ barrier ();
1749+ WRITE_ONCE (pt -> pause_allowed , 1 );
1750+ }
16861751
16871752 return ret ;
16881753}
@@ -1798,7 +1863,9 @@ static __init int pt_init(void)
17981863 if (!intel_pt_validate_hw_cap (PT_CAP_topa_multiple_entries ))
17991864 pt_pmu .pmu .capabilities = PERF_PMU_CAP_AUX_NO_SG ;
18001865
1801- pt_pmu .pmu .capabilities |= PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE ;
1866+ pt_pmu .pmu .capabilities |= PERF_PMU_CAP_EXCLUSIVE |
1867+ PERF_PMU_CAP_ITRACE |
1868+ PERF_PMU_CAP_AUX_PAUSE ;
18021869 pt_pmu .pmu .attr_groups = pt_attr_groups ;
18031870 pt_pmu .pmu .task_ctx_nr = perf_sw_context ;
18041871 pt_pmu .pmu .event_init = pt_event_init ;
0 commit comments