1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Unit test for the clocksource watchdog. 4 * 5 * Copyright (C) 2021 Facebook, Inc. 6 * Copyright (C) 2026 Intel Corp. 7 * 8 * Author: Paul E. McKenney <paulmck@kernel.org> 9 * Author: Thomas Gleixner <tglx@kernel.org> 10 */ 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/clocksource.h> 14 #include <linux/delay.h> 15 #include <linux/module.h> 16 #include <linux/kthread.h> 17 18 #include "tick-internal.h" 19 #include "timekeeping_internal.h" 20 21 MODULE_LICENSE("GPL"); 22 MODULE_DESCRIPTION("Clocksource watchdog unit test"); 23 MODULE_AUTHOR("Paul E. McKenney <paulmck@kernel.org>"); 24 MODULE_AUTHOR("Thomas Gleixner <tglx@kernel.org>"); 25 26 enum wdtest_states { 27 WDTEST_INJECT_NONE, 28 WDTEST_INJECT_DELAY, 29 WDTEST_INJECT_POSITIVE, 30 WDTEST_INJECT_NEGATIVE, 31 WDTEST_INJECT_PERCPU = 0x100, 32 }; 33 34 static enum wdtest_states wdtest_state; 35 static unsigned long wdtest_test_count; 36 static ktime_t wdtest_last_ts, wdtest_offset; 37 38 #define SHIFT_4000PPM 8 39 40 static ktime_t wdtest_get_offset(struct clocksource *cs) 41 { 42 if (wdtest_state < WDTEST_INJECT_PERCPU) 43 return wdtest_test_count & 0x1 ? 0 : wdtest_offset >> SHIFT_4000PPM; 44 45 /* Only affect the readout of the "remote" CPU */ 46 return cs->wd_cpu == smp_processor_id() ? 0 : NSEC_PER_MSEC; 47 } 48 49 static u64 wdtest_ktime_read(struct clocksource *cs) 50 { 51 ktime_t now = ktime_get_raw_fast_ns(); 52 ktime_t intv = now - wdtest_last_ts; 53 54 /* 55 * Only increment the test counter once per watchdog interval and 56 * store the interval for the offset calculation of this step. This 57 * guarantees a consistent behaviour even if the other side needs 58 * to repeat due to a watchdog read timeout. 59 */ 60 if (intv > (NSEC_PER_SEC / 4)) { 61 WRITE_ONCE(wdtest_test_count, wdtest_test_count + 1); 62 wdtest_last_ts = now; 63 wdtest_offset = intv; 64 } 65 66 switch (wdtest_state & ~WDTEST_INJECT_PERCPU) { 67 case WDTEST_INJECT_POSITIVE: 68 return now + wdtest_get_offset(cs); 69 case WDTEST_INJECT_NEGATIVE: 70 return now - wdtest_get_offset(cs); 71 case WDTEST_INJECT_DELAY: 72 udelay(500); 73 return now; 74 default: 75 return now; 76 } 77 } 78 79 #define KTIME_FLAGS (CLOCK_SOURCE_IS_CONTINUOUS | \ 80 CLOCK_SOURCE_CALIBRATED | \ 81 CLOCK_SOURCE_MUST_VERIFY | \ 82 CLOCK_SOURCE_WDTEST) 83 84 static struct clocksource clocksource_wdtest_ktime = { 85 .name = "wdtest-ktime", 86 .rating = 10, 87 .read = wdtest_ktime_read, 88 .mask = CLOCKSOURCE_MASK(64), 89 .flags = KTIME_FLAGS, 90 .list = LIST_HEAD_INIT(clocksource_wdtest_ktime.list), 91 }; 92 93 static void wdtest_clocksource_reset(enum wdtest_states which, bool percpu) 94 { 95 clocksource_unregister(&clocksource_wdtest_ktime); 96 97 pr_info("Test: State %d percpu %d\n", which, percpu); 98 99 wdtest_state = which; 100 if (percpu) 101 wdtest_state |= WDTEST_INJECT_PERCPU; 102 wdtest_test_count = 0; 103 wdtest_last_ts = 0; 104 105 clocksource_wdtest_ktime.rating = 10; 106 clocksource_wdtest_ktime.flags = KTIME_FLAGS; 107 if (percpu) 108 clocksource_wdtest_ktime.flags |= CLOCK_SOURCE_WDTEST_PERCPU; 109 clocksource_register_khz(&clocksource_wdtest_ktime, 1000 * 1000); 110 } 111 112 static bool wdtest_execute(enum wdtest_states which, bool percpu, unsigned int expect, 113 unsigned long calls) 114 { 115 wdtest_clocksource_reset(which, percpu); 116 117 for (; READ_ONCE(wdtest_test_count) < calls; msleep(100)) { 118 unsigned int flags = READ_ONCE(clocksource_wdtest_ktime.flags); 119 120 if (kthread_should_stop()) 121 return false; 122 123 if (flags & CLOCK_SOURCE_UNSTABLE) { 124 if (expect & CLOCK_SOURCE_UNSTABLE) 125 return true; 126 pr_warn("Fail: Unexpected unstable\n"); 127 return false; 128 } 129 if (flags & CLOCK_SOURCE_VALID_FOR_HRES) { 130 if (expect & CLOCK_SOURCE_VALID_FOR_HRES) 131 return true; 132 pr_warn("Fail: Unexpected valid for highres\n"); 133 return false; 134 } 135 } 136 137 if (!expect) 138 return true; 139 140 pr_warn("Fail: Timed out\n"); 141 return false; 142 } 143 144 static bool wdtest_run(bool percpu) 145 { 146 if (!wdtest_execute(WDTEST_INJECT_NONE, percpu, CLOCK_SOURCE_VALID_FOR_HRES, 8)) 147 return false; 148 149 if (!wdtest_execute(WDTEST_INJECT_DELAY, percpu, 0, 4)) 150 return false; 151 152 if (!wdtest_execute(WDTEST_INJECT_POSITIVE, percpu, CLOCK_SOURCE_UNSTABLE, 8)) 153 return false; 154 155 if (!wdtest_execute(WDTEST_INJECT_NEGATIVE, percpu, CLOCK_SOURCE_UNSTABLE, 8)) 156 return false; 157 158 return true; 159 } 160 161 static int wdtest_func(void *arg) 162 { 163 clocksource_register_khz(&clocksource_wdtest_ktime, 1000 * 1000); 164 if (wdtest_run(false)) { 165 if (wdtest_run(true)) 166 pr_info("Success: All tests passed\n"); 167 } 168 clocksource_unregister(&clocksource_wdtest_ktime); 169 170 if (!IS_MODULE(CONFIG_TEST_CLOCKSOURCE_WATCHDOG)) 171 return 0; 172 173 while (!kthread_should_stop()) 174 schedule_timeout_interruptible(3600 * HZ); 175 return 0; 176 } 177 178 static struct task_struct *wdtest_thread; 179 180 static int __init clocksource_wdtest_init(void) 181 { 182 struct task_struct *t = kthread_run(wdtest_func, NULL, "wdtest"); 183 184 if (IS_ERR(t)) { 185 pr_warn("Failed to create wdtest kthread.\n"); 186 return PTR_ERR(t); 187 } 188 wdtest_thread = t; 189 return 0; 190 } 191 module_init(clocksource_wdtest_init); 192 193 static void clocksource_wdtest_cleanup(void) 194 { 195 if (wdtest_thread) 196 kthread_stop(wdtest_thread); 197 } 198 module_exit(clocksource_wdtest_cleanup); 199