1 2/****************************************************************/ 3/* */ 4/* nettest_dlpi.c */ 5/* */ 6/* the actual test routines... */ 7/* */ 8/* send_dlpi_co_stream() perform a CO DLPI stream test */ 9/* recv_dlpi_co_stream() */ 10/* send_dlpi_co_rr() perform a CO DLPI req/res */ 11/* recv_dlpi_co_rr() */ 12/* send_dlpi_cl_stream() perform a CL DLPI stream test */ 13/* recv_dlpi_cl_stream() */ 14/* send_dlpi_cl_rr() perform a CL DLPI req/res */ 15/* recv_dlpi_cl_rr() */ 16/* */ 17/****************************************************************/ 18 19#ifdef HAVE_CONFIG_H 20#include "config.h" 21#endif 22 23#ifdef WANT_DLPI 24char nettest_dlpi_id[]="\ 25@(#)nettest_dlpi.c (c) Copyright 1993,1995,2004 Hewlett-Packard Co. Version 2.4.3"; 26 27#include <sys/types.h> 28#include <fcntl.h> 29#include <errno.h> 30#include <signal.h> 31#include <stdio.h> 32#include <string.h> 33#include <time.h> 34#include <malloc.h> 35#include <sys/stream.h> 36#include <sys/stropts.h> 37#include <sys/poll.h> 38#ifdef __osf__ 39#include <sys/dlpihdr.h> 40#else /* __osf__ */ 41#include <sys/dlpi.h> 42#ifdef __hpux__ 43#include <sys/dlpi_ext.h> 44#endif /* __hpux__ */ 45#endif /* __osf__ */ 46 47#include "netlib.h" 48#include "netsh.h" 49#include "nettest_dlpi.h" 50 51/* these are some variables global to all the DLPI tests. declare */ 52/* them static to make them global only to this file */ 53 54static int 55 rsw_size, /* remote send window size */ 56 rrw_size, /* remote recv window size */ 57 lsw_size, /* local send window size */ 58 lrw_size, /* local recv window size */ 59 req_size = 100, /* request size */ 60 rsp_size = 200, /* response size */ 61 send_size, /* how big are individual sends */ 62 recv_size; /* how big are individual receives */ 63 64int 65 loc_ppa = 4, /* the ppa for the local interface, */ 66 /* as shown as the NM Id in lanscan */ 67 rem_ppa = 4, /* the ppa for the remote interface */ 68 dlpi_sap = 84; /* which 802.2 SAP should we use? */ 69 70char loc_dlpi_device[32] = "/dev/dlpi"; 71char rem_dlpi_device[32] = "/dev/dlpi"; 72 73char dlpi_usage[] = "\n\ 74Usage: netperf [global options] -- [test options] \n\ 75\n\ 76CO/CL DLPI Test Options:\n\ 77 -D dev[,dev] Set the local/remote DLPI device file name\n\ 78 -h Display this text\n\ 79 -M bytes Set the recv size (DLCO_STREAM, DLCL_STREAM)\n\ 80 -m bytes Set the send size (DLCO_STREAM, DLCL_STREAM)\n\ 81 -p loc[,rem] Set the local/remote PPA for the test\n\ 82 -R bytes Set response size (DLCO_RR, DLCL_RR)\n\ 83 -r bytes Set request size (DLCO_RR, DLCL_RR)\n\ 84 -s sap Set the 802.2 sap for the test\n\ 85 -W send[,recv] Set remote send/recv window sizes\n\ 86 -w send[,recv] Set local send/recv window sizes\n\ 87\n\ 88For those options taking two parms, at least one must be specified;\n\ 89specifying one value without a comma will set both parms to that\n\ 90value, specifying a value with a leading comma will set just the second\n\ 91parm, a value with a trailing comma will set just the first. To set\n\ 92each parm to unique values, specify both and separate them with a\n\ 93comma.\n"; 94 95 96/* This routine implements the CO unidirectional data transfer test */ 97/* (a.k.a. stream) for the sockets interface. It receives its */ 98/* parameters via global variables from the shell and writes its */ 99/* output to the standard output. */ 100 101 102void 103send_dlpi_co_stream() 104{ 105 106 char *tput_title = "\ 107Recv Send Send \n\ 108Window Window Message Elapsed \n\ 109Size Size Size Time Throughput \n\ 110frames frames bytes secs. %s/sec \n\n"; 111 112 char *tput_fmt_0 = 113 "%7.2f\n"; 114 115 char *tput_fmt_1 = 116 "%5d %5d %6d %-6.2f %7.2f \n"; 117 118 char *cpu_title = "\ 119Recv Send Send Utilization Service Demand\n\ 120Window Window Message Elapsed Send Recv Send Recv\n\ 121Size Size Size Time Throughput local remote local remote\n\ 122frames frames bytes secs. %-8.8s/s %% %% us/KB us/KB\n\n"; 123 124 char *cpu_fmt_0 = 125 "%6.3f\n"; 126 127 char *cpu_fmt_1 = 128 "%5d %5d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; 129 130 char *ksink_fmt = "\n\ 131Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ 132Local Remote Local Remote Xfered Per Per\n\ 133Send Recv Send Recv Send (avg) Recv (avg)\n\ 134%5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; 135 136 137 float elapsed_time; 138 139#ifdef WANT_INTERVALS 140 int interval_count; 141#endif /* WANT_INTERVALS */ 142 143 /* what we want is to have a buffer space that is at least one */ 144 /* send-size greater than our send window. this will insure that we */ 145 /* are never trying to re-use a buffer that may still be in the hands */ 146 /* of the transport. This buffer will be malloc'd after we have found */ 147 /* the size of the local senc socket buffer. We will want to deal */ 148 /* with alignment and offset concerns as well. */ 149 150 struct ring_elt *send_ring; 151 char *message; 152 char *message_ptr; 153 struct strbuf send_message; 154 char dlsap[BUFSIZ]; 155 int dlsap_len; 156 int *message_int_ptr; 157 int message_offset; 158 int malloc_size; 159 160 int len; 161 int nummessages; 162 int send_descriptor; 163 int bytes_remaining; 164 /* with links like fddi, one can send > 32 bits worth of bytes */ 165 /* during a test... ;-) */ 166 double bytes_sent; 167 168#ifdef DIRTY 169 int i; 170#endif /* DIRTY */ 171 172 float local_cpu_utilization; 173 float local_service_demand; 174 float remote_cpu_utilization; 175 float remote_service_demand; 176 double thruput; 177 178 struct dlpi_co_stream_request_struct *dlpi_co_stream_request; 179 struct dlpi_co_stream_response_struct *dlpi_co_stream_response; 180 struct dlpi_co_stream_results_struct *dlpi_co_stream_result; 181 182 dlpi_co_stream_request = 183 (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; 184 dlpi_co_stream_response = 185 (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; 186 dlpi_co_stream_result = 187 (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; 188 189 if ( print_headers ) { 190 fprintf(where,"DLPI CO STREAM TEST\n"); 191 if (local_cpu_usage || remote_cpu_usage) 192 fprintf(where,cpu_title,format_units()); 193 else 194 fprintf(where,tput_title,format_units()); 195 } 196 197 /* initialize a few counters */ 198 199 nummessages = 0; 200 bytes_sent = 0.0; 201 times_up = 0; 202 203 /*set up the data descriptor */ 204 send_descriptor = dl_open(loc_dlpi_device,loc_ppa); 205 if (send_descriptor < 0){ 206 perror("netperf: send_dlpi_co_stream: dlpi stream data descriptor"); 207 exit(1); 208 } 209 210 /* bind the puppy and get the assigned dlsap */ 211 dlsap_len = BUFSIZ; 212 if (dl_bind(send_descriptor, 213 dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { 214 fprintf(where,"send_dlpi_co_rr: bind failure\n"); 215 fflush(where); 216 exit(1); 217 } 218 219 if (debug) { 220 fprintf(where,"send_dlpi_co_stream: send_descriptor obtained...\n"); 221 } 222 223#ifdef DL_HP_SET_LOCAL_WIN_REQ 224 if (lsw_size > 0) { 225 if (debug > 1) { 226 fprintf(where,"netperf: send_dlpi_co_stream: window send size altered from system default...\n"); 227 fprintf(where," send: %d\n",lsw_size); 228 } 229 } 230 if (lrw_size > 0) { 231 if (debug > 1) { 232 fprintf(where, 233 "netperf: send_dlpi_co_stream: window recv size altered from system default...\n"); 234 fprintf(where," recv: %d\n",lrw_size); 235 } 236 } 237 238 239 /* Now, we will find-out what the size actually became, and report */ 240 /* that back to the user. If the call fails, we will just report a -1 */ 241 /* back to the initiator for the recv buffer size. */ 242 243 244 if (debug) { 245 fprintf(where, 246 "netperf: send_dlpi_co_stream: window sizes determined...\n"); 247 fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); 248 ffluch(where); 249 } 250 251#else /* DL_HP_SET_LOCAL_WIN_REQ */ 252 253 lsw_size = -1; 254 lrw_size = -1; 255 256#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 257 258 /* we should pick a default send_size, it should not be larger than */ 259 /* the min of the two interface MTU's, and should perhaps default to */ 260 /* the Interface MTU, but for now, we will default it to 1024... if */ 261 /* someone wants to change this, the should change the corresponding */ 262 /* lines in the recv_dlpi_co_stream routine */ 263 264 if (send_size == 0) { 265 send_size = 1024; 266 } 267 268 /* set-up the data buffer with the requested alignment and offset. */ 269 /* After we have calculated the proper starting address, we want to */ 270 /* put that back into the message variable so we go back to the */ 271 /* proper place. note that this means that only the first send is */ 272 /* guaranteed to be at the alignment specified by the -a parameter. I */ 273 /* think that this is a little more "real-world" than what was found */ 274 /* in previous versions. note also that we have allocated a quantity */ 275 /* of memory that is at least one send-size greater than our socket */ 276 /* buffer size. We want to be sure that there are at least two */ 277 /* buffers allocated - this can be a bit of a problem when the */ 278 /* send_size is bigger than the socket size, so we must check... the */ 279 /* user may have wanted to explicitly set the "width" of our send */ 280 /* buffers, we should respect that wish... */ 281 if (send_width == 0) { 282 send_width = (lsw_size/send_size) + 1; 283 if (send_width == 1) send_width++; 284 } 285 286 send_ring = allocate_buffer_ring(send_width, 287 send_size, 288 local_send_align, 289 local_send_offset); 290 291 send_message.maxlen = send_size; 292 send_message.len = send_size; 293 send_message.buf = send_ring->buffer_ptr; 294 295 /* If the user has requested cpu utilization measurements, we must */ 296 /* calibrate the cpu(s). We will perform this task within the tests */ 297 /* themselves. If the user has specified the cpu rate, then */ 298 /* calibrate_local_cpu will return rather quickly as it will have */ 299 /* nothing to do. If local_cpu_rate is zero, then we will go through */ 300 /* all the "normal" calibration stuff and return the rate back.*/ 301 302 if (local_cpu_usage) { 303 local_cpu_rate = calibrate_local_cpu(local_cpu_rate); 304 } 305 306 /* Tell the remote end to do a listen. The server alters the socket */ 307 /* paramters on the other side at this point, hence the reason for */ 308 /* all the values being passed in the setup message. If the user did */ 309 /* not specify any of the parameters, they will be passed as 0, which */ 310 /* will indicate to the remote that no changes beyond the system's */ 311 /* default should be used. */ 312 313 netperf_request.content.request_type = DO_DLPI_CO_STREAM; 314 dlpi_co_stream_request->send_win_size = rsw_size; 315 dlpi_co_stream_request->recv_win_size = rrw_size; 316 dlpi_co_stream_request->receive_size = recv_size; 317 dlpi_co_stream_request->recv_alignment= remote_recv_align; 318 dlpi_co_stream_request->recv_offset = remote_recv_offset; 319 dlpi_co_stream_request->measure_cpu = remote_cpu_usage; 320 dlpi_co_stream_request->cpu_rate = remote_cpu_rate; 321 dlpi_co_stream_request->ppa = rem_ppa; 322 dlpi_co_stream_request->sap = dlpi_sap; 323 dlpi_co_stream_request->dev_name_len = strlen(rem_dlpi_device); 324 strcpy(dlpi_co_stream_request->dlpi_device, 325 rem_dlpi_device); 326 327#ifdef __alpha 328 329 /* ok - even on a DEC box, strings are strings. I didn't really want */ 330 /* to ntohl the words of a string. since I don't want to teach the */ 331 /* send_ and recv_ _request and _response routines about the types, */ 332 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 333 /* solution would be to use XDR, but I am still leary of being able */ 334 /* to find XDR libs on all platforms I want running netperf. raj */ 335 { 336 int *charword; 337 int *initword; 338 int *lastword; 339 340 initword = (int *) dlpi_co_stream_request->dlpi_device; 341 lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); 342 343 for (charword = initword; 344 charword < lastword; 345 charword++) { 346 347 *charword = ntohl(*charword); 348 } 349 } 350#endif /* __alpha */ 351 352 if (test_time) { 353 dlpi_co_stream_request->test_length = test_time; 354 } 355 else { 356 dlpi_co_stream_request->test_length = test_bytes; 357 } 358#ifdef DIRTY 359 dlpi_co_stream_request->dirty_count = rem_dirty_count; 360 dlpi_co_stream_request->clean_count = rem_clean_count; 361#endif /* DIRTY */ 362 363 364 if (debug > 1) { 365 fprintf(where, 366 "netperf: send_dlpi_co_stream: requesting DLPI CO stream test\n"); 367 } 368 369 send_request(); 370 371 /* The response from the remote will contain all of the relevant */ 372 /* parameters for this test type. We will put them back into */ 373 /* the variables here so they can be displayed if desired. The */ 374 /* remote will have calibrated CPU if necessary, and will have done */ 375 /* all the needed set-up we will have calibrated the cpu locally */ 376 /* before sending the request, and will grab the counter value right */ 377 /* after the connect returns. The remote will grab the counter right */ 378 /* after the accept call. This saves the hassle of extra messages */ 379 /* being sent for the TCP tests. */ 380 381 recv_response(); 382 383 if (!netperf_response.content.serv_errno) { 384 if (debug) 385 fprintf(where,"remote listen done.\n"); 386 rrw_size = dlpi_co_stream_response->recv_win_size; 387 rsw_size = dlpi_co_stream_response->send_win_size; 388 remote_cpu_usage= dlpi_co_stream_response->measure_cpu; 389 remote_cpu_rate = dlpi_co_stream_response->cpu_rate; 390 } 391 else { 392 Set_errno(netperf_response.content.serv_errno); 393 perror("netperf: remote error"); 394 exit(1); 395 } 396 397 /* Connect up to the remote port on the data descriptor */ 398 if(dl_connect(send_descriptor, 399 dlpi_co_stream_response->station_addr, 400 dlpi_co_stream_response->station_addr_len) != 0) { 401 fprintf(where,"recv_dlpi_co_stream: connect failure\n"); 402 fflush(where); 403 exit(1); 404 } 405 406 /* Data Socket set-up is finished. If there were problems, either the */ 407 /* connect would have failed, or the previous response would have */ 408 /* indicated a problem. I failed to see the value of the extra */ 409 /* message after the accept on the remote. If it failed, we'll see it */ 410 /* here. If it didn't, we might as well start pumping data. */ 411 412 /* Set-up the test end conditions. For a stream test, they can be */ 413 /* either time or byte-count based. */ 414 415 if (test_time) { 416 /* The user wanted to end the test after a period of time. */ 417 times_up = 0; 418 bytes_remaining = 0; 419 start_timer(test_time); 420 } 421 else { 422 /* The tester wanted to send a number of bytes. */ 423 bytes_remaining = test_bytes; 424 times_up = 1; 425 } 426 427 /* The cpu_start routine will grab the current time and possibly */ 428 /* value of the idle counter for later use in measuring cpu */ 429 /* utilization and/or service demand and thruput. */ 430 431 cpu_start(local_cpu_usage); 432 433 /* We use an "OR" to control test execution. When the test is */ 434 /* controlled by time, the byte count check will always return false. */ 435 /* When the test is controlled by byte count, the time test will */ 436 /* always return false. When the test is finished, the whole */ 437 /* expression will go false and we will stop sending data. */ 438 439#ifdef DIRTY 440 /* initialize the random number generator for putting dirty stuff */ 441 /* into the send buffer. raj */ 442 srand((int) getpid()); 443#endif /* DIRTY */ 444 445 while ((!times_up) || (bytes_remaining > 0)) { 446 447#ifdef DIRTY 448 /* we want to dirty some number of consecutive integers in the buffer */ 449 /* we are about to send. we may also want to bring some number of */ 450 /* them cleanly into the cache. The clean ones will follow any dirty */ 451 /* ones into the cache. */ 452 message_int_ptr = (int *)message_ptr; 453 for (i = 0; i < loc_dirty_count; i++) { 454 *message_int_ptr = rand(); 455 message_int_ptr++; 456 } 457 for (i = 0; i < loc_clean_count; i++) { 458 loc_dirty_count = *message_int_ptr; 459 message_int_ptr++; 460 } 461#endif /* DIRTY */ 462 463 if((putmsg(send_descriptor, 464 0, 465 &send_message, 466 0)) != 0) { 467 if (errno == EINTR) 468 break; 469 perror("netperf: data send error"); 470 exit(1); 471 } 472 send_ring = send_ring->next; 473 send_message.buf = send_ring->buffer_ptr; 474#ifdef WANT_INTERVALS 475 for (interval_count = 0; 476 interval_count < interval_wate; 477 interval_count++); 478#endif /* WANT_INTERVALS */ 479 480 if (debug > 4) { 481 fprintf(where,"netperf: send_clpi_co_stream: putmsg called "); 482 fprintf(where,"len is %d\n",send_message.len); 483 fflush(where); 484 } 485 486 nummessages++; 487 if (bytes_remaining) { 488 bytes_remaining -= send_size; 489 } 490 } 491 492 /* The test is over. Flush the buffers to the remote end. We do a */ 493 /* graceful release to insure that all data has been taken by the */ 494 /* remote. this needs a little work - there is no three-way */ 495 /* handshake with type two as there is with TCP, so there really */ 496 /* should be a message exchange here. however, we will finesse it by */ 497 /* saying that the tests shoudl run for a while. */ 498 499 if (debug) { 500 fprintf(where,"sending test end signal \n"); 501 fflush(where); 502 } 503 504 send_message.len = (send_size - 1); 505 if (send_message.len == 0) send_message.len = 2; 506 507 if((putmsg(send_descriptor, 508 0, 509 &send_message, 510 0)) != 0) { 511 perror("netperf: data send error"); 512 exit(1); 513 } 514 515 /* this call will always give us the elapsed time for the test, and */ 516 /* will also store-away the necessaries for cpu utilization */ 517 518 cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ 519 /* how long did we really run? */ 520 521 /* Get the statistics from the remote end. The remote will have */ 522 /* calculated service demand and all those interesting things. If it */ 523 /* wasn't supposed to care, it will return obvious values. */ 524 525 recv_response(); 526 if (!netperf_response.content.serv_errno) { 527 if (debug) 528 fprintf(where,"remote results obtained\n"); 529 } 530 else { 531 Set_errno(netperf_response.content.serv_errno); 532 perror("netperf: remote error"); 533 534 exit(1); 535 } 536 537 /* We now calculate what our thruput was for the test. In the future, */ 538 /* we may want to include a calculation of the thruput measured by */ 539 /* the remote, but it should be the case that for a TCP stream test, */ 540 /* that the two numbers should be *very* close... We calculate */ 541 /* bytes_sent regardless of the way the test length was controlled. */ 542 /* If it was time, we needed to, and if it was by bytes, the user may */ 543 /* have specified a number of bytes that wasn't a multiple of the */ 544 /* send_size, so we really didn't send what he asked for ;-) */ 545 546 bytes_sent = ((double) send_size * (double) nummessages) + (double) len; 547 thruput = calc_thruput(bytes_sent); 548 549 if (local_cpu_usage || remote_cpu_usage) { 550 /* We must now do a little math for service demand and cpu */ 551 /* utilization for the system(s) */ 552 /* Of course, some of the information might be bogus because */ 553 /* there was no idle counter in the kernel(s). We need to make */ 554 /* a note of this for the user's benefit...*/ 555 if (local_cpu_usage) { 556 if (local_cpu_rate == 0.0) { 557 fprintf(where, 558 "WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); 559 fprintf(where, 560 "Local CPU usage numbers based on process information only!\n"); 561 fflush(where); 562 } 563 local_cpu_utilization = calc_cpu_util(0.0); 564 local_service_demand = calc_service_demand(bytes_sent, 565 0.0, 566 0.0, 567 0); 568 } 569 else { 570 local_cpu_utilization = -1.0; 571 local_service_demand = -1.0; 572 } 573 574 if (remote_cpu_usage) { 575 if (remote_cpu_rate == 0.0) { 576 fprintf(where, 577 "DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); 578 fprintf(where, 579 "Remote CPU usage numbers based on process information only!\n"); 580 fflush(where); 581 } 582 remote_cpu_utilization = dlpi_co_stream_result->cpu_util; 583 remote_service_demand = calc_service_demand(bytes_sent, 584 0.0, 585 remote_cpu_utilization, 586 dlpi_co_stream_result->num_cpus); 587 } 588 else { 589 remote_cpu_utilization = -1.0; 590 remote_service_demand = -1.0; 591 } 592 593 /* We are now ready to print all the information. If the user */ 594 /* has specified zero-level verbosity, we will just print the */ 595 /* local service demand, or the remote service demand. If the */ 596 /* user has requested verbosity level 1, he will get the basic */ 597 /* "streamperf" numbers. If the user has specified a verbosity */ 598 /* of greater than 1, we will display a veritable plethora of */ 599 /* background information from outside of this block as it it */ 600 /* not cpu_measurement specific... */ 601 602 switch (verbosity) { 603 case 0: 604 if (local_cpu_usage) { 605 fprintf(where, 606 cpu_fmt_0, 607 local_service_demand); 608 } 609 else { 610 fprintf(where, 611 cpu_fmt_0, 612 remote_service_demand); 613 } 614 break; 615 case 1: 616 case 2: 617 fprintf(where, 618 cpu_fmt_1, /* the format string */ 619 rrw_size, /* remote recvbuf size */ 620 lsw_size, /* local sendbuf size */ 621 send_size, /* how large were the sends */ 622 elapsed_time, /* how long was the test */ 623 thruput, /* what was the xfer rate */ 624 local_cpu_utilization, /* local cpu */ 625 remote_cpu_utilization, /* remote cpu */ 626 local_service_demand, /* local service demand */ 627 remote_service_demand); /* remote service demand */ 628 break; 629 } 630 } 631 else { 632 /* The tester did not wish to measure service demand. */ 633 switch (verbosity) { 634 case 0: 635 fprintf(where, 636 tput_fmt_0, 637 thruput); 638 break; 639 case 1: 640 case 2: 641 fprintf(where, 642 tput_fmt_1, /* the format string */ 643 rrw_size, /* remote recvbuf size */ 644 lsw_size, /* local sendbuf size */ 645 send_size, /* how large were the sends */ 646 elapsed_time, /* how long did it take */ 647 thruput);/* how fast did it go */ 648 break; 649 } 650 } 651 652 /* it would be a good thing to include information about some of the */ 653 /* other parameters that may have been set for this test, but at the */ 654 /* moment, I do not wish to figure-out all the formatting, so I will */ 655 /* just put this comment here to help remind me that it is something */ 656 /* that should be done at a later time. */ 657 658 if (verbosity > 1) { 659 /* The user wanted to know it all, so we will give it to him. */ 660 /* This information will include as much as we can find about */ 661 /* TCP statistics, the alignments of the sends and receives */ 662 /* and all that sort of rot... */ 663 664 fprintf(where, 665 ksink_fmt, 666 "Bytes", 667 "Bytes", 668 "Bytes", 669 local_send_align, 670 remote_recv_align, 671 local_send_offset, 672 remote_recv_offset, 673 bytes_sent, 674 bytes_sent / (double)nummessages, 675 nummessages, 676 bytes_sent / (double)dlpi_co_stream_result->recv_calls, 677 dlpi_co_stream_result->recv_calls); 678 } 679 680} 681 682 683/* This is the server-side routine for the tcp stream test. It is */ 684/* implemented as one routine. I could break things-out somewhat, but */ 685/* didn't feel it was necessary. */ 686 687int 688 recv_dlpi_co_stream() 689{ 690 691 int data_descriptor; 692 int flags = 0; 693 int measure_cpu; 694 int bytes_received; 695 int receive_calls; 696 float elapsed_time; 697 698 struct ring_elt *recv_ring; 699 char *message_ptr; 700 char *message; 701 int *message_int_ptr; 702 struct strbuf recv_message; 703 int dirty_count; 704 int clean_count; 705 int i; 706 707 struct dlpi_co_stream_request_struct *dlpi_co_stream_request; 708 struct dlpi_co_stream_response_struct *dlpi_co_stream_response; 709 struct dlpi_co_stream_results_struct *dlpi_co_stream_results; 710 711 dlpi_co_stream_request = (struct dlpi_co_stream_request_struct *)netperf_request.content.test_specific_data; 712 dlpi_co_stream_response = (struct dlpi_co_stream_response_struct *)netperf_response.content.test_specific_data; 713 dlpi_co_stream_results = (struct dlpi_co_stream_results_struct *)netperf_response.content.test_specific_data; 714 715 if (debug) { 716 fprintf(where,"netserver: recv_dlpi_co_stream: entered...\n"); 717 fflush(where); 718 } 719 720 /* We want to set-up the listen socket with all the desired */ 721 /* parameters and then let the initiator know that all is ready. If */ 722 /* socket size defaults are to be used, then the initiator will have */ 723 /* sent us 0's. If the socket sizes cannot be changed, then we will */ 724 /* send-back what they are. If that information cannot be determined, */ 725 /* then we send-back -1's for the sizes. If things go wrong for any */ 726 /* reason, we will drop back ten yards and punt. */ 727 728 /* If anything goes wrong, we want the remote to know about it. It */ 729 /* would be best if the error that the remote reports to the user is */ 730 /* the actual error we encountered, rather than some bogus unexpected */ 731 /* response type message. */ 732 733 netperf_response.content.response_type = DLPI_CO_STREAM_RESPONSE; 734 735 /* We now alter the message_ptr variable to be at the desired */ 736 /* alignment with the desired offset. */ 737 738 if (debug > 1) { 739 fprintf(where,"recv_dlpi_co_stream: requested alignment of %d\n", 740 dlpi_co_stream_request->recv_alignment); 741 fflush(where); 742 } 743 744 745 /* Grab a descriptor to listen on, and then listen on it. */ 746 747 if (debug > 1) { 748 fprintf(where,"recv_dlpi_co_stream: grabbing a descriptor...\n"); 749 fflush(where); 750 } 751 752 753 754#ifdef __alpha 755 756 /* ok - even on a DEC box, strings are strings. I din't really want */ 757 /* to ntohl the words of a string. since I don't want to teach the */ 758 /* send_ and recv_ _request and _response routines about the types, */ 759 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 760 /* solution would be to use XDR, but I am still leary of being able */ 761 /* to find XDR libs on all platforms I want running netperf. raj */ 762 { 763 int *charword; 764 int *initword; 765 int *lastword; 766 767 initword = (int *) dlpi_co_stream_request->dlpi_device; 768 lastword = initword + ((dlpi_co_stream_request->dev_name_len + 3) / 4); 769 770 for (charword = initword; 771 charword < lastword; 772 charword++) { 773 774 *charword = htonl(*charword); 775 } 776 } 777#endif /* __alpha */ 778 779 data_descriptor = dl_open(dlpi_co_stream_request->dlpi_device, 780 dlpi_co_stream_request->ppa); 781 if (data_descriptor < 0) { 782 netperf_response.content.serv_errno = errno; 783 send_response(); 784 exit(1); 785 } 786 787 /* Let's get an address assigned to this descriptor so we can tell the */ 788 /* initiator how to reach the data descriptor. There may be a desire to */ 789 /* nail this descriptor to a specific address in a multi-homed, */ 790 /* multi-connection situation, but for now, we'll ignore the issue */ 791 /* and concentrate on single connection testing. */ 792 793 /* bind the sap and retrieve the dlsap assigned by the system */ 794 dlpi_co_stream_response->station_addr_len = 14; /* arbitrary */ 795 if (dl_bind(data_descriptor, 796 dlpi_co_stream_request->sap, 797 DL_CODLS, 798 (char *)dlpi_co_stream_response->station_addr, 799 &dlpi_co_stream_response->station_addr_len) != 0) { 800 fprintf(where,"recv_dlpi_co_stream: bind failure\n"); 801 fflush(where); 802 exit(1); 803 } 804 805 /* The initiator may have wished-us to modify the socket buffer */ 806 /* sizes. We should give it a shot. If he didn't ask us to change the */ 807 /* sizes, we should let him know what sizes were in use at this end. */ 808 /* If none of this code is compiled-in, then we will tell the */ 809 /* initiator that we were unable to play with the socket buffer by */ 810 /* setting the size in the response to -1. */ 811 812#ifdef DL_HP_SET_LOCAL_WIN_REQ 813 814 if (dlpi_co_stream_request->recv_win_size) { 815 } 816 /* Now, we will find-out what the size actually became, and report */ 817 /* that back to the user. If the call fails, we will just report a -1 */ 818 /* back to the initiator for the recv buffer size. */ 819 820#else /* the system won't let us play with the buffers */ 821 822 dlpi_co_stream_response->recv_win_size = -1; 823 824#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 825 826 /* what sort of sizes did we end-up with? */ 827 /* this bit of code whould default to the Interface MTU */ 828 if (dlpi_co_stream_request->receive_size == 0) { 829 recv_size = 1024; 830 } 831 else { 832 recv_size = dlpi_co_stream_request->receive_size; 833 } 834 835 /* tell the other fellow what our receive size became */ 836 dlpi_co_stream_response->receive_size = recv_size; 837 838 /* just a little prep work for when we may have to behave like the */ 839 /* sending side... */ 840 message = (char *)malloc(recv_size * 2); 841 if (message == NULL) { 842 printf("malloc(%d) failed!\n", recv_size * 2); 843 exit(1); 844 } 845 846 message_ptr = ALIGN_BUFFER(message, dlpi_co_stream_request->recv_alignment, dlpi_co_stream_request->recv_offset); 847 recv_message.maxlen = recv_size; 848 recv_message.len = 0; 849 recv_message.buf = message_ptr; 850 851 if (debug > 1) { 852 fprintf(where, 853 "recv_dlpi_co_stream: receive alignment and offset set...\n"); 854 fflush(where); 855 } 856 857 netperf_response.content.serv_errno = 0; 858 859 /* But wait, there's more. If the initiator wanted cpu measurements, */ 860 /* then we must call the calibrate routine, which will return the max */ 861 /* rate back to the initiator. If the CPU was not to be measured, or */ 862 /* something went wrong with the calibration, we will return a -1 to */ 863 /* the initiator. */ 864 865 dlpi_co_stream_response->cpu_rate = 0.0; /* assume no cpu */ 866 if (dlpi_co_stream_request->measure_cpu) { 867 dlpi_co_stream_response->measure_cpu = 1; 868 dlpi_co_stream_response->cpu_rate = 869 calibrate_local_cpu(dlpi_co_stream_request->cpu_rate); 870 } 871 872 send_response(); 873 874 /* accept a connection on this file descriptor. at some point, */ 875 /* dl_accept will "do the right thing" with the last two parms, but */ 876 /* for now it ignores them, so we will pass zeros. */ 877 878 if(dl_accept(data_descriptor, 0, 0) != 0) { 879 fprintf(where, 880 "recv_dlpi_co_stream: error in accept, errno %d\n", 881 errno); 882 fflush(where); 883 netperf_response.content.serv_errno = errno; 884 send_response(); 885 exit(1); 886 } 887 888 if (debug) { 889 fprintf(where,"netserver:recv_dlpi_co_stream: connection accepted\n"); 890 fflush(where); 891 } 892 893 /* Now it's time to start receiving data on the connection. We will */ 894 /* first grab the apropriate counters and then start grabbing. */ 895 896 cpu_start(dlpi_co_stream_request->measure_cpu); 897 898#ifdef DIRTY 899 /* we want to dirty some number of consecutive integers in the buffer */ 900 /* we are about to recv. we may also want to bring some number of */ 901 /* them cleanly into the cache. The clean ones will follow any dirty */ 902 /* ones into the cache. */ 903 904 dirty_count = dlpi_co_stream_request->dirty_count; 905 clean_count = dlpi_co_stream_request->clean_count; 906 message_int_ptr = (int *)message_ptr; 907 for (i = 0; i < dirty_count; i++) { 908 *message_int_ptr = rand(); 909 message_int_ptr++; 910 } 911 for (i = 0; i < clean_count; i++) { 912 dirty_count = *message_int_ptr; 913 message_int_ptr++; 914 } 915#endif /* DIRTY */ 916 917 recv_message.len = recv_size; 918 while (recv_message.len == recv_size) { 919 if (getmsg(data_descriptor, 920 0, 921 &recv_message, 922 &flags) != 0) { 923 netperf_response.content.serv_errno = errno; 924 send_response(); 925 exit(1); 926 } 927 bytes_received += recv_message.len; 928 receive_calls++; 929 930 if (debug) { 931 fprintf(where, 932 "netserver:recv_dlpi_co_stream: getmsg accepted %d bytes\n", 933 recv_message.len); 934 fflush(where); 935 } 936 937 938#ifdef DIRTY 939 message_int_ptr = (int *)message_ptr; 940 for (i = 0; i < dirty_count; i++) { 941 *message_int_ptr = rand(); 942 message_int_ptr++; 943 } 944 for (i = 0; i < clean_count; i++) { 945 dirty_count = *message_int_ptr; 946 message_int_ptr++; 947 } 948#endif /* DIRTY */ 949 950 } 951 952 /* The loop now exits due to zero bytes received. */ 953 /* should perform a disconnect to signal the sender that */ 954 /* we have received all the data sent. */ 955 956 if (close(data_descriptor) == -1) { 957 netperf_response.content.serv_errno = errno; 958 send_response(); 959 exit(1); 960 } 961 962 cpu_stop(dlpi_co_stream_request->measure_cpu,&elapsed_time); 963 964 /* send the results to the sender */ 965 966 if (debug) { 967 fprintf(where, 968 "recv_dlpi_co_stream: got %d bytes\n", 969 bytes_received); 970 fprintf(where, 971 "recv_dlpi_co_stream: got %d recvs\n", 972 receive_calls); 973 fflush(where); 974 } 975 976 dlpi_co_stream_results->bytes_received = bytes_received; 977 dlpi_co_stream_results->elapsed_time = elapsed_time; 978 dlpi_co_stream_results->recv_calls = receive_calls; 979 980 if (dlpi_co_stream_request->measure_cpu) { 981 dlpi_co_stream_results->cpu_util = calc_cpu_util(0.0); 982 }; 983 984 if (debug > 1) { 985 fprintf(where, 986 "recv_dlpi_co_stream: test complete, sending results.\n"); 987 fflush(where); 988 } 989 990 send_response(); 991} 992 993/*********************************/ 994 995int send_dlpi_co_rr(char remote_host[]) 996{ 997 998 char *tput_title = "\ 999 Local /Remote\n\ 1000 Window Size Request Resp. Elapsed Trans.\n\ 1001 Send Recv Size Size Time Rate \n\ 1002 frames frames bytes bytes secs. per sec \n\n"; 1003 1004 char *tput_fmt_0 = 1005 "%7.2f\n"; 1006 1007 char *tput_fmt_1_line_1 = "\ 1008 %-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; 1009 char *tput_fmt_1_line_2 = "\ 1010 %-6d %-6d\n"; 1011 1012 char *cpu_title = "\ 1013 Local /Remote\n\ 1014 Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ 1015 Send Recv Size Size Time Rate local remote local remote\n\ 1016 frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; 1017 1018 char *cpu_fmt_0 = 1019 "%6.3f\n"; 1020 1021 char *cpu_fmt_1_line_1 = "\ 1022 %-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; 1023 1024 char *cpu_fmt_1_line_2 = "\ 1025 %-6d %-6d\n"; 1026 1027 char *ksink_fmt = "\ 1028 Alignment Offset\n\ 1029 Local Remote Local Remote\n\ 1030 Send Recv Send Recv\n\ 1031 %5d %5d %5d %5d\n"; 1032 1033 1034 int timed_out = 0; 1035 float elapsed_time; 1036 int dlsap_len; 1037 char dlsap[BUFSIZ]; 1038 1039 int flags = 0; 1040 char *send_message_ptr; 1041 char *recv_message_ptr; 1042 char *temp_message_ptr; 1043 struct strbuf send_message; 1044 struct strbuf recv_message; 1045 1046 int nummessages; 1047 int send_descriptor; 1048 int trans_remaining; 1049 double bytes_xferd; 1050 1051 int rsp_bytes_left; 1052 1053 /* we assume that station adresses fit within two ints */ 1054 unsigned int remote_address[1]; 1055 1056 float local_cpu_utilization; 1057 float local_service_demand; 1058 float remote_cpu_utilization; 1059 float remote_service_demand; 1060 double thruput; 1061 1062 struct dlpi_co_rr_request_struct *dlpi_co_rr_request; 1063 struct dlpi_co_rr_response_struct *dlpi_co_rr_response; 1064 struct dlpi_co_rr_results_struct *dlpi_co_rr_result; 1065 1066 dlpi_co_rr_request = 1067 (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; 1068 dlpi_co_rr_response = 1069 (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; 1070 dlpi_co_rr_result = 1071 (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; 1072 1073 /* since we are now disconnected from the code that established the */ 1074 /* control socket, and since we want to be able to use different */ 1075 /* protocols and such, we are passed the name of the remote host and */ 1076 /* must turn that into the test specific addressing information. */ 1077 1078 if ( print_headers ) { 1079 fprintf(where,"DLPI CO REQUEST/RESPONSE TEST\n"); 1080 if (local_cpu_usage || remote_cpu_usage) 1081 fprintf(where,cpu_title,format_units()); 1082 else 1083 fprintf(where,tput_title,format_units()); 1084 } 1085 1086 /* initialize a few counters */ 1087 1088 nummessages = 0; 1089 bytes_xferd = 0.0; 1090 times_up = 0; 1091 1092 /* set-up the data buffers with the requested alignment and offset */ 1093 temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); 1094 if (temp_message_ptr == NULL) { 1095 printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); 1096 exit(1); 1097 } 1098 send_message_ptr = (char *)(( (long) temp_message_ptr + 1099 (long) local_send_align - 1) & 1100 ~((long) local_send_align - 1)); 1101 send_message_ptr = send_message_ptr + local_send_offset; 1102 send_message.maxlen = req_size+MAXALIGNMENT+MAXOFFSET; 1103 send_message.len = req_size; 1104 send_message.buf = send_message_ptr; 1105 1106 temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); 1107 if (temp_message_ptr == NULL) { 1108 printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); 1109 exit(1); 1110 } 1111 recv_message_ptr = (char *)(( (long) temp_message_ptr + 1112 (long) local_recv_align - 1) & 1113 ~((long) local_recv_align - 1)); 1114 recv_message_ptr = recv_message_ptr + local_recv_offset; 1115 recv_message.maxlen = rsp_size+MAXALIGNMENT+MAXOFFSET; 1116 recv_message.len = 0; 1117 recv_message.buf = send_message_ptr; 1118 1119 /*set up the data socket */ 1120 1121 send_descriptor = dl_open(loc_dlpi_device,loc_ppa); 1122 if (send_descriptor < 0){ 1123 perror("netperf: send_dlpi_co_rr: tcp stream data descriptor"); 1124 exit(1); 1125 } 1126 1127 if (debug) { 1128 fprintf(where,"send_dlpi_co_rr: send_descriptor obtained...\n"); 1129 } 1130 1131 /* bind the puppy and get the assigned dlsap */ 1132 1133 dlsap_len = BUFSIZ; 1134 if (dl_bind(send_descriptor, 1135 dlpi_sap, DL_CODLS, dlsap, &dlsap_len) != 0) { 1136 fprintf(where,"send_dlpi_co_rr: bind failure\n"); 1137 fflush(where); 1138 exit(1); 1139 } 1140 1141 /* Modify the local socket size. The reason we alter the send buffer */ 1142 /* size here rather than when the connection is made is to take care */ 1143 /* of decreases in buffer size. Decreasing the window size after */ 1144 /* connection establishment is a TCP no-no. Also, by setting the */ 1145 /* buffer (window) size before the connection is established, we can */ 1146 /* control the TCP MSS (segment size). The MSS is never more that 1/2 */ 1147 /* the minimum receive buffer size at each half of the connection. */ 1148 /* This is why we are altering the receive buffer size on the sending */ 1149 /* size of a unidirectional transfer. If the user has not requested */ 1150 /* that the socket buffers be altered, we will try to find-out what */ 1151 /* their values are. If we cannot touch the socket buffer in any way, */ 1152 /* we will set the values to -1 to indicate that. */ 1153 1154#ifdef DL_HP_SET_LOCAL_WIN_REQ 1155 if (lsw_size > 0) { 1156 if (debug > 1) { 1157 fprintf(where,"netperf: send_dlpi_co_rr: socket send size altered from system default...\n"); 1158 fprintf(where," send: %d\n",lsw_size); 1159 } 1160 } 1161 if (lrw_size > 0) { 1162 if (debug > 1) { 1163 fprintf(where,"netperf: send_dlpi_co_rr: socket recv size altered from system default...\n"); 1164 fprintf(where," recv: %d\n",lrw_size); 1165 } 1166 } 1167 1168 1169 /* Now, we will find-out what the size actually became, and report */ 1170 /* that back to the user. If the call fails, we will just report a -1 */ 1171 /* back to the initiator for the recv buffer size. */ 1172 1173 1174 if (debug) { 1175 fprintf(where,"netperf: send_dlpi_co_rr: socket sizes determined...\n"); 1176 fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); 1177 } 1178 1179#else /* DL_HP_SET_LOCAL_WIN_REQ */ 1180 1181 lsw_size = -1; 1182 lrw_size = -1; 1183 1184#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 1185 1186 /* If the user has requested cpu utilization measurements, we must */ 1187 /* calibrate the cpu(s). We will perform this task within the tests */ 1188 /* themselves. If the user has specified the cpu rate, then */ 1189 /* calibrate_local_cpu will return rather quickly as it will have */ 1190 /* nothing to do. If local_cpu_rate is zero, then we will go through */ 1191 /* all the "normal" calibration stuff and return the rate back.*/ 1192 1193 if (local_cpu_usage) { 1194 local_cpu_rate = calibrate_local_cpu(local_cpu_rate); 1195 } 1196 1197 /* Tell the remote end to do a listen. The server alters the socket */ 1198 /* paramters on the other side at this point, hence the reason for */ 1199 /* all the values being passed in the setup message. If the user did */ 1200 /* not specify any of the parameters, they will be passed as 0, which */ 1201 /* will indicate to the remote that no changes beyond the system's */ 1202 /* default should be used. Alignment is the exception, it will */ 1203 /* default to 8, which will be no alignment alterations. */ 1204 1205 netperf_request.content.request_type = DO_DLPI_CO_RR; 1206 dlpi_co_rr_request->recv_win_size = rrw_size; 1207 dlpi_co_rr_request->send_win_size = rsw_size; 1208 dlpi_co_rr_request->recv_alignment = remote_recv_align; 1209 dlpi_co_rr_request->recv_offset = remote_recv_offset; 1210 dlpi_co_rr_request->send_alignment = remote_send_align; 1211 dlpi_co_rr_request->send_offset = remote_send_offset; 1212 dlpi_co_rr_request->request_size = req_size; 1213 dlpi_co_rr_request->response_size = rsp_size; 1214 dlpi_co_rr_request->measure_cpu = remote_cpu_usage; 1215 dlpi_co_rr_request->cpu_rate = remote_cpu_rate; 1216 dlpi_co_rr_request->ppa = rem_ppa; 1217 dlpi_co_rr_request->sap = dlpi_sap; 1218 dlpi_co_rr_request->dev_name_len = strlen(rem_dlpi_device); 1219 strcpy(dlpi_co_rr_request->dlpi_device, 1220 rem_dlpi_device); 1221#ifdef __alpha 1222 1223 /* ok - even on a DEC box, strings are strings. I din't really want */ 1224 /* to ntohl the words of a string. since I don't want to teach the */ 1225 /* send_ and recv_ _request and _response routines about the types, */ 1226 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 1227 /* solution would be to use XDR, but I am still leary of being able */ 1228 /* to find XDR libs on all platforms I want running netperf. raj */ 1229 { 1230 int *charword; 1231 int *initword; 1232 int *lastword; 1233 1234 initword = (int *) dlpi_co_rr_request->dlpi_device; 1235 lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); 1236 1237 for (charword = initword; 1238 charword < lastword; 1239 charword++) { 1240 1241 *charword = ntohl(*charword); 1242 } 1243 } 1244#endif /* __alpha */ 1245 1246 if (test_time) { 1247 dlpi_co_rr_request->test_length = test_time; 1248 } 1249 else { 1250 dlpi_co_rr_request->test_length = test_trans * -1; 1251 } 1252 1253 if (debug > 1) { 1254 fprintf(where,"netperf: send_dlpi_co_rr: requesting TCP stream test\n"); 1255 } 1256 1257 send_request(); 1258 1259 /* The response from the remote will contain all of the relevant */ 1260 /* socket parameters for this test type. We will put them back into */ 1261 /* the variables here so they can be displayed if desired. The */ 1262 /* remote will have calibrated CPU if necessary, and will have done */ 1263 /* all the needed set-up we will have calibrated the cpu locally */ 1264 /* before sending the request, and will grab the counter value right */ 1265 /* after the connect returns. The remote will grab the counter right */ 1266 /* after the accept call. This saves the hassle of extra messages */ 1267 /* being sent for the TCP tests. */ 1268 1269 recv_response(); 1270 1271 if (!netperf_response.content.serv_errno) { 1272 if (debug) 1273 fprintf(where,"remote listen done.\n"); 1274 rrw_size = dlpi_co_rr_response->recv_win_size; 1275 rsw_size = dlpi_co_rr_response->send_win_size; 1276 remote_cpu_usage= dlpi_co_rr_response->measure_cpu; 1277 remote_cpu_rate = dlpi_co_rr_response->cpu_rate; 1278 1279 } 1280 else { 1281 Set_errno(netperf_response.content.serv_errno); 1282 perror("netperf: remote error"); 1283 1284 exit(1); 1285 } 1286 1287 /*Connect up to the remote port on the data descriptor */ 1288 1289 if(dl_connect(send_descriptor, 1290 dlpi_co_rr_response->station_addr, 1291 dlpi_co_rr_response->station_addr_len) != 0) { 1292 fprintf(where,"send_dlpi_co_rr: connect failure\n"); 1293 fflush(where); 1294 exit(1); 1295 } 1296 1297 /* Data Socket set-up is finished. If there were problems, either the */ 1298 /* connect would have failed, or the previous response would have */ 1299 /* indicated a problem. I failed to see the value of the extra */ 1300 /* message after the accept on the remote. If it failed, we'll see it */ 1301 /* here. If it didn't, we might as well start pumping data. */ 1302 1303 /* Set-up the test end conditions. For a request/response test, they */ 1304 /* can be either time or transaction based. */ 1305 1306 if (test_time) { 1307 /* The user wanted to end the test after a period of time. */ 1308 times_up = 0; 1309 trans_remaining = 0; 1310 start_timer(test_time); 1311 } 1312 else { 1313 /* The tester wanted to send a number of bytes. */ 1314 trans_remaining = test_bytes; 1315 times_up = 1; 1316 } 1317 1318 /* The cpu_start routine will grab the current time and possibly */ 1319 /* value of the idle counter for later use in measuring cpu */ 1320 /* utilization and/or service demand and thruput. */ 1321 1322 cpu_start(local_cpu_usage); 1323 1324 /* We use an "OR" to control test execution. When the test is */ 1325 /* controlled by time, the byte count check will always return false. */ 1326 /* When the test is controlled by byte count, the time test will */ 1327 /* always return false. When the test is finished, the whole */ 1328 /* expression will go false and we will stop sending data. I think I */ 1329 /* just arbitrarily decrement trans_remaining for the timed test, but */ 1330 /* will not do that just yet... One other question is whether or not */ 1331 /* the send buffer and the receive buffer should be the same buffer. */ 1332 1333 while ((!times_up) || (trans_remaining > 0)) { 1334 /* send the request */ 1335 if((putmsg(send_descriptor, 1336 0, 1337 &send_message, 1338 0)) != 0) { 1339 if (errno == EINTR) { 1340 /* we hit the end of a */ 1341 /* timed test. */ 1342 timed_out = 1; 1343 break; 1344 } 1345 perror("send_dlpi_co_rr: putmsg error"); 1346 exit(1); 1347 } 1348 1349 if (debug) { 1350 fprintf(where,"recv_message.len %d\n",recv_message.len); 1351 fprintf(where,"send_message.len %d\n",send_message.len); 1352 fflush(where); 1353 } 1354 1355 /* receive the response */ 1356 /* this needs some work with streams buffers if we are going to */ 1357 /* support requests and responses larger than the MTU of the */ 1358 /* network, but this can wait until later */ 1359 rsp_bytes_left = rsp_size; 1360 recv_message.len = rsp_size; 1361 while(rsp_bytes_left > 0) { 1362 if((getmsg(send_descriptor, 1363 0, 1364 &recv_message, 1365 &flags)) < 0) { 1366 if (errno == EINTR) { 1367 /* We hit the end of a timed test. */ 1368 timed_out = 1; 1369 break; 1370 } 1371 perror("send_dlpi_co_rr: data recv error"); 1372 exit(1); 1373 } 1374 rsp_bytes_left -= recv_message.len; 1375 } 1376 1377 if (timed_out) { 1378 /* we may have been in a nested while loop - we need */ 1379 /* another call to break. */ 1380 break; 1381 } 1382 1383 nummessages++; 1384 if (trans_remaining) { 1385 trans_remaining--; 1386 } 1387 1388 if (debug > 3) { 1389 fprintf(where, 1390 "Transaction %d completed\n", 1391 nummessages); 1392 fflush(where); 1393 } 1394 } 1395 1396 /* At this point we used to call shutdown onthe data socket to be */ 1397 /* sure all the data was delivered, but this was not germane in a */ 1398 /* request/response test, and it was causing the tests to "hang" when */ 1399 /* they were being controlled by time. So, I have replaced this */ 1400 /* shutdown call with a call to close that can be found later in the */ 1401 /* procedure. */ 1402 1403 /* this call will always give us the elapsed time for the test, and */ 1404 /* will also store-away the necessaries for cpu utilization */ 1405 1406 cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ 1407 /* how long did we really run? */ 1408 1409 /* Get the statistics from the remote end. The remote will have */ 1410 /* calculated service demand and all those interesting things. If it */ 1411 /* wasn't supposed to care, it will return obvious values. */ 1412 1413 recv_response(); 1414 if (!netperf_response.content.serv_errno) { 1415 if (debug) 1416 fprintf(where,"remote results obtained\n"); 1417 } 1418 else { 1419 Set_errno(netperf_response.content.serv_errno); 1420 perror("netperf: remote error"); 1421 1422 exit(1); 1423 } 1424 1425 /* We now calculate what our thruput was for the test. In the future, */ 1426 /* we may want to include a calculation of the thruput measured by */ 1427 /* the remote, but it should be the case that for a TCP stream test, */ 1428 /* that the two numbers should be *very* close... We calculate */ 1429 /* bytes_sent regardless of the way the test length was controlled. */ 1430 /* If it was time, we needed to, and if it was by bytes, the user may */ 1431 /* have specified a number of bytes that wasn't a multiple of the */ 1432 /* send_size, so we really didn't send what he asked for ;-) We use */ 1433 /* Kbytes/s as the units of thruput for a TCP stream test, where K = */ 1434 /* 1024. A future enhancement *might* be to choose from a couple of */ 1435 /* unit selections. */ 1436 1437 bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); 1438 thruput = calc_thruput(bytes_xferd); 1439 1440 if (local_cpu_usage || remote_cpu_usage) { 1441 /* We must now do a little math for service demand and cpu */ 1442 /* utilization for the system(s) */ 1443 /* Of course, some of the information might be bogus because */ 1444 /* there was no idle counter in the kernel(s). We need to make */ 1445 /* a note of this for the user's benefit...*/ 1446 if (local_cpu_usage) { 1447 if (local_cpu_rate == 0.0) { 1448 fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); 1449 fprintf(where,"Local CPU usage numbers based on process information only!\n"); 1450 fflush(where); 1451 } 1452 local_cpu_utilization = calc_cpu_util(0.0); 1453 /* since calc_service demand is doing ms/Kunit we will */ 1454 /* multiply the number of transaction by 1024 to get */ 1455 /* "good" numbers */ 1456 local_service_demand = calc_service_demand((double) nummessages*1024, 1457 0.0, 1458 0.0, 1459 0); 1460 } 1461 else { 1462 local_cpu_utilization = -1.0; 1463 local_service_demand = -1.0; 1464 } 1465 1466 if (remote_cpu_usage) { 1467 if (remote_cpu_rate == 0.0) { 1468 fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); 1469 fprintf(where,"Remote CPU usage numbers based on process information only!\n"); 1470 fflush(where); 1471 } 1472 remote_cpu_utilization = dlpi_co_rr_result->cpu_util; 1473 /* since calc_service demand is doing ms/Kunit we will */ 1474 /* multiply the number of transaction by 1024 to get */ 1475 /* "good" numbers */ 1476 remote_service_demand = calc_service_demand((double) nummessages*1024, 1477 0.0, 1478 remote_cpu_utilization, 1479 dlpi_co_rr_result->num_cpus); 1480 } 1481 else { 1482 remote_cpu_utilization = -1.0; 1483 remote_service_demand = -1.0; 1484 } 1485 1486 /* We are now ready to print all the information. If the user */ 1487 /* has specified zero-level verbosity, we will just print the */ 1488 /* local service demand, or the remote service demand. If the */ 1489 /* user has requested verbosity level 1, he will get the basic */ 1490 /* "streamperf" numbers. If the user has specified a verbosity */ 1491 /* of greater than 1, we will display a veritable plethora of */ 1492 /* background information from outside of this block as it it */ 1493 /* not cpu_measurement specific... */ 1494 1495 switch (verbosity) { 1496 case 0: 1497 if (local_cpu_usage) { 1498 fprintf(where, 1499 cpu_fmt_0, 1500 local_service_demand); 1501 } 1502 else { 1503 fprintf(where, 1504 cpu_fmt_0, 1505 remote_service_demand); 1506 } 1507 break; 1508 case 1: 1509 fprintf(where, 1510 cpu_fmt_1_line_1, /* the format string */ 1511 lsw_size, /* local sendbuf size */ 1512 lrw_size, 1513 req_size, /* how large were the requests */ 1514 rsp_size, /* guess */ 1515 elapsed_time, /* how long was the test */ 1516 nummessages/elapsed_time, 1517 local_cpu_utilization, /* local cpu */ 1518 remote_cpu_utilization, /* remote cpu */ 1519 local_service_demand, /* local service demand */ 1520 remote_service_demand); /* remote service demand */ 1521 fprintf(where, 1522 cpu_fmt_1_line_2, 1523 rsw_size, 1524 rrw_size); 1525 break; 1526 } 1527 } 1528 else { 1529 /* The tester did not wish to measure service demand. */ 1530 switch (verbosity) { 1531 case 0: 1532 fprintf(where, 1533 tput_fmt_0, 1534 nummessages/elapsed_time); 1535 break; 1536 case 1: 1537 fprintf(where, 1538 tput_fmt_1_line_1, /* the format string */ 1539 lsw_size, 1540 lrw_size, 1541 req_size, /* how large were the requests */ 1542 rsp_size, /* how large were the responses */ 1543 elapsed_time, /* how long did it take */ 1544 nummessages/elapsed_time); 1545 fprintf(where, 1546 tput_fmt_1_line_2, 1547 rsw_size, /* remote recvbuf size */ 1548 rrw_size); 1549 1550 break; 1551 } 1552 } 1553 1554 /* it would be a good thing to include information about some of the */ 1555 /* other parameters that may have been set for this test, but at the */ 1556 /* moment, I do not wish to figure-out all the formatting, so I will */ 1557 /* just put this comment here to help remind me that it is something */ 1558 /* that should be done at a later time. */ 1559 1560 if (verbosity > 1) { 1561 /* The user wanted to know it all, so we will give it to him. */ 1562 /* This information will include as much as we can find about */ 1563 /* TCP statistics, the alignments of the sends and receives */ 1564 /* and all that sort of rot... */ 1565 1566 fprintf(where, 1567 ksink_fmt); 1568 } 1569 /* The test is over. Kill the data descriptor */ 1570 1571 if (close(send_descriptor) == -1) { 1572 perror("send_dlpi_co_rr: cannot shutdown tcp stream descriptor"); 1573 } 1574 1575} 1576 1577void 1578 send_dlpi_cl_stream(char remote_host[]) 1579{ 1580 /************************************************************************/ 1581 /* */ 1582 /* UDP Unidirectional Send Test */ 1583 /* */ 1584 /************************************************************************/ 1585 char *tput_title = 1586 "Window Message Elapsed Messages \n\ 1587Size Size Time Okay Errors Throughput\n\ 1588frames bytes secs # # %s/sec\n\n"; 1589 1590 char *tput_fmt_0 = 1591 "%7.2f\n"; 1592 1593 char *tput_fmt_1 = 1594 "%5d %5d %-7.2f %7d %6d %7.2f\n\ 1595%5d %-7.2f %7d %7.2f\n\n"; 1596 1597 1598 char *cpu_title = 1599 "Window Message Elapsed Messages CPU Service\n\ 1600Size Size Time Okay Errors Throughput Util Demand\n\ 1601frames bytes secs # # %s/sec %% us/KB\n\n"; 1602 1603 char *cpu_fmt_0 = 1604 "%6.2f\n"; 1605 1606 char *cpu_fmt_1 = 1607 "%5d %5d %-7.2f %7d %6d %7.1f %-6.2f %-6.3f\n\ 1608%5d %-7.2f %7d %7.1f %-6.2f %-6.3f\n\n"; 1609 1610 int messages_recvd; 1611 float elapsed_time, 1612 local_cpu_utilization, 1613 remote_cpu_utilization; 1614 1615 float local_service_demand, remote_service_demand; 1616 double local_thruput, remote_thruput; 1617 double bytes_sent; 1618 double bytes_recvd; 1619 1620 1621 int *message_int_ptr; 1622 char *message_ptr; 1623 char *message; 1624 char sctl_data[BUFSIZ]; 1625 struct strbuf send_message; 1626 struct strbuf sctl_message; 1627 dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; 1628 1629 char dlsap[BUFSIZ]; 1630 int dlsap_len; 1631 int message_offset; 1632 int message_max_offset; 1633 int failed_sends; 1634 int failed_cows; 1635 int messages_sent; 1636 int data_descriptor; 1637 1638 1639#ifdef WANT_INTERVALS 1640 int interval_count; 1641#endif /* WANT_INTERVALS */ 1642#ifdef DIRTY 1643 int i; 1644#endif /* DIRTY */ 1645 1646 struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; 1647 struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; 1648 struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; 1649 1650 dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; 1651 dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; 1652 dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; 1653 1654 if ( print_headers ) { 1655 printf("DLPI CL UNIDIRECTIONAL SEND TEST\n"); 1656 if (local_cpu_usage || remote_cpu_usage) 1657 printf(cpu_title,format_units()); 1658 else 1659 printf(tput_title,format_units()); 1660 } 1661 1662 failed_sends = 0; 1663 messages_sent = 0; 1664 times_up = 0; 1665 1666 /*set up the data descriptor */ 1667 1668 data_descriptor = dl_open(loc_dlpi_device,loc_ppa); 1669 if (data_descriptor < 0){ 1670 perror("send_dlpi_cl_stream: data descriptor"); 1671 exit(1); 1672 } 1673 1674 /* bind the puppy and get the assigned dlsap */ 1675 dlsap_len = BUFSIZ; 1676 if (dl_bind(data_descriptor, 1677 dlpi_sap, DL_CLDLS, dlsap, &dlsap_len) != 0) { 1678 fprintf(where,"send_dlpi_cl_stream: bind failure\n"); 1679 fflush(where); 1680 exit(1); 1681 } 1682 1683 /* Modify the local socket size (SNDBUF size) */ 1684 1685#ifdef DL_HP_SET_LOCAL_WIN_REQ 1686 if (lsw_size > 0) { 1687 if (debug > 1) { 1688 fprintf(where,"netperf: send_dlpi_cl_stream: descriptor send size altered from system default...\n"); 1689 fprintf(where," send: %d\n",lsw_size); 1690 } 1691 } 1692 if (lrw_size > 0) { 1693 if (debug > 1) { 1694 fprintf(where,"netperf: send_dlpi_cl_stream: descriptor recv size altered from system default...\n"); 1695 fprintf(where," recv: %d\n",lrw_size); 1696 } 1697 } 1698 1699 1700 /* Now, we will find-out what the size actually became, and report */ 1701 /* that back to the user. If the call fails, we will just report a -1 */ 1702 /* back to the initiator for the recv buffer size. */ 1703 1704#else /* DL_HP_SET_LOCAL_WIN_REQ */ 1705 1706 lsw_size = -1; 1707 lrw_size = -1; 1708 1709#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 1710 1711 /* now, we want to see if we need to set the send_size */ 1712 if (send_size == 0) { 1713 send_size = 1024; 1714 } 1715 1716 1717 /* set-up the data buffer with the requested alignment and offset, */ 1718 /* most of the numbers here are just a hack to pick something nice */ 1719 /* and big in an attempt to never try to send a buffer a second time */ 1720 /* before it leaves the node...unless the user set the width */ 1721 /* explicitly. */ 1722 if (send_width == 0) send_width = 32; 1723 message = (char *)malloc(send_size * (send_width + 1) + local_send_align + local_send_offset); 1724 if (message == NULL) { 1725 printf("malloc(%d) failed!\n", send_size * (send_width + 1) + local_send_align + local_send_offset); 1726 exit(1); 1727 } 1728 message_ptr = (char *)(( (long) message + 1729 (long) local_send_align - 1) & 1730 ~((long) local_send_align - 1)); 1731 message_ptr = message_ptr + local_send_offset; 1732 message = message_ptr; 1733 send_message.maxlen = send_size; 1734 send_message.len = send_size; 1735 send_message.buf = message; 1736 1737 sctl_message.maxlen = BUFSIZ; 1738 sctl_message.len = 0; 1739 sctl_message.buf = sctl_data; 1740 1741 /* if the user supplied a cpu rate, this call will complete rather */ 1742 /* quickly, otherwise, the cpu rate will be retured to us for */ 1743 /* possible display. The Library will keep it's own copy of this data */ 1744 /* for use elsewhere. We will only display it. (Does that make it */ 1745 /* "opaque" to us?) */ 1746 1747 if (local_cpu_usage) 1748 local_cpu_rate = calibrate_local_cpu(local_cpu_rate); 1749 1750 /* Tell the remote end to set up the data connection. The server */ 1751 /* sends back the port number and alters the socket parameters there. */ 1752 /* Of course this is a datagram service so no connection is actually */ 1753 /* set up, the server just sets up the socket and binds it. */ 1754 1755 netperf_request.content.request_type = DO_DLPI_CL_STREAM; 1756 dlpi_cl_stream_request->recv_win_size = rrw_size; 1757 dlpi_cl_stream_request->message_size = send_size; 1758 dlpi_cl_stream_request->recv_alignment = remote_recv_align; 1759 dlpi_cl_stream_request->recv_offset = remote_recv_offset; 1760 dlpi_cl_stream_request->measure_cpu = remote_cpu_usage; 1761 dlpi_cl_stream_request->cpu_rate = remote_cpu_rate; 1762 dlpi_cl_stream_request->ppa = rem_ppa; 1763 dlpi_cl_stream_request->sap = dlpi_sap; 1764 dlpi_cl_stream_request->dev_name_len = strlen(rem_dlpi_device); 1765 strcpy(dlpi_cl_stream_request->dlpi_device, 1766 rem_dlpi_device); 1767 1768#ifdef __alpha 1769 1770 /* ok - even on a DEC box, strings are strings. I din't really want */ 1771 /* to ntohl the words of a string. since I don't want to teach the */ 1772 /* send_ and recv_ _request and _response routines about the types, */ 1773 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 1774 /* solution would be to use XDR, but I am still leary of being able */ 1775 /* to find XDR libs on all platforms I want running netperf. raj */ 1776 { 1777 int *charword; 1778 int *initword; 1779 int *lastword; 1780 1781 initword = (int *) dlpi_cl_stream_request->dlpi_device; 1782 lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); 1783 1784 for (charword = initword; 1785 charword < lastword; 1786 charword++) { 1787 1788 *charword = ntohl(*charword); 1789 } 1790 } 1791#endif /* __alpha */ 1792 1793 if (test_time) { 1794 dlpi_cl_stream_request->test_length = test_time; 1795 } 1796 else { 1797 dlpi_cl_stream_request->test_length = test_bytes * -1; 1798 } 1799 1800 1801 send_request(); 1802 1803 recv_response(); 1804 1805 if (!netperf_response.content.serv_errno) { 1806 if (debug) 1807 fprintf(where,"send_dlpi_cl_stream: remote data connection done.\n"); 1808 } 1809 else { 1810 Set_errno(netperf_response.content.serv_errno); 1811 perror("send_dlpi_cl_stream: error on remote"); 1812 exit(1); 1813 } 1814 1815 /* place some of the remote's addressing information into the send */ 1816 /* structure so our sends can be sent to the correct place. Also get */ 1817 /* some of the returned socket buffer information for user display. */ 1818 1819 /* set-up the destination addressing control info */ 1820 data_req->dl_primitive = DL_UNITDATA_REQ; 1821 bcopy((char *)(dlpi_cl_stream_response->station_addr), 1822 ((char *)data_req + sizeof(dl_unitdata_req_t)), 1823 dlpi_cl_stream_response->station_addr_len); 1824 data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); 1825 data_req->dl_dest_addr_length = dlpi_cl_stream_response->station_addr_len; 1826 /* there is a dl_priority structure too, but I am ignoring it for */ 1827 /* the time being. */ 1828 /* however... it is best to put some value in there lest some code 1829 get grumpy about it - fix from Nicolas Thomas */ 1830 data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; 1831 data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; 1832 1833 sctl_message.len = sizeof(dl_unitdata_req_t) + 1834 data_req->dl_dest_addr_length; 1835 1836 rrw_size = dlpi_cl_stream_response->recv_win_size; 1837 rsw_size = dlpi_cl_stream_response->send_win_size; 1838 remote_cpu_rate = dlpi_cl_stream_response->cpu_rate; 1839 1840 1841 /* set up the timer to call us after test_time */ 1842 start_timer(test_time); 1843 1844 /* Get the start count for the idle counter and the start time */ 1845 1846 cpu_start(local_cpu_usage); 1847 1848#ifdef WANT_INTERVALS 1849 interval_count = interval_burst; 1850#endif /* WANT_INTERVALS */ 1851 1852 /* Send datagrams like there was no tomorrow */ 1853 while (!times_up) { 1854#ifdef DIRTY 1855 /* we want to dirty some number of consecutive integers in the buffer */ 1856 /* we are about to send. we may also want to bring some number of */ 1857 /* them cleanly into the cache. The clean ones will follow any dirty */ 1858 /* ones into the cache. */ 1859 message_int_ptr = (int *)message_ptr; 1860 for (i = 0; i < loc_dirty_count; i++) { 1861 *message_int_ptr = 4; 1862 message_int_ptr++; 1863 } 1864 for (i = 0; i < loc_clean_count; i++) { 1865 loc_dirty_count = *message_int_ptr; 1866 message_int_ptr++; 1867 } 1868#endif /* DIRTY */ 1869 if (putmsg(data_descriptor, 1870 &sctl_message, 1871 &send_message, 1872 0) != 0) { 1873 if (errno == EINTR) { 1874 break; 1875 } 1876 if (errno == ENOBUFS) { 1877 /* we might not ever hit this with STREAMS, it would probably */ 1878 /* be better to do a getinfo request at the end of the test to */ 1879 /* get all sorts of gory statistics. in the meantime, we will */ 1880 /* keep this code in place. */ 1881 failed_sends++; 1882 continue; 1883 } 1884 perror("send_dlpi_cl_stream: data send error"); 1885 if (debug) { 1886 fprintf(where,"messages_sent %u\n",messages_sent); 1887 fflush(where); 1888 } 1889 exit(1); 1890 } 1891 messages_sent++; 1892 1893 /* now we want to move our pointer to the next position in the */ 1894 /* data buffer...since there was a successful send */ 1895 1896 1897#ifdef WANT_INTERVALS 1898 /* in this case, the interval count is the count-down couter */ 1899 /* to decide to sleep for a little bit */ 1900 if ((interval_burst) && (--interval_count == 0)) { 1901 /* call the sleep routine for some milliseconds, if our */ 1902 /* timer popped while we were in there, we want to */ 1903 /* break out of the loop. */ 1904 if (msec_sleep(interval_wate)) { 1905 break; 1906 } 1907 interval_count = interval_burst; 1908 } 1909 1910#endif /* WANT_INTERVALS */ 1911 1912 } 1913 1914 /* This is a timed test, so the remote will be returning to us after */ 1915 /* a time. We should not need to send any "strange" messages to tell */ 1916 /* the remote that the test is completed, unless we decide to add a */ 1917 /* number of messages to the test. */ 1918 1919 /* the test is over, so get stats and stuff */ 1920 cpu_stop(local_cpu_usage, 1921 &elapsed_time); 1922 1923 /* Get the statistics from the remote end */ 1924 recv_response(); 1925 if (!netperf_response.content.serv_errno) { 1926 if (debug) 1927 fprintf(where,"send_dlpi_cl_stream: remote results obtained\n"); 1928 } 1929 else { 1930 Set_errno(netperf_response.content.serv_errno); 1931 perror("send_dlpi_cl_stream: error on remote"); 1932 exit(1); 1933 } 1934 1935 bytes_sent = send_size * messages_sent; 1936 local_thruput = calc_thruput(bytes_sent); 1937 1938 messages_recvd = dlpi_cl_stream_results->messages_recvd; 1939 bytes_recvd = send_size * messages_recvd; 1940 1941 /* we asume that the remote ran for as long as we did */ 1942 1943 remote_thruput = calc_thruput(bytes_recvd); 1944 1945 /* print the results for this descriptor and message size */ 1946 1947 if (local_cpu_usage || remote_cpu_usage) { 1948 /* We must now do a little math for service demand and cpu */ 1949 /* utilization for the system(s) We pass zeros for the local */ 1950 /* cpu utilization and elapsed time to tell the routine to use */ 1951 /* the libraries own values for those. */ 1952 if (local_cpu_usage) { 1953 if (local_cpu_rate == 0.0) { 1954 fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); 1955 fprintf(where,"Local CPU usage numbers based on process information only!\n"); 1956 fflush(where); 1957 } 1958 1959 local_cpu_utilization = calc_cpu_util(0.0); 1960 local_service_demand = calc_service_demand(bytes_sent, 1961 0.0, 1962 0.0, 1963 0); 1964 } 1965 else { 1966 local_cpu_utilization = -1.0; 1967 local_service_demand = -1.0; 1968 } 1969 1970 /* The local calculations could use variables being kept by */ 1971 /* the local netlib routines. The remote calcuations need to */ 1972 /* have a few things passed to them. */ 1973 if (remote_cpu_usage) { 1974 if (remote_cpu_rate == 0.0) { 1975 fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); 1976 fprintf(where,"REMOTE CPU usage numbers based on process information only!\n"); 1977 fflush(where); 1978 } 1979 1980 remote_cpu_utilization = dlpi_cl_stream_results->cpu_util; 1981 remote_service_demand = calc_service_demand(bytes_recvd, 1982 0.0, 1983 remote_cpu_utilization, 1984 dlpi_cl_stream_results->num_cpus); 1985 } 1986 else { 1987 remote_cpu_utilization = -1.0; 1988 remote_service_demand = -1.0; 1989 } 1990 1991 /* We are now ready to print all the information. If the user */ 1992 /* has specified zero-level verbosity, we will just print the */ 1993 /* local service demand, or the remote service demand. If the */ 1994 /* user has requested verbosity level 1, he will get the basic */ 1995 /* "streamperf" numbers. If the user has specified a verbosity */ 1996 /* of greater than 1, we will display a veritable plethora of */ 1997 /* background information from outside of this block as it it */ 1998 /* not cpu_measurement specific... */ 1999 2000 switch (verbosity) { 2001 case 0: 2002 if (local_cpu_usage) { 2003 fprintf(where, 2004 cpu_fmt_0, 2005 local_service_demand); 2006 } 2007 else { 2008 fprintf(where, 2009 cpu_fmt_0, 2010 remote_service_demand); 2011 } 2012 break; 2013 case 1: 2014 fprintf(where, 2015 cpu_fmt_1, /* the format string */ 2016 lsw_size, /* local sendbuf size */ 2017 send_size, /* how large were the sends */ 2018 elapsed_time, /* how long was the test */ 2019 messages_sent, 2020 failed_sends, 2021 local_thruput, /* what was the xfer rate */ 2022 local_cpu_utilization, /* local cpu */ 2023 local_service_demand, /* local service demand */ 2024 rrw_size, 2025 elapsed_time, 2026 messages_recvd, 2027 remote_thruput, 2028 remote_cpu_utilization, /* remote cpu */ 2029 remote_service_demand); /* remote service demand */ 2030 break; 2031 } 2032 } 2033 else { 2034 /* The tester did not wish to measure service demand. */ 2035 switch (verbosity) { 2036 case 0: 2037 fprintf(where, 2038 tput_fmt_0, 2039 local_thruput); 2040 break; 2041 case 1: 2042 fprintf(where, 2043 tput_fmt_1, /* the format string */ 2044 lsw_size, /* local sendbuf size */ 2045 send_size, /* how large were the sends */ 2046 elapsed_time, /* how long did it take */ 2047 messages_sent, 2048 failed_sends, 2049 local_thruput, 2050 rrw_size, /* remote recvbuf size */ 2051 elapsed_time, 2052 messages_recvd, 2053 remote_thruput 2054 ); 2055 break; 2056 } 2057 } 2058} 2059 2060int 2061 recv_dlpi_cl_stream() 2062{ 2063 2064 char *message; 2065 int data_descriptor; 2066 int len; 2067 char *message_ptr; 2068 char rctl_data[BUFSIZ]; 2069 struct strbuf recv_message; 2070 struct strbuf rctl_message; 2071 int flags = 0; 2072 /* these are to make reading some of the DLPI control messages easier */ 2073 dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; 2074 dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; 2075 2076 int bytes_received = 0; 2077 float elapsed_time; 2078 2079 int message_size; 2080 int messages_recvd = 0; 2081 int measure_cpu; 2082 2083 struct dlpi_cl_stream_request_struct *dlpi_cl_stream_request; 2084 struct dlpi_cl_stream_response_struct *dlpi_cl_stream_response; 2085 struct dlpi_cl_stream_results_struct *dlpi_cl_stream_results; 2086 2087 dlpi_cl_stream_request = (struct dlpi_cl_stream_request_struct *)netperf_request.content.test_specific_data; 2088 dlpi_cl_stream_response = (struct dlpi_cl_stream_response_struct *)netperf_response.content.test_specific_data; 2089 dlpi_cl_stream_results = (struct dlpi_cl_stream_results_struct *)netperf_response.content.test_specific_data; 2090 2091 if (debug) { 2092 fprintf(where,"netserver: recv_dlpi_cl_stream: entered...\n"); 2093 fflush(where); 2094 } 2095 2096 /* We want to set-up the listen descriptor with all the desired */ 2097 /* parameters and then let the initiator know that all is ready. If */ 2098 /* socket size defaults are to be used, then the initiator will have */ 2099 /* sent us 0's. If the socket sizes cannot be changed, then we will */ 2100 /* send-back what they are. If that information cannot be determined, */ 2101 /* then we send-back -1's for the sizes. If things go wrong for any */ 2102 /* reason, we will drop back ten yards and punt. */ 2103 2104 /* If anything goes wrong, we want the remote to know about it. It */ 2105 /* would be best if the error that the remote reports to the user is */ 2106 /* the actual error we encountered, rather than some bogus unexpected */ 2107 /* response type message. */ 2108 2109 if (debug > 1) { 2110 fprintf(where,"recv_dlpi_cl_stream: setting the response type...\n"); 2111 fflush(where); 2112 } 2113 2114 netperf_response.content.response_type = DLPI_CL_STREAM_RESPONSE; 2115 2116 if (debug > 2) { 2117 fprintf(where,"recv_dlpi_cl_stream: the response type is set...\n"); 2118 fflush(where); 2119 } 2120 2121 /* set-up the data buffer with the requested alignment and offset */ 2122 message = (char *)malloc(DATABUFFERLEN); 2123 if (message == NULL) { 2124 printf("malloc(%d) failed!\n", DATABUFFERLEN); 2125 exit(1); 2126 } 2127 2128 /* We now alter the message_ptr variable to be at the desired */ 2129 /* alignment with the desired offset. */ 2130 2131 if (debug > 1) { 2132 fprintf(where,"recv_dlpi_cl_stream: requested alignment of %d\n", 2133 dlpi_cl_stream_request->recv_alignment); 2134 fflush(where); 2135 } 2136 2137 message_ptr = ALIGN_BUFFER(message, dlpi_cl_stream_request->recv_alignment, dlpi_cl_stream_request->recv_offset); 2138 2139 if (dlpi_cl_stream_request->message_size > 0) { 2140 recv_message.maxlen = dlpi_cl_stream_request->message_size; 2141 } 2142 else { 2143 recv_message.maxlen = 4096; 2144 } 2145 recv_message.len = 0; 2146 recv_message.buf = message_ptr; 2147 2148 rctl_message.maxlen = BUFSIZ; 2149 rctl_message.len = 0; 2150 rctl_message.buf = rctl_data; 2151 2152 if (debug > 1) { 2153 fprintf(where, 2154 "recv_dlpi_cl_stream: receive alignment and offset set...\n"); 2155 fflush(where); 2156 } 2157 2158 if (debug > 1) { 2159 fprintf(where,"recv_dlpi_cl_stream: grabbing a descriptor...\n"); 2160 fflush(where); 2161 } 2162 2163#ifdef __alpha 2164 2165 /* ok - even on a DEC box, strings are strings. I din't really want */ 2166 /* to ntohl the words of a string. since I don't want to teach the */ 2167 /* send_ and recv_ _request and _response routines about the types, */ 2168 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 2169 /* solution would be to use XDR, but I am still leary of being able */ 2170 /* to find XDR libs on all platforms I want running netperf. raj */ 2171 { 2172 int *charword; 2173 int *initword; 2174 int *lastword; 2175 2176 initword = (int *) dlpi_cl_stream_request->dlpi_device; 2177 lastword = initword + ((dlpi_cl_stream_request->dev_name_len + 3) / 4); 2178 2179 for (charword = initword; 2180 charword < lastword; 2181 charword++) { 2182 2183 *charword = htonl(*charword); 2184 } 2185 } 2186#endif /* __alpha */ 2187 2188 data_descriptor = dl_open(dlpi_cl_stream_request->dlpi_device, 2189 dlpi_cl_stream_request->ppa); 2190 if (data_descriptor < 0) { 2191 netperf_response.content.serv_errno = errno; 2192 send_response(); 2193 exit(1); 2194 } 2195 2196 /* The initiator may have wished-us to modify the window */ 2197 /* sizes. We should give it a shot. If he didn't ask us to change the */ 2198 /* sizes, we should let him know what sizes were in use at this end. */ 2199 /* If none of this code is compiled-in, then we will tell the */ 2200 /* initiator that we were unable to play with the sizes by */ 2201 /* setting the size in the response to -1. */ 2202 2203#ifdef DL_HP_SET_LOCAL_WIN_REQ 2204 2205 if (dlpi_cl_stream_request->recv_win_size) { 2206 dlpi_cl_stream_response->recv_win_size = -1; 2207 } 2208 2209#else /* the system won't let us play with the buffers */ 2210 2211 dlpi_cl_stream_response->recv_win_size = -1; 2212 2213#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 2214 2215 dlpi_cl_stream_response->test_length = dlpi_cl_stream_request->test_length; 2216 2217 /* bind the sap and retrieve the dlsap assigned by the system */ 2218 dlpi_cl_stream_response->station_addr_len = 14; /* arbitrary */ 2219 if (dl_bind(data_descriptor, 2220 dlpi_cl_stream_request->sap, 2221 DL_CLDLS, 2222 (char *)dlpi_cl_stream_response->station_addr, 2223 &dlpi_cl_stream_response->station_addr_len) != 0) { 2224 fprintf(where,"send_dlpi_cl_stream: bind failure\n"); 2225 fflush(where); 2226 exit(1); 2227 } 2228 2229 netperf_response.content.serv_errno = 0; 2230 2231 /* But wait, there's more. If the initiator wanted cpu measurements, */ 2232 /* then we must call the calibrate routine, which will return the max */ 2233 /* rate back to the initiator. If the CPU was not to be measured, or */ 2234 /* something went wrong with the calibration, we will return a -1 to */ 2235 /* the initiator. */ 2236 2237 dlpi_cl_stream_response->cpu_rate = 0.0; /* assume no cpu */ 2238 if (dlpi_cl_stream_request->measure_cpu) { 2239 /* We will pass the rate into the calibration routine. If the */ 2240 /* user did not specify one, it will be 0.0, and we will do a */ 2241 /* "real" calibration. Otherwise, all it will really do is */ 2242 /* store it away... */ 2243 dlpi_cl_stream_response->measure_cpu = 1; 2244 dlpi_cl_stream_response->cpu_rate = calibrate_local_cpu(dlpi_cl_stream_request->cpu_rate); 2245 } 2246 2247 message_size = dlpi_cl_stream_request->message_size; 2248 test_time = dlpi_cl_stream_request->test_length; 2249 2250 send_response(); 2251 2252 /* Now it's time to start receiving data on the connection. We will */ 2253 /* first grab the apropriate counters and then start grabbing. */ 2254 2255 cpu_start(dlpi_cl_stream_request->measure_cpu); 2256 2257 /* The loop will exit when the timer pops, or if we happen to recv a */ 2258 /* message of less than send_size bytes... */ 2259 2260 times_up = 0; 2261 start_timer(test_time + PAD_TIME); 2262 2263 if (debug) { 2264 fprintf(where,"recv_dlpi_cl_stream: about to enter inner sanctum.\n"); 2265 fflush(where); 2266 } 2267 2268 while (!times_up) { 2269 if((getmsg(data_descriptor, 2270 &rctl_message, 2271 &recv_message, 2272 &flags) != 0) || 2273 (data_ind->dl_primitive != DL_UNITDATA_IND)) { 2274 if (errno == EINTR) { 2275 /* Again, we have likely hit test-end time */ 2276 break; 2277 } 2278 fprintf(where, 2279 "dlpi_recv_cl_stream: getmsg failure: errno %d primitive 0x%x\n", 2280 errno, 2281 data_ind->dl_primitive); 2282 fflush(where); 2283 netperf_response.content.serv_errno = 996; 2284 send_response(); 2285 exit(1); 2286 } 2287 messages_recvd++; 2288 } 2289 2290 if (debug) { 2291 fprintf(where,"recv_dlpi_cl_stream: got %d messages.\n",messages_recvd); 2292 fflush(where); 2293 } 2294 2295 2296 /* The loop now exits due timer or < send_size bytes received. */ 2297 2298 cpu_stop(dlpi_cl_stream_request->measure_cpu,&elapsed_time); 2299 2300 if (times_up) { 2301 /* we ended on a timer, subtract the PAD_TIME */ 2302 elapsed_time -= (float)PAD_TIME; 2303 } 2304 else { 2305 stop_timer(); 2306 } 2307 2308 if (debug) { 2309 fprintf(where,"recv_dlpi_cl_stream: test ended in %f seconds.\n",elapsed_time); 2310 fflush(where); 2311 } 2312 2313 2314 /* We will count the "off" message */ 2315 bytes_received = (messages_recvd * message_size) + len; 2316 2317 /* send the results to the sender */ 2318 2319 if (debug) { 2320 fprintf(where, 2321 "recv_dlpi_cl_stream: got %d bytes\n", 2322 bytes_received); 2323 fflush(where); 2324 } 2325 2326 netperf_response.content.response_type = DLPI_CL_STREAM_RESULTS; 2327 dlpi_cl_stream_results->bytes_received = bytes_received; 2328 dlpi_cl_stream_results->messages_recvd = messages_recvd; 2329 dlpi_cl_stream_results->elapsed_time = elapsed_time; 2330 if (dlpi_cl_stream_request->measure_cpu) { 2331 dlpi_cl_stream_results->cpu_util = calc_cpu_util(elapsed_time); 2332 } 2333 else { 2334 dlpi_cl_stream_results->cpu_util = -1.0; 2335 } 2336 2337 if (debug > 1) { 2338 fprintf(where, 2339 "recv_dlpi_cl_stream: test complete, sending results.\n"); 2340 fflush(where); 2341 } 2342 2343 send_response(); 2344 2345} 2346 2347int send_dlpi_cl_rr(char remote_host[]) 2348{ 2349 2350 char *tput_title = "\ 2351Local /Remote\n\ 2352Window Size Request Resp. Elapsed Trans.\n\ 2353Send Recv Size Size Time Rate \n\ 2354frames frames bytes bytes secs. per sec \n\n"; 2355 2356 char *tput_fmt_0 = 2357 "%7.2f\n"; 2358 2359 char *tput_fmt_1_line_1 = "\ 2360%-6d %-6d %-6d %-6d %-6.2f %7.2f \n"; 2361 char *tput_fmt_1_line_2 = "\ 2362%-6d %-6d\n"; 2363 2364 char *cpu_title = "\ 2365Local /Remote\n\ 2366Window Size Request Resp. Elapsed Trans. CPU CPU S.dem S.dem\n\ 2367Send Recv Size Size Time Rate local remote local remote\n\ 2368frames frames bytes bytes secs. per sec %% %% us/Tr us/Tr\n\n"; 2369 2370 char *cpu_fmt_0 = 2371 "%6.3f\n"; 2372 2373 char *cpu_fmt_1_line_1 = "\ 2374%-6d %-6d %-6d %-6d %-6.2f %-6.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; 2375 2376 char *cpu_fmt_1_line_2 = "\ 2377%-6d %-6d\n"; 2378 2379 char *ksink_fmt = "\ 2380Alignment Offset\n\ 2381Local Remote Local Remote\n\ 2382Send Recv Send Recv\n\ 2383%5d %5d %5d %5d\n"; 2384 2385 2386 float elapsed_time; 2387 2388 int dlsap_len; 2389 int flags = 0; 2390 char *send_message_ptr; 2391 char *recv_message_ptr; 2392 char *temp_message_ptr; 2393 char sctl_data[BUFSIZ]; 2394 char rctl_data[BUFSIZ]; 2395 char dlsap[BUFSIZ]; 2396 struct strbuf send_message; 2397 struct strbuf recv_message; 2398 struct strbuf sctl_message; 2399 struct strbuf rctl_message; 2400 2401 /* these are to make reading some of the DLPI control messages easier */ 2402 dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; 2403 dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; 2404 dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; 2405 2406 int nummessages; 2407 int send_descriptor; 2408 int trans_remaining; 2409 int bytes_xferd; 2410 2411 float local_cpu_utilization; 2412 float local_service_demand; 2413 float remote_cpu_utilization; 2414 float remote_service_demand; 2415 double thruput; 2416 2417#ifdef WANT_INTERVALS 2418 /* timing stuff */ 2419#define MAX_KEPT_TIMES 1024 2420 int time_index = 0; 2421 int unused_buckets; 2422 int kept_times[MAX_KEPT_TIMES]; 2423 int sleep_usecs; 2424 unsigned int total_times=0; 2425 struct timezone dummy_zone; 2426 struct timeval send_time; 2427 struct timeval recv_time; 2428 struct timeval sleep_timeval; 2429#endif /* WANT_INTERVALS */ 2430 2431 struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; 2432 struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; 2433 struct dlpi_cl_rr_results_struct *dlpi_cl_rr_result; 2434 2435 dlpi_cl_rr_request = 2436 (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; 2437 dlpi_cl_rr_response = 2438 (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; 2439 dlpi_cl_rr_result = 2440 (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; 2441 2442 /* we want to zero out the times, so we can detect unused entries. */ 2443#ifdef WANT_INTERVALS 2444 time_index = 0; 2445 while (time_index < MAX_KEPT_TIMES) { 2446 kept_times[time_index] = 0; 2447 time_index += 1; 2448 } 2449 time_index = 0; 2450#endif /* WANT_INTERVALS */ 2451 2452 if (print_headers) { 2453 fprintf(where,"DLPI CL REQUEST/RESPONSE TEST\n"); 2454 if (local_cpu_usage || remote_cpu_usage) 2455 fprintf(where,cpu_title,format_units()); 2456 else 2457 fprintf(where,tput_title,format_units()); 2458 } 2459 2460 /* initialize a few counters */ 2461 2462 nummessages = 0; 2463 bytes_xferd = 0; 2464 times_up = 0; 2465 2466 /* set-up the data buffer with the requested alignment and offset */ 2467 temp_message_ptr = (char *)malloc(req_size+MAXALIGNMENT+MAXOFFSET); 2468 if (temp_message_ptr == NULL) { 2469 printf("malloc(%d) failed!\n", req_size+MAXALIGNMENT+MAXOFFSET); 2470 exit(1); 2471 } 2472 send_message_ptr = (char *)(( (long)temp_message_ptr + 2473 (long) local_send_align - 1) & 2474 ~((long) local_send_align - 1)); 2475 send_message_ptr = send_message_ptr + local_send_offset; 2476 send_message.maxlen = req_size; 2477 send_message.len = req_size; 2478 send_message.buf = send_message_ptr; 2479 2480 temp_message_ptr = (char *)malloc(rsp_size+MAXALIGNMENT+MAXOFFSET); 2481 if (temp_message_ptr == NULL) { 2482 printf("malloc(%d) failed!\n", rsp_size+MAXALIGNMENT+MAXOFFSET); 2483 exit(1); 2484 } 2485 recv_message_ptr = (char *)(( (long)temp_message_ptr + 2486 (long) local_recv_align - 1) & 2487 ~((long) local_recv_align - 1)); 2488 recv_message_ptr = recv_message_ptr + local_recv_offset; 2489 recv_message.maxlen = rsp_size; 2490 recv_message.len = 0; 2491 recv_message.buf = recv_message_ptr; 2492 2493 sctl_message.maxlen = BUFSIZ; 2494 sctl_message.len = 0; 2495 sctl_message.buf = sctl_data; 2496 2497 rctl_message.maxlen = BUFSIZ; 2498 rctl_message.len = 0; 2499 rctl_message.buf = rctl_data; 2500 2501 /* lets get ourselves a file descriptor */ 2502 2503 send_descriptor = dl_open(loc_dlpi_device,loc_ppa); 2504 if (send_descriptor < 0){ 2505 perror("netperf: send_dlpi_cl_rr: dlpi cl rr send descriptor"); 2506 exit(1); 2507 } 2508 2509 if (debug) { 2510 fprintf(where,"send_dlpi_cl_rr: send_descriptor obtained...\n"); 2511 } 2512 2513 /* bind the sap to the descriptor and get the dlsap */ 2514 dlsap_len = BUFSIZ; 2515 if (dl_bind(send_descriptor, 2516 dlpi_sap, 2517 DL_CLDLS, 2518 dlsap, 2519 &dlsap_len) != 0) { 2520 fprintf(where,"send_dlpi_cl_rr: bind failure\n"); 2521 fflush(where); 2522 exit(1); 2523 } 2524 2525 /* Modify the local socket size. If the user has not requested that */ 2526 /* the socket buffers be altered, we will try to find-out what their */ 2527 /* values are. If we cannot touch the socket buffer in any way, we */ 2528 /* will set the values to -1 to indicate that. The receive socket */ 2529 /* must have enough space to hold addressing information so += a */ 2530 /* sizeof struct sockaddr_in to it. */ 2531 2532 /* this is actually nothing code, and should be replaced with the */ 2533 /* alalagous calls in the STREAM test where the window size is set */ 2534 /* with the HP DLPI Extension. raj 8/94 */ 2535#ifdef SO_SNDBUF 2536 if (lsw_size > 0) { 2537 if (debug > 1) { 2538 fprintf(where,"netperf: send_dlpi_cl_rr: local window size altered from system default...\n"); 2539 fprintf(where," window: %d\n",lsw_size); 2540 } 2541 } 2542 if (lrw_size > 0) { 2543 if (debug > 1) { 2544 fprintf(where,"netperf: send_dlpi_cl_rr: remote window size altered from system default...\n"); 2545 fprintf(where," remote: %d\n",lrw_size); 2546 } 2547 } 2548 2549 2550 /* Now, we will find-out what the size actually became, and report */ 2551 /* that back to the user. If the call fails, we will just report a -1 */ 2552 /* back to the initiator for the recv buffer size. */ 2553 2554 if (debug) { 2555 fprintf(where,"netperf: send_dlpi_cl_rr: socket sizes determined...\n"); 2556 fprintf(where," send: %d recv: %d\n",lsw_size,lrw_size); 2557 } 2558 2559#else /* SO_SNDBUF */ 2560 2561 lsw_size = -1; 2562 lrw_size = -1; 2563 2564#endif /* SO_SNDBUF */ 2565 2566 /* If the user has requested cpu utilization measurements, we must */ 2567 /* calibrate the cpu(s). We will perform this task within the tests */ 2568 /* themselves. If the user has specified the cpu rate, then */ 2569 /* calibrate_local_cpu will return rather quickly as it will have */ 2570 /* nothing to do. If local_cpu_rate is zero, then we will go through */ 2571 /* all the "normal" calibration stuff and return the rate back. If */ 2572 /* there is no idle counter in the kernel idle loop, the */ 2573 /* local_cpu_rate will be set to -1. */ 2574 2575 if (local_cpu_usage) { 2576 local_cpu_rate = calibrate_local_cpu(local_cpu_rate); 2577 } 2578 2579 /* Tell the remote end to do a listen. The server alters the socket */ 2580 /* paramters on the other side at this point, hence the reason for */ 2581 /* all the values being passed in the setup message. If the user did */ 2582 /* not specify any of the parameters, they will be passed as 0, which */ 2583 /* will indicate to the remote that no changes beyond the system's */ 2584 /* default should be used. Alignment is the exception, it will */ 2585 /* default to 8, which will be no alignment alterations. */ 2586 2587 netperf_request.content.request_type = DO_DLPI_CL_RR; 2588 dlpi_cl_rr_request->recv_win_size = rrw_size; 2589 dlpi_cl_rr_request->send_win_size = rsw_size; 2590 dlpi_cl_rr_request->recv_alignment = remote_recv_align; 2591 dlpi_cl_rr_request->recv_offset = remote_recv_offset; 2592 dlpi_cl_rr_request->send_alignment = remote_send_align; 2593 dlpi_cl_rr_request->send_offset = remote_send_offset; 2594 dlpi_cl_rr_request->request_size = req_size; 2595 dlpi_cl_rr_request->response_size = rsp_size; 2596 dlpi_cl_rr_request->measure_cpu = remote_cpu_usage; 2597 dlpi_cl_rr_request->cpu_rate = remote_cpu_rate; 2598 dlpi_cl_rr_request->ppa = rem_ppa; 2599 dlpi_cl_rr_request->sap = dlpi_sap; 2600 dlpi_cl_rr_request->dev_name_len = strlen(rem_dlpi_device); 2601 strcpy(dlpi_cl_rr_request->dlpi_device, 2602 rem_dlpi_device); 2603 2604#ifdef __alpha 2605 2606 /* ok - even on a DEC box, strings are strings. I din't really want */ 2607 /* to ntohl the words of a string. since I don't want to teach the */ 2608 /* send_ and recv_ _request and _response routines about the types, */ 2609 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 2610 /* solution would be to use XDR, but I am still leary of being able */ 2611 /* to find XDR libs on all platforms I want running netperf. raj */ 2612 { 2613 int *charword; 2614 int *initword; 2615 int *lastword; 2616 2617 initword = (int *) dlpi_cl_rr_request->dlpi_device; 2618 lastword = initword + ((strlen(rem_dlpi_device) + 3) / 4); 2619 2620 for (charword = initword; 2621 charword < lastword; 2622 charword++) { 2623 2624 *charword = ntohl(*charword); 2625 } 2626 } 2627#endif /* __alpha */ 2628 2629 if (test_time) { 2630 dlpi_cl_rr_request->test_length = test_time; 2631 } 2632 else { 2633 dlpi_cl_rr_request->test_length = test_trans * -1; 2634 } 2635 2636 if (debug > 1) { 2637 fprintf(where,"netperf: send_dlpi_cl_rr: requesting DLPI CL request/response test\n"); 2638 } 2639 2640 send_request(); 2641 2642 /* The response from the remote will contain all of the relevant */ 2643 /* socket parameters for this test type. We will put them back into */ 2644 /* the variables here so they can be displayed if desired. The */ 2645 /* remote will have calibrated CPU if necessary, and will have done */ 2646 /* all the needed set-up we will have calibrated the cpu locally */ 2647 /* before sending the request, and will grab the counter value right */ 2648 /* after the connect returns. The remote will grab the counter right */ 2649 /* after the accept call. This saves the hassle of extra messages */ 2650 /* being sent for the tests. */ 2651 2652 recv_response(); 2653 2654 if (!netperf_response.content.serv_errno) { 2655 if (debug) 2656 fprintf(where,"remote listen done.\n"); 2657 rrw_size = dlpi_cl_rr_response->recv_win_size; 2658 rsw_size = dlpi_cl_rr_response->send_win_size; 2659 remote_cpu_usage= dlpi_cl_rr_response->measure_cpu; 2660 remote_cpu_rate = dlpi_cl_rr_response->cpu_rate; 2661 2662 /* set-up the destination addressing control info */ 2663 data_req->dl_primitive = DL_UNITDATA_REQ; 2664 bcopy((char *)(dlpi_cl_rr_response->station_addr), 2665 ((char *)data_req + sizeof(dl_unitdata_req_t)), 2666 dlpi_cl_rr_response->station_addr_len); 2667 data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); 2668 data_req->dl_dest_addr_length = dlpi_cl_rr_response->station_addr_len; 2669 /* there is a dl_priority structure too, but I am ignoring it for */ 2670 /* the time being. */ 2671 sctl_message.len = sizeof(dl_unitdata_req_t) + 2672 data_req->dl_dest_addr_length; 2673 /* famous last words - some DLPI providers get unhappy if the 2674 priority stuff is not initialized. fix from Nicolas Thomas. */ 2675 data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; 2676 data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; 2677 2678 } 2679 else { 2680 Set_errno(netperf_response.content.serv_errno); 2681 perror("netperf: remote error"); 2682 exit(1); 2683 } 2684 2685 /* Data Socket set-up is finished. If there were problems, either the */ 2686 /* connect would have failed, or the previous response would have */ 2687 /* indicated a problem. I failed to see the value of the extra */ 2688 /* message after the accept on the remote. If it failed, we'll see it */ 2689 /* here. If it didn't, we might as well start pumping data. */ 2690 2691 /* Set-up the test end conditions. For a request/response test, they */ 2692 /* can be either time or transaction based. */ 2693 2694 if (test_time) { 2695 /* The user wanted to end the test after a period of time. */ 2696 times_up = 0; 2697 trans_remaining = 0; 2698 start_timer(test_time); 2699 } 2700 else { 2701 /* The tester wanted to send a number of bytes. */ 2702 trans_remaining = test_bytes; 2703 times_up = 1; 2704 } 2705 2706 /* The cpu_start routine will grab the current time and possibly */ 2707 /* value of the idle counter for later use in measuring cpu */ 2708 /* utilization and/or service demand and thruput. */ 2709 2710 cpu_start(local_cpu_usage); 2711 2712 /* We use an "OR" to control test execution. When the test is */ 2713 /* controlled by time, the byte count check will always return false. */ 2714 /* When the test is controlled by byte count, the time test will */ 2715 /* always return false. When the test is finished, the whole */ 2716 /* expression will go false and we will stop sending data. I think I */ 2717 /* just arbitrarily decrement trans_remaining for the timed test, but */ 2718 /* will not do that just yet... One other question is whether or not */ 2719 /* the send buffer and the receive buffer should be the same buffer. */ 2720 while ((!times_up) || (trans_remaining > 0)) { 2721 /* send the request */ 2722#ifdef WANT_INTERVALS 2723 gettimeofday(&send_time,&dummy_zone); 2724#endif /* WANT_INTERVALS */ 2725 if(putmsg(send_descriptor, 2726 &sctl_message, 2727 &send_message, 2728 0) != 0) { 2729 if (errno == EINTR) { 2730 /* We likely hit */ 2731 /* test-end time. */ 2732 break; 2733 } 2734 /* there is more we could do here, but it can wait */ 2735 perror("send_dlpi_cl_rr: data send error"); 2736 exit(1); 2737 } 2738 2739 /* receive the response. at some point, we will need to handle */ 2740 /* sending responses which are greater than the datalink MTU. we */ 2741 /* may also want to add some DLPI error checking, but for now we */ 2742 /* will ignore that and just let errors stop the test with little */ 2743 /* indication of what might actually be wrong. */ 2744 2745 if((getmsg(send_descriptor, 2746 &rctl_message, 2747 &recv_message, 2748 &flags) != 0) || 2749 (data_ind->dl_primitive != DL_UNITDATA_IND)) { 2750 if (errno == EINTR) { 2751 /* Again, we have likely hit test-end time */ 2752 break; 2753 } 2754 fprintf(where, 2755 "send_dlpi_cl_rr: recv error: errno %d primitive 0x%x\n", 2756 errno, 2757 data_ind->dl_primitive); 2758 fflush(where); 2759 exit(1); 2760 } 2761#ifdef WANT_INTERVALS 2762 gettimeofday(&recv_time,&dummy_zone); 2763 2764 /* now we do some arithmatic on the two timevals */ 2765 if (recv_time.tv_usec < send_time.tv_usec) { 2766 /* we wrapped around a second */ 2767 recv_time.tv_usec += 1000000; 2768 recv_time.tv_sec -= 1; 2769 } 2770 2771 /* and store it away */ 2772 kept_times[time_index] = (recv_time.tv_sec - send_time.tv_sec) * 1000000; 2773 kept_times[time_index] += (recv_time.tv_usec - send_time.tv_usec); 2774 2775 /* at this point, we may wish to sleep for some period of */ 2776 /* time, so we see how long that last transaction just took, */ 2777 /* and sleep for the difference of that and the interval. We */ 2778 /* will not sleep if the time would be less than a */ 2779 /* millisecond. */ 2780 if (interval_usecs > 0) { 2781 sleep_usecs = interval_usecs - kept_times[time_index]; 2782 if (sleep_usecs > 1000) { 2783 /* we sleep */ 2784 sleep_timeval.tv_sec = sleep_usecs / 1000000; 2785 sleep_timeval.tv_usec = sleep_usecs % 1000000; 2786 select(0, 2787 0, 2788 0, 2789 0, 2790 &sleep_timeval); 2791 } 2792 } 2793 2794 /* now up the time index */ 2795 time_index = (time_index +1)%MAX_KEPT_TIMES; 2796#endif /* WANT_INTERVALS */ 2797 nummessages++; 2798 if (trans_remaining) { 2799 trans_remaining--; 2800 } 2801 2802 if (debug > 3) { 2803 fprintf(where,"Transaction %d completed\n",nummessages); 2804 fflush(where); 2805 } 2806 2807 } 2808 2809 /* this call will always give us the elapsed time for the test, and */ 2810 /* will also store-away the necessaries for cpu utilization */ 2811 2812 cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being measured? */ 2813 /* how long did we really run? */ 2814 2815 /* Get the statistics from the remote end. The remote will have */ 2816 /* calculated service demand and all those interesting things. If it */ 2817 /* wasn't supposed to care, it will return obvious values. */ 2818 2819 recv_response(); 2820 if (!netperf_response.content.serv_errno) { 2821 if (debug) 2822 fprintf(where,"remote results obtained\n"); 2823 } 2824 else { 2825 Set_errno(netperf_response.content.serv_errno); 2826 perror("netperf: remote error"); 2827 2828 exit(1); 2829 } 2830 2831 /* We now calculate what our thruput was for the test. In the future, */ 2832 /* we may want to include a calculation of the thruput measured by */ 2833 /* the remote, but it should be the case that for a UDP stream test, */ 2834 /* that the two numbers should be *very* close... We calculate */ 2835 /* bytes_sent regardless of the way the test length was controlled. */ 2836 /* If it was time, we needed to, and if it was by bytes, the user may */ 2837 /* have specified a number of bytes that wasn't a multiple of the */ 2838 /* send_size, so we really didn't send what he asked for ;-) We use */ 2839 2840 bytes_xferd = (req_size * nummessages) + (rsp_size * nummessages); 2841 thruput = calc_thruput(bytes_xferd); 2842 2843 if (local_cpu_usage || remote_cpu_usage) { 2844 /* We must now do a little math for service demand and cpu */ 2845 /* utilization for the system(s) */ 2846 /* Of course, some of the information might be bogus because */ 2847 /* there was no idle counter in the kernel(s). We need to make */ 2848 /* a note of this for the user's benefit...*/ 2849 if (local_cpu_usage) { 2850 if (local_cpu_rate == 0.0) { 2851 fprintf(where,"WARNING WARNING WARNING WARNING WARNING WARNING WARNING!\n"); 2852 fprintf(where,"Local CPU usage numbers based on process information only!\n"); 2853 fflush(where); 2854 } 2855 local_cpu_utilization = calc_cpu_util(0.0); 2856 /* since calc_service demand is doing ms/Kunit we will */ 2857 /* multiply the number of transaction by 1024 to get */ 2858 /* "good" numbers */ 2859 local_service_demand = calc_service_demand((double) nummessages*1024, 2860 0.0, 2861 0.0, 2862 0); 2863 } 2864 else { 2865 local_cpu_utilization = -1.0; 2866 local_service_demand = -1.0; 2867 } 2868 2869 if (remote_cpu_usage) { 2870 if (remote_cpu_rate == 0.0) { 2871 fprintf(where,"DANGER DANGER DANGER DANGER DANGER DANGER DANGER!\n"); 2872 fprintf(where,"Remote CPU usage numbers based on process information only!\n"); 2873 fflush(where); 2874 } 2875 remote_cpu_utilization = dlpi_cl_rr_result->cpu_util; 2876 /* since calc_service demand is doing ms/Kunit we will */ 2877 /* multiply the number of transaction by 1024 to get */ 2878 /* "good" numbers */ 2879 remote_service_demand = calc_service_demand((double) nummessages*1024, 2880 0.0, 2881 remote_cpu_utilization, 2882 dlpi_cl_rr_result->num_cpus); 2883 } 2884 else { 2885 remote_cpu_utilization = -1.0; 2886 remote_service_demand = -1.0; 2887 } 2888 2889 /* We are now ready to print all the information. If the user */ 2890 /* has specified zero-level verbosity, we will just print the */ 2891 /* local service demand, or the remote service demand. If the */ 2892 /* user has requested verbosity level 1, he will get the basic */ 2893 /* "streamperf" numbers. If the user has specified a verbosity */ 2894 /* of greater than 1, we will display a veritable plethora of */ 2895 /* background information from outside of this block as it it */ 2896 /* not cpu_measurement specific... */ 2897 2898 switch (verbosity) { 2899 case 0: 2900 if (local_cpu_usage) { 2901 fprintf(where, 2902 cpu_fmt_0, 2903 local_service_demand); 2904 } 2905 else { 2906 fprintf(where, 2907 cpu_fmt_0, 2908 remote_service_demand); 2909 } 2910 break; 2911 case 1: 2912 case 2: 2913 fprintf(where, 2914 cpu_fmt_1_line_1, /* the format string */ 2915 lsw_size, /* local sendbuf size */ 2916 lrw_size, 2917 req_size, /* how large were the requests */ 2918 rsp_size, /* guess */ 2919 elapsed_time, /* how long was the test */ 2920 nummessages/elapsed_time, 2921 local_cpu_utilization, /* local cpu */ 2922 remote_cpu_utilization, /* remote cpu */ 2923 local_service_demand, /* local service demand */ 2924 remote_service_demand); /* remote service demand */ 2925 fprintf(where, 2926 cpu_fmt_1_line_2, 2927 rsw_size, 2928 rrw_size); 2929 break; 2930 } 2931 } 2932 else { 2933 /* The tester did not wish to measure service demand. */ 2934 switch (verbosity) { 2935 case 0: 2936 fprintf(where, 2937 tput_fmt_0, 2938 nummessages/elapsed_time); 2939 break; 2940 case 1: 2941 case 2: 2942 fprintf(where, 2943 tput_fmt_1_line_1, /* the format string */ 2944 lsw_size, 2945 lrw_size, 2946 req_size, /* how large were the requests */ 2947 rsp_size, /* how large were the responses */ 2948 elapsed_time, /* how long did it take */ 2949 nummessages/elapsed_time); 2950 fprintf(where, 2951 tput_fmt_1_line_2, 2952 rsw_size, /* remote recvbuf size */ 2953 rrw_size); 2954 2955 break; 2956 } 2957 } 2958 2959 /* it would be a good thing to include information about some of the */ 2960 /* other parameters that may have been set for this test, but at the */ 2961 /* moment, I do not wish to figure-out all the formatting, so I will */ 2962 /* just put this comment here to help remind me that it is something */ 2963 /* that should be done at a later time. */ 2964 2965 if (verbosity > 1) { 2966 /* The user wanted to know it all, so we will give it to him. */ 2967 /* This information will include as much as we can find about */ 2968 /* UDP statistics, the alignments of the sends and receives */ 2969 /* and all that sort of rot... */ 2970 2971#ifdef WANT_INTERVALS 2972 kept_times[MAX_KEPT_TIMES] = 0; 2973 time_index = 0; 2974 while (time_index < MAX_KEPT_TIMES) { 2975 if (kept_times[time_index] > 0) { 2976 total_times += kept_times[time_index]; 2977 } 2978 else 2979 unused_buckets++; 2980 time_index += 1; 2981 } 2982 total_times /= (MAX_KEPT_TIMES-unused_buckets); 2983 fprintf(where, 2984 "Average response time %d usecs\n", 2985 total_times); 2986#endif 2987 } 2988} 2989 2990int 2991 recv_dlpi_cl_rr() 2992{ 2993 2994 char *message; 2995 int data_descriptor; 2996 int flags = 0; 2997 int measure_cpu; 2998 2999 char *recv_message_ptr; 3000 char *send_message_ptr; 3001 char sctl_data[BUFSIZ]; 3002 char rctl_data[BUFSIZ]; 3003 char dlsap[BUFSIZ]; 3004 struct strbuf send_message; 3005 struct strbuf recv_message; 3006 struct strbuf sctl_message; 3007 struct strbuf rctl_message; 3008 3009 /* these are to make reading some of the DLPI control messages easier */ 3010 dl_unitdata_ind_t *data_ind = (dl_unitdata_ind_t *)rctl_data; 3011 dl_unitdata_req_t *data_req = (dl_unitdata_req_t *)sctl_data; 3012 dl_uderror_ind_t *uder_ind = (dl_uderror_ind_t *)rctl_data; 3013 3014 int trans_received; 3015 int trans_remaining; 3016 float elapsed_time; 3017 3018 struct dlpi_cl_rr_request_struct *dlpi_cl_rr_request; 3019 struct dlpi_cl_rr_response_struct *dlpi_cl_rr_response; 3020 struct dlpi_cl_rr_results_struct *dlpi_cl_rr_results; 3021 3022 dlpi_cl_rr_request = 3023 (struct dlpi_cl_rr_request_struct *)netperf_request.content.test_specific_data; 3024 dlpi_cl_rr_response = 3025 (struct dlpi_cl_rr_response_struct *)netperf_response.content.test_specific_data; 3026 dlpi_cl_rr_results = 3027 (struct dlpi_cl_rr_results_struct *)netperf_response.content.test_specific_data; 3028 3029 if (debug) { 3030 fprintf(where,"netserver: recv_dlpi_cl_rr: entered...\n"); 3031 fflush(where); 3032 } 3033 3034 /* We want to set-up the listen descriptor with all the desired */ 3035 /* parameters and then let the initiator know that all is ready. If */ 3036 /* socket size defaults are to be used, then the initiator will have */ 3037 /* sent us 0's. If the descriptor sizes cannot be changed, then we will */ 3038 /* send-back what they are. If that information cannot be determined, */ 3039 /* then we send-back -1's for the sizes. If things go wrong for any */ 3040 /* reason, we will drop back ten yards and punt. */ 3041 3042 /* If anything goes wrong, we want the remote to know about it. It */ 3043 /* would be best if the error that the remote reports to the user is */ 3044 /* the actual error we encountered, rather than some bogus unexpected */ 3045 /* response type message. */ 3046 3047 if (debug) { 3048 fprintf(where,"recv_dlpi_cl_rr: setting the response type...\n"); 3049 fflush(where); 3050 } 3051 3052 netperf_response.content.response_type = DLPI_CL_RR_RESPONSE; 3053 3054 if (debug) { 3055 fprintf(where,"recv_dlpi_cl_rr: the response type is set...\n"); 3056 fflush(where); 3057 } 3058 3059 /* set-up the data buffer with the requested alignment and offset */ 3060 message = (char *)malloc(DATABUFFERLEN); 3061 if (message == NULL) { 3062 printf("malloc(%d) failed!\n", DATABUFFERLEN); 3063 exit(1); 3064 } 3065 3066 /* We now alter the message_ptr variables to be at the desired */ 3067 /* alignments with the desired offsets. */ 3068 3069 if (debug) { 3070 fprintf(where, 3071 "recv_dlpi_cl_rr: requested recv alignment of %d offset %d\n", 3072 dlpi_cl_rr_request->recv_alignment, 3073 dlpi_cl_rr_request->recv_offset); 3074 fprintf(where, 3075 "recv_dlpi_cl_rr: requested send alignment of %d offset %d\n", 3076 dlpi_cl_rr_request->send_alignment, 3077 dlpi_cl_rr_request->send_offset); 3078 fflush(where); 3079 } 3080 3081 recv_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->recv_alignment, dlpi_cl_rr_request->recv_offset); 3082 recv_message.maxlen = dlpi_cl_rr_request->request_size; 3083 recv_message.len = 0; 3084 recv_message.buf = recv_message_ptr; 3085 3086 send_message_ptr = ALIGN_BUFFER(message, dlpi_cl_rr_request->send_alignment, dlpi_cl_rr_request->send_offset); 3087 send_message.maxlen = dlpi_cl_rr_request->response_size; 3088 send_message.len = dlpi_cl_rr_request->response_size; 3089 send_message.buf = send_message_ptr; 3090 3091 sctl_message.maxlen = BUFSIZ; 3092 sctl_message.len = 0; 3093 sctl_message.buf = sctl_data; 3094 3095 rctl_message.maxlen = BUFSIZ; 3096 rctl_message.len = 0; 3097 rctl_message.buf = rctl_data; 3098 3099 if (debug) { 3100 fprintf(where,"recv_dlpi_cl_rr: receive alignment and offset set...\n"); 3101 fprintf(where,"recv_dlpi_cl_rr: grabbing a socket...\n"); 3102 fflush(where); 3103 } 3104 3105 3106#ifdef __alpha 3107 3108 /* ok - even on a DEC box, strings are strings. I din't really want */ 3109 /* to ntohl the words of a string. since I don't want to teach the */ 3110 /* send_ and recv_ _request and _response routines about the types, */ 3111 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 3112 /* solution would be to use XDR, but I am still leary of being able */ 3113 /* to find XDR libs on all platforms I want running netperf. raj */ 3114 { 3115 int *charword; 3116 int *initword; 3117 int *lastword; 3118 3119 initword = (int *) dlpi_cl_rr_request->dlpi_device; 3120 lastword = initword + ((dlpi_cl_rr_request->dev_name_len + 3) / 4); 3121 3122 for (charword = initword; 3123 charword < lastword; 3124 charword++) { 3125 3126 *charword = htonl(*charword); 3127 } 3128 } 3129#endif /* __alpha */ 3130 3131 data_descriptor = dl_open(dlpi_cl_rr_request->dlpi_device, 3132 dlpi_cl_rr_request->ppa); 3133 if (data_descriptor < 0) { 3134 netperf_response.content.serv_errno = errno; 3135 send_response(); 3136 exit(1); 3137 } 3138 3139 3140 /* The initiator may have wished-us to modify the window */ 3141 /* sizes. We should give it a shot. If he didn't ask us to change the */ 3142 /* sizes, we should let him know what sizes were in use at this end. */ 3143 /* If none of this code is compiled-in, then we will tell the */ 3144 /* initiator that we were unable to play with the sizes by */ 3145 /* setting the size in the response to -1. */ 3146 3147#ifdef DL_HP_SET_LOCAL_WIN_REQ 3148 3149 if (dlpi_cl_rr_request->recv_win_size) { 3150 } 3151 3152 if (dlpi_cl_rr_request->send_win_size) { 3153 } 3154 3155 /* Now, we will find-out what the sizes actually became, and report */ 3156 /* them back to the user. If the calls fail, we will just report a -1 */ 3157 /* back to the initiator for the buffer size. */ 3158 3159#else /* the system won't let us play with the buffers */ 3160 3161 dlpi_cl_rr_response->recv_win_size = -1; 3162 dlpi_cl_rr_response->send_win_size = -1; 3163 3164#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 3165 3166 /* bind the sap and retrieve the dlsap assigned by the system */ 3167 dlpi_cl_rr_response->station_addr_len = 14; /* arbitrary */ 3168 if (dl_bind(data_descriptor, 3169 dlpi_cl_rr_request->sap, 3170 DL_CLDLS, 3171 (char *)dlpi_cl_rr_response->station_addr, 3172 &dlpi_cl_rr_response->station_addr_len) != 0) { 3173 fprintf(where,"send_dlpi_cl_rr: bind failure\n"); 3174 fflush(where); 3175 exit(1); 3176 } 3177 3178 netperf_response.content.serv_errno = 0; 3179 3180 /* But wait, there's more. If the initiator wanted cpu measurements, */ 3181 /* then we must call the calibrate routine, which will return the max */ 3182 /* rate back to the initiator. If the CPU was not to be measured, or */ 3183 /* something went wrong with the calibration, we will return a 0.0 to */ 3184 /* the initiator. */ 3185 3186 dlpi_cl_rr_response->cpu_rate = 0.0; /* assume no cpu */ 3187 if (dlpi_cl_rr_request->measure_cpu) { 3188 dlpi_cl_rr_response->measure_cpu = 1; 3189 dlpi_cl_rr_response->cpu_rate = calibrate_local_cpu(dlpi_cl_rr_request->cpu_rate); 3190 } 3191 3192 send_response(); 3193 3194 /* Now it's time to start receiving data on the connection. We will */ 3195 /* first grab the apropriate counters and then start receiving. */ 3196 3197 cpu_start(dlpi_cl_rr_request->measure_cpu); 3198 3199 if (dlpi_cl_rr_request->test_length > 0) { 3200 times_up = 0; 3201 trans_remaining = 0; 3202 start_timer(dlpi_cl_rr_request->test_length + PAD_TIME); 3203 } 3204else { 3205 times_up = 1; 3206 trans_remaining = dlpi_cl_rr_request->test_length * -1; 3207} 3208 3209 while ((!times_up) || (trans_remaining > 0)) { 3210 3211 /* receive the request from the other side. at some point we need */ 3212 /* to handle "logical" requests and responses which are larger */ 3213 /* than the data link MTU */ 3214 3215 if((getmsg(data_descriptor, 3216 &rctl_message, 3217 &recv_message, 3218 &flags) != 0) || 3219 (data_ind->dl_primitive != DL_UNITDATA_IND)) { 3220 if (errno == EINTR) { 3221 /* Again, we have likely hit test-end time */ 3222 break; 3223 } 3224 fprintf(where, 3225 "dlpi_recv_cl_rr: getmsg failure: errno %d primitive 0x%x\n", 3226 errno, 3227 data_ind->dl_primitive); 3228 fprintf(where, 3229 " recevied %u transactions\n", 3230 trans_received); 3231 fflush(where); 3232 netperf_response.content.serv_errno = 995; 3233 send_response(); 3234 exit(1); 3235 } 3236 3237 /* Now, send the response to the remote. first copy the dlsap */ 3238 /* information from the receive to the sending control message */ 3239 3240 data_req->dl_dest_addr_offset = sizeof(dl_unitdata_req_t); 3241 bcopy((char *)data_ind + data_ind->dl_src_addr_offset, 3242 (char *)data_req + data_req->dl_dest_addr_offset, 3243 data_ind->dl_src_addr_length); 3244 data_req->dl_dest_addr_length = data_ind->dl_src_addr_length; 3245 data_req->dl_primitive = DL_UNITDATA_REQ; 3246 /* be sure to initialize the priority fields. fix from Nicholas 3247 Thomas */ 3248 data_req->dl_priority.dl_min = DL_QOS_DONT_CARE; 3249 data_req->dl_priority.dl_max = DL_QOS_DONT_CARE; 3250 3251 sctl_message.len = sizeof(dl_unitdata_req_t) + 3252 data_ind->dl_src_addr_length; 3253 if(putmsg(data_descriptor, 3254 &sctl_message, 3255 &send_message, 3256 0) != 0) { 3257 if (errno == EINTR) { 3258 /* We likely hit */ 3259 /* test-end time. */ 3260 break; 3261 } 3262 /* there is more we could do here, but it can wait */ 3263 fprintf(where, 3264 "dlpi_recv_cl_rr: putmsg failure: errno %d\n", 3265 errno); 3266 fflush(where); 3267 netperf_response.content.serv_errno = 993; 3268 send_response(); 3269 exit(1); 3270 } 3271 3272 trans_received++; 3273 if (trans_remaining) { 3274 trans_remaining--; 3275 } 3276 3277 if (debug) { 3278 fprintf(where, 3279 "recv_dlpi_cl_rr: Transaction %d complete.\n", 3280 trans_received); 3281 fflush(where); 3282 } 3283 3284 } 3285 3286 3287 /* The loop now exits due to timeout or transaction count being */ 3288 /* reached */ 3289 3290 cpu_stop(dlpi_cl_rr_request->measure_cpu,&elapsed_time); 3291 3292 if (times_up) { 3293 /* we ended the test by time, which was at least 2 seconds */ 3294 /* longer than we wanted to run. so, we want to subtract */ 3295 /* PAD_TIME from the elapsed_time. */ 3296 elapsed_time -= PAD_TIME; 3297 } 3298 /* send the results to the sender */ 3299 3300 if (debug) { 3301 fprintf(where, 3302 "recv_dlpi_cl_rr: got %d transactions\n", 3303 trans_received); 3304 fflush(where); 3305 } 3306 3307 dlpi_cl_rr_results->bytes_received = (trans_received * 3308 (dlpi_cl_rr_request->request_size + 3309 dlpi_cl_rr_request->response_size)); 3310 dlpi_cl_rr_results->trans_received = trans_received; 3311 dlpi_cl_rr_results->elapsed_time = elapsed_time; 3312 if (dlpi_cl_rr_request->measure_cpu) { 3313 dlpi_cl_rr_results->cpu_util = calc_cpu_util(elapsed_time); 3314 } 3315 3316 if (debug) { 3317 fprintf(where, 3318 "recv_dlpi_cl_rr: test complete, sending results.\n"); 3319 fflush(where); 3320 } 3321 3322 send_response(); 3323 3324} 3325 3326int 3327 recv_dlpi_co_rr() 3328{ 3329 3330 char *message; 3331 SOCKET s_listen,data_descriptor; 3332 3333 int measure_cpu; 3334 3335 int flags = 0; 3336 char *recv_message_ptr; 3337 char *send_message_ptr; 3338 struct strbuf send_message; 3339 struct strbuf recv_message; 3340 3341 int trans_received; 3342 int trans_remaining; 3343 int request_bytes_remaining; 3344 int timed_out = 0; 3345 float elapsed_time; 3346 3347 struct dlpi_co_rr_request_struct *dlpi_co_rr_request; 3348 struct dlpi_co_rr_response_struct *dlpi_co_rr_response; 3349 struct dlpi_co_rr_results_struct *dlpi_co_rr_results; 3350 3351 dlpi_co_rr_request = (struct dlpi_co_rr_request_struct *)netperf_request.content.test_specific_data; 3352 dlpi_co_rr_response = (struct dlpi_co_rr_response_struct *)netperf_response.content.test_specific_data; 3353 dlpi_co_rr_results = (struct dlpi_co_rr_results_struct *)netperf_response.content.test_specific_data; 3354 3355 if (debug) { 3356 fprintf(where,"netserver: recv_dlpi_co_rr: entered...\n"); 3357 fflush(where); 3358 } 3359 3360 /* We want to set-up the listen socket with all the desired */ 3361 /* parameters and then let the initiator know that all is ready. If */ 3362 /* socket size defaults are to be used, then the initiator will have */ 3363 /* sent us 0's. If the socket sizes cannot be changed, then we will */ 3364 /* send-back what they are. If that information cannot be determined, */ 3365 /* then we send-back -1's for the sizes. If things go wrong for any */ 3366 /* reason, we will drop back ten yards and punt. */ 3367 3368 /* If anything goes wrong, we want the remote to know about it. It */ 3369 /* would be best if the error that the remote reports to the user is */ 3370 /* the actual error we encountered, rather than some bogus unexpected */ 3371 /* response type message. */ 3372 3373 if (debug) { 3374 fprintf(where,"recv_dlpi_co_rr: setting the response type...\n"); 3375 fflush(where); 3376 } 3377 3378 netperf_response.content.response_type = DLPI_CO_RR_RESPONSE; 3379 3380 if (debug) { 3381 fprintf(where,"recv_dlpi_co_rr: the response type is set...\n"); 3382 fflush(where); 3383 } 3384 3385 /* set-up the data buffer with the requested alignment and offset */ 3386 message = (char *)malloc(DATABUFFERLEN); 3387 if (message == NULL) { 3388 printf("malloc(%d) failed!\n", DATABUFFERLEN); 3389 exit(1); 3390 } 3391 3392 /* We now alter the message_ptr variables to be at the desired */ 3393 /* alignments with the desired offsets. */ 3394 3395 if (debug) { 3396 fprintf(where, 3397 "recv_dlpi_co_rr: requested recv alignment of %d offset %d\n", 3398 dlpi_co_rr_request->recv_alignment, 3399 dlpi_co_rr_request->recv_offset); 3400 fprintf(where, 3401 "recv_dlpi_co_rr: requested send alignment of %d offset %d\n", 3402 dlpi_co_rr_request->send_alignment, 3403 dlpi_co_rr_request->send_offset); 3404 fflush(where); 3405 } 3406 3407 recv_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->recv_alignment, dlpi_co_rr_request->recv_offset); 3408 recv_message.maxlen = dlpi_co_rr_request->request_size; 3409 recv_message.len = 0; 3410 recv_message.buf = recv_message_ptr; 3411 3412 send_message_ptr = ALIGN_BUFFER(message, dlpi_co_rr_request->send_alignment, dlpi_co_rr_request->send_offset); 3413 send_message.maxlen = dlpi_co_rr_request->response_size; 3414 send_message.len = dlpi_co_rr_request->response_size; 3415 send_message.buf = send_message_ptr; 3416 3417 if (debug) { 3418 fprintf(where,"recv_dlpi_co_rr: receive alignment and offset set...\n"); 3419 fprintf(where,"recv_dlpi_co_rr: send_message.buf %x .len %d .maxlen %d\n", 3420 send_message.buf,send_message.len,send_message.maxlen); 3421 fprintf(where,"recv_dlpi_co_rr: recv_message.buf %x .len %d .maxlen %d\n", 3422 recv_message.buf,recv_message.len,recv_message.maxlen); 3423 fflush(where); 3424 } 3425 3426 /* Let's clear-out our sockaddr for the sake of cleanlines. Then we */ 3427 /* can put in OUR values !-) At some point, we may want to nail this */ 3428 /* socket to a particular network-level address, but for now, */ 3429 /* INADDR_ANY should be just fine. */ 3430 3431 /* Grab a socket to listen on, and then listen on it. */ 3432 3433 if (debug) { 3434 fprintf(where,"recv_dlpi_co_rr: grabbing a socket...\n"); 3435 fflush(where); 3436 } 3437 3438 /* lets grab a file descriptor for a particular link */ 3439 3440#ifdef __alpha 3441 3442 /* ok - even on a DEC box, strings are strings. I din't really want */ 3443 /* to ntohl the words of a string. since I don't want to teach the */ 3444 /* send_ and recv_ _request and _response routines about the types, */ 3445 /* I will put "anti-ntohl" calls here. I imagine that the "pure" */ 3446 /* solution would be to use XDR, but I am still leary of being able */ 3447 /* to find XDR libs on all platforms I want running netperf. raj */ 3448 { 3449 int *charword; 3450 int *initword; 3451 int *lastword; 3452 3453 initword = (int *) dlpi_co_rr_request->dlpi_device; 3454 lastword = initword + ((dlpi_co_rr_request->dev_name_len + 3) / 4); 3455 3456 for (charword = initword; 3457 charword < lastword; 3458 charword++) { 3459 3460 *charword = htonl(*charword); 3461 } 3462 } 3463#endif /* __alpha */ 3464 3465 if ((data_descriptor = dl_open(dlpi_co_rr_request->dlpi_device, 3466 dlpi_co_rr_request->ppa)) < 0) { 3467 netperf_response.content.serv_errno = errno; 3468 send_response(); 3469 exit(1); 3470 } 3471 3472 /* bind the file descriptor to a sap and get the resultant dlsap */ 3473 dlpi_co_rr_response->station_addr_len = 14; /*arbitrary needs fixing */ 3474 if (dl_bind(data_descriptor, 3475 dlpi_co_rr_request->sap, 3476 DL_CODLS, 3477 (char *)dlpi_co_rr_response->station_addr, 3478 &dlpi_co_rr_response->station_addr_len) != 0) { 3479 netperf_response.content.serv_errno = errno; 3480 send_response(); 3481 exit(1); 3482 } 3483 3484 /* The initiator may have wished-us to modify the socket buffer */ 3485 /* sizes. We should give it a shot. If he didn't ask us to change the */ 3486 /* sizes, we should let him know what sizes were in use at this end. */ 3487 /* If none of this code is compiled-in, then we will tell the */ 3488 /* initiator that we were unable to play with the socket buffer by */ 3489 /* setting the size in the response to -1. */ 3490 3491#ifdef DL_HP_SET_LOCAL_WIN_REQ 3492 3493 if (dlpi_co_rr_request->recv_win_size) { 3494 /* SMOP */ 3495 } 3496 3497 if (dlpi_co_rr_request->send_win_size) { 3498 /* SMOP */ 3499 } 3500 3501 /* Now, we will find-out what the sizes actually became, and report */ 3502 /* them back to the user. If the calls fail, we will just report a -1 */ 3503 /* back to the initiator for the buffer size. */ 3504 3505#else /* the system won't let us play with the buffers */ 3506 3507 dlpi_co_rr_response->recv_win_size = -1; 3508 dlpi_co_rr_response->send_win_size = -1; 3509 3510#endif /* DL_HP_SET_LOCAL_WIN_REQ */ 3511 3512 /* we may have been requested to enable the copy avoidance features. */ 3513 /* can we actually do this with DLPI, the world wonders */ 3514 3515 if (dlpi_co_rr_request->so_rcvavoid) { 3516#ifdef SO_RCV_COPYAVOID 3517 dlpi_co_rr_response->so_rcvavoid = 0; 3518#else 3519 /* it wasn't compiled in... */ 3520 dlpi_co_rr_response->so_rcvavoid = 0; 3521#endif 3522 } 3523 3524 if (dlpi_co_rr_request->so_sndavoid) { 3525#ifdef SO_SND_COPYAVOID 3526 dlpi_co_rr_response->so_sndavoid = 0; 3527#else 3528 /* it wasn't compiled in... */ 3529 dlpi_co_rr_response->so_sndavoid = 0; 3530#endif 3531 } 3532 3533 netperf_response.content.serv_errno = 0; 3534 3535 /* But wait, there's more. If the initiator wanted cpu measurements, */ 3536 /* then we must call the calibrate routine, which will return the max */ 3537 /* rate back to the initiator. If the CPU was not to be measured, or */ 3538 /* something went wrong with the calibration, we will return a 0.0 to */ 3539 /* the initiator. */ 3540 3541 dlpi_co_rr_response->cpu_rate = 0.0; /* assume no cpu */ 3542 if (dlpi_co_rr_request->measure_cpu) { 3543 dlpi_co_rr_response->measure_cpu = 1; 3544 dlpi_co_rr_response->cpu_rate = calibrate_local_cpu(dlpi_co_rr_request->cpu_rate); 3545 } 3546 3547 send_response(); 3548 3549 /* accept a connection on this file descriptor. at some point, */ 3550 /* dl_accept will "do the right thing" with the last two parms, but */ 3551 /* for now it ignores them, so we will pass zeros. */ 3552 3553 if(dl_accept(data_descriptor, 0, 0) != 0) { 3554 fprintf(where, 3555 "recv_dlpi_co_rr: error in accept, errno %d\n", 3556 errno); 3557 fflush(where); 3558 netperf_response.content.serv_errno = errno; 3559 send_response(); 3560 exit(1); 3561 } 3562 3563 if (debug) { 3564 fprintf(where, 3565 "recv_dlpi_co_rr: accept completes on the data connection.\n"); 3566 fflush(where); 3567 } 3568 3569 /* Now it's time to start receiving data on the connection. We will */ 3570 /* first grab the apropriate counters and then start grabbing. */ 3571 3572 cpu_start(dlpi_co_rr_request->measure_cpu); 3573 3574 /* The loop will exit when the sender does a shutdown, which will */ 3575 /* return a length of zero */ 3576 3577 if (dlpi_co_rr_request->test_length > 0) { 3578 times_up = 0; 3579 trans_remaining = 0; 3580 start_timer(dlpi_co_rr_request->test_length + PAD_TIME); 3581 } 3582else { 3583 times_up = 1; 3584 trans_remaining = dlpi_co_rr_request->test_length * -1; 3585} 3586 3587 while ((!times_up) || (trans_remaining > 0)) { 3588 request_bytes_remaining = dlpi_co_rr_request->request_size; 3589 3590 /* receive the request from the other side. there needs to be some */ 3591 /* more login in place for handling messages larger than link mtu, */ 3592 /* but that can wait for later */ 3593 while(request_bytes_remaining > 0) { 3594 if((getmsg(data_descriptor, 3595 0, 3596 &recv_message, 3597 &flags)) < 0) { 3598 if (errno == EINTR) { 3599 /* the timer popped */ 3600 timed_out = 1; 3601 break; 3602 } 3603 3604 if (debug) { 3605 fprintf(where,"failed getmsg call errno %d\n",errno); 3606 fprintf(where,"recv_message.len %d\n",recv_message.len); 3607 fprintf(where,"send_message.len %d\n",send_message.len); 3608 fflush(where); 3609 } 3610 3611 netperf_response.content.serv_errno = errno; 3612 send_response(); 3613 exit(1); 3614 } 3615 else { 3616 request_bytes_remaining -= recv_message.len; 3617 } 3618 } 3619 3620 if (timed_out) { 3621 /* we hit the end of the test based on time - lets bail out of */ 3622 /* here now... */ 3623 break; 3624 } 3625 3626 if (debug) { 3627 fprintf(where,"recv_message.len %d\n",recv_message.len); 3628 fprintf(where,"send_message.len %d\n",send_message.len); 3629 fflush(where); 3630 } 3631 3632 /* Now, send the response to the remote */ 3633 if((putmsg(data_descriptor, 3634 0, 3635 &send_message, 3636 0)) != 0) { 3637 if (errno == EINTR) { 3638 /* the test timer has popped */ 3639 timed_out = 1; 3640 break; 3641 } 3642 netperf_response.content.serv_errno = 994; 3643 send_response(); 3644 exit(1); 3645 } 3646 3647 trans_received++; 3648 if (trans_remaining) { 3649 trans_remaining--; 3650 } 3651 3652 if (debug) { 3653 fprintf(where, 3654 "recv_dlpi_co_rr: Transaction %d complete\n", 3655 trans_received); 3656 fflush(where); 3657 } 3658 } 3659 3660 3661 /* The loop now exits due to timeout or transaction count being */ 3662 /* reached */ 3663 3664 cpu_stop(dlpi_co_rr_request->measure_cpu,&elapsed_time); 3665 3666 if (timed_out) { 3667 /* we ended the test by time, which was at least 2 seconds */ 3668 /* longer than we wanted to run. so, we want to subtract */ 3669 /* PAD_TIME from the elapsed_time. */ 3670 elapsed_time -= PAD_TIME; 3671 } 3672 /* send the results to the sender */ 3673 3674 if (debug) { 3675 fprintf(where, 3676 "recv_dlpi_co_rr: got %d transactions\n", 3677 trans_received); 3678 fflush(where); 3679 } 3680 3681 dlpi_co_rr_results->bytes_received = (trans_received * 3682 (dlpi_co_rr_request->request_size + 3683 dlpi_co_rr_request->response_size)); 3684 dlpi_co_rr_results->trans_received = trans_received; 3685 dlpi_co_rr_results->elapsed_time = elapsed_time; 3686 if (dlpi_co_rr_request->measure_cpu) { 3687 dlpi_co_rr_results->cpu_util = calc_cpu_util(elapsed_time); 3688 } 3689 3690 if (debug) { 3691 fprintf(where, 3692 "recv_dlpi_co_rr: test complete, sending results.\n"); 3693 fflush(where); 3694 } 3695 3696 send_response(); 3697 3698} 3699 3700/* this routine will display the usage string for the DLPI tests */ 3701void 3702 print_dlpi_usage() 3703 3704{ 3705 fwrite(dlpi_usage, sizeof(char), strlen(dlpi_usage), stdout); 3706} 3707 3708 3709/* this routine will scan the command line for DLPI test arguments */ 3710void 3711 scan_dlpi_args(int argc, char *argv[]) 3712{ 3713 extern int optind, opterrs; /* index of first unused arg */ 3714 extern char *optarg; /* pointer to option string */ 3715 3716 int c; 3717 3718 char arg1[BUFSIZ], /* argument holders */ 3719 arg2[BUFSIZ]; 3720 3721 if (no_control) { 3722 fprintf(where, 3723 "The DLPI tests do not know how to run with no control connection\n"); 3724 exit(-1); 3725 } 3726 3727 /* Go through all the command line arguments and break them */ 3728 /* out. For those options that take two parms, specifying only */ 3729 /* the first will set both to that value. Specifying only the */ 3730 /* second will leave the first untouched. To change only the */ 3731 /* first, use the form first, (see the routine break_args.. */ 3732 3733#define DLPI_ARGS "D:hM:m:p:r:s:W:w:" 3734 3735 while ((c= getopt(argc, argv, DLPI_ARGS)) != EOF) { 3736 switch (c) { 3737 case '?': 3738 case 'h': 3739 print_dlpi_usage(); 3740 exit(1); 3741 case 'D': 3742 /* set the dlpi device file name(s) */ 3743 break_args(optarg,arg1,arg2); 3744 if (arg1[0]) 3745 strcpy(loc_dlpi_device,arg1); 3746 if (arg2[0]) 3747 strcpy(rem_dlpi_device,arg2); 3748 break; 3749 case 'm': 3750 /* set the send size */ 3751 send_size = atoi(optarg); 3752 break; 3753 case 'M': 3754 /* set the recv size */ 3755 recv_size = atoi(optarg); 3756 break; 3757 case 'p': 3758 /* set the local/remote ppa */ 3759 break_args(optarg,arg1,arg2); 3760 if (arg1[0]) 3761 loc_ppa = atoi(arg1); 3762 if (arg2[0]) 3763 rem_ppa = atoi(arg2); 3764 break; 3765 case 'r': 3766 /* set the request/response sizes */ 3767 break_args(optarg,arg1,arg2); 3768 if (arg1[0]) 3769 req_size = atoi(arg1); 3770 if (arg2[0]) 3771 rsp_size = atoi(arg2); 3772 break; 3773 case 's': 3774 /* set the 802.2 sap for the test */ 3775 dlpi_sap = atoi(optarg); 3776 break; 3777 case 'w': 3778 /* set local window sizes */ 3779 break_args(optarg,arg1,arg2); 3780 if (arg1[0]) 3781 lsw_size = atoi(arg1); 3782 if (arg2[0]) 3783 lrw_size = atoi(arg2); 3784 break; 3785 case 'W': 3786 /* set remote window sizes */ 3787 break_args(optarg,arg1,arg2); 3788 if (arg1[0]) 3789 rsw_size = atoi(arg1); 3790 if (arg2[0]) 3791 rrw_size = atoi(arg2); 3792 break; 3793 }; 3794 } 3795} 3796 3797 3798#endif /* WANT_DLPI */ 3799