Annotation of prex/sys/kern/sched.c, Revision 1.1.1.1
1.1 nbrk 1: /*-
2: * Copyright (c) 2005-2008, Kohsuke Ohtani
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. Neither the name of the author nor the names of any co-contributors
14: * may be used to endorse or promote products derived from this software
15: * without specific prior written permission.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
28: */
29:
30: /*
31: * sched.c - scheduler
32: */
33:
34: /**
35: * General design:
36: *
37: * The Prex scheduler is based on the algorithm known as priority
38: * based multi level queue. Each thread has its own priority
39: * assigned between 0 and 255. The lower number means higher
40: * priority like BSD unix. The scheduler maintains 256 level run
41: * queues mapped to each priority. The lowest priority (=255) is
42: * used only for an idle thread.
43: *
44: * All threads have two different types of priorities:
45: *
46: * Base priority:
47: * This is a static priority used for priority computation.
48: * A user mode program can change this value via system call.
49: *
50: * Current priority:
51: * An actual scheduling priority. A kernel may adjust this
52: * priority dynamically if it's needed.
53: *
54: * Each thread has one of the following state.
55: *
56: * - TH_RUN Running or ready to run
57: * - TH_SLEEP Sleep for some event
58: * - TH_SUSPEND Suspend count is not 0
59: * - TH_EXIT Terminated
60: *
61: * The thread is always preemptive even in the kernel mode.
62: * There are following 4 reasons to switch thread.
63: *
64: * (1) Block
65: * Thread is blocked for sleep or suspend.
66: * It is put on the tail of the run queue when it becomes
67: * runnable again.
68: *
69: * (2) Preemption
70: * If higher priority thread becomes runnable, the current
71: * thread is put on the _head_ of the run queue.
72: *
73: * (3) Quantum expiration
74: * If the thread consumes its time quantum, it is put on
75: * the tail of the run queue.
76: *
77: * (4) Yield
78: * If the thread releases CPU by itself, it is put on the
79: * tail of the run queue.
80: *
81: * There are following three types of scheduling policies.
82: *
83: * - SCHED_FIFO First in-first-out
84: * - SCHED_RR Round robin (SCHED_FIFO + timeslice)
85: * - SCHED_OTHER Another scheduling (not supported)
86: */
87:
88: #include <kernel.h>
89: #include <queue.h>
90: #include <event.h>
91: #include <irq.h>
92: #include <thread.h>
93: #include <timer.h>
94: #include <vm.h>
95: #include <task.h>
96: #include <system.h>
97: #include <sched.h>
98:
99: static struct queue runq[NPRIO]; /* run queues */
100: static struct queue wakeq; /* queue for waking threads */
101: static struct queue dpcq; /* DPC queue */
102: static int top_prio; /* highest priority in runq */
103: static struct event dpc_event; /* event for DPC */
104:
105: /*
106: * Search for highest-priority runnable thread.
107: */
108: static int
109: runq_top(void)
110: {
111: int prio;
112:
113: for (prio = 0; prio < MIN_PRIO; prio++)
114: if (!queue_empty(&runq[prio]))
115: break;
116: return prio;
117: }
118:
119: /*
120: * Put a thread on the tail of the run queue.
121: */
122: static void
123: runq_enqueue(thread_t th)
124: {
125:
126: enqueue(&runq[th->prio], &th->link);
127: if (th->prio < top_prio) {
128: top_prio = th->prio;
129: cur_thread->resched = 1;
130: }
131: }
132:
133: /*
134: * Insert a thread to the head of the run queue.
135: * We assume this routine is called while thread switching.
136: */
137: static void
138: runq_insert(thread_t th)
139: {
140:
141: queue_insert(&runq[th->prio], &th->link);
142: if (th->prio < top_prio)
143: top_prio = th->prio;
144: }
145:
146: /*
147: * Pick up and remove the highest-priority thread
148: * from the run queue.
149: */
150: static thread_t
151: runq_dequeue(void)
152: {
153: queue_t q;
154: thread_t th;
155:
156: q = dequeue(&runq[top_prio]);
157: th = queue_entry(q, struct thread, link);
158: if (queue_empty(&runq[top_prio]))
159: top_prio = runq_top();
160: return th;
161: }
162:
163: /*
164: * Remove the specified thread from the run queue.
165: */
166: static void
167: runq_remove(thread_t th)
168: {
169:
170: queue_remove(&th->link);
171: top_prio = runq_top();
172: }
173:
174: /*
175: * Process all pending woken threads.
176: */
177: static void
178: wakeq_flush(void)
179: {
180: queue_t q;
181: thread_t th;
182:
183: while (!queue_empty(&wakeq)) {
184: /*
185: * Set a thread runnable.
186: */
187: q = dequeue(&wakeq);
188: th = queue_entry(q, struct thread, link);
189: th->slpevt = NULL;
190: th->state &= ~TH_SLEEP;
191: if (th != cur_thread && th->state == TH_RUN)
192: runq_enqueue(th);
193: }
194: }
195:
196: /*
197: * sched_switch - this is the scheduler proper:
198: *
199: * If the scheduling reason is preemption, the current
200: * thread will remain at the head of the run queue. So,
201: * the thread still has right to run first again among
202: * the same priority threads. For other scheduling reason,
203: * the current thread is inserted into the tail of the run
204: * queue.
205: */
206: static void
207: sched_switch(void)
208: {
209: thread_t prev, next;
210:
211: /*
212: * Move a current thread to the run queue.
213: */
214: prev = cur_thread;
215: if (prev->state == TH_RUN) {
216: if (prev->prio > top_prio)
217: runq_insert(prev); /* preemption */
218: else
219: runq_enqueue(prev);
220: }
221: prev->resched = 0;
222:
223: /*
224: * Select the thread to run the CPU next.
225: */
226: next = runq_dequeue();
227: if (next == prev)
228: return;
229: cur_thread = next;
230:
231: /*
232: * Switch to the new thread.
233: * You are expected to understand this..
234: */
235: if (prev->task != next->task)
236: vm_switch(next->task->map);
237: context_switch(&prev->ctx, &next->ctx);
238: }
239:
240: /*
241: * sleep_expire - sleep timer is expired:
242: *
243: * Wake up the thread which is sleeping in sched_tsleep().
244: */
245: static void
246: sleep_expire(void *arg)
247: {
248:
249: sched_unsleep((thread_t)arg, SLP_TIMEOUT);
250: }
251:
252: /*
253: * sched_tsleep - sleep the current thread until a wakeup
254: * is performed on the specified event.
255: *
256: * This routine returns a sleep result. If the thread is
257: * woken by sched_wakeup() or sched_wakeone(), it returns 0.
258: * Otherwise, it will return the result value which is passed
259: * by sched_unsleep(). We allow calling sched_sleep() with
260: * interrupt disabled.
261: *
262: * sched_sleep() is also defined as a wrapper macro for
263: * sched_tsleep() without timeout. Note that all sleep
264: * requests are interruptible with this kernel.
265: */
266: int
267: sched_tsleep(struct event *evt, u_long msec)
268: {
269:
270: ASSERT(irq_level == 0);
271: ASSERT(evt);
272:
273: sched_lock();
274: irq_lock();
275:
276: cur_thread->slpevt = evt;
277: cur_thread->state |= TH_SLEEP;
278: enqueue(&evt->sleepq, &cur_thread->link);
279:
280: if (msec != 0) {
281: /*
282: * Program timer to wake us up at timeout.
283: */
284: timer_callout(&cur_thread->timeout, msec, &sleep_expire,
285: cur_thread);
286: }
287: wakeq_flush();
288: sched_switch(); /* Sleep here. Zzzz.. */
289:
290: irq_unlock();
291: sched_unlock();
292: return cur_thread->slpret;
293: }
294:
295: /*
296: * sched_wakeup - wake up all threads sleeping on event.
297: *
298: * A thread can have sleep and suspend state simultaneously.
299: * So, the thread does not always run even if it woke up.
300: *
301: * Since this routine can be called from ISR at interrupt
302: * level, there may be contention for access to some data.
303: * Thus, this routine will temporary move the waking thread
304: * into wakeq, and they will be moved to runq at more safer
305: * time in wakeq_flush().
306: *
307: * The woken thread will be put on the tail of runq
308: * regardless of its scheduling policy. If woken threads have
309: * same priority, next running thread is selected by FIFO order.
310: */
311: void
312: sched_wakeup(struct event *evt)
313: {
314: queue_t q;
315: thread_t th;
316:
317: ASSERT(evt);
318:
319: sched_lock();
320: irq_lock();
321: while (!queue_empty(&evt->sleepq)) {
322: /*
323: * Move a sleeping thread to the wake queue.
324: */
325: q = dequeue(&evt->sleepq);
326: th = queue_entry(q, struct thread, link);
327: th->slpret = 0;
328: enqueue(&wakeq, q);
329: timer_stop(&th->timeout);
330: }
331: irq_unlock();
332: sched_unlock();
333: }
334:
335: /*
336: * sched_wakeone - wake up one thread sleeping on event.
337: *
338: * The highest priority thread is woken among sleeping
339: * threads. This routine returns the thread ID of the
340: * woken thread, or NULL if no threads are sleeping.
341: */
342: thread_t
343: sched_wakeone(struct event *evt)
344: {
345: queue_t head, q;
346: thread_t top, th = NULL;
347:
348: sched_lock();
349: irq_lock();
350: head = &evt->sleepq;
351: if (!queue_empty(head)) {
352: /*
353: * Select the highet priority thread in
354: * the sleep queue, and wakeup it.
355: */
356: q = queue_first(head);
357: top = queue_entry(q, struct thread, link);
358: while (!queue_end(head, q)) {
359: th = queue_entry(q, struct thread, link);
360: if (th->prio < top->prio)
361: top = th;
362: q = queue_next(q);
363: }
364: queue_remove(&top->link);
365: top->slpret = 0;
366: enqueue(&wakeq, &top->link);
367: timer_stop(&top->timeout);
368: th = top;
369: }
370: irq_unlock();
371: sched_unlock();
372: return th;
373: }
374:
375: /*
376: * sched_unsleep - cancel sleep.
377: *
378: * sched_unsleep() removes the specified thread from its
379: * sleep queue. The specified sleep result will be passed
380: * to the sleeping thread as a return value of sched_tsleep().
381: */
382: void
383: sched_unsleep(thread_t th, int result)
384: {
385:
386: sched_lock();
387: if (th->state & TH_SLEEP) {
388: irq_lock();
389: queue_remove(&th->link);
390: th->slpret = result;
391: enqueue(&wakeq, &th->link);
392: timer_stop(&th->timeout);
393: irq_unlock();
394: }
395: sched_unlock();
396: }
397:
398: /*
399: * Yield the current processor to another thread.
400: *
401: * Note that the current thread may run immediately again,
402: * if no other thread exists in the same priority queue.
403: */
404: void
405: sched_yield(void)
406: {
407:
408: sched_lock();
409:
410: if (!queue_empty(&runq[cur_thread->prio]))
411: cur_thread->resched = 1;
412:
413: sched_unlock(); /* Switch current thread here */
414: }
415:
416: /*
417: * Suspend the specified thread.
418: * Called with scheduler locked.
419: */
420: void
421: sched_suspend(thread_t th)
422: {
423:
424: if (th->state == TH_RUN) {
425: if (th == cur_thread)
426: cur_thread->resched = 1;
427: else
428: runq_remove(th);
429: }
430: th->state |= TH_SUSPEND;
431: }
432:
433: /*
434: * Resume the specified thread.
435: * Called with scheduler locked.
436: */
437: void
438: sched_resume(thread_t th)
439: {
440:
441: if (th->state & TH_SUSPEND) {
442: th->state &= ~TH_SUSPEND;
443: if (th->state == TH_RUN)
444: runq_enqueue(th);
445: }
446: }
447:
448: /*
449: * sched_tick() is called from timer_tick() once every tick.
450: * Check quantum expiration, and mark a rescheduling flag.
451: * We don't need locking in here.
452: */
453: void
454: sched_tick(void)
455: {
456:
457: /* Profile running time. */
458: cur_thread->time++;
459:
460: if (cur_thread->policy == SCHED_RR) {
461: if (--cur_thread->timeleft <= 0) {
462: /*
463: * The quantum is up.
464: * Give the thread another.
465: */
466: cur_thread->timeleft += QUANTUM;
467: cur_thread->resched = 1;
468: }
469: }
470: }
471:
472: /*
473: * Set up stuff for thread scheduling.
474: */
475: void
476: sched_start(thread_t th)
477: {
478:
479: th->state = TH_RUN | TH_SUSPEND;
480: th->policy = SCHED_RR;
481: th->prio = PRIO_USER;
482: th->baseprio = PRIO_USER;
483: th->timeleft = QUANTUM;
484: }
485:
486: /*
487: * Stop thread scheduling.
488: */
489: void
490: sched_stop(thread_t th)
491: {
492:
493: if (th == cur_thread) {
494: /*
495: * If specified thread is current thread,
496: * the scheduling lock count is force set
497: * to 1 to ensure the thread switching in
498: * the next sched_unlock().
499: */
500: cur_thread->locks = 1;
501: cur_thread->resched = 1;
502: } else {
503: if (th->state == TH_RUN)
504: runq_remove(th);
505: else if (th->state & TH_SLEEP)
506: queue_remove(&th->link);
507: }
508: timer_stop(&th->timeout);
509: th->state = TH_EXIT;
510: }
511:
512: /*
513: * sched_lock - lock the scheduler.
514: *
515: * The thread switch is disabled during scheduler locked.
516: * This is mainly used to synchronize the thread execution
517: * to protect global resources. Even when scheduler is
518: * locked, an interrupt handler can run. So, we have to
519: * use irq_lock() to synchronize a global data with ISR.
520: *
521: * Since the scheduling lock can be nested any number of
522: * times, the caller has the responsible to unlock the same
523: * number of locks.
524: */
525: void
526: sched_lock(void)
527: {
528:
529: cur_thread->locks++;
530: }
531:
532: /*
533: * sched_unlock - unlock scheduler.
534: *
535: * If nobody locks the scheduler anymore, it checks the
536: * rescheduling flag and kick scheduler if it's marked.
537: *
538: * Note that this routine will be always called at the end
539: * of each interrupt handler.
540: */
541: void
542: sched_unlock(void)
543: {
544: int s;
545:
546: ASSERT(cur_thread->locks > 0);
547:
548: interrupt_save(&s);
549: interrupt_disable();
550:
551: if (cur_thread->locks == 1) {
552: wakeq_flush();
553: while (cur_thread->resched) {
554:
555: /* Kick scheduler */
556: sched_switch();
557:
558: /*
559: * Now we run pending interrupts which fired
560: * during the thread switch. So, we can catch
561: * the rescheduling request from such ISRs.
562: * Otherwise, the reschedule may be deferred
563: * until _next_ sched_unlock() call.
564: */
565: interrupt_restore(s);
566: interrupt_disable();
567: wakeq_flush();
568: }
569: }
570: cur_thread->locks--;
571:
572: interrupt_restore(s);
573: }
574:
575: int
576: sched_getprio(thread_t th)
577: {
578:
579: return th->prio;
580: }
581:
582: /*
583: * sched_setprio - set priority of thread.
584: *
585: * The rescheduling flag is set if the priority is
586: * better than the currently running thread.
587: * Called with scheduler locked.
588: */
589: void
590: sched_setprio(thread_t th, int baseprio, int prio)
591: {
592:
593: th->baseprio = baseprio;
594:
595: if (th == cur_thread) {
596: /*
597: * If we change the current thread's priority,
598: * rescheduling may be happened.
599: */
600: th->prio = prio;
601: top_prio = runq_top();
602: if (prio != top_prio)
603: cur_thread->resched = 1;
604: } else {
605: if (th->state == TH_RUN) {
606: /*
607: * Update the thread priority and adjust
608: * the run queue position for new priority.
609: */
610: runq_remove(th);
611: th->prio = prio;
612: runq_enqueue(th);
613: } else
614: th->prio = prio;
615: }
616: }
617:
618: int
619: sched_getpolicy(thread_t th)
620: {
621:
622: return th->policy;
623: }
624:
625: int
626: sched_setpolicy(thread_t th, int policy)
627: {
628: int err = 0;
629:
630: switch (policy) {
631: case SCHED_RR:
632: case SCHED_FIFO:
633: th->timeleft = QUANTUM;
634: th->policy = policy;
635: break;
636: default:
637: err = -1;
638: break;
639: }
640: return err;
641: }
642:
643: /*
644: * Schedule DPC callback.
645: *
646: * DPC (Deferred Procedure Call) is used to call the specific
647: * function at some later time with a DPC priority. It is also
648: * known as AST or SoftIRQ in other kernels. DPC is typically
649: * used by device drivers to do the low-priority jobs without
650: * degrading real-time performance.
651: * This routine can be called from ISR.
652: */
653: void
654: sched_dpc(struct dpc *dpc, void (*func)(void *), void *arg)
655: {
656: ASSERT(dpc);
657: ASSERT(func);
658:
659: irq_lock();
660: /*
661: * Insert request to DPC queue.
662: */
663: dpc->func = func;
664: dpc->arg = arg;
665: if (dpc->state != DPC_PENDING)
666: enqueue(&dpcq, &dpc->link);
667: dpc->state = DPC_PENDING;
668:
669: /* Wake DPC thread */
670: sched_wakeup(&dpc_event);
671:
672: irq_unlock();
673: }
674:
675: /*
676: * DPC thread.
677: *
678: * This is a kernel thread to process the pending call back
679: * request within DPC queue. Each DPC routine is called with
680: * the following conditions.
681: * - Interrupt is enabled.
682: * - Scheduler is unlocked.
683: */
684: static void
685: dpc_thread(void *arg)
686: {
687: queue_t q;
688: struct dpc *dpc;
689:
690: for (;;) {
691: /* Wait until next DPC request. */
692: sched_sleep(&dpc_event);
693:
694: while (!queue_empty(&dpcq)) {
695: q = dequeue(&dpcq);
696: dpc = queue_entry(q, struct dpc, link);
697: dpc->state = DPC_FREE;
698:
699: /*
700: * Call DPC routine.
701: */
702: interrupt_enable();
703: (*dpc->func)(dpc->arg);
704: interrupt_disable();
705: }
706: }
707: /* NOTREACHED */
708: }
709:
710: /*
711: * Initialize the global scheduler state.
712: */
713: void
714: sched_init(void)
715: {
716: thread_t th;
717: int i;
718:
719: for (i = 0; i < NPRIO; i++)
720: queue_init(&runq[i]);
721: queue_init(&wakeq);
722: queue_init(&dpcq);
723: event_init(&dpc_event, "dpc");
724: top_prio = PRIO_IDLE;
725: cur_thread->resched = 1;
726:
727: /* Create a DPC thread. */
728: th = kthread_create(dpc_thread, NULL, PRIO_DPC);
729: if (th == NULL)
730: panic("sched_init");
731:
732: DPRINTF(("Time slice is %d msec\n", CONFIG_TIME_SLICE));
733: }
CVSweb