aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/watchdog/watchdog-kernel-api.txt22
-rw-r--r--drivers/watchdog/watchdog_dev.c52
-rw-r--r--include/linux/watchdog.h10
3 files changed, 64 insertions, 20 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
index 15a02595ade1..954134a5c4a4 100644
--- a/Documentation/watchdog/watchdog-kernel-api.txt
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -137,10 +137,10 @@ are:
137* stop: with this routine the watchdog timer device is being stopped. 137* stop: with this routine the watchdog timer device is being stopped.
138 The routine needs a pointer to the watchdog timer device structure as a 138 The routine needs a pointer to the watchdog timer device structure as a
139 parameter. It returns zero on success or a negative errno code for failure. 139 parameter. It returns zero on success or a negative errno code for failure.
140 Some watchdog timer hardware can only be started and not be stopped. The 140 Some watchdog timer hardware can only be started and not be stopped.
141 driver supporting this hardware needs to make sure that a start and stop 141 If a watchdog can not be stopped, the watchdog driver must set the
142 routine is being provided. This can be done by using a timer in the driver 142 WDOG_HW_RUNNING flag in its stop function to inform the watchdog core that
143 that regularly sends a keepalive ping to the watchdog timer hardware. 143 the watchdog is still running.
144 144
145Not all watchdog timer hardware supports the same functionality. That's why 145Not all watchdog timer hardware supports the same functionality. That's why
146all other routines/operations are optional. They only need to be provided if 146all other routines/operations are optional. They only need to be provided if
@@ -189,11 +189,19 @@ The 'ref' and 'unref' operations are no longer used and deprecated.
189The status bits should (preferably) be set with the set_bit and clear_bit alike 189The status bits should (preferably) be set with the set_bit and clear_bit alike
190bit-operations. The status bits that are defined are: 190bit-operations. The status bits that are defined are:
191* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device 191* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
192 is active or not. When the watchdog is active after booting, then you should 192 is active or not from user perspective. User space is expected to send
193 set this status bit (Note: when you register the watchdog timer device with 193 heartbeat requests to the driver while this flag is set.
194 this bit set, then opening /dev/watchdog will skip the start operation)
195* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. 194* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
196 If this bit is set then the watchdog timer will not be able to stop. 195 If this bit is set then the watchdog timer will not be able to stop.
196* WDOG_HW_RUNNING: Set by the watchdog driver if the hardware watchdog is
197 running. The bit must be set if the watchdog timer hardware can not be
198 stopped. The bit may also be set if the watchdog timer is running after
199 booting, before the watchdog device is opened. If set, the watchdog
200 infrastructure will send keepalives to the watchdog hardware while
201 WDOG_ACTIVE is not set.
202 Note: when you register the watchdog timer device with this bit set,
203 then opening /dev/watchdog will skip the start operation but send a keepalive
204 request instead.
197 205
198 To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog 206 To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
199 timer device) you can either: 207 timer device) you can either:
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index e668a9e8b648..5d3a9fa4856e 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -92,7 +92,8 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd)
92 * requests. 92 * requests.
93 * - Userspace requests a longer timeout than the hardware can handle. 93 * - Userspace requests a longer timeout than the hardware can handle.
94 */ 94 */
95 return watchdog_active(wdd) && hm && t > hm; 95 return hm && ((watchdog_active(wdd) && t > hm) ||
96 (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)));
96} 97}
97 98
98static long watchdog_next_keepalive(struct watchdog_device *wdd) 99static long watchdog_next_keepalive(struct watchdog_device *wdd)
@@ -108,6 +109,9 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd)
108 hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms); 109 hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms);
109 keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); 110 keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
110 111
112 if (!watchdog_active(wdd))
113 return keepalive_interval;
114
111 /* 115 /*
112 * To ensure that the watchdog times out wdd->timeout seconds 116 * To ensure that the watchdog times out wdd->timeout seconds
113 * after the most recent ping from userspace, the last 117 * after the most recent ping from userspace, the last
@@ -161,7 +165,7 @@ static int watchdog_ping(struct watchdog_device *wdd)
161{ 165{
162 struct watchdog_core_data *wd_data = wdd->wd_data; 166 struct watchdog_core_data *wd_data = wdd->wd_data;
163 167
164 if (!watchdog_active(wdd)) 168 if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))
165 return 0; 169 return 0;
166 170
167 wd_data->last_keepalive = jiffies; 171 wd_data->last_keepalive = jiffies;
@@ -178,7 +182,7 @@ static void watchdog_ping_work(struct work_struct *work)
178 182
179 mutex_lock(&wd_data->lock); 183 mutex_lock(&wd_data->lock);
180 wdd = wd_data->wdd; 184 wdd = wd_data->wdd;
181 if (wdd && watchdog_active(wdd)) 185 if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)))
182 __watchdog_ping(wdd); 186 __watchdog_ping(wdd);
183 mutex_unlock(&wd_data->lock); 187 mutex_unlock(&wd_data->lock);
184} 188}
@@ -204,7 +208,10 @@ static int watchdog_start(struct watchdog_device *wdd)
204 return 0; 208 return 0;
205 209
206 started_at = jiffies; 210 started_at = jiffies;
207 err = wdd->ops->start(wdd); 211 if (watchdog_hw_running(wdd) && wdd->ops->ping)
212 err = wdd->ops->ping(wdd);
213 else
214 err = wdd->ops->start(wdd);
208 if (err == 0) { 215 if (err == 0) {
209 set_bit(WDOG_ACTIVE, &wdd->status); 216 set_bit(WDOG_ACTIVE, &wdd->status);
210 wd_data->last_keepalive = started_at; 217 wd_data->last_keepalive = started_at;
@@ -228,8 +235,7 @@ static int watchdog_start(struct watchdog_device *wdd)
228 235
229static int watchdog_stop(struct watchdog_device *wdd) 236static int watchdog_stop(struct watchdog_device *wdd)
230{ 237{
231 struct watchdog_core_data *wd_data = wdd->wd_data; 238 int err = 0;
232 int err;
233 239
234 if (!watchdog_active(wdd)) 240 if (!watchdog_active(wdd))
235 return 0; 241 return 0;
@@ -243,7 +249,7 @@ static int watchdog_stop(struct watchdog_device *wdd)
243 err = wdd->ops->stop(wdd); 249 err = wdd->ops->stop(wdd);
244 if (err == 0) { 250 if (err == 0) {
245 clear_bit(WDOG_ACTIVE, &wdd->status); 251 clear_bit(WDOG_ACTIVE, &wdd->status);
246 cancel_delayed_work(&wd_data->work); 252 watchdog_update_worker(wdd);
247 } 253 }
248 254
249 return err; 255 return err;
@@ -641,7 +647,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
641 * If the /dev/watchdog device is open, we don't want the module 647 * If the /dev/watchdog device is open, we don't want the module
642 * to be unloaded. 648 * to be unloaded.
643 */ 649 */
644 if (!try_module_get(wdd->ops->owner)) { 650 if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
645 err = -EBUSY; 651 err = -EBUSY;
646 goto out_clear; 652 goto out_clear;
647 } 653 }
@@ -652,7 +658,8 @@ static int watchdog_open(struct inode *inode, struct file *file)
652 658
653 file->private_data = wd_data; 659 file->private_data = wd_data;
654 660
655 kref_get(&wd_data->kref); 661 if (!watchdog_hw_running(wdd))
662 kref_get(&wd_data->kref);
656 663
657 /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ 664 /* dev/watchdog is a virtual (and thus non-seekable) filesystem */
658 return nonseekable_open(inode, file); 665 return nonseekable_open(inode, file);
@@ -713,15 +720,22 @@ static int watchdog_release(struct inode *inode, struct file *file)
713 } 720 }
714 721
715 cancel_delayed_work_sync(&wd_data->work); 722 cancel_delayed_work_sync(&wd_data->work);
723 watchdog_update_worker(wdd);
716 724
717 /* make sure that /dev/watchdog can be re-opened */ 725 /* make sure that /dev/watchdog can be re-opened */
718 clear_bit(_WDOG_DEV_OPEN, &wd_data->status); 726 clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
719 727
720done: 728done:
721 mutex_unlock(&wd_data->lock); 729 mutex_unlock(&wd_data->lock);
722 /* Allow the owner module to be unloaded again */ 730 /*
723 module_put(wd_data->cdev.owner); 731 * Allow the owner module to be unloaded again unless the watchdog
724 kref_put(&wd_data->kref, watchdog_core_data_release); 732 * is still running. If the watchdog is still running, it can not
733 * be stopped, and its driver must not be unloaded.
734 */
735 if (!watchdog_hw_running(wdd)) {
736 module_put(wdd->ops->owner);
737 kref_put(&wd_data->kref, watchdog_core_data_release);
738 }
725 return 0; 739 return 0;
726} 740}
727 741
@@ -798,8 +812,20 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
798 old_wd_data = NULL; 812 old_wd_data = NULL;
799 kref_put(&wd_data->kref, watchdog_core_data_release); 813 kref_put(&wd_data->kref, watchdog_core_data_release);
800 } 814 }
815 return err;
801 } 816 }
802 return err; 817
818 /*
819 * If the watchdog is running, prevent its driver from being unloaded,
820 * and schedule an immediate ping.
821 */
822 if (watchdog_hw_running(wdd)) {
823 __module_get(wdd->ops->owner);
824 kref_get(&wd_data->kref);
825 queue_delayed_work(watchdog_wq, &wd_data->work, 0);
826 }
827
828 return 0;
803} 829}
804 830
805/* 831/*
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 8e82daecb7d3..e2f45549b243 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -105,6 +105,7 @@ struct watchdog_device {
105#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ 105#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
106#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ 106#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */
107#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ 107#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
108#define WDOG_HW_RUNNING 3 /* True if HW watchdog running */
108 struct list_head deferred; 109 struct list_head deferred;
109}; 110};
110 111
@@ -117,6 +118,15 @@ static inline bool watchdog_active(struct watchdog_device *wdd)
117 return test_bit(WDOG_ACTIVE, &wdd->status); 118 return test_bit(WDOG_ACTIVE, &wdd->status);
118} 119}
119 120
121/*
122 * Use the following function to check whether or not the hardware watchdog
123 * is running
124 */
125static inline bool watchdog_hw_running(struct watchdog_device *wdd)
126{
127 return test_bit(WDOG_HW_RUNNING, &wdd->status);
128}
129
120/* Use the following function to set the nowayout feature */ 130/* Use the following function to set the nowayout feature */
121static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) 131static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout)
122{ 132{