1122592Ssam/*	$FreeBSD$	*/
2122592Ssam/*	$NetBSD: kttcp.c,v 1.3 2002/07/03 19:36:52 thorpej Exp $	*/
3122592Ssam
4122592Ssam/*
5122592Ssam * Copyright (c) 2002 Wasabi Systems, Inc.
6122592Ssam * All rights reserved.
7122592Ssam *
8122592Ssam * Written by Frank van der Linden and Jason R. Thorpe for
9122592Ssam * Wasabi Systems, Inc.
10122592Ssam *
11122592Ssam * Redistribution and use in source and binary forms, with or without
12122592Ssam * modification, are permitted provided that the following conditions
13122592Ssam * are met:
14122592Ssam * 1. Redistributions of source code must retain the above copyright
15122592Ssam *    notice, this list of conditions and the following disclaimer.
16122592Ssam * 2. Redistributions in binary form must reproduce the above copyright
17122592Ssam *    notice, this list of conditions and the following disclaimer in the
18122592Ssam *    documentation and/or other materials provided with the distribution.
19122592Ssam * 3. All advertising materials mentioning features or use of this software
20122592Ssam *    must display the following acknowledgement:
21122592Ssam *	This product includes software developed for the NetBSD Project by
22122592Ssam *	Wasabi Systems, Inc.
23122592Ssam * 4. The name of Wasabi Systems, Inc. may not be used to endorse
24122592Ssam *    or promote products derived from this software without specific prior
25122592Ssam *    written permission.
26122592Ssam *
27122592Ssam * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
28122592Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29122592Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30122592Ssam * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
31122592Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32122592Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33122592Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34122592Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35122592Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36122592Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37122592Ssam * POSSIBILITY OF SUCH DAMAGE.
38122592Ssam */
39122592Ssam
40122592Ssam/*
41122592Ssam * kttcp.c --
42122592Ssam *
43122592Ssam *	This module provides kernel support for testing network
44122592Ssam *	throughput from the perspective of the kernel.  It is
45122592Ssam *	similar in spirit to the classic ttcp network benchmark
46122592Ssam *	program, the main difference being that with kttcp, the
47122592Ssam *	kernel is the source and sink of the data.
48122592Ssam *
49122592Ssam *	Testing like this is useful for a few reasons:
50122592Ssam *
51122592Ssam *	1. This allows us to know what kind of performance we can
52122592Ssam *	   expect from network applications that run in the kernel
53122592Ssam *	   space, such as the NFS server or the NFS client.  These
54122592Ssam *	   applications don't have to move the data to/from userspace,
55122592Ssam *	   and so benchmark programs which run in userspace don't
56122592Ssam *	   give us an accurate model.
57122592Ssam *
58122592Ssam *	2. Since data received is just thrown away, the receiver
59122592Ssam *	   is very fast.  This can provide better exercise for the
60122592Ssam *	   sender at the other end.
61122592Ssam *
62122592Ssam *	3. Since the NetBSD kernel currently uses a run-to-completion
63122592Ssam *	   scheduling model, kttcp provides a benchmark model where
64122592Ssam *	   preemption of the benchmark program is not an issue.
65122592Ssam */
66122592Ssam
67122592Ssam#include <sys/param.h>
68122592Ssam#include <sys/systm.h>
69122592Ssam#include <sys/malloc.h>
70122592Ssam#include <sys/mbuf.h>
71122592Ssam#include <sys/sysctl.h>
72122592Ssam#include <sys/file.h>
73122592Ssam#include <sys/filedesc.h>
74122592Ssam#include <sys/errno.h>
75122592Ssam#include <sys/uio.h>
76122592Ssam#include <sys/conf.h>
77122592Ssam#include <sys/kernel.h>
78122592Ssam#include <sys/fcntl.h>
79122592Ssam#include <sys/protosw.h>
80122592Ssam#include <sys/socketvar.h>
81122592Ssam#include <sys/socket.h>
82122592Ssam#include <sys/mbuf.h>
83122592Ssam#include <sys/resourcevar.h>
84122592Ssam#include <sys/proc.h>
85138822Sgallatin#include <sys/module.h>
86122592Ssam
87122629Ssam#include "kttcpio.h"
88122592Ssam
89122592Ssam#ifndef timersub
90122592Ssam#define timersub(tvp, uvp, vvp)						\
91122592Ssam	do {								\
92122592Ssam		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
93122592Ssam		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
94122592Ssam		if ((vvp)->tv_usec < 0) {				\
95122592Ssam			(vvp)->tv_sec--;				\
96122592Ssam			(vvp)->tv_usec += 1000000;			\
97122592Ssam		}							\
98122592Ssam	} while (0)
99122592Ssam#endif
100122592Ssam
101122592Ssamstatic int kttcp_send(struct thread *p, struct kttcp_io_args *);
102122592Ssamstatic int kttcp_recv(struct thread *p, struct kttcp_io_args *);
103122592Ssam
104122592Ssamstatic d_open_t		kttcpopen;
105122592Ssamstatic d_ioctl_t	kttcpioctl;
106122592Ssam
107122592Ssamstatic struct cdevsw kttcp_cdevsw = {
108122592Ssam	.d_open =	kttcpopen,
109122592Ssam	.d_ioctl =	kttcpioctl,
110122592Ssam	.d_name =	"kttcp",
111122592Ssam	.d_maj =	MAJOR_AUTO,
112138822Sgallatin	.d_version =	D_VERSION,
113122592Ssam};
114122592Ssam
115122592Ssamstatic int
116138822Sgallatinkttcpopen(struct cdev *dev, int flag, int mode, struct thread *td)
117122592Ssam{
118122592Ssam	/* Always succeeds. */
119122592Ssam	return (0);
120122592Ssam}
121122592Ssam
122122592Ssamstatic int
123138822Sgallatinkttcpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
124122592Ssam{
125122592Ssam	int error;
126122592Ssam
127122592Ssam	if ((flag & FWRITE) == 0)
128122592Ssam		return EPERM;
129122592Ssam
130122592Ssam	switch (cmd) {
131122592Ssam	case KTTCP_IO_SEND:
132122592Ssam		error = kttcp_send(td, (struct kttcp_io_args *) data);
133122592Ssam		break;
134122592Ssam
135122592Ssam	case KTTCP_IO_RECV:
136122592Ssam		error = kttcp_recv(td, (struct kttcp_io_args *) data);
137122592Ssam		break;
138122592Ssam
139122592Ssam	default:
140122592Ssam		return EINVAL;
141122592Ssam	}
142122592Ssam
143122592Ssam	return error;
144122592Ssam}
145122592Ssam
146138822Sgallatinstatic int nbyte = 65536;
147138822Sgallatin
148122592Ssamstatic int
149122592Ssamkttcp_send(struct thread *td, struct kttcp_io_args *kio)
150122592Ssam{
151122592Ssam	struct file *fp;
152122592Ssam	int error;
153122592Ssam	struct timeval t0, t1;
154122592Ssam	unsigned long long len = 0;
155138822Sgallatin	struct uio auio;
156138822Sgallatin	struct iovec aiov;
157122592Ssam
158138822Sgallatin	bzero(&aiov, sizeof(aiov));
159138822Sgallatin	bzero(&auio, sizeof(auio));
160138822Sgallatin	auio.uio_iov = &aiov;
161138822Sgallatin	auio.uio_segflg = UIO_NOCOPY;
162122592Ssam
163122592Ssam	error = fget(td, kio->kio_socket, &fp);
164122592Ssam	if (error != 0)
165122592Ssam		return error;
166138822Sgallatin
167122592Ssam	if ((fp->f_flag & FWRITE) == 0) {
168122592Ssam		fdrop(fp, td);
169122592Ssam		return EBADF;
170122592Ssam	}
171122592Ssam	if (fp->f_type == DTYPE_SOCKET) {
172122592Ssam		len = kio->kio_totalsize;
173122592Ssam		microtime(&t0);
174122592Ssam		do {
175138822Sgallatin			nbyte =  MIN(len, (unsigned long long)nbyte);
176138822Sgallatin			aiov.iov_len = nbyte;
177138822Sgallatin			auio.uio_resid = nbyte;
178138822Sgallatin			auio.uio_offset = 0;
179138822Sgallatin			error = sosend((struct socket *)fp->f_data, NULL,
180138822Sgallatin				       &auio, NULL, NULL, 0, td);
181138822Sgallatin			len -= auio.uio_offset;
182138822Sgallatin		} while (error == 0 && len != 0);
183122592Ssam		microtime(&t1);
184122592Ssam	} else
185122592Ssam		error = EFTYPE;
186122592Ssam	fdrop(fp, td);
187122592Ssam	if (error != 0)
188122592Ssam		return error;
189122592Ssam	timersub(&t1, &t0, &kio->kio_elapsed);
190122592Ssam
191122592Ssam	kio->kio_bytesdone = kio->kio_totalsize - len;
192122592Ssam
193122592Ssam	return 0;
194122592Ssam}
195122592Ssam
196122592Ssamstatic int
197122592Ssamkttcp_recv(struct thread *td, struct kttcp_io_args *kio)
198122592Ssam{
199122592Ssam	struct file *fp;
200122592Ssam	int error;
201122592Ssam	struct timeval t0, t1;
202122592Ssam	unsigned long long len = 0;
203138822Sgallatin	struct uio auio;
204138822Sgallatin	struct iovec aiov;
205122592Ssam
206138822Sgallatin	bzero(&aiov, sizeof(aiov));
207138822Sgallatin	bzero(&auio, sizeof(auio));
208138822Sgallatin	auio.uio_iov = &aiov;
209138822Sgallatin	auio.uio_segflg = UIO_NOCOPY;
210122592Ssam
211122592Ssam	error = fget(td, kio->kio_socket, &fp);
212122592Ssam	if (error != 0)
213122592Ssam		return error;
214138822Sgallatin
215122592Ssam	if ((fp->f_flag & FWRITE) == 0) {
216122592Ssam		fdrop(fp, td);
217122592Ssam		return EBADF;
218122592Ssam	}
219122592Ssam	if (fp->f_type == DTYPE_SOCKET) {
220122592Ssam		len = kio->kio_totalsize;
221122592Ssam		microtime(&t0);
222122592Ssam		do {
223138822Sgallatin			nbyte =  MIN(len, (unsigned long long)nbyte);
224138822Sgallatin			aiov.iov_len = nbyte;
225138822Sgallatin			auio.uio_resid = nbyte;
226138822Sgallatin			auio.uio_offset = 0;
227138822Sgallatin			error = soreceive((struct socket *)fp->f_data,
228138822Sgallatin					  NULL, &auio, NULL, NULL, NULL);
229138822Sgallatin			len -= auio.uio_offset;
230138822Sgallatin		} while (error == 0 && len > 0 && auio.uio_offset != 0);
231122592Ssam		microtime(&t1);
232122592Ssam		if (error == EPIPE)
233122592Ssam			error = 0;
234122592Ssam	} else
235122592Ssam		error = EFTYPE;
236122592Ssam	fdrop(fp, td);
237122592Ssam	if (error != 0)
238122592Ssam		return error;
239122592Ssam	timersub(&t1, &t0, &kio->kio_elapsed);
240122592Ssam
241122592Ssam	kio->kio_bytesdone = kio->kio_totalsize - len;
242122592Ssam
243122592Ssam	return 0;
244122592Ssam}
245122592Ssam
246138822Sgallatinstatic struct cdev *kttcp_dev;
247122592Ssam
248122592Ssam/*
249122592Ssam * Initialization code, both for static and dynamic loading.
250122592Ssam */
251122592Ssamstatic int
252122592Ssamkttcpdev_modevent(module_t mod, int type, void *unused)
253122592Ssam{
254122592Ssam	switch (type) {
255122592Ssam	case MOD_LOAD:
256122592Ssam		kttcp_dev = make_dev(&kttcp_cdevsw, 0,
257122592Ssam				      UID_ROOT, GID_WHEEL, 0666,
258122592Ssam				      "kttcp");
259122592Ssam		return 0;
260122592Ssam	case MOD_UNLOAD:
261122592Ssam		/*XXX disallow if active sessions */
262122592Ssam		destroy_dev(kttcp_dev);
263122592Ssam		return 0;
264122592Ssam	}
265122592Ssam	return EINVAL;
266122592Ssam}
267122592Ssam
268122592Ssamstatic moduledata_t kttcpdev_mod = {
269122592Ssam	"kttcpdev",
270122592Ssam	kttcpdev_modevent,
271241394Skevlo	0
272122592Ssam};
273122592SsamMODULE_VERSION(kttcpdev, 1);
274122592SsamDECLARE_MODULE(kttcpdev, kttcpdev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
275