upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / s390 / char / vmwatchdog.c
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
30 static char vmwdt_cmd[MAX_CMDLEN] = "IPL";
31 static int vmwdt_conceal;
32
33 static int vmwdt_nowayout = WATCHDOG_NOWAYOUT;
34
35 MODULE_LICENSE("GPL");
36 MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
37 MODULE_DESCRIPTION("z/VM Watchdog Timer");
38 module_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644);
39 MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers");
40 module_param_named(conceal, vmwdt_conceal, bool, 0644);
41 MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog "
42                 " is active");
43 module_param_named(nowayout, vmwdt_nowayout, bool, 0);
44 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
45                 " (default=CONFIG_WATCHDOG_NOWAYOUT)");
46 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
47
48 static unsigned int vmwdt_interval = 60;
49 static unsigned long vmwdt_is_open;
50 static int vmwdt_expect_close;
51
52 static 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
57 enum 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
66 static 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
86 static 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
113 static 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
121 static 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
135 static 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
146 static 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
155 static 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
161 static 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
208 static 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
218 static 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
243 static 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  */
256 static 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  */
275 static 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
290 static struct notifier_block vmwdt_power_notifier = {
291         .notifier_call = vmwdt_power_event,
292 };
293
294 static 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
302 static struct miscdevice vmwdt_dev = {
303         .minor      = WATCHDOG_MINOR,
304         .name       = "watchdog",
305         .fops       = &vmwdt_fops,
306 };
307
308 static 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 }
329 module_init(vmwdt_init);
330
331 static void __exit vmwdt_exit(void)
332 {
333         unregister_pm_notifier(&vmwdt_power_notifier);
334         misc_deregister(&vmwdt_dev);
335 }
336 module_exit(vmwdt_exit);