Home | History | Annotate | Download | only in io
      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