1 /* EDF scheduling */ 2 #include <u.h> 3 #include "../port/lib.h" 4 #include "mem.h" 5 #include "dat.h" 6 #include "fns.h" 7 #include "../port/error.h" 8 #include "../port/edf.h" 9 #include <trace.h> 10 11 /* debugging */ 12 enum { 13 Dontprint = 1, 14 }; 15 16 #define DPRINT if(Dontprint){}else print 17 18 static long now; /* Low order 32 bits of time in µs */ 19 extern ulong delayedscheds; 20 extern Schedq runq[Nrq]; 21 extern int nrdy; 22 extern ulong runvec; 23 24 /* Statistics stuff */ 25 ulong nilcount; 26 ulong scheds; 27 ulong edfnrun; 28 int misseddeadlines; 29 30 /* Edfschedlock protects modification of admission params */ 31 int edfinited; 32 QLock edfschedlock; 33 static Lock thelock; 34 35 enum{ 36 Dl, /* Invariant for schedulability test: Dl < Rl */ 37 Rl, 38 }; 39 40 static char *testschedulability(Proc*); 41 static Proc *qschedulability; 42 43 enum { 44 Onemicrosecond = 1, 45 Onemillisecond = 1000, 46 Onesecond = 1000000, 47 OneRound = Onemillisecond/2, 48 }; 49 50 static int 51 timeconv(Fmt *f) 52 { 53 char buf[128], *sign; 54 vlong t; 55 56 buf[0] = 0; 57 switch(f->r) { 58 case 'U': 59 t = va_arg(f->args, uvlong); 60 break; 61 case 't': /* vlong in nanoseconds */ 62 t = va_arg(f->args, long); 63 break; 64 default: 65 return fmtstrcpy(f, "(timeconv)"); 66 } 67 if (t < 0) { 68 sign = "-"; 69 t = -t; 70 } 71 else 72 sign = ""; 73 if (t > Onesecond){ 74 t += OneRound; 75 sprint(buf, "%s%d.%.3ds", sign, (int)(t / Onesecond), 76 (int)(t % Onesecond)/Onemillisecond); 77 }else if (t > Onemillisecond) 78 sprint(buf, "%s%d.%.3dms", sign, (int)(t / Onemillisecond), 79 (int)(t % Onemillisecond)); 80 else 81 sprint(buf, "%s%dµs", sign, (int)t); 82 return fmtstrcpy(f, buf); 83 } 84 85 long edfcycles; 86 87 Edf* 88 edflock(Proc *p) 89 { 90 Edf *e; 91 92 if (p->edf == nil) 93 return nil; 94 ilock(&thelock); 95 if((e = p->edf) && (e->flags & Admitted)){ 96 thelock.pc = getcallerpc(&p); 97 #ifdef EDFCYCLES 98 edfcycles -= lcycles(); 99 #endif 100 now = µs(); 101 return e; 102 } 103 iunlock(&thelock); 104 return nil; 105 } 106 107 void 108 edfunlock(void) 109 { 110 111 #ifdef EDFCYCLES 112 edfcycles += lcycles(); 113 #endif 114 edfnrun++; 115 iunlock(&thelock); 116 } 117 118 void 119 edfinit(Proc*p) 120 { 121 if(!edfinited){ 122 fmtinstall('t', timeconv); 123 edfinited++; 124 } 125 now = µs(); 126 DPRINT("%lud edfinit %lud[%s]\n", now, p->pid, statename[p->state]); 127 p->edf = malloc(sizeof(Edf)); 128 if(p->edf == nil) 129 error(Enomem); 130 return; 131 } 132 133 static void 134 deadlineintr(Ureg*, Timer *t) 135 { 136 /* Proc reached deadline */ 137 extern int panicking; 138 Proc *p; 139 void (*pt)(Proc*, int, vlong); 140 141 if(panicking || active.exiting) 142 return; 143 144 p = t->ta; 145 now = µs(); 146 DPRINT("%lud deadlineintr %lud[%s]\n", now, p->pid, statename[p->state]); 147 /* If we're interrupting something other than the proc pointed to by t->a, 148 * we've already achieved recheduling, so we need not do anything 149 * Otherwise, we must cause a reschedule, but if we call sched() 150 * here directly, the timer interrupt routine will not finish its business 151 * Instead, we cause the resched to happen when the interrupted proc 152 * returns to user space 153 */ 154 if(p == up){ 155 if(up->trace && (pt = proctrace)) 156 pt(up, SInts, 0); 157 up->delaysched++; 158 delayedscheds++; 159 } 160 } 161 162 static void 163 release(Proc *p) 164 { 165 /* Called with edflock held */ 166 Edf *e; 167 void (*pt)(Proc*, int, vlong); 168 long n; 169 vlong nowns; 170 171 e = p->edf; 172 e->flags &= ~Yield; 173 if(e->d - now < 0){ 174 e->periods++; 175 e->r = now; 176 if((e->flags & Sporadic) == 0){ 177 /* 178 * Non sporadic processes stay true to their period; 179 * calculate next release time. 180 * Second test limits duration of while loop. 181 */ 182 if((n = now - e->t) > 0){ 183 if(n < e->T) 184 e->t += e->T; 185 else 186 e->t = now + e->T - (n % e->T); 187 } 188 }else{ 189 /* Sporadic processes may not be released earlier than 190 * one period after this release 191 */ 192 e->t = e->r + e->T; 193 } 194 e->d = e->r + e->D; 195 e->S = e->C; 196 DPRINT("%lud release %lud[%s], r=%lud, d=%lud, t=%lud, S=%lud\n", 197 now, p->pid, statename[p->state], e->r, e->d, e->t, e->S); 198 if(pt = proctrace){ 199 nowns = todget(nil); 200 pt(p, SRelease, nowns); 201 pt(p, SDeadline, nowns + 1000LL*e->D); 202 } 203 }else{ 204 DPRINT("%lud release %lud[%s], too late t=%lud, called from %#p\n", 205 now, p->pid, statename[p->state], e->t, getcallerpc(&p)); 206 } 207 } 208 209 static void 210 releaseintr(Ureg*, Timer *t) 211 { 212 Proc *p; 213 extern int panicking; 214 Schedq *rq; 215 216 if(panicking || active.exiting) 217 return; 218 219 p = t->ta; 220 if((edflock(p)) == nil) 221 return; 222 DPRINT("%lud releaseintr %lud[%s]\n", now, p->pid, statename[p->state]); 223 switch(p->state){ 224 default: 225 edfunlock(); 226 return; 227 case Ready: 228 /* remove proc from current runq */ 229 rq = &runq[p->priority]; 230 if(dequeueproc(rq, p) != p){ 231 DPRINT("releaseintr: can't find proc or lock race\n"); 232 release(p); /* It'll start best effort */ 233 edfunlock(); 234 return; 235 } 236 p->state = Waitrelease; 237 /* fall through */ 238 case Waitrelease: 239 release(p); 240 edfunlock(); 241 if(p->state == Wakeme){ 242 iprint("releaseintr: wakeme\n"); 243 } 244 ready(p); 245 if(up){ 246 up->delaysched++; 247 delayedscheds++; 248 } 249 return; 250 case Running: 251 release(p); 252 edfrun(p, 1); 253 break; 254 case Wakeme: 255 release(p); 256 edfunlock(); 257 if(p->trend) 258 wakeup(p->trend); 259 p->trend = nil; 260 if(up){ 261 up->delaysched++; 262 delayedscheds++; 263 } 264 return; 265 } 266 edfunlock(); 267 } 268 269 void 270 edfrecord(Proc *p) 271 { 272 long used; 273 Edf *e; 274 void (*pt)(Proc*, int, vlong); 275 276 if((e = edflock(p)) == nil) 277 return; 278 used = now - e->s; 279 if(e->d - now <= 0) 280 e->edfused += used; 281 else 282 e->extraused += used; 283 if(e->S > 0){ 284 if(e->S <= used){ 285 if(pt = proctrace) 286 pt(p, SSlice, 0); 287 DPRINT("%lud edfrecord slice used up\n", now); 288 e->d = now; 289 e->S = 0; 290 }else 291 e->S -= used; 292 } 293 e->s = now; 294 edfunlock(); 295 } 296 297 void 298 edfrun(Proc *p, int edfpri) 299 { 300 Edf *e; 301 void (*pt)(Proc*, int, vlong); 302 long tns; 303 304 e = p->edf; 305 /* Called with edflock held */ 306 if(edfpri){ 307 tns = e->d - now; 308 if(tns <= 0 || e->S == 0){ 309 /* Deadline reached or resources exhausted, 310 * deschedule forthwith 311 */ 312 p->delaysched++; 313 delayedscheds++; 314 e->s = now; 315 return; 316 } 317 if(e->S < tns) 318 tns = e->S; 319 if(tns < 20) 320 tns = 20; 321 e->tns = 1000LL * tns; /* µs to ns */ 322 if(e->tt == nil || e->tf != deadlineintr){ 323 DPRINT("%lud edfrun, deadline=%lud\n", now, tns); 324 }else{ 325 DPRINT("v"); 326 } 327 if(p->trace && (pt = proctrace)) 328 pt(p, SInte, todget(nil) + e->tns); 329 e->tmode = Trelative; 330 e->tf = deadlineintr; 331 e->ta = p; 332 timeradd(e); 333 }else{ 334 DPRINT("<"); 335 } 336 e->s = now; 337 } 338 339 char * 340 edfadmit(Proc *p) 341 { 342 char *err; 343 Edf *e; 344 int i; 345 Proc *r; 346 void (*pt)(Proc*, int, vlong); 347 long tns; 348 349 e = p->edf; 350 if (e->flags & Admitted) 351 return "task state"; /* should never happen */ 352 353 /* simple sanity checks */ 354 if (e->T == 0) 355 return "T not set"; 356 if (e->C == 0) 357 return "C not set"; 358 if (e->D > e->T) 359 return "D > T"; 360 if (e->D == 0) /* if D is not set, set it to T */ 361 e->D = e->T; 362 if (e->C > e->D) 363 return "C > D"; 364 365 qlock(&edfschedlock); 366 if (err = testschedulability(p)){ 367 qunlock(&edfschedlock); 368 return err; 369 } 370 e->flags |= Admitted; 371 372 edflock(p); 373 374 if(p->trace && (pt = proctrace)) 375 pt(p, SAdmit, 0); 376 377 /* Look for another proc with the same period to synchronize to */ 378 SET(r); 379 for(i=0; i<conf.nproc; i++) { 380 r = proctab(i); 381 if(r->state == Dead || r == p) 382 continue; 383 if (r->edf == nil || (r->edf->flags & Admitted) == 0) 384 continue; 385 if (r->edf->T == e->T) 386 break; 387 } 388 if (i == conf.nproc){ 389 /* Can't synchronize to another proc, release now */ 390 e->t = now; 391 e->d = 0; 392 release(p); 393 if (p == up){ 394 DPRINT("%lud edfadmit self %lud[%s], release now: r=%lud d=%lud t=%lud\n", 395 now, p->pid, statename[p->state], e->r, e->d, e->t); 396 /* We're already running */ 397 edfrun(p, 1); 398 }else{ 399 /* We're releasing another proc */ 400 DPRINT("%lud edfadmit other %lud[%s], release now: r=%lud d=%lud t=%lud\n", 401 now, p->pid, statename[p->state], e->r, e->d, e->t); 402 p->ta = p; 403 edfunlock(); 404 qunlock(&edfschedlock); 405 releaseintr(nil, p); 406 return nil; 407 } 408 }else{ 409 /* Release in synch to something else */ 410 e->t = r->edf->t; 411 if (p == up){ 412 DPRINT("%lud edfadmit self %lud[%s], release at %lud\n", 413 now, p->pid, statename[p->state], e->t); 414 edfunlock(); 415 qunlock(&edfschedlock); 416 return nil; 417 }else{ 418 DPRINT("%lud edfadmit other %lud[%s], release at %lud\n", 419 now, p->pid, statename[p->state], e->t); 420 if(e->tt == nil){ 421 e->tf = releaseintr; 422 e->ta = p; 423 tns = e->t - now; 424 if(tns < 20) 425 tns = 20; 426 e->tns = 1000LL * tns; 427 e->tmode = Trelative; 428 timeradd(e); 429 } 430 } 431 } 432 edfunlock(); 433 qunlock(&edfschedlock); 434 return nil; 435 } 436 437 void 438 edfstop(Proc *p) 439 { 440 Edf *e; 441 void (*pt)(Proc*, int, vlong); 442 443 if(e = edflock(p)){ 444 DPRINT("%lud edfstop %lud[%s]\n", now, p->pid, statename[p->state]); 445 if(p->trace && (pt = proctrace)) 446 pt(p, SExpel, 0); 447 e->flags &= ~Admitted; 448 if(e->tt) 449 timerdel(e); 450 edfunlock(); 451 } 452 } 453 454 static int 455 yfn(void *) 456 { 457 now = µs(); 458 return up->trend == nil || now - up->edf->r >= 0; 459 } 460 461 void 462 edfyield(void) 463 { 464 /* sleep until next release */ 465 Edf *e; 466 void (*pt)(Proc*, int, vlong); 467 long n; 468 469 if((e = edflock(up)) == nil) 470 return; 471 if(up->trace && (pt = proctrace)) 472 pt(up, SYield, 0); 473 if((n = now - e->t) > 0){ 474 if(n < e->T) 475 e->t += e->T; 476 else 477 e->t = now + e->T - (n % e->T); 478 } 479 e->r = e->t; 480 e->flags |= Yield; 481 e->d = now; 482 if (up->tt == nil){ 483 n = e->t - now; 484 if(n < 20) 485 n = 20; 486 up->tns = 1000LL * n; 487 up->tf = releaseintr; 488 up->tmode = Trelative; 489 up->ta = up; 490 up->trend = &up->sleep; 491 timeradd(up); 492 }else if(up->tf != releaseintr) 493 print("edfyield: surprise! %#p\n", up->tf); 494 edfunlock(); 495 sleep(&up->sleep, yfn, nil); 496 } 497 498 int 499 edfready(Proc *p) 500 { 501 Edf *e; 502 Schedq *rq; 503 Proc *l, *pp; 504 void (*pt)(Proc*, int, vlong); 505 long n; 506 507 if((e = edflock(p)) == nil) 508 return 0; 509 510 if(p->state == Wakeme && p->r){ 511 iprint("edfready: wakeme\n"); 512 } 513 if(e->d - now <= 0){ 514 /* past deadline, arrange for next release */ 515 if((e->flags & Sporadic) == 0){ 516 /* 517 * Non sporadic processes stay true to their period; 518 * calculate next release time. 519 */ 520 if((n = now - e->t) > 0){ 521 if(n < e->T) 522 e->t += e->T; 523 else 524 e->t = now + e->T - (n % e->T); 525 } 526 } 527 if(now - e->t < 0){ 528 /* Next release is in the future, schedule it */ 529 if(e->tt == nil || e->tf != releaseintr){ 530 n = e->t - now; 531 if(n < 20) 532 n = 20; 533 e->tns = 1000LL * n; 534 e->tmode = Trelative; 535 e->tf = releaseintr; 536 e->ta = p; 537 timeradd(e); 538 DPRINT("%lud edfready %lud[%s], release=%lud\n", 539 now, p->pid, statename[p->state], e->t); 540 } 541 if(p->state == Running && (e->flags & (Yield|Yieldonblock)) == 0 && (e->flags & Extratime)){ 542 /* If we were running, we've overrun our CPU allocation 543 * or missed the deadline, continue running best-effort at low priority 544 * Otherwise we were blocked. If we don't yield on block, we continue 545 * best effort 546 */ 547 DPRINT(">"); 548 p->basepri = PriExtra; 549 p->fixedpri = 1; 550 edfunlock(); 551 return 0; /* Stick on runq[PriExtra] */ 552 } 553 DPRINT("%lud edfready %lud[%s] wait release at %lud\n", 554 now, p->pid, statename[p->state], e->t); 555 p->state = Waitrelease; 556 edfunlock(); 557 return 1; /* Make runnable later */ 558 } 559 DPRINT("%lud edfready %lud %s release now\n", now, p->pid, statename[p->state]); 560 /* release now */ 561 release(p); 562 } 563 edfunlock(); 564 DPRINT("^"); 565 rq = &runq[PriEdf]; 566 /* insert in queue in earliest deadline order */ 567 lock(runq); 568 l = nil; 569 for(pp = rq->head; pp; pp = pp->rnext){ 570 if(pp->edf->d > e->d) 571 break; 572 l = pp; 573 } 574 p->rnext = pp; 575 if (l == nil) 576 rq->head = p; 577 else 578 l->rnext = p; 579 if(pp == nil) 580 rq->tail = p; 581 rq->n++; 582 nrdy++; 583 runvec |= 1 << PriEdf; 584 p->priority = PriEdf; 585 p->readytime = m->ticks; 586 p->state = Ready; 587 unlock(runq); 588 if(p->trace && (pt = proctrace)) 589 pt(p, SReady, 0); 590 return 1; 591 } 592 593 594 static void 595 testenq(Proc *p) 596 { 597 Proc *xp, **xpp; 598 Edf *e; 599 600 e = p->edf; 601 e->testnext = nil; 602 if (qschedulability == nil) { 603 qschedulability = p; 604 return; 605 } 606 SET(xp); 607 for (xpp = &qschedulability; *xpp; xpp = &xp->edf->testnext) { 608 xp = *xpp; 609 if (e->testtime - xp->edf->testtime < 0 610 || (e->testtime == xp->edf->testtime && e->testtype < xp->edf->testtype)){ 611 e->testnext = xp; 612 *xpp = p; 613 return; 614 } 615 } 616 assert(xp->edf->testnext == nil); 617 xp->edf->testnext = p; 618 } 619 620 static char * 621 testschedulability(Proc *theproc) 622 { 623 Proc *p; 624 long H, G, Cb, ticks; 625 int steps, i; 626 627 /* initialize */ 628 DPRINT("schedulability test %lud\n", theproc->pid); 629 qschedulability = nil; 630 for(i=0; i<conf.nproc; i++) { 631 p = proctab(i); 632 if(p->state == Dead) 633 continue; 634 if ((p->edf == nil || (p->edf->flags & Admitted) == 0) && p != theproc) 635 continue; 636 p->edf->testtype = Rl; 637 p->edf->testtime = 0; 638 DPRINT("\tInit: edfenqueue %lud\n", p->pid); 639 testenq(p); 640 } 641 H=0; 642 G=0; 643 for(steps = 0; steps < Maxsteps; steps++){ 644 p = qschedulability; 645 qschedulability = p->edf->testnext; 646 ticks = p->edf->testtime; 647 switch (p->edf->testtype){ 648 case Dl: 649 H += p->edf->C; 650 Cb = 0; 651 DPRINT("\tStep %3d, Ticks %lud, pid %lud, deadline, H += %lud → %lud, Cb = %lud\n", 652 steps, ticks, p->pid, p->edf->C, H, Cb); 653 if (H+Cb>ticks){ 654 DPRINT("not schedulable\n"); 655 return "not schedulable"; 656 } 657 p->edf->testtime += p->edf->T - p->edf->D; 658 p->edf->testtype = Rl; 659 testenq(p); 660 break; 661 case Rl: 662 DPRINT("\tStep %3d, Ticks %lud, pid %lud, release, G %lud, C%lud\n", 663 steps, ticks, p->pid, p->edf->C, G); 664 if(ticks && G <= ticks){ 665 DPRINT("schedulable\n"); 666 return nil; 667 } 668 G += p->edf->C; 669 p->edf->testtime += p->edf->D; 670 p->edf->testtype = Dl; 671 testenq(p); 672 break; 673 default: 674 assert(0); 675 } 676 } 677 DPRINT("probably not schedulable\n"); 678 return "probably not schedulable"; 679 } 680