]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/libMicro/apple/lmbench_select_tcp.c
aea77dae101690a2663016db191fbd6cc0464b97
[apple/xnu.git] / tools / tests / libMicro / apple / lmbench_select_tcp.c
1 /*
2 * Copyright (c) 2006 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30 /*
31 * Order of Execution
32 *
33 * benchmark_init
34 *
35 * benchmark_optswitch
36 *
37 * benchmark_initrun
38 *
39 * benchmark_initworker
40 * benchmark_initbatch
41 * benchmark
42 * benchmark_finibatch
43 * benchmark_initbatch
44 * benchmark
45 * benchmark_finibatch, etc.
46 * benchmark_finiworker
47 *
48 * benchmark_result
49 *
50 * benchmark_finirun
51 *
52 * benchmark_fini
53 */
54
55
56
57 #ifdef __sun
58 #pragma ident "@(#)socket.c 1.3 05/08/04 Apple Inc."
59 #endif
60
61
62
63 #include <unistd.h>
64 #include <stdlib.h>
65 #include <stdio.h>
66 #include <fcntl.h>
67 #include <string.h>
68 #include <errno.h>
69
70 #include <netinet/in.h>
71 #include <signal.h>
72 #include <string.h>
73 #include <sys/errno.h>
74 #include <sys/fcntl.h>
75 #include <netdb.h>
76 #include <rpc/rpc.h>
77 #include <rpc/pmap_clnt.h>
78
79 #include "../libmicro.h"
80
81 /*
82 * lmbench routines, etc. brought over for this benchmark
83 */
84 int open_file(void* tsd);
85 void server(void* tsd);
86 int tcp_accept(int sock, int rdwr);
87 void sock_optimize(int sock, int flags);
88 int sockport(int s);
89 int tcp_server(int prog, int rdwr);
90 int tcp_connect(char *host, int prog, int rdwr);
91 int open_socket(void *tsd);
92
93
94 typedef int (*open_f)(void* tsd);
95 /*
96 * end of lmbench support routines
97 */
98
99 /*
100 * Your state variables should live in the tsd_t struct below
101 */
102 typedef struct {
103 char fname[L_tmpnam];
104 open_f fid_f;
105 pid_t pid;
106 int sock;
107 int fid;
108 int num;
109 int max;
110 fd_set set;
111 } tsd_t;
112
113 static int optt = 1;
114 static int optn = -1;
115 static int optp = 1;
116 static int optw = 0;
117
118 /*
119 * lmbench routines, etc. brought over for this benchmark
120 */
121
122 void
123 morefds(void)
124 {
125 #ifdef RLIMIT_NOFILE
126 struct rlimit r;
127
128 getrlimit(RLIMIT_NOFILE, &r);
129 r.rlim_cur = r.rlim_max;
130 setrlimit(RLIMIT_NOFILE, &r);
131 #endif
132 }
133
134 int
135 open_file(void* tsd)
136 {
137 tsd_t* ts = (tsd_t*)tsd;
138 return (int) open(ts->fname, O_RDONLY);
139 }
140
141 int
142 open_socket(void* tsd)
143 {
144 return tcp_connect("localhost", TCP_SELECT, SOCKOPT_NONE);
145 }
146
147 void
148 server(void* tsd)
149 {
150 int pid;
151 tsd_t *ts = (tsd_t *)tsd;
152
153 pid = getpid();
154 ts->pid = 0;
155
156 if (ts->fid_f == open_file) {
157 /* Create a temporary file for clients to open */
158 sprintf(ts->fname, "lat_selectXXXXXX");
159 ts->fid = mkstemp(ts->fname);
160 if (ts->fid <= 0) {
161 char buf[L_tmpnam+128];
162 sprintf(buf, "lat_select: Could not create temp file %s", ts->fname);
163 perror(buf);
164 exit(1);
165 }
166 close(ts->fid);
167 return;
168 }
169
170 /* Create a socket for clients to connect to */
171 ts->sock = tcp_server(TCP_SELECT, SOCKOPT_REUSE);
172 if (ts->sock <= 0) {
173 perror("lat_select: Could not open tcp server socket");
174 exit(1);
175 }
176
177 /* Start a server process to accept client connections */
178 switch(ts->pid = fork()) {
179 case 0:
180 /* child server process */
181 while (pid == getppid()) {
182 int newsock = tcp_accept(ts->sock, SOCKOPT_NONE);
183 read(newsock, &ts->fid, 1);
184 close(newsock);
185 }
186 exit(0);
187 case -1:
188 /* error */
189 perror("lat_select::server(): fork() failed");
190 exit(1);
191 default:
192 break;
193 }
194 }
195
196
197 /*
198 * Accept a connection and return it
199 */
200 int
201 tcp_accept(int sock, int rdwr)
202 {
203 struct sockaddr_in s;
204 int newsock;
205 socklen_t namelen;
206
207 namelen = sizeof(s);
208 bzero((void*)&s, namelen);
209
210 retry:
211 if ((newsock = accept(sock, (struct sockaddr*)&s, &namelen)) < 0) {
212 if (errno == EINTR)
213 goto retry;
214 perror("accept");
215 exit(6);
216 }
217 #ifdef LIBTCP_VERBOSE
218 fprintf(stderr, "Server newsock port %d\n", sockport(newsock));
219 #endif
220 sock_optimize(newsock, rdwr);
221 return (newsock);
222 }
223
224 void
225 sock_optimize(int sock, int flags)
226 {
227 if (flags & SOCKOPT_READ) {
228 int sockbuf = SOCKBUF;
229
230 while (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &sockbuf,
231 sizeof(int))) {
232 sockbuf >>= 1;
233 }
234 #ifdef LIBTCP_VERBOSE
235 fprintf(stderr, "sockopt %d: RCV: %dK\n", sock, sockbuf>>10);
236 #endif
237 }
238 if (flags & SOCKOPT_WRITE) {
239 int sockbuf = SOCKBUF;
240
241 while (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sockbuf,
242 sizeof(int))) {
243 sockbuf >>= 1;
244 }
245 #ifdef LIBTCP_VERBOSE
246 fprintf(stderr, "sockopt %d: SND: %dK\n", sock, sockbuf>>10);
247 #endif
248 }
249 if (flags & SOCKOPT_REUSE) {
250 int val = 1;
251 if (setsockopt(sock, SOL_SOCKET,
252 SO_REUSEADDR, &val, sizeof(val)) == -1) {
253 perror("SO_REUSEADDR");
254 }
255 }
256 }
257
258 int
259 sockport(int s)
260 {
261 socklen_t namelen;
262 struct sockaddr_in sin;
263
264 namelen = sizeof(sin);
265 if (getsockname(s, (struct sockaddr *)&sin, &namelen) < 0) {
266 perror("getsockname");
267 return(-1);
268 }
269 return ((int)ntohs(sin.sin_port));
270 }
271
272 /*
273 * Get a TCP socket, bind it, figure out the port,
274 * and advertise the port as program "prog".
275 *
276 * XXX - it would be nice if you could advertise ascii strings.
277 */
278 int
279 tcp_server(int prog, int rdwr)
280 {
281 int sock;
282 struct sockaddr_in s;
283
284 #ifdef LIBTCP_VERBOSE
285 fprintf(stderr, "tcp_server(%u, %u)\n", prog, rdwr);
286 #endif
287 if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
288 perror("socket");
289 exit(1);
290 }
291 sock_optimize(sock, rdwr);
292 bzero((void*)&s, sizeof(s));
293 s.sin_family = AF_INET;
294 if (prog < 0) {
295 s.sin_port = htons(-prog);
296 }
297 if (bind(sock, (struct sockaddr*)&s, sizeof(s)) < 0) {
298 perror("bind");
299 exit(2);
300 }
301 if (listen(sock, 100) < 0) {
302 perror("listen");
303 exit(4);
304 }
305 if (prog > 0) {
306 #ifdef LIBTCP_VERBOSE
307 fprintf(stderr, "Server port %d\n", sockport(sock));
308 #endif
309 (void)pmap_unset((u_long)prog, (u_long)1);
310 if (!pmap_set((u_long)prog, (u_long)1, (u_long)IPPROTO_TCP,
311 (unsigned short)sockport(sock))) {
312 perror("pmap_set");
313 exit(5);
314 }
315 }
316 return (sock);
317 }
318
319
320 /*
321 * Connect to the TCP socket advertised as "prog" on "host" and
322 * return the connected socket.
323 *
324 * Hacked Thu Oct 27 1994 to cache pmap_getport calls. This saves
325 * about 4000 usecs in loopback lat_connect calls. I suppose we
326 * should time gethostbyname() & pmap_getprot(), huh?
327 */
328 int
329 tcp_connect(char *host, int prog, int rdwr)
330 {
331 static struct hostent *h;
332 static struct sockaddr_in s;
333 static u_short save_port;
334 static u_long save_prog;
335 static char *save_host;
336 int sock;
337 static int tries = 0;
338
339 if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
340 perror("socket");
341 exit(1);
342 }
343 if (rdwr & SOCKOPT_PID) {
344 static unsigned short port;
345 struct sockaddr_in sin;
346
347 if (!port) {
348 port = (unsigned short)(getpid() << 4);
349 if (port < 1024) {
350 port += 1024;
351 }
352 }
353 do {
354 port++;
355 bzero((void*)&sin, sizeof(sin));
356 sin.sin_family = AF_INET;
357 sin.sin_port = htons(port);
358 } while (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) == -1);
359 }
360 #ifdef LIBTCP_VERBOSE
361 else {
362 struct sockaddr_in sin;
363
364 bzero((void*)&sin, sizeof(sin));
365 sin.sin_family = AF_INET;
366 if (bind(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
367 perror("bind");
368 exit(2);
369 }
370 }
371 fprintf(stderr, "Client port %d\n", sockport(sock));
372 #endif
373 sock_optimize(sock, rdwr);
374 if (!h || host != save_host || prog != save_prog) {
375 save_host = host; /* XXX - counting on them not
376 * changing it - benchmark only.
377 */
378 save_prog = prog;
379 if (!(h = gethostbyname(host))) {
380 perror(host);
381 exit(2);
382 }
383 bzero((void *) &s, sizeof(s));
384 s.sin_family = AF_INET;
385 bcopy((void*)h->h_addr, (void *)&s.sin_addr, h->h_length);
386 if (prog > 0) {
387 save_port = pmap_getport(&s, prog,
388 (u_long)1, IPPROTO_TCP);
389 if (!save_port) {
390 perror("lib TCP: No port found");
391 exit(3);
392 }
393 #ifdef LIBTCP_VERBOSE
394 fprintf(stderr, "Server port %d\n", save_port);
395 #endif
396 s.sin_port = htons(save_port);
397 } else {
398 s.sin_port = htons(-prog);
399 }
400 }
401 if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0) {
402 if (errno == ECONNRESET
403 || errno == ECONNREFUSED
404 || errno == EAGAIN) {
405 close(sock);
406 if (++tries > 10) return(-1);
407 return (tcp_connect(host, prog, rdwr));
408 }
409 perror("connect");
410 exit(4);
411 }
412 tries = 0;
413 return (sock);
414 }
415
416
417 /*
418 * end of lmbench support routines
419 */
420
421 /*ARGSUSED*/
422 int
423 benchmark_initbatch(void *tsd)
424 {
425 /*
426 * initialize your state variables here second
427 */
428 return (0);
429 }
430
431 int
432 benchmark_finirun()
433 {
434 return (0);
435 }
436
437 int
438 benchmark_init()
439 {
440 /*
441 * the lm_optstr must be defined here or no options for you
442 *
443 * ...and the framework will throw an error
444 *
445 */
446 (void) sprintf(lm_optstr, "p:w:n:t:");
447 /*
448 * working hypothesis:
449 *
450 * tsd_t is the struct that we can pass around our
451 * state info in
452 *
453 * lm_tsdsize will allocate the space we need for this
454 * structure throughout the rest of the framework
455 */
456 lm_tsdsize = sizeof (tsd_t);
457
458 (void) sprintf(lm_usage,
459 " [-p parallelism (default 1)]\n"
460 " [-w warmup (default 0)]\n"
461 " [-n number of descriptors (default 1)]\n"
462 " [-t int (default 1)]\n"
463 "notes: measures lmbench_select_file\n");
464 lm_defB = 1;
465 return (0);
466 }
467
468 int
469 benchmark_fini()
470 {
471 return (0);
472 }
473
474 int
475 benchmark_finibatch(void *tsd)
476 {
477 return (0);
478 }
479
480 char *
481 benchmark_result()
482 {
483 static char result = '\0';
484 return (&result);
485 }
486
487 int
488 benchmark_finiworker(void *tsd)
489 {
490 tsd_t *ts = (tsd_t *)tsd;
491 int i;
492 // pulls in the lmbench cleanup code
493 for (i = 0; i <= ts->max; ++i) {
494 if (FD_ISSET(i, &(ts->set)))
495 close(i);
496 }
497 FD_ZERO(&(ts->set));
498 unlink(ts->fname);
499 return (0);
500 }
501
502 int
503 benchmark_optswitch(int opt, char *optarg)
504 {
505
506 switch (opt) {
507 case 't':
508 optt = sizetoint(optarg);
509 break;
510 case 'n':
511 optn = sizetoint(optarg);
512 break;
513 case 'p':
514 optp = sizetoint(optarg);
515 break;
516 case 'w':
517 optw = sizetoint(optarg);
518 break;
519 default:
520 return (-1);
521 }
522 return (0);
523 }
524
525 int
526 benchmark_initworker(void *tsd)
527 {
528 // pulls in code from lmbench main and initialize
529 int n = 0;
530 /*
531 * initialize your state variables here first
532 */
533 tsd_t *ts = (tsd_t *)tsd;
534 int N, fid, fd;
535
536 /*
537 * default number of file descriptors
538 */
539 ts->num = 200;
540 if (optn > 0) {
541 ts->num = optn;
542 }
543 N = ts->num;
544
545 /*
546 * grab more file descriptors
547 */
548
549 morefds();
550
551 ts->fid_f = open_socket;
552 server(ts);
553 /*
554 * Initialize function from lmbench
555 * for this test
556 */
557 fid = (*ts->fid_f)(ts);
558 if (fid <= 0) {
559 perror("Could not open device");
560 exit(1);
561 }
562 ts->max = 0;
563 FD_ZERO(&(ts->set));
564 for (n = 0; n < N; n++) {
565 fd = dup(fid);
566 //(void) fprintf(stderr, "benchmark_initworker: errno result is %d - \"%s\"\n",errno, strerror(errno));
567
568 if (fd == -1) break;
569 if (fd > ts->max)
570 ts->max = fd;
571 FD_SET(fd, &(ts->set));
572 //(void) fprintf(stderr, "initworker FD_SET: ts->set result is %i\n",ts->set);
573
574 }
575 //(void) fprintf(stderr, "benchmark_initworker: after second macro/loop\n");
576
577 ts->max++;
578 close(fid);
579 if (n != N)
580 exit(1);
581 /* end of initialize function */
582 return (0);
583 }
584
585 int
586 benchmark_initrun()
587 {
588 return (0);
589 }
590
591 int
592 benchmark(void *tsd, result_t *res)
593 {
594 /*
595 * initialize your state variables here last
596 *
597 * and realize that you are paying for your initialization here
598 * and it is really a bad idea
599 */
600 tsd_t *ts = (tsd_t *)tsd;
601 fd_set nosave;
602 static struct timeval tv;
603
604 //(void) fprintf(stderr, "benchmark\n");
605
606 int i;
607 //int sel_res;
608 tv.tv_sec = 0;
609 tv.tv_usec = 0;
610
611
612 for (i = 0; i < lm_optB; i++) {
613 nosave = ts->set;
614 //(void) fprintf(stderr, "benchmark: nosave is %i\n", nosave);
615
616 select(ts->num, 0, &nosave, 0, &tv);
617
618 }
619 res->re_count = i;
620 return (0);
621 }
622