aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2009-06-16 04:30:35 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-06-16 04:31:14 -0400
commit58872d5f367876a81dcda82465381d80d321a81d (patch)
tree0a40c6dd2bf48ad3dc60ed04e5e45061a2313c2a /drivers/s390
parentdaa70fa960fb5625d762988cca7e4733573d90a3 (diff)
[S390] pm: vmwatchdog power management callbacks.
This patch implements suspend/hibernation for the vmwatchdog driver. The pm_notifier_callchain is used to get control on PM events. Since watchdog operation and suspend cannot work together in a reliable fashion, the open flag is also used to prevent suspend and open from happening at the same time. The watchdog can also be active with no open file descriptor. This patch adds another flag which is only changed in vmwdt_keep_alive and vmwdt_disable. Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/char/vmwatchdog.c81
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;
43static unsigned long vmwdt_is_open; 49static unsigned long vmwdt_is_open;
44static int vmwdt_expect_close; 50static 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
46enum vmwdt_func { 55enum 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
237static 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 */
250static 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 */
269static 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
284static struct notifier_block vmwdt_power_notifier = {
285 .notifier_call = vmwdt_power_event,
286};
287
226static const struct file_operations vmwdt_fops = { 288static 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}
249module_init(vmwdt_init); 319module_init(vmwdt_init);
250 320
251static void __exit vmwdt_exit(void) 321static 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}
255module_exit(vmwdt_exit); 326module_exit(vmwdt_exit);