11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Watchdog implementation based on z/VM Watchdog Timer API 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * Copyright IBM Corp. 2004,2009 558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The user space watchdog daemon can use this driver as 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * /dev/vmwatchdog to have z/VM execute the specified CP 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * command when the timeout expires. The default command is 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "IPL", which which cause an immediate reboot. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger#define KMSG_COMPONENT "vmwatchdog" 1258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h> 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h> 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h> 205a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 2158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger#include <linux/suspend.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/ebcdic.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_CMDLEN 240 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MIN_INTERVAL 15 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char vmwdt_cmd[MAX_CMDLEN] = "IPL"; 3190ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool vmwdt_conceal; 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3390ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool vmwdt_nowayout = WATCHDOG_NOWAYOUT; 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("z/VM Watchdog Timer"); 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644); 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers"); 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_named(conceal, vmwdt_conceal, bool, 0644); 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog " 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds " is active"); 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_named(nowayout, vmwdt_nowayout, bool, 0); 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds " (default=CONFIG_WATCHDOG_NOWAYOUT)"); 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int vmwdt_interval = 60; 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long vmwdt_is_open; 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmwdt_expect_close; 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 52feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaeferstatic DEFINE_MUTEX(vmwdt_mutex); 53feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer 5458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ 5558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger#define VMWDT_RUNNING 1 /* The watchdog is armed */ 5658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsenum vmwdt_func { 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* function codes */ 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wdt_init = 0, 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wdt_change = 1, 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wdt_cancel = 2, 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* flags */ 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wdt_conceal = 0x80000000, 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __diag288(enum vmwdt_func func, unsigned int timeout, 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *cmd, size_t len) 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6994c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky register unsigned long __func asm("2") = func; 7094c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky register unsigned long __timeout asm("3") = timeout; 7194c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky register unsigned long __cmdp asm("4") = virt_to_phys(cmd); 7294c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky register unsigned long __cmdl asm("5") = len; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7594c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky err = -EINVAL; 7694c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky asm volatile( 7794c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky " diag %1,%3,0x288\n" 7894c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky "0: la %0,0\n" 7994c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky "1:\n" 8094c12cc7d196bab34aaa98d38521549fa1e5ef76Martin Schwidefsky EX_TABLE(0b,1b) 812b12f996c992334f65f6d9e0a3e731ec049fdad3Heiko Carstens : "+d" (err) : "d"(__func), "d"(__timeout), 822b12f996c992334f65f6d9e0a3e731ec049fdad3Heiko Carstens "d"(__cmdp), "d"(__cmdl) : "1", "cc"); 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmwdt_keepalive(void) 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* we allocate new memory every time to avoid having 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to track the state. static allocation is not an 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * option since that might not be contiguous in real 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * storage in case of a modular build */ 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static char *ebc_cmd; 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t len; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int func; 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ebc_cmd) 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN); 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ASCEBC(ebc_cmd, MAX_CMDLEN); 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; 10658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger set_bit(VMWDT_RUNNING, &vmwdt_is_open); 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = __diag288(func, vmwdt_interval, ebc_cmd, len); 1080d130066801af8f0a0ea8c70c9c7374c51fd1a92Martin Schwidefsky WARN_ON(ret != 0); 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(ebc_cmd); 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmwdt_disable(void) 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = __diag288(wdt_cancel, 0, "", 0); 1160d130066801af8f0a0ea8c70c9c7374c51fd1a92Martin Schwidefsky WARN_ON(ret != 0); 11758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger clear_bit(VMWDT_RUNNING, &vmwdt_is_open); 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init vmwdt_probe(void) 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* there is no real way to see if the watchdog is supported, 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * so we try initializing it with a NOP command ("BEGIN") 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that won't cause any harm even if the following disable 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fails for some reason */ 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static char __initdata ebc_begin[] = { 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 194, 197, 199, 201, 213 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds }; 1300d130066801af8f0a0ea8c70c9c7374c51fd1a92Martin Schwidefsky if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return vmwdt_disable(); 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmwdt_open(struct inode *i, struct file *f) 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 138feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EBUSY; 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = vmwdt_keepalive(); 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 14258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger clear_bit(VMWDT_OPEN, &vmwdt_is_open); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret ? ret : nonseekable_open(i, f); 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmwdt_close(struct inode *i, struct file *f) 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (vmwdt_expect_close == 42) 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_disable(); 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_expect_close = 0; 15158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger clear_bit(VMWDT_OPEN, &vmwdt_is_open); 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct watchdog_info vmwdt_info = { 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .firmware_version = 0, 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .identity = "z/VM Watchdog Timer", 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 161feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaeferstatic int __vmwdt_ioctl(unsigned int cmd, unsigned long arg) 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (cmd) { 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_GETSUPPORT: 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_to_user((void __user *)arg, &vmwdt_info, 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sizeof(vmwdt_info))) 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_GETSTATUS: 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_GETBOOTSTATUS: 171d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens return put_user(0, (int __user *)arg); 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_GETTEMP: 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_SETOPTIONS: 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int options, ret; 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (get_user(options, (int __user *)arg)) 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EINVAL; 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (options & WDIOS_DISABLECARD) { 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = vmwdt_disable(); 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (options & WDIOS_ENABLECARD) { 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = vmwdt_keepalive(); 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_GETTIMEOUT: 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return put_user(vmwdt_interval, (int __user *)arg); 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_SETTIMEOUT: 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int interval; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (get_user(interval, (int __user *)arg)) 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (interval < MIN_INTERVAL) 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_interval = interval; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return vmwdt_keepalive(); 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case WDIOC_KEEPALIVE: 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return vmwdt_keepalive(); 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 208feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaeferstatic long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 209feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer{ 210feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer int rc; 211feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer 212feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer mutex_lock(&vmwdt_mutex); 213feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer rc = __vmwdt_ioctl(cmd, arg); 214feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer mutex_unlock(&vmwdt_mutex); 215feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer return (long) rc; 216feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer} 217feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t vmwdt_write(struct file *f, const char __user *buf, 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t count, loff_t *ppos) 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(count) { 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!vmwdt_nowayout) { 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t i; 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* note: just in case someone wrote the magic character 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * five months ago... */ 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_expect_close = 0; 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i != count; i++) { 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c; 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (get_user(c, buf+i)) 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (c == 'V') 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_expect_close = 42; 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* someone wrote to us, we should restart timer */ 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vmwdt_keepalive(); 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return count; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 24358872d5f367876a81dcda82465381d80d321a81dChristian Borntraegerstatic int vmwdt_resume(void) 24458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger{ 24558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger clear_bit(VMWDT_OPEN, &vmwdt_is_open); 24658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return NOTIFY_DONE; 24758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger} 24858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 24958872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger/* 25058872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * It makes no sense to go into suspend while the watchdog is running. 25158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * Depending on the memory size, the watchdog might trigger, while we 25258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * are still saving the memory. 25358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * We reuse the open flag to ensure that suspend and watchdog open are 25458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * exclusive operations 25558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger */ 25658872d5f367876a81dcda82465381d80d321a81dChristian Borntraegerstatic int vmwdt_suspend(void) 25758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger{ 25858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { 2592c48c4d631970b70d60a4f926b0f68f194a0d559Christian Borntraeger pr_err("The system cannot be suspended while the watchdog" 2602c48c4d631970b70d60a4f926b0f68f194a0d559Christian Borntraeger " is in use\n"); 261f0c077a8b7f9dce590c760a7b2f3c417dffa52d1Akinobu Mita return notifier_from_errno(-EBUSY); 26258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger } 26358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { 26458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger clear_bit(VMWDT_OPEN, &vmwdt_is_open); 2652c48c4d631970b70d60a4f926b0f68f194a0d559Christian Borntraeger pr_err("The system cannot be suspended while the watchdog" 2662c48c4d631970b70d60a4f926b0f68f194a0d559Christian Borntraeger " is running\n"); 267f0c077a8b7f9dce590c760a7b2f3c417dffa52d1Akinobu Mita return notifier_from_errno(-EBUSY); 26858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger } 26958872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return NOTIFY_DONE; 27058872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger} 27158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 27258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger/* 27358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger * This function is called for suspend and resume. 27458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger */ 27558872d5f367876a81dcda82465381d80d321a81dChristian Borntraegerstatic int vmwdt_power_event(struct notifier_block *this, unsigned long event, 27658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger void *ptr) 27758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger{ 27858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger switch (event) { 27958872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger case PM_POST_HIBERNATION: 28058872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger case PM_POST_SUSPEND: 28158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return vmwdt_resume(); 28258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger case PM_HIBERNATION_PREPARE: 28358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger case PM_SUSPEND_PREPARE: 28458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return vmwdt_suspend(); 28558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger default: 28658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return NOTIFY_DONE; 28758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger } 28858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger} 28958872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 29058872d5f367876a81dcda82465381d80d321a81dChristian Borntraegerstatic struct notifier_block vmwdt_power_notifier = { 29158872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger .notifier_call = vmwdt_power_event, 29258872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger}; 29358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger 294d54b1fdb1d9f82e375a299e22bd366aad52d4c34Arjan van de Venstatic const struct file_operations vmwdt_fops = { 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = &vmwdt_open, 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .release = &vmwdt_close, 297feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer .unlocked_ioctl = &vmwdt_ioctl, 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .write = &vmwdt_write, 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .owner = THIS_MODULE, 3006038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann .llseek = noop_llseek, 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice vmwdt_dev = { 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .minor = WATCHDOG_MINOR, 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "watchdog", 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .fops = &vmwdt_fops, 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init vmwdt_init(void) 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = vmwdt_probe(); 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 31658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger ret = register_pm_notifier(&vmwdt_power_notifier); 31758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger if (ret) 31858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return ret; 319feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer /* 320feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer * misc_register() has to be the last action in module_init(), because 321feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer * file operations will be available right after this. 322feb5c5a450a5e56f3be09a18b541996c86bd94bcGerald Schaefer */ 32358872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger ret = misc_register(&vmwdt_dev); 32458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger if (ret) { 32558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger unregister_pm_notifier(&vmwdt_power_notifier); 32658872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return ret; 32758872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger } 32858872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger return 0; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(vmwdt_init); 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit vmwdt_exit(void) 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 33458872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger unregister_pm_notifier(&vmwdt_power_notifier); 33558872d5f367876a81dcda82465381d80d321a81dChristian Borntraeger misc_deregister(&vmwdt_dev); 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(vmwdt_exit); 338