xref: /netbsd-src/sys/dev/sysmon/sysmon_wdog.c (revision 54853d3dca98132886bdeab790dd5c95437cf1d2)
1*54853d3dSriastradh /*	$NetBSD: sysmon_wdog.c,v 1.30 2021/12/31 11:05:41 riastradh Exp $	*/
2e67f5e66Sthorpej 
3e67f5e66Sthorpej /*-
4e67f5e66Sthorpej  * Copyright (c) 2000 Zembu Labs, Inc.
5e67f5e66Sthorpej  * All rights reserved.
6e67f5e66Sthorpej  *
7e67f5e66Sthorpej  * Author: Jason R. Thorpe <thorpej@zembu.com>
8e67f5e66Sthorpej  *
9e67f5e66Sthorpej  * Redistribution and use in source and binary forms, with or without
10e67f5e66Sthorpej  * modification, are permitted provided that the following conditions
11e67f5e66Sthorpej  * are met:
12e67f5e66Sthorpej  * 1. Redistributions of source code must retain the above copyright
13e67f5e66Sthorpej  *    notice, this list of conditions and the following disclaimer.
14e67f5e66Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15e67f5e66Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16e67f5e66Sthorpej  *    documentation and/or other materials provided with the distribution.
17e67f5e66Sthorpej  * 3. All advertising materials mentioning features or use of this software
18e67f5e66Sthorpej  *    must display the following acknowledgement:
19e67f5e66Sthorpej  *	This product includes software developed by Zembu Labs, Inc.
20e67f5e66Sthorpej  * 4. Neither the name of Zembu Labs nor the names of its employees may
21e67f5e66Sthorpej  *    be used to endorse or promote products derived from this software
22e67f5e66Sthorpej  *    without specific prior written permission.
23e67f5e66Sthorpej  *
24e67f5e66Sthorpej  * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS
25e67f5e66Sthorpej  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR-
26e67f5e66Sthorpej  * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
27e67f5e66Sthorpej  * CLAIMED.  IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
28e67f5e66Sthorpej  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29e67f5e66Sthorpej  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30e67f5e66Sthorpej  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31e67f5e66Sthorpej  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32e67f5e66Sthorpej  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33e67f5e66Sthorpej  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34e67f5e66Sthorpej  */
35e67f5e66Sthorpej 
36e67f5e66Sthorpej /*
37e67f5e66Sthorpej  * Watchdog timer framework for sysmon.  Hardware (and software)
38e67f5e66Sthorpej  * watchdog timers can register themselves here to provide a
39e67f5e66Sthorpej  * watchdog function, which provides an abstract interface to the
40e67f5e66Sthorpej  * user.
41e67f5e66Sthorpej  */
42e67f5e66Sthorpej 
43640249d1Slukem #include <sys/cdefs.h>
44*54853d3dSriastradh __KERNEL_RCSID(0, "$NetBSD: sysmon_wdog.c,v 1.30 2021/12/31 11:05:41 riastradh Exp $");
45640249d1Slukem 
46e67f5e66Sthorpej #include <sys/param.h>
47e67f5e66Sthorpej #include <sys/conf.h>
48e67f5e66Sthorpej #include <sys/errno.h>
49e67f5e66Sthorpej #include <sys/fcntl.h>
5010bb018aSdyoung #include <sys/condvar.h>
5103b78093Sxtraeme #include <sys/mutex.h>
52e67f5e66Sthorpej #include <sys/callout.h>
53e67f5e66Sthorpej #include <sys/kernel.h>
54e67f5e66Sthorpej #include <sys/systm.h>
55e67f5e66Sthorpej #include <sys/proc.h>
568061eedeSpgoyette #include <sys/module.h>
57eb659e13Spgoyette #include <sys/once.h>
58e67f5e66Sthorpej 
59e67f5e66Sthorpej #include <dev/sysmon/sysmonvar.h>
60e67f5e66Sthorpej 
61c371d1d0Sxtraeme static LIST_HEAD(, sysmon_wdog) sysmon_wdog_list =
62e67f5e66Sthorpej     LIST_HEAD_INITIALIZER(&sysmon_wdog_list);
63c371d1d0Sxtraeme static int sysmon_wdog_count;
64c371d1d0Sxtraeme static kmutex_t sysmon_wdog_list_mtx, sysmon_wdog_mtx;
6510bb018aSdyoung static kcondvar_t sysmon_wdog_cv;
66c371d1d0Sxtraeme static struct sysmon_wdog *sysmon_armed_wdog;
67c371d1d0Sxtraeme static callout_t sysmon_wdog_callout;
68c371d1d0Sxtraeme static void *sysmon_wdog_sdhook;
69833bb81cSmatt static void *sysmon_wdog_cphook;
703a91cc8aSad 
71e67f5e66Sthorpej struct sysmon_wdog *sysmon_wdog_find(const char *);
72e67f5e66Sthorpej void	sysmon_wdog_release(struct sysmon_wdog *);
73e67f5e66Sthorpej int	sysmon_wdog_setmode(struct sysmon_wdog *, int, u_int);
74e67f5e66Sthorpej void	sysmon_wdog_ktickle(void *);
75833bb81cSmatt void	sysmon_wdog_critpoll(void *);
76e67f5e66Sthorpej void	sysmon_wdog_shutdown(void *);
7710bb018aSdyoung void	sysmon_wdog_ref(struct sysmon_wdog *);
78e67f5e66Sthorpej 
798061eedeSpgoyette static struct sysmon_opvec sysmon_wdog_opvec = {
808061eedeSpgoyette         sysmonopen_wdog, sysmonclose_wdog, sysmonioctl_wdog,
818061eedeSpgoyette         NULL, NULL, NULL
828061eedeSpgoyette };
838061eedeSpgoyette 
84142f2b40Spgoyette MODULE(MODULE_CLASS_DRIVER, sysmon_wdog, "sysmon");
858061eedeSpgoyette 
86eb659e13Spgoyette ONCE_DECL(once_wdog);
87eb659e13Spgoyette 
88eb659e13Spgoyette static int
wdog_preinit(void)89eb659e13Spgoyette wdog_preinit(void)
90eb659e13Spgoyette {
91eb659e13Spgoyette 
92eb659e13Spgoyette 	mutex_init(&sysmon_wdog_list_mtx, MUTEX_DEFAULT, IPL_NONE);
93eb659e13Spgoyette 	mutex_init(&sysmon_wdog_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
94eb659e13Spgoyette 	cv_init(&sysmon_wdog_cv, "wdogref");
95dacb809fSmatt 	callout_init(&sysmon_wdog_callout, 0);
96eb659e13Spgoyette 
97eb659e13Spgoyette 	return 0;
98eb659e13Spgoyette }
99eb659e13Spgoyette 
1008061eedeSpgoyette int
sysmon_wdog_init(void)101c371d1d0Sxtraeme sysmon_wdog_init(void)
102c371d1d0Sxtraeme {
1038061eedeSpgoyette 	int error;
1048061eedeSpgoyette 
105eb659e13Spgoyette 	(void)RUN_ONCE(&once_wdog, wdog_preinit);
106eb659e13Spgoyette 
107833bb81cSmatt 	sysmon_wdog_sdhook = shutdownhook_establish(sysmon_wdog_shutdown, NULL);
108833bb81cSmatt 	if (sysmon_wdog_sdhook == NULL)
109833bb81cSmatt 		printf("WARNING: unable to register watchdog shutdown hook\n");
110833bb81cSmatt 	sysmon_wdog_cphook = critpollhook_establish(sysmon_wdog_critpoll, NULL);
111833bb81cSmatt 	if (sysmon_wdog_cphook == NULL)
112833bb81cSmatt 		printf("WARNING: unable to register watchdog critpoll hook\n");
1138061eedeSpgoyette 
1148061eedeSpgoyette 	error = sysmon_attach_minor(SYSMON_MINOR_WDOG, &sysmon_wdog_opvec);
1158061eedeSpgoyette 
1168061eedeSpgoyette 	return error;
1178061eedeSpgoyette }
1188061eedeSpgoyette 
1198061eedeSpgoyette int
sysmon_wdog_fini(void)1208061eedeSpgoyette sysmon_wdog_fini(void)
1218061eedeSpgoyette {
1228061eedeSpgoyette 	int error;
1238061eedeSpgoyette 
1248061eedeSpgoyette 	if ( ! LIST_EMPTY(&sysmon_wdog_list))
1258061eedeSpgoyette 		return EBUSY;
1268061eedeSpgoyette 
1278061eedeSpgoyette 	error = sysmon_attach_minor(SYSMON_MINOR_WDOG, NULL);
1288061eedeSpgoyette 
1298061eedeSpgoyette 	if (error == 0) {
1308061eedeSpgoyette 		callout_destroy(&sysmon_wdog_callout);
1318061eedeSpgoyette 		critpollhook_disestablish(sysmon_wdog_cphook);
1328061eedeSpgoyette 		shutdownhook_disestablish(sysmon_wdog_sdhook);
1338061eedeSpgoyette 		cv_destroy(&sysmon_wdog_cv);
1348061eedeSpgoyette 		mutex_destroy(&sysmon_wdog_mtx);
1358061eedeSpgoyette 		mutex_destroy(&sysmon_wdog_list_mtx);
1368061eedeSpgoyette 	}
1378061eedeSpgoyette 
1388061eedeSpgoyette 	return error;
139c371d1d0Sxtraeme }
140c371d1d0Sxtraeme 
141e67f5e66Sthorpej /*
142e67f5e66Sthorpej  * sysmonopen_wdog:
143e67f5e66Sthorpej  *
144e67f5e66Sthorpej  *	Open the system monitor device.
145e67f5e66Sthorpej  */
146e67f5e66Sthorpej int
sysmonopen_wdog(dev_t dev,int flag,int mode,struct lwp * l)147c371d1d0Sxtraeme sysmonopen_wdog(dev_t dev, int flag, int mode, struct lwp *l)
148e67f5e66Sthorpej {
149e67f5e66Sthorpej 
150c371d1d0Sxtraeme 	return 0;
151e67f5e66Sthorpej }
152e67f5e66Sthorpej 
153e67f5e66Sthorpej /*
154e67f5e66Sthorpej  * sysmonclose_wdog:
155e67f5e66Sthorpej  *
156e67f5e66Sthorpej  *	Close the system monitor device.
157e67f5e66Sthorpej  */
158e67f5e66Sthorpej int
sysmonclose_wdog(dev_t dev,int flag,int mode,struct lwp * l)159c371d1d0Sxtraeme sysmonclose_wdog(dev_t dev, int flag, int mode, struct lwp *l)
160e67f5e66Sthorpej {
161e67f5e66Sthorpej 	struct sysmon_wdog *smw;
162c371d1d0Sxtraeme 	int error = 0;
163e67f5e66Sthorpej 
164e67f5e66Sthorpej 	/*
165e67f5e66Sthorpej 	 * If this is the last close, and there is a watchdog
166e67f5e66Sthorpej 	 * running in UTICKLE mode, we need to disable it,
167e67f5e66Sthorpej 	 * otherwise the system will reset in short order.
168e67f5e66Sthorpej 	 *
169e67f5e66Sthorpej 	 * XXX Maybe we should just go into KTICKLE mode?
170e67f5e66Sthorpej 	 */
171c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_mtx);
172e67f5e66Sthorpej 	if ((smw = sysmon_armed_wdog) != NULL) {
1733ad43282Sdrochner 		if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_UTICKLE) {
174e67f5e66Sthorpej 			error = sysmon_wdog_setmode(smw,
175e67f5e66Sthorpej 			    WDOG_MODE_DISARMED, smw->smw_period);
176e67f5e66Sthorpej 			if (error) {
177e67f5e66Sthorpej 				printf("WARNING: UNABLE TO DISARM "
178e67f5e66Sthorpej 				    "WATCHDOG %s ON CLOSE!\n",
179e67f5e66Sthorpej 				    smw->smw_name);
180e67f5e66Sthorpej 				/*
181e67f5e66Sthorpej 				 * ...we will probably reboot soon.
182e67f5e66Sthorpej 				 */
183e67f5e66Sthorpej 			}
184e67f5e66Sthorpej 		}
185e67f5e66Sthorpej 	}
186c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_mtx);
187e67f5e66Sthorpej 
188c371d1d0Sxtraeme 	return error;
189e67f5e66Sthorpej }
190e67f5e66Sthorpej 
191e67f5e66Sthorpej /*
192e67f5e66Sthorpej  * sysmonioctl_wdog:
193e67f5e66Sthorpej  *
194e67f5e66Sthorpej  *	Perform a watchdog control request.
195e67f5e66Sthorpej  */
196e67f5e66Sthorpej int
sysmonioctl_wdog(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)197c371d1d0Sxtraeme sysmonioctl_wdog(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
198e67f5e66Sthorpej {
199e67f5e66Sthorpej 	struct sysmon_wdog *smw;
200c371d1d0Sxtraeme 	int error = 0;
201e67f5e66Sthorpej 
202e67f5e66Sthorpej 	switch (cmd) {
203e67f5e66Sthorpej 	case WDOGIOC_GMODE:
204e67f5e66Sthorpej 	    {
205e67f5e66Sthorpej 		struct wdog_mode *wm = (void *) data;
206e67f5e66Sthorpej 
207e67f5e66Sthorpej 		wm->wm_name[sizeof(wm->wm_name) - 1] = '\0';
208e67f5e66Sthorpej 		smw = sysmon_wdog_find(wm->wm_name);
209e67f5e66Sthorpej 		if (smw == NULL) {
210e67f5e66Sthorpej 			error = ESRCH;
211e67f5e66Sthorpej 			break;
212e67f5e66Sthorpej 		}
213e67f5e66Sthorpej 
214e67f5e66Sthorpej 		wm->wm_mode = smw->smw_mode;
215e67f5e66Sthorpej 		wm->wm_period = smw->smw_period;
216e67f5e66Sthorpej 		sysmon_wdog_release(smw);
217e67f5e66Sthorpej 		break;
218e67f5e66Sthorpej 	    }
219e67f5e66Sthorpej 
220e67f5e66Sthorpej 	case WDOGIOC_SMODE:
221e67f5e66Sthorpej 	    {
222e67f5e66Sthorpej 		struct wdog_mode *wm = (void *) data;
223e67f5e66Sthorpej 
224e67f5e66Sthorpej 		if ((flag & FWRITE) == 0) {
225e67f5e66Sthorpej 			error = EPERM;
226e67f5e66Sthorpej 			break;
227e67f5e66Sthorpej 		}
228e67f5e66Sthorpej 
229e67f5e66Sthorpej 		wm->wm_name[sizeof(wm->wm_name) - 1] = '\0';
230e67f5e66Sthorpej 		smw = sysmon_wdog_find(wm->wm_name);
231e67f5e66Sthorpej 		if (smw == NULL) {
232e67f5e66Sthorpej 			error = ESRCH;
233e67f5e66Sthorpej 			break;
234e67f5e66Sthorpej 		}
235e67f5e66Sthorpej 
236e67f5e66Sthorpej 		if (wm->wm_mode & ~(WDOG_MODE_MASK|WDOG_FEATURE_MASK))
237e67f5e66Sthorpej 			error = EINVAL;
238e67f5e66Sthorpej 		else {
239c371d1d0Sxtraeme 			mutex_enter(&sysmon_wdog_mtx);
240e67f5e66Sthorpej 			error = sysmon_wdog_setmode(smw, wm->wm_mode,
241e67f5e66Sthorpej 			    wm->wm_period);
242c371d1d0Sxtraeme 			mutex_exit(&sysmon_wdog_mtx);
243e67f5e66Sthorpej 		}
244e67f5e66Sthorpej 
245e67f5e66Sthorpej 		sysmon_wdog_release(smw);
246e67f5e66Sthorpej 		break;
247e67f5e66Sthorpej 	    }
248e67f5e66Sthorpej 
249e67f5e66Sthorpej 	case WDOGIOC_WHICH:
250e67f5e66Sthorpej 	    {
251e67f5e66Sthorpej 		struct wdog_mode *wm = (void *) data;
252e67f5e66Sthorpej 
253c371d1d0Sxtraeme 		mutex_enter(&sysmon_wdog_mtx);
254e67f5e66Sthorpej 		if ((smw = sysmon_armed_wdog) != NULL) {
255e67f5e66Sthorpej 			strcpy(wm->wm_name, smw->smw_name);
256e67f5e66Sthorpej 			wm->wm_mode = smw->smw_mode;
257e67f5e66Sthorpej 			wm->wm_period = smw->smw_period;
258e67f5e66Sthorpej 		} else
259e67f5e66Sthorpej 			error = ESRCH;
260c371d1d0Sxtraeme 		mutex_exit(&sysmon_wdog_mtx);
261e67f5e66Sthorpej 		break;
262e67f5e66Sthorpej 	    }
263e67f5e66Sthorpej 
264e67f5e66Sthorpej 	case WDOGIOC_TICKLE:
265e67f5e66Sthorpej 		if ((flag & FWRITE) == 0) {
266e67f5e66Sthorpej 			error = EPERM;
267e67f5e66Sthorpej 			break;
268e67f5e66Sthorpej 		}
269e67f5e66Sthorpej 
270c371d1d0Sxtraeme 		mutex_enter(&sysmon_wdog_mtx);
271e67f5e66Sthorpej 		if ((smw = sysmon_armed_wdog) != NULL) {
272e67f5e66Sthorpej 			error = (*smw->smw_tickle)(smw);
273e67f5e66Sthorpej 			if (error == 0)
27495e1ffb1Schristos 				smw->smw_tickler = l->l_proc->p_pid;
275e67f5e66Sthorpej 		} else
276e67f5e66Sthorpej 			error = ESRCH;
277c371d1d0Sxtraeme 		mutex_exit(&sysmon_wdog_mtx);
278e67f5e66Sthorpej 		break;
279e67f5e66Sthorpej 
280e67f5e66Sthorpej 	case WDOGIOC_GTICKLER:
28110f735a0Ssimonb 		if ((smw = sysmon_armed_wdog) != NULL)
282e67f5e66Sthorpej 			*(pid_t *)data = smw->smw_tickler;
28310f735a0Ssimonb 		else
28410f735a0Ssimonb 			error = ESRCH;
285e67f5e66Sthorpej 		break;
286e67f5e66Sthorpej 
287e67f5e66Sthorpej 	case WDOGIOC_GWDOGS:
288e67f5e66Sthorpej 	    {
289e67f5e66Sthorpej 		struct wdog_conf *wc = (void *) data;
290e67f5e66Sthorpej 		char *cp;
291e67f5e66Sthorpej 		int i;
292e67f5e66Sthorpej 
293c371d1d0Sxtraeme 		mutex_enter(&sysmon_wdog_list_mtx);
294e67f5e66Sthorpej 		if (wc->wc_names == NULL)
295e67f5e66Sthorpej 			wc->wc_count = sysmon_wdog_count;
296e67f5e66Sthorpej 		else {
297e67f5e66Sthorpej 			for (i = 0, cp = wc->wc_names,
298e67f5e66Sthorpej 			       smw = LIST_FIRST(&sysmon_wdog_list);
299e67f5e66Sthorpej 			     i < sysmon_wdog_count && smw != NULL && error == 0;
300e67f5e66Sthorpej 			     i++, cp += WDOG_NAMESIZE,
301e67f5e66Sthorpej 			       smw = LIST_NEXT(smw, smw_list))
302e67f5e66Sthorpej 				error = copyout(smw->smw_name, cp,
303e67f5e66Sthorpej 				    strlen(smw->smw_name) + 1);
304e67f5e66Sthorpej 			wc->wc_count = i;
305e67f5e66Sthorpej 		}
306c371d1d0Sxtraeme 		mutex_exit(&sysmon_wdog_list_mtx);
307e67f5e66Sthorpej 		break;
308e67f5e66Sthorpej 	    }
309e67f5e66Sthorpej 
310e67f5e66Sthorpej 	default:
311e67f5e66Sthorpej 		error = ENOTTY;
312e67f5e66Sthorpej 	}
313e67f5e66Sthorpej 
314c371d1d0Sxtraeme 	return error;
315e67f5e66Sthorpej }
316e67f5e66Sthorpej 
317e67f5e66Sthorpej /*
318e67f5e66Sthorpej  * sysmon_wdog_register:
319e67f5e66Sthorpej  *
320e67f5e66Sthorpej  *	Register a watchdog device.
321e67f5e66Sthorpej  */
322e67f5e66Sthorpej int
sysmon_wdog_register(struct sysmon_wdog * smw)323e67f5e66Sthorpej sysmon_wdog_register(struct sysmon_wdog *smw)
324e67f5e66Sthorpej {
325e67f5e66Sthorpej 	struct sysmon_wdog *lsmw;
326e67f5e66Sthorpej 	int error = 0;
327e67f5e66Sthorpej 
328eb659e13Spgoyette 	(void)RUN_ONCE(&once_wdog, wdog_preinit);
329eb659e13Spgoyette 
330c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_list_mtx);
331e67f5e66Sthorpej 
33219b79f5cSdyoung 	LIST_FOREACH(lsmw, &sysmon_wdog_list, smw_list) {
333e67f5e66Sthorpej 		if (strcmp(lsmw->smw_name, smw->smw_name) == 0) {
334e67f5e66Sthorpej 			error = EEXIST;
335e67f5e66Sthorpej 			goto out;
336e67f5e66Sthorpej 		}
337e67f5e66Sthorpej 	}
338e67f5e66Sthorpej 
339e67f5e66Sthorpej 	smw->smw_mode = WDOG_MODE_DISARMED;
340e67f5e66Sthorpej 	smw->smw_tickler = (pid_t) -1;
341e67f5e66Sthorpej 	smw->smw_refcnt = 0;
342e67f5e66Sthorpej 	sysmon_wdog_count++;
343e67f5e66Sthorpej 	LIST_INSERT_HEAD(&sysmon_wdog_list, smw, smw_list);
344e67f5e66Sthorpej 
345e67f5e66Sthorpej  out:
346c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_list_mtx);
347c371d1d0Sxtraeme 	return error;
348e67f5e66Sthorpej }
349e67f5e66Sthorpej 
350e67f5e66Sthorpej /*
351e67f5e66Sthorpej  * sysmon_wdog_unregister:
352e67f5e66Sthorpej  *
353e67f5e66Sthorpej  *	Unregister a watchdog device.
354e67f5e66Sthorpej  */
35510bb018aSdyoung int
sysmon_wdog_unregister(struct sysmon_wdog * smw)356e67f5e66Sthorpej sysmon_wdog_unregister(struct sysmon_wdog *smw)
357e67f5e66Sthorpej {
35810bb018aSdyoung 	int rc = 0;
359e67f5e66Sthorpej 
360c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_list_mtx);
36110bb018aSdyoung 	while (smw->smw_refcnt > 0 && rc == 0) {
36210bb018aSdyoung 		aprint_debug("%s: %d users remain\n", smw->smw_name,
36310bb018aSdyoung 		    smw->smw_refcnt);
36410bb018aSdyoung 		rc = cv_wait_sig(&sysmon_wdog_cv, &sysmon_wdog_list_mtx);
36510bb018aSdyoung 	}
36610bb018aSdyoung 	if (rc == 0) {
367e67f5e66Sthorpej 		sysmon_wdog_count--;
368e67f5e66Sthorpej 		LIST_REMOVE(smw, smw_list);
36910bb018aSdyoung 	}
370c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_list_mtx);
37110bb018aSdyoung 	return rc;
372e67f5e66Sthorpej }
373e67f5e66Sthorpej 
374e67f5e66Sthorpej /*
375833bb81cSmatt  * sysmon_wdog_critpoll:
376833bb81cSmatt  *
377833bb81cSmatt  *	Perform critical operations during long polling periods
378833bb81cSmatt  */
379833bb81cSmatt void
sysmon_wdog_critpoll(void * arg)380833bb81cSmatt sysmon_wdog_critpoll(void *arg)
381833bb81cSmatt {
382833bb81cSmatt 	struct sysmon_wdog *smw = sysmon_armed_wdog;
383833bb81cSmatt 
384833bb81cSmatt 	if (smw == NULL)
385833bb81cSmatt 		return;
386833bb81cSmatt 
387833bb81cSmatt 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE) {
388833bb81cSmatt 		if ((*smw->smw_tickle)(smw) != 0) {
389833bb81cSmatt 			printf("WARNING: KERNEL TICKLE OF WATCHDOG %s "
390833bb81cSmatt 			    "FAILED!\n", smw->smw_name);
391833bb81cSmatt 		}
392833bb81cSmatt 	}
393833bb81cSmatt }
394833bb81cSmatt 
395833bb81cSmatt /*
396e67f5e66Sthorpej  * sysmon_wdog_find:
397e67f5e66Sthorpej  *
398e67f5e66Sthorpej  *	Find a watchdog device.  We increase the reference
399e67f5e66Sthorpej  *	count on a match.
400e67f5e66Sthorpej  */
401e67f5e66Sthorpej struct sysmon_wdog *
sysmon_wdog_find(const char * name)402e67f5e66Sthorpej sysmon_wdog_find(const char *name)
403e67f5e66Sthorpej {
404e67f5e66Sthorpej 	struct sysmon_wdog *smw;
405e67f5e66Sthorpej 
406c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_list_mtx);
407e67f5e66Sthorpej 
40819b79f5cSdyoung 	LIST_FOREACH(smw, &sysmon_wdog_list, smw_list) {
409e67f5e66Sthorpej 		if (strcmp(smw->smw_name, name) == 0)
410e67f5e66Sthorpej 			break;
411e67f5e66Sthorpej 	}
412e67f5e66Sthorpej 
413e67f5e66Sthorpej 	if (smw != NULL)
414e67f5e66Sthorpej 		smw->smw_refcnt++;
415e67f5e66Sthorpej 
416c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_list_mtx);
417c371d1d0Sxtraeme 	return smw;
418e67f5e66Sthorpej }
419e67f5e66Sthorpej 
420e67f5e66Sthorpej /*
421e67f5e66Sthorpej  * sysmon_wdog_release:
422e67f5e66Sthorpej  *
423e67f5e66Sthorpej  *	Release a watchdog device.
424e67f5e66Sthorpej  */
425e67f5e66Sthorpej void
sysmon_wdog_release(struct sysmon_wdog * smw)426e67f5e66Sthorpej sysmon_wdog_release(struct sysmon_wdog *smw)
427e67f5e66Sthorpej {
428e67f5e66Sthorpej 
429c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_list_mtx);
430e67f5e66Sthorpej 	KASSERT(smw->smw_refcnt != 0);
431e67f5e66Sthorpej 	smw->smw_refcnt--;
43210bb018aSdyoung 	cv_signal(&sysmon_wdog_cv);
43310bb018aSdyoung 	mutex_exit(&sysmon_wdog_list_mtx);
43410bb018aSdyoung }
43510bb018aSdyoung 
43610bb018aSdyoung void
sysmon_wdog_ref(struct sysmon_wdog * smw)43710bb018aSdyoung sysmon_wdog_ref(struct sysmon_wdog *smw)
43810bb018aSdyoung {
43910bb018aSdyoung 	mutex_enter(&sysmon_wdog_list_mtx);
44010bb018aSdyoung 	smw->smw_refcnt++;
441c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_list_mtx);
442e67f5e66Sthorpej }
443e67f5e66Sthorpej 
444e67f5e66Sthorpej /*
445e67f5e66Sthorpej  * sysmon_wdog_setmode:
446e67f5e66Sthorpej  *
447e67f5e66Sthorpej  *	Set the mode of a watchdog device.
448e67f5e66Sthorpej  */
449e67f5e66Sthorpej int
sysmon_wdog_setmode(struct sysmon_wdog * smw,int mode,u_int period)450e67f5e66Sthorpej sysmon_wdog_setmode(struct sysmon_wdog *smw, int mode, u_int period)
451e67f5e66Sthorpej {
452e67f5e66Sthorpej 	u_int operiod = smw->smw_period;
453e67f5e66Sthorpej 	int omode = smw->smw_mode;
454e67f5e66Sthorpej 	int error = 0;
455e67f5e66Sthorpej 
456e67f5e66Sthorpej 	smw->smw_period = period;
457e67f5e66Sthorpej 	smw->smw_mode = mode;
458e67f5e66Sthorpej 
459e67f5e66Sthorpej 	switch (mode & WDOG_MODE_MASK) {
460e67f5e66Sthorpej 	case WDOG_MODE_DISARMED:
461e67f5e66Sthorpej 		if (smw != sysmon_armed_wdog) {
462e67f5e66Sthorpej 			error = EINVAL;
463e67f5e66Sthorpej 			goto out;
464e67f5e66Sthorpej 		}
465e67f5e66Sthorpej 		break;
466e67f5e66Sthorpej 
467e67f5e66Sthorpej 	case WDOG_MODE_KTICKLE:
468e67f5e66Sthorpej 	case WDOG_MODE_UTICKLE:
469ddd2ade2Ssmb 	case WDOG_MODE_ETICKLE:
470e67f5e66Sthorpej 		if (sysmon_armed_wdog != NULL) {
471e67f5e66Sthorpej 			error = EBUSY;
472e67f5e66Sthorpej 			goto out;
473e67f5e66Sthorpej 		}
474e67f5e66Sthorpej 		break;
475e67f5e66Sthorpej 
476e67f5e66Sthorpej 	default:
477e67f5e66Sthorpej 		error = EINVAL;
478e67f5e66Sthorpej 		goto out;
479e67f5e66Sthorpej 	}
480e67f5e66Sthorpej 
481e67f5e66Sthorpej 	error = (*smw->smw_setmode)(smw);
482e67f5e66Sthorpej 
483e67f5e66Sthorpej  out:
484e67f5e66Sthorpej 	if (error) {
485e67f5e66Sthorpej 		smw->smw_period = operiod;
486e67f5e66Sthorpej 		smw->smw_mode = omode;
487e67f5e66Sthorpej 	} else {
488e67f5e66Sthorpej 		if ((mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
4894e7ff09eSthorpej 			sysmon_armed_wdog = NULL;
490e67f5e66Sthorpej 			smw->smw_tickler = (pid_t) -1;
49110bb018aSdyoung 			sysmon_wdog_release(smw);
492e67f5e66Sthorpej 			if ((omode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE)
493e67f5e66Sthorpej 				callout_stop(&sysmon_wdog_callout);
494e67f5e66Sthorpej 		} else {
495e67f5e66Sthorpej 			sysmon_armed_wdog = smw;
49610bb018aSdyoung 			sysmon_wdog_ref(smw);
497e67f5e66Sthorpej 			if ((mode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE) {
498e67f5e66Sthorpej 				callout_reset(&sysmon_wdog_callout,
499e67f5e66Sthorpej 				    WDOG_PERIOD_TO_TICKS(smw->smw_period) / 2,
500e67f5e66Sthorpej 				    sysmon_wdog_ktickle, NULL);
501e67f5e66Sthorpej 			}
502e67f5e66Sthorpej 		}
503e67f5e66Sthorpej 	}
504c371d1d0Sxtraeme 	return error;
505e67f5e66Sthorpej }
506e67f5e66Sthorpej 
507e67f5e66Sthorpej /*
508e67f5e66Sthorpej  * sysmon_wdog_ktickle:
509e67f5e66Sthorpej  *
510e67f5e66Sthorpej  *	Kernel watchdog tickle routine.
511e67f5e66Sthorpej  */
512e67f5e66Sthorpej void
sysmon_wdog_ktickle(void * arg)513168cd830Schristos sysmon_wdog_ktickle(void *arg)
514e67f5e66Sthorpej {
515e67f5e66Sthorpej 	struct sysmon_wdog *smw;
516e67f5e66Sthorpej 
517c371d1d0Sxtraeme 	mutex_enter(&sysmon_wdog_mtx);
518e67f5e66Sthorpej 	if ((smw = sysmon_armed_wdog) != NULL) {
519e67f5e66Sthorpej 		if ((*smw->smw_tickle)(smw) != 0) {
520e67f5e66Sthorpej 			printf("WARNING: KERNEL TICKLE OF WATCHDOG %s "
521e67f5e66Sthorpej 			    "FAILED!\n", smw->smw_name);
522e67f5e66Sthorpej 			/*
523e67f5e66Sthorpej 			 * ...we will probably reboot soon.
524e67f5e66Sthorpej 			 */
525e67f5e66Sthorpej 		}
526e67f5e66Sthorpej 		callout_reset(&sysmon_wdog_callout,
527e67f5e66Sthorpej 		    WDOG_PERIOD_TO_TICKS(smw->smw_period) / 2,
528e67f5e66Sthorpej 		    sysmon_wdog_ktickle, NULL);
529e67f5e66Sthorpej 	}
530c371d1d0Sxtraeme 	mutex_exit(&sysmon_wdog_mtx);
531e67f5e66Sthorpej }
532e67f5e66Sthorpej 
533e67f5e66Sthorpej /*
534e67f5e66Sthorpej  * sysmon_wdog_shutdown:
535e67f5e66Sthorpej  *
536e67f5e66Sthorpej  *	Perform shutdown-time operations.
537e67f5e66Sthorpej  */
538e67f5e66Sthorpej void
sysmon_wdog_shutdown(void * arg)539168cd830Schristos sysmon_wdog_shutdown(void *arg)
540e67f5e66Sthorpej {
541e67f5e66Sthorpej 	struct sysmon_wdog *smw;
542e67f5e66Sthorpej 
543e67f5e66Sthorpej 	/*
544e67f5e66Sthorpej 	 * XXX Locking here?  I don't think it's necessary.
545e67f5e66Sthorpej 	 */
546e67f5e66Sthorpej 
547e67f5e66Sthorpej 	if ((smw = sysmon_armed_wdog) != NULL) {
548e67f5e66Sthorpej 		if (sysmon_wdog_setmode(smw, WDOG_MODE_DISARMED,
549e67f5e66Sthorpej 		    smw->smw_period))
550e67f5e66Sthorpej 			printf("WARNING: FAILED TO SHUTDOWN WATCHDOG %s!\n",
551e67f5e66Sthorpej 			    smw->smw_name);
552e67f5e66Sthorpej 	}
553e67f5e66Sthorpej }
554*54853d3dSriastradh 
555*54853d3dSriastradh static int
sysmon_wdog_modcmd(modcmd_t cmd,void * arg)5568061eedeSpgoyette sysmon_wdog_modcmd(modcmd_t cmd, void *arg)
5578061eedeSpgoyette {
5588061eedeSpgoyette         int ret;
5598061eedeSpgoyette 
5608061eedeSpgoyette         switch (cmd) {
5618061eedeSpgoyette         case MODULE_CMD_INIT:
5628061eedeSpgoyette                 ret = sysmon_wdog_init();
5638061eedeSpgoyette                 break;
5648061eedeSpgoyette         case MODULE_CMD_FINI:
5658061eedeSpgoyette                 ret = sysmon_wdog_fini();
5668061eedeSpgoyette                 break;
5678061eedeSpgoyette         case MODULE_CMD_STAT:
5688061eedeSpgoyette         default:
5698061eedeSpgoyette                 ret = ENOTTY;
5708061eedeSpgoyette         }
5718061eedeSpgoyette 
5728061eedeSpgoyette         return ret;
5738061eedeSpgoyette }
574