cp: implement mutex dependency tracking
[hvf.git] / cp / nucleus / sched.c
blob33589c121b73b9f99d6c32a23049c7b275c88715
1 /*
2 * (C) Copyright 2007-2011 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
4 * This file is released under the GPLv2. See the COPYING file for more
5 * details.
6 */
8 #include <sched.h>
9 #include <page.h>
10 #include <buddy.h>
11 #include <slab.h>
12 #include <magic.h>
14 /* list of all runnable processes */
15 static struct list_head runnable;
17 /* list of all processes in the system */
18 static struct list_head processes;
20 /* idle task */
21 static struct task idle_task;
23 /**
24 * start_task - helper used to start the task's code
25 * @f: function to execute
27 static void start_task(int (*f)(void*), void *data)
30 * Start executing the code
32 if (f)
33 (*f)(data);
36 * Done, now, it's time to cleanup
38 * FIXME:
39 * - delete from processes list
40 * - free stack page
41 * - free struct
42 * - schedule
44 BUG();
47 /**
48 * __init_task - helper to initialize the task structure
49 * @task: task struct to initialize
50 * @f: pointer to function to execute
51 * @stack: pointer to the page for stack
53 static void __init_task(struct task *task, void *f, void *data, void *stack)
55 memset(task, 0, sizeof(struct task));
57 task->regs.psw.io = 1;
58 task->regs.psw.ex = 1;
59 task->regs.psw.ea = 1;
60 task->regs.psw.ba = 1;
62 /* code to execute */
63 task->regs.psw.ptr = (u64) start_task;
65 task->regs.gpr[2] = (u64) f;
66 task->regs.gpr[3] = (u64) data;
67 task->regs.gpr[15] = ((u64) stack) + PAGE_SIZE - STACK_FRAME_SIZE;
69 task->state = TASK_SLEEPING;
71 task->nr_locks = 0;
74 /**
75 * init_idle_task - initialize the idle task
77 * Since the initial thread of execution already has a stack, and the task
78 * struct is a global variable, all that is left for us to do is to
79 * associate the current stack with idle_task.
81 static void init_idle_task(void)
83 u64 stack;
85 stack = ((u64) &stack) & ~(PAGE_SIZE-1);
87 __init_task(&idle_task, NULL, NULL, (void*) stack);
88 set_task_ptr(&idle_task);
91 * NOTE: The idle task is _not_ supposed to be on either of the
92 * two process lists.
96 /**
97 * create_task - create a new task
98 * @name: name for the task
99 * @f: function pointer to where thread of execution should begin
100 * @data: arbitrary data to pass to the task
102 struct task* create_task(char *name, int (*f)(void *), void *data)
104 struct page *page;
105 struct task *task;
108 * Allocate a page for stack, and struct task itself
110 page = alloc_pages(0, ZONE_NORMAL);
111 task = malloc(sizeof(struct task), ZONE_NORMAL);
112 if (!page || !task)
113 goto out;
116 * Set up task's state
118 __init_task(task, f, data, page_to_addr(page));
120 strncpy(task->name, name, TASK_NAME_LEN);
123 * Add the task to the scheduler lists
125 list_add_tail(&task->proc_list, &processes);
126 list_add_tail(&task->run_queue, &runnable);
128 return task;
130 out:
131 free(task);
132 free_pages(page_to_addr(page), 0);
134 return ERR_PTR(-ENOMEM);
138 * __sched - core of the scheduler code. This decides which task to run
139 * next, and switches over to it
141 static void __sched(int force_idle)
143 struct task *next;
146 * If there are no runnable tasks, let's use the idle thread
148 if (force_idle || list_empty(&runnable)) {
149 next = &idle_task;
150 goto run;
153 next = list_first_entry(&runnable, struct task, run_queue);
154 BUG_ON(!next);
157 * Remove the new task from the run queue & mark it as running
159 list_del(&next->run_queue);
160 next->state = TASK_RUNNING;
162 run:
163 next->slice_end_time = (force_idle) ? force_idle : ticks + SCHED_TICKS_PER_SLICE;
166 * NOTE: Because we need to load the registers _BEFORE_ we issue
167 * lpswe, we have to place the new psw into PSA and use register 0
169 memcpy(PSA_TMP_PSW, &next->regs.psw, sizeof(struct psw));
170 set_task_ptr(next);
172 load_pasce(next->regs.cr1);
175 * Load the next task
177 * FIXME: fp? ar? cr?
179 asm volatile(
180 "lmg %%r0,%%r15,%0\n" /* load gpr */
181 "lpswe %1\n" /* load new psw */
182 : /* output */
183 : /* input */
184 "m" (next->regs.gpr[0]),
185 "m" (*PSA_TMP_PSW)
188 /* unreachable */
189 BUG();
193 * schedule_preempted - called to switch tasks
195 void __schedule(struct psw *old_psw, int newstate)
197 struct task *prev;
198 int force_idle = 0;
201 * Save last process's state
204 prev = current;
205 BUG_ON(!prev);
208 * Save the registers (gpr, psw, ...)
210 * FIXME: fp? ar? cr?
212 memcpy(prev->regs.gpr, PSA_INT_GPR, sizeof(u64)*16);
213 memcpy(&prev->regs.psw, old_psw, sizeof(struct psw));
216 * Idle task doesn't get added back to the queue
218 if (prev == &idle_task)
219 goto go;
221 store_pasce(&prev->regs.cr1);
224 * Add back on the queue
226 prev->state = newstate;
227 if (newstate != TASK_LOCKED)
228 list_add_tail(&prev->run_queue, &runnable);
231 * If the previous task didn't use it's full slice, force idle_task
232 * to take over.
234 if (prev->slice_end_time >= (ticks + SCHED_TICKS_PER_SLICE))
235 force_idle = prev->slice_end_time;
239 * Run the rest of the scheduler that selects the next task and
240 * context switches
242 __sched(force_idle);
246 * __schedule_svc - wrapper for the supervisor-service call handler
248 void __schedule_svc(void)
250 __schedule(SVC_INT_OLD_PSW, TASK_SLEEPING);
254 * __schedule_blocked__svc - wrapper for the supervisor-service call handler
256 void __schedule_blocked_svc(void)
258 __schedule(SVC_INT_OLD_PSW, TASK_LOCKED);
262 * Initialize the schduler, and all associated structures
264 void init_sched(void)
266 u64 cr0;
268 INIT_LIST_HEAD(&runnable);
269 INIT_LIST_HEAD(&processes);
271 init_idle_task();
273 /* enable cpu timer interrupt subclass */
274 asm volatile(
275 "stctg 0,0,%0\n" /* get cr0 */
276 "oi %1,0x04\n" /* enable cpu timer subclass */
277 "ni %1,0x7f\n" /* disable clock comp */
278 "lctlg 0,0,%0\n" /* reload cr0 */
279 : /* output */
280 : /* input */
281 "m" (cr0),
282 "m" (*(((u8*)&cr0) + 6))
285 set_timer();
288 void list_tasks(struct console *con,
289 void (*f)(struct console *, struct task*))
291 struct task *t;
293 list_for_each_entry(t, &processes, proc_list)
294 f(con, t);
297 void make_runnable(struct task *task)
299 task->state = TASK_SLEEPING;
300 list_add_tail(&task->run_queue, &runnable);