1/**
2 * \file
3 * \brief Kernel round-robin scheduling policy
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2009, 2010, 2013, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <kernel.h>
16#include <dispatch.h>
17#include <kcb.h>
18
19#include <timer.h> // update_sched_timer
20
21/**
22 * \brief Scheduler policy.
23 *
24 * \return Next DCB to schedule or NULL if wait for interrupts.
25 */
26struct dcb *schedule(void)
27{
28    // empty ring
29    if(kcb_current->ring_current == NULL) {
30        return NULL;
31    }
32
33    assert(kcb_current->ring_current->next != NULL);
34    assert(kcb_current->ring_current->prev != NULL);
35
36    kcb_current->ring_current = kcb_current->ring_current->next;
37    #ifdef CONFIG_ONESHOT_TIMER
38    update_sched_timer(kernel_now + kernel_timeslice);
39    #endif
40    return ring_current;
41}
42
43void make_runnable(struct dcb *dcb)
44{
45    // Insert into schedule ring if not in there already
46    if(dcb->prev == NULL || dcb->next == NULL) {
47        assert(dcb->prev == NULL && dcb->next == NULL);
48
49        // Ring empty
50        if(kcb_current->ring_current == NULL) {
51            kcb_current->ring_current = dcb;
52            dcb->next = dcb;
53        }
54
55        // Insert after current ring position
56        dcb->prev = kcb_current->ring_current;
57        dcb->next = kcb_current->ring_current->next;
58        kcb_current->ring_current->next->prev = dcb;
59        kcb_current->ring_current->next = dcb;
60    }
61}
62
63/**
64 * \brief Remove 'dcb' from scheduler ring.
65 *
66 * Removes dispatcher 'dcb' from the scheduler ring. If it was not in
67 * the ring, this function is a no-op. The postcondition for this
68 * function is that dcb is not in the ring.
69 *
70 * \param dcb   Pointer to DCB to remove.
71 */
72void scheduler_remove(struct dcb *dcb)
73{
74    // No-op if not in scheduler ring
75    if(dcb->prev == NULL || dcb->next == NULL) {
76        assert(dcb->prev == NULL && dcb->next == NULL);
77        return;
78    }
79
80    struct dcb *next = kcb_current->ring_current->next;
81
82    // Remove dcb from scheduler ring
83    dcb->prev->next = dcb->next;
84    dcb->next->prev = dcb->prev;
85    dcb->prev = dcb->next = NULL;
86
87    // Removing ring_current
88    if(dcb == kcb_current->ring_current) {
89        if(dcb == next) {
90            // Only guy in the ring
91            kcb_current->ring_current = NULL;
92        } else {
93            // Advance ring_current
94            kcb_current->ring_current = next;
95        }
96    }
97}
98
99/**
100 * \brief Yield 'dcb' for the rest of the current timeslice.
101 *
102 * Re-sorts 'dcb' into the scheduler queue with its release time increased by
103 * the timeslice period. It is an error to yield a dispatcher not in the
104 * scheduler queue.
105 *
106 * \param dcb   Pointer to DCB to remove.
107 */
108void scheduler_yield(struct dcb *dcb)
109{
110    if(dcb->prev == NULL || dcb->next == NULL) {
111        struct dispatcher_shared_generic *dsg =
112            get_dispatcher_shared_generic(dcb->disp);
113        panic("Yield of %.*s not in scheduler queue", DISP_NAME_LEN,
114              dsg->name);
115    }
116
117    // No-op for the round-robin scheduler
118}
119
120void scheduler_reset_time(void)
121{
122    // No-Op in RR scheduler
123}
124
125void scheduler_convert(void)
126{
127    enum sched_state from = kcb_current->sched;
128    switch (from) {
129        case SCHED_RBED:
130        {
131            // initialize RR ring
132            struct dcb *last = NULL;
133            for (struct dcb *i = kcb_current->queue_head; i; i = i->next)
134            {
135                i->prev = last;
136                last = i;
137            }
138            // at this point: we have a dll, but need to close the ring
139            kcb_current->queue_head->prev = kcb_current->queue_tail;
140            kcb_current->queue_tail->next = kcb_current->queue_head;
141            break;
142        }
143        case SCHED_RR:
144            // do nothing
145            break;
146        default:
147            printf("don't know how to convert %d to RBED state\n", from);
148            break;
149    }
150    kcb_current->ring_current = kcb_current->queue_head;
151    for (struct dcb *i = kcb_current->ring_current; i != kcb_current->ring_current; i=i->next) {
152        printf("dcb %p\n  prev=%p\n  next=%p\n", i, i->prev, i->next);
153    }
154}
155
156void scheduler_restore_state(void)
157{
158    // No-Op in RR scheduler
159}
160