1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * sun4v application watchdog driver
28 */
29
30 #include <sys/types.h>
31 #include <sys/file.h>
32 #include <sys/errno.h>
33 #include <sys/open.h>
34 #include <sys/callb.h>
35 #include <sys/cred.h>
36 #include <sys/cyclic.h>
37 #include <sys/uio.h>
38 #include <sys/stat.h>
39 #include <sys/ksynch.h>
40 #include <sys/modctl.h>
41 #include <sys/conf.h>
42 #include <sys/devops.h>
43 #include <sys/debug.h>
44 #include <sys/cmn_err.h>
45 #include <sys/ddi.h>
46 #include <sys/reboot.h>
47 #include <sys/sunddi.h>
48 #include <sys/signal.h>
49 #include <sys/ntwdt.h>
50 #include <sys/note.h>
51
52 /*
53 * tunables
54 */
55 int ntwdt_disable_timeout_action = 0;
56
57 #ifdef DEBUG
58
59 int ntwdt_debug = 0; /* ntwdt debug flag, dbg all for now. */
60
61 /*
62 * Flags to set in ntwdt_debug.
63 */
64 #define NTWDT_DBG_ENTRY 0x00000001 /* drv entry points */
65 #define NTWDT_DBG_IOCTL 0x00000002 /* ioctl's */
66 #define NTWDT_DBG_NTWDT 0x00000004 /* other ntwdt debug */
67
68 #define NTWDT_DBG(flag, msg) \
69 { if ((ntwdt_debug) & (flag)) (void) printf msg; }
70 #else /* DEBUG */
71 #define NTWDT_DBG(flag, msg)
72 #endif /* DEBUG */
73
74 #define NTWDT_MINOR_NODE "awdt"
75 #define getstate(minor) \
76 ((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor)))
77
78 /*
79 * The ntwdt cyclic interval in nanosecond unit as cyclic subsystem supports
80 * nanosecond resolution.
81 */
82 #define NTWDT_CYCLIC_INTERVAL NANOSEC /* 1 seconds */
83
84 /*
85 * The ntwdt decrement interval in 1 second resolution.
86 */
87 #define NTWDT_DECREMENT_INTERVAL 1 /* 1 second */
88
89 /*
90 * ntwdt_watchdog_flags and macros to set/clear one bit in it.
91 */
92 #define NTWDT_FLAG_SKIP_CYCLIC 0x1 /* skip next cyclic */
93
94 #define NTWDT_MAX_TIMEOUT (3 * 60 * 60) /* 3 hours */
95
96 #define WDT_MIN_COREAPI_MAJOR 1
97 #define WDT_MIN_COREAPI_MINOR 1
98
99 /*
100 * Application watchdog state.
101 */
102 typedef struct ntwdt_runstate {
103 kmutex_t ntwdt_runstate_mutex;
104 ddi_iblock_cookie_t ntwdt_runstate_mtx_cookie;
105 int ntwdt_watchdog_enabled; /* wdog enabled ? */
106 int ntwdt_reset_enabled; /* reset enabled ? */
107 int ntwdt_timer_running; /* wdog running ? */
108 int ntwdt_watchdog_expired; /* wdog expired ? */
109 uint32_t ntwdt_time_remaining; /* expiration timer */
110 uint32_t ntwdt_watchdog_timeout; /* timeout in seconds */
111 hrtime_t ntwdt_cyclic_interval; /* cyclic interval */
112 cyc_handler_t ntwdt_cycl_hdlr;
113 cyc_time_t ntwdt_cycl_time;
114 uint32_t ntwdt_watchdog_flags;
115 } ntwdt_runstate_t;
116
117 /*
118 * softstate of NTWDT
119 */
120 typedef struct {
121 kmutex_t ntwdt_mutex;
122 dev_info_t *ntwdt_dip; /* dip */
123 int ntwdt_open_flag; /* file open ? */
124 ntwdt_runstate_t *ntwdt_run_state; /* wdog state */
125 cyclic_id_t ntwdt_cycl_id;
126 } ntwdt_state_t;
127
128 static void *ntwdt_statep; /* softstate */
129 static dev_info_t *ntwdt_dip;
130
131 static ddi_softintr_t ntwdt_cyclic_softint_id;
132
133 static int ntwdt_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
134 static int ntwdt_attach(dev_info_t *, ddi_attach_cmd_t);
135 static int ntwdt_detach(dev_info_t *, ddi_detach_cmd_t);
136 static int ntwdt_open(dev_t *, int, int, cred_t *);
137 static int ntwdt_close(dev_t, int, int, cred_t *);
138 static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
139
140 static int ntwdt_chk_watchdog_support();
141 static void ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state);
142 static void ntwdt_cyclic_pat(void);
143 static uint_t ntwdt_cyclic_softint(caddr_t arg);
144 static void ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr);
145 static void ntwdt_stop_timer_lock(void *arg);
146 static void ntwdt_stop_timer(void *arg);
147 static void ntwdt_enforce_timeout();
148
149 static struct cb_ops ntwdt_cb_ops = {
150 ntwdt_open, /* cb_open */
151 ntwdt_close, /* cb_close */
152 nodev, /* cb_strategy */
153 nodev, /* cb_print */
154 nodev, /* cb_dump */
155 nodev, /* cb_read */
156 nodev, /* cb_write */
157 ntwdt_ioctl, /* cb_ioctl */
158 nodev, /* cb_devmap */
159 nodev, /* cb_mmap */
160 nodev, /* cb_segmap */
161 nochpoll, /* cb_chpoll */
162 ddi_prop_op, /* cb_prop_op */
163 NULL, /* cb_str */
164 D_NEW | D_MP /* cb_flag */
165 };
166
167 static struct dev_ops ntwdt_dev_ops = {
168 DEVO_REV, /* devo_rev */
169 0, /* devo_refcnt */
170 ntwdt_info, /* devo_info */
171 nulldev, /* devo_identify */
172 nulldev, /* devo_probe */
173 ntwdt_attach, /* devo_attach */
174 ntwdt_detach, /* devo_detach */
175 nodev, /* devo_reset */
176 &ntwdt_cb_ops, /* devo_cb_ops */
177 NULL, /* devo_bus_ops */
178 nulldev, /* devo_power */
179 ddi_quiesce_not_supported, /* devo_quiesce */
180 };
181
182 static struct modldrv modldrv = {
183 &mod_driverops,
184 "Application Watchdog Driver",
185 &ntwdt_dev_ops
186 };
187
188 static struct modlinkage modlinkage = {
189 MODREV_1,
190 (void *)&modldrv,
191 NULL
192 };
193
194 int
_init(void)195 _init(void)
196 {
197 int error = 0;
198
199 NTWDT_DBG(NTWDT_DBG_ENTRY, ("_init"));
200
201 /* Initialize the soft state structures */
202 if ((error = ddi_soft_state_init(&ntwdt_statep,
203 sizeof (ntwdt_state_t), 1)) != 0) {
204 return (error);
205 }
206
207 /* Install the loadable module */
208 if ((error = mod_install(&modlinkage)) != 0) {
209 ddi_soft_state_fini(&ntwdt_statep);
210 }
211 return (error);
212 }
213
214 int
_info(struct modinfo * modinfop)215 _info(struct modinfo *modinfop)
216 {
217 NTWDT_DBG(NTWDT_DBG_ENTRY, ("_info"));
218
219 return (mod_info(&modlinkage, modinfop));
220 }
221
222 int
_fini(void)223 _fini(void)
224 {
225 int retval;
226
227 NTWDT_DBG(NTWDT_DBG_ENTRY, ("_fini"));
228
229 if ((retval = mod_remove(&modlinkage)) == 0) {
230 ddi_soft_state_fini(&ntwdt_statep);
231 }
232
233 return (retval);
234 }
235
236 static int
ntwdt_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)237 ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
238 {
239 int instance;
240 ntwdt_state_t *ntwdt_ptr = NULL; /* pointer to ntwdt_runstatep */
241 ntwdt_runstate_t *ntwdt_runstatep = NULL;
242 cyc_handler_t *hdlr = NULL;
243
244 switch (cmd) {
245 case DDI_ATTACH:
246 break;
247
248 case DDI_RESUME:
249 return (DDI_SUCCESS);
250
251 default:
252 return (DDI_FAILURE);
253 }
254
255 if (ntwdt_chk_watchdog_support() != 0) {
256 return (DDI_FAILURE);
257 }
258
259 instance = ddi_get_instance(dip);
260 ASSERT(instance == 0);
261
262 if (ddi_soft_state_zalloc(ntwdt_statep, instance) != DDI_SUCCESS) {
263 return (DDI_FAILURE);
264 }
265 ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
266 ASSERT(ntwdt_ptr != NULL);
267
268 ntwdt_dip = dip;
269
270 ntwdt_ptr->ntwdt_dip = dip;
271 ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
272 mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL,
273 MUTEX_DRIVER, NULL);
274
275 /*
276 * Initialize the watchdog structure
277 */
278 ntwdt_ptr->ntwdt_run_state =
279 kmem_zalloc(sizeof (ntwdt_runstate_t), KM_SLEEP);
280 ntwdt_runstatep = ntwdt_ptr->ntwdt_run_state;
281
282 if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
283 &ntwdt_runstatep->ntwdt_runstate_mtx_cookie) != DDI_SUCCESS) {
284 cmn_err(CE_WARN, "init of iblock cookie failed "
285 "for ntwdt_runstate_mutex");
286 goto err1;
287 } else {
288 mutex_init(&ntwdt_runstatep->ntwdt_runstate_mutex,
289 NULL,
290 MUTEX_DRIVER,
291 (void *)ntwdt_runstatep->ntwdt_runstate_mtx_cookie);
292 }
293
294 /* Cyclic fires once per second: */
295 ntwdt_runstatep->ntwdt_cyclic_interval = NTWDT_CYCLIC_INTERVAL;
296
297 /* init the Cyclic that drives the NTWDT */
298 hdlr = &ntwdt_runstatep->ntwdt_cycl_hdlr;
299 hdlr->cyh_level = CY_LOCK_LEVEL;
300 hdlr->cyh_func = (cyc_func_t)ntwdt_cyclic_pat;
301 hdlr->cyh_arg = NULL;
302
303 /* Softint that will be triggered by Cyclic that drives NTWDT */
304 if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id,
305 NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr)
306 != DDI_SUCCESS) {
307 cmn_err(CE_WARN, "failed to add cyclic softintr");
308 goto err2;
309 }
310
311 /*
312 * Create Minor Node as last activity. This prevents
313 * application from accessing our implementation until it
314 * is initialized.
315 */
316 if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0,
317 DDI_PSEUDO, NULL) == DDI_FAILURE) {
318 cmn_err(CE_WARN, "failed to create Minor Node: %s",
319 NTWDT_MINOR_NODE);
320 goto err3;
321 }
322
323 /* Display our driver info in the banner */
324 ddi_report_dev(dip);
325
326 return (DDI_SUCCESS);
327
328 err3:
329 ddi_remove_softintr(ntwdt_cyclic_softint_id);
330 err2:
331 mutex_destroy(&ntwdt_runstatep->ntwdt_runstate_mutex);
332 err1:
333 /* clean up the driver stuff here */
334 kmem_free(ntwdt_runstatep, sizeof (ntwdt_runstate_t));
335 ntwdt_ptr->ntwdt_run_state = NULL;
336 mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
337 ddi_soft_state_free(ntwdt_statep, instance);
338 ntwdt_dip = NULL;
339
340 return (DDI_FAILURE);
341 }
342
343 /*ARGSUSED*/
344 static int
ntwdt_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)345 ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
346 {
347 dev_t dev;
348 int instance;
349 int error = DDI_SUCCESS;
350
351 switch (infocmd) {
352 case DDI_INFO_DEVT2DEVINFO:
353 dev = (dev_t)arg;
354 if (getminor(dev) == 0) {
355 *result = (void *)ntwdt_dip;
356 } else {
357 error = DDI_FAILURE;
358 }
359 break;
360
361 case DDI_INFO_DEVT2INSTANCE:
362 dev = (dev_t)arg;
363 instance = getminor(dev);
364 *result = (void *)(uintptr_t)instance;
365 break;
366
367 default:
368 error = DDI_FAILURE;
369
370 }
371
372 return (error);
373 }
374
375 /*ARGSUSED*/
376 static int
ntwdt_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)377 ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
378 {
379 int instance = ddi_get_instance(dip);
380 ntwdt_state_t *ntwdt_ptr = NULL;
381
382 ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance);
383 if (ntwdt_ptr == NULL) {
384 return (DDI_FAILURE);
385 }
386
387 switch (cmd) {
388 case DDI_SUSPEND:
389 return (DDI_SUCCESS);
390
391 case DDI_DETACH:
392 /*
393 * release resources in opposite (LIFO) order as
394 * were allocated in attach.
395 */
396 ddi_remove_minor_node(dip, NULL);
397 ntwdt_stop_timer_lock((void *)ntwdt_ptr);
398 ddi_remove_softintr(ntwdt_cyclic_softint_id);
399
400 mutex_destroy(
401 &ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
402 kmem_free(ntwdt_ptr->ntwdt_run_state,
403 sizeof (ntwdt_runstate_t));
404 ntwdt_ptr->ntwdt_run_state = NULL;
405
406 mutex_destroy(&ntwdt_ptr->ntwdt_mutex);
407
408 ddi_soft_state_free(ntwdt_statep, instance);
409
410 ntwdt_dip = NULL;
411 return (DDI_SUCCESS);
412
413 default:
414 return (DDI_FAILURE);
415 }
416 }
417
418 /*ARGSUSED*/
419 static int
ntwdt_open(dev_t * devp,int flag,int otyp,cred_t * credp)420 ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp)
421 {
422 int instance = getminor(*devp);
423 int retval = 0;
424 ntwdt_state_t *ntwdt_ptr = getstate(instance);
425
426 if (ntwdt_ptr == NULL) {
427 return (ENXIO);
428 }
429
430 /*
431 * ensure caller is a priviledged process.
432 */
433 if (drv_priv(credp) != 0) {
434 return (EPERM);
435 }
436
437 mutex_enter(&ntwdt_ptr->ntwdt_mutex);
438 if (ntwdt_ptr->ntwdt_open_flag) {
439 retval = EAGAIN;
440 } else {
441 ntwdt_ptr->ntwdt_open_flag = 1;
442 }
443 mutex_exit(&ntwdt_ptr->ntwdt_mutex);
444
445 return (retval);
446 }
447
448 /*ARGSUSED*/
449 static int
ntwdt_close(dev_t dev,int flag,int otyp,cred_t * credp)450 ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp)
451 {
452 int instance = getminor(dev);
453 ntwdt_state_t *ntwdt_ptr = getstate(instance);
454
455 if (ntwdt_ptr == NULL) {
456 return (ENXIO);
457 }
458
459 mutex_enter(&ntwdt_ptr->ntwdt_mutex);
460 ntwdt_ptr->ntwdt_open_flag = 0;
461 mutex_exit(&ntwdt_ptr->ntwdt_mutex);
462
463 return (0);
464 }
465
466 /*ARGSUSED*/
467 static int
ntwdt_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)468 ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
469 int *rvalp)
470 {
471 int instance = getminor(dev);
472 int retval = 0;
473 ntwdt_state_t *ntwdt_ptr = NULL;
474 ntwdt_runstate_t *ntwdt_state;
475 lom_dogstate_t lom_dogstate;
476 lom_dogctl_t lom_dogctl;
477 uint32_t lom_dogtime;
478
479 if ((ntwdt_ptr = getstate(instance)) == NULL) {
480 return (ENXIO);
481 }
482
483 ntwdt_state = ntwdt_ptr->ntwdt_run_state;
484
485 switch (cmd) {
486 case LOMIOCDOGSTATE:
487 mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
488 lom_dogstate.reset_enable = ntwdt_state->ntwdt_reset_enabled;
489 lom_dogstate.dog_enable = ntwdt_state->ntwdt_watchdog_enabled;
490 lom_dogstate.dog_timeout = ntwdt_state->ntwdt_watchdog_timeout;
491 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
492
493 if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg,
494 sizeof (lom_dogstate_t), mode) != 0) {
495 retval = EFAULT;
496 }
497 break;
498
499 case LOMIOCDOGCTL:
500 if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl,
501 sizeof (lom_dogctl_t), mode) != 0) {
502 retval = EFAULT;
503 break;
504 }
505
506 NTWDT_DBG(NTWDT_DBG_IOCTL, ("reset_enable: %d, and dog_enable: "
507 "%d, watchdog_timeout %d", lom_dogctl.reset_enable,
508 lom_dogctl.dog_enable,
509 ntwdt_state->ntwdt_watchdog_timeout));
510 /*
511 * ignore request to enable reset while disabling watchdog.
512 */
513 if (!lom_dogctl.dog_enable && lom_dogctl.reset_enable) {
514 NTWDT_DBG(NTWDT_DBG_IOCTL, ("invalid combination of "
515 "reset_enable: %d, and dog_enable: %d",
516 lom_dogctl.reset_enable,
517 lom_dogctl.dog_enable));
518 retval = EINVAL;
519 break;
520 }
521
522 mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
523
524 if (ntwdt_state->ntwdt_watchdog_timeout == 0) {
525 /*
526 * the LOMIOCDOGTIME has never been used to setup
527 * a valid timeout.
528 */
529 NTWDT_DBG(NTWDT_DBG_IOCTL, ("timeout has not been set"
530 "watchdog_timeout: %d",
531 ntwdt_state->ntwdt_watchdog_timeout));
532 retval = EINVAL;
533 goto end;
534 }
535
536 /*
537 * Store the user specified state in the softstate.
538 */
539 ntwdt_state->ntwdt_reset_enabled = lom_dogctl.reset_enable;
540 ntwdt_state->ntwdt_watchdog_enabled = lom_dogctl.dog_enable;
541
542 if (ntwdt_state->ntwdt_watchdog_enabled != 0) {
543 /*
544 * The user wants to enable the watchdog.
545 * Arm the watchdog and start the cyclic.
546 */
547 ntwdt_arm_watchdog(ntwdt_state);
548
549 if (ntwdt_state->ntwdt_timer_running == 0) {
550 ntwdt_start_timer(ntwdt_ptr);
551 }
552
553 NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is enabled"));
554 } else {
555 /*
556 * The user wants to disable the watchdog.
557 */
558 if (ntwdt_state->ntwdt_timer_running != 0) {
559 ntwdt_stop_timer(ntwdt_ptr);
560 }
561 NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT is disabled"));
562 }
563
564 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
565 break;
566
567 case LOMIOCDOGTIME:
568 if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime,
569 sizeof (uint32_t), mode) != 0) {
570 retval = EFAULT;
571 break;
572 }
573
574 NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set timeout: %d",
575 lom_dogtime));
576
577 /*
578 * Ensure specified timeout is valid.
579 */
580 if ((lom_dogtime == 0) ||
581 (lom_dogtime > (uint32_t)NTWDT_MAX_TIMEOUT)) {
582 retval = EINVAL;
583 NTWDT_DBG(NTWDT_DBG_IOCTL, ("user set invalid "
584 "timeout: %d", (int)TICK_TO_MSEC(lom_dogtime)));
585 break;
586 }
587
588 mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
589
590 ntwdt_state->ntwdt_watchdog_timeout = lom_dogtime;
591
592 /*
593 * If awdt is currently running, re-arm it with the
594 * newly-specified timeout value.
595 */
596 if (ntwdt_state->ntwdt_timer_running != 0) {
597 ntwdt_arm_watchdog(ntwdt_state);
598 }
599 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
600 break;
601
602 case LOMIOCDOGPAT:
603 /*
604 * Allow user to pat the watchdog timer.
605 */
606 NTWDT_DBG(NTWDT_DBG_IOCTL, ("DOGPAT is invoked"));
607 mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
608
609 /*
610 * If awdt is not enabled or underlying cyclic is not
611 * running, exit.
612 */
613 if (!(ntwdt_state->ntwdt_watchdog_enabled &&
614 ntwdt_state->ntwdt_timer_running)) {
615 NTWDT_DBG(NTWDT_DBG_IOCTL, ("PAT: AWDT not enabled"));
616 goto end;
617 }
618
619 if (ntwdt_state->ntwdt_watchdog_expired == 0) {
620 /*
621 * re-arm the awdt.
622 */
623 ntwdt_arm_watchdog(ntwdt_state);
624 NTWDT_DBG(NTWDT_DBG_IOCTL, ("AWDT patted, "
625 "remainning seconds: %d",
626 ntwdt_state->ntwdt_time_remaining));
627 }
628
629 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
630 break;
631
632 default:
633 retval = EINVAL;
634 break;
635 }
636 return (retval);
637 end:
638 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
639 return (retval);
640 }
641
642 static void
ntwdt_cyclic_pat(void)643 ntwdt_cyclic_pat(void)
644 {
645 ddi_trigger_softintr(ntwdt_cyclic_softint_id);
646 }
647
648 static uint_t
ntwdt_cyclic_softint(caddr_t arg)649 ntwdt_cyclic_softint(caddr_t arg)
650 {
651 /*LINTED E_BAD_PTR_CAST_ALIGN*/
652 ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
653 ntwdt_runstate_t *ntwdt_state;
654
655 ntwdt_state = ntwdt_ptr->ntwdt_run_state;
656
657 mutex_enter(&ntwdt_state->ntwdt_runstate_mutex);
658
659 if ((ntwdt_state->ntwdt_watchdog_flags & NTWDT_FLAG_SKIP_CYCLIC) != 0) {
660 ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
661 goto end;
662 }
663
664 if ((ntwdt_state->ntwdt_timer_running == 0) ||
665 (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) ||
666 (ntwdt_state->ntwdt_watchdog_enabled == 0)) {
667 goto end;
668 }
669
670 NTWDT_DBG(NTWDT_DBG_IOCTL, ("cyclic_softint: %d"
671 "ddi_get_lbolt64(): %d\n", ntwdt_state->ntwdt_watchdog_timeout,
672 (int)TICK_TO_MSEC(ddi_get_lbolt64())));
673
674 /*
675 * Decrement the virtual watchdog timer and check if it has expired.
676 */
677 ntwdt_state->ntwdt_time_remaining -= NTWDT_DECREMENT_INTERVAL;
678
679 if (ntwdt_state->ntwdt_time_remaining == 0) {
680 cmn_err(CE_WARN, "application-watchdog expired");
681 ntwdt_state->ntwdt_watchdog_expired = 1;
682
683 if (ntwdt_state->ntwdt_reset_enabled != 0) {
684 /*
685 * The user wants to reset the system.
686 */
687 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
688
689 NTWDT_DBG(NTWDT_DBG_NTWDT, ("recovery being done"));
690 ntwdt_enforce_timeout();
691 } else {
692 NTWDT_DBG(NTWDT_DBG_NTWDT, ("no recovery being done"));
693 ntwdt_state->ntwdt_watchdog_enabled = 0;
694 }
695
696 /*
697 * Schedule Callout to stop the cyclic.
698 */
699 (void) timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0);
700 } else {
701 _NOTE(EMPTY)
702 NTWDT_DBG(NTWDT_DBG_NTWDT, ("time remaining in AWDT: %d secs",
703 (int)TICK_TO_MSEC(ntwdt_state->ntwdt_time_remaining)));
704 }
705
706 end:
707 mutex_exit(&ntwdt_state->ntwdt_runstate_mutex);
708 return (DDI_INTR_CLAIMED);
709 }
710
711 static void
ntwdt_arm_watchdog(ntwdt_runstate_t * ntwdt_state)712 ntwdt_arm_watchdog(ntwdt_runstate_t *ntwdt_state)
713 {
714 ntwdt_state->ntwdt_time_remaining = ntwdt_state->ntwdt_watchdog_timeout;
715
716 if (ntwdt_state->ntwdt_timer_running != 0) {
717 ntwdt_state->ntwdt_watchdog_flags |= NTWDT_FLAG_SKIP_CYCLIC;
718 } else {
719 ntwdt_state->ntwdt_watchdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC;
720 }
721 }
722
723 static void
ntwdt_start_timer(ntwdt_state_t * ntwdt_ptr)724 ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr)
725 {
726 ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state;
727 cyc_handler_t *hdlr = &ntwdt_state->ntwdt_cycl_hdlr;
728 cyc_time_t *when = &ntwdt_state->ntwdt_cycl_time;
729
730 /*
731 * Init the cyclic.
732 */
733 when->cyt_interval = ntwdt_state->ntwdt_cyclic_interval;
734 when->cyt_when = gethrtime() + when->cyt_interval;
735
736 ntwdt_state->ntwdt_watchdog_expired = 0;
737 ntwdt_state->ntwdt_timer_running = 1;
738
739 mutex_enter(&cpu_lock);
740 if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) {
741 ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when);
742 }
743 mutex_exit(&cpu_lock);
744
745 NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is started"));
746 }
747
748 static void
ntwdt_stop_timer(void * arg)749 ntwdt_stop_timer(void *arg)
750 {
751 ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
752 ntwdt_runstate_t *ntwdt_state = ntwdt_ptr->ntwdt_run_state;
753
754 mutex_enter(&cpu_lock);
755 if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) {
756 cyclic_remove(ntwdt_ptr->ntwdt_cycl_id);
757 }
758 mutex_exit(&cpu_lock);
759
760 ntwdt_state->ntwdt_watchdog_flags = 0;
761 ntwdt_state->ntwdt_timer_running = 0;
762 ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE;
763
764 NTWDT_DBG(NTWDT_DBG_NTWDT, ("cyclic-driven timer is stopped"));
765 }
766
767 /*
768 * This is a wrapper function for ntwdt_stop_timer as some callers
769 * will already have the appropriate mutex locked, and others not.
770 */
771 static void
ntwdt_stop_timer_lock(void * arg)772 ntwdt_stop_timer_lock(void *arg)
773 {
774 ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg;
775
776 mutex_enter(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
777 ntwdt_stop_timer(arg);
778 mutex_exit(&ntwdt_ptr->ntwdt_run_state->ntwdt_runstate_mutex);
779 }
780
781 static void
ntwdt_enforce_timeout()782 ntwdt_enforce_timeout()
783 {
784 if (ntwdt_disable_timeout_action != 0) {
785 cmn_err(CE_NOTE, "Appication watchdog timer expired, "
786 "taking no action");
787 return;
788 }
789
790 NTWDT_DBG(NTWDT_DBG_NTWDT, ("dump cores and rebooting ..."));
791
792 (void) kadmin(A_DUMP, AD_BOOT, NULL, kcred);
793 cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed");
794 _NOTE(NOTREACHED);
795 }
796
797 static int
ntwdt_chk_watchdog_support()798 ntwdt_chk_watchdog_support()
799 {
800 int retval = 0;
801
802 if ((boothowto & RB_DEBUG) != 0) {
803 cmn_err(CE_WARN, "kernel debugger was booted; "
804 "application watchdog is not available.");
805 retval = ENOTSUP;
806 }
807 return (retval);
808 }
809