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