diff options
-rw-r--r-- | drivers/s390/char/vmwatchdog.c | 81 |
1 files changed, 76 insertions, 5 deletions
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 21a2a829bf4e..cb7854c10c04 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c | |||
@@ -1,17 +1,23 @@ | |||
1 | /* | 1 | /* |
2 | * Watchdog implementation based on z/VM Watchdog Timer API | 2 | * Watchdog implementation based on z/VM Watchdog Timer API |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2004,2009 | ||
5 | * | ||
4 | * The user space watchdog daemon can use this driver as | 6 | * The user space watchdog daemon can use this driver as |
5 | * /dev/vmwatchdog to have z/VM execute the specified CP | 7 | * /dev/vmwatchdog to have z/VM execute the specified CP |
6 | * command when the timeout expires. The default command is | 8 | * command when the timeout expires. The default command is |
7 | * "IPL", which which cause an immediate reboot. | 9 | * "IPL", which which cause an immediate reboot. |
8 | */ | 10 | */ |
11 | #define KMSG_COMPONENT "vmwatchdog" | ||
12 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
13 | |||
9 | #include <linux/init.h> | 14 | #include <linux/init.h> |
10 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
11 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
12 | #include <linux/miscdevice.h> | 17 | #include <linux/miscdevice.h> |
13 | #include <linux/module.h> | 18 | #include <linux/module.h> |
14 | #include <linux/moduleparam.h> | 19 | #include <linux/moduleparam.h> |
20 | #include <linux/suspend.h> | ||
15 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
16 | #include <linux/smp_lock.h> | 22 | #include <linux/smp_lock.h> |
17 | 23 | ||
@@ -43,6 +49,9 @@ static unsigned int vmwdt_interval = 60; | |||
43 | static unsigned long vmwdt_is_open; | 49 | static unsigned long vmwdt_is_open; |
44 | static int vmwdt_expect_close; | 50 | static int vmwdt_expect_close; |
45 | 51 | ||
52 | #define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ | ||
53 | #define VMWDT_RUNNING 1 /* The watchdog is armed */ | ||
54 | |||
46 | enum vmwdt_func { | 55 | enum vmwdt_func { |
47 | /* function codes */ | 56 | /* function codes */ |
48 | wdt_init = 0, | 57 | wdt_init = 0, |
@@ -92,6 +101,7 @@ static int vmwdt_keepalive(void) | |||
92 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); | 101 | EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); |
93 | 102 | ||
94 | func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; | 103 | func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; |
104 | set_bit(VMWDT_RUNNING, &vmwdt_is_open); | ||
95 | ret = __diag288(func, vmwdt_interval, ebc_cmd, len); | 105 | ret = __diag288(func, vmwdt_interval, ebc_cmd, len); |
96 | WARN_ON(ret != 0); | 106 | WARN_ON(ret != 0); |
97 | kfree(ebc_cmd); | 107 | kfree(ebc_cmd); |
@@ -102,6 +112,7 @@ static int vmwdt_disable(void) | |||
102 | { | 112 | { |
103 | int ret = __diag288(wdt_cancel, 0, "", 0); | 113 | int ret = __diag288(wdt_cancel, 0, "", 0); |
104 | WARN_ON(ret != 0); | 114 | WARN_ON(ret != 0); |
115 | clear_bit(VMWDT_RUNNING, &vmwdt_is_open); | ||
105 | return ret; | 116 | return ret; |
106 | } | 117 | } |
107 | 118 | ||
@@ -123,13 +134,13 @@ static int vmwdt_open(struct inode *i, struct file *f) | |||
123 | { | 134 | { |
124 | int ret; | 135 | int ret; |
125 | lock_kernel(); | 136 | lock_kernel(); |
126 | if (test_and_set_bit(0, &vmwdt_is_open)) { | 137 | if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { |
127 | unlock_kernel(); | 138 | unlock_kernel(); |
128 | return -EBUSY; | 139 | return -EBUSY; |
129 | } | 140 | } |
130 | ret = vmwdt_keepalive(); | 141 | ret = vmwdt_keepalive(); |
131 | if (ret) | 142 | if (ret) |
132 | clear_bit(0, &vmwdt_is_open); | 143 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); |
133 | unlock_kernel(); | 144 | unlock_kernel(); |
134 | return ret ? ret : nonseekable_open(i, f); | 145 | return ret ? ret : nonseekable_open(i, f); |
135 | } | 146 | } |
@@ -139,7 +150,7 @@ static int vmwdt_close(struct inode *i, struct file *f) | |||
139 | if (vmwdt_expect_close == 42) | 150 | if (vmwdt_expect_close == 42) |
140 | vmwdt_disable(); | 151 | vmwdt_disable(); |
141 | vmwdt_expect_close = 0; | 152 | vmwdt_expect_close = 0; |
142 | clear_bit(0, &vmwdt_is_open); | 153 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); |
143 | return 0; | 154 | return 0; |
144 | } | 155 | } |
145 | 156 | ||
@@ -223,6 +234,57 @@ static ssize_t vmwdt_write(struct file *f, const char __user *buf, | |||
223 | return count; | 234 | return count; |
224 | } | 235 | } |
225 | 236 | ||
237 | static int vmwdt_resume(void) | ||
238 | { | ||
239 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); | ||
240 | return NOTIFY_DONE; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * It makes no sense to go into suspend while the watchdog is running. | ||
245 | * Depending on the memory size, the watchdog might trigger, while we | ||
246 | * are still saving the memory. | ||
247 | * We reuse the open flag to ensure that suspend and watchdog open are | ||
248 | * exclusive operations | ||
249 | */ | ||
250 | static int vmwdt_suspend(void) | ||
251 | { | ||
252 | if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { | ||
253 | pr_err("The watchdog is in use. " | ||
254 | "This prevents hibernation or suspend.\n"); | ||
255 | return NOTIFY_BAD; | ||
256 | } | ||
257 | if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { | ||
258 | clear_bit(VMWDT_OPEN, &vmwdt_is_open); | ||
259 | pr_err("The watchdog is running. " | ||
260 | "This prevents hibernation or suspend.\n"); | ||
261 | return NOTIFY_BAD; | ||
262 | } | ||
263 | return NOTIFY_DONE; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * This function is called for suspend and resume. | ||
268 | */ | ||
269 | static int vmwdt_power_event(struct notifier_block *this, unsigned long event, | ||
270 | void *ptr) | ||
271 | { | ||
272 | switch (event) { | ||
273 | case PM_POST_HIBERNATION: | ||
274 | case PM_POST_SUSPEND: | ||
275 | return vmwdt_resume(); | ||
276 | case PM_HIBERNATION_PREPARE: | ||
277 | case PM_SUSPEND_PREPARE: | ||
278 | return vmwdt_suspend(); | ||
279 | default: | ||
280 | return NOTIFY_DONE; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static struct notifier_block vmwdt_power_notifier = { | ||
285 | .notifier_call = vmwdt_power_event, | ||
286 | }; | ||
287 | |||
226 | static const struct file_operations vmwdt_fops = { | 288 | static const struct file_operations vmwdt_fops = { |
227 | .open = &vmwdt_open, | 289 | .open = &vmwdt_open, |
228 | .release = &vmwdt_close, | 290 | .release = &vmwdt_close, |
@@ -244,12 +306,21 @@ static int __init vmwdt_init(void) | |||
244 | ret = vmwdt_probe(); | 306 | ret = vmwdt_probe(); |
245 | if (ret) | 307 | if (ret) |
246 | return ret; | 308 | return ret; |
247 | return misc_register(&vmwdt_dev); | 309 | ret = register_pm_notifier(&vmwdt_power_notifier); |
310 | if (ret) | ||
311 | return ret; | ||
312 | ret = misc_register(&vmwdt_dev); | ||
313 | if (ret) { | ||
314 | unregister_pm_notifier(&vmwdt_power_notifier); | ||
315 | return ret; | ||
316 | } | ||
317 | return 0; | ||
248 | } | 318 | } |
249 | module_init(vmwdt_init); | 319 | module_init(vmwdt_init); |
250 | 320 | ||
251 | static void __exit vmwdt_exit(void) | 321 | static void __exit vmwdt_exit(void) |
252 | { | 322 | { |
253 | WARN_ON(misc_deregister(&vmwdt_dev) != 0); | 323 | unregister_pm_notifier(&vmwdt_power_notifier); |
324 | misc_deregister(&vmwdt_dev); | ||
254 | } | 325 | } |
255 | module_exit(vmwdt_exit); | 326 | module_exit(vmwdt_exit); |