]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf/x86/intel: Improve validation and configuration of ACR masks
authorDapeng Mi <dapeng1.mi@linux.intel.com>
Thu, 30 Apr 2026 00:25:54 +0000 (08:25 +0800)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 5 May 2026 10:47:21 +0000 (12:47 +0200)
Currently there are several issues on the user space ACR mask validation
and configuration.
- The validation for user space ACR mask (attr.config2) is incomplete,
  e.g., the ACR mask could include the index which belongs to another
  ACR events group, but it's not validated.
- An early return on an invalid ACR mask caused all subsequent ACR groups
  to be skipped.
- The stale hardware ACR mask (hw.config1) is not cleared before setting
  new hardware ACR mask.

The following changes address all of the above issues.
- Figure out the event index group of an ACR group. Any bits in the
  user-space mask not present in the index group are now dropped.
- Instead of an early return on invalid bits, drop only the invalid
  portions and continue iterating through all ACR events to ensure full
  configuration.
- Explicitly clear the stale hardware ACR mask for each event prior to
  writing the new configuration.

Besides, a non-leader event member of ACR group could be disabled in
theory. This could cause bit-shifting errors in the acr_mask of remaining
group members. But since ACR sampling requires all events to be active,
this should not be a big concern in real use case. Add a "FIXME" comment
to notice this risk.

Fixes: ec980e4facef ("perf/x86/intel: Support auto counter reload")
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260430002558.712334-2-dapeng1.mi@linux.intel.com
arch/x86/events/intel/core.c

index d9488ade0f8ec4c275a8d6c50565802d4f86368e..f8deb67b3c51131ed740eb616010e2c3ac646b81 100644 (file)
@@ -3332,23 +3332,41 @@ static void intel_pmu_enable_event(struct perf_event *event)
 static void intel_pmu_acr_late_setup(struct cpu_hw_events *cpuc)
 {
        struct perf_event *event, *leader;
-       int i, j, idx;
+       int i, j, k, bit, idx;
 
+       /*
+        * FIXME: ACR mask parsing relies on cpuc->event_list[] (active events only).
+        * Disabling an ACR event causes bit-shifting errors in the acr_mask of
+        * remaining group members. As ACR sampling requires all events to be active,
+        * this limitation is acceptable for now. Revisit if independent event toggling
+        * is required.
+        */
        for (i = 0; i < cpuc->n_events; i++) {
                leader = cpuc->event_list[i];
                if (!is_acr_event_group(leader))
                        continue;
 
-               /* The ACR events must be contiguous. */
+               /* Find the last event of the ACR group. */
                for (j = i; j < cpuc->n_events; j++) {
                        event = cpuc->event_list[j];
                        if (event->group_leader != leader->group_leader)
                                break;
-                       for_each_set_bit(idx, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) {
-                               if (i + idx >= cpuc->n_events ||
-                                   !is_acr_event_group(cpuc->event_list[i + idx]))
-                                       return;
-                               __set_bit(cpuc->assign[i + idx], (unsigned long *)&event->hw.config1);
+               }
+
+               /*
+                * Translate the user-space ACR mask (attr.config2) into the physical
+                * counter bitmask (hw.config1) for each ACR event in the group.
+                * NOTE: ACR event contiguity is guaranteed by intel_pmu_hw_config().
+                */
+               for (k = i; k < j; k++) {
+                       event = cpuc->event_list[k];
+                       event->hw.config1 = 0;
+                       for_each_set_bit(bit, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) {
+                               idx = i + bit;
+                               /* Event index of ACR group must locate in [i, j). */
+                               if (idx >= j || !is_acr_event_group(cpuc->event_list[idx]))
+                                       continue;
+                               __set_bit(cpuc->assign[idx], (unsigned long *)&event->hw.config1);
                        }
                }
                i = j - 1;