hangcheck-timer.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * hangcheck-timer.c
  4. *
  5. * Driver for a little io fencing timer.
  6. *
  7. * Copyright (C) 2002, 2003 Oracle. All rights reserved.
  8. *
  9. * Author: Joel Becker <joel.becker@oracle.com>
  10. */
  11. /*
  12. * The hangcheck-timer driver uses the TSC to catch delays that
  13. * jiffies does not notice. A timer is set. When the timer fires, it
  14. * checks whether it was delayed and if that delay exceeds a given
  15. * margin of error. The hangcheck_tick module parameter takes the timer
  16. * duration in seconds. The hangcheck_margin parameter defines the
  17. * margin of error, in seconds. The defaults are 60 seconds for the
  18. * timer and 180 seconds for the margin of error. IOW, a timer is set
  19. * for 60 seconds. When the timer fires, the callback checks the
  20. * actual duration that the timer waited. If the duration exceeds the
  21. * allotted time and margin (here 60 + 180, or 240 seconds), the machine
  22. * is restarted. A healthy machine will have the duration match the
  23. * expected timeout very closely.
  24. */
  25. #include <linux/module.h>
  26. #include <linux/moduleparam.h>
  27. #include <linux/types.h>
  28. #include <linux/kernel.h>
  29. #include <linux/fs.h>
  30. #include <linux/mm.h>
  31. #include <linux/reboot.h>
  32. #include <linux/init.h>
  33. #include <linux/delay.h>
  34. #include <linux/uaccess.h>
  35. #include <linux/sysrq.h>
  36. #include <linux/timer.h>
  37. #include <linux/hrtimer.h>
  38. #define VERSION_STR "0.9.1"
  39. #define DEFAULT_IOFENCE_MARGIN 60 /* Default fudge factor, in seconds */
  40. #define DEFAULT_IOFENCE_TICK 180 /* Default timer timeout, in seconds */
  41. static int hangcheck_tick = DEFAULT_IOFENCE_TICK;
  42. static int hangcheck_margin = DEFAULT_IOFENCE_MARGIN;
  43. static int hangcheck_reboot; /* Defaults to not reboot */
  44. static int hangcheck_dump_tasks; /* Defaults to not dumping SysRQ T */
  45. /* options - modular */
  46. module_param(hangcheck_tick, int, 0);
  47. MODULE_PARM_DESC(hangcheck_tick, "Timer delay.");
  48. module_param(hangcheck_margin, int, 0);
  49. MODULE_PARM_DESC(hangcheck_margin, "If the hangcheck timer has been delayed more than hangcheck_margin seconds, the driver will fire.");
  50. module_param(hangcheck_reboot, int, 0);
  51. MODULE_PARM_DESC(hangcheck_reboot, "If nonzero, the machine will reboot when the timer margin is exceeded.");
  52. module_param(hangcheck_dump_tasks, int, 0);
  53. MODULE_PARM_DESC(hangcheck_dump_tasks, "If nonzero, the machine will dump the system task state when the timer margin is exceeded.");
  54. MODULE_AUTHOR("Oracle");
  55. MODULE_DESCRIPTION("Hangcheck-timer detects when the system has gone out to lunch past a certain margin.");
  56. MODULE_LICENSE("GPL");
  57. MODULE_VERSION(VERSION_STR);
  58. /* options - nonmodular */
  59. #ifndef MODULE
  60. static int __init hangcheck_parse_tick(char *str)
  61. {
  62. int par;
  63. if (get_option(&str,&par))
  64. hangcheck_tick = par;
  65. return 1;
  66. }
  67. static int __init hangcheck_parse_margin(char *str)
  68. {
  69. int par;
  70. if (get_option(&str,&par))
  71. hangcheck_margin = par;
  72. return 1;
  73. }
  74. static int __init hangcheck_parse_reboot(char *str)
  75. {
  76. int par;
  77. if (get_option(&str,&par))
  78. hangcheck_reboot = par;
  79. return 1;
  80. }
  81. static int __init hangcheck_parse_dump_tasks(char *str)
  82. {
  83. int par;
  84. if (get_option(&str,&par))
  85. hangcheck_dump_tasks = par;
  86. return 1;
  87. }
  88. __setup("hcheck_tick", hangcheck_parse_tick);
  89. __setup("hcheck_margin", hangcheck_parse_margin);
  90. __setup("hcheck_reboot", hangcheck_parse_reboot);
  91. __setup("hcheck_dump_tasks", hangcheck_parse_dump_tasks);
  92. #endif /* not MODULE */
  93. #define TIMER_FREQ 1000000000ULL
  94. /* Last time scheduled */
  95. static unsigned long long hangcheck_tsc, hangcheck_tsc_margin;
  96. static void hangcheck_fire(struct timer_list *);
  97. static DEFINE_TIMER(hangcheck_ticktock, hangcheck_fire);
  98. static void hangcheck_fire(struct timer_list *unused)
  99. {
  100. unsigned long long cur_tsc, tsc_diff;
  101. cur_tsc = ktime_get_ns();
  102. if (cur_tsc > hangcheck_tsc)
  103. tsc_diff = cur_tsc - hangcheck_tsc;
  104. else
  105. tsc_diff = (cur_tsc + (~0ULL - hangcheck_tsc)); /* or something */
  106. if (tsc_diff > hangcheck_tsc_margin) {
  107. if (hangcheck_dump_tasks) {
  108. printk(KERN_CRIT "Hangcheck: Task state:\n");
  109. #ifdef CONFIG_MAGIC_SYSRQ
  110. handle_sysrq('t');
  111. #endif /* CONFIG_MAGIC_SYSRQ */
  112. }
  113. if (hangcheck_reboot) {
  114. printk(KERN_CRIT "Hangcheck: hangcheck is restarting the machine.\n");
  115. emergency_restart();
  116. } else {
  117. printk(KERN_CRIT "Hangcheck: hangcheck value past margin!\n");
  118. }
  119. }
  120. #if 0
  121. /*
  122. * Enable to investigate delays in detail
  123. */
  124. printk("Hangcheck: called %Ld ns since last time (%Ld ns overshoot)\n",
  125. tsc_diff, tsc_diff - hangcheck_tick*TIMER_FREQ);
  126. #endif
  127. mod_timer(&hangcheck_ticktock, jiffies + (hangcheck_tick*HZ));
  128. hangcheck_tsc = ktime_get_ns();
  129. }
  130. static int __init hangcheck_init(void)
  131. {
  132. printk("Hangcheck: starting hangcheck timer %s (tick is %d seconds, margin is %d seconds).\n",
  133. VERSION_STR, hangcheck_tick, hangcheck_margin);
  134. hangcheck_tsc_margin =
  135. (unsigned long long)hangcheck_margin + hangcheck_tick;
  136. hangcheck_tsc_margin *= TIMER_FREQ;
  137. hangcheck_tsc = ktime_get_ns();
  138. mod_timer(&hangcheck_ticktock, jiffies + (hangcheck_tick*HZ));
  139. return 0;
  140. }
  141. static void __exit hangcheck_exit(void)
  142. {
  143. del_timer_sync(&hangcheck_ticktock);
  144. printk("Hangcheck: Stopped hangcheck timer.\n");
  145. }
  146. module_init(hangcheck_init);
  147. module_exit(hangcheck_exit);