1 /* 2 * watchdog framework 3 */ 4 #include "u.h" 5 #include "../port/lib.h" 6 #include "mem.h" 7 #include "dat.h" 8 #include "fns.h" 9 #include "io.h" 10 #include "../port/error.h" 11 12 enum { 13 Qdir, 14 Qwdctl, 15 }; 16 17 /* 18 * these are exposed so that delay() and the like can disable the watchdog 19 * before busy looping for a long time. 20 */ 21 Watchdog*watchdog; 22 int watchdogon; 23 24 static Watchdog *wd; 25 static int wdautopet; 26 static int wdclock0called; 27 static Ref refs; 28 static Dirtab wddir[] = { 29 ".", { Qdir, 0, QTDIR }, 0, 0555, 30 "wdctl", { Qwdctl, 0 }, 0, 0664, 31 }; 32 33 34 void 35 addwatchdog(Watchdog *wdog) 36 { 37 if(wd){ 38 print("addwatchdog: watchdog already installed\n"); 39 return; 40 } 41 wd = watchdog = wdog; 42 if(wd) 43 wd->disable(); 44 } 45 46 static int 47 wdallowed(void) 48 { 49 return getconf("*nowatchdog") == nil; 50 } 51 52 static void 53 wdshutdown(void) 54 { 55 if (wd) { 56 wd->disable(); 57 watchdogon = 0; 58 } 59 } 60 61 /* called from clock interrupt, so restart needs ilock internally */ 62 static void 63 wdpet(void) 64 { 65 /* watchdog could be paused; if so, don't restart */ 66 if (wdautopet && watchdogon) 67 wd->restart(); 68 } 69 70 /* 71 * reassure the watchdog from the clock interrupt 72 * until the user takes control of it. 73 */ 74 static void 75 wdautostart(void) 76 { 77 if (wdautopet || !wd || !wdallowed()) 78 return; 79 if (waserror()) { 80 iprint("watchdog: enable failed\n"); 81 return; 82 } 83 wd->enable(); 84 poperror(); 85 86 iprint("watchdog: on with clock strokes\n"); 87 wdautopet = watchdogon = 1; 88 if (!wdclock0called) { 89 addclock0link(wdpet, 200); 90 wdclock0called = 1; 91 } 92 } 93 94 /* 95 * disable strokes from the clock interrupt. 96 * have to disable the watchdog to mark it `not in use'. 97 */ 98 static void 99 wdautostop(void) 100 { 101 if (!wdautopet) 102 return; 103 wdautopet = 0; 104 wdshutdown(); 105 iprint("watchdog: disabled before open\n"); 106 } 107 108 /* 109 * user processes exist and up is non-nil when the 110 * device init routines are called. 111 */ 112 static void 113 wdinit(void) 114 { 115 wdautostart(); 116 } 117 118 static Chan* 119 wdattach(char *spec) 120 { 121 return devattach('w', spec); 122 } 123 124 static Walkqid* 125 wdwalk(Chan *c, Chan *nc, char **name, int nname) 126 { 127 return devwalk(c, nc, name, nname, wddir, nelem(wddir), devgen); 128 } 129 130 static int 131 wdstat(Chan *c, uchar *dp, int n) 132 { 133 return devstat(c, dp, n, wddir, nelem(wddir), devgen); 134 } 135 136 static Chan* 137 wdopen(Chan* c, int omode) 138 { 139 wdautostop(); 140 c = devopen(c, omode, wddir, nelem(wddir), devgen); 141 if (c->qid.path == Qwdctl) 142 incref(&refs); 143 return c; 144 } 145 146 static void 147 wdclose(Chan *c) 148 { 149 if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0) 150 wdshutdown(); 151 } 152 153 static long 154 wdread(Chan* c, void* a, long n, vlong off) 155 { 156 ulong offset = off; 157 char *p; 158 159 switch((ulong)c->qid.path){ 160 case Qdir: 161 return devdirread(c, a, n, wddir, nelem(wddir), devgen); 162 163 case Qwdctl: 164 if(wd == nil || wd->stat == nil) 165 return 0; 166 167 p = malloc(READSTR); 168 if(p == nil) 169 error(Enomem); 170 if(waserror()){ 171 free(p); 172 nexterror(); 173 } 174 175 wd->stat(p, p + READSTR); 176 n = readstr(offset, a, n, p); 177 free(p); 178 poperror(); 179 return n; 180 181 default: 182 error(Egreg); 183 break; 184 } 185 return 0; 186 } 187 188 static long 189 wdwrite(Chan* c, void* a, long n, vlong off) 190 { 191 ulong offset = off; 192 char *p; 193 194 switch((ulong)c->qid.path){ 195 case Qdir: 196 error(Eperm); 197 198 case Qwdctl: 199 if(wd == nil) 200 return n; 201 202 if(offset || n >= READSTR) 203 error(Ebadarg); 204 205 if((p = strchr(a, '\n')) != nil) 206 *p = 0; 207 208 if(strncmp(a, "enable", n) == 0) { 209 if (waserror()) { 210 iprint("watchdog: enable failed\n"); 211 nexterror(); 212 } 213 wd->enable(); 214 poperror(); 215 watchdogon = 1; 216 } else if(strncmp(a, "disable", n) == 0) 217 wdshutdown(); 218 else if(strncmp(a, "restart", n) == 0) 219 wd->restart(); 220 else 221 error(Ebadarg); 222 return n; 223 224 default: 225 error(Egreg); 226 break; 227 } 228 229 return 0; 230 } 231 232 Dev wddevtab = { 233 'w', 234 "watchdog", 235 236 devreset, 237 wdinit, 238 wdshutdown, 239 wdattach, 240 wdwalk, 241 wdstat, 242 wdopen, 243 devcreate, 244 wdclose, 245 wdread, 246 devbread, 247 wdwrite, 248 devbwrite, 249 devremove, 250 devwstat, 251 devpower, 252 }; 253