1/** 2 * \file 3 * \brief Benchmark revoke of remote copy of moveable cap 4 */ 5 6/* 7 * Copyright (c) 2017, 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, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <barrelfish/barrelfish.h> 16#include <if/bench_distops_defs.h> 17 18#include <bitmacros.h> 19 20#include <bench/bench.h> 21#include <trace/trace.h> 22 23#include "benchapi.h" 24 25#define REVOKE_COPIES 10 26 27//{{{1 debugging helpers 28static void debug_capref(const char *prefix, struct capref cap) 29{ 30 char buf[128]; 31 debug_print_capref(buf, 128, cap); 32 printf("%s capref = %s\n", prefix, buf); 33} 34 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_REVOKE, 41 BENCH_CMD_REVOKE_DONE, 42 BENCH_CMD_FORWARD_COPIES, 43 BENCH_CMD_COPIES_RX_DONE, 44 BENCH_CMD_PRINT_DONE, 45}; 46 47//{{{1 shared helper functions 48static size_t get_mdb_size(void) 49{ 50 errval_t err; 51 size_t cap_base_count = 0; 52 err = sys_debug_get_mdb_size(&cap_base_count); 53 assert(err_is_ok(err)); 54 return cap_base_count; 55} 56 57 58//{{{1 Managment node: implement orchestration for benchmark 59 60//{{{2 Management node: state management 61 62struct global_state { 63 struct capref ram; 64 struct capref fwdcap; 65 coreid_t *nodes; 66 int nodes_seen; 67 int nodecount; 68 int copies_done; 69 int copycount; 70 int rx_seen; 71 int allocnode; 72 int revokenode; 73 int currcopies; 74}; 75 76errval_t mgmt_init_benchmark(void **st, int nodecount) 77{ 78 if (nodecount < 2) { 79 printf("Cannot execute revoke remote copy benchmark with less than 2 worker nodes!\n"); 80 return ERR_INVALID_ARGS; 81 } 82 *st = calloc(1, sizeof(struct global_state)); 83 if (!*st) { 84 return LIB_ERR_MALLOC_FAIL; 85 } 86 struct global_state *gs = *st; 87 gs->nodes = calloc(nodecount, sizeof(coreid_t)); 88 gs->nodecount = nodecount; 89 gs->copies_done = 0; 90 gs->rx_seen = 0; 91 gs->allocnode = -1; 92 gs->revokenode = -1; 93 return ram_alloc(&gs->ram, BASE_PAGE_BITS); 94} 95 96static int sort_coreid(const void *a_, const void *b_) 97{ 98 // deref pointers as coreids, store as ints 99 int a = *((coreid_t*)a_); 100 int b = *((coreid_t*)b_); 101 // subtract as ints 102 return a-b; 103} 104 105void mgmt_register_node(void *st, coreid_t nodeid) 106{ 107 struct global_state *gs = st; 108 gs->nodes[gs->nodes_seen++] = nodeid; 109 // if we've seen all nodes, sort nodes array and configure printnode 110 if (gs->nodes_seen == gs->nodecount) { 111 qsort(gs->nodes, gs->nodecount, sizeof(coreid_t), sort_coreid); 112 gs->allocnode = gs->nodes[0]; 113 gs->revokenode = gs->nodes[1]; 114 } 115} 116 117struct mgmt_node_state { 118}; 119 120errval_t mgmt_init_node(void **st) 121{ 122 *st = malloc(sizeof(struct mgmt_node_state)); 123 if (!*st) { 124 return LIB_ERR_MALLOC_FAIL; 125 } 126 return SYS_ERR_OK; 127} 128 129//{{{2 Management node: benchmark impl 130void mgmt_run_benchmark(void *st) 131{ 132 struct global_state *gs = st; 133 134 printf("All clients sent hello! Benchmark starting...\n"); 135 136 printf("# Benchmarking REVOKE REMOTE COPY: nodes=%d\n", gs->nodecount); 137 138 printf("# Starting out with %d copies, will by powers of 2 up to %d...\n", 139 NUM_COPIES_START, NUM_COPIES_END); 140 141 TRACE(CAPOPS, START, 0); 142 143 gs->currcopies = NUM_COPIES_START; 144 broadcast_caps(BENCH_CMD_CREATE_COPIES, NUM_COPIES_START, gs->ram); 145} 146 147void mgmt_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b) 148{ 149 errval_t err; 150 struct global_state *gs = get_global_state(b); 151 152 switch(cmd) { 153 case BENCH_CMD_COPIES_DONE: 154 gs->copies_done++; 155 if (gs->copies_done == gs->nodecount) { 156 printf("# All copies made!\n"); 157 unicast_cmd(gs->allocnode, BENCH_CMD_DO_ALLOC, ITERS); 158 } 159 break; 160 case BENCH_CMD_COPIES_RX_DONE: 161 gs->rx_seen++; 162 DEBUG("got BENCH_CMD_COPIES_RX_DONE: seen = %d, expected = %d\n", 163 gs->rx_seen, gs->copycount); 164 if (gs->rx_seen == gs->copycount) { 165 DEBUG("# All nodes have copies of cap-to-del\n"); 166 err = cap_destroy(gs->fwdcap); 167 assert(err_is_ok(err)); 168 gs->rx_seen = 0; 169 unicast_cmd(gs->revokenode, BENCH_CMD_DO_REVOKE, ITERS); 170 } 171 break; 172 case BENCH_CMD_REVOKE_DONE: 173 unicast_cmd(gs->allocnode, BENCH_CMD_DO_ALLOC, 0); 174 break; 175 case BENCH_CMD_PRINT_DONE: 176 if (gs->currcopies == NUM_COPIES_END) { 177 printf("# Benchmark done!\n"); 178 TRACE(CAPOPS, STOP, 0); 179 mgmt_trace_flush(NOP_CONT); 180 return; 181 } 182 printf("# Round done!\n"); 183 printf("# mgmt node mdb size: %zu\n", get_mdb_size()); 184 // Reset counters for next round 185 gs->currcopies *= 2; 186 gs->copies_done = 0; 187 gs->rx_seen = 0; 188 // Start new round 189 broadcast_cmd(BENCH_CMD_CREATE_COPIES, gs->currcopies); 190 break; 191 default: 192 printf("mgmt node got unknown command %d over binding %p\n", cmd, b); 193 break; 194 } 195} 196 197void mgmt_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1, 198 struct bench_distops_binding *b) 199{ 200 struct global_state *gs = get_global_state(b); 201 switch (cmd) { 202 case BENCH_CMD_FORWARD_COPIES: 203 { 204 // forward to all cores except gs->allocnode, which we set to 205 // gs->nodes[0]. 206 coreid_t *cores = &gs->nodes[1]; 207 gs->copycount = gs->nodecount-1; 208 gs->fwdcap = cap1; 209 multicast_caps(BENCH_CMD_FORWARD_COPIES, arg, cap1, cores, gs->copycount); 210 DEBUG("cmd_fwd_copies: multicast done\n"); 211 break; 212 } 213 default: 214 printf("mgmt node got caps + command %"PRIu32", arg=%d over binding %p:\n", 215 cmd, arg, b); 216 debug_capref("cap1:", cap1); 217 break; 218 } 219} 220 221//{{{1 Node 222 223struct node_state { 224 struct capref cap; 225 struct capref ram; 226 struct capref *copies; 227 int numcopies; 228 struct capref *ramcopies; 229 int numramcopies; 230 uint64_t *delcycles; 231 uint32_t benchcount; 232 uint32_t iter; 233 bool benchnode; 234}; 235 236static coreid_t my_core_id = -1; 237 238void init_node(struct bench_distops_binding *b) 239{ 240 printf("%s: binding = %p\n", __FUNCTION__, b); 241 242 my_core_id = disp_get_core_id(); 243 244 bench_init(); 245 246 // Allocate client state struct 247 b->st = malloc(sizeof(struct node_state)); 248 assert(b->st); 249 if (!b->st) { 250 USER_PANIC("state malloc() in client"); 251 } 252 253 struct node_state *ns = b->st; 254 ns->benchnode = false; 255 ns->ramcopies = NULL; 256 ns->numramcopies = 0; 257} 258 259static void node_create_copies(struct node_state *ns) 260{ 261 errval_t err; 262 ns->copies = calloc(ns->numcopies, sizeof(struct capref)); 263 for (int i = 0; i < ns->numcopies; i++) { 264 err = slot_alloc(&ns->copies[i]); 265 PANIC_IF_ERR(err, "slot_alloc for copy %d\n", i); 266 err = cap_copy(ns->copies[i], ns->ram); 267 PANIC_IF_ERR(err, "cap_copy for copy %d\n", i); 268 } 269} 270 271static void init_bench_round(struct node_state *ns) 272{ 273 errval_t err; 274 printf("# node %d: initializing benchmarking metadata\n", my_core_id); 275 // First call only 276 ns->iter = 0; 277 ns->benchnode = true; 278 ns->delcycles = calloc(ns->benchcount, sizeof(uint64_t)); 279 assert(ns->delcycles); 280 if (!ns->ramcopies) { 281 ns->ramcopies = calloc(REVOKE_COPIES, sizeof(struct capref)); 282 } 283 assert(ns->ramcopies); 284 for (int c = 0; c < REVOKE_COPIES; c++) { 285 err = slot_alloc(&ns->ramcopies[c]); 286 assert(err_is_ok(err)); 287 } 288} 289 290static void print_bench_round(struct node_state *ns, struct bench_distops_binding *b) 291{ 292 errval_t err; 293 // Exit if we've done enough iterations 294 printf("# node %d: tsc_per_us = %ld; numcopies = %d\n", 295 my_core_id, bench_tsc_per_us(), ns->numcopies); 296 printf("# delete latency in cycles\n"); 297 for (int i = 0; i < ns->benchcount; i++) { 298 printf("%ld\n", ns->delcycles[i]); 299 } 300 free(ns->delcycles); 301 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_PRINT_DONE, 0); 302 assert(err_is_ok(err)); 303} 304 305extern bool info; 306void node_cmd(uint32_t cmd, uint32_t arg, struct bench_distops_binding *b) 307{ 308 errval_t err; 309 struct node_state *ns = b->st; 310 assert(ns); 311 312 switch(cmd) { 313 case BENCH_CMD_CREATE_COPIES: 314 if (ns->copies) { 315 // Cleanup before next round 316 for (int i = 0; i < ns->numcopies; i++) { 317 err = cap_destroy(ns->copies[i]); 318 assert(err_is_ok(err)); 319 } 320 free(ns->copies); 321 } 322 printf("# node %d: creating %d cap copies\n", my_core_id, arg); 323 ns->numcopies = arg; 324 node_create_copies(ns); 325 printf("# node %d: %zu capabilities on node\n", my_core_id, get_mdb_size()); 326 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 1); 327 PANIC_IF_ERR(err, "signaling cap_copy() done\n"); 328 // Use create copies command as call for resetting benchmark 329 ns->benchcount = ITERS; 330 init_bench_round(ns); 331 break; 332 case BENCH_CMD_DO_REVOKE: 333 DEBUG("# node %d: revoking our copy for benchmark (mdb size %zu)\n", 334 my_core_id, get_mdb_size()); 335 uint64_t start, end; 336 start = bench_tsc(); 337 TRACE(CAPOPS, USER_REVOKE_CALL, (ns->numcopies << 16) | ns->iter); 338 err = cap_revoke(ns->cap); 339 TRACE(CAPOPS, USER_REVOKE_RESP, (ns->numcopies << 16) | ns->iter); 340 end = bench_tsc(); 341 ns->delcycles[ns->iter] = end - start; 342 assert(err_is_ok(err)); 343 // Check that revoke went through correctly 344 struct capref slot; 345 err = slot_alloc(&slot); 346 assert(err_is_ok(err)); 347 err = cap_retype(slot, ns->cap, 0, ObjType_RAM, BASE_PAGE_SIZE, 1); 348 PANIC_IF_ERR(err, "retype after revoke!"); 349 err = cap_destroy(slot); 350 assert(err_is_ok(err)); 351 // increase iteration counter 352 ns->iter ++; 353 // cleanup last cap copy 354 err = cap_destroy(ns->cap); 355 assert(err_is_ok(err)); 356 ns->cap = NULL_CAP; 357 if (ns->iter == ITERS) { 358 // print results and send print_done if we've done enough 359 // iterations 360 print_bench_round(ns, b); 361 break; 362 } 363 // send revoke_done 364 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_REVOKE_DONE, 0); 365 assert(err_is_ok(err)); 366 break; 367 case BENCH_CMD_DO_ALLOC: 368 // We need to allocate a new ram cap every round, as the remote 369 // revoke will have deleted our original copy. 370 err = ram_alloc(&ns->cap, BASE_PAGE_BITS+2); 371 assert(err_is_ok(err)); 372 DEBUG("# node %d: forwarding copies for benchmark (mdb size %zu)\n", 373 my_core_id, get_mdb_size()); 374 err = bench_distops_caps__tx(b, NOP_CONT, BENCH_CMD_FORWARD_COPIES, 10, ns->cap); 375 PANIC_IF_ERR(err, "fwd copies"); 376 assert(err_is_ok(err)); 377 break; 378 default: 379 printf("node %d got command %"PRIu32"\n", my_core_id, cmd); 380 break; 381 } 382} 383 384void node_cmd_caps(uint32_t cmd, uint32_t arg, struct capref cap1, 385 struct bench_distops_binding *b) 386{ 387 errval_t err; 388 struct node_state *ns = b->st; 389 390 switch (cmd) { 391 case BENCH_CMD_CREATE_COPIES: 392 printf("# node %d: creating %d cap copies\n", my_core_id, arg); 393 ns->ram = cap1; 394 ns->numcopies = arg; 395 node_create_copies(ns); 396 printf("# node %d: %zu caps on node\n", my_core_id, get_mdb_size()); 397 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_DONE, 0); 398 PANIC_IF_ERR(err, "signaling cap_copy() done\n"); 399 // Use create copies command as call for resetting benchmark 400 ns->benchcount = ITERS; 401 init_bench_round(ns); 402 break; 403 case BENCH_CMD_FORWARD_COPIES: 404 { 405 bool descs = false; 406 if (capref_is_null(ns->cap)) { 407 // store copy in ns->cap so revoke knows where to look 408 ns->cap = cap1; 409 } else { 410 // We're core that allocated the cap 411 err = cap_destroy(cap1); 412 assert(err_is_ok(err)); 413 descs = true; 414 } 415 // create array if not exists 416 if (!ns->ramcopies || ns->numramcopies != arg) { 417 if (ns->numramcopies != arg) { 418 free(ns->ramcopies); 419 ns->numramcopies = arg; 420 ns->ramcopies = malloc(arg * sizeof(struct capref)); 421 } 422 for (int i = 0; i < ns->numramcopies; i++) { 423 err = slot_alloc(&ns->ramcopies[i]); 424 assert(err_is_ok(err)); 425 } 426 } 427 assert(ns->ramcopies); 428 DEBUG("# node %d: creating %d copies of cap to delete\n", 429 my_core_id, ns->numramcopies); 430 for (int i = 0; i < ns->numramcopies; i++) { 431 if (descs && i < 4) { 432 err = cap_retype(ns->ramcopies[i], ns->cap, 433 i*BASE_PAGE_SIZE, ObjType_RAM, BASE_PAGE_SIZE, 1); 434 if (err_is_fail(err)) { 435 DEBUG_ERR(err, "[node %d] retyping c=%d", 436 my_core_id, i); 437 } 438 assert(err_is_ok(err)); 439 } else { 440 err = cap_copy(ns->ramcopies[i], ns->cap); 441 assert(err_is_ok(err)); 442 } 443 } 444 err = bench_distops_cmd__tx(b, NOP_CONT, BENCH_CMD_COPIES_RX_DONE, 0); 445 assert(err_is_ok(err)); 446 } 447 break; 448 default: 449 printf("node %d got caps + command %"PRIu32", arg=%d:\n", 450 my_core_id, cmd, arg); 451 debug_capref("cap1:", cap1); 452 break; 453 } 454} 455