1/*
2 * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdio.h>
11#include <stdint.h>
12#include <string.h>
13#include <barrelfish/barrelfish.h>
14#include <barrelfish/nameservice_client.h>
15#include <bench/bench.h>
16#include <if/bench_defs.h>
17
18// for cache benchmark
19#include <barrelfish/sys_debug.h>
20#include <arch/x86/barrelfish/perfmon.h>
21#include <arch/x86/barrelfish_kpi/perfmon_amd.h>
22
23static bool cache_benchmark;
24static bool start_benchmark_flag = false;
25static struct bench_binding *binding;
26static struct lmp_chan *chan;
27
28#define ITERATIONS      1000
29
30struct timestamps {
31    uint64_t time0;
32    uint64_t time1;
33};
34static struct timestamps timestamps[ITERATIONS];
35static struct timestamps overhead[ITERATIONS];
36static int currentiter;
37
38
39#ifndef __x86_64__
40#define rdpmc(x) bench_tsc()
41#endif
42
43// server side handler
44static void lrpc_bench_handler(void *arg)
45{
46    errval_t err;
47    uint64_t val;
48    if (cache_benchmark) {
49        val = rdpmc(0);
50    } else {
51        val = bench_tsc();
52    }
53
54    // try to pull a message out of the channel
55    struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
56    err = lmp_chan_recv(chan, &msg, NULL);
57    if (err_is_fail(err)) {
58        DEBUG_ERR(err, "endpoint poll failed");
59        printf("endpoint_poll failed\n");
60        abort();
61    }
62
63    // send back reply
64    struct bench_binding *b = arg;
65    err = b->tx_vtbl.lrpc_bench_reply(b, NOP_CONT, val);
66    if (err_is_fail(err)) {
67        DEBUG_ERR(err, "error sending back reply");
68        printf("lrpc_bench_reply failed\n");
69        abort();
70    }
71
72    // re-register
73    struct event_closure ec = {
74        .handler = lrpc_bench_handler,
75        .arg = b,
76    };
77    lmp_chan_register_recv(chan, get_default_waitset(), ec);
78}
79
80static void lrpc_init(struct bench_binding *b)
81{
82    binding = b;
83    chan = &((struct bench_lmp_binding *)b)->chan;
84}
85
86// client side handler
87static void lrpc_bench_reply_handler(struct bench_binding *b,
88                                     uint64_t value)
89{
90    timestamps[currentiter].time1 = value;
91}
92
93static void lrpc_init_reply(struct bench_binding *b)
94{
95    chan = &((struct bench_lmp_binding *)b)->chan;
96    start_benchmark_flag = true;
97}
98
99/// server and client rx vtbl
100static struct bench_rx_vtbl rx_vtbl = {
101    .lrpc_init        = lrpc_init,
102    .lrpc_init_reply  = lrpc_init_reply,
103    .lrpc_bench_reply = lrpc_bench_reply_handler,
104};
105
106/// Called on the client side when client connected to the server
107static void bind_cb(void *st, errval_t err, struct bench_binding *b)
108{
109    assert(err_is_ok(err));
110    b->rx_vtbl = rx_vtbl;
111
112    err = b->tx_vtbl.lrpc_init(b, NOP_CONT);
113    if (err_is_fail(err)) {
114        DEBUG_ERR(err, "lrpc_init_reply failed");
115        printf("lrpc_init_reply failed\n");
116        abort();
117    }
118}
119
120static void lrpc_benchmark(uint64_t event, uint64_t umask)
121{
122    errval_t err;
123
124    if (cache_benchmark) {
125        perfmon_setup(curdispatcher(), 0, event, umask, true);
126
127        /* Measure measurement overhead */
128        for (size_t i = 0; i < ITERATIONS; i++) {
129            overhead[i].time0 = rdpmc(0);
130            sys_debug_flush_cache();
131            overhead[i].time1 = rdpmc(0);
132        }
133    }
134
135    for(currentiter = 0; currentiter < ITERATIONS; currentiter++) {
136        // yield to receiver to make sure they are ready
137        thread_yield_dispatcher(chan->remote_cap);
138
139        if (cache_benchmark) {
140            timestamps[currentiter].time0 = rdpmc(0);
141            sys_debug_flush_cache();
142        } else {
143            timestamps[currentiter].time0 = bench_tsc();
144        }
145
146        err = lmp_ep_send0(chan->remote_cap, LMP_FLAG_SYNC, NULL_CAP);
147        if (err_is_fail(err)) {
148            DEBUG_ERR(err, "LRPC %d failed", currentiter);
149            printf("lmp_ep_send0 failed\n");
150            abort();
151        }
152        while (timestamps[currentiter].time1 == 0) {
153            messages_wait_and_handle_next();
154        }
155    }
156
157    /* Print results */
158    for (int i = 0; i < ITERATIONS; i++) {
159        printf("page %d took %"PRIu64"\n", i,
160                timestamps[i].time1 - bench_tscoverhead() -
161                timestamps[i].time0);
162    }
163}
164
165/// Called when servers setup their services
166static void export_cb(void *st, errval_t err, iref_t iref)
167{
168    assert(err_is_ok(err));
169    err = nameservice_register("lrpc_server", iref);
170    assert(err_is_ok(err));
171}
172
173/// Called when the client connects
174static errval_t connect_cb(void *st, struct bench_binding *b)
175{
176    b->rx_vtbl = rx_vtbl;
177    return SYS_ERR_OK;
178}
179
180int main(int argc, char *argv[])
181{
182    enum {CLIENT, SERVER} mode = -1;
183    errval_t err;
184
185    for (int i = 1; i < argc; i++) {
186        if (strcmp(argv[i], "client") == 0) {
187            mode = CLIENT;
188        } else if (strcmp(argv[i], "server") == 0) {
189            mode = SERVER;
190        } else if (strcmp(argv[i], "cache") == 0) {
191            cache_benchmark = true;
192        } else {
193            fprintf(stderr, "%s: unknown argument '%s'\n", argv[0], argv[i]);
194            return -1;
195        }
196    }
197
198    if (mode == -1) {
199        fprintf(stderr, "Usage: %s client|server [cache]\n", argv[0]);
200        return -1;
201    }
202
203    if (mode == SERVER) { /* Server */
204        /* Setup a server */
205        err = bench_export(NULL, export_cb, connect_cb, get_default_waitset(),
206                           IDC_EXPORT_FLAGS_DEFAULT);
207        if (err_is_fail(err)) {
208            DEBUG_ERR(err, "failed to setup a server");
209            exit(EXIT_FAILURE);
210        }
211
212        while (chan == NULL || binding == NULL) {
213            messages_wait_and_handle_next();
214        }
215
216        // cancel flounder-generated event loop and install our own
217        lmp_chan_deregister_recv(chan);
218        struct event_closure ec = {
219            .handler = lrpc_bench_handler,
220            .arg = binding,
221        };
222        lmp_chan_register_recv(chan, get_default_waitset(), ec);
223
224        err = binding->tx_vtbl.lrpc_init_reply(binding, NOP_CONT);
225        if (err_is_fail(err)) {
226            DEBUG_ERR(err, "lrpc_init_reply failed");
227            printf("lrpc_init_reply failed\n");
228            abort();
229        }
230
231        messages_handler_loop();
232    }
233
234    /* Client */
235    /* Initialize the benchmarking library */
236    bench_init();
237
238    /* Connect to the server */
239    iref_t serv_iref;
240    err = nameservice_blocking_lookup("lrpc_server", &serv_iref);
241    if (err_is_fail(err)) {
242        DEBUG_ERR(err, "failed to lookup server");
243        exit(EXIT_FAILURE);
244    }
245    assert(serv_iref != 0);
246
247    err = bench_bind(serv_iref, bind_cb, NULL, get_default_waitset(),
248                     IDC_BIND_FLAGS_DEFAULT);
249    if (err_is_fail(err)) {
250        DEBUG_ERR(err, "failed to connect to server");
251        exit(EXIT_FAILURE);
252    }
253
254    while (!start_benchmark_flag) {
255        messages_wait_and_handle_next();
256    }
257
258    printf("LRPC call benchmark:\n");
259
260    if (cache_benchmark) {
261        fprintf(stderr, "cache benchmark NYI, sorry");
262        return EXIT_FAILURE;
263
264        lrpc_benchmark(EVENT_AMD_DATA_CACHE_MISSES, 0);
265        lrpc_benchmark(EVENT_AMD_DATA_CACHE_LINES_EVICTED, UMASK_COUNT_ALL);
266        lrpc_benchmark(EVENT_AMD_INSTRUCTION_CACHE_MISSES, 0);
267        lrpc_benchmark(EVENT_AMD_L2_FILL_WRITEBACK, UMASK_AMD_L2_FILL_WRITEBACK_FILLS);
268    } else {
269        lrpc_benchmark(0, 0);
270    }
271
272    printf("End of benchmarks.\n");
273    return 0;
274}
275