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> 3277d1565cSae112802 #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 4277d1565cSae112802 433c431bb5Swentaoy /* 443c431bb5Swentaoy * MILLISEC defines the number of milliseconds in a second. 453c431bb5Swentaoy */ 4677d1565cSae112802 #define WDT_DEFAULT_RESOLUTION (1 * MILLISEC) /* Default resolution = 1s */ 4777d1565cSae112802 #define WDT_MIN_TIMEOUT (1 * MILLISEC) /* Minimum timeout = 1s */ 4877d1565cSae112802 #define WDT_REGULAR_TIMEOUT (10 * MILLISEC) /* Default timeout = 10s */ 4977d1565cSae112802 #define WDT_LONG_TIMEOUT (60 * MILLISEC) /* Long timeout = 60s */ 5077d1565cSae112802 513c431bb5Swentaoy #define WDT_MIN_COREAPI_MAJOR 1 523c431bb5Swentaoy #define WDT_MIN_COREAPI_MINOR 1 533c431bb5Swentaoy 543c431bb5Swentaoy static void config_watchdog(uint64_t, int); 5577d1565cSae112802 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; 6177d1565cSae112802 6277d1565cSae112802 /* 6377d1565cSae112802 * Tuneable to control watchdog functionality. Watchdog can be 6477d1565cSae112802 * disabled via /etc/system. 6577d1565cSae112802 */ 6677d1565cSae112802 int watchdog_enabled = 1; 67*fda7b194Swentaoy static int watchdog_initialized = 0; 6877d1565cSae112802 6977d1565cSae112802 /* 7077d1565cSae112802 * The following tuneable can be set via /etc/system to control 7177d1565cSae112802 * watchdog pat frequency, which is set to approximately 44% of 7277d1565cSae112802 * the timeout value. 7377d1565cSae112802 */ 7477d1565cSae112802 static uint64_t watchdog_timeout = WDT_REGULAR_TIMEOUT; 7577d1565cSae112802 7677d1565cSae112802 static uint64_t watchdog_long_timeout = WDT_LONG_TIMEOUT; 773c431bb5Swentaoy static uint64_t watchdog_resolution = WDT_DEFAULT_RESOLUTION; 783c431bb5Swentaoy 793c431bb5Swentaoy 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; 9077d1565cSae112802 hrtime_t cyclic_interval; 913c431bb5Swentaoy 923c431bb5Swentaoy if (!watchdog_enabled) { 933c431bb5Swentaoy return; 943c431bb5Swentaoy } 953c431bb5Swentaoy 963c431bb5Swentaoy if (hsvc_version(HSVC_GROUP_CORE, &major, &minor) != 0 || 973c431bb5Swentaoy major != WDT_MIN_COREAPI_MAJOR || 983c431bb5Swentaoy minor < WDT_MIN_COREAPI_MINOR) { 993c431bb5Swentaoy cmn_err(CE_NOTE, "Disabling watchdog as watchdog services are " 1003c431bb5Swentaoy "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, " 1103c431bb5Swentaoy "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), 1223c431bb5Swentaoy 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", 12777d1565cSae112802 &watchdog_max_timeout) || watchdog_max_timeout < WDT_MIN_TIMEOUT) { 12877d1565cSae112802 cmn_err(CE_WARN, "Invalid watchdog-max-timeout, watchdog " 1293c431bb5Swentaoy "is disabled."); 1303c431bb5Swentaoy watchdog_enabled = 0; 1313c431bb5Swentaoy kmem_free(listp, listsz); 1323c431bb5Swentaoy (void) md_fini_handle(mdp); 1333c431bb5Swentaoy return; 1343c431bb5Swentaoy } 1353c431bb5Swentaoy 13677d1565cSae112802 /* 13777d1565cSae112802 * Make sure that watchdog timeout value is within limits. 13877d1565cSae112802 */ 13977d1565cSae112802 if (watchdog_timeout < WDT_MIN_TIMEOUT) 14077d1565cSae112802 watchdog_timeout = WDT_MIN_TIMEOUT; 14177d1565cSae112802 else if (watchdog_timeout > WDT_LONG_TIMEOUT) 14277d1565cSae112802 watchdog_timeout = WDT_LONG_TIMEOUT; 14377d1565cSae112802 14477d1565cSae112802 if (watchdog_timeout > watchdog_max_timeout) 14577d1565cSae112802 watchdog_timeout = watchdog_max_timeout; 14677d1565cSae112802 14777d1565cSae112802 if (watchdog_long_timeout > watchdog_max_timeout) 14877d1565cSae112802 watchdog_long_timeout = watchdog_max_timeout; 1493c431bb5Swentaoy 1503c431bb5Swentaoy if (md_get_prop_val(mdp, listp[0], "watchdog-resolution", 1513c431bb5Swentaoy &watchdog_resolution)) { 1523c431bb5Swentaoy cmn_err(CE_WARN, "Cannot read watchdog-resolution, watchdog " 1533c431bb5Swentaoy "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 || 16177d1565cSae112802 watchdog_resolution > WDT_DEFAULT_RESOLUTION) 1623c431bb5Swentaoy watchdog_resolution = WDT_DEFAULT_RESOLUTION; 16377d1565cSae112802 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 -= 1713c431bb5Swentaoy watchdog_long_timeout % watchdog_resolution; 17277d1565cSae112802 watchdog_timeout -= 17377d1565cSae112802 watchdog_timeout % watchdog_resolution; 1743c431bb5Swentaoy 17577d1565cSae112802 /* 17677d1565cSae112802 * Cyclic need to be fired twice the frequency of regular 17777d1565cSae112802 * watchdog timeout. Pedantic here and setting cyclic 17877d1565cSae112802 * frequency to approximately 44% of watchdog_timeout. 17977d1565cSae112802 */ 18077d1565cSae112802 cyclic_interval = (watchdog_timeout >> 1) - (watchdog_timeout >> 4); 18177d1565cSae112802 /* 18277d1565cSae112802 * Note that regular timeout interval is in millisecond, 18377d1565cSae112802 * therefore to get cyclic interval in nanosecond need to 18477d1565cSae112802 * multiply by MICROSEC. 18577d1565cSae112802 */ 18677d1565cSae112802 cyclic_interval *= MICROSEC; 18777d1565cSae112802 18877d1565cSae112802 watchdog_cyclic_init(cyclic_interval); 189*fda7b194Swentaoy watchdog_initialized = 1; 190*fda7b194Swentaoy config_watchdog(watchdog_timeout, WDT_ON); 1913c431bb5Swentaoy } 1923c431bb5Swentaoy 1933c431bb5Swentaoy /* 19477d1565cSae112802 * Pat the watchdog timer periodically using the hypervisor API. 19577d1565cSae112802 * Regular pat occurs when the system runs normally. 19677d1565cSae112802 * Long pat is when system panics. 1973c431bb5Swentaoy */ 1983c431bb5Swentaoy void 1993c431bb5Swentaoy watchdog_pat() 2003c431bb5Swentaoy { 2013c431bb5Swentaoy if (watchdog_enabled && watchdog_activated) { 20277d1565cSae112802 if (panicstr) 20377d1565cSae112802 config_watchdog(watchdog_long_timeout, WDT_ON); 20477d1565cSae112802 else 20577d1565cSae112802 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 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 2243c431bb5Swentaoy watchdog_resume() 2253c431bb5Swentaoy { 2263c431bb5Swentaoy if (watchdog_enabled && !watchdog_activated) { 2273c431bb5Swentaoy if (panicstr) { 2283c431bb5Swentaoy config_watchdog(watchdog_long_timeout, WDT_ON); 2293c431bb5Swentaoy } else { 23077d1565cSae112802 config_watchdog(watchdog_timeout, WDT_ON); 2313c431bb5Swentaoy } 2323c431bb5Swentaoy } 2333c431bb5Swentaoy } 2343c431bb5Swentaoy 2353c431bb5Swentaoy void 2363c431bb5Swentaoy watchdog_clear() 2373c431bb5Swentaoy { 2383c431bb5Swentaoy if (watchdog_enabled && watchdog_activated) { 2393c431bb5Swentaoy config_watchdog(0, WDT_OFF); 2403c431bb5Swentaoy } 2413c431bb5Swentaoy } 2423c431bb5Swentaoy 2433c431bb5Swentaoy static void 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) { 2503c431bb5Swentaoy watchdog_activated = new_state; 2513c431bb5Swentaoy ret = hv_mach_set_watchdog(timeout, &time_remaining); 2523c431bb5Swentaoy if (ret != H_EOK) { 2533c431bb5Swentaoy cmn_err(CE_WARN, "Failed to operate on the watchdog. " 2543c431bb5Swentaoy "Error = 0x%lx", ret); 2553c431bb5Swentaoy watchdog_enabled = 0; 2563c431bb5Swentaoy } 2573c431bb5Swentaoy } 258*fda7b194Swentaoy } 25977d1565cSae112802 26077d1565cSae112802 /* 26177d1565cSae112802 * Once the watchdog cyclic is initialized, it won't be removed. 26277d1565cSae112802 * The only way to not add the watchdog cyclic is to disable the watchdog 26377d1565cSae112802 * by setting the watchdog_enabled to 0 in /etc/system file. 26477d1565cSae112802 */ 26577d1565cSae112802 static void 26677d1565cSae112802 watchdog_cyclic_init(hrtime_t wdt_cyclic_interval) 26777d1565cSae112802 { 26877d1565cSae112802 cyc_handler_t hdlr; 26977d1565cSae112802 cyc_time_t when; 27077d1565cSae112802 27177d1565cSae112802 hdlr.cyh_func = (cyc_func_t)watchdog_pat; 27277d1565cSae112802 hdlr.cyh_level = CY_HIGH_LEVEL; 27377d1565cSae112802 hdlr.cyh_arg = NULL; 27477d1565cSae112802 27577d1565cSae112802 when.cyt_when = 0; 27677d1565cSae112802 when.cyt_interval = wdt_cyclic_interval; 27777d1565cSae112802 27877d1565cSae112802 mutex_enter(&cpu_lock); 27977d1565cSae112802 (void) cyclic_add(&hdlr, &when); 28077d1565cSae112802 mutex_exit(&cpu_lock); 28177d1565cSae112802 } 282