Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf/x86/intel/pt: Add support for pause / resume

Prevent tracing to start if aux_paused.

Implement support for PERF_EF_PAUSE / PERF_EF_RESUME. When aux_paused, stop
tracing. When not aux_paused, only start tracing if it isn't currently
meant to be stopped.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Link: https://lkml.kernel.org/r/20241022155920.17511-4-adrian.hunter@intel.com

authored by

Adrian Hunter and committed by
Peter Zijlstra
08c7454c 18d92bb5

+74 -3
+70 -3
arch/x86/events/intel/pt.c
··· 418 418 struct pt *pt = this_cpu_ptr(&pt_ctx); 419 419 u64 ctl = event->hw.aux_config; 420 420 421 + if (READ_ONCE(event->hw.aux_paused)) 422 + return; 423 + 421 424 ctl |= RTIT_CTL_TRACEEN; 422 425 if (READ_ONCE(pt->vmx_on)) 423 426 perf_aux_output_flag(&pt->handle, PERF_AUX_FLAG_PARTIAL); ··· 537 534 reg |= (event->attr.config & PT_CONFIG_MASK); 538 535 539 536 event->hw.aux_config = reg; 537 + 538 + /* 539 + * Allow resume before starting so as not to overwrite a value set by a 540 + * PMI. 541 + */ 542 + barrier(); 543 + WRITE_ONCE(pt->resume_allowed, 1); 544 + /* Configuration is complete, it is now OK to handle an NMI */ 545 + barrier(); 546 + WRITE_ONCE(pt->handle_nmi, 1); 547 + barrier(); 540 548 pt_config_start(event); 549 + barrier(); 550 + /* 551 + * Allow pause after starting so its pt_config_stop() doesn't race with 552 + * pt_config_start(). 553 + */ 554 + WRITE_ONCE(pt->pause_allowed, 1); 541 555 } 542 556 543 557 static void pt_config_stop(struct perf_event *event) ··· 1536 1516 buf = perf_aux_output_begin(&pt->handle, event); 1537 1517 if (!buf) { 1538 1518 event->hw.state = PERF_HES_STOPPED; 1519 + WRITE_ONCE(pt->resume_allowed, 0); 1539 1520 return; 1540 1521 } 1541 1522 ··· 1545 1524 ret = pt_buffer_reset_markers(buf, &pt->handle); 1546 1525 if (ret) { 1547 1526 perf_aux_output_end(&pt->handle, 0); 1527 + WRITE_ONCE(pt->resume_allowed, 0); 1548 1528 return; 1549 1529 } 1550 1530 ··· 1600 1578 struct pt *pt = this_cpu_ptr(&pt_ctx); 1601 1579 struct pt_buffer *buf; 1602 1580 1581 + if (mode & PERF_EF_RESUME) { 1582 + if (READ_ONCE(pt->resume_allowed)) { 1583 + u64 status; 1584 + 1585 + /* 1586 + * Only if the trace is not active and the error and 1587 + * stopped bits are clear, is it safe to start, but a 1588 + * PMI might have just cleared these, so resume_allowed 1589 + * must be checked again also. 1590 + */ 1591 + rdmsrl(MSR_IA32_RTIT_STATUS, status); 1592 + if (!(status & (RTIT_STATUS_TRIGGEREN | 1593 + RTIT_STATUS_ERROR | 1594 + RTIT_STATUS_STOPPED)) && 1595 + READ_ONCE(pt->resume_allowed)) 1596 + pt_config_start(event); 1597 + } 1598 + return; 1599 + } 1600 + 1603 1601 buf = perf_aux_output_begin(&pt->handle, event); 1604 1602 if (!buf) 1605 1603 goto fail_stop; ··· 1630 1588 goto fail_end_stop; 1631 1589 } 1632 1590 1633 - WRITE_ONCE(pt->handle_nmi, 1); 1634 1591 hwc->state = 0; 1635 1592 1636 1593 pt_config_buffer(buf); ··· 1647 1606 { 1648 1607 struct pt *pt = this_cpu_ptr(&pt_ctx); 1649 1608 1609 + if (mode & PERF_EF_PAUSE) { 1610 + if (READ_ONCE(pt->pause_allowed)) 1611 + pt_config_stop(event); 1612 + return; 1613 + } 1614 + 1650 1615 /* 1651 1616 * Protect against the PMI racing with disabling wrmsr, 1652 1617 * see comment in intel_pt_interrupt(). 1653 1618 */ 1654 1619 WRITE_ONCE(pt->handle_nmi, 0); 1620 + barrier(); 1621 + 1622 + /* 1623 + * Prevent a resume from attempting to restart tracing, or a pause 1624 + * during a subsequent start. Do this after clearing handle_nmi so that 1625 + * pt_event_snapshot_aux() will not re-allow them. 1626 + */ 1627 + WRITE_ONCE(pt->pause_allowed, 0); 1628 + WRITE_ONCE(pt->resume_allowed, 0); 1655 1629 barrier(); 1656 1630 1657 1631 pt_config_stop(event); ··· 1718 1662 if (WARN_ON_ONCE(!buf->snapshot)) 1719 1663 return 0; 1720 1664 1665 + /* Prevent pause/resume from attempting to start/stop tracing */ 1666 + WRITE_ONCE(pt->pause_allowed, 0); 1667 + WRITE_ONCE(pt->resume_allowed, 0); 1668 + barrier(); 1721 1669 /* 1722 1670 * There is no PT interrupt in this mode, so stop the trace and it will 1723 1671 * remain stopped while the buffer is copied. ··· 1741 1681 * Here, handle_nmi tells us if the tracing was on. 1742 1682 * If the tracing was on, restart it. 1743 1683 */ 1744 - if (READ_ONCE(pt->handle_nmi)) 1684 + if (READ_ONCE(pt->handle_nmi)) { 1685 + WRITE_ONCE(pt->resume_allowed, 1); 1686 + barrier(); 1745 1687 pt_config_start(event); 1688 + barrier(); 1689 + WRITE_ONCE(pt->pause_allowed, 1); 1690 + } 1746 1691 1747 1692 return ret; 1748 1693 } ··· 1863 1798 if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries)) 1864 1799 pt_pmu.pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG; 1865 1800 1866 - pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE; 1801 + pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE | 1802 + PERF_PMU_CAP_ITRACE | 1803 + PERF_PMU_CAP_AUX_PAUSE; 1867 1804 pt_pmu.pmu.attr_groups = pt_attr_groups; 1868 1805 pt_pmu.pmu.task_ctx_nr = perf_sw_context; 1869 1806 pt_pmu.pmu.event_init = pt_event_init;
+4
arch/x86/events/intel/pt.h
··· 119 119 * @filters: last configured filters 120 120 * @handle_nmi: do handle PT PMI on this cpu, there's an active event 121 121 * @vmx_on: 1 if VMX is ON on this cpu 122 + * @pause_allowed: PERF_EF_PAUSE is allowed to stop tracing 123 + * @resume_allowed: PERF_EF_RESUME is allowed to start tracing 122 124 * @output_base: cached RTIT_OUTPUT_BASE MSR value 123 125 * @output_mask: cached RTIT_OUTPUT_MASK MSR value 124 126 */ ··· 129 127 struct pt_filters filters; 130 128 int handle_nmi; 131 129 int vmx_on; 130 + int pause_allowed; 131 + int resume_allowed; 132 132 u64 output_base; 133 133 u64 output_mask; 134 134 };