Re: [PATCH v2] perf test amd ibs: avoid using executable heap

From: Ian Rogers

Date: Wed Jul 01 2026 - 14:57:52 EST


On Wed, Jul 1, 2026 at 9:59 AM Ravi Bangoria <ravi.bangoria@xxxxxxx> wrote:
>
> Hi Peter, Ondrej,
>
> >> permission under SELinux (things like JIT or regex compilation need it
> >> as well). mmap() with MAP_ANONYMOUS will give us a zeroed mapping that
> >> begins on a page boundary, so the result is equivalent to the original
> >> code even without a memset() or the page-alignment dance.
> >
> > I would argue that having RWX is a problem, you really want RW->RO->RX
> > transitions, so even with mmap() you want to combine with mprotect().
>
> My original intent for using RWX was to generate sufficient Icache miss
> samples for the IBS Fetch unit by overwriting the code prior to execution.
> I am wondering whether it would be possible to achieve the same result
> by using CLFLUSH with RX permissions. Something like below (build tested
> only).

(Minor 2 cents) To make the code more canonical JIT code it should
probably use memory protection keys for permissions.

Thanks,
Ian

> --- a/tools/perf/arch/x86/tests/amd-ibs-period.c
> +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c
> @@ -25,6 +25,7 @@ static int page_size;
> #define PERF_MMAP_TOTAL_PAGES (PERF_MMAP_DATA_PAGES + 1)
> #define PERF_MMAP_TOTAL_SIZE (PERF_MMAP_TOTAL_PAGES * page_size)
>
> +#define mb() asm volatile("mfence":::"memory")
> #define rmb() asm volatile("lfence":::"memory")
>
> enum {
> @@ -41,10 +42,16 @@ struct perf_pmu *fetch_pmu;
> struct perf_pmu *op_pmu;
> unsigned int perf_event_max_sample_rate;
>
> +static inline void clflush(const volatile void *p)
> +{
> + asm volatile("clflush (%0)" :: "r"(p) : "memory");
> +}
> +
> /* Dummy workload to generate IBS samples. */
> static int dummy_workload_1(unsigned long count)
> {
> - int (*func)(void);
> + int (*func1)(void);
> + int (*func2)(void);
> int ret = 0;
> char *p;
> char insn1[] = {
> @@ -59,33 +66,42 @@ static int dummy_workload_1(unsigned long count)
> 0xcc, /* int 3 */
> };
>
> - p = calloc(2, page_size);
> - if (!p) {
> - printf("malloc() failed. %m");
> +
> + p = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
> + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> + if (p == MAP_FAILED) {
> + printf("mmap() failed. %m");
> return 1;
> }
>
> - func = (void *)((unsigned long)(p + page_size - 1) & ~(page_size - 1));
> + memcpy(p, insn1, sizeof(insn1));
> + memcpy(p + 128, insn2, sizeof(insn2));
>
> - ret = mprotect(func, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
> + ret = mprotect(p, page_size, PROT_READ | PROT_EXEC);
> if (ret) {
> printf("mprotect() failed. %m");
> goto out;
> }
>
> + func1 = (void *)(p);
> + func2 = (void *)(p + 128);
> +
> if (count < 100000)
> count = 100000;
> else if (count > 10000000)
> count = 10000000;
> while (count--) {
> - memcpy((void *)func, insn1, sizeof(insn1));
> - if (func() != 1) {
> + clflush(func1);
> + mb();
> + if (func1() != 1) {
> pr_debug("ERROR insn1\n");
> ret = -1;
> goto out;
> }
> - memcpy((void *)func, insn2, sizeof(insn2));
> - if (func() != 2) {
> +
> + clflush(func2);
> + mb();
> + if (func2() != 2) {
> pr_debug("ERROR insn2\n");
> ret = -1;
> goto out;
> @@ -93,7 +109,7 @@ static int dummy_workload_1(unsigned long count)
> }
>
> out:
> - free(p);
> + munmap(p, page_size);
> return ret;
> }
>
> ---
>
> Thanks,
> Ravi