sys_self_notify() takes a flag for private mboxes
[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 <smp.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <schedule.h>
19 #include <hashtable.h>
20
21 /* This deals with a request for more cores.  The request is already stored in
22  * the proc's amt_wanted (it is compared to amt_granted). 
23  *
24  * It doesn't take the amount requested directly to avoid a race (or holding the
25  * proc_lock across the call), and allowing it to be called in other situations,
26  * such as if there was not a new request, but it's time to look at the
27  * difference between amt_wanted and amt_granted (maybe on a timer interrupt).
28  *
29  * Will return either the number actually granted or an error code.  This will
30  * not decrease the actual amount of cores (e.g. from 5 to 2), but it will
31  * transition a process from _M to _S (amt_wanted == 0).
32  *
33  * This needs a consumable/edible reference of p, in case it doesn't return.
34  *
35  * TODO: this is a giant function.  need to split it up a bit, probably move the
36  * guts to process.c and have functions to call for the brains.
37  */
38 ssize_t core_request(struct proc *p)
39 {
40         size_t num_granted;
41         ssize_t amt_new;
42         uint32_t corelist[MAX_NUM_CPUS];
43         bool need_to_idle = FALSE;
44         int8_t state = 0;
45
46         spin_lock(&p->proc_lock);
47         if (p->state == PROC_DYING) {
48                 spin_unlock(&p->proc_lock);
49                 return -EFAIL;
50         }
51         /* check to see if this is a full deallocation.  for cores, it's a
52          * transition from _M to _S.  Will be issues with handling this async. */
53         if (!p->resources[RES_CORES].amt_wanted) {
54                 printk("[kernel] trying to transition _M -> _S (deprecated)!\n");
55                 assert(p->state == PROC_RUNNING_M); // TODO: (ACR) async core req
56                 /* save the context, to be restarted in _S mode */
57                 disable_irqsave(&state);        /* protect cur_tf */
58                 assert(current_tf);
59                 p->env_tf = *current_tf;
60                 clear_owning_proc(core_id());   /* so we don't restart */
61                 enable_irqsave(&state);
62                 env_push_ancillary_state(p); // TODO: (HSS)
63                 /* sending death, since it's not our job to save contexts or anything in
64                  * this case.  also, if this returns true, we will not return down
65                  * below, and need to eat the reference to p */
66                 __proc_take_allcores(p, __death, 0, 0, 0);
67                 __proc_set_state(p, PROC_RUNNABLE_S);
68                 schedule_proc(p);
69                 spin_unlock(&p->proc_lock);
70                 return 0;
71         }
72         /* Fail if we can never handle this amount (based on how many we told the
73          * process it can get). */
74         if (p->resources[RES_CORES].amt_wanted > p->procinfo->max_vcores) {
75                 spin_unlock(&p->proc_lock);
76                 return -EFAIL;
77         }
78         /* otherwise, see how many new cores are wanted */
79         amt_new = p->resources[RES_CORES].amt_wanted -
80                   p->resources[RES_CORES].amt_granted;
81         if (amt_new < 0) {
82                 p->resources[RES_CORES].amt_wanted = p->resources[RES_CORES].amt_granted;
83                 spin_unlock(&p->proc_lock);
84                 return -EINVAL;
85         } else if (amt_new == 0) {
86                 spin_unlock(&p->proc_lock);
87                 return 0;
88         }
89         // else, we try to handle the request
90
91         /* TODO: someone needs to decide if the process gets the resources.
92          * we just check to see if they are available and give them out.  This
93          * should call out to the scheduler or some other *smart* function.  You
94          * could also imagine just putting it on the scheduler's queue and letting
95          * that do the core request */
96         spin_lock(&idle_lock);
97         if (num_idlecores >= amt_new) {
98                 for (int i = 0; i < amt_new; i++) {
99                         // grab the last one on the list
100                         corelist[i] = idlecoremap[num_idlecores-1];
101                         num_idlecores--;
102                 }
103                 num_granted = amt_new;
104         } else {
105                 /* In this case, you might want to preempt or do other fun things... */
106                 num_granted = 0;
107         }
108         spin_unlock(&idle_lock);
109
110         // Now, actually give them out
111         if (num_granted) {
112                 switch (p->state) {
113                         case (PROC_RUNNING_S):
114                                 // issue with if we're async or not (need to preempt it)
115                                 // either of these should trip it. TODO: (ACR) async core req
116                                 // TODO: relies on vcore0 being the caller (VC#)
117                                 // TODO: do this in process.c and use this line:
118                                 //if ((current != p) || (get_pcoreid(p, 0) != core_id()))
119                                 if ((current != p) || (p->procinfo->vcoremap[0].pcoreid != core_id()))
120                                         panic("We don't handle async RUNNING_S core requests yet.");
121                                 /* save the tf so userspace can restart it.  Like in __notify,
122                                  * this assumes a user tf is the same as a kernel tf.  We save
123                                  * it in the preempt slot so that we can also save the silly
124                                  * state. */
125                                 struct preempt_data *vcpd = &p->procdata->vcore_preempt_data[0];
126                                 disable_irqsave(&state);        /* protect cur_tf */
127                                 /* Note this won't play well with concurrent proc kmsgs, but
128                                  * since we're _S and locked, we shouldn't have any. */
129                                 assert(current_tf);
130                                 /* Copy uthread0's context to the notif slot */
131                                 vcpd->notif_tf = *current_tf;
132                                 clear_owning_proc(core_id());   /* so we don't restart */
133                                 save_fp_state(&vcpd->preempt_anc);
134                                 enable_irqsave(&state);
135                                 /* Userspace needs to not fuck with notif_disabled before
136                                  * transitioning to _M. */
137                                 if (vcpd->notif_disabled) {
138                                         printk("[kernel] user bug: notifs disabled for vcore 0\n");
139                                         vcpd->notif_disabled = FALSE;
140                                 }
141                                 /* in the async case, we'll need to remotely stop and bundle
142                                  * vcore0's TF.  this is already done for the sync case (local
143                                  * syscall). */
144                                 /* this process no longer runs on its old location (which is
145                                  * this core, for now, since we don't handle async calls) */
146                                 __seq_start_write(&p->procinfo->coremap_seqctr);
147                                 // TODO: (VC#) might need to adjust num_vcores
148                                 // TODO: (ACR) will need to unmap remotely (receive-side)
149                                 __unmap_vcore(p, 0);    /* VC# keep in sync with proc_run _S */
150                                 __seq_end_write(&p->procinfo->coremap_seqctr);
151                                 // will need to give up this core / idle later (sync)
152                                 need_to_idle = TRUE;
153                                 // change to runnable_m (it's TF is already saved)
154                                 __proc_set_state(p, PROC_RUNNABLE_M);
155                                 p->procinfo->is_mcp = TRUE;
156                                 break;
157                         case (PROC_RUNNABLE_S):
158                                 /* Issues: being on the runnable_list, proc_set_state not liking
159                                  * it, and not clearly thinking through how this would happen.
160                                  * Perhaps an async call that gets serviced after you're
161                                  * descheduled? */
162                                 panic("Not supporting RUNNABLE_S -> RUNNABLE_M yet.\n");
163                                 break;
164                         case (PROC_DYING):
165                                 warn("Dying, core request coming from %d\n", core_id());
166                         default:
167                                 break;
168                 }
169                 /* give them the cores.  this will start up the extras if RUNNING_M. */
170                 __proc_give_cores(p, corelist, num_granted);
171                 spin_unlock(&p->proc_lock);
172                 /* if there's a race on state (like DEATH), it'll get handled by
173                  * proc_run or proc_destroy.  TODO: Theoretical race here, since someone
174                  * else could make p an _S (in theory), and then we would be calling
175                  * this with an inedible ref (which is currently a concern). */
176                 if (p->state == PROC_RUNNABLE_M)
177                         proc_run(p);    /* I dislike this - caller should run it */
178         } else { // nothing granted, just return
179                 spin_unlock(&p->proc_lock);
180         }
181         return num_granted;
182 }
183
184 error_t resource_req(struct proc *p, int type, size_t amt_wanted,
185                      size_t amt_wanted_min, uint32_t flags)
186 {
187         error_t retval;
188         printd("Received request for type: %d, amt_wanted: %d, amt_wanted_min: %d, "
189                "flag: %d\n", type, amt_wanted, amt_wanted_min, flags);
190         if (flags & REQ_ASYNC)
191                 // We have no sense of time yet, or of half-filling requests
192                 printk("[kernel] Async requests treated synchronously for now.\n");
193
194         /* set the desired resource amount in the process's resource list. */
195         spin_lock(&p->proc_lock);
196         size_t old_amount = p->resources[type].amt_wanted;
197         p->resources[type].amt_wanted = amt_wanted;
198         p->resources[type].amt_wanted_min = MIN(amt_wanted_min, amt_wanted);
199         p->resources[type].flags = flags;
200         spin_unlock(&p->proc_lock);
201
202         switch (type) {
203                 case RES_CORES:
204                         retval = core_request(p);
205                         // i don't like this retval hackery
206                         if (retval < 0) {
207                                 set_errno(-retval);
208                                 return -1;
209                         }
210                         else
211                                 return 0;
212                         break;
213                 case RES_MEMORY:
214                         // not clear if we should be in RUNNABLE_M or not
215                         printk("[kernel] Memory requests are not implemented.\n");
216                         return -EFAIL;
217                         break;
218                 case RES_APPLE_PIES:
219                         printk("You can have all the apple pies you want.\n");
220                         break;
221                 default:
222                         printk("[kernel] Unknown resource!  No oranges for you!\n");
223                         return -EINVAL;
224         }
225         return 0;
226 }
227
228 void print_resources(struct proc *p)
229 {
230         printk("--------------------\n");
231         printk("PID: %d\n", p->pid);
232         printk("--------------------\n");
233         for (int i = 0; i < MAX_NUM_RESOURCES; i++)
234                 printk("Res type: %02d, amt wanted: %08d, amt granted: %08d\n", i,
235                        p->resources[i].amt_wanted, p->resources[i].amt_granted);
236 }
237
238 void print_all_resources(void)
239 {
240         /* Hash helper */
241         void __print_resources(void *item)
242         {
243                 print_resources((struct proc*)item);
244         }
245         spin_lock(&pid_hash_lock);
246         hash_for_each(pid_hash, __print_resources);
247         spin_unlock(&pid_hash_lock);
248 }