1184902Srwatson/** 2184902Srwatson * \file 3184902Srwatson * \brief Benchmark retype with remote copies present for source cap 4184902Srwatson */ 5191273Srwatson 6191273Srwatson/* 7184902Srwatson * Copyright (c) 2017, ETH Zurich. 8184902Srwatson * All rights reserved. 9184902Srwatson * 10184902Srwatson * This file is distributed under the terms in the attached LICENSE file. 11187214Srwatson * If you do not find this file, copies can be found by writing to: 12187214Srwatson * ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group. 13187214Srwatson */ 14187214Srwatson 15187214Srwatson#include <barrelfish/barrelfish.h> 16187214Srwatson#include <if/bench_distops_defs.h> 17191273Srwatson 18184902Srwatson#include <bitmacros.h> 19184902Srwatson 20184902Srwatson#include <bench/bench.h> 21191273Srwatson#include <trace/trace.h> 22191273Srwatson 23191273Srwatson#include "benchapi.h" 24191273Srwatson 25184902Srwatson#define RETYPE_COPIES 10 26184902Srwatson 27184902Srwatson//{{{1 debugging helpers 28184902Srwatsonstatic void debug_capref(const char *prefix, struct capref cap) 29184902Srwatson{ 30184902Srwatson char buf[128]; 31184902Srwatson debug_print_capref(buf, 128, cap); 32184902Srwatson printf("%s capref = %s\n", prefix, buf); 33184902Srwatson} 34184902Srwatson 35//{{{1 shared commands 36enum bench_cmd { 37 BENCH_CMD_CREATE_COPIES, 38 BENCH_CMD_COPIES_DONE, 39 BENCH_CMD_DO_ALLOC, 40 BENCH_CMD_DO_RETYPE, 41 BENCH_CMD_FORWARD_COPIES, 42 BENCH_CMD_COPIES_RX_DONE, 43 BENCH_CMD_PRINT_DONE, 44}; 45 46//{{{1 shared helper functions 47static size_t get_mdb_size(void) 48{ 49 errval_t err; 50 size_t cap_base_count = 0; 51 err = sys_debug_get_mdb_size(&cap_base_count); 52 assert(err_is_ok(err)); 53 return cap_base_count; 54} 55 56 57//{{{1 Managment node: implement orchestration for benchmark 58 59//{{{2 Management node: state management 60 61struct global_state { 62 struct capref ram; 63 struct capref fwdcap; 64 coreid_t *nodes; 65 int nodes_seen; 66 int nodecount; 67 int copies_done; 68 int copycount; 69 int rx_seen; 70 int masternode; 71 int currcopies; 72}; 73 74errval_t mgmt_init_benchmark(void **st, int nodecount) 75{ 76 *st = calloc(1, sizeof(struct global_state)); 77 if (!*st) { 78 return LIB_ERR_MALLOC_FAIL; 79 } 80 struct global_state *gs = *st; 81 gs->nodes = calloc(nodecount, sizeof(coreid_t)); 82 gs->nodecount = nodecount; 83 gs->copies_done = 0; 84 gs->rx_seen = 0; 85 gs->masternode = -1; 86 return ram_alloc(&gs->ram, BASE_PAGE_BITS); 87} 88 89static int sort_coreid(const void *a_, const void *b_) 90{ 91 // deref pointers as coreids, store as ints 92 int a = *((coreid_t*)a_); 93 int b = *((coreid_t*)b_); 94 // subtract as ints 95 return a-b; 96} 97 98void mgmt_register_node(void *st, coreid_t nodeid) 99{ 100 struct global_state *gs = st; 101 gs->nodes[gs->nodes_seen++] = nodeid; 102 // if we've seen all nodes, sort nodes array and configure printnode 103 if (gs->nodes_seen == gs->nodecount) { 104 qsort(gs->nodes, gs->nodecount, sizeof(coreid_t), sort_coreid); 105 gs->masternode = gs->nodes[0]; 106 } 107} 108 109struct mgmt_node_state { 110}; 111 112errval_t mgmt_init_node(void **st) 113{ 114 *st = malloc(sizeof(struct mgmt_node_state)); 115 if (!*st) { 116 return LIB_ERR_MALLOC_FAIL; 117 } 118 return SYS_ERR_OK; 119} 120 121//{{{2 Management node: benchmark impl 122void mgmt_run_benchmark(void *st) 123{ 124 struct global_state *gs = st; 125 126 printf("All clients sent hello! Benchmark starting...\n"); 127 128 printf("# Benchmarking RETYPE WITH REMOTE COPIES: nodes=%d\n", gs->nodecount); 129 130 printf("# Starting out with %d copies, will by powers of 2 up to %d...\n", 131 NUM_COPIES_START, NUM_COPIES_END); 132 133 TRACE(CAPOPS, START, 0); 134 135 gs->currcopies = NUM_COPIES_START; 136 broadcast_caps(BENCH_CMD_CREATE_COPIES, NUM_COPIES_START, gs->ram); 137} 138 139void mgmt_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b) 140{ 141 errval_t err; 142 struct global_state *gs = get_global_state(b); 143 144 switch(cmd) { 145 case BENCH_CMD_COPIES_DONE: 146 gs->copies_done++; 147 if (gs->copies_done == gs->nodecount) { 148 printf("# All copies made!\n"); 149 unicast_cmd(gs->masternode, BENCH_CMD_DO_ALLOC, ITERS); 150 } 151 break; 152 case BENCH_CMD_COPIES_RX_DONE: 153 gs->rx_seen++; 154 DEBUG("got BENCH_CMD_COPIES_RX_DONE: seen = %d, expected = %d\n", 155 gs->rx_seen, gs->copycount); 156 if (gs->rx_seen == gs->copycount) { 157 DEBUG("# All nodes have copies of cap-to-del\n"); 158 err = cap_destroy(gs->fwdcap); 159 assert(err_is_ok(err)); 160 gs->rx_seen = 0; 161 unicast_cmd(gs->masternode, BENCH_CMD_DO_RETYPE, 0); 162 } 163 break; 164 case BENCH_CMD_PRINT_DONE: 165 if (gs->currcopies == NUM_COPIES_END) { 166 printf("# Benchmark done!\n"); 167 TRACE(CAPOPS, STOP, 0); 168 mgmt_trace_flush(NOP_CONT); 169 return; 170 } 171 printf("# Round done!\n"); 172 printf("# mgmt node mdb size: %zu\n", get_mdb_size()); 173 // Reset counters for next round 174 gs->currcopies *= 2; 175 gs->copies_done = 0; 176 gs->rx_seen = 0; 177 // Start new round 178 broadcast_cmd(BENCH_CMD_CREATE_COPIES, gs->currcopies); 179 break; 180 default: 181 printf("mgmt node got unknown command %d over binding %p\n", cmd, b); 182 break; 183 } 184} 185 186void mgmt_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1, 187 struct bench_distops_binding *b) 188{ 189 struct global_state *gs = get_global_state(b); 190 switch (cmd) { 191 case BENCH_CMD_FORWARD_COPIES: 192 { 193 coreid_t cores[] = { gs->nodes[1] }; 194 gs->copycount = 1; 195 gs->fwdcap = cap1; 196 multicast_caps(BENCH_CMD_FORWARD_COPIES, arg, cap1, cores, gs->copycount); 197 DEBUG("cmd_fwd_copies: multicast done\n"); 198 break; 199 } 200 default: 201 printf("mgmt node got caps + command %"PRIu32", arg=%d over binding %p:\n", 202 cmd, arg, b); 203 debug_capref("cap1:", cap1); 204 break; 205 } 206} 207 208//{{{1 Node 209 210struct node_state { 211 struct capref cap; 212 struct capref ram; 213 struct capref *copies; 214 int numcopies; 215 struct capref *ramcopies; 216 int numramcopies; 217 uint64_t *delcycles; 218 uint32_t benchcount; 219 uint32_t iter; 220 bool benchnode; 221}; 222 223static coreid_t my_core_id = -1; 224 225void init_node(struct bench_distops_binding *b) 226{ 227 printf("%s: binding = %p\n", __FUNCTION__, b); 228 229 my_core_id = disp_get_core_id(); 230 231 bench_init(); 232 233 // Allocate client state struct 234 b->st = malloc(sizeof(struct node_state)); 235 assert(b->st); 236 if (!b->st) { 237 USER_PANIC("state malloc() in client"); 238 } 239 240 struct node_state *ns = b->st; 241 ns->benchnode = false; 242 ns->ramcopies = NULL; 243 ns->numramcopies = 0; 244} 245 246static void node_create_copies(struct node_state *ns) 247{ 248 errval_t err; 249 ns->copies = calloc(ns->numcopies, sizeof(struct capref)); 250 for (int i = 0; i < ns->numcopies; i++) { 251 err = slot_alloc(&ns->copies[i]); 252 PANIC_IF_ERR(err, "slot_alloc for copy %d\n", i); 253 err = cap_copy(ns->copies[i], ns->ram); 254 PANIC_IF_ERR(err, "cap_copy for copy %d\n", i); 255 } 256} 257 258extern bool info; 259void node_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b) 260{ 261 struct node_state *ns = b->st; 262 errval_t err; 263 264 switch(cmd) { 265 case BENCH_CMD_CREATE_COPIES: 266 if (ns->copies) { 267 // Cleanup before next round 268 for (int i = 0; i < ns->numcopies; i++) { 269 err = cap_destroy(ns->copies[i]); 270 assert(err_is_ok(err)); 271 } 272 free(ns->copies); 273 } 274 printf("# node %d: creating %d cap copies\n", my_core_id, arg); 275 ns->numcopies = arg; 276 node_create_copies(ns); 277 printf("# node %d: %zu capabilities on node\n", my_core_id, get_mdb_size()); 278 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 1); 279 PANIC_IF_ERR(err, "signaling cap_copy() done\n"); 280 break; 281 case BENCH_CMD_DO_RETYPE: 282 DEBUG("# node %d: revoking our copy for benchmark (mdb size %zu)\n", 283 my_core_id, get_mdb_size()); 284 uint64_t start, end; 285 struct capref slot; 286 err = slot_alloc(&slot); 287 assert(err_is_ok(err)); 288 start = bench_tsc(); 289 // cycle through pages in source cap 290 TRACE(CAPOPS, USER_RETYPE_CALL, (ns->numcopies << 16) | ns->iter); 291 err = cap_retype(slot, ns->cap, 292 (ns->iter*BASE_PAGE_SIZE) % LARGE_PAGE_SIZE, 293 ObjType_Frame, BASE_PAGE_SIZE, 1); 294 TRACE(CAPOPS, USER_RETYPE_RESP, (ns->numcopies << 16) | ns->iter); 295 end = bench_tsc(); 296 ns->delcycles[ns->iter] = end - start; 297 assert(err_is_ok(err)); 298 // cleanup 299 err = cap_destroy(slot); 300 assert(err_is_ok(err)); 301 // Use revoke for cleanup, so we don't have to worry about 302 // copies and stuff on other cores 303 err = cap_revoke(ns->cap); 304 assert(err_is_ok(err)); 305 // increase iteration counter 306 ns->iter ++; 307 // fall-through to next round 308 case BENCH_CMD_DO_ALLOC: 309 if (arg != 0) { 310 DEBUG("Initializing node %d benchmarking meta\n", my_core_id); 311 // First call only 312 ns->benchcount = arg; 313 ns->iter = 0; 314 ns->benchnode = true; 315 ns->delcycles = calloc(ns->benchcount, sizeof(uint64_t)); 316 assert(ns->delcycles); 317 err = ram_alloc(&ns->cap, LARGE_PAGE_BITS+1); 318 assert(err_is_ok(err)); 319 if (!ns->ramcopies) { 320 ns->ramcopies = calloc(RETYPE_COPIES, sizeof(struct capref)); 321 } 322 assert(ns->ramcopies); 323 for (int c = 0; c < RETYPE_COPIES; c++) { 324 err = slot_alloc(&ns->ramcopies[c]); 325 assert(err_is_ok(err)); 326 } 327 } 328 if (ns->iter == ns->benchcount) { 329 // Exit if we've done enough iterations 330 printf("# node %d: tsc_per_us = %ld; numcopies = %d\n", 331 my_core_id, bench_tsc_per_us(), ns->numcopies); 332 printf("# delete latency in cycles\n"); 333 for (int i = 0; i < ns->benchcount; i++) { 334 printf("%ld\n", ns->delcycles[i]); 335 } 336 free(ns->delcycles); 337 err = cap_destroy(ns->cap); 338 assert(err_is_ok(err)); 339 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_PRINT_DONE, 0); 340 assert(err_is_ok(err)); 341 break; 342 } 343 // TODO: #copies / copy/desc mix 344 // XXX: can only create copies on non-owning core, bc. owning core 345 // already has descendants 346 for (int c = 0; c < RETYPE_COPIES; c++) { 347 err = cap_copy(ns->ramcopies[c], ns->cap); 348 if (err_is_fail(err)) { 349 DEBUG_ERR(err, "[core %d] creating copy %d", my_core_id, c); 350 } 351 assert(err_is_ok(err)); 352 } 353 DEBUG("# node %d: forwarding copies for benchmark (mdb size %zu)\n", 354 my_core_id, get_mdb_size()); 355 err = bench_distops_caps__tx(b, NOP_CONT, BENCH_CMD_FORWARD_COPIES, 10, ns->cap); 356 PANIC_IF_ERR(err, "fwd copies"); 357 assert(err_is_ok(err)); 358 break; 359 default: 360 printf("node %d got command %"PRIu32"\n", my_core_id, cmd); 361 break; 362 } 363} 364 365void node_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1, 366 struct bench_distops_binding *b) 367{ 368 errval_t err; 369 struct node_state *ns = b->st; 370 371 switch (cmd) { 372 case BENCH_CMD_CREATE_COPIES: 373 printf("# node %d: creating %d cap copies\n", my_core_id, arg); 374 ns->ram = cap1; 375 ns->numcopies = arg; 376 node_create_copies(ns); 377 printf("# node %d: %zu caps on node\n", my_core_id, get_mdb_size()); 378 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 0); 379 PANIC_IF_ERR(err, "signaling cap_copy() done\n"); 380 break; 381 case BENCH_CMD_FORWARD_COPIES: 382 if (ns->benchnode) { 383 printf("# node %d: just deleting forwarded copy\n", my_core_id); 384 printf("# node %d: mdb size: %zu\n", my_core_id, get_mdb_size()); 385 // node on which we benchmark delete 386 // delete forwarded copy 387 err = cap_destroy(cap1); 388 assert(err_is_ok(err)); 389 } else { 390 // other nodes 391 if (!ns->ramcopies || ns->numramcopies != arg) { 392 if (ns->numramcopies != arg) { 393 free(ns->ramcopies); 394 ns->numramcopies = arg; 395 ns->ramcopies = malloc(arg * sizeof(struct capref)); 396 } 397 for (int i = 0; i < ns->numramcopies; i++) { 398 err = slot_alloc(&ns->ramcopies[i]); 399 assert(err_is_ok(err)); 400 } 401 } 402 assert(ns->ramcopies); 403 DEBUG("# node %d: creating %d copies of cap to delete\n", 404 my_core_id, ns->numramcopies); 405 for (int i = 0; i < ns->numramcopies; i++) { 406 // make descendants 407 err = cap_retype(ns->ramcopies[i], cap1, 408 LARGE_PAGE_SIZE + i*BASE_PAGE_SIZE, 409 ObjType_RAM, BASE_PAGE_SIZE, 1); 410 if (err_is_fail(err)) { 411 DEBUG_ERR(err, "[node %d] retyping c=%d", 412 my_core_id, i); 413 } 414 assert(err_is_ok(err)); 415 } 416 err = cap_destroy(cap1); 417 assert(err_is_ok(err)); 418 } 419 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_RX_DONE, 0); 420 assert(err_is_ok(err)); 421 break; 422 default: 423 printf("node %d got caps + command %"PRIu32", arg=%d:\n", 424 my_core_id, cmd, arg); 425 debug_capref("cap1:", cap1); 426 break; 427 } 428} 429