Blame view

drivers/watchdog/softdog.c 7.51 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   *	SoftDog	0.07:	A Software Watchdog Device
   *
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
4
5
   *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
   *							All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
   *	warranty for any of this software. This material is provided
   *	"AS-IS" and at no charge.
   *
   *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
   *
   *	Software only watchdog driver. Unlike its big brother the WDT501P
   *	driver this won't always recover a failed machine.
   *
   *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
   *	Modularised.
   *	Added soft_margin; use upon insmod to change the timer delay.
   *	NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
   *	    minors.
   *
   *  19980911 Alan Cox
   *	Made SMP safe for 2.3.x
   *
   *  20011127 Joel Becker (jlbec@evilplan.org>
   *	Added soft_noboot; Allows testing the softdog trigger without
   *	requiring a recompile.
   *	Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
   *
   *  20020530 Joel Becker <joel.becker@oracle.com>
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
36
   *	Added Matt Domsch's nowayout module option.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
  #include <linux/types.h>
  #include <linux/timer.h>
  #include <linux/miscdevice.h>
  #include <linux/watchdog.h>
  #include <linux/fs.h>
  #include <linux/notifier.h>
  #include <linux/reboot.h>
  #include <linux/init.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
49
  #include <linux/jiffies.h>
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
50
  #include <linux/uaccess.h>
7fff4beb3   Anithra P Janakiraman   watchdog: softdog...
51
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
  
  #define PFX "SoftDog: "
  
  #define TIMER_MARGIN	60		/* Default is 60 seconds */
  static int soft_margin = TIMER_MARGIN;	/* in seconds */
  module_param(soft_margin, int, 0);
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
58
59
60
  MODULE_PARM_DESC(soft_margin,
  	"Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
  					__MODULE_STRING(TIMER_MARGIN) ")");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61

4bfdf3783   Andrey Panin   [PATCH] consolida...
62
  static int nowayout = WATCHDOG_NOWAYOUT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  module_param(nowayout, int, 0);
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
64
65
66
  MODULE_PARM_DESC(nowayout,
  		"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
74
  
  #ifdef ONLY_TESTING
  static int soft_noboot = 1;
  #else
  static int soft_noboot = 0;
  #endif  /* ONLY_TESTING */
  
  module_param(soft_noboot, int, 0);
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
75
76
77
  MODULE_PARM_DESC(soft_noboot,
  	"Softdog action, set to 1 to ignore reboots, 0 to reboot "
  					"(default depends on ONLY_TESTING)");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78

7fff4beb3   Anithra P Janakiraman   watchdog: softdog...
79
80
81
82
  static int soft_panic;
  module_param(soft_panic, int, 0);
  MODULE_PARM_DESC(soft_panic,
  	"Softdog action, set to 1 to panic, 0 to reboot (default=0)");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
89
90
  /*
   *	Our timer
   */
  
  static void watchdog_fire(unsigned long);
  
  static struct timer_list watchdog_ticktock =
  		TIMER_INITIALIZER(watchdog_fire, 0, 0);
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
91
  static unsigned long driver_open, orphan_timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
  static char expect_close;
  
  
  /*
   *	If the timer expires..
   */
  
  static void watchdog_fire(unsigned long data)
  {
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
101
102
  	if (test_and_clear_bit(0, &orphan_timer))
  		module_put(THIS_MODULE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
  	if (soft_noboot)
  		printk(KERN_CRIT PFX "Triggered - Reboot ignored.
  ");
7fff4beb3   Anithra P Janakiraman   watchdog: softdog...
106
107
108
109
110
  	else if (soft_panic) {
  		printk(KERN_CRIT PFX "Initiating panic.
  ");
  		panic("Software Watchdog Timer expired.");
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
  		printk(KERN_CRIT PFX "Initiating system reboot.
  ");
479d0f41e   Andrew Morton   [PATCH] softdog b...
113
  		emergency_restart();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  		printk(KERN_CRIT PFX "Reboot didn't ?????
  ");
  	}
  }
  
  /*
   *	Softdog operations
   */
  
  static int softdog_keepalive(void)
  {
  	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
  	return 0;
  }
  
  static int softdog_stop(void)
  {
  	del_timer(&watchdog_ticktock);
  	return 0;
  }
  
  static int softdog_set_heartbeat(int t)
  {
  	if ((t < 0x0001) || (t > 0xFFFF))
  		return -EINVAL;
  
  	soft_margin = t;
  	return 0;
  }
  
  /*
   *	/dev/watchdog handling
   */
  
  static int softdog_open(struct inode *inode, struct file *file)
  {
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
150
  	if (test_and_set_bit(0, &driver_open))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  		return -EBUSY;
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
152
  	if (!test_and_clear_bit(0, &orphan_timer))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
  		__module_get(THIS_MODULE);
  	/*
  	 *	Activate timer
  	 */
  	softdog_keepalive();
  	return nonseekable_open(inode, file);
  }
  
  static int softdog_release(struct inode *inode, struct file *file)
  {
  	/*
  	 *	Shut off the timer.
5f3b27569   Wim Van Sebroeck   watchdog: cleanup...
165
  	 *	Lock it in if it's a module and we set nowayout
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
  	 */
  	if (expect_close == 42) {
  		softdog_stop();
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
169
  		module_put(THIS_MODULE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  	} else {
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
171
172
173
  		printk(KERN_CRIT PFX
  			"Unexpected close, not stopping watchdog!
  ");
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
174
  		set_bit(0, &orphan_timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  		softdog_keepalive();
  	}
1cc772481   Chuck Ebbert   [WATCHDOG] softdo...
177
  	clear_bit(0, &driver_open);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
  	expect_close = 0;
  	return 0;
  }
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
181
182
  static ssize_t softdog_write(struct file *file, const char __user *data,
  						size_t len, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
  {
  	/*
  	 *	Refresh the timer.
  	 */
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
187
  	if (len) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  		if (!nowayout) {
  			size_t i;
  
  			/* In case it was set long ago */
  			expect_close = 0;
  
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, data + i))
  					return -EFAULT;
  				if (c == 'V')
  					expect_close = 42;
  			}
  		}
  		softdog_keepalive();
  	}
  	return len;
  }
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
207
208
  static long softdog_ioctl(struct file *file, unsigned int cmd,
  							unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  {
  	void __user *argp = (void __user *)arg;
  	int __user *p = argp;
  	int new_margin;
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
213
  	static const struct watchdog_info ident = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
219
220
  		.options =		WDIOF_SETTIMEOUT |
  					WDIOF_KEEPALIVEPING |
  					WDIOF_MAGICCLOSE,
  		.firmware_version =	0,
  		.identity =		"Software Watchdog",
  	};
  	switch (cmd) {
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	case WDIOC_GETSUPPORT:
  		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
  		return put_user(0, p);
  	case WDIOC_KEEPALIVE:
  		softdog_keepalive();
  		return 0;
  	case WDIOC_SETTIMEOUT:
  		if (get_user(new_margin, p))
  			return -EFAULT;
  		if (softdog_set_heartbeat(new_margin))
  			return -EINVAL;
  		softdog_keepalive();
  		/* Fall */
  	case WDIOC_GETTIMEOUT:
  		return put_user(soft_margin, p);
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
238
239
  	default:
  		return -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
  	}
  }
  
  /*
   *	Notifier for system down
   */
  
  static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
  	void *unused)
  {
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
250
  	if (code == SYS_DOWN || code == SYS_HALT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
  		/* Turn the WDT off */
  		softdog_stop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
  	return NOTIFY_DONE;
  }
  
  /*
   *	Kernel Interfaces
   */
62322d255   Arjan van de Ven   [PATCH] make more...
259
  static const struct file_operations softdog_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
  	.write		= softdog_write,
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
263
  	.unlocked_ioctl	= softdog_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
272
273
274
275
276
  	.open		= softdog_open,
  	.release	= softdog_release,
  };
  
  static struct miscdevice softdog_miscdev = {
  	.minor		= WATCHDOG_MINOR,
  	.name		= "watchdog",
  	.fops		= &softdog_fops,
  };
  
  static struct notifier_block softdog_notifier = {
  	.notifier_call	= softdog_notify_sys,
  };
a77dba7e4   Wim Van Sebroeck   [WATCHDOG] Some m...
277
  static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 "
7fff4beb3   Anithra P Janakiraman   watchdog: softdog...
278
279
280
  	"initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d "
  	"(nowayout= %d)
  ";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
  
  static int __init watchdog_init(void)
  {
  	int ret;
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
285
286
  	/* Check that the soft_margin value is within it's range;
  	   if not reset to the default */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
  	if (softdog_set_heartbeat(soft_margin)) {
  		softdog_set_heartbeat(TIMER_MARGIN);
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
289
290
291
  		printk(KERN_INFO PFX
  		    "soft_margin must be 0 < soft_margin < 65536, using %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
  			TIMER_MARGIN);
  	}
  
  	ret = register_reboot_notifier(&softdog_notifier);
  	if (ret) {
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
297
298
299
  		printk(KERN_ERR PFX
  			"cannot register reboot notifier (err=%d)
  ", ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
  		return ret;
  	}
  
  	ret = misc_register(&softdog_miscdev);
  	if (ret) {
f92d3749d   Alan Cox   [WATCHDOG 47/57] ...
305
306
307
308
  		printk(KERN_ERR PFX
  			"cannot register miscdev on minor=%d (err=%d)
  ",
  						WATCHDOG_MINOR, ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
  		unregister_reboot_notifier(&softdog_notifier);
  		return ret;
  	}
7fff4beb3   Anithra P Janakiraman   watchdog: softdog...
312
  	printk(banner, soft_noboot, soft_margin, soft_panic, nowayout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  
  	return 0;
  }
  
  static void __exit watchdog_exit(void)
  {
  	misc_deregister(&softdog_miscdev);
  	unregister_reboot_notifier(&softdog_notifier);
  }
  
  module_init(watchdog_init);
  module_exit(watchdog_exit);
  
  MODULE_AUTHOR("Alan Cox");
  MODULE_DESCRIPTION("Software Watchdog Device Driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);