Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.25 260 lines 6.3 kB view raw
1/* 2 * Watchdog implementation based on z/VM Watchdog Timer API 3 * 4 * The user space watchdog daemon can use this driver as 5 * /dev/vmwatchdog to have z/VM execute the specified CP 6 * command when the timeout expires. The default command is 7 * "IPL", which which cause an immediate reboot. 8 */ 9#include <linux/init.h> 10#include <linux/fs.h> 11#include <linux/kernel.h> 12#include <linux/miscdevice.h> 13#include <linux/module.h> 14#include <linux/moduleparam.h> 15#include <linux/watchdog.h> 16 17#include <asm/ebcdic.h> 18#include <asm/io.h> 19#include <asm/uaccess.h> 20 21#define MAX_CMDLEN 240 22#define MIN_INTERVAL 15 23static char vmwdt_cmd[MAX_CMDLEN] = "IPL"; 24static int vmwdt_conceal; 25 26static int vmwdt_nowayout = WATCHDOG_NOWAYOUT; 27 28MODULE_LICENSE("GPL"); 29MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); 30MODULE_DESCRIPTION("z/VM Watchdog Timer"); 31module_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644); 32MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers"); 33module_param_named(conceal, vmwdt_conceal, bool, 0644); 34MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog " 35 " is active"); 36module_param_named(nowayout, vmwdt_nowayout, bool, 0); 37MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" 38 " (default=CONFIG_WATCHDOG_NOWAYOUT)"); 39MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 40 41static unsigned int vmwdt_interval = 60; 42static unsigned long vmwdt_is_open; 43static int vmwdt_expect_close; 44 45enum vmwdt_func { 46 /* function codes */ 47 wdt_init = 0, 48 wdt_change = 1, 49 wdt_cancel = 2, 50 /* flags */ 51 wdt_conceal = 0x80000000, 52}; 53 54static int __diag288(enum vmwdt_func func, unsigned int timeout, 55 char *cmd, size_t len) 56{ 57 register unsigned long __func asm("2") = func; 58 register unsigned long __timeout asm("3") = timeout; 59 register unsigned long __cmdp asm("4") = virt_to_phys(cmd); 60 register unsigned long __cmdl asm("5") = len; 61 int err; 62 63 err = -EINVAL; 64 asm volatile( 65 " diag %1,%3,0x288\n" 66 "0: la %0,0\n" 67 "1:\n" 68 EX_TABLE(0b,1b) 69 : "+d" (err) : "d"(__func), "d"(__timeout), 70 "d"(__cmdp), "d"(__cmdl) : "1", "cc"); 71 return err; 72} 73 74static int vmwdt_keepalive(void) 75{ 76 /* we allocate new memory every time to avoid having 77 * to track the state. static allocation is not an 78 * option since that might not be contiguous in real 79 * storage in case of a modular build */ 80 static char *ebc_cmd; 81 size_t len; 82 int ret; 83 unsigned int func; 84 85 ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); 86 if (!ebc_cmd) 87 return -ENOMEM; 88 89 len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN); 90 ASCEBC(ebc_cmd, MAX_CMDLEN); 91 EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); 92 93 func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; 94 ret = __diag288(func, vmwdt_interval, ebc_cmd, len); 95 kfree(ebc_cmd); 96 97 if (ret) { 98 printk(KERN_WARNING "%s: problem setting interval %d, " 99 "cmd %s\n", __FUNCTION__, vmwdt_interval, 100 vmwdt_cmd); 101 } 102 return ret; 103} 104 105static int vmwdt_disable(void) 106{ 107 int ret = __diag288(wdt_cancel, 0, "", 0); 108 if (ret) { 109 printk(KERN_WARNING "%s: problem disabling watchdog\n", 110 __FUNCTION__); 111 } 112 return ret; 113} 114 115static int __init vmwdt_probe(void) 116{ 117 /* there is no real way to see if the watchdog is supported, 118 * so we try initializing it with a NOP command ("BEGIN") 119 * that won't cause any harm even if the following disable 120 * fails for some reason */ 121 static char __initdata ebc_begin[] = { 122 194, 197, 199, 201, 213 123 }; 124 if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) { 125 printk(KERN_INFO "z/VM watchdog not available\n"); 126 return -EINVAL; 127 } 128 return vmwdt_disable(); 129} 130 131static int vmwdt_open(struct inode *i, struct file *f) 132{ 133 int ret; 134 if (test_and_set_bit(0, &vmwdt_is_open)) 135 return -EBUSY; 136 ret = vmwdt_keepalive(); 137 if (ret) 138 clear_bit(0, &vmwdt_is_open); 139 return ret ? ret : nonseekable_open(i, f); 140} 141 142static int vmwdt_close(struct inode *i, struct file *f) 143{ 144 if (vmwdt_expect_close == 42) 145 vmwdt_disable(); 146 vmwdt_expect_close = 0; 147 clear_bit(0, &vmwdt_is_open); 148 return 0; 149} 150 151static struct watchdog_info vmwdt_info = { 152 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 153 .firmware_version = 0, 154 .identity = "z/VM Watchdog Timer", 155}; 156 157static int vmwdt_ioctl(struct inode *i, struct file *f, 158 unsigned int cmd, unsigned long arg) 159{ 160 switch (cmd) { 161 case WDIOC_GETSUPPORT: 162 if (copy_to_user((void __user *)arg, &vmwdt_info, 163 sizeof(vmwdt_info))) 164 return -EFAULT; 165 return 0; 166 case WDIOC_GETSTATUS: 167 case WDIOC_GETBOOTSTATUS: 168 return put_user(0, (int __user *)arg); 169 case WDIOC_GETTEMP: 170 return -EINVAL; 171 case WDIOC_SETOPTIONS: 172 { 173 int options, ret; 174 if (get_user(options, (int __user *)arg)) 175 return -EFAULT; 176 ret = -EINVAL; 177 if (options & WDIOS_DISABLECARD) { 178 ret = vmwdt_disable(); 179 if (ret) 180 return ret; 181 } 182 if (options & WDIOS_ENABLECARD) { 183 ret = vmwdt_keepalive(); 184 } 185 return ret; 186 } 187 case WDIOC_GETTIMEOUT: 188 return put_user(vmwdt_interval, (int __user *)arg); 189 case WDIOC_SETTIMEOUT: 190 { 191 int interval; 192 if (get_user(interval, (int __user *)arg)) 193 return -EFAULT; 194 if (interval < MIN_INTERVAL) 195 return -EINVAL; 196 vmwdt_interval = interval; 197 } 198 return vmwdt_keepalive(); 199 case WDIOC_KEEPALIVE: 200 return vmwdt_keepalive(); 201 } 202 203 return -EINVAL; 204} 205 206static ssize_t vmwdt_write(struct file *f, const char __user *buf, 207 size_t count, loff_t *ppos) 208{ 209 if(count) { 210 if (!vmwdt_nowayout) { 211 size_t i; 212 213 /* note: just in case someone wrote the magic character 214 * five months ago... */ 215 vmwdt_expect_close = 0; 216 217 for (i = 0; i != count; i++) { 218 char c; 219 if (get_user(c, buf+i)) 220 return -EFAULT; 221 if (c == 'V') 222 vmwdt_expect_close = 42; 223 } 224 } 225 /* someone wrote to us, we should restart timer */ 226 vmwdt_keepalive(); 227 } 228 return count; 229} 230 231static const struct file_operations vmwdt_fops = { 232 .open = &vmwdt_open, 233 .release = &vmwdt_close, 234 .ioctl = &vmwdt_ioctl, 235 .write = &vmwdt_write, 236 .owner = THIS_MODULE, 237}; 238 239static struct miscdevice vmwdt_dev = { 240 .minor = WATCHDOG_MINOR, 241 .name = "watchdog", 242 .fops = &vmwdt_fops, 243}; 244 245static int __init vmwdt_init(void) 246{ 247 int ret; 248 249 ret = vmwdt_probe(); 250 if (ret) 251 return ret; 252 return misc_register(&vmwdt_dev); 253} 254module_init(vmwdt_init); 255 256static void __exit vmwdt_exit(void) 257{ 258 WARN_ON(misc_deregister(&vmwdt_dev) != 0); 259} 260module_exit(vmwdt_exit);