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