watchdog.c revision 279413
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: head/sys/dev/watchdog/watchdog.c 279413 2015-02-28 22:37:48Z rstone $");
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
53static int wd_set_pretimeout(int newtimeout, int disableiftoolong);
54static void wd_timeout_cb(void *arg);
55
56static struct callout wd_pretimeo_handle;
57static int wd_pretimeout;
58static int wd_pretimeout_act = WD_SOFT_LOG;
59
60static struct callout wd_softtimeo_handle;
61static int wd_softtimer;	/* true = use softtimer instead of hardware
62				   watchdog */
63static int wd_softtimeout_act = WD_SOFT_LOG;	/* action for the software timeout */
64
65static struct cdev *wd_dev;
66static volatile u_int wd_last_u;    /* last timeout value set by kern_do_pat */
67static u_int wd_last_u_sysctl;    /* last timeout value set by kern_do_pat */
68static u_int wd_last_u_sysctl_secs;    /* wd_last_u in seconds */
69
70SYSCTL_NODE(_hw, OID_AUTO, watchdog, CTLFLAG_RD, 0, "Main watchdog device");
71SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u, CTLFLAG_RD,
72    &wd_last_u_sysctl, 0, "Watchdog last update time");
73SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u_secs, CTLFLAG_RD,
74    &wd_last_u_sysctl_secs, 0, "Watchdog last update time");
75
76static int wd_lastpat_valid = 0;
77static time_t wd_lastpat = 0;	/* when the watchdog was last patted */
78
79static void
80pow2ns_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
89static int
90pow2ns_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
100static int
101seconds_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
117int
118wdog_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
180static int
181wd_valid_act(int act)
182{
183
184	if ((act & ~(WD_SOFT_MASK)) != 0)
185		return false;
186	return true;
187}
188
189static int
190wd_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
209static int
210wd_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
224static void
225wd_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 */
252static int
253wd_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
301static int
302wd_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 */
374u_int
375wdog_kern_last_timeout(void)
376{
377
378	return (wd_last_u);
379}
380
381static struct cdevsw wd_cdevsw = {
382	.d_version =	D_VERSION,
383	.d_ioctl =	wd_ioctl,
384	.d_name =	"watchdog",
385};
386
387static int
388watchdog_modevent(module_t mod __unused, int type, void *data __unused)
389{
390	switch(type) {
391	case MOD_LOAD:
392		callout_init(&wd_pretimeo_handle, true);
393		callout_init(&wd_softtimeo_handle, true);
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
411DEV_MODULE(watchdog, watchdog_modevent, NULL);
412