1 // SPDX-License-Identifier: GPL-2.0
9 #include <linux/string.h>
10 #include <linux/types.h>
13 #include "tests/tests.h"
16 #include <internal/lib.h> // page_size
17 #include "arch-tests.h"
19 static u64
rdpmc(unsigned int counter
)
21 unsigned int low
, high
;
23 asm volatile("rdpmc" : "=a" (low
), "=d" (high
) : "c" (counter
));
25 return low
| ((u64
)high
) << 32;
28 static u64
rdtsc(void)
30 unsigned int low
, high
;
32 asm volatile("rdtsc" : "=a" (low
), "=d" (high
));
34 return low
| ((u64
)high
) << 32;
37 static u64
mmap_read_self(void *addr
)
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
;
47 enabled
= pc
->time_enabled
;
48 running
= pc
->time_running
;
50 if (enabled
!= running
) {
52 time_mult
= pc
->time_mult
;
53 time_shift
= pc
->time_shift
;
54 time_offset
= pc
->time_offset
;
60 count
+= rdpmc(idx
- 1);
63 } while (pc
->lock
!= seq
);
65 if (enabled
!= running
) {
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
);
77 quot
= count
/ running
;
78 rem
= count
% running
;
79 count
= quot
* enabled
+ (rem
* enabled
) / running
;
86 * If the RDPMC instruction faults then signal this back to the test parent task:
88 static void segfault_handler(int sig __maybe_unused
,
89 siginfo_t
*info __maybe_unused
,
90 void *uc __maybe_unused
)
95 static int __test__rdpmc(void)
102 struct perf_event_attr attr
= {
103 .type
= PERF_TYPE_HARDWARE
,
104 .config
= PERF_COUNT_HW_INSTRUCTIONS
,
109 char sbuf
[STRERR_BUFSIZE
];
111 sigfillset(&sa
.sa_mask
);
112 sa
.sa_sigaction
= segfault_handler
;
114 sigaction(SIGSEGV
, &sa
, NULL
);
116 fd
= sys_perf_event_open(&attr
, 0, -1, -1,
117 perf_event_open_cloexec_flag());
119 pr_err("Error: sys_perf_event_open() syscall returned "
120 "with %d (%s)\n", fd
,
121 str_error_r(errno
, sbuf
, sizeof(sbuf
)));
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
)));
132 for (n
= 0; n
< 6; n
++) {
133 u64 stamp
, now
, delta
;
135 stamp
= mmap_read_self(addr
);
137 for (i
= 0; i
< loops
; i
++)
140 now
= mmap_read_self(addr
);
144 pr_debug("%14d: %14Lu\n", n
, (long long)delta
);
149 munmap(addr
, page_size
);
160 int test__rdpmc(struct test
*test __maybe_unused
, int subtest __maybe_unused
)
172 ret
= __test__rdpmc();
177 wret
= waitpid(pid
, &status
, 0);
178 if (wret
< 0 || status
)