2005-04-17 02:20:36 +04:00
/*
2012-02-29 02:48:11 +04:00
* SoftDog : A Software Watchdog Device
2005-04-17 02:20:36 +04:00
*
2009-03-18 11:35:09 +03:00
* ( c ) Copyright 1996 Alan Cox < alan @ lxorguk . ukuu . org . uk > ,
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Neither Alan Cox nor CymruNet Ltd . admit liability nor provide
* warranty for any of this software . This material is provided
* " AS-IS " and at no charge .
*
* ( c ) Copyright 1995 Alan Cox < alan @ lxorguk . ukuu . org . uk >
*
* Software only watchdog driver . Unlike its big brother the WDT501P
* driver this won ' t always recover a failed machine .
*
* 03 / 96 : Angelo Haritsis < ah @ doc . ic . ac . uk > :
* Modularised .
* Added soft_margin ; use upon insmod to change the timer delay .
* NB : uses same minor as wdt ( WATCHDOG_MINOR ) ; we could use separate
* minors .
*
* 19980911 Alan Cox
* Made SMP safe for 2.3 . x
*
* 20011127 Joel Becker ( jlbec @ evilplan . org >
* Added soft_noboot ; Allows testing the softdog trigger without
* requiring a recompile .
* Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT .
*
* 20020530 Joel Becker < joel . becker @ oracle . com >
2009-03-18 11:35:09 +03:00
* Added Matt Domsch ' s nowayout module option .
2005-04-17 02:20:36 +04:00
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/timer.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/notifier.h>
# include <linux/reboot.h>
# include <linux/init.h>
2005-10-31 02:03:48 +03:00
# include <linux/jiffies.h>
2011-03-29 01:29:19 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# define TIMER_MARGIN 60 /* Default is 60 seconds */
2012-02-29 02:48:11 +04:00
static unsigned int soft_margin = TIMER_MARGIN ; /* in seconds */
module_param ( soft_margin , uint , 0 ) ;
2008-05-19 17:09:06 +04:00
MODULE_PARM_DESC ( soft_margin ,
" Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default= "
__MODULE_STRING ( TIMER_MARGIN ) " ) " ) ;
2005-04-17 02:20:36 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:09:06 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2005-04-17 02:20:36 +04:00
static int soft_noboot = 0 ;
module_param ( soft_noboot , int , 0 ) ;
2009-04-15 00:20:07 +04:00
MODULE_PARM_DESC ( soft_noboot ,
2012-02-29 02:48:11 +04:00
" Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0) " ) ;
2005-04-17 02:20:36 +04:00
2011-03-29 01:29:19 +04:00
static int soft_panic ;
module_param ( soft_panic , int , 0 ) ;
MODULE_PARM_DESC ( soft_panic ,
" Softdog action, set to 1 to panic, 0 to reboot (default=0) " ) ;
2005-04-17 02:20:36 +04:00
/*
* Our timer
*/
static void watchdog_fire ( unsigned long ) ;
static struct timer_list watchdog_ticktock =
TIMER_INITIALIZER ( watchdog_fire , 0 , 0 ) ;
/*
* If the timer expires . .
*/
static void watchdog_fire ( unsigned long data )
{
if ( soft_noboot )
2012-02-16 03:06:19 +04:00
pr_crit ( " Triggered - Reboot ignored \n " ) ;
2011-03-29 01:29:19 +04:00
else if ( soft_panic ) {
2012-02-16 03:06:19 +04:00
pr_crit ( " Initiating panic \n " ) ;
panic ( " Software Watchdog Timer expired " ) ;
2011-03-29 01:29:19 +04:00
} else {
2012-02-16 03:06:19 +04:00
pr_crit ( " Initiating system reboot \n " ) ;
2005-07-27 08:41:38 +04:00
emergency_restart ( ) ;
2012-02-16 03:06:19 +04:00
pr_crit ( " Reboot didn't ????? \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Softdog operations
*/
2012-02-29 02:48:11 +04:00
static int softdog_ping ( struct watchdog_device * w )
2005-04-17 02:20:36 +04:00
{
2012-02-29 02:48:11 +04:00
mod_timer ( & watchdog_ticktock , jiffies + ( w - > timeout * HZ ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-02-29 02:48:11 +04:00
static int softdog_stop ( struct watchdog_device * w )
2005-04-17 02:20:36 +04:00
{
del_timer ( & watchdog_ticktock ) ;
return 0 ;
}
2012-02-29 02:48:11 +04:00
static int softdog_set_timeout ( struct watchdog_device * w , unsigned int t )
2005-04-17 02:20:36 +04:00
{
2012-02-29 02:48:11 +04:00
w - > timeout = t ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Notifier for system down
*/
static int softdog_notify_sys ( struct notifier_block * this , unsigned long code ,
void * unused )
{
2008-05-19 17:09:06 +04:00
if ( code = = SYS_DOWN | | code = = SYS_HALT )
2005-04-17 02:20:36 +04:00
/* Turn the WDT off */
2012-02-29 02:48:11 +04:00
softdog_stop ( NULL ) ;
2005-04-17 02:20:36 +04:00
return NOTIFY_DONE ;
}
/*
* Kernel Interfaces
*/
2012-02-29 02:48:11 +04:00
static struct notifier_block softdog_notifier = {
. notifier_call = softdog_notify_sys ,
2005-04-17 02:20:36 +04:00
} ;
2012-02-29 02:48:11 +04:00
static struct watchdog_info softdog_info = {
. identity = " Software Watchdog " ,
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
2005-04-17 02:20:36 +04:00
} ;
2012-02-29 02:48:11 +04:00
static struct watchdog_ops softdog_ops = {
. owner = THIS_MODULE ,
. start = softdog_ping ,
. stop = softdog_stop ,
. ping = softdog_ping ,
. set_timeout = softdog_set_timeout ,
} ;
static struct watchdog_device softdog_dev = {
. info = & softdog_info ,
. ops = & softdog_ops ,
. min_timeout = 1 ,
. max_timeout = 0xFFFF
2005-04-17 02:20:36 +04:00
} ;
static int __init watchdog_init ( void )
{
int ret ;
2008-05-19 17:09:06 +04:00
/* Check that the soft_margin value is within it's range;
if not reset to the default */
2012-02-29 02:48:11 +04:00
if ( soft_margin < 1 | | soft_margin > 65535 ) {
2012-02-16 03:06:19 +04:00
pr_info ( " soft_margin must be 0 < soft_margin < 65536, using %d \n " ,
2005-04-17 02:20:36 +04:00
TIMER_MARGIN ) ;
2012-02-29 02:48:11 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2012-02-29 02:48:11 +04:00
softdog_dev . timeout = soft_margin ;
watchdog_set_nowayout ( & softdog_dev , nowayout ) ;
2005-04-17 02:20:36 +04:00
ret = register_reboot_notifier ( & softdog_notifier ) ;
if ( ret ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot register reboot notifier (err=%d) \n " , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2012-02-29 02:48:11 +04:00
ret = watchdog_register_device ( & softdog_dev ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
unregister_reboot_notifier ( & softdog_notifier ) ;
return ret ;
}
2012-02-29 02:48:11 +04:00
pr_info ( " Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d) \n " ,
2012-02-16 03:06:19 +04:00
soft_noboot , soft_margin , soft_panic , nowayout ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit watchdog_exit ( void )
{
2012-02-29 02:48:11 +04:00
watchdog_unregister_device ( & softdog_dev ) ;
2005-04-17 02:20:36 +04:00
unregister_reboot_notifier ( & softdog_notifier ) ;
}
module_init ( watchdog_init ) ;
module_exit ( watchdog_exit ) ;
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " Software Watchdog Device Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;