brintos

brintos / linux-shallow public Read only

0
0
Text · 4.3 KiB · 8db86ad Raw
188 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *	sun4v watchdog timer4 *	(c) Copyright 2016 Oracle Corporation5 *6 *	Implement a simple watchdog driver using the built-in sun4v hypervisor7 *	watchdog support. If time expires, the hypervisor stops or bounces8 *	the guest domain.9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 13#include <linux/errno.h>14#include <linux/init.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <linux/watchdog.h>19#include <asm/hypervisor.h>20#include <asm/mdesc.h>21 22#define WDT_TIMEOUT			6023#define WDT_MAX_TIMEOUT			3153600024#define WDT_MIN_TIMEOUT			125#define WDT_DEFAULT_RESOLUTION_MS	1000	/* 1 second */26 27static unsigned int timeout;28module_param(timeout, uint, 0);29MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="30	__MODULE_STRING(WDT_TIMEOUT) ")");31 32static bool nowayout = WATCHDOG_NOWAYOUT;33module_param(nowayout, bool, S_IRUGO);34MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="35	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");36 37static int sun4v_wdt_stop(struct watchdog_device *wdd)38{39	sun4v_mach_set_watchdog(0, NULL);40 41	return 0;42}43 44static int sun4v_wdt_ping(struct watchdog_device *wdd)45{46	int hverr;47 48	/*49	 * HV watchdog timer will round up the timeout50	 * passed in to the nearest multiple of the51	 * watchdog resolution in milliseconds.52	 */53	hverr = sun4v_mach_set_watchdog(wdd->timeout * 1000, NULL);54	if (hverr == HV_EINVAL)55		return -EINVAL;56 57	return 0;58}59 60static int sun4v_wdt_set_timeout(struct watchdog_device *wdd,61				 unsigned int timeout)62{63	wdd->timeout = timeout;64 65	return 0;66}67 68static const struct watchdog_info sun4v_wdt_ident = {69	.options =	WDIOF_SETTIMEOUT |70			WDIOF_MAGICCLOSE |71			WDIOF_KEEPALIVEPING,72	.identity =	"sun4v hypervisor watchdog",73	.firmware_version = 0,74};75 76static const struct watchdog_ops sun4v_wdt_ops = {77	.owner =	THIS_MODULE,78	.start =	sun4v_wdt_ping,79	.stop =		sun4v_wdt_stop,80	.ping =		sun4v_wdt_ping,81	.set_timeout =	sun4v_wdt_set_timeout,82};83 84static struct watchdog_device wdd = {85	.info = &sun4v_wdt_ident,86	.ops = &sun4v_wdt_ops,87	.min_timeout = WDT_MIN_TIMEOUT,88	.max_timeout = WDT_MAX_TIMEOUT,89	.timeout = WDT_TIMEOUT,90};91 92static int __init sun4v_wdt_init(void)93{94	struct mdesc_handle *handle;95	u64 node;96	const u64 *value;97	int err = 0;98	unsigned long major = 1, minor = 1;99 100	/*101	 * There are 2 properties that can be set from the control102	 * domain for the watchdog.103	 * watchdog-resolution104	 * watchdog-max-timeout105	 *106	 * We can expect a handle to be returned otherwise something107	 * serious is wrong. Correct to return -ENODEV here.108	 */109 110	handle = mdesc_grab();111	if (!handle)112		return -ENODEV;113 114	node = mdesc_node_by_name(handle, MDESC_NODE_NULL, "platform");115	err = -ENODEV;116	if (node == MDESC_NODE_NULL)117		goto out_release;118 119	/*120	 * This is a safe way to validate if we are on the right121	 * platform.122	 */123	if (sun4v_hvapi_register(HV_GRP_CORE, major, &minor))124		goto out_hv_unreg;125 126	/* Allow value of watchdog-resolution up to 1s (default) */127	value = mdesc_get_property(handle, node, "watchdog-resolution", NULL);128	err = -EINVAL;129	if (value) {130		if (*value == 0 ||131		    *value > WDT_DEFAULT_RESOLUTION_MS)132			goto out_hv_unreg;133	}134 135	value = mdesc_get_property(handle, node, "watchdog-max-timeout", NULL);136	if (value) {137		/*138		 * If the property value (in ms) is smaller than139		 * min_timeout, return -EINVAL.140		 */141		if (*value < wdd.min_timeout * 1000)142			goto out_hv_unreg;143 144		/*145		 * If the property value is smaller than146		 * default max_timeout  then set watchdog max_timeout to147		 * the value of the property in seconds.148		 */149		if (*value < wdd.max_timeout * 1000)150			wdd.max_timeout = *value  / 1000;151	}152 153	watchdog_init_timeout(&wdd, timeout, NULL);154 155	watchdog_set_nowayout(&wdd, nowayout);156 157	err = watchdog_register_device(&wdd);158	if (err)159		goto out_hv_unreg;160 161	pr_info("initialized (timeout=%ds, nowayout=%d)\n",162		 wdd.timeout, nowayout);163 164	mdesc_release(handle);165 166	return 0;167 168out_hv_unreg:169	sun4v_hvapi_unregister(HV_GRP_CORE);170 171out_release:172	mdesc_release(handle);173	return err;174}175 176static void __exit sun4v_wdt_exit(void)177{178	sun4v_hvapi_unregister(HV_GRP_CORE);179	watchdog_unregister_device(&wdd);180}181 182module_init(sun4v_wdt_init);183module_exit(sun4v_wdt_exit);184 185MODULE_AUTHOR("Wim Coekaerts <wim.coekaerts@oracle.com>");186MODULE_DESCRIPTION("sun4v watchdog driver");187MODULE_LICENSE("GPL");188