xref: /illumos-gate/usr/src/uts/sun4v/io/ntwdt.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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  */
56 int ntwdt_disable_timeout_action = 0;
57 
58 #ifdef DEBUG
59 
60 int 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  */
103 typedef 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  */
121 typedef 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 
129 static void *ntwdt_statep;	/* softstate */
130 static dev_info_t *ntwdt_dip;
131 
132 static ddi_softintr_t	ntwdt_cyclic_softint_id;
133 
134 static int ntwdt_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
135 static int ntwdt_attach(dev_info_t *, ddi_attach_cmd_t);
136 static int ntwdt_detach(dev_info_t *, ddi_detach_cmd_t);
137 static int ntwdt_open(dev_t *, int, int, cred_t *);
138 static int ntwdt_close(dev_t, int, int, cred_t *);
139 static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
140 
141 static int ntwdt_chk_watchdog_support();
142 static void ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state);
143 static void ntwdt_cyclic_pat(void);
144 static uint_t ntwdt_cyclic_softint(caddr_t arg);
145 static void ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr);
146 static void ntwdt_stop_timer_lock(void *arg);
147 static void ntwdt_stop_timer(void *arg);
148 static void ntwdt_enforce_timeout();
149 
150 static 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 
168 static 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 
183 static struct modldrv modldrv = {
184 	&mod_driverops,
185 	"Application Watchdog Driver",
186 	&ntwdt_dev_ops
187 };
188 
189 static struct modlinkage modlinkage = {
190 	MODREV_1,
191 	(void *)&modldrv,
192 	NULL
193 };
194 
195 int
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 
215 int
216 _info(struct modinfo *modinfop)
217 {
218 	NTWDT_DBG(NTWDT_DBG_ENTRY, ("_info"));
219 
220 	return (mod_info(&modlinkage, modinfop));
221 }
222 
223 int
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 
237 static int
238 ntwdt_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 
329 err3:
330 	ddi_remove_softintr(ntwdt_cyclic_softint_id);
331 err2:
332 	mutex_destroy(&ntwdt_runstatep->ntwdt_runstate_mutex);
333 err1:
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*/
345 static int
346 ntwdt_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*/
377 static int
378 ntwdt_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*/
420 static int
421 ntwdt_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*/
450 static int
451 ntwdt_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*/
468 static int
469 ntwdt_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);
638 end:
639 	mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
640 	return (retval);
641 }
642 
643 static void
644 ntwdt_cyclic_pat(void)
645 {
646 	ddi_trigger_softintr(ntwdt_cyclic_softint_id);
647 }
648 
649 static uint_t
650 ntwdt_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 
707 end:
708 	mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
709 	return (DDI_INTR_CLAIMED);
710 }
711 
712 static void
713 ntwdt_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 
724 static void
725 ntwdt_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 
749 static void
750 ntwdt_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  */
772 static void
773 ntwdt_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 
782 static void
783 ntwdt_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 
798 static int
799 ntwdt_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