aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/softdog.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/softdog.c')
-rw-r--r--drivers/watchdog/softdog.c211
1 files changed, 47 insertions, 164 deletions
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
index bf16ffb4d21e..fe83beb8f1b7 100644
--- a/drivers/watchdog/softdog.c
+++ b/drivers/watchdog/softdog.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * SoftDog 0.07: A Software Watchdog Device 2 * SoftDog: A Software Watchdog Device
3 * 3 *
4 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 4 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
5 * All Rights Reserved. 5 * All Rights Reserved.
@@ -36,45 +36,37 @@
36 * Added Matt Domsch's nowayout module option. 36 * Added Matt Domsch's nowayout module option.
37 */ 37 */
38 38
39#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
40
39#include <linux/module.h> 41#include <linux/module.h>
40#include <linux/moduleparam.h> 42#include <linux/moduleparam.h>
41#include <linux/types.h> 43#include <linux/types.h>
42#include <linux/timer.h> 44#include <linux/timer.h>
43#include <linux/miscdevice.h> 45#include <linux/miscdevice.h>
44#include <linux/watchdog.h> 46#include <linux/watchdog.h>
45#include <linux/fs.h>
46#include <linux/notifier.h> 47#include <linux/notifier.h>
47#include <linux/reboot.h> 48#include <linux/reboot.h>
48#include <linux/init.h> 49#include <linux/init.h>
49#include <linux/jiffies.h> 50#include <linux/jiffies.h>
50#include <linux/uaccess.h>
51#include <linux/kernel.h> 51#include <linux/kernel.h>
52 52
53#define PFX "SoftDog: "
54
55#define TIMER_MARGIN 60 /* Default is 60 seconds */ 53#define TIMER_MARGIN 60 /* Default is 60 seconds */
56static int soft_margin = TIMER_MARGIN; /* in seconds */ 54static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
57module_param(soft_margin, int, 0); 55module_param(soft_margin, uint, 0);
58MODULE_PARM_DESC(soft_margin, 56MODULE_PARM_DESC(soft_margin,
59 "Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default=" 57 "Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
60 __MODULE_STRING(TIMER_MARGIN) ")"); 58 __MODULE_STRING(TIMER_MARGIN) ")");
61 59
62static int nowayout = WATCHDOG_NOWAYOUT; 60static bool nowayout = WATCHDOG_NOWAYOUT;
63module_param(nowayout, int, 0); 61module_param(nowayout, bool, 0);
64MODULE_PARM_DESC(nowayout, 62MODULE_PARM_DESC(nowayout,
65 "Watchdog cannot be stopped once started (default=" 63 "Watchdog cannot be stopped once started (default="
66 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 64 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
67 65
68#ifdef ONLY_TESTING
69static int soft_noboot = 1;
70#else
71static int soft_noboot = 0; 66static int soft_noboot = 0;
72#endif /* ONLY_TESTING */
73
74module_param(soft_noboot, int, 0); 67module_param(soft_noboot, int, 0);
75MODULE_PARM_DESC(soft_noboot, 68MODULE_PARM_DESC(soft_noboot,
76 "Softdog action, set to 1 to ignore reboots, 0 to reboot " 69 "Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)");
77 "(default depends on ONLY_TESTING)");
78 70
79static int soft_panic; 71static int soft_panic;
80module_param(soft_panic, int, 0); 72module_param(soft_panic, int, 0);
@@ -89,9 +81,6 @@ static void watchdog_fire(unsigned long);
89 81
90static struct timer_list watchdog_ticktock = 82static struct timer_list watchdog_ticktock =
91 TIMER_INITIALIZER(watchdog_fire, 0, 0); 83 TIMER_INITIALIZER(watchdog_fire, 0, 0);
92static unsigned long driver_open, orphan_timer;
93static char expect_close;
94
95 84
96/* 85/*
97 * If the timer expires.. 86 * If the timer expires..
@@ -99,18 +88,15 @@ static char expect_close;
99 88
100static void watchdog_fire(unsigned long data) 89static void watchdog_fire(unsigned long data)
101{ 90{
102 if (test_and_clear_bit(0, &orphan_timer))
103 module_put(THIS_MODULE);
104
105 if (soft_noboot) 91 if (soft_noboot)
106 printk(KERN_CRIT PFX "Triggered - Reboot ignored.\n"); 92 pr_crit("Triggered - Reboot ignored\n");
107 else if (soft_panic) { 93 else if (soft_panic) {
108 printk(KERN_CRIT PFX "Initiating panic.\n"); 94 pr_crit("Initiating panic\n");
109 panic("Software Watchdog Timer expired."); 95 panic("Software Watchdog Timer expired");
110 } else { 96 } else {
111 printk(KERN_CRIT PFX "Initiating system reboot.\n"); 97 pr_crit("Initiating system reboot\n");
112 emergency_restart(); 98 emergency_restart();
113 printk(KERN_CRIT PFX "Reboot didn't ?????\n"); 99 pr_crit("Reboot didn't ?????\n");
114 } 100 }
115} 101}
116 102
@@ -118,127 +104,24 @@ static void watchdog_fire(unsigned long data)
118 * Softdog operations 104 * Softdog operations
119 */ 105 */
120 106
121static int softdog_keepalive(void) 107static int softdog_ping(struct watchdog_device *w)
122{ 108{
123 mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); 109 mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ));
124 return 0; 110 return 0;
125} 111}
126 112
127static int softdog_stop(void) 113static int softdog_stop(struct watchdog_device *w)
128{ 114{
129 del_timer(&watchdog_ticktock); 115 del_timer(&watchdog_ticktock);
130 return 0; 116 return 0;
131} 117}
132 118
133static int softdog_set_heartbeat(int t) 119static int softdog_set_timeout(struct watchdog_device *w, unsigned int t)
134{
135 if ((t < 0x0001) || (t > 0xFFFF))
136 return -EINVAL;
137
138 soft_margin = t;
139 return 0;
140}
141
142/*
143 * /dev/watchdog handling
144 */
145
146static int softdog_open(struct inode *inode, struct file *file)
147{
148 if (test_and_set_bit(0, &driver_open))
149 return -EBUSY;
150 if (!test_and_clear_bit(0, &orphan_timer))
151 __module_get(THIS_MODULE);
152 /*
153 * Activate timer
154 */
155 softdog_keepalive();
156 return nonseekable_open(inode, file);
157}
158
159static int softdog_release(struct inode *inode, struct file *file)
160{ 120{
161 /* 121 w->timeout = t;
162 * Shut off the timer.
163 * Lock it in if it's a module and we set nowayout
164 */
165 if (expect_close == 42) {
166 softdog_stop();
167 module_put(THIS_MODULE);
168 } else {
169 printk(KERN_CRIT PFX
170 "Unexpected close, not stopping watchdog!\n");
171 set_bit(0, &orphan_timer);
172 softdog_keepalive();
173 }
174 clear_bit(0, &driver_open);
175 expect_close = 0;
176 return 0; 122 return 0;
177} 123}
178 124
179static ssize_t softdog_write(struct file *file, const char __user *data,
180 size_t len, loff_t *ppos)
181{
182 /*
183 * Refresh the timer.
184 */
185 if (len) {
186 if (!nowayout) {
187 size_t i;
188
189 /* In case it was set long ago */
190 expect_close = 0;
191
192 for (i = 0; i != len; i++) {
193 char c;
194
195 if (get_user(c, data + i))
196 return -EFAULT;
197 if (c == 'V')
198 expect_close = 42;
199 }
200 }
201 softdog_keepalive();
202 }
203 return len;
204}
205
206static long softdog_ioctl(struct file *file, unsigned int cmd,
207 unsigned long arg)
208{
209 void __user *argp = (void __user *)arg;
210 int __user *p = argp;
211 int new_margin;
212 static const struct watchdog_info ident = {
213 .options = WDIOF_SETTIMEOUT |
214 WDIOF_KEEPALIVEPING |
215 WDIOF_MAGICCLOSE,
216 .firmware_version = 0,
217 .identity = "Software Watchdog",
218 };
219 switch (cmd) {
220 case WDIOC_GETSUPPORT:
221 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
222 case WDIOC_GETSTATUS:
223 case WDIOC_GETBOOTSTATUS:
224 return put_user(0, p);
225 case WDIOC_KEEPALIVE:
226 softdog_keepalive();
227 return 0;
228 case WDIOC_SETTIMEOUT:
229 if (get_user(new_margin, p))
230 return -EFAULT;
231 if (softdog_set_heartbeat(new_margin))
232 return -EINVAL;
233 softdog_keepalive();
234 /* Fall */
235 case WDIOC_GETTIMEOUT:
236 return put_user(soft_margin, p);
237 default:
238 return -ENOTTY;
239 }
240}
241
242/* 125/*
243 * Notifier for system down 126 * Notifier for system down
244 */ 127 */
@@ -248,7 +131,7 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
248{ 131{
249 if (code == SYS_DOWN || code == SYS_HALT) 132 if (code == SYS_DOWN || code == SYS_HALT)
250 /* Turn the WDT off */ 133 /* Turn the WDT off */
251 softdog_stop(); 134 softdog_stop(NULL);
252 return NOTIFY_DONE; 135 return NOTIFY_DONE;
253} 136}
254 137
@@ -256,28 +139,29 @@ static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
256 * Kernel Interfaces 139 * Kernel Interfaces
257 */ 140 */
258 141
259static const struct file_operations softdog_fops = { 142static struct notifier_block softdog_notifier = {
260 .owner = THIS_MODULE, 143 .notifier_call = softdog_notify_sys,
261 .llseek = no_llseek,
262 .write = softdog_write,
263 .unlocked_ioctl = softdog_ioctl,
264 .open = softdog_open,
265 .release = softdog_release,
266}; 144};
267 145
268static struct miscdevice softdog_miscdev = { 146static struct watchdog_info softdog_info = {
269 .minor = WATCHDOG_MINOR, 147 .identity = "Software Watchdog",
270 .name = "watchdog", 148 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
271 .fops = &softdog_fops,
272}; 149};
273 150
274static struct notifier_block softdog_notifier = { 151static struct watchdog_ops softdog_ops = {
275 .notifier_call = softdog_notify_sys, 152 .owner = THIS_MODULE,
153 .start = softdog_ping,
154 .stop = softdog_stop,
155 .ping = softdog_ping,
156 .set_timeout = softdog_set_timeout,
276}; 157};
277 158
278static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 " 159static struct watchdog_device softdog_dev = {
279 "initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d " 160 .info = &softdog_info,
280 "(nowayout= %d)\n"; 161 .ops = &softdog_ops,
162 .min_timeout = 1,
163 .max_timeout = 0xFFFF
164};
281 165
282static int __init watchdog_init(void) 166static int __init watchdog_init(void)
283{ 167{
@@ -285,37 +169,36 @@ static int __init watchdog_init(void)
285 169
286 /* Check that the soft_margin value is within it's range; 170 /* Check that the soft_margin value is within it's range;
287 if not reset to the default */ 171 if not reset to the default */
288 if (softdog_set_heartbeat(soft_margin)) { 172 if (soft_margin < 1 || soft_margin > 65535) {
289 softdog_set_heartbeat(TIMER_MARGIN); 173 pr_info("soft_margin must be 0 < soft_margin < 65536, using %d\n",
290 printk(KERN_INFO PFX
291 "soft_margin must be 0 < soft_margin < 65536, using %d\n",
292 TIMER_MARGIN); 174 TIMER_MARGIN);
175 return -EINVAL;
293 } 176 }
177 softdog_dev.timeout = soft_margin;
178
179 watchdog_set_nowayout(&softdog_dev, nowayout);
294 180
295 ret = register_reboot_notifier(&softdog_notifier); 181 ret = register_reboot_notifier(&softdog_notifier);
296 if (ret) { 182 if (ret) {
297 printk(KERN_ERR PFX 183 pr_err("cannot register reboot notifier (err=%d)\n", ret);
298 "cannot register reboot notifier (err=%d)\n", ret);
299 return ret; 184 return ret;
300 } 185 }
301 186
302 ret = misc_register(&softdog_miscdev); 187 ret = watchdog_register_device(&softdog_dev);
303 if (ret) { 188 if (ret) {
304 printk(KERN_ERR PFX
305 "cannot register miscdev on minor=%d (err=%d)\n",
306 WATCHDOG_MINOR, ret);
307 unregister_reboot_notifier(&softdog_notifier); 189 unregister_reboot_notifier(&softdog_notifier);
308 return ret; 190 return ret;
309 } 191 }
310 192
311 printk(banner, soft_noboot, soft_margin, soft_panic, nowayout); 193 pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
194 soft_noboot, soft_margin, soft_panic, nowayout);
312 195
313 return 0; 196 return 0;
314} 197}
315 198
316static void __exit watchdog_exit(void) 199static void __exit watchdog_exit(void)
317{ 200{
318 misc_deregister(&softdog_miscdev); 201 watchdog_unregister_device(&softdog_dev);
319 unregister_reboot_notifier(&softdog_notifier); 202 unregister_reboot_notifier(&softdog_notifier);
320} 203}
321 204