FILE
diag288_wdt.c
File preview with syntax highlighting.
linux-6.19.14/drivers/watchdog/diag288_wdt.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Watchdog driver for z/VM and LPAR using the diag 288 interface. 4 * 5 * Under z/VM, expiration of the watchdog will send a "system restart" command 6 * to CP. 7 * 8 * The command can be altered using the module parameter "cmd". This is 9 * not recommended because it's only supported on z/VM but not with LPAR. 10 * 11 * On LPAR, the watchdog will always trigger a system restart. The module 12 * parameter cmd is meaningless here. 13 * 14 * 15 * Copyright IBM Corp. 2004, 2013 16 * Author(s): Arnd Bergmann (arndb@de.ibm.com) 17 * Philipp Hachtmann (phacht@de.ibm.com) 18 * 19 */ 20 21#define pr_fmt(fmt) "diag288_wdt: " fmt 22 23#include <linux/init.h> 24#include <linux/kernel.h> 25#include <linux/module.h> 26#include <linux/moduleparam.h> 27#include <linux/slab.h> 28#include <linux/watchdog.h> 29#include <asm/machine.h> 30#include <asm/ebcdic.h> 31#include <asm/diag288.h> 32#include <asm/diag.h> 33#include <linux/io.h> 34 35#define MAX_CMDLEN 240 36#define DEFAULT_CMD "SYSTEM RESTART" 37 38static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; 39static bool conceal_on; 40static bool nowayout_info = WATCHDOG_NOWAYOUT; 41 42MODULE_LICENSE("GPL"); 43MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); 44MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>"); 45 46MODULE_DESCRIPTION("System z diag288 Watchdog Timer"); 47 48module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644); 49MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)"); 50 51module_param_named(conceal, conceal_on, bool, 0644); 52MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)"); 53 54module_param_named(nowayout, nowayout_info, bool, 0444); 55MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)"); 56 57MODULE_ALIAS("vmwatchdog"); 58 59static char *cmd_buf; 60 61static int diag288(unsigned int func, unsigned int timeout, 62 unsigned long action, unsigned int len) 63{ 64 diag_stat_inc(DIAG_STAT_X288); 65 return __diag288(func, timeout, action, len); 66} 67 68static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) 69{ 70 ssize_t len; 71 72 len = strscpy(cmd_buf, cmd, MAX_CMDLEN); 73 if (len < 0) 74 return len; 75 ASCEBC(cmd_buf, MAX_CMDLEN); 76 EBC_TOUPPER(cmd_buf, MAX_CMDLEN); 77 78 return diag288(func, timeout, virt_to_phys(cmd_buf), len); 79} 80 81static int wdt_start(struct watchdog_device *dev) 82{ 83 int ret; 84 unsigned int func; 85 86 if (machine_is_vm()) { 87 func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) 88 : WDT_FUNC_INIT; 89 ret = diag288_str(func, dev->timeout, wdt_cmd); 90 WARN_ON(ret != 0); 91 } else { 92 ret = diag288(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART, 0); 93 } 94 95 if (ret) { 96 pr_err("The watchdog cannot be activated\n"); 97 return ret; 98 } 99 return 0; 100} 101 102static int wdt_stop(struct watchdog_device *dev) 103{ 104 return diag288(WDT_FUNC_CANCEL, 0, 0, 0); 105} 106 107static int wdt_ping(struct watchdog_device *dev) 108{ 109 int ret; 110 unsigned int func; 111 112 if (machine_is_vm()) { 113 /* 114 * It seems to be ok to z/VM to use the init function to 115 * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must 116 * be used when the watchdog is running. 117 */ 118 func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) 119 : WDT_FUNC_INIT; 120 121 ret = diag288_str(func, dev->timeout, wdt_cmd); 122 WARN_ON(ret != 0); 123 } else { 124 ret = diag288(WDT_FUNC_CHANGE, dev->timeout, 0, 0); 125 } 126 127 if (ret) 128 pr_err("The watchdog timer cannot be started or reset\n"); 129 return ret; 130} 131 132static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to) 133{ 134 dev->timeout = new_to; 135 return wdt_ping(dev); 136} 137 138static const struct watchdog_ops wdt_ops = { 139 .owner = THIS_MODULE, 140 .start = wdt_start, 141 .stop = wdt_stop, 142 .ping = wdt_ping, 143 .set_timeout = wdt_set_timeout, 144}; 145 146static const struct watchdog_info wdt_info = { 147 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 148 .firmware_version = 0, 149 .identity = "z Watchdog", 150}; 151 152static struct watchdog_device wdt_dev = { 153 .parent = NULL, 154 .info = &wdt_info, 155 .ops = &wdt_ops, 156 .bootstatus = 0, 157 .timeout = WDT_DEFAULT_TIMEOUT, 158 .min_timeout = MIN_INTERVAL, 159 .max_timeout = MAX_INTERVAL, 160}; 161 162static int __init diag288_init(void) 163{ 164 watchdog_set_nowayout(&wdt_dev, nowayout_info); 165 166 if (machine_is_vm()) { 167 cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL); 168 if (!cmd_buf) { 169 pr_err("The watchdog cannot be initialized\n"); 170 return -ENOMEM; 171 } 172 } 173 174 return watchdog_register_device(&wdt_dev); 175} 176 177static void __exit diag288_exit(void) 178{ 179 watchdog_unregister_device(&wdt_dev); 180 kfree(cmd_buf); 181} 182 183module_cpu_feature_match(S390_CPU_FEATURE_D288, diag288_init); 184module_exit(diag288_exit);
~/
Read-only file preview.