1/**
2 * Tests cross core cap management
3 *
4 *
5 * Copyright (c) 2010, 2011, ETH Zurich.
6 * All rights reserved.
7 *
8 * This file is distributed under the terms in the attached LICENSE file.
9 * If you do not find this file, copies can be found by writing to:
10 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
11 */
12#include <barrelfish/barrelfish.h>
13#include <stdio.h>
14#include <if/xcorecapbench_defs.h>
15#include <barrelfish/nameservice_client.h>
16#include <barrelfish/spawn_client.h>
17#include <trace/trace.h>
18#include <string.h>
19#include <bench/bench.h>
20
21#define PARENT_BYTES  (1UL << 19)
22#define CHILD_BYTES   4096
23#define CAPS_PER_CORE (PARENT_BYTES / CHILD_BYTES)
24
25/* --- Globals ---*/
26
27static bool is_bsp;
28static bool exported;
29static bool connected;
30static bool connect;
31static bool start_sending_caps;
32static bool start_retyping_caps;
33static struct xcorecapbench_binding *bindings[MAX_CPUS];
34static coreid_t my_coreid;
35static coreid_t num_cores;
36static coreid_t wait_for;
37static struct capref my_caps[CAPS_PER_CORE];
38static struct capref retyped_caps[CAPS_PER_CORE];
39static cycles_t total_cycles;
40
41/* --- Binding handlers --- */
42
43static void connect_handler(struct xcorecapbench_binding *b);
44static void start_retyping_handler(struct xcorecapbench_binding *b);
45static void start_sending_handler(struct xcorecapbench_binding *b);
46static void send_cap_handler(struct xcorecapbench_binding *b,
47                             struct capref ram_cap);
48static void barrier_done_handler(struct xcorecapbench_binding *b, uint64_t cycles);
49
50struct xcorecapbench_rx_vtbl rx_vtbl = {
51    .start_retyping= start_retyping_handler,
52    .start_sending = start_sending_handler,
53    .send_cap      = send_cap_handler,
54    .barrier_done  = barrier_done_handler,
55    .connect       = connect_handler,
56};
57
58
59static void create_caps(void)
60{
61    errval_t err;
62
63    // allocate a bunch of ramcaps
64    for (int i=0; i<CAPS_PER_CORE; i++) {
65        err = ram_alloc(&my_caps[i], log2ceil(CHILD_BYTES));
66        if (err_is_fail(err)) {
67            DEBUG_ERR(err, "xcorecap: RAM alloc failed\n");
68            abort();
69        }
70        err = slot_alloc(&retyped_caps[i]);
71        if (err_is_fail(err)) {
72            DEBUG_ERR(err, "slot alloc err\n");
73            abort();
74        }
75    }
76    printf("core %i caps created\n", my_coreid);
77}
78
79static inline bool redo_message(errval_t err) {
80    if (err == FLOUNDER_ERR_TX_BUSY) {
81        messages_wait_and_handle_next();
82        return true;
83    } else {
84        return false;
85    }
86}
87
88static cycles_t send_caps(void)
89{
90    errval_t err;
91    cycles_t time_taken = 0;
92
93    srand(bench_tsc());  // random starting seed
94
95    for (int i=0; i<CAPS_PER_CORE; i++) {
96        coreid_t to_core;
97        do {
98            to_core = rand() % num_cores;
99        } while(to_core == my_coreid);
100
101        do {
102            cycles_t start =  bench_tsc();
103            err = bindings[to_core]->tx_vtbl.send_cap(
104                       bindings[to_core], NOP_CONT, my_caps[i]);
105            if (i >= 20 && i <= (CAPS_PER_CORE - 20)) { // avoid warmup / cooldown
106                time_taken += (bench_tsc() - start);
107            }
108        } while(redo_message(err));
109
110        if (err_is_fail(err)) {
111            DEBUG_ERR(err, "xcorecap: cap send failed\n");
112            abort();
113        }
114    }
115
116    return time_taken / (CAPS_PER_CORE - 40);
117}
118
119static cycles_t retype_caps(void)
120{
121    errval_t err;
122    cycles_t time_taken = 0;
123    for (int i=0; i<CAPS_PER_CORE; i++) {
124        cycles_t start =  bench_tsc();
125        err = cap_retype(retyped_caps[i], my_caps[i], 0, ObjType_Frame, CHILD_BYTES, 1);
126        if (i >= 20 && i <= (CAPS_PER_CORE - 20)) { // avoid warmup / cooldown
127            time_taken += (bench_tsc() - start);
128        }
129        if (err_is_fail(err)) {
130            DEBUG_ERR(err, "xcorecap: Retype to frame failed\n");
131        }
132    }
133
134    return time_taken / (CAPS_PER_CORE - 40);
135}
136
137static void destroy_caps(void)
138{
139    errval_t err;
140    for (int i=0; i<CAPS_PER_CORE; i++) {
141        err = cap_destroy(retyped_caps[i]);
142        err = cap_revoke(my_caps[i]);
143        if (err_is_fail(err)) {
144            DEBUG_ERR(err, "xcorecap: Retype to frame failed\n");
145        }
146    }
147}
148
149static void start_sending_handler(struct xcorecapbench_binding *b)
150{
151    start_sending_caps = true;
152}
153
154static void start_retyping_handler(struct xcorecapbench_binding *b)
155{
156    start_retyping_caps = true;
157}
158
159static void connect_handler(struct xcorecapbench_binding *b)
160{
161    connect = true;
162}
163static void barrier_done_handler(struct xcorecapbench_binding *b,
164                                 uint64_t cycles)
165{
166    total_cycles += (cycles_t)cycles;
167    assert(wait_for != 0);
168    wait_for--;
169}
170
171static void send_cap_handler(struct xcorecapbench_binding *b,
172                             struct capref ram_cap)
173{
174    // don't do anything
175}
176
177/* ------------------------- Domain spawning code -------------------------- */
178
179static errval_t spawn_other_cores(int argc, char *argv[]) {
180    errval_t err;
181
182    char core_id_char[32];
183    snprintf(core_id_char, sizeof(core_id_char), "%d", num_cores);
184    core_id_char[sizeof(core_id_char) - 1] = '\0';
185
186    char *xargv[] = {argv[0], "client", core_id_char, NULL};
187
188    for (int i=1; i<num_cores; i++) {
189        /* XXX: assumes core IDs are 0-based and contiguous */
190        err = spawn_program(i, xargv[0], xargv, NULL,
191                            SPAWN_FLAGS_DEFAULT, NULL);
192        if (err_is_fail(err)) {
193            DEBUG_ERR(err, "error spawning other core");
194            abort();
195        }
196    }
197
198    return SYS_ERR_OK;
199}
200
201
202/* ---------------------------- Binding Setup Callbacks ------------------- */
203
204
205static inline void get_service_name(char * service_name, size_t size,
206                                    coreid_t coreid)
207{
208    memset(service_name, 0, size);
209    strncpy(service_name, "xcorecapbench_",
210            sizeof("xcorecapbench_"));
211    char local_id[32];
212    snprintf(local_id, sizeof(local_id), "%d",
213             coreid);
214    strcat(service_name, local_id);
215}
216
217
218static errval_t connect_cb(void *st, struct xcorecapbench_binding *b)
219{
220    // copy my message receive handler vtable to the binding
221    b->rx_vtbl = rx_vtbl;
222
223    // accept the connection
224    return SYS_ERR_OK;
225}
226
227static void export_cb(void *st, errval_t err, iref_t iref)
228{
229    if (err_is_fail(err)) {
230        DEBUG_ERR(err, "export failed");
231        abort();
232    }
233
234    // register this iref with the name service
235    char my_service_name[128];
236    get_service_name(my_service_name, 128, disp_get_core_id());
237    err = nameservice_register(my_service_name, iref);
238    if (err_is_fail(err)) {
239        DEBUG_ERR(err, "nameservice_register failed");
240        abort();
241    }
242    exported = true;
243}
244
245static void bind_cb(void *st, errval_t err, struct xcorecapbench_binding *b)
246{
247    assert(err_is_ok(err));
248
249    b->rx_vtbl = rx_vtbl;
250
251    coreid_t core = (coreid_t)(uintptr_t) st;
252    bindings[core] = b;
253
254    // check if we are finished
255    for (int i=0; i<num_cores; i++) {
256        if (i != my_coreid && bindings[i] == NULL) {
257            return;  // not finished yet
258        }
259    }
260    connected = true;
261}
262
263static void bind_core(coreid_t core) {
264    errval_t err;
265    iref_t iref;
266    char core_service_name[128];
267    get_service_name(core_service_name, 128, core);
268    err = nameservice_blocking_lookup(core_service_name, &iref);
269    if (err_is_fail(err)) {
270        fprintf(stderr, "could not connect to the xcorecapbench.\n"
271                "Terminating.\n");
272        abort();
273    }
274    err = xcorecapbench_bind(iref, bind_cb, (void*)(uintptr_t)core,
275                             get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
276    if (err_is_fail(err)) {
277        DEBUG_ERR(err, "bind failed");
278        abort();
279    }
280}
281
282/* ----- Experiment ------- */
283static void do_experiment_bsp(void)
284{
285    errval_t err;
286
287    printf("Sending Caps\n");
288    wait_for = num_cores;
289    total_cycles = 0;
290
291    for (int i=1; i<num_cores; i++) {
292        do {
293            err = bindings[i]->tx_vtbl.start_sending(bindings[i], NOP_CONT);
294        } while (redo_message(err));
295
296        assert(err_is_ok(err));
297    }
298    total_cycles += send_caps();
299    wait_for--;
300
301    // wait for other cores to finish
302    while(wait_for) {
303        messages_wait_and_handle_next();
304    }
305
306    printf("Retyping Caps\n");
307    for(int iter=0; iter<10; iter++) {
308        wait_for = num_cores;
309        total_cycles = 0;
310
311        for (int i=1; i<num_cores; i++) {
312            do {
313                err = bindings[i]->tx_vtbl.start_retyping(bindings[i], NOP_CONT);
314            } while (redo_message(err));
315
316            assert(err_is_ok(err));
317        }
318        total_cycles += retype_caps();
319        wait_for--;
320
321        // wait for other cores to finish
322        while(wait_for) {
323            messages_wait_and_handle_next();
324        }
325
326        destroy_caps();
327
328        printf("%" PRIuCYCLES "\n", total_cycles / num_cores);
329    }
330    printf("Done\n");
331}
332
333
334static void do_experiment_client(void)
335{
336    errval_t err;
337    cycles_t cycles;
338
339    while(!start_sending_caps) {
340        messages_wait_and_handle_next();
341    }
342    cycles = send_caps();
343    do {
344        err = bindings[0]->tx_vtbl.barrier_done(bindings[0], NOP_CONT, cycles);
345    } while (redo_message(err));
346    assert(err_is_ok(err));
347
348    while (true) {
349        start_retyping_caps = false;
350        while(!start_retyping_caps) {
351            messages_wait_and_handle_next();
352        }
353        cycles = retype_caps();
354
355        destroy_caps();
356        do {
357            err = bindings[0]->tx_vtbl.barrier_done(bindings[0], NOP_CONT, cycles);
358        } while (redo_message(err));
359        assert(err_is_ok(err));
360    }
361}
362/* ------------------------------------ MAIN ----------------------------- */
363
364#define NUM_RAM_CAPS 100
365static struct capref ram_caps[NUM_RAM_CAPS];
366
367static errval_t my_ram_alloc(struct capref *ret, uint8_t size_bits,
368                             uint64_t minbase, uint64_t maxlimit)
369{
370    if (size_bits != BASE_PAGE_BITS) {
371        printf("%d I cannot allocate this frame %d %d\n", my_coreid, size_bits, BASE_PAGE_BITS);
372       abort();
373    }
374    static int i = 0;
375    if (i == NUM_RAM_CAPS) {
376        printf("%d, out of ram caps\n", my_coreid);
377        abort();
378    }
379    *ret = ram_caps[i++];
380    return SYS_ERR_OK;
381}
382
383static void ram_hack(void)
384{
385    errval_t err;
386
387    for (int i = 0; i < 100; i++) {
388        err = ram_alloc(&ram_caps[i], BASE_PAGE_BITS);
389        assert(err_is_ok(err));
390    }
391
392    err = ram_alloc_set(my_ram_alloc);
393    assert(err_is_ok(err));
394}
395
396int main (int argc, char* argv[])
397{
398    errval_t err;
399    my_coreid = disp_get_core_id();
400
401    exported  = false;
402    connected = false;
403    start_sending_caps  = false;
404    start_retyping_caps = false;
405
406    // munge up a bunch of caps
407    create_caps();
408
409    assert (argc >= 2);
410    if (!strncmp(argv[1], "client", sizeof("client"))) {
411        is_bsp = false;
412        assert (argc >= 3);
413        num_cores = atoi(argv[2]);
414        ram_hack();
415    } else {
416        is_bsp = true;
417        num_cores = atoi(argv[1]);
418    }
419
420    // export our binding
421    exported = false;
422    err = xcorecapbench_export(NULL, export_cb, connect_cb,
423                               get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
424    while(!exported) {
425        messages_wait_and_handle_next();
426    }
427
428    if (is_bsp) {
429        wait_for = num_cores;
430        // spawn other cores
431        printf("Core %d starting xcorecapbench on %i cores\n", my_coreid,
432               num_cores);
433        assert(disp_get_core_id() == 0);
434        spawn_other_cores(argc, argv);
435    } else {
436        printf("Starting xcorecapbench on core %i \n", my_coreid);
437    }
438
439    // connect to other cores
440    connected = false;
441    for(int i=0; i<num_cores; i++) {
442        if (i != my_coreid) {
443            bind_core(i);
444        }
445    }
446    while(!connected) {
447        messages_wait_and_handle_next();
448    }
449
450    if (is_bsp) {
451        wait_for--;
452        // wait for cores to connect
453        while(wait_for) {
454            messages_wait_and_handle_next();
455        }
456    } else {
457        err = bindings[0]->tx_vtbl.barrier_done(bindings[0], NOP_CONT, 0);
458        assert(err_is_ok(err));
459    }
460
461    if (is_bsp) {
462        do_experiment_bsp();
463    } else {
464        do_experiment_client();
465    }
466
467    messages_handler_loop();
468
469    return 0;
470}
471
472