#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "error.h" #include "realtime.h" #include "edf.h" #pragma varargck type "T" vlong /* debugging */ extern int edfprint; extern Edfinterface realedf, *edf; static Schedevent *events; static int nevents, revent, wevent; static Rendez eventr; static QLock elock; static Ref logopens; static Ref debugopens; static uvlong fasthz; enum { Qistask = 0x10000, Qdir = 0, Qrealtime, Qclone, Qdebug, Qdump, Qlog, Qnblog, Qresrc, Qtask, Qtime, Nevents = 10000, Clockshift = 17, // Good to about 10GHz clock and max. 5(s) }; Dirtab schedrootdir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "realtime", {Qrealtime, 0, QTDIR}, 0, DMDIR|0555, }; Dirtab scheddir[]={ ".", {Qrealtime, 0, QTDIR}, 0, DMDIR|0555, "clone", {Qclone}, 0, 0666, "debug", {Qdebug}, 0, 0444, "dump", {Qdump}, 0, 0444, "log", {Qlog}, 0, 0444, /* one open only */ "nblog", {Qnblog}, 0, 0444, /* nonblocking version of log */ "resources", {Qresrc}, 0, 0444, "task", {Qtask, 0, QTDIR}, 0, DMDIR|0555, "time", {Qtime}, 0, 0444, }; static char *schedstatename[] = { [SRelease] = "Release", [SRun] = "Run", [SPreempt] = "Preempt", [SBlock] = "Block", [SResume] = "Resume", [SDeadline] = "Deadline", [SYield] = "Yield", [SSlice] = "Slice", [SExpel] = "Expel", }; static int taskno; static Task * taskinit(void) { Dirtab *d; Task *t; t = malloc(sizeof(Task)); if (t == nil) error("taskinit: malloc"); d = &t->dir; if (up->user) kstrdup(&t->user, up->user); else kstrdup(&t->user, eve); t->state = EdfExpelled; t->taskno = ++taskno; snprint(d->name, sizeof d->name, "%d", t->taskno); mkqid(&d->qid, Qistask | t->taskno, 0, QTFILE); d->length = 0; d->perm = 0600; enlist(&tasks, t); incref(t); return t; } void taskfree(Task *t) { if (decref(t)) return; assert(t->procs.n == 0); assert(t->csns.n == 0); free(t->user); free(t); } /* * the zeroth element of the table MUST be the directory itself for .. */ int schedgen(Chan *c, char*, Dirtab *, int, int i, Dir *dp) { Dirtab *tab; int ntab; char *owner; ulong taskindex; Qid qid; Task *t; List *l; if((ulong)c->qid.path & Qistask){ qlock(&edfschedlock); taskindex = (ulong)c->qid.path & (Qistask-1); if ((t = findtask(taskindex)) == nil){ qunlock(&edfschedlock); return -1; } }else if((ulong)c->qid.path == Qtask){ qlock(&edfschedlock); taskindex = i; SET(t); for (l = tasks.next; l; l = l->next) if ((t = l->i) && taskindex-- == 0) break; if (l == nil){ qunlock(&edfschedlock); return -1; } }else { if((ulong)c->qid.path == Qdir){ tab = schedrootdir; ntab = nelem(schedrootdir); }else{ tab = scheddir; ntab = nelem(scheddir); } if(i != DEVDOTDOT){ /* skip over the first element, that for . itself */ i++; if(i >= ntab) return -1; tab += i; } devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); return 1; } if(i == DEVDOTDOT){ mkqid(&qid, Qtask, 0, QTDIR); devdir(c, qid, ".", 0, eve, 0555, dp); }else{ owner = t->user; if (owner == nil) owner = eve; tab = &t->dir; devdir(c, tab->qid, tab->name, tab->length, owner, tab->perm, dp); } qunlock(&edfschedlock); return 1; } static void _devrt(Task *t, Ticks t1, SEvent etype) { if (logopens.ref == 0 || nevents == Nevents) return; if(edfprint)iprint("state %s\n", schedstatename[etype]); events[wevent].tid = t->taskno; events[wevent].ts = 0; if (t1) events[wevent].ts = ticks2time(t1); else events[wevent].ts = 0; events[wevent].etype = etype; if (!canqlock(&elock)) return; wevent = (wevent + 1) % Nevents; if (nevents < Nevents) nevents++; else revent = (revent + 1) % Nevents; if(edfprint)iprint("wakesched\n"); /* To avoid circular wakeup when used in combination with * EDF scheduling. */ if (eventr.p && eventr.p->state == Wakeme) wakeup(&eventr); qunlock(&elock); } static void devrtinit(void) { fmtinstall('T', timeconv); fmtinstall('U', timeconv); fastticks(&fasthz); devrt = _devrt; events = (Schedevent *)malloc(sizeof(Schedevent) * Nevents); assert(events); nevents = revent = wevent = 0; edf = &realedf; } static Chan * devrtattach(char *param) { return devattach('R', param); } static Walkqid * devrtwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, nil, 0, schedgen); } static int devrtstat(Chan *c, uchar *db, int n) { return devstat(c, db, n, nil, 0, schedgen); } static Chan * devrtopen(Chan *c, int mode) { Task *t; switch ((ulong)c->qid.path){ case Qlog: case Qnblog: if (mode != OREAD) error(Eperm); incref(&logopens); if (logopens.ref > 1){ decref(&logopens); error("already open"); } break; case Qdebug: if (mode != OREAD) error(Eperm); incref(&debugopens); if (debugopens.ref > 1){ decref(&debugopens); error("already open"); } break; case Qdump: if (mode != OREAD) error(Eperm); break; case Qclone: if (mode == OREAD) error(Eperm); edf->edfinit(); qlock(&edfschedlock); /* open a new task */ t = taskinit(); c->qid.vers = t->taskno; qunlock(&edfschedlock); break; } // print("open %lux, mode %o\n", (ulong)c->qid.path, mode); return devopen(c, mode, nil, 0, schedgen); } static void devrtclose(Chan *c) { switch ((ulong)c->qid.path){ case Qlog: case Qnblog: nevents = revent = wevent = 0; decref(&logopens); break; case Qdebug: nevents = revent = wevent = 0; decref(&debugopens); break; } } static int eventsavailable(void *) { return nevents > 0; } static long devrtread(Chan *c, void *v, long n, vlong offs) { char *p, *e; char buf[1024]; long n0; int navail; Task *t; int s, i; Ticks now; Time tim; List *l; n0 = n; // print("schedread 0x%lux\n", (ulong)c->qid.path); buf[0] = '\0'; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, v, n, schedrootdir, nelem(schedrootdir), devgen); case Qrealtime: return devdirread(c, v, n, scheddir, nelem(scheddir), devgen); case Qtask: return devdirread(c, v, n, nil, 0, schedgen); case Qtime: if (n < sizeof(Time)) error(Ebadarg); now = fastticks(nil); tim = ticks2time(now); memmove(v, &tim, sizeof(Time)); n -= sizeof(Ticks); if (n >= sizeof(Ticks)){ memmove((char*)v + sizeof(Time), &now, sizeof(Ticks)); n -= sizeof(Ticks); } if (n >= sizeof(Ticks)){ memmove((char*)v + sizeof(Time) + sizeof(Ticks), &fasthz, sizeof(Ticks)); n -= sizeof(Ticks); } break; case Qnblog: if (eventsavailable(nil)) goto getevnt; break; case Qlog: while (!eventsavailable(nil)) sleep(&eventr, eventsavailable, nil); getevnt: p = (char *)v; navail = nevents; if (navail > n / sizeof(Schedevent)) navail = n / sizeof(Schedevent); n -= navail * sizeof(Schedevent); qlock(&elock); while (navail > 0) { int ncopy; ncopy = (revent + navail > Nevents)? Nevents - revent: navail; memmove(p, &events[revent], ncopy * sizeof(Schedevent)); revent = (revent+ ncopy) % Nevents; p += ncopy * sizeof(Schedevent); navail -= ncopy; nevents -= ncopy; } qunlock(&elock); break; case Qresrc: qlock(&edfschedlock); if(waserror()){ qunlock(&edfschedlock); nexterror(); } seprintresources(buf, buf + sizeof(buf)); qunlock(&edfschedlock); poperror(); return readstr(offs, v, n, buf); break; case Qdump: p = buf; e = p + sizeof(buf); qlock(&edfschedlock); qunlock(&edfschedlock); seprint(p, e, "\n"); return readstr(offs, v, n, buf); case Qdebug: p = buf; e = p + sizeof(buf); ilock(&edflock); now = fastticks(nil); for (i = 0; i < conf.nmach; i++){ p = seprint(p, e, "edfstack[%d]\n", i); p = dumpq(p, e, edfstack + i, now); } p = seprint(p, e, "qreleased\n"); p = dumpq(p, e, &qreleased, now); p = seprint(p, e, "qwaitrelease\n"); p = dumpq(p, e, &qwaitrelease, now); p = seprint(p, e, "qextratime\n"); dumpq(p, e, &qextratime, now); iunlock(&edflock); return readstr(offs, v, n, buf); case Qclone: s = c->qid.vers; goto common; default: if ((c->qid.path & Qistask) == 0) error(Enonexist); s = (ulong)c->qid.path & (Qistask - 1); common: qlock(&edfschedlock); t = findtask(s); if (t == nil){ qunlock(&edfschedlock); error(Enonexist); } p = buf; e = p + sizeof(buf); p = seprint(p, e, "task=%d", s); p = seprint(p, e, " state=%s", edfstatename[t->state]); if (t->T) p = seprint(p, e, " T=%T", ticks2time(t->T)); if (t->D) p = seprint(p, e, " D=%T", ticks2time(t->D)); if (t->C) p = seprint(p, e, " C=%T", ticks2time(t->C)); if (t->Delta) p = seprint(p, e, " Δ=%T", ticks2time(t->Delta)); else if (t->testDelta) p = seprint(p, e, " testΔ=%T", ticks2time(t->testDelta)); p = seprint(p, e, " yieldonblock=%d", (t->flags & Verbose) != 0); if (t->csns.n){ p = seprint(p, e, " resources='"); p = seprintcsn(p, e, &t->csns); p = seprint(p, e, "'"); } if (t->procs.n){ p = seprint(p, e, " procs='"); for (l = t->procs.next; l; l = l->next){ Proc *pr = l->i; assert(pr); if (l != t->procs.next) p = seprint(p, e, " "); p = seprint(p, e, "%lud", pr->pid); } p = seprint(p, e, "'"); } if (t->periods) p = seprint(p, e, " n=%lud", t->periods); if (t->missed) p = seprint(p, e, " m=%lud", t->missed); if (t->preemptions) p = seprint(p, e, " p=%lud", t->preemptions); if (t->total) p = seprint(p, e, " t=%T", ticks2time(t->total)); if (t->aged) p = seprint(p, e, " c=%T", ticks2time(t->aged)); seprint(p, e, "\n"); qunlock(&edfschedlock); return readstr(offs, v, n, buf); } return n0 - n; } static long devrtwrite(Chan *c, void *va, long n, vlong) { char *a, *v, *e, *args[16], *rargs[16], buf[512]; int i, j, s, nargs, nrargs, add; Resource *r; Task *t; Ticks ticks; Time time; long pid; Proc *p; CSN *l; a = va; if (c->mode == OREAD) error(Eperm); switch((ulong)c->qid.path){ case Qclone: s = c->qid.vers; goto common; default: if ((c->qid.path & Qistask) == 0) error(Enonexist); s = (ulong)c->qid.path & (Qistask - 1); common: qlock(&edfschedlock); if (waserror()){ qunlock(&edfschedlock); nexterror(); } t = findtask(s); if (t == nil) error(Enonexist); if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; nargs = tokenize(buf, args, nelem(args)); for (i = 0; i < nargs; i++){ a = args[i]; add = 0; if (v = strchr(a, '=')){ *v = '\0'; if (v != a && v[-1] == '+'){ add = 1; v[-1] = '\0'; } else if (v != a && v[-1] == '-'){ add = -1; v[-1] = '\0'; } v++; } if (strcmp(a, "T") == 0){ if (e=parsetime(&time, v)) error(e); ticks = time2ticks(time); edf->edfexpel(t); switch(add){ case -1: if (ticks > t->T) t->T = 0; else t->T -= ticks; break; case 0: t->T = ticks; break; case 1: t->T += ticks; break; } if (t->T < time2ticks(10000000/HZ)) error("period too short"); DEBUG("Task %d, T=%T\n", t->taskno, ticks2time(t->T)); }else if (strcmp(a, "D") == 0){ if (e=parsetime(&time, v)) error(e); ticks = time2ticks(time); edf->edfexpel(t); switch(add){ case -1: if (ticks > t->D) t->D = 0; else t->D -= ticks; break; case 0: t->D = ticks; break; case 1: t->D += ticks; break; } DEBUG("Task %d, D=%T\n", t->taskno, ticks2time(t->D)); }else if (strcmp(a, "C") == 0){ if (e=parsetime(&time, v)) error(e); ticks = time2ticks(time); edf->edfexpel(t); switch(add){ case -1: if (ticks > t->C) t->C = 0; else t->C -= ticks; break; case 0: t->C = ticks; break; case 1: t->C += ticks; break; } if (t->C < time2ticks(10000000/HZ)) error("cost too small"); DEBUG("Task %d, C=%T\n", t->taskno, ticks2time(t->C)); }else if (strcmp(a, "resources") == 0){ if (v == nil) error("resources: value missing"); edf->edfexpel(t); if (add < 0) error("can't remove resources yet"); if (add == 0){ List *l; while (l = t->csns.next) { r = l->i; assert(r); if (delist(&r->tasks, t)) taskfree(t); if (delist(&t->csns, r)) resourcefree(r); } assert(t->csns.n == 0); add = 1; USED(add); } v = parseresource(&t->csns, nil, v); if (v && *v) error("resources: parse error"); }else if (strcmp(a, "acquire") == 0){ if (v == nil) error("acquire: value missing"); if (up->task != t) error("acquire: not for another task"); if ((r = resource(v, 0)) == nil) error("acquire: no such resource"); for (l = (CSN*)t->csns.next; l; l = (CSN*)l->next){ if (l->i == r){ DEBUG("l->p (0x%p) == t->curcsn (0x%p) && l->S (%T) != 0\n", l->p, t->curcsn, ticks2time(l->S)); if(l->p == t->curcsn && l->S != 0) break; } } if (l == nil) error("acquire: no access or resource exhausted"); edf->resacquire(t, l); }else if (strcmp(a, "release") == 0){ if (v == nil) error("release: value missing"); if (up->task != t) error("release: not for another task"); if ((r = resource(v, 0)) == nil) error("release: no such resource"); if (t->curcsn->i != r) error("release: release not held or illegal release order"); edf->resrelease(t); }else if (strcmp(a, "procs") == 0){ if (v == nil) error("procs: value missing"); if (add <= 0){ edf->edfexpel(t); } if (add == 0){ List *l; while (l = t->procs.next){ p = l->i; assert(p->task == t); delist(&t->procs, p); p->task = nil; } add = 1; } nrargs = tokenize(v, rargs, nelem(rargs)); for (j = 0; j < nrargs; j++){ if (strcmp("self", rargs[j]) == 0){ p = up; }else{ pid = atoi(rargs[j]); if (pid <= 0) error("bad process number"); s = procindex(pid); if(s < 0) error("no such process"); p = proctab(s); } if(p->task && p->task != t) error("proc belongs to another task"); if (add > 0){ enlist(&t->procs, p); p->task = t; }else{ delist(&t->procs, p); p->task = nil; } } }else if (strcmp(a, "admit") == 0){ if (e = edf->edfadmit(t)) error(e); }else if (strcmp(a, "besteffort") == 0){ t->T = Infinity; t->D = Infinity; t->C = 0; t->flags |= BestEffort; if (e = edf->edfadmit(t)) error(e); }else if (strcmp(a, "expel") == 0){ edf->edfexpel(t); }else if (strcmp(a, "remove") == 0){ removetask(t); poperror(); qunlock(&edfschedlock); return n; /* Ignore any subsequent commands */ }else if (strcmp(a, "verbose") == 0){ if (t->flags & Verbose) t->flags &= ~Verbose; else t->flags |= Verbose; }else if (strcmp(a, "yieldonblock") == 0){ if (v == nil) error("yieldonblock: value missing"); if (add != 0) error("yieldonblock: cannot increment/decrement"); if (atoi(v) == 0) t->flags &= ~Useblocking; else t->flags |= Useblocking; }else if (strcmp(a, "yield") == 0){ if (edf->isedf(up) && up->task == t){ edf->edfdeadline(up); /* schedule next release */ qunlock(&edfschedlock); sched(); qlock(&edfschedlock); }else error("yield outside task"); }else error("unrecognized command"); } poperror(); qunlock(&edfschedlock); } return n; } static void devrtremove(Chan *c) { int s; Task *t; if ((c->qid.path & Qistask) == 0) error(Eperm); s = (ulong)c->qid.path & (Qistask - 1); t = findtask(s); if (t == nil) error(Enonexist); qlock(&edfschedlock); removetask(t); qunlock(&edfschedlock); } Dev realtimedevtab = { 'R', "scheduler", devreset, devrtinit, devshutdown, devrtattach, devrtwalk, devrtstat, devrtopen, devcreate, devrtclose, devrtread, devbread, devrtwrite, devbwrite, devrtremove, devwstat, };