1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD: releng/10.3/usr.sbin/bhyve/mevent_test.c 255690 2013-09-19 04:48:26Z grehan $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan/*
30221828Sgrehan * Test program for the micro event library. Set up a simple TCP echo
31221828Sgrehan * service.
32221828Sgrehan *
33221828Sgrehan *  cc mevent_test.c mevent.c -lpthread
34221828Sgrehan */
35221828Sgrehan
36221828Sgrehan#include <sys/types.h>
37255690Sgrehan#include <sys/stdint.h>
38255690Sgrehan#include <sys/sysctl.h>
39221828Sgrehan#include <sys/socket.h>
40221828Sgrehan#include <netinet/in.h>
41255690Sgrehan#include <machine/cpufunc.h>
42221828Sgrehan
43221828Sgrehan#include <stdio.h>
44221828Sgrehan#include <stdlib.h>
45221828Sgrehan#include <pthread.h>
46255690Sgrehan#include <unistd.h>
47221828Sgrehan
48221828Sgrehan#include "mevent.h"
49221828Sgrehan
50221828Sgrehan#define TEST_PORT	4321
51221828Sgrehan
52221828Sgrehanstatic pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
53221828Sgrehanstatic pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER;
54221828Sgrehan
55255690Sgrehanstatic struct mevent *tevp;
56255690Sgrehan
57255690Sgrehanchar *vmname = "test vm";
58255690Sgrehan
59255690Sgrehan
60221828Sgrehan#define MEVENT_ECHO
61221828Sgrehan
62255690Sgrehan/* Number of timer events to capture */
63255690Sgrehan#define TEVSZ	4096
64255690Sgrehanuint64_t tevbuf[TEVSZ];
65255690Sgrehan
66255690Sgrehanstatic void
67255690Sgrehantimer_print(void)
68255690Sgrehan{
69255690Sgrehan	uint64_t min, max, diff, sum, tsc_freq;
70255690Sgrehan	size_t len;
71255690Sgrehan	int j;
72255690Sgrehan
73255690Sgrehan	min = UINT64_MAX;
74255690Sgrehan	max = 0;
75255690Sgrehan	sum = 0;
76255690Sgrehan
77255690Sgrehan	len = sizeof(tsc_freq);
78255690Sgrehan	sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0);
79255690Sgrehan
80255690Sgrehan	for (j = 1; j < TEVSZ; j++) {
81255690Sgrehan		/* Convert a tsc diff into microseconds */
82255690Sgrehan		diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq;
83255690Sgrehan		sum += diff;
84255690Sgrehan		if (min > diff)
85255690Sgrehan			min = diff;
86255690Sgrehan		if (max < diff)
87255690Sgrehan			max = diff;
88255690Sgrehan	}
89255690Sgrehan
90255690Sgrehan	printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max,
91255690Sgrehan	    sum/(TEVSZ - 1));
92255690Sgrehan}
93255690Sgrehan
94255690Sgrehanstatic void
95255690Sgrehantimer_callback(int fd, enum ev_type type, void *param)
96255690Sgrehan{
97255690Sgrehan	static int i;
98255690Sgrehan
99255690Sgrehan	if (i >= TEVSZ)
100255690Sgrehan		abort();
101255690Sgrehan
102255690Sgrehan	tevbuf[i++] = rdtsc();
103255690Sgrehan
104255690Sgrehan	if (i == TEVSZ) {
105255690Sgrehan		mevent_delete(tevp);
106255690Sgrehan		timer_print();
107255690Sgrehan	}
108255690Sgrehan}
109255690Sgrehan
110255690Sgrehan
111221828Sgrehan#ifdef MEVENT_ECHO
112221828Sgrehanstruct esync {
113221828Sgrehan	pthread_mutex_t	e_mt;
114221828Sgrehan	pthread_cond_t	e_cond;
115221828Sgrehan};
116221828Sgrehan
117221828Sgrehanstatic void
118221828Sgrehanechoer_callback(int fd, enum ev_type type, void *param)
119221828Sgrehan{
120221828Sgrehan	struct esync *sync = param;
121221828Sgrehan
122221828Sgrehan	pthread_mutex_lock(&sync->e_mt);
123221828Sgrehan	pthread_cond_signal(&sync->e_cond);
124221828Sgrehan	pthread_mutex_unlock(&sync->e_mt);
125221828Sgrehan}
126221828Sgrehan
127221828Sgrehanstatic void *
128221828Sgrehanechoer(void *param)
129221828Sgrehan{
130221828Sgrehan	struct esync sync;
131221828Sgrehan	struct mevent *mev;
132221828Sgrehan	char buf[128];
133221828Sgrehan	int fd = (int)(uintptr_t) param;
134221828Sgrehan	int len;
135221828Sgrehan
136221828Sgrehan	pthread_mutex_init(&sync.e_mt, NULL);
137221828Sgrehan	pthread_cond_init(&sync.e_cond, NULL);
138221828Sgrehan
139221828Sgrehan	pthread_mutex_lock(&sync.e_mt);
140221828Sgrehan
141221828Sgrehan	mev = mevent_add(fd, EVF_READ, echoer_callback, &sync);
142221828Sgrehan	if (mev == NULL) {
143221828Sgrehan		printf("Could not allocate echoer event\n");
144221828Sgrehan		exit(1);
145221828Sgrehan	}
146221828Sgrehan
147221828Sgrehan	while (!pthread_cond_wait(&sync.e_cond, &sync.e_mt)) {
148221828Sgrehan		len = read(fd, buf, sizeof(buf));
149221828Sgrehan		if (len > 0) {
150221828Sgrehan			write(fd, buf, len);
151221828Sgrehan			write(0, buf, len);
152221828Sgrehan		} else {
153221828Sgrehan			break;
154221828Sgrehan		}
155221828Sgrehan	}
156221828Sgrehan
157221828Sgrehan	mevent_delete_close(mev);
158221828Sgrehan
159221828Sgrehan	pthread_mutex_unlock(&sync.e_mt);
160221828Sgrehan	pthread_mutex_destroy(&sync.e_mt);
161221828Sgrehan	pthread_cond_destroy(&sync.e_cond);
162255690Sgrehan
163255690Sgrehan	return (NULL);
164221828Sgrehan}
165221828Sgrehan
166221828Sgrehan#else
167221828Sgrehan
168221828Sgrehanstatic void *
169221828Sgrehanechoer(void *param)
170221828Sgrehan{
171221828Sgrehan	char buf[128];
172221828Sgrehan	int fd = (int)(uintptr_t) param;
173221828Sgrehan	int len;
174221828Sgrehan
175221828Sgrehan	while ((len = read(fd, buf, sizeof(buf))) > 0) {
176221828Sgrehan		write(1, buf, len);
177221828Sgrehan	}
178255690Sgrehan
179255690Sgrehan	return (NULL);
180221828Sgrehan}
181221828Sgrehan#endif /* MEVENT_ECHO */
182221828Sgrehan
183221828Sgrehanstatic void
184221828Sgrehanacceptor_callback(int fd, enum ev_type type, void *param)
185221828Sgrehan{
186221828Sgrehan	pthread_mutex_lock(&accept_mutex);
187221828Sgrehan	pthread_cond_signal(&accept_condvar);
188221828Sgrehan	pthread_mutex_unlock(&accept_mutex);
189221828Sgrehan}
190221828Sgrehan
191221828Sgrehanstatic void *
192221828Sgrehanacceptor(void *param)
193221828Sgrehan{
194221828Sgrehan	struct sockaddr_in sin;
195221828Sgrehan	pthread_t tid;
196221828Sgrehan	int news;
197221828Sgrehan	int s;
198255690Sgrehan	static int first;
199221828Sgrehan
200221828Sgrehan        if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
201221828Sgrehan                perror("socket");
202221828Sgrehan                exit(1);
203221828Sgrehan        }
204221828Sgrehan
205221828Sgrehan        sin.sin_len = sizeof(sin);
206221828Sgrehan        sin.sin_family = AF_INET;
207221828Sgrehan        sin.sin_addr.s_addr = htonl(INADDR_ANY);
208221828Sgrehan        sin.sin_port = htons(TEST_PORT);
209221828Sgrehan
210221828Sgrehan        if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
211221828Sgrehan                perror("bind");
212221828Sgrehan                exit(1);
213221828Sgrehan        }
214221828Sgrehan
215221828Sgrehan        if (listen(s, 1) < 0) {
216221828Sgrehan                perror("listen");
217221828Sgrehan                exit(1);
218221828Sgrehan        }
219221828Sgrehan
220221828Sgrehan	(void) mevent_add(s, EVF_READ, acceptor_callback, NULL);
221221828Sgrehan
222221828Sgrehan	pthread_mutex_lock(&accept_mutex);
223221828Sgrehan
224221828Sgrehan	while (!pthread_cond_wait(&accept_condvar, &accept_mutex)) {
225221828Sgrehan		news = accept(s, NULL, NULL);
226221828Sgrehan		if (news < 0) {
227221828Sgrehan			perror("accept error");
228221828Sgrehan		} else {
229255690Sgrehan			static int first = 1;
230255690Sgrehan
231255690Sgrehan			if (first) {
232255690Sgrehan				/*
233255690Sgrehan				 * Start a timer
234255690Sgrehan				 */
235255690Sgrehan				first = 0;
236255690Sgrehan				tevp = mevent_add(1, EVF_TIMER, timer_callback,
237255690Sgrehan						  NULL);
238255690Sgrehan			}
239255690Sgrehan
240221828Sgrehan			printf("incoming connection, spawning thread\n");
241221828Sgrehan			pthread_create(&tid, NULL, echoer,
242221828Sgrehan				       (void *)(uintptr_t)news);
243221828Sgrehan		}
244221828Sgrehan	}
245255690Sgrehan
246255690Sgrehan	return (NULL);
247221828Sgrehan}
248221828Sgrehan
249221828Sgrehanmain()
250221828Sgrehan{
251221828Sgrehan	pthread_t tid;
252221828Sgrehan
253221828Sgrehan	pthread_create(&tid, NULL, acceptor, NULL);
254221828Sgrehan
255221828Sgrehan	mevent_dispatch();
256221828Sgrehan}
257