T245: capops: fix distributed delete
[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 "caplock.h"
16 #include "capqueue.h"
17 #include "dom_invocations.h"
18 #include "delete_int.h"
19 #include "internal.h"
20 #include "ram_alloc.h"
21 #include <if/mem_defs.h>
22
23 struct delete_remote_mc_st {
24     struct capsend_mc_st mc_st;
25     struct delete_st *del_st;
26     errval_t status;
27 };
28
29 struct delete_remote_result_msg_st {
30     struct intermon_msg_queue_elem queue_elem;
31     errval_t status;
32     genvaddr_t st;
33 };
34
35 static void delete_trylock_cont(void *st);
36
37 static void
38 delete_result__rx(errval_t status, struct delete_st *del_st, bool locked)
39 {
40     DEBUG_CAPOPS("%s: status=%s, locked=%d\n", __FUNCTION__, err_getcode(status), locked);
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) && err_no(err) != LIB_ERR_SLOT_UNALLOCATED) {
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_binding *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->rpc_tx_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.level, 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     DEBUG_CAPOPS("%s\n", __FUNCTION__);
159     errval_t err;
160     struct delete_remote_mc_st *mc_st;
161
162     err = malloce(sizeof(*mc_st), &mc_st);
163     GOTO_IF_ERR(err, report_error);
164     mc_st->del_st = st;
165     mc_st->status = SYS_ERR_OK;
166
167     err = capsend_copies(cap, delete_remote__send,
168                          (struct capsend_mc_st*)mc_st);
169     GOTO_IF_ERR(err, report_error);
170
171     return;
172
173 report_error:
174     delete_result__rx(err, st, true);
175 }
176
177 static void
178 delete_remote_result__send(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
179 {
180     errval_t err;
181     struct delete_remote_result_msg_st *msg_st = (struct delete_remote_result_msg_st*)e;
182     err = intermon_capops_delete_remote_result__tx(b, NOP_CONT, msg_st->status, msg_st->st);
183
184     if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
185         DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
186         struct intermon_state *inter_st = (struct intermon_state *)b->st;
187         // requeue send request at front and return
188         err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
189                                              (struct msg_queue_elem *)e);
190         GOTO_IF_ERR(err, handle_err);
191         return;
192     }
193
194 handle_err:
195     PANIC_IF_ERR(err, "failed to send delete_remote_result msg");
196     free(msg_st);
197 }
198
199 static void
200 delete_remote_result__enq(coreid_t dest, errval_t status, genvaddr_t st)
201 {
202     DEBUG_CAPOPS("%s: dest=%d, status=%s\n", __FUNCTION__, dest, err_getcode(status));
203     errval_t err;
204
205     struct delete_remote_result_msg_st *msg_st;
206     err = calloce(1, sizeof(*msg_st), &msg_st);
207     PANIC_IF_ERR(err, "allocating delete_remote_result st");
208
209     msg_st->queue_elem.cont = delete_remote_result__send;
210     msg_st->status = status;
211     msg_st->st = st;
212
213     err = capsend_target(dest, (struct msg_queue_elem*)msg_st);
214     PANIC_IF_ERR(err, "failed to send delete_remote result");
215 }
216
217 void
218 delete_remote__rx(struct intermon_binding *b, intermon_caprep_t caprep,
219                   genvaddr_t st)
220 {
221     DEBUG_CAPOPS("%s\n", __FUNCTION__);
222     errval_t err, err2;
223     struct capability cap;
224     struct intermon_state *inter_st = (struct intermon_state*)b->st;
225     coreid_t from = inter_st->core_id;
226     caprep_to_capability(&caprep, &cap);
227     struct capref capref;
228
229     err = slot_alloc(&capref);
230     GOTO_IF_ERR(err, send_err);
231
232     err = monitor_copy_if_exists(&cap, capref);
233     if (err_is_fail(err)) {
234         DEBUG_CAPOPS("%s: monitor_copy_if_exists: %s\n", __FUNCTION__, err_getcode(err));
235         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
236             // not found implies there were no copies, so everything is OK
237             err = SYS_ERR_OK;
238         }
239         goto free_slot;
240     }
241
242     err = monitor_delete_foreigns(capref);
243     DEBUG_CAPOPS("%s: monitor_delete_foreigns: %s\n", __FUNCTION__, err_getcode(err));
244     //err = monitor_delete_copies(capref);
245     //err2 = cap_delete(capref);
246     //DEBUG_IF_ERR(err2, "deleting temp delete_remote cap");
247     //if (err_is_ok(err) && err_is_fail(err2)) {
248     //    err = err2;
249     //}
250
251 free_slot:
252     err2 = slot_free(capref);
253     DEBUG_IF_ERR(err2, "freeing temp delete_remote cap, will leak");
254
255 send_err:
256     delete_remote_result__enq(from, err, st);
257 }
258
259 void
260 delete_remote_result__rx(struct intermon_binding *b, errval_t status,
261                          genvaddr_t st)
262 {
263     DEBUG_CAPOPS("%s\n", __FUNCTION__);
264     errval_t err;
265     struct delete_remote_mc_st *mc_st = (struct delete_remote_mc_st*)(lvaddr_t)st;
266     struct delete_st *del_st = mc_st->del_st;
267
268     // XXX: do something with received errors?
269     if (err_is_fail(status)) {
270         mc_st->status = status;
271     }
272     status = mc_st->status;
273
274     if (!capsend_handle_mc_reply(&mc_st->mc_st)) {
275         // multicast not complete
276         return;
277     }
278
279     // multicast is complete, free state
280     free(mc_st);
281
282     // unlock cap so it can be deleted
283     caplock_unlock(del_st->capref);
284
285     if (err_is_ok(status)) {
286         // remote copies have been deleted, reset corresponding relations bit
287         err = monitor_domcap_remote_relations(del_st->capref.croot,
288                                               del_st->capref.cptr,
289                                               del_st->capref.level,
290                                               0, RRELS_COPY_BIT, NULL);
291         if (err_is_fail(err)) {
292             USER_PANIC_ERR(err, "clearing remote descs bit after remote delete");
293         }
294
295         // All remote copies deleted, delete local copy; can be last
296         err = dom_cnode_delete(del_st->capref);
297         errval_t last_owned = err_push(SYS_ERR_DELETE_LAST_OWNED,
298                                        SYS_ERR_RETRY_THROUGH_MONITOR);
299         // We got DELETE_LAST_OWNED from cpu driver, do delete_last()
300         if (err == last_owned) {
301             delete_last(del_st);
302             // We just assume that delete_last() succeeds
303             err = SYS_ERR_OK;
304         }
305         else if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
306             // this shouldn't really happen either, but isn't a problem
307             err = SYS_ERR_OK;
308         }
309         else if (err_is_fail(err)) {
310             // other than DELETE_LAST_OWNED, the simple delete should not fail
311             // here.
312             USER_PANIC_ERR(err, "this really should not happen");
313         }
314     }
315     else {
316         err = status;
317     }
318
319     delete_result__rx(err, del_st, false);
320 }
321
322 /*
323  * Moveable cap type: try to migrate ownership elsewhere
324  */
325
326 static void move_result_cont(errval_t status, void *st);
327
328 static void
329 find_core_cont(errval_t status, coreid_t core, void *st)
330 {
331     DEBUG_CAPOPS("%s\n", __FUNCTION__);
332     // called with the result of "find core with cap" when trying to move the
333     // last cap
334     errval_t err = status;
335     struct delete_st *del_st = (struct delete_st*)st;
336
337     // unlock cap so it can be manipulated
338     caplock_unlock(del_st->capref);
339
340     if (err_no(status) == SYS_ERR_CAP_NOT_FOUND) {
341         // no core with cap exists, delete local cap with cleanup
342         err = monitor_domcap_remote_relations(del_st->capref.croot,
343                                               del_st->capref.cptr,
344                                               del_st->capref.level,
345                                               0, RRELS_COPY_BIT, NULL);
346         if (err_is_fail(err)) {
347             if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
348                 err = SYS_ERR_OK;
349             }
350             goto report_error;
351         }
352
353         delete_last(del_st);
354     }
355     else if (err_is_fail(status)) {
356         // an error occured
357         goto report_error;
358     }
359     else {
360         // core found, attempt move
361         err = capops_move(del_st->capref, core, move_result_cont, st);
362         GOTO_IF_ERR(err, report_error);
363     }
364
365     return;
366
367 report_error:
368     delete_result__rx(err, del_st, false);
369 }
370
371 static void
372 move_result_cont(errval_t status, void *st)
373 {
374     DEBUG_CAPOPS("%s\n", __FUNCTION__);
375     errval_t err = status;
376     struct delete_st *del_st = (struct delete_st*)st;
377     assert(distcap_is_moveable(del_st->cap.type));
378
379     if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
380         // the found remote copy has disappeared, restart move process
381         delete_trylock_cont(del_st);
382     }
383     else if (err_is_fail(err)) {
384         delete_result__rx(err, del_st, false);
385     }
386     else {
387         // move succeeded, cap is now foreign
388         err = dom_cnode_delete(del_st->capref);
389         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
390             err = SYS_ERR_OK;
391         }
392         delete_result__rx(err, del_st, false);
393     }
394 }
395
396 /*
397  * Delete operation
398  */
399
400 static void
401 delete_trylock_cont(void *st)
402 {
403     DEBUG_CAPOPS("%s\n", __FUNCTION__);
404     errval_t err;
405     bool locked = false;
406     struct delete_st *del_st = (struct delete_st*)st;
407
408     // try a simple delete
409     // NOTE: on the first pass, this is done twice (once in the capops_delete
410     // entry), but only this function is executed on every unlock event
411     err = dom_cnode_delete(del_st->capref);
412     if (err_no(err) != SYS_ERR_RETRY_THROUGH_MONITOR) {
413         // If cap is already locked, just enqueue for retry
414         if (err_no(err) == SYS_ERR_CAP_LOCKED) {
415             DEBUG_CAPOPS("%s: from cnode_delete(): cap already locked, queuing retry\n", __FUNCTION__);
416             caplock_wait(del_st->capref, &del_st->lock_qn,
417                          MKCLOSURE(delete_trylock_cont, del_st));
418             return;
419         }
420         // If cap not found, it has been deleted elsewhere, return OK
421         if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
422             DEBUG_CAPOPS("%s: from cnode_delete(): cap not found, got deleted from elsewhere\n", __FUNCTION__);
423             err = err_push(SYS_ERR_OK, err);
424         }
425         goto report_error;
426     }
427
428     err = monitor_lock_cap(del_st->capref.croot, del_st->capref.cptr,
429                            del_st->capref.level);
430     if (err_no(err) == SYS_ERR_CAP_LOCKED) {
431         caplock_wait(del_st->capref, &del_st->lock_qn,
432                      MKCLOSURE(delete_trylock_cont, del_st));
433         return;
434     }
435     else if (err_no(err) == SYS_ERR_CAP_NOT_FOUND) {
436         // Some other operation (another delete or a revoke) has deleted the
437         // target cap. This is OK.
438         err = err_push(SYS_ERR_OK, err);
439         goto report_error;
440     }
441     else if (err_is_fail(err)) {
442         DEBUG_ERR(err, "locking cap for delete");
443         goto report_error;
444     }
445     else {
446         locked = true;
447     }
448
449     // check if there could be any remote relations
450     uint8_t relations;
451     err = monitor_domcap_remote_relations(del_st->capref.croot,
452                                           del_st->capref.cptr,
453                                           del_st->capref.level,
454                                           0, 0, &relations);
455     GOTO_IF_ERR(err, report_error);
456
457     if (!(relations & RRELS_COPY_BIT)) {
458         // no remote relations, proceed with final delete
459         DEBUG_CAPOPS("%s: deleting last copy\n", __FUNCTION__);
460         delete_last(del_st);
461     }
462     else if (distcap_is_moveable(del_st->cap.type)) {
463         // if cap is moveable, move ownership so cap can then be deleted
464         DEBUG_CAPOPS("%s: move ownership\n", __FUNCTION__);
465         err = capsend_find_cap(&del_st->cap, find_core_cont, del_st);
466         GOTO_IF_ERR(err, report_error);
467     }
468     else {
469         DEBUG_CAPOPS("%s: cap type %d not moveable, delete all copies\n",
470                 __FUNCTION__, del_st->cap.type);
471         // otherwise delete all remote copies and then delete last copy
472         delete_remote__enq(&del_st->cap, del_st);
473     }
474
475     return;
476
477 report_error:
478     delete_result__rx(err, del_st, locked);
479 }
480
481 void
482 capops_delete_int(struct delete_st *del_st)
483 {
484     DEBUG_CAPOPS("%s\n", __FUNCTION__);
485     delete_trylock_cont(del_st);
486 }
487
488 void
489 capops_delete(struct domcapref cap,
490               delete_result_handler_t result_handler,
491               void *st)
492 {
493     errval_t err;
494     DEBUG_CAPOPS("%s\n", __FUNCTION__);
495
496     // try a simple delete
497     DEBUG_CAPOPS("%s: trying simple delete\n", __FUNCTION__);
498     err = dom_cnode_delete(cap);
499     // We can also continue here if we get SYS_ERR_CAP_LOCKED, as we're going
500     // to handle already locked caps correctly in delete_trylock_cont().
501     // -SG, 2017-05-02
502     if (err_no(err) != SYS_ERR_RETRY_THROUGH_MONITOR &&
503         err_no(err) != SYS_ERR_CAP_LOCKED)
504     {
505         DEBUG_CAPOPS("%s: err != RETRY && err != LOCKED\n", __FUNCTION__);
506         goto err_cont;
507     }
508
509     // simple delete was not able to delete cap as:
510     // * it was last copy and:
511     //    - may have remote copies, need to move or revoke cap
512     //    - contains further slots which need to be cleared
513     // * currently locked
514
515     struct delete_st *del_st;
516     err = calloce(1, sizeof(*del_st), &del_st);
517     GOTO_IF_ERR(err, err_cont);
518
519     err = monitor_domains_cap_identify(cap.croot, cap.cptr, cap.level,
520                                        &del_st->cap);
521     GOTO_IF_ERR(err, free_st);
522
523     err = slot_alloc(&del_st->newcap);
524     GOTO_IF_ERR(err, free_st);
525
526     del_st->capref = cap;
527     del_st->wait = true;
528     del_st->result_handler = result_handler;
529     del_st->st = st;
530
531     // after this setup is complete, nothing less than a catastrophic failure
532     // should stop the delete
533     delete_trylock_cont(del_st);
534     return;
535
536 free_st:
537     free(del_st);
538
539 err_cont:
540     DEBUG_CAPOPS("%s: calling result handler with err=%"PRIuERRV"\n", __FUNCTION__, err);
541     result_handler(err, st);
542 }