1/**
2 * \file
3 * \brief Microbenchmark to measure the cost of shared memory clock
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2009, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <string.h>
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/nameservice_client.h>
19#include <barrelfish/spawn_client.h>
20#include <bench/bench.h>
21#include <if/monitor_defs.h>
22#include <if/bench_defs.h>
23#include "clock.h"
24
25#define MAX_COUNT 1000
26
27static coreid_t my_core_id;
28static coreid_t num_cores;
29static struct capref clock_frame;
30static struct bench_binding *array[MAX_CPUS];
31static bool start_experiment_flag;
32
33struct timestamp {
34    cycles_t time0;
35    cycles_t time1;
36};
37
38static struct timestamp timestamps[MAX_COUNT];
39
40static void run_experiment(void)
41{
42    for (int i = 0; i < MAX_COUNT; i++) {
43        timestamps[i].time0 = bench_tsc();
44        clock_get_timestamp();
45        timestamps[i].time1 = bench_tsc();
46    }
47}
48
49static void shmc_start(struct bench_binding *b)
50{
51    run_experiment();
52    errval_t err;
53    err = b->tx_vtbl.shmc_done(b, NOP_CONT);
54    if (err_is_fail(err)) {
55        USER_PANIC_ERR(err, "sending shmc_done failed");
56    }
57}
58
59static bool experiment_flag;
60static int experiment_count;
61static void shmc_done(struct bench_binding *b)
62{
63    static int count = 0;
64
65    count++;
66    if (count == experiment_count) {
67        count = 0;
68        experiment_flag = true;
69    }
70}
71
72static void start_experiment(void)
73{
74    errval_t err;
75
76    for (int i = 1; i < num_cores; i++) {
77
78        int count = 0;
79        experiment_flag = false;
80        experiment_count = i;
81        for (int j = 0; j < MAX_CPUS; j++) {
82            if (array[j]) {
83                while(1) {
84                    err = array[j]->tx_vtbl.shmc_start(array[j], NOP_CONT);
85                    if (err_is_ok(err)) {
86                        break;
87                    } else if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
88                        messages_wait_and_handle_next();
89                    } else {
90                        USER_PANIC_ERR(err, "sending shmc_start failed");
91                    }
92                }
93                count++;
94                if (count == i) {
95                    break;
96                }
97            }
98        }
99        run_experiment();
100
101        printf("Running on %d cores\n", i + 1);
102        for (int j = 0; j < MAX_COUNT; j++) {
103            printf("page %d took %"PRIuCYCLES"\n", j,
104                   timestamps[j].time1 - timestamps[j].time0 - bench_tscoverhead());
105        }
106
107        while(!experiment_flag) {
108            messages_wait_and_handle_next();
109        }
110    }
111    printf("client done\n");
112}
113
114static void shmc_init_request(struct bench_binding *b, coreid_t id)
115{
116    array[id] = b;
117
118    errval_t err;
119    err = b->tx_vtbl.shmc_init_reply(b, NOP_CONT, clock_frame);
120    if (err_is_fail(err)) {
121        USER_PANIC_ERR(err, "sending shmc_init_reply failed");
122    }
123
124    static coreid_t count = 0;
125    count++;
126    if (count + 1 == num_cores) {
127        start_experiment_flag = true;
128    }
129}
130
131static void shmc_init_reply(struct bench_binding *b, struct capref cap)
132{
133    clock_frame = cap;
134    errval_t err = clock_init(cap);
135    if (err_is_fail(err)) {
136        USER_PANIC_ERR(err, "clock_init failed");
137    }
138}
139
140static struct bench_rx_vtbl rx_vtbl = {
141    .shmc_init_request = shmc_init_request,
142    .shmc_init_reply   = shmc_init_reply,
143    .shmc_start = shmc_start,
144    .shmc_done = shmc_done,
145};
146
147static void bind_cb(void *st, errval_t err, struct bench_binding *b)
148{
149    if (err_is_fail(err)) {
150        USER_PANIC_ERR(err, "bind failed");
151    }
152
153    // copy my message receive handler vtable to the binding
154    b->rx_vtbl = rx_vtbl;
155
156    b->tx_vtbl.shmc_init_request(b, NOP_CONT, my_core_id);
157    if (err_is_fail(err)) {
158        USER_PANIC_ERR(err, "sending shm_init_request failed");
159    }
160}
161
162static void export_cb(void *st, errval_t err, iref_t iref)
163{
164    if (err_is_fail(err)) {
165        USER_PANIC_ERR(err, "export failed");
166    }
167
168    // register this iref with the name service
169    err = nameservice_register("server", iref);
170    if (err_is_fail(err)) {
171        USER_PANIC_ERR(err, "nameservice_register failed");
172    }
173}
174
175static errval_t connect_cb(void *st, struct bench_binding *b)
176{
177    b->rx_vtbl = rx_vtbl;
178    return SYS_ERR_OK;
179}
180
181int main(int argc, char *argv[])
182{
183    errval_t err;
184    my_core_id = disp_get_core_id();
185    bench_init();
186
187    if (argc == 1) { /* server */
188        // Spawn client on another core
189        char *xargv[] = {"shared_mem_clock_bench", "dummy", NULL};
190        err = spawn_program_on_all_cores(false, xargv[0], xargv, NULL,
191                                         SPAWN_FLAGS_DEFAULT, NULL, &num_cores);
192        if (err_is_fail(err)) {
193            USER_PANIC_ERR(err, "error spawning on other cores");
194        }
195
196        // Export service
197        err = bench_export(NULL, export_cb, connect_cb, get_default_waitset(),
198                          IDC_EXPORT_FLAGS_DEFAULT);
199        if (err_is_fail(err)) {
200            USER_PANIC_ERR(err, "export failed");
201        }
202
203        // Allocate a cap for the shared memory
204        err = frame_alloc(&clock_frame, BASE_PAGE_SIZE, NULL);
205        if (err_is_fail(err)) {
206            USER_PANIC_ERR(err, "frame_alloc failed");
207        }
208        err = clock_init(clock_frame);
209        if (err_is_fail(err)) {
210            USER_PANIC_ERR(err, "clock_init failed");
211        }
212
213        // Wait for all connections to be established
214        start_experiment_flag = false;
215        while(!start_experiment_flag) {
216            messages_wait_and_handle_next();
217        }
218
219        // Start experiments
220        start_experiment();
221
222    } else { /* client */
223        // Lookup service
224        iref_t iref;
225        err = nameservice_blocking_lookup("server", &iref);
226        if (err_is_fail(err)) {
227            USER_PANIC_ERR(err, "nameservice_blocking_lookup failed");
228        }
229
230        // Bind to service
231        err = bench_bind(iref, bind_cb, NULL, get_default_waitset(),
232                         IDC_BIND_FLAGS_DEFAULT);
233        if (err_is_fail(err)) {
234            USER_PANIC_ERR(err, "bind failed");
235        }
236    }
237
238    messages_handler_loop();
239}
240