1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Unit test for the clocksource watchdog. |
4 | * |
5 | * Copyright (C) 2021 Facebook, Inc. |
6 | * |
7 | * Author: Paul E. McKenney <paulmck@kernel.org> |
8 | */ |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/device.h> |
12 | #include <linux/clocksource.h> |
13 | #include <linux/init.h> |
14 | #include <linux/module.h> |
15 | #include <linux/sched.h> /* for spin_unlock_irq() using preempt_count() m68k */ |
16 | #include <linux/tick.h> |
17 | #include <linux/kthread.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/prandom.h> |
20 | #include <linux/cpu.h> |
21 | |
22 | #include "tick-internal.h" |
23 | |
24 | MODULE_LICENSE("GPL" ); |
25 | MODULE_AUTHOR("Paul E. McKenney <paulmck@kernel.org>" ); |
26 | |
27 | static int holdoff = IS_BUILTIN(CONFIG_TEST_CLOCKSOURCE_WATCHDOG) ? 10 : 0; |
28 | module_param(holdoff, int, 0444); |
29 | MODULE_PARM_DESC(holdoff, "Time to wait to start test (s)." ); |
30 | |
31 | /* Watchdog kthread's task_struct pointer for debug purposes. */ |
32 | static struct task_struct *wdtest_task; |
33 | |
34 | static u64 wdtest_jiffies_read(struct clocksource *cs) |
35 | { |
36 | return (u64)jiffies; |
37 | } |
38 | |
39 | static struct clocksource clocksource_wdtest_jiffies = { |
40 | .name = "wdtest-jiffies" , |
41 | .rating = 1, /* lowest valid rating*/ |
42 | .uncertainty_margin = TICK_NSEC, |
43 | .read = wdtest_jiffies_read, |
44 | .mask = CLOCKSOURCE_MASK(32), |
45 | .flags = CLOCK_SOURCE_MUST_VERIFY, |
46 | .mult = TICK_NSEC << JIFFIES_SHIFT, /* details above */ |
47 | .shift = JIFFIES_SHIFT, |
48 | .max_cycles = 10, |
49 | }; |
50 | |
51 | static int wdtest_ktime_read_ndelays; |
52 | static bool wdtest_ktime_read_fuzz; |
53 | |
54 | static u64 wdtest_ktime_read(struct clocksource *cs) |
55 | { |
56 | int wkrn = READ_ONCE(wdtest_ktime_read_ndelays); |
57 | static int sign = 1; |
58 | u64 ret; |
59 | |
60 | if (wkrn) { |
61 | udelay(cs->uncertainty_margin / 250); |
62 | WRITE_ONCE(wdtest_ktime_read_ndelays, wkrn - 1); |
63 | } |
64 | ret = ktime_get_real_fast_ns(); |
65 | if (READ_ONCE(wdtest_ktime_read_fuzz)) { |
66 | sign = -sign; |
67 | ret = ret + sign * 100 * NSEC_PER_MSEC; |
68 | } |
69 | return ret; |
70 | } |
71 | |
72 | static void wdtest_ktime_cs_mark_unstable(struct clocksource *cs) |
73 | { |
74 | pr_info("--- Marking %s unstable due to clocksource watchdog.\n" , cs->name); |
75 | } |
76 | |
77 | #define KTIME_FLAGS (CLOCK_SOURCE_IS_CONTINUOUS | \ |
78 | CLOCK_SOURCE_VALID_FOR_HRES | \ |
79 | CLOCK_SOURCE_MUST_VERIFY | \ |
80 | CLOCK_SOURCE_VERIFY_PERCPU) |
81 | |
82 | static struct clocksource clocksource_wdtest_ktime = { |
83 | .name = "wdtest-ktime" , |
84 | .rating = 300, |
85 | .read = wdtest_ktime_read, |
86 | .mask = CLOCKSOURCE_MASK(64), |
87 | .flags = KTIME_FLAGS, |
88 | .mark_unstable = wdtest_ktime_cs_mark_unstable, |
89 | .list = LIST_HEAD_INIT(clocksource_wdtest_ktime.list), |
90 | }; |
91 | |
92 | /* Reset the clocksource if needed. */ |
93 | static void wdtest_ktime_clocksource_reset(void) |
94 | { |
95 | if (clocksource_wdtest_ktime.flags & CLOCK_SOURCE_UNSTABLE) { |
96 | clocksource_unregister(&clocksource_wdtest_ktime); |
97 | clocksource_wdtest_ktime.flags = KTIME_FLAGS; |
98 | schedule_timeout_uninterruptible(HZ / 10); |
99 | clocksource_register_khz(cs: &clocksource_wdtest_ktime, khz: 1000 * 1000); |
100 | } |
101 | } |
102 | |
103 | /* Run the specified series of watchdog tests. */ |
104 | static int wdtest_func(void *arg) |
105 | { |
106 | unsigned long j1, j2; |
107 | char *s; |
108 | int i; |
109 | |
110 | schedule_timeout_uninterruptible(timeout: holdoff * HZ); |
111 | |
112 | /* |
113 | * Verify that jiffies-like clocksources get the manually |
114 | * specified uncertainty margin. |
115 | */ |
116 | pr_info("--- Verify jiffies-like uncertainty margin.\n" ); |
117 | __clocksource_register(cs: &clocksource_wdtest_jiffies); |
118 | WARN_ON_ONCE(clocksource_wdtest_jiffies.uncertainty_margin != TICK_NSEC); |
119 | |
120 | j1 = clocksource_wdtest_jiffies.read(&clocksource_wdtest_jiffies); |
121 | schedule_timeout_uninterruptible(HZ); |
122 | j2 = clocksource_wdtest_jiffies.read(&clocksource_wdtest_jiffies); |
123 | WARN_ON_ONCE(j1 == j2); |
124 | |
125 | clocksource_unregister(&clocksource_wdtest_jiffies); |
126 | |
127 | /* |
128 | * Verify that tsc-like clocksources are assigned a reasonable |
129 | * uncertainty margin. |
130 | */ |
131 | pr_info("--- Verify tsc-like uncertainty margin.\n" ); |
132 | clocksource_register_khz(cs: &clocksource_wdtest_ktime, khz: 1000 * 1000); |
133 | WARN_ON_ONCE(clocksource_wdtest_ktime.uncertainty_margin < NSEC_PER_USEC); |
134 | |
135 | j1 = clocksource_wdtest_ktime.read(&clocksource_wdtest_ktime); |
136 | udelay(1); |
137 | j2 = clocksource_wdtest_ktime.read(&clocksource_wdtest_ktime); |
138 | pr_info("--- tsc-like times: %lu - %lu = %lu.\n" , j2, j1, j2 - j1); |
139 | WARN_ON_ONCE(time_before(j2, j1 + NSEC_PER_USEC)); |
140 | |
141 | /* Verify tsc-like stability with various numbers of errors injected. */ |
142 | for (i = 0; i <= max_cswd_read_retries + 1; i++) { |
143 | if (i <= 1 && i < max_cswd_read_retries) |
144 | s = "" ; |
145 | else if (i <= max_cswd_read_retries) |
146 | s = ", expect message" ; |
147 | else |
148 | s = ", expect clock skew" ; |
149 | pr_info("--- Watchdog with %dx error injection, %lu retries%s.\n" , i, max_cswd_read_retries, s); |
150 | WRITE_ONCE(wdtest_ktime_read_ndelays, i); |
151 | schedule_timeout_uninterruptible(timeout: 2 * HZ); |
152 | WARN_ON_ONCE(READ_ONCE(wdtest_ktime_read_ndelays)); |
153 | WARN_ON_ONCE((i <= max_cswd_read_retries) != |
154 | !(clocksource_wdtest_ktime.flags & CLOCK_SOURCE_UNSTABLE)); |
155 | wdtest_ktime_clocksource_reset(); |
156 | } |
157 | |
158 | /* Verify tsc-like stability with clock-value-fuzz error injection. */ |
159 | pr_info("--- Watchdog clock-value-fuzz error injection, expect clock skew and per-CPU mismatches.\n" ); |
160 | WRITE_ONCE(wdtest_ktime_read_fuzz, true); |
161 | schedule_timeout_uninterruptible(timeout: 2 * HZ); |
162 | WARN_ON_ONCE(!(clocksource_wdtest_ktime.flags & CLOCK_SOURCE_UNSTABLE)); |
163 | clocksource_verify_percpu(cs: &clocksource_wdtest_ktime); |
164 | WRITE_ONCE(wdtest_ktime_read_fuzz, false); |
165 | |
166 | clocksource_unregister(&clocksource_wdtest_ktime); |
167 | |
168 | pr_info("--- Done with test.\n" ); |
169 | return 0; |
170 | } |
171 | |
172 | static void wdtest_print_module_parms(void) |
173 | { |
174 | pr_alert("--- holdoff=%d\n" , holdoff); |
175 | } |
176 | |
177 | /* Cleanup function. */ |
178 | static void clocksource_wdtest_cleanup(void) |
179 | { |
180 | } |
181 | |
182 | static int __init clocksource_wdtest_init(void) |
183 | { |
184 | int ret = 0; |
185 | |
186 | wdtest_print_module_parms(); |
187 | |
188 | /* Create watchdog-test task. */ |
189 | wdtest_task = kthread_run(wdtest_func, NULL, "wdtest" ); |
190 | if (IS_ERR(ptr: wdtest_task)) { |
191 | ret = PTR_ERR(ptr: wdtest_task); |
192 | pr_warn("%s: Failed to create wdtest kthread.\n" , __func__); |
193 | wdtest_task = NULL; |
194 | return ret; |
195 | } |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | module_init(clocksource_wdtest_init); |
201 | module_exit(clocksource_wdtest_cleanup); |
202 | |