1927a453eSwentaoy /* 2927a453eSwentaoy * CDDL HEADER START 3927a453eSwentaoy * 4927a453eSwentaoy * The contents of this file are subject to the terms of the 5927a453eSwentaoy * Common Development and Distribution License (the "License"). 6927a453eSwentaoy * You may not use this file except in compliance with the License. 7927a453eSwentaoy * 8927a453eSwentaoy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9927a453eSwentaoy * or http://www.opensolaris.org/os/licensing. 10927a453eSwentaoy * See the License for the specific language governing permissions 11927a453eSwentaoy * and limitations under the License. 12927a453eSwentaoy * 13927a453eSwentaoy * When distributing Covered Code, include this CDDL HEADER in each 14927a453eSwentaoy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15927a453eSwentaoy * If applicable, add the following below this CDDL HEADER, with the 16927a453eSwentaoy * fields enclosed by brackets "[]" replaced with your own identifying 17927a453eSwentaoy * information: Portions Copyright [yyyy] [name of copyright owner] 18927a453eSwentaoy * 19927a453eSwentaoy * CDDL HEADER END 20927a453eSwentaoy */ 21927a453eSwentaoy /* 22*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23927a453eSwentaoy * Use is subject to license terms. 24927a453eSwentaoy */ 25927a453eSwentaoy 26927a453eSwentaoy /* 27927a453eSwentaoy * sun4v application watchdog driver 28927a453eSwentaoy */ 29927a453eSwentaoy 30927a453eSwentaoy #include <sys/types.h> 31927a453eSwentaoy #include <sys/file.h> 32927a453eSwentaoy #include <sys/errno.h> 33927a453eSwentaoy #include <sys/open.h> 34927a453eSwentaoy #include <sys/callb.h> 35927a453eSwentaoy #include <sys/cred.h> 36927a453eSwentaoy #include <sys/cyclic.h> 37927a453eSwentaoy #include <sys/uio.h> 38927a453eSwentaoy #include <sys/stat.h> 39927a453eSwentaoy #include <sys/ksynch.h> 40927a453eSwentaoy #include <sys/modctl.h> 41927a453eSwentaoy #include <sys/conf.h> 42927a453eSwentaoy #include <sys/devops.h> 43927a453eSwentaoy #include <sys/debug.h> 44927a453eSwentaoy #include <sys/cmn_err.h> 45927a453eSwentaoy #include <sys/ddi.h> 46927a453eSwentaoy #include <sys/reboot.h> 47927a453eSwentaoy #include <sys/sunddi.h> 48927a453eSwentaoy #include <sys/signal.h> 49927a453eSwentaoy #include <sys/ntwdt.h> 50927a453eSwentaoy #include <sys/note.h> 51927a453eSwentaoy 52927a453eSwentaoy /* 53927a453eSwentaoy * tunables 54927a453eSwentaoy */ 55927a453eSwentaoy int ntwdt_disable_timeout_action = 0; 56927a453eSwentaoy 57927a453eSwentaoy #ifdef DEBUG 58927a453eSwentaoy 59927a453eSwentaoy int ntwdt_debug = 0; /* ntwdt debug flag, dbg all for now. */ 60927a453eSwentaoy 61927a453eSwentaoy /* 62927a453eSwentaoy * Flags to set in ntwdt_debug. 63927a453eSwentaoy */ 64927a453eSwentaoy #define NTWDT_DBG_ENTRY 0x00000001 /* drv entry points */ 65927a453eSwentaoy #define NTWDT_DBG_IOCTL 0x00000002 /* ioctl's */ 66927a453eSwentaoy #define NTWDT_DBG_NTWDT 0x00000004 /* other ntwdt debug */ 67927a453eSwentaoy 68927a453eSwentaoy #define NTWDT_DBG(flag, msg) \ 69927a453eSwentaoy { if ((ntwdt_debug) & (flag)) (void) printf msg; } 70927a453eSwentaoy #else /* DEBUG */ 71927a453eSwentaoy #define NTWDT_DBG(flag, msg) 72927a453eSwentaoy #endif /* DEBUG */ 73927a453eSwentaoy 74927a453eSwentaoy #define NTWDT_MINOR_NODE "awdt" 75927a453eSwentaoy #define getstate(minor) \ 76927a453eSwentaoy ((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor))) 77927a453eSwentaoy 78927a453eSwentaoy /* 79927a453eSwentaoy * The ntwdt cyclic interval in nanosecond unit as cyclic subsystem supports 80927a453eSwentaoy * nanosecond resolution. 81927a453eSwentaoy */ 82927a453eSwentaoy #define NTWDT_CYCLIC_INTERVAL NANOSEC /* 1 seconds */ 83927a453eSwentaoy 84927a453eSwentaoy /* 85927a453eSwentaoy * The ntwdt decrement interval in 1 second resolution. 86927a453eSwentaoy */ 87927a453eSwentaoy #define NTWDT_DECREMENT_INTERVAL 1 /* 1 second */ 88927a453eSwentaoy 89927a453eSwentaoy /* 90927a453eSwentaoy * ntwdt_watchdog_flags and macros to set/clear one bit in it. 91927a453eSwentaoy */ 92927a453eSwentaoy #define NTWDT_FLAG_SKIP_CYCLIC 0x1 /* skip next cyclic */ 93927a453eSwentaoy 94927a453eSwentaoy #define NTWDT_MAX_TIMEOUT (3 * 60 * 60) /* 3 hours */ 95927a453eSwentaoy 96927a453eSwentaoy #define WDT_MIN_COREAPI_MAJOR 1 97927a453eSwentaoy #define WDT_MIN_COREAPI_MINOR 1 98927a453eSwentaoy 99927a453eSwentaoy /* 100927a453eSwentaoy * Application watchdog state. 101927a453eSwentaoy */ 102927a453eSwentaoy typedef struct ntwdt_runstate { 103927a453eSwentaoy kmutex_t ntwdt_runstate_mutex; 104927a453eSwentaoy ddi_iblock_cookie_t ntwdt_runstate_mtx_cookie; 105927a453eSwentaoy int ntwdt_watchdog_enabled; /* wdog enabled ? */ 106927a453eSwentaoy int ntwdt_reset_enabled; /* reset enabled ? */ 107927a453eSwentaoy int ntwdt_timer_running; /* wdog running ? */ 108927a453eSwentaoy int ntwdt_watchdog_expired; /* wdog expired ? */ 109927a453eSwentaoy uint32_t ntwdt_time_remaining; /* expiration timer */ 110927a453eSwentaoy uint32_t ntwdt_watchdog_timeout; /* timeout in seconds */ 111927a453eSwentaoy hrtime_t ntwdt_cyclic_interval; /* cyclic interval */ 112927a453eSwentaoy cyc_handler_t ntwdt_cycl_hdlr; 113927a453eSwentaoy cyc_time_t ntwdt_cycl_time; 114927a453eSwentaoy uint32_t ntwdt_watchdog_flags; 115927a453eSwentaoy } ntwdt_runstate_t; 116927a453eSwentaoy 117927a453eSwentaoy /* 118927a453eSwentaoy * softstate of NTWDT 119927a453eSwentaoy */ 120927a453eSwentaoy typedef struct { 121927a453eSwentaoy kmutex_t ntwdt_mutex; 122927a453eSwentaoy dev_info_t *ntwdt_dip; /* dip */ 123927a453eSwentaoy int ntwdt_open_flag; /* file open ? */ 124927a453eSwentaoy ntwdt_runstate_t *ntwdt_run_state; /* wdog state */ 125927a453eSwentaoy cyclic_id_t ntwdt_cycl_id; 126927a453eSwentaoy } ntwdt_state_t; 127927a453eSwentaoy 128927a453eSwentaoy static void *ntwdt_statep; /* softstate */ 129927a453eSwentaoy static dev_info_t *ntwdt_dip; 130927a453eSwentaoy 131927a453eSwentaoy static ddi_softintr_t ntwdt_cyclic_softint_id; 132927a453eSwentaoy 133927a453eSwentaoy static int ntwdt_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 134927a453eSwentaoy static int ntwdt_attach(dev_info_t *, ddi_attach_cmd_t); 135927a453eSwentaoy static int ntwdt_detach(dev_info_t *, ddi_detach_cmd_t); 136927a453eSwentaoy static int ntwdt_open(dev_t *, int, int, cred_t *); 137927a453eSwentaoy static int ntwdt_close(dev_t, int, int, cred_t *); 138927a453eSwentaoy static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 139927a453eSwentaoy 140927a453eSwentaoy static int ntwdt_chk_watchdog_support(); 141927a453eSwentaoy static void ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state); 142927a453eSwentaoy static void ntwdt_cyclic_pat(void); 143927a453eSwentaoy static uint_t ntwdt_cyclic_softint(caddr_t arg); 144927a453eSwentaoy static void ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr); 145927a453eSwentaoy static void ntwdt_stop_timer_lock(void *arg); 146927a453eSwentaoy static void ntwdt_stop_timer(void *arg); 147927a453eSwentaoy static void ntwdt_enforce_timeout(); 148927a453eSwentaoy 149927a453eSwentaoy static struct cb_ops ntwdt_cb_ops = { 150927a453eSwentaoy ntwdt_open, /* cb_open */ 151927a453eSwentaoy ntwdt_close, /* cb_close */ 152927a453eSwentaoy nodev, /* cb_strategy */ 153927a453eSwentaoy nodev, /* cb_print */ 154927a453eSwentaoy nodev, /* cb_dump */ 155927a453eSwentaoy nodev, /* cb_read */ 156927a453eSwentaoy nodev, /* cb_write */ 157927a453eSwentaoy ntwdt_ioctl, /* cb_ioctl */ 158927a453eSwentaoy nodev, /* cb_devmap */ 159927a453eSwentaoy nodev, /* cb_mmap */ 160927a453eSwentaoy nodev, /* cb_segmap */ 161927a453eSwentaoy nochpoll, /* cb_chpoll */ 162927a453eSwentaoy ddi_prop_op, /* cb_prop_op */ 163927a453eSwentaoy NULL, /* cb_str */ 164927a453eSwentaoy D_NEW | D_MP /* cb_flag */ 165927a453eSwentaoy }; 166927a453eSwentaoy 167927a453eSwentaoy static struct dev_ops ntwdt_dev_ops = { 168927a453eSwentaoy DEVO_REV, /* devo_rev */ 169927a453eSwentaoy 0, /* devo_refcnt */ 170927a453eSwentaoy ntwdt_info, /* devo_info */ 171927a453eSwentaoy nulldev, /* devo_identify */ 172927a453eSwentaoy nulldev, /* devo_probe */ 173927a453eSwentaoy ntwdt_attach, /* devo_attach */ 174927a453eSwentaoy ntwdt_detach, /* devo_detach */ 175927a453eSwentaoy nodev, /* devo_reset */ 176927a453eSwentaoy &ntwdt_cb_ops, /* devo_cb_ops */ 177927a453eSwentaoy NULL, /* devo_bus_ops */ 17819397407SSherry Moore nulldev, /* devo_power */ 17919397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 180927a453eSwentaoy }; 181927a453eSwentaoy 182927a453eSwentaoy static struct modldrv modldrv = { 183927a453eSwentaoy &mod_driverops, 18419397407SSherry Moore "Application Watchdog Driver", 185927a453eSwentaoy &ntwdt_dev_ops 186927a453eSwentaoy }; 187927a453eSwentaoy 188927a453eSwentaoy static struct modlinkage modlinkage = { 189927a453eSwentaoy MODREV_1, 190927a453eSwentaoy (void *)&modldrv, 191927a453eSwentaoy NULL 192927a453eSwentaoy }; 193927a453eSwentaoy 194927a453eSwentaoy int 195927a453eSwentaoy _init(void) 196927a453eSwentaoy { 197927a453eSwentaoy int error = 0; 198927a453eSwentaoy 199927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_init")); 200927a453eSwentaoy 201927a453eSwentaoy /* Initialize the soft state structures */ 202927a453eSwentaoy if ((error = ddi_soft_state_init(&ntwdt_statep, 203927a453eSwentaoy sizeof (ntwdt_state_t), 1)) != 0) { 204927a453eSwentaoy return (error); 205927a453eSwentaoy } 206927a453eSwentaoy 207927a453eSwentaoy /* Install the loadable module */ 208927a453eSwentaoy if ((error = mod_install(&modlinkage)) != 0) { 209927a453eSwentaoy ddi_soft_state_fini(&ntwdt_statep); 210927a453eSwentaoy } 211927a453eSwentaoy return (error); 212927a453eSwentaoy } 213927a453eSwentaoy 214927a453eSwentaoy int 215927a453eSwentaoy _info(struct modinfo *modinfop) 216927a453eSwentaoy { 217927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_info")); 218927a453eSwentaoy 219927a453eSwentaoy return (mod_info(&modlinkage, modinfop)); 220927a453eSwentaoy } 221927a453eSwentaoy 222927a453eSwentaoy int 223927a453eSwentaoy _fini(void) 224927a453eSwentaoy { 225927a453eSwentaoy int retval; 226927a453eSwentaoy 227927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_ENTRY, ("_fini")); 228927a453eSwentaoy 229927a453eSwentaoy if ((retval = mod_remove(&modlinkage)) == 0) { 230927a453eSwentaoy ddi_soft_state_fini(&ntwdt_statep); 231927a453eSwentaoy } 232927a453eSwentaoy 233927a453eSwentaoy return (retval); 234927a453eSwentaoy } 235927a453eSwentaoy 236927a453eSwentaoy static int 237927a453eSwentaoy ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 238927a453eSwentaoy { 239927a453eSwentaoy int instance; 240927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = NULL; /* pointer to ntwdt_runstatep */ 241927a453eSwentaoy ntwdt_runstate_t *ntwdt_runstatep = NULL; 242927a453eSwentaoy cyc_handler_t *hdlr = NULL; 243927a453eSwentaoy 244927a453eSwentaoy switch (cmd) { 245927a453eSwentaoy case DDI_ATTACH: 246927a453eSwentaoy break; 247927a453eSwentaoy 248927a453eSwentaoy case DDI_RESUME: 249927a453eSwentaoy return (DDI_SUCCESS); 250927a453eSwentaoy 251927a453eSwentaoy default: 252927a453eSwentaoy return (DDI_FAILURE); 253927a453eSwentaoy } 254927a453eSwentaoy 255927a453eSwentaoy if (ntwdt_chk_watchdog_support() != 0) { 256927a453eSwentaoy return (DDI_FAILURE); 257927a453eSwentaoy } 258927a453eSwentaoy 259927a453eSwentaoy instance = ddi_get_instance(dip); 260927a453eSwentaoy ASSERT(instance == 0); 261927a453eSwentaoy 262927a453eSwentaoy if (ddi_soft_state_zalloc(ntwdt_statep, instance) != DDI_SUCCESS) { 263927a453eSwentaoy return (DDI_FAILURE); 264927a453eSwentaoy } 265927a453eSwentaoy ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 266927a453eSwentaoy ASSERT(ntwdt_ptr != NULL); 267927a453eSwentaoy 268927a453eSwentaoy ntwdt_dip = dip; 269927a453eSwentaoy 270927a453eSwentaoy ntwdt_ptr->ntwdt_dip = dip; 271927a453eSwentaoy ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 272927a453eSwentaoy mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL, 273927a453eSwentaoy MUTEX_DRIVER, NULL); 274927a453eSwentaoy 275927a453eSwentaoy /* 276927a453eSwentaoy * Initialize the watchdog structure 277927a453eSwentaoy */ 278927a453eSwentaoy ntwdt_ptr->ntwdt_run_state = 279927a453eSwentaoy kmem_zalloc(sizeof (ntwdt_runstate_t), KM_SLEEP); 280927a453eSwentaoy ntwdt_runstatep = ntwdt_ptr->ntwdt_run_state; 281927a453eSwentaoy 282927a453eSwentaoy if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 283927a453eSwentaoy &ntwdt_runstatep->ntwdt_runstate_mtx_cookie) != DDI_SUCCESS) { 284927a453eSwentaoy cmn_err(CE_WARN, "init of iblock cookie failed " 285927a453eSwentaoy "for ntwdt_runstate_mutex"); 286927a453eSwentaoy goto err1; 287927a453eSwentaoy } else { 288927a453eSwentaoy mutex_init(&ntwdt_runstatep->ntwdt_runstate_mutex, 289927a453eSwentaoy NULL, 290927a453eSwentaoy MUTEX_DRIVER, 291927a453eSwentaoy (void *)ntwdt_runstatep->ntwdt_runstate_mtx_cookie); 292927a453eSwentaoy } 293927a453eSwentaoy 294927a453eSwentaoy /* Cyclic fires once per second: */ 295927a453eSwentaoy ntwdt_runstatep->ntwdt_cyclic_interval = NTWDT_CYCLIC_INTERVAL; 296927a453eSwentaoy 297927a453eSwentaoy /* init the Cyclic that drives the NTWDT */ 298927a453eSwentaoy hdlr = &ntwdt_runstatep->ntwdt_cycl_hdlr; 299927a453eSwentaoy hdlr->cyh_level = CY_LOCK_LEVEL; 300927a453eSwentaoy hdlr->cyh_func = (cyc_func_t)ntwdt_cyclic_pat; 301927a453eSwentaoy hdlr->cyh_arg = NULL; 302927a453eSwentaoy 303927a453eSwentaoy /* Softint that will be triggered by Cyclic that drives NTWDT */ 304927a453eSwentaoy if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id, 305927a453eSwentaoy NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr) 306927a453eSwentaoy != DDI_SUCCESS) { 307927a453eSwentaoy cmn_err(CE_WARN, "failed to add cyclic softintr"); 308927a453eSwentaoy goto err2; 309927a453eSwentaoy } 310927a453eSwentaoy 311927a453eSwentaoy /* 312927a453eSwentaoy * Create Minor Node as last activity. This prevents 313927a453eSwentaoy * application from accessing our implementation until it 314927a453eSwentaoy * is initialized. 315927a453eSwentaoy */ 316927a453eSwentaoy if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0, 317927a453eSwentaoy DDI_PSEUDO, NULL) == DDI_FAILURE) { 318927a453eSwentaoy cmn_err(CE_WARN, "failed to create Minor Node: %s", 319927a453eSwentaoy NTWDT_MINOR_NODE); 320927a453eSwentaoy goto err3; 321927a453eSwentaoy } 322927a453eSwentaoy 323927a453eSwentaoy /* Display our driver info in the banner */ 324927a453eSwentaoy ddi_report_dev(dip); 325927a453eSwentaoy 326927a453eSwentaoy return (DDI_SUCCESS); 327927a453eSwentaoy 328927a453eSwentaoy err3: 329927a453eSwentaoy ddi_remove_softintr(ntwdt_cyclic_softint_id); 330927a453eSwentaoy err2: 331927a453eSwentaoy mutex_destroy(&ntwdt_runstatep->ntwdt_runstate_mutex); 332927a453eSwentaoy err1: 333927a453eSwentaoy /* clean up the driver stuff here */ 334927a453eSwentaoy kmem_free(ntwdt_runstatep, sizeof (ntwdt_runstate_t)); 335927a453eSwentaoy ntwdt_ptr->ntwdt_run_state = NULL; 336927a453eSwentaoy mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 337927a453eSwentaoy ddi_soft_state_free(ntwdt_statep, instance); 338927a453eSwentaoy ntwdt_dip = NULL; 339927a453eSwentaoy 340927a453eSwentaoy return (DDI_FAILURE); 341927a453eSwentaoy } 342927a453eSwentaoy 343927a453eSwentaoy /*ARGSUSED*/ 344927a453eSwentaoy static int 345927a453eSwentaoy ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 346927a453eSwentaoy { 347927a453eSwentaoy dev_t dev; 348927a453eSwentaoy int instance; 349927a453eSwentaoy int error = DDI_SUCCESS; 350927a453eSwentaoy 351927a453eSwentaoy switch (infocmd) { 352927a453eSwentaoy case DDI_INFO_DEVT2DEVINFO: 353927a453eSwentaoy dev = (dev_t)arg; 354927a453eSwentaoy if (getminor(dev) == 0) { 355927a453eSwentaoy *result = (void *)ntwdt_dip; 356927a453eSwentaoy } else { 357927a453eSwentaoy error = DDI_FAILURE; 358927a453eSwentaoy } 359927a453eSwentaoy break; 360927a453eSwentaoy 361927a453eSwentaoy case DDI_INFO_DEVT2INSTANCE: 362927a453eSwentaoy dev = (dev_t)arg; 363927a453eSwentaoy instance = getminor(dev); 364927a453eSwentaoy *result = (void *)(uintptr_t)instance; 365927a453eSwentaoy break; 366927a453eSwentaoy 367927a453eSwentaoy default: 368927a453eSwentaoy error = DDI_FAILURE; 369927a453eSwentaoy 370927a453eSwentaoy } 371927a453eSwentaoy 372927a453eSwentaoy return (error); 373927a453eSwentaoy } 374927a453eSwentaoy 375927a453eSwentaoy /*ARGSUSED*/ 376927a453eSwentaoy static int 377927a453eSwentaoy ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 378927a453eSwentaoy { 379927a453eSwentaoy int instance = ddi_get_instance(dip); 380927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = NULL; 381927a453eSwentaoy 382927a453eSwentaoy ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 383927a453eSwentaoy if (ntwdt_ptr == NULL) { 384927a453eSwentaoy return (DDI_FAILURE); 385927a453eSwentaoy } 386927a453eSwentaoy 387927a453eSwentaoy switch (cmd) { 388927a453eSwentaoy case DDI_SUSPEND: 389927a453eSwentaoy return (DDI_SUCCESS); 390927a453eSwentaoy 391927a453eSwentaoy case DDI_DETACH: 392927a453eSwentaoy /* 393927a453eSwentaoy * release resources in opposite (LIFO) order as 394927a453eSwentaoy * were allocated in attach. 395927a453eSwentaoy */ 396927a453eSwentaoy ddi_remove_minor_node(dip, NULL); 397927a453eSwentaoy ntwdt_stop_timer_lock((void *)ntwdt_ptr); 398927a453eSwentaoy ddi_remove_softintr(ntwdt_cyclic_softint_id); 399927a453eSwentaoy 400927a453eSwentaoy mutex_destroy( 401927a453eSwentaoy &ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex); 402927a453eSwentaoy kmem_free(ntwdt_ptr->ntwdt_run_state, 403927a453eSwentaoy sizeof (ntwdt_runstate_t)); 404927a453eSwentaoy ntwdt_ptr->ntwdt_run_state = NULL; 405927a453eSwentaoy 406927a453eSwentaoy mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 407927a453eSwentaoy 408927a453eSwentaoy ddi_soft_state_free(ntwdt_statep, instance); 409927a453eSwentaoy 410927a453eSwentaoy ntwdt_dip = NULL; 411927a453eSwentaoy return (DDI_SUCCESS); 412927a453eSwentaoy 413927a453eSwentaoy default: 414927a453eSwentaoy return (DDI_FAILURE); 415927a453eSwentaoy } 416927a453eSwentaoy } 417927a453eSwentaoy 418927a453eSwentaoy /*ARGSUSED*/ 419927a453eSwentaoy static int 420927a453eSwentaoy ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp) 421927a453eSwentaoy { 422927a453eSwentaoy int instance = getminor(*devp); 423927a453eSwentaoy int retval = 0; 424927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = getstate(instance); 425927a453eSwentaoy 426927a453eSwentaoy if (ntwdt_ptr == NULL) { 427927a453eSwentaoy return (ENXIO); 428927a453eSwentaoy } 429927a453eSwentaoy 430927a453eSwentaoy /* 431927a453eSwentaoy * ensure caller is a priviledged process. 432927a453eSwentaoy */ 433927a453eSwentaoy if (drv_priv(credp) != 0) { 434927a453eSwentaoy return (EPERM); 435927a453eSwentaoy } 436927a453eSwentaoy 437927a453eSwentaoy mutex_enter(&ntwdt_ptr->ntwdt_mutex); 438927a453eSwentaoy if (ntwdt_ptr->ntwdt_open_flag) { 439927a453eSwentaoy retval = EAGAIN; 440927a453eSwentaoy } else { 441927a453eSwentaoy ntwdt_ptr->ntwdt_open_flag = 1; 442927a453eSwentaoy } 443927a453eSwentaoy mutex_exit(&ntwdt_ptr->ntwdt_mutex); 444927a453eSwentaoy 445927a453eSwentaoy return (retval); 446927a453eSwentaoy } 447927a453eSwentaoy 448927a453eSwentaoy /*ARGSUSED*/ 449927a453eSwentaoy static int 450927a453eSwentaoy ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp) 451927a453eSwentaoy { 452927a453eSwentaoy int instance = getminor(dev); 453927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = getstate(instance); 454927a453eSwentaoy 455927a453eSwentaoy if (ntwdt_ptr == NULL) { 456927a453eSwentaoy return (ENXIO); 457927a453eSwentaoy } 458927a453eSwentaoy 459927a453eSwentaoy mutex_enter(&ntwdt_ptr->ntwdt_mutex); 460927a453eSwentaoy ntwdt_ptr->ntwdt_open_flag = 0; 461927a453eSwentaoy mutex_exit(&ntwdt_ptr->ntwdt_mutex); 462927a453eSwentaoy 463927a453eSwentaoy return (0); 464927a453eSwentaoy } 465927a453eSwentaoy 466927a453eSwentaoy /*ARGSUSED*/ 467927a453eSwentaoy static int 468927a453eSwentaoy ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 469927a453eSwentaoy int *rvalp) 470927a453eSwentaoy { 471927a453eSwentaoy int instance = getminor(dev); 472927a453eSwentaoy int retval = 0; 473927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = NULL; 474927a453eSwentaoy ntwdt_runstate_t *ntwdt_state; 475927a453eSwentaoy lom_dogstate_t lom_dogstate; 476927a453eSwentaoy lom_dogctl_t lom_dogctl; 477927a453eSwentaoy uint32_t lom_dogtime; 478927a453eSwentaoy 479927a453eSwentaoy if ((ntwdt_ptr = getstate(instance)) == NULL) { 480927a453eSwentaoy return (ENXIO); 481927a453eSwentaoy } 482927a453eSwentaoy 483927a453eSwentaoy ntwdt_state = ntwdt_ptr->ntwdt_run_state; 484927a453eSwentaoy 485927a453eSwentaoy switch (cmd) { 486927a453eSwentaoy case LOMIOCDOGSTATE: 487927a453eSwentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex); 488927a453eSwentaoy lom_dogstate.reset_enable = ntwdt_state->ntwdt_reset_enabled; 489927a453eSwentaoy lom_dogstate.dog_enable = ntwdt_state->ntwdt_watchdog_enabled; 490927a453eSwentaoy lom_dogstate.dog_timeout = ntwdt_state->ntwdt_watchdog_timeout; 491927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 492927a453eSwentaoy 493927a453eSwentaoy if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg, 494927a453eSwentaoy sizeof (lom_dogstate_t), mode) != 0) { 495927a453eSwentaoy retval = EFAULT; 496927a453eSwentaoy } 497927a453eSwentaoy break; 498927a453eSwentaoy 499927a453eSwentaoy case LOMIOCDOGCTL: 500927a453eSwentaoy if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl, 501927a453eSwentaoy sizeof (lom_dogctl_t), mode) != 0) { 502927a453eSwentaoy retval = EFAULT; 503927a453eSwentaoy break; 504927a453eSwentaoy } 505927a453eSwentaoy 506927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("reset_enable: %d, and dog_enable: " 507927a453eSwentaoy "%d, watchdog_timeout %d", lom_dogctl.reset_enable, 508927a453eSwentaoy lom_dogctl.dog_enable, 509927a453eSwentaoy ntwdt_state->ntwdt_watchdog_timeout)); 510927a453eSwentaoy /* 511927a453eSwentaoy * ignore request to enable reset while disabling watchdog. 512927a453eSwentaoy */ 513927a453eSwentaoy if (!lom_dogctl.dog_enable && lom_dogctl.reset_enable) { 514927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("invalid combination of " 515927a453eSwentaoy "reset_enable: %d, and dog_enable: %d", 516927a453eSwentaoy lom_dogctl.reset_enable, 517927a453eSwentaoy lom_dogctl.dog_enable)); 518927a453eSwentaoy retval = EINVAL; 519927a453eSwentaoy break; 520927a453eSwentaoy } 521927a453eSwentaoy 522927a453eSwentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex); 523927a453eSwentaoy 524927a453eSwentaoy if (ntwdt_state->ntwdt_watchdog_timeout == 0) { 525927a453eSwentaoy /* 526927a453eSwentaoy * the LOMIOCDOGTIME has never been used to setup 527927a453eSwentaoy * a valid timeout. 528927a453eSwentaoy */ 529927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("timeout has not been set" 530927a453eSwentaoy "watchdog_timeout: %d", 531927a453eSwentaoy ntwdt_state->ntwdt_watchdog_timeout)); 532927a453eSwentaoy retval = EINVAL; 533927a453eSwentaoy goto end; 534927a453eSwentaoy } 535927a453eSwentaoy 536927a453eSwentaoy /* 537927a453eSwentaoy * Store the user specified state in the softstate. 538927a453eSwentaoy */ 539927a453eSwentaoy ntwdt_state->ntwdt_reset_enabled = lom_dogctl.reset_enable; 540927a453eSwentaoy ntwdt_state->ntwdt_watchdog_enabled = lom_dogctl.dog_enable; 541927a453eSwentaoy 542927a453eSwentaoy if (ntwdt_state->ntwdt_watchdog_enabled != 0) { 543927a453eSwentaoy /* 544927a453eSwentaoy * The user wants to enable the watchdog. 545927a453eSwentaoy * Arm the watchdog and start the cyclic. 546927a453eSwentaoy */ 547927a453eSwentaoy ntwdt_arm_watchdog(ntwdt_state); 548927a453eSwentaoy 549927a453eSwentaoy if (ntwdt_state->ntwdt_timer_running == 0) { 550927a453eSwentaoy ntwdt_start_timer(ntwdt_ptr); 551927a453eSwentaoy } 552927a453eSwentaoy 553927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is enabled")); 554927a453eSwentaoy } else { 555927a453eSwentaoy /* 556927a453eSwentaoy * The user wants to disable the watchdog. 557927a453eSwentaoy */ 558927a453eSwentaoy if (ntwdt_state->ntwdt_timer_running != 0) { 559927a453eSwentaoy ntwdt_stop_timer(ntwdt_ptr); 560927a453eSwentaoy } 561927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is disabled")); 562927a453eSwentaoy } 563927a453eSwentaoy 564927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 565927a453eSwentaoy break; 566927a453eSwentaoy 567927a453eSwentaoy case LOMIOCDOGTIME: 568927a453eSwentaoy if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime, 569927a453eSwentaoy sizeof (uint32_t), mode) != 0) { 570927a453eSwentaoy retval = EFAULT; 571927a453eSwentaoy break; 572927a453eSwentaoy } 573927a453eSwentaoy 574927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set timeout: %d", 575927a453eSwentaoy lom_dogtime)); 576927a453eSwentaoy 577927a453eSwentaoy /* 578927a453eSwentaoy * Ensure specified timeout is valid. 579927a453eSwentaoy */ 580927a453eSwentaoy if ((lom_dogtime == 0) || 581927a453eSwentaoy (lom_dogtime > (uint32_t)NTWDT_MAX_TIMEOUT)) { 582927a453eSwentaoy retval = EINVAL; 583927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set invalid " 584927a453eSwentaoy "timeout: %d", (int)TICK_TO_MSEC(lom_dogtime))); 585927a453eSwentaoy break; 586927a453eSwentaoy } 587927a453eSwentaoy 588927a453eSwentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex); 589927a453eSwentaoy 590927a453eSwentaoy ntwdt_state->ntwdt_watchdog_timeout = lom_dogtime; 591927a453eSwentaoy 592927a453eSwentaoy /* 593927a453eSwentaoy * If awdt is currently running, re-arm it with the 594927a453eSwentaoy * newly-specified timeout value. 595927a453eSwentaoy */ 596927a453eSwentaoy if (ntwdt_state->ntwdt_timer_running != 0) { 597927a453eSwentaoy ntwdt_arm_watchdog(ntwdt_state); 598927a453eSwentaoy } 599927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 600927a453eSwentaoy break; 601927a453eSwentaoy 602927a453eSwentaoy case LOMIOCDOGPAT: 603927a453eSwentaoy /* 604927a453eSwentaoy * Allow user to pat the watchdog timer. 605927a453eSwentaoy */ 606927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("DOGPAT is invoked")); 607927a453eSwentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex); 608927a453eSwentaoy 609927a453eSwentaoy /* 610927a453eSwentaoy * If awdt is not enabled or underlying cyclic is not 611927a453eSwentaoy * running, exit. 612927a453eSwentaoy */ 613927a453eSwentaoy if (!(ntwdt_state->ntwdt_watchdog_enabled && 614927a453eSwentaoy ntwdt_state->ntwdt_timer_running)) { 615927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("PAT: AWDT not enabled")); 616927a453eSwentaoy goto end; 617927a453eSwentaoy } 618927a453eSwentaoy 619927a453eSwentaoy if (ntwdt_state->ntwdt_watchdog_expired == 0) { 620927a453eSwentaoy /* 621927a453eSwentaoy * re-arm the awdt. 622927a453eSwentaoy */ 623927a453eSwentaoy ntwdt_arm_watchdog(ntwdt_state); 624927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT patted, " 625927a453eSwentaoy "remainning seconds: %d", 626927a453eSwentaoy ntwdt_state->ntwdt_time_remaining)); 627927a453eSwentaoy } 628927a453eSwentaoy 629927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 630927a453eSwentaoy break; 631927a453eSwentaoy 632927a453eSwentaoy default: 633927a453eSwentaoy retval = EINVAL; 634927a453eSwentaoy break; 635927a453eSwentaoy } 636927a453eSwentaoy return (retval); 637927a453eSwentaoy end: 638927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 639927a453eSwentaoy return (retval); 640927a453eSwentaoy } 641927a453eSwentaoy 642927a453eSwentaoy static void 643927a453eSwentaoy ntwdt_cyclic_pat(void) 644927a453eSwentaoy { 645927a453eSwentaoy ddi_trigger_softintr(ntwdt_cyclic_softint_id); 646927a453eSwentaoy } 647927a453eSwentaoy 648927a453eSwentaoy static uint_t 649927a453eSwentaoy ntwdt_cyclic_softint(caddr_t arg) 650927a453eSwentaoy { 651927a453eSwentaoy /*LINTED E_BAD_PTR_CAST_ALIGN*/ 652927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg; 653927a453eSwentaoy ntwdt_runstate_t *ntwdt_state; 654927a453eSwentaoy 655927a453eSwentaoy ntwdt_state = ntwdt_ptr->ntwdt_run_state; 656927a453eSwentaoy 657927a453eSwentaoy mutex_enter(&ntwdt_state->ntwdt_runstate_mutex); 658927a453eSwentaoy 659927a453eSwentaoy if ((ntwdt_state->ntwdt_watchdog_flags & NTWDT_FLAG_SKIP_CYCLIC) != 0) { 660927a453eSwentaoy ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC; 661927a453eSwentaoy goto end; 662927a453eSwentaoy } 663927a453eSwentaoy 664927a453eSwentaoy if ((ntwdt_state->ntwdt_timer_running == 0) || 665927a453eSwentaoy (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) || 666927a453eSwentaoy (ntwdt_state->ntwdt_watchdog_enabled == 0)) { 667927a453eSwentaoy goto end; 668927a453eSwentaoy } 669927a453eSwentaoy 670927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_IOCTL, ("cyclic_softint: %d" 671*d3d50737SRafael Vanoni "ddi_get_lbolt64(): %d\n", ntwdt_state->ntwdt_watchdog_timeout, 672*d3d50737SRafael Vanoni (int)TICK_TO_MSEC(ddi_get_lbolt64()))); 673927a453eSwentaoy 674927a453eSwentaoy /* 675927a453eSwentaoy * Decrement the virtual watchdog timer and check if it has expired. 676927a453eSwentaoy */ 677927a453eSwentaoy ntwdt_state->ntwdt_time_remaining -= NTWDT_DECREMENT_INTERVAL; 678927a453eSwentaoy 679927a453eSwentaoy if (ntwdt_state->ntwdt_time_remaining == 0) { 680927a453eSwentaoy cmn_err(CE_WARN, "application-watchdog expired"); 681927a453eSwentaoy ntwdt_state->ntwdt_watchdog_expired = 1; 682927a453eSwentaoy 683927a453eSwentaoy if (ntwdt_state->ntwdt_reset_enabled != 0) { 684927a453eSwentaoy /* 685927a453eSwentaoy * The user wants to reset the system. 686927a453eSwentaoy */ 687927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 688927a453eSwentaoy 689927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("recovery being done")); 690927a453eSwentaoy ntwdt_enforce_timeout(); 691927a453eSwentaoy } else { 692927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("no recovery being done")); 693927a453eSwentaoy ntwdt_state->ntwdt_watchdog_enabled = 0; 694927a453eSwentaoy } 695927a453eSwentaoy 696927a453eSwentaoy /* 697927a453eSwentaoy * Schedule Callout to stop the cyclic. 698927a453eSwentaoy */ 699927a453eSwentaoy (void) timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0); 700927a453eSwentaoy } else { 701927a453eSwentaoy _NOTE(EMPTY) 702927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("time remaining in AWDT: %d secs", 703927a453eSwentaoy (int)TICK_TO_MSEC(ntwdt_state->ntwdt_time_remaining))); 704927a453eSwentaoy } 705927a453eSwentaoy 706927a453eSwentaoy end: 707927a453eSwentaoy mutex_exit(&ntwdt_state->ntwdt_runstate_mutex); 708927a453eSwentaoy return (DDI_INTR_CLAIMED); 709927a453eSwentaoy } 710927a453eSwentaoy 711927a453eSwentaoy static void 712927a453eSwentaoy ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state) 713927a453eSwentaoy { 714927a453eSwentaoy ntwdt_state->ntwdt_time_remaining = ntwdt_state->ntwdt_watchdog_timeout; 715927a453eSwentaoy 716927a453eSwentaoy if (ntwdt_state->ntwdt_timer_running != 0) { 717927a453eSwentaoy ntwdt_state->ntwdt_watchdog_flags |= NTWDT_FLAG_SKIP_CYCLIC; 718927a453eSwentaoy } else { 719927a453eSwentaoy ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC; 720927a453eSwentaoy } 721927a453eSwentaoy } 722927a453eSwentaoy 723927a453eSwentaoy static void 724927a453eSwentaoy ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr) 725927a453eSwentaoy { 726927a453eSwentaoy ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state; 727927a453eSwentaoy cyc_handler_t *hdlr = &ntwdt_state->ntwdt_cycl_hdlr; 728927a453eSwentaoy cyc_time_t *when = &ntwdt_state->ntwdt_cycl_time; 729927a453eSwentaoy 730927a453eSwentaoy /* 731927a453eSwentaoy * Init the cyclic. 732927a453eSwentaoy */ 733927a453eSwentaoy when->cyt_interval = ntwdt_state->ntwdt_cyclic_interval; 734927a453eSwentaoy when->cyt_when = gethrtime() + when->cyt_interval; 735927a453eSwentaoy 736927a453eSwentaoy ntwdt_state->ntwdt_watchdog_expired = 0; 737927a453eSwentaoy ntwdt_state->ntwdt_timer_running = 1; 738927a453eSwentaoy 739927a453eSwentaoy mutex_enter(&cpu_lock); 740927a453eSwentaoy if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) { 741927a453eSwentaoy ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when); 742927a453eSwentaoy } 743927a453eSwentaoy mutex_exit(&cpu_lock); 744927a453eSwentaoy 745927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is started")); 746927a453eSwentaoy } 747927a453eSwentaoy 748927a453eSwentaoy static void 749927a453eSwentaoy ntwdt_stop_timer(void *arg) 750927a453eSwentaoy { 751927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg; 752927a453eSwentaoy ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state; 753927a453eSwentaoy 754927a453eSwentaoy mutex_enter(&cpu_lock); 755927a453eSwentaoy if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) { 756927a453eSwentaoy cyclic_remove(ntwdt_ptr->ntwdt_cycl_id); 757927a453eSwentaoy } 758927a453eSwentaoy mutex_exit(&cpu_lock); 759927a453eSwentaoy 760927a453eSwentaoy ntwdt_state->ntwdt_watchdog_flags = 0; 761927a453eSwentaoy ntwdt_state->ntwdt_timer_running = 0; 762927a453eSwentaoy ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 763927a453eSwentaoy 764927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is stopped")); 765927a453eSwentaoy } 766927a453eSwentaoy 767927a453eSwentaoy /* 768927a453eSwentaoy * This is a wrapper function for ntwdt_stop_timer as some callers 769927a453eSwentaoy * will already have the appropriate mutex locked, and others not. 770927a453eSwentaoy */ 771927a453eSwentaoy static void 772927a453eSwentaoy ntwdt_stop_timer_lock(void *arg) 773927a453eSwentaoy { 774927a453eSwentaoy ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg; 775927a453eSwentaoy 776927a453eSwentaoy mutex_enter(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex); 777927a453eSwentaoy ntwdt_stop_timer(arg); 778927a453eSwentaoy mutex_exit(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex); 779927a453eSwentaoy } 780927a453eSwentaoy 781927a453eSwentaoy static void 782927a453eSwentaoy ntwdt_enforce_timeout() 783927a453eSwentaoy { 784927a453eSwentaoy if (ntwdt_disable_timeout_action != 0) { 785927a453eSwentaoy cmn_err(CE_NOTE, "Appication watchdog timer expired, " 786927a453eSwentaoy "taking no action"); 787927a453eSwentaoy return; 788927a453eSwentaoy } 789927a453eSwentaoy 790927a453eSwentaoy NTWDT_DBG(NTWDT_DBG_NTWDT, ("dump cores and rebooting ...")); 791927a453eSwentaoy 792927a453eSwentaoy (void) kadmin(A_DUMP, AD_BOOT, NULL, kcred); 793927a453eSwentaoy cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed"); 794927a453eSwentaoy _NOTE(NOTREACHED); 795927a453eSwentaoy } 796927a453eSwentaoy 797927a453eSwentaoy static int 798927a453eSwentaoy ntwdt_chk_watchdog_support() 799927a453eSwentaoy { 800927a453eSwentaoy int retval = 0; 801927a453eSwentaoy 802927a453eSwentaoy if ((boothowto & RB_DEBUG) != 0) { 803927a453eSwentaoy cmn_err(CE_WARN, "kernel debugger was booted; " 804927a453eSwentaoy "application watchdog is not available."); 805927a453eSwentaoy retval = ENOTSUP; 806927a453eSwentaoy } 807927a453eSwentaoy return (retval); 808927a453eSwentaoy } 809