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 <if/intermon_defs.h>
12#include "monitor.h"
13#include "capops.h"
14#include "capsend.h"
15#include "caplock.h"
16#include "internal.h"
17#include "dom_invocations.h"
18
19/*
20 * RPC State {{{1
21 */
22
23struct cap_move_rpc_st {
24    // capref to a copy of the cap in question
25    struct domcapref capref;
26    // caller st
27    void *st;
28    // reuslt handler
29    move_result_handler_t result_handler;
30};
31
32/*
33 * Move request
34 */
35
36struct move_request_msg_st {
37    struct intermon_msg_queue_elem queue_elem;
38    intermon_caprep_t caprep;
39    uint8_t relations;
40    struct cap_move_rpc_st *st;
41};
42
43static void
44move_request_send_cont(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
45{
46    errval_t err;
47    struct move_request_msg_st *msg_st = (struct move_request_msg_st*)e;
48    err = intermon_capops_move_request__tx(b, NOP_CONT, msg_st->caprep,
49                                           msg_st->relations, (lvaddr_t)msg_st->st);
50
51    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
52        DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
53        struct intermon_state *inter_st = (struct intermon_state *)b->st;
54        // requeue send request at front and return
55        err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
56                                             (struct msg_queue_elem *)e);
57        GOTO_IF_ERR(err, handle_err);
58        return;
59    }
60
61handle_err:
62    if (err_is_fail(err)) {
63        struct cap_move_rpc_st *rpc_st = (struct cap_move_rpc_st*)msg_st->st;
64        if (rpc_st->result_handler) {
65            rpc_st->result_handler(err, rpc_st->st);
66        }
67        free(rpc_st);
68    }
69    free(msg_st);
70}
71
72static errval_t
73move_request(struct domcapref capref, struct capability *cap, uint8_t relations,
74             coreid_t dest, move_result_handler_t result_handler, void *st)
75{
76    errval_t err;
77
78    struct cap_move_rpc_st *rpc_st = malloc(sizeof(struct cap_move_rpc_st));
79    if (!rpc_st) {
80        return LIB_ERR_MALLOC_FAIL;
81    }
82    rpc_st->capref = capref;
83    rpc_st->st = st;
84    rpc_st->result_handler = result_handler;
85
86    struct move_request_msg_st *msg_st = malloc(sizeof(struct move_request_msg_st));
87    if (!msg_st) {
88        free(rpc_st);
89        return LIB_ERR_MALLOC_FAIL;
90    }
91    msg_st->queue_elem.cont = move_request_send_cont;
92    capability_to_caprep(cap, &msg_st->caprep);
93    msg_st->relations = relations;
94    msg_st->st = rpc_st;
95
96    err = capsend_target(dest, (struct msg_queue_elem*)msg_st);
97    if (err_is_fail(err)) {
98        free(msg_st);
99        free(rpc_st);
100        return err;
101    }
102
103    return SYS_ERR_OK;
104}
105
106/*
107 * Move result
108 */
109
110struct move_result_msg_st {
111    struct intermon_msg_queue_elem queue_elem;
112    errval_t status;
113    genvaddr_t st;
114};
115
116static void
117move_result_send_cont(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
118{
119    errval_t err;
120    struct move_result_msg_st *msg_st = (struct move_result_msg_st*)e;
121    err = intermon_capops_move_result__tx(b, NOP_CONT, msg_st->status, msg_st->st);
122
123    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
124        DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
125        struct intermon_state *inter_st = (struct intermon_state *)b->st;
126        // requeue send request at front and return
127        err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
128                                             (struct msg_queue_elem *)e);
129        GOTO_IF_ERR(err, handle_err);
130        return;
131    }
132
133handle_err:
134    if (err_is_fail(err)) {
135        USER_PANIC_ERR(err, "failed to send move_result");
136    }
137    free(msg_st);
138}
139
140static errval_t
141move_result(coreid_t dest, errval_t status, genvaddr_t st)
142{
143    errval_t err;
144
145    struct move_result_msg_st *msg_st = calloc(1, sizeof(struct move_result_msg_st));
146    msg_st->queue_elem.cont = move_result_send_cont;
147    msg_st->status = status;
148    msg_st->st = st;
149
150    err = capsend_target(dest, (struct msg_queue_elem*)msg_st);
151    if (err_is_fail(err)) {
152        free(msg_st);
153        return err;
154    }
155
156    return SYS_ERR_OK;
157}
158
159/*
160 * Receive handlers {{{1
161 */
162
163static void
164free_owner_recv_cap(void *arg)
165{
166    struct capref *cap = (struct capref*)arg;
167    caplock_unlock(get_cap_domref(*cap));
168    errval_t err = cap_destroy(*cap);
169    if (err_is_fail(err)) {
170        DEBUG_ERR(err, "destroying cap receiving ownersihp");
171    }
172    free(cap);
173}
174
175void
176move_request__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep, uint8_t relations, genvaddr_t st)
177{
178    errval_t err, send_err;
179    struct intermon_state *inter_st = (struct intermon_state*)b->st;
180    coreid_t from = inter_st->core_id;
181    assert(from != my_core_id);
182
183    struct capability cap;
184    caprep_to_capability(&caprep, &cap);
185
186    struct capref *capref = calloc(1, sizeof(*capref));
187    if (!capref) {
188        err = LIB_ERR_MALLOC_FAIL;
189        goto send_err;
190    }
191
192    err = slot_alloc(capref);
193    if (err_is_fail(err)) {
194        goto free_st;
195    }
196
197    struct domcapref domcapref = get_cap_domref(*capref);
198
199    err = monitor_copy_if_exists(&cap, *capref);
200    if (err_is_fail(err)) {
201        goto free_slot;
202    }
203
204    err = monitor_lock_cap(domcapref.croot, domcapref.cptr, domcapref.level);
205    if (err_is_fail(err)) {
206        goto destroy_cap;
207    }
208
209    err = monitor_set_domcap_owner(domcapref, my_core_id);
210    if (err_is_fail(err)) {
211        goto unlock_cap;
212    }
213
214    err = monitor_domcap_remote_relations(domcapref.croot, domcapref.cptr,
215                                          domcapref.level, relations,
216                                          ~(uint8_t)0, NULL);
217    if (err_is_fail(err)) {
218        goto reset_owner;
219    }
220
221    // If broadcast send doesn't fail, unlock cap only after all nodes have
222    // acknowledged ownership change, see free_owner_recv_cap().
223    // XXX: should we wait to signal move completion until broadcast really
224    // completes? -SG,2017-11-09.
225    err = capsend_update_owner(domcapref, MKCONT(free_owner_recv_cap, capref));
226    if (err_is_fail(err)) {
227        goto reset_owner;
228    }
229
230    err = SYS_ERR_OK;
231    goto send_err;
232
233reset_owner:
234    send_err = monitor_set_domcap_owner(domcapref, from);
235    if (err_is_fail(send_err)) {
236        USER_PANIC_ERR(send_err, "failed to reset owner while handling move failure");
237    }
238
239unlock_cap:
240    send_err = monitor_unlock_cap(domcapref.croot, domcapref.cptr,
241                                  domcapref.level);
242    if (err_is_fail(send_err)) {
243        USER_PANIC_ERR(send_err, "failed to unlock cap while handling move failure");
244    }
245
246destroy_cap:
247    cap_destroy(*capref);
248
249free_slot:
250    slot_free(*capref);
251
252free_st:
253    free(capref);
254
255send_err:
256    send_err = move_result(from, err, st);
257    if (err_is_fail(send_err)) {
258        USER_PANIC_ERR(send_err, "failed to send error to request_copy sender");
259    }
260}
261
262void
263move_result__rx_handler(struct intermon_binding *b, errval_t status, genvaddr_t st)
264{
265    struct intermon_state *inter_st = (struct intermon_state*)b->st;
266    coreid_t from = inter_st->core_id;
267    assert(from != my_core_id);
268    struct cap_move_rpc_st *rpc_st = (struct cap_move_rpc_st*)(lvaddr_t)st;
269
270    caplock_unlock(rpc_st->capref);
271    rpc_st->result_handler(status, rpc_st->st);
272    free(rpc_st);
273}
274
275/*
276 * Move operation {{{1
277 */
278
279errval_t
280capops_move(struct domcapref capref, coreid_t dest, move_result_handler_t result_handler,
281            void *st)
282{
283    errval_t err;
284    distcap_state_t state;
285
286    err = dom_cnode_get_state(capref, &state);
287    if (err_is_fail(err)) {
288        return err;
289    }
290    if (distcap_state_is_busy(state)) {
291        return MON_ERR_REMOTE_CAP_RETRY;
292    }
293    if (distcap_state_is_foreign(state)) {
294        return MON_ERR_CAP_FOREIGN;
295    }
296
297    if (dest == my_core_id) {
298        // tried to move to self and already owner, no-op
299        if (result_handler) {
300            result_handler(SYS_ERR_OK, st);
301        }
302        return SYS_ERR_OK;
303    }
304
305    struct capability cap;
306    err = monitor_domains_cap_identify(capref.croot, capref.cptr, capref.level, &cap);
307    if (err_is_fail(err)) {
308        return err;
309    }
310
311    if (!distcap_needs_locality(cap.type)) {
312        // XXX: move doesn't make sense here, but is "OK" result correct?
313        return SYS_ERR_OK;
314    }
315    if (!distcap_is_moveable(cap.type)) {
316        return MON_ERR_CAP_MOVE;
317    }
318
319    err = monitor_lock_cap(capref.croot, capref.cptr, capref.level);
320    if (err_is_fail(err)) {
321        return err;
322    }
323
324    uint8_t relations;
325    err = monitor_domcap_remote_relations(capref.croot, capref.cptr, capref.level,
326                                          0, 0, &relations);
327    if (err_is_fail(err)) {
328        caplock_unlock(capref);
329        return err;
330    }
331
332    err = move_request(capref, &cap, relations, dest, result_handler, st);
333    if (err_is_fail(err)) {
334        caplock_unlock(capref);
335        return err;
336    }
337
338    return SYS_ERR_OK;
339}
340