xref: /illumos-gate/usr/src/uts/sun4v/os/wdt.c (revision fda7b194)
13c431bb5Swentaoy /*
23c431bb5Swentaoy  * CDDL HEADER START
33c431bb5Swentaoy  *
43c431bb5Swentaoy  * The contents of this file are subject to the terms of the
53c431bb5Swentaoy  * Common Development and Distribution License (the "License").
63c431bb5Swentaoy  * You may not use this file except in compliance with the License.
73c431bb5Swentaoy  *
83c431bb5Swentaoy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93c431bb5Swentaoy  * or http://www.opensolaris.org/os/licensing.
103c431bb5Swentaoy  * See the License for the specific language governing permissions
113c431bb5Swentaoy  * and limitations under the License.
123c431bb5Swentaoy  *
133c431bb5Swentaoy  * When distributing Covered Code, include this CDDL HEADER in each
143c431bb5Swentaoy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153c431bb5Swentaoy  * If applicable, add the following below this CDDL HEADER, with the
163c431bb5Swentaoy  * fields enclosed by brackets "[]" replaced with your own identifying
173c431bb5Swentaoy  * information: Portions Copyright [yyyy] [name of copyright owner]
183c431bb5Swentaoy  *
193c431bb5Swentaoy  * CDDL HEADER END
203c431bb5Swentaoy  */
213c431bb5Swentaoy /*
22927a453eSwentaoy  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
233c431bb5Swentaoy  * Use is subject to license terms.
243c431bb5Swentaoy  */
253c431bb5Swentaoy 
263c431bb5Swentaoy #pragma ident	"%Z%%M%	%I%	%E% SMI"
273c431bb5Swentaoy 
283c431bb5Swentaoy #include <sys/types.h>
293c431bb5Swentaoy #include <sys/hsvc.h>
303c431bb5Swentaoy #include <sys/wdt.h>
313c431bb5Swentaoy #include <sys/cmn_err.h>
3277d1565cSae #include <sys/cyclic.h>
333c431bb5Swentaoy #include <sys/kmem.h>
343c431bb5Swentaoy #include <sys/systm.h>
353c431bb5Swentaoy #include <sys/sysmacros.h>
363c431bb5Swentaoy #include <sys/hypervisor_api.h>
373c431bb5Swentaoy #include <sys/mach_descrip.h>
383c431bb5Swentaoy #include <sys/mdesc.h>
393c431bb5Swentaoy 
403c431bb5Swentaoy #define	WDT_ON			1
413c431bb5Swentaoy #define	WDT_OFF			0
4277d1565cSae 
433c431bb5Swentaoy /*
443c431bb5Swentaoy  * MILLISEC defines the number of milliseconds in a second.
453c431bb5Swentaoy  */
4677d1565cSae #define	WDT_DEFAULT_RESOLUTION	(1 * MILLISEC)	/* Default resolution = 1s */
4777d1565cSae #define	WDT_MIN_TIMEOUT		(1 * MILLISEC)	/* Minimum timeout = 1s */
4877d1565cSae #define	WDT_REGULAR_TIMEOUT	(10 * MILLISEC)	/* Default timeout = 10s */
4977d1565cSae #define	WDT_LONG_TIMEOUT	(60 * MILLISEC)	/* Long timeout = 60s */
5077d1565cSae 
513c431bb5Swentaoy #define	WDT_MIN_COREAPI_MAJOR	1
523c431bb5Swentaoy #define	WDT_MIN_COREAPI_MINOR	1
533c431bb5Swentaoy 
543c431bb5Swentaoy static void config_watchdog(uint64_t, int);
5577d1565cSae static void watchdog_cyclic_init(hrtime_t);
563c431bb5Swentaoy 
573c431bb5Swentaoy /*
583c431bb5Swentaoy  * Flag used to pat/suspend/resume the watchdog timer.
593c431bb5Swentaoy  */
60927a453eSwentaoy int watchdog_activated = WDT_OFF;
6177d1565cSae 
6277d1565cSae /*
6377d1565cSae  * Tuneable to control watchdog functionality. Watchdog can be
6477d1565cSae  * disabled via /etc/system.
6577d1565cSae  */
6677d1565cSae int watchdog_enabled = 1;
67*fda7b194Swentaoy static int watchdog_initialized = 0;
6877d1565cSae 
6977d1565cSae /*
7077d1565cSae  * The following tuneable can be set via /etc/system to control
7177d1565cSae  * watchdog pat frequency, which is set to approximately 44% of
7277d1565cSae  * the timeout value.
7377d1565cSae  */
7477d1565cSae static uint64_t watchdog_timeout = WDT_REGULAR_TIMEOUT;
7577d1565cSae 
7677d1565cSae static uint64_t watchdog_long_timeout = WDT_LONG_TIMEOUT;
773c431bb5Swentaoy static uint64_t watchdog_resolution = WDT_DEFAULT_RESOLUTION;
783c431bb5Swentaoy 
793c431bb5Swentaoy void
watchdog_init(void)803c431bb5Swentaoy watchdog_init(void)
813c431bb5Swentaoy {
823c431bb5Swentaoy 	int num_nodes;
833c431bb5Swentaoy 	int nplat;
843c431bb5Swentaoy 	md_t *mdp;
853c431bb5Swentaoy 	mde_cookie_t *listp = NULL;
863c431bb5Swentaoy 	int listsz;
873c431bb5Swentaoy 	uint64_t major;
883c431bb5Swentaoy 	uint64_t minor;
893c431bb5Swentaoy 	uint64_t watchdog_max_timeout;
9077d1565cSae 	hrtime_t cyclic_interval;
913c431bb5Swentaoy 
923c431bb5Swentaoy 	if (!watchdog_enabled) {
933c431bb5Swentaoy 		return;
943c431bb5Swentaoy 	}
953c431bb5Swentaoy 
963c431bb5Swentaoy 	if (hsvc_version(HSVC_GROUP_CORE, &major, &minor) != 0 ||
97*fda7b194Swentaoy 	    major != WDT_MIN_COREAPI_MAJOR ||
98*fda7b194Swentaoy 	    minor < WDT_MIN_COREAPI_MINOR) {
993c431bb5Swentaoy 		cmn_err(CE_NOTE, "Disabling watchdog as watchdog services are "
10077d1565cSae 		    "not available\n");
1013c431bb5Swentaoy 		watchdog_enabled = 0;
1023c431bb5Swentaoy 		return;
1033c431bb5Swentaoy 	}
1043c431bb5Swentaoy 
1053c431bb5Swentaoy 	/*
1063c431bb5Swentaoy 	 * Get the watchdog-max-timeout and watchdog-resolution MD properties.
1073c431bb5Swentaoy 	 */
1083c431bb5Swentaoy 	if ((mdp = md_get_handle()) == NULL) {
1093c431bb5Swentaoy 		cmn_err(CE_WARN, "Unable to initialize machine description, "
11077d1565cSae 		    "watchdog is disabled.");
1113c431bb5Swentaoy 		watchdog_enabled = 0;
1123c431bb5Swentaoy 		return;
1133c431bb5Swentaoy 	}
1143c431bb5Swentaoy 
1153c431bb5Swentaoy 	num_nodes = md_node_count(mdp);
1163c431bb5Swentaoy 	ASSERT(num_nodes > 0);
1173c431bb5Swentaoy 
1183c431bb5Swentaoy 	listsz = num_nodes * sizeof (mde_cookie_t);
1193c431bb5Swentaoy 	listp = kmem_zalloc(listsz, KM_SLEEP);
1203c431bb5Swentaoy 
1213c431bb5Swentaoy 	nplat = md_scan_dag(mdp, md_root_node(mdp),
12277d1565cSae 	    md_find_name(mdp, "platform"), md_find_name(mdp, "fwd"), listp);
1233c431bb5Swentaoy 
1243c431bb5Swentaoy 	ASSERT(nplat == 1);
1253c431bb5Swentaoy 
1263c431bb5Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-max-timeout",
12777d1565cSae 	    &watchdog_max_timeout) || watchdog_max_timeout < WDT_MIN_TIMEOUT) {
12877d1565cSae 		cmn_err(CE_WARN, "Invalid watchdog-max-timeout, watchdog "
12977d1565cSae 		    "is disabled.");
1303c431bb5Swentaoy 		watchdog_enabled = 0;
1313c431bb5Swentaoy 		kmem_free(listp, listsz);
1323c431bb5Swentaoy 		(void) md_fini_handle(mdp);
1333c431bb5Swentaoy 		return;
1343c431bb5Swentaoy 	}
1353c431bb5Swentaoy 
13677d1565cSae 	/*
13777d1565cSae 	 * Make sure that watchdog timeout value is within limits.
13877d1565cSae 	 */
13977d1565cSae 	if (watchdog_timeout < WDT_MIN_TIMEOUT)
14077d1565cSae 		watchdog_timeout = WDT_MIN_TIMEOUT;
14177d1565cSae 	else if (watchdog_timeout > WDT_LONG_TIMEOUT)
14277d1565cSae 		watchdog_timeout = WDT_LONG_TIMEOUT;
14377d1565cSae 
14477d1565cSae 	if (watchdog_timeout > watchdog_max_timeout)
14577d1565cSae 		watchdog_timeout = watchdog_max_timeout;
14677d1565cSae 
14777d1565cSae 	if (watchdog_long_timeout > watchdog_max_timeout)
14877d1565cSae 		watchdog_long_timeout = watchdog_max_timeout;
1493c431bb5Swentaoy 
1503c431bb5Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-resolution",
15177d1565cSae 	    &watchdog_resolution)) {
1523c431bb5Swentaoy 		cmn_err(CE_WARN, "Cannot read watchdog-resolution, watchdog "
15377d1565cSae 		    "is disabled.");
1543c431bb5Swentaoy 		watchdog_enabled = 0;
1553c431bb5Swentaoy 		kmem_free(listp, listsz);
1563c431bb5Swentaoy 		(void) md_fini_handle(mdp);
1573c431bb5Swentaoy 		return;
1583c431bb5Swentaoy 	}
1593c431bb5Swentaoy 
1603c431bb5Swentaoy 	if (watchdog_resolution == 0 ||
16177d1565cSae 	    watchdog_resolution > WDT_DEFAULT_RESOLUTION)
1623c431bb5Swentaoy 		watchdog_resolution = WDT_DEFAULT_RESOLUTION;
16377d1565cSae 
1643c431bb5Swentaoy 	kmem_free(listp, listsz);
1653c431bb5Swentaoy 	(void) md_fini_handle(mdp);
1663c431bb5Swentaoy 
1673c431bb5Swentaoy 	/*
1683c431bb5Swentaoy 	 * round the timeout to the nearest smaller value.
1693c431bb5Swentaoy 	 */
1703c431bb5Swentaoy 	watchdog_long_timeout -=
17177d1565cSae 	    watchdog_long_timeout % watchdog_resolution;
17277d1565cSae 	watchdog_timeout -=
17377d1565cSae 	    watchdog_timeout % watchdog_resolution;
17477d1565cSae 
17577d1565cSae 	/*
17677d1565cSae 	 * Cyclic need to be fired twice the frequency of regular
17777d1565cSae 	 * watchdog timeout. Pedantic here and setting cyclic
17877d1565cSae 	 * frequency to approximately 44% of watchdog_timeout.
17977d1565cSae 	 */
18077d1565cSae 	cyclic_interval = (watchdog_timeout >> 1) - (watchdog_timeout >> 4);
18177d1565cSae 	/*
18277d1565cSae 	 * Note that regular timeout interval is in millisecond,
18377d1565cSae 	 * therefore to get cyclic interval in nanosecond need to
18477d1565cSae 	 * multiply by MICROSEC.
18577d1565cSae 	 */
18677d1565cSae 	cyclic_interval *= MICROSEC;
1873c431bb5Swentaoy 
18877d1565cSae 	watchdog_cyclic_init(cyclic_interval);
189*fda7b194Swentaoy 	watchdog_initialized = 1;
190*fda7b194Swentaoy 	config_watchdog(watchdog_timeout, WDT_ON);
1913c431bb5Swentaoy }
1923c431bb5Swentaoy 
1933c431bb5Swentaoy /*
19477d1565cSae  * Pat the watchdog timer periodically using the hypervisor API.
19577d1565cSae  * Regular pat occurs when the system runs normally.
19677d1565cSae  * Long pat is when system panics.
1973c431bb5Swentaoy  */
1983c431bb5Swentaoy void
watchdog_pat()1993c431bb5Swentaoy watchdog_pat()
2003c431bb5Swentaoy {
2013c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
20277d1565cSae 		if (panicstr)
20377d1565cSae 			config_watchdog(watchdog_long_timeout, WDT_ON);
20477d1565cSae 		else
20577d1565cSae 			config_watchdog(watchdog_timeout, WDT_ON);
2063c431bb5Swentaoy 	}
2073c431bb5Swentaoy }
2083c431bb5Swentaoy 
2093c431bb5Swentaoy /*
2103c431bb5Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2113c431bb5Swentaoy  */
2123c431bb5Swentaoy void
watchdog_suspend()2133c431bb5Swentaoy watchdog_suspend()
2143c431bb5Swentaoy {
2153c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2163c431bb5Swentaoy 		config_watchdog(0, WDT_OFF);
2173c431bb5Swentaoy 	}
2183c431bb5Swentaoy }
2193c431bb5Swentaoy 
2203c431bb5Swentaoy /*
2213c431bb5Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2223c431bb5Swentaoy  */
2233c431bb5Swentaoy void
watchdog_resume()2243c431bb5Swentaoy watchdog_resume()
2253c431bb5Swentaoy {
2263c431bb5Swentaoy 	if (watchdog_enabled && !watchdog_activated) {
2273c431bb5Swentaoy 		if (panicstr) {
2283c431bb5Swentaoy 			config_watchdog(watchdog_long_timeout, WDT_ON);
2293c431bb5Swentaoy 		} else {
23077d1565cSae 			config_watchdog(watchdog_timeout, WDT_ON);
2313c431bb5Swentaoy 		}
2323c431bb5Swentaoy 	}
2333c431bb5Swentaoy }
2343c431bb5Swentaoy 
2353c431bb5Swentaoy void
watchdog_clear()2363c431bb5Swentaoy watchdog_clear()
2373c431bb5Swentaoy {
2383c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2393c431bb5Swentaoy 		config_watchdog(0, WDT_OFF);
2403c431bb5Swentaoy 	}
2413c431bb5Swentaoy }
2423c431bb5Swentaoy 
2433c431bb5Swentaoy static void
config_watchdog(uint64_t timeout,int new_state)2443c431bb5Swentaoy config_watchdog(uint64_t timeout, int new_state)
2453c431bb5Swentaoy {
2463c431bb5Swentaoy 	uint64_t time_remaining;
2473c431bb5Swentaoy 	uint64_t ret;
2483c431bb5Swentaoy 
249*fda7b194Swentaoy 	if (watchdog_initialized) {
250*fda7b194Swentaoy 		watchdog_activated = new_state;
251*fda7b194Swentaoy 		ret = hv_mach_set_watchdog(timeout, &time_remaining);
252*fda7b194Swentaoy 		if (ret != H_EOK) {
253*fda7b194Swentaoy 			cmn_err(CE_WARN, "Failed to operate on the watchdog. "
254*fda7b194Swentaoy 			    "Error = 0x%lx", ret);
255*fda7b194Swentaoy 			watchdog_enabled = 0;
256*fda7b194Swentaoy 		}
2573c431bb5Swentaoy 	}
2583c431bb5Swentaoy }
25977d1565cSae 
26077d1565cSae /*
26177d1565cSae  * Once the watchdog cyclic is initialized, it won't be removed.
26277d1565cSae  * The only way to not add the watchdog cyclic is to disable the watchdog
26377d1565cSae  * by setting the watchdog_enabled to 0 in /etc/system file.
26477d1565cSae  */
26577d1565cSae static void
watchdog_cyclic_init(hrtime_t wdt_cyclic_interval)26677d1565cSae watchdog_cyclic_init(hrtime_t wdt_cyclic_interval)
26777d1565cSae {
26877d1565cSae 	cyc_handler_t hdlr;
26977d1565cSae 	cyc_time_t when;
27077d1565cSae 
27177d1565cSae 	hdlr.cyh_func = (cyc_func_t)watchdog_pat;
27277d1565cSae 	hdlr.cyh_level = CY_HIGH_LEVEL;
27377d1565cSae 	hdlr.cyh_arg = NULL;
27477d1565cSae 
27577d1565cSae 	when.cyt_when = 0;
27677d1565cSae 	when.cyt_interval = wdt_cyclic_interval;
27777d1565cSae 
27877d1565cSae 	mutex_enter(&cpu_lock);
27977d1565cSae 	(void) cyclic_add(&hdlr, &when);
28077d1565cSae 	mutex_exit(&cpu_lock);
28177d1565cSae }
282