clocksource-wdtest.c 5.9 KB

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