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