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