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