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(&clocksource_wdtest_ktime, 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(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(&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(&clocksource_wdtest_ktime, 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(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(2 * HZ); 162 WARN_ON_ONCE(!(clocksource_wdtest_ktime.flags & CLOCK_SOURCE_UNSTABLE)); 163 clocksource_verify_percpu(&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(wdtest_task)) { 191 ret = PTR_ERR(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