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