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