watchdog (iTCO_wdt) heartbeat/timeout value is ignored after suspend-to-{RAM,disk}

April 15th, 2012 - 12:40 pm ET by David Madore | Report spam
Dear list,

My motherboard's chipset (ICH7) has a watchdog handled by the iTCO_wdt
driver. This module takes a "heartbeat" parameter which specifies the
timeout after which the system is reset.

It appears that this parameter no longer has an effect after the
system is suspended (to RAM or disk) and then resumed: I use
heartbeat, which initially works, but after a resume, the system is
rebooted despite the wd_keepalive daemon writing to the device every
30 seconds or so.

I imagine this is easy to fix.

David A. Madore
( http://www.madore.org/~david/ )
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
email Follow the discussionReplies 1 replyReplies Make a reply

Replies

#1 David Madore
April 15th, 2012 - 04:00 pm ET | Report spam
The iTCO_wdt watchdog state and timeout (heartbeat) need to be
restored after resume. Use pm_ops to do this.

Signed-off-by: David A. Madore <david+



old/drivers/watchdog/iTCO_wdt.c 2012-04-13 18:42:02.000000000 +0200
+++ new/drivers/watchdog/iTCO_wdt.c 2012-04-15 20:43:39.609718697 +0200
@@ -56,6 +56,7 @@
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV
(WATCHDOG_MINOR) */
+#include <linux/pm.h> /* For struct dev_pm_ops */
#include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
#include <linux/fs.h> /* For file operations */
@@ -400,6 +401,8 @@ static struct { /* this is private data
spinlock_t io_lock;
/* the PCI-device */
struct pci_dev *pdev;
+ /* whether the timer is started */
+ char is_started;
} iTCO_wdt_private;

/* the watchdog platform device */
@@ -506,6 +509,7 @@ static int iTCO_wdt_start(void)
val &= 0xf7ff;
outw(val, TCO1_CNT);
val = inw(TCO1_CNT);
+ iTCO_wdt_private.is_started = 1;
spin_unlock(&iTCO_wdt_private.io_lock);

if (val & 0x0800)
@@ -530,6 +534,8 @@ static int iTCO_wdt_stop(void)
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit();

+ iTCO_wdt_private.is_started = 0;
+
spin_unlock(&iTCO_wdt_private.io_lock);

if ((val & 0x0800) == 0)
@@ -965,6 +971,35 @@ static void iTCO_wdt_shutdown(struct pla
iTCO_wdt_stop();
}

+#ifdef CONFIG_PM
+static int iTCO_wdt_suspend(struct device *dev)
+{
+ char save_is_started = iTCO_wdt_private.is_started;
+ iTCO_wdt_stop();
+ iTCO_wdt_private.is_started = save_is_started;
+ return 0;
+}
+
+static int iTCO_wdt_resume(struct device *dev)
+{
+ iTCO_wdt_set_heartbeat(heartbeat);
+ iTCO_wdt_keepalive();
+ if (iTCO_wdt_private.is_started) {
+ printk(KERN_INFO PFX "Watchdog restarted after resume.");
+ iTCO_wdt_start();
+ } else {
+ printk(KERN_INFO PFX "Watchdog stopped after resume.");
+ iTCO_wdt_stop();
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops iTCO_wdt_pm_ops = {
+ .suspend = iTCO_wdt_suspend,
+ .resume = iTCO_wdt_resume,
+};
+#endif /* CONFIG_PM */
+
static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe,
.remove = __devexit_p(iTCO_wdt_remove),
@@ -972,6 +1007,9 @@ static struct platform_driver iTCO_wdt_d
.driver = {
.owner = THIS_MODULE,
.name = DRV_NAME,
+#ifdef CONFIG_PM
+ .pm = &iTCO_wdt_pm_ops,
+#endif /* CONFIG_PM */
},
};


David A. Madore
( http://www.madore.org/~david/ )
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Similar topics