1/** \file
2 *  \brief IDC system test code
3 */
4
5/*
6 * Copyright (c) 2010, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#define _USE_XOPEN /* for strdup() */
15#include <string.h>
16#include <stdio.h>
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/nameservice_client.h>
19#include <barrelfish/debug.h>
20#include <if/bench_defs.h>
21#include <if/bench_thc.h>
22#include <thc/thc.h>
23#include <bench/bench.h>
24
25/* ------------------------------ COMMON ------------------------------ */
26
27// Real
28#define ITERATIONS 10000
29#define SKIP        9000
30
31// Simulation
32//#define ITERATIONS 100
33//#define SKIP        90
34
35#define NUM_CONFIG    13
36#define NUM_REP        1
37
38#define CONFIG_IS_THC_CLIENT(_x)      ((_x)&1)
39#define CONFIG_IS_FLOUNDER_CLIENT(_x) (!((_x)&1))
40#define CONFIG_IS_THC_SERVER(_x)      ((_x)&2)
41#define CONFIG_IS_FLOUNDER_SERVER(_x) (!((_x)&2))
42#define CONFIG_IS_SEQUENTIAL1(_x)     (((_x)&12) == 0)
43#define CONFIG_IS_SEQUENTIAL2(_x)     (((_x)&12) == 4)
44#define CONFIG_IS_PIPELINED2(_x)      (((_x)&12) == 8)
45#define CONFIG_IS_PIPELINED1(_x)      (((_x)&12) == 12)
46
47#define CONFIG_IS_PIPELINED(_x) (CONFIG_IS_PIPELINED1(_x) || CONFIG_IS_PIPELINED2(_x))
48#define CONFIG_IS_1CHAN(_x)     (CONFIG_IS_SEQUENTIAL1(_x) || CONFIG_IS_PIPELINED1(_x))
49#define CONFIG_IS_2CHAN(_x)     (CONFIG_IS_SEQUENTIAL2(_x) || CONFIG_IS_PIPELINED2(_x))
50
51// Mode:Client<->Server
52//
53// s1 => sequential using 1 channel
54// s2 => sequential using 2 channels
55// p2 => pipelined using 2 channels
56//
57// . => Flounder      t => THC
58
59static char *config_name[NUM_CONFIG]
60= {"s1:.<->.",
61   "s1:t<->.",
62   "s1:.<->t",
63   "s1:t<->t",
64   "s2:.<->.",
65   "s2:t<->.",
66   "s2:.<->t",
67   "s2:t<->t",
68   "p2:.<->.",
69   "p2:t<->.",
70   "p2:.<->t",
71   "p2:t<->t",
72   "p1:.<->."};
73
74static const char *my_service_name = "thc_v_flounder";
75static const char *kind;
76static int config;
77
78// IDC bindings allocated at start of day.  We do not use these
79// directly: instead, we use these priv_b_* bindings to initialize
80// the b_* bindings.  This lets us switch between 1-channel and
81// 2-channel implementations.
82
83static struct bench_binding *priv_b_c2s;
84static struct bench_binding *priv_b_s2c;
85
86// IDC bindings for use in direct-to-Flounder code.
87//
88// c2s => binding used for sending messages from client to server
89// s2c => binding used for sending messages from server to client
90
91static struct bench_binding *b_c2s;
92static struct bench_binding *b_s2c;
93
94/* ------------------------------ CLIENT ------------------------------ */
95
96static cycles_t timings[ITERATIONS];
97static cycles_t total_timing;
98
99// THC client code
100
101static void client_thc(struct bench_thc_client_binding_t *cl) {
102  assert(CONFIG_IS_THC_CLIENT(config));
103  for (int i = 0; i < SKIP; i ++) {
104    cycles_t start = bench_tsc();
105    errval_t err = cl->send.fsb_empty_request(cl);
106    assert(err_is_ok(err));
107    err = cl->recv.fsb_empty_reply(cl);
108    assert(err_is_ok(err));
109    timings[i] = (bench_tsc() - start);
110  }
111
112  cycles_t loop_start = bench_tsc();
113
114  for (int i = SKIP; i < ITERATIONS; i ++) {
115    cycles_t start = bench_tsc();
116    cl->send.fsb_empty_request(cl);
117    cl->recv.fsb_empty_reply(cl);
118    timings[i] = (bench_tsc() - start);
119  }
120
121  total_timing = bench_tsc()-loop_start;
122}
123
124static void client_thc_pipeline(struct bench_thc_client_binding_t *cl) {
125  assert(CONFIG_IS_THC_CLIENT(config));
126  cycles_t loop_start;
127
128  DO_FINISH({
129      ASYNC({
130          for (int i = 0; i < SKIP; i++) {
131            timings[i] = bench_tsc();
132            cl->send.fsb_empty_request(cl);
133          }
134
135          loop_start = bench_tsc();
136
137          for (int i = SKIP; i < ITERATIONS; i++) {
138            timings[i] = bench_tsc();
139            cl->send.fsb_empty_request(cl);
140          }
141        });
142
143      for (int i = 0; i < SKIP; i ++) {
144        cl->recv.fsb_empty_reply(cl);
145        timings[i] = (bench_tsc() - timings[i]);
146      }
147
148      for (int i = SKIP; i < ITERATIONS; i ++) {
149        cl->recv.fsb_empty_reply(cl);
150        timings[i] = (bench_tsc() - timings[i]);
151      }
152
153    });
154
155  total_timing = bench_tsc()-loop_start;
156}
157
158// Direct-to-flounder client code
159
160static int tx_count;
161static int rx_count;
162static void *old_fn;
163static thc_sem_t flounder_test_done_sem;
164
165static void client_handler(struct bench_binding *b) {
166  assert(CONFIG_IS_FLOUNDER_CLIENT(config));
167  assert(!CONFIG_IS_PIPELINED(config));
168  assert(b == b_s2c);
169  timings[rx_count] = bench_tsc()-timings[rx_count];
170  rx_count++;
171  if (rx_count == ITERATIONS) {
172    // Done
173    total_timing = bench_tsc() - total_timing;
174    b_s2c->rx_vtbl.fsb_empty_reply = old_fn;
175    thc_sem_v(&flounder_test_done_sem);
176  } else {
177    // Send next request
178    cycles_t t = bench_tsc();
179    tx_count++;
180    if (tx_count == SKIP) {
181      total_timing = t;
182    }
183    timings[tx_count] = t;
184    errval_t err = b_c2s->tx_vtbl.fsb_empty_request(b_c2s,
185                                                    NOP_CONT);
186    if (err_is_fail(err)) {
187      DEBUG_ERR(err, "Client send failed\n");
188      abort();
189    }
190  }
191}
192
193static void client_flounder(void) {
194  assert(CONFIG_IS_FLOUNDER_CLIENT(config));
195  assert(!CONFIG_IS_PIPELINED(config));
196  old_fn = b_s2c->rx_vtbl.fsb_empty_reply;
197  b_s2c->rx_vtbl.fsb_empty_reply = client_handler;
198  tx_count = rx_count = 0;
199  thc_sem_init(&flounder_test_done_sem, 0);
200
201  // Send request
202  timings[0] = total_timing = bench_tsc();
203  errval_t err = b_c2s->tx_vtbl.fsb_empty_request(b_c2s,
204                                                  NOP_CONT);
205  if (err_is_fail(err)) {
206    DEBUG_ERR(err, "Client send failed (sequential)\n");
207    abort();
208  }
209
210  // Wait for callbacks to finish
211  thc_sem_p(&flounder_test_done_sem);
212}
213
214static void client_tx_pipeline_handler(struct bench_binding *b) {
215  assert(CONFIG_IS_FLOUNDER_CLIENT(config));
216  assert(CONFIG_IS_PIPELINED(config));
217  assert(b == b_c2s);
218  while (tx_count < ITERATIONS) {
219    errval_t err = b_c2s->tx_vtbl.fsb_empty_request(b_c2s, NOP_CONT);
220    if (err == FLOUNDER_ERR_TX_BUSY) {
221      err = b_c2s->register_send(b_c2s,
222                                 get_default_waitset(),
223                                 MKCONT((void(*)(void*))client_tx_pipeline_handler,
224                                        b));
225      assert(err_is_ok(err));
226      break;
227    } else if (err_is_fail(err)) {
228      DEBUG_ERR(err, "Client send failed (pipelined)\n");
229      abort();
230    }
231    timings[tx_count] = bench_tsc();
232    if (tx_count == SKIP) {
233      total_timing = bench_tsc();
234    }
235    tx_count ++;
236  }
237}
238
239static void client_rx_pipeline_handler(struct bench_binding *b) {
240  assert(CONFIG_IS_FLOUNDER_CLIENT(config));
241  assert(CONFIG_IS_PIPELINED(config));
242  assert(b == b_s2c);
243  timings[rx_count] = bench_tsc()-timings[rx_count];
244  rx_count++;
245  if (rx_count == ITERATIONS) {
246    total_timing = bench_tsc() - total_timing;
247    b_s2c->rx_vtbl.fsb_empty_reply = old_fn;
248    thc_sem_v(&flounder_test_done_sem);
249  }
250}
251
252static void client_flounder_pipeline(void) {
253  assert(CONFIG_IS_FLOUNDER_CLIENT(config));
254  assert(CONFIG_IS_PIPELINED(config));
255  old_fn = b_s2c->rx_vtbl.fsb_empty_reply;
256  b_s2c->rx_vtbl.fsb_empty_reply = client_rx_pipeline_handler;
257  tx_count = rx_count = 0;
258  thc_sem_init(&flounder_test_done_sem, 0);
259
260  // Send as many requests as possible
261  client_tx_pipeline_handler(b_c2s);
262
263  // Wait for callbacks to finish
264  thc_sem_p(&flounder_test_done_sem);
265}
266
267// Generic client harness
268
269static int compar(const void *a, const void *b) {
270  cycles_t ca = *(cycles_t*)a;
271  cycles_t cb = *(cycles_t*)b;
272  return ca-cb;
273}
274
275static void client_work(void) {
276  errval_t err;
277
278  debug_printf("connecting to service from core=%d\n", disp_get_core_id());
279
280  err = bench_thc_connect_by_name(my_service_name,
281                                  get_default_waitset(),
282                                  IDC_BIND_FLAGS_DEFAULT,
283                                  &priv_b_c2s);
284  if (err_is_fail(err)) {
285    DEBUG_ERR(err, "connect failed");
286    abort();
287  }
288
289  debug_printf("              ... client done first connection\n");
290
291  err = bench_thc_connect_by_name(my_service_name,
292                                  get_default_waitset(),
293                                  IDC_BIND_FLAGS_DEFAULT,
294                                  &priv_b_s2c);
295  if (err_is_fail(err)) {
296    DEBUG_ERR(err, "connect failed");
297    abort();
298  }
299
300  debug_printf("              ... client done second connection\n");
301
302  struct bench_thc_client_binding_t cl;
303  while (1) {
304    for (config = 0; config < NUM_CONFIG; config ++) {
305      for (int rep = 0; rep < NUM_REP; rep ++) {
306        // Configure 1-channel v 2-channel as appropriate
307        if (CONFIG_IS_1CHAN(config)) {
308          err = bench_thc_init_client(&cl, priv_b_c2s, priv_b_c2s);
309        } else {
310          assert(CONFIG_IS_2CHAN(config));
311          err = bench_thc_init_client(&cl, priv_b_c2s, priv_b_s2c);
312        }
313        assert(err_is_ok(err));
314        b_c2s = cl._c2s_st;
315        b_s2c = cl._s2c_st;
316
317        // Run THC / flounder client
318        if (CONFIG_IS_PIPELINED(config)) {
319          if (CONFIG_IS_THC_CLIENT(config)) {
320            client_thc_pipeline(&cl);
321          } else {
322            assert(CONFIG_IS_FLOUNDER_CLIENT(config));
323            client_flounder_pipeline();
324          }
325        } else {
326          if (CONFIG_IS_THC_CLIENT(config)) {
327            client_thc(&cl);
328          } else {
329            assert(CONFIG_IS_FLOUNDER_CLIENT(config));
330            client_flounder();
331          }
332        }
333
334        // Calculate stats
335        cycles_t total_of_timings = 0;
336        for (int i = SKIP; i < ITERATIONS; i ++) {
337          total_of_timings += timings[i];
338        }
339        qsort(&timings[SKIP], ITERATIONS-SKIP, sizeof(cycles_t), &compar);
340
341        // Report stats
342        cycles_t min = timings[SKIP];
343        cycles_t pc5 = timings[SKIP+(5*(ITERATIONS-SKIP))/100];
344        cycles_t pc50 = timings[SKIP+(ITERATIONS-SKIP) / 2];
345        cycles_t pc95 = timings[SKIP+(95*(ITERATIONS-SKIP))/100];
346        cycles_t max = timings[SKIP+(ITERATIONS-SKIP) - 1];
347        cycles_t per = total_timing / (ITERATIONS-SKIP);
348
349        if (max > 50 *pc50) {
350          debug_printf("%2d %s ******\n", config, config_name[config]);
351        } else {
352          debug_printf("%2d %s min %5zd 5%%-ile %5zd median %5zd 95%%-ile %5zd max %5zd per %4zd\n",
353                       config, config_name[config], (size_t)min, (size_t)pc5, (size_t)pc50, (size_t)pc95, (size_t)max, (size_t)per);
354        }
355      }
356    }
357    THCDumpStats(1);
358  }
359}
360
361/* ------------------------------ SERVER ------------------------------ */
362
363// THC server code
364
365static void server_thc(struct bench_thc_service_binding_t *sv) {
366  assert(CONFIG_IS_THC_SERVER(config));
367  for (int i = 0; i < ITERATIONS; i++) {
368    errval_t err = sv->recv.fsb_empty_request(sv);
369    assert(err_is_ok(err));
370    err = sv->send.fsb_empty_reply(sv);
371    assert(err_is_ok(err));
372  }
373}
374
375// Direct-to-flounder server code
376static int sends_due = 0;
377
378static void server_tx_handler(struct bench_binding *b) {
379  assert(CONFIG_IS_FLOUNDER_SERVER(config));
380  assert(b == b_s2c);
381  assert(sends_due > 0);
382
383  while (sends_due > 0) {
384    // Retry send to client
385    errval_t err = b_s2c->tx_vtbl.fsb_empty_reply(b_s2c,
386                                                  NOP_CONT);
387    if (err == FLOUNDER_ERR_TX_BUSY) {
388      // Stalled again, register another callback
389      err = b_s2c->register_send(b_s2c,
390                                 get_default_waitset(),
391                                 MKCONT((void(*)(void*))server_tx_handler,
392                                        b));
393      assert(err_is_ok(err));
394      return;
395    }
396
397    // Done send
398    rx_count++;
399    sends_due--;
400  }
401
402  // Wake the main server loop if we have sent the last reply
403  assert(rx_count <= ITERATIONS);
404  if (rx_count == ITERATIONS) {
405    b_c2s->rx_vtbl.fsb_empty_request = old_fn;
406    thc_sem_v(&flounder_test_done_sem);
407  }
408}
409
410static void server_handler(struct bench_binding *b) {
411  assert(CONFIG_IS_FLOUNDER_SERVER(config));
412  assert(b == b_c2s);
413
414  if (sends_due > 0) {
415    // Already registered a callback, increase the number of
416    // messages we need to transmit
417    sends_due ++;
418  } else {
419    // Try to send a reply directly
420    errval_t err = b_s2c->tx_vtbl.fsb_empty_reply(b_s2c,
421                                                  NOP_CONT);
422    if (err == FLOUNDER_ERR_TX_BUSY) {
423      assert(sends_due == 0);
424      sends_due = 1;
425      err = b_s2c->register_send(b_s2c,
426                                 get_default_waitset(),
427                                 MKCONT((void(*)(void*))server_tx_handler,
428                                        b_s2c));
429      assert(err_is_ok(err));
430      return;
431    }
432
433    // Done send
434    rx_count++;
435    if (rx_count == ITERATIONS) {
436      b_c2s->rx_vtbl.fsb_empty_request = old_fn;
437      thc_sem_v(&flounder_test_done_sem);
438    }
439  }
440}
441
442static void server_flounder(void) {
443  assert(CONFIG_IS_FLOUNDER_SERVER(config));
444
445  thc_sem_init(&flounder_test_done_sem, 0);
446  old_fn = b_c2s->rx_vtbl.fsb_empty_request;
447
448  // Initialize non-THC receive handler
449  b_c2s->rx_vtbl.fsb_empty_request = server_handler;
450  rx_count = 0;
451
452  // Wait for callback-soup to finish
453  thc_sem_p(&flounder_test_done_sem);
454}
455
456// Generic server harness
457
458static void server_work(void) {
459  struct bench_thc_export_info info;
460  errval_t err;
461
462  debug_printf("exporting service from core=%d\n", disp_get_core_id());
463  err = bench_thc_export(&info,
464                         my_service_name,
465                         get_default_waitset(),
466                         IDC_EXPORT_FLAGS_DEFAULT,
467                         NULL);
468  if (err_is_fail(err)) {
469    DEBUG_ERR(err, "export failed");
470    abort();
471  }
472
473  debug_printf("exported service, waiting for client c2s connection\n");
474
475  err = bench_thc_accept(&info, &priv_b_c2s);
476  if (err_is_fail(err)) {
477    DEBUG_ERR(err, "accept failed");
478    abort();
479  }
480
481  debug_printf("              ... waiting for client s2c connection\n");
482
483  err = bench_thc_accept(&info, &priv_b_s2c);
484  if (err_is_fail(err)) {
485    DEBUG_ERR(err, "accept failed");
486    abort();
487  }
488
489  debug_printf("              ... done\n");
490
491  struct bench_thc_service_binding_t sv;
492  while (1) {
493    for (config = 0; config < NUM_CONFIG; config ++) {
494      for (int rep = 0; rep < NUM_REP; rep++) {
495        // Configure 1-channel v 2-channel as appropriate
496        if (CONFIG_IS_1CHAN(config)) {
497          err = bench_thc_init_service(&sv, priv_b_c2s, priv_b_c2s);
498        } else {
499          assert(CONFIG_IS_2CHAN(config));
500          err = bench_thc_init_service(&sv, priv_b_c2s, priv_b_s2c);
501        }
502        assert(err_is_ok(err));
503        b_c2s = sv._c2s_st;
504        b_s2c = sv._s2c_st;
505
506        // Run THC / flounder server
507        if (CONFIG_IS_THC_SERVER(config)) {
508          server_thc(&sv);
509        } else {
510          assert(CONFIG_IS_FLOUNDER_SERVER(config));
511          server_flounder();
512        }
513      }
514    }
515  }
516}
517
518/* ------------------------------ MAIN ------------------------------ */
519
520int main(int argc, char *argv[])
521{
522  // Allow arbitrary early parameters (e.g., "boot" when invoked
523  // directly from menu.lst by monitor)
524  if (argc >= 2 && strcmp(argv[argc-1], "client") == 0) {
525    kind = "client";
526    client_work();
527  } else if (argc >= 2 && strcmp(argv[argc-1], "server") == 0) {
528    kind = "server";
529    server_work();
530  } else {
531    debug_printf("Usage: %s ... client|server\n", argv[0]);
532    return EXIT_FAILURE;
533  }
534
535  return EXIT_SUCCESS;
536}
537
538