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 */ 84int open_file(void* tsd); 85void server(void* tsd); 86int tcp_accept(int sock, int rdwr); 87void sock_optimize(int sock, int flags); 88int sockport(int s); 89int tcp_server(int prog, int rdwr); 90int tcp_connect(char *host, int prog, int rdwr); 91int open_socket(void *tsd); 92 93 94typedef 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 */ 102typedef 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 113static int optt = 1; 114static int optn = -1; 115static int optp = 1; 116static int optw = 0; 117 118/* 119 * lmbench routines, etc. brought over for this benchmark 120 */ 121 122void 123morefds(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 134int 135open_file(void* tsd) 136{ 137 tsd_t* ts = (tsd_t*)tsd; 138 return (int) open(ts->fname, O_RDONLY); 139} 140 141int 142open_socket(void* tsd) 143{ 144 return tcp_connect("localhost", TCP_SELECT, SOCKOPT_NONE); 145} 146 147void 148server(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 */ 200int 201tcp_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 210retry: 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 224void 225sock_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 258int 259sockport(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 */ 278int 279tcp_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 */ 328int 329tcp_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*/ 422int 423benchmark_initbatch(void *tsd) 424{ 425 /* 426 * initialize your state variables here second 427 */ 428 return (0); 429} 430 431int 432benchmark_finirun() 433{ 434 return (0); 435} 436 437int 438benchmark_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 468int 469benchmark_fini() 470{ 471 return (0); 472} 473 474int 475benchmark_finibatch(void *tsd) 476{ 477 return (0); 478} 479 480char * 481benchmark_result() 482{ 483 static char result = '\0'; 484 return (&result); 485} 486 487int 488benchmark_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 502int 503benchmark_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 525int 526benchmark_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 585int 586benchmark_initrun() 587{ 588 return (0); 589} 590 591int 592benchmark(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 623