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 int 124 wdog_kern_pat(u_int utim) 125 { 126 int error; 127 static int first = 1; 128 129 if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0) 130 return (EINVAL); 131 132 if ((utim & WD_LASTVAL) != 0) { 133 /* 134 * if WD_LASTVAL is set, fill in the bits for timeout 135 * from the saved value in wd_last_u. 136 */ 137 MPASS((wd_last_u & ~WD_INTERVAL) == 0); 138 utim &= ~WD_LASTVAL; 139 utim |= wd_last_u; 140 } else { 141 /* 142 * Otherwise save the new interval. 143 * This can be zero (to disable the watchdog) 144 */ 145 wd_last_u = (utim & WD_INTERVAL); 146 wd_last_u_sysctl = wd_last_u; 147 wd_last_u_sysctl_secs = pow2ns_to_ticks(wd_last_u) / hz; 148 } 149 if ((utim & WD_INTERVAL) == WD_TO_NEVER) { 150 utim = 0; 151 152 /* Assume all is well; watchdog signals failure. */ 153 error = 0; 154 } else { 155 /* Assume no watchdog available; watchdog flags success */ 156 error = EOPNOTSUPP; 157 } 158 if (wd_softtimer) { 159 if (utim == 0) { 160 callout_stop(&wd_softtimeo_handle); 161 } else { 162 (void) callout_reset(&wd_softtimeo_handle, 163 pow2ns_to_ticks(utim), wd_timeout_cb, "soft"); 164 } 165 error = 0; 166 } else { 167 EVENTHANDLER_INVOKE(watchdog_list, utim, &error); 168 } 169 /* 170 * If we no hardware watchdog responded, we have not tried to 171 * attach an external software watchdog, and one is available, 172 * attach it now and retry. 173 */ 174 if (error == EOPNOTSUPP && first && *wdog_software_attach != NULL) { 175 (*wdog_software_attach)(); 176 EVENTHANDLER_INVOKE(watchdog_list, utim, &error); 177 } 178 first = 0; 179 180 wd_set_pretimeout(wd_pretimeout, true); 181 /* 182 * If we were able to arm/strobe the watchdog, then 183 * update the last time it was strobed for WDIOC_GETTIMELEFT 184 */ 185 if (!error) { 186 struct timespec ts; 187 188 error = kern_clock_gettime(curthread /* XXX */, 189 CLOCK_MONOTONIC_FAST, &ts); 190 if (!error) { 191 wd_lastpat = ts.tv_sec; 192 wd_lastpat_valid = 1; 193 } 194 } 195 return (error); 196 } 197 198 static int 199 wd_valid_act(int act) 200 { 201 202 if ((act & ~(WD_SOFT_MASK)) != 0) 203 return false; 204 return true; 205 } 206 207 static int 208 wd_ioctl_patpat(caddr_t data) 209 { 210 u_int u; 211 212 u = *(u_int *)data; 213 if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL)) 214 return (EINVAL); 215 if ((u & (WD_ACTIVE | WD_PASSIVE)) == (WD_ACTIVE | WD_PASSIVE)) 216 return (EINVAL); 217 if ((u & (WD_ACTIVE | WD_PASSIVE)) == 0 && ((u & WD_INTERVAL) > 0 || 218 (u & WD_LASTVAL) != 0)) 219 return (EINVAL); 220 if (u & WD_PASSIVE) 221 return (ENOSYS); /* XXX Not implemented yet */ 222 u &= ~(WD_ACTIVE | WD_PASSIVE); 223 224 return (wdog_kern_pat(u)); 225 } 226 227 static int 228 wd_get_time_left(struct thread *td, time_t *remainp) 229 { 230 struct timespec ts; 231 int error; 232 233 error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts); 234 if (error) 235 return (error); 236 if (!wd_lastpat_valid) 237 return (ENOENT); 238 *remainp = ts.tv_sec - wd_lastpat; 239 return (0); 240 } 241 242 static void 243 wd_timeout_cb(void *arg) 244 { 245 const char *type = arg; 246 247 #ifdef DDB 248 if ((wd_pretimeout_act & WD_SOFT_DDB)) { 249 char kdb_why[80]; 250 snprintf(kdb_why, sizeof(kdb_why), "watchdog %s-timeout", type); 251 kdb_backtrace(); 252 kdb_enter(KDB_WHY_WATCHDOG, kdb_why); 253 } 254 #endif 255 if ((wd_pretimeout_act & WD_SOFT_LOG)) 256 log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG\n", type); 257 if ((wd_pretimeout_act & WD_SOFT_PRINTF)) 258 printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type); 259 if ((wd_pretimeout_act & WD_SOFT_PANIC)) 260 panic("watchdog %s-timeout, WD_SOFT_PANIC set", type); 261 } 262 263 /* 264 * Called to manage timeouts. 265 * newtimeout needs to be in the range of 0 to actual watchdog timeout. 266 * if 0, we disable the pre-timeout. 267 * otherwise we set the pre-timeout provided it's not greater than the 268 * current actual watchdog timeout. 269 */ 270 static int 271 wd_set_pretimeout(int newtimeout, int disableiftoolong) 272 { 273 u_int utime; 274 struct timespec utime_ts; 275 int timeout_ticks; 276 277 utime = wdog_kern_last_timeout(); 278 pow2ns_to_ts(utime, &utime_ts); 279 /* do not permit a pre-timeout >= than the timeout. */ 280 if (newtimeout >= utime_ts.tv_sec) { 281 /* 282 * If 'disableiftoolong' then just fall through 283 * so as to disable the pre-watchdog 284 */ 285 if (disableiftoolong) 286 newtimeout = 0; 287 else 288 return EINVAL; 289 } 290 291 /* disable the pre-timeout */ 292 if (newtimeout == 0) { 293 wd_pretimeout = 0; 294 callout_stop(&wd_pretimeo_handle); 295 return 0; 296 } 297 298 timeout_ticks = pow2ns_to_ticks(utime) - (hz*newtimeout); 299 #if 0 300 printf("wd_set_pretimeout: " 301 "newtimeout: %d, " 302 "utime: %d -> utime_ticks: %d, " 303 "hz*newtimeout: %d, " 304 "timeout_ticks: %d -> sec: %d\n", 305 newtimeout, 306 utime, pow2ns_to_ticks(utime), 307 hz*newtimeout, 308 timeout_ticks, timeout_ticks / hz); 309 #endif 310 311 /* We determined the value is sane, so reset the callout */ 312 (void) callout_reset(&wd_pretimeo_handle, 313 timeout_ticks, wd_timeout_cb, "pre"); 314 wd_pretimeout = newtimeout; 315 return 0; 316 } 317 318 static int 319 wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, 320 int flags __unused, struct thread *td) 321 { 322 u_int u; 323 time_t timeleft; 324 int error; 325 326 error = 0; 327 328 switch (cmd) { 329 case WDIOC_SETSOFT: 330 u = *(int *)data; 331 /* do nothing? */ 332 if (u == wd_softtimer) 333 break; 334 /* If there is a pending timeout disallow this ioctl */ 335 if (wd_last_u != 0) { 336 error = EINVAL; 337 break; 338 } 339 wd_softtimer = u; 340 break; 341 case WDIOC_SETSOFTTIMEOUTACT: 342 u = *(int *)data; 343 if (wd_valid_act(u)) { 344 wd_softtimeout_act = u; 345 } else { 346 error = EINVAL; 347 } 348 break; 349 case WDIOC_SETPRETIMEOUTACT: 350 u = *(int *)data; 351 if (wd_valid_act(u)) { 352 wd_pretimeout_act = u; 353 } else { 354 error = EINVAL; 355 } 356 break; 357 case WDIOC_GETPRETIMEOUT: 358 *(int *)data = (int)wd_pretimeout; 359 break; 360 case WDIOC_SETPRETIMEOUT: 361 error = wd_set_pretimeout(*(int *)data, false); 362 break; 363 case WDIOC_GETTIMELEFT: 364 error = wd_get_time_left(td, &timeleft); 365 if (error) 366 break; 367 *(int *)data = (int)timeleft; 368 break; 369 case WDIOC_SETTIMEOUT: 370 u = *(u_int *)data; 371 error = wdog_kern_pat(seconds_to_pow2ns(u)); 372 break; 373 case WDIOC_GETTIMEOUT: 374 u = wdog_kern_last_timeout(); 375 *(u_int *)data = u; 376 break; 377 case WDIOCPATPAT: 378 error = wd_ioctl_patpat(data); 379 break; 380 default: 381 error = ENOIOCTL; 382 break; 383 } 384 return (error); 385 } 386 387 /* 388 * Return the last timeout set, this is NOT the seconds from NOW until timeout, 389 * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT. 390 */ 391 u_int 392 wdog_kern_last_timeout(void) 393 { 394 395 return (wd_last_u); 396 } 397 398 static struct cdevsw wd_cdevsw = { 399 .d_version = D_VERSION, 400 .d_ioctl = wd_ioctl, 401 .d_name = "watchdog", 402 }; 403 404 static int 405 watchdog_modevent(module_t mod __unused, int type, void *data __unused) 406 { 407 switch(type) { 408 case MOD_LOAD: 409 callout_init(&wd_pretimeo_handle, 1); 410 callout_init(&wd_softtimeo_handle, 1); 411 wd_dev = make_dev(&wd_cdevsw, 0, 412 UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG); 413 return 0; 414 case MOD_UNLOAD: 415 callout_stop(&wd_pretimeo_handle); 416 callout_stop(&wd_softtimeo_handle); 417 callout_drain(&wd_pretimeo_handle); 418 callout_drain(&wd_softtimeo_handle); 419 destroy_dev(wd_dev); 420 return 0; 421 case MOD_SHUTDOWN: 422 return 0; 423 default: 424 return EOPNOTSUPP; 425 } 426 } 427 428 DEV_MODULE(watchdog, watchdog_modevent, NULL); 429