1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include "opt_cpu.h" 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/systm.h> 38 #include <sys/malloc.h> 39 #include <sys/sysproto.h> 40 #include <sys/spinlock2.h> 41 #include <sys/conf.h> 42 #include <sys/device.h> 43 #include <sys/filedesc.h> 44 #include <sys/sysctl.h> 45 #include <sys/unistd.h> 46 #include <sys/event.h> 47 #include <sys/queue.h> 48 #include <sys/wdog.h> 49 #include <machine/limits.h> 50 51 #ifdef WATCHDOG_ENABLE 52 static LIST_HEAD(, watchdog) wdoglist = LIST_HEAD_INITIALIZER(&wdoglist); 53 static struct spinlock wdogmtx; 54 static struct callout wdog_callout; 55 56 static int wdog_auto_enable = 1; 57 static int wdog_auto_period = WDOG_DEFAULT_PERIOD; 58 59 static void wdog_reset_all(void *unused); 60 61 void 62 wdog_register(struct watchdog *wd) 63 { 64 spin_lock(&wdogmtx); 65 wd->period = WDOG_DEFAULT_PERIOD; 66 LIST_INSERT_HEAD(&wdoglist, wd, link); 67 spin_unlock(&wdogmtx); 68 69 wdog_reset_all(NULL); 70 71 kprintf("wdog: Watchdog %s registered, max period = %ds , period = %ds\n", 72 wd->name, wd->period_max, wd->period); 73 } 74 75 void 76 wdog_unregister(struct watchdog *wd) 77 { 78 spin_lock(&wdogmtx); 79 LIST_REMOVE(wd, link); 80 spin_unlock(&wdogmtx); 81 82 kprintf("wdog: Watchdog %s unregistered\n", wd->name); 83 } 84 85 static int 86 wdog_reset(struct watchdog *wd) 87 { 88 return (wd->period = wd->wdog_fn(wd->arg, wd->period)); 89 } 90 91 static void 92 wdog_reset_all(void *unused) 93 { 94 struct watchdog *wd; 95 int period, min_period = INT_MAX; 96 97 spin_lock(&wdogmtx); 98 LIST_FOREACH(wd, &wdoglist, link) { 99 period = wdog_reset(wd); 100 if (period < min_period) 101 min_period = period; 102 } 103 if (wdog_auto_enable) 104 callout_reset(&wdog_callout, min_period * hz / 2, wdog_reset_all, NULL); 105 106 wdog_auto_period = min_period; 107 108 spin_unlock(&wdogmtx); 109 } 110 111 static void 112 wdog_set_period(int period) 113 { 114 struct watchdog *wd; 115 116 spin_lock(&wdogmtx); 117 LIST_FOREACH(wd, &wdoglist, link) { 118 /* XXX: check for period_max */ 119 wd->period = period; 120 } 121 spin_unlock(&wdogmtx); 122 } 123 124 125 static int 126 wdog_sysctl_auto(SYSCTL_HANDLER_ARGS) 127 { 128 int error; 129 130 error = sysctl_handle_int(oidp, &wdog_auto_enable, 1, req); 131 if (error || req->newptr == NULL) 132 return error; 133 134 /* has changed, do something */ 135 callout_stop(&wdog_callout); 136 if (wdog_auto_enable) { 137 wdog_reset_all(NULL); 138 } 139 140 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 141 (wdog_auto_enable)?"enabled":"disabled"); 142 143 return 0; 144 } 145 146 static int 147 wdog_sysctl_period(SYSCTL_HANDLER_ARGS) 148 { 149 int error; 150 151 error = sysctl_handle_int(oidp, &wdog_auto_period, WDOG_DEFAULT_PERIOD, req); 152 if (error || req->newptr == NULL) 153 return error; 154 155 /* has changed, do something */ 156 callout_stop(&wdog_callout); 157 wdog_set_period(wdog_auto_period); 158 wdog_reset_all(NULL); 159 160 if (wdog_auto_period != 0) 161 kprintf("wdog: Watchdog period set to %ds\n", wdog_auto_period); 162 else 163 kprintf("wdog: Disabled watchdog(s)\n"); 164 165 return 0; 166 } 167 168 void 169 wdog_disable(void) 170 { 171 callout_stop(&wdog_callout); 172 wdog_set_period(0); 173 wdog_reset_all(NULL); 174 } 175 176 static SYSCTL_NODE(_kern, OID_AUTO, watchdog, CTLFLAG_RW, 0, "watchdog"); 177 SYSCTL_PROC(_kern_watchdog, OID_AUTO, auto, CTLTYPE_INT | CTLFLAG_RW, 178 NULL, 0, wdog_sysctl_auto, "I", "auto in-kernel watchdog reset " 179 "(0 = disabled, 1 = enabled)"); 180 SYSCTL_PROC(_kern_watchdog, OID_AUTO, period, CTLTYPE_INT | CTLFLAG_RW, 181 NULL, 0, wdog_sysctl_period, "I", "watchdog period " 182 "(value in seconds)"); 183 184 185 static int 186 wdog_ioctl(struct dev_ioctl_args *ap) 187 { 188 if (wdog_auto_enable) 189 return EINVAL; 190 191 if (ap->a_cmd == WDIOCRESET) { 192 wdog_reset_all(NULL); 193 } else { 194 return EINVAL; 195 } 196 197 return 0; 198 } 199 200 static struct dev_ops wdog_ops = { 201 { "wdog", 0, 0 }, 202 .d_ioctl = wdog_ioctl, 203 }; 204 205 static void 206 wdog_init(void) 207 { 208 spin_init(&wdogmtx); 209 make_dev(&wdog_ops, 0, 210 UID_ROOT, GID_WHEEL, 0600, "wdog"); 211 callout_init_mp(&wdog_callout); 212 213 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 214 (wdog_auto_enable)?"enabled":"disabled"); 215 } 216 217 static void 218 wdog_uninit(void) 219 { 220 callout_stop(&wdog_callout); 221 callout_deactivate(&wdog_callout); 222 dev_ops_remove_all(&wdog_ops); 223 spin_uninit(&wdogmtx); 224 } 225 226 SYSINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_init, NULL); 227 SYSUNINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_uninit, NULL); 228 229 #endif /* WATCHDOG_ENABLE */ 230