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