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