1/**
2 * \file
3 * \brief
4 */
5
6/*
7 * Copyright (c) 2010, ETH Zurich and Mircosoft Corporation.
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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <ctype.h>
16#include "internal.h"
17#include <barrelfish/nameservice_client.h>
18
19struct bind_state {
20    coreid_t id;
21};
22
23struct coreset *set;
24struct relations *rel;
25static callback retcb;
26
27static void done_reply(struct boot_perfmon_binding *b)
28{
29    retcb();
30}
31
32static errval_t done_msging(void *st, coreid_t id,
33                            struct boot_perfmon_binding *b)
34{
35    return b->tx_vtbl.done_reply(b, NOP_CONT);
36}
37
38static void done_request(struct boot_perfmon_binding *b)
39{
40    errval_t err;
41    static coreid_t count = 0;
42    count++;
43
44    if (count == (coreset_count(set) - 1)) {
45        err = relations_iterate(rel, NULL, done_msging);
46        if (err_is_fail(err)) {
47            USER_PANIC_ERR(err, "relations_iterate failed");
48        }
49
50        retcb();
51    }
52}
53
54static void init(struct boot_perfmon_binding *b, coreid_t id)
55{
56    errval_t err;
57
58    err = relations_add(rel, id, b);
59    if (err_is_fail(err)) {
60        USER_PANIC_ERR(err, "relations_add failed");
61    }
62
63    // Check if done connecting
64    if (relations_count(rel) == (coreset_count(set) - 1)) {
65
66        if (!check_leader()) { // Message leader that connect is done
67            struct boot_perfmon_binding *lb;
68            err = relations_get(rel, get_leader_id(), &lb);
69            if (err_is_fail(err)) {
70                USER_PANIC_ERR(err, "relations_get failed");
71            }
72            err = lb->tx_vtbl.done_request(lb, NOP_CONT);
73            if (err_is_fail(err)) {
74                USER_PANIC_ERR(err, "sending done failed");
75            }
76        }
77    }
78}
79
80static struct boot_perfmon_rx_vtbl vtbl = {
81    .init = init,
82    .done_reply = done_reply,
83    .done_request = done_request,
84    .ping = ping,
85    .pong = pong,
86    .exit = exit_msg,
87};
88
89static void bind_cb(void *st, errval_t err, struct boot_perfmon_binding *b)
90{
91    if (err_is_fail(err)) {
92        USER_PANIC_ERR(err, "failure in bind_cb");
93    }
94    b->rx_vtbl = vtbl;
95
96    struct bind_state *bs = st;
97    err = relations_add(rel, bs->id, b);
98    if (err_is_fail(err)) {
99        USER_PANIC_ERR(err, "relations_add failed");
100    }
101    free(bs);
102
103    err = b->tx_vtbl.init(b, NOP_CONT, my_core_id);
104    if (err_is_fail(err)) {
105        USER_PANIC_ERR(err, "sending init failed");
106    }
107
108    // Check if done connecting
109    if (relations_count(rel) == (coreset_count(set) - 1)) {
110
111        if (!check_leader()) { // Message leader that connect is done
112            struct boot_perfmon_binding *lb;
113            err = relations_get(rel, get_leader_id(), &lb);
114            if (err_is_fail(err)) {
115                USER_PANIC_ERR(err, "relations_get failed");
116            }
117            err = lb->tx_vtbl.done_request(lb, NOP_CONT);
118            if (err_is_fail(err)) {
119                USER_PANIC_ERR(err, "sending done failed");
120            }
121        }
122    }
123}
124
125/**
126 * \brief Connect to every core with id bigger than mine
127 */
128static errval_t iter_connect(void *st, coreid_t id)
129{
130    errval_t err;
131
132    if (my_core_id > id) {
133        iref_t iref;
134        char name[128];
135        snprintf(name, 128, "boot_perfmon:%d", id);
136        err = nameservice_blocking_lookup(name, &iref);
137        if (err_is_fail(err)) {
138            DEBUG_ERR(err, "nameservice_blocking_lookup failed");
139            abort();
140        }
141
142        struct bind_state *bs = malloc(sizeof(struct bind_state));
143        if (!bs) {
144            USER_PANIC("malloc failed");
145        }
146        bs->id = id;
147        err = boot_perfmon_bind(iref, bind_cb, bs, get_default_waitset(),
148                                IDC_BIND_FLAGS_DEFAULT);
149        if (err_is_fail(err)) {
150            USER_PANIC_ERR(err, "boot_perfmon_bind failed");
151        }
152    }
153
154    return SYS_ERR_OK;
155}
156
157static void export_cb(void *st, errval_t err, iref_t iref)
158{
159    if (err_is_fail(err)) {
160        USER_PANIC_ERR(err, "export failed");
161    }
162
163    char name[128];
164    snprintf(name, 128, "boot_perfmon:%d", my_core_id);
165    err = nameservice_register(name, iref);
166    if (err_is_fail(err)) {
167        USER_PANIC_ERR(err, "nameservice_register failed");
168    }
169}
170
171static errval_t connect_cb(void *st, struct boot_perfmon_binding *b)
172{
173    b->rx_vtbl = vtbl;
174    return SYS_ERR_OK;
175}
176
177/**
178 * \brief Find and add all coreids to coreset
179 */
180static void set_skb_present(char *str)
181{
182    errval_t err;
183
184    while (*str != '\0') {
185        if (!isdigit((int)*str)) {
186            str++;
187            continue;
188        }
189
190        coreid_t id = strtol(str, &str, 10);
191        err = coreset_add(set, id);
192        if (err_is_fail(err)) {
193            USER_PANIC_ERR(err, "coreset_add failed");
194        }
195    }
196}
197
198errval_t connect(callback cb)
199{
200    errval_t err;
201    retcb = cb;
202
203    /* Initialize the coreset */
204    err = coreset_new(&set);
205    if (err_is_fail(err)) {
206        return err_push(err, LIB_ERR_CORESET_NEW);
207    }
208
209    /* Initialize the relations */
210    err = relations_new(&rel);
211    if (err_is_fail(err)) {
212        USER_PANIC_ERR(err, "export failed");
213    }
214
215    /* Get the list of coreids in the system and add them to the coreset */
216    char *result, *str_err;
217    int32_t int_err;
218    err = skb_evaluate("get_core_id_list(L),write(L).",
219                       &result, &str_err, &int_err);
220    if (err_is_fail(err)) {
221        return err_push(err, SKB_ERR_EVALUATE);
222    }
223    set_skb_present(result);
224    free(result);
225    free(str_err);
226
227    /* Export service */
228    err = boot_perfmon_export(NULL, export_cb, connect_cb,
229                              get_default_waitset(),
230                              IDC_EXPORT_FLAGS_DEFAULT);
231    if (err_is_fail(err)) {
232        USER_PANIC_ERR(err, "export failed");
233    }
234
235    /* Connect */
236    err = coreset_iterate(set, NULL, iter_connect);
237    if (err_is_fail(err)) {
238        USER_PANIC_ERR(err, "coreset_add failed");
239    }
240
241    return SYS_ERR_OK;
242}
243