1 // SPDX-License-Identifier: GPL-2.0+
3 * Unit test for the clocksource watchdog.
5 * Copyright (C) 2021 Facebook, Inc.
7 * Author: Paul E. McKenney <paulmck@kernel.org>
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
22 #include "tick-internal.h"
24 MODULE_LICENSE("GPL");
25 MODULE_DESCRIPTION("Clocksource watchdog unit test");
26 MODULE_AUTHOR("Paul E. McKenney <paulmck@kernel.org>");
28 static int holdoff
= IS_BUILTIN(CONFIG_TEST_CLOCKSOURCE_WATCHDOG
) ? 10 : 0;
29 module_param(holdoff
, int, 0444);
30 MODULE_PARM_DESC(holdoff
, "Time to wait to start test (s).");
32 /* Watchdog kthread's task_struct pointer for debug purposes. */
33 static struct task_struct
*wdtest_task
;
35 static u64
wdtest_jiffies_read(struct clocksource
*cs
)
40 static struct clocksource clocksource_wdtest_jiffies
= {
41 .name
= "wdtest-jiffies",
42 .rating
= 1, /* lowest valid rating*/
43 .uncertainty_margin
= TICK_NSEC
,
44 .read
= wdtest_jiffies_read
,
45 .mask
= CLOCKSOURCE_MASK(32),
46 .flags
= CLOCK_SOURCE_MUST_VERIFY
,
47 .mult
= TICK_NSEC
<< JIFFIES_SHIFT
, /* details above */
48 .shift
= JIFFIES_SHIFT
,
52 static int wdtest_ktime_read_ndelays
;
53 static bool wdtest_ktime_read_fuzz
;
55 static u64
wdtest_ktime_read(struct clocksource
*cs
)
57 int wkrn
= READ_ONCE(wdtest_ktime_read_ndelays
);
62 udelay(cs
->uncertainty_margin
/ 250);
63 WRITE_ONCE(wdtest_ktime_read_ndelays
, wkrn
- 1);
65 ret
= ktime_get_real_fast_ns();
66 if (READ_ONCE(wdtest_ktime_read_fuzz
)) {
68 ret
= ret
+ sign
* 100 * NSEC_PER_MSEC
;
73 static void wdtest_ktime_cs_mark_unstable(struct clocksource
*cs
)
75 pr_info("--- Marking %s unstable due to clocksource watchdog.\n", cs
->name
);
78 #define KTIME_FLAGS (CLOCK_SOURCE_IS_CONTINUOUS | \
79 CLOCK_SOURCE_VALID_FOR_HRES | \
80 CLOCK_SOURCE_MUST_VERIFY | \
81 CLOCK_SOURCE_VERIFY_PERCPU)
83 static struct clocksource clocksource_wdtest_ktime
= {
84 .name
= "wdtest-ktime",
86 .read
= wdtest_ktime_read
,
87 .mask
= CLOCKSOURCE_MASK(64),
89 .mark_unstable
= wdtest_ktime_cs_mark_unstable
,
90 .list
= LIST_HEAD_INIT(clocksource_wdtest_ktime
.list
),
93 /* Reset the clocksource if needed. */
94 static void wdtest_ktime_clocksource_reset(void)
96 if (clocksource_wdtest_ktime
.flags
& CLOCK_SOURCE_UNSTABLE
) {
97 clocksource_unregister(&clocksource_wdtest_ktime
);
98 clocksource_wdtest_ktime
.flags
= KTIME_FLAGS
;
99 schedule_timeout_uninterruptible(HZ
/ 10);
100 clocksource_register_khz(&clocksource_wdtest_ktime
, 1000 * 1000);
104 /* Run the specified series of watchdog tests. */
105 static int wdtest_func(void *arg
)
107 unsigned long j1
, j2
;
111 schedule_timeout_uninterruptible(holdoff
* HZ
);
114 * Verify that jiffies-like clocksources get the manually
115 * specified uncertainty margin.
117 pr_info("--- Verify jiffies-like uncertainty margin.\n");
118 __clocksource_register(&clocksource_wdtest_jiffies
);
119 WARN_ON_ONCE(clocksource_wdtest_jiffies
.uncertainty_margin
!= TICK_NSEC
);
121 j1
= clocksource_wdtest_jiffies
.read(&clocksource_wdtest_jiffies
);
122 schedule_timeout_uninterruptible(HZ
);
123 j2
= clocksource_wdtest_jiffies
.read(&clocksource_wdtest_jiffies
);
124 WARN_ON_ONCE(j1
== j2
);
126 clocksource_unregister(&clocksource_wdtest_jiffies
);
129 * Verify that tsc-like clocksources are assigned a reasonable
130 * uncertainty margin.
132 pr_info("--- Verify tsc-like uncertainty margin.\n");
133 clocksource_register_khz(&clocksource_wdtest_ktime
, 1000 * 1000);
134 WARN_ON_ONCE(clocksource_wdtest_ktime
.uncertainty_margin
< NSEC_PER_USEC
);
136 j1
= clocksource_wdtest_ktime
.read(&clocksource_wdtest_ktime
);
138 j2
= clocksource_wdtest_ktime
.read(&clocksource_wdtest_ktime
);
139 pr_info("--- tsc-like times: %lu - %lu = %lu.\n", j2
, j1
, j2
- j1
);
140 WARN_ON_ONCE(time_before(j2
, j1
+ NSEC_PER_USEC
));
142 /* Verify tsc-like stability with various numbers of errors injected. */
143 max_retries
= clocksource_get_max_watchdog_retry();
144 for (i
= 0; i
<= max_retries
+ 1; i
++) {
145 if (i
<= 1 && i
< max_retries
)
147 else if (i
<= max_retries
)
148 s
= ", expect message";
150 s
= ", expect clock skew";
151 pr_info("--- Watchdog with %dx error injection, %d retries%s.\n", i
, max_retries
, s
);
152 WRITE_ONCE(wdtest_ktime_read_ndelays
, i
);
153 schedule_timeout_uninterruptible(2 * HZ
);
154 WARN_ON_ONCE(READ_ONCE(wdtest_ktime_read_ndelays
));
155 WARN_ON_ONCE((i
<= max_retries
) !=
156 !(clocksource_wdtest_ktime
.flags
& CLOCK_SOURCE_UNSTABLE
));
157 wdtest_ktime_clocksource_reset();
160 /* Verify tsc-like stability with clock-value-fuzz error injection. */
161 pr_info("--- Watchdog clock-value-fuzz error injection, expect clock skew and per-CPU mismatches.\n");
162 WRITE_ONCE(wdtest_ktime_read_fuzz
, true);
163 schedule_timeout_uninterruptible(2 * HZ
);
164 WARN_ON_ONCE(!(clocksource_wdtest_ktime
.flags
& CLOCK_SOURCE_UNSTABLE
));
165 clocksource_verify_percpu(&clocksource_wdtest_ktime
);
166 WRITE_ONCE(wdtest_ktime_read_fuzz
, false);
168 clocksource_unregister(&clocksource_wdtest_ktime
);
170 pr_info("--- Done with test.\n");
174 static void wdtest_print_module_parms(void)
176 pr_alert("--- holdoff=%d\n", holdoff
);
179 /* Cleanup function. */
180 static void clocksource_wdtest_cleanup(void)
184 static int __init
clocksource_wdtest_init(void)
188 wdtest_print_module_parms();
190 /* Create watchdog-test task. */
191 wdtest_task
= kthread_run(wdtest_func
, NULL
, "wdtest");
192 if (IS_ERR(wdtest_task
)) {
193 ret
= PTR_ERR(wdtest_task
);
194 pr_warn("%s: Failed to create wdtest kthread.\n", __func__
);
202 module_init(clocksource_wdtest_init
);
203 module_exit(clocksource_wdtest_cleanup
);