1*3c431bb5Swentaoy /* 2*3c431bb5Swentaoy * CDDL HEADER START 3*3c431bb5Swentaoy * 4*3c431bb5Swentaoy * The contents of this file are subject to the terms of the 5*3c431bb5Swentaoy * Common Development and Distribution License (the "License"). 6*3c431bb5Swentaoy * You may not use this file except in compliance with the License. 7*3c431bb5Swentaoy * 8*3c431bb5Swentaoy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*3c431bb5Swentaoy * or http://www.opensolaris.org/os/licensing. 10*3c431bb5Swentaoy * See the License for the specific language governing permissions 11*3c431bb5Swentaoy * and limitations under the License. 12*3c431bb5Swentaoy * 13*3c431bb5Swentaoy * When distributing Covered Code, include this CDDL HEADER in each 14*3c431bb5Swentaoy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*3c431bb5Swentaoy * If applicable, add the following below this CDDL HEADER, with the 16*3c431bb5Swentaoy * fields enclosed by brackets "[]" replaced with your own identifying 17*3c431bb5Swentaoy * information: Portions Copyright [yyyy] [name of copyright owner] 18*3c431bb5Swentaoy * 19*3c431bb5Swentaoy * CDDL HEADER END 20*3c431bb5Swentaoy */ 21*3c431bb5Swentaoy /* 22*3c431bb5Swentaoy * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*3c431bb5Swentaoy * Use is subject to license terms. 24*3c431bb5Swentaoy */ 25*3c431bb5Swentaoy 26*3c431bb5Swentaoy #pragma ident "%Z%%M% %I% %E% SMI" 27*3c431bb5Swentaoy 28*3c431bb5Swentaoy #include <sys/types.h> 29*3c431bb5Swentaoy #include <sys/hsvc.h> 30*3c431bb5Swentaoy #include <sys/wdt.h> 31*3c431bb5Swentaoy #include <sys/cmn_err.h> 32*3c431bb5Swentaoy #include <sys/kmem.h> 33*3c431bb5Swentaoy #include <sys/systm.h> 34*3c431bb5Swentaoy #include <sys/sysmacros.h> 35*3c431bb5Swentaoy #include <sys/hypervisor_api.h> 36*3c431bb5Swentaoy #include <sys/mach_descrip.h> 37*3c431bb5Swentaoy #include <sys/mdesc.h> 38*3c431bb5Swentaoy 39*3c431bb5Swentaoy #define WDT_ON 1 40*3c431bb5Swentaoy #define WDT_OFF 0 41*3c431bb5Swentaoy #define WDT_DEFAULT_RESOLUTION 10 /* 10 milliseconds */ 42*3c431bb5Swentaoy /* 43*3c431bb5Swentaoy * MILLISEC defines the number of milliseconds in a second. 44*3c431bb5Swentaoy */ 45*3c431bb5Swentaoy #define WDT_MAX_RESOLUTION (1 * MILLISEC) /* 1 second */ 46*3c431bb5Swentaoy #define WDT_REGULAR_TIMEOUT (10 * MILLISEC) /* 10 seconds */ 47*3c431bb5Swentaoy #define WDT_LONG_TIMEOUT (60 * MILLISEC) /* 60 seconds */ 48*3c431bb5Swentaoy #define WDT_MIN_COREAPI_MAJOR 1 49*3c431bb5Swentaoy #define WDT_MIN_COREAPI_MINOR 1 50*3c431bb5Swentaoy /* 51*3c431bb5Swentaoy * The ratio to calculate the watchdog timer pat interval. 52*3c431bb5Swentaoy */ 53*3c431bb5Swentaoy #define WDT_PAT_INTERVAL(x) ((x) / 2) 54*3c431bb5Swentaoy 55*3c431bb5Swentaoy int watchdog_enabled = 1; 56*3c431bb5Swentaoy 57*3c431bb5Swentaoy static void set_watchdog_pat_intervals(void); 58*3c431bb5Swentaoy static void config_watchdog(uint64_t, int); 59*3c431bb5Swentaoy 60*3c431bb5Swentaoy /* 61*3c431bb5Swentaoy * Flag used to pat/suspend/resume the watchdog timer. 62*3c431bb5Swentaoy */ 63*3c431bb5Swentaoy static int watchdog_activated = WDT_OFF; 64*3c431bb5Swentaoy static uint64_t watchdog_regular_timeout = WDT_REGULAR_TIMEOUT; 65*3c431bb5Swentaoy static uint64_t watchdog_long_timeout = 0; 66*3c431bb5Swentaoy static uint64_t watchdog_resolution = WDT_DEFAULT_RESOLUTION; 67*3c431bb5Swentaoy static int64_t watchdog_last_pat = 0; /* The time of last pat. */ 68*3c431bb5Swentaoy static int64_t last_pat_interval = 0; /* The pat interval of last pat. */ 69*3c431bb5Swentaoy static int64_t watchdog_long_pat_interval = 0; 70*3c431bb5Swentaoy static int64_t watchdog_regular_pat_interval = 0; 71*3c431bb5Swentaoy 72*3c431bb5Swentaoy void 73*3c431bb5Swentaoy watchdog_init(void) 74*3c431bb5Swentaoy { 75*3c431bb5Swentaoy int num_nodes; 76*3c431bb5Swentaoy int nplat; 77*3c431bb5Swentaoy md_t *mdp; 78*3c431bb5Swentaoy mde_cookie_t *listp = NULL; 79*3c431bb5Swentaoy int listsz; 80*3c431bb5Swentaoy uint64_t major; 81*3c431bb5Swentaoy uint64_t minor; 82*3c431bb5Swentaoy uint64_t watchdog_max_timeout; 83*3c431bb5Swentaoy 84*3c431bb5Swentaoy if (!watchdog_enabled) { 85*3c431bb5Swentaoy return; 86*3c431bb5Swentaoy } 87*3c431bb5Swentaoy 88*3c431bb5Swentaoy if (hsvc_version(HSVC_GROUP_CORE, &major, &minor) != 0 || 89*3c431bb5Swentaoy major != WDT_MIN_COREAPI_MAJOR || 90*3c431bb5Swentaoy minor < WDT_MIN_COREAPI_MINOR) { 91*3c431bb5Swentaoy cmn_err(CE_NOTE, "Disabling watchdog as watchdog services are " 92*3c431bb5Swentaoy "not available\n"); 93*3c431bb5Swentaoy watchdog_enabled = 0; 94*3c431bb5Swentaoy return; 95*3c431bb5Swentaoy } 96*3c431bb5Swentaoy 97*3c431bb5Swentaoy /* 98*3c431bb5Swentaoy * Get the watchdog-max-timeout and watchdog-resolution MD properties. 99*3c431bb5Swentaoy */ 100*3c431bb5Swentaoy if ((mdp = md_get_handle()) == NULL) { 101*3c431bb5Swentaoy cmn_err(CE_WARN, "Unable to initialize machine description, " 102*3c431bb5Swentaoy "watchdog is disabled."); 103*3c431bb5Swentaoy watchdog_enabled = 0; 104*3c431bb5Swentaoy return; 105*3c431bb5Swentaoy } 106*3c431bb5Swentaoy 107*3c431bb5Swentaoy num_nodes = md_node_count(mdp); 108*3c431bb5Swentaoy ASSERT(num_nodes > 0); 109*3c431bb5Swentaoy 110*3c431bb5Swentaoy listsz = num_nodes * sizeof (mde_cookie_t); 111*3c431bb5Swentaoy listp = kmem_zalloc(listsz, KM_SLEEP); 112*3c431bb5Swentaoy 113*3c431bb5Swentaoy nplat = md_scan_dag(mdp, md_root_node(mdp), 114*3c431bb5Swentaoy md_find_name(mdp, "platform"), md_find_name(mdp, "fwd"), listp); 115*3c431bb5Swentaoy 116*3c431bb5Swentaoy ASSERT(nplat == 1); 117*3c431bb5Swentaoy 118*3c431bb5Swentaoy if (md_get_prop_val(mdp, listp[0], "watchdog-max-timeout", 119*3c431bb5Swentaoy &watchdog_max_timeout)) { 120*3c431bb5Swentaoy cmn_err(CE_WARN, "Cannot read watchdog-max-timeout, watchdog " 121*3c431bb5Swentaoy "is disabled."); 122*3c431bb5Swentaoy watchdog_enabled = 0; 123*3c431bb5Swentaoy kmem_free(listp, listsz); 124*3c431bb5Swentaoy (void) md_fini_handle(mdp); 125*3c431bb5Swentaoy return; 126*3c431bb5Swentaoy } 127*3c431bb5Swentaoy 128*3c431bb5Swentaoy if (watchdog_max_timeout < WDT_REGULAR_TIMEOUT) { 129*3c431bb5Swentaoy cmn_err(CE_WARN, "Invalid watchdog-max-timeout value, watchdog " 130*3c431bb5Swentaoy "is disabled."); 131*3c431bb5Swentaoy watchdog_enabled = 0; 132*3c431bb5Swentaoy kmem_free(listp, listsz); 133*3c431bb5Swentaoy (void) md_fini_handle(mdp); 134*3c431bb5Swentaoy return; 135*3c431bb5Swentaoy } 136*3c431bb5Swentaoy 137*3c431bb5Swentaoy if (md_get_prop_val(mdp, listp[0], "watchdog-resolution", 138*3c431bb5Swentaoy &watchdog_resolution)) { 139*3c431bb5Swentaoy cmn_err(CE_WARN, "Cannot read watchdog-resolution, watchdog " 140*3c431bb5Swentaoy "is disabled."); 141*3c431bb5Swentaoy watchdog_enabled = 0; 142*3c431bb5Swentaoy kmem_free(listp, listsz); 143*3c431bb5Swentaoy (void) md_fini_handle(mdp); 144*3c431bb5Swentaoy return; 145*3c431bb5Swentaoy } 146*3c431bb5Swentaoy 147*3c431bb5Swentaoy if (watchdog_resolution == 0 || 148*3c431bb5Swentaoy watchdog_resolution > WDT_MAX_RESOLUTION) { 149*3c431bb5Swentaoy watchdog_resolution = WDT_DEFAULT_RESOLUTION; 150*3c431bb5Swentaoy } 151*3c431bb5Swentaoy kmem_free(listp, listsz); 152*3c431bb5Swentaoy (void) md_fini_handle(mdp); 153*3c431bb5Swentaoy 154*3c431bb5Swentaoy watchdog_long_timeout = MIN(WDT_LONG_TIMEOUT, watchdog_max_timeout); 155*3c431bb5Swentaoy 156*3c431bb5Swentaoy /* 157*3c431bb5Swentaoy * round the timeout to the nearest smaller value. 158*3c431bb5Swentaoy */ 159*3c431bb5Swentaoy watchdog_long_timeout -= 160*3c431bb5Swentaoy watchdog_long_timeout % watchdog_resolution; 161*3c431bb5Swentaoy watchdog_regular_timeout -= 162*3c431bb5Swentaoy watchdog_regular_timeout % watchdog_resolution; 163*3c431bb5Swentaoy set_watchdog_pat_intervals(); 164*3c431bb5Swentaoy 165*3c431bb5Swentaoy config_watchdog(watchdog_regular_timeout, WDT_ON); 166*3c431bb5Swentaoy } 167*3c431bb5Swentaoy 168*3c431bb5Swentaoy /* 169*3c431bb5Swentaoy * Pat the watchdog timer periodically, for regular pat in tod_get when 170*3c431bb5Swentaoy * the kernel runs normally and long pat in deadman when panicking. 171*3c431bb5Swentaoy */ 172*3c431bb5Swentaoy void 173*3c431bb5Swentaoy watchdog_pat() 174*3c431bb5Swentaoy { 175*3c431bb5Swentaoy int64_t pat_interval; 176*3c431bb5Swentaoy int64_t current_lbolt64; 177*3c431bb5Swentaoy uint64_t timeout; 178*3c431bb5Swentaoy 179*3c431bb5Swentaoy if (watchdog_enabled && watchdog_activated) { 180*3c431bb5Swentaoy if (panicstr) { 181*3c431bb5Swentaoy /* 182*3c431bb5Swentaoy * long timeout is only used while panicking. 183*3c431bb5Swentaoy */ 184*3c431bb5Swentaoy timeout = watchdog_long_timeout; 185*3c431bb5Swentaoy pat_interval = watchdog_long_pat_interval; 186*3c431bb5Swentaoy } else { 187*3c431bb5Swentaoy timeout = watchdog_regular_timeout; 188*3c431bb5Swentaoy pat_interval = watchdog_regular_pat_interval; 189*3c431bb5Swentaoy } 190*3c431bb5Swentaoy 191*3c431bb5Swentaoy current_lbolt64 = lbolt64; 192*3c431bb5Swentaoy 193*3c431bb5Swentaoy if ((current_lbolt64 - watchdog_last_pat) 194*3c431bb5Swentaoy >= last_pat_interval) { 195*3c431bb5Swentaoy /* 196*3c431bb5Swentaoy * Pat the watchdog via hv api: 197*3c431bb5Swentaoy */ 198*3c431bb5Swentaoy config_watchdog(timeout, WDT_ON); 199*3c431bb5Swentaoy 200*3c431bb5Swentaoy last_pat_interval = pat_interval; 201*3c431bb5Swentaoy watchdog_last_pat = current_lbolt64; 202*3c431bb5Swentaoy } 203*3c431bb5Swentaoy } 204*3c431bb5Swentaoy } 205*3c431bb5Swentaoy 206*3c431bb5Swentaoy /* 207*3c431bb5Swentaoy * We don't save/restore the remaining watchdog timeout time at present. 208*3c431bb5Swentaoy */ 209*3c431bb5Swentaoy void 210*3c431bb5Swentaoy watchdog_suspend() 211*3c431bb5Swentaoy { 212*3c431bb5Swentaoy if (watchdog_enabled && watchdog_activated) { 213*3c431bb5Swentaoy config_watchdog(0, WDT_OFF); 214*3c431bb5Swentaoy } 215*3c431bb5Swentaoy } 216*3c431bb5Swentaoy 217*3c431bb5Swentaoy /* 218*3c431bb5Swentaoy * We don't save/restore the remaining watchdog timeout time at present. 219*3c431bb5Swentaoy */ 220*3c431bb5Swentaoy void 221*3c431bb5Swentaoy watchdog_resume() 222*3c431bb5Swentaoy { 223*3c431bb5Swentaoy if (watchdog_enabled && !watchdog_activated) { 224*3c431bb5Swentaoy if (panicstr) { 225*3c431bb5Swentaoy config_watchdog(watchdog_long_timeout, WDT_ON); 226*3c431bb5Swentaoy } else { 227*3c431bb5Swentaoy config_watchdog(watchdog_regular_timeout, WDT_ON); 228*3c431bb5Swentaoy } 229*3c431bb5Swentaoy } 230*3c431bb5Swentaoy } 231*3c431bb5Swentaoy 232*3c431bb5Swentaoy void 233*3c431bb5Swentaoy watchdog_clear() 234*3c431bb5Swentaoy { 235*3c431bb5Swentaoy if (watchdog_enabled && watchdog_activated) { 236*3c431bb5Swentaoy config_watchdog(0, WDT_OFF); 237*3c431bb5Swentaoy } 238*3c431bb5Swentaoy } 239*3c431bb5Swentaoy 240*3c431bb5Swentaoy /* 241*3c431bb5Swentaoy * Set the pat intervals for both regular (when Solaris is running), 242*3c431bb5Swentaoy * and long timeout (i.e., when panicking) cases. 243*3c431bb5Swentaoy */ 244*3c431bb5Swentaoy static void 245*3c431bb5Swentaoy set_watchdog_pat_intervals(void) 246*3c431bb5Swentaoy { 247*3c431bb5Swentaoy watchdog_regular_pat_interval = 248*3c431bb5Swentaoy MSEC_TO_TICK(WDT_PAT_INTERVAL(watchdog_regular_timeout)); 249*3c431bb5Swentaoy watchdog_long_pat_interval = 250*3c431bb5Swentaoy MSEC_TO_TICK(WDT_PAT_INTERVAL(watchdog_long_timeout)); 251*3c431bb5Swentaoy } 252*3c431bb5Swentaoy 253*3c431bb5Swentaoy static void 254*3c431bb5Swentaoy config_watchdog(uint64_t timeout, int new_state) 255*3c431bb5Swentaoy { 256*3c431bb5Swentaoy uint64_t time_remaining; 257*3c431bb5Swentaoy uint64_t ret; 258*3c431bb5Swentaoy 259*3c431bb5Swentaoy watchdog_activated = new_state; 260*3c431bb5Swentaoy ret = hv_mach_set_watchdog(timeout, &time_remaining); 261*3c431bb5Swentaoy if (ret != H_EOK) { 262*3c431bb5Swentaoy cmn_err(CE_WARN, "Failed to operate on the watchdog. " 263*3c431bb5Swentaoy "Error = 0x%lx", ret); 264*3c431bb5Swentaoy watchdog_enabled = 0; 265*3c431bb5Swentaoy } 266*3c431bb5Swentaoy } 267