Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  *  SoftDog:    A Software Watchdog Device
0004  *
0005  *  (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
0006  *                          All Rights Reserved.
0007  *
0008  *  Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
0009  *  warranty for any of this software. This material is provided
0010  *  "AS-IS" and at no charge.
0011  *
0012  *  (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
0013  *
0014  *  Software only watchdog driver. Unlike its big brother the WDT501P
0015  *  driver this won't always recover a failed machine.
0016  */
0017 
0018 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0019 
0020 #include <linux/hrtimer.h>
0021 #include <linux/init.h>
0022 #include <linux/kernel.h>
0023 #include <linux/kthread.h>
0024 #include <linux/module.h>
0025 #include <linux/moduleparam.h>
0026 #include <linux/reboot.h>
0027 #include <linux/types.h>
0028 #include <linux/watchdog.h>
0029 #include <linux/workqueue.h>
0030 
0031 #define TIMER_MARGIN    60      /* Default is 60 seconds */
0032 static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
0033 module_param(soft_margin, uint, 0);
0034 MODULE_PARM_DESC(soft_margin,
0035     "Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
0036                     __MODULE_STRING(TIMER_MARGIN) ")");
0037 
0038 static bool nowayout = WATCHDOG_NOWAYOUT;
0039 module_param(nowayout, bool, 0);
0040 MODULE_PARM_DESC(nowayout,
0041         "Watchdog cannot be stopped once started (default="
0042                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0043 
0044 static int soft_noboot;
0045 module_param(soft_noboot, int, 0);
0046 MODULE_PARM_DESC(soft_noboot,
0047     "Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)");
0048 
0049 static int soft_panic;
0050 module_param(soft_panic, int, 0);
0051 MODULE_PARM_DESC(soft_panic,
0052     "Softdog action, set to 1 to panic, 0 to reboot (default=0)");
0053 
0054 static char *soft_reboot_cmd;
0055 module_param(soft_reboot_cmd, charp, 0000);
0056 MODULE_PARM_DESC(soft_reboot_cmd,
0057     "Set reboot command. Emergency reboot takes place if unset");
0058 
0059 static bool soft_active_on_boot;
0060 module_param(soft_active_on_boot, bool, 0000);
0061 MODULE_PARM_DESC(soft_active_on_boot,
0062     "Set to true to active Softdog on boot (default=false)");
0063 
0064 static struct hrtimer softdog_ticktock;
0065 static struct hrtimer softdog_preticktock;
0066 
0067 static int reboot_kthread_fn(void *data)
0068 {
0069     kernel_restart(soft_reboot_cmd);
0070     return -EPERM; /* Should not reach here */
0071 }
0072 
0073 static void reboot_work_fn(struct work_struct *unused)
0074 {
0075     kthread_run(reboot_kthread_fn, NULL, "softdog_reboot");
0076 }
0077 
0078 static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
0079 {
0080     static bool soft_reboot_fired;
0081 
0082     module_put(THIS_MODULE);
0083     if (soft_noboot) {
0084         pr_crit("Triggered - Reboot ignored\n");
0085     } else if (soft_panic) {
0086         pr_crit("Initiating panic\n");
0087         panic("Software Watchdog Timer expired");
0088     } else {
0089         pr_crit("Initiating system reboot\n");
0090         if (!soft_reboot_fired && soft_reboot_cmd != NULL) {
0091             static DECLARE_WORK(reboot_work, reboot_work_fn);
0092             /*
0093              * The 'kernel_restart' is a 'might-sleep' operation.
0094              * Also, executing it in system-wide workqueues blocks
0095              * any driver from using the same workqueue in its
0096              * shutdown callback function. Thus, we should execute
0097              * the 'kernel_restart' in a standalone kernel thread.
0098              * But since starting a kernel thread is also a
0099              * 'might-sleep' operation, so the 'reboot_work' is
0100              * required as a launcher of the kernel thread.
0101              *
0102              * After request the reboot, restart the timer to
0103              * schedule an 'emergency_restart' reboot after
0104              * 'TIMER_MARGIN' seconds. It's because if the softdog
0105              * hangs, it might be because of scheduling issues. And
0106              * if that is the case, both 'schedule_work' and
0107              * 'kernel_restart' may possibly be malfunctional at the
0108              * same time.
0109              */
0110             soft_reboot_fired = true;
0111             schedule_work(&reboot_work);
0112             hrtimer_add_expires_ns(timer,
0113                     (u64)TIMER_MARGIN * NSEC_PER_SEC);
0114 
0115             return HRTIMER_RESTART;
0116         }
0117         emergency_restart();
0118         pr_crit("Reboot didn't ?????\n");
0119     }
0120 
0121     return HRTIMER_NORESTART;
0122 }
0123 
0124 static struct watchdog_device softdog_dev;
0125 
0126 static enum hrtimer_restart softdog_pretimeout(struct hrtimer *timer)
0127 {
0128     watchdog_notify_pretimeout(&softdog_dev);
0129 
0130     return HRTIMER_NORESTART;
0131 }
0132 
0133 static int softdog_ping(struct watchdog_device *w)
0134 {
0135     if (!hrtimer_active(&softdog_ticktock))
0136         __module_get(THIS_MODULE);
0137     hrtimer_start(&softdog_ticktock, ktime_set(w->timeout, 0),
0138               HRTIMER_MODE_REL);
0139 
0140     if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) {
0141         if (w->pretimeout)
0142             hrtimer_start(&softdog_preticktock,
0143                       ktime_set(w->timeout - w->pretimeout, 0),
0144                       HRTIMER_MODE_REL);
0145         else
0146             hrtimer_cancel(&softdog_preticktock);
0147     }
0148 
0149     return 0;
0150 }
0151 
0152 static int softdog_stop(struct watchdog_device *w)
0153 {
0154     if (hrtimer_cancel(&softdog_ticktock))
0155         module_put(THIS_MODULE);
0156 
0157     if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT))
0158         hrtimer_cancel(&softdog_preticktock);
0159 
0160     return 0;
0161 }
0162 
0163 static struct watchdog_info softdog_info = {
0164     .identity = "Software Watchdog",
0165     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0166 };
0167 
0168 static const struct watchdog_ops softdog_ops = {
0169     .owner = THIS_MODULE,
0170     .start = softdog_ping,
0171     .stop = softdog_stop,
0172 };
0173 
0174 static struct watchdog_device softdog_dev = {
0175     .info = &softdog_info,
0176     .ops = &softdog_ops,
0177     .min_timeout = 1,
0178     .max_timeout = 65535,
0179     .timeout = TIMER_MARGIN,
0180 };
0181 
0182 static int __init softdog_init(void)
0183 {
0184     int ret;
0185 
0186     watchdog_init_timeout(&softdog_dev, soft_margin, NULL);
0187     watchdog_set_nowayout(&softdog_dev, nowayout);
0188     watchdog_stop_on_reboot(&softdog_dev);
0189 
0190     hrtimer_init(&softdog_ticktock, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
0191     softdog_ticktock.function = softdog_fire;
0192 
0193     if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) {
0194         softdog_info.options |= WDIOF_PRETIMEOUT;
0195         hrtimer_init(&softdog_preticktock, CLOCK_MONOTONIC,
0196                  HRTIMER_MODE_REL);
0197         softdog_preticktock.function = softdog_pretimeout;
0198     }
0199 
0200     if (soft_active_on_boot)
0201         softdog_ping(&softdog_dev);
0202 
0203     ret = watchdog_register_device(&softdog_dev);
0204     if (ret)
0205         return ret;
0206 
0207     pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
0208         soft_noboot, softdog_dev.timeout, soft_panic, nowayout);
0209     pr_info("             soft_reboot_cmd=%s soft_active_on_boot=%d\n",
0210         soft_reboot_cmd ?: "<not set>", soft_active_on_boot);
0211 
0212     return 0;
0213 }
0214 module_init(softdog_init);
0215 
0216 static void __exit softdog_exit(void)
0217 {
0218     watchdog_unregister_device(&softdog_dev);
0219 }
0220 module_exit(softdog_exit);
0221 
0222 MODULE_AUTHOR("Alan Cox");
0223 MODULE_DESCRIPTION("Software Watchdog Device Driver");
0224 MODULE_LICENSE("GPL");