Commit | Line | Data |
---|---|---|
3752e453 ME |
1 | /* |
2 | * Copyright 2014, 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 | ||
13 | #include "ebb.h" | |
14 | ||
15 | ||
16 | /* | |
17 | * Run a calibrated instruction loop and count instructions executed using | |
18 | * EBBs. Make sure the counts look right. | |
19 | */ | |
20 | ||
21 | extern void thirty_two_instruction_loop(uint64_t loops); | |
22 | ||
23 | static bool counters_frozen = true; | |
24 | ||
25 | static int do_count_loop(struct event *event, uint64_t instructions, | |
26 | uint64_t overhead, bool report) | |
27 | { | |
28 | int64_t difference, expected; | |
29 | double percentage; | |
30 | ||
31 | clear_ebb_stats(); | |
32 | ||
33 | counters_frozen = false; | |
34 | mb(); | |
35 | mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | |
36 | ||
37 | thirty_two_instruction_loop(instructions >> 5); | |
38 | ||
39 | counters_frozen = true; | |
40 | mb(); | |
41 | mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC); | |
42 | ||
43 | count_pmc(4, sample_period); | |
44 | ||
45 | event->result.value = ebb_state.stats.pmc_count[4-1]; | |
46 | expected = instructions + overhead; | |
47 | difference = event->result.value - expected; | |
48 | percentage = (double)difference / event->result.value * 100; | |
49 | ||
50 | if (report) { | |
51 | printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead); | |
52 | printf("Expected %lu\n", expected); | |
53 | printf("Actual %llu\n", event->result.value); | |
54 | printf("Error %ld, %f%%\n", difference, percentage); | |
55 | printf("Took %d EBBs\n", ebb_state.stats.ebb_count); | |
56 | } | |
57 | ||
58 | if (difference < 0) | |
59 | difference = -difference; | |
60 | ||
61 | /* Tolerate a difference of up to 0.0001 % */ | |
62 | difference *= 10000 * 100; | |
63 | if (difference / event->result.value) | |
64 | return -1; | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | /* Count how many instructions it takes to do a null loop */ | |
70 | static uint64_t determine_overhead(struct event *event) | |
71 | { | |
72 | uint64_t current, overhead; | |
73 | int i; | |
74 | ||
75 | do_count_loop(event, 0, 0, false); | |
76 | overhead = event->result.value; | |
77 | ||
78 | for (i = 0; i < 100; i++) { | |
79 | do_count_loop(event, 0, 0, false); | |
80 | current = event->result.value; | |
81 | if (current < overhead) { | |
82 | printf("Replacing overhead %lu with %lu\n", overhead, current); | |
83 | overhead = current; | |
84 | } | |
85 | } | |
86 | ||
87 | return overhead; | |
88 | } | |
89 | ||
90 | static void pmc4_ebb_callee(void) | |
91 | { | |
92 | uint64_t val; | |
93 | ||
94 | val = mfspr(SPRN_BESCR); | |
95 | if (!(val & BESCR_PMEO)) { | |
96 | ebb_state.stats.spurious++; | |
97 | goto out; | |
98 | } | |
99 | ||
100 | ebb_state.stats.ebb_count++; | |
101 | count_pmc(4, sample_period); | |
102 | out: | |
103 | if (counters_frozen) | |
104 | reset_ebb_with_clear_mask(MMCR0_PMAO); | |
105 | else | |
106 | reset_ebb(); | |
107 | } | |
108 | ||
109 | int instruction_count(void) | |
110 | { | |
111 | struct event event; | |
112 | uint64_t overhead; | |
113 | ||
39fcfb91 DK |
114 | SKIP_IF(!ebb_is_supported()); |
115 | ||
3752e453 ME |
116 | event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL"); |
117 | event_leader_ebb_init(&event); | |
118 | event.attr.exclude_kernel = 1; | |
119 | event.attr.exclude_hv = 1; | |
120 | event.attr.exclude_idle = 1; | |
121 | ||
122 | FAIL_IF(event_open(&event)); | |
123 | FAIL_IF(ebb_event_enable(&event)); | |
124 | ||
125 | sample_period = COUNTER_OVERFLOW; | |
126 | ||
127 | setup_ebb_handler(pmc4_ebb_callee); | |
128 | mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | |
129 | ebb_global_enable(); | |
130 | ||
131 | overhead = determine_overhead(&event); | |
132 | printf("Overhead of null loop: %lu instructions\n", overhead); | |
133 | ||
134 | /* Run for 1M instructions */ | |
135 | FAIL_IF(do_count_loop(&event, 0x100000, overhead, true)); | |
136 | ||
137 | /* Run for 10M instructions */ | |
138 | FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true)); | |
139 | ||
140 | /* Run for 100M instructions */ | |
141 | FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true)); | |
142 | ||
143 | /* Run for 1G instructions */ | |
144 | FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true)); | |
145 | ||
146 | /* Run for 16G instructions */ | |
147 | FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true)); | |
148 | ||
149 | /* Run for 64G instructions */ | |
150 | FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true)); | |
151 | ||
152 | /* Run for 128G instructions */ | |
153 | FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true)); | |
154 | ||
155 | ebb_global_disable(); | |
156 | event_close(&event); | |
157 | ||
158 | printf("Finished OK\n"); | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | int main(void) | |
164 | { | |
165 | return test_harness(instruction_count, "instruction_count"); | |
166 | } |