return amd_pmu_adjust_nmi_window(handled);
 }
 
+/*
+ * AMD-specific callback invoked through perf_snapshot_branch_stack static
+ * call, defined in include/linux/perf_event.h. See its definition for API
+ * details. It's up to caller to provide enough space in *entries* to fit all
+ * LBR records, otherwise returned result will be truncated to *cnt* entries.
+ */
+static int amd_pmu_v2_snapshot_branch_stack(struct perf_branch_entry *entries, unsigned int cnt)
+{
+       struct cpu_hw_events *cpuc;
+       unsigned long flags;
+
+       /*
+        * The sequence of steps to freeze LBR should be completely inlined
+        * and contain no branches to minimize contamination of LBR snapshot
+        */
+       local_irq_save(flags);
+       amd_pmu_core_disable_all();
+       __amd_pmu_lbr_disable();
+
+       cpuc = this_cpu_ptr(&cpu_hw_events);
+
+       amd_pmu_lbr_read();
+       cnt = min(cnt, x86_pmu.lbr_nr);
+       memcpy(entries, cpuc->lbr_entries, sizeof(struct perf_branch_entry) * cnt);
+
+       amd_pmu_v2_enable_all(0);
+       local_irq_restore(flags);
+
+       return cnt;
+}
+
 static int amd_pmu_v2_handle_irq(struct pt_regs *regs)
 {
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
                static_call_update(amd_pmu_branch_reset, amd_pmu_lbr_reset);
                static_call_update(amd_pmu_branch_add, amd_pmu_lbr_add);
                static_call_update(amd_pmu_branch_del, amd_pmu_lbr_del);
+
+               /* Only support branch_stack snapshot on perfmon v2 */
+               if (x86_pmu.handle_irq == amd_pmu_v2_handle_irq)
+                       static_call_update(perf_snapshot_branch_stack, amd_pmu_v2_snapshot_branch_stack);
        } else if (!amd_brs_init()) {
                /*
                 * BRS requires special event constraints and flushing on ctxsw.