1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2014, Michael Ellerman, IBM Corp. |
4 | */ |
5 | |
6 | #include <sched.h> |
7 | #include <signal.h> |
8 | #include <stdio.h> |
9 | #include <stdlib.h> |
10 | #include <sys/mman.h> |
11 | |
12 | #include "ebb.h" |
13 | |
14 | |
15 | /* |
16 | * Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect |
17 | * where an exception triggers but we context switch before it is delivered and |
18 | * lose the exception. |
19 | */ |
20 | |
21 | static int test_body(void) |
22 | { |
23 | int i, orig_period, max_period; |
24 | struct event event; |
25 | |
26 | SKIP_IF(!ebb_is_supported()); |
27 | |
28 | /* We use PMC4 to make sure the kernel switches all counters correctly */ |
29 | event_init_named(e: &event, config: 0x40002, name: "instructions" ); |
30 | event_leader_ebb_init(e: &event); |
31 | |
32 | event.attr.exclude_kernel = 1; |
33 | event.attr.exclude_hv = 1; |
34 | event.attr.exclude_idle = 1; |
35 | |
36 | FAIL_IF(event_open(e: &event)); |
37 | |
38 | ebb_enable_pmc_counting(pmc: 4); |
39 | setup_ebb_handler(standard_ebb_callee); |
40 | ebb_global_enable(); |
41 | FAIL_IF(ebb_event_enable(e: &event)); |
42 | |
43 | /* |
44 | * We want a low sample period, but we also want to get out of the EBB |
45 | * handler without tripping up again. |
46 | * |
47 | * This value picked after much experimentation. |
48 | */ |
49 | orig_period = max_period = sample_period = 400; |
50 | |
51 | mtspr(SPRN_PMC4, pmc_sample_period(value: sample_period)); |
52 | |
53 | while (ebb_state.stats.ebb_count < 1000000) { |
54 | /* |
55 | * We are trying to get the EBB exception to race exactly with |
56 | * us entering the kernel to do the syscall. We then need the |
57 | * kernel to decide our timeslice is up and context switch to |
58 | * the other thread. When we come back our EBB will have been |
59 | * lost and we'll spin in this while loop forever. |
60 | */ |
61 | |
62 | for (i = 0; i < 100000; i++) |
63 | sched_yield(); |
64 | |
65 | /* Change the sample period slightly to try and hit the race */ |
66 | if (sample_period >= (orig_period + 200)) |
67 | sample_period = orig_period; |
68 | else |
69 | sample_period++; |
70 | |
71 | if (sample_period > max_period) |
72 | max_period = sample_period; |
73 | } |
74 | |
75 | ebb_freeze_pmcs(); |
76 | ebb_global_disable(); |
77 | |
78 | mtspr(SPRN_PMC4, 0xdead); |
79 | |
80 | dump_summary_ebb_state(); |
81 | dump_ebb_hw_state(); |
82 | |
83 | event_close(e: &event); |
84 | |
85 | FAIL_IF(ebb_state.stats.ebb_count == 0); |
86 | |
87 | /* We vary our sample period so we need extra fudge here */ |
88 | FAIL_IF(!ebb_check_count(pmc: 4, sample_period: orig_period, fudge: 2 * (max_period - orig_period))); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | static int lost_exception(void) |
94 | { |
95 | return eat_cpu(test_function: test_body); |
96 | } |
97 | |
98 | int main(void) |
99 | { |
100 | test_harness_set_timeout(300); |
101 | return test_harness(lost_exception, "lost_exception" ); |
102 | } |
103 | |