ntwdt.c revision 7656:2621e50fdf4a
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 * sun4v application watchdog driver
29 */
30
31#include <sys/types.h>
32#include <sys/file.h>
33#include <sys/errno.h>
34#include <sys/open.h>
35#include <sys/callb.h>
36#include <sys/cred.h>
37#include <sys/cyclic.h>
38#include <sys/uio.h>
39#include <sys/stat.h>
40#include <sys/ksynch.h>
41#include <sys/modctl.h>
42#include <sys/conf.h>
43#include <sys/devops.h>
44#include <sys/debug.h>
45#include <sys/cmn_err.h>
46#include <sys/ddi.h>
47#include <sys/reboot.h>
48#include <sys/sunddi.h>
49#include <sys/signal.h>
50#include <sys/ntwdt.h>
51#include <sys/note.h>
52
53/*
54 * tunables
55 */
56int ntwdt_disable_timeout_action = 0;
57
58#ifdef DEBUG
59
60int ntwdt_debug = 0;		/* ntwdt debug flag, dbg all for now. */
61
62/*
63 * Flags to set in ntwdt_debug.
64 */
65#define	NTWDT_DBG_ENTRY	0x00000001	/* drv entry points */
66#define	NTWDT_DBG_IOCTL	0x00000002	/* ioctl's */
67#define	NTWDT_DBG_NTWDT	0x00000004	/* other ntwdt debug */
68
69#define	NTWDT_DBG(flag, msg) \
70	{ if ((ntwdt_debug) & (flag)) (void) printf msg; }
71#else	/* DEBUG */
72#define	NTWDT_DBG(flag, msg)
73#endif	/* DEBUG */
74
75#define	NTWDT_MINOR_NODE	"awdt"
76#define	getstate(minor)	\
77	((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor)))
78
79/*
80 * The ntwdt cyclic interval in nanosecond unit as cyclic subsystem supports
81 * nanosecond resolution.
82 */
83#define	NTWDT_CYCLIC_INTERVAL	NANOSEC	/* 1 seconds */
84
85/*
86 * The ntwdt decrement interval in 1 second resolution.
87 */
88#define	NTWDT_DECREMENT_INTERVAL	1	/* 1 second */
89
90/*
91 * ntwdt_watchdog_flags and macros to set/clear one bit in it.
92 */
93#define	NTWDT_FLAG_SKIP_CYCLIC	0x1	/* skip next cyclic */
94
95#define	NTWDT_MAX_TIMEOUT	(3 * 60 * 60)	/* 3 hours */
96
97#define	WDT_MIN_COREAPI_MAJOR	1
98#define	WDT_MIN_COREAPI_MINOR	1
99
100/*
101 * Application watchdog state.
102 */
103typedef struct ntwdt_runstate {
104	kmutex_t		ntwdt_runstate_mutex;
105	ddi_iblock_cookie_t	ntwdt_runstate_mtx_cookie;
106	int			ntwdt_watchdog_enabled;	/* wdog enabled ? */
107	int			ntwdt_reset_enabled;	/* reset enabled ? */
108	int			ntwdt_timer_running;	/* wdog running ? */
109	int			ntwdt_watchdog_expired;	/* wdog expired ? */
110	uint32_t		ntwdt_time_remaining;	/* expiration timer */
111	uint32_t		ntwdt_watchdog_timeout;	/* timeout in seconds */
112	hrtime_t		ntwdt_cyclic_interval;	/* cyclic interval */
113	cyc_handler_t		ntwdt_cycl_hdlr;
114	cyc_time_t		ntwdt_cycl_time;
115	uint32_t		ntwdt_watchdog_flags;
116} ntwdt_runstate_t;
117
118/*
119 * softstate of NTWDT
120 */
121typedef struct {
122	kmutex_t		ntwdt_mutex;
123	dev_info_t		*ntwdt_dip;		/* dip */
124	int			ntwdt_open_flag;	/* file open ? */
125	ntwdt_runstate_t	*ntwdt_run_state;	/* wdog state */
126	cyclic_id_t		ntwdt_cycl_id;
127} ntwdt_state_t;
128
129static void *ntwdt_statep;	/* softstate */
130static dev_info_t *ntwdt_dip;
131
132static ddi_softintr_t	ntwdt_cyclic_softint_id;
133
134static int ntwdt_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
135static int ntwdt_attach(dev_info_t *, ddi_attach_cmd_t);
136static int ntwdt_detach(dev_info_t *, ddi_detach_cmd_t);
137static int ntwdt_open(dev_t *, int, int, cred_t *);
138static int ntwdt_close(dev_t, int, int, cred_t *);
139static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
140
141static int ntwdt_chk_watchdog_support();
142static void ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state);
143static void ntwdt_cyclic_pat(void);
144static uint_t ntwdt_cyclic_softint(caddr_t arg);
145static void ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr);
146static void ntwdt_stop_timer_lock(void *arg);
147static void ntwdt_stop_timer(void *arg);
148static void ntwdt_enforce_timeout();
149
150static struct cb_ops ntwdt_cb_ops = {
151	ntwdt_open,		/* cb_open */
152	ntwdt_close,		/* cb_close */
153	nodev,			/* cb_strategy */
154	nodev,			/* cb_print */
155	nodev,			/* cb_dump */
156	nodev,			/* cb_read */
157	nodev,			/* cb_write */
158	ntwdt_ioctl,		/* cb_ioctl */
159	nodev,			/* cb_devmap */
160	nodev,			/* cb_mmap */
161	nodev,			/* cb_segmap */
162	nochpoll,		/* cb_chpoll */
163	ddi_prop_op,		/* cb_prop_op */
164	NULL,			/* cb_str */
165	D_NEW | D_MP		/* cb_flag */
166};
167
168static struct dev_ops ntwdt_dev_ops = {
169	DEVO_REV,		/* devo_rev */
170	0,			/* devo_refcnt */
171	ntwdt_info,		/* devo_info */
172	nulldev,		/* devo_identify */
173	nulldev,		/* devo_probe */
174	ntwdt_attach,		/* devo_attach */
175	ntwdt_detach,		/* devo_detach */
176	nodev,			/* devo_reset */
177	&ntwdt_cb_ops,		/* devo_cb_ops */
178	NULL,			/* devo_bus_ops */
179	nulldev,		/* devo_power */
180	ddi_quiesce_not_supported,	/* devo_quiesce */
181};
182
183static struct modldrv modldrv = {
184	&mod_driverops,
185	"Application Watchdog Driver",
186	&ntwdt_dev_ops
187};
188
189static struct modlinkage modlinkage = {
190	MODREV_1,
191	(void *)&modldrv,
192	NULL
193};
194
195int
196_init(void)
197{
198	int error = 0;
199
200	NTWDT_DBG(NTWDT_DBG_ENTRY, ("_init"));
201
202	/* Initialize the soft state structures */
203	if ((error = ddi_soft_state_init(&ntwdt_statep,
204	    sizeof (ntwdt_state_t), 1)) != 0) {
205		return (error);
206	}
207
208	/* Install the loadable module */
209	if ((error = mod_install(&modlinkage)) != 0) {
210		ddi_soft_state_fini(&ntwdt_statep);
211	}
212	return (error);
213}
214
215int
216_info(struct modinfo *modinfop)
217{
218	NTWDT_DBG(NTWDT_DBG_ENTRY, ("_info"));
219
220	return (mod_info(&modlinkage, modinfop));
221}
222
223int
224_fini(void)
225{
226	int retval;
227
228	NTWDT_DBG(NTWDT_DBG_ENTRY, ("_fini"));
229
230	if ((retval = mod_remove(&modlinkage)) == 0) {
231		ddi_soft_state_fini(&ntwdt_statep);
232	}
233
234	return (retval);
235}
236
237static int
238ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
239{
240	int instance;
241	ntwdt_state_t *ntwdt_ptr = NULL;	/* pointer to ntwdt_runstatep */
242	ntwdt_runstate_t *ntwdt_runstatep = NULL;
243	cyc_handler_t *hdlr = NULL;
244
245	switch (cmd) {
246	case DDI_ATTACH:
247		break;
248
249	case DDI_RESUME:
250		return (DDI_SUCCESS);
251
252	default:
253		return (DDI_FAILURE);
254	}
255
256	if (ntwdt_chk_watchdog_support() != 0) {
257		return (DDI_FAILURE);
258	}
259
260	instance = ddi_get_instance(dip);
261	ASSERT(instance == 0);
262
263	if (ddi_soft_state_zalloc(ntwdt_statep, instance) != DDI_SUCCESS) {
264		return (DDI_FAILURE);
265	}
266	ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
267	ASSERT(ntwdt_ptr != NULL);
268
269	ntwdt_dip = dip;
270
271	ntwdt_ptr->ntwdt_dip = dip;
272	ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
273	mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL,
274	    MUTEX_DRIVER, NULL);
275
276	/*
277	 * Initialize the watchdog structure
278	 */
279	ntwdt_ptr->ntwdt_run_state =
280	    kmem_zalloc(sizeof (ntwdt_runstate_t), KM_SLEEP);
281	ntwdt_runstatep = ntwdt_ptr->ntwdt_run_state;
282
283	if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
284	    &ntwdt_runstatep->ntwdt_runstate_mtx_cookie) != DDI_SUCCESS) {
285		cmn_err(CE_WARN, "init of iblock cookie failed "
286		    "for ntwdt_runstate_mutex");
287		goto err1;
288	} else {
289		mutex_init(&ntwdt_runstatep->ntwdt_runstate_mutex,
290		    NULL,
291		    MUTEX_DRIVER,
292		    (void *)ntwdt_runstatep->ntwdt_runstate_mtx_cookie);
293	}
294
295	/* Cyclic fires once per second: */
296	ntwdt_runstatep->ntwdt_cyclic_interval = NTWDT_CYCLIC_INTERVAL;
297
298	/* init the Cyclic that drives the NTWDT */
299	hdlr = &ntwdt_runstatep->ntwdt_cycl_hdlr;
300	hdlr->cyh_level = CY_LOCK_LEVEL;
301	hdlr->cyh_func = (cyc_func_t)ntwdt_cyclic_pat;
302	hdlr->cyh_arg = NULL;
303
304	/* Softint that will be triggered by Cyclic that drives NTWDT */
305	if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id,
306	    NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr)
307	    != DDI_SUCCESS) {
308		cmn_err(CE_WARN, "failed to add cyclic softintr");
309		goto err2;
310	}
311
312	/*
313	 * Create Minor Node as last activity.  This prevents
314	 * application from accessing our implementation until it
315	 * is initialized.
316	 */
317	if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0,
318	    DDI_PSEUDO, NULL) == DDI_FAILURE) {
319		cmn_err(CE_WARN, "failed to create Minor Node: %s",
320		    NTWDT_MINOR_NODE);
321		goto err3;
322	}
323
324	/* Display our driver info in the banner */
325	ddi_report_dev(dip);
326
327	return (DDI_SUCCESS);
328
329err3:
330	ddi_remove_softintr(ntwdt_cyclic_softint_id);
331err2:
332	mutex_destroy(&ntwdt_runstatep->ntwdt_runstate_mutex);
333err1:
334	/* clean up the driver stuff here */
335	kmem_free(ntwdt_runstatep, sizeof (ntwdt_runstate_t));
336	ntwdt_ptr->ntwdt_run_state = NULL;
337	mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
338	ddi_soft_state_free(ntwdt_statep, instance);
339	ntwdt_dip = NULL;
340
341	return (DDI_FAILURE);
342}
343
344/*ARGSUSED*/
345static int
346ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
347{
348	dev_t dev;
349	int instance;
350	int error = DDI_SUCCESS;
351
352	switch (infocmd) {
353	case DDI_INFO_DEVT2DEVINFO:
354		dev = (dev_t)arg;
355		if (getminor(dev) == 0) {
356			*result = (void *)ntwdt_dip;
357		} else {
358			error = DDI_FAILURE;
359		}
360		break;
361
362	case DDI_INFO_DEVT2INSTANCE:
363		dev = (dev_t)arg;
364		instance = getminor(dev);
365		*result = (void *)(uintptr_t)instance;
366		break;
367
368	default:
369		error = DDI_FAILURE;
370
371	}
372
373	return (error);
374}
375
376/*ARGSUSED*/
377static int
378ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
379{
380	int instance = ddi_get_instance(dip);
381	ntwdt_state_t *ntwdt_ptr = NULL;
382
383	ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
384	if (ntwdt_ptr == NULL) {
385		return (DDI_FAILURE);
386	}
387
388	switch (cmd) {
389	case DDI_SUSPEND:
390		return (DDI_SUCCESS);
391
392	case DDI_DETACH:
393		/*
394		 * release resources in opposite (LIFO) order as
395		 * were allocated in attach.
396		 */
397		ddi_remove_minor_node(dip, NULL);
398		ntwdt_stop_timer_lock((void *)ntwdt_ptr);
399		ddi_remove_softintr(ntwdt_cyclic_softint_id);
400
401		mutex_destroy(
402		    &ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
403		kmem_free(ntwdt_ptr->ntwdt_run_state,
404		    sizeof (ntwdt_runstate_t));
405		ntwdt_ptr->ntwdt_run_state = NULL;
406
407		mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
408
409		ddi_soft_state_free(ntwdt_statep, instance);
410
411		ntwdt_dip = NULL;
412		return (DDI_SUCCESS);
413
414	default:
415		return (DDI_FAILURE);
416	}
417}
418
419/*ARGSUSED*/
420static int
421ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp)
422{
423	int instance = getminor(*devp);
424	int retval = 0;
425	ntwdt_state_t *ntwdt_ptr = getstate(instance);
426
427	if (ntwdt_ptr == NULL) {
428		return (ENXIO);
429	}
430
431	/*
432	 * ensure caller is a priviledged process.
433	 */
434	if (drv_priv(credp) != 0) {
435		return (EPERM);
436	}
437
438	mutex_enter(&ntwdt_ptr->ntwdt_mutex);
439	if (ntwdt_ptr->ntwdt_open_flag) {
440		retval = EAGAIN;
441	} else {
442		ntwdt_ptr->ntwdt_open_flag = 1;
443	}
444	mutex_exit(&ntwdt_ptr->ntwdt_mutex);
445
446	return (retval);
447}
448
449/*ARGSUSED*/
450static int
451ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp)
452{
453	int instance = getminor(dev);
454	ntwdt_state_t *ntwdt_ptr = getstate(instance);
455
456	if (ntwdt_ptr == NULL) {
457		return (ENXIO);
458	}
459
460	mutex_enter(&ntwdt_ptr->ntwdt_mutex);
461	ntwdt_ptr->ntwdt_open_flag = 0;
462	mutex_exit(&ntwdt_ptr->ntwdt_mutex);
463
464	return (0);
465}
466
467/*ARGSUSED*/
468static int
469ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
470	int *rvalp)
471{
472	int instance = getminor(dev);
473	int retval = 0;
474	ntwdt_state_t *ntwdt_ptr = NULL;
475	ntwdt_runstate_t *ntwdt_state;
476	lom_dogstate_t lom_dogstate;
477	lom_dogctl_t lom_dogctl;
478	uint32_t lom_dogtime;
479
480	if ((ntwdt_ptr = getstate(instance)) == NULL) {
481		return (ENXIO);
482	}
483
484	ntwdt_state = ntwdt_ptr->ntwdt_run_state;
485
486	switch (cmd) {
487	case LOMIOCDOGSTATE:
488		mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
489		lom_dogstate.reset_enable = ntwdt_state->ntwdt_reset_enabled;
490		lom_dogstate.dog_enable = ntwdt_state->ntwdt_watchdog_enabled;
491		lom_dogstate.dog_timeout = ntwdt_state->ntwdt_watchdog_timeout;
492		mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
493
494		if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg,
495		    sizeof (lom_dogstate_t), mode) != 0) {
496			retval = EFAULT;
497		}
498		break;
499
500	case LOMIOCDOGCTL:
501		if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl,
502		    sizeof (lom_dogctl_t), mode) != 0) {
503			retval = EFAULT;
504			break;
505		}
506
507		NTWDT_DBG(NTWDT_DBG_IOCTL, ("reset_enable: %d, and dog_enable: "
508		    "%d, watchdog_timeout %d", lom_dogctl.reset_enable,
509		    lom_dogctl.dog_enable,
510		    ntwdt_state->ntwdt_watchdog_timeout));
511		/*
512		 * ignore request to enable reset while disabling watchdog.
513		 */
514		if (!lom_dogctl.dog_enable && lom_dogctl.reset_enable) {
515			NTWDT_DBG(NTWDT_DBG_IOCTL, ("invalid combination of "
516			    "reset_enable: %d, and dog_enable: %d",
517			    lom_dogctl.reset_enable,
518			    lom_dogctl.dog_enable));
519			retval = EINVAL;
520			break;
521		}
522
523		mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
524
525		if (ntwdt_state->ntwdt_watchdog_timeout == 0) {
526			/*
527			 * the LOMIOCDOGTIME has never been used to setup
528			 * a valid timeout.
529			 */
530			NTWDT_DBG(NTWDT_DBG_IOCTL, ("timeout has not been set"
531			    "watchdog_timeout: %d",
532			    ntwdt_state->ntwdt_watchdog_timeout));
533			retval = EINVAL;
534			goto end;
535		}
536
537		/*
538		 * Store the user specified state in the softstate.
539		 */
540		ntwdt_state->ntwdt_reset_enabled = lom_dogctl.reset_enable;
541		ntwdt_state->ntwdt_watchdog_enabled = lom_dogctl.dog_enable;
542
543		if (ntwdt_state->ntwdt_watchdog_enabled != 0) {
544			/*
545			 * The user wants to enable the watchdog.
546			 * Arm the watchdog and start the cyclic.
547			 */
548			ntwdt_arm_watchdog(ntwdt_state);
549
550			if (ntwdt_state->ntwdt_timer_running == 0) {
551				ntwdt_start_timer(ntwdt_ptr);
552			}
553
554			NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is enabled"));
555		} else {
556			/*
557			 * The user wants to disable the watchdog.
558			 */
559			if (ntwdt_state->ntwdt_timer_running != 0) {
560				ntwdt_stop_timer(ntwdt_ptr);
561			}
562			NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is disabled"));
563		}
564
565		mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
566		break;
567
568	case LOMIOCDOGTIME:
569		if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime,
570		    sizeof (uint32_t), mode) != 0) {
571			retval = EFAULT;
572			break;
573		}
574
575		NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set timeout: %d",
576		    lom_dogtime));
577
578		/*
579		 * Ensure specified timeout is valid.
580		 */
581		if ((lom_dogtime == 0) ||
582		    (lom_dogtime > (uint32_t)NTWDT_MAX_TIMEOUT)) {
583			retval = EINVAL;
584			NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set invalid "
585			    "timeout: %d", (int)TICK_TO_MSEC(lom_dogtime)));
586			break;
587		}
588
589		mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
590
591		ntwdt_state->ntwdt_watchdog_timeout = lom_dogtime;
592
593		/*
594		 * If awdt is currently running, re-arm it with the
595		 * newly-specified timeout value.
596		 */
597		if (ntwdt_state->ntwdt_timer_running != 0) {
598			ntwdt_arm_watchdog(ntwdt_state);
599		}
600		mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
601		break;
602
603	case LOMIOCDOGPAT:
604		/*
605		 * Allow user to pat the watchdog timer.
606		 */
607		NTWDT_DBG(NTWDT_DBG_IOCTL, ("DOGPAT is invoked"));
608		mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
609
610		/*
611		 * If awdt is not enabled or underlying cyclic is not
612		 * running, exit.
613		 */
614		if (!(ntwdt_state->ntwdt_watchdog_enabled &&
615		    ntwdt_state->ntwdt_timer_running)) {
616			NTWDT_DBG(NTWDT_DBG_IOCTL, ("PAT: AWDT not enabled"));
617			goto end;
618		}
619
620		if (ntwdt_state->ntwdt_watchdog_expired == 0) {
621			/*
622			 * re-arm the awdt.
623			 */
624			ntwdt_arm_watchdog(ntwdt_state);
625			NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT patted, "
626			    "remainning seconds: %d",
627			    ntwdt_state->ntwdt_time_remaining));
628		}
629
630		mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
631		break;
632
633	default:
634		retval = EINVAL;
635		break;
636	}
637	return (retval);
638end:
639	mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
640	return (retval);
641}
642
643static void
644ntwdt_cyclic_pat(void)
645{
646	ddi_trigger_softintr(ntwdt_cyclic_softint_id);
647}
648
649static uint_t
650ntwdt_cyclic_softint(caddr_t arg)
651{
652	/*LINTED E_BAD_PTR_CAST_ALIGN*/
653	ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
654	ntwdt_runstate_t *ntwdt_state;
655
656	ntwdt_state = ntwdt_ptr->ntwdt_run_state;
657
658	mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
659
660	if ((ntwdt_state->ntwdt_watchdog_flags & NTWDT_FLAG_SKIP_CYCLIC) != 0) {
661		ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
662		goto end;
663	}
664
665	if ((ntwdt_state->ntwdt_timer_running == 0) ||
666	    (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) ||
667	    (ntwdt_state->ntwdt_watchdog_enabled == 0)) {
668		goto end;
669	}
670
671	NTWDT_DBG(NTWDT_DBG_IOCTL, ("cyclic_softint: %d"
672	    "lbolt64: %d\n", ntwdt_state->ntwdt_watchdog_timeout,
673	    (int)TICK_TO_MSEC(lbolt64)));
674
675	/*
676	 * Decrement the virtual watchdog timer and check if it has expired.
677	 */
678	ntwdt_state->ntwdt_time_remaining -= NTWDT_DECREMENT_INTERVAL;
679
680	if (ntwdt_state->ntwdt_time_remaining == 0) {
681		cmn_err(CE_WARN, "application-watchdog expired");
682		ntwdt_state->ntwdt_watchdog_expired = 1;
683
684		if (ntwdt_state->ntwdt_reset_enabled != 0) {
685			/*
686			 * The user wants to reset the system.
687			 */
688			mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
689
690			NTWDT_DBG(NTWDT_DBG_NTWDT, ("recovery being done"));
691			ntwdt_enforce_timeout();
692		} else {
693			NTWDT_DBG(NTWDT_DBG_NTWDT, ("no recovery being done"));
694			ntwdt_state->ntwdt_watchdog_enabled = 0;
695		}
696
697		/*
698		 * Schedule Callout to stop the cyclic.
699		 */
700		(void) timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0);
701	} else {
702		_NOTE(EMPTY)
703		NTWDT_DBG(NTWDT_DBG_NTWDT, ("time remaining in AWDT: %d secs",
704		    (int)TICK_TO_MSEC(ntwdt_state->ntwdt_time_remaining)));
705	}
706
707end:
708	mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
709	return (DDI_INTR_CLAIMED);
710}
711
712static void
713ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state)
714{
715	ntwdt_state->ntwdt_time_remaining = ntwdt_state->ntwdt_watchdog_timeout;
716
717	if (ntwdt_state->ntwdt_timer_running != 0) {
718		ntwdt_state->ntwdt_watchdog_flags |= NTWDT_FLAG_SKIP_CYCLIC;
719	} else {
720		ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
721	}
722}
723
724static void
725ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr)
726{
727	ntwdt_runstate_t	*ntwdt_state = ntwdt_ptr->ntwdt_run_state;
728	cyc_handler_t		*hdlr = &ntwdt_state->ntwdt_cycl_hdlr;
729	cyc_time_t		*when = &ntwdt_state->ntwdt_cycl_time;
730
731	/*
732	 * Init the cyclic.
733	 */
734	when->cyt_interval = ntwdt_state->ntwdt_cyclic_interval;
735	when->cyt_when = gethrtime() + when->cyt_interval;
736
737	ntwdt_state->ntwdt_watchdog_expired = 0;
738	ntwdt_state->ntwdt_timer_running = 1;
739
740	mutex_enter(&cpu_lock);
741	if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) {
742		ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when);
743	}
744	mutex_exit(&cpu_lock);
745
746	NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is started"));
747}
748
749static void
750ntwdt_stop_timer(void *arg)
751{
752	ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
753	ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state;
754
755	mutex_enter(&cpu_lock);
756	if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) {
757		cyclic_remove(ntwdt_ptr->ntwdt_cycl_id);
758	}
759	mutex_exit(&cpu_lock);
760
761	ntwdt_state->ntwdt_watchdog_flags = 0;
762	ntwdt_state->ntwdt_timer_running = 0;
763	ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
764
765	NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is stopped"));
766}
767
768/*
769 * This is a wrapper function for ntwdt_stop_timer as some callers
770 * will already have the appropriate mutex locked, and others not.
771 */
772static void
773ntwdt_stop_timer_lock(void *arg)
774{
775	ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
776
777	mutex_enter(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
778	ntwdt_stop_timer(arg);
779	mutex_exit(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
780}
781
782static void
783ntwdt_enforce_timeout()
784{
785	if (ntwdt_disable_timeout_action != 0) {
786		cmn_err(CE_NOTE, "Appication watchdog timer expired, "
787		    "taking no action");
788		return;
789	}
790
791	NTWDT_DBG(NTWDT_DBG_NTWDT, ("dump cores and rebooting ..."));
792
793	(void) kadmin(A_DUMP, AD_BOOT, NULL, kcred);
794	cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed");
795	_NOTE(NOTREACHED);
796}
797
798static int
799ntwdt_chk_watchdog_support()
800{
801	int	retval = 0;
802
803	if ((boothowto & RB_DEBUG) != 0) {
804		cmn_err(CE_WARN, "kernel debugger was booted; "
805		    "application watchdog is not available.");
806		retval = ENOTSUP;
807	}
808	return (retval);
809}
810