1/** \file
2 *  \brief multi-hop interconnect driver test code
3 *
4 *  We send messages from the client to the server and the opposite way in order
5 *  to make sure that the multi-hop interconnect driver works for bidirectional
6 *  message passing.
7 */
8
9/*
10 * Copyright (c) 2010, ETH Zurich.
11 * All rights reserved.
12 *
13 * This file is distributed under the terms in the attached LICENSE file.
14 * If you do not find this file, copies can be found by writing to:
15 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
16 */
17
18#define _USE_XOPEN /* for strdup() */
19#include <string.h>
20#include <stdio.h>
21#include <barrelfish/barrelfish.h>
22#include <barrelfish/nameservice_client.h>
23#include <barrelfish/debug.h>
24#include <if/test_defs.h>
25
26static const char *my_service_name = "multihoptest";
27
28static const char *longstr =
29        ""
30                "Far out in the uncharted backwaters of the unfashionable end of the\n"
31                "western spiral arm of the Galaxy lies a small unregarded yellow sun.\n"
32                "\n"
33                "Orbiting this at a distance of roughly ninety-two million miles is an\n"
34                "utterly insignificant little blue green planet whose ape- descended life\n"
35                "forms are so amazingly primitive that they still think digital watches\n"
36                "are a pretty neat idea.\n"
37                "\n"
38                "This planet has - or rather had - a problem, which was this: most of the\n"
39                "people on it were unhappy for pretty much of the time. Many solutions\n"
40                "were suggested for this problem, but most of these were largely concerned\n"
41                "with the movements of small green pieces of paper, which is odd because\n"
42                "on the whole it wasn't the small green pieces of paper that were unhappy.\n"
43                "\n"
44                "And so the problem remained; lots of the people were mean, and most\n"
45                "of them were miserable, even the ones with digital watches.\n"
46                "\n"
47                "Many were increasingly of the opinion that they'd all made a big mistake\n"
48                "in coming down from the trees in the first place. And some said that\n"
49                "even the trees had been a bad move, and that no one should ever have\n"
50                "left the oceans.\n"
51                "\n"
52                "And then, one Thursday, nearly two thousand years after one man had\n"
53                "been nailed to a tree for saying how great it would be to be nice to people\n"
54                "for a change, one girl sitting on her own in a small cafe in Rickmansworth\n"
55                "suddenly realized what it was that had been going wrong all this time,\n"
56                "and she finally knew how the world could be made a good and happy\n"
57                "place. This time it was right, it would work, and no one would have to\n"
58                "get nailed to anything.\n"
59                "\n"
60                "Sadly, however, before she could get to a phone to tell anyone- about it,\n"
61                "a terribly stupid catastrophe occurred, and the idea was lost forever.\n"
62                "\n"
63                "This is her story.";
64
65static enum {
66    CLIENT, SERVER
67} role;
68
69static char* get_role_name(void)
70{
71    if (role == CLIENT) {
72        return "client";
73    } else if (role == SERVER) {
74        return "server";
75    } else {
76        USER_PANIC("unknown role");
77        return "";
78    }
79}
80
81/* ----------------------- SENDING MESSAGES ------------------------ */
82
83struct test_state {
84    struct test_binding *binding;
85    int nextmsg;
86    char *str;
87    struct capref cap1, cap2;
88};
89
90// send the next message in our sequence
91static void send_cont(void *arg)
92{
93    struct test_state *myst = arg;
94    struct test_binding *b = myst->binding;
95    struct event_closure txcont = MKCONT(send_cont, myst);
96    errval_t err;
97
98    printf("%s sending msg %d\n", get_role_name(), myst->nextmsg);
99
100    switch (myst->nextmsg) {
101    case 0:
102        err = test_basic__tx(b, txcont, 7);
103        break;
104
105    case 1:
106
107        // send a long string
108        err = test_str__tx(b, txcont, 42, longstr);
109        break;
110
111    case 2:
112        // create some caps to send (assume it all works)
113        err = frame_alloc(&myst->cap1, BASE_PAGE_SIZE, NULL);
114        assert(err_is_ok(err));
115
116        err = slot_alloc(&myst->cap2);
117        assert(err_is_ok(err));
118
119        err = vnode_create(myst->cap2, ObjType_VNode_x86_64_ptable);
120        assert(err_is_ok(err));
121
122        // XXX: why do we need to do this? -SG, 2015-06-29.
123        struct test_multihop_binding *mb = (struct test_multihop_binding *)b;
124        mb->capst.tx_capnum = 0;
125
126        err = test_caps__tx(b, txcont, 69, myst->cap1, myst->cap2);
127        break;
128
129    case 3:
130        // delete the Frame cap, now it's been sent, the page table cap is not
131        // movable, and if the sender (who's the owner) deletes it before the
132        // receiver is done with it's work, we run into odd situations.
133        // -SG 2016-11-08
134        err = cap_destroy(myst->cap1);
135        assert(err_is_ok(err));
136
137        // send a "buffer"
138        err = test_buf__tx(b, txcont, (uint8_t *) longstr, strlen(longstr));
139        break;
140
141    case 4:
142        // here is where we would deallocate the buffer, if it wasn't static
143        printf("%s all done!\n", get_role_name());
144
145        return;
146
147    default:
148        err = LIB_ERR_NOT_IMPLEMENTED; // TODO: Make meaningful
149        assert(!"shouldn't happen");
150    }
151
152    if (err_is_ok(err)) {
153        myst->nextmsg++;
154    } else {
155        DEBUG_ERR(err, "error sending message %d", myst->nextmsg);
156
157        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
158            assert(!"binding is busy for tx?!");
159
160            // this error should never happen to us, because we serialize all
161            // sends with the continuation, however it may happen in another
162            // situation if the binding is already busy sending. in this case,
163            // the user can use something like:
164
165            struct waitset *ws = get_default_waitset();
166            err = b->register_send(b, ws, txcont);
167            if (err_is_fail(err)) {
168                // note that only one continuation may be registered at a time
169                DEBUG_ERR(err, "register_send on binding failed!");
170            }
171        }
172
173        abort();
174    }
175}
176
177/* ------------------------ COMMON MESSAGE HANDLERS ------------------------ */
178
179static void rx_basic(struct test_binding *b, uint32_t arg)
180{
181
182    // make sure we received the correct argument(s)
183    if (arg != 7) {
184        USER_PANIC("received wrong argument in \"basic\" message!\n");
185    }
186    printf("%s rx_basic %"PRIu32"\n", get_role_name(), arg);
187}
188
189static void rx_str(struct test_binding *b, uint32_t arg, const char *s)
190{
191
192    // make sure we received the correct argument(s)
193    if (memcmp(s, longstr, strlen(longstr)) || (arg != 42)) {
194        USER_PANIC("received wrong argument in \"str\" message!\n");
195    }
196
197    printf("%s rx_str %"PRIu32" '%s'\n", get_role_name(), arg, s);
198}
199
200static void rx_caps(struct test_binding *b, uint32_t arg, struct capref cap1,
201        struct capref cap2)
202{
203    // make sure we received the correct argument(s)
204    if (arg != 69) {
205        USER_PANIC("received wrong argument in \"caps\" message!\n");
206    }
207
208    char buf1[256], buf2[256];
209    debug_print_cap_at_capref(buf1, sizeof(buf1), cap1);
210    debug_print_cap_at_capref(buf2, sizeof(buf2), cap2);
211    buf1[sizeof(buf1) - 1] = '\0';
212    buf2[sizeof(buf2) - 1] = '\0';
213    cap_destroy(cap1);
214    // Need to revoke and delete pagetable cap here, as otherwise it might get
215    // deleted before we get our hands on it! -SG,2016-11-08
216    cap_revoke(cap2);
217    cap_destroy(cap2);
218    printf("%s rx_caps %"PRIu32" [%s] [%s]\n", get_role_name(), arg, buf1, buf2);
219}
220
221static void rx_buf(struct test_binding *b, const uint8_t *buf, size_t buflen)
222{
223
224    // make sure we received the correct argument(s)
225    if (memcmp(buf, longstr, strlen(longstr)) || (buflen != strlen(longstr))) {
226        USER_PANIC("received wrong argument in \"buf\" message!\n");
227    }
228
229    printf("%s rx_buf (%zu bytes)\n", get_role_name(), buflen);
230
231    /**
232     * We exchange roles here: If we are the server, we send now messages to the client.
233     */
234    if (role == SERVER) {
235
236        printf(
237                "changing roles: server is now sending messages to the client...\n");
238        // construct local per-binding state
239        struct test_state *myst = malloc(sizeof(struct test_state));
240        assert(myst != NULL);
241        myst->nextmsg = 0;
242        myst->binding = b;
243        b->st = myst;
244
245        // start sending stuff to the client
246        send_cont(myst);
247
248    }
249}
250
251static struct test_rx_vtbl rx_vtbl = { .basic = rx_basic, .str = rx_str, .caps =
252        rx_caps, .buf = rx_buf, };
253
254/* ------------------------------ CLIENT ------------------------------ */
255
256static void bind_cb(void *st, errval_t err, struct test_binding *b)
257{
258    if (err_is_fail(err)) {
259        USER_PANIC_ERR(err, "bind failed");
260    }
261
262    printf("client bound!\n");
263
264    // copy my message receive handler vtable to the binding
265    b->rx_vtbl = rx_vtbl;
266
267    // construct local per-binding state
268    struct test_state *myst = malloc(sizeof(struct test_state));
269    assert(myst != NULL);
270    myst->nextmsg = 0;
271    myst->binding = b;
272    b->st = myst;
273
274    // start sending stuff to the service
275    send_cont(myst);
276}
277
278static void start_client(void)
279{
280    iref_t iref;
281    errval_t err;
282
283    printf("client looking up '%s' in name service...\n", my_service_name);
284    err = nameservice_blocking_lookup(my_service_name, &iref);
285    if (err_is_fail(err)) {
286        USER_PANIC_ERR(err, "nameservice_blocking_lookup failed");
287    }
288
289    printf("client binding to %"PRIuIREF"...\n", iref);
290    /**
291     * We don't use the flounder bind function here to
292     * enforce a binding over the multi-hop interconnect driver.
293     *
294     * This can generally also be achieved by passing the flag
295     * IDC_BIND_FLAG_MULTIHOP. But because founder has a fall back
296     * to UMP in case multi-hop does not work, we do not use the
297     * flag here.
298     *
299     */
300    struct test_multihop_binding *binding = malloc(
301            sizeof(struct test_multihop_binding));
302    assert((binding) != NULL);
303
304    err = test_multihop_bind(binding, iref, bind_cb, NULL,
305            get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
306    assert(err_is_ok(err));
307
308    if (err_is_fail(err)) {
309        USER_PANIC_ERR(err, "bind failed");
310    }
311}
312
313/* ------------------------------ SERVER ------------------------------ */
314
315static void export_cb(void *st, errval_t err, iref_t iref)
316{
317    if (err_is_fail(err)) {
318        USER_PANIC_ERR(err, "export failed");
319    }
320
321    printf("service exported at iref %"PRIuIREF"\n", iref);
322
323    // register this iref with the name service
324    err = nameservice_register(my_service_name, iref);
325    if (err_is_fail(err)) {
326        USER_PANIC_ERR(err, "nameservice_register failed");
327    }
328}
329
330static errval_t connect_cb(void *st, struct test_binding *b)
331{
332    printf("service got a connection!\n");
333
334    // copy my message receive handler vtable to the binding
335    b->rx_vtbl = rx_vtbl;
336
337    // accept the connection (we could return an error to refuse it)
338    return SYS_ERR_OK;
339}
340
341static void start_server(void)
342{
343    errval_t err;
344
345    err = test_export(NULL /* state pointer for connect/export callbacks */,
346            export_cb, connect_cb, get_default_waitset(),
347            IDC_EXPORT_FLAGS_DEFAULT);
348    if (err_is_fail(err)) {
349        USER_PANIC_ERR(err, "export failed");
350    }
351}
352
353/* ------------------------------ MAIN ------------------------------ */
354
355int main(int argc, char *argv[])
356{
357    errval_t err;
358
359    if (argc == 2 && strcmp(argv[1], "client") == 0) {
360        role = CLIENT;
361        start_client();
362    } else if (argc == 2 && strcmp(argv[1], "server") == 0) {
363        role = SERVER;
364        start_server();
365    } else {
366        printf("Usage: %s client|server\n", argv[0]);
367        return EXIT_FAILURE;
368    }
369
370    struct waitset *ws = get_default_waitset();
371    while (1) {
372        err = event_dispatch(ws);
373        if (err_is_fail(err)) {
374            DEBUG_ERR(err, "in event_dispatch");
375            break;
376        }
377    }
378
379    return EXIT_FAILURE;
380}
381