1 | /* |
---|
2 | **************************************************************************** |
---|
3 | * (C) 2005 - Grzegorz Milos - Intel Research Cambridge |
---|
4 | **************************************************************************** |
---|
5 | * |
---|
6 | * File: sched.c |
---|
7 | * Author: Grzegorz Milos |
---|
8 | * Changes: Robert Kaiser |
---|
9 | * |
---|
10 | * Date: Aug 2005 |
---|
11 | * |
---|
12 | * Environment: Xen Minimal OS |
---|
13 | * Description: simple scheduler for Mini-Os |
---|
14 | * |
---|
15 | * The scheduler is non-preemptive (cooperative), and schedules according |
---|
16 | * to Round Robin algorithm. |
---|
17 | * |
---|
18 | **************************************************************************** |
---|
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
---|
20 | * of this software and associated documentation files (the "Software"), to |
---|
21 | * deal in the Software without restriction, including without limitation the |
---|
22 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
---|
23 | * sell copies of the Software, and to permit persons to whom the Software is |
---|
24 | * furnished to do so, subject to the following conditions: |
---|
25 | * |
---|
26 | * The above copyright notice and this permission notice shall be included in |
---|
27 | * all copies or substantial portions of the Software. |
---|
28 | * |
---|
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
---|
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
---|
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
---|
34 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
35 | * DEALINGS IN THE SOFTWARE. |
---|
36 | */ |
---|
37 | |
---|
38 | #include <os.h> |
---|
39 | #include <hypervisor.h> |
---|
40 | #include <time.h> |
---|
41 | #include <mm.h> |
---|
42 | #include <types.h> |
---|
43 | #include <lib.h> |
---|
44 | #include <xmalloc.h> |
---|
45 | #include <list.h> |
---|
46 | #include <sched.h> |
---|
47 | #include <semaphore.h> |
---|
48 | |
---|
49 | |
---|
50 | #ifdef SCHED_DEBUG |
---|
51 | #define DEBUG(_f, _a...) \ |
---|
52 | printk("MINI_OS(file=sched.c, line=%d) " _f "\n", __LINE__, ## _a) |
---|
53 | #else |
---|
54 | #define DEBUG(_f, _a...) ((void)0) |
---|
55 | #endif |
---|
56 | |
---|
57 | struct thread *idle_thread = NULL; |
---|
58 | LIST_HEAD(exited_threads); |
---|
59 | |
---|
60 | void inline print_runqueue(void) |
---|
61 | { |
---|
62 | struct list_head *it; |
---|
63 | struct thread *th; |
---|
64 | list_for_each(it, &idle_thread->thread_list) |
---|
65 | { |
---|
66 | th = list_entry(it, struct thread, thread_list); |
---|
67 | printk(" Thread \"%s\", runnable=%d\n", th->name, is_runnable(th)); |
---|
68 | } |
---|
69 | printk("\n"); |
---|
70 | } |
---|
71 | |
---|
72 | /* Find the time when the next timeout expires. If this is more than |
---|
73 | 10 seconds from now, return 10 seconds from now. */ |
---|
74 | static s_time_t blocking_time(void) |
---|
75 | { |
---|
76 | struct thread *thread; |
---|
77 | struct list_head *iterator; |
---|
78 | s_time_t min_wakeup_time; |
---|
79 | unsigned long flags; |
---|
80 | local_irq_save(flags); |
---|
81 | /* default-block the domain for 10 seconds: */ |
---|
82 | min_wakeup_time = NOW() + SECONDS(10); |
---|
83 | |
---|
84 | /* Thread list needs to be protected */ |
---|
85 | list_for_each(iterator, &idle_thread->thread_list) |
---|
86 | { |
---|
87 | thread = list_entry(iterator, struct thread, thread_list); |
---|
88 | if(!is_runnable(thread) && thread->wakeup_time != 0LL) |
---|
89 | { |
---|
90 | if(thread->wakeup_time < min_wakeup_time) |
---|
91 | { |
---|
92 | min_wakeup_time = thread->wakeup_time; |
---|
93 | } |
---|
94 | } |
---|
95 | } |
---|
96 | local_irq_restore(flags); |
---|
97 | return(min_wakeup_time); |
---|
98 | } |
---|
99 | |
---|
100 | /* Wake up all threads with expired timeouts. */ |
---|
101 | static void wake_expired(void) |
---|
102 | { |
---|
103 | struct thread *thread; |
---|
104 | struct list_head *iterator; |
---|
105 | s_time_t now = NOW(); |
---|
106 | unsigned long flags; |
---|
107 | local_irq_save(flags); |
---|
108 | /* Thread list needs to be protected */ |
---|
109 | list_for_each(iterator, &idle_thread->thread_list) |
---|
110 | { |
---|
111 | thread = list_entry(iterator, struct thread, thread_list); |
---|
112 | if(!is_runnable(thread) && thread->wakeup_time != 0LL) |
---|
113 | { |
---|
114 | if(thread->wakeup_time <= now) |
---|
115 | wake(thread); |
---|
116 | } |
---|
117 | } |
---|
118 | local_irq_restore(flags); |
---|
119 | } |
---|
120 | |
---|
121 | void schedule(void) |
---|
122 | { |
---|
123 | struct thread *prev, *next, *thread; |
---|
124 | struct list_head *iterator; |
---|
125 | unsigned long flags; |
---|
126 | prev = current; |
---|
127 | local_irq_save(flags); |
---|
128 | list_for_each(iterator, &exited_threads) |
---|
129 | { |
---|
130 | thread = list_entry(iterator, struct thread, thread_list); |
---|
131 | if(thread != prev) |
---|
132 | { |
---|
133 | list_del(&thread->thread_list); |
---|
134 | free_pages(thread->stack, 1); |
---|
135 | xfree(thread); |
---|
136 | } |
---|
137 | } |
---|
138 | next = idle_thread; |
---|
139 | /* Thread list needs to be protected */ |
---|
140 | list_for_each(iterator, &idle_thread->thread_list) |
---|
141 | { |
---|
142 | thread = list_entry(iterator, struct thread, thread_list); |
---|
143 | if(is_runnable(thread)) |
---|
144 | { |
---|
145 | next = thread; |
---|
146 | /* Put this thread on the end of the list */ |
---|
147 | list_del(&thread->thread_list); |
---|
148 | list_add_tail(&thread->thread_list, &idle_thread->thread_list); |
---|
149 | break; |
---|
150 | } |
---|
151 | } |
---|
152 | local_irq_restore(flags); |
---|
153 | /* Interrupting the switch is equivalent to having the next thread |
---|
154 | inturrupted at the return instruction. And therefore at safe point. */ |
---|
155 | if(prev != next) switch_threads(prev, next); |
---|
156 | } |
---|
157 | |
---|
158 | struct thread* create_thread(char *name, void (*function)(void *), void *data) |
---|
159 | { |
---|
160 | struct thread *thread; |
---|
161 | unsigned long flags; |
---|
162 | /* Call architecture specific setup. */ |
---|
163 | thread = arch_create_thread(name, function, data); |
---|
164 | /* Not runable, not exited, not sleeping */ |
---|
165 | thread->flags = 0; |
---|
166 | thread->wakeup_time = 0LL; |
---|
167 | set_runnable(thread); |
---|
168 | local_irq_save(flags); |
---|
169 | if(idle_thread != NULL) { |
---|
170 | list_add_tail(&thread->thread_list, &idle_thread->thread_list); |
---|
171 | } else if(function != idle_thread_fn) |
---|
172 | { |
---|
173 | printk("BUG: Not allowed to create thread before initialising scheduler.\n"); |
---|
174 | BUG(); |
---|
175 | } |
---|
176 | local_irq_restore(flags); |
---|
177 | return thread; |
---|
178 | } |
---|
179 | |
---|
180 | void exit_thread(void) |
---|
181 | { |
---|
182 | unsigned long flags; |
---|
183 | struct thread *thread = current; |
---|
184 | printk("Thread \"%s\" exited.\n", thread->name); |
---|
185 | local_irq_save(flags); |
---|
186 | /* Remove from the thread list */ |
---|
187 | list_del(&thread->thread_list); |
---|
188 | clear_runnable(thread); |
---|
189 | /* Put onto exited list */ |
---|
190 | list_add(&thread->thread_list, &exited_threads); |
---|
191 | local_irq_restore(flags); |
---|
192 | /* Schedule will free the resources */ |
---|
193 | schedule(); |
---|
194 | } |
---|
195 | |
---|
196 | void block(struct thread *thread) |
---|
197 | { |
---|
198 | thread->wakeup_time = 0LL; |
---|
199 | clear_runnable(thread); |
---|
200 | } |
---|
201 | |
---|
202 | void sleep(u32 millisecs) |
---|
203 | { |
---|
204 | struct thread *thread = get_current(); |
---|
205 | thread->wakeup_time = NOW() + MILLISECS(millisecs); |
---|
206 | clear_runnable(thread); |
---|
207 | schedule(); |
---|
208 | } |
---|
209 | |
---|
210 | void wake(struct thread *thread) |
---|
211 | { |
---|
212 | thread->wakeup_time = 0LL; |
---|
213 | set_runnable(thread); |
---|
214 | } |
---|
215 | |
---|
216 | void idle_thread_fn(void *unused) |
---|
217 | { |
---|
218 | s_time_t until; |
---|
219 | for(;;) |
---|
220 | { |
---|
221 | schedule(); |
---|
222 | /* block until the next timeout expires, or for 10 secs, whichever comes first */ |
---|
223 | until = blocking_time(); |
---|
224 | block_domain(until); |
---|
225 | wake_expired(); |
---|
226 | } |
---|
227 | } |
---|
228 | |
---|
229 | DECLARE_MUTEX(mutex); |
---|
230 | |
---|
231 | void th_f1(void *data) |
---|
232 | { |
---|
233 | struct timeval tv1, tv2; |
---|
234 | |
---|
235 | for(;;) |
---|
236 | { |
---|
237 | down(&mutex); |
---|
238 | printk("Thread \"%s\" got semaphore, runnable %d\n", current->name, is_runnable(current)); |
---|
239 | schedule(); |
---|
240 | printk("Thread \"%s\" releases the semaphore\n", current->name); |
---|
241 | up(&mutex); |
---|
242 | |
---|
243 | |
---|
244 | gettimeofday(&tv1); |
---|
245 | for(;;) |
---|
246 | { |
---|
247 | gettimeofday(&tv2); |
---|
248 | if(tv2.tv_sec - tv1.tv_sec > 2) break; |
---|
249 | } |
---|
250 | |
---|
251 | |
---|
252 | schedule(); |
---|
253 | } |
---|
254 | } |
---|
255 | |
---|
256 | void th_f2(void *data) |
---|
257 | { |
---|
258 | for(;;) |
---|
259 | { |
---|
260 | printk("Thread OTHER executing, data 0x%lx\n", data); |
---|
261 | schedule(); |
---|
262 | } |
---|
263 | } |
---|
264 | |
---|
265 | |
---|
266 | |
---|
267 | void init_sched(void) |
---|
268 | { |
---|
269 | printk("Initialising scheduler\n"); |
---|
270 | |
---|
271 | idle_thread = create_thread("Idle", idle_thread_fn, NULL); |
---|
272 | INIT_LIST_HEAD(&idle_thread->thread_list); |
---|
273 | } |
---|
274 | |
---|