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/sysmsg.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, 106 wdog_reset_all, NULL); 107 } 108 wdog_auto_period = min_period; 109 110 done: 111 spin_unlock(&wdogmtx); 112 } 113 114 static void 115 wdog_set_period(int period) 116 { 117 struct watchdog *wd; 118 119 spin_lock(&wdogmtx); 120 LIST_FOREACH(wd, &wdoglist, link) { 121 /* XXX: check for period_max */ 122 wd->period = period; 123 } 124 spin_unlock(&wdogmtx); 125 } 126 127 128 static int 129 wdog_sysctl_auto(SYSCTL_HANDLER_ARGS) 130 { 131 int error; 132 133 error = sysctl_handle_int(oidp, &wdog_auto_enable, 1, req); 134 if (error || req->newptr == NULL) 135 return error; 136 137 /* has changed, do something */ 138 callout_stop(&wdog_callout); 139 if (wdog_auto_enable) { 140 wdog_reset_all(NULL); 141 } 142 143 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 144 (wdog_auto_enable)?"enabled":"disabled"); 145 146 return 0; 147 } 148 149 static int 150 wdog_sysctl_period(SYSCTL_HANDLER_ARGS) 151 { 152 int error; 153 154 error = sysctl_handle_int(oidp, &wdog_auto_period, WDOG_DEFAULT_PERIOD, req); 155 if (error || req->newptr == NULL) 156 return error; 157 158 /* has changed, do something */ 159 callout_stop(&wdog_callout); 160 wdog_set_period(wdog_auto_period); 161 wdog_reset_all(NULL); 162 163 if (wdog_auto_period != 0) 164 kprintf("wdog: Watchdog period set to %ds\n", wdog_auto_period); 165 else 166 kprintf("wdog: Disabled watchdog(s)\n"); 167 168 return 0; 169 } 170 171 void 172 wdog_disable(void) 173 { 174 callout_stop(&wdog_callout); 175 wdog_set_period(0); 176 wdog_reset_all(NULL); 177 } 178 179 static SYSCTL_NODE(_kern, OID_AUTO, watchdog, CTLFLAG_RW, 0, "watchdog"); 180 SYSCTL_PROC(_kern_watchdog, OID_AUTO, auto, CTLTYPE_INT | CTLFLAG_RW, 181 NULL, 0, wdog_sysctl_auto, "I", "auto in-kernel watchdog reset " 182 "(0 = disabled, 1 = enabled)"); 183 SYSCTL_PROC(_kern_watchdog, OID_AUTO, period, CTLTYPE_INT | CTLFLAG_RW, 184 NULL, 0, wdog_sysctl_period, "I", "watchdog period " 185 "(value in seconds)"); 186 187 188 static int 189 wdog_ioctl(struct dev_ioctl_args *ap) 190 { 191 if (wdog_auto_enable) 192 return EINVAL; 193 194 if (ap->a_cmd == WDIOCRESET) { 195 wdog_reset_all(NULL); 196 } else { 197 return EINVAL; 198 } 199 200 return 0; 201 } 202 203 static struct dev_ops wdog_ops = { 204 { "wdog", 0, 0 }, 205 .d_ioctl = wdog_ioctl, 206 }; 207 208 static void 209 wdog_init(void) 210 { 211 spin_init(&wdogmtx, "wdog"); 212 make_dev(&wdog_ops, 0, 213 UID_ROOT, GID_WHEEL, 0600, "wdog"); 214 callout_init_mp(&wdog_callout); 215 216 kprintf("wdog: In-kernel automatic watchdog reset %s\n", 217 (wdog_auto_enable)?"enabled":"disabled"); 218 } 219 220 static void 221 wdog_uninit(void) 222 { 223 callout_cancel(&wdog_callout); 224 callout_terminate(&wdog_callout); 225 dev_ops_remove_all(&wdog_ops); 226 spin_uninit(&wdogmtx); 227 } 228 229 SYSINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_init, NULL); 230 SYSUNINIT(wdog_register, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, wdog_uninit, NULL); 231