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