Release 4.11 drivers/watchdog/diag288_wdt.c
/*
* Watchdog driver for z/VM and LPAR using the diag 288 interface.
*
* Under z/VM, expiration of the watchdog will send a "system restart" command
* 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.
*
* On LPAR, the watchdog will always trigger a system restart. the module
* paramter cmd is meaningless here.
*
*
* Copyright IBM Corp. 2004, 2013
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Philipp Hachtmann (phacht@de.ibm.com)
*
*/
#define KMSG_COMPONENT "diag288_wdt"
#define pr_fmt(fmt) KMSG_COMPONENT ": " 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/ebcdic.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;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>");
MODULE_DESCRIPTION("System z diag288 Watchdog Timer");
module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644);
MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)");
module_param_named(conceal, conceal_on, bool, 0644);
MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)");
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arnd Bergmann | 60 | 75.00% | 1 | 33.33% |
Philipp Hachtmann | 11 | 13.75% | 1 | 33.33% |
Martin Schwidefsky | 9 | 11.25% | 1 | 33.33% |
Total | 80 | 100.00% | 3 | 100.00% |
static int __diag288_vm(unsigned int func, unsigned int timeout,
char *cmd, size_t len)
{
diag_stat_inc(DIAG_STAT_X288);
return __diag288(func, timeout, virt_to_phys(cmd), len);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 31 | 75.61% | 1 | 33.33% |
Arnd Bergmann | 5 | 12.20% | 1 | 33.33% |
Martin Schwidefsky | 5 | 12.20% | 1 | 33.33% |
Total | 41 | 100.00% | 3 | 100.00% |
static int __diag288_lpar(unsigned int func, unsigned int timeout,
unsigned long action)
{
diag_stat_inc(DIAG_STAT_X288);
return __diag288(func, timeout, action, 0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 30 | 85.71% | 1 | 50.00% |
Martin Schwidefsky | 5 | 14.29% | 1 | 50.00% |
Total | 35 | 100.00% | 2 | 100.00% |
static unsigned long wdt_status;
#define DIAG_WDOG_BUSY 0
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);
func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
: WDT_FUNC_INIT;
ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
WARN_ON(ret != 0);
kfree(ebc_cmd);
} else {
ret = __diag288_lpar(WDT_FUNC_INIT,
dev->timeout, LPARWDT_RESTART);
}
if (ret) {
pr_err("The watchdog cannot be activated\n");
clear_bit(DIAG_WDOG_BUSY, &wdt_status);
return ret;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arnd Bergmann | 85 | 46.45% | 1 | 14.29% |
Philipp Hachtmann | 58 | 31.69% | 2 | 28.57% |
Guenter Roeck | 32 | 17.49% | 1 | 14.29% |
Martin Schwidefsky | 6 | 3.28% | 1 | 14.29% |
Christian Bornträger | 1 | 0.55% | 1 | 14.29% |
Xu Wang | 1 | 0.55% | 1 | 14.29% |
Total | 183 | 100.00% | 7 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arnd Bergmann | 19 | 44.19% | 1 | 20.00% |
Philipp Hachtmann | 10 | 23.26% | 1 | 20.00% |
Guenter Roeck | 8 | 18.60% | 1 | 20.00% |
Martin Schwidefsky | 6 | 13.95% | 2 | 40.00% |
Total | 43 | 100.00% | 5 | 100.00% |
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);
/*
* It seems to be ok to z/VM to use the init function to
* retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must
* be used when the watchdog is running.
*/
func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
: WDT_FUNC_INIT;
ret = __diag288_vm(func, dev->timeout, ebc_cmd, len);
WARN_ON(ret != 0);
kfree(ebc_cmd);
} else {
ret = __diag288_lpar(WDT_FUNC_CHANGE, dev->timeout, 0);
}
if (ret)
pr_err("The watchdog timer cannot be started or reset\n");
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 92 | 62.59% | 2 | 40.00% |
Arnd Bergmann | 49 | 33.33% | 1 | 20.00% |
Xu Wang | 3 | 2.04% | 1 | 20.00% |
Heiko Carstens | 3 | 2.04% | 1 | 20.00% |
Total | 147 | 100.00% | 5 | 100.00% |
static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to)
{
dev->timeout = new_to;
return wdt_ping(dev);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gerald Schaefer | 15 | 55.56% | 1 | 50.00% |
Philipp Hachtmann | 12 | 44.44% | 1 | 50.00% |
Total | 27 | 100.00% | 2 | 100.00% |
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdt_start,
.stop = wdt_stop,
.ping = wdt_ping,
.set_timeout = wdt_set_timeout,
};
static struct watchdog_info wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "z Watchdog",
};
static struct watchdog_device wdt_dev = {
.parent = NULL,
.info = &wdt_info,
.ops = &wdt_ops,
.bootstatus = 0,
.timeout = WDT_DEFAULT_TIMEOUT,
.min_timeout = MIN_INTERVAL,
.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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christian Bornträger | 26 | 74.29% | 1 | 25.00% |
Akinobu Mita | 5 | 14.29% | 1 | 25.00% |
Philipp Hachtmann | 2 | 5.71% | 1 | 25.00% |
Guenter Roeck | 2 | 5.71% | 1 | 25.00% |
Total | 35 | 100.00% | 4 | 100.00% |
static int wdt_resume(void)
{
clear_bit(DIAG_WDOG_BUSY, &wdt_status);
return NOTIFY_DONE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 17 | 89.47% | 1 | 50.00% |
Guenter Roeck | 2 | 10.53% | 1 | 50.00% |
Total | 19 | 100.00% | 2 | 100.00% |
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;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Christian Bornträger | 46 | 93.88% | 1 | 50.00% |
Philipp Hachtmann | 3 | 6.12% | 1 | 50.00% |
Total | 49 | 100.00% | 2 | 100.00% |
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)) {
pr_err("The watchdog cannot be initialized\n");
return -EINVAL;
}
}
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 104 | 67.53% | 2 | 40.00% |
Christian Bornträger | 29 | 18.83% | 1 | 20.00% |
Arnd Bergmann | 20 | 12.99% | 1 | 20.00% |
Xu Wang | 1 | 0.65% | 1 | 20.00% |
Total | 154 | 100.00% | 5 | 100.00% |
static void __exit diag288_exit(void)
{
watchdog_unregister_device(&wdt_dev);
unregister_pm_notifier(&wdt_power_notifier);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arnd Bergmann | 13 | 61.90% | 1 | 33.33% |
Philipp Hachtmann | 5 | 23.81% | 1 | 33.33% |
Christian Bornträger | 3 | 14.29% | 1 | 33.33% |
Total | 21 | 100.00% | 3 | 100.00% |
module_init(diag288_init);
module_exit(diag288_exit);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Philipp Hachtmann | 535 | 45.96% | 2 | 11.76% |
Arnd Bergmann | 373 | 32.04% | 1 | 5.88% |
Christian Bornträger | 128 | 11.00% | 1 | 5.88% |
Guenter Roeck | 54 | 4.64% | 1 | 5.88% |
Martin Schwidefsky | 34 | 2.92% | 3 | 17.65% |
Gerald Schaefer | 18 | 1.55% | 1 | 5.88% |
Xu Wang | 7 | 0.60% | 2 | 11.76% |
Akinobu Mita | 5 | 0.43% | 1 | 5.88% |
Tejun Heo | 3 | 0.26% | 1 | 5.88% |
Heiko Carstens | 3 | 0.26% | 1 | 5.88% |
Rusty Russell | 2 | 0.17% | 1 | 5.88% |
Bhumika Goyal | 1 | 0.09% | 1 | 5.88% |
Andrey Panin | 1 | 0.09% | 1 | 5.88% |
Total | 1164 | 100.00% | 17 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.