82db0a4bfc30d5dfb16464191821c5c71d925077
[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 /* This deals with a request for more cores.  The request is already stored in
21  * the proc's amt_wanted (it is compared to amt_granted). 
22  *
23  * It doesn't take the amount requested directly to avoid a race (or holding the
24  * proc_lock across the call), and allowing it to be called in other situations,
25  * such as if there was not a new request, but it's time to look at the
26  * difference between amt_wanted and amt_granted (maybe on a timer interrupt).
27  *
28  * Will return either the number actually granted or an error code.  This will
29  * not decrease the actual amount of cores (e.g. from 5 to 2), but it will
30  * transition a process from _M to _S (amt_wanted == 0).
31  *
32  * This needs a consumable/edible reference of p, in case it doesn't return.
33  *
34  * TODO: this is a giant function.  need to split it up a bit, probably move the
35  * guts to process.c and have functions to call for the brains.
36  */
37 ssize_t core_request(struct proc *p)
38 {
39         size_t num_granted;
40         ssize_t amt_new;
41         int32_t corelist[MAX_NUM_CPUS];
42         bool need_to_idle = FALSE;
43         bool self_ipi_pending = FALSE;
44
45         spin_lock_irqsave(&p->proc_lock);
46         /* check to see if this is a full deallocation.  for cores, it's a
47          * transition from _M to _S.  Will be issues with handling this async. */
48         if (!p->resources[RES_CORES].amt_wanted) {
49                 assert(p->state == PROC_RUNNING_M); // TODO: (ACR) async core req
50                 // save the context, to be restarted in _S mode
51                 p->env_tf = *current_tf;
52                 env_push_ancillary_state(p);
53                 proc_set_syscall_retval(&p->env_tf, ESUCCESS);
54                 /* sending death, since it's not our job to save contexts or anything in
55                  * this case.  also, if this returns true, we will not return down
56                  * below, and need to eat the reference to p */
57                 self_ipi_pending = __proc_take_allcores(p, __death, 0, 0, 0);
58                 __proc_set_state(p, PROC_RUNNABLE_S);
59                 schedule_proc(p);
60                 __proc_unlock_ipi_pending(p, self_ipi_pending);
61                 return 0;
62         }
63         /* otherwise, see how many new cores are wanted */
64         amt_new = p->resources[RES_CORES].amt_wanted -
65                   p->resources[RES_CORES].amt_granted;
66         if (amt_new < 0) {
67                 p->resources[RES_CORES].amt_wanted = p->resources[RES_CORES].amt_granted;
68                 spin_unlock_irqsave(&p->proc_lock);
69                 return -EINVAL;
70         } else if (amt_new == 0) {
71                 spin_unlock_irqsave(&p->proc_lock);
72                 return 0;
73         }
74         // else, we try to handle the request
75
76         /* TODO: someone needs to decide if the process gets the resources.
77          * we just check to see if they are available and give them out.  This
78          * should call out to the scheduler or some other *smart* function.  You
79          * could also imagine just putting it on the scheduler's queue and letting
80          * that do the core request */
81         spin_lock(&idle_lock);
82         if (num_idlecores >= amt_new) {
83                 for (int i = 0; i < amt_new; i++) {
84                         // grab the last one on the list
85                         corelist[i] = idlecoremap[num_idlecores-1];
86                         num_idlecores--;
87                 }
88                 num_granted = amt_new;
89         } else {
90                 num_granted = 0;
91         }
92         spin_unlock(&idle_lock);
93         // Now, actually give them out
94         if (num_granted) {
95                 p->resources[RES_CORES].amt_granted += num_granted;
96                 switch (p->state) {
97                         case (PROC_RUNNING_S):
98                                 // issue with if we're async or not (need to preempt it)
99                                 // either of these should trip it. TODO: (ACR) async core req
100                                 if ((current != p) || (p->vcoremap[0] != core_id()))
101                                         panic("We don't handle async RUNNING_S core requests yet.");
102                                 /* save the tf to be restarted on another core (in proc_run) */
103                                 p->env_tf = *current_tf;
104                                 env_push_ancillary_state(p);
105                                 /* set the return code to 0. since we're transitioning, vcore0
106                                  * will start up with the tf manually, and not get the return
107                                  * value through the regular syscall return path */
108                                 proc_set_syscall_retval(&p->env_tf, ESUCCESS);
109                                 /* in the async case, we'll need to remotely stop and bundle
110                                  * vcore0's TF.  this is already done for the sync case (local
111                                  * syscall). */
112                                 /* this process no longer runs on its old location (which is
113                                  * this core, for now, since we don't handle async calls) */
114                                 p->vcoremap[0] = -1;
115                                 // will need to give up this core / idle later (sync)
116                                 need_to_idle = TRUE;
117                                 // change to runnable_m (it's TF is already saved)
118                                 __proc_set_state(p, PROC_RUNNABLE_M);
119                                 // signals to proc_run that this is a _S to _M transition
120                                 p->env_flags |= PROC_TRANSITION_TO_M;
121                                 break;
122                         case (PROC_RUNNABLE_S):
123                                 /* Issues: being on the runnable_list, proc_set_state not liking
124                                  * it, and not clearly thinking through how this would happen.
125                                  * Perhaps an async call that gets serviced after you're
126                                  * descheduled? */
127                                 panic("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
128                                 break;
129                         default:
130                                 break;
131                 }
132                 /* give them the cores.  this will start up the extras if RUNNING_M. */
133                 self_ipi_pending = __proc_give_cores(p, corelist, num_granted);
134                 __proc_unlock_ipi_pending(p, self_ipi_pending);
135                 /* if there's a race on state (like DEATH), it'll get handled by
136                  * proc_run or proc_destroy */
137                 if (p->state == PROC_RUNNABLE_M)
138                         proc_run(p);
139                 /* if we are moving to a partitionable core from a RUNNING_S on a
140                  * management core, the kernel needs to do something else on this core
141                  * (just like in proc_destroy).  it also needs to decref, to consume the
142                  * reference that came into this function (since we don't return).  */
143                 if (need_to_idle) {
144                         proc_decref(p, 1);
145                         abandon_core();
146                 }
147         } else { // nothing granted, just return
148                 spin_unlock_irqsave(&p->proc_lock);
149         }
150         return num_granted;
151 }
152
153 error_t resource_req(struct proc *p, int type, size_t amt_wanted,
154                      size_t amt_wanted_min, uint32_t flags)
155 {
156         error_t retval;
157         printd("Received request for type: %d, amt_wanted: %d, amt_wanted_min: %d, "
158                "flag: %d\n", type, amt_wanted, amt_wanted_min, flags);
159         if (flags & REQ_ASYNC)
160                 // We have no sense of time yet, or of half-filling requests
161                 printk("[kernel] Async requests treated synchronously for now.\n");
162
163         /* set the desired resource amount in the process's resource list. */
164         spin_lock_irqsave(&p->proc_lock);
165         size_t old_amount = p->resources[type].amt_wanted;
166         p->resources[type].amt_wanted = amt_wanted;
167         p->resources[type].amt_wanted_min = MIN(amt_wanted_min, amt_wanted);
168         p->resources[type].flags = flags;
169         spin_unlock_irqsave(&p->proc_lock);
170
171         // no change in the amt_wanted
172         if (old_amount == amt_wanted)
173                 return 0;
174
175         switch (type) {
176                 case RES_CORES:
177                         retval = core_request(p);
178                         // i don't like this retval hackery
179                         if (retval < 0)
180                                 return retval;
181                         else
182                                 return 0;
183                         break;
184                 case RES_MEMORY:
185                         // not clear if we should be in RUNNABLE_M or not
186                         printk("[kernel] Memory requests are not implemented.\n");
187                         return -EFAIL;
188                         break;
189                 case RES_APPLE_PIES:
190                         printk("You can have all the apple pies you want.\n");
191                         break;
192                 default:
193                         printk("[kernel] Unknown resource!  No oranges for you!\n");
194                         return -EINVAL;
195         }
196         return 0;
197 }
198
199 void print_resources(struct proc *p)
200 {
201         printk("--------------------\n");
202         printk("PID: %d\n", p->pid);
203         printk("--------------------\n");
204         for (int i = 0; i < MAX_NUM_RESOURCES; i++)
205                 printk("Res type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
206                        p->resources[i].amt_wanted, p->resources[i].amt_granted);
207 }
208
209 void print_all_resources(void)
210 {
211         spin_lock(&pid_hash_lock);
212         if (hashtable_count(pid_hash)) {
213                 hashtable_itr_t *phtable_i = hashtable_iterator(pid_hash);
214                 do {
215                         print_resources(hashtable_iterator_value(phtable_i));
216                 } while (hashtable_iterator_advance(phtable_i));
217         }
218         spin_unlock(&pid_hash_lock);
219 }