13349Swentaoy /*
23349Swentaoy * CDDL HEADER START
33349Swentaoy *
43349Swentaoy * The contents of this file are subject to the terms of the
53349Swentaoy * Common Development and Distribution License (the "License").
63349Swentaoy * You may not use this file except in compliance with the License.
73349Swentaoy *
83349Swentaoy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93349Swentaoy * or http://www.opensolaris.org/os/licensing.
103349Swentaoy * See the License for the specific language governing permissions
113349Swentaoy * and limitations under the License.
123349Swentaoy *
133349Swentaoy * When distributing Covered Code, include this CDDL HEADER in each
143349Swentaoy * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153349Swentaoy * If applicable, add the following below this CDDL HEADER, with the
163349Swentaoy * fields enclosed by brackets "[]" replaced with your own identifying
173349Swentaoy * information: Portions Copyright [yyyy] [name of copyright owner]
183349Swentaoy *
193349Swentaoy * CDDL HEADER END
203349Swentaoy */
213349Swentaoy /*
22*11066Srafael.vanoni@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
233349Swentaoy * Use is subject to license terms.
243349Swentaoy */
253349Swentaoy
263349Swentaoy /*
273349Swentaoy * sun4v application watchdog driver
283349Swentaoy */
293349Swentaoy
303349Swentaoy #include <sys/types.h>
313349Swentaoy #include <sys/file.h>
323349Swentaoy #include <sys/errno.h>
333349Swentaoy #include <sys/open.h>
343349Swentaoy #include <sys/callb.h>
353349Swentaoy #include <sys/cred.h>
363349Swentaoy #include <sys/cyclic.h>
373349Swentaoy #include <sys/uio.h>
383349Swentaoy #include <sys/stat.h>
393349Swentaoy #include <sys/ksynch.h>
403349Swentaoy #include <sys/modctl.h>
413349Swentaoy #include <sys/conf.h>
423349Swentaoy #include <sys/devops.h>
433349Swentaoy #include <sys/debug.h>
443349Swentaoy #include <sys/cmn_err.h>
453349Swentaoy #include <sys/ddi.h>
463349Swentaoy #include <sys/reboot.h>
473349Swentaoy #include <sys/sunddi.h>
483349Swentaoy #include <sys/signal.h>
493349Swentaoy #include <sys/ntwdt.h>
503349Swentaoy #include <sys/note.h>
513349Swentaoy
523349Swentaoy /*
533349Swentaoy * tunables
543349Swentaoy */
553349Swentaoy int ntwdt_disable_timeout_action = 0;
563349Swentaoy
573349Swentaoy #ifdef DEBUG
583349Swentaoy
593349Swentaoy int ntwdt_debug = 0; /* ntwdt debug flag, dbg all for now. */
603349Swentaoy
613349Swentaoy /*
623349Swentaoy * Flags to set in ntwdt_debug.
633349Swentaoy */
643349Swentaoy #define NTWDT_DBG_ENTRY 0x00000001 /* drv entry points */
653349Swentaoy #define NTWDT_DBG_IOCTL 0x00000002 /* ioctl's */
663349Swentaoy #define NTWDT_DBG_NTWDT 0x00000004 /* other ntwdt debug */
673349Swentaoy
683349Swentaoy #define NTWDT_DBG(flag, msg) \
693349Swentaoy { if ((ntwdt_debug) & (flag)) (void) printf msg; }
703349Swentaoy #else /* DEBUG */
713349Swentaoy #define NTWDT_DBG(flag, msg)
723349Swentaoy #endif /* DEBUG */
733349Swentaoy
743349Swentaoy #define NTWDT_MINOR_NODE "awdt"
753349Swentaoy #define getstate(minor) \
763349Swentaoy ((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor)))
773349Swentaoy
783349Swentaoy /*
793349Swentaoy * The ntwdt cyclic interval in nanosecond unit as cyclic subsystem supports
803349Swentaoy * nanosecond resolution.
813349Swentaoy */
823349Swentaoy #define NTWDT_CYCLIC_INTERVAL NANOSEC /* 1 seconds */
833349Swentaoy
843349Swentaoy /*
853349Swentaoy * The ntwdt decrement interval in 1 second resolution.
863349Swentaoy */
873349Swentaoy #define NTWDT_DECREMENT_INTERVAL 1 /* 1 second */
883349Swentaoy
893349Swentaoy /*
903349Swentaoy * ntwdt_watchdog_flags and macros to set/clear one bit in it.
913349Swentaoy */
923349Swentaoy #define NTWDT_FLAG_SKIP_CYCLIC 0x1 /* skip next cyclic */
933349Swentaoy
943349Swentaoy #define NTWDT_MAX_TIMEOUT (3 * 60 * 60) /* 3 hours */
953349Swentaoy
963349Swentaoy #define WDT_MIN_COREAPI_MAJOR 1
973349Swentaoy #define WDT_MIN_COREAPI_MINOR 1
983349Swentaoy
993349Swentaoy /*
1003349Swentaoy * Application watchdog state.
1013349Swentaoy */
1023349Swentaoy typedef struct ntwdt_runstate {
1033349Swentaoy kmutex_t ntwdt_runstate_mutex;
1043349Swentaoy ddi_iblock_cookie_t ntwdt_runstate_mtx_cookie;
1053349Swentaoy int ntwdt_watchdog_enabled; /* wdog enabled ? */
1063349Swentaoy int ntwdt_reset_enabled; /* reset enabled ? */
1073349Swentaoy int ntwdt_timer_running; /* wdog running ? */
1083349Swentaoy int ntwdt_watchdog_expired; /* wdog expired ? */
1093349Swentaoy uint32_t ntwdt_time_remaining; /* expiration timer */
1103349Swentaoy uint32_t ntwdt_watchdog_timeout; /* timeout in seconds */
1113349Swentaoy hrtime_t ntwdt_cyclic_interval; /* cyclic interval */
1123349Swentaoy cyc_handler_t ntwdt_cycl_hdlr;
1133349Swentaoy cyc_time_t ntwdt_cycl_time;
1143349Swentaoy uint32_t ntwdt_watchdog_flags;
1153349Swentaoy } ntwdt_runstate_t;
1163349Swentaoy
1173349Swentaoy /*
1183349Swentaoy * softstate of NTWDT
1193349Swentaoy */
1203349Swentaoy typedef struct {
1213349Swentaoy kmutex_t ntwdt_mutex;
1223349Swentaoy dev_info_t *ntwdt_dip; /* dip */
1233349Swentaoy int ntwdt_open_flag; /* file open ? */
1243349Swentaoy ntwdt_runstate_t *ntwdt_run_state; /* wdog state */
1253349Swentaoy cyclic_id_t ntwdt_cycl_id;
1263349Swentaoy } ntwdt_state_t;
1273349Swentaoy
1283349Swentaoy static void *ntwdt_statep; /* softstate */
1293349Swentaoy static dev_info_t *ntwdt_dip;
1303349Swentaoy
1313349Swentaoy static ddi_softintr_t ntwdt_cyclic_softint_id;
1323349Swentaoy
1333349Swentaoy static int ntwdt_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1343349Swentaoy static int ntwdt_attach(dev_info_t *, ddi_attach_cmd_t);
1353349Swentaoy static int ntwdt_detach(dev_info_t *, ddi_detach_cmd_t);
1363349Swentaoy static int ntwdt_open(dev_t *, int, int, cred_t *);
1373349Swentaoy static int ntwdt_close(dev_t, int, int, cred_t *);
1383349Swentaoy static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1393349Swentaoy
1403349Swentaoy static int ntwdt_chk_watchdog_support();
1413349Swentaoy static void ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state);
1423349Swentaoy static void ntwdt_cyclic_pat(void);
1433349Swentaoy static uint_t ntwdt_cyclic_softint(caddr_t arg);
1443349Swentaoy static void ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr);
1453349Swentaoy static void ntwdt_stop_timer_lock(void *arg);
1463349Swentaoy static void ntwdt_stop_timer(void *arg);
1473349Swentaoy static void ntwdt_enforce_timeout();
1483349Swentaoy
1493349Swentaoy static struct cb_ops ntwdt_cb_ops = {
1503349Swentaoy ntwdt_open, /* cb_open */
1513349Swentaoy ntwdt_close, /* cb_close */
1523349Swentaoy nodev, /* cb_strategy */
1533349Swentaoy nodev, /* cb_print */
1543349Swentaoy nodev, /* cb_dump */
1553349Swentaoy nodev, /* cb_read */
1563349Swentaoy nodev, /* cb_write */
1573349Swentaoy ntwdt_ioctl, /* cb_ioctl */
1583349Swentaoy nodev, /* cb_devmap */
1593349Swentaoy nodev, /* cb_mmap */
1603349Swentaoy nodev, /* cb_segmap */
1613349Swentaoy nochpoll, /* cb_chpoll */
1623349Swentaoy ddi_prop_op, /* cb_prop_op */
1633349Swentaoy NULL, /* cb_str */
1643349Swentaoy D_NEW | D_MP /* cb_flag */
1653349Swentaoy };
1663349Swentaoy
1673349Swentaoy static struct dev_ops ntwdt_dev_ops = {
1683349Swentaoy DEVO_REV, /* devo_rev */
1693349Swentaoy 0, /* devo_refcnt */
1703349Swentaoy ntwdt_info, /* devo_info */
1713349Swentaoy nulldev, /* devo_identify */
1723349Swentaoy nulldev, /* devo_probe */
1733349Swentaoy ntwdt_attach, /* devo_attach */
1743349Swentaoy ntwdt_detach, /* devo_detach */
1753349Swentaoy nodev, /* devo_reset */
1763349Swentaoy &ntwdt_cb_ops, /* devo_cb_ops */
1773349Swentaoy NULL, /* devo_bus_ops */
1787656SSherry.Moore@Sun.COM nulldev, /* devo_power */
1797656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1803349Swentaoy };
1813349Swentaoy
1823349Swentaoy static struct modldrv modldrv = {
1833349Swentaoy &mod_driverops,
1847656SSherry.Moore@Sun.COM "Application Watchdog Driver",
1853349Swentaoy &ntwdt_dev_ops
1863349Swentaoy };
1873349Swentaoy
1883349Swentaoy static struct modlinkage modlinkage = {
1893349Swentaoy MODREV_1,
1903349Swentaoy (void *)&modldrv,
1913349Swentaoy NULL
1923349Swentaoy };
1933349Swentaoy
1943349Swentaoy int
_init(void)1953349Swentaoy _init(void)
1963349Swentaoy {
1973349Swentaoy int error = 0;
1983349Swentaoy
1993349Swentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_init"));
2003349Swentaoy
2013349Swentaoy /* Initialize the soft state structures */
2023349Swentaoy if ((error = ddi_soft_state_init(&ntwdt_statep,
2033349Swentaoy sizeof (ntwdt_state_t), 1)) != 0) {
2043349Swentaoy return (error);
2053349Swentaoy }
2063349Swentaoy
2073349Swentaoy /* Install the loadable module */
2083349Swentaoy if ((error = mod_install(&modlinkage)) != 0) {
2093349Swentaoy ddi_soft_state_fini(&ntwdt_statep);
2103349Swentaoy }
2113349Swentaoy return (error);
2123349Swentaoy }
2133349Swentaoy
2143349Swentaoy int
_info(struct modinfo * modinfop)2153349Swentaoy _info(struct modinfo *modinfop)
2163349Swentaoy {
2173349Swentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_info"));
2183349Swentaoy
2193349Swentaoy return (mod_info(&modlinkage, modinfop));
2203349Swentaoy }
2213349Swentaoy
2223349Swentaoy int
_fini(void)2233349Swentaoy _fini(void)
2243349Swentaoy {
2253349Swentaoy int retval;
2263349Swentaoy
2273349Swentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_fini"));
2283349Swentaoy
2293349Swentaoy if ((retval = mod_remove(&modlinkage)) == 0) {
2303349Swentaoy ddi_soft_state_fini(&ntwdt_statep);
2313349Swentaoy }
2323349Swentaoy
2333349Swentaoy return (retval);
2343349Swentaoy }
2353349Swentaoy
2363349Swentaoy static int
ntwdt_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2373349Swentaoy ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2383349Swentaoy {
2393349Swentaoy int instance;
2403349Swentaoy ntwdt_state_t *ntwdt_ptr = NULL; /* pointer to ntwdt_runstatep */
2413349Swentaoy ntwdt_runstate_t *ntwdt_runstatep = NULL;
2423349Swentaoy cyc_handler_t *hdlr = NULL;
2433349Swentaoy
2443349Swentaoy switch (cmd) {
2453349Swentaoy case DDI_ATTACH:
2463349Swentaoy break;
2473349Swentaoy
2483349Swentaoy case DDI_RESUME:
2493349Swentaoy return (DDI_SUCCESS);
2503349Swentaoy
2513349Swentaoy default:
2523349Swentaoy return (DDI_FAILURE);
2533349Swentaoy }
2543349Swentaoy
2553349Swentaoy if (ntwdt_chk_watchdog_support() != 0) {
2563349Swentaoy return (DDI_FAILURE);
2573349Swentaoy }
2583349Swentaoy
2593349Swentaoy instance = ddi_get_instance(dip);
2603349Swentaoy ASSERT(instance == 0);
2613349Swentaoy
2623349Swentaoy if (ddi_soft_state_zalloc(ntwdt_statep, instance) != DDI_SUCCESS) {
2633349Swentaoy return (DDI_FAILURE);
2643349Swentaoy }
2653349Swentaoy ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
2663349Swentaoy ASSERT(ntwdt_ptr != NULL);
2673349Swentaoy
2683349Swentaoy ntwdt_dip = dip;
2693349Swentaoy
2703349Swentaoy ntwdt_ptr->ntwdt_dip = dip;
2713349Swentaoy ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
2723349Swentaoy mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL,
2733349Swentaoy MUTEX_DRIVER, NULL);
2743349Swentaoy
2753349Swentaoy /*
2763349Swentaoy * Initialize the watchdog structure
2773349Swentaoy */
2783349Swentaoy ntwdt_ptr->ntwdt_run_state =
2793349Swentaoy kmem_zalloc(sizeof (ntwdt_runstate_t), KM_SLEEP);
2803349Swentaoy ntwdt_runstatep = ntwdt_ptr->ntwdt_run_state;
2813349Swentaoy
2823349Swentaoy if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
2833349Swentaoy &ntwdt_runstatep->ntwdt_runstate_mtx_cookie) != DDI_SUCCESS) {
2843349Swentaoy cmn_err(CE_WARN, "init of iblock cookie failed "
2853349Swentaoy "for ntwdt_runstate_mutex");
2863349Swentaoy goto err1;
2873349Swentaoy } else {
2883349Swentaoy mutex_init(&ntwdt_runstatep->ntwdt_runstate_mutex,
2893349Swentaoy NULL,
2903349Swentaoy MUTEX_DRIVER,
2913349Swentaoy (void *)ntwdt_runstatep->ntwdt_runstate_mtx_cookie);
2923349Swentaoy }
2933349Swentaoy
2943349Swentaoy /* Cyclic fires once per second: */
2953349Swentaoy ntwdt_runstatep->ntwdt_cyclic_interval = NTWDT_CYCLIC_INTERVAL;
2963349Swentaoy
2973349Swentaoy /* init the Cyclic that drives the NTWDT */
2983349Swentaoy hdlr = &ntwdt_runstatep->ntwdt_cycl_hdlr;
2993349Swentaoy hdlr->cyh_level = CY_LOCK_LEVEL;
3003349Swentaoy hdlr->cyh_func = (cyc_func_t)ntwdt_cyclic_pat;
3013349Swentaoy hdlr->cyh_arg = NULL;
3023349Swentaoy
3033349Swentaoy /* Softint that will be triggered by Cyclic that drives NTWDT */
3043349Swentaoy if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id,
3053349Swentaoy NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr)
3063349Swentaoy != DDI_SUCCESS) {
3073349Swentaoy cmn_err(CE_WARN, "failed to add cyclic softintr");
3083349Swentaoy goto err2;
3093349Swentaoy }
3103349Swentaoy
3113349Swentaoy /*
3123349Swentaoy * Create Minor Node as last activity. This prevents
3133349Swentaoy * application from accessing our implementation until it
3143349Swentaoy * is initialized.
3153349Swentaoy */
3163349Swentaoy if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0,
3173349Swentaoy DDI_PSEUDO, NULL) == DDI_FAILURE) {
3183349Swentaoy cmn_err(CE_WARN, "failed to create Minor Node: %s",
3193349Swentaoy NTWDT_MINOR_NODE);
3203349Swentaoy goto err3;
3213349Swentaoy }
3223349Swentaoy
3233349Swentaoy /* Display our driver info in the banner */
3243349Swentaoy ddi_report_dev(dip);
3253349Swentaoy
3263349Swentaoy return (DDI_SUCCESS);
3273349Swentaoy
3283349Swentaoy err3:
3293349Swentaoy ddi_remove_softintr(ntwdt_cyclic_softint_id);
3303349Swentaoy err2:
3313349Swentaoy mutex_destroy(&ntwdt_runstatep->ntwdt_runstate_mutex);
3323349Swentaoy err1:
3333349Swentaoy /* clean up the driver stuff here */
3343349Swentaoy kmem_free(ntwdt_runstatep, sizeof (ntwdt_runstate_t));
3353349Swentaoy ntwdt_ptr->ntwdt_run_state = NULL;
3363349Swentaoy mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
3373349Swentaoy ddi_soft_state_free(ntwdt_statep, instance);
3383349Swentaoy ntwdt_dip = NULL;
3393349Swentaoy
3403349Swentaoy return (DDI_FAILURE);
3413349Swentaoy }
3423349Swentaoy
3433349Swentaoy /*ARGSUSED*/
3443349Swentaoy static int
ntwdt_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3453349Swentaoy ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3463349Swentaoy {
3473349Swentaoy dev_t dev;
3483349Swentaoy int instance;
3493349Swentaoy int error = DDI_SUCCESS;
3503349Swentaoy
3513349Swentaoy switch (infocmd) {
3523349Swentaoy case DDI_INFO_DEVT2DEVINFO:
3533349Swentaoy dev = (dev_t)arg;
3543349Swentaoy if (getminor(dev) == 0) {
3553349Swentaoy *result = (void *)ntwdt_dip;
3563349Swentaoy } else {
3573349Swentaoy error = DDI_FAILURE;
3583349Swentaoy }
3593349Swentaoy break;
3603349Swentaoy
3613349Swentaoy case DDI_INFO_DEVT2INSTANCE:
3623349Swentaoy dev = (dev_t)arg;
3633349Swentaoy instance = getminor(dev);
3643349Swentaoy *result = (void *)(uintptr_t)instance;
3653349Swentaoy break;
3663349Swentaoy
3673349Swentaoy default:
3683349Swentaoy error = DDI_FAILURE;
3693349Swentaoy
3703349Swentaoy }
3713349Swentaoy
3723349Swentaoy return (error);
3733349Swentaoy }
3743349Swentaoy
3753349Swentaoy /*ARGSUSED*/
3763349Swentaoy static int
ntwdt_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3773349Swentaoy ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3783349Swentaoy {
3793349Swentaoy int instance = ddi_get_instance(dip);
3803349Swentaoy ntwdt_state_t *ntwdt_ptr = NULL;
3813349Swentaoy
3823349Swentaoy ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
3833349Swentaoy if (ntwdt_ptr == NULL) {
3843349Swentaoy return (DDI_FAILURE);
3853349Swentaoy }
3863349Swentaoy
3873349Swentaoy switch (cmd) {
3883349Swentaoy case DDI_SUSPEND:
3893349Swentaoy return (DDI_SUCCESS);
3903349Swentaoy
3913349Swentaoy case DDI_DETACH:
3923349Swentaoy /*
3933349Swentaoy * release resources in opposite (LIFO) order as
3943349Swentaoy * were allocated in attach.
3953349Swentaoy */
3963349Swentaoy ddi_remove_minor_node(dip, NULL);
3973349Swentaoy ntwdt_stop_timer_lock((void *)ntwdt_ptr);
3983349Swentaoy ddi_remove_softintr(ntwdt_cyclic_softint_id);
3993349Swentaoy
4003349Swentaoy mutex_destroy(
4017656SSherry.Moore@Sun.COM &ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
4023349Swentaoy kmem_free(ntwdt_ptr->ntwdt_run_state,
4037656SSherry.Moore@Sun.COM sizeof (ntwdt_runstate_t));
4043349Swentaoy ntwdt_ptr->ntwdt_run_state = NULL;
4053349Swentaoy
4063349Swentaoy mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
4073349Swentaoy
4083349Swentaoy ddi_soft_state_free(ntwdt_statep, instance);
4093349Swentaoy
4103349Swentaoy ntwdt_dip = NULL;
4113349Swentaoy return (DDI_SUCCESS);
4123349Swentaoy
4133349Swentaoy default:
4143349Swentaoy return (DDI_FAILURE);
4153349Swentaoy }
4163349Swentaoy }
4173349Swentaoy
4183349Swentaoy /*ARGSUSED*/
4193349Swentaoy static int
ntwdt_open(dev_t * devp,int flag,int otyp,cred_t * credp)4203349Swentaoy ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4213349Swentaoy {
4223349Swentaoy int instance = getminor(*devp);
4233349Swentaoy int retval = 0;
4243349Swentaoy ntwdt_state_t *ntwdt_ptr = getstate(instance);
4253349Swentaoy
4263349Swentaoy if (ntwdt_ptr == NULL) {
4273349Swentaoy return (ENXIO);
4283349Swentaoy }
4293349Swentaoy
4303349Swentaoy /*
4313349Swentaoy * ensure caller is a priviledged process.
4323349Swentaoy */
4333349Swentaoy if (drv_priv(credp) != 0) {
4343349Swentaoy return (EPERM);
4353349Swentaoy }
4363349Swentaoy
4373349Swentaoy mutex_enter(&ntwdt_ptr->ntwdt_mutex);
4383349Swentaoy if (ntwdt_ptr->ntwdt_open_flag) {
4393349Swentaoy retval = EAGAIN;
4403349Swentaoy } else {
4413349Swentaoy ntwdt_ptr->ntwdt_open_flag = 1;
4423349Swentaoy }
4433349Swentaoy mutex_exit(&ntwdt_ptr->ntwdt_mutex);
4443349Swentaoy
4453349Swentaoy return (retval);
4463349Swentaoy }
4473349Swentaoy
4483349Swentaoy /*ARGSUSED*/
4493349Swentaoy static int
ntwdt_close(dev_t dev,int flag,int otyp,cred_t * credp)4503349Swentaoy ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp)
4513349Swentaoy {
4523349Swentaoy int instance = getminor(dev);
4533349Swentaoy ntwdt_state_t *ntwdt_ptr = getstate(instance);
4543349Swentaoy
4553349Swentaoy if (ntwdt_ptr == NULL) {
4563349Swentaoy return (ENXIO);
4573349Swentaoy }
4583349Swentaoy
4593349Swentaoy mutex_enter(&ntwdt_ptr->ntwdt_mutex);
4603349Swentaoy ntwdt_ptr->ntwdt_open_flag = 0;
4613349Swentaoy mutex_exit(&ntwdt_ptr->ntwdt_mutex);
4623349Swentaoy
4633349Swentaoy return (0);
4643349Swentaoy }
4653349Swentaoy
4663349Swentaoy /*ARGSUSED*/
4673349Swentaoy static int
ntwdt_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)4683349Swentaoy ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
4693349Swentaoy int *rvalp)
4703349Swentaoy {
4713349Swentaoy int instance = getminor(dev);
4723349Swentaoy int retval = 0;
4733349Swentaoy ntwdt_state_t *ntwdt_ptr = NULL;
4743349Swentaoy ntwdt_runstate_t *ntwdt_state;
4753349Swentaoy lom_dogstate_t lom_dogstate;
4763349Swentaoy lom_dogctl_t lom_dogctl;
4773349Swentaoy uint32_t lom_dogtime;
4783349Swentaoy
4793349Swentaoy if ((ntwdt_ptr = getstate(instance)) == NULL) {
4803349Swentaoy return (ENXIO);
4813349Swentaoy }
4823349Swentaoy
4833349Swentaoy ntwdt_state = ntwdt_ptr->ntwdt_run_state;
4843349Swentaoy
4853349Swentaoy switch (cmd) {
4863349Swentaoy case LOMIOCDOGSTATE:
4873349Swentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
4883349Swentaoy lom_dogstate.reset_enable = ntwdt_state->ntwdt_reset_enabled;
4893349Swentaoy lom_dogstate.dog_enable = ntwdt_state->ntwdt_watchdog_enabled;
4903349Swentaoy lom_dogstate.dog_timeout = ntwdt_state->ntwdt_watchdog_timeout;
4913349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
4923349Swentaoy
4933349Swentaoy if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg,
4947656SSherry.Moore@Sun.COM sizeof (lom_dogstate_t), mode) != 0) {
4953349Swentaoy retval = EFAULT;
4963349Swentaoy }
4973349Swentaoy break;
4983349Swentaoy
4993349Swentaoy case LOMIOCDOGCTL:
5003349Swentaoy if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl,
5017656SSherry.Moore@Sun.COM sizeof (lom_dogctl_t), mode) != 0) {
5023349Swentaoy retval = EFAULT;
5033349Swentaoy break;
5043349Swentaoy }
5053349Swentaoy
5063349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("reset_enable: %d, and dog_enable: "
5077656SSherry.Moore@Sun.COM "%d, watchdog_timeout %d", lom_dogctl.reset_enable,
5087656SSherry.Moore@Sun.COM lom_dogctl.dog_enable,
5097656SSherry.Moore@Sun.COM ntwdt_state->ntwdt_watchdog_timeout));
5103349Swentaoy /*
5113349Swentaoy * ignore request to enable reset while disabling watchdog.
5123349Swentaoy */
5133349Swentaoy if (!lom_dogctl.dog_enable && lom_dogctl.reset_enable) {
5143349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("invalid combination of "
5157656SSherry.Moore@Sun.COM "reset_enable: %d, and dog_enable: %d",
5167656SSherry.Moore@Sun.COM lom_dogctl.reset_enable,
5177656SSherry.Moore@Sun.COM lom_dogctl.dog_enable));
5183349Swentaoy retval = EINVAL;
5193349Swentaoy break;
5203349Swentaoy }
5213349Swentaoy
5223349Swentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
5233349Swentaoy
5243349Swentaoy if (ntwdt_state->ntwdt_watchdog_timeout == 0) {
5253349Swentaoy /*
5263349Swentaoy * the LOMIOCDOGTIME has never been used to setup
5273349Swentaoy * a valid timeout.
5283349Swentaoy */
5293349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("timeout has not been set"
5307656SSherry.Moore@Sun.COM "watchdog_timeout: %d",
5317656SSherry.Moore@Sun.COM ntwdt_state->ntwdt_watchdog_timeout));
5323349Swentaoy retval = EINVAL;
5333349Swentaoy goto end;
5343349Swentaoy }
5353349Swentaoy
5363349Swentaoy /*
5373349Swentaoy * Store the user specified state in the softstate.
5383349Swentaoy */
5393349Swentaoy ntwdt_state->ntwdt_reset_enabled = lom_dogctl.reset_enable;
5403349Swentaoy ntwdt_state->ntwdt_watchdog_enabled = lom_dogctl.dog_enable;
5413349Swentaoy
5423349Swentaoy if (ntwdt_state->ntwdt_watchdog_enabled != 0) {
5433349Swentaoy /*
5443349Swentaoy * The user wants to enable the watchdog.
5453349Swentaoy * Arm the watchdog and start the cyclic.
5463349Swentaoy */
5473349Swentaoy ntwdt_arm_watchdog(ntwdt_state);
5483349Swentaoy
5493349Swentaoy if (ntwdt_state->ntwdt_timer_running == 0) {
5503349Swentaoy ntwdt_start_timer(ntwdt_ptr);
5513349Swentaoy }
5523349Swentaoy
5533349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is enabled"));
5543349Swentaoy } else {
5553349Swentaoy /*
5563349Swentaoy * The user wants to disable the watchdog.
5573349Swentaoy */
5583349Swentaoy if (ntwdt_state->ntwdt_timer_running != 0) {
5593349Swentaoy ntwdt_stop_timer(ntwdt_ptr);
5603349Swentaoy }
5613349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is disabled"));
5623349Swentaoy }
5633349Swentaoy
5643349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
5653349Swentaoy break;
5663349Swentaoy
5673349Swentaoy case LOMIOCDOGTIME:
5683349Swentaoy if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime,
5697656SSherry.Moore@Sun.COM sizeof (uint32_t), mode) != 0) {
5703349Swentaoy retval = EFAULT;
5713349Swentaoy break;
5723349Swentaoy }
5733349Swentaoy
5743349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set timeout: %d",
5757656SSherry.Moore@Sun.COM lom_dogtime));
5763349Swentaoy
5773349Swentaoy /*
5783349Swentaoy * Ensure specified timeout is valid.
5793349Swentaoy */
5803349Swentaoy if ((lom_dogtime == 0) ||
5817656SSherry.Moore@Sun.COM (lom_dogtime > (uint32_t)NTWDT_MAX_TIMEOUT)) {
5823349Swentaoy retval = EINVAL;
5833349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set invalid "
5847656SSherry.Moore@Sun.COM "timeout: %d", (int)TICK_TO_MSEC(lom_dogtime)));
5853349Swentaoy break;
5863349Swentaoy }
5873349Swentaoy
5883349Swentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
5893349Swentaoy
5903349Swentaoy ntwdt_state->ntwdt_watchdog_timeout = lom_dogtime;
5913349Swentaoy
5923349Swentaoy /*
5933349Swentaoy * If awdt is currently running, re-arm it with the
5943349Swentaoy * newly-specified timeout value.
5953349Swentaoy */
5963349Swentaoy if (ntwdt_state->ntwdt_timer_running != 0) {
5973349Swentaoy ntwdt_arm_watchdog(ntwdt_state);
5983349Swentaoy }
5993349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
6003349Swentaoy break;
6013349Swentaoy
6023349Swentaoy case LOMIOCDOGPAT:
6033349Swentaoy /*
6043349Swentaoy * Allow user to pat the watchdog timer.
6053349Swentaoy */
6063349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("DOGPAT is invoked"));
6073349Swentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
6083349Swentaoy
6093349Swentaoy /*
6103349Swentaoy * If awdt is not enabled or underlying cyclic is not
6113349Swentaoy * running, exit.
6123349Swentaoy */
6133349Swentaoy if (!(ntwdt_state->ntwdt_watchdog_enabled &&
6147656SSherry.Moore@Sun.COM ntwdt_state->ntwdt_timer_running)) {
6153349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("PAT: AWDT not enabled"));
6163349Swentaoy goto end;
6173349Swentaoy }
6183349Swentaoy
6193349Swentaoy if (ntwdt_state->ntwdt_watchdog_expired == 0) {
6203349Swentaoy /*
6213349Swentaoy * re-arm the awdt.
6223349Swentaoy */
6233349Swentaoy ntwdt_arm_watchdog(ntwdt_state);
6243349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT patted, "
6257656SSherry.Moore@Sun.COM "remainning seconds: %d",
6267656SSherry.Moore@Sun.COM ntwdt_state->ntwdt_time_remaining));
6273349Swentaoy }
6283349Swentaoy
6293349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
6303349Swentaoy break;
6313349Swentaoy
6323349Swentaoy default:
6333349Swentaoy retval = EINVAL;
6343349Swentaoy break;
6353349Swentaoy }
6363349Swentaoy return (retval);
6373349Swentaoy end:
6383349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
6393349Swentaoy return (retval);
6403349Swentaoy }
6413349Swentaoy
6423349Swentaoy static void
ntwdt_cyclic_pat(void)6433349Swentaoy ntwdt_cyclic_pat(void)
6443349Swentaoy {
6453349Swentaoy ddi_trigger_softintr(ntwdt_cyclic_softint_id);
6463349Swentaoy }
6473349Swentaoy
6483349Swentaoy static uint_t
ntwdt_cyclic_softint(caddr_t arg)6493349Swentaoy ntwdt_cyclic_softint(caddr_t arg)
6503349Swentaoy {
6513349Swentaoy /*LINTED E_BAD_PTR_CAST_ALIGN*/
6523349Swentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
6533349Swentaoy ntwdt_runstate_t *ntwdt_state;
6543349Swentaoy
6553349Swentaoy ntwdt_state = ntwdt_ptr->ntwdt_run_state;
6563349Swentaoy
6573349Swentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
6583349Swentaoy
6593349Swentaoy if ((ntwdt_state->ntwdt_watchdog_flags & NTWDT_FLAG_SKIP_CYCLIC) != 0) {
6603349Swentaoy ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
6613349Swentaoy goto end;
6623349Swentaoy }
6633349Swentaoy
6643349Swentaoy if ((ntwdt_state->ntwdt_timer_running == 0) ||
6657656SSherry.Moore@Sun.COM (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) ||
6667656SSherry.Moore@Sun.COM (ntwdt_state->ntwdt_watchdog_enabled == 0)) {
6673349Swentaoy goto end;
6683349Swentaoy }
6693349Swentaoy
6703349Swentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("cyclic_softint: %d"
671*11066Srafael.vanoni@sun.com "ddi_get_lbolt64(): %d\n", ntwdt_state->ntwdt_watchdog_timeout,
672*11066Srafael.vanoni@sun.com (int)TICK_TO_MSEC(ddi_get_lbolt64())));
6733349Swentaoy
6743349Swentaoy /*
6753349Swentaoy * Decrement the virtual watchdog timer and check if it has expired.
6763349Swentaoy */
6773349Swentaoy ntwdt_state->ntwdt_time_remaining -= NTWDT_DECREMENT_INTERVAL;
6783349Swentaoy
6793349Swentaoy if (ntwdt_state->ntwdt_time_remaining == 0) {
6803349Swentaoy cmn_err(CE_WARN, "application-watchdog expired");
6813349Swentaoy ntwdt_state->ntwdt_watchdog_expired = 1;
6823349Swentaoy
6833349Swentaoy if (ntwdt_state->ntwdt_reset_enabled != 0) {
6843349Swentaoy /*
6853349Swentaoy * The user wants to reset the system.
6863349Swentaoy */
6873349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
6883349Swentaoy
6893349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("recovery being done"));
6903349Swentaoy ntwdt_enforce_timeout();
6913349Swentaoy } else {
6923349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("no recovery being done"));
6933349Swentaoy ntwdt_state->ntwdt_watchdog_enabled = 0;
6943349Swentaoy }
6953349Swentaoy
6963349Swentaoy /*
6973349Swentaoy * Schedule Callout to stop the cyclic.
6983349Swentaoy */
6993349Swentaoy (void) timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0);
7003349Swentaoy } else {
7013349Swentaoy _NOTE(EMPTY)
7023349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("time remaining in AWDT: %d secs",
7037656SSherry.Moore@Sun.COM (int)TICK_TO_MSEC(ntwdt_state->ntwdt_time_remaining)));
7043349Swentaoy }
7053349Swentaoy
7063349Swentaoy end:
7073349Swentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
7083349Swentaoy return (DDI_INTR_CLAIMED);
7093349Swentaoy }
7103349Swentaoy
7113349Swentaoy static void
ntwdt_arm_watchdog(ntwdt_runstate_t * ntwdt_state)7123349Swentaoy ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state)
7133349Swentaoy {
7143349Swentaoy ntwdt_state->ntwdt_time_remaining = ntwdt_state->ntwdt_watchdog_timeout;
7153349Swentaoy
7163349Swentaoy if (ntwdt_state->ntwdt_timer_running != 0) {
7173349Swentaoy ntwdt_state->ntwdt_watchdog_flags |= NTWDT_FLAG_SKIP_CYCLIC;
7183349Swentaoy } else {
7193349Swentaoy ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
7203349Swentaoy }
7213349Swentaoy }
7223349Swentaoy
7233349Swentaoy static void
ntwdt_start_timer(ntwdt_state_t * ntwdt_ptr)7243349Swentaoy ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr)
7253349Swentaoy {
7263349Swentaoy ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state;
7273349Swentaoy cyc_handler_t *hdlr = &ntwdt_state->ntwdt_cycl_hdlr;
7283349Swentaoy cyc_time_t *when = &ntwdt_state->ntwdt_cycl_time;
7293349Swentaoy
7303349Swentaoy /*
7313349Swentaoy * Init the cyclic.
7323349Swentaoy */
7333349Swentaoy when->cyt_interval = ntwdt_state->ntwdt_cyclic_interval;
7343349Swentaoy when->cyt_when = gethrtime() + when->cyt_interval;
7353349Swentaoy
7363349Swentaoy ntwdt_state->ntwdt_watchdog_expired = 0;
7373349Swentaoy ntwdt_state->ntwdt_timer_running = 1;
7383349Swentaoy
7393349Swentaoy mutex_enter(&cpu_lock);
7403349Swentaoy if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) {
7413349Swentaoy ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when);
7423349Swentaoy }
7433349Swentaoy mutex_exit(&cpu_lock);
7443349Swentaoy
7453349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is started"));
7463349Swentaoy }
7473349Swentaoy
7483349Swentaoy static void
ntwdt_stop_timer(void * arg)7493349Swentaoy ntwdt_stop_timer(void *arg)
7503349Swentaoy {
7513349Swentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
7523349Swentaoy ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state;
7533349Swentaoy
7543349Swentaoy mutex_enter(&cpu_lock);
7553349Swentaoy if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) {
7563349Swentaoy cyclic_remove(ntwdt_ptr->ntwdt_cycl_id);
7573349Swentaoy }
7583349Swentaoy mutex_exit(&cpu_lock);
7593349Swentaoy
7603349Swentaoy ntwdt_state->ntwdt_watchdog_flags = 0;
7613349Swentaoy ntwdt_state->ntwdt_timer_running = 0;
7623349Swentaoy ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
7633349Swentaoy
7643349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is stopped"));
7653349Swentaoy }
7663349Swentaoy
7673349Swentaoy /*
7683349Swentaoy * This is a wrapper function for ntwdt_stop_timer as some callers
7693349Swentaoy * will already have the appropriate mutex locked, and others not.
7703349Swentaoy */
7713349Swentaoy static void
ntwdt_stop_timer_lock(void * arg)7723349Swentaoy ntwdt_stop_timer_lock(void *arg)
7733349Swentaoy {
7743349Swentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
7753349Swentaoy
7763349Swentaoy mutex_enter(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
7773349Swentaoy ntwdt_stop_timer(arg);
7783349Swentaoy mutex_exit(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
7793349Swentaoy }
7803349Swentaoy
7813349Swentaoy static void
ntwdt_enforce_timeout()7823349Swentaoy ntwdt_enforce_timeout()
7833349Swentaoy {
7843349Swentaoy if (ntwdt_disable_timeout_action != 0) {
7853349Swentaoy cmn_err(CE_NOTE, "Appication watchdog timer expired, "
7867656SSherry.Moore@Sun.COM "taking no action");
7873349Swentaoy return;
7883349Swentaoy }
7893349Swentaoy
7903349Swentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("dump cores and rebooting ..."));
7913349Swentaoy
7923349Swentaoy (void) kadmin(A_DUMP, AD_BOOT, NULL, kcred);
7933349Swentaoy cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed");
7943349Swentaoy _NOTE(NOTREACHED);
7953349Swentaoy }
7963349Swentaoy
7973349Swentaoy static int
ntwdt_chk_watchdog_support()7983349Swentaoy ntwdt_chk_watchdog_support()
7993349Swentaoy {
8003349Swentaoy int retval = 0;
8013349Swentaoy
8023349Swentaoy if ((boothowto & RB_DEBUG) != 0) {
8033349Swentaoy cmn_err(CE_WARN, "kernel debugger was booted; "
8043349Swentaoy "application watchdog is not available.");
8053349Swentaoy retval = ENOTSUP;
8063349Swentaoy }
8073349Swentaoy return (retval);
8083349Swentaoy }
809