Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

topdown: Protect topdown on heterogeneous processors with rseq #329

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

willowec
Copy link
Contributor

Pull Request Description

This PR extends #286 with librseq to guarantee that attempting to access the PERF_METRICS MSR on an Intel heterogeneous multicore processor will never result in a segmentation fault in the event of an unexpected process migration from p-core to e-core.

The topdown component dynamically loads librseq, and falls back to the protections introduced in #286 if librseq cannot be found or loaded. This PR is tested on a Raptor Lake machine, with patched version of librseq that fixes an issue preventing it from being dynamically loaded.

As of the opening of this PR, librseq cannot be dynamically loaded due to a static thread-local storage structure it uses. However, the maintainer has indicated that this will be removed in the future (see librseq #22). Until it is removed, there is no urgency to merging this PR as it doesn't offer any benefits without a dynamically loadable librseq.

Thanks to Mathieu Desnoyers for the help understanding how restartable sequences work, for creating librseq, and for the help with how to use it.

Author Checklist

  • Description
    Why this PR exists. Reference all relevant information, including background, issues, test failures, etc
  • Commits
    Commits are self contained and only do one thing
    Commits have a header of the form: module: short description
    Commits have a body (whenever relevant) containing a detailed description of the addressed problem and its solution
  • Tests
    The PR needs to pass all the tests

…S MSR

Add a component that collects Intel's topdown metrics from the
PERF_METRICS MSR and automatically converts the raw metric values to
user-consumable percentages.

The intent of this component is to provide an intuitive interface for
accessing topdown metrics on the supported processors.

Tested on a RaptorLake-S/HX machine (family/model/stepping
0x6/0xb7/0x1). To add other supported architectures the switch statment
in _topdown_init_component() should be populated for the architecture's
model number, whether it supports level 2 topdown metrics, and in the
case of a heterogeneous processor what core type it must be run on.
While the offical Software Developer Manual only lists the availability
of the PERF_METRICS MSR for three architectures, we can use the
'perfmon' repository maintained by Intel to discover what architectures
support the MSR (repo here: https://github.com/intel/perfmon).

Architectures that the repository demonstrates support the events
'PERF_METRICS.BACKEND_BOUND', 'PERF_METRICS.FRONTEND_BOUND', etc. must
support the topdown level 1 metrics of the PERF_METRICS MSR. Similarly,
the presence of the events 'PERF_METRICS.FETCH_LATENCY',
'PERF_METRICS.MEMORY_BOUND', etc. demonstrates support for topdown L2
metrics in the PERF_METRICS MSR. By cross-referencing the architecture
names in the perfmon repository with their DisplayFamily/DisplayModel
values in Table 2-1 of volume 4 of the IA32 SDM, we can add support for
the following architectures:

- Rocket Lake
- Ice Lake (icl & icx)
- Tiger Lake
- Sapphire Rapids
- Meteor Lake (redwood cove p-core only)
- Alder Lake (golden cove p-core only)
- Granite Rapids
- Everald Rapids

None of these additional architectures have been tested with
the topdown component yet. While Arrow Lake is shown to support L1 &
L2 metrics in the prefmon repository, its FamilyModel is not yet
available in the IA32 SDM so it has not been added.
All of Intel's heterogeneous CPUs that support the PERF_METRICS MSR only
support it for their performance (p-core) cores. This means that if a
program that is being measured using the topdown component in PAPI
happens to be rescheduled to a e-core during its runtime, PAPI will
segfault.

To fix this, add a check in _topdown_start() and _topdown_stop() to exit
gracefully if the core affinity of the process has changed to an
unsupported core type.
Previously, the x86intrin.h header file had been included in order to
provide definitions for _rdpmc(). However, this has caused the github
actions testing compilation of the component on ARM systems to fail.
Therefore, remove the include and add a manual definition for _rdpmc()
taken from the perf_event component.
To prevent programs using the topdown component on heterogeneous
processors that only supply the PERF_METRICS MSR on some of their cores
from segfaulting due to trying to read the MSR after being moved to an
unsupported core type, the topdown component periodically checks it is
on a supported core and exits if not.

Previously, this check occured at the start of PAPI_start and PAPI_stop.
After writing a script that starts a program being calipered with the
topdown component and moves it to an unsupported core after a random
amount of time, for N=100,000 tests the heterogeneous checks failed to
prevent a segmentation fault 0.08% of the time. This patch moves the
heterogeneous checks to occur only directly before the rdpmc calls,
resulting in cleaner code and a reduced segfault prevention failure
rate of 0.064%.

While it is frustrating that the failure rate is non-zero, since there
appears to be no way to tell a process to ignore changes to its
affinity, I believe there to be no perfect solution at this time.
Previously, the topdown component calculated metrics by taking the
difference of the metrics before and the metrics after the calculated
code block using Equation 1:

M% = (Mb*Sb/255 - Ma*Sa/255) / (Sb - Sa) * 100                   (1)

where Mx are the raw bytes of the metric before and after the calipered
code block and Sx are the slots. However, if Sa = 0 this simplifies to

M% = Mb/255 * 100                                                (2)

Therefore it is sufficient to simply reset the PERF_METRICS MSR and
SLOTS during PAPI_start() and then use Equation 2 in PAPI_stop, reducing
the number of dangerous rdpmc calls, reducing overhead, and simplifying
the code.
On Intel's heterogeneous multicore processors such as Raptor Lake, the
PERF_METRICS MSR is only available on the performance cores (p-cores).
If the rdpmc instruction is executed attempting to access the MSR while
the process is on an efficient core (e-core), a segmentation fault
occurs.

Previously, the topdown component has used a simple check before every
execution of the rdpmc instruction to ensure the core the program is
bound to is a p-core. However, this can fail if the program is moved to
another core between the check and the execution of rdpmc. While rare, a
worst-case scenario test that repeatedly moves a program which is using
the topdown component from p-core to e-core at a random time saw 338
segmentation faults out of 1 million affinity switches (a 0.0338% error
rate). This is a non-zero number of segmentation faults, and we can do
better.

Use librseq to protect the rdpmc instruction with a restartable
sequence (rseq). When the process is preempted by an affinity change,
the sequence immediately aborts and can be restarted. By keeping the
check that the process is on a p-core and the rdpmc instruction itself
within the critical section of the rseq, it is guaranteed that the
rdpmc instruction will never be executed on an invalid core. The same
test described in the previous paragraph sees 0 segmentation faults.
@willowec willowec force-pushed the topdown-rdpmc-protected branch from 9071109 to 8731432 Compare March 12, 2025 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant