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