Merge branch 'caps_next'
[barrelfish] / usr / monitor / capops / delete.c
1 /*
2  * Copyright (c) 2012 ETH Zurich.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8  */
9
10 #include <barrelfish/barrelfish.h>
11 #include <barrelfish/core_state.h>
12 #include "monitor.h"
13 #include "capops.h"
14 #include "capsend.h"
15 #include "magic.h"
16 #include "caplock.h"
17 #include "capqueue.h"
18 #include "dom_invocations.h"
19 #include "delete_int.h"
20 #include "internal.h"
21 #include "ram_alloc.h"
22 #include <if/mem_rpcclient_defs.h>
23
24 struct delete_remote_mc_st {
25     struct capsend_mc_st mc_st;
26     struct delete_st *del_st;
27     errval_t status;
28 };
29
30 struct delete_remote_result_msg_st {
31     struct intermon_msg_queue_elem queue_elem;
32     errval_t status;
33     genvaddr_t st;
34 };
35
36 static void delete_trylock_cont(void *st);
37
38 static void
39 delete_result__rx(errval_t status, struct delete_st *del_st, bool locked)
40 {
41     errval_t err;
42
43     if (locked) {
44         caplock_unlock(del_st->capref);
45     }
46
47     err = slot_free(del_st->newcap);
48     if (err_is_fail(err)) {
49         DEBUG_ERR(err, "freeing reclamation slot, will leak");
50     }
51
52     delete_result_handler_t handler = del_st->result_handler;
53     void *st = del_st->st;
54     free(del_st);
55     handler(status, st);
56 }
57
58 void
59 send_new_ram_cap(struct capref cap)
60 {
61     DEBUG_CAPOPS("%s\n", __FUNCTION__);
62     errval_t err, result;
63
64     struct capability cap_data;
65     err = monitor_cap_identify(cap, &cap_data);
66     assert(err_is_ok(err));
67     assert(cap_data.type == ObjType_RAM);
68     struct RAM ram = cap_data.u.ram;
69
70     struct ram_alloc_state *ram_alloc_state = get_ram_alloc_state();
71     thread_mutex_lock(&ram_alloc_state->ram_alloc_lock);
72
73     struct mem_rpc_client *b = get_mem_client();
74     if (!b) {
75         DEBUG_CAPOPS("%s: forwarding to monitor.0\n", __FUNCTION__);
76         // we're not on core 0, so forward free_monitor msg to monitor.0
77         err = mon_ram_free(&cap_data, ram.base, log2ceil(ram.bytes));
78         assert(err_is_ok(err));
79     } else {
80         DEBUG_CAPOPS("%s: we are monitor.0\n", __FUNCTION__);
81         // XXX: This should not be an RPC! It could stall the monitor, but
82         // we trust mem_serv for the moment.
83         err = b->vtbl.free_monitor(b, cap, ram.base, log2ceil(ram.bytes), &result);
84         assert(err_is_ok(err));
85         assert(err_is_ok(result));
86     }
87
88     thread_mutex_unlock(&ram_alloc_state->ram_alloc_lock);
89
90     // XXX: this seems to happen during the lmp transfer anyway -SG
91     if (!b) {
92         DEBUG_CAPOPS("%s: not monitor.0, deleting local copy\n", __FUNCTION__);
93         // should we do this if not on core 0? -SG
94         err = cap_delete(cap);
95         assert(err_is_ok(err));
96     }
97     DEBUG_CAPOPS("%s: finished\n", __FUNCTION__);
98 }
99
100 static void delete_wait__fin(void *st_)
101 {
102     DEBUG_CAPOPS("%s\n", __FUNCTION__);
103     struct delete_st *st = (struct delete_st*)st_;
104     delete_result__rx(SYS_ERR_OK, st, false);
105 }
106
107 static void delete_last(struct delete_st* del_st)
108 {
109     DEBUG_CAPOPS("%s\n", __FUNCTION__);
110     errval_t err;
111     bool locked = true;
112
113     err = monitor_delete_last(del_st->capref.croot, del_st->capref.cptr,
114                               del_st->capref.bits, del_st->newcap);
115     GOTO_IF_ERR(err, report_error);
116     if (err_no(err) == SYS_ERR_RAM_CAP_CREATED) {
117         DEBUG_CAPOPS("%s: sending reclaimed RAM to memserv.\n", __FUNCTION__);
118         send_new_ram_cap(del_st->newcap);
119         err = SYS_ERR_OK;
120     }
121
122     DEBUG_CAPOPS("%s: deleted last copy\n", __FUNCTION__);
123     // at this point the cap has become "unlocked" because it is either deleted
124     // or in a clear/delete queue
125     locked = false;
126
127     if (!del_st->wait) {
128         goto report_error;
129     }
130
131     DEBUG_CAPOPS("%s: waiting on delete queue\n", __FUNCTION__);
132     delete_queue_wait(&del_st->qn, MKCLOSURE(delete_wait__fin, del_st));
133
134     return;
135
136 report_error:
137     DEBUG_CAPOPS("%s: reporting error: %s\n", __FUNCTION__,
138             err_getstring(err));
139     delete_result__rx(err, del_st, locked);
140 }
141
142 /*
143  * Non-moveable cap types: deleting all foreign copies when last owned copy of
144  * cap is deleted
145  */
146
147 static errval_t
148 delete_remote__send(struct intermon_binding *b, intermon_caprep_t *caprep,
149                     struct capsend_mc_st *st)
150 {
151     return intermon_capops_delete_remote__tx(b, NOP_CONT, *caprep,
152                                              (lvaddr_t)st);
153 }
154
155 static void
156 delete_remote__enq(struct capability *cap, struct delete_st *st)
157 {
158     errval_t err;
159     struct delete_remote_mc_st *mc_st;
160
161     err = malloce(sizeof(*mc_st), &mc_st);
162     GOTO_IF_ERR(err, report_error);
163     mc_st->del_st = st;
164
165     err = capsend_copies(cap, delete_remote__send,
166                          (struct capsend_mc_st*)mc_st);
167     GOTO_IF_ERR(err, report_error);
168
169     return;
170
171 report_error:
172     delete_result__rx(err, st, true);
173 }
174
175 static void
176 delete_remote_result__send(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
177 {
178     errval_t err;
179     struct delete_remote_result_msg_st *msg_st = (struct delete_remote_result_msg_st*)e;
180     err = intermon_capops_delete_remote_result__tx(b, NOP_CONT, msg_st->status, msg_st->st);
181
182     if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
183         DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
184         struct intermon_state *inter_st = (struct intermon_state *)b->st;
185         // requeue send request at front and return
186         err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
187                                              (struct msg_queue_elem *)e);
188         GOTO_IF_ERR(err, handle_err);
189         return;
190     }
191
192 handle_err:
193     PANIC_IF_ERR(err, "failed to send delete_remote_result msg");
194     free(msg_st);
195 }
196
197 static void
198 delete_remote_result__enq(coreid_t dest, errval_t status, genvaddr_t st)
199 {
200     errval_t err;
201
202     struct delete_remote_result_msg_st *msg_st;
203     err = calloce(1, sizeof(*msg_st), &msg_st);
204     PANIC_IF_ERR(err, "allocating delete_remote_result st");
205
206     msg_st->queue_elem.cont = delete_remote_result__send;
207     msg_st->status = status;
208     msg_st->st = st;
209
210     err = capsend_target(dest, (struct msg_queue_elem*)msg_st);
211     PANIC_IF_ERR(err, "failed to send delete_remote result");
212 }
213
214 void
215 delete_remote__rx(struct intermon_binding *b, intermon_caprep_t caprep,
216                   genvaddr_t st)
217 {
218     errval_t err, err2;
219     struct capability cap;
220     struct intermon_state *inter_st = (struct intermon_state*)b->st;
221     coreid_t from = inter_st->core_id;
222     caprep_to_capability(&caprep, &cap);
223     struct capref capref;
224
225     err = slot_alloc(&capref);
226     GOTO_IF_ERR(err, send_err);
227
228     err = monitor_copy_if_exists(&cap, capref);
229     if (err_is_fail(err)) {
230         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
231             // not found implies there were no copies, so everything is OK
232             err = SYS_ERR_OK;
233         }
234         goto free_slot;
235     }
236
237     err = monitor_delete_foreigns(capref);
238     //err = monitor_delete_copies(capref);
239     //err2 = cap_delete(capref);
240     //DEBUG_IF_ERR(err2, "deleting temp delete_remote cap");
241     //if (err_is_ok(err) && err_is_fail(err2)) {
242     //    err = err2;
243     //}
244
245 free_slot:
246     err2 = slot_free(capref);
247     DEBUG_IF_ERR(err2, "freeing temp delete_remote cap, will leak");
248
249 send_err:
250     delete_remote_result__enq(from, err, st);
251 }
252
253 void
254 delete_remote_result__rx(struct intermon_binding *b, errval_t status,
255                          genvaddr_t st)
256 {
257     errval_t err;
258     struct delete_remote_mc_st *mc_st = (struct delete_remote_mc_st*)(lvaddr_t)st;
259     struct delete_st *del_st = mc_st->del_st;
260
261     // XXX: do something with received errors?
262     if (err_is_fail(status)) {
263         mc_st->status = status;
264     }
265     status = mc_st->status;
266
267     if (!capsend_handle_mc_reply(&mc_st->mc_st)) {
268         // multicast not complete
269         return;
270     }
271
272     // multicast is complete, free state
273     free(mc_st);
274
275     // unlock cap so it can be deleted
276     caplock_unlock(del_st->capref);
277
278     if (err_is_ok(status)) {
279         // remote copies have been deleted, reset corresponding relations bit
280         err = monitor_domcap_remote_relations(del_st->capref.croot,
281                                               del_st->capref.cptr,
282                                               del_st->capref.bits,
283                                               0, RRELS_COPY_BIT, NULL);
284         if (err_is_fail(err)) {
285             USER_PANIC_ERR(err, "clearing remote descs bit after remote delete");
286         }
287
288         // now a "regular" delete should work again
289         err = dom_cnode_delete(del_st->capref);
290         if (err_no(err) == SYS_ERR_RETRY_THROUGH_MONITOR) {
291             USER_PANIC_ERR(err, "this really should not happen");
292         }
293         else if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
294             // this shouldn't really happen either, but isn't a problem
295             err = SYS_ERR_OK;
296         }
297     }
298     else {
299         err = status;
300     }
301
302     delete_result__rx(err, del_st, false);
303 }
304
305 /*
306  * Moveable cap type: try to migrate ownership elsewhere
307  */
308
309 static void move_result_cont(errval_t status, void *st);
310
311 static void
312 find_core_cont(errval_t status, coreid_t core, void *st)
313 {
314     // called with the result of "find core with cap" when trying to move the
315     // last cap
316     errval_t err = status;
317     struct delete_st *del_st = (struct delete_st*)st;
318
319     // unlock cap so it can be manipulated
320     caplock_unlock(del_st->capref);
321
322     if (err_no(status) == SYS_ERR_CAP_NOT_FOUND) {
323         // no core with cap exists, delete local cap with cleanup
324         err = monitor_domcap_remote_relations(del_st->capref.croot,
325                                               del_st->capref.cptr,
326                                               del_st->capref.bits,
327                                               0, RRELS_COPY_BIT, NULL);
328         if (err_is_fail(err)) {
329             if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
330                 err = SYS_ERR_OK;
331             }
332             goto report_error;
333         }
334
335         delete_last(del_st);
336     }
337     else if (err_is_fail(status)) {
338         // an error occured
339         goto report_error;
340     }
341     else {
342         // core found, attempt move
343         err = capops_move(del_st->capref, core, move_result_cont, st);
344         GOTO_IF_ERR(err, report_error);
345     }
346
347     return;
348
349 report_error:
350     delete_result__rx(err, del_st, false);
351 }
352
353 static void
354 move_result_cont(errval_t status, void *st)
355 {
356     errval_t err = status;
357     struct delete_st *del_st = (struct delete_st*)st;
358     assert(distcap_is_moveable(del_st->cap.type));
359
360     if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
361         // the found remote copy has disappeared, restart move process
362         delete_trylock_cont(del_st);
363     }
364     else if (err_is_fail(err)) {
365         delete_result__rx(err, del_st, false);
366     }
367     else {
368         // move succeeded, cap is now foreign
369         err = dom_cnode_delete(del_st->capref);
370         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
371             err = SYS_ERR_OK;
372         }
373         delete_result__rx(err, del_st, false);
374     }
375 }
376
377 /*
378  * Delete operation
379  */
380
381 static void
382 delete_trylock_cont(void *st)
383 {
384     errval_t err;
385     bool locked = false;
386     struct delete_st *del_st = (struct delete_st*)st;
387
388     // try a simple delete
389     // NOTE: on the first pass, this is done twice (once in the capops_delete
390     // entry), but only this function is executed on every unlock event
391     err = dom_cnode_delete(del_st->capref);
392     if (err_no(err) != SYS_ERR_RETRY_THROUGH_MONITOR) {
393         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
394             err = SYS_ERR_OK;
395         }
396         goto report_error;
397     }
398
399     err = monitor_lock_cap(del_st->capref.croot, del_st->capref.cptr,
400                            del_st->capref.bits);
401     if (err_no(err) == SYS_ERR_CAP_LOCKED) {
402         caplock_wait(del_st->capref, &del_st->lock_qn,
403                      MKCLOSURE(delete_trylock_cont, del_st));
404         return;
405     }
406     else if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
407         // Some other operation (another delete or a revoke) has deleted the
408         // target cap. This is OK.
409         err = err_push(SYS_ERR_OK, err);
410         goto report_error;
411     }
412     else if (err_is_fail(err)) {
413         DEBUG_ERR(err, "locking cap for delete");
414         goto report_error;
415     }
416     else {
417         locked = true;
418     }
419
420     // check if there could be any remote relations
421     uint8_t relations;
422     err = monitor_domcap_remote_relations(del_st->capref.croot,
423                                           del_st->capref.cptr,
424                                           del_st->capref.bits,
425                                           0, 0, &relations);
426     GOTO_IF_ERR(err, report_error);
427
428     if (!(relations & RRELS_COPY_BIT)) {
429         // no remote relations, proceed with final delete
430         DEBUG_CAPOPS("%s: deleting last copy\n", __FUNCTION__);
431         delete_last(del_st);
432     }
433     else if (distcap_is_moveable(del_st->cap.type)) {
434         // if cap is moveable, move ownership so cap can then be deleted
435         err = capsend_find_cap(&del_st->cap, find_core_cont, del_st);
436         GOTO_IF_ERR(err, report_error);
437     }
438     else {
439         // otherwise delete all remote copies and then delete last copy
440         delete_remote__enq(&del_st->cap, del_st);
441     }
442
443     return;
444
445 report_error:
446     delete_result__rx(err, del_st, locked);
447 }
448
449 void
450 capops_delete_int(struct delete_st *del_st)
451 {
452     delete_trylock_cont(del_st);
453 }
454
455 void
456 capops_delete(struct domcapref cap,
457               delete_result_handler_t result_handler,
458               void *st)
459 {
460     errval_t err;
461     DEBUG_CAPOPS("%s\n", __FUNCTION__);
462
463     // try a simple delete
464     DEBUG_CAPOPS("%s: trying simple delete\n", __FUNCTION__);
465     err = dom_cnode_delete(cap);
466     if (err_no(err) != SYS_ERR_RETRY_THROUGH_MONITOR) {
467         DEBUG_CAPOPS("%s: err != RETRY\n", __FUNCTION__);
468         goto err_cont;
469     }
470
471     // simple delete was not able to delete cap as it was last copy and:
472     //  - may have remote copies, need to move or revoke cap
473     //  - contains further slots which need to be cleared
474
475     struct delete_st *del_st;
476     err = calloce(1, sizeof(*del_st), &del_st);
477     GOTO_IF_ERR(err, err_cont);
478
479     err = monitor_domains_cap_identify(cap.croot, cap.cptr, cap.bits,
480                                        &del_st->cap);
481     GOTO_IF_ERR(err, free_st);
482
483     err = slot_alloc(&del_st->newcap);
484     GOTO_IF_ERR(err, free_st);
485
486     del_st->capref = cap;
487     del_st->wait = true;
488     del_st->result_handler = result_handler;
489     del_st->st = st;
490
491     // after this setup is complete, nothing less than a catastrophic failure
492     // should stop the delete
493     delete_trylock_cont(del_st);
494     return;
495
496 free_st:
497     free(del_st);
498
499 err_cont:
500     DEBUG_CAPOPS("%s: calling result handler with err=%"PRIuERRV"\n", __FUNCTION__, err);
501     result_handler(err, st);
502 }