Fix for EXPER_TRADPROC and vcore0
[akaros.git] / kern / src / resource.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  * Kernel resource management.
7  */
8
9 #ifdef __IVY__
10 #pragma nosharc
11 #endif
12
13 #include <resource.h>
14 #include <process.h>
15 #include <stdio.h>
16 #include <assert.h>
17 #include <schedule.h>
18 #include <hashtable.h>
19
20 #ifdef __CONFIG_OSDI__
21 /* Whoever was preempted from.  Ghetto hacks in yield will use this to give the
22  * core back.  Assumes only one proc is getting preempted, which is true for
23  * OSDI. */
24 struct proc *victim = NULL;
25 #endif
26
27 /* This deals with a request for more cores.  The request is already stored in
28  * the proc's amt_wanted (it is compared to amt_granted). 
29  *
30  * It doesn't take the amount requested directly to avoid a race (or holding the
31  * proc_lock across the call), and allowing it to be called in other situations,
32  * such as if there was not a new request, but it's time to look at the
33  * difference between amt_wanted and amt_granted (maybe on a timer interrupt).
34  *
35  * Will return either the number actually granted or an error code.  This will
36  * not decrease the actual amount of cores (e.g. from 5 to 2), but it will
37  * transition a process from _M to _S (amt_wanted == 0).
38  *
39  * This needs a consumable/edible reference of p, in case it doesn't return.
40  *
41  * TODO: this is a giant function.  need to split it up a bit, probably move the
42  * guts to process.c and have functions to call for the brains.
43  */
44 ssize_t core_request(struct proc *p)
45 {
46         size_t num_granted;
47         ssize_t amt_new;
48         uint32_t corelist[MAX_NUM_CPUS];
49         bool need_to_idle = FALSE;
50         bool self_ipi_pending = FALSE;
51
52         spin_lock(&p->proc_lock);
53         /* check to see if this is a full deallocation.  for cores, it's a
54          * transition from _M to _S.  Will be issues with handling this async. */
55         if (!p->resources[RES_CORES].amt_wanted) {
56                 assert(p->state == PROC_RUNNING_M); // TODO: (ACR) async core req
57                 // save the context, to be restarted in _S mode
58                 p->env_tf = *current_tf;
59                 env_push_ancillary_state(p);
60                 proc_set_syscall_retval(&p->env_tf, ESUCCESS);
61                 /* sending death, since it's not our job to save contexts or anything in
62                  * this case.  also, if this returns true, we will not return down
63                  * below, and need to eat the reference to p */
64                 self_ipi_pending = __proc_take_allcores(p, __death, 0, 0, 0);
65                 __proc_set_state(p, PROC_RUNNABLE_S);
66                 schedule_proc(p);
67                 spin_unlock(&p->proc_lock);
68                 __proc_kmsg_pending(p, self_ipi_pending);
69                 return 0;
70         }
71         /* Fail if we can never handle this amount (based on how many we told the
72          * process it can get). */
73         if (p->resources[RES_CORES].amt_wanted > p->procinfo->max_vcores) {
74                 spin_unlock(&p->proc_lock);
75                 return -EFAIL;
76         }
77         /* otherwise, see how many new cores are wanted */
78         amt_new = p->resources[RES_CORES].amt_wanted -
79                   p->resources[RES_CORES].amt_granted;
80         if (amt_new < 0) {
81                 p->resources[RES_CORES].amt_wanted = p->resources[RES_CORES].amt_granted;
82                 spin_unlock(&p->proc_lock);
83                 return -EINVAL;
84         } else if (amt_new == 0) {
85                 spin_unlock(&p->proc_lock);
86                 return 0;
87         }
88         // else, we try to handle the request
89
90         /* TODO: someone needs to decide if the process gets the resources.
91          * we just check to see if they are available and give them out.  This
92          * should call out to the scheduler or some other *smart* function.  You
93          * could also imagine just putting it on the scheduler's queue and letting
94          * that do the core request */
95         spin_lock(&idle_lock);
96         if (num_idlecores >= amt_new) {
97                 for (int i = 0; i < amt_new; i++) {
98                         // grab the last one on the list
99                         corelist[i] = idlecoremap[num_idlecores-1];
100                         num_idlecores--;
101                 }
102                 num_granted = amt_new;
103         } else {
104 #ifdef __CONFIG_OSDI__
105                 /* take what we can from the idlecoremap, then if enough aren't
106                  * available, take what we can now. */  
107                 num_granted = num_idlecores;
108                 for (int i = 0; i < num_granted; i++) {
109                         corelist[i] = idlecoremap[num_idlecores-1];
110                         num_idlecores--;
111                 }
112 #else
113                 num_granted = 0;
114 #endif /* __CONFIG_OSDI__ */
115         }
116         spin_unlock(&idle_lock);
117 #ifdef __CONFIG_OSDI__
118         /* Ghetto, using the SOFT flag to mean "take this from someone else" */
119         if (p->resources[RES_CORES].flags & REQ_SOFT) {
120                 /* And take whatever else we can from whoever is using other cores */
121                 size_t num_to_preempt = amt_new - num_granted;
122                 size_t num_preempted = 0;
123                 
124                 printd("Attempted to preempt %d cores for proc %d (%p)\n",
125                        num_to_preempt, p->pid, p);
126                 /* Find and preempt some cores.  Note the skipping of core 0.  Also note
127                  * this is a horrible way to do it.  A reasonably smart scheduler can
128                  * check its pcoremap. */
129                 for (int i = 1; i < num_cpus; i++) {
130                         victim = per_cpu_info[i].cur_proc;
131                         /* victim is a core with a current proc that isn't us */
132                         if (victim && victim != p) {
133                                 printd("Preempting pcore %d from proc %d (%p)\n", i, 
134                                        victim->pid, victim);
135                                 /* preempt_core technically needs an edible reference, though
136                                  * currently we always return since the victim isn't current */
137                                 proc_incref(victim, 1);
138                                 /* no waiting or anything, just take it.  telling them 1 sec */
139                                 proc_preempt_core(victim, i, 1000000);
140                                 proc_decref(victim, 1);
141                                 num_preempted++;
142                         }
143                         if (num_preempted == num_to_preempt)
144                                 break;
145                 }
146                 assert(num_preempted == num_to_preempt);
147                 printd("Trying to get the idlecores recently preempted.\n");
148                 /* Then take the idlecores for ourself.  Cannot handle a concurrent
149                  * core_request.  If this fails, things will be fucked. */
150                 spin_on(num_idlecores < num_to_preempt);
151                 spin_lock(&idle_lock);
152                 for (int i = num_granted; i < amt_new; i++) {
153                         // grab the last one on the list
154                         corelist[i] = idlecoremap[num_idlecores-1];
155                         num_idlecores--;
156                 }
157                 assert(num_idlecores >= 0);
158                 spin_unlock(&idle_lock);
159                 num_granted += num_preempted;
160                 assert(num_granted == amt_new);
161         }
162 #endif /* __CONFIG_OSDI__ */
163
164         // Now, actually give them out
165         if (num_granted) {
166                 switch (p->state) {
167                         case (PROC_RUNNING_S):
168                                 // issue with if we're async or not (need to preempt it)
169                                 // either of these should trip it. TODO: (ACR) async core req
170                                 // TODO: relies on vcore0 being the caller (VC#)
171                                 if ((current != p) || (p->procinfo->vcoremap[0].pcoreid != core_id()))
172                                         panic("We don't handle async RUNNING_S core requests yet.");
173                                 /* save the tf so userspace can restart it.  Like in __notify,
174                                  * this assumes a user tf is the same as a kernel tf.  We save
175                                  * it in the preempt slot so that we can also save the silly
176                                  * state. */
177                                 struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[0];
178                                 vcpd->preempt_tf = *current_tf;
179                                 save_fp_state(&vcpd->preempt_anc);
180                                 __seq_start_write(&vcpd->preempt_tf_valid);
181                                 /* If we remove this, vcore0 will start where the _S left off */
182                                 vcpd->notif_pending = TRUE;
183                                 assert(vcpd->notif_enabled);
184 #ifdef __CONFIG_EXPER_TRADPROC__
185                                 /* the proc that represents vcore0 will start at the entry
186                                  * point, as if it was a notification handler, so we'll mimic
187                                  * what __startcore would have done for a vcore0 restart. */
188                                 vcpd->notif_tf = *current_tf;
189                                 proc_init_trapframe(&p->env_tf, 0, p->env_entry,
190                                                     vcpd->transition_stack);
191                                 vcpd->notif_pending = FALSE;
192                                 vcpd->notif_enabled = FALSE;
193 #endif /* __CONFIG_EXPER_TRADPROC__ */
194                                 /* in the async case, we'll need to remotely stop and bundle
195                                  * vcore0's TF.  this is already done for the sync case (local
196                                  * syscall). */
197                                 /* this process no longer runs on its old location (which is
198                                  * this core, for now, since we don't handle async calls) */
199                                 __seq_start_write(&p->procinfo->coremap_seqctr);
200                                 // TODO: (VC#) might need to adjust num_vcores
201                                 // TODO: (ACR) will need to unmap remotely (receive-side)
202                                 __unmap_vcore(p, 0);
203                                 __seq_end_write(&p->procinfo->coremap_seqctr);
204                                 // will need to give up this core / idle later (sync)
205                                 need_to_idle = TRUE;
206                                 // change to runnable_m (it's TF is already saved)
207                                 __proc_set_state(p, PROC_RUNNABLE_M);
208                                 break;
209                         case (PROC_RUNNABLE_S):
210                                 /* Issues: being on the runnable_list, proc_set_state not liking
211                                  * it, and not clearly thinking through how this would happen.
212                                  * Perhaps an async call that gets serviced after you're
213                                  * descheduled? */
214                                 panic("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
215                                 break;
216                         default:
217                                 break;
218                 }
219                 /* give them the cores.  this will start up the extras if RUNNING_M. */
220                 self_ipi_pending = __proc_give_cores(p, corelist, num_granted);
221                 spin_unlock(&p->proc_lock);
222                 // TODO: (RMS) think about this, esp when its called from a scheduler
223                 __proc_kmsg_pending(p, self_ipi_pending);
224                 /* if there's a race on state (like DEATH), it'll get handled by
225                  * proc_run or proc_destroy */
226                 if (p->state == PROC_RUNNABLE_M)
227                         proc_run(p);
228                 /* if we are moving to a partitionable core from a RUNNING_S on a
229                  * management core, the kernel needs to do something else on this core
230                  * (just like in proc_destroy).  it also needs to decref, to consume the
231                  * reference that came into this function (since we don't return).  */
232                 if (need_to_idle) {
233                         proc_decref(p, 1);
234                         abandon_core();
235                 }
236         } else { // nothing granted, just return
237                 spin_unlock(&p->proc_lock);
238         }
239         return num_granted;
240 }
241
242 error_t resource_req(struct proc *p, int type, size_t amt_wanted,
243                      size_t amt_wanted_min, uint32_t flags)
244 {
245         error_t retval;
246         printd("Received request for type: %d, amt_wanted: %d, amt_wanted_min: %d, "
247                "flag: %d\n", type, amt_wanted, amt_wanted_min, flags);
248         if (flags & REQ_ASYNC)
249                 // We have no sense of time yet, or of half-filling requests
250                 printk("[kernel] Async requests treated synchronously for now.\n");
251
252 #ifdef __CONFIG_EXPER_TRADPROC__
253         /* this might be fucking with refcnts */
254         struct proc *tp;
255         if (!is_real_proc(p)) {
256                 tp = p->true_proc;
257                 assert(tp && !tp->true_proc);
258                 return resource_req(tp, type, amt_wanted, amt_wanted_min, flags);
259         }
260 #endif /* __CONFIG_EXPER_TRADPROC__ */
261         /* set the desired resource amount in the process's resource list. */
262         spin_lock(&p->proc_lock);
263         size_t old_amount = p->resources[type].amt_wanted;
264         p->resources[type].amt_wanted = amt_wanted;
265         p->resources[type].amt_wanted_min = MIN(amt_wanted_min, amt_wanted);
266         p->resources[type].flags = flags;
267         spin_unlock(&p->proc_lock);
268
269         switch (type) {
270                 case RES_CORES:
271                         retval = core_request(p);
272                         // i don't like this retval hackery
273                         if (retval < 0) {
274                                 set_errno(current_tf, -retval);
275                                 return -1;
276                         }
277                         else
278                                 return 0;
279                         break;
280                 case RES_MEMORY:
281                         // not clear if we should be in RUNNABLE_M or not
282                         printk("[kernel] Memory requests are not implemented.\n");
283                         return -EFAIL;
284                         break;
285                 case RES_APPLE_PIES:
286                         printk("You can have all the apple pies you want.\n");
287                         break;
288                 default:
289                         printk("[kernel] Unknown resource!  No oranges for you!\n");
290                         return -EINVAL;
291         }
292         return 0;
293 }
294
295 void print_resources(struct proc *p)
296 {
297         printk("--------------------\n");
298         printk("PID: %d\n", p->pid);
299         printk("--------------------\n");
300         for (int i = 0; i < MAX_NUM_RESOURCES; i++)
301                 printk("Res type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
302                        p->resources[i].amt_wanted, p->resources[i].amt_granted);
303 }
304
305 void print_all_resources(void)
306 {
307         spin_lock(&pid_hash_lock);
308         if (hashtable_count(pid_hash)) {
309                 hashtable_itr_t *phtable_i = hashtable_iterator(pid_hash);
310                 do {
311                         print_resources(hashtable_iterator_value(phtable_i));
312                 } while (hashtable_iterator_advance(phtable_i));
313         }
314         spin_unlock(&pid_hash_lock);
315 }