/* * SoftDog 0.07: A Software Watchdog Device * * (c) Copyright 1996 Alan Cox , All Rights Reserved. * http://www.redhat.com * * 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 * * Software only watchdog driver. Unlike its big brother the WDT501P * driver this won't always recover a failed machine. * * 03/96: Angelo Haritsis : * 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 * Added Matt Domsch's nowayout module option. * * 20030221 Rusty Lynch * Moved implementation to use the new watchdog infrastructure. This * adds the softdog to the sysfs topography and moves the common * miscdevice functions to the infrastructure as well. * */ #include #include #include #include #include #include #include #include #define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ static int soft_margin = TIMER_MARGIN; /* in seconds */ module_param(soft_margin,int,0); MODULE_PARM_DESC(soft_margin, "Watchdog timer margin (timeout) in seconds"); static int soft_noboot = 0; module_param(soft_noboot,int,0); MODULE_PARM_DESC(soft_noboot, "Disable machine restarts on watchdog timeout"); #ifdef CONFIG_WATCHDOG_NOWAYOUT static int nowayout = 1; #else static int nowayout = 0; #endif module_param(nowayout,int,0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); /* * If the timer expires.. */ static void watchdog_fire(unsigned long data) { if (soft_noboot) printk(KERN_CRIT "SOFTDOG: Triggered - Reboot ignored.\n"); else { printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n"); machine_restart(NULL); printk("SOFTDOG: Reboot didn't ?????\n"); } } /* * Software timer */ static struct timer_list watchdog_ticktock = TIMER_INITIALIZER(watchdog_fire, 0, 0); /* * Watchdog ops callback functions */ static int softdog_start(struct watchdog_driver *d) { mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); return 0; } static int softdog_stop(struct watchdog_driver *d) { if (nowayout) { printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly. " "WDT will not stop!\n"); return -1; } del_timer(&watchdog_ticktock); return 0; } static int softdog_keepalive(struct watchdog_driver *d) { mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); return 0; } static int softdog_get_timeout(struct watchdog_driver *d, int *timeout) { *timeout = soft_margin; return 0; } static int softdog_set_timeout(struct watchdog_driver *d, int timeout) { if (timeout < 1) return -1; soft_margin = timeout; mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); return 0; } static int softdog_get_options(struct watchdog_driver *d, int *options) { *options = WDIOF_SETTIMEOUT; return 0; } static int softdog_get_nowayout(struct watchdog_driver *d, int *n) { *n = nowayout; return 0; } static int softdog_set_nowayout(struct watchdog_driver *d, int n) { if (n) nowayout = 1; else nowayout = 0; return 0; } /* * Structures required to register as a watchdog driver */ static struct watchdog_ops softdog_ops = { .start = softdog_start, .stop = softdog_stop, .keepalive = softdog_keepalive, .get_timeout = softdog_get_timeout, .set_timeout = softdog_set_timeout, .get_nowayout = softdog_get_nowayout, .set_nowayout = softdog_set_nowayout, .get_options = softdog_get_options, /* get_bootstatus not implemented */ /* get_status not implemented */ /* get/set_temppanic not implemented */ /* get_firmware_version not implemented */ }; static struct watchdog_driver softdog_driver = { .ops = &softdog_ops, .driver = { .name = "softdog", .bus = &system_bus_type, .devclass = &watchdog_devclass, } }; /* * enable testing the of driver to not cause a machine restart */ static ssize_t soft_noboot_show(struct device_driver * d, char * buf) { return sprintf(buf, "%i\n",soft_noboot); } static ssize_t soft_noboot_store(struct device_driver *d,const char * buf, size_t count) { int tmp; if (sscanf(buf,"%i",&tmp) != 1) return -EINVAL; if (tmp) soft_noboot = 1; else soft_noboot = 0; return count; } DRIVER_ATTR(soft_noboot,0644,soft_noboot_show,soft_noboot_store); static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07, soft_margin: %d sec, nowayout: %d\n"; static int __init watchdog_init(void) { int ret; ret = watchdog_driver_register(&softdog_driver); if (ret) return ret; driver_create_file(&softdog_driver.driver, &driver_attr_soft_noboot); printk(banner, soft_margin, nowayout); return 0; } static void __exit watchdog_exit(void) { driver_remove_file(&softdog_driver.driver, &driver_attr_soft_noboot); watchdog_driver_unregister(&softdog_driver); /* ensure somebody didn't leave the watchdog ticking */ del_timer(&watchdog_ticktock); } module_init(watchdog_init); module_exit(watchdog_exit); MODULE_LICENSE("GPL");