xref: /freebsd/tools/tools/netrate/juggle/juggle.c (revision b3e76948)
14992df3eSRobert Watson /*-
24992df3eSRobert Watson  * Copyright (c) 2005 Robert N. M. Watson
34992df3eSRobert Watson  * All rights reserved.
44992df3eSRobert Watson  *
54992df3eSRobert Watson  * Redistribution and use in source and binary forms, with or without
64992df3eSRobert Watson  * modification, are permitted provided that the following conditions
74992df3eSRobert Watson  * are met:
84992df3eSRobert Watson  * 1. Redistributions of source code must retain the above copyright
94992df3eSRobert Watson  *    notice, this list of conditions and the following disclaimer.
104992df3eSRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
114992df3eSRobert Watson  *    notice, this list of conditions and the following disclaimer in the
124992df3eSRobert Watson  *    documentation and/or other materials provided with the distribution.
134992df3eSRobert Watson  *
144992df3eSRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154992df3eSRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164992df3eSRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174992df3eSRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184992df3eSRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194992df3eSRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204992df3eSRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214992df3eSRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224992df3eSRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234992df3eSRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244992df3eSRobert Watson  * SUCH DAMAGE.
254992df3eSRobert Watson  */
264992df3eSRobert Watson 
274992df3eSRobert Watson #include <sys/types.h>
284992df3eSRobert Watson #include <sys/socket.h>
29cd94bb1eSSergey Kandaurov #include <sys/stdint.h>
304992df3eSRobert Watson #include <sys/time.h>
314992df3eSRobert Watson #include <sys/utsname.h>
324992df3eSRobert Watson #include <sys/wait.h>
334992df3eSRobert Watson 
344992df3eSRobert Watson #include <netinet/in.h>
354992df3eSRobert Watson 
364992df3eSRobert Watson #include <err.h>
374992df3eSRobert Watson #include <errno.h>
384992df3eSRobert Watson #include <pthread.h>
394992df3eSRobert Watson #include <signal.h>
404992df3eSRobert Watson #include <stdio.h>
414992df3eSRobert Watson #include <stdlib.h>
424992df3eSRobert Watson #include <string.h>
434992df3eSRobert Watson #include <unistd.h>
444992df3eSRobert Watson 
454992df3eSRobert Watson /*
464992df3eSRobert Watson  * juggle is a simple IPC/context switch performance test, which works on
474992df3eSRobert Watson  * pairs of file descriptors of various types.  In various runs, it considers
484992df3eSRobert Watson  * the cost of bouncing a message synchronously across the descriptor pair,
494992df3eSRobert Watson  * either in the same thread, two different threads, or two different
504992df3eSRobert Watson  * processes.  Timing measurements for each series of I/O's are reported, but
514992df3eSRobert Watson  * the first measurement in each series discarded as "warmup" on the IPC
524992df3eSRobert Watson  * primitive.  Variations on the test permit for pipelining, or the insertion
534992df3eSRobert Watson  * of more than one packet into the stream at a time, intended to permit
544992df3eSRobert Watson  * greater parallelism, hopefully allowing performance numbers to reflect
554992df3eSRobert Watson  * use of available parallelism, and/or intelligence in context switching to
564992df3eSRobert Watson  * avoid premature switching when multiple messages are queued.
574992df3eSRobert Watson  */
584992df3eSRobert Watson 
594992df3eSRobert Watson /*
604992df3eSRobert Watson  * The UDP test uses UDP over the loopback interface.  Two arbitrary but
614992df3eSRobert Watson  * fixed port numbers.
624992df3eSRobert Watson  */
634992df3eSRobert Watson #define	UDP_PORT1	2020
644992df3eSRobert Watson #define	UDP_PORT2	2021
654992df3eSRobert Watson 
664992df3eSRobert Watson /*
674992df3eSRobert Watson  * Size of each message.  Must be smaller than the socket buffer or pipe
684992df3eSRobert Watson  * buffer maximum size, as we want to send it atomically without blocking.
694992df3eSRobert Watson  * If pipelining is in use, must be able to fit PIPELINE_MAX of these
704992df3eSRobert Watson  * messages into the send queue.
714992df3eSRobert Watson  */
724992df3eSRobert Watson #define	MESSAGELEN	128
734992df3eSRobert Watson 
744992df3eSRobert Watson /*
754992df3eSRobert Watson  * Number of message cycles -- into fd1, out of fd2, into fd2, and out of
764992df3eSRobert Watson  * fd1.  By counting in cycles, we allow the master thread or process to
774992df3eSRobert Watson  * perform timing without explicitly synchronizing with the secondary thread
784992df3eSRobert Watson  * or process.
794992df3eSRobert Watson  */
804992df3eSRobert Watson #define	NUMCYCLES	1024
814992df3eSRobert Watson 
824992df3eSRobert Watson /*
834992df3eSRobert Watson  * Number of times to run each test.
844992df3eSRobert Watson  */
854992df3eSRobert Watson #define	LOOPS		10
864992df3eSRobert Watson 
874992df3eSRobert Watson /*
884992df3eSRobert Watson  * Number of in-flight messages per cycle.  I adjusting this value, be
894992df3eSRobert Watson  * careful not to exceed the socket/etc buffer depth, or messages may be lost
904992df3eSRobert Watson  * or result in blocking.
914992df3eSRobert Watson  */
924992df3eSRobert Watson #define	PIPELINE_MAX	4
934992df3eSRobert Watson 
944992df3eSRobert Watson static int
udp_create(int * fd1p,int * fd2p)954992df3eSRobert Watson udp_create(int *fd1p, int *fd2p)
964992df3eSRobert Watson {
974992df3eSRobert Watson 	struct sockaddr_in sin1, sin2;
984992df3eSRobert Watson 	int sock1, sock2;
994992df3eSRobert Watson 
1004992df3eSRobert Watson 	sock1 = socket(PF_INET, SOCK_DGRAM, 0);
1014992df3eSRobert Watson 	if (sock1 == -1)
1024992df3eSRobert Watson 		return (-1);
1034992df3eSRobert Watson 
1044992df3eSRobert Watson 	sock2 = socket(PF_INET, SOCK_DGRAM, 0);
1054992df3eSRobert Watson 	if (sock2 == -1) {
1064992df3eSRobert Watson 		close(sock1);
1074992df3eSRobert Watson 		return (-1);
1084992df3eSRobert Watson 	}
1094992df3eSRobert Watson 
1104992df3eSRobert Watson 	bzero(&sin1, sizeof(sin1));
1114992df3eSRobert Watson 	sin1.sin_len = sizeof(sin1);
1124992df3eSRobert Watson 	sin1.sin_family = AF_INET;
1134992df3eSRobert Watson 	sin1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1144992df3eSRobert Watson 	sin1.sin_port = htons(UDP_PORT1);
1154992df3eSRobert Watson 
1164992df3eSRobert Watson 	bzero(&sin2, sizeof(sin2));
1174992df3eSRobert Watson 	sin2.sin_len = sizeof(sin2);
1184992df3eSRobert Watson 	sin2.sin_family = AF_INET;
1194992df3eSRobert Watson 	sin2.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1204992df3eSRobert Watson 	sin2.sin_port = htons(UDP_PORT2);
1214992df3eSRobert Watson 
1224992df3eSRobert Watson 	if (bind(sock1, (struct sockaddr *) &sin1, sizeof(sin1)) < 0) {
1234992df3eSRobert Watson 		close(sock1);
1244992df3eSRobert Watson 		close(sock2);
1254992df3eSRobert Watson 		return (-1);
1264992df3eSRobert Watson 	}
1274992df3eSRobert Watson 
1284992df3eSRobert Watson 	if (bind(sock2, (struct sockaddr *) &sin2, sizeof(sin2)) < 0) {
1294992df3eSRobert Watson 		close(sock1);
1304992df3eSRobert Watson 		close(sock2);
1314992df3eSRobert Watson 		return (-1);
1324992df3eSRobert Watson 	}
1334992df3eSRobert Watson 
1344992df3eSRobert Watson 	if (connect(sock1, (struct sockaddr *) &sin2, sizeof(sin2)) < 0) {
1354992df3eSRobert Watson 		close(sock1);
1364992df3eSRobert Watson 		close(sock2);
1374992df3eSRobert Watson 		return (-1);
1384992df3eSRobert Watson 	}
1394992df3eSRobert Watson 
1404992df3eSRobert Watson 	if (connect(sock2, (struct sockaddr *) &sin1, sizeof(sin1)) < 0) {
1414992df3eSRobert Watson 		close(sock1);
1424992df3eSRobert Watson 		close(sock2);
1434992df3eSRobert Watson 		return (-1);
1444992df3eSRobert Watson 	}
1454992df3eSRobert Watson 
1464992df3eSRobert Watson 	*fd1p = sock1;
1474992df3eSRobert Watson 	*fd2p = sock2;
1484992df3eSRobert Watson 
1494992df3eSRobert Watson 	return (0);
1504992df3eSRobert Watson }
1514992df3eSRobert Watson 
1524992df3eSRobert Watson static int
pipe_create(int * fd1p,int * fd2p)1534992df3eSRobert Watson pipe_create(int *fd1p, int *fd2p)
1544992df3eSRobert Watson {
1554992df3eSRobert Watson 	int fds[2];
1564992df3eSRobert Watson 
1574992df3eSRobert Watson 	if (pipe(fds) < 0)
1584992df3eSRobert Watson 		return (-1);
1594992df3eSRobert Watson 
1604992df3eSRobert Watson 	*fd1p = fds[0];
1614992df3eSRobert Watson 	*fd2p = fds[1];
1624992df3eSRobert Watson 
1634992df3eSRobert Watson 	return (0);
1644992df3eSRobert Watson }
1654992df3eSRobert Watson 
1664992df3eSRobert Watson static int
socketpairdgram_create(int * fd1p,int * fd2p)1674992df3eSRobert Watson socketpairdgram_create(int *fd1p, int *fd2p)
1684992df3eSRobert Watson {
1694992df3eSRobert Watson 	int fds[2];
1704992df3eSRobert Watson 
1714992df3eSRobert Watson 	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) < 0)
1724992df3eSRobert Watson 		return (-1);
1734992df3eSRobert Watson 
1744992df3eSRobert Watson 	*fd1p = fds[0];
1754992df3eSRobert Watson 	*fd2p = fds[1];
1764992df3eSRobert Watson 
1774992df3eSRobert Watson 	return (0);
1784992df3eSRobert Watson }
1794992df3eSRobert Watson 
1804992df3eSRobert Watson static int
socketpairstream_create(int * fd1p,int * fd2p)1814992df3eSRobert Watson socketpairstream_create(int *fd1p, int *fd2p)
1824992df3eSRobert Watson {
1834992df3eSRobert Watson 	int fds[2];
1844992df3eSRobert Watson 
1854992df3eSRobert Watson 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0)
1864992df3eSRobert Watson 		return (-1);
1874992df3eSRobert Watson 
1884992df3eSRobert Watson 	*fd1p = fds[0];
1894992df3eSRobert Watson 	*fd2p = fds[1];
1904992df3eSRobert Watson 
1914992df3eSRobert Watson 	return (0);
1924992df3eSRobert Watson }
1934992df3eSRobert Watson 
1944992df3eSRobert Watson static int
message_send(int s)1954992df3eSRobert Watson message_send(int s)
1964992df3eSRobert Watson {
1974992df3eSRobert Watson 	u_char buffer[MESSAGELEN];
1984992df3eSRobert Watson 	ssize_t len;
1994992df3eSRobert Watson 
2004992df3eSRobert Watson 	bzero(buffer, sizeof(buffer));
2014992df3eSRobert Watson 
2024992df3eSRobert Watson 	len = write(s, buffer, sizeof(buffer));
2034992df3eSRobert Watson 	if (len == -1)
2044992df3eSRobert Watson 		return (-1);
2054992df3eSRobert Watson 	if (len != sizeof(buffer)) {
2064992df3eSRobert Watson 		errno = EMSGSIZE;
2074992df3eSRobert Watson 		return (-1);
2084992df3eSRobert Watson 	}
2094992df3eSRobert Watson 	return (0);
2104992df3eSRobert Watson }
2114992df3eSRobert Watson 
2124992df3eSRobert Watson static int
message_recv(int s)2134992df3eSRobert Watson message_recv(int s)
2144992df3eSRobert Watson {
2154992df3eSRobert Watson 	u_char buffer[MESSAGELEN];
2164992df3eSRobert Watson 	ssize_t len;
2174992df3eSRobert Watson 
2184992df3eSRobert Watson 	len = read(s, buffer, sizeof(buffer));
2194992df3eSRobert Watson 	if (len == -1)
2204992df3eSRobert Watson 		return (-1);
2214992df3eSRobert Watson 	if (len != sizeof(buffer)) {
2224992df3eSRobert Watson 		errno = EMSGSIZE;
2234992df3eSRobert Watson 		return (-1);
2244992df3eSRobert Watson 	}
2254992df3eSRobert Watson 	return (0);
2264992df3eSRobert Watson }
2274992df3eSRobert Watson 
2284992df3eSRobert Watson /*
2294992df3eSRobert Watson  * Juggle messages between two file descriptors in a single thread/process,
2304992df3eSRobert Watson  * so simply a measure of IPC performance.
2314992df3eSRobert Watson  */
2324992df3eSRobert Watson static struct timespec
juggle(int fd1,int fd2,int pipeline)2334992df3eSRobert Watson juggle(int fd1, int fd2, int pipeline)
2344992df3eSRobert Watson {
2354992df3eSRobert Watson 	struct timespec tstart, tfinish;
2364992df3eSRobert Watson 	int i, j;
2374992df3eSRobert Watson 
2384992df3eSRobert Watson 	if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
2394992df3eSRobert Watson 		err(-1, "juggle: clock_gettime");
2404992df3eSRobert Watson 
2414992df3eSRobert Watson 	for (i = 0; i < NUMCYCLES; i++) {
2424992df3eSRobert Watson 
2434992df3eSRobert Watson 		for (j = 0; j < pipeline; j++) {
2444992df3eSRobert Watson 			if (message_send(fd1) < 0)
2454992df3eSRobert Watson 				err(-1, "message_send fd1");
2464992df3eSRobert Watson 		}
2474992df3eSRobert Watson 
2484992df3eSRobert Watson 		for (j = 0; j < pipeline; j++) {
2494992df3eSRobert Watson 			if (message_recv(fd2) < 0)
2504992df3eSRobert Watson 				err(-1, "message_recv fd2");
2514992df3eSRobert Watson 
2524992df3eSRobert Watson 			if (message_send(fd2) < 0)
2534992df3eSRobert Watson 				err(-1, "message_send fd2");
2544992df3eSRobert Watson 		}
2554992df3eSRobert Watson 
2564992df3eSRobert Watson 		for (j = 0; j < pipeline; j++) {
2574992df3eSRobert Watson 			if (message_recv(fd1) < 0)
2584992df3eSRobert Watson 				err(-1, "message_recv fd1");
2594992df3eSRobert Watson 		}
2604992df3eSRobert Watson 	}
2614992df3eSRobert Watson 
2624992df3eSRobert Watson 	if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
2634992df3eSRobert Watson 		err(-1, "juggle: clock_gettime");
2644992df3eSRobert Watson 
2656040822cSAlan Somers 	timespecsub(&tfinish, &tstart, &tfinish);
2664992df3eSRobert Watson 
2674992df3eSRobert Watson 	return (tfinish);
2684992df3eSRobert Watson }
2694992df3eSRobert Watson 
2704992df3eSRobert Watson /*
2714992df3eSRobert Watson  * Juggle messages between two file descriptors in two threads, so measure
2724992df3eSRobert Watson  * the cost of IPC and the cost of a thread context switch.
2734992df3eSRobert Watson  *
2744992df3eSRobert Watson  * In order to avoid measuring thread creation time, we make use of a
2754992df3eSRobert Watson  * condition variable to decide when both threads are ready to begin
2764992df3eSRobert Watson  * juggling.
2774992df3eSRobert Watson  */
2784992df3eSRobert Watson static int threaded_child_ready;
2794992df3eSRobert Watson static pthread_mutex_t threaded_mtx;
2804992df3eSRobert Watson static pthread_cond_t threaded_cond;
2814992df3eSRobert Watson static int threaded_pipeline;
2824992df3eSRobert Watson 
2834992df3eSRobert Watson static void *
juggling_thread(void * arg)2844992df3eSRobert Watson juggling_thread(void *arg)
2854992df3eSRobert Watson {
2864992df3eSRobert Watson 	int fd2, i, j;
2874992df3eSRobert Watson 
2884992df3eSRobert Watson 	fd2 = *(int *)arg;
2894992df3eSRobert Watson 
2902949bd10SRuslan Ermilov 	if (pthread_mutex_lock(&threaded_mtx) != 0)
2914992df3eSRobert Watson 		err(-1, "juggling_thread: pthread_mutex_lock");
2924992df3eSRobert Watson 
2934992df3eSRobert Watson 	threaded_child_ready = 1;
2944992df3eSRobert Watson 
2952949bd10SRuslan Ermilov 	if (pthread_cond_signal(&threaded_cond) != 0)
2964992df3eSRobert Watson 		err(-1, "juggling_thread: pthread_cond_signal");
2974992df3eSRobert Watson 
2982949bd10SRuslan Ermilov 	if (pthread_mutex_unlock(&threaded_mtx) != 0)
2994992df3eSRobert Watson 		err(-1, "juggling_thread: pthread_mutex_unlock");
3004992df3eSRobert Watson 
3014992df3eSRobert Watson 	for (i = 0; i < NUMCYCLES; i++) {
3024992df3eSRobert Watson 		for (j = 0; j < threaded_pipeline; j++) {
3034992df3eSRobert Watson 			if (message_recv(fd2) < 0)
3044992df3eSRobert Watson 				err(-1, "message_recv fd2");
3054992df3eSRobert Watson 
3064992df3eSRobert Watson 			if (message_send(fd2) < 0)
3074992df3eSRobert Watson 				err(-1, "message_send fd2");
3084992df3eSRobert Watson 		}
3094992df3eSRobert Watson 	}
3104992df3eSRobert Watson 
3114992df3eSRobert Watson 	return (NULL);
3124992df3eSRobert Watson }
3134992df3eSRobert Watson 
3144992df3eSRobert Watson static struct timespec
thread_juggle(int fd1,int fd2,int pipeline)3154992df3eSRobert Watson thread_juggle(int fd1, int fd2, int pipeline)
3164992df3eSRobert Watson {
3174992df3eSRobert Watson 	struct timespec tstart, tfinish;
3184992df3eSRobert Watson 	pthread_t thread;
3194992df3eSRobert Watson 	int i, j;
3204992df3eSRobert Watson 
3214992df3eSRobert Watson 	threaded_pipeline = pipeline;
3224992df3eSRobert Watson 
3232949bd10SRuslan Ermilov 	if (pthread_mutex_init(&threaded_mtx, NULL) != 0)
3244992df3eSRobert Watson 		err(-1, "thread_juggle: pthread_mutex_init");
3254992df3eSRobert Watson 
3262949bd10SRuslan Ermilov 	if (pthread_create(&thread, NULL, juggling_thread, &fd2) != 0)
3274992df3eSRobert Watson 		err(-1, "thread_juggle: pthread_create");
3284992df3eSRobert Watson 
3292949bd10SRuslan Ermilov 	if (pthread_mutex_lock(&threaded_mtx) != 0)
3304992df3eSRobert Watson 		err(-1, "thread_juggle: pthread_mutex_lock");
3314992df3eSRobert Watson 
3324992df3eSRobert Watson 	while (!threaded_child_ready) {
3332949bd10SRuslan Ermilov 		if (pthread_cond_wait(&threaded_cond, &threaded_mtx) != 0)
3344992df3eSRobert Watson 			err(-1, "thread_juggle: pthread_cond_wait");
3354992df3eSRobert Watson 	}
3364992df3eSRobert Watson 
3372949bd10SRuslan Ermilov 	if (pthread_mutex_unlock(&threaded_mtx) != 0)
3384992df3eSRobert Watson 		err(-1, "thread_juggle: pthread_mutex_unlock");
3394992df3eSRobert Watson 
3404992df3eSRobert Watson 	if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
3414992df3eSRobert Watson 		err(-1, "thread_juggle: clock_gettime");
3424992df3eSRobert Watson 
3434992df3eSRobert Watson 	for (i = 0; i < NUMCYCLES; i++) {
3444992df3eSRobert Watson 		for (j = 0; j < pipeline; j++) {
3454992df3eSRobert Watson 			if (message_send(fd1) < 0)
3464992df3eSRobert Watson 				err(-1, "message_send fd1");
3474992df3eSRobert Watson 		}
3484992df3eSRobert Watson 
3494992df3eSRobert Watson 		for (j = 0; j < pipeline; j++) {
3504992df3eSRobert Watson 			if (message_recv(fd1) < 0)
3514992df3eSRobert Watson 				err(-1, "message_recv fd1");
3524992df3eSRobert Watson 		}
3534992df3eSRobert Watson 	}
3544992df3eSRobert Watson 
3554992df3eSRobert Watson 	if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
3564992df3eSRobert Watson 		err(-1, "thread_juggle: clock_gettime");
3574992df3eSRobert Watson 
3582949bd10SRuslan Ermilov 	if (pthread_join(thread, NULL) != 0)
3594992df3eSRobert Watson 		err(-1, "thread_juggle: pthread_join");
3604992df3eSRobert Watson 
3616040822cSAlan Somers 	timespecsub(&tfinish, &tstart, &tfinish);
3624992df3eSRobert Watson 
3634992df3eSRobert Watson 	return (tfinish);
3644992df3eSRobert Watson }
3654992df3eSRobert Watson 
3664992df3eSRobert Watson /*
3674992df3eSRobert Watson  * Juggle messages between two file descriptors in two processes, so measure
3684992df3eSRobert Watson  * the cost of IPC and the cost of a process context switch.
3694992df3eSRobert Watson  *
3704992df3eSRobert Watson  * Since we can't use a mutex between the processes, we simply do an extra
3714992df3eSRobert Watson  * write on the child to let the parent know that it's ready to start.
3724992df3eSRobert Watson  */
3734992df3eSRobert Watson static struct timespec
process_juggle(int fd1,int fd2,int pipeline)3744992df3eSRobert Watson process_juggle(int fd1, int fd2, int pipeline)
3754992df3eSRobert Watson {
3764992df3eSRobert Watson 	struct timespec tstart, tfinish;
3774992df3eSRobert Watson 	pid_t pid, ppid, wpid;
3784992df3eSRobert Watson 	int error, i, j;
3794992df3eSRobert Watson 
3804992df3eSRobert Watson 	ppid = getpid();
3814992df3eSRobert Watson 
3824992df3eSRobert Watson 	pid = fork();
3834992df3eSRobert Watson 	if (pid < 0)
3844992df3eSRobert Watson 		err(-1, "process_juggle: fork");
3854992df3eSRobert Watson 
3864992df3eSRobert Watson 	if (pid == 0) {
3874992df3eSRobert Watson 		if (message_send(fd2) < 0) {
3884992df3eSRobert Watson 			error = errno;
3894992df3eSRobert Watson 			kill(ppid, SIGTERM);
3904992df3eSRobert Watson 			errno = error;
3914992df3eSRobert Watson 			err(-1, "process_juggle: child: message_send");
3924992df3eSRobert Watson 		}
3934992df3eSRobert Watson 
3944992df3eSRobert Watson 		for (i = 0; i < NUMCYCLES; i++) {
3954992df3eSRobert Watson 			for (j = 0; j < pipeline; j++) {
3964992df3eSRobert Watson 				if (message_send(fd2) < 0)
3974992df3eSRobert Watson 					err(-1, "message_send fd2");
3984992df3eSRobert Watson 
3994992df3eSRobert Watson 				if (message_recv(fd2) < 0)
4004992df3eSRobert Watson 					err(-1, "message_recv fd2");
4014992df3eSRobert Watson 			}
4024992df3eSRobert Watson 		}
4034992df3eSRobert Watson 
4044992df3eSRobert Watson 		exit(0);
4054992df3eSRobert Watson 	} else {
4064992df3eSRobert Watson 		if (message_recv(fd1) < 0) {
4074992df3eSRobert Watson 			error = errno;
4084992df3eSRobert Watson 			kill(pid, SIGTERM);
4094992df3eSRobert Watson 			errno = error;
4104992df3eSRobert Watson 			err(-1, "process_juggle: parent: message_recv");
4114992df3eSRobert Watson 		}
4124992df3eSRobert Watson 
4134992df3eSRobert Watson 		if (clock_gettime(CLOCK_REALTIME, &tstart) < 0)
4144992df3eSRobert Watson 			err(-1, "process_juggle: clock_gettime");
4154992df3eSRobert Watson 
4164992df3eSRobert Watson 		for (i = 0; i < NUMCYCLES; i++) {
4174992df3eSRobert Watson 			for (j = 0; j < pipeline; j++) {
4184992df3eSRobert Watson 				if (message_send(fd1) < 0) {
4194992df3eSRobert Watson 					error = errno;
4204992df3eSRobert Watson 					kill(pid, SIGTERM);
4214992df3eSRobert Watson 					errno = error;
4224992df3eSRobert Watson 					err(-1, "message_send fd1");
4234992df3eSRobert Watson 				}
4244992df3eSRobert Watson 			}
4254992df3eSRobert Watson 
4264992df3eSRobert Watson 			for (j = 0; j < pipeline; j++) {
4274992df3eSRobert Watson 				if (message_recv(fd1) < 0) {
4284992df3eSRobert Watson 					error = errno;
4294992df3eSRobert Watson 					kill(pid, SIGTERM);
4304992df3eSRobert Watson 					errno = error;
4314992df3eSRobert Watson 					err(-1, "message_recv fd1");
4324992df3eSRobert Watson 				}
4334992df3eSRobert Watson 			}
4344992df3eSRobert Watson 		}
4354992df3eSRobert Watson 
4364992df3eSRobert Watson 		if (clock_gettime(CLOCK_REALTIME, &tfinish) < 0)
4374992df3eSRobert Watson 			err(-1, "process_juggle: clock_gettime");
4384992df3eSRobert Watson 	}
4394992df3eSRobert Watson 
4404992df3eSRobert Watson 	wpid = waitpid(pid, NULL, 0);
4414992df3eSRobert Watson 	if (wpid < 0)
4424992df3eSRobert Watson 		err(-1, "process_juggle: waitpid");
4434992df3eSRobert Watson 	if (wpid != pid)
4444992df3eSRobert Watson 		errx(-1, "process_juggle: waitpid: pid != wpid");
4454992df3eSRobert Watson 
4466040822cSAlan Somers 	timespecsub(&tfinish, &tstart, &tfinish);
4474992df3eSRobert Watson 
4484992df3eSRobert Watson 	return (tfinish);
4494992df3eSRobert Watson }
4504992df3eSRobert Watson 
4514992df3eSRobert Watson /*
4524992df3eSRobert Watson  * When we print out results for larger pipeline sizes, we scale back by the
4534992df3eSRobert Watson  * depth of the pipeline.  This generally means dividing by the pipeline
4544992df3eSRobert Watson  * depth.  Except when it means dividing by zero.
4554992df3eSRobert Watson  */
4564992df3eSRobert Watson static void
scale_timespec(struct timespec * ts,int p)4574992df3eSRobert Watson scale_timespec(struct timespec *ts, int p)
4584992df3eSRobert Watson {
4594992df3eSRobert Watson 
4604992df3eSRobert Watson 	if (p == 0)
4614992df3eSRobert Watson 		return;
4624992df3eSRobert Watson 
4634992df3eSRobert Watson 	ts->tv_sec /= p;
4644992df3eSRobert Watson 	ts->tv_nsec /= p;
4654992df3eSRobert Watson }
4664992df3eSRobert Watson 
4674992df3eSRobert Watson static const struct ipctype {
4684992df3eSRobert Watson 	int		(*it_create)(int *fd1p, int *fd2p);
4694992df3eSRobert Watson 	const char	*it_name;
4704992df3eSRobert Watson } ipctypes[] = {
4714992df3eSRobert Watson 	{ pipe_create, "pipe" },
4724992df3eSRobert Watson 	{ udp_create, "udp" },
4734992df3eSRobert Watson 	{ socketpairdgram_create, "socketpairdgram" },
4744992df3eSRobert Watson 	{ socketpairstream_create, "socketpairstream" },
4754992df3eSRobert Watson };
4764992df3eSRobert Watson static const int ipctypes_len = (sizeof(ipctypes) / sizeof(struct ipctype));
4774992df3eSRobert Watson 
4784992df3eSRobert Watson int
main(int argc,char * argv[])4794992df3eSRobert Watson main(int argc, char *argv[])
4804992df3eSRobert Watson {
4814992df3eSRobert Watson 	struct timespec juggle_results[LOOPS], process_results[LOOPS];
4824992df3eSRobert Watson 	struct timespec thread_results[LOOPS];
4834992df3eSRobert Watson 	int fd1, fd2, i, j, p;
4844992df3eSRobert Watson 	struct utsname uts;
4854992df3eSRobert Watson 
4867f9f1e42SRobert Watson 	printf("version, juggle.c %s\n", "$FreeBSD$");
4874992df3eSRobert Watson 
4884992df3eSRobert Watson 	if (uname(&uts) < 0)
4894992df3eSRobert Watson 		err(-1, "utsname");
4904992df3eSRobert Watson 	printf("sysname, %s\n", uts.sysname);
4914992df3eSRobert Watson 	printf("nodename, %s\n", uts.nodename);
4924992df3eSRobert Watson 	printf("release, %s\n", uts.release);
4934992df3eSRobert Watson 	printf("version, %s\n", uts.version);
4944992df3eSRobert Watson 	printf("machine, %s\n", uts.machine);
4954992df3eSRobert Watson 	printf("\n");
4964992df3eSRobert Watson 
4974992df3eSRobert Watson 	printf("MESSAGELEN, %d\n", MESSAGELEN);
4984992df3eSRobert Watson 	printf("NUMCYCLES, %d\n", NUMCYCLES);
4994992df3eSRobert Watson 	printf("LOOPS, %d\n", LOOPS);
5004992df3eSRobert Watson 	printf("PIPELINE_MAX, %d\n", PIPELINE_MAX);
5014992df3eSRobert Watson 	printf("\n\n");
5024992df3eSRobert Watson 
5034992df3eSRobert Watson 	printf("ipctype, test, pipeline_depth");
5044992df3eSRobert Watson 	for (j = 0; j < LOOPS; j++)
5054992df3eSRobert Watson 		printf(", data%d", j);
5064992df3eSRobert Watson 	printf("\n");
5074992df3eSRobert Watson 	fflush(stdout);
5084992df3eSRobert Watson 	for (p = 0; p < PIPELINE_MAX + 1; p++) {
5094992df3eSRobert Watson 		for (i = 0; i < ipctypes_len; i++) {
5104992df3eSRobert Watson 			if (ipctypes[i].it_create(&fd1, &fd2) < 0)
5114992df3eSRobert Watson 				err(-1, "main: %s", ipctypes[i].it_name);
5124992df3eSRobert Watson 
5134992df3eSRobert Watson 			/*
5144992df3eSRobert Watson 			 * For each test, do one uncounted warmup, then LOOPS
5154992df3eSRobert Watson 			 * runs of the actual test.
5164992df3eSRobert Watson 			 */
5174992df3eSRobert Watson 			juggle(fd1, fd2, p);
5184992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++)
5194992df3eSRobert Watson 				juggle_results[j] = juggle(fd1, fd2, p);
5204992df3eSRobert Watson 			process_juggle(fd1, fd2, p);
5214992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++)
5224992df3eSRobert Watson 				process_results[j] = process_juggle(fd1, fd2,
5234992df3eSRobert Watson 				    p);
5244992df3eSRobert Watson 			thread_juggle(fd1, fd2, p);
5254992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++)
5264992df3eSRobert Watson 				thread_results[j] = thread_juggle(fd1, fd2,
5274992df3eSRobert Watson 				    p);
5284992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++) {
5294992df3eSRobert Watson 				thread_results[j].tv_sec = 0;
5304992df3eSRobert Watson 				thread_results[j].tv_nsec = 0;
5314992df3eSRobert Watson 			}
5324992df3eSRobert Watson 			close(fd1);
5334992df3eSRobert Watson 			close(fd2);
5344992df3eSRobert Watson 		}
5354992df3eSRobert Watson 		/*
5364992df3eSRobert Watson 		 * When printing results for the round, normalize the results
5374992df3eSRobert Watson 		 * with respect to the pipeline depth.  We're doing p times
5384992df3eSRobert Watson 		 * as much work, and are we taking p times as long?
5394992df3eSRobert Watson 		 */
5404992df3eSRobert Watson 		for (i = 0; i < ipctypes_len; i++) {
5414992df3eSRobert Watson 			printf("%s, juggle, %d, ", ipctypes[i].it_name, p);
5424992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++) {
5434992df3eSRobert Watson 				if (j != 0)
5444992df3eSRobert Watson 					printf(", ");
5454992df3eSRobert Watson 				scale_timespec(&juggle_results[j], p);
546cd94bb1eSSergey Kandaurov 				printf("%jd.%09lu",
547cd94bb1eSSergey Kandaurov 				    (intmax_t)juggle_results[j].tv_sec,
5484992df3eSRobert Watson 				    juggle_results[j].tv_nsec);
5494992df3eSRobert Watson 			}
5504992df3eSRobert Watson 			printf("\n");
5514992df3eSRobert Watson 			printf("%s, process_juggle, %d, ",
5524992df3eSRobert Watson 			    ipctypes[i].it_name, p);
5534992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++) {
5544992df3eSRobert Watson 				if (j != 0)
5554992df3eSRobert Watson 					printf(", ");
5564992df3eSRobert Watson 				scale_timespec(&process_results[j], p);
557cd94bb1eSSergey Kandaurov 				printf("%jd.%09lu",
558cd94bb1eSSergey Kandaurov                                     (intmax_t)process_results[j].tv_sec,
5594992df3eSRobert Watson 				    process_results[j].tv_nsec);
5604992df3eSRobert Watson 			}
5614992df3eSRobert Watson 			printf("\n");
5624992df3eSRobert Watson 			printf("%s, thread_juggle, %d, ",
5634992df3eSRobert Watson 			    ipctypes[i].it_name, p);
5644992df3eSRobert Watson 			for (j = 0; j < LOOPS; j++) {
5654992df3eSRobert Watson 				if (j != 0)
5664992df3eSRobert Watson 					printf(", ");
5674992df3eSRobert Watson 				scale_timespec(&thread_results[j], p);
568cd94bb1eSSergey Kandaurov 				printf("%jd.%09lu",
569cd94bb1eSSergey Kandaurov 				    (intmax_t)thread_results[j].tv_sec,
5704992df3eSRobert Watson 				    thread_results[j].tv_nsec);
5714992df3eSRobert Watson 			}
5724992df3eSRobert Watson 			printf("\n");
5734992df3eSRobert Watson 		}
5744992df3eSRobert Watson 		fflush(stdout);
5754992df3eSRobert Watson 	}
5764992df3eSRobert Watson 	return (0);
5774992df3eSRobert Watson }
578