Thread (16 messages) 16 messages, 6 authors, 2025-05-29

Re: [PATCH v22 5/5] perf: arm_pmuv3: Add support for the Branch Record Buffer Extension (BRBE)

From: Rob Herring <robh@kernel.org>
Date: 2025-05-29 17:27:33
Also in: kvmarm, linux-doc, linux-perf-users, lkml
Subsystem: arm pmu profiling and debugging, the rest · Maintainers: Will Deacon, Mark Rutland, Linus Torvalds

On Wed, May 21, 2025 at 11:03 AM James Clark [off-list ref] wrote:
On 20/05/2025 11:27 pm, Rob Herring (Arm) wrote:
quoted
The ARMv9.2 architecture introduces the optional Branch Record Buffer
Extension (BRBE), which records information about branches as they are
executed into set of branch record registers. BRBE is similar to x86's
Last Branch Record (LBR) and PowerPC's Branch History Rolling Buffer
(BHRB).
[...]
quoted
+/*
+ * BRBE is configured with an OR of permissions from all events, so there may
+ * be events which have to be dropped or events where just the source or target
+ * address has to be zeroed.
+ */
+static bool filter_branch_privilege(struct perf_branch_entry *entry, u64 branch_sample_type)
+{
+     /* We can only have a half record if permissions have not been expanded */
+     if (!entry->from || !entry->to)
+             return true;
+
+     bool from_user = access_ok((void __user *)(unsigned long)entry->from, 4);
+     bool to_user = access_ok((void __user *)(unsigned long)entry->to, 4);
+     bool exclude_kernel = !((branch_sample_type & PERF_SAMPLE_BRANCH_KERNEL) ||
+             (is_kernel_in_hyp_mode() && (branch_sample_type & PERF_SAMPLE_BRANCH_HV)));
+
+     /*
+      * If record is within a single exception level, just need to either
+      * drop or keep the entire record.
+      */
+     if (from_user == to_user)
+             return ((entry->priv == PERF_BR_PRIV_KERNEL) && !exclude_kernel) ||
+                     ((entry->priv == PERF_BR_PRIV_USER) &&
+                      (branch_sample_type & PERF_SAMPLE_BRANCH_USER));
+
+     // Fixup calls which are syscalls
+     if (entry->type == PERF_BR_CALL && from_user && !to_user)
+             entry->type = PERF_BR_SYSCALL;
+
+     /*
+      * Record is across exception levels, mask addresses for the exception
+      * level we're not capturing.
+      */
+     if (!(branch_sample_type & PERF_SAMPLE_BRANCH_USER)) {
+             if (from_user)
+                     entry->from = 0;
+             if (to_user)
+                     entry->to = 0;
+     }
+
+     if (exclude_kernel) {
+             if (!from_user)
+                     entry->from = 0;
+             if (!to_user)
+                     entry->to = 0;
+     }
+     return true;
+}
+
+static bool filter_branch_type(struct perf_branch_entry *entry,
+                            const unsigned long *event_type_mask)
+{
+     if (entry->type == PERF_BR_EXTEND_ABI)
+             return test_bit(PERF_BR_MAX + entry->new_type, event_type_mask);
+     else
+             return test_bit(entry->type, event_type_mask);
+}
+
+static bool filter_branch_record(struct perf_branch_entry *entry,
+                              u64 branch_sample,
+                              const unsigned long *event_type_mask)
+{
+     return filter_branch_type(entry, event_type_mask) &&
+             filter_branch_privilege(entry, branch_sample);
filter_branch_privilege() sometimes changes the branch type for
PERF_BR_SYSCALL so I think it should come before filter_branch_type(). I
didn't see any actual issue caused by this, but it's a bit hard to
review to see if it's working correctly.
Looking at this again, I think we can drop that with this change:
diff --git a/drivers/perf/arm_brbe.c b/drivers/perf/arm_brbe.c
index 2f254bd40af3..acdde61a8559 100644
--- a/drivers/perf/arm_brbe.c
+++ b/drivers/perf/arm_brbe.c
@@ -546,7 +546,7 @@ static const int
brbe_type_to_perf_type_map[BRBINFx_EL1_TYPE_DEBUG_EXIT + 1][2]
        [BRBINFx_EL1_TYPE_INDIRECT_LINK] = { PERF_BR_IND_CALL, 0 },
        [BRBINFx_EL1_TYPE_RET] = { PERF_BR_RET, 0 },
        [BRBINFx_EL1_TYPE_DIRECT_COND] = { PERF_BR_COND, 0 },
-       [BRBINFx_EL1_TYPE_CALL] = { PERF_BR_CALL, 0 },
+       [BRBINFx_EL1_TYPE_CALL] = { PERF_BR_SYSCALL, 0 },
        [BRBINFx_EL1_TYPE_ERET] = { PERF_BR_ERET, 0 },
        [BRBINFx_EL1_TYPE_IRQ] = { PERF_BR_IRQ, 0 },
        [BRBINFx_EL1_TYPE_TRAP] = { PERF_BR_IRQ, 0 },

AFAICT, the only cases for BRBINFx_EL1_TYPE_CALL are SVC, SMC, and HVC.

Rob
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help