xref: /titanic_53/usr/src/uts/sun4v/os/wdt.c (revision 77d1565c06c50a8eb957f25b30dd76fab299ff2e)
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>
32*77d1565cSae112802 #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
42*77d1565cSae112802 
433c431bb5Swentaoy /*
443c431bb5Swentaoy  * MILLISEC defines the number of milliseconds in a second.
453c431bb5Swentaoy  */
46*77d1565cSae112802 #define	WDT_DEFAULT_RESOLUTION	(1 * MILLISEC)	/* Default resolution = 1s */
47*77d1565cSae112802 #define	WDT_MIN_TIMEOUT		(1 * MILLISEC)	/* Minimum timeout = 1s */
48*77d1565cSae112802 #define	WDT_REGULAR_TIMEOUT	(10 * MILLISEC)	/* Default timeout = 10s */
49*77d1565cSae112802 #define	WDT_LONG_TIMEOUT	(60 * MILLISEC)	/* Long timeout = 60s */
50*77d1565cSae112802 
513c431bb5Swentaoy #define	WDT_MIN_COREAPI_MAJOR	1
523c431bb5Swentaoy #define	WDT_MIN_COREAPI_MINOR	1
533c431bb5Swentaoy 
543c431bb5Swentaoy static void config_watchdog(uint64_t, int);
55*77d1565cSae112802 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;
61*77d1565cSae112802 
62*77d1565cSae112802 /*
63*77d1565cSae112802  * Tuneable to control watchdog functionality. Watchdog can be
64*77d1565cSae112802  * disabled via /etc/system.
65*77d1565cSae112802  */
66*77d1565cSae112802 int watchdog_enabled = 1;
67*77d1565cSae112802 
68*77d1565cSae112802 /*
69*77d1565cSae112802  * The following tuneable can be set via /etc/system to control
70*77d1565cSae112802  * watchdog pat frequency, which is set to approximately 44% of
71*77d1565cSae112802  * the timeout value.
72*77d1565cSae112802  */
73*77d1565cSae112802 static uint64_t watchdog_timeout = WDT_REGULAR_TIMEOUT;
74*77d1565cSae112802 
75*77d1565cSae112802 static uint64_t watchdog_long_timeout = WDT_LONG_TIMEOUT;
763c431bb5Swentaoy static uint64_t watchdog_resolution = WDT_DEFAULT_RESOLUTION;
773c431bb5Swentaoy 
783c431bb5Swentaoy void
793c431bb5Swentaoy watchdog_init(void)
803c431bb5Swentaoy {
813c431bb5Swentaoy 	int num_nodes;
823c431bb5Swentaoy 	int nplat;
833c431bb5Swentaoy 	md_t *mdp;
843c431bb5Swentaoy 	mde_cookie_t *listp = NULL;
853c431bb5Swentaoy 	int listsz;
863c431bb5Swentaoy 	uint64_t major;
873c431bb5Swentaoy 	uint64_t minor;
883c431bb5Swentaoy 	uint64_t watchdog_max_timeout;
89*77d1565cSae112802 	hrtime_t cyclic_interval;
903c431bb5Swentaoy 
913c431bb5Swentaoy 	if (!watchdog_enabled) {
923c431bb5Swentaoy 		return;
933c431bb5Swentaoy 	}
943c431bb5Swentaoy 
953c431bb5Swentaoy 	if (hsvc_version(HSVC_GROUP_CORE, &major, &minor) != 0 ||
963c431bb5Swentaoy 		major != WDT_MIN_COREAPI_MAJOR ||
973c431bb5Swentaoy 		minor < WDT_MIN_COREAPI_MINOR) {
983c431bb5Swentaoy 		cmn_err(CE_NOTE, "Disabling watchdog as watchdog services are "
993c431bb5Swentaoy 		    "not available\n");
1003c431bb5Swentaoy 		watchdog_enabled = 0;
1013c431bb5Swentaoy 		return;
1023c431bb5Swentaoy 	}
1033c431bb5Swentaoy 
1043c431bb5Swentaoy 	/*
1053c431bb5Swentaoy 	 * Get the watchdog-max-timeout and watchdog-resolution MD properties.
1063c431bb5Swentaoy 	 */
1073c431bb5Swentaoy 	if ((mdp = md_get_handle()) == NULL) {
1083c431bb5Swentaoy 		cmn_err(CE_WARN, "Unable to initialize machine description, "
1093c431bb5Swentaoy 		    "watchdog is disabled.");
1103c431bb5Swentaoy 		watchdog_enabled = 0;
1113c431bb5Swentaoy 		return;
1123c431bb5Swentaoy 	}
1133c431bb5Swentaoy 
1143c431bb5Swentaoy 	num_nodes = md_node_count(mdp);
1153c431bb5Swentaoy 	ASSERT(num_nodes > 0);
1163c431bb5Swentaoy 
1173c431bb5Swentaoy 	listsz = num_nodes * sizeof (mde_cookie_t);
1183c431bb5Swentaoy 	listp = kmem_zalloc(listsz, KM_SLEEP);
1193c431bb5Swentaoy 
1203c431bb5Swentaoy 	nplat = md_scan_dag(mdp, md_root_node(mdp),
1213c431bb5Swentaoy 	    md_find_name(mdp, "platform"), md_find_name(mdp, "fwd"), listp);
1223c431bb5Swentaoy 
1233c431bb5Swentaoy 	ASSERT(nplat == 1);
1243c431bb5Swentaoy 
1253c431bb5Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-max-timeout",
126*77d1565cSae112802 	    &watchdog_max_timeout) || watchdog_max_timeout < WDT_MIN_TIMEOUT) {
127*77d1565cSae112802 		cmn_err(CE_WARN, "Invalid watchdog-max-timeout, watchdog "
1283c431bb5Swentaoy 		    "is disabled.");
1293c431bb5Swentaoy 		watchdog_enabled = 0;
1303c431bb5Swentaoy 		kmem_free(listp, listsz);
1313c431bb5Swentaoy 		(void) md_fini_handle(mdp);
1323c431bb5Swentaoy 		return;
1333c431bb5Swentaoy 	}
1343c431bb5Swentaoy 
135*77d1565cSae112802 	/*
136*77d1565cSae112802 	 * Make sure that watchdog timeout value is within limits.
137*77d1565cSae112802 	 */
138*77d1565cSae112802 	if (watchdog_timeout < WDT_MIN_TIMEOUT)
139*77d1565cSae112802 		watchdog_timeout = WDT_MIN_TIMEOUT;
140*77d1565cSae112802 	else if (watchdog_timeout > WDT_LONG_TIMEOUT)
141*77d1565cSae112802 		watchdog_timeout = WDT_LONG_TIMEOUT;
142*77d1565cSae112802 
143*77d1565cSae112802 	if (watchdog_timeout > watchdog_max_timeout)
144*77d1565cSae112802 		watchdog_timeout = watchdog_max_timeout;
145*77d1565cSae112802 
146*77d1565cSae112802 	if (watchdog_long_timeout > watchdog_max_timeout)
147*77d1565cSae112802 		watchdog_long_timeout = watchdog_max_timeout;
1483c431bb5Swentaoy 
1493c431bb5Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-resolution",
1503c431bb5Swentaoy 	    &watchdog_resolution)) {
1513c431bb5Swentaoy 		cmn_err(CE_WARN, "Cannot read watchdog-resolution, watchdog "
1523c431bb5Swentaoy 		    "is disabled.");
1533c431bb5Swentaoy 		watchdog_enabled = 0;
1543c431bb5Swentaoy 		kmem_free(listp, listsz);
1553c431bb5Swentaoy 		(void) md_fini_handle(mdp);
1563c431bb5Swentaoy 		return;
1573c431bb5Swentaoy 	}
1583c431bb5Swentaoy 
1593c431bb5Swentaoy 	if (watchdog_resolution == 0 ||
160*77d1565cSae112802 	    watchdog_resolution > WDT_DEFAULT_RESOLUTION)
1613c431bb5Swentaoy 		watchdog_resolution = WDT_DEFAULT_RESOLUTION;
162*77d1565cSae112802 
1633c431bb5Swentaoy 	kmem_free(listp, listsz);
1643c431bb5Swentaoy 	(void) md_fini_handle(mdp);
1653c431bb5Swentaoy 
1663c431bb5Swentaoy 	/*
1673c431bb5Swentaoy 	 * round the timeout to the nearest smaller value.
1683c431bb5Swentaoy 	 */
1693c431bb5Swentaoy 	watchdog_long_timeout -=
1703c431bb5Swentaoy 	    watchdog_long_timeout % watchdog_resolution;
171*77d1565cSae112802 	watchdog_timeout -=
172*77d1565cSae112802 	    watchdog_timeout % watchdog_resolution;
1733c431bb5Swentaoy 
174*77d1565cSae112802 	config_watchdog(watchdog_timeout, WDT_ON);
175*77d1565cSae112802 
176*77d1565cSae112802 	/*
177*77d1565cSae112802 	 * Cyclic need to be fired twice the frequency of regular
178*77d1565cSae112802 	 * watchdog timeout. Pedantic here and setting cyclic
179*77d1565cSae112802 	 * frequency to approximately 44% of watchdog_timeout.
180*77d1565cSae112802 	 */
181*77d1565cSae112802 	cyclic_interval = (watchdog_timeout >> 1) - (watchdog_timeout >> 4);
182*77d1565cSae112802 	/*
183*77d1565cSae112802 	 * Note that regular timeout interval is in millisecond,
184*77d1565cSae112802 	 * therefore to get cyclic interval in nanosecond need to
185*77d1565cSae112802 	 * multiply by MICROSEC.
186*77d1565cSae112802 	 */
187*77d1565cSae112802 	cyclic_interval *= MICROSEC;
188*77d1565cSae112802 
189*77d1565cSae112802 	watchdog_cyclic_init(cyclic_interval);
1903c431bb5Swentaoy }
1913c431bb5Swentaoy 
1923c431bb5Swentaoy /*
193*77d1565cSae112802  * Pat the watchdog timer periodically using the hypervisor API.
194*77d1565cSae112802  * Regular pat occurs when the system runs normally.
195*77d1565cSae112802  * Long pat is when system panics.
1963c431bb5Swentaoy  */
1973c431bb5Swentaoy void
1983c431bb5Swentaoy watchdog_pat()
1993c431bb5Swentaoy {
2003c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
201*77d1565cSae112802 		if (panicstr)
202*77d1565cSae112802 			config_watchdog(watchdog_long_timeout, WDT_ON);
203*77d1565cSae112802 		else
204*77d1565cSae112802 			config_watchdog(watchdog_timeout, WDT_ON);
2053c431bb5Swentaoy 	}
2063c431bb5Swentaoy }
2073c431bb5Swentaoy 
2083c431bb5Swentaoy /*
2093c431bb5Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2103c431bb5Swentaoy  */
2113c431bb5Swentaoy void
2123c431bb5Swentaoy watchdog_suspend()
2133c431bb5Swentaoy {
2143c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2153c431bb5Swentaoy 		config_watchdog(0, WDT_OFF);
2163c431bb5Swentaoy 	}
2173c431bb5Swentaoy }
2183c431bb5Swentaoy 
2193c431bb5Swentaoy /*
2203c431bb5Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2213c431bb5Swentaoy  */
2223c431bb5Swentaoy void
2233c431bb5Swentaoy watchdog_resume()
2243c431bb5Swentaoy {
2253c431bb5Swentaoy 	if (watchdog_enabled && !watchdog_activated) {
2263c431bb5Swentaoy 		if (panicstr) {
2273c431bb5Swentaoy 			config_watchdog(watchdog_long_timeout, WDT_ON);
2283c431bb5Swentaoy 		} else {
229*77d1565cSae112802 			config_watchdog(watchdog_timeout, WDT_ON);
2303c431bb5Swentaoy 		}
2313c431bb5Swentaoy 	}
2323c431bb5Swentaoy }
2333c431bb5Swentaoy 
2343c431bb5Swentaoy void
2353c431bb5Swentaoy watchdog_clear()
2363c431bb5Swentaoy {
2373c431bb5Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2383c431bb5Swentaoy 		config_watchdog(0, WDT_OFF);
2393c431bb5Swentaoy 	}
2403c431bb5Swentaoy }
2413c431bb5Swentaoy 
2423c431bb5Swentaoy static void
2433c431bb5Swentaoy config_watchdog(uint64_t timeout, int new_state)
2443c431bb5Swentaoy {
2453c431bb5Swentaoy 	uint64_t time_remaining;
2463c431bb5Swentaoy 	uint64_t ret;
2473c431bb5Swentaoy 
2483c431bb5Swentaoy 	watchdog_activated = new_state;
2493c431bb5Swentaoy 	ret = hv_mach_set_watchdog(timeout, &time_remaining);
2503c431bb5Swentaoy 	if (ret != H_EOK) {
2513c431bb5Swentaoy 		cmn_err(CE_WARN, "Failed to operate on the watchdog. "
2523c431bb5Swentaoy 		    "Error = 0x%lx", ret);
2533c431bb5Swentaoy 		watchdog_enabled = 0;
2543c431bb5Swentaoy 	}
2553c431bb5Swentaoy }
256*77d1565cSae112802 
257*77d1565cSae112802 /*
258*77d1565cSae112802  * Once the watchdog cyclic is initialized, it won't be removed.
259*77d1565cSae112802  * The only way to not add the watchdog cyclic is to disable the watchdog
260*77d1565cSae112802  * by setting the watchdog_enabled to 0 in /etc/system file.
261*77d1565cSae112802  */
262*77d1565cSae112802 static void
263*77d1565cSae112802 watchdog_cyclic_init(hrtime_t wdt_cyclic_interval)
264*77d1565cSae112802 {
265*77d1565cSae112802 	cyc_handler_t hdlr;
266*77d1565cSae112802 	cyc_time_t when;
267*77d1565cSae112802 
268*77d1565cSae112802 	hdlr.cyh_func = (cyc_func_t)watchdog_pat;
269*77d1565cSae112802 	hdlr.cyh_level = CY_HIGH_LEVEL;
270*77d1565cSae112802 	hdlr.cyh_arg = NULL;
271*77d1565cSae112802 
272*77d1565cSae112802 	when.cyt_when = 0;
273*77d1565cSae112802 	when.cyt_interval = wdt_cyclic_interval;
274*77d1565cSae112802 
275*77d1565cSae112802 	mutex_enter(&cpu_lock);
276*77d1565cSae112802 	(void) cyclic_add(&hdlr, &when);
277*77d1565cSae112802 	mutex_exit(&cpu_lock);
278*77d1565cSae112802 }
279