1/** \file 2 * \brief multi-hop interconnect driver test code 3 * 4 * We send messages from the client to the server and the opposite way in order 5 * to make sure that the multi-hop interconnect driver works for bidirectional 6 * message passing. 7 */ 8 9/* 10 * Copyright (c) 2010, ETH Zurich. 11 * All rights reserved. 12 * 13 * This file is distributed under the terms in the attached LICENSE file. 14 * If you do not find this file, copies can be found by writing to: 15 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 16 */ 17 18#define _USE_XOPEN /* for strdup() */ 19#include <string.h> 20#include <stdio.h> 21#include <barrelfish/barrelfish.h> 22#include <barrelfish/nameservice_client.h> 23#include <barrelfish/debug.h> 24#include <if/test_defs.h> 25 26static const char *my_service_name = "multihoptest"; 27 28static const char *longstr = 29 "" 30 "Far out in the uncharted backwaters of the unfashionable end of the\n" 31 "western spiral arm of the Galaxy lies a small unregarded yellow sun.\n" 32 "\n" 33 "Orbiting this at a distance of roughly ninety-two million miles is an\n" 34 "utterly insignificant little blue green planet whose ape- descended life\n" 35 "forms are so amazingly primitive that they still think digital watches\n" 36 "are a pretty neat idea.\n" 37 "\n" 38 "This planet has - or rather had - a problem, which was this: most of the\n" 39 "people on it were unhappy for pretty much of the time. Many solutions\n" 40 "were suggested for this problem, but most of these were largely concerned\n" 41 "with the movements of small green pieces of paper, which is odd because\n" 42 "on the whole it wasn't the small green pieces of paper that were unhappy.\n" 43 "\n" 44 "And so the problem remained; lots of the people were mean, and most\n" 45 "of them were miserable, even the ones with digital watches.\n" 46 "\n" 47 "Many were increasingly of the opinion that they'd all made a big mistake\n" 48 "in coming down from the trees in the first place. And some said that\n" 49 "even the trees had been a bad move, and that no one should ever have\n" 50 "left the oceans.\n" 51 "\n" 52 "And then, one Thursday, nearly two thousand years after one man had\n" 53 "been nailed to a tree for saying how great it would be to be nice to people\n" 54 "for a change, one girl sitting on her own in a small cafe in Rickmansworth\n" 55 "suddenly realized what it was that had been going wrong all this time,\n" 56 "and she finally knew how the world could be made a good and happy\n" 57 "place. This time it was right, it would work, and no one would have to\n" 58 "get nailed to anything.\n" 59 "\n" 60 "Sadly, however, before she could get to a phone to tell anyone- about it,\n" 61 "a terribly stupid catastrophe occurred, and the idea was lost forever.\n" 62 "\n" 63 "This is her story."; 64 65static enum { 66 CLIENT, SERVER 67} role; 68 69static char* get_role_name(void) 70{ 71 if (role == CLIENT) { 72 return "client"; 73 } else if (role == SERVER) { 74 return "server"; 75 } else { 76 USER_PANIC("unknown role"); 77 return ""; 78 } 79} 80 81/* ----------------------- SENDING MESSAGES ------------------------ */ 82 83struct test_state { 84 struct test_binding *binding; 85 int nextmsg; 86 char *str; 87 struct capref cap1, cap2; 88}; 89 90// send the next message in our sequence 91static void send_cont(void *arg) 92{ 93 struct test_state *myst = arg; 94 struct test_binding *b = myst->binding; 95 struct event_closure txcont = MKCONT(send_cont, myst); 96 errval_t err; 97 98 printf("%s sending msg %d\n", get_role_name(), myst->nextmsg); 99 100 switch (myst->nextmsg) { 101 case 0: 102 err = test_basic__tx(b, txcont, 7); 103 break; 104 105 case 1: 106 107 // send a long string 108 err = test_str__tx(b, txcont, 42, longstr); 109 break; 110 111 case 2: 112 // create some caps to send (assume it all works) 113 err = frame_alloc(&myst->cap1, BASE_PAGE_SIZE, NULL); 114 assert(err_is_ok(err)); 115 116 err = slot_alloc(&myst->cap2); 117 assert(err_is_ok(err)); 118 119 err = vnode_create(myst->cap2, ObjType_VNode_x86_64_ptable); 120 assert(err_is_ok(err)); 121 122 // XXX: why do we need to do this? -SG, 2015-06-29. 123 struct test_multihop_binding *mb = (struct test_multihop_binding *)b; 124 mb->capst.tx_capnum = 0; 125 126 err = test_caps__tx(b, txcont, 69, myst->cap1, myst->cap2); 127 break; 128 129 case 3: 130 // delete the Frame cap, now it's been sent, the page table cap is not 131 // movable, and if the sender (who's the owner) deletes it before the 132 // receiver is done with it's work, we run into odd situations. 133 // -SG 2016-11-08 134 err = cap_destroy(myst->cap1); 135 assert(err_is_ok(err)); 136 137 // send a "buffer" 138 err = test_buf__tx(b, txcont, (uint8_t *) longstr, strlen(longstr)); 139 break; 140 141 case 4: 142 // here is where we would deallocate the buffer, if it wasn't static 143 printf("%s all done!\n", get_role_name()); 144 145 return; 146 147 default: 148 err = LIB_ERR_NOT_IMPLEMENTED; // TODO: Make meaningful 149 assert(!"shouldn't happen"); 150 } 151 152 if (err_is_ok(err)) { 153 myst->nextmsg++; 154 } else { 155 DEBUG_ERR(err, "error sending message %d", myst->nextmsg); 156 157 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { 158 assert(!"binding is busy for tx?!"); 159 160 // this error should never happen to us, because we serialize all 161 // sends with the continuation, however it may happen in another 162 // situation if the binding is already busy sending. in this case, 163 // the user can use something like: 164 165 struct waitset *ws = get_default_waitset(); 166 err = b->register_send(b, ws, txcont); 167 if (err_is_fail(err)) { 168 // note that only one continuation may be registered at a time 169 DEBUG_ERR(err, "register_send on binding failed!"); 170 } 171 } 172 173 abort(); 174 } 175} 176 177/* ------------------------ COMMON MESSAGE HANDLERS ------------------------ */ 178 179static void rx_basic(struct test_binding *b, uint32_t arg) 180{ 181 182 // make sure we received the correct argument(s) 183 if (arg != 7) { 184 USER_PANIC("received wrong argument in \"basic\" message!\n"); 185 } 186 printf("%s rx_basic %"PRIu32"\n", get_role_name(), arg); 187} 188 189static void rx_str(struct test_binding *b, uint32_t arg, const char *s) 190{ 191 192 // make sure we received the correct argument(s) 193 if (memcmp(s, longstr, strlen(longstr)) || (arg != 42)) { 194 USER_PANIC("received wrong argument in \"str\" message!\n"); 195 } 196 197 printf("%s rx_str %"PRIu32" '%s'\n", get_role_name(), arg, s); 198} 199 200static void rx_caps(struct test_binding *b, uint32_t arg, struct capref cap1, 201 struct capref cap2) 202{ 203 // make sure we received the correct argument(s) 204 if (arg != 69) { 205 USER_PANIC("received wrong argument in \"caps\" message!\n"); 206 } 207 208 char buf1[256], buf2[256]; 209 debug_print_cap_at_capref(buf1, sizeof(buf1), cap1); 210 debug_print_cap_at_capref(buf2, sizeof(buf2), cap2); 211 buf1[sizeof(buf1) - 1] = '\0'; 212 buf2[sizeof(buf2) - 1] = '\0'; 213 cap_destroy(cap1); 214 // Need to revoke and delete pagetable cap here, as otherwise it might get 215 // deleted before we get our hands on it! -SG,2016-11-08 216 cap_revoke(cap2); 217 cap_destroy(cap2); 218 printf("%s rx_caps %"PRIu32" [%s] [%s]\n", get_role_name(), arg, buf1, buf2); 219} 220 221static void rx_buf(struct test_binding *b, const uint8_t *buf, size_t buflen) 222{ 223 224 // make sure we received the correct argument(s) 225 if (memcmp(buf, longstr, strlen(longstr)) || (buflen != strlen(longstr))) { 226 USER_PANIC("received wrong argument in \"buf\" message!\n"); 227 } 228 229 printf("%s rx_buf (%zu bytes)\n", get_role_name(), buflen); 230 231 /** 232 * We exchange roles here: If we are the server, we send now messages to the client. 233 */ 234 if (role == SERVER) { 235 236 printf( 237 "changing roles: server is now sending messages to the client...\n"); 238 // construct local per-binding state 239 struct test_state *myst = malloc(sizeof(struct test_state)); 240 assert(myst != NULL); 241 myst->nextmsg = 0; 242 myst->binding = b; 243 b->st = myst; 244 245 // start sending stuff to the client 246 send_cont(myst); 247 248 } 249} 250 251static struct test_rx_vtbl rx_vtbl = { .basic = rx_basic, .str = rx_str, .caps = 252 rx_caps, .buf = rx_buf, }; 253 254/* ------------------------------ CLIENT ------------------------------ */ 255 256static void bind_cb(void *st, errval_t err, struct test_binding *b) 257{ 258 if (err_is_fail(err)) { 259 USER_PANIC_ERR(err, "bind failed"); 260 } 261 262 printf("client bound!\n"); 263 264 // copy my message receive handler vtable to the binding 265 b->rx_vtbl = rx_vtbl; 266 267 // construct local per-binding state 268 struct test_state *myst = malloc(sizeof(struct test_state)); 269 assert(myst != NULL); 270 myst->nextmsg = 0; 271 myst->binding = b; 272 b->st = myst; 273 274 // start sending stuff to the service 275 send_cont(myst); 276} 277 278static void start_client(void) 279{ 280 iref_t iref; 281 errval_t err; 282 283 printf("client looking up '%s' in name service...\n", my_service_name); 284 err = nameservice_blocking_lookup(my_service_name, &iref); 285 if (err_is_fail(err)) { 286 USER_PANIC_ERR(err, "nameservice_blocking_lookup failed"); 287 } 288 289 printf("client binding to %"PRIuIREF"...\n", iref); 290 /** 291 * We don't use the flounder bind function here to 292 * enforce a binding over the multi-hop interconnect driver. 293 * 294 * This can generally also be achieved by passing the flag 295 * IDC_BIND_FLAG_MULTIHOP. But because founder has a fall back 296 * to UMP in case multi-hop does not work, we do not use the 297 * flag here. 298 * 299 */ 300 struct test_multihop_binding *binding = malloc( 301 sizeof(struct test_multihop_binding)); 302 assert((binding) != NULL); 303 304 err = test_multihop_bind(binding, iref, bind_cb, NULL, 305 get_default_waitset(), IDC_BIND_FLAGS_DEFAULT); 306 assert(err_is_ok(err)); 307 308 if (err_is_fail(err)) { 309 USER_PANIC_ERR(err, "bind failed"); 310 } 311} 312 313/* ------------------------------ SERVER ------------------------------ */ 314 315static void export_cb(void *st, errval_t err, iref_t iref) 316{ 317 if (err_is_fail(err)) { 318 USER_PANIC_ERR(err, "export failed"); 319 } 320 321 printf("service exported at iref %"PRIuIREF"\n", iref); 322 323 // register this iref with the name service 324 err = nameservice_register(my_service_name, iref); 325 if (err_is_fail(err)) { 326 USER_PANIC_ERR(err, "nameservice_register failed"); 327 } 328} 329 330static errval_t connect_cb(void *st, struct test_binding *b) 331{ 332 printf("service got a connection!\n"); 333 334 // copy my message receive handler vtable to the binding 335 b->rx_vtbl = rx_vtbl; 336 337 // accept the connection (we could return an error to refuse it) 338 return SYS_ERR_OK; 339} 340 341static void start_server(void) 342{ 343 errval_t err; 344 345 err = test_export(NULL /* state pointer for connect/export callbacks */, 346 export_cb, connect_cb, get_default_waitset(), 347 IDC_EXPORT_FLAGS_DEFAULT); 348 if (err_is_fail(err)) { 349 USER_PANIC_ERR(err, "export failed"); 350 } 351} 352 353/* ------------------------------ MAIN ------------------------------ */ 354 355int main(int argc, char *argv[]) 356{ 357 errval_t err; 358 359 if (argc == 2 && strcmp(argv[1], "client") == 0) { 360 role = CLIENT; 361 start_client(); 362 } else if (argc == 2 && strcmp(argv[1], "server") == 0) { 363 role = SERVER; 364 start_server(); 365 } else { 366 printf("Usage: %s client|server\n", argv[0]); 367 return EXIT_FAILURE; 368 } 369 370 struct waitset *ws = get_default_waitset(); 371 while (1) { 372 err = event_dispatch(ws); 373 if (err_is_fail(err)) { 374 DEBUG_ERR(err, "in event_dispatch"); 375 break; 376 } 377 } 378 379 return EXIT_FAILURE; 380} 381