From 5eaa2ead9b12764dc85ab7c0dcb619aa200154c1 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Mon, 18 Mar 2024 18:54:05 +0530 Subject: [PATCH] Processwatch: Add support for AMD Zen processors Processwatch uses software pmu cpu-clock event if it fails to detect the architecture. Although it works, cpu-clock event is imprecise. On AMD, ibs_op// pmu provides precise sampling, so port Processwatch to use ibs_op// on AMD. Notes: o Processwatch with systemwide mode is supported with all versions of kernel. However, IBS on Linux supports per-process sampling only with v6.2 and later kernels. Thus processwatch with per-process mode (--pid=[pid] / -p option) is supported only with v6.2 and later. o Processwatch fails to run with "WARNING: CPU xxx is offline." on AMD machines where some cores fused off in the hw. The issue is with how kernel exposes number of possible cpus in the sysfs. Below kernel patches fixed the issue. v5.16 aa06e20f1be6 ("x86/ACPI: Don't add CPUs that are not online capable") v6.3 e2869bd7af60 ("x86/acpi/boot: Do not register processors that cannot be onlined for x2APIC") o Currently IBS is not supported in KVM guest and thus Processwatch falls back to cpu-clock event when run inside the KVM guest. o TMA support is not added for AMD. Signed-off-by: Ravi Bangoria --- src/kerninfo.h | 41 +++++++++++++++++++++++++++++++++++++++++ src/setup_bpf.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/kerninfo.h b/src/kerninfo.h index 04f3b18..6074e26 100644 --- a/src/kerninfo.h +++ b/src/kerninfo.h @@ -6,10 +6,51 @@ #include +void get_vendor(char *vendor) { + unsigned int a[4]; + + asm ( + /* %rbx must be preserved. */ + "mov %%rbx, %%rdi\n" + "cpuid\n" + "xchg %%rdi, %%rbx\n" + : "=a"(a[0]), "=D"(a[1]), "=c"(a[2]), "=d"(a[3]) + : "a"(0) + ); + strncpy(&vendor[0], (char *)&a[1], 4); + strncpy(&vendor[4], (char *)&a[3], 4); + strncpy(&vendor[8], (char *)&a[2], 4); +} + +int is_amd_arch(void) { + static int amd = -1; /* -1: Unknown, 1: Yes, 0: No */ + char vendor[13] = {0}; + + if (amd != -1) + return amd; + + get_vendor(vendor); + amd = strcmp(vendor, "AuthenticAMD") ? 0 : 1; + return amd; +} + void get_pmu_string(char *pmu_name) { FILE *f; size_t retval; + if (is_amd_arch()) { + f = fopen("/sys/bus/event_source/devices/ibs_op", "r"); + if (!f) { + fprintf(stderr, "WARNING: Unable to open '/sys/bus/event_source/devices/ibs_op'. " + "Using software events.\n"); + strcpy(pmu_name, "invalid"); + return; + } + fclose(f); + strcpy(pmu_name, "ibs_op"); + return; + } + f = fopen("/sys/devices/cpu/caps/pmu_name", "r"); if(!f) { fprintf(stderr, "WARNING: Unable to open '/sys/devices/cpu/caps/pmu_name'. Using software events.\n"); diff --git a/src/setup_bpf.h b/src/setup_bpf.h index b5fee31..5e9cd17 100644 --- a/src/setup_bpf.h +++ b/src/setup_bpf.h @@ -147,6 +147,35 @@ static int single_tma_event(struct event *e, struct event *leader, #else +/* Return value: >0: Valid, -1: Error */ +static int get_ibs_op_type(void) { + static int type = -1; /* -1 : Unknown, 0: Failed to read first time, >0: Valid */ + FILE *fp; + int ret; + + if (type != -1) { + if (!type) + return -1; + return type; + } + + fp = fopen("/sys/bus/event_source/devices/ibs_op/type", "r"); + if (!fp) { + fprintf(stderr, "Failed to find ibs_op// pmu sysfs. [%m]\n"); + type = 0; + return -1; + } + + ret = fscanf(fp, "%d", &type); + fclose(fp); + if (ret != 1) { + fprintf(stderr, "Failed to read ibs_op// type. [%m]\n"); + type = 0; + return -1; + } + return type; +} + /** single_insn_event - Handles a single CPU, PMU, socket event. Returns: @@ -177,6 +206,12 @@ static int single_insn_event(int cpu, int pid) { } else if(strncmp(bpf_info->pmu_name, "sapphire_rapids", 7) == 0) { attr.type = PERF_TYPE_RAW; attr.config = 0x00c0; + } else if(strncmp(bpf_info->pmu_name, "ibs_op", 6) == 0) { + attr.type = get_ibs_op_type(); + if (attr.type < 0) + return -1; + attr.config = 0x80000; + attr.exclude_guest = 0; } else { attr.type = PERF_TYPE_SOFTWARE; attr.config = PERF_COUNT_SW_CPU_CLOCK;