146136019SDavid du Colombier /*
246136019SDavid du Colombier * watchdog framework
346136019SDavid du Colombier */
4079fe160SDavid du Colombier #include "u.h"
5079fe160SDavid du Colombier #include "../port/lib.h"
6079fe160SDavid du Colombier #include "mem.h"
7079fe160SDavid du Colombier #include "dat.h"
8079fe160SDavid du Colombier #include "fns.h"
9079fe160SDavid du Colombier #include "io.h"
10079fe160SDavid du Colombier #include "../port/error.h"
11079fe160SDavid du Colombier
12079fe160SDavid du Colombier enum {
13079fe160SDavid du Colombier Qdir,
14079fe160SDavid du Colombier Qwdctl,
15079fe160SDavid du Colombier };
16079fe160SDavid du Colombier
17a650be7dSDavid du Colombier /*
18a650be7dSDavid du Colombier * these are exposed so that delay() and the like can disable the watchdog
19a650be7dSDavid du Colombier * before busy looping for a long time.
20a650be7dSDavid du Colombier */
21a650be7dSDavid du Colombier Watchdog*watchdog;
22a650be7dSDavid du Colombier int watchdogon;
23a650be7dSDavid du Colombier
24079fe160SDavid du Colombier static Watchdog *wd;
25f54a2a50SDavid du Colombier static int wdautopet;
26a650be7dSDavid du Colombier static int wdclock0called;
27606e0002SDavid du Colombier static Ref refs;
28079fe160SDavid du Colombier static Dirtab wddir[] = {
2961fd6f66SDavid du Colombier ".", { Qdir, 0, QTDIR }, 0, 0555,
3061fd6f66SDavid du Colombier "wdctl", { Qwdctl, 0 }, 0, 0664,
31079fe160SDavid du Colombier };
32079fe160SDavid du Colombier
33079fe160SDavid du Colombier
34079fe160SDavid du Colombier void
addwatchdog(Watchdog * wdog)35a650be7dSDavid du Colombier addwatchdog(Watchdog *wdog)
36079fe160SDavid du Colombier {
37079fe160SDavid du Colombier if(wd){
38079fe160SDavid du Colombier print("addwatchdog: watchdog already installed\n");
39079fe160SDavid du Colombier return;
40079fe160SDavid du Colombier }
41a650be7dSDavid du Colombier wd = watchdog = wdog;
42079fe160SDavid du Colombier if(wd)
43079fe160SDavid du Colombier wd->disable();
44079fe160SDavid du Colombier }
45079fe160SDavid du Colombier
46a650be7dSDavid du Colombier static int
wdallowed(void)47a650be7dSDavid du Colombier wdallowed(void)
48a650be7dSDavid du Colombier {
49a650be7dSDavid du Colombier return getconf("*nowatchdog") == nil;
50a650be7dSDavid du Colombier }
51a650be7dSDavid du Colombier
52f54a2a50SDavid du Colombier static void
wdshutdown(void)5386abb9fbSDavid du Colombier wdshutdown(void)
5486abb9fbSDavid du Colombier {
5586abb9fbSDavid du Colombier if (wd) {
5686abb9fbSDavid du Colombier wd->disable();
5786abb9fbSDavid du Colombier watchdogon = 0;
5886abb9fbSDavid du Colombier }
5986abb9fbSDavid du Colombier }
6086abb9fbSDavid du Colombier
6186abb9fbSDavid du Colombier /* called from clock interrupt, so restart needs ilock internally */
6286abb9fbSDavid du Colombier static void
wdpet(void)63f54a2a50SDavid du Colombier wdpet(void)
64f54a2a50SDavid du Colombier {
65a650be7dSDavid du Colombier /* watchdog could be paused; if so, don't restart */
66a650be7dSDavid du Colombier if (wdautopet && watchdogon)
67f54a2a50SDavid du Colombier wd->restart();
68f54a2a50SDavid du Colombier }
69f54a2a50SDavid du Colombier
70f54a2a50SDavid du Colombier /*
71f54a2a50SDavid du Colombier * reassure the watchdog from the clock interrupt
72f54a2a50SDavid du Colombier * until the user takes control of it.
73f54a2a50SDavid du Colombier */
74f54a2a50SDavid du Colombier static void
wdautostart(void)75f54a2a50SDavid du Colombier wdautostart(void)
76f54a2a50SDavid du Colombier {
77a650be7dSDavid du Colombier if (wdautopet || !wd || !wdallowed())
78f54a2a50SDavid du Colombier return;
79f80c7c99SDavid du Colombier if (waserror()) {
80*99c55427SDavid du Colombier print("watchdog: automatic enable failed\n");
81f80c7c99SDavid du Colombier return;
82f80c7c99SDavid du Colombier }
83f54a2a50SDavid du Colombier wd->enable();
84f80c7c99SDavid du Colombier poperror();
85f80c7c99SDavid du Colombier
86a650be7dSDavid du Colombier wdautopet = watchdogon = 1;
87a650be7dSDavid du Colombier if (!wdclock0called) {
88f54a2a50SDavid du Colombier addclock0link(wdpet, 200);
89a650be7dSDavid du Colombier wdclock0called = 1;
90a650be7dSDavid du Colombier }
91f54a2a50SDavid du Colombier }
92f54a2a50SDavid du Colombier
9386abb9fbSDavid du Colombier /*
9486abb9fbSDavid du Colombier * disable strokes from the clock interrupt.
9586abb9fbSDavid du Colombier * have to disable the watchdog to mark it `not in use'.
9686abb9fbSDavid du Colombier */
97f54a2a50SDavid du Colombier static void
wdautostop(void)98f54a2a50SDavid du Colombier wdautostop(void)
99f54a2a50SDavid du Colombier {
100f54a2a50SDavid du Colombier if (!wdautopet)
101f54a2a50SDavid du Colombier return;
10286abb9fbSDavid du Colombier wdautopet = 0;
10386abb9fbSDavid du Colombier wdshutdown();
104f54a2a50SDavid du Colombier }
105f54a2a50SDavid du Colombier
10670b6ec21SDavid du Colombier /*
10770b6ec21SDavid du Colombier * user processes exist and up is non-nil when the
10870b6ec21SDavid du Colombier * device init routines are called.
10970b6ec21SDavid du Colombier */
110f54a2a50SDavid du Colombier static void
wdinit(void)11170b6ec21SDavid du Colombier wdinit(void)
112f54a2a50SDavid du Colombier {
113f54a2a50SDavid du Colombier wdautostart();
114f54a2a50SDavid du Colombier }
115f54a2a50SDavid du Colombier
116079fe160SDavid du Colombier static Chan*
wdattach(char * spec)117079fe160SDavid du Colombier wdattach(char *spec)
118079fe160SDavid du Colombier {
119079fe160SDavid du Colombier return devattach('w', spec);
120079fe160SDavid du Colombier }
121079fe160SDavid du Colombier
122079fe160SDavid du Colombier static Walkqid*
wdwalk(Chan * c,Chan * nc,char ** name,int nname)123079fe160SDavid du Colombier wdwalk(Chan *c, Chan *nc, char **name, int nname)
124079fe160SDavid du Colombier {
125079fe160SDavid du Colombier return devwalk(c, nc, name, nname, wddir, nelem(wddir), devgen);
126079fe160SDavid du Colombier }
127079fe160SDavid du Colombier
128079fe160SDavid du Colombier static int
wdstat(Chan * c,uchar * dp,int n)129079fe160SDavid du Colombier wdstat(Chan *c, uchar *dp, int n)
130079fe160SDavid du Colombier {
131079fe160SDavid du Colombier return devstat(c, dp, n, wddir, nelem(wddir), devgen);
132079fe160SDavid du Colombier }
133079fe160SDavid du Colombier
134079fe160SDavid du Colombier static Chan*
wdopen(Chan * c,int omode)135079fe160SDavid du Colombier wdopen(Chan* c, int omode)
136079fe160SDavid du Colombier {
137f54a2a50SDavid du Colombier wdautostop();
138606e0002SDavid du Colombier c = devopen(c, omode, wddir, nelem(wddir), devgen);
139606e0002SDavid du Colombier if (c->qid.path == Qwdctl)
140606e0002SDavid du Colombier incref(&refs);
141606e0002SDavid du Colombier return c;
142079fe160SDavid du Colombier }
143079fe160SDavid du Colombier
144079fe160SDavid du Colombier static void
wdclose(Chan * c)145a650be7dSDavid du Colombier wdclose(Chan *c)
146606e0002SDavid du Colombier {
147a650be7dSDavid du Colombier if(c->qid.path == Qwdctl && c->flag&COPEN && decref(&refs) <= 0)
148a650be7dSDavid du Colombier wdshutdown();
149079fe160SDavid du Colombier }
150079fe160SDavid du Colombier
151079fe160SDavid du Colombier static long
wdread(Chan * c,void * a,long n,vlong off)152079fe160SDavid du Colombier wdread(Chan* c, void* a, long n, vlong off)
153079fe160SDavid du Colombier {
154079fe160SDavid du Colombier ulong offset = off;
15546136019SDavid du Colombier char *p;
156079fe160SDavid du Colombier
157079fe160SDavid du Colombier switch((ulong)c->qid.path){
158079fe160SDavid du Colombier case Qdir:
159079fe160SDavid du Colombier return devdirread(c, a, n, wddir, nelem(wddir), devgen);
160079fe160SDavid du Colombier
161079fe160SDavid du Colombier case Qwdctl:
162079fe160SDavid du Colombier if(wd == nil || wd->stat == nil)
163079fe160SDavid du Colombier return 0;
164079fe160SDavid du Colombier
165079fe160SDavid du Colombier p = malloc(READSTR);
166aa72973aSDavid du Colombier if(p == nil)
167aa72973aSDavid du Colombier error(Enomem);
168079fe160SDavid du Colombier if(waserror()){
169079fe160SDavid du Colombier free(p);
170079fe160SDavid du Colombier nexterror();
171079fe160SDavid du Colombier }
172079fe160SDavid du Colombier
173079fe160SDavid du Colombier wd->stat(p, p + READSTR);
174079fe160SDavid du Colombier n = readstr(offset, a, n, p);
175079fe160SDavid du Colombier free(p);
17646136019SDavid du Colombier poperror();
177079fe160SDavid du Colombier return n;
178079fe160SDavid du Colombier
179079fe160SDavid du Colombier default:
180079fe160SDavid du Colombier error(Egreg);
181079fe160SDavid du Colombier break;
182079fe160SDavid du Colombier }
183079fe160SDavid du Colombier return 0;
184079fe160SDavid du Colombier }
185079fe160SDavid du Colombier
186079fe160SDavid du Colombier static long
wdwrite(Chan * c,void * a,long n,vlong off)187079fe160SDavid du Colombier wdwrite(Chan* c, void* a, long n, vlong off)
188079fe160SDavid du Colombier {
189079fe160SDavid du Colombier ulong offset = off;
19046136019SDavid du Colombier char *p;
191079fe160SDavid du Colombier
192079fe160SDavid du Colombier switch((ulong)c->qid.path){
193079fe160SDavid du Colombier case Qdir:
194079fe160SDavid du Colombier error(Eperm);
195079fe160SDavid du Colombier
196079fe160SDavid du Colombier case Qwdctl:
197079fe160SDavid du Colombier if(wd == nil)
198079fe160SDavid du Colombier return n;
199079fe160SDavid du Colombier
200079fe160SDavid du Colombier if(offset || n >= READSTR)
201079fe160SDavid du Colombier error(Ebadarg);
202079fe160SDavid du Colombier
20346136019SDavid du Colombier if((p = strchr(a, '\n')) != nil)
20446136019SDavid du Colombier *p = 0;
20546136019SDavid du Colombier
206a650be7dSDavid du Colombier if(strncmp(a, "enable", n) == 0) {
207f80c7c99SDavid du Colombier if (waserror()) {
2086bbfed0dSDavid du Colombier print("watchdog: enable failed\n");
209f80c7c99SDavid du Colombier nexterror();
210f80c7c99SDavid du Colombier }
21146136019SDavid du Colombier wd->enable();
212f80c7c99SDavid du Colombier poperror();
213a650be7dSDavid du Colombier watchdogon = 1;
214a650be7dSDavid du Colombier } else if(strncmp(a, "disable", n) == 0)
215a650be7dSDavid du Colombier wdshutdown();
21646136019SDavid du Colombier else if(strncmp(a, "restart", n) == 0)
21746136019SDavid du Colombier wd->restart();
21846136019SDavid du Colombier else
21946136019SDavid du Colombier error(Ebadarg);
220079fe160SDavid du Colombier return n;
221079fe160SDavid du Colombier
222079fe160SDavid du Colombier default:
223079fe160SDavid du Colombier error(Egreg);
224079fe160SDavid du Colombier break;
225079fe160SDavid du Colombier }
226079fe160SDavid du Colombier
227079fe160SDavid du Colombier return 0;
228079fe160SDavid du Colombier }
229079fe160SDavid du Colombier
230079fe160SDavid du Colombier Dev wddevtab = {
231079fe160SDavid du Colombier 'w',
232079fe160SDavid du Colombier "watchdog",
233079fe160SDavid du Colombier
23470b6ec21SDavid du Colombier devreset,
23570b6ec21SDavid du Colombier wdinit,
236606e0002SDavid du Colombier wdshutdown,
237079fe160SDavid du Colombier wdattach,
238079fe160SDavid du Colombier wdwalk,
239079fe160SDavid du Colombier wdstat,
240079fe160SDavid du Colombier wdopen,
241079fe160SDavid du Colombier devcreate,
242079fe160SDavid du Colombier wdclose,
243079fe160SDavid du Colombier wdread,
244079fe160SDavid du Colombier devbread,
245079fe160SDavid du Colombier wdwrite,
246079fe160SDavid du Colombier devbwrite,
247079fe160SDavid du Colombier devremove,
248079fe160SDavid du Colombier devwstat,
249079fe160SDavid du Colombier devpower,
250079fe160SDavid du Colombier };
251