root/tools/perf/arch/x86/tests/rdpmc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rdpmc
  2. rdtsc
  3. mmap_read_self
  4. segfault_handler
  5. __test__rdpmc
  6. test__rdpmc

   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 
  19 static 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 
  28 static 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 
  37 static 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  */
  88 static void segfault_handler(int sig __maybe_unused,
  89                              siginfo_t *info __maybe_unused,
  90                              void *uc __maybe_unused)
  91 {
  92         exit(-1);
  93 }
  94 
  95 static 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("   ");
 151 out_close:
 152         close(fd);
 153 
 154         if (!delta_sum)
 155                 return -1;
 156 
 157         return 0;
 158 }
 159 
 160 int 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 }

/* [<][>][^][v][top][bottom][index][help] */