Moves the idlecoremap to the ksched
[akaros.git] / kern / src / schedule.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  * Scheduling and dispatching.
7  */
8
9 #ifdef __SHARC__
10 #pragma nosharc
11 #endif
12
13 #include <schedule.h>
14 #include <process.h>
15 #include <monitor.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <atomic.h>
19 #include <resource.h>
20 #include <smp.h>
21 #include <sys/queue.h>
22
23 // This could be useful for making scheduling decisions.  
24 /* Physical coremap: each index is a physical core id, with a proc ptr for
25  * whoever *should be or is* running.  Very similar to current, which is what
26  * process is *really* running there. */
27 struct proc *pcoremap[MAX_NUM_CPUS];
28
29 /* Tracks which cores are idle, similar to the vcoremap.  Each value is the
30  * physical coreid of an unallocated core. */
31 spinlock_t idle_lock = SPINLOCK_INITIALIZER;
32 uint32_t idlecoremap[MAX_NUM_CPUS];
33 uint32_t num_idlecores = 0;
34 uint32_t num_mgmtcores = 1;
35
36 void schedule_init(void)
37 {
38         TAILQ_INIT(&proc_runnablelist);
39
40         /* Ghetto old idle core init */
41         /* Init idle cores. Core 0 is the management core. */
42         spin_lock(&idle_lock);
43 #ifdef __CONFIG_DISABLE_SMT__
44         /* assumes core0 is the only management core (NIC and monitor functionality
45          * are run there too.  it just adds the odd cores to the idlecoremap */
46         assert(!(num_cpus % 2));
47         // TODO: consider checking x86 for machines that actually hyperthread
48         num_idlecores = num_cpus >> 1;
49  #ifdef __CONFIG_ARSC_SERVER__
50         // Dedicate one core (core 2) to sysserver, might be able to share wit NIC
51         num_mgmtcores++;
52         assert(num_cpus >= num_mgmtcores);
53         send_kernel_message(2, (amr_t)arsc_server, 0,0,0, KMSG_ROUTINE);
54  #endif
55         for (int i = 0; i < num_idlecores; i++)
56                 idlecoremap[i] = (i * 2) + 1;
57 #else
58         // __CONFIG_DISABLE_SMT__
59         #ifdef __CONFIG_NETWORKING__
60         num_mgmtcores++; // Next core is dedicated to the NIC
61         assert(num_cpus >= num_mgmtcores);
62         #endif
63         #ifdef __CONFIG_APPSERVER__
64         #ifdef __CONFIG_DEDICATED_MONITOR__
65         num_mgmtcores++; // Next core dedicated to running the kernel monitor
66         assert(num_cpus >= num_mgmtcores);
67         // Need to subtract 1 from the num_mgmtcores # to get the cores index
68         send_kernel_message(num_mgmtcores-1, (amr_t)monitor, 0,0,0, KMSG_ROUTINE);
69         #endif
70         #endif
71  #ifdef __CONFIG_ARSC_SERVER__
72         // Dedicate one core (core 2) to sysserver, might be able to share with NIC
73         num_mgmtcores++;
74         assert(num_cpus >= num_mgmtcores);
75         send_kernel_message(num_mgmtcores-1, (amr_t)arsc_server, 0,0,0, KMSG_ROUTINE);
76  #endif
77         num_idlecores = num_cpus - num_mgmtcores;
78         for (int i = 0; i < num_idlecores; i++)
79                 idlecoremap[i] = i + num_mgmtcores;
80 #endif /* __CONFIG_DISABLE_SMT__ */
81         spin_unlock(&idle_lock);
82         return;
83 }
84
85 void schedule_proc(struct proc *p)
86 {
87         /* up the refcnt since we are storing the reference */
88         proc_incref(p, 1);
89         spin_lock_irqsave(&runnablelist_lock);
90         printd("Scheduling PID: %d\n", p->pid);
91         TAILQ_INSERT_TAIL(&proc_runnablelist, p, proc_link);
92         spin_unlock_irqsave(&runnablelist_lock);
93         return;
94 }
95
96 /* TODO: race here.  it's possible that p was already removed from the
97  * list (by schedule()), while proc_destroy is trying to remove it from the
98  * list.  schedule()'s proc_run() won't actually run it (since it's DYING), but
99  * this code will probably fuck up.  Having TAILQ_REMOVE not hurt will help. */
100 void deschedule_proc(struct proc *p)
101 {
102         spin_lock_irqsave(&runnablelist_lock);
103         printd("Descheduling PID: %d\n", p->pid);
104         TAILQ_REMOVE(&proc_runnablelist, p, proc_link);
105         spin_unlock_irqsave(&runnablelist_lock);
106         /* down the refcnt, since its no longer stored */
107         proc_decref(p);
108         return;
109 }
110
111 /*
112  * FIFO - just pop the head from the list and run it.
113  * Using irqsave spinlocks for now, since this could be called from a timer
114  * interrupt handler (though ought to be in a bottom half or something).
115  */
116 void schedule(void)
117 {
118         struct proc *p;
119         
120         spin_lock_irqsave(&runnablelist_lock);
121         p = TAILQ_FIRST(&proc_runnablelist);
122         if (p) {
123                 TAILQ_REMOVE(&proc_runnablelist, p, proc_link);
124                 spin_unlock_irqsave(&runnablelist_lock);
125                 printd("PID of proc i'm running: %d\n", p->pid);
126                 /* We can safely read is_mcp without locking (i think). */
127                 if (__proc_is_mcp(p)) {
128                         /* _Ms need to get some cores, which will call proc_run() internally
129                          * (for now) */
130                         if (core_request(p) <= 0)
131                                 schedule_proc(p);       /* got none, put it back on the queue */
132                 } else {
133                         /* _S proc, just run it */
134                         proc_run(p);
135                 }
136                 /* proc_run will either eat the ref, or we'll decref manually. */
137                 proc_decref(p);
138         } else {
139                 spin_unlock_irqsave(&runnablelist_lock);
140         }
141         return;
142 }
143
144 /* Helper function to return a core to the idlemap.  It causes some more lock
145  * acquisitions (like in a for loop), but it's a little easier.  Plus, one day
146  * we might be able to do this without locks (for the putting). */
147 void put_idle_core(uint32_t coreid)
148 {
149         spin_lock(&idle_lock);
150         idlecoremap[num_idlecores++] = coreid;
151         spin_unlock(&idle_lock);
152 }
153
154 /* Normally it'll be the max number of CG cores ever */
155 uint32_t max_vcores(struct proc *p)
156 {
157 #ifdef __CONFIG_DISABLE_SMT__
158         return num_cpus >> 1;
159 #else
160         return MAX(1, num_cpus - num_mgmtcores);
161 #endif /* __CONFIG_DISABLE_SMT__ */
162 }
163
164 /* Ghetto old interface, hacked out of resource.c.  It doesn't even care about
165  * the proc yet, but in general the whole core_request bit needs reworked. */
166 uint32_t proc_wants_cores(struct proc *p, uint32_t *pc_arr, uint32_t amt_new)
167 {
168         uint32_t num_granted;
169         /* You should do something smarter than just giving the stuff out.  Like
170          * take in to account priorities, node locations, etc */
171         spin_lock(&idle_lock);
172         if (num_idlecores >= amt_new) {
173                 for (int i = 0; i < amt_new; i++) {
174                         // grab the last one on the list
175                         pc_arr[i] = idlecoremap[num_idlecores - 1];
176                         num_idlecores--;
177                 }
178                 num_granted = amt_new;
179         } else {
180                 /* In this case, you might want to preempt or do other fun things... */
181                 num_granted = 0;
182         }
183         spin_unlock(&idle_lock);
184         return num_granted;
185 }
186
187 /************** Debugging **************/
188 void dump_proclist(struct proc_list *list)
189 {
190         struct proc *p;
191         TAILQ_FOREACH(p, list, proc_link)
192                 printk("PID: %d\n", p->pid);
193         return;
194 }
195
196 void print_idlecoremap(void)
197 {
198         spin_lock(&idle_lock);
199         printk("There are %d idle cores.\n", num_idlecores);
200         for (int i = 0; i < num_idlecores; i++)
201                 printk("idlecoremap[%d] = %d\n", i, idlecoremap[i]);
202         spin_unlock(&idle_lock);
203 }