20
20
#include <linux/hrtimer.h>
21
21
#include <linux/init.h>
22
22
#include <linux/kernel.h>
23
+ #include <linux/kthread.h>
23
24
#include <linux/module.h>
24
25
#include <linux/moduleparam.h>
25
26
#include <linux/reboot.h>
26
27
#include <linux/types.h>
27
28
#include <linux/watchdog.h>
29
+ #include <linux/workqueue.h>
28
30
29
31
#define TIMER_MARGIN 60 /* Default is 60 seconds */
30
32
static unsigned int soft_margin = TIMER_MARGIN ; /* in seconds */
@@ -49,11 +51,34 @@ module_param(soft_panic, int, 0);
49
51
MODULE_PARM_DESC (soft_panic ,
50
52
"Softdog action, set to 1 to panic, 0 to reboot (default=0)" );
51
53
54
+ static char * soft_reboot_cmd ;
55
+ module_param (soft_reboot_cmd , charp , 0000 );
56
+ MODULE_PARM_DESC (soft_reboot_cmd ,
57
+ "Set reboot command. Emergency reboot takes place if unset" );
58
+
59
+ static bool soft_active_on_boot ;
60
+ module_param (soft_active_on_boot , bool , 0000 );
61
+ MODULE_PARM_DESC (soft_active_on_boot ,
62
+ "Set to true to active Softdog on boot (default=false)" );
63
+
52
64
static struct hrtimer softdog_ticktock ;
53
65
static struct hrtimer softdog_preticktock ;
54
66
67
+ static int reboot_kthread_fn (void * data )
68
+ {
69
+ kernel_restart (soft_reboot_cmd );
70
+ return - EPERM ; /* Should not reach here */
71
+ }
72
+
73
+ static void reboot_work_fn (struct work_struct * unused )
74
+ {
75
+ kthread_run (reboot_kthread_fn , NULL , "softdog_reboot" );
76
+ }
77
+
55
78
static enum hrtimer_restart softdog_fire (struct hrtimer * timer )
56
79
{
80
+ static bool soft_reboot_fired ;
81
+
57
82
module_put (THIS_MODULE );
58
83
if (soft_noboot ) {
59
84
pr_crit ("Triggered - Reboot ignored\n" );
@@ -62,6 +87,33 @@ static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
62
87
panic ("Software Watchdog Timer expired" );
63
88
} else {
64
89
pr_crit ("Initiating system reboot\n" );
90
+ if (!soft_reboot_fired && soft_reboot_cmd != NULL ) {
91
+ static DECLARE_WORK (reboot_work , reboot_work_fn ) ;
92
+ /*
93
+ * The 'kernel_restart' is a 'might-sleep' operation.
94
+ * Also, executing it in system-wide workqueues blocks
95
+ * any driver from using the same workqueue in its
96
+ * shutdown callback function. Thus, we should execute
97
+ * the 'kernel_restart' in a standalone kernel thread.
98
+ * But since starting a kernel thread is also a
99
+ * 'might-sleep' operation, so the 'reboot_work' is
100
+ * required as a launcher of the kernel thread.
101
+ *
102
+ * After request the reboot, restart the timer to
103
+ * schedule an 'emergency_restart' reboot after
104
+ * 'TIMER_MARGIN' seconds. It's because if the softdog
105
+ * hangs, it might be because of scheduling issues. And
106
+ * if that is the case, both 'schedule_work' and
107
+ * 'kernel_restart' may possibly be malfunctional at the
108
+ * same time.
109
+ */
110
+ soft_reboot_fired = true;
111
+ schedule_work (& reboot_work );
112
+ hrtimer_add_expires_ns (timer ,
113
+ (u64 )TIMER_MARGIN * NSEC_PER_SEC );
114
+
115
+ return HRTIMER_RESTART ;
116
+ }
65
117
emergency_restart ();
66
118
pr_crit ("Reboot didn't ?????\n" );
67
119
}
@@ -145,12 +197,17 @@ static int __init softdog_init(void)
145
197
softdog_preticktock .function = softdog_pretimeout ;
146
198
}
147
199
200
+ if (soft_active_on_boot )
201
+ softdog_ping (& softdog_dev );
202
+
148
203
ret = watchdog_register_device (& softdog_dev );
149
204
if (ret )
150
205
return ret ;
151
206
152
207
pr_info ("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n" ,
153
208
soft_noboot , softdog_dev .timeout , soft_panic , nowayout );
209
+ pr_info (" soft_reboot_cmd=%s soft_active_on_boot=%d\n" ,
210
+ soft_reboot_cmd ?: "<not set>" , soft_active_on_boot );
154
211
155
212
return 0 ;
156
213
}
0 commit comments