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