1 /*
2  * Copyright 2013, Michael Ellerman, IBM Corp.
3  * Licensed under GPLv2.
4  */
5 
6 #define _GNU_SOURCE
7 
8 #include <stdio.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <sys/prctl.h>
12 #include <asm/cputable.h>
13 
14 #include "event.h"
15 #include "utils.h"
16 #include "lib.h"
17 
18 extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);
19 
20 static void setup_event(struct event *e, u64 config, int type, char *name)
21 {
22 	event_init_opts(e, config, type, name);
23 
24 	e->attr.disabled = 1;
25 	e->attr.exclude_kernel = 1;
26 	e->attr.exclude_hv = 1;
27 	e->attr.exclude_idle = 1;
28 }
29 
30 static int do_count_loop(struct event *events, u64 instructions,
31 			 u64 overhead, bool report)
32 {
33 	s64 difference, expected;
34 	double percentage;
35 	u64 dummy;
36 
37 	prctl(PR_TASK_PERF_EVENTS_ENABLE);
38 
39 	/* Run for 1M instructions */
40 	thirty_two_instruction_loop_with_ll_sc(instructions >> 5, &dummy);
41 
42 	prctl(PR_TASK_PERF_EVENTS_DISABLE);
43 
44 	event_read(&events[0]);
45 	event_read(&events[1]);
46 	event_read(&events[2]);
47 
48 	expected = instructions + overhead + (events[2].result.value * 10);
49 	difference = events[0].result.value - expected;
50 	percentage = (double)difference / events[0].result.value * 100;
51 
52 	if (report) {
53 		printf("-----\n");
54 		event_report(&events[0]);
55 		event_report(&events[1]);
56 		event_report(&events[2]);
57 
58 		printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
59 		printf("Expected %llu\n", expected);
60 		printf("Actual   %llu\n", events[0].result.value);
61 		printf("Delta    %lld, %f%%\n", difference, percentage);
62 	}
63 
64 	event_reset(&events[0]);
65 	event_reset(&events[1]);
66 	event_reset(&events[2]);
67 
68 	if (difference < 0)
69 		difference = -difference;
70 
71 	/* Tolerate a difference below 0.0001 % */
72 	difference *= 10000 * 100;
73 	if (difference / events[0].result.value)
74 		return -1;
75 
76 	return 0;
77 }
78 
79 /* Count how many instructions it takes to do a null loop */
80 static u64 determine_overhead(struct event *events)
81 {
82 	u64 current, overhead;
83 	int i;
84 
85 	do_count_loop(events, 0, 0, false);
86 	overhead = events[0].result.value;
87 
88 	for (i = 0; i < 100; i++) {
89 		do_count_loop(events, 0, 0, false);
90 		current = events[0].result.value;
91 		if (current < overhead) {
92 			printf("Replacing overhead %llu with %llu\n", overhead, current);
93 			overhead = current;
94 		}
95 	}
96 
97 	return overhead;
98 }
99 
100 #define	PM_MRK_STCX_FAIL	0x03e158
101 #define PM_STCX_FAIL	0x01e058
102 
103 static int test_body(void)
104 {
105 	struct event events[3];
106 	u64 overhead;
107 
108 	// The STCX_FAIL event we use works on Power8 or later
109 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
110 
111 	setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "instructions");
112 	setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "cycles");
113 	setup_event(&events[2], PM_STCX_FAIL, PERF_TYPE_RAW, "stcx_fail");
114 
115 	if (event_open(&events[0])) {
116 		perror("perf_event_open");
117 		return -1;
118 	}
119 
120 	if (event_open_with_group(&events[1], events[0].fd)) {
121 		perror("perf_event_open");
122 		return -1;
123 	}
124 
125 	if (event_open_with_group(&events[2], events[0].fd)) {
126 		perror("perf_event_open");
127 		return -1;
128 	}
129 
130 	overhead = determine_overhead(events);
131 	printf("Overhead of null loop: %llu instructions\n", overhead);
132 
133 	/* Run for 1Mi instructions */
134 	FAIL_IF(do_count_loop(events, 1000000, overhead, true));
135 
136 	/* Run for 10Mi instructions */
137 	FAIL_IF(do_count_loop(events, 10000000, overhead, true));
138 
139 	/* Run for 100Mi instructions */
140 	FAIL_IF(do_count_loop(events, 100000000, overhead, true));
141 
142 	/* Run for 1Bi instructions */
143 	FAIL_IF(do_count_loop(events, 1000000000, overhead, true));
144 
145 	/* Run for 16Bi instructions */
146 	FAIL_IF(do_count_loop(events, 16000000000, overhead, true));
147 
148 	/* Run for 64Bi instructions */
149 	FAIL_IF(do_count_loop(events, 64000000000, overhead, true));
150 
151 	event_close(&events[0]);
152 	event_close(&events[1]);
153 
154 	return 0;
155 }
156 
157 static int count_ll_sc(void)
158 {
159 	return eat_cpu(test_body);
160 }
161 
162 int main(void)
163 {
164 	return test_harness(count_ll_sc, "count_ll_sc");
165 }
166