xref: /onnv-gate/usr/src/uts/sun4v/os/wdt.c (revision 4438:d0910f8f449c)
12036Swentaoy /*
22036Swentaoy  * CDDL HEADER START
32036Swentaoy  *
42036Swentaoy  * The contents of this file are subject to the terms of the
52036Swentaoy  * Common Development and Distribution License (the "License").
62036Swentaoy  * You may not use this file except in compliance with the License.
72036Swentaoy  *
82036Swentaoy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92036Swentaoy  * or http://www.opensolaris.org/os/licensing.
102036Swentaoy  * See the License for the specific language governing permissions
112036Swentaoy  * and limitations under the License.
122036Swentaoy  *
132036Swentaoy  * When distributing Covered Code, include this CDDL HEADER in each
142036Swentaoy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152036Swentaoy  * If applicable, add the following below this CDDL HEADER, with the
162036Swentaoy  * fields enclosed by brackets "[]" replaced with your own identifying
172036Swentaoy  * information: Portions Copyright [yyyy] [name of copyright owner]
182036Swentaoy  *
192036Swentaoy  * CDDL HEADER END
202036Swentaoy  */
212036Swentaoy /*
223349Swentaoy  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
232036Swentaoy  * Use is subject to license terms.
242036Swentaoy  */
252036Swentaoy 
262036Swentaoy #pragma ident	"%Z%%M%	%I%	%E% SMI"
272036Swentaoy 
282036Swentaoy #include <sys/types.h>
292036Swentaoy #include <sys/hsvc.h>
302036Swentaoy #include <sys/wdt.h>
312036Swentaoy #include <sys/cmn_err.h>
323996Sae112802 #include <sys/cyclic.h>
332036Swentaoy #include <sys/kmem.h>
342036Swentaoy #include <sys/systm.h>
352036Swentaoy #include <sys/sysmacros.h>
362036Swentaoy #include <sys/hypervisor_api.h>
372036Swentaoy #include <sys/mach_descrip.h>
382036Swentaoy #include <sys/mdesc.h>
392036Swentaoy 
402036Swentaoy #define	WDT_ON			1
412036Swentaoy #define	WDT_OFF			0
423996Sae112802 
432036Swentaoy /*
442036Swentaoy  * MILLISEC defines the number of milliseconds in a second.
452036Swentaoy  */
463996Sae112802 #define	WDT_DEFAULT_RESOLUTION	(1 * MILLISEC)	/* Default resolution = 1s */
473996Sae112802 #define	WDT_MIN_TIMEOUT		(1 * MILLISEC)	/* Minimum timeout = 1s */
483996Sae112802 #define	WDT_REGULAR_TIMEOUT	(10 * MILLISEC)	/* Default timeout = 10s */
493996Sae112802 #define	WDT_LONG_TIMEOUT	(60 * MILLISEC)	/* Long timeout = 60s */
503996Sae112802 
512036Swentaoy #define	WDT_MIN_COREAPI_MAJOR	1
522036Swentaoy #define	WDT_MIN_COREAPI_MINOR	1
532036Swentaoy 
542036Swentaoy static void config_watchdog(uint64_t, int);
553996Sae112802 static void watchdog_cyclic_init(hrtime_t);
562036Swentaoy 
572036Swentaoy /*
582036Swentaoy  * Flag used to pat/suspend/resume the watchdog timer.
592036Swentaoy  */
603349Swentaoy int watchdog_activated = WDT_OFF;
613996Sae112802 
623996Sae112802 /*
633996Sae112802  * Tuneable to control watchdog functionality. Watchdog can be
643996Sae112802  * disabled via /etc/system.
653996Sae112802  */
663996Sae112802 int watchdog_enabled = 1;
67*4438Swentaoy static int watchdog_initialized = 0;
683996Sae112802 
693996Sae112802 /*
703996Sae112802  * The following tuneable can be set via /etc/system to control
713996Sae112802  * watchdog pat frequency, which is set to approximately 44% of
723996Sae112802  * the timeout value.
733996Sae112802  */
743996Sae112802 static uint64_t watchdog_timeout = WDT_REGULAR_TIMEOUT;
753996Sae112802 
763996Sae112802 static uint64_t watchdog_long_timeout = WDT_LONG_TIMEOUT;
772036Swentaoy static uint64_t watchdog_resolution = WDT_DEFAULT_RESOLUTION;
782036Swentaoy 
792036Swentaoy void
watchdog_init(void)802036Swentaoy watchdog_init(void)
812036Swentaoy {
822036Swentaoy 	int num_nodes;
832036Swentaoy 	int nplat;
842036Swentaoy 	md_t *mdp;
852036Swentaoy 	mde_cookie_t *listp = NULL;
862036Swentaoy 	int listsz;
872036Swentaoy 	uint64_t major;
882036Swentaoy 	uint64_t minor;
892036Swentaoy 	uint64_t watchdog_max_timeout;
903996Sae112802 	hrtime_t cyclic_interval;
912036Swentaoy 
922036Swentaoy 	if (!watchdog_enabled) {
932036Swentaoy 		return;
942036Swentaoy 	}
952036Swentaoy 
962036Swentaoy 	if (hsvc_version(HSVC_GROUP_CORE, &major, &minor) != 0 ||
97*4438Swentaoy 	    major != WDT_MIN_COREAPI_MAJOR ||
98*4438Swentaoy 	    minor < WDT_MIN_COREAPI_MINOR) {
992036Swentaoy 		cmn_err(CE_NOTE, "Disabling watchdog as watchdog services are "
1003996Sae112802 		    "not available\n");
1012036Swentaoy 		watchdog_enabled = 0;
1022036Swentaoy 		return;
1032036Swentaoy 	}
1042036Swentaoy 
1052036Swentaoy 	/*
1062036Swentaoy 	 * Get the watchdog-max-timeout and watchdog-resolution MD properties.
1072036Swentaoy 	 */
1082036Swentaoy 	if ((mdp = md_get_handle()) == NULL) {
1092036Swentaoy 		cmn_err(CE_WARN, "Unable to initialize machine description, "
1103996Sae112802 		    "watchdog is disabled.");
1112036Swentaoy 		watchdog_enabled = 0;
1122036Swentaoy 		return;
1132036Swentaoy 	}
1142036Swentaoy 
1152036Swentaoy 	num_nodes = md_node_count(mdp);
1162036Swentaoy 	ASSERT(num_nodes > 0);
1172036Swentaoy 
1182036Swentaoy 	listsz = num_nodes * sizeof (mde_cookie_t);
1192036Swentaoy 	listp = kmem_zalloc(listsz, KM_SLEEP);
1202036Swentaoy 
1212036Swentaoy 	nplat = md_scan_dag(mdp, md_root_node(mdp),
1223996Sae112802 	    md_find_name(mdp, "platform"), md_find_name(mdp, "fwd"), listp);
1232036Swentaoy 
1242036Swentaoy 	ASSERT(nplat == 1);
1252036Swentaoy 
1262036Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-max-timeout",
1273996Sae112802 	    &watchdog_max_timeout) || watchdog_max_timeout < WDT_MIN_TIMEOUT) {
1283996Sae112802 		cmn_err(CE_WARN, "Invalid watchdog-max-timeout, watchdog "
1293996Sae112802 		    "is disabled.");
1302036Swentaoy 		watchdog_enabled = 0;
1312036Swentaoy 		kmem_free(listp, listsz);
1322036Swentaoy 		(void) md_fini_handle(mdp);
1332036Swentaoy 		return;
1342036Swentaoy 	}
1352036Swentaoy 
1363996Sae112802 	/*
1373996Sae112802 	 * Make sure that watchdog timeout value is within limits.
1383996Sae112802 	 */
1393996Sae112802 	if (watchdog_timeout < WDT_MIN_TIMEOUT)
1403996Sae112802 		watchdog_timeout = WDT_MIN_TIMEOUT;
1413996Sae112802 	else if (watchdog_timeout > WDT_LONG_TIMEOUT)
1423996Sae112802 		watchdog_timeout = WDT_LONG_TIMEOUT;
1433996Sae112802 
1443996Sae112802 	if (watchdog_timeout > watchdog_max_timeout)
1453996Sae112802 		watchdog_timeout = watchdog_max_timeout;
1463996Sae112802 
1473996Sae112802 	if (watchdog_long_timeout > watchdog_max_timeout)
1483996Sae112802 		watchdog_long_timeout = watchdog_max_timeout;
1492036Swentaoy 
1502036Swentaoy 	if (md_get_prop_val(mdp, listp[0], "watchdog-resolution",
1513996Sae112802 	    &watchdog_resolution)) {
1522036Swentaoy 		cmn_err(CE_WARN, "Cannot read watchdog-resolution, watchdog "
1533996Sae112802 		    "is disabled.");
1542036Swentaoy 		watchdog_enabled = 0;
1552036Swentaoy 		kmem_free(listp, listsz);
1562036Swentaoy 		(void) md_fini_handle(mdp);
1572036Swentaoy 		return;
1582036Swentaoy 	}
1592036Swentaoy 
1602036Swentaoy 	if (watchdog_resolution == 0 ||
1613996Sae112802 	    watchdog_resolution > WDT_DEFAULT_RESOLUTION)
1622036Swentaoy 		watchdog_resolution = WDT_DEFAULT_RESOLUTION;
1633996Sae112802 
1642036Swentaoy 	kmem_free(listp, listsz);
1652036Swentaoy 	(void) md_fini_handle(mdp);
1662036Swentaoy 
1672036Swentaoy 	/*
1682036Swentaoy 	 * round the timeout to the nearest smaller value.
1692036Swentaoy 	 */
1702036Swentaoy 	watchdog_long_timeout -=
1713996Sae112802 	    watchdog_long_timeout % watchdog_resolution;
1723996Sae112802 	watchdog_timeout -=
1733996Sae112802 	    watchdog_timeout % watchdog_resolution;
1743996Sae112802 
1753996Sae112802 	/*
1763996Sae112802 	 * Cyclic need to be fired twice the frequency of regular
1773996Sae112802 	 * watchdog timeout. Pedantic here and setting cyclic
1783996Sae112802 	 * frequency to approximately 44% of watchdog_timeout.
1793996Sae112802 	 */
1803996Sae112802 	cyclic_interval = (watchdog_timeout >> 1) - (watchdog_timeout >> 4);
1813996Sae112802 	/*
1823996Sae112802 	 * Note that regular timeout interval is in millisecond,
1833996Sae112802 	 * therefore to get cyclic interval in nanosecond need to
1843996Sae112802 	 * multiply by MICROSEC.
1853996Sae112802 	 */
1863996Sae112802 	cyclic_interval *= MICROSEC;
1873996Sae112802 
1883996Sae112802 	watchdog_cyclic_init(cyclic_interval);
189*4438Swentaoy 	watchdog_initialized = 1;
190*4438Swentaoy 	config_watchdog(watchdog_timeout, WDT_ON);
1912036Swentaoy }
1922036Swentaoy 
1932036Swentaoy /*
1943996Sae112802  * Pat the watchdog timer periodically using the hypervisor API.
1953996Sae112802  * Regular pat occurs when the system runs normally.
1963996Sae112802  * Long pat is when system panics.
1972036Swentaoy  */
1982036Swentaoy void
watchdog_pat()1992036Swentaoy watchdog_pat()
2002036Swentaoy {
2012036Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2023996Sae112802 		if (panicstr)
2033996Sae112802 			config_watchdog(watchdog_long_timeout, WDT_ON);
2043996Sae112802 		else
2053996Sae112802 			config_watchdog(watchdog_timeout, WDT_ON);
2062036Swentaoy 	}
2072036Swentaoy }
2082036Swentaoy 
2092036Swentaoy /*
2102036Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2112036Swentaoy  */
2122036Swentaoy void
watchdog_suspend()2132036Swentaoy watchdog_suspend()
2142036Swentaoy {
2152036Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2162036Swentaoy 		config_watchdog(0, WDT_OFF);
2172036Swentaoy 	}
2182036Swentaoy }
2192036Swentaoy 
2202036Swentaoy /*
2212036Swentaoy  * We don't save/restore the remaining watchdog timeout time at present.
2222036Swentaoy  */
2232036Swentaoy void
watchdog_resume()2242036Swentaoy watchdog_resume()
2252036Swentaoy {
2262036Swentaoy 	if (watchdog_enabled && !watchdog_activated) {
2272036Swentaoy 		if (panicstr) {
2282036Swentaoy 			config_watchdog(watchdog_long_timeout, WDT_ON);
2292036Swentaoy 		} else {
2303996Sae112802 			config_watchdog(watchdog_timeout, WDT_ON);
2312036Swentaoy 		}
2322036Swentaoy 	}
2332036Swentaoy }
2342036Swentaoy 
2352036Swentaoy void
watchdog_clear()2362036Swentaoy watchdog_clear()
2372036Swentaoy {
2382036Swentaoy 	if (watchdog_enabled && watchdog_activated) {
2392036Swentaoy 		config_watchdog(0, WDT_OFF);
2402036Swentaoy 	}
2412036Swentaoy }
2422036Swentaoy 
2432036Swentaoy static void
config_watchdog(uint64_t timeout,int new_state)2442036Swentaoy config_watchdog(uint64_t timeout, int new_state)
2452036Swentaoy {
2462036Swentaoy 	uint64_t time_remaining;
2472036Swentaoy 	uint64_t ret;
2482036Swentaoy 
249*4438Swentaoy 	if (watchdog_initialized) {
250*4438Swentaoy 		watchdog_activated = new_state;
251*4438Swentaoy 		ret = hv_mach_set_watchdog(timeout, &time_remaining);
252*4438Swentaoy 		if (ret != H_EOK) {
253*4438Swentaoy 			cmn_err(CE_WARN, "Failed to operate on the watchdog. "
254*4438Swentaoy 			    "Error = 0x%lx", ret);
255*4438Swentaoy 			watchdog_enabled = 0;
256*4438Swentaoy 		}
2572036Swentaoy 	}
2582036Swentaoy }
2593996Sae112802 
2603996Sae112802 /*
2613996Sae112802  * Once the watchdog cyclic is initialized, it won't be removed.
2623996Sae112802  * The only way to not add the watchdog cyclic is to disable the watchdog
2633996Sae112802  * by setting the watchdog_enabled to 0 in /etc/system file.
2643996Sae112802  */
2653996Sae112802 static void
watchdog_cyclic_init(hrtime_t wdt_cyclic_interval)2663996Sae112802 watchdog_cyclic_init(hrtime_t wdt_cyclic_interval)
2673996Sae112802 {
2683996Sae112802 	cyc_handler_t hdlr;
2693996Sae112802 	cyc_time_t when;
2703996Sae112802 
2713996Sae112802 	hdlr.cyh_func = (cyc_func_t)watchdog_pat;
2723996Sae112802 	hdlr.cyh_level = CY_HIGH_LEVEL;
2733996Sae112802 	hdlr.cyh_arg = NULL;
2743996Sae112802 
2753996Sae112802 	when.cyt_when = 0;
2763996Sae112802 	when.cyt_interval = wdt_cyclic_interval;
2773996Sae112802 
2783996Sae112802 	mutex_enter(&cpu_lock);
2793996Sae112802 	(void) cyclic_add(&hdlr, &when);
2803996Sae112802 	mutex_exit(&cpu_lock);
2813996Sae112802 }
282