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.16-rc3 288 lines 6.8 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"); 58 register unsigned long __timeout asm("3"); 59 register unsigned long __cmdp asm("4"); 60 register unsigned long __cmdl asm("5"); 61 int err; 62 63 __func = func; 64 __timeout = timeout; 65 __cmdp = virt_to_phys(cmd); 66 __cmdl = len; 67 err = 0; 68 asm volatile ( 69#ifdef CONFIG_64BIT 70 "diag %2,%4,0x288\n" 71 "1: \n" 72 ".section .fixup,\"ax\"\n" 73 "2: lghi %0,%1\n" 74 " jg 1b\n" 75 ".previous\n" 76 ".section __ex_table,\"a\"\n" 77 " .align 8\n" 78 " .quad 1b,2b\n" 79 ".previous\n" 80#else 81 "diag %2,%4,0x288\n" 82 "1: \n" 83 ".section .fixup,\"ax\"\n" 84 "2: lhi %0,%1\n" 85 " bras 1,3f\n" 86 " .long 1b\n" 87 "3: l 1,0(1)\n" 88 " br 1\n" 89 ".previous\n" 90 ".section __ex_table,\"a\"\n" 91 " .align 4\n" 92 " .long 1b,2b\n" 93 ".previous\n" 94#endif 95 : "+&d"(err) 96 : "i"(-EINVAL), "d"(__func), "d"(__timeout), 97 "d"(__cmdp), "d"(__cmdl) 98 : "1", "cc"); 99 return err; 100} 101 102static int vmwdt_keepalive(void) 103{ 104 /* we allocate new memory every time to avoid having 105 * to track the state. static allocation is not an 106 * option since that might not be contiguous in real 107 * storage in case of a modular build */ 108 static char *ebc_cmd; 109 size_t len; 110 int ret; 111 unsigned int func; 112 113 ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); 114 if (!ebc_cmd) 115 return -ENOMEM; 116 117 len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN); 118 ASCEBC(ebc_cmd, MAX_CMDLEN); 119 EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); 120 121 func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; 122 ret = __diag288(func, vmwdt_interval, ebc_cmd, len); 123 kfree(ebc_cmd); 124 125 if (ret) { 126 printk(KERN_WARNING "%s: problem setting interval %d, " 127 "cmd %s\n", __FUNCTION__, vmwdt_interval, 128 vmwdt_cmd); 129 } 130 return ret; 131} 132 133static int vmwdt_disable(void) 134{ 135 int ret = __diag288(wdt_cancel, 0, "", 0); 136 if (ret) { 137 printk(KERN_WARNING "%s: problem disabling watchdog\n", 138 __FUNCTION__); 139 } 140 return ret; 141} 142 143static int __init vmwdt_probe(void) 144{ 145 /* there is no real way to see if the watchdog is supported, 146 * so we try initializing it with a NOP command ("BEGIN") 147 * that won't cause any harm even if the following disable 148 * fails for some reason */ 149 static char __initdata ebc_begin[] = { 150 194, 197, 199, 201, 213 151 }; 152 if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) { 153 printk(KERN_INFO "z/VM watchdog not available\n"); 154 return -EINVAL; 155 } 156 return vmwdt_disable(); 157} 158 159static int vmwdt_open(struct inode *i, struct file *f) 160{ 161 int ret; 162 if (test_and_set_bit(0, &vmwdt_is_open)) 163 return -EBUSY; 164 ret = vmwdt_keepalive(); 165 if (ret) 166 clear_bit(0, &vmwdt_is_open); 167 return ret ? ret : nonseekable_open(i, f); 168} 169 170static int vmwdt_close(struct inode *i, struct file *f) 171{ 172 if (vmwdt_expect_close == 42) 173 vmwdt_disable(); 174 vmwdt_expect_close = 0; 175 clear_bit(0, &vmwdt_is_open); 176 return 0; 177} 178 179static struct watchdog_info vmwdt_info = { 180 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 181 .firmware_version = 0, 182 .identity = "z/VM Watchdog Timer", 183}; 184 185static int vmwdt_ioctl(struct inode *i, struct file *f, 186 unsigned int cmd, unsigned long arg) 187{ 188 switch (cmd) { 189 case WDIOC_GETSUPPORT: 190 if (copy_to_user((void __user *)arg, &vmwdt_info, 191 sizeof(vmwdt_info))) 192 return -EFAULT; 193 return 0; 194 case WDIOC_GETSTATUS: 195 case WDIOC_GETBOOTSTATUS: 196 return put_user(0, (int *)arg); 197 case WDIOC_GETTEMP: 198 return -EINVAL; 199 case WDIOC_SETOPTIONS: 200 { 201 int options, ret; 202 if (get_user(options, (int __user *)arg)) 203 return -EFAULT; 204 ret = -EINVAL; 205 if (options & WDIOS_DISABLECARD) { 206 ret = vmwdt_disable(); 207 if (ret) 208 return ret; 209 } 210 if (options & WDIOS_ENABLECARD) { 211 ret = vmwdt_keepalive(); 212 } 213 return ret; 214 } 215 case WDIOC_GETTIMEOUT: 216 return put_user(vmwdt_interval, (int __user *)arg); 217 case WDIOC_SETTIMEOUT: 218 { 219 int interval; 220 if (get_user(interval, (int __user *)arg)) 221 return -EFAULT; 222 if (interval < MIN_INTERVAL) 223 return -EINVAL; 224 vmwdt_interval = interval; 225 } 226 return vmwdt_keepalive(); 227 case WDIOC_KEEPALIVE: 228 return vmwdt_keepalive(); 229 } 230 231 return -EINVAL; 232} 233 234static ssize_t vmwdt_write(struct file *f, const char __user *buf, 235 size_t count, loff_t *ppos) 236{ 237 if(count) { 238 if (!vmwdt_nowayout) { 239 size_t i; 240 241 /* note: just in case someone wrote the magic character 242 * five months ago... */ 243 vmwdt_expect_close = 0; 244 245 for (i = 0; i != count; i++) { 246 char c; 247 if (get_user(c, buf+i)) 248 return -EFAULT; 249 if (c == 'V') 250 vmwdt_expect_close = 42; 251 } 252 } 253 /* someone wrote to us, we should restart timer */ 254 vmwdt_keepalive(); 255 } 256 return count; 257} 258 259static struct file_operations vmwdt_fops = { 260 .open = &vmwdt_open, 261 .release = &vmwdt_close, 262 .ioctl = &vmwdt_ioctl, 263 .write = &vmwdt_write, 264 .owner = THIS_MODULE, 265}; 266 267static struct miscdevice vmwdt_dev = { 268 .minor = WATCHDOG_MINOR, 269 .name = "watchdog", 270 .fops = &vmwdt_fops, 271}; 272 273static int __init vmwdt_init(void) 274{ 275 int ret; 276 277 ret = vmwdt_probe(); 278 if (ret) 279 return ret; 280 return misc_register(&vmwdt_dev); 281} 282module_init(vmwdt_init); 283 284static void __exit vmwdt_exit(void) 285{ 286 WARN_ON(misc_deregister(&vmwdt_dev) != 0); 287} 288module_exit(vmwdt_exit);