linux/tools/perf/arch/x86/tests/rdpmc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <errno.h>
   3#include <unistd.h>
   4#include <stdlib.h>
   5#include <signal.h>
   6#include <sys/mman.h>
   7#include <sys/types.h>
   8#include <sys/wait.h>
   9#include <linux/string.h>
  10#include <linux/types.h>
  11#include "perf-sys.h"
  12#include "debug.h"
  13#include "tests/tests.h"
  14#include "cloexec.h"
  15#include "event.h"
  16#include <internal/lib.h> // page_size
  17#include "arch-tests.h"
  18
  19static u64 rdpmc(unsigned int counter)
  20{
  21        unsigned int low, high;
  22
  23        asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
  24
  25        return low | ((u64)high) << 32;
  26}
  27
  28static u64 rdtsc(void)
  29{
  30        unsigned int low, high;
  31
  32        asm volatile("rdtsc" : "=a" (low), "=d" (high));
  33
  34        return low | ((u64)high) << 32;
  35}
  36
  37static u64 mmap_read_self(void *addr)
  38{
  39        struct perf_event_mmap_page *pc = addr;
  40        u32 seq, idx, time_mult = 0, time_shift = 0;
  41        u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
  42
  43        do {
  44                seq = pc->lock;
  45                barrier();
  46
  47                enabled = pc->time_enabled;
  48                running = pc->time_running;
  49
  50                if (enabled != running) {
  51                        cyc = rdtsc();
  52                        time_mult = pc->time_mult;
  53                        time_shift = pc->time_shift;
  54                        time_offset = pc->time_offset;
  55                }
  56
  57                idx = pc->index;
  58                count = pc->offset;
  59                if (idx)
  60                        count += rdpmc(idx - 1);
  61
  62                barrier();
  63        } while (pc->lock != seq);
  64
  65        if (enabled != running) {
  66                u64 quot, rem;
  67
  68                quot = (cyc >> time_shift);
  69                rem = cyc & (((u64)1 << time_shift) - 1);
  70                delta = time_offset + quot * time_mult +
  71                        ((rem * time_mult) >> time_shift);
  72
  73                enabled += delta;
  74                if (idx)
  75                        running += delta;
  76
  77                quot = count / running;
  78                rem = count % running;
  79                count = quot * enabled + (rem * enabled) / running;
  80        }
  81
  82        return count;
  83}
  84
  85/*
  86 * If the RDPMC instruction faults then signal this back to the test parent task:
  87 */
  88static void segfault_handler(int sig __maybe_unused,
  89                             siginfo_t *info __maybe_unused,
  90                             void *uc __maybe_unused)
  91{
  92        exit(-1);
  93}
  94
  95static int __test__rdpmc(void)
  96{
  97        volatile int tmp = 0;
  98        u64 i, loops = 1000;
  99        int n;
 100        int fd;
 101        void *addr;
 102        struct perf_event_attr attr = {
 103                .type = PERF_TYPE_HARDWARE,
 104                .config = PERF_COUNT_HW_INSTRUCTIONS,
 105                .exclude_kernel = 1,
 106        };
 107        u64 delta_sum = 0;
 108        struct sigaction sa;
 109        char sbuf[STRERR_BUFSIZE];
 110
 111        sigfillset(&sa.sa_mask);
 112        sa.sa_sigaction = segfault_handler;
 113        sa.sa_flags = 0;
 114        sigaction(SIGSEGV, &sa, NULL);
 115
 116        fd = sys_perf_event_open(&attr, 0, -1, -1,
 117                                 perf_event_open_cloexec_flag());
 118        if (fd < 0) {
 119                pr_err("Error: sys_perf_event_open() syscall returned "
 120                       "with %d (%s)\n", fd,
 121                       str_error_r(errno, sbuf, sizeof(sbuf)));
 122                return -1;
 123        }
 124
 125        addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
 126        if (addr == (void *)(-1)) {
 127                pr_err("Error: mmap() syscall returned with (%s)\n",
 128                       str_error_r(errno, sbuf, sizeof(sbuf)));
 129                goto out_close;
 130        }
 131
 132        for (n = 0; n < 6; n++) {
 133                u64 stamp, now, delta;
 134
 135                stamp = mmap_read_self(addr);
 136
 137                for (i = 0; i < loops; i++)
 138                        tmp++;
 139
 140                now = mmap_read_self(addr);
 141                loops *= 10;
 142
 143                delta = now - stamp;
 144                pr_debug("%14d: %14Lu\n", n, (long long)delta);
 145
 146                delta_sum += delta;
 147        }
 148
 149        munmap(addr, page_size);
 150        pr_debug("   ");
 151out_close:
 152        close(fd);
 153
 154        if (!delta_sum)
 155                return -1;
 156
 157        return 0;
 158}
 159
 160int test__rdpmc(struct test *test __maybe_unused, int subtest __maybe_unused)
 161{
 162        int status = 0;
 163        int wret = 0;
 164        int ret;
 165        int pid;
 166
 167        pid = fork();
 168        if (pid < 0)
 169                return -1;
 170
 171        if (!pid) {
 172                ret = __test__rdpmc();
 173
 174                exit(ret);
 175        }
 176
 177        wret = waitpid(pid, &status, 0);
 178        if (wret < 0 || status)
 179                return -1;
 180
 181        return 0;
 182}
 183