void k_printf(char* fmt, ...){ va_list ap; va_start(ap, fmt); char *p, *sval; int ival; static char s[80]; for(p = fmt; *p; p++){ if(*p != '%'){ k_putchar(*p); continue; } switch(*++p){ case 'd': ival = va_arg(ap, int); itoa(s, ival, 10); k_puts(s); break; case 'x': ival = va_arg(ap, int); itoa(s, ival, 16); k_puts(s); break; case 'c': ival = va_arg(ap, int); k_putchar(ival); break; case 's': for(sval = va_arg(ap, char*); *sval; sval++){ k_putchar(*sval); } break; default: k_putchar(*p); break; } } va_end(ap); return; }
void ksched_scheduler(addr_t stp, dcb_ro_t *cur_dcb, context_t *cur_ctxt ) { kernel_st *st = (kernel_st *)stp; SDom_t *cur_sdom; /* Current sdom */ Time_ns time; /* Current time? */ Time_ns itime; /* Time on interval timer */ Time_ns newtime; /* What to set the itimer to */ Time_ns ranfor; /* How long the domain ran */ dcb_ro_t *dcb; /* tmp. domain control block */ dcb_rw_t *dcbrw; SDom_t *sdom; /* tmp. scheduling domain */ bool_t blocking; /* does the domain wish to block? */ Activation_Reason reason; /* Increment the heartbeat counter in the PIP. This allows benchmark code, etc. to see how many passes through the scheduler happened as a result of certain tests - useful for spotting bugs! */ INFO_PAGE.sheartbeat++; /* Squash timer and read the time */ time = Timer$Clear(st->t, &itime); /* Give the Measure boys a change to do their stuff */ MEASURE(ac_measure(st, time)); /* If we were spinning in the idle loop, there is no current * domain to deschedule. */ if (cur_dcb == NULL) { goto deschedule_done; } /***************************** * * Deschedule the current scheduling domain * ****************************/ dcbrw = DCB_RO2RW(cur_dcb); #ifndef __IX86__ /* We enable activations here for an RFABlock to avoid trashing the actctx on the way into the scheduler. Turn on activations and then treat just like a block */ if (cur_dcb->schst == DCBST_K_RFABLOCK) { dcbrw->mode = 0; cur_dcb->schst = DCBST_K_BLOCK; } #endif #if defined(INTEL) || defined(__ARM__) /* Send any delayed events */ while(cur_dcb->outevs_processed < dcbrw->outevs_pushed) { word_t next_event = cur_dcb->outevs_processed + 1; dcb_event *ev = &dcbrw->outevs[next_event % NUM_DCB_EVENTS]; #ifdef INTEL k_dsc_send(ev->ep, ev->val); #else k_sc_send(ev->ep, ev->val); #endif cur_dcb->outevs_processed++; } #endif /* Record the time the domain was preempted and for how long it ran. Work out if the domain is going to be blocked to save some pointless queue shuffling */ cur_sdom = st->cur_sdom; cur_sdom->lastdsch = time; ranfor = (time - cur_sdom->lastschd); blocking = (cur_dcb->schst == DCBST_K_BLOCK) && EP_FIFO_EMPTY (dcbrw); if ((cur_sdom->state == sdom_run) || (cur_sdom->state == sdom_unblocked)) { /* In this block, we are doing accounting for an sdom which has been running in contracted time. Note that this could now happen even if the domain is on the wait queue (i.e. if it blocked) */ /* Deduct guaranteed time from the domain */ cur_sdom->remain -= ranfor; cur_sdom->ac_time += ranfor; MEASURE(cur_sdom->ac_m_tm += ranfor); /* If guaranteed time has run out... */ if (!blocking && (cur_sdom->remain <= 0)) { /* Move domain to correct position in WAIT queue */ /* XXX sdom_unblocked doesn't need this since it is already in the correct place. */ dequeue(st, cur_sdom); cur_sdom->state = sdom_wait; requeue(st, cur_sdom); } } else { /* * In this block, the sdom was running optimistically - so no * accounting is necessary. The sdom is not the head of the * run queue, but st->next_optm gives its position * in the wait queue */ cur_sdom->ac_x += ranfor; MEASURE(cur_sdom->ac_m_tm += ranfor); } /****************** * * If the domain wishes to block, then block it. * * We only block a domain if it has no events in the fifo - this * is to avoid the "wake-up-waiting" race * *******************/ if (blocking) { /* Remove the domain from whichever queue and block it */ dequeue(st, cur_sdom); block(st, cur_sdom); } deschedule_done: /***************************** * * We have now successfully descheduled the current sdom. * The next task is the allocate CPU time to any sdom it is due to. * ****************************/ cur_dcb = (dcb_ro_t *)0; cur_sdom = (SDom_t *)0; /***************************** * * Allocate CPU to any waiting domains who have passed their * period deadline. If necessary, move them to run queue. * ****************************/ while(PQ_SIZE(st->wait) && (sdom = PQ_HEAD(st->wait))->deadline <= time ) { /* Remove from HEAD of wait queue */ PQ_REMOVE(st->wait); /* Domain begins a new period and receives a slice of CPU * If this domain has been blocking then throw away the * rest of it's remain - it can't be trusted */ if (sdom->remain > 0) sdom->remain = sdom->slice; else sdom->remain += sdom->slice; sdom->prevddln = sdom->deadline; sdom->deadline += sdom->period; sdom->state = (sdom->remain > 0) ? sdom_run : sdom_wait; /* Place on the appropriate queue */ requeue(st, sdom); /* Update the accounts */ endofperiod(sdom, time); } /***************************** * * Next we must handle all domains to which new events have been * delivered. These domains will have been placed in a fifo by * the event send code. * ****************************/ while (st->domq.tail != st->domq.head) { /* Let the event delivery mechanism know we've seen this one. */ st->domq.tail = (st->domq.tail + 1) % CFG_UNBLOCK_QUEUE_SIZE; dcb = st->domq.q[st->domq.tail]; sdom = dcb->sdomptr; /* * Do not reschedule stopped or dying domains. * They are not on ANY queue, but other domains may still * send them events. */ if (dcb->schst == DCBST_K_STOPPED || dcb->schst == DCBST_K_DYING) continue; /* Unblock the domain if necessary */ if(sdom->state == sdom_block) { /* Remove from the MIDDLE of the blocked queue */ dequeue(st, sdom); /* Unblock and move to the appropriate queue */ unblock(st, sdom, time); requeue(st, sdom); } } /***************************** * * Next we must handle all domains whose block timeouts have expired. * Currently we treat a timeout as though an event had arrived. * ****************************/ /* Wake up any blocked domains whose timeout has expired */ while(PQ_SIZE(st->blocked) && (sdom = PQ_HEAD(st->blocked))->deadline <= time ) { /* Remove from HEAD of blocked queue */ PQ_REMOVE(st->blocked); /* Unblock and move to the appropriate queue */ unblock(st, sdom, time); requeue(st, sdom); } /***************************** * * Next we need to pick an sdom to run. * If anything is actually 'runnable', we run that. * If nothing is, we pick a waiting sdom to run optimistically. * If there aren't even any of those, we have to spin waiting for an * event or a suitable time condition to happen. * ****************************/ newtime = FOREVER; reason = 0; /* If we have an sdom on the RUN queue, then run it. */ if (PQ_SIZE(st->run)) { cur_sdom = PQ_HEAD(st->run); cur_dcb = cur_sdom->dcb; newtime = time + cur_sdom->remain; reason = (cur_sdom->prevddln > cur_sdom->lastschd) ? Activation_Reason_Allocated : Activation_Reason_Preempted; } else if (PQ_SIZE(st->wait)) { int i; /* Try running a domain on the WAIT queue - this part of the scheduler isn't particularly efficient but then again, we don't have any guaranteed domains to worry about. */ /* See if there are any unblocked domains on the WAIT queue who we can give preferential treatment to. */ for (i = 1; i <= PQ_SIZE(st->wait); i++) { sdom = PQ_NTH(st->wait, i); if (sdom->state == sdom_unblocked) { cur_sdom = sdom; cur_dcb = sdom->dcb; newtime = time + sdom->remain; reason = Activation_Reason_Preempted; goto found; } } /* Last chance: pick a domain on the wait queue with the XTRA flag set. The NEXT_OPTM field is used to cheaply achieve an approximation of round-robin order */ for (i = 0; i < PQ_SIZE(st->wait); i++) { /* Pick (roughly) the next domain on the wait q. */ if (++(st->next_optm) > PQ_SIZE(st->wait)) st->next_optm = 1; sdom = PQ_NTH(st->wait, st->next_optm); if (sdom->xtratime) { cur_sdom = sdom; cur_dcb = sdom->dcb; newtime = time + BESTEFFORT_QUANTUM; reason = Activation_Reason_Extra; goto found; } } } found: /********************** * * We now have to work out the time when we next need to * make a scheduling decision. We set the alarm timer * to cause an interrupt at that time. * **********************/ /* If we might be able to run a waiting domain before this one has */ /* exhausted its time, cut short the time allocation */ if (PQ_SIZE(st->wait)) newtime = MIN(newtime, PQ_HEAD(st->wait)->deadline); /* If necessary reenter the scheduler to wake up blocked domains */ if (PQ_SIZE(st->blocked)) newtime = MIN(newtime, PQ_HEAD(st->blocked)->deadline); MEASURE(newtime = MIN(newtime, ac_next)); /* Set timer for newtime */ Timer$Set(st->t, newtime); MEASURE_TRACE(Trace$Add(st->trace, cur_dcb ? cur_dcb->id : 0, time)); if (cur_dcb) { MTRC(k_putchar('0' + (cur_dcb->id & 7))); /* New state */ st->cur_dcb = cur_dcb; st->cur_sdom = cur_sdom; st->cur_sdom->lastschd = time; cur_dcb->schst = DCBST_K_RUN; /* Switch to the new protection domain */ k_swppdom (cur_dcb); /* Activate the chosen domain */ k_activate (cur_dcb, reason); } else { MTRC(k_putchar('.')); MTRC(k_putchar('0' + PQ_SIZE(st->run))); MTRC(k_putchar('0' + PQ_SIZE(st->wait))); MTRC(k_putchar('0' + PQ_SIZE(st->blocked))); st->cur_dcb = NULL; /* tested by ksched.S */ /* Enable interrupts and spin */ k_idle(); } }