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