2020#include <linux/hrtimer.h>
2121#include <linux/init.h>
2222#include <linux/kernel.h>
23+ #include <linux/kthread.h>
2324#include <linux/module.h>
2425#include <linux/moduleparam.h>
2526#include <linux/reboot.h>
2627#include <linux/types.h>
2728#include <linux/watchdog.h>
29+ #include <linux/workqueue.h>
2830
2931#define TIMER_MARGIN 60 /* Default is 60 seconds */
3032static unsigned int soft_margin = TIMER_MARGIN ; /* in seconds */
@@ -49,11 +51,34 @@ module_param(soft_panic, int, 0);
4951MODULE_PARM_DESC (soft_panic ,
5052 "Softdog action, set to 1 to panic, 0 to reboot (default=0)" );
5153
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+
5264static struct hrtimer softdog_ticktock ;
5365static struct hrtimer softdog_preticktock ;
5466
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+
5578static enum hrtimer_restart softdog_fire (struct hrtimer * timer )
5679{
80+ static bool soft_reboot_fired ;
81+
5782 module_put (THIS_MODULE );
5883 if (soft_noboot ) {
5984 pr_crit ("Triggered - Reboot ignored\n" );
@@ -62,6 +87,33 @@ static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
6287 panic ("Software Watchdog Timer expired" );
6388 } else {
6489 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+ }
65117 emergency_restart ();
66118 pr_crit ("Reboot didn't ?????\n" );
67119 }
@@ -145,12 +197,17 @@ static int __init softdog_init(void)
145197 softdog_preticktock .function = softdog_pretimeout ;
146198 }
147199
200+ if (soft_active_on_boot )
201+ softdog_ping (& softdog_dev );
202+
148203 ret = watchdog_register_device (& softdog_dev );
149204 if (ret )
150205 return ret ;
151206
152207 pr_info ("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n" ,
153208 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 );
154211
155212 return 0 ;
156213}
0 commit comments