diff mbox

cbus-retu-wdt: Make the watchdog driver fully preemptible

Message ID 1299082307.13604.12.camel@marge (mailing list archive)
State Accepted
Commit 03d3be4d5e771268d6c3a6d6613ee00e590ecc2e
Delegated to: Tony Lindgren
Headers show

Commit Message

Michael Buesch March 2, 2011, 4:11 p.m. UTC
None
diff mbox

Patch

Index: linux-2.6.38-rc6/drivers/cbus/retu-wdt.c
===================================================================
--- linux-2.6.38-rc6.orig/drivers/cbus/retu-wdt.c	2011-03-02 16:08:59.022211654 +0100
+++ linux-2.6.38-rc6/drivers/cbus/retu-wdt.c	2011-03-02 16:29:43.215284501 +0100
@@ -58,13 +58,11 @@  struct retu_wdt_dev {
 	struct device		*dev;
 	int			users;
 	struct miscdevice	retu_wdt_miscdev;
-	struct timer_list	ping_timer;
+	struct delayed_work	ping_work;
 };
 
 static struct retu_wdt_dev *retu_wdt;
 
-static void retu_wdt_set_ping_timer(unsigned long enable);
-
 static int _retu_modify_counter(unsigned int new)
 {
 	if (retu_wdt)
@@ -86,6 +84,31 @@  static int retu_modify_counter(unsigned
 	return 0;
 }
 
+/*
+ * Since retu watchdog cannot be disabled in hardware, we must kick it
+ * with a timer until userspace watchdog software takes over. Do this
+ * unless /dev/watchdog is open or CONFIG_WATCHDOG_NOWAYOUT is set.
+ */
+static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
+{
+	_retu_modify_counter(RETU_WDT_MAX_TIMER);
+	schedule_delayed_work(&wdev->ping_work,
+			      round_jiffies_relative(RETU_WDT_DEFAULT_TIMER * HZ));
+}
+
+static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
+{
+	_retu_modify_counter(RETU_WDT_MAX_TIMER);
+	cancel_delayed_work_sync(&wdev->ping_work);
+}
+
+static void retu_wdt_ping_work(struct work_struct *work)
+{
+	struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
+					struct retu_wdt_dev, ping_work);
+	retu_wdt_ping_enable(wdev);
+}
+
 static ssize_t retu_wdt_period_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
@@ -105,7 +128,7 @@  static ssize_t retu_wdt_period_store(str
 	int ret;
 
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
-	retu_wdt_set_ping_timer(0);
+	retu_wdt_ping_disable(retu_wdt);
 #endif
 
 	if (sscanf(buf, "%u", &new_period) != 1) {
@@ -136,30 +159,13 @@  static DEVICE_ATTR(period, S_IRUGO | S_I
 			retu_wdt_period_store);
 static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL);
 
-/*----------------------------------------------------------------------------*/
-
-/*
- * Since retu watchdog cannot be disabled in hardware, we must kick it
- * with a timer until userspace watchdog software takes over. Do this
- * unless /dev/watchdog is open or CONFIG_WATCHDOG_NOWAYOUT is set.
- */
-static void retu_wdt_set_ping_timer(unsigned long enable)
-{
-	_retu_modify_counter(RETU_WDT_MAX_TIMER);
-	if (enable)
-		mod_timer(&retu_wdt->ping_timer,
-				jiffies + RETU_WDT_DEFAULT_TIMER * HZ);
-	else
-		del_timer_sync(&retu_wdt->ping_timer);
-}
-
 static int retu_wdt_open(struct inode *inode, struct file *file)
 {
 	if (test_and_set_bit(1, (unsigned long *)&(retu_wdt->users)))
 		return -EBUSY;
 
 	file->private_data = (void *)retu_wdt;
-	retu_wdt_set_ping_timer(0);
+	retu_wdt_ping_disable(retu_wdt);
 
 	return nonseekable_open(inode, file);
 }
@@ -169,7 +175,7 @@  static int retu_wdt_release(struct inode
 	struct retu_wdt_dev *wdev = file->private_data;
 
 #ifndef CONFIG_WATCHDOG_NOWAYOUT
-	retu_wdt_set_ping_timer(1);
+	retu_wdt_ping_enable(retu_wdt);
 #endif
 	wdev->users = 0;
 
@@ -232,7 +238,7 @@  static int __devinit retu_wdt_ping(void)
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
 	retu_modify_counter(RETU_WDT_MAX_TIMER);
 #else
-	retu_wdt_set_ping_timer(1);
+	retu_wdt_ping_enable(retu_wdt);
 #endif
 
 	return 0;
@@ -283,7 +289,7 @@  static int __init retu_wdt_probe(struct
 	if (ret)
 		goto free3;
 
-	setup_timer(&wdev->ping_timer, retu_wdt_set_ping_timer, 1);
+	INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
 
 	/* passed as module parameter? */
 	ret = retu_modify_counter(counter_param);
@@ -326,6 +332,7 @@  static int __devexit retu_wdt_remove(str
 	misc_deregister(&wdev->retu_wdt_miscdev);
 	device_remove_file(&pdev->dev, &dev_attr_period);
 	device_remove_file(&pdev->dev, &dev_attr_counter);
+	cancel_delayed_work_sync(&wdev->ping_work);
 	kfree(wdev);
 
 	return 0;