1/*
2 * Copyright (c) 2009, 2010, 2011, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <string.h>
11#include <stdio.h>
12#include <assert.h>
13#include <inttypes.h>
14#include <barrelfish/barrelfish.h>
15#include <barrelfish/bulk_transfer.h>
16#include <barrelfish/nameservice_client.h>
17#include <barrelfish/sys_debug.h>
18#include <if/bulkbench_defs.h>
19#include <bench/bench.h>
20
21#define MAXROUND        1000
22#define INIT_ROUNDS     10
23#define MINBUFSIZE      (1 << 16)
24#define MAXBUFSIZE      (1 << 24)
25#define BULKSIZE        65536
26
27#define BULK_PAGE_MAP VREGION_FLAGS_READ_WRITE
28
29static char buffer[BULKSIZE];
30static bool request_done = false;
31static struct bulkbench_binding *peer = NULL;
32static size_t block_size, bulk_inflight = 0;
33static struct bulk_transfer bt;
34static struct bulk_transfer_slave btr;
35static bool client = false, use_memcpy = false;
36static char *mode = "server";
37
38/* done by client */
39/* server sends the msg once he has created a cap and mapped it locally.
40 * Server passes that cap to client so that client can also mount it. */
41static void bulk_sys_init(struct bulkbench_binding *b, struct capref shared_mem)
42{
43    errval_t err;
44
45    // Map the frame in local memory
46    void *pool;
47    err = vspace_map_one_frame_attr(&pool, BULKSIZE, shared_mem,
48                                    BULK_PAGE_MAP, NULL, NULL);
49    assert(pool != NULL);
50    assert(err_is_ok(err));
51
52    // Init receiver
53    err = bulk_slave_init(pool, BULKSIZE, &btr);
54    assert(err_is_ok(err));
55    printf("%s: bulk_sys_init: done\n", mode);
56
57    err = peer->tx_vtbl.bulk_init_reply(peer, NOP_CONT);
58    assert(err_is_ok(err));
59}
60
61/* Done by server */
62static void bulk_init_reply(struct bulkbench_binding *b)
63{
64    assert(!request_done);
65    request_done = true;
66    printf("%s: bulk_init_reply: done\n", mode);
67
68}
69
70static void bulk_message_request(struct bulkbench_binding *b, uint64_t id,
71                                 uint64_t size, uint8_t last_fragment)
72{
73    void *buf = bulk_slave_buf_get_mem(&btr, id, NULL);
74    static int iter = 0;
75    static bool startup_round = true;
76    static uint64_t timestamp[MAXROUND];
77
78    printf("%s: bulk_message_request: started for id %"PRIu64"\n", mode, id);
79
80    assert(last_fragment);
81
82    if(use_memcpy) {
83        memcpy(buffer, buf, size);
84    }
85
86    timestamp[iter] = bench_tsc();
87    iter++;
88    if(iter == MAXROUND) {
89        iter = 0;
90        if(startup_round) {
91            startup_round = false;
92        } else {
93            for(int i = 1; i < MAXROUND; i++) {
94                uint64_t diff = timestamp[i] - timestamp[i - 1];
95                printf("rawresult %" PRIu64 "\n", diff);
96            }
97            printf("client done.\n");
98            abort();
99        }
100    }
101
102    printf("%s: bulk_message_request: almost done for id %"PRIu64", sending reply\n",
103					mode, id);
104
105    errval_t err =
106        peer->tx_vtbl.bulk_message_reply(peer, NOP_CONT, id, last_fragment);
107    assert(err_is_ok(err));
108    printf("%s: bulk_message_request: done for id %"PRIu64", with sending reply\n",
109					mode, id);
110
111}
112
113/* in server */
114static void bulk_message_reply(struct bulkbench_binding *b, uint64_t id,
115                               uint8_t last_fragment)
116{
117    errval_t err = bulk_free(&bt, id);
118    assert(err_is_ok(err));
119    bulk_inflight--;
120    printf("%s: bulk_msg_reply: %"PRIu64" done\n", mode, id);
121}
122
123static struct bulkbench_rx_vtbl bulkbench_vtbl = {
124    .bulk_init = bulk_sys_init,
125    .bulk_init_reply = bulk_init_reply,
126    .bulk_message_request = bulk_message_request,
127    .bulk_message_reply = bulk_message_reply
128};
129
130static void _listening(void *st, errval_t err, iref_t iref)
131{
132    assert(err_is_ok(err));
133
134    /* Register the service with the nameserver */
135    err = nameservice_register("bulkbench", iref);
136    if (err_is_fail(err)) {
137        DEBUG_ERR(err, "nameservice_register failed");
138        abort();
139    }
140    printf("%s: _listening:nameservice bulkbench registered\n", mode);
141}
142
143static errval_t _connected(void *st, struct bulkbench_binding *b)
144{
145    b->rx_vtbl = bulkbench_vtbl;
146    peer = b;
147    assert(!request_done);
148    request_done = true;
149    printf("%s: _connected: connection arrived\n", mode);
150    return SYS_ERR_OK;
151}
152
153static void client_connected(void *st, errval_t err,
154                             struct bulkbench_binding *b)
155{
156    assert(err_is_ok(err));
157    b->rx_vtbl = bulkbench_vtbl;
158    peer = b;
159    printf("%s: client_connected: done\n", mode);
160}
161
162/* in  server */
163static void send(char *buf, size_t size)
164{
165    errval_t err;
166
167    printf("%s: send: started\n", mode);
168    for(size_t i = 0; i < size; i += block_size) {
169        struct bulk_buf *bb;
170        /* get memory chunk from shared memory */
171        do {
172            bb = bulk_alloc(&bt);
173            if(bb == NULL) {
174                // dispatch one
175                event_dispatch(get_default_waitset());
176            }
177        } while(bb == NULL);
178
179        bulk_inflight++;
180        void *bbuf = bulk_buf_get_mem(bb);
181        size_t sendsize = i + block_size < size ? block_size : size - i;
182        bool last_fragment = i + block_size < size ? false : true;
183
184        memcpy(bbuf, buf, sendsize);
185        uintptr_t id = bulk_prepare_send(bb);
186
187    retry:
188        err = peer->tx_vtbl.bulk_message_request(peer, NOP_CONT, id, size,
189                                                 last_fragment);
190        if(err_is_fail(err)) {
191            if(err_no(err) == FLOUNDER_ERR_TX_BUSY) {
192                // Dispatch one
193                event_dispatch(get_default_waitset());
194                goto retry;
195            } else {
196                DEBUG_ERR(err, "Failure in send()");
197                abort();
198            }
199        }
200    }
201    printf("%s: send: done\n", mode);
202}
203
204int main(int argc, char *argv[])
205{
206    /* int round; */
207    /* size_t bufsize; */
208    errval_t err;
209
210    if(argc < 4) {
211        printf("Usage: %s blocksize send/recv memcpy/nomemcpy\n",
212               argv[0]);
213        exit(EXIT_FAILURE);
214    }
215
216    bench_init();
217
218    block_size = atoi(argv[1]);
219    assert(block_size <= BULKSIZE);
220    if(!strcmp(argv[2], "recv")) {
221        client = true;
222        mode = "client";
223    }
224    if(!strcmp(argv[3], "memcpy")) {
225        use_memcpy = true;
226    }
227
228    // Runs only on the peer
229    if(client) {
230        iref_t iref;
231        err = nameservice_blocking_lookup("bulkbench", &iref);
232        if (err_is_fail(err)) {
233            DEBUG_ERR(err, "nameservice_blocking_lookup failed");
234            abort();
235        }
236        assert(iref != 0);
237
238        err = bulkbench_bind(iref, client_connected, NULL, get_default_waitset(),
239                             IDC_BIND_FLAGS_DEFAULT);
240        assert(err_is_ok(err));
241
242        for(;;) {
243            event_dispatch(get_default_waitset());
244        }
245    }
246
247    // Export service
248    request_done = false;
249    err = bulkbench_export(NULL, _listening, _connected, get_default_waitset(),
250                           IDC_EXPORT_FLAGS_DEFAULT);
251    if (err_is_fail(err)) {
252        DEBUG_ERR(err, "rcce_export failed");
253        abort();
254    }
255    while (!request_done) {
256        event_dispatch(get_default_waitset());
257    }
258
259    // Init sender
260    struct capref frame;
261    err = bulk_create(BULKSIZE, block_size, &frame, &bt);
262    assert(err_is_ok(err));
263
264    err = peer->tx_vtbl.bulk_init(peer, NOP_CONT, frame);
265    assert(err_is_ok(err));
266
267    request_done = false;
268    while (!request_done) {
269        event_dispatch(get_default_waitset());
270    }
271
272    for(;;) {
273        send(buffer, block_size);
274    }
275
276    // Run the benchmark
277    /* for(bufsize = MINBUFSIZE; bufsize <= MAXBUFSIZE; bufsize *= 2) { */
278    /*     for (round = 0; round < MAXROUND; round++) { */
279            /* send(buffer, bufsize); */
280        /* } */
281
282        // Let the benchmark cool down
283    /*     while(bulk_inflight > 0) { */
284    /*         event_dispatch(get_default_waitset()); */
285    /*     } */
286    /* } */
287
288    printf("client done\n");
289    return 0;
290}
291