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