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