1/**
2 * \file
3 * \brief Benchmark revoke of remote copy of moveable cap
4 */
5
6/*
7 * Copyright (c) 2017, 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, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <barrelfish/barrelfish.h>
16#include <if/bench_distops_defs.h>
17
18#include <bitmacros.h>
19
20#include <bench/bench.h>
21#include <trace/trace.h>
22
23#include "benchapi.h"
24
25#define REVOKE_COPIES 10
26
27//{{{1 debugging helpers
28static void debug_capref(const char *prefix, struct capref cap)
29{
30    char buf[128];
31    debug_print_capref(buf, 128, cap);
32    printf("%s capref = %s\n", prefix, buf);
33}
34
35//{{{1 shared commands
36enum bench_cmd {
37    BENCH_CMD_CREATE_COPIES,
38    BENCH_CMD_COPIES_DONE,
39    BENCH_CMD_DO_ALLOC,
40    BENCH_CMD_DO_REVOKE,
41    BENCH_CMD_REVOKE_DONE,
42    BENCH_CMD_FORWARD_COPIES,
43    BENCH_CMD_COPIES_RX_DONE,
44    BENCH_CMD_PRINT_DONE,
45};
46
47//{{{1 shared helper functions
48static size_t get_mdb_size(void)
49{
50    errval_t err;
51    size_t cap_base_count = 0;
52    err = sys_debug_get_mdb_size(&cap_base_count);
53    assert(err_is_ok(err));
54    return cap_base_count;
55}
56
57
58//{{{1 Managment node: implement orchestration for benchmark
59
60//{{{2 Management node: state management
61
62struct global_state {
63    struct capref ram;
64    struct capref fwdcap;
65    coreid_t *nodes;
66    int nodes_seen;
67    int nodecount;
68    int copies_done;
69    int copycount;
70    int rx_seen;
71    int allocnode;
72    int revokenode;
73    int currcopies;
74};
75
76errval_t mgmt_init_benchmark(void **st, int nodecount)
77{
78    if (nodecount < 2) {
79        printf("Cannot execute revoke remote copy benchmark with less than 2 worker nodes!\n");
80        return ERR_INVALID_ARGS;
81    }
82    *st = calloc(1, sizeof(struct global_state));
83    if (!*st) {
84        return LIB_ERR_MALLOC_FAIL;
85    }
86    struct global_state *gs = *st;
87    gs->nodes = calloc(nodecount, sizeof(coreid_t));
88    gs->nodecount = nodecount;
89    gs->copies_done = 0;
90    gs->rx_seen = 0;
91    gs->allocnode = -1;
92    gs->revokenode = -1;
93    return ram_alloc(&gs->ram, BASE_PAGE_BITS);
94}
95
96static int sort_coreid(const void *a_, const void *b_)
97{
98    // deref pointers as coreids, store as ints
99    int a = *((coreid_t*)a_);
100    int b = *((coreid_t*)b_);
101    // subtract as ints
102    return a-b;
103}
104
105void mgmt_register_node(void *st, coreid_t nodeid)
106{
107    struct global_state *gs = st;
108    gs->nodes[gs->nodes_seen++] = nodeid;
109    // if we've seen all nodes, sort nodes array and configure printnode
110    if (gs->nodes_seen == gs->nodecount) {
111        qsort(gs->nodes, gs->nodecount, sizeof(coreid_t), sort_coreid);
112        gs->allocnode = gs->nodes[0];
113        gs->revokenode = gs->nodes[1];
114    }
115}
116
117struct mgmt_node_state {
118};
119
120errval_t mgmt_init_node(void **st)
121{
122     *st = malloc(sizeof(struct mgmt_node_state));
123     if (!*st) {
124         return LIB_ERR_MALLOC_FAIL;
125     }
126    return SYS_ERR_OK;
127}
128
129//{{{2 Management node: benchmark impl
130void mgmt_run_benchmark(void *st)
131{
132    struct global_state *gs = st;
133
134    printf("All clients sent hello! Benchmark starting...\n");
135
136    printf("# Benchmarking REVOKE REMOTE COPY: nodes=%d\n", gs->nodecount);
137
138    printf("# Starting out with %d copies, will by powers of 2 up to %d...\n",
139            NUM_COPIES_START, NUM_COPIES_END);
140
141    TRACE(CAPOPS, START, 0);
142
143    gs->currcopies = NUM_COPIES_START;
144    broadcast_caps(BENCH_CMD_CREATE_COPIES, NUM_COPIES_START, gs->ram);
145}
146
147void mgmt_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b)
148{
149    errval_t err;
150    struct global_state *gs = get_global_state(b);
151
152    switch(cmd) {
153        case BENCH_CMD_COPIES_DONE:
154            gs->copies_done++;
155            if (gs->copies_done == gs->nodecount) {
156                printf("# All copies made!\n");
157                unicast_cmd(gs->allocnode, BENCH_CMD_DO_ALLOC, ITERS);
158            }
159            break;
160        case BENCH_CMD_COPIES_RX_DONE:
161            gs->rx_seen++;
162            DEBUG("got BENCH_CMD_COPIES_RX_DONE: seen = %d, expected = %d\n",
163                    gs->rx_seen, gs->copycount);
164            if (gs->rx_seen == gs->copycount) {
165                DEBUG("# All nodes have copies of cap-to-del\n");
166                err = cap_destroy(gs->fwdcap);
167                assert(err_is_ok(err));
168                gs->rx_seen = 0;
169                unicast_cmd(gs->revokenode, BENCH_CMD_DO_REVOKE, ITERS);
170            }
171            break;
172        case BENCH_CMD_REVOKE_DONE:
173            unicast_cmd(gs->allocnode, BENCH_CMD_DO_ALLOC, 0);
174            break;
175        case BENCH_CMD_PRINT_DONE:
176            if (gs->currcopies == NUM_COPIES_END) {
177                printf("# Benchmark done!\n");
178                TRACE(CAPOPS, STOP, 0);
179                mgmt_trace_flush(NOP_CONT);
180                return;
181            }
182            printf("# Round done!\n");
183            printf("# mgmt node mdb size: %zu\n", get_mdb_size());
184            // Reset counters for next round
185            gs->currcopies *= 2;
186            gs->copies_done = 0;
187            gs->rx_seen = 0;
188            // Start new round
189            broadcast_cmd(BENCH_CMD_CREATE_COPIES, gs->currcopies);
190            break;
191        default:
192            printf("mgmt node got unknown command %d over binding %p\n", cmd, b);
193            break;
194    }
195}
196
197void mgmt_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1,
198                   struct bench_distops_binding *b)
199{
200    struct global_state *gs = get_global_state(b);
201    switch (cmd) {
202        case BENCH_CMD_FORWARD_COPIES:
203            {
204            // forward to all cores except gs->allocnode, which we set to
205            // gs->nodes[0].
206            coreid_t *cores = &gs->nodes[1];
207            gs->copycount = gs->nodecount-1;
208            gs->fwdcap = cap1;
209            multicast_caps(BENCH_CMD_FORWARD_COPIES, arg, cap1, cores, gs->copycount);
210            DEBUG("cmd_fwd_copies: multicast done\n");
211            break;
212            }
213        default:
214            printf("mgmt node got caps + command %"PRIu32", arg=%d over binding %p:\n",
215                    cmd, arg, b);
216            debug_capref("cap1:", cap1);
217            break;
218    }
219}
220
221//{{{1 Node
222
223struct node_state {
224    struct capref cap;
225    struct capref ram;
226    struct capref *copies;
227    int numcopies;
228    struct capref *ramcopies;
229    int numramcopies;
230    uint64_t *delcycles;
231    uint32_t benchcount;
232    uint32_t iter;
233    bool benchnode;
234};
235
236static coreid_t my_core_id = -1;
237
238void init_node(struct bench_distops_binding *b)
239{
240    printf("%s: binding = %p\n", __FUNCTION__, b);
241
242    my_core_id = disp_get_core_id();
243
244    bench_init();
245
246    // Allocate client state struct
247    b->st = malloc(sizeof(struct node_state));
248    assert(b->st);
249    if (!b->st) {
250        USER_PANIC("state malloc() in client");
251    }
252
253    struct node_state *ns = b->st;
254    ns->benchnode = false;
255    ns->ramcopies = NULL;
256    ns->numramcopies = 0;
257}
258
259static void node_create_copies(struct node_state *ns)
260{
261    errval_t err;
262    ns->copies = calloc(ns->numcopies, sizeof(struct capref));
263    for (int i = 0; i < ns->numcopies; i++) {
264        err = slot_alloc(&ns->copies[i]);
265        PANIC_IF_ERR(err, "slot_alloc for copy %d\n", i);
266        err = cap_copy(ns->copies[i], ns->ram);
267        PANIC_IF_ERR(err, "cap_copy for copy %d\n", i);
268    }
269}
270
271static void init_bench_round(struct node_state *ns)
272{
273    errval_t err;
274    printf("# node %d: initializing benchmarking metadata\n", my_core_id);
275    // First call only
276    ns->iter = 0;
277    ns->benchnode = true;
278    ns->delcycles = calloc(ns->benchcount, sizeof(uint64_t));
279    assert(ns->delcycles);
280    if (!ns->ramcopies) {
281        ns->ramcopies = calloc(REVOKE_COPIES, sizeof(struct capref));
282    }
283    assert(ns->ramcopies);
284    for (int c = 0; c < REVOKE_COPIES; c++) {
285        err = slot_alloc(&ns->ramcopies[c]);
286        assert(err_is_ok(err));
287    }
288}
289
290static void print_bench_round(struct node_state *ns, struct bench_distops_binding *b)
291{
292    errval_t err;
293    // Exit if we've done enough iterations
294    printf("# node %d: tsc_per_us = %ld; numcopies = %d\n",
295            my_core_id, bench_tsc_per_us(), ns->numcopies);
296    printf("# delete latency in cycles\n");
297    for (int i = 0; i < ns->benchcount; i++) {
298        printf("%ld\n", ns->delcycles[i]);
299    }
300    free(ns->delcycles);
301    err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_PRINT_DONE, 0);
302    assert(err_is_ok(err));
303}
304
305extern bool info;
306void node_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b)
307{
308    errval_t err;
309    struct node_state *ns = b->st;
310    assert(ns);
311
312    switch(cmd) {
313        case BENCH_CMD_CREATE_COPIES:
314            if (ns->copies) {
315                // Cleanup before next round
316                for (int i = 0; i < ns->numcopies; i++) {
317                    err = cap_destroy(ns->copies[i]);
318                    assert(err_is_ok(err));
319                }
320                free(ns->copies);
321            }
322            printf("# node %d: creating %d cap copies\n", my_core_id, arg);
323            ns->numcopies = arg;
324            node_create_copies(ns);
325            printf("# node %d: %zu capabilities on node\n", my_core_id, get_mdb_size());
326            err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 1);
327            PANIC_IF_ERR(err, "signaling cap_copy() done\n");
328            // Use create copies command as call for resetting benchmark
329            ns->benchcount = ITERS;
330            init_bench_round(ns);
331            break;
332        case BENCH_CMD_DO_REVOKE:
333            DEBUG("# node %d: revoking our copy for benchmark (mdb size %zu)\n",
334                    my_core_id, get_mdb_size());
335            uint64_t start, end;
336            start = bench_tsc();
337            TRACE(CAPOPS, USER_REVOKE_CALL, (ns->numcopies << 16) | ns->iter);
338            err = cap_revoke(ns->cap);
339            TRACE(CAPOPS, USER_REVOKE_RESP, (ns->numcopies << 16) | ns->iter);
340            end = bench_tsc();
341            ns->delcycles[ns->iter] = end - start;
342            assert(err_is_ok(err));
343            // Check that revoke went through correctly
344            struct capref slot;
345            err = slot_alloc(&slot);
346            assert(err_is_ok(err));
347            err = cap_retype(slot, ns->cap, 0, ObjType_RAM, BASE_PAGE_SIZE, 1);
348            PANIC_IF_ERR(err, "retype after revoke!");
349            err = cap_destroy(slot);
350            assert(err_is_ok(err));
351            // increase iteration counter
352            ns->iter ++;
353            // cleanup last cap copy
354            err = cap_destroy(ns->cap);
355            assert(err_is_ok(err));
356            ns->cap = NULL_CAP;
357            if (ns->iter == ITERS) {
358                // print results and send print_done if we've done enough
359                // iterations
360                print_bench_round(ns, b);
361                break;
362            }
363            // send revoke_done
364            err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_REVOKE_DONE, 0);
365            assert(err_is_ok(err));
366            break;
367        case BENCH_CMD_DO_ALLOC:
368            // We need to allocate a new ram cap every round, as the remote
369            // revoke will have deleted our original copy.
370            err = ram_alloc(&ns->cap, BASE_PAGE_BITS+2);
371            assert(err_is_ok(err));
372            DEBUG("# node %d: forwarding copies for benchmark (mdb size %zu)\n",
373                    my_core_id, get_mdb_size());
374            err = bench_distops_caps__tx(b, NOP_CONT, BENCH_CMD_FORWARD_COPIES, 10, ns->cap);
375            PANIC_IF_ERR(err, "fwd copies");
376            assert(err_is_ok(err));
377            break;
378        default:
379            printf("node %d got command %"PRIu32"\n", my_core_id, cmd);
380            break;
381    }
382}
383
384void node_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1,
385                   struct bench_distops_binding *b)
386{
387    errval_t err;
388    struct node_state *ns = b->st;
389
390    switch (cmd) {
391        case BENCH_CMD_CREATE_COPIES:
392            printf("# node %d: creating %d cap copies\n", my_core_id, arg);
393            ns->ram = cap1;
394            ns->numcopies = arg;
395            node_create_copies(ns);
396            printf("# node %d: %zu caps on node\n", my_core_id, get_mdb_size());
397            err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 0);
398            PANIC_IF_ERR(err, "signaling cap_copy() done\n");
399            // Use create copies command as call for resetting benchmark
400            ns->benchcount = ITERS;
401            init_bench_round(ns);
402            break;
403        case BENCH_CMD_FORWARD_COPIES:
404            {
405            bool descs = false;
406            if (capref_is_null(ns->cap)) {
407                // store copy in ns->cap so revoke knows where to look
408                ns->cap = cap1;
409            } else {
410                // We're core that allocated the cap
411                err = cap_destroy(cap1);
412                assert(err_is_ok(err));
413                descs = true;
414            }
415            // create array if not exists
416            if (!ns->ramcopies || ns->numramcopies != arg) {
417                if (ns->numramcopies != arg) {
418                    free(ns->ramcopies);
419                    ns->numramcopies = arg;
420                    ns->ramcopies = malloc(arg * sizeof(struct capref));
421                }
422                for (int i = 0; i < ns->numramcopies; i++) {
423                    err = slot_alloc(&ns->ramcopies[i]);
424                    assert(err_is_ok(err));
425                }
426            }
427            assert(ns->ramcopies);
428            DEBUG("# node %d: creating %d copies of cap to delete\n",
429                    my_core_id, ns->numramcopies);
430            for (int i = 0; i < ns->numramcopies; i++) {
431                if (descs && i < 4) {
432                    err = cap_retype(ns->ramcopies[i], ns->cap,
433                            i*BASE_PAGE_SIZE, ObjType_RAM, BASE_PAGE_SIZE, 1);
434                    if (err_is_fail(err)) {
435                        DEBUG_ERR(err, "[node %d] retyping c=%d",
436                                my_core_id, i);
437                    }
438                    assert(err_is_ok(err));
439                } else {
440                    err = cap_copy(ns->ramcopies[i], ns->cap);
441                    assert(err_is_ok(err));
442                }
443            }
444            err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_RX_DONE, 0);
445            assert(err_is_ok(err));
446            }
447            break;
448        default:
449            printf("node %d got caps + command %"PRIu32", arg=%d:\n",
450                my_core_id, cmd, arg);
451            debug_capref("cap1:", cap1);
452            break;
453    }
454}
455