Path: blob/master/tools/perf/arch/x86/tests/amd-ibs-period.c
26292 views
// SPDX-License-Identifier: GPL-2.01#include <sched.h>2#include <sys/syscall.h>3#include <sys/mman.h>4#include <sys/ioctl.h>5#include <sys/utsname.h>6#include <string.h>78#include "arch-tests.h"9#include "linux/perf_event.h"10#include "linux/zalloc.h"11#include "tests/tests.h"12#include "../perf-sys.h"13#include "pmu.h"14#include "pmus.h"15#include "debug.h"16#include "util.h"17#include "strbuf.h"18#include "../util/env.h"1920static int page_size;2122#define PERF_MMAP_DATA_PAGES 32L23#define PERF_MMAP_DATA_SIZE (PERF_MMAP_DATA_PAGES * page_size)24#define PERF_MMAP_DATA_MASK (PERF_MMAP_DATA_SIZE - 1)25#define PERF_MMAP_TOTAL_PAGES (PERF_MMAP_DATA_PAGES + 1)26#define PERF_MMAP_TOTAL_SIZE (PERF_MMAP_TOTAL_PAGES * page_size)2728#define rmb() asm volatile("lfence":::"memory")2930enum {31FD_ERROR,32FD_SUCCESS,33};3435enum {36IBS_FETCH,37IBS_OP,38};3940struct perf_pmu *fetch_pmu;41struct perf_pmu *op_pmu;42unsigned int perf_event_max_sample_rate;4344/* Dummy workload to generate IBS samples. */45static int dummy_workload_1(unsigned long count)46{47int (*func)(void);48int ret = 0;49char *p;50char insn1[] = {510xb8, 0x01, 0x00, 0x00, 0x00, /* mov 1,%eax */520xc3, /* ret */530xcc, /* int 3 */54};5556char insn2[] = {570xb8, 0x02, 0x00, 0x00, 0x00, /* mov 2,%eax */580xc3, /* ret */590xcc, /* int 3 */60};6162p = zalloc(2 * page_size);63if (!p) {64printf("malloc() failed. %m");65return 1;66}6768func = (void *)((unsigned long)(p + page_size - 1) & ~(page_size - 1));6970ret = mprotect(func, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);71if (ret) {72printf("mprotect() failed. %m");73goto out;74}7576if (count < 100000)77count = 100000;78else if (count > 10000000)79count = 10000000;80while (count--) {81memcpy((void *)func, insn1, sizeof(insn1));82if (func() != 1) {83pr_debug("ERROR insn1\n");84ret = -1;85goto out;86}87memcpy((void *)func, insn2, sizeof(insn2));88if (func() != 2) {89pr_debug("ERROR insn2\n");90ret = -1;91goto out;92}93}9495out:96free(p);97return ret;98}99100/* Another dummy workload to generate IBS samples. */101static void dummy_workload_2(char *perf)102{103char bench[] = " bench sched messaging -g 10 -l 5000 > /dev/null 2>&1";104char taskset[] = "taskset -c 0 ";105int ret __maybe_unused;106struct strbuf sb;107char *cmd;108109strbuf_init(&sb, 0);110strbuf_add(&sb, taskset, strlen(taskset));111strbuf_add(&sb, perf, strlen(perf));112strbuf_add(&sb, bench, strlen(bench));113cmd = strbuf_detach(&sb, NULL);114ret = system(cmd);115free(cmd);116}117118static int sched_affine(int cpu)119{120cpu_set_t set;121122CPU_ZERO(&set);123CPU_SET(cpu, &set);124if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {125pr_debug("sched_setaffinity() failed. [%m]");126return -1;127}128return 0;129}130131static void132copy_sample_data(void *src, unsigned long offset, void *dest, size_t size)133{134size_t chunk1_size, chunk2_size;135136if ((offset + size) < (size_t)PERF_MMAP_DATA_SIZE) {137memcpy(dest, src + offset, size);138} else {139chunk1_size = PERF_MMAP_DATA_SIZE - offset;140chunk2_size = size - chunk1_size;141142memcpy(dest, src + offset, chunk1_size);143memcpy(dest + chunk1_size, src, chunk2_size);144}145}146147static int rb_read(struct perf_event_mmap_page *rb, void *dest, size_t size)148{149void *base;150unsigned long data_tail, data_head;151152/* Casting to (void *) is needed. */153base = (void *)rb + page_size;154155data_head = rb->data_head;156rmb();157data_tail = rb->data_tail;158159if ((data_head - data_tail) < size)160return -1;161162data_tail &= PERF_MMAP_DATA_MASK;163copy_sample_data(base, data_tail, dest, size);164rb->data_tail += size;165return 0;166}167168static void rb_skip(struct perf_event_mmap_page *rb, size_t size)169{170size_t data_head = rb->data_head;171172rmb();173174if ((rb->data_tail + size) > data_head)175rb->data_tail = data_head;176else177rb->data_tail += size;178}179180/* Sample period value taken from perf sample must match with expected value. */181static int period_equal(unsigned long exp_period, unsigned long act_period)182{183return exp_period == act_period ? 0 : -1;184}185186/*187* Sample period value taken from perf sample must be >= minimum sample period188* supported by IBS HW.189*/190static int period_higher(unsigned long min_period, unsigned long act_period)191{192return min_period <= act_period ? 0 : -1;193}194195static int rb_drain_samples(struct perf_event_mmap_page *rb,196unsigned long exp_period,197int *nr_samples,198int (*callback)(unsigned long, unsigned long))199{200struct perf_event_header hdr;201unsigned long period;202int ret = 0;203204/*205* PERF_RECORD_SAMPLE:206* struct {207* struct perf_event_header hdr;208* { u64 period; } && PERF_SAMPLE_PERIOD209* };210*/211while (1) {212if (rb_read(rb, &hdr, sizeof(hdr)))213return ret;214215if (hdr.type == PERF_RECORD_SAMPLE) {216(*nr_samples)++;217period = 0;218if (rb_read(rb, &period, sizeof(period)))219pr_debug("rb_read(period) error. [%m]");220ret |= callback(exp_period, period);221} else {222rb_skip(rb, hdr.size - sizeof(hdr));223}224}225return ret;226}227228static long perf_event_open(struct perf_event_attr *attr, pid_t pid,229int cpu, int group_fd, unsigned long flags)230{231return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);232}233234static void fetch_prepare_attr(struct perf_event_attr *attr,235unsigned long long config, int freq,236unsigned long sample_period)237{238memset(attr, 0, sizeof(struct perf_event_attr));239240attr->type = fetch_pmu->type;241attr->size = sizeof(struct perf_event_attr);242attr->config = config;243attr->disabled = 1;244attr->sample_type = PERF_SAMPLE_PERIOD;245attr->freq = freq;246attr->sample_period = sample_period; /* = ->sample_freq */247}248249static void op_prepare_attr(struct perf_event_attr *attr,250unsigned long config, int freq,251unsigned long sample_period)252{253memset(attr, 0, sizeof(struct perf_event_attr));254255attr->type = op_pmu->type;256attr->size = sizeof(struct perf_event_attr);257attr->config = config;258attr->disabled = 1;259attr->sample_type = PERF_SAMPLE_PERIOD;260attr->freq = freq;261attr->sample_period = sample_period; /* = ->sample_freq */262}263264struct ibs_configs {265/* Input */266unsigned long config;267268/* Expected output */269unsigned long period;270int fd;271};272273/*274* Somehow first Fetch event with sample period = 0x10 causes 0275* samples. So start with large period and decrease it gradually.276*/277struct ibs_configs fetch_configs[] = {278{ .config = 0xffff, .period = 0xffff0, .fd = FD_SUCCESS },279{ .config = 0x1000, .period = 0x10000, .fd = FD_SUCCESS },280{ .config = 0xff, .period = 0xff0, .fd = FD_SUCCESS },281{ .config = 0x1, .period = 0x10, .fd = FD_SUCCESS },282{ .config = 0x0, .period = -1, .fd = FD_ERROR },283{ .config = 0x10000, .period = -1, .fd = FD_ERROR },284};285286struct ibs_configs op_configs[] = {287{ .config = 0x0, .period = -1, .fd = FD_ERROR },288{ .config = 0x1, .period = -1, .fd = FD_ERROR },289{ .config = 0x8, .period = -1, .fd = FD_ERROR },290{ .config = 0x9, .period = 0x90, .fd = FD_SUCCESS },291{ .config = 0xf, .period = 0xf0, .fd = FD_SUCCESS },292{ .config = 0x1000, .period = 0x10000, .fd = FD_SUCCESS },293{ .config = 0xffff, .period = 0xffff0, .fd = FD_SUCCESS },294{ .config = 0x10000, .period = -1, .fd = FD_ERROR },295{ .config = 0x100000, .period = 0x100000, .fd = FD_SUCCESS },296{ .config = 0xf00000, .period = 0xf00000, .fd = FD_SUCCESS },297{ .config = 0xf0ffff, .period = 0xfffff0, .fd = FD_SUCCESS },298{ .config = 0x1f0ffff, .period = 0x1fffff0, .fd = FD_SUCCESS },299{ .config = 0x7f0ffff, .period = 0x7fffff0, .fd = FD_SUCCESS },300{ .config = 0x8f0ffff, .period = -1, .fd = FD_ERROR },301{ .config = 0x17f0ffff, .period = -1, .fd = FD_ERROR },302};303304static int __ibs_config_test(int ibs_type, struct ibs_configs *config, int *nr_samples)305{306struct perf_event_attr attr;307int fd, i;308void *rb;309int ret = 0;310311if (ibs_type == IBS_FETCH)312fetch_prepare_attr(&attr, config->config, 0, 0);313else314op_prepare_attr(&attr, config->config, 0, 0);315316/* CPU0, All processes */317fd = perf_event_open(&attr, -1, 0, -1, 0);318if (config->fd == FD_ERROR) {319if (fd != -1) {320close(fd);321return -1;322}323return 0;324}325if (fd <= -1)326return -1;327328rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,329MAP_SHARED, fd, 0);330if (rb == MAP_FAILED) {331pr_debug("mmap() failed. [%m]\n");332return -1;333}334335ioctl(fd, PERF_EVENT_IOC_RESET, 0);336ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);337338i = 5;339while (i--) {340dummy_workload_1(1000000);341342ret = rb_drain_samples(rb, config->period, nr_samples,343period_equal);344if (ret)345break;346}347348ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);349munmap(rb, PERF_MMAP_TOTAL_SIZE);350close(fd);351return ret;352}353354static int ibs_config_test(void)355{356int nr_samples = 0;357unsigned long i;358int ret = 0;359int r;360361pr_debug("\nIBS config tests:\n");362pr_debug("-----------------\n");363364pr_debug("Fetch PMU tests:\n");365for (i = 0; i < ARRAY_SIZE(fetch_configs); i++) {366nr_samples = 0;367r = __ibs_config_test(IBS_FETCH, &(fetch_configs[i]), &nr_samples);368369if (fetch_configs[i].fd == FD_ERROR) {370pr_debug("0x%-16lx: %-4s\n", fetch_configs[i].config,371!r ? "Ok" : "Fail");372} else {373/*374* Although nr_samples == 0 is reported as Fail here,375* the failure status is not cascaded up because, we376* can not decide whether test really failed or not377* without actual samples.378*/379pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", fetch_configs[i].config,380(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);381}382383ret |= r;384}385386pr_debug("Op PMU tests:\n");387for (i = 0; i < ARRAY_SIZE(op_configs); i++) {388nr_samples = 0;389r = __ibs_config_test(IBS_OP, &(op_configs[i]), &nr_samples);390391if (op_configs[i].fd == FD_ERROR) {392pr_debug("0x%-16lx: %-4s\n", op_configs[i].config,393!r ? "Ok" : "Fail");394} else {395/*396* Although nr_samples == 0 is reported as Fail here,397* the failure status is not cascaded up because, we398* can not decide whether test really failed or not399* without actual samples.400*/401pr_debug("0x%-16lx: %-4s (nr samples: %d)\n", op_configs[i].config,402(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);403}404405ret |= r;406}407408return ret;409}410411struct ibs_period {412/* Input */413int freq;414unsigned long sample_freq;415416/* Output */417int ret;418unsigned long period;419};420421struct ibs_period fetch_period[] = {422{ .freq = 0, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },423{ .freq = 0, .sample_freq = 1, .ret = FD_ERROR, .period = -1 },424{ .freq = 0, .sample_freq = 0xf, .ret = FD_ERROR, .period = -1 },425{ .freq = 0, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x10 },426{ .freq = 0, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x10 },427{ .freq = 0, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x80 },428{ .freq = 0, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },429{ .freq = 0, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },430{ .freq = 0, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x4d0 },431{ .freq = 0, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x1000 },432{ .freq = 0, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0xfff0 },433{ .freq = 0, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0xfff0 },434{ .freq = 0, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10010 },435{ .freq = 0, .sample_freq = 0x7fffff, .ret = FD_SUCCESS, .period = 0x7ffff0 },436{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },437{ .freq = 1, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },438{ .freq = 1, .sample_freq = 1, .ret = FD_SUCCESS, .period = 0x10 },439{ .freq = 1, .sample_freq = 0xf, .ret = FD_SUCCESS, .period = 0x10 },440{ .freq = 1, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x10 },441{ .freq = 1, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x10 },442{ .freq = 1, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x10 },443{ .freq = 1, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x10 },444{ .freq = 1, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x10 },445{ .freq = 1, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x10 },446{ .freq = 1, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x10 },447{ .freq = 1, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0x10 },448{ .freq = 1, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0x10 },449{ .freq = 1, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10 },450/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */451{ .freq = 1, .sample_freq = 0x7fffff, .ret = FD_ERROR, .period = -1 },452};453454struct ibs_period op_period[] = {455{ .freq = 0, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },456{ .freq = 0, .sample_freq = 1, .ret = FD_ERROR, .period = -1 },457{ .freq = 0, .sample_freq = 0xf, .ret = FD_ERROR, .period = -1 },458{ .freq = 0, .sample_freq = 0x10, .ret = FD_ERROR, .period = -1 },459{ .freq = 0, .sample_freq = 0x11, .ret = FD_ERROR, .period = -1 },460{ .freq = 0, .sample_freq = 0x8f, .ret = FD_ERROR, .period = -1 },461{ .freq = 0, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },462{ .freq = 0, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },463{ .freq = 0, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x4d0 },464{ .freq = 0, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x1000 },465{ .freq = 0, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0xfff0 },466{ .freq = 0, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0xfff0 },467{ .freq = 0, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x10010 },468{ .freq = 0, .sample_freq = 0x7fffff, .ret = FD_SUCCESS, .period = 0x7ffff0 },469{ .freq = 0, .sample_freq = 0xfffffff, .ret = FD_SUCCESS, .period = 0xffffff0 },470{ .freq = 1, .sample_freq = 0, .ret = FD_ERROR, .period = -1 },471{ .freq = 1, .sample_freq = 1, .ret = FD_SUCCESS, .period = 0x90 },472{ .freq = 1, .sample_freq = 0xf, .ret = FD_SUCCESS, .period = 0x90 },473{ .freq = 1, .sample_freq = 0x10, .ret = FD_SUCCESS, .period = 0x90 },474{ .freq = 1, .sample_freq = 0x11, .ret = FD_SUCCESS, .period = 0x90 },475{ .freq = 1, .sample_freq = 0x8f, .ret = FD_SUCCESS, .period = 0x90 },476{ .freq = 1, .sample_freq = 0x90, .ret = FD_SUCCESS, .period = 0x90 },477{ .freq = 1, .sample_freq = 0x91, .ret = FD_SUCCESS, .period = 0x90 },478{ .freq = 1, .sample_freq = 0x4d2, .ret = FD_SUCCESS, .period = 0x90 },479{ .freq = 1, .sample_freq = 0x1007, .ret = FD_SUCCESS, .period = 0x90 },480{ .freq = 1, .sample_freq = 0xfff0, .ret = FD_SUCCESS, .period = 0x90 },481{ .freq = 1, .sample_freq = 0xffff, .ret = FD_SUCCESS, .period = 0x90 },482{ .freq = 1, .sample_freq = 0x10010, .ret = FD_SUCCESS, .period = 0x90 },483/* ret=FD_ERROR because freq > default perf_event_max_sample_rate (100000) */484{ .freq = 1, .sample_freq = 0x7fffff, .ret = FD_ERROR, .period = -1 },485};486487static int __ibs_period_constraint_test(int ibs_type, struct ibs_period *period,488int *nr_samples)489{490struct perf_event_attr attr;491int ret = 0;492void *rb;493int fd;494495if (period->freq && period->sample_freq > perf_event_max_sample_rate)496period->ret = FD_ERROR;497498if (ibs_type == IBS_FETCH)499fetch_prepare_attr(&attr, 0, period->freq, period->sample_freq);500else501op_prepare_attr(&attr, 0, period->freq, period->sample_freq);502503/* CPU0, All processes */504fd = perf_event_open(&attr, -1, 0, -1, 0);505if (period->ret == FD_ERROR) {506if (fd != -1) {507close(fd);508return -1;509}510return 0;511}512if (fd <= -1)513return -1;514515rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,516MAP_SHARED, fd, 0);517if (rb == MAP_FAILED) {518pr_debug("mmap() failed. [%m]\n");519close(fd);520return -1;521}522523ioctl(fd, PERF_EVENT_IOC_RESET, 0);524ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);525526if (period->freq) {527dummy_workload_1(100000);528ret = rb_drain_samples(rb, period->period, nr_samples,529period_higher);530} else {531dummy_workload_1(period->sample_freq * 10);532ret = rb_drain_samples(rb, period->period, nr_samples,533period_equal);534}535536ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);537munmap(rb, PERF_MMAP_TOTAL_SIZE);538close(fd);539return ret;540}541542static int ibs_period_constraint_test(void)543{544unsigned long i;545int nr_samples;546int ret = 0;547int r;548549pr_debug("\nIBS sample period constraint tests:\n");550pr_debug("-----------------------------------\n");551552pr_debug("Fetch PMU test:\n");553for (i = 0; i < ARRAY_SIZE(fetch_period); i++) {554nr_samples = 0;555r = __ibs_period_constraint_test(IBS_FETCH, &fetch_period[i],556&nr_samples);557558if (fetch_period[i].ret == FD_ERROR) {559pr_debug("freq %d, sample_freq %9ld: %-4s\n",560fetch_period[i].freq, fetch_period[i].sample_freq,561!r ? "Ok" : "Fail");562} else {563/*564* Although nr_samples == 0 is reported as Fail here,565* the failure status is not cascaded up because, we566* can not decide whether test really failed or not567* without actual samples.568*/569pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",570fetch_period[i].freq, fetch_period[i].sample_freq,571(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);572}573ret |= r;574}575576pr_debug("Op PMU test:\n");577for (i = 0; i < ARRAY_SIZE(op_period); i++) {578nr_samples = 0;579r = __ibs_period_constraint_test(IBS_OP, &op_period[i],580&nr_samples);581582if (op_period[i].ret == FD_ERROR) {583pr_debug("freq %d, sample_freq %9ld: %-4s\n",584op_period[i].freq, op_period[i].sample_freq,585!r ? "Ok" : "Fail");586} else {587/*588* Although nr_samples == 0 is reported as Fail here,589* the failure status is not cascaded up because, we590* can not decide whether test really failed or not591* without actual samples.592*/593pr_debug("freq %d, sample_freq %9ld: %-4s (nr samples: %d)\n",594op_period[i].freq, op_period[i].sample_freq,595(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);596}597ret |= r;598}599600return ret;601}602603struct ibs_ioctl {604/* Input */605int freq;606unsigned long period;607608/* Expected output */609int ret;610};611612struct ibs_ioctl fetch_ioctl[] = {613{ .freq = 0, .period = 0x0, .ret = FD_ERROR },614{ .freq = 0, .period = 0x1, .ret = FD_ERROR },615{ .freq = 0, .period = 0xf, .ret = FD_ERROR },616{ .freq = 0, .period = 0x10, .ret = FD_SUCCESS },617{ .freq = 0, .period = 0x11, .ret = FD_ERROR },618{ .freq = 0, .period = 0x1f, .ret = FD_ERROR },619{ .freq = 0, .period = 0x20, .ret = FD_SUCCESS },620{ .freq = 0, .period = 0x80, .ret = FD_SUCCESS },621{ .freq = 0, .period = 0x8f, .ret = FD_ERROR },622{ .freq = 0, .period = 0x90, .ret = FD_SUCCESS },623{ .freq = 0, .period = 0x91, .ret = FD_ERROR },624{ .freq = 0, .period = 0x100, .ret = FD_SUCCESS },625{ .freq = 0, .period = 0xfff0, .ret = FD_SUCCESS },626{ .freq = 0, .period = 0xffff, .ret = FD_ERROR },627{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },628{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },629{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR },630{ .freq = 1, .period = 0x0, .ret = FD_ERROR },631{ .freq = 1, .period = 0x1, .ret = FD_SUCCESS },632{ .freq = 1, .period = 0xf, .ret = FD_SUCCESS },633{ .freq = 1, .period = 0x10, .ret = FD_SUCCESS },634{ .freq = 1, .period = 0x11, .ret = FD_SUCCESS },635{ .freq = 1, .period = 0x1f, .ret = FD_SUCCESS },636{ .freq = 1, .period = 0x20, .ret = FD_SUCCESS },637{ .freq = 1, .period = 0x80, .ret = FD_SUCCESS },638{ .freq = 1, .period = 0x8f, .ret = FD_SUCCESS },639{ .freq = 1, .period = 0x90, .ret = FD_SUCCESS },640{ .freq = 1, .period = 0x91, .ret = FD_SUCCESS },641{ .freq = 1, .period = 0x100, .ret = FD_SUCCESS },642};643644struct ibs_ioctl op_ioctl[] = {645{ .freq = 0, .period = 0x0, .ret = FD_ERROR },646{ .freq = 0, .period = 0x1, .ret = FD_ERROR },647{ .freq = 0, .period = 0xf, .ret = FD_ERROR },648{ .freq = 0, .period = 0x10, .ret = FD_ERROR },649{ .freq = 0, .period = 0x11, .ret = FD_ERROR },650{ .freq = 0, .period = 0x1f, .ret = FD_ERROR },651{ .freq = 0, .period = 0x20, .ret = FD_ERROR },652{ .freq = 0, .period = 0x80, .ret = FD_ERROR },653{ .freq = 0, .period = 0x8f, .ret = FD_ERROR },654{ .freq = 0, .period = 0x90, .ret = FD_SUCCESS },655{ .freq = 0, .period = 0x91, .ret = FD_ERROR },656{ .freq = 0, .period = 0x100, .ret = FD_SUCCESS },657{ .freq = 0, .period = 0xfff0, .ret = FD_SUCCESS },658{ .freq = 0, .period = 0xffff, .ret = FD_ERROR },659{ .freq = 0, .period = 0x10000, .ret = FD_SUCCESS },660{ .freq = 0, .period = 0x1fff0, .ret = FD_SUCCESS },661{ .freq = 0, .period = 0x1fff5, .ret = FD_ERROR },662{ .freq = 1, .period = 0x0, .ret = FD_ERROR },663{ .freq = 1, .period = 0x1, .ret = FD_SUCCESS },664{ .freq = 1, .period = 0xf, .ret = FD_SUCCESS },665{ .freq = 1, .period = 0x10, .ret = FD_SUCCESS },666{ .freq = 1, .period = 0x11, .ret = FD_SUCCESS },667{ .freq = 1, .period = 0x1f, .ret = FD_SUCCESS },668{ .freq = 1, .period = 0x20, .ret = FD_SUCCESS },669{ .freq = 1, .period = 0x80, .ret = FD_SUCCESS },670{ .freq = 1, .period = 0x8f, .ret = FD_SUCCESS },671{ .freq = 1, .period = 0x90, .ret = FD_SUCCESS },672{ .freq = 1, .period = 0x91, .ret = FD_SUCCESS },673{ .freq = 1, .period = 0x100, .ret = FD_SUCCESS },674};675676static int __ibs_ioctl_test(int ibs_type, struct ibs_ioctl *ibs_ioctl)677{678struct perf_event_attr attr;679int ret = 0;680int fd;681int r;682683if (ibs_type == IBS_FETCH)684fetch_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);685else686op_prepare_attr(&attr, 0, ibs_ioctl->freq, 1000);687688/* CPU0, All processes */689fd = perf_event_open(&attr, -1, 0, -1, 0);690if (fd <= -1) {691pr_debug("event_open() Failed\n");692return -1;693}694695r = ioctl(fd, PERF_EVENT_IOC_PERIOD, &ibs_ioctl->period);696if ((ibs_ioctl->ret == FD_SUCCESS && r <= -1) ||697(ibs_ioctl->ret == FD_ERROR && r >= 0)) {698ret = -1;699}700701close(fd);702return ret;703}704705static int ibs_ioctl_test(void)706{707unsigned long i;708int ret = 0;709int r;710711pr_debug("\nIBS ioctl() tests:\n");712pr_debug("------------------\n");713714pr_debug("Fetch PMU tests\n");715for (i = 0; i < ARRAY_SIZE(fetch_ioctl); i++) {716r = __ibs_ioctl_test(IBS_FETCH, &fetch_ioctl[i]);717718pr_debug("ioctl(%s = 0x%-7lx): %s\n",719fetch_ioctl[i].freq ? "freq " : "period",720fetch_ioctl[i].period, r ? "Fail" : "Ok");721ret |= r;722}723724pr_debug("Op PMU tests\n");725for (i = 0; i < ARRAY_SIZE(op_ioctl); i++) {726r = __ibs_ioctl_test(IBS_OP, &op_ioctl[i]);727728pr_debug("ioctl(%s = 0x%-7lx): %s\n",729op_ioctl[i].freq ? "freq " : "period",730op_ioctl[i].period, r ? "Fail" : "Ok");731ret |= r;732}733734return ret;735}736737static int ibs_freq_neg_test(void)738{739struct perf_event_attr attr;740int fd;741742pr_debug("\nIBS freq (negative) tests:\n");743pr_debug("--------------------------\n");744745/*746* Assuming perf_event_max_sample_rate <= 100000,747* config: 0x300D40 ==> MaxCnt: 200000748*/749op_prepare_attr(&attr, 0x300D40, 1, 0);750751/* CPU0, All processes */752fd = perf_event_open(&attr, -1, 0, -1, 0);753if (fd != -1) {754pr_debug("freq 1, sample_freq 200000: Fail\n");755close(fd);756return -1;757}758759pr_debug("freq 1, sample_freq 200000: Ok\n");760761return 0;762}763764struct ibs_l3missonly {765/* Input */766int freq;767unsigned long sample_freq;768769/* Expected output */770int ret;771unsigned long min_period;772};773774struct ibs_l3missonly fetch_l3missonly = {775.freq = 1,776.sample_freq = 10000,777.ret = FD_SUCCESS,778.min_period = 0x10,779};780781struct ibs_l3missonly op_l3missonly = {782.freq = 1,783.sample_freq = 10000,784.ret = FD_SUCCESS,785.min_period = 0x90,786};787788static int __ibs_l3missonly_test(char *perf, int ibs_type, int *nr_samples,789struct ibs_l3missonly *l3missonly)790{791struct perf_event_attr attr;792int ret = 0;793void *rb;794int fd;795796if (l3missonly->sample_freq > perf_event_max_sample_rate)797l3missonly->ret = FD_ERROR;798799if (ibs_type == IBS_FETCH) {800fetch_prepare_attr(&attr, 0x800000000000000UL, l3missonly->freq,801l3missonly->sample_freq);802} else {803op_prepare_attr(&attr, 0x10000, l3missonly->freq,804l3missonly->sample_freq);805}806807/* CPU0, All processes */808fd = perf_event_open(&attr, -1, 0, -1, 0);809if (l3missonly->ret == FD_ERROR) {810if (fd != -1) {811close(fd);812return -1;813}814return 0;815}816if (fd == -1) {817pr_debug("perf_event_open() failed. [%m]\n");818return -1;819}820821rb = mmap(NULL, PERF_MMAP_TOTAL_SIZE, PROT_READ | PROT_WRITE,822MAP_SHARED, fd, 0);823if (rb == MAP_FAILED) {824pr_debug("mmap() failed. [%m]\n");825close(fd);826return -1;827}828829ioctl(fd, PERF_EVENT_IOC_RESET, 0);830ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);831832dummy_workload_2(perf);833834ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);835836ret = rb_drain_samples(rb, l3missonly->min_period, nr_samples, period_higher);837838munmap(rb, PERF_MMAP_TOTAL_SIZE);839close(fd);840return ret;841}842843static int ibs_l3missonly_test(char *perf)844{845int nr_samples = 0;846int ret = 0;847int r = 0;848849pr_debug("\nIBS L3MissOnly test: (takes a while)\n");850pr_debug("--------------------\n");851852if (perf_pmu__has_format(fetch_pmu, "l3missonly")) {853nr_samples = 0;854r = __ibs_l3missonly_test(perf, IBS_FETCH, &nr_samples, &fetch_l3missonly);855if (fetch_l3missonly.ret == FD_ERROR) {856pr_debug("Fetch L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");857} else {858/*859* Although nr_samples == 0 is reported as Fail here,860* the failure status is not cascaded up because, we861* can not decide whether test really failed or not862* without actual samples.863*/864pr_debug("Fetch L3MissOnly: %-4s (nr_samples: %d)\n",865(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);866}867ret |= r;868}869870if (perf_pmu__has_format(op_pmu, "l3missonly")) {871nr_samples = 0;872r = __ibs_l3missonly_test(perf, IBS_OP, &nr_samples, &op_l3missonly);873if (op_l3missonly.ret == FD_ERROR) {874pr_debug("Op L3MissOnly: %-4s\n", !r ? "Ok" : "Fail");875} else {876/*877* Although nr_samples == 0 is reported as Fail here,878* the failure status is not cascaded up because, we879* can not decide whether test really failed or not880* without actual samples.881*/882pr_debug("Op L3MissOnly: %-4s (nr_samples: %d)\n",883(!r && nr_samples != 0) ? "Ok" : "Fail", nr_samples);884}885ret |= r;886}887888return ret;889}890891static unsigned int get_perf_event_max_sample_rate(void)892{893unsigned int max_sample_rate = 100000;894FILE *fp;895int ret;896897fp = fopen("/proc/sys/kernel/perf_event_max_sample_rate", "r");898if (!fp) {899pr_debug("Can't open perf_event_max_sample_rate. Assuming %d\n",900max_sample_rate);901goto out;902}903904ret = fscanf(fp, "%d", &max_sample_rate);905if (ret == EOF) {906pr_debug("Can't read perf_event_max_sample_rate. Assuming 100000\n");907max_sample_rate = 100000;908}909fclose(fp);910911out:912return max_sample_rate;913}914915/*916* Bunch of IBS sample period fixes that this test exercise went in v6.15.917* Skip the test on older kernels to distinguish between test failure due918* to a new bug vs known failure due to older kernel.919*/920static bool kernel_v6_15_or_newer(void)921{922struct utsname utsname;923char *endptr = NULL;924long major, minor;925926if (uname(&utsname) < 0) {927pr_debug("uname() failed. [%m]");928return false;929}930931major = strtol(utsname.release, &endptr, 10);932endptr++;933minor = strtol(endptr, NULL, 10);934935return major >= 6 && minor >= 15;936}937938int test__amd_ibs_period(struct test_suite *test __maybe_unused,939int subtest __maybe_unused)940{941char perf[PATH_MAX] = {'\0'};942int ret = TEST_OK;943944page_size = sysconf(_SC_PAGESIZE);945946/*947* Reading perf_event_max_sample_rate only once _might_ cause some948* of the test to fail if kernel changes it after reading it here.949*/950perf_event_max_sample_rate = get_perf_event_max_sample_rate();951fetch_pmu = perf_pmus__find("ibs_fetch");952op_pmu = perf_pmus__find("ibs_op");953954if (!x86__is_amd_cpu() || !fetch_pmu || !op_pmu)955return TEST_SKIP;956957if (!kernel_v6_15_or_newer()) {958pr_debug("Need v6.15 or newer kernel. Skipping.\n");959return TEST_SKIP;960}961962perf_exe(perf, sizeof(perf));963964if (sched_affine(0))965return TEST_FAIL;966967/*968* Perf event can be opened in two modes:969* 1 Freq mode970* perf_event_attr->freq = 1, ->sample_freq = <frequency>971* 2 Sample period mode972* perf_event_attr->freq = 0, ->sample_period = <period>973*974* Instead of using above interface, IBS event in 'sample period mode'975* can also be opened by passing <period> value directly in a MaxCnt976* bitfields of perf_event_attr->config. Test this IBS specific special977* interface.978*/979if (ibs_config_test())980ret = TEST_FAIL;981982/*983* IBS Fetch and Op PMUs have HW constraints on minimum sample period.984* Also, sample period value must be in multiple of 0x10. Test that IBS985* driver honors HW constraints for various possible values in Freq as986* well as Sample Period mode IBS events.987*/988if (ibs_period_constraint_test())989ret = TEST_FAIL;990991/*992* Test ioctl() with various sample period values for IBS event.993*/994if (ibs_ioctl_test())995ret = TEST_FAIL;996997/*998* Test that opening of freq mode IBS event fails when the freq value999* is passed through ->config, not explicitly in ->sample_freq. Also1000* use high freq value (beyond perf_event_max_sample_rate) to test IBS1001* driver do not bypass perf_event_max_sample_rate checks.1002*/1003if (ibs_freq_neg_test())1004ret = TEST_FAIL;10051006/*1007* L3MissOnly is a post-processing filter, i.e. IBS HW checks for L31008* Miss at the completion of the tagged uOp. The sample is discarded1009* if the tagged uOp did not cause L3Miss. Also, IBS HW internally1010* resets CurCnt to a small pseudo-random value and resumes counting.1011* A new uOp is tagged once CurCnt reaches to MaxCnt. But the process1012* repeats until the tagged uOp causes an L3 Miss.1013*1014* With the freq mode event, the next sample period is calculated by1015* generic kernel on every sample to achieve desired freq of samples.1016*1017* Since the number of times HW internally reset CurCnt and the pseudo-1018* random value of CurCnt for all those occurrences are not known to SW,1019* the sample period adjustment by kernel goes for a toes for freq mode1020* IBS events. Kernel will set very small period for the next sample if1021* the window between current sample and prev sample is too high due to1022* multiple samples being discarded internally by IBS HW.1023*1024* Test that IBS sample period constraints are honored when L3MissOnly1025* is ON.1026*/1027if (ibs_l3missonly_test(perf))1028ret = TEST_FAIL;10291030return ret;1031}103210331034