1/**
2 * \file
3 * \brief Forwards bulk_transfer traffic
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdlib.h>
16
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/nameservice_client.h>
19
20#include <bulk_transfer/bulk_transfer.h>
21#include <bulk_transfer/bulk_sm.h>
22
23// ------------------------------------------------------------------------
24// Global State
25// ------------------------------------------------------------------------
26
27enum forward_state {
28    STARTUP,
29    BIND_BOUND,
30    EXPORT_BOUND,
31    RUNNING,
32    UNREACHABLE,
33    ERROR,
34};
35volatile enum forward_state state;
36
37enum bulk_trust_level fw_trust;
38
39const uint8_t       bind = 0;
40char                *bind_name;
41struct waitset      bind_waitset;
42struct bulk_channel bind_channel;
43
44const uint8_t       export = 1;
45char                *export_name;
46struct waitset      export_waitset;
47struct bulk_channel export_channel;
48
49/**
50 * Wait for given state and dispatch waitsets on the go.
51 */
52static void wait_state_or_error(enum forward_state cond)
53{
54    struct bulk_sm_ws_item ws_list[3];
55
56    ws_list[0].ws   = get_default_waitset();
57    ws_list[1].ws   = &bind_waitset;
58    ws_list[2].ws   = &export_waitset;
59    ws_list[0].next = &ws_list[1];
60    ws_list[1].next = &ws_list[2];
61    ws_list[2].next = NULL;
62
63    while (state != cond && state != ERROR) {
64        errval_t err = bulk_sm_multiple_event_dispatch(ws_list);
65        if (err_is_fail(err)) {
66            USER_PANIC_ERR(err, "wait_state: event_dispach");
67        }
68    }
69}
70
71/**
72 * Wait for boolean to become true.
73 */
74static void wait_cond(volatile bool *cond)
75{
76    struct bulk_sm_ws_item ws_list[3];
77
78    ws_list[0].ws   = get_default_waitset();
79    ws_list[1].ws   = &bind_waitset;
80    ws_list[2].ws   = &export_waitset;
81    ws_list[0].next = &ws_list[1];
82    ws_list[1].next = &ws_list[2];
83    ws_list[2].next = NULL;
84
85    while (!*cond) {
86        errval_t err = bulk_sm_multiple_event_dispatch(ws_list);
87        if (err_is_fail(err)) {
88            USER_PANIC_ERR(err, "wait_cond: event_dispach");
89        }
90    }
91}
92
93// ------------------------------------------------------------------------
94// Traffic Passthrough Helper Functions
95// ------------------------------------------------------------------------
96
97static inline void wait_enabled(void)
98{
99    if (state != RUNNING) {
100        wait_state_or_error(RUNNING);
101    }
102}
103
104static inline struct bulk_channel *other_channel(struct bulk_channel *channel)
105{
106    if (channel == &bind_channel) {
107        return &export_channel;
108    } else {
109        return &bind_channel;
110    }
111}
112
113/**
114 * Holds reply for future response.
115 */
116struct future_reply {
117    volatile bool valid;
118    errval_t      err;
119};
120
121#define REPLY_INIT ((struct future_reply) { false, SYS_ERR_OK })
122
123/**
124 * Fill struct future_reply with result.
125 */
126static void fill_reply_cont(void *arg, errval_t err,
127                            struct bulk_channel *channel)
128{
129    struct future_reply *r = arg;
130    r->err   = err;
131    r->valid = true;
132}
133
134/**
135 * Wait for a reply to be filled and return error code.
136 */
137static errval_t wait_reply(struct future_reply *r)
138{
139    wait_cond(&r->valid);
140    return r->err;
141}
142
143// ------------------------------------------------------------------------
144// Traffic Passthrough Callbacks
145// ------------------------------------------------------------------------
146
147/**
148 * The other endpoint requests to assign a new pool to this channel.
149 * @return If an error value is returned, the pool is not assigned and the
150 *         error code is sent to the other side (veto).
151 */
152static errval_t pool_assigned_fw(struct bulk_channel *channel,
153                                 struct bulk_pool    *pool)
154{
155    wait_enabled();
156
157    struct bulk_channel *other = other_channel(channel);
158
159    struct future_reply r = REPLY_INIT;
160    errval_t err =  bulk_channel_assign_pool(other, pool,
161            MK_BULK_CONT(fill_reply_cont, &r));
162
163    if (err_is_fail(err)) {
164        return err;
165    } else {
166        return wait_reply(&r);
167    }
168}
169
170/**
171 * The other endpoint wants to remove a pool from this channel
172 */
173static errval_t pool_removed_fw(struct bulk_channel *channel,
174                                struct bulk_pool    *pool)
175{
176    wait_enabled();
177
178    struct bulk_channel *other = other_channel(channel);
179
180    struct future_reply r = REPLY_INIT;
181    errval_t err =  bulk_channel_remove_pool(other, pool,
182            MK_BULK_CONT(fill_reply_cont, &r));
183
184    if (err_is_fail(err)) {
185        return err;
186    } else {
187        return wait_reply(&r);
188    }
189}
190
191/** Incoming moved buffer (sink) */
192static void move_received_fw(struct bulk_channel *channel,
193                             struct bulk_buffer  *buffer,
194                             void                *meta)
195{
196    wait_enabled();
197
198    struct bulk_channel *other = other_channel(channel);
199
200    struct future_reply r = REPLY_INIT;
201    errval_t err =  bulk_channel_move(other, buffer, meta,
202            MK_BULK_CONT(fill_reply_cont, &r));
203
204    if (err_is_ok(err)) {
205        wait_reply(&r);
206    }
207}
208
209/** Incoming passed buffer (source) */
210static void buffer_received_fw(struct bulk_channel *channel,
211                               struct bulk_buffer  *buffer,
212                               void                *meta)
213{
214    wait_enabled();
215
216    struct bulk_channel *other = other_channel(channel);
217
218    struct future_reply r = REPLY_INIT;
219    errval_t err =  bulk_channel_pass(other, buffer, meta,
220            MK_BULK_CONT(fill_reply_cont, &r));
221
222    if (err_is_ok(err)) {
223        wait_reply(&r);
224    }
225}
226
227/** Incoming copied buffer (sink) */
228static void copy_received_fw(struct bulk_channel *channel,
229                             struct bulk_buffer  *buffer,
230                             void                *meta)
231{
232    wait_enabled();
233
234    struct bulk_channel *other = other_channel(channel);
235
236    struct future_reply r = REPLY_INIT;
237    errval_t err =  bulk_channel_copy(other, buffer, meta,
238            MK_BULK_CONT(fill_reply_cont, &r));
239
240    if (err_is_ok(err)) {
241        wait_reply(&r);
242    }
243}
244
245/** Released copied buffer (source) */
246static void copy_released_fw(struct bulk_channel *channel,
247                             struct bulk_buffer  *buffer)
248{
249    wait_enabled();
250
251    struct bulk_channel *other = other_channel(channel);
252
253    struct future_reply r = REPLY_INIT;
254    errval_t err =  bulk_channel_release(other, buffer,
255            MK_BULK_CONT(fill_reply_cont, &r));
256
257    if (err_is_ok(err)) {
258        wait_reply(&r);
259    }
260}
261
262// ------------------------------------------------------------------------
263// Communication Setup
264// ------------------------------------------------------------------------
265
266static errval_t export_bound(struct bulk_channel *channel)
267{
268    assert(state == BIND_BOUND);
269    state = EXPORT_BOUND;
270    return SYS_ERR_OK;
271}
272
273static void bind_bound_cont(void *arg, errval_t err,
274                            struct bulk_channel *channel)
275{
276    assert(state == STARTUP);
277
278    if (err_is_ok(err)) {
279        state = BIND_BOUND;
280    } else {
281        DEBUG_ERR(err, "bind continuation");
282        state = ERROR;
283    }
284}
285
286struct bulk_channel_callbacks bind_callbacks = {
287    .bind_received     = NULL,
288    .teardown_received = NULL,
289    .pool_assigned     = pool_assigned_fw,
290    .pool_removed      = pool_removed_fw,
291    .move_received     = move_received_fw,
292    .buffer_received   = buffer_received_fw,
293    .copy_received     = copy_received_fw,
294    .copy_released     = copy_released_fw,
295};
296
297struct bulk_channel_callbacks export_callbacks = {
298    .bind_received     = export_bound,
299    .teardown_received = NULL,
300    .pool_assigned     = pool_assigned_fw,
301    .pool_removed      = pool_removed_fw,
302    .move_received     = move_received_fw,
303    .buffer_received   = buffer_received_fw,
304    .copy_received     = copy_received_fw,
305    .copy_released     = copy_released_fw,
306};
307
308/**
309 * This application takes two commandline arguments. It binds to the bulk
310 * transfer interface given as first argument. It exports a bulk transfer
311 * interface as given in the second argument. Traffic is mediated between
312 * the two ports.
313 */
314int main(int argc, char *argv[])
315{
316    if (argc != 4) {
317        debug_printf("bulk_transfer passthrough\n");
318        debug_printf("  Usage: %s bind export trust\n", argv[0]);
319        return EXIT_FAILURE;
320    }
321
322    bind_name   = argv[1];
323    export_name = argv[2];
324    fw_trust    = ((argv[3][0] == '0') ? BULK_TRUST_NONE : BULK_TRUST_FULL);
325
326    debug_printf("bulk_transfer passthrough: bind=%s, export=%s trust=%s\n",
327            bind_name, export_name,
328            (fw_trust==BULK_TRUST_NONE ? "NONE" : "FULL"));
329
330    errval_t err;
331
332    // Data Initializtaion ------------------------------------------------
333    state = STARTUP;
334    memset(&bind_channel, 0, sizeof(bind_channel));
335    memset(&export_channel, 0, sizeof(bind_channel));
336    waitset_init(&bind_waitset);
337    waitset_init(&export_waitset);
338
339    // Bind ---------------------------------------------------------------
340    debug_printf("Connecting to %s...\n", bind_name);
341
342    iref_t bind_iref;
343    err = nameservice_blocking_lookup(bind_name, &bind_iref);
344    if (err_is_fail(err)) {
345        USER_PANIC_ERR(err, "bind: nameservice_blocking_lookup");
346    }
347
348    struct bulk_sm_endpoint_descriptor bind_ep;
349    err = bulk_sm_ep_create_remote(&bind_ep, bind_iref);
350    if (err_is_fail(err)) {
351        USER_PANIC_ERR(err, "bind: bulk_sm_ep_create_remote");
352    }
353
354    struct bulk_channel_bind_params bind_params = {
355        .role    = BULK_ROLE_GENERIC,
356        .trust   = fw_trust,
357        .waitset = &bind_waitset
358    };
359    err = bulk_channel_bind(&bind_channel,
360                            (struct bulk_endpoint_descriptor*)&bind_ep,
361                            &bind_callbacks, &bind_params,
362                            MK_BULK_CONT(bind_bound_cont, NULL));
363    if (err_is_fail(err)) {
364        USER_PANIC_ERR(err, "bind: bulk_channel_bind");
365    }
366
367    wait_state_or_error(BIND_BOUND);
368    if (state == ERROR) {
369        debug_printf("Error during bind. Exiting.\n");
370        return EXIT_FAILURE;
371    }
372
373    debug_printf("Bound to %s.\n", bind_name);
374
375    // Export -------------------------------------------------------------
376    debug_printf("Exporting port to %s...\n", export_name);
377
378    struct bulk_sm_endpoint_descriptor export_ep;
379    err = bulk_sm_ep_create(&export_ep);
380    if (err_is_fail(err)) {
381        USER_PANIC_ERR(err, "export: bulk_sm_ep_create");
382    }
383
384    enum bulk_channel_direction export_direction = BULK_DIRECTION_TX;
385    if (bind_channel.direction == BULK_DIRECTION_TX) {
386        export_direction = BULK_DIRECTION_RX;
387    }
388
389    enum bulk_channel_role export_role = BULK_ROLE_MASTER;
390    if (bind_channel.role == BULK_ROLE_MASTER) {
391        export_role = BULK_ROLE_SLAVE;
392    }
393
394    struct bulk_channel_setup export_setup = {
395        .direction   = export_direction,
396        .role        = export_role,
397        .trust       = fw_trust,
398        .constraints = bind_channel.constraints,
399        .meta_size   = bind_channel.meta_size,
400        .waitset     = &export_waitset,
401        .user_state  = NULL
402    };
403
404    err = bulk_channel_create(&export_channel,
405                              (struct bulk_endpoint_descriptor*)&export_ep,
406                              &export_callbacks, &export_setup);
407    if (err_is_fail(err)) {
408        USER_PANIC_ERR(err, "export: bulk_sm_ep_create");
409    }
410
411    err = nameservice_register(export_name, export_ep.iref);
412    if (err_is_fail(err)) {
413        USER_PANIC_ERR(err, "export: nameservice_register");
414    }
415
416    debug_printf("Exported %s.\n", export_name);
417
418    // Wait for domain to connect on exported port ------------------------
419    debug_printf("Waiting for connection on %s...\n", export_name);
420
421    wait_state_or_error(EXPORT_BOUND);
422    if (state == ERROR) {
423        debug_printf("Error during export. Exiting.\n");
424        return EXIT_FAILURE;
425    }
426
427    debug_printf("Connected on %s.\n", export_name);
428
429    // Forward messages ---------------------------------------------------
430    debug_printf("Connection %s <-> %s established. Enabeling passthrough.\n",
431            bind_name, export_name);
432
433    assert(state == EXPORT_BOUND);
434    state = RUNNING;
435
436    wait_state_or_error(UNREACHABLE);
437
438    return EXIT_SUCCESS;
439}
440