123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768 |
- #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,
- };
|