1/*	$NetBSD: kttcp.c,v 1.3 2002/07/03 19:36:52 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Frank van der Linden and Jason R. Thorpe for
8 * Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed for the NetBSD Project by
21 *	Wasabi Systems, Inc.
22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23 *    or promote products derived from this software without specific prior
24 *    written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * kttcp.c --
41 *
42 *	This module provides kernel support for testing network
43 *	throughput from the perspective of the kernel.  It is
44 *	similar in spirit to the classic ttcp network benchmark
45 *	program, the main difference being that with kttcp, the
46 *	kernel is the source and sink of the data.
47 *
48 *	Testing like this is useful for a few reasons:
49 *
50 *	1. This allows us to know what kind of performance we can
51 *	   expect from network applications that run in the kernel
52 *	   space, such as the NFS server or the NFS client.  These
53 *	   applications don't have to move the data to/from userspace,
54 *	   and so benchmark programs which run in userspace don't
55 *	   give us an accurate model.
56 *
57 *	2. Since data received is just thrown away, the receiver
58 *	   is very fast.  This can provide better exercise for the
59 *	   sender at the other end.
60 *
61 *	3. Since the NetBSD kernel currently uses a run-to-completion
62 *	   scheduling model, kttcp provides a benchmark model where
63 *	   preemption of the benchmark program is not an issue.
64 */
65
66#include <sys/param.h>
67#include <sys/systm.h>
68#include <sys/malloc.h>
69#include <sys/mbuf.h>
70#include <sys/sysctl.h>
71#include <sys/file.h>
72#include <sys/filedesc.h>
73#include <sys/errno.h>
74#include <sys/uio.h>
75#include <sys/conf.h>
76#include <sys/kernel.h>
77#include <sys/fcntl.h>
78#include <sys/protosw.h>
79#include <sys/socketvar.h>
80#include <sys/socket.h>
81#include <sys/mbuf.h>
82#include <sys/resourcevar.h>
83#include <sys/proc.h>
84#include <sys/module.h>
85
86#include "kttcpio.h"
87
88#ifndef timersub
89#define timersub(tvp, uvp, vvp)						\
90	do {								\
91		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
92		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
93		if ((vvp)->tv_usec < 0) {				\
94			(vvp)->tv_sec--;				\
95			(vvp)->tv_usec += 1000000;			\
96		}							\
97	} while (0)
98#endif
99
100static int kttcp_send(struct thread *p, struct kttcp_io_args *);
101static int kttcp_recv(struct thread *p, struct kttcp_io_args *);
102
103static d_open_t		kttcpopen;
104static d_ioctl_t	kttcpioctl;
105
106static struct cdevsw kttcp_cdevsw = {
107	.d_open =	kttcpopen,
108	.d_ioctl =	kttcpioctl,
109	.d_name =	"kttcp",
110	.d_maj =	MAJOR_AUTO,
111	.d_version =	D_VERSION,
112};
113
114static int
115kttcpopen(struct cdev *dev, int flag, int mode, struct thread *td)
116{
117	/* Always succeeds. */
118	return (0);
119}
120
121static int
122kttcpioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
123{
124	int error;
125
126	if ((flag & FWRITE) == 0)
127		return EPERM;
128
129	switch (cmd) {
130	case KTTCP_IO_SEND:
131		error = kttcp_send(td, (struct kttcp_io_args *) data);
132		break;
133
134	case KTTCP_IO_RECV:
135		error = kttcp_recv(td, (struct kttcp_io_args *) data);
136		break;
137
138	default:
139		return EINVAL;
140	}
141
142	return error;
143}
144
145static int nbyte = 65536;
146
147static int
148kttcp_send(struct thread *td, struct kttcp_io_args *kio)
149{
150	struct file *fp;
151	int error;
152	struct timeval t0, t1;
153	unsigned long long len = 0;
154	struct uio auio;
155	struct iovec aiov;
156
157	bzero(&aiov, sizeof(aiov));
158	bzero(&auio, sizeof(auio));
159	auio.uio_iov = &aiov;
160	auio.uio_segflg = UIO_NOCOPY;
161
162	error = fget(td, kio->kio_socket, &fp);
163	if (error != 0)
164		return error;
165
166	if ((fp->f_flag & FWRITE) == 0) {
167		fdrop(fp, td);
168		return EBADF;
169	}
170	if (fp->f_type == DTYPE_SOCKET) {
171		len = kio->kio_totalsize;
172		microtime(&t0);
173		do {
174			nbyte =  MIN(len, (unsigned long long)nbyte);
175			aiov.iov_len = nbyte;
176			auio.uio_resid = nbyte;
177			auio.uio_offset = 0;
178			error = sosend((struct socket *)fp->f_data, NULL,
179				       &auio, NULL, NULL, 0, td);
180			len -= auio.uio_offset;
181		} while (error == 0 && len != 0);
182		microtime(&t1);
183	} else
184		error = EFTYPE;
185	fdrop(fp, td);
186	if (error != 0)
187		return error;
188	timersub(&t1, &t0, &kio->kio_elapsed);
189
190	kio->kio_bytesdone = kio->kio_totalsize - len;
191
192	return 0;
193}
194
195static int
196kttcp_recv(struct thread *td, struct kttcp_io_args *kio)
197{
198	struct file *fp;
199	int error;
200	struct timeval t0, t1;
201	unsigned long long len = 0;
202	struct uio auio;
203	struct iovec aiov;
204
205	bzero(&aiov, sizeof(aiov));
206	bzero(&auio, sizeof(auio));
207	auio.uio_iov = &aiov;
208	auio.uio_segflg = UIO_NOCOPY;
209
210	error = fget(td, kio->kio_socket, &fp);
211	if (error != 0)
212		return error;
213
214	if ((fp->f_flag & FWRITE) == 0) {
215		fdrop(fp, td);
216		return EBADF;
217	}
218	if (fp->f_type == DTYPE_SOCKET) {
219		len = kio->kio_totalsize;
220		microtime(&t0);
221		do {
222			nbyte =  MIN(len, (unsigned long long)nbyte);
223			aiov.iov_len = nbyte;
224			auio.uio_resid = nbyte;
225			auio.uio_offset = 0;
226			error = soreceive((struct socket *)fp->f_data,
227					  NULL, &auio, NULL, NULL, NULL);
228			len -= auio.uio_offset;
229		} while (error == 0 && len > 0 && auio.uio_offset != 0);
230		microtime(&t1);
231		if (error == EPIPE)
232			error = 0;
233	} else
234		error = EFTYPE;
235	fdrop(fp, td);
236	if (error != 0)
237		return error;
238	timersub(&t1, &t0, &kio->kio_elapsed);
239
240	kio->kio_bytesdone = kio->kio_totalsize - len;
241
242	return 0;
243}
244
245static struct cdev *kttcp_dev;
246
247/*
248 * Initialization code, both for static and dynamic loading.
249 */
250static int
251kttcpdev_modevent(module_t mod, int type, void *unused)
252{
253	switch (type) {
254	case MOD_LOAD:
255		kttcp_dev = make_dev(&kttcp_cdevsw, 0,
256				      UID_ROOT, GID_WHEEL, 0666,
257				      "kttcp");
258		return 0;
259	case MOD_UNLOAD:
260		/*XXX disallow if active sessions */
261		destroy_dev(kttcp_dev);
262		return 0;
263	}
264	return EINVAL;
265}
266
267static moduledata_t kttcpdev_mod = {
268	"kttcpdev",
269	kttcpdev_modevent,
270	0
271};
272MODULE_VERSION(kttcpdev, 1);
273DECLARE_MODULE(kttcpdev, kttcpdev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
274