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/ioccom.h> 41 #include <sys/spinlock2.h> 42 #include <sys/conf.h> 43 #include <sys/device.h> 44 #include <sys/filedesc.h> 45 #include <sys/sysctl.h> 46 #include <sys/unistd.h> 47 #include <sys/event.h> 48 #include <sys/queue.h> 49 #include <sys/wdog.h> 50 #include <machine/limits.h> 51 52 #ifdef WATCHDOG_ENABLE 53 static LIST_HEAD(, watchdog) wdoglist = LIST_HEAD_INITIALIZER(&wdoglist); 54 static struct spinlock wdogmtx; 55 static struct callout wdog_callout; 56 57 static int wdog_auto_enable = 1; 58 static int wdog_auto_period = WDOG_DEFAULT_PERIOD; 59 60 static void wdog_reset_all(void *unused); 61 62 void 63 wdog_register(struct watchdog *wd) 64 { 65 spin_lock(&wdogmtx); 66 wd->period = WDOG_DEFAULT_PERIOD; 67 LIST_INSERT_HEAD(&wdoglist, wd, link); 68 spin_unlock(&wdogmtx); 69 70 wdog_reset_all(NULL); 71 72 kprintf("wdog: Watchdog %s registered, max period = %ds , period = %ds\n", 73 wd->name, wd->period_max, wd->period); 74 } 75 76 void 77 wdog_unregister(struct watchdog *wd) 78 { 79 spin_lock(&wdogmtx); 80 LIST_REMOVE(wd, link); 81 spin_unlock(&wdogmtx); 82 83 kprintf("wdog: Watchdog %s unregistered\n", wd->name); 84 } 85 86 static int 87 wdog_reset(struct watchdog *wd) 88 { 89 return (wd->period = wd->wdog_fn(wd->arg, wd->period)); 90 } 91 92 static void 93 wdog_reset_all(void *unused) 94 { 95 struct watchdog *wd; 96 int period, min_period = INT_MAX; 97 98 spin_lock(&wdogmtx); 99 LIST_FOREACH(wd, &wdoglist, link) { 100 period = wdog_reset(wd); 101 if (period < min_period) 102 min_period = period; 103 } 104 if (wdog_auto_enable) 105 callout_reset(&wdog_callout, min_period * hz / 2, wdog_reset_all, NULL); 106 107 wdog_auto_period = min_period; 108 109 spin_unlock(&wdogmtx); 110 } 111 112 static void 113 wdog_set_period(int period) 114 { 115 struct watchdog *wd; 116 117 spin_lock(&wdogmtx); 118 LIST_FOREACH(wd, &wdoglist, link) { 119 /* XXX: check for period_max */ 120 wd->period = period; 121 } 122 spin_unlock(&wdogmtx); 123 } 124 125 126 static int 127 wdog_sysctl_auto(SYSCTL_HANDLER_ARGS) 128 { 129 int error; 130 131 error = sysctl_handle_int(oidp, &wdog_auto_enable, 1, req); 132 if (error || req->newptr == NULL) 133 return error; 134 135 /* has changed, do something */ 136 callout_stop(&wdog_callout); 137 if (wdog_auto_enable) { 138 wdog_reset_all(NULL); 139 } 140 141 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 142 (wdog_auto_enable)?"enabled":"disabled"); 143 144 return 0; 145 } 146 147 static int 148 wdog_sysctl_period(SYSCTL_HANDLER_ARGS) 149 { 150 int error; 151 152 error = sysctl_handle_int(oidp, &wdog_auto_period, WDOG_DEFAULT_PERIOD, req); 153 if (error || req->newptr == NULL) 154 return error; 155 156 /* has changed, do something */ 157 callout_stop(&wdog_callout); 158 wdog_set_period(wdog_auto_period); 159 wdog_reset_all(NULL); 160 161 if (wdog_auto_period != 0) 162 kprintf("wdog: Watchdog period set to %ds\n", wdog_auto_period); 163 else 164 kprintf("wdog: Disabled watchdog(s)\n"); 165 166 return 0; 167 } 168 169 void 170 wdog_disable(void) 171 { 172 callout_stop(&wdog_callout); 173 wdog_set_period(0); 174 wdog_reset_all(NULL); 175 } 176 177 static SYSCTL_NODE(_kern, OID_AUTO, watchdog, CTLFLAG_RW, 0, "watchdog"); 178 SYSCTL_PROC(_kern_watchdog, OID_AUTO, auto, CTLTYPE_INT | CTLFLAG_RW, 179 NULL, 0, wdog_sysctl_auto, "I", "auto in-kernel watchdog reset " 180 "(0 = disabled, 1 = enabled)"); 181 SYSCTL_PROC(_kern_watchdog, OID_AUTO, period, CTLTYPE_INT | CTLFLAG_RW, 182 NULL, 0, wdog_sysctl_period, "I", "watchdog period " 183 "(value in seconds)"); 184 185 186 static int 187 wdog_ioctl(struct dev_ioctl_args *ap) 188 { 189 if (wdog_auto_enable) 190 return EINVAL; 191 192 if (ap->a_cmd == WDIOCRESET) { 193 wdog_reset_all(NULL); 194 } else { 195 return EINVAL; 196 } 197 198 return 0; 199 } 200 201 static struct dev_ops wdog_ops = { 202 { "wdog", 0, 0 }, 203 .d_ioctl = wdog_ioctl, 204 }; 205 206 static void 207 wdog_init(void) 208 { 209 spin_init(&wdogmtx); 210 make_dev(&wdog_ops, 0, 211 UID_ROOT, GID_WHEEL, 0600, "wdog"); 212 callout_init_mp(&wdog_callout); 213 214 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 215 (wdog_auto_enable)?"enabled":"disabled"); 216 } 217 218 static void 219 wdog_uninit(void) 220 { 221 callout_stop(&wdog_callout); 222 callout_deactivate(&wdog_callout); 223 dev_ops_remove_all(&wdog_ops); 224 spin_uninit(&wdogmtx); 225 } 226 227 SYSINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_init, NULL); 228 SYSUNINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_uninit, NULL); 229 230 #endif /* WATCHDOG_ENABLE */ 231