diff options
Diffstat (limited to 'drivers/watchdog/diag288_wdt.c')
| -rw-r--r-- | drivers/watchdog/diag288_wdt.c | 200 |
1 files changed, 34 insertions, 166 deletions
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 806a04a676b7..ea2b0171e70b 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for z/VM and LPAR using the diag 288 interface. * @@ -5,10 +6,10 @@ * to CP. * * The command can be altered using the module parameter "cmd". This is - * not recommended because it's only supported on z/VM but not whith LPAR. + * not recommended because it's only supported on z/VM but not with LPAR. * - * On LPAR, the watchdog will always trigger a system restart. the module - * paramter cmd is meaningless here. + * On LPAR, the watchdog will always trigger a system restart. The module + * parameter cmd is meaningless here. * * * Copyright IBM Corp. 2004, 2013 @@ -17,39 +18,23 @@ * */ -#define KMSG_COMPONENT "diag288_wdt" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "diag288_wdt: " fmt #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> -#include <linux/miscdevice.h> #include <linux/watchdog.h> -#include <linux/suspend.h> +#include <asm/machine.h> #include <asm/ebcdic.h> +#include <asm/diag288.h> #include <asm/diag.h> #include <linux/io.h> -#include <linux/uaccess.h> #define MAX_CMDLEN 240 #define DEFAULT_CMD "SYSTEM RESTART" -#define MIN_INTERVAL 15 /* Minimal time supported by diag88 */ -#define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ - -#define WDT_DEFAULT_TIMEOUT 30 - -/* Function codes - init, change, cancel */ -#define WDT_FUNC_INIT 0 -#define WDT_FUNC_CHANGE 1 -#define WDT_FUNC_CANCEL 2 -#define WDT_FUNC_CONCEAL 0x80000000 - -/* Action codes for LPAR watchdog */ -#define LPARWDT_RESTART 0 - static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; static bool conceal_on; static bool nowayout_info = WATCHDOG_NOWAYOUT; @@ -69,82 +54,46 @@ MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is ac module_param_named(nowayout, nowayout_info, bool, 0444); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("vmwatchdog"); -static int __diag288(unsigned int func, unsigned int timeout, - unsigned long action, unsigned int len) -{ - register unsigned long __func asm("2") = func; - register unsigned long __timeout asm("3") = timeout; - register unsigned long __action asm("4") = action; - register unsigned long __len asm("5") = len; - int err; - - err = -EINVAL; - asm volatile( - " diag %1, %3, 0x288\n" - "0: la %0, 0\n" - "1:\n" - EX_TABLE(0b, 1b) - : "+d" (err) : "d"(__func), "d"(__timeout), - "d"(__action), "d"(__len) : "1", "cc"); - return err; -} +static char *cmd_buf; -static int __diag288_vm(unsigned int func, unsigned int timeout, - char *cmd, size_t len) +static int diag288(unsigned int func, unsigned int timeout, + unsigned long action, unsigned int len) { diag_stat_inc(DIAG_STAT_X288); - return __diag288(func, timeout, virt_to_phys(cmd), len); + return __diag288(func, timeout, action, len); } -static int __diag288_lpar(unsigned int func, unsigned int timeout, - unsigned long action) +static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) { - diag_stat_inc(DIAG_STAT_X288); - return __diag288(func, timeout, action, 0); -} + ssize_t len; -static unsigned long wdt_status; + len = strscpy(cmd_buf, cmd, MAX_CMDLEN); + if (len < 0) + return len; + ASCEBC(cmd_buf, MAX_CMDLEN); + EBC_TOUPPER(cmd_buf, MAX_CMDLEN); -#define DIAG_WDOG_BUSY 0 + return diag288(func, timeout, virt_to_phys(cmd_buf), len); +} static int wdt_start(struct watchdog_device *dev) { - char *ebc_cmd; - size_t len; int ret; unsigned int func; - if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) - return -EBUSY; - - ret = -ENODEV; - - if (MACHINE_IS_VM) { - ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); - if (!ebc_cmd) { - clear_bit(DIAG_WDOG_BUSY, &wdt_status); - return -ENOMEM; - } - len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); - ASCEBC(ebc_cmd, MAX_CMDLEN); - EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); - + if (machine_is_vm()) { func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + ret = diag288_str(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); - kfree(ebc_cmd); } else { - ret = __diag288_lpar(WDT_FUNC_INIT, - dev->timeout, LPARWDT_RESTART); + ret = diag288(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART, 0); } if (ret) { pr_err("The watchdog cannot be activated\n"); - clear_bit(DIAG_WDOG_BUSY, &wdt_status); return ret; } return 0; @@ -152,33 +101,15 @@ static int wdt_start(struct watchdog_device *dev) static int wdt_stop(struct watchdog_device *dev) { - int ret; - - diag_stat_inc(DIAG_STAT_X288); - ret = __diag288(WDT_FUNC_CANCEL, 0, 0, 0); - - clear_bit(DIAG_WDOG_BUSY, &wdt_status); - - return ret; + return diag288(WDT_FUNC_CANCEL, 0, 0, 0); } static int wdt_ping(struct watchdog_device *dev) { - char *ebc_cmd; - size_t len; int ret; unsigned int func; - ret = -ENODEV; - - if (MACHINE_IS_VM) { - ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); - if (!ebc_cmd) - return -ENOMEM; - len = strlcpy(ebc_cmd, wdt_cmd, MAX_CMDLEN); - ASCEBC(ebc_cmd, MAX_CMDLEN); - EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); - + if (machine_is_vm()) { /* * It seems to be ok to z/VM to use the init function to * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must @@ -187,11 +118,10 @@ static int wdt_ping(struct watchdog_device *dev) func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; - ret = __diag288_vm(func, dev->timeout, ebc_cmd, len); + ret = diag288_str(func, dev->timeout, wdt_cmd); WARN_ON(ret != 0); - kfree(ebc_cmd); } else { - ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0); + ret = diag288(WDT_FUNC_CHANGE, dev->timeout, 0, 0); } if (ret) @@ -229,88 +159,26 @@ static struct watchdog_device wdt_dev = { .max_timeout = MAX_INTERVAL, }; -/* - * It makes no sense to go into suspend while the watchdog is running. - * Depending on the memory size, the watchdog might trigger, while we - * are still saving the memory. - */ -static int wdt_suspend(void) -{ - if (test_and_set_bit(DIAG_WDOG_BUSY, &wdt_status)) { - pr_err("Linux cannot be suspended while the watchdog is in use\n"); - return notifier_from_errno(-EBUSY); - } - return NOTIFY_DONE; -} - -static int wdt_resume(void) -{ - clear_bit(DIAG_WDOG_BUSY, &wdt_status); - return NOTIFY_DONE; -} - -static int wdt_power_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - switch (event) { - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - return wdt_resume(); - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - return wdt_suspend(); - default: - return NOTIFY_DONE; - } -} - -static struct notifier_block wdt_power_notifier = { - .notifier_call = wdt_power_event, -}; - static int __init diag288_init(void) { - int ret; - char ebc_begin[] = { - 194, 197, 199, 201, 213 - }; - watchdog_set_nowayout(&wdt_dev, nowayout_info); - if (MACHINE_IS_VM) { - if (__diag288_vm(WDT_FUNC_INIT, 15, - ebc_begin, sizeof(ebc_begin)) != 0) { - pr_err("The watchdog cannot be initialized\n"); - return -EINVAL; - } - } else { - if (__diag288_lpar(WDT_FUNC_INIT, 30, LPARWDT_RESTART)) { + if (machine_is_vm()) { + cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL); + if (!cmd_buf) { pr_err("The watchdog cannot be initialized\n"); - return -EINVAL; + return -ENOMEM; } } - if (__diag288_lpar(WDT_FUNC_CANCEL, 0, 0)) { - pr_err("The watchdog cannot be deactivated\n"); - return -EINVAL; - } - - ret = register_pm_notifier(&wdt_power_notifier); - if (ret) - return ret; - - ret = watchdog_register_device(&wdt_dev); - if (ret) - unregister_pm_notifier(&wdt_power_notifier); - - return ret; + return watchdog_register_device(&wdt_dev); } static void __exit diag288_exit(void) { watchdog_unregister_device(&wdt_dev); - unregister_pm_notifier(&wdt_power_notifier); + kfree(cmd_buf); } -module_init(diag288_init); +module_cpu_feature_match(S390_CPU_FEATURE_D288, diag288_init); module_exit(diag288_exit); |
