1/**
2 * \file
3 * \brief Arch-specific inter-monitor communication
4 */
5
6/*
7 * Copyright (c) 2009, 2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <inttypes.h>
16#include "monitor.h"
17#include <trace/trace.h>
18
19struct bind_monitor_reply_state {
20    struct intermon_msg_queue_elem elem;
21    struct intermon_binding *orig_binding;
22    errval_t err;
23};
24
25static void send_bind_monitor_reply(struct intermon_binding *b, errval_t err);
26
27static void send_bind_monitor_reply_cont(struct intermon_binding *b,
28                                         struct intermon_msg_queue_elem *e)
29{
30    struct bind_monitor_reply_state *st = (struct bind_monitor_reply_state *)e;
31    send_bind_monitor_reply(st->orig_binding, st->err);
32    free(st);
33}
34
35static void send_bind_monitor_reply(struct intermon_binding *b, errval_t err)
36{
37    errval_t err2 = b->tx_vtbl.bind_monitor_reply(b, NOP_CONT, err);
38    if (err_is_fail(err2)) {
39        if (err_no(err2) == FLOUNDER_ERR_TX_BUSY) {
40            struct intermon_state *is = b->st;
41            struct bind_monitor_reply_state *st = malloc(sizeof(*st));
42            assert(st != NULL);
43
44            st->orig_binding = b;
45            st->elem.cont = send_bind_monitor_reply_cont;
46            st->err = err;
47
48            err2 = intermon_enqueue_send(b, &is->queue,
49                                         get_default_waitset(), &st->elem.queue);
50            assert(err_is_ok(err2));
51
52        } else {
53            DEBUG_ERR(err2, "reply failed");
54        }
55    }
56}
57
58/**
59 * A monitor newly booted monitor receives
60 * request to setup a connection with another monitor
61 * from a third monitor.
62 *
63 * \param core_id Originating core id
64 * \param caprep Endpoint capability
65 */
66static void bind_monitor_request(struct intermon_binding *b,
67                                 coreid_t core_id,
68                                 intermon_caprep_t caprep)
69{
70    //printf("%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
71    errval_t err;
72    trace_event(TRACE_SUBSYS_MONITOR, TRACE_EVENT_MONITOR_BIND_MONITOR_REQUEST, core_id);
73
74    /* Create the cap */
75    struct capability cap_raw;
76    caprep_to_capability(&caprep, &cap_raw);
77    if (cap_raw.type != ObjType_Frame) {
78        err = MON_ERR_WRONG_CAP_TYPE;
79        goto error;
80    }
81
82    struct capref frame;
83    err = slot_alloc(&frame);
84    if (err_is_fail(err)) {
85        goto error;
86    }
87
88    err = monitor_cap_create(frame, &cap_raw, core_id);
89    if (err_is_fail(err)) {
90        goto error;
91    }
92
93    /* Setup the connection */
94    void *buf;
95    err = vspace_map_one_frame(&buf, MON_URPC_SIZE, frame, NULL, NULL);
96    if (err_is_fail(err)) {
97        err = err_push(err, LIB_ERR_VSPACE_MAP);
98        goto error;
99    }
100
101    // setup our side of the binding
102    struct intermon_ump_binding *umpb;
103    umpb = malloc(sizeof(struct intermon_ump_binding));
104    assert(umpb != NULL);
105
106    err = intermon_ump_init(umpb, get_default_waitset(),
107                            (char *)buf + MON_URPC_CHANNEL_LEN,
108                            MON_URPC_CHANNEL_LEN,
109                            buf, MON_URPC_CHANNEL_LEN);
110    assert(err_is_ok(err));
111
112    // Identify UMP frame for tracing
113    umpb->ump_state.chan.sendid = (uintptr_t)cap_raw.u.frame.base;
114    umpb->ump_state.chan.recvid =
115        (uintptr_t)(cap_raw.u.frame.base + MON_URPC_CHANNEL_LEN);
116
117    // connect it to our request handlers
118    err = intermon_init(&umpb->b, core_id);
119    assert(err_is_ok(err));
120
121    /* Send reply */
122reply:
123    send_bind_monitor_reply(b, err);
124    return;
125
126error:
127    // FIXME: cleanup!
128    goto reply;
129}
130
131/**
132 * The monitor that proxied the request for one monitor to
133 * setup a connection with another monitor gets the reply.
134 *
135 * In the current set-up this happens to be the BSP monitor.
136 *
137 * \param err Error status of bind request.
138 */
139static void bind_monitor_reply(struct intermon_binding *closure,
140                               errval_t err)
141{
142    if (err_is_fail(err)) {
143        DEBUG_ERR(err, "Got error in bind monitor reply");
144    }
145    trace_event(TRACE_SUBSYS_MONITOR, TRACE_EVENT_MONITOR_BIND_MONITOR_REPLY, 0);
146    seen_connections++;
147}
148
149/* ---------------------- BIND_MONITOR_PROXY CODE START --------------------- */
150struct bind_monitor_proxy_state {
151    struct intermon_msg_queue_elem elem;
152    struct intermon_binding *orig_binding;
153    coreid_t dst_core_id;
154    intermon_caprep_t caprep;
155};
156
157static void bind_monitor_proxy(struct intermon_binding *b,
158                               coreid_t dst_core_id,
159                               intermon_caprep_t caprep);
160
161static void bind_monitor_proxy_cont(struct intermon_binding *b,
162                                    struct intermon_msg_queue_elem *e)
163{
164    struct bind_monitor_proxy_state *st = (struct bind_monitor_proxy_state*)e;
165    bind_monitor_proxy(st->orig_binding, st->dst_core_id, st->caprep);
166    free(st);
167}
168
169/**
170 * \brief A monitor asks this monitor to proxy
171 * its request to bind to another monitor
172 */
173static void bind_monitor_proxy(struct intermon_binding *b,
174                               coreid_t dst_core_id,
175                               intermon_caprep_t caprep)
176{
177    //printf("%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
178    errval_t err;
179
180    /* Get source monitor's core id */
181    coreid_t src_core_id = ((struct intermon_state *)b->st)->core_id;
182
183    /* Get destination monitor */
184    struct intermon_binding *dst_binding = NULL;
185    err = intermon_binding_get(dst_core_id, &dst_binding);
186    if (err_is_fail(err)) {
187        DEBUG_ERR(err, "intermon_binding_get failed");
188        printf("%s:%s:%d: my_core_id=%"PRIuCOREID" dst_core_id=%"PRIuCOREID"\n",
189               __FILE__, __FUNCTION__, __LINE__, my_core_id, dst_core_id);
190    }
191
192    // Proxy the request
193    err = dst_binding->tx_vtbl.
194        bind_monitor_request(dst_binding, NOP_CONT, src_core_id,
195                             caprep);
196    if (err_is_fail(err)) {
197        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
198            struct intermon_state *is = dst_binding->st;
199            struct bind_monitor_proxy_state *st =
200                malloc(sizeof(struct bind_monitor_proxy_state));
201            assert(st);
202
203            st->orig_binding = b;
204            st->elem.cont = bind_monitor_proxy_cont;
205            st->dst_core_id = dst_core_id;
206            st->caprep = caprep;
207
208            err = intermon_enqueue_send(dst_binding, &is->queue,
209                                        get_default_waitset(), &st->elem.queue);
210            assert(err_is_ok(err));
211
212        } else {
213            DEBUG_ERR(err, "forwarding bind request failed");
214        }
215    }
216}
217
218/* ---------------------- BIND_MONITOR_PROXY CODE END ----------------------- */
219
220/**
221 * \brief Notification of a newly booted monitor.
222 *  Setup our connection and request the sender to proxy
223 *  the bind request to the monitor
224 */
225static void new_monitor_notify(struct intermon_binding *st,
226                               coreid_t core_id)
227{
228    //printf("%s:%s:%d for core %"PRIuCOREID"\n",
229    //       __FILE__, __FUNCTION__, __LINE__, core_id);
230    errval_t err;
231
232    /* Setup the connection */
233    struct capref frame;
234    err = frame_alloc(&frame, MON_URPC_SIZE, NULL);
235    if (err_is_fail(err)) {
236        DEBUG_ERR(err, "frame_alloc failed");
237        return; // FIXME: cleanup
238    }
239
240    void *buf;
241    err = vspace_map_one_frame(&buf, MON_URPC_SIZE, frame, NULL, NULL);
242    if (err_is_fail(err)) {
243        DEBUG_ERR(err, "vspace_map_one_frame failed");
244        assert(buf); // XXX
245    }
246
247    // init our end of the binding and channel
248    struct intermon_ump_binding *ump_binding = malloc(sizeof(struct intermon_ump_binding));
249    assert(ump_binding != NULL);
250    err = intermon_ump_init(ump_binding, get_default_waitset(),
251                            buf, MON_URPC_CHANNEL_LEN,
252                            (char *)buf + MON_URPC_CHANNEL_LEN,
253                            MON_URPC_CHANNEL_LEN);
254    if (err_is_fail(err)) {
255        cap_destroy(frame);
256        USER_PANIC_ERR(err, "intermonitor channel initialization");
257        // TODO(gz): Recover from this
258    }
259
260    // Identify UMP frame for tracing
261    struct frame_identity umpid = { .base = 0, .bytes = 0 };
262    err = invoke_frame_identify(frame, &umpid);
263    assert(err_is_ok(err));
264    ump_binding->ump_state.chan.recvid = (uintptr_t)umpid.base;
265    ump_binding->ump_state.chan.sendid =
266        (uintptr_t)(umpid.base + MON_URPC_CHANNEL_LEN);
267
268    err = intermon_init(&ump_binding->b, core_id);
269    assert(err_is_ok(err));
270
271    /* Identify the frame cap */
272    struct capability frame_cap;
273    err = monitor_cap_identify(frame, &frame_cap);
274    if (err_is_fail(err)) {
275        DEBUG_ERR(err, "monitor_cap_identify failed");
276        return; // FIXME: cleanup
277    }
278
279    intermon_caprep_t caprep;
280    capability_to_caprep(&frame_cap, &caprep);
281
282    /* reply to the sending monitor to proxy request */
283    err = st->tx_vtbl.bind_monitor_proxy(st, NOP_CONT, core_id, caprep);
284    if (err_is_fail(err)) {
285        DEBUG_ERR(err, "bind proxy request failed");
286    }
287}
288
289errval_t arch_intermon_init(struct intermon_binding *b)
290{
291    b->rx_vtbl.bind_monitor_request = bind_monitor_request;
292    b->rx_vtbl.bind_monitor_reply = bind_monitor_reply;
293    b->rx_vtbl.bind_monitor_proxy = bind_monitor_proxy;
294    b->rx_vtbl.new_monitor_notify = new_monitor_notify;
295
296    return SYS_ERR_OK;
297}
298