Commit 77a557ae authored by Wim Van Sebroeck's avatar Wim Van Sebroeck Committed by Linus Torvalds

[WATCHDOG] v2.6.2 shwdt-cleanup

Make heartbeat a module parameter and some general clean-up.
parent aeee0c35
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/watchdog.h> #include <asm/watchdog.h>
#define PFX "shwdt: "
/* /*
* Default clock division ratio is 5.25 msecs. For an additional table of * Default clock division ratio is 5.25 msecs. For an additional table of
* values, consult the asm-sh/watchdog.h. Overload this at module load * values, consult the asm-sh/watchdog.h. Overload this at module load
...@@ -70,7 +72,9 @@ static struct watchdog_info sh_wdt_info; ...@@ -70,7 +72,9 @@ static struct watchdog_info sh_wdt_info;
static char shwdt_expect_close; static char shwdt_expect_close;
static struct timer_list timer; static struct timer_list timer;
static unsigned long next_heartbeat; static unsigned long next_heartbeat;
static int heartbeat = 30;
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
#ifdef CONFIG_WATCHDOG_NOWAYOUT #ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1; static int nowayout = 1;
...@@ -87,8 +91,8 @@ static void sh_wdt_start(void) ...@@ -87,8 +91,8 @@ static void sh_wdt_start(void)
{ {
__u8 csr; __u8 csr;
mod_timer(&timer, next_ping_period(clock_division_ratio));
next_heartbeat = jiffies + (heartbeat * HZ); next_heartbeat = jiffies + (heartbeat * HZ);
mod_timer(&timer, next_ping_period(clock_division_ratio));
csr = sh_wdt_read_csr(); csr = sh_wdt_read_csr();
csr |= WTCSR_WT | clock_division_ratio; csr |= WTCSR_WT | clock_division_ratio;
...@@ -141,6 +145,30 @@ static void sh_wdt_stop(void) ...@@ -141,6 +145,30 @@ static void sh_wdt_stop(void)
sh_wdt_write_csr(csr); sh_wdt_write_csr(csr);
} }
/**
* sh_wdt_keepalive - Keep the Userspace Watchdog Alive
*
* The Userspace watchdog got a KeepAlive: schedule the next heartbeat.
*/
static void sh_wdt_keepalive(void)
{
next_heartbeat = jiffies + (heartbeat * HZ);
}
/**
* sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat
*
* Set the Userspace Watchdog heartbeat
*/
static int sh_wdt_set_heartbeat(int t)
{
if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
return -EINVAL;
heartbeat = t;
return 0;
}
/** /**
* sh_wdt_ping - Ping the Watchdog * sh_wdt_ping - Ping the Watchdog
* *
...@@ -160,6 +188,8 @@ static void sh_wdt_ping(unsigned long data) ...@@ -160,6 +188,8 @@ static void sh_wdt_ping(unsigned long data)
sh_wdt_write_cnt(0); sh_wdt_write_cnt(0);
mod_timer(&timer, next_ping_period(clock_division_ratio)); mod_timer(&timer, next_ping_period(clock_division_ratio));
} else {
printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
} }
} }
...@@ -193,11 +223,11 @@ static int sh_wdt_open(struct inode *inode, struct file *file) ...@@ -193,11 +223,11 @@ static int sh_wdt_open(struct inode *inode, struct file *file)
*/ */
static int sh_wdt_close(struct inode *inode, struct file *file) static int sh_wdt_close(struct inode *inode, struct file *file)
{ {
if (!nowayout && shwdt_expect_close == 42) { if (shwdt_expect_close == 42) {
sh_wdt_stop(); sh_wdt_stop();
} else { } else {
printk(KERN_CRIT "shwdt: Unexpected close, not stopping watchdog!\n"); printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
next_heartbeat = jiffies + (heartbeat * HZ); sh_wdt_keepalive();
} }
clear_bit(0, &shwdt_is_open); clear_bit(0, &shwdt_is_open);
...@@ -224,18 +254,20 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, ...@@ -224,18 +254,20 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf,
return -ESPIPE; return -ESPIPE;
if (count) { if (count) {
size_t i; if (!nowayout) {
size_t i;
shwdt_expect_close = 0; shwdt_expect_close = 0;
for (i = 0; i != count; i++) { for (i = 0; i != count; i++) {
char c; char c;
if (get_user(c, buf + i)) if (get_user(c, buf + i))
return -EFAULT; return -EFAULT;
if (c == 'V') if (c == 'V')
shwdt_expect_close = 42; shwdt_expect_close = 42;
}
} }
next_heartbeat = jiffies + (heartbeat * HZ); sh_wdt_keepalive();
} }
return count; return count;
...@@ -255,38 +287,32 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, ...@@ -255,38 +287,32 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf,
static int sh_wdt_ioctl(struct inode *inode, struct file *file, static int sh_wdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int new_timeout; int new_heartbeat;
int options, retval = -EINVAL;
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, return copy_to_user((struct watchdog_info *)arg,
&sh_wdt_info, &sh_wdt_info,
sizeof(sh_wdt_info))) { sizeof(sh_wdt_info)) ? -EFAULT : 0;
return -EFAULT;
}
break;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *)arg); return put_user(0, (int *)arg);
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
next_heartbeat = jiffies + (heartbeat * HZ); sh_wdt_keepalive();
return 0;
break;
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, (int *)arg)) if (get_user(new_heartbeat, (int *)arg))
return -EFAULT; return -EFAULT;
if (new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */
if (sh_wdt_set_heartbeat(new_heartbeat))
return -EINVAL; return -EINVAL;
heartbeat = new_timeout;
next_heartbeat = jiffies + (heartbeat * HZ); sh_wdt_keepalive();
/* Fall */ /* Fall */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, (int *)arg); return put_user(heartbeat, (int *)arg);
case WDIOC_SETOPTIONS: case WDIOC_SETOPTIONS:
{
int options, retval = -EINVAL;
if (get_user(options, (int *)arg)) if (get_user(options, (int *)arg))
return -EFAULT; return -EFAULT;
...@@ -301,7 +327,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, ...@@ -301,7 +327,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
} }
return retval; return retval;
}
default: default:
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
...@@ -346,7 +371,6 @@ static struct watchdog_info sh_wdt_info = { ...@@ -346,7 +371,6 @@ static struct watchdog_info sh_wdt_info = {
static struct notifier_block sh_wdt_notifier = { static struct notifier_block sh_wdt_notifier = {
.notifier_call = sh_wdt_notify_sys, .notifier_call = sh_wdt_notify_sys,
.priority = 0,
}; };
static struct miscdevice sh_wdt_miscdev = { static struct miscdevice sh_wdt_miscdev = {
...@@ -363,21 +387,42 @@ static struct miscdevice sh_wdt_miscdev = { ...@@ -363,21 +387,42 @@ static struct miscdevice sh_wdt_miscdev = {
*/ */
static int __init sh_wdt_init(void) static int __init sh_wdt_init(void)
{ {
if (misc_register(&sh_wdt_miscdev)) { int rc;
printk(KERN_ERR "shwdt: Can't register misc device\n");
return -EINVAL; if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) {
clock_division_ratio = WTCSR_CKS_4096;
printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n",
clock_division_ratio);
} }
if (register_reboot_notifier(&sh_wdt_notifier)) { if (sh_wdt_set_heartbeat(heartbeat))
printk(KERN_ERR "shwdt: Can't register reboot notifier\n"); {
misc_deregister(&sh_wdt_miscdev); heartbeat = WATCHDOG_HEARTBEAT;
return -EINVAL; printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n",
heartbeat);
} }
init_timer(&timer); init_timer(&timer);
timer.function = sh_wdt_ping; timer.function = sh_wdt_ping;
timer.data = 0; timer.data = 0;
rc = register_reboot_notifier(&sh_wdt_notifier);
if (rc) {
printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc);
return rc;
}
rc = misc_register(&sh_wdt_miscdev)
if (rc) {
printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n",
sh_wdt_miscdev.minor, rc);
unregister_reboot_notifier(&sh_wdt_notifier);
return rc;
}
printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
return 0; return 0;
} }
...@@ -389,17 +434,21 @@ static int __init sh_wdt_init(void) ...@@ -389,17 +434,21 @@ static int __init sh_wdt_init(void)
*/ */
static void __exit sh_wdt_exit(void) static void __exit sh_wdt_exit(void)
{ {
unregister_reboot_notifier(&sh_wdt_notifier);
misc_deregister(&sh_wdt_miscdev); misc_deregister(&sh_wdt_miscdev);
unregister_reboot_notifier(&sh_wdt_notifier);
} }
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
MODULE_DESCRIPTION("SuperH watchdog driver"); MODULE_DESCRIPTION("SuperH watchdog driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(clock_division_ratio, int, 0); module_param(clock_division_ratio, int, 0);
MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
module_param(nowayout, int, 0); module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment