• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/s390/char/
1/*
2 * Watchdog implementation based on z/VM Watchdog Timer API
3 *
4 * Copyright IBM Corp. 2004,2009
5 *
6 * The user space watchdog daemon can use this driver as
7 * /dev/vmwatchdog to have z/VM execute the specified CP
8 * command when the timeout expires. The default command is
9 * "IPL", which which cause an immediate reboot.
10 */
11#define KMSG_COMPONENT "vmwatchdog"
12#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
13
14#include <linux/init.h>
15#include <linux/fs.h>
16#include <linux/kernel.h>
17#include <linux/miscdevice.h>
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/slab.h>
21#include <linux/suspend.h>
22#include <linux/watchdog.h>
23
24#include <asm/ebcdic.h>
25#include <asm/io.h>
26#include <asm/uaccess.h>
27
28#define MAX_CMDLEN 240
29#define MIN_INTERVAL 15
30static char vmwdt_cmd[MAX_CMDLEN] = "IPL";
31static int vmwdt_conceal;
32
33static int vmwdt_nowayout = WATCHDOG_NOWAYOUT;
34
35MODULE_LICENSE("GPL");
36MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
37MODULE_DESCRIPTION("z/VM Watchdog Timer");
38module_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644);
39MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers");
40module_param_named(conceal, vmwdt_conceal, bool, 0644);
41MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog "
42		" is active");
43module_param_named(nowayout, vmwdt_nowayout, bool, 0);
44MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
45		" (default=CONFIG_WATCHDOG_NOWAYOUT)");
46MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
47
48static unsigned int vmwdt_interval = 60;
49static unsigned long vmwdt_is_open;
50static int vmwdt_expect_close;
51
52static DEFINE_MUTEX(vmwdt_mutex);
53
54#define VMWDT_OPEN	0	/* devnode is open or suspend in progress */
55#define VMWDT_RUNNING	1	/* The watchdog is armed */
56
57enum vmwdt_func {
58	/* function codes */
59	wdt_init   = 0,
60	wdt_change = 1,
61	wdt_cancel = 2,
62	/* flags */
63	wdt_conceal = 0x80000000,
64};
65
66static int __diag288(enum vmwdt_func func, unsigned int timeout,
67			    char *cmd, size_t len)
68{
69	register unsigned long __func asm("2") = func;
70	register unsigned long __timeout asm("3") = timeout;
71	register unsigned long __cmdp asm("4") = virt_to_phys(cmd);
72	register unsigned long __cmdl asm("5") = len;
73	int err;
74
75	err = -EINVAL;
76	asm volatile(
77		"	diag	%1,%3,0x288\n"
78		"0:	la	%0,0\n"
79		"1:\n"
80		EX_TABLE(0b,1b)
81		: "+d" (err) : "d"(__func), "d"(__timeout),
82		  "d"(__cmdp), "d"(__cmdl) : "1", "cc");
83	return err;
84}
85
86static int vmwdt_keepalive(void)
87{
88	/* we allocate new memory every time to avoid having
89	 * to track the state. static allocation is not an
90	 * option since that might not be contiguous in real
91	 * storage in case of a modular build */
92	static char *ebc_cmd;
93	size_t len;
94	int ret;
95	unsigned int func;
96
97	ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
98	if (!ebc_cmd)
99		return -ENOMEM;
100
101	len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN);
102	ASCEBC(ebc_cmd, MAX_CMDLEN);
103	EBC_TOUPPER(ebc_cmd, MAX_CMDLEN);
104
105	func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init;
106	set_bit(VMWDT_RUNNING, &vmwdt_is_open);
107	ret = __diag288(func, vmwdt_interval, ebc_cmd, len);
108	WARN_ON(ret != 0);
109	kfree(ebc_cmd);
110	return ret;
111}
112
113static int vmwdt_disable(void)
114{
115	int ret = __diag288(wdt_cancel, 0, "", 0);
116	WARN_ON(ret != 0);
117	clear_bit(VMWDT_RUNNING, &vmwdt_is_open);
118	return ret;
119}
120
121static int __init vmwdt_probe(void)
122{
123	/* there is no real way to see if the watchdog is supported,
124	 * so we try initializing it with a NOP command ("BEGIN")
125	 * that won't cause any harm even if the following disable
126	 * fails for some reason */
127	static char __initdata ebc_begin[] = {
128		194, 197, 199, 201, 213
129	};
130	if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0)
131		return -EINVAL;
132	return vmwdt_disable();
133}
134
135static int vmwdt_open(struct inode *i, struct file *f)
136{
137	int ret;
138	if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open))
139		return -EBUSY;
140	ret = vmwdt_keepalive();
141	if (ret)
142		clear_bit(VMWDT_OPEN, &vmwdt_is_open);
143	return ret ? ret : nonseekable_open(i, f);
144}
145
146static int vmwdt_close(struct inode *i, struct file *f)
147{
148	if (vmwdt_expect_close == 42)
149		vmwdt_disable();
150	vmwdt_expect_close = 0;
151	clear_bit(VMWDT_OPEN, &vmwdt_is_open);
152	return 0;
153}
154
155static struct watchdog_info vmwdt_info = {
156	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
157	.firmware_version = 0,
158	.identity = "z/VM Watchdog Timer",
159};
160
161static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg)
162{
163	switch (cmd) {
164	case WDIOC_GETSUPPORT:
165		if (copy_to_user((void __user *)arg, &vmwdt_info,
166					sizeof(vmwdt_info)))
167			return -EFAULT;
168		return 0;
169	case WDIOC_GETSTATUS:
170	case WDIOC_GETBOOTSTATUS:
171		return put_user(0, (int __user *)arg);
172	case WDIOC_GETTEMP:
173		return -EINVAL;
174	case WDIOC_SETOPTIONS:
175		{
176			int options, ret;
177			if (get_user(options, (int __user *)arg))
178				return -EFAULT;
179			ret = -EINVAL;
180			if (options & WDIOS_DISABLECARD) {
181				ret = vmwdt_disable();
182				if (ret)
183					return ret;
184			}
185			if (options & WDIOS_ENABLECARD) {
186				ret = vmwdt_keepalive();
187			}
188			return ret;
189		}
190	case WDIOC_GETTIMEOUT:
191		return put_user(vmwdt_interval, (int __user *)arg);
192	case WDIOC_SETTIMEOUT:
193		{
194			int interval;
195			if (get_user(interval, (int __user *)arg))
196				return -EFAULT;
197			if (interval < MIN_INTERVAL)
198				return -EINVAL;
199			vmwdt_interval = interval;
200		}
201		return vmwdt_keepalive();
202	case WDIOC_KEEPALIVE:
203		return vmwdt_keepalive();
204	}
205	return -EINVAL;
206}
207
208static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
209{
210	int rc;
211
212	mutex_lock(&vmwdt_mutex);
213	rc = __vmwdt_ioctl(cmd, arg);
214	mutex_unlock(&vmwdt_mutex);
215	return (long) rc;
216}
217
218static ssize_t vmwdt_write(struct file *f, const char __user *buf,
219				size_t count, loff_t *ppos)
220{
221	if(count) {
222		if (!vmwdt_nowayout) {
223			size_t i;
224
225			/* note: just in case someone wrote the magic character
226			 * five months ago... */
227			vmwdt_expect_close = 0;
228
229			for (i = 0; i != count; i++) {
230				char c;
231				if (get_user(c, buf+i))
232					return -EFAULT;
233				if (c == 'V')
234					vmwdt_expect_close = 42;
235			}
236		}
237		/* someone wrote to us, we should restart timer */
238		vmwdt_keepalive();
239	}
240	return count;
241}
242
243static int vmwdt_resume(void)
244{
245	clear_bit(VMWDT_OPEN, &vmwdt_is_open);
246	return NOTIFY_DONE;
247}
248
249/*
250 * It makes no sense to go into suspend while the watchdog is running.
251 * Depending on the memory size, the watchdog might trigger, while we
252 * are still saving the memory.
253 * We reuse the open flag to ensure that suspend and watchdog open are
254 * exclusive operations
255 */
256static int vmwdt_suspend(void)
257{
258	if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
259		pr_err("The system cannot be suspended while the watchdog"
260			" is in use\n");
261		return NOTIFY_BAD;
262	}
263	if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) {
264		clear_bit(VMWDT_OPEN, &vmwdt_is_open);
265		pr_err("The system cannot be suspended while the watchdog"
266			" is running\n");
267		return NOTIFY_BAD;
268	}
269	return NOTIFY_DONE;
270}
271
272/*
273 * This function is called for suspend and resume.
274 */
275static int vmwdt_power_event(struct notifier_block *this, unsigned long event,
276			     void *ptr)
277{
278	switch (event) {
279	case PM_POST_HIBERNATION:
280	case PM_POST_SUSPEND:
281		return vmwdt_resume();
282	case PM_HIBERNATION_PREPARE:
283	case PM_SUSPEND_PREPARE:
284		return vmwdt_suspend();
285	default:
286		return NOTIFY_DONE;
287	}
288}
289
290static struct notifier_block vmwdt_power_notifier = {
291	.notifier_call = vmwdt_power_event,
292};
293
294static const struct file_operations vmwdt_fops = {
295	.open    = &vmwdt_open,
296	.release = &vmwdt_close,
297	.unlocked_ioctl = &vmwdt_ioctl,
298	.write   = &vmwdt_write,
299	.owner   = THIS_MODULE,
300};
301
302static struct miscdevice vmwdt_dev = {
303	.minor      = WATCHDOG_MINOR,
304	.name       = "watchdog",
305	.fops       = &vmwdt_fops,
306};
307
308static int __init vmwdt_init(void)
309{
310	int ret;
311
312	ret = vmwdt_probe();
313	if (ret)
314		return ret;
315	ret = register_pm_notifier(&vmwdt_power_notifier);
316	if (ret)
317		return ret;
318	/*
319	 * misc_register() has to be the last action in module_init(), because
320	 * file operations will be available right after this.
321	 */
322	ret = misc_register(&vmwdt_dev);
323	if (ret) {
324		unregister_pm_notifier(&vmwdt_power_notifier);
325		return ret;
326	}
327	return 0;
328}
329module_init(vmwdt_init);
330
331static void __exit vmwdt_exit(void)
332{
333	unregister_pm_notifier(&vmwdt_power_notifier);
334	misc_deregister(&vmwdt_dev);
335}
336module_exit(vmwdt_exit);
337