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