1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2004 Poul-Henning Kamp 5 * Copyright (c) 2013 iXsystems.com, 6 * author: Alfred Perlstein <alfred@freebsd.org> 7 * 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer 15 * in this position and unchanged. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include "opt_ddb.h" 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/bus.h> 40 #include <sys/conf.h> 41 #include <sys/eventhandler.h> 42 #include <sys/kdb.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/module.h> 46 #include <sys/mutex.h> 47 #include <sys/sysctl.h> 48 #include <sys/syslog.h> 49 #include <sys/systm.h> 50 #include <sys/uio.h> 51 #include <sys/watchdog.h> 52 #include <machine/bus.h> 53 54 #include <sys/syscallsubr.h> /* kern_clock_gettime() */ 55 56 static int wd_set_pretimeout(int newtimeout, int disableiftoolong); 57 static void wd_timeout_cb(void *arg); 58 59 static struct callout wd_pretimeo_handle; 60 static int wd_pretimeout; 61 static int wd_pretimeout_act = WD_SOFT_LOG; 62 63 static struct callout wd_softtimeo_handle; 64 static int wd_softtimer; /* true = use softtimer instead of hardware 65 watchdog */ 66 static int wd_softtimeout_act = WD_SOFT_LOG; /* action for the software timeout */ 67 68 static struct cdev *wd_dev; 69 static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */ 70 static u_int wd_last_u_sysctl; /* last timeout value set by kern_do_pat */ 71 static u_int wd_last_u_sysctl_secs; /* wd_last_u in seconds */ 72 73 SYSCTL_NODE(_hw, OID_AUTO, watchdog, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 74 "Main watchdog device"); 75 SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u, CTLFLAG_RD, 76 &wd_last_u_sysctl, 0, "Watchdog last update time"); 77 SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u_secs, CTLFLAG_RD, 78 &wd_last_u_sysctl_secs, 0, "Watchdog last update time"); 79 80 static int wd_lastpat_valid = 0; 81 static time_t wd_lastpat = 0; /* when the watchdog was last patted */ 82 83 /* Hook for external software watchdog to register for use if needed */ 84 void (*wdog_software_attach)(void); 85 86 static void 87 pow2ns_to_ts(int pow2ns, struct timespec *ts) 88 { 89 uint64_t ns; 90 91 ns = 1ULL << pow2ns; 92 ts->tv_sec = ns / 1000000000ULL; 93 ts->tv_nsec = ns % 1000000000ULL; 94 } 95 96 static int 97 pow2ns_to_ticks(int pow2ns) 98 { 99 struct timeval tv; 100 struct timespec ts; 101 102 pow2ns_to_ts(pow2ns, &ts); 103 TIMESPEC_TO_TIMEVAL(&tv, &ts); 104 return (tvtohz(&tv)); 105 } 106 107 static int 108 seconds_to_pow2ns(int seconds) 109 { 110 uint64_t power; 111 uint64_t ns; 112 uint64_t shifted; 113 114 ns = ((uint64_t)seconds) * 1000000000ULL; 115 power = flsll(ns); 116 shifted = 1ULL << power; 117 if (shifted <= ns) { 118 power++; 119 } 120 return (power); 121 } 122 123 124 int 125 wdog_kern_pat(u_int utim) 126 { 127 int error; 128 static int first = 1; 129 130 if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0) 131 return (EINVAL); 132 133 if ((utim & WD_LASTVAL) != 0) { 134 /* 135 * if WD_LASTVAL is set, fill in the bits for timeout 136 * from the saved value in wd_last_u. 137 */ 138 MPASS((wd_last_u & ~WD_INTERVAL) == 0); 139 utim &= ~WD_LASTVAL; 140 utim |= wd_last_u; 141 } else { 142 /* 143 * Otherwise save the new interval. 144 * This can be zero (to disable the watchdog) 145 */ 146 wd_last_u = (utim & WD_INTERVAL); 147 wd_last_u_sysctl = wd_last_u; 148 wd_last_u_sysctl_secs = pow2ns_to_ticks(wd_last_u) / hz; 149 } 150 if ((utim & WD_INTERVAL) == WD_TO_NEVER) { 151 utim = 0; 152 153 /* Assume all is well; watchdog signals failure. */ 154 error = 0; 155 } else { 156 /* Assume no watchdog available; watchdog flags success */ 157 error = EOPNOTSUPP; 158 } 159 if (wd_softtimer) { 160 if (utim == 0) { 161 callout_stop(&wd_softtimeo_handle); 162 } else { 163 (void) callout_reset(&wd_softtimeo_handle, 164 pow2ns_to_ticks(utim), wd_timeout_cb, "soft"); 165 } 166 error = 0; 167 } else { 168 EVENTHANDLER_INVOKE(watchdog_list, utim, &error); 169 } 170 /* 171 * If we no hardware watchdog responded, we have not tried to 172 * attach an external software watchdog, and one is available, 173 * attach it now and retry. 174 */ 175 if (error == EOPNOTSUPP && first && *wdog_software_attach != NULL) { 176 (*wdog_software_attach)(); 177 EVENTHANDLER_INVOKE(watchdog_list, utim, &error); 178 } 179 first = 0; 180 181 wd_set_pretimeout(wd_pretimeout, true); 182 /* 183 * If we were able to arm/strobe the watchdog, then 184 * update the last time it was strobed for WDIOC_GETTIMELEFT 185 */ 186 if (!error) { 187 struct timespec ts; 188 189 error = kern_clock_gettime(curthread /* XXX */, 190 CLOCK_MONOTONIC_FAST, &ts); 191 if (!error) { 192 wd_lastpat = ts.tv_sec; 193 wd_lastpat_valid = 1; 194 } 195 } 196 return (error); 197 } 198 199 static int 200 wd_valid_act(int act) 201 { 202 203 if ((act & ~(WD_SOFT_MASK)) != 0) 204 return false; 205 return true; 206 } 207 208 static int 209 wd_ioctl_patpat(caddr_t data) 210 { 211 u_int u; 212 213 u = *(u_int *)data; 214 if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL)) 215 return (EINVAL); 216 if ((u & (WD_ACTIVE | WD_PASSIVE)) == (WD_ACTIVE | WD_PASSIVE)) 217 return (EINVAL); 218 if ((u & (WD_ACTIVE | WD_PASSIVE)) == 0 && ((u & WD_INTERVAL) > 0 || 219 (u & WD_LASTVAL) != 0)) 220 return (EINVAL); 221 if (u & WD_PASSIVE) 222 return (ENOSYS); /* XXX Not implemented yet */ 223 u &= ~(WD_ACTIVE | WD_PASSIVE); 224 225 return (wdog_kern_pat(u)); 226 } 227 228 static int 229 wd_get_time_left(struct thread *td, time_t *remainp) 230 { 231 struct timespec ts; 232 int error; 233 234 error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts); 235 if (error) 236 return (error); 237 if (!wd_lastpat_valid) 238 return (ENOENT); 239 *remainp = ts.tv_sec - wd_lastpat; 240 return (0); 241 } 242 243 static void 244 wd_timeout_cb(void *arg) 245 { 246 const char *type = arg; 247 248 #ifdef DDB 249 if ((wd_pretimeout_act & WD_SOFT_DDB)) { 250 char kdb_why[80]; 251 snprintf(kdb_why, sizeof(kdb_why), "watchdog %s-timeout", type); 252 kdb_backtrace(); 253 kdb_enter(KDB_WHY_WATCHDOG, kdb_why); 254 } 255 #endif 256 if ((wd_pretimeout_act & WD_SOFT_LOG)) 257 log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG\n", type); 258 if ((wd_pretimeout_act & WD_SOFT_PRINTF)) 259 printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type); 260 if ((wd_pretimeout_act & WD_SOFT_PANIC)) 261 panic("watchdog %s-timeout, WD_SOFT_PANIC set", type); 262 } 263 264 /* 265 * Called to manage timeouts. 266 * newtimeout needs to be in the range of 0 to actual watchdog timeout. 267 * if 0, we disable the pre-timeout. 268 * otherwise we set the pre-timeout provided it's not greater than the 269 * current actual watchdog timeout. 270 */ 271 static int 272 wd_set_pretimeout(int newtimeout, int disableiftoolong) 273 { 274 u_int utime; 275 struct timespec utime_ts; 276 int timeout_ticks; 277 278 utime = wdog_kern_last_timeout(); 279 pow2ns_to_ts(utime, &utime_ts); 280 /* do not permit a pre-timeout >= than the timeout. */ 281 if (newtimeout >= utime_ts.tv_sec) { 282 /* 283 * If 'disableiftoolong' then just fall through 284 * so as to disable the pre-watchdog 285 */ 286 if (disableiftoolong) 287 newtimeout = 0; 288 else 289 return EINVAL; 290 } 291 292 /* disable the pre-timeout */ 293 if (newtimeout == 0) { 294 wd_pretimeout = 0; 295 callout_stop(&wd_pretimeo_handle); 296 return 0; 297 } 298 299 timeout_ticks = pow2ns_to_ticks(utime) - (hz*newtimeout); 300 #if 0 301 printf("wd_set_pretimeout: " 302 "newtimeout: %d, " 303 "utime: %d -> utime_ticks: %d, " 304 "hz*newtimeout: %d, " 305 "timeout_ticks: %d -> sec: %d\n", 306 newtimeout, 307 utime, pow2ns_to_ticks(utime), 308 hz*newtimeout, 309 timeout_ticks, timeout_ticks / hz); 310 #endif 311 312 /* We determined the value is sane, so reset the callout */ 313 (void) callout_reset(&wd_pretimeo_handle, 314 timeout_ticks, wd_timeout_cb, "pre"); 315 wd_pretimeout = newtimeout; 316 return 0; 317 } 318 319 static int 320 wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, 321 int flags __unused, struct thread *td) 322 { 323 u_int u; 324 time_t timeleft; 325 int error; 326 327 error = 0; 328 329 switch (cmd) { 330 case WDIOC_SETSOFT: 331 u = *(int *)data; 332 /* do nothing? */ 333 if (u == wd_softtimer) 334 break; 335 /* If there is a pending timeout disallow this ioctl */ 336 if (wd_last_u != 0) { 337 error = EINVAL; 338 break; 339 } 340 wd_softtimer = u; 341 break; 342 case WDIOC_SETSOFTTIMEOUTACT: 343 u = *(int *)data; 344 if (wd_valid_act(u)) { 345 wd_softtimeout_act = u; 346 } else { 347 error = EINVAL; 348 } 349 break; 350 case WDIOC_SETPRETIMEOUTACT: 351 u = *(int *)data; 352 if (wd_valid_act(u)) { 353 wd_pretimeout_act = u; 354 } else { 355 error = EINVAL; 356 } 357 break; 358 case WDIOC_GETPRETIMEOUT: 359 *(int *)data = (int)wd_pretimeout; 360 break; 361 case WDIOC_SETPRETIMEOUT: 362 error = wd_set_pretimeout(*(int *)data, false); 363 break; 364 case WDIOC_GETTIMELEFT: 365 error = wd_get_time_left(td, &timeleft); 366 if (error) 367 break; 368 *(int *)data = (int)timeleft; 369 break; 370 case WDIOC_SETTIMEOUT: 371 u = *(u_int *)data; 372 error = wdog_kern_pat(seconds_to_pow2ns(u)); 373 break; 374 case WDIOC_GETTIMEOUT: 375 u = wdog_kern_last_timeout(); 376 *(u_int *)data = u; 377 break; 378 case WDIOCPATPAT: 379 error = wd_ioctl_patpat(data); 380 break; 381 default: 382 error = ENOIOCTL; 383 break; 384 } 385 return (error); 386 } 387 388 /* 389 * Return the last timeout set, this is NOT the seconds from NOW until timeout, 390 * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT. 391 */ 392 u_int 393 wdog_kern_last_timeout(void) 394 { 395 396 return (wd_last_u); 397 } 398 399 static struct cdevsw wd_cdevsw = { 400 .d_version = D_VERSION, 401 .d_ioctl = wd_ioctl, 402 .d_name = "watchdog", 403 }; 404 405 static int 406 watchdog_modevent(module_t mod __unused, int type, void *data __unused) 407 { 408 switch(type) { 409 case MOD_LOAD: 410 callout_init(&wd_pretimeo_handle, 1); 411 callout_init(&wd_softtimeo_handle, 1); 412 wd_dev = make_dev(&wd_cdevsw, 0, 413 UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG); 414 return 0; 415 case MOD_UNLOAD: 416 callout_stop(&wd_pretimeo_handle); 417 callout_stop(&wd_softtimeo_handle); 418 callout_drain(&wd_pretimeo_handle); 419 callout_drain(&wd_softtimeo_handle); 420 destroy_dev(wd_dev); 421 return 0; 422 case MOD_SHUTDOWN: 423 return 0; 424 default: 425 return EOPNOTSUPP; 426 } 427 } 428 429 DEV_MODULE(watchdog, watchdog_modevent, NULL); 430